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