gstreamer的caps event和new_segment event


caps event


先说下caps event,简单的讲,caps event就是用来配置格式的event,当一个媒体格式被协商时,对等元素会收到CAPS event的GstCaps通知。


caps event从哪里来?


这里以播放h264.mp4的pipeline为例:

gst-launch-1.0 filesrc location=/home/hui/h264.mp4 ! qtdemux ! avdec_h264 ! xvimagesink

为例,在avdec_h264中收到的caps event只有一个可能就是从qtdemux中来的:

设置好环境变量GST_DEBUG=qtdemux:6,GST_EVENT:6,libav:6,保存pipeline运行log后,搜索new_caps可以看到:

  qtdemux qtdemux.c:8877:gst_qtdemux_configure_stream:<qtdemux0> setting caps video/x-h264,
GST_EVENT gstevent.c:892:gst_event_new_caps: creating caps event video/x-h264, stream-format

根据LOG信息,在qtdemux代码中,很快可以找到gst_qtdemux_configure_stream中创建了caps event,creating caps event是在gst_pad_set_caps函数里面,通过gst_event_new_caps创建caps,然后输出的:

    if (CUR_STREAM (stream)->caps) {
      if (!prev_caps
          || !gst_caps_is_equal_fixed (prev_caps, CUR_STREAM (stream)->caps)) {
        GST_DEBUG_OBJECT (qtdemux, "setting caps %" GST_PTR_FORMAT,
            CUR_STREAM (stream)->caps);
        // 创建event caps 
        gst_pad_set_caps (stream->pad, CUR_STREAM (stream)->caps);
      } else {
        GST_DEBUG_OBJECT (qtdemux, "ignore duplicated caps");
      }

gst_pad_set_cap函数创建event caps


在gst_pad_set_caps中,创建了GST_EVENT_CAPS类型的event,然后gst_pad_push_event将这个event发送到pad上,如果pad没有link,event会被存起来,处于pending状态,从后面的log中可以看到这个信息。

static inline gboolean
gst_pad_set_caps (GstPad * pad, GstCaps * caps)
{
  GstEvent *event;
  gboolean res = TRUE;

  g_return_val_if_fail (GST_IS_PAD (pad), FALSE);
  g_return_val_if_fail (caps != NULL && gst_caps_is_fixed (caps), FALSE);

  // 创建新的caps event
  event = gst_event_new_caps (caps);

  if (GST_PAD_IS_SRC (pad))
    res = gst_pad_push_event (pad, event);
  else
    res = gst_pad_send_event (pad, event);

  return res;
}

stored sticky:

store_sticky_event:<'':video_0> stored sticky event caps
store_sticky_event:<'':video_0> notify caps
check_sticky:<'':video_0> pushing all sticky events
push_sticky:<'':video_0> event stream-start was already received
gst_pad_push_event_unchecked:<'':video_0> Dropping event caps because pad is not linked
push_sticky:<'':video_0> pad was not linked, mark pending

所以在qtdemux中创建了caps event,然后下发给avdec_h264之后,之后进入gst_video_decoder_sink_event_default处理,因为videodecoder是avdec_h264(gstavviddec)的基类。

gst_video_decoder_sink_event_default中又会调用gst_video_decoder_setcaps将caps给avdec_h264,然后调用decoder_class->set_format就到了avviddec中的gst_ffmpegviddec_set_format函数,因为avviddec继承自videodecoder:

// gst_video_decoder_class_init
// klass->sink_event = gst_video_decoder_sink_event_default;

// gst_video_decoder_sink_event_default:
case GST_EVENT_CAPS:
{
  GstCaps *caps;

  gst_event_parse_caps (event, &caps);
  ret = gst_video_decoder_setcaps (decoder, caps);
  gst_event_unref (event);
  event = NULL;
  break;
}

// gst_video_decoder_setcaps
  if (decoder_class->set_format)
    ret = decoder_class->set_format (decoder, state);

Fix caps

当Caps没有范围或列表时,它就是固定的。利用gst_caps_is_fixed()可以测试是否是fixed capsfixed caps可用于向下游元素通知当前媒体的类型。


gst_ffmpegviddec_set_format中解析codec_data


为什么是在gst_ffmpegviddec_set_format函数中,因为在gst_ffmpegviddec_class_init函数中,viddec_class->set_format使用
gst_ffmpegviddec_set_format函数初始化的。

通过在子类中赋值父类的函数指针,在父类中调用,达到了C++中的类似动态调用的效果。

// gst_ffmpegviddec_class_init

viddec_class->set_format = gst_ffmpegviddec_set_format;
viddec_class->handle_frame = gst_ffmpegviddec_handle_frame;
viddec_class->start = gst_ffmpegviddec_start;
viddec_class->stop = gst_ffmpegviddec_stop;
viddec_class->flush = gst_ffmpegviddec_flush;
viddec_class->finish = gst_ffmpegviddec_finish;
viddec_class->drain = gst_ffmpegviddec_drain;
viddec_class->decide_allocation = gst_ffmpegviddec_decide_allocation;
viddec_class->propose_allocation = gst_ffmpegviddec_propose_allocation;

同样的,这些在其他继承了Videodecoder的插件中也都是这样的实现的,下面这段是从v4l2dec中找到的代码:

// gst_v4l2_video_dec_class_init

video_decoder_class->open = GST_DEBUG_FUNCPTR (gst_v4l2_video_dec_open);
video_decoder_class->close = GST_DEBUG_FUNCPTR (gst_v4l2_video_dec_close);
video_decoder_class->start = GST_DEBUG_FUNCPTR (gst_v4l2_video_dec_start);
video_decoder_class->stop = GST_DEBUG_FUNCPTR (gst_v4l2_video_dec_stop);
video_decoder_class->finish = GST_DEBUG_FUNCPTR (gst_v4l2_video_dec_finish);
video_decoder_class->flush = GST_DEBUG_FUNCPTR (gst_v4l2_video_dec_flush);
video_decoder_class->drain = GST_DEBUG_FUNCPTR (gst_v4l2_video_dec_drain);
video_decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_v4l2_video_dec_set_format);

