gstreamer中tee如何实现动态增减支路(预览+截图+录像)

Posted 唯獨你没懂

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了gstreamer中tee如何实现动态增减支路(预览+截图+录像)相关的知识,希望对你有一定的参考价值。

系列文章目录

Gstreamer中获取帧数据的方式

gstreamer中如何使用probe(探针)获取帧数据
gstreamer拉流rtsp使用appsink获取帧数据(预览+截图)
gstreamer中如何使用fakesink获取帧数据(预览+截图)

文章目录


前言

前面已经介绍过多种方式实现在显示预览的同时如何获取帧数据用来截图。本文就继续研究在此前的基础上利用tee如何实现可控的录像的支路。


Tee

Tee有一个 sink pad 而没有初始的 source pads:需要请求tee它们然后 添加它们。通过这种方式,输入流可以被复制任意次。缺点是使用 Request Pads 链接元素不像链接 Always Pads 那样自动连接。同时tee的支路阻塞会响应其他所有支路,导致整个管道阻塞。我们可以利用Request Pad特性就可以实现下游支路有需求时连接tee,不需要时可以断开tee。这样就可以实现控制单个支路的同时不影响其他支路。下面来看演示。

管道结构

  1. 录像前

  2. 开始录像(tee和录像支路连接)

  3. 录像结束后(tee只断开录像支路)
    这里演示的管道截图功能使用的是前面介绍的fakesink方式。几种方式截图都是可行的。使用fakesink和appsink会在tee后面多一条支路。而使用probe的方式可以在pipeline中我们想要的element位置任意选择。
    为了方便演示还是使用videotestsrc作为测试源。通过管道图能够看到开始录像后tee多了一条用来录像的支路。录像支路的连接和断开都不会影响其他支路。从管道图可以知道录像使用先编码再封装的方式保存为视频文件。gstreamer插件中提供了多种视频的编码方式。有h264、h265、vp8、vp9等等可供选择。下面介绍tee如何实现动态连接和断开。

qt程序界面


除了录制和截图功能外。还有可选择播放源的分辨率以及暂停和播放功能。录像按钮S_Record使用SIGNAL(toggled(bool))方式触发,开始和结束录制由同一个按钮的两种状态控制。为了避免意外,录制时关闭更改分辨率和暂停功能。

Tee的request方式连接和断开(录像)

代码如下(示例):

typedef struct _CustomData 
	GstElement *pipeline;
	 GstElement *vsrc, *capsfilter, *tee, *play_queue, *vconvert, *scale, *vsink ;
	 GstElement r*record_queue, *h264enc, *recordcapsfilte *h264parse, *qtmux, *filesink;
	 GstElement *fake_queue, *fconvert, *fakecapsfilter, *fsink;
	
	 GstPad *tee_record_pad;
	 GstPad *queue_record_pad;
	 GstPad *fsink_pad;
	 GstCaps *filter_caps,*filter_caps2;
	 GstCaps *x264enc_caps;
	
	 GstMapInfo  map_info;
	 int Width = 640, Height = 480;
	
	 // 截图和录像标志
	 bool capture_flag = false;
	 bool record_flag = false;
 CustomData;	

//下面用的相关变量都在结构体里创建
CustomData *date;

