Qt+ffmpeg+SDL硬件解码并渲染

一、概述

  在上一篇博客中使用Qt+ffmpeg+SDL实现H264/H265软解码并渲染。本节则在上一篇的基础上加上硬解码并渲染出画面。

  使用AV_HWDEVICE_TYPE_DXVA2做硬解码测试。其解码出来的数据格式为NV12格式。

  格式说明:

  NV12 格式的视频数据由Y 平面和UV 平面组成,其中:
    • Y 平面:存储亮度信息(灰度值),分辨率与原始视频相同(如 1080p、720p)。
    • UV 平面:存储色度信息(色差信号),分辨率为 Y 平面的 1/2(宽)×1/2(高)(即 4:2:0 采样),且 UV 分量交替存储在同一个平面中。
    • 内存布局如下:

       

二、代码示例

  1.初始化硬解码上下文

//硬件加速格式 DXVA2
auto hw_type = AV_HWDEVICE_TYPE_DXVA2;
//初始化硬件加速上下文
AVBufferRef* hw_ctx = nullptr;
av_hwdevice_ctx_create(&hw_ctx, hw_type, NULL, NULL, 0);

 

  2.给AVCodecContext设置硬解码(GPU加速)

//设定硬件GPU加速
codecCtx->hw_device_ctx = av_buffer_ref(hw_ctx);

 

  3.开始硬解码转换

if (codecCtx->hw_device_ctx)//如果支持硬解码
{
                //硬解码转换GPU =》CPU 显存=》内存
                //AV_PIX_FMT_NV12,      ///< planar YUV 4:2:0
                av_hwframe_transfer_data(hw_frame, frame, 0);
                pframe = hw_frame;
                qDebug() << "硬解码";
}

 

  4.渲染,渲染的时候要考虑字节对齐问题。如果字节对齐了就进行平面复制。如果字节没有对齐就逐行复制。防止有花屏的情况发生

switch (frame->format)
{
case AV_PIX_FMT_YUV420P:
    return renderUtil->DrawFrame(frame->data[0], frame->data[1], frame->data[2], frame->linesize[0], frame->linesize[1], frame->linesize[2]);
case AV_PIX_FMT_NV12:
{
    if (!nv12_data_cache)
    {
        nv12_data_cache = new char[frame->width * frame->height * 1.5];
    }
    //将data的数据复制到缓存中
    if (frame->linesize[0] == frame->width)//字节对齐了
    {
        memcpy(nv12_data_cache, frame->data[0], frame->linesize[0] * frame->height);//y
        memcpy(nv12_data_cache + frame->linesize[0] * frame->height, frame->data[1], frame->linesize[1] * frame->height / 2);//uv
    }
    else {//字节未对齐需要逐行复制
        //先复制y
        for (int i = 0;i < frame->height;i++)
        {
            memcpy(nv12_data_cache + i * frame->linesize[0], frame->data[0] + frame->linesize[0] * i, frame->width);
        }

        //再复制uv
        for (int i = 0;i < frame->height / 2;i++)
        {
            auto p = nv12_data_cache + frame->height * frame->width;
            memcpy(p + i * frame->width, frame->data[1] + i * frame->linesize[1], frame->width);
        }
    }
    int data_size = frame->width * frame->height * 1.5;
    return renderUtil->DrawFrame((const unsigned char*)nv12_data_cache, data_size);
}
case AV_PIX_FMT_BGRA:
case AV_PIX_FMT_ARGB:
case AV_PIX_FMT_RGBA:
case AV_PIX_FMT_RGB24:
    return renderUtil->DrawFrame(frame->data[0], frame->linesize[0]);
}

 

posted on 2025-05-26 13:39  飘杨......  阅读(152)  评论(0)    收藏  举报