当前位置: 首页 > article >正文

ffmpeg库视频硬解码使用流程

        FFmpeg 的硬解码(Hardware Decoding)通过调用 GPU 或专用硬件的编解码能力实现,能显著降低 CPU 占用率。

一、FFmpeg 支持的硬件解码类型

FFmpeg 原生支持多种硬件加速类型,具体由 AVHWDeviceType 定义,包括:

  • NVIDIA CUDA/NVDEC‌:基于 NVIDIA 显卡的解码‌。
  • Intel Quick Sync Video (QSV)‌:Intel 集成显卡的硬件加速‌。
  • VAAPI‌:适用于 Intel/AMD 硬件的通用视频加速 API‌。
  • VideoToolbox‌:macOS/iOS 平台的硬解码‌。
  • MediaCodec‌:Android 平台的硬解码(需 FFmpeg 编译时启用)‌。
    enum AVHWDeviceType {
        AV_HWDEVICE_TYPE_NONE,
        AV_HWDEVICE_TYPE_VDPAU,
        AV_HWDEVICE_TYPE_CUDA,
        AV_HWDEVICE_TYPE_VAAPI,
        AV_HWDEVICE_TYPE_DXVA2,
        AV_HWDEVICE_TYPE_QSV,
        AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
        AV_HWDEVICE_TYPE_D3D11VA,
        AV_HWDEVICE_TYPE_DRM,
        AV_HWDEVICE_TYPE_OPENCL,
        AV_HWDEVICE_TYPE_MEDIACODEC,
    };

av_hwdevice_find_type_by_name:根据名称查找对应的AVHWDeviceType。支持的名称如下所示。

static const char *const hw_type_names[] = {
    [AV_HWDEVICE_TYPE_CUDA]   = "cuda",
    [AV_HWDEVICE_TYPE_DRM]    = "drm",
    [AV_HWDEVICE_TYPE_DXVA2]  = "dxva2",
    [AV_HWDEVICE_TYPE_D3D11VA] = "d3d11va",
    [AV_HWDEVICE_TYPE_OPENCL] = "opencl",
    [AV_HWDEVICE_TYPE_QSV]    = "qsv",
    [AV_HWDEVICE_TYPE_VAAPI]  = "vaapi",
    [AV_HWDEVICE_TYPE_VDPAU]  = "vdpau",
    [AV_HWDEVICE_TYPE_VIDEOTOOLBOX] = "videotoolbox",
    [AV_HWDEVICE_TYPE_MEDIACODEC] = "mediacodec",
};

 与硬解相关的函数:

avcodec_get_hw_config:用于获取编解码器支持的硬件配置AVCodecHWConfig。这里用于获取硬件支持的像素格式。
av_hwdevice_ctx_create:av_hwdevice_ctx_create创建硬件设备相关的上下文信息AVHWDeviceContext和对硬件设备进行初始化。
decoder_ctx->get_format = get_hw_format ,get_hw_format是向AVCodecContext注册的一个函数,用于协商支持的像素格式。
av_hwframe_transfer_data:拷贝数据到一个硬件的surface,或者从一个硬件surface拷贝数据,也就是GPU和CPU之间数据拷贝。这里用于GPU拷贝到CPU。GPU解码后数据格式默认类型是从硬件读取,CUDA可能是AV_PIX_FMT_NV12;而CPU解码后的数据一般是YUV数据,比如AV_PIX_FMT_YUV420P。
av_find_best_stream:查找最佳媒体流(如视频、音频、字幕等)的函数。

enum AVPixelFormat hw_pix_fmt;
enum AVHWDeviceType type = AV_HWDEVICE_TYPE_CUDA;
AVCodec *decoder = NULL;

/* open the input file */
    if (avformat_open_input(&input_ctx, argv[2], NULL, NULL) != 0) {
        fprintf(stderr, "Cannot open input file '%s'\n", argv[2]);
        return -1;
    }

    if (avformat_find_stream_info(input_ctx, NULL) < 0) {
        fprintf(stderr, "Cannot find input stream information.\n");
        return -1;
    }

    /* find the video stream information */
    ret = av_find_best_stream(input_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0);
    if (ret < 0) {
        fprintf(stderr, "Cannot find a video stream in the input file\n");
        return -1;
    }

for (i = 0;; i++) {
        const AVCodecHWConfig *config = avcodec_get_hw_config(decoder, i);
        if (!config) {
            fprintf(stderr, "Decoder %s does not support device type %s.\n",
                    decoder->name, av_hwdevice_get_type_name(type));
            return -1;
        }
        if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX &&
            config->device_type == type) {
            hw_pix_fmt = config->pix_fmt;
            break;
        }
    }

二、FFmpeg 硬件解码器名称及对应编码格式

