播放器开发之ffmpeg 硬件解码方案
硬件编解码的概念
硬件编解码是⾮CPU通过烧写运⾏视频加速功能对⾼清视频流进⾏编解码,其中⾮CPU可包括GPU、FPGA或者 ASIC等独⽴硬件模块,把CPU⾼使⽤率的视频解码⼯作从CPU⾥分离出来,降低CPU的使⽤负荷,使得平台能 ⾼效且流畅的执⾏视频的编解码动作。
软件编解码的概念
软件编解码指的是在信息传输的过程中,视频等信号直接通过CPU进⾏编解码。如此⽽来,会增加CPU的⼯作 负荷,提升功耗,如果CPU能⼒不⾜,则软件也会受到⼀定程度的影响。 由于CPU拥有更强⼤的计算能⼒以及更精确的数据位宽,因此CPU能更好地兼容各种编解码的⼯作。
总结一下硬件编解码和软件编解码的优缺点:
硬件编解码的优点:
-
可以分离出音视频编解码的工作,少量占用CPU资源
-
编解码性能更高,同等条件下速度更快
-
同等条件下,硬件编解码的功耗更低
硬件编解码的缺点:
-
软件支持度不如软件编解码,起步较晚
-
由于硬件功能性限制,兼容性较低
-
在画面输出补偿、画质增强等技术方面有一定缺陷
-
需要独立进行硬件设置,对小白用户不太友好
软件编解码的优点:
-
编解码器更易于开发,协议兼容度更高
-
拥有更多画面输出补偿和画质增强技术,画质更优
软件编解码的缺点:
-
对CPU性能要求较高,在高帧率游戏或高清视频播放时CPU负担较重
GPU编解码的常⽤技术⽅案
对应gpu编解码编程,硬件⼚家都有相应SDK⽅案,应⽤开发者可以直接调⽤⼚家的SDK 来完成编解码器⼯作。

FFmpeg⽅案
Windows的d 3 d 11 va 屏蔽不同的gpu。

ffmpeg对⼚家/或平台SDK进⾏封装和集成,实现部分的硬件编解码。主要关注nvida和Intel的⽅案。
许多硬件解码器的⼀个共同特点是能够在适合其他组件使⽤的硬件表⾯中⽣成输出(对于独⽴显卡,这意味着表 ⾯在卡上的内存中⽽不是在系统内存中)—— 这通常对播放很有⽤,因为在呈现输出之前不需要进⼀步复制,并 且在某些情况下,它还可以与⽀持硬件表⾯输⼊的编码器⼀起使⽤,以避免在转码情况下进⾏任何复制 (Darren注:从gpu拷⻉数据到内存,和内存拷⻉数据gpu都⽐较耗时)。
硬件编码器⽣成的输出质量通常⽐好的软件编码器(如 x 264 )低,但通常速度更快并且不使⽤太多 CPU 资源。
使用ffmpeg查看⽀持的可⽤硬件加速
ffmpeg -hwaccels
代码使用
注册阶段
1通过 av_hwdevice_find_type_by_name() 根据名称自动寻找硬件解码 API 如 "qsv"、"cuvid"、"d3d11va"、"dxva2"等
2使用 avcodec_get_hw_config 找到对应的硬件解码格式回调 cuda -> cuvid 解码器 ->h264_nvenc
3将解码器格式赋值为硬件解码格式
4创建硬件设备上下文 av_hwdevice_ctx_create
使用阶段
5进行硬件解码 avcodec_send_packet avcodec_receive_frame 6转换硬件解码的数据 av_hwframe_transfer_data() 从 GPU 内存转移到 CPU 内存中
下图所示(选取参考文献):

