惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Last Week in AI
Last Week in AI
阮一峰的网络日志
阮一峰的网络日志
P
Proofpoint News Feed
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
MongoDB | Blog
MongoDB | Blog
云风的 BLOG
云风的 BLOG
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
J
Java Code Geeks
WordPress大学
WordPress大学
T
The Blog of Author Tim Ferriss
V
Visual Studio Blog
小众软件
小众软件
Microsoft Azure Blog
Microsoft Azure Blog
博客园_首页
IT之家
IT之家
Vercel News
Vercel News
C
Check Point Blog
Google DeepMind News
Google DeepMind News
月光博客
月光博客
D
DataBreaches.Net
酷 壳 – CoolShell
酷 壳 – CoolShell
美团技术团队
Y
Y Combinator Blog
Hugging Face - Blog
Hugging Face - Blog

博客园 - 飘杨......

为ZLMediaKit在windows10编译srtp Windows10安装部署ZLMediaKit SurfaceView+EGL+OpenGL ES+ffmpeg播放视频并解决黑屏不显示画面的问题 SurfaceView+ANativeWindow+ffmpeg播放视频并解决花屏问题 SDL播放音频工具类封装 Qt QWidget中dropEvent(QDropEvent* e)拖拽放开不接收事件/不执行,且拖拽到目标后鼠标显示禁用符号 ffmpeg视频转码遇到H264黑屏问题 ffmpeg根据输入的参数截取视频 ffmpeg将视频流录制下来并保存到本地 ffmpeg解封装mp4并解码渲染(SDL2) ffmpeg+sdl播放H264/H265文件 FFmpeg实战:录制Windows屏幕并用FFmpeg压缩成H.264/H.265并保存 ffmpeg SwsContext像素格式转换BGRA转YUV420P H264/H265 NALU的nalu_unit_type解析 ffmpeg码率控制模式详解 ffmpeg-H264/H265常用编解码预设参数 Qt+FFmpeg+SDL 实现多路多格式原始视频播放工具:从像素处理到实时渲染 Qt+SDL 实战:多格式视频播放工具开发 —— 支持 RGBA/ARGB/RGB24/YUV420P 的完整实现 SDL将YUV视频渲染到Qt窗口播放
Qt+ffmpeg+SDL硬件解码并渲染
飘杨...... · 2025-05-26 · via 博客园 - 飘杨......

一、概述

  在上一篇博客中使用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]);
}