如何提升FFmpeg 1‰的转码性能

在8K视频编解码特别是解码部分,我做了一些优化工作,转码速度提升了50%以上。专家们评价曰:“主要围绕算法并行度的优化,属于算法性能优化的常规手段,在创新性和技术难度方面的体现较为一般”。评价过于犀利,不服不行,可能专家们认为编解码提升并行度是多开几个线程的事。

好吧,那我换用另一种手段,叫“Don't taking off your pants to fart”,来做一个提升1‰的优化。读过唐诗的都知道,千是虚数,优化效果基本上被淹没在测试噪声中…

0、前置信息

H.264常用的bitstream格式有三种:annexb、avcc以及RTP协议中的特有格式。简单来说,annexb格式是start code加nal_unit,avcc是nal_unit的长度加nal_unit,RTP H.264负载格式既没有start code也没有长度信息。

不同的容器格式使用不同的bitstream格式,比如MP4/MKV/FLV使用avcc格式,TS使用annexb格式,直接H.264裸流也是使用annexb格式。当然,国内有些团队比较随性,在FLV里使用annexb格式,再让大家兼容它。

除了bitstream格式外,还有一个in-band/out-of-band参数集的问题,就是在哪里存放SPS/PPS。像MP4/MKV,“一般”是要求在文件特定位置存SPS/PPS,其他位置只有音视频帧数据。而像TS格式,SPS/PPS是随着IDR帧重复出现的。FFmpeg libavformat定义了一个flag,叫

#define AVFMT_GLOBALHEADER  0x0040 /**< Format wants global header. *

libavcodec定义了一个flag,叫

#define AV_CODEC_FLAG_GLOBAL_HEADER   (1 << 22)

global header约等于out-of-band SPS/PPS。

关于bitstream格式和in-band/out-of-band参数集问题就介绍到这里。相关资料比较多,详细信息可以阅读具体的标准文档。

1、问题描述

我们的优化手段是“Don't taking off your pants to fart”,所以对应的问题是一个“taking off your pants to fart”问题:

  • x264编码器能够输出annexb和avcc两种bitstream格式
  • FFmpeg的种种限制,导致x264只能输出annexb格式
  • 当编码后封装为MP4/MKV格式,或者生成FLV走RTMP推流时,libavformat再做一次格式转换,把annexb转成avcc格式。注意这里是处理所有视频帧,处理的时候要拷贝一次packet数据,既浪费CPU,又浪费内存

当前的处理流程:

[x264]---annexb--->[avformat/avc]---avcc--->[avformat/movenc]--->mp4

优化的处理流程:

[x264]---avcc--->[avformat/movenc]--->mp4

看起来很简单,但工程落地,困难往往藏在细节中,把牛顿三定律背的滚瓜烂熟,未必造的出二踢脚,更别说火箭了。

一个小小的优化,我前后改了六次,遇到一些有趣的、容易被忽视的小细节,下面展开描述。

2、如何让x264输出avcc格式bitstream

x264参数配置有两种方式:直接给成员变量赋值,以及x264_param_parse接口。

x264设置bitstream格式的成员变量:


int b_annexb;     /* if set, place start codes (4 bytes) before NAL units,* otherwise place size (4 bytes) before NAL units. */

通过x264_param_parse配置方式:

x264_param_parse(param, "annexb", "0");

FFmpeg配置x264输出avcc格式:

ffmpeg -i foo.mp4 \-c:a copy \-c:v libx264 \-x264-params "annexb=0" \-y bar.mp4

转码运行过程看着没问题,但如果你试着播放输出的视频文件,会发现视频无法解码,没有画面。问题出在哪里?

3、FFmpeg bitstream格式限制

FFmpeg框架层既支持annexb格式也支持avcc格式,但是有以下限制:

  • extradata的bitstream格式和AVPacket中的bitstream格式应当是同一种格式,不可以混用两种格式。很多地方,FFmpeg会根据extradata的格式来判断AVPacket的格式,前提假设是两者为同一种格式
  • 对于avcc格式,FFmpeg要求extradata是完整的AVCDecoderConfigurationRecord

当x264配置输出avcc格式时,x264_encoder_headers输出的还是一个个的NALU,是sps_nal_length+sps_nal、pps_nal_length+pps_nal、sei_nal_length+sei_nal,不是完整的AVCDecoderConfigurationRecord。

