gstreamer笔记

gstreamer官方教程gstreamer中文教程gstreamer插件文档

常用插件:Platform-specific elementsHandy elements

本文内容全部来自于上面这些教程,仅作为个人学习记录。

pipeline

pipeline是gst从输入到输出的一条处理管线,pipeline由element组成,element必须加入pipeline,并按次序首尾相接才能工作。pipeline构建好以后有各种state,包括NULL,READY,PAUSE,PLAYING,state设为PLAYING时,pipline才运行。

//空pipeline的初始化
data.pipeline = gst_pipeline_new ("test-pipeline");

//将element加入pipeline,此时还没指定顺序
//data.source, data.sink都是初始化好的element,gst_bin_add_many()必须以NULL结尾
gst_bin_add_many (GST_BIN (data.pipeline), data.source, data.sink, NULL);

//将elements按顺序连接,只能连接同一个pipeline中的element
gst_element_link (source, sink);
gst_element_link_many (data.convert, data.resample, data.sink, NULL);

//设置pipeline的state
gst_element_set_state (data.pipeline, GST_STATE_PLAYING);
gst_element_set_state (data.pipeline, GST_STATE_NULL);

//对bus进行监听以获取message,可用于debug和异常处理
bus = gst_element_get_bus (data.pipeline);

element

element是gst的基本组件单元,下面这些是不同类型element的示意图。element也就是我们可以在gstreamer插件文档里找到的插件,每个element负责一个功能,比如获取/产生数据(v4l2src),处理数据(videoscale),显示数据(xvimagesink)等等。下面这些element示意图中深色的方块叫做pad,pad是数据输入输出的端口。对于同一个element来讲,它的数据来源于sink pad,处理后从src pad输出,这样看就好像和我们的常识相反。但如果看多个element,则上一个element输出数据到当前elememt的sink pad,而当前element从src pad输出数据到下一个element。

element有很多类型,只有src pad 的叫做source element,只有sink pad 的叫做sink element,二者都有的叫做filter。还有一些特殊的element有一个sink pad,多个src pad,例如demuxer,它会把一个视频解复用成音轨和视频轨,分别输出。

element之间互相连接时,src pad和sink pad之间传输的数据类型必须要能够兼容,在一个视频source后面接一个音频filter肯定是没法工作的。当前传输的数据类型称为capabilities,简称caps。

//生成element,第一个参数是插件的名字,第二个是我们命名的标识
source = gst_element_factory_make ("videotestsrc", "source");
sink = gst_element_factory_make ("autovideosink", "sink");

//有些element包含我们一些可以设置的属性。可以用此函数设置element的properties
//以第一个参数是element,第二个是属性名,第三个是要设置的值,必须以NULL结尾
g_object_set (data.source, "uri", "https://....webm", NULL);

已知一个element的名字以后,我们可以用gst-inspect-1.0工具来获取它的详细信息,例如

#打开终端
gst-inspect-1.0 xvimagesink

gst-inspect-1.0 uridecodebin

gst-inspect-1.0 jpegdec

pad

pad是element数据的出入口。elements之间的连接,实际上是pads之间的连接,我们用gst_element_link连接两个element时,其实是自动找到了这两个element的src pad和sink pad进行了连接。而有时候一个element里存在多个src pad,或者针对pad之间的连接有比较复杂的情况要处理,这时就需要手动对pads进行连接操作。

//用pad的名字获取element上的pad
pad = gst_element_get_static_pad (element, pad_name);

//连接两个pad
gst_pad_link (new_pad, sink_pad);

//检查该pad是否已经被连接
gst_pad_is_linked (sink_pad);

下面展示的是gst-inspect-1.0 jpegdec的结果,找到pad相关信息,我们主要关注的有:src pad、 sink pad的availiability,会有always,sometimes,on request三种情况。还要关注capabilities。这个会在下面详细讲。

caps

之前也提到,pads之间传输的数据类型就叫做caps。一个element的src pad可能可以支持好几种数据类型,例如v4l2src这个插件就可以输出mpeg、h264等各种格式。同理,一个element 的sink pad也可能支持好几种数据类型。这些数据类型就写在了capabilities里,还是看上面的这个图,我们可以知道jpegdec这个element的src pad支持I420,RGB,BGR等等格式的输出,sink pad只有image/jpeg。也就是这个jpeg解码模块,只接受jpeg图像输入,但解压缩后的输出可以是I420,RGB等格式。

//下面这段代码的含义是查询new_pad当前包含的caps是否为"audio/x-raw"格式

//从pad中获取当前caps
new_pad_caps = gst_pad_get_current_caps (new_pad, NULL);
//进一步提取cap中的第一个structure
new_pad_struct = gst_caps_get_structure (new_pad_caps, 0);
//进一步提取cap的名字
new_pad_type = gst_structure_get_name (new_pad_struct);
if (!g_str_has_prefix (new_pad_type, "audio/x-raw")) {
  g_print ("It has type '%s' which is not raw audio. Ignoring.\n", new_pad_type);
  goto exit;
}