硬解init
if(m_isHw_device && stream_index == AVMEDIA_TYPE_VIDEO)
{// 初始化硬件解码器(在avcodec_open2前调用)if(initHWDecoder(avctx,codec) < 0){msg_queue_->notify_msg(FFP_MSG_VIDEO_HW_DECODE_NONE);}
}
if ((ret = avcodec_open2(avctx, codec, NULL)) < 0) {goto fail;
}int FFPlayer::initHWDecoder(AVCodecContext*avctx, const AVCodec *codec)
{if(!avctx && !codec) return -2;enum AVHWDeviceType type = av_hwdevice_find_type_by_name(hw_device_type.data()); // 根据名称自动寻找硬解码if (type == AV_HWDEVICE_TYPE_NONE){return -1;}//2.获取编码器支持的硬件配置:for(int i = 0; ; i++){const AVCodecHWConfig* config = avcodec_get_hw_config(codec, i); // 检索编解码器支持的硬件配置。if(!config){LOG(ERROR)<< "打开硬件解码器失败!";m_isHw_device = false;hw_device_type.clear();return -1; // 没有找到支持的硬件配置}if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX&& config->device_type == type) // 判断是否是设备类型{hw_pix_fmt = config->pix_fmt;LOG(INFO) << "打开硬件解码器:" << av_hwdevice_get_type_name(config->device_type);// 打开指定类型的设备,并为其创建AVHWDeviceContext。int ret = av_hwdevice_ctx_create(&hw_device_ctx, config->device_type, nullptr, nullptr, 0);if(ret < 0){fprintf(stderr, "Failed to create specified HW device.\n");return ret;}avctx->hw_device_ctx = av_buffer_ref(hw_device_ctx); // 创建一个对AVBuffer的新引用。avctx->get_format = get_hw_format; // 由一些解码器调用,以选择将用于输出帧的像素格式return 1;}}return -1;
}
硬解后数据获取
从gpu拿解码数据
//硬解2 从硬件buffer拷贝的内存来
//这里可以考虑直接使用frame给显存显示, 这里如果开启耗时增加%75左右
if(is->m_isHw_device){hw_frame = av_frame_alloc();if(hw_decoder_gpu_tocpu_copy(hw_frame,frame) < 0)tmp_frame = frame;elsetmp_frame = hw_frame;
}
else{tmp_frame = frame;
}int Decoder::hw_decoder_gpu_tocpu_copy(AVFrame *dst, AVFrame *src)
{if (src->format == is->hw_pix_fmt){av_hwframe_map在ffmpeg3.3以后才有,经过测试av_hwframe_transfer_data的耗时大概是av_hwframe_map的【1.5倍】int ret = av_hwframe_transfer_data(dst, src, 0); // 映射硬件数据帧// av_hwframe_map()if(ret < 0){LOG(ERROR)<<"av_hwframe_map failt " << is->hw_pix_fmt;return -1;}} else{dst = src;}return 1;
}
总结
把解码当成解压缩文件,压缩算法越高级,得到的文件内存占用量就少。但是cpu使用率就高,硬件解码也是同一道理。
硬件编解码器与软件编解码器的对比:
-
输出质量:
-
硬件编解码器的输出质量通常低于优质的软件编解码器(如x264)。
-
为了达到相同的感知质量,硬件编解码器需要更高的码率。
-
在相同码率下,软件编解码器的输出质量通常更好。
-
-
性能和效率:
-
硬件编解码器的编解码速度更快,同时也更节省CPU资源。
-
这使其更适合实时视频编解码的应用场景。
-
-
功能支持:
-
硬件编解码器往往只支持编解码器的部分功能配置,而不是全部。比如本机有qsv硬件,但是没有qsv对应的专属解码器程序,导致解码失败。
-
例如在H.264编码中,可能只支持8位4:2:0格式,而不支持更复杂的配置。
-
-
硬件表面输出:
-
硬件编解码器可以直接在显卡显存中生成输出表面。
-
这避免了从GPU到系统内存的数据拷贝,提高了效率。
-
还可以与支持硬件表面的编码器配合使用,消除转码过程中的数据拷贝。
-
-
API支持:
-
不同的硬件编解码器可能支持不同的API标准,如VAAPI、NVIDIA NVDEC/NVENC等。
-
FFmpeg等工具可以提供对这些API的访问能力。
-
综上所述,硬件编解码器在性能和效率方面有优势,但在输出质量和功能支持上可能略逊于优秀的软件编解码器。选择时需要权衡具体的应用需求。
测试
测试4k影片,正常播放30s,本次测试保留了从gpu到cpu拷贝到缓存队列,如果直接将gpu转给显存,性能将大幅提升。
不使用硬解后 cpu使用率波动15-25左右
使用硬解后 cpu使用率 波动 7-18左右
参考文献
Qt-FFmpeg开发-视频播放【硬解码】(2) - mahuifa - 博客园
学习资料分享
0voice · GitHub
相关文章:
播放器开发之ffmpeg 硬件解码方案
硬件编解码的概念 硬件编解码是⾮CPU通过烧写运⾏视频加速功能对⾼清视频流进⾏编解码,其中⾮CPU可包括GPU、FPGA或者 ASIC等独⽴硬件模块,把CPU⾼使⽤率的视频解码⼯作从CPU⾥分离出来,降低CPU的使⽤负荷,使得平台能 ⾼效且流畅…...
n、nvm、nrm、pnpm、yarn各种指令大全
n mac的版本管理工具(可能与nvm冲突) 安装 # 使用 npm / yarn npm i -g n yarn global add n # 使用 brew brew install n环境变量 export PATH"/usr/local/n/versions/node:$PATH"命令详解 版本查看 # 查看 n 版本 n --version/-V # 查…...
数据库管理-根据日期字段进行数据筛选更新数据
项目场景 数据插入、更新、查询 数据库中一张审计表格用来记录数据的操作包括数据的id,数据名称sjmc,数据状态sjzt,数据创建时间createtime,数据更新时间updatetime。 具体需求如下: 根据数据名称更新sjzt和update…...
03. 运算符
一、运算符与表达式 运算符 就是对字面量或者变量进行操作的符号;表达式 是指用运算符把字面量或者变量连接起来,符合 Python 语法的式子。不同运算符连接的表达式体现的是不同类型的表达式;Python 中的运算符主要包括 算术运算符、赋值运算符…...
【最优清零方案——贪心+滑动窗口+线段树】
题目 代码 #include <bits/stdc.h> using namespace std; using ll long long; const int N 1e6 10; int a[N]; struct node {int l, r;int m, p, lazy; } tr[4 * N]; void pushup(node &u, node &l, node &r) {if (l.m r.m){u.m l.m;u.p max(l.p, r.…...
一个点绕任意点旋转后的点的坐标
在平面坐标上,任意点P(x1,y1),绕一个坐标点Q(x2,y2)逆时针旋转θ角度后,新的坐标设为(x, y)的计算公式: x (x1 - x2)*cos(θ) - (y1 - y2)*sin(θ) x2 ; y (x1 - x2)*sin(θ) (y1 - y2)*cos(θ) y2 ; 另一个场景应用,坐标轴绕…...
大数据面试题每日练习--HDFS是如何工作的?
HDFS(Hadoop Distributed File System)是一个分布式文件系统,设计用于存储非常大的文件。它的主要工作原理如下: NameNode:管理文件系统的命名空间,维护文件目录树和文件元数据信息。NameNode记录每个文件…...
Python的3D可视化库 - vedo (2)visual子模块 基本可视化行为
文章目录 1. visual模块的继承关系2. 基类CommonVisual的方法2.1 获取对象信息2.1.1 对象本身信息2.1.2 对象的查找表2.1.3 对象标量范围2.1.4 对象缩略图 2.2 呈现对象2.2.1 在窗口显示1.2.2 对象可见性 2.2.3 对象颜色2.2.4 对象透明度 2.3 添加标度条2.3.1 2D标度条2.3.2 3D…...
Java AIO(NIO.2)
Java AIO(Asynchronous I/O,异步I/O),也被称为NIO.2,是Java平台提供的一种处理异步输入/输出操作的机制。作为Java NIO(New I/O)的扩展,AIO引入了一些新的API和特性,旨在…...
Flink 常用问题及常用配置(有用)
一、Flink 常用问题及常用配置 参数 示例 说明 execution.checkpointing.interval 3min Checkpoint 触发间隔 state.backend rocksdb / filesystem 用于设置statebackend类型, 默认会以内存为statebackend(无法支持大状态) taskmanager.memory.jvm-overhead.max 204…...
RocketMQ: 消息过滤,通信组件,服务发现
消息过滤 1 ) 简单消息过滤 /*** 订阅指定topic下tags分别等于 TagA 或 TagC 或 TagD */consumer.subscribe("TopicTest1", "TagA || TagC || TagD");如以上代码所示,简单消息过滤通过指定多个 Tag 来过滤消息,过滤的动作在服务器进…...
linux ubuntu的脚本知
目录 一、变量的引用 二、判断指定的文件是否存在 三、判断目录是否存在 四、判断最近一次命令执行是否成功 五、一些比较符号 六、"文件"的读取和写入 七、echo打印输出 八、ubuntu切换到root用户 N、其它可以参考的网址 脚本功能强大,用起来也…...
HTTP有哪些风险?是怎么解决的?
一、风险 HTTP是通过明文传输的,存在窃听风险、篡改风险以及冒充风险。 二、如何解决 HTTPS在HTTP的下层加了一个SSL/TLS层,保证了安全,通过混合加密解决窃听风险、数字签名解决篡改风险、数字证书解决冒充风险。 (1࿰…...
3.12MayBeSomeLinearAlgebra
X是M*(D1),XT为(D1)*M Ω是一行D1列,X乘以欧米噶是M行D1列 行是说样本个数,列是特征数量 如果是小样本,那么可能会出现特征数量大于样本个数 如果MD*DM就是M*M,...
学习日志015--python单链表
创建 class Node:def __init__(self,data):# 数据域self.data data# 链接域self.next Noneclass LinkList:def __init__(self,):# 初始化头节点self.head None# 记录链表的长度self.size 0 增加 #头插def insert_head(self,value):# 创建新节点node Node(value)q self…...
如何在Windows右键新建菜单中添加自定义项
Windows Registry Editor Version 5.00[HKEY_CLASSES_ROOT\.py] "Python.File"[HKEY_CLASSES_ROOT\.py\ShellNew] "NullFile"""[HKEY_CLASSES_ROOT\Python.File] "FriendlyTypeName""文本.py"[HKEY_CLASSES_ROOT\Python.Fil…...
Spring Boot 3.0废弃了JavaEE,改用了Jakarta EE
Spring Boot 3.0废弃了JavaEE,改用了Jakarta EE 历史背景 javax变成Jakarta的主要原因是因为Java EE项目从Oracle转移到了Eclipse Foundation,并改名为Jakarta EE。 JavaEE是从Java 1.2版本开始推出的Java企业级开发平台,最初的名称是J2EE(J…...
pdf文档动态插入文字水印,45度角,旋转倾斜,位于文档中央,多行水印可插入中文
一行水印 /*** param inputFile 你的PDF文件地址* param outputFile 添加水印后生成PDF存放的地址* param waterMarkName 你的水印* return*/public static boolean waterMark(String inputFile,String outputFile, String waterMarkName){try {PdfReader reader new PdfRead…...
[ 渗透测试面试篇-2 ] 针对大规模资产的攻击思路
🍬 博主介绍 👨🎓 博主介绍:大家好,我是 _PowerShell ,很高兴认识大家~ ✨主攻领域:【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 🎉点赞➕评论➕收藏 养成习…...
深入解析 Web 应用中的 CHIPS(Partitioned Cookie Attribute)
深入解析 Web 应用中的 CHIPS(Partitioned Cookie Attribute) 最新发现flask3.1.0 的版本引入了新的特性:对CHIPS的支持。不少同学对这个可能有点陌生,本文带大家了解一下。 为了在隐私保护和功能需求之间取得平衡,Goo…...
7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...
如何通过git命令查看项目连接的仓库地址?
要通过 Git 命令查看项目连接的仓库地址,您可以使用以下几种方法: 1. 查看所有远程仓库地址 使用 git remote -v 命令,它会显示项目中配置的所有远程仓库及其对应的 URL: git remote -v输出示例: origin https://…...
