gstreamer ffmpeg avdec解码数据流向分析

39 篇文章 17 订阅
9 篇文章 2 订阅

avdec_xxx是gst-libav中的解码插件,对应文件是gstavviddec.c。

avdec初始化的时候,decoder中初始化了下面这些函数,所以libav的avviddec插件中,主要的数据处理,关注几个函数的实现就可以了:

  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->handle_frame接收upstream数据进行处理,viddec_class->finish是在基类sink_event中,收到EOS之后会被调用。

ffmpeg中调用@gst_video_decoder_finish_frame将解码的数据push到downstream,经过以下过程处理:

gst_video_decoder_decode_frame:基类decoder frame
- gst_ffmpegviddec_handle_frame
  - gst_ffmpegviddec_frame
    - gst_ffmpegviddec_video_frame
      - gst_video_decoder_finish_frame
gst_ffmpegviddec_handle_frame功能
  • 将frame->input_buffer复制到ffmpeg的packet中
  • avcodec_send_packet将上面的packet发给解码器
  • 从gst_ffmpegviddec_frame中获取解码buffer并发送到downstream
gst_ffmpegviddec_video_frame功能

gst_ffmpegviddec_video_frame中做的工作列举出来如下,最后gst_video_decoder_finish_frame是最关键的一部,将解码后的buffer继续组合成GstBuffer,发给downstream进行处理。

gst_ffmpegviddec_video_frame
- gst_ffmpegviddec_do_qos
- avcodec_receive_frame
- gst_ffmpegviddec_negotiate
  - update_video_context
  - gst_ffmpeg_pixfmt_to_videoformat
  - gst_video_decoder_set_output_state
  - gst_video_decoder_negotiate
- gst_video_decoder_finish_frame(最后关键的一步,push到下游)
解码后的QOS

从上面的栈次序来看,解码后需要做QOS,用到了AVDiscard中定义,AVDiscard的类型定义如下,这个在gst_ffmpegviddec_do_qos函数中可以看到,解码器在在解码下一帧之前执行 qos 计算,设置 skip_frame 标志,如果情况非常糟糕,则跳到下一个关键帧。

enum AVDiscard{
    /* We leave some space between them for extensions (drop some
     * keyframes for intra-only or drop just some bidir frames). */
    AVDISCARD_NONE    =-16, ///< discard nothing 什么都不丢弃
    AVDISCARD_DEFAULT =  0, ///< discard useless packets like 0 size packets in avi 丢弃无用的数据包
    AVDISCARD_NONREF  =  8, ///< discard all non reference 丢弃所有非参考帧
    AVDISCARD_BIDIR   = 16, ///< discard all bidirectional frames 丢弃所有双向帧
    AVDISCARD_NONINTRA= 24, ///< discard all non intra frames 丢弃所有非帧内帧
    AVDISCARD_NONKEY  = 32, ///< discard all frames except keyframes 丢弃除关键帧以外的所有帧
    AVDISCARD_ALL     = 48, ///< discard all 全部丢弃
};
gst_ffmpegviddec_negotiate

gst_ffmpegviddec_negotiate中将avcodec的AVCodecContext中的相关信息转换为gstreamer中的GstVideoInfo,然后调用gst_video_decoder_negotiate进行和downstream的协商。

这是获取caps信息的方法:

  in_s = gst_caps_get_structure (ffmpegdec->input_state->caps, 0);
  if (!gst_structure_has_field (in_s, "interlace-mode")) {
      // ...
  }
  // ...

gst_video_decoder_negotiate

与下游元素协商当前配置的GstVideoCodecState,在任何情况下都取消flag GST_PAD_FLAG_NEED_RECONFIGURE。但如果协商失败,就需要标记上。

gst_ffmpegviddec_get_buffer2

在avviddec中,get_buffer2是ffmpeg解码的数据转换成gstreamer中的GstVideoCodecFrame类型,Fill avpicture的过程,就是将GstVideoCodecFrame对应的buffer地址转换到avpicture里面,后面用到picture的时候会往里面写数据。

  // picture->reordered_opaque : system_frame_nunber;
  frame = gst_video_decoder_get_frame (GST_VIDEO_DECODER (ffmpegdec), picture->reordered_opaque);
  if (G_UNLIKELY (frame->output_buffer != NULL))
    goto duplicate_frame;

  if (picture->opaque) {
    dframe = picture->opaque;
    dframe->frame = frame;
  } else {
    picture->opaque = dframe =
        gst_ffmpegviddec_video_frame_new (ffmpegdec, frame);
  }


 /* Fill avpicture */
  if (!gst_video_frame_map (&dframe->vframe, &ffmpegdec->pool_info,
          dframe->buffer, GST_MAP_READWRITE))
    goto map_failed;
  dframe->mapped = TRUE;

  for (c = 0; c < AV_NUM_DATA_POINTERS; c++) {
    if (c < GST_VIDEO_INFO_N_PLANES (&ffmpegdec->pool_info)) {
      picture->data[c] = GST_VIDEO_FRAME_PLANE_DATA (&dframe->vframe, c);
      picture->linesize[c] = GST_VIDEO_FRAME_PLANE_STRIDE (&dframe->vframe, c);

      if (ffmpegdec->stride[c] == -1)
        ffmpegdec->stride[c] = picture->linesize[c];

      /* libav does not allow stride changes, decide allocation should check
       * before replacing the internal pool with a downstream pool.
       * https://bugzilla.gnome.org/show_bug.cgi?id=704769
       * https://bugzilla.libav.org/show_bug.cgi?id=556
       */
      g_assert (picture->linesize[c] == ffmpegdec->stride[c]);
    } else {
      picture->data[c] = NULL;
      picture->linesize[c] = 0;
    }
    GST_LOG_OBJECT (ffmpegdec, "linesize %d, data %p", picture->linesize[c],
        picture->data[c]);
  }
gst_ffmpegviddec_finish

gst_ffmpegviddec_finish是在结束的时候GST_EVENT_EOS事件后被调用,往下会调用avcodec_flush_buffers进行数据的flush操作。

gst_video_decoder_sink_event_default
    - gst_ffmpegviddec_finish
    	- gst_ffmpegviddec_drain(drain:流走)
      		- gst_ffmpegviddec_frame
       	 		- gst_ffmpegviddec_video_frame
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值