I do have a gstreamer pipeline string running with gst-launch (CreateVideoPipelineSinkWithLaunch).
Now I want to build the same pipeline in code with go-gst (golang bindings).
The resulting pipeline (CreateVideoPipelineSink) does run, but for some reason the webrtc frontend is showing a frame every 30s only.
Have tried to run it with GST_DEBUG=*:3 but this does not show any meaningful (The same warnings are logged with the string launch).
0:00:50.268410257 109844 0x75003c000f70 WARN v4l2 gstv4l2object.c:4721:gst_v4l2_object_set_crop:<v4l2src0:src> VIDIOC_S_CROP failed
0:00:50.278151417 109844 0x75003c000f70 WARN v4l2 gstv4l2object.c:3429:gst_v4l2_object_reset_compose_region:<v4l2src0:src> Failed to get default compose rectangle with VIDIOC_G_SELECTION: Invalid argument
I had this problem many times that just writing the same as in a launch string, does result in trouble and I would understand why.
func CreateVideoPipelineSinkWithLaunch(_ *zap.Logger) (*gst.Pipeline, <-chan media.Sample, error) {
pipeline, err := gst.NewPipelineFromString(`v4l2src device=/dev/video4 ! capsfilter caps="video/x-raw,width=(int)320,height=(int)240" ! videoconvert ! queue ! capsfilter caps="video/x-raw,format=(string)I420" ! x264enc speed-preset=ultrafast tune=zerolatency key-int-max=20 ! capsfilter caps="video/x-h264,stream-format=(string)byte-stream" ! appsink name=appsink`)
if err != nil {
return nil, nil, err
}
elem, err := pipeline.GetElementByName("appsink")
if err != nil {
return nil, nil, err
}
appsink := app.SinkFromElement(elem)
ch := make(chan media.Sample, 100)
setCallback(appsink, ch)
return pipeline, ch, nil
}
func CreateVideoPipelineSink(lg *zap.Logger) (*gst.Pipeline, <-chan media.Sample, error) {
// Create a pipeline
pipeline, err := gst.NewPipeline("pion-video-pipeline")
if err != nil {
return nil, nil, err
}
elems := make([]*gst.Element, 0)
// Create the src
src, err := gst.NewElement("v4l2src")
if err != nil {
return nil, nil, err
}
elems = append(elems, src)
src.Set("device", "/dev/video4")
filter1, err := gst.NewElement("capsfilter")
if err != nil {
return nil, nil, err
}
elems = append(elems, filter1)
c := gst.NewEmptySimpleCaps("video/x-raw")
c.SetValue("width", int(320))
c.SetValue("height", int(240))
lg.Info("capsfilter", zap.String("caps", c.String()))
err = filter1.SetProperty("caps", c)
if err != nil {
return nil, nil, err
}
// just to be on the save side
conv, err := gst.NewElement("videoconvert")
if err != nil {
return nil, nil, err
}
elems = append(elems, conv)
// add a queue
queue, err := gst.NewElement("queue")
if err != nil {
return nil, nil, err
}
elems = append(elems, queue)
// add a filter before enc
filterIn, err := gst.NewElement("capsfilter")
if err != nil {
return nil, nil, err
}
elems = append(elems, filterIn)
cIn := gst.NewEmptySimpleCaps("video/x-raw")
cIn.SetValue("format", "I420")
lg.Info("capsfilter", zap.String("caps", cIn.String()))
err = filterIn.SetProperty("caps", cIn)
if err != nil {
return nil, nil, err
}
// Create the enc
enc, err := gst.NewElement("x264enc")
if err != nil {
return nil, nil, err
}
elems = append(elems, enc)
enc.SetProperty("speed-preset", "ultrafast")
enc.SetProperty("tune", "zerolatency")
enc.SetProperty("key-int-max", 20)
//enc.SetProperty("bitrate", 300)
// add a filter after enc
filterOut, err := gst.NewElement("capsfilter")
if err != nil {
return nil, nil, err
}
elems = append(elems, filterOut)
cOut := gst.NewEmptySimpleCaps("video/x-h264")
cOut.SetValue("stream-format", "byte-stream")
lg.Info("capsfilter", zap.String("caps", cOut.String()))
err = filterOut.SetProperty("caps", cOut)
if err != nil {
return nil, nil, err
}
// Create the sink
appsink, err := app.NewAppSink()
if err != nil {
return nil, nil, err
}
elems = append(elems, appsink.Element)
ch := make(chan media.Sample, 100)
setCallback(appsink, ch)
// Add the elements to the pipeline
pipeline.AddMany(elems...)
// link the elements
gst.ElementLinkMany(elems...)
return pipeline, ch, nil
}