到此,在这个简单例子中,caps event由qtdemux创建,然后gst_pad_push_event将caps event push到pad上,avdec_h264的sink event上收到后进行处理,进而在gst_ffmpegviddec_set_format里面对caps event进行解析。


new_segment event


new_segment的创建处理过程

还是以前面的pipeline为例,看下new_segment的创建处理过程,首先,看一下这个过程的调用栈(从下往上看):

1   gst_ffmpeg_caps_with_codecid         gstavcodecmap.c   3258 0x7ffff6ab1b4a 
2   gst_ffmpegviddec_set_format          gstavviddec.c     526  0x7ffff6abf9b9 
3   gst_video_decoder_setcaps            gstvideodecoder.c 897  0x7ffff6ca54a3 
4   gst_video_decoder_sink_event_default gstvideodecoder.c 1379 0x7ffff6ca7159 
5   gst_video_decoder_sink_event         gstvideodecoder.c 1690 0x7ffff6ca8327 
6   gst_pad_send_event_unchecked         gstpad.c          5900 0x7ffff7ed7ba3 
7   gst_pad_push_event_unchecked         gstpad.c          5544 0x7ffff7ed6420 
8   push_sticky                          gstpad.c          4047 0x7ffff7ed0257 
9   events_foreach                       gstpad.c          608  0x7ffff7ec578a 
10  check_sticky                         gstpad.c          4106 0x7ffff7ed0620 
11  gst_pad_push_event                   gstpad.c          5675 0x7ffff7ed6c8b 
12  gst_qtdemux_stream_update_segment    qtdemux.c         4987 0x7ffff6e853dd 

gst_qtdemux_stream_update_segment中创建new_segment event,然后通过gst_pad_push_event把event push到pad上。

 GST_EVENT_SEGMENT = GST_EVENT_MAKE_TYPE (70, FLAG(DOWNSTREAM) | FLAG(SERIALIZED) | FLAG(STICKY)),