int x264_encoder_headers( x264_t *, x264_nal_t **pp_nal, int *pi_nal );

FFmpeg当前构造extradata,是直接把x264_encoder_headers输出的数据拼接到一起。annexb格式没问题,avcc格式直接拼一起得到的extradata不是完整的AVCDecoderConfigurationRecord,不符合FFmpeg的格式要求。输出mp4时,FFmpeg误认为extradata是完整的AVCDecoderConfigurationRecord,导致生成的mp4文件格式错误,视频无法解码。

4、解决方案

我考虑了几种解决方案:

1、让FFmpeg extradata支持x264 headers的格式

2、在libavcodec里加一个统一的模块,把x264 header格式转成AVCDecoderConfigurationRecord

3、在FFmpeg x264 wrapper里做处理,把header转成AVCDecoderConfigurationRecord格式extradata

方案1,新增一种extradata格式,影响面太广,容易制造混乱。

方案2,其实我是考虑了其他的编码器,比如videotoolbox,也面临同样问题。但是否能复用,复用到什么程度还不确定。

方案3,在x264 wrapper里做处理最简单,时机成熟了,可以再抽取出来复用到其他编码器。

跟 Andreas Rheinhardt 讨论之后,选择了方案3。

5、方案落地

由SPS/PPS生成AVCDecoderConfigurationRecord,好像很简单,毕竟libavformat/avc.c里已经实现了一遍。简单粗暴的做法,那就直接抄了。但FFmpeg里做事的风格不是这样的。

如果你打开libavformat/avc.c看看,你会发现处理过程还是挺复杂的,原因有两点:

  • 由SPS/PPS生成AVCDecoderConfigurationRecord涉及SPS的parse过程
  • libavformat不能依赖libavcodec的内部API,因为两者以动态库编译时,libavcodec内部的API对libavformat来说是不可见的

因为libavcodec和libavformat的隔离,导致libavformat/avc.c实现的啰嗦。在libavcodec/libx264.c里,我们可以利用libavcodec的基础设施,不需要也不应该像libavformat/avc.c一样啰嗦

一开始我用ff_h264_decode_seq_parameter_set来解析SPS,好像没什么问题。但是Andreas Rheinhardt指出来一个非常隐蔽的漏洞:ff_h264_decode_seq_parameter_set处理的是移除了emulation prevention byte的码流,x264_encoder_headers输出的NAL可能带着emulation prevention byte,所以有可能出现解析错误。

emulation prevention byte的作用是防止出现假的start code。比如,如果NAL内的数据出现了0x000001,则在里面插入一个emulation prevention byte 0x03,变成0x00000301,这样就不会误当成start code了。解码的时候,需要把0x03剔除掉

James Almer提出了一个疑问,可能很多人也存在同样的误区:

emulation prevention byte是防止出现假的start code,avcc格式没有start code,所以avcc格式没有emulation prevention byte。

这是错误的,avcc格式也要使用emulation prevention byte

考虑剔除emulation prevention byte再加上ff_h264_decode_seq_parameter_set的操作开销很大,而AVCDecoderConfigurationRecord里稍微复杂的解析只有chroma format和bit depth的解析,所以最后选择用libavcodec提供的golomb utils,手动从SPS里解出三个字段。

除了emulation prevention byte,还有个小问题被我忽略了。x264_encoder_headers除了输出SPS/PPS,还输出一个SEI,SEI里是x264版权信息和编码参数配置。这个信息是没法保留在AVCDecoderConfigurationRecord里的,需要单独拷贝出来,跟着后面的IDR帧一起输出。

到此为止,我们修复了libx264输出的extradata格式问题,以下命令生成正常的mp4文件


ffmpeg -i foo.mp4 \-c:a copy \-c:v libx264 \-x264-params "annexb=0" \-y bar.mp4

整个流程变成了 [x264]---avcc--->[avformat/movenc]--->mp4。

CPU占用能节省多少呢?和编码相比,微乎其微。内存占用的节省倒是能测出来,感兴趣的同学可以自己测测看。

6、遗留问题

最后遗留的一个问题是:为什么还需要手动设置-x264-params "annexb=0",而不是自动的在需要的时候输出avcc格式呢?