void PlayerWindow::onRecordClicked(bool checked) 	//这里使用qt中的按键slots函数

    if(checked)
		.........................................................
		.........................................................
		.........................................................
		
		//这里只列出关键代码  完整代码可见qt工程链接
		
		//先将支路element放入bin中
        gst_bin_add_many(GST_BIN (date->pipeline), date->record_queue, date->h264enc, date->recordcapsfilter,
                         date->h264parse, date->qtmux, date->filesink, NULL);

        gst_element_sync_state_with_parent(date->record_queue);
		
		// link element
        if(gst_element_link_many(date->record_queue, date->h264enc, date->recordcapsfilter,
                            date->h264parse, date->qtmux, date->filesink, NULL) != TRUE)
            g_printerr ("Elements could not be linked.\\n");
            gst_object_unref (date->pipeline);
              return ;
        
		
		//这里是关键
        date->tee_record_pad = gst_element_get_request_pad (date->tee, "src_%u");
        g_print ("Obtained request pad %s for capture branch.\\n", gst_pad_get_name (date->tee_record_pad));
        date->queue_record_pad = gst_element_get_static_pad (date->record_queue, "sink");

        if(gst_pad_link (date->tee_record_pad, date->queue_record_pad) != GST_PAD_LINK_OK)
            g_printerr ("Tee capture could not be linked.\\n");
            gst_object_unref (date->pipeline);
            return ;
        
        gst_object_unref (date->queue_record_pad);
        g_print ("start record \\n");
        date->record_flag = true;

        gst_element_set_state (date->pipeline, GST_STATE_PLAYING);
    else
        //stop record
        .........................................................
		.........................................................
		.........................................................		
		//这里只列出关键代码  完整代码可见qt工程链接


        // 发送eos通知支路的qtmux视频封装结束 。异常结束会导致视频文件没有结尾导致文件不能正常播放
        if(FALSE == gst_element_send_event(date->qtmux, gst_event_new_eos()))
        
            g_print("send eos fail\\n");
        		
		//要结束录像则先断开(tee的srcpad)与录像支路的(queue的sinkpad)连接
        date->queue_record_pad = gst_element_get_static_pad (date->record_queue, "sink");
        if(gst_pad_unlink(date->tee_record_pad, date->queue_record_pad) != TRUE)
            g_printerr ("Tee capture could not be unlinked.\\n");
            gst_object_unref (date->pipeline);
            return ;
        
        gst_object_unref (date->queue_record_pad);
        
        gst_element_release_request_pad (date->tee, date->tee_record_pad);
        gst_object_unref (date->tee_record_pad);	//销毁teepad



        .........................................................
		.........................................................
		.........................................................		
		//这里只列出关键代码  完整代码可见qt工程链接

    


总结

通过tee机制的学习和了解后,不仅仅只是实现可控录像的功能。在其他媒体流中能用到它的地方有很多。比如前面说到的显示的同时tee一条支路appsink来将数据写入文件。或者播放音乐和连接可视化模块。那了解tee如何动态连接后我们就能做更多灵活的处理。

附 ubuntu20.04 qt5.14 工程链接

gstreamer+qt实现照相机功能(显示预览+可控录像+拍照截图)

如何在 gstreamer 中暂停视频录制

【中文标题】如何在 gstreamer 中暂停视频录制【英文标题】:How to pause video recording in gstreamer 【发布时间】:2011-09-23 05:30:22 【问题描述】:

我这样创建了一个管道:

v4l2src -> tee -> queue -> encoder -> avimux -> filesink
           tee -> queue -> v4l2sink

现在我想暂停录制(保持环回,但暂停编码器),当我恢复时,我希望重新编码文件从我恢复的地方继续。 我尝试使用 gst_element_set_state: 如果我暂停管道,则环回停止。 如果我暂停编码器,则 gst_element_set_state 的返回值是可以的,但编码器并没有真正暂停。 我暂停了avimux,与编码器相同。 任何人都可以帮忙吗?非常感谢。

【问题讨论】:

这有帮助吗? ***.com/questions/4712266/… 【参考方案1】:

请查看 gst-plugins-bad 中的 camerabin / camerabin2。不幸的是,您想要做的有点复杂。我会解释的。对于初学者,您需要将 src-pad 放在队列中,将其设置为leaky=downstream 并阻止 src-pad。这会暂停视频。您也可以在队列之后使用阀门元素以获得相同的效果。如果你幸运的话,一切都很好(avimux 应该是这种情况)。对于其他格式(mp4mux),您需要记住暂停时最后一个缓冲区的时间戳(通过 pad-data-probe),当您在取消暂停后获得第一个新缓冲区时,从时间戳中减去暂停时间。否则,您将在生成的视频中暂停。这是因为 mp4 容器中的视频流可能很稀疏。这也会影响其他格式。从理论上讲,您还应该能够通过在取消暂停后(在第一个缓冲区之前)向下游发送新段事件来处理此问题,但我没有尝试过。再次检查它是如何完成的,尤其是在 camerabin2 中。还可以考虑只使用 camerabin2 :)

【讨论】:

以上是关于gstreamer中tee如何实现动态增减支路(预览+截图+录像)的主要内容,如果未能解决你的问题,请参考以下文章

Angular 动态增减表单项

如何通过动态链接在 GStreamer 合成器中正确播放视频?

iview 动态实现增减输入框

如何在 C 程序中使用 Gstreamer 在视频中动态添加和删除字幕

仅向视频管道提供音频时,Gstreamer 管道卡在预滚动状态

haproxy动态增减主机与keepalived高级应用