当前位置: 首页 > 工具软件 > OpenH264 > 使用案例 >

openh264解码数据流向分析

公冶俊达
2023-12-01

openh264dec中finish_frame的逻辑和avviddec比起来比较简单,在handle_frame中,buffer先送给解码器解码(DecodeFrameNoDelay),然后调用gst_video_decoder_finish_frame将解码后的buffer发到downstream。

DecodeFrameNoDelay解码,返回yuvdata,完成后unmap input_buffer:

openh264dec->decoder->DecodeFrameNoDelay (map_info.data, map_info.size,
                                          yuvdata, &dst_buf_info);
gst_buffer_unmap (frame->input_buffer, &map_info);

gst_video_frame_map的功能

gst_video_frame_map()将在GstVideoFrame结构中填入buffer的像素以及所需的各种视频信息。

gboolean
gst_video_frame_map (GstVideoFrame * frame, GstVideoInfo * info, GstBuffer * buffer, GstMapFlags flags)

使用@info和@buffer来填入到@frame,地址会被传给分配在堆栈中的#GstVideoFrame结构。

可以使用访问器宏访问,如:

 GST_VIDEO_FRAME_COMP_DATA()
 GST_VIDEO_FRAME_PLANE_DATA()
 GST_VIDEO_FRAME_COMP_STRIDE()
 GST_VIDEO_FRAME_PLANE_STRIDE()

这个功能的目的是使你能够很容易地获得视频像素,而不需要担心太多细节问题。

比如视频数据是被分配在一个连续的内存块中还是多个内存块(例如,对于每一个plane都有一个内存块),或者是否使用自定义strides和自定义plane偏移(由每个缓冲区上的GstVideoMeta记录)。

这个函数只是用正确的值填充#GstVideoFrame结构,使用访问器宏,可以很容易地访问数据。它还会为你映射底层的内存块。

gst_video_frame_map的使用从ffmpeg avviddec和openh264dec的实现也可以看到,能方便的把解码器解码的数据放到plane内存块中。

GstVideoFrame的定义

/**
 * GstVideoFrame:
 * @info: the #GstVideoInfo
 * @flags: #GstVideoFrameFlags for the frame
 * @buffer: the mapped GstBuffer's buffer
 * @meta: pointer to metadata if any
 * @id: id of the mapped frame. the id can for example be used to
 *   identify the frame in case of multiview video.
 * @data: pointers to the plane data
 * @map: mappings of the planes
 *
 * A video frame obtained from gst_video_frame_map()
 */
struct _GstVideoFrame {
  GstVideoInfo info;
  GstVideoFrameFlags flags;

  GstBuffer *buffer;
  gpointer   meta;
  gint       id;

  gpointer   data[GST_VIDEO_MAX_PLANES];
  GstMapInfo map[GST_VIDEO_MAX_PLANES];

  /*< private >*/
  gpointer _gst_reserved[GST_PADDING];
};

gst_video_decoder_allocate_output_frame

辅助函数,为@decoder当前的#GstVideoCodecState分配一个缓冲区来容纳一个视频帧。子类应该已经配置了视频状态并设置了src pad caps。从openH264dec和avdec_h264中都可以看到,最后解码的数据要先allocate_output_frame,然后copy数据到这个里面,avviddec里面的处理需要看get_output_buffer函数的实现。

gst_video_decoder_finish_frame

在openh264dec中,通过gst_video_decoder_finish_frame将解码后的数据push到downstream,如果没有提供输出数据,frame被视为skipped。在任何情况下,frame都被视为finished和released。

gst_openh264dec_handle_frame

  • 收到frame后先交给openh264dec解码->DecodeFrameNoDelay
  • gst_video_frame_map:映射frame->output_buffer,将解码的yuvdata填进去。
  • gst_video_decoder_set_output_state:在srcpad上创建新的codec state作为解码器的输出状态
  • gst_video_decoder_finish_frame发送给downstream

因为output state部分配置的是I420,所以在进行数据copy的时候,是yuv依次copy:

// i = 0; Y plane
// i = 1; U plane
// i = 2; V plane
for (i = 0; i < 3; i++) {
    p = GST_VIDEO_FRAME_COMP_DATA (&video_frame, i);
    row_stride = GST_VIDEO_FRAME_COMP_STRIDE (&video_frame, i);
    component_width = GST_VIDEO_FRAME_COMP_WIDTH (&video_frame, i);
    component_height = GST_VIDEO_FRAME_COMP_HEIGHT (&video_frame, i);
    src_width =
        i <
        1 ? dst_buf_info.UsrData.sSystemBuffer.
        iStride[0] : dst_buf_info.UsrData.sSystemBuffer.iStride[1];
    for (row = 0; row < component_height; row++) {
        memcpy (p, yuvdata[i], component_width);
        p += row_stride;
        yuvdata[i] += src_width;
    }
}
 类似资料: