Live video on the AT Protocol
1package media
2
3import (
4 "context"
5 "fmt"
6 "io"
7 "strings"
8
9 "github.com/go-gst/go-gst/gst"
10 "github.com/go-gst/go-gst/gst/app"
11 "stream.place/streamplace/pkg/log"
12)
13
14func Thumbnail(ctx context.Context, r io.Reader, w io.Writer, format string) error {
15 ctx = log.WithLogValues(ctx, "function", "Thumbnail")
16 ctx, cancel := context.WithCancel(ctx)
17 defer cancel()
18
19 var encoder string
20 switch format {
21 case "jpeg":
22 encoder = "jpegenc snapshot=true"
23 case "png":
24 encoder = "pngenc snapshot=true"
25 default:
26 log.Error(ctx, "media.Thumbnail: expected jpeg or png as format and recieved %s", format)
27 encoder = "pngenc snapshot=true"
28 }
29
30 pipelineSlice := []string{
31 "appsrc name=appsrc ! qtdemux name=demux ! decodebin ! videoconvert ! videoscale ! videorate ! capsfilter name=capsfilter caps=video/x-raw,width=[1,1280],height=[1,720],pixel-aspect-ratio=1/1,framerate=1/999999 ! queue ! ",
32 encoder,
33 " ! appsink name=appsink",
34 }
35
36 pipeline, err := gst.NewPipelineFromString(strings.Join(pipelineSlice, "\n"))
37 if err != nil {
38 return fmt.Errorf("error creating Thumbnail pipeline: %w", err)
39 }
40
41 defer func() {
42 cancel()
43 err = pipeline.BlockSetState(gst.StateNull)
44 }()
45 appsrc, err := pipeline.GetElementByName("appsrc")
46 if err != nil {
47 return err
48 }
49
50 src := app.SrcFromElement(appsrc)
51 src.SetCallbacks(&app.SourceCallbacks{
52 NeedDataFunc: ReaderNeedData(ctx, r),
53 })
54
55 appsink, err := pipeline.GetElementByName("appsink")
56 if err != nil {
57 return err
58 }
59
60 errCh := make(chan error)
61 go func() {
62 err := HandleBusMessages(ctx, pipeline)
63 cancel()
64 errCh <- err
65 close(errCh)
66 }()
67
68 sink := app.SinkFromElement(appsink)
69 sink.SetCallbacks(&app.SinkCallbacks{
70 NewSampleFunc: WriterNewSample(ctx, w),
71 })
72
73 if err := pipeline.BlockSetState(gst.StatePlaying); err != nil {
74 return fmt.Errorf("error setting pipeline state: %w", err)
75 }
76
77 <-ctx.Done()
78
79 if err := pipeline.BlockSetState(gst.StateNull); err != nil {
80 return fmt.Errorf("error setting pipeline state: %w", err)
81 }
82
83 return <-errCh
84}