gstreamer笔记:capabilities negociation 规格协商

caps指pad的capabilities,翻译为该pad支持的规格。每一个组件都有自己可以处理的数据规格,规定了当前元素的功能和支持的参数,比如常见的音视频格式、比特率等。
在元素连接的过程中,相邻的元素需要协商彼此可以衔接的数据格式,以便将对应的数据传给对方。比如解码器支持输出RGB和YUV,显示组件支持YUV,两者协商,决定使用YUV的数据格式。

caps协商主要通过问询和事件机制来实现. caps规格协商分为三类:固定协商、传输协商、动态协商。

1.固定协商

src pad只提供一种规格,下游元素不能请求其它的规格,则其实就直接设定死了,并不需要协商过程。比如typefinder或者filesrc,只是读取二进制数据,不存在更多的格式。
在这里插入图片描述

主要流程:

  • gst_pad_use_fixed_caps (src_pad),标记规格是固定
  • gst_pad_set_caps,设定src_pad的规格,通知到下游节点
  /* capsnego */
  caps = gst_caps_new_simple ("audio/x-raw", "format", G_TYPE_STRING, GST_MPC_FORMAT, "layout", G_TYPE_STRING, "interleaved",
      "channels", G_TYPE_INT, i.channels,  "rate", G_TYPE_INT, i.sample_freq, NULL);
  gst_pad_use_fixed_caps (musepackdec->srcpad);
  if (!gst_pad_set_caps (musepackdec->srcpad, caps)) {
    GST_ELEMENT_ERROR (musepackdec, CORE, NEGOTIATION, (NULL), (NULL));
    return FALSE;
  }

gst_pad_set_caps函数会向对等节点发送GST_EVENT_CAPS消息,下游的对等节点收到之后就会进行对应的传输协商。此时下游节点会在event函数中收到GST_EVENT_CAPS事件,解析到该规格,然后往更下游设定。

2. 传输协商

传输协商是单向地决定某个规格,sink pad上收到上游的GST_EVENT_CAPS,解析caps的内容,然后将其设置到src pad。
上面提到,固定协商中,上游节点使用了gst_pad_set_caps函数来设定了固定的规格,下游的节点sink_pad就会从event GST_EVENT_CAPS收到这个固定的规格,然后将这个规格设定到自己的src pad。
在这里插入图片描述

主要流程:

  • gst_xxx_sink_event,收到GST_EVENT_CAPS事件
  • gst_pad_set_caps,解析并设定caps,将caps通知到下游节点
static gboolean gst_my_filter_setcaps (GstMyFilter *filter, GstCaps *caps)
{
  GstStructure *structure;
  int rate, channels;
  gboolean ret;
  GstCaps *outcaps;

  structure = gst_caps_get_structure (caps, 0);
  ret = gst_structure_get_int (structure, "rate", &rate);
  ret = ret && gst_structure_get_int (structure, "channels", &channels);
  if (!ret)
    return FALSE;

  outcaps = gst_caps_new_simple ("audio/x-raw", "format", G_TYPE_STRING, GST_AUDIO_NE(S16),
      "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, channels, NULL);
  ret = gst_pad_set_caps (filter->srcpad, outcaps);
  gst_caps_unref (outcaps);

  return ret;
}

static gboolean gst_my_filter_sink_event (GstPad    *pad, GstObject *parent, GstEvent  *event)
{
  gboolean ret;
  GstMyFilter *filter = GST_MY_FILTER (parent);
  
  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_CAPS:
    {
      GstCaps *caps;
      gst_event_parse_caps (event, &caps);
      ret = gst_my_filter_setcaps (filter, caps);
      break;
    }
    default:
      ret = gst_pad_event_default (pad, parent, event);
      break;
  }
  return ret;
}

3. 动态协商

动态协商和上述的传输协商类似,不过这里收到的规格可能和下游元素不匹配,因此需要询问和协商。此时需要把收到的规格,同下游节点确认,选择一个彼此都可用的规格来使用。
在这里插入图片描述
主要流程:

  • gst_xxx_sink_event : 收到上游节点GST_EVENT_CAPS事件和对应的caps
  • gst_pad_get_allowed_caps :获取下游节点支持的caps(GST_QUERY_CAPS问询)。
  • 根据收到的caps和下游返回的caps,选择一个固定的caps。如果两者的caps没有交集,可以内部做格式转换(否则连接失败)。
  • gst_pad_set_caps,解析并设定caps,将caps通知到下游节点
static gboolean gst_my_filter_setcaps (GstMyFilter *filter, GstCaps *caps)
{
  if (gst_pad_set_caps (filter->srcpad, caps)) {
    filter->passthrough = TRUE;
  } else {
    othercaps = gst_pad_get_allowed_caps (filter->srcpad);//获取下游对等节点支持的caps
    newcaps = gst_caps_copy_nth (othercaps, 0);
    gst_pad_fixate_caps (filter->srcpad, newcaps);//选定一个caps
    if (!gst_pad_set_caps (filter->srcpad, newcaps))
      return FALSE;
...
static gboolean gst_my_filter_sink_event (GstPad    *pad, GstObject *parent, GstEvent  *event)
{
  gboolean ret;
  GstMyFilter *filter = GST_MY_FILTER (parent);
  
  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_CAPS:
    {
      GstCaps *caps;
      gst_event_parse_caps (event, &caps);
      ret = gst_my_filter_setcaps (filter, caps);
      ...

4. 重配置GST_EVENT_RECONFIGURE

当下游元素遇到状态变化,需要上游改变数据规格的时候,可以通过发送GST_EVENT_RECONFIGURE事件,请求重新配置格式。比如用户改变了窗口大小,需要同步对视频尺寸进行裁剪,接受新的图像尺寸。
该事件只是一个通知,下游节点在环境变化时,更改自己的规格,然后请求上游节点重新进行一次协商。之后上游节点拿到的caps规格就是下游节点重新调整的参数。当然,最终规格还是要由上游节点来决定。
当需要时,直接向对方pad发送该事件即可。

    gst_pad_push_event (srcpad, gst_event_new_reconfigure ());

5. 交互事件实现

上述过程中,协商主要涉及GST_EVENT_CAPS、GST_QUERY_CAPS、GST_QUERY_ACCEPT_CAPS。
当前sink pad收到上游的GST_EVENT_CAPS,然后让src pad通过GST_QUERY_CAPS询问下游元素sink pad支持的格式,从中选择一个共同支持的格式。
另外,下游也可以在规格需要更改的时候,发送重新配置的事件GST_EVENT_RECONFIGURE,之后会重新走一遍协商流程。
无论怎样,都是上游的src pad来决定最终的格式。

GstBaseSrcClass中有一个默认的协商函数gst_base_src_default_negotiate,一般的插件不需要重新定义协商函数。该函数获取当前src的caps,再获取对等pad的caps,两者比较选择一个固定的caps,并通知对等节点。这里单独用gst_base_src_default_negotiate代码为例,追溯对应逻辑流程。

5.1. GST_QUERY_CAPS

src pad调用gst_pad_peer_query_caps函数,发出GST_QUERY_CAPS查询,目标是下游的sink pad:

gst_base_src_default_negotiate
	gst_pad_query_caps			//获取当前caps
	gst_pad_peer_query_caps		//获取对等pad的caps		
		--->gst_query_new_caps//新建问询 GST_QUERY_CAPS
				--->gst_query_new_custom
		--->gst_pad_peer_query//发出问询 GST_QUERY_CAPS
	gst_base_src_fixate			//匹配caps
	gst_base_src_set_caps		//设置caps

GST_QUERY_CAPS有默认的处理函数,继续往下游传输。需要当前的下游元素需要自己来处理,就在query函数来实现自己处理相关流程,否则不用关心。下游元素需要将支持的caps作为问询结果,用gst_query_set_caps_result (query, caps)设置caps为问询的结果,回传给上游。如果当前无法决定,可以通过gst_pad_query_default由下游节点来处理。

static gboolean gst_my_filter_sink_query( GstPad *pad, GstObject *parent, GstQuery *query)
{   
    GstMyFilter* filter = GST_MYFILTER(parent);
    GST_LOG_OBJECT ( filter, "gst_my_filter_sink_query received %s query: %" GST_PTR_FORMAT,GST_QUERY_TYPE_NAME (query), query);
    switch( GST_QUERY_TYPE( query ) ) 
    {
        case GST_QUERY_CAPS: 
      	GstCaps *filter, *caps;
      	gst_query_parse_caps (query, &filter);
      	caps = gst_ass_render_get_videosink_caps (pad, (GstAssRender *) parent, filter);
      	gst_query_set_caps_result (query, caps);
      	gst_caps_unref (caps);
      	res = TRUE;
      	break;
      	
      default:
      res = gst_pad_query_default (pad, parent, query);
      break;

注意问询和事件的所有处理都需要考虑更加下游的元素,gst_pad_query_default一定不能漏掉,否则链条断掉,下游元件就收不到应该处理的消息。
gst pad也将GST_QUERY_CAPS和返回值封装成了gst_pad_get_allowed_caps (GstPad * pad)函数,可以直接调用拿到下游节点的规格。

5.2 GST_QUERY_ACCEPT_CAPS

gst_base_src_default_negotiate函数调用gst_base_src_set_caps,根据问询结果创建一个新的caps,并推送对应事件。在事件处理流程中,会调用gst_pad_query_accept_caps来发出问询,并检查结果。

gst_base_src_default_negotiate--->
	gst_base_src_set_caps--->
		gst_pad_push_event--->		//发送事件
			gst_pad_send_event_unchecked--->
				pre_eventfunc_check--->					
					gst_pad_query_accept_caps--->//发送accept
						gst_query_new_accept_caps---> //新建GST_QUERY_ACCEPT_CAPS 
						gst_pad_query--->				//发送GST_QUERY_ACCEPT_CAPS
						gst_query_parse_accept_caps_result--->//检查问询结果

GST_QUERY_ACCEPT_CAPS 也是在问询处理函数处理,因为这里是通过上一阶段返回的caps来选择的,一般默认返回正确,可以不用再做确认。


static gboolean gst_my_filter_sink_query( GstPad *pad, GstObject *parent, GstQuery *query)
{   
    GstMyFilter* filter = GST_MYFILTER(parent);
    GST_LOG_OBJECT ( filter, "gst_my_filter_sink_query received %s query: %" GST_PTR_FORMAT,GST_QUERY_TYPE_NAME (query), query);
    switch( GST_QUERY_TYPE( query ) ) 
    {
        case GST_QUERY_CAPS:
         {
         gst_pad_query_default( pad, parent, query );         
         }
         break;
        case GST_QUERY_ACCEPT_CAPS:
         break;
        default:
         gst_pad_query_default( pad, parent, query );
         break;
    }
}

5.3 GST_EVENT_CAPS

GST_EVENT_CAPS是告诉对方,将要发送对应规格的数据了。该事件在确认caps accept之后,直接发送的。流程和上述一样,在pre_eventfunc_check之后,就会拿到pad的event处理函数,然后进行调用。

gst_base_src_default_negotiate--->
	gst_base_src_set_caps--->
		gst_pad_push_event--->//发送事件
			gst_pad_send_event_unchecked--->
				eventfullfunc = GST_PAD_EVENTFULLFUNC (pad);
  				eventfunc = GST_PAD_EVENTFUNC (pad);
				pre_eventfunc_check						//发送accept 问询			
			    if (eventfullfunc)						//调用pad event handle函数
			    ret = eventfullfunc (pad, parent, event);
			    else
			    ret = eventfunc (pad, parent, event))

这里调用的eventfunc就是我们初始化时候设置的事件处理函数:

static gboolean gst_my_filter_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
  GstMyFilter *filter;
  gboolean ret;
  filter = GST_MYFILTER (parent);
  switch (GST_EVENT_TYPE (event)) {
    case GST_EVENT_CAPS:
    {
      GstCaps *caps;
      gst_event_parse_caps (event, &caps);
      /* do something with the caps */
      /* and forward */
      ret = gst_pad_event_default (pad, parent, event);
      break;
    }

同样地,在代码中一般使用接口gst_pad_set_caps来设置规格,该函数封装了GST_EVENT_CAPS的创建和发送。

5.4 总结

利用gst_base_src_default_negotiate总结整个流程如下:

gst_base_src_default_negotiate
	gst_pad_query_caps			//获取当前caps
	gst_pad_peer_query_caps		//获取对等pad的caps		
		--->gst_query_new_caps//新建问询 GST_QUERY_CAPS
				--->gst_query_new_custom
		--->gst_pad_peer_query//发出问询 GST_QUERY_CAPS
	gst_base_src_fixate			//匹配caps
	gst_base_src_set_caps		//设置caps--->
		gst_pad_push_event--->		//发送GST_EVENT_CAPS事件
			gst_pad_send_event_unchecked--->		
				pre_eventfunc_check--->					
					gst_pad_query_accept_caps--->//发送accept 问询
						gst_query_new_accept_caps---> //新建GST_QUERY_ACCEPT_CAPS 
						gst_pad_query--->				//发送GST_QUERY_ACCEPT_CAPS
						gst_query_parse_accept_caps_result--->//检查问询结果
				ret = eventfunc (pad, parent, event)	//调用事件处理函数GST_EVENT_CAPS
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值