//我们还可以这样查询pad支持的所有caps
gst_pad_query_caps(new_pad)

signal

有些element可以在某些事件发生的时候触发signal,并以回调的方式通知你。

使用场景:例如uridecodebin这个element,它的作用是输入一个uri,从uri中获得音视频,再输出给filter element。但是,uridecodebin在经历初始化(gst_element_factory_make)和加入pipeline(gst_bin_add_many)以后,并不能直接与后续element进行连接(gst_element_link)。因为在数据真正流到uridecodebin之前,它没法预知将从uri中获取到啥样的数据,所以它的src pad在一开始是不存在的。我们只能先把后面的elements连接好,等到数据真正流入uridecodebin之后,它会创建出src pad,并且触发一个名为"pad added"的signal通知我们,我们可以用g_signal_connect来监听signal的出现,把signal出现时我们要执行的操作写在回调函数里(在这个例子里,就是要把src pad和后面的element的sink pad连在一起)。

//signal的监听
g_signal_connect (data.source, "pad-added", G_CALLBACK (pad_added_handler), &data);

//实现回调函数pad_added_handler,实现把src pad和后面的element的sink pad连在一起
static void pad_added_handler (GstElement *src, GstPad *new_pad, CustomData *data) {
GstPad *sink_pad = gst_element_get_static_pad (data->convert, "sink");
GstPadLinkReturn ret;
GstCaps *new_pad_caps = NULL;
GstStructure *new_pad_struct = NULL;
const gchar *new_pad_type = NULL;
​
g_print ("Received new pad '%s' from '%s':\n", GST_PAD_NAME (new_pad), GST_ELEMENT_NAME (src));
​
/* If our converter is already linked, we have nothing to do here */
if (gst_pad_is_linked (sink_pad)) {
g_print ("We are already linked. Ignoring.\n");
goto exit;
}
​
/* Check the new pad's type */
new_pad_caps = gst_pad_get_current_caps (new_pad);
new_pad_struct = gst_caps_get_structure (new_pad_caps, 0);
new_pad_type = gst_structure_get_name (new_pad_struct);
if (!g_str_has_prefix (new_pad_type, "audio/x-raw")) {
g_print ("It has type '%s' which is not raw audio. Ignoring.\n", new_pad_type);
goto exit;
}
​
/* Attempt the link */
ret = gst_pad_link (new_pad, sink_pad);
if (GST_PAD_LINK_FAILED (ret)) {
g_print ("Type is '%s' but link failed.\n", new_pad_type);
} else {
g_print ("Link succeeded (type '%s').\n", new_pad_type);
}
​
exit:
/* Unreference the new pad's caps, if we got them */
if (new_pad_caps != NULL)
gst_caps_unref (new_pad_caps);
​
/* Unreference the sink pad */
gst_object_unref (sink_pad);
}

signal也可以用gst-inspect-1.0来查询,下面图中是查询appsink组件得到的signal部分

appsink

appsink是一个element,用于从gst的pipeline中取出数据,送到其它模块进行后续处理。 

appsink具有new-sample和new-preroll信号,new-sample信号在新的sample数据准备好时触发,new-preroll在新的preroll sample数据准备好时触发。在使用前,需要允许appsink发出signal,否则new-sample和new-preroll信号都是无效的。

g_object_set (m_appsink, "emit-signals", TRUE, NULL);

preroll:一个sink元素当且仅当有一个buffer进入pad之后才能完成PAUSED状态的转变,这个过程叫做preroll。为了能够尽快完成向PLAYING状态的转变,避免给用户造成视觉上的延迟,向pipeline中填充buffer(Preroll)是有很有必要的。Preroll在音视频同步方面是非常关键的,确保不会有buffer被sink元素抛弃。

使用appsink把数据取出来的基本步骤如下

//在"pull-sample" signal出现时,将数据用sample接收
g_signal_emit_by_name (appsink, "pull-sample", &sample, &ret);

//如果sample成功拿到数据,下一步用buffer把sample里的数据取出来
buffer = gst_sample_get_buffer (sample);

//再用map将buffer中的数据读出来,设置为只读
gst_buffer_map (buffer, &map, GST_MAP_READ);

​//查询sample的caps,确认数据类型,信息存在info里
caps = gst_sample_get_caps (sample);
info = gst_caps_get_structure (caps, 0);

//把宽高的int型变量读出
gst_structure_get_int (info, "width", &sample_width);
gst_structure_get_int (info, "height", &sample_height);

//用opencv的Mat结构接收map.data,由于之前设置为只读,所以还需作一次深拷贝
cv::Mat img (sample_height, sample_width, CV_8UC3, (unsigned char*)map.data, cv::Mat::AUTO_STEP);
img = img.clone();
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值