从muxer到encoder,有一个AV_CODEC_FLAG_GLOBAL_HEADER flag。FFmpeg libx264.c里,当AV_CODEC_FLAG_GLOBAL_HEADER 开启时,输出out-of-band参数集。虽然常见的global header的mp4、mkv、flv都是要avcc格式,但global header和avcc没有强绑定关系,不能看到global header就开启avcc。

当前缺少让muxer通知encoder输出什么bitstream格式的机制,感兴趣的同学可以考虑实现这个功能。FFmpeg API用户可以根据媒体文件格式手动配置-x264-params "annexb=0",基本也够用了。

粉丝福利, 免费领取C++音视频学习资料包+学习路线大纲、技术视频/代码,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,编解码,推拉流,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/769151.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一文道破将bean注入到Spring中的几种方式

前言&#xff1a; 前两天有学妹问我如何将bean注入到Spring中&#xff0c;虽问题较简单&#xff0c;但还是写此文以告之。 在Java的Spring框架中&#xff0c;将bean注入到容器中是核心概念之一&#xff0c;这是实现依赖注入的基础。Spring提供了多种方式来将bean注入到容器中…

MySQL高可用解决方案――从主从复制到InnoDB Cluster架构

2024送书福利正式起航 关注「哪吒编程」&#xff0c;提升Java技能 文末送5本《MySQL高可用解决方案――从主从复制到InnoDB Cluster架构》 大家好&#xff0c;我是哪吒。 爱奇艺每天都为数以亿计的用户提供7x24小时不间断的视频服务。通过爱奇艺的平台&#xff0c;用户可以…

力扣:290. 单词规律

前言&#xff1a;剑指offer刷题系列 问题&#xff1a; 给定一种规律 pattern 和一个字符串 s &#xff0c;判断 s 是否遵循相同的规律。 这里的 遵循 指完全匹配&#xff0c;例如&#xff0c; pattern 里的每个字母和字符串 s 中的每个非空单词之间存在着双向连接的对应规律…

docker推拉时的数据交换详解

前言 docker用了这么久了, 有没有想过, 在执行docker push 和 docker pull命令的时候, 数据是如何传递的呢? 换句话说, 如果要实现一个镜像仓库, 针对推拉的服务, 如何实现接口呢? 根据OCI 分发规范文档 的描述, 已经对整个推拉过程中要调用的接口有描述了. 但是, 纸上学来…

CNN、Transformer、Uniformer之外,我们终于有了更高效的视频理解技术

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了人工智能中文站https://ai.weoknow.com 每天给大家更新可用的国内可用chatGPT资源 发布在https://it.weoknow.com 更多资源欢迎关注 视频理解因大量时空冗余和复杂时空依赖&#xff0c;同时克服两个问题难度巨大…

力扣每日一题 2024/3/24 零钱兑换

题目描述 用例说明 思路讲解 动态规划五步法 第一步确定dp数组的含义&#xff1a;dp[i]为凑到金额为i所用最少的硬币数量 第二步确定动态规划方程&#xff1a;凑足金额为j-coins[i]所需最少的硬币个数为dp[j-coins[i]]&#xff0c;那凑足金额为j所用的最少硬币数为dp[j-coin…

怎么将文件快速生成二维码?文件二维码的在线生成技巧

现在越来越多的人都开始通过二维码的方式来传递文件&#xff0c;将word、pdf、excel、pdf等格式的文件通过扫码的方式展示或者下载文件&#xff0c;这种方式有很多的优势&#xff0c;包括传播速度快成本低&#xff0c;只需要生成一张二维码图片&#xff0c;就可以让其他人能够同…

Prompt-RAG:在特定领域中应用的革新性无需向量嵌入的RAG技术

论文地址&#xff1a;https://arxiv.org/ftp/arxiv/papers/2401/2401.11246.pdf 原文地址&#xff1a;https://cobusgreyling.medium.com/prompt-rag-98288fb38190 2024 年 3 月 21 日 虽然 Prompt-RAG 确实有其局限性&#xff0c;但在特定情况下它可以有效地替代传统向量嵌入 …

QTableWidget删除单元格

如果单元格内有内容&#xff0c;可以使用函数selectedItems() 获取有内容行的一个链表 QList<QTableWidgetItem *> items ui->qtableWidget->selectedItems(); //选中有内容的行可选择有内容的行int count items.count();for(int i 0 ; i < count; i){ …

