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) error {
15 ctx = log.WithLogValues(ctx, "function", "Thumbnail")
16 ctx, cancel := context.WithCancel(ctx)
17 defer cancel()
18
19 pipelineSlice := []string{
20 "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 ! pngenc snapshot=true ! appsink name=appsink",
21 }
22
23 pipeline, err := gst.NewPipelineFromString(strings.Join(pipelineSlice, "\n"))
24 if err != nil {
25 return fmt.Errorf("error creating Thumbnail pipeline: %w", err)
26 }
27
28 defer func() {
29 cancel()
30 err = pipeline.BlockSetState(gst.StateNull)
31 }()
32 appsrc, err := pipeline.GetElementByName("appsrc")
33 if err != nil {
34 return err
35 }
36
37 src := app.SrcFromElement(appsrc)
38 src.SetCallbacks(&app.SourceCallbacks{
39 NeedDataFunc: ReaderNeedData(ctx, r),
40 })
41
42 appsink, err := pipeline.GetElementByName("appsink")
43 if err != nil {
44 return err
45 }
46
47 errCh := make(chan error)
48 go func() {
49 err := HandleBusMessages(ctx, pipeline)
50 cancel()
51 errCh <- err
52 close(errCh)
53 }()
54
55 sink := app.SinkFromElement(appsink)
56 sink.SetCallbacks(&app.SinkCallbacks{
57 NewSampleFunc: WriterNewSample(ctx, w),
58 })
59
60 if err := pipeline.BlockSetState(gst.StatePlaying); err != nil {
61 return fmt.Errorf("error setting pipeline state: %w", err)
62 }
63
64 <-ctx.Done()
65
66 if err := pipeline.BlockSetState(gst.StateNull); err != nil {
67 return fmt.Errorf("error setting pipeline state: %w", err)
68 }
69
70 return <-errCh
71}