当前位置: 首页 > 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. 初始化 FFmpegavformat_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,需适配后续处理流程‌。

    相关文章:

    ffmpeg库视频硬解码使用流程

    FFmpeg 的硬解码&#xff08;Hardware Decoding&#xff09;通过调用 GPU 或专用硬件的编解码能力实现&#xff0c;能显著降低 CPU 占用率。 ‌一、FFmpeg 支持的硬件解码类型‌ FFmpeg 原生支持多种硬件加速类型&#xff0c;具体由 AVHWDeviceType 定义&#xff0c;包括&…...

    LS-NET-006-思科MDS 9148S 查看内存

    LS-NET-006-思科MDS 9148S 查看内存 方法一&#xff1a;使用 show version​ 命令 该命令可显示设备的基本系统信息&#xff0c;包括内存总量。 登录交换机的CLI&#xff08;通过控制台或SSH连接&#xff09;。输入命令&#xff1a; show version 在输出中查找类似以下内容…...

    小程序API —— 54 路由与通信 - 编程式导航

    在小程序中实现页面的跳转&#xff0c;有两种方式&#xff1a; 声明式导航&#xff1a;navigator 组件编程式导航&#xff1a;使用小程序提供的 API 编程式导航 API 提供了五个常用的 API 方法&#xff1a; wx.navigateTo()&#xff1a;保留当前页面&#xff0c;跳转到应用内…...

    关于金融开发领域的一些专业知识总结

    目录 1. 交易生命周期 1.1 证券交易所 1.1.1 交易前 1) 订单生成&#xff08;Order Generation&#xff09; 2) 订单管理&#xff08;Order Management&#xff09; 1.1.2 交易执行 3) 交易匹配&#xff08;Trade Matching&#xff09; 1.1.3 交易后 4) 交易确认&…...

    使用 `pytest` 框架时,可以通过极限封装将 YAML 文件的读取、解析

    在使用 pytest 框架时,可以通过极限封装将 YAML 文件的读取、解析和测试用例的通用逻辑封装成共享的方法或 fixture,从而减少重复代码。以下是详细的实现步骤和示例。 1. 封装 YAML 文件读取和解析 将 YAML 文件的读取和解析逻辑封装到一个工具函数中,供所有测试用例调用。…...

    蓝桥杯练习day3:反转字符串

    一、题意 写一个函数&#xff0c;其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间&#xff0c;你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 示例 1&#xff1a; 输入&#xff1a;s [“h”,“e”,“…...

    DeepSeek-R1深度解读

    deepseek提出了一种通过强化学习&#xff08;RL&#xff09;激励大语言模型&#xff08;LLMs&#xff09;推理能力的方法&#xff0c;个人认为最让人兴奋的点是&#xff1a;通过RL发现了一个叫“Aha Moment”的现象&#xff0c;这个时刻发生在模型的中间版本中。在这个阶段&…...

    15-双链表-双链表基本操作

    题目 来源 827. 双链表 - AcWing题库 思路 此题我只想说&#xff0c;千万千万别漏了头结点和尾结点&#xff0c;不然根本查不出来是哪里出了问题&#xff0c;因为传入的k会有问题&#xff1b;最左边插入&#xff0c;相当于是在头结点的右边插入&#xff08;也就是0号节点的右…...

    正则表达式详解(regular expression)

    &#x1f4a1; 正则表达式&#xff08;Regular Expression, regex&#xff09;知识点总结 &#x1f4a1; 正则表达式是一种用于匹配字符串的模式&#xff0c;广泛用于搜索、替换、验证等操作。 &#x1f4cc; 正则表达式的主要作用 1️⃣ 字符串匹配 &#x1f9d0; 检查一个…...

    经典面试题:C/C++中static关键字的三大核心作用与实战应用

    一、修饰局部变量&#xff1a;改变生命周期&#xff0c;保留跨调用状态 核心作用&#xff1a; ​延长生命周期&#xff1a;将局部变量从栈区移至静态存储区&#xff08;数据段或BSS段&#xff09;&#xff0c;生命周期与程序一致​保留状态&#xff1a;变量在函数多次调用间保…...

    笔记:代码随想录算法训练营day57:99.岛屿数量 深搜、岛屿数量 广搜、100.岛屿的最大面积

    学习资料&#xff1a;代码随想录 注&#xff1a;文中含大模型生成内容 99. 岛屿数量 卡码网题目链接&#xff08;ACM模式&#xff09; 先看深搜方法&#xff1a;找到未标标记过的说明找到一片陆地的或者一片陆地的一个角落&#xff0c;dfs搜索是寻找相连接的陆地其余部分并…...

    【小也的Java之旅系列】01 分布式、集群、微服务的区别

    前言 做Java开发多年&#xff0c;一直以来都有想把Java做成一个系列的想法&#xff0c;最近整理自己的笔记发现有很多值得写的内容&#xff0c;但这些内容又往往杂乱不堪。CSDN上有很多高质量的Java博客&#xff0c;但大多不是从一个人成长的角度去写的。而我们——一个技术人…...

    基于视觉的核桃分级与套膜装置研究(大纲)

    基于视觉的核桃分级与套膜装置研究&#xff1a;从设计到实现的完整指南 &#xff08;SolidWorks、OpenCV、STM32开发实践&#xff09; &#x1f31f; 项目背景与目标 1.1 为什么选择视觉分级与套膜&#xff1f; 产业痛点&#xff1a; 中国核桃年产量全球第一&#xff0c;但…...

    JimuReport与deepseek结合,颠覆现有BI模式

    在数字化转型的浪潮中&#xff0c;企业对数据的依赖程度越来越高&#xff0c;如何高效地分析和利用数据成为关键。JimuReport凭借其强大的报表设计能力和灵活的数据处理功能&#xff0c;已经成为众多企业的首选工具。如今&#xff0c;它即将与DeepSeek深度结合&#xff0c;为企…...

    大白话详细解读函数之柯里化

    1. 函数柯里化是什么&#xff1f; 函数柯里化是一种将多参数函数转换成一系列单参数函数的技术。简单来说&#xff0c;就是把一个接收多个参数的函数&#xff0c;变成每次只接收一个参数&#xff0c;并返回一个新函数&#xff0c;直到所有参数都接收完毕&#xff0c;最后返回结…...

    11、STL中的set使用方法

    一、了解 set 是 C 标准模板库&#xff08;STL&#xff09;中提供的有序关联容器之一。基于红黑树&#xff08;Red-Black Tree&#xff09;实现&#xff0c;用于存储一组唯一的元素&#xff0c;并按照元素的值进行排序。 set的特性 唯一性 键是唯一的。无重复。 有序性 按升序…...

    git 子模块的使用

    1. 子模块的核心概念 独立性&#xff1a;子模块是一个独立的 Git 仓库&#xff0c;有自己的提交历史和分支。 指针机制&#xff1a;主仓库仅记录子模块的特定提交&#xff08;而不是分支&#xff09;&#xff0c;确保代码版本可控。 适用场景&#xff1a;依赖第三方库、多项目…...

    vsftpd服务权限配置

    主配置文件&#xff1a;/etc/vsftpd/vsftpd.conf anonymous_enableYES   #是否启用匿名用户 no_anon_passwordYES   #匿名用户login时不询问口令 anon_upload_enableyes | no # 匿名用户对文件&#xff08;非目录&#xff09;上传权限。 anon_world_readable_onlyyes | …...

    遥感数据获取、处理、分析到模型搭建全流程学习!DeepSeek、Python、OpenCV驱动空天地遥感数据分析

    【扔进数据&#xff0c;直接出结果】在科技飞速发展的时代&#xff0c;遥感数据的精准分析已经成为推动各行业智能决策的关键工具。从无人机监测农田到卫星数据支持气候研究&#xff0c;空天地遥感数据正以前所未有的方式为科研和商业带来深刻变革。然而&#xff0c;对于许多专…...

    操作系统——(管程、线程、进程通信)

    目录 一、管程机制 &#xff08;1&#xff09;管程定义 &#xff08;2&#xff09;特点&#xff1a; 二、进程通信 &#xff08;1&#xff09;概念 &#xff08;2&#xff09;高级通信机制 三、线程 &#xff08;1&#xff09;概念 &#xff08;2&#xff09;与进程比较…...

    Sqlserver安全篇之_启用和禁用Named Pipes的案列介绍

    https://learn.microsoft.com/zh-cn/sql/tools/configuration-manager/named-pipes-properties?viewsql-server-ver16 https://learn.microsoft.com/zh-cn/sql/tools/configuration-manager/client-protocols-named-pipes-properties-protocol-tab?viewsql-server-ver16 默认…...

    Redis 本地安装

    首先安装&#xff1a; https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/install-redis-from-source/ 进入root目录 tar -xzvf redis-stable.tar.gz cd redis-stable make然后 install sudo make install最后可以直接启动 redis-server但是此时启…...

    外卖订单如何教会我变量与数据类型?

    目录 前言一、现实场景1.1 你点的每一碗&#xff0c;都是程序员的KPI1.2 关键数据角色扮演 二、技术映射三、知识点呈现3.1 变量——你的数字日记本3.2 数据类型——数值的「职业规划」3.3 运算符——数学老师的黑板擦 四、代码实现4.1 基础版&#xff1a;计算器の复仇4.2 进阶…...

    HOW - 平时如何保持学习和成长?

    目录 前言数字时代的系统性学习方法论一、场景驱动的实战学习&#xff1a;从工具赋能到知识沉淀二、结构化的系统学习&#xff1a;构建知识体系的方法论&#xff08;一&#xff09;精准学习策略&#xff08;二&#xff09;学习成效评估体系&#xff08;三&#xff09;专项研究 …...

    Web开发-JS应用原生代码前端数据加密CryptoJS库jsencrypt库代码混淆

    知识点&#xff1a; 1、安全开发-原生JS-数据加密&代码混淆 2、安全开发-原生JS-数据解密安全案例 一、演示案例-WEB开发-原生JS&第三方库-数据加密 前端技术JS实现&#xff1a; 1、非加密数据大致流程&#xff1a; 客户端发送->明文数据传输-服务端接受数据->…...

    手动集成sqlite的方法

    注意到sqlite有backup方法&#xff08;https://www.sqlite.org/backup.html&#xff09;。 也注意到android中sysroot下&#xff0c;没有sqlite3的库&#xff0c;也没有相关头文件。 如果要使用 sqlite 的backup&#xff0c;那么就需要手动集成sqlite代码到项目中。可以如下操…...

    比特币牛市还在不在

    在加密货币的风云世界里&#xff0c;比特币的一举一动始终牵动着投资者们的神经。近期比特币的涨幅动作&#xff0c;再次引发了市场对于牛市是否仍在延续的激烈讨论。 在深入探索比特币市场的过程中&#xff0c;获取全面且及时的资讯至关重要。您可以通过访问Techub News&#…...

    Python、MATLAB和PPT完成数学建模竞赛中的地图绘制

    参加数学建模比赛时&#xff0c;很多题目——诸如统计类、数据挖掘类、环保类、建议类的题目总会涉及到地理相关的情景&#xff0c;往往要求我们制作与地图相关的可视化内容。如下图&#xff0c;这是21年亚太赛的那道塞罕坝的题目&#xff0c;期间涉及到温度、降水和森林覆盖率…...

    跨平台RTSP高性能实时播放器实现思路

    跨平台RTSP高性能实时播放器实现思路 目标&#xff1a;局域网100ms以内超低延迟 一、引言 现有播放器&#xff08;如VLC&#xff09;在RTSP实时播放场景中面临高延迟&#xff08;通常数秒&#xff09;和资源占用大的问题。本文提出一种跨平台解决方案&#xff0c;通过网络层…...

    编写一个简单的chrome截图扩展

    文件结构&#xff1a; screenshot |-- background.js ---> service_worker运行的js |-- images ---> 图片 | |-- logo-128x128.png | |-- logo-16x16.png | |-- logo-32x32.png | -- logo-48x48.png -- manifest.json --->…...