搭建vite项目

文章目录 Vite 是一个基于 Webpack 的开发服务器&#xff0c;用于开发 Vue 3 和 Vite 应用程序 一、创建一个vite项目二、集成Vue Router1.安装 vue-routernext插件2.在 src 目录下创建一个名为 router 的文件夹&#xff0c;并在其中创建一个名为 index.js 的文件。在这个文件中…

element-ui radio-group 组件源码分享

接着上篇的 radio 组件源码分享&#xff0c;继续探索 radio-group 源码部分的实现过程&#xff0c;主要从以下四个方面来讲解&#xff1a; 1、el-radio-group 页面结构 2、el-radio-group 组件属性 3、el-radio-group 组件方法 4、核心代码部分 一、页面结构&#xff0c;如…

docker 不同架构镜像融合问题解决

1、背景 docker 作为目前容器的标准之一&#xff0c;但是对于多种架构的平台的混合编译支撑不是很好。因此衍生了镜像融合&#xff0c;分别将多种不同的架构构建好&#xff0c;然后将镜像进行融合上传。拉取镜像的会根据当前系统的架构拉取不同的镜像&#xff0c;也可以通过 -…

Linux内核err.h文件分析

在阅读和编写内核相关的代码时&#xff0c;经常会看到IS_ERR、ERR_PTR等函数。这些函数在内核头文件的err.h中。以我服务器的代码为例&#xff0c;内核版本为5.15。 这个文件的代码如下&#xff1a; /* SPDX-License-Identifier: GPL-2.0 */ #ifndef _LINUX_ERR_H #define _L…

基于nodejs+vue在线作业管理系统的设计与实现python-flask-django-php

这种个性化的网络系统管理更重视相互协调和管理合作,能激发管理者的创造性和主动性,这对在线作业管理系统来说非常有益。 关键词&#xff1a;在线作业管理系统&#xff0c;nodejs语言&#xff0c;express框架&#xff0c; 前端技术&#xff1a;nodejsvueelementui, Express 框架…

易源堂梵仕哲品牌新品发布会

祥龙启新&#xff0c;非凡无际&#xff01;2024年3月16日&#xff0c;上海易源堂集团梵仕哲品牌新品发布会在有着“山水宁海,寿者之乡”称号的浙江宁海顺利召开&#xff1b;易源堂集团联合创始人集团副总经理李振雨、易源堂集团营销策划总监曹斌&#xff0c;易源堂副总经理姚军…

mysql 用户管理-账户管理

学习了《mysql 用户管理-权限表》。接着学习更常用的的账户管理。 2&#xff0c;账户管理 MySQL提供许多语句用来管理用户账号,这些语句可以用来管理包括登录和退出MySQL服务器、创建用户、删除用户、密码管理和权限管理等内容。MySQL 数据库的安全性&#xff0c;需要通过账户管…

注册马来西亚商标常见问题

马来西亚商标法于1983年9月1日正式生效。这部商标法废除了马来亚、沙巴和沙捞越三地区各自的商标法规和申请程序&#xff0c;使马来西亚有了一部统一商标法。此外&#xff0c;马来西亚有关商标的法规包括1983年9月1日同时生效的《1983年商标法实施细则》。在马来西亚&#xff0…

Xcode-双架构arm64 x86_64编译

要启用通用构建&#xff0c;在最新版本的 Xcode 中&#xff0c;请打开您的项目设置&#xff0c;然后依次选择&#xff1a; 1. “Build Settings” 选项卡。 2. 在顶部输入框中输入 “Architectures”。 3. 在 “Architectures” 下拉列表中选择 “Other”。 4. 在输入框中输入 …

国内git最新版本下载链接2.44

git官网地址:Git - Downloading Package (git-scm.com) 蓝奏云: ​​​​​​gGit-2.44.0-64-bit.exe - 蓝奏云 git仓库地址:git/git: Git Source Code Mirror - This is a publish-only repository but pull requests can be turned into patches to the mailing list via …

2024常用接口抓包以及接口测试工具总结【建议收藏】

接口 统称为API&#xff0c;程序与程序之间的对接、交接。 接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点&#xff0c;主要是为了检验不同组件&#xff08;模块&#xff09;之间数据的传递是否正确&#xff0c;同时接口测试还要测试当前系统与第三方…