• openh264解码数据流向分析


    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);
    
    • 1
    • 2
    • 3

    gst_video_frame_map的功能

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

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

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

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

     GST_VIDEO_FRAME_COMP_DATA()
     GST_VIDEO_FRAME_PLANE_DATA()
     GST_VIDEO_FRAME_COMP_STRIDE()
     GST_VIDEO_FRAME_PLANE_STRIDE()
    
    • 1
    • 2
    • 3
    • 4

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

    比如视频数据是被分配在一个连续的内存块中还是多个内存块(例如,对于每一个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];
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  • 相关阅读:
    自己动手写数据库:并发管理器的实现,以及并发交易流程的分析
    千寻简Git连接Gitee
    《golang设计模式》第二部分·结构型模式-05-门面模式Facade)
    5、Nacos服务注册服务端源码分析(四)之NotifyCenter
    C++中指针指向无效的内存单元
    “圆”来如此——关于圆周率 π 的36 个有趣事实
    redis基础与安装
    laravel框架基础建设:接口验签封装
    IO和进程day08(消息队列、共享内存、信号灯集)
    C++STL【string】下模拟实现string
  • 原文地址:https://blog.csdn.net/hongszh/article/details/125607368