FFmpeg 支持的硬件解码器名称与编码格式关联紧密,需根据具体硬件平台(如 NVIDIA、Intel、AMD)及接口协议(如 CUDA、VAAPI、QSV)选择适配方案。以下是主流编码格式对应的硬件解码器名称示例:

2.1、视频编码格式与硬件解码器
  1. H.264/AVC

    • h264_cuvid(NVIDIA CUDA加速)
    • h264_qsv(Intel Quick Sync Video)
    • h264_vaapi(跨平台开源接口)
    • h264_amf(AMD Advanced Media Framework)‌
  2. H.265/HEVC

    • hevc_cuvid(NVIDIA)
    • hevc_qsv(Intel)
    • hevc_vaapi(通用接口)
    • hevc_amf(AMD)‌
  3. VP8/VP9

    • vp8_cuvid(NVIDIA)
    • vp9_vaapi(通用接口)
    • vp9_qsv(Intel)‌
  4. AV1

    • av1_qsv(Intel)
    • av1_vaapi(通用接口)‌
2.2、音频编码格式与硬件解码器
  1. AAC

    • 硬件解码依赖平台驱动支持(如 Intel HD Audio),FFmpeg 中通常通过系统接口调用,无独立硬解名称‌。
  2. MP3/Opus

    • 硬解支持较少,多采用软件解码‌。

‌三、硬解码实现流程

1. 初始化硬件设备
  • 获取硬件设备类型
    通过 av_hwdevice_find_type_by_name 或枚举类型确定目标硬解码设备‌。
  • 创建硬件设备上下文
    使用 av_hwdevice_ctx_create 初始化硬件设备上下文(hw_device_ctx)‌。
2. 配置解码器
  • 查找支持硬解码的编解码器
    例如 H.264 硬解需查找 h264_cuvid(NVIDIA)或 h264_mediacodec(Android)等解码器‌。
  • 设置解码器参数
    在 AVCodecContext 中指定 hw_device_ctx,关联硬件设备上下文‌。
3. 解码数据
  • 发送数据包
    调用 avcodec_send_packet 将压缩数据送入解码器。
  • 接收解码帧
    通过 avcodec_receive_frame 获取解码后的帧数据,硬件解码的帧通常存储在 GPU 内存中‌。
4. 处理解码数据
  • 内存映射与格式转换
    若需 CPU 访问解码数据,需使用 av_hwframe_transfer_data 将帧从 GPU 内存复制到 CPU 内存‌。

‌四、代码示例

3.1实现硬件解码(以 ‌NVIDIA CUDA/NVDEC‌ 为例)的完整示例代码。

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/hwcontext.h>

int main(int argc, char *argv[]) {
    AVFormatContext *fmt_ctx = NULL;
    AVCodecContext *codec_ctx = NULL;
    const AVCodec *codec = NULL;
    AVBufferRef *hw_device_ctx = NULL;
    AVPacket *pkt = NULL;
    AVFrame *hw_frame = NULL, *sw_frame = NULL;
    int video_stream_idx = -1;

    // 1. 初始化 FFmpeg
    avformat_network_init();

    // 2. 打开输入文件
    if (avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL) < 0) {
        fprintf(stderr, "无法打开输入文件\n");
        return -1;
    }

    // 3. 查找视频流索引
    if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
        fprintf(stderr, "无法获取流信息\n");
        goto cleanup;
    }
    for (int i = 0; i < fmt_ctx->nb_streams; i++) {
        if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_stream_idx = i;
            break;
        }
    }
    if (video_stream_idx == -1) {
        fprintf(stderr, "未找到视频流\n");
        goto cleanup;
    }

    // 4. 初始化硬件设备 (CUDA)
    if (av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_CUDA, NULL, NULL, 0) < 0) {
        fprintf(stderr, "无法创建 CUDA 硬件设备\n");
        goto cleanup;
    }

    // 5. 配置硬件解码器
    codec = avcodec_find_decoder_by_name("h264_cuvid"); // NVIDIA 硬解解码器
    if (!codec) {
        fprintf(stderr, "未找到支持的硬解解码器\n");
        goto cleanup;
    }
    codec_ctx = avcodec_alloc_context3(codec);
    avcodec_parameters_to_context(codec_ctx, fmt_ctx->streams[video_stream_idx]->codecpar);
    codec_ctx->hw_device_ctx = av_buffer_ref(hw_device_ctx); // 关联硬件设备

    // 6. 打开解码器
    if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
        fprintf(stderr, "无法打开硬解解码器\n");
        goto cleanup;
    }

    // 7. 初始化数据包和帧
    pkt = av_packet_alloc();
    hw_frame = av_frame_alloc();
    sw_frame = av_frame_alloc();

    // 8. 解码循环
    while (av_read_frame(fmt_ctx, pkt) >= 0) {
        if (pkt->stream_index == video_stream_idx) {
            // 发送数据包到解码器
            if (avcodec_send_packet(codec_ctx, pkt) < 0) {
                fprintf(stderr, "发送数据包失败\n");
                continue;
            }

            // 接收解码后的帧
            while (avcodec_receive_frame(codec_ctx, hw_frame) == 0) {
                // 检查是否为硬件帧
                if (hw_frame->format == AV_PIX_FMT_CUDA) {
                    // 将 GPU 内存数据复制到 CPU 内存
                    if (av_hwframe_transfer_data(sw_frame, hw_frame, 0) < 0) {
                        fprintf(stderr, "GPU→CPU 内存拷贝失败\n");
                        continue;
                    }

                    // 在此处理 sw_frame(YUV420 数据)
                    // 例如:保存到文件、渲染、转码等
                    printf("解码一帧:宽度=%d, 高度=%d\n", sw_frame->width, sw_frame->height);
                }
                av_frame_unref(hw_frame);
                av_frame_unref(sw_frame);
            }
        }
        av_packet_unref(pkt);
    }