gst_pad_push_event将事件发送到给定pad的peer pad上:

  • srcpad: TYPE_EVENT类型是GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM
  • sinkpad: TYPE_EVENT类型是GST_PAD_PROBE_TYPE_EVENT_UPSTREAM

在gst_pad_send_event_unchecked中从pad上获取eventfullfunceventfunc函数(在gstvideodecoder.c里面的代码是,从这段代码中也可以看到,如果是GST_EVENT_CAPS类型,没有eventfunc处理,那么就会返回GST_FLOW_NOT_NEGOTIATED

  eventfullfunc = GST_PAD_EVENTFULLFUNC (pad);
  eventfunc = GST_PAD_EVENTFUNC (pad);
  // check ...
  
  if (eventfullfunc) {
    ret = eventfullfunc (pad, parent, event);
  } else if (eventfunc (pad, parent, event)) {
    ret = GST_FLOW_OK;
  } else {
    /* something went wrong */
    switch (event_type) {
      case GST_EVENT_CAPS:
        ret = GST_FLOW_NOT_NEGOTIATED;
        break;
      default:
        ret = GST_FLOW_ERROR;
        break;
    }
  }

gst_pad_set_event_function (pad, GST_DEBUG_FUNCPTR (gst_video_decoder_sink_event))),这个例子中,从pad上获取的eventfunc就是gst_video_decoder_sink_event。

static gboolean
gst_video_decoder_sink_event (GstPad * pad, GstObject * parent,
    GstEvent * event)
{
  GstVideoDecoder *decoder;
  GstVideoDecoderClass *decoder_class;
  gboolean ret = FALSE;

  decoder = GST_VIDEO_DECODER (parent);
  decoder_class = GST_VIDEO_DECODER_GET_CLASS (decoder);

  GST_DEBUG_OBJECT (decoder, "received event %d, %s", GST_EVENT_TYPE (event),
      GST_EVENT_TYPE_NAME (event));

  // gst_video_decoder_sink_event_default
  if (decoder_class->sink_event)
    ret = decoder_class->sink_event (decoder, event);

  return ret;
}

gst_video_decoder_sink_event中的sink_event函数初始化为gst_video_decoder_sink_event_default,所以接下来gst_video_decoder_sink_event_default是处理event的函数:


 case GST_EVENT_SEGMENT:
 {
   GstSegment segment;

   gst_event_copy_segment (event, &segment);
   // ...
}

完成上面的代码分析,下面是对segment的说明。


segment详解


segment事件只能与buffer同步向downstream发送,并包含buffer的时间信息和播放属性。

一个segment有一个开始时间@start,一个停止时间@stop和一个处理速率@applied_rate

@start@stop标记要处理的buffer范围,全部不在范围内的数据将不被处理。

@start不能是-1,@stop可以是-1。一个有效的@stop,它必须大于或等于@start。

@applied_rate值表示调整后的速率,对时间戳和缓冲区的内容进行了调整。

例如,如果一个元素有一个输入segment预期播放速率为2.0,应用速率applied_rate为1.0,它可以调整为传入的timestamps和buffer内容的一半,并输出一个segment事件速率为1.0,应用速率为2.0。


segment


GStreamer中的一个segment表示一组必须被处理的media samples。一个segment有一个开始时间,一个停止时间和一个处理速率。

一个媒体流有一个开始时间和一个停止时间。开始时间总是0,停止时间是总时长(如果不知道,比如直播流,则是-1)。我们把这称为完整的媒体流。

完整media stream的片段可以通过在媒体流上发布seek来播放。seek有一个开始时间,一个停止时间和一个处理速率。

            complete stream
+------------------------------------------------+
0                                              duration
       segment
   |--------------------------|
 start                       stop

segment的播放通过从sourcedemuxer元素,push包含segment的开始时间停止时间速率SEGMENT事件开始。 目的是通知下游元素请求的segment位置。 某些element可能会产生位于segment之外的buffer,因此可能会被丢弃或剪裁。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值