cleanup:
    // 9. 释放资源
    av_frame_free(&hw_frame);
    av_frame_free(&sw_frame);
    av_packet_free(&pkt);
    avcodec_free_context(&codec_ctx);
    av_buffer_unref(&hw_device_ctx);
    avformat_close_input(&fmt_ctx);
    avformat_network_deinit();
    return 0;
}

说明:

AV_PIX_FMT_CUDA等像素格式对比。

格式存储位置典型用途性能优势
AV_PIX_FMT_CUDAGPU 显存硬解码、全流程 GPU 处理零拷贝、低延迟‌
AV_PIX_FMT_NV12CPU 内存软解码、跨设备处理兼容性强,但需拷贝‌
AV_PIX_FMT_RGB24CPU 内存图像显示、算法输入通用性强,但带宽占用高‌

‌五、注意事项

  1. 编译配置
    启用硬解码需在 FFmpeg 编译时添加对应选项(如 --enable-cuda --enable-cuvid --enable-nonfree)‌。
  2. 平台差异
    • Windows:常用 DXVA2 或 NVIDIA CUDA‌6。
    • Android:需启用 --enable-mediacodec 并关联 MediaCodec API‌。
  3. 兼容性回退
    硬解码失败时需切换至软解(如 h264 解码器)‌。
  4. 硬件类型选择
    若需使用其他硬件(如 Intel QSV 或 VAAPI):
    1)解码器名称改为 h264_qsv 或 h264_vaapi。
    2)修改 AV_HWDEVICE_TYPE_CUDA 为 AV_HWDEVICE_TYPE_QSV 或 AV_HWDEVICE_TYPE_VAAPI。
    • 滤镜处理‌:通过 libavfilter 实现缩放、裁剪、水印等操作‌。
    • 封装格式转换‌:使用 avformat_write_header() 和 av_write_frame() 实现转封装(如 MP4 转 TS)‌。

    ‌六、性能优化

    • 减少内存拷贝‌:直接在 GPU 内存中处理数据(如 OpenGL 渲染)‌。
    • 帧格式限制‌:硬解码输出格式通常为 NV12 或 YUV420P,需适配后续处理流程‌。

    原文地址:https://blog.csdn.net/byxdaz/article/details/146373621
    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.kler.cn/a/595662.html

    相关文章:

  5. 17153. 班级活动(蓝桥杯-python)
  6. 在 Kubernetes(k8s)部署过程中常见的问题
  7. YOLO-UniOW: 高效通用开放世界目标检测模型【附论文与源码】
  8. OpenHarmony子系统开发 - 电源管理(一)
  9. JDK 24 发布,新特性解读!
  10. 基于OFDM+16QAM+Turbo的车联网V2X通信系统matlab性能仿真,对比RS
  11. 使用 Selenium 控制现有 Edge 窗口以规避爬虫检测
  12. 如何在SQL中高效使用聚合函数、日期函数和字符串函数:实用技巧与案例解析
  13. 炫酷的3D按钮效果实现 - CSS3高级特性应用
  14. 大数据点燃智能制造变革之火——从数据到价值的跃迁
  15. PostgreSQL_数据下载并保存(psycopg2)
  16. 关于 2>/dev/null 的作用以及机理
  17. github代理 | 快速clone项目
  18. 云计算赛题私有云服务搭建题目
  19. 【动态规划】详解混合背包问题
  20. 云原生网络拓扑:服务网格的量子纠缠效应
  21. 鸿蒙ArkTS+ArkUI实现五子棋游戏
  22. 2025 centos系统安装docker并迁移docker,docker安装到指定的目录以及文件迁移,docker迁移文件
  23. Redis数据类型详解
  24. 用 .net Core 实现简单的缓存,在小型单体项目中替代Redis