FFmpeg 是什么
FFmpeg 是非常强大的多媒体编解码框架,堪称多媒体处理的瑞士军刀,是集录制,转码,流的完整的跨平台多媒体解决方案。本身涵盖了大量的多媒体处理工具。
视频知识简述
- 图像由空间中二维平铺的不同颜色的像素点组成
- 视频则是在时间上连续的图片连成的产物。是三维的数据
- 音频则是在时间上振幅与频率的数据,是一维的数据
而我们常说的视频,其实是包含的视频,音频甚至还有字幕的一个整体。因为视频本身是三维的数据,在计算中直接存储空间非常大,通常无法接受,所以,会对其进行一些编码和压缩,这个过程通常是有损的,但却极大了降低了它的存储空间。
视频相关的几个概念
- 帧(Frame)视频中单位时间上一张静止的画面,是视频的最小单位
- 帧速率(FPS):每秒播放图片的数量
- 码率(Bit Rate):视频文件在单位时间内使用的数据流量,决定视频的质量和大小,单位是 kb/s 或者 Mb/s。
视频流
视频数据相比位图数据多了一维,但其本身还是由像素点组成。
- 经过压缩算法压缩的流数据,称为编码流,如H264码流
- 未经压缩的流数据,即解码后的流数据,称为原始流。即由一幅幅在时间上连续的图像组成的数据,也称YUV流
编码、压缩在流媒体领域是一项非常重要的技术:从H264码流到YUV流的过程称为解码,反之称为编码。
视频帧
帧是流的基本元素,即视频流中的图像。在原始流里,各帧都一样。但经过编码压缩后的帧,通常分为下面几种:
- I帧,英文全写Intra Picture,又称帧内编码帧,俗称关键帧。拥有完整的图像信息。一般来说I帧不需要依赖前后帧信息,可独立进行解码。
- P帧,英文全写predictive-frame,又称前向预测编码帧,也有帧间预测编码帧。顾名思义,P帧需要依赖前面的I帧或者P帧才能进行编解码,因为一般来说,P帧存储的是当前帧画面与前一帧(前一帧可能是I帧也可能是P帧)的差别,较专业的说法是压缩了时间冗余信息,或者说提取了运动特性。P帧的压缩率约在20左右,几乎所有的H264编码流都带有大量的P帧。
- B帧,英文全写bi-directional interpolatedprediction frame,又称 双向预测内插编码帧,简称双向预测编码帧。B帧非常特殊,它存储的是本帧与前后帧的差别,因此带有B帧的视频在解码时的逻辑会更复杂些,CPU开销会更大。因此,不是所有的视频都带有B帧,笔者目前还没有接触过带B帧的视频。【找到带B帧视频一定要珍藏起来好好研究!】
- IDR帧,英文全写Instantaneous Decoding Refresh,翻译过来是即时解码刷新。听上去,这类帧并不是名词概念,倒像是个动词?IDR帧是一种特殊的I帧,它是为了服务于编解码而提出的概念,IDR帧的作用是立刻刷新,使错误不致传播,从IDR帧开始,重新算一个新的序列开始编码
注意:I、P、B帧,并不是依据视频帧数据内部的元素的不同来区分的,从解码后的帧本身而言,它们没有任何区别。仅仅是在编码时,对帧处理的方式不同而已。
视频封闭格式及视频编码格式
首先视频文件与视频编码格式是两个概念。
- 视频封闭格式 即是我们常见的视频文件(如mp4,avi),它是一个文件容器。它的作用是将压缩编码后的视频和音频等数据尽可能紧凑的排布。它内部可以包含视频,音频,字幕等数据。包括的视频,音频,字幕可能有各自不同的编码格式。
- 视频编码格式(如mpeg4,h264)是视频数据的编码方式。包括视频制式、压缩率、以及相关的编码方式
视频文件通常结构
一般包括以下部分:
- header 标记文件类型,音视频码流的基本属性信息
- index 索引表,每个frame有对应的offset,size,timestamp
- stream 真正的音视频流数据
FFmpeg 处理视频的工作流程
FFmpeg 处理视频转码时的常规过程如下:
- 输入媒体文件
- 对文件demux
- 对流进行相应的decode
- 使用输出的编码进行encode
- 进行Mux操作
- 输出媒体文件
各自状态的数据产物如下:
Demux
视频中会有音频,视频,它们在文件有各自的编码压缩方式,但为了传输过程方便,将压缩过的音频和视频捆绑在一起进行传输。首先就需要将媒体文件中的各个音频,视频,字幕流等分开。
这一步叫Demux(解复用)。总结就是把不同的流从某种容器(文件)中解析出来。如上图所示,并且,一个媒体文件中,还可以分别有不止一个音频,视频和字幕流。
Decode
媒体数据为了降低存储量,针对里面的音频,视频,字幕等都会采取特定方式的编码压缩。
这一步便对各自的流采用其对应的解码算法进行处理。得到原始流。
即这一步是以帧为单位实现压缩数据到原始数据转换。
Encode
这一步再按照目标的编码方式对流进行编码。即这一步是以帧为单位实现原始数据到压缩数据转换。
Mux
Demux的逆操作,把不同的流按照某种容器的规则放入容器
FFmpeg源码实现的main流程主要概括
- 编解码器注册
- 输入输出格式和设备注册
- 上下文初始化和编解码参数读入
- 之后进行具体的编解码工作
- 输入输出流初始化。
- 根据输入输出流确定需要的编解码器,并初始化
- 写输出文件的各部分
FFmpeg filter
按照编解码器的位置划分:
- prefilters 在encode之前
- intrafilters 编码时(是编码器不可缺少的一部分)
- postfilters 在解码之后
FFmpeg中filter分为:
- source filter (只有输出)
- audio filter
- video filter
- Multimedia filter
- sink filter (只有输入)
FFmpeg 硬件加速支持情况
平台可用API情况
FFmpeg实现的API情况
VDPAU的加速方案(通过Nvidia的GPU)
因其仅有解码部分的硬件加速,缺少编码部分的加速。且Nvidia有单身以NVENC和NVDEC替代掉这一块的意思,这里不多做细节介绍。细节请看文档
支持的格式有:
- mpeg-1, mpeg-2, mpeg-4
- h264, h265
- vc-11
CUDA(NVENC/NVDEC) 加速方案(通过Nvidia的GPU)
优点:
- 得益于强大的GPU,视频编解码的能力强
缺点: - 需要相应的GPU设备支持
- 在多块GPU的机器上,并发跑任务时,需要一定的GPU策略进行分配
- 需要提高码率才能达到与软编解相似的图像效果
- GPU资源浪费,一个GPU同时只能执行一个转码任务
编码支持
- NVDEC支持解码 H.264, HEVC, MJPEG, MPEG-1/2/4, VP8/VP9, VC-1
- NVENC支持编码 H.264, HEVC
各种型号的设计支持情况请看Video Encode and Decode GPU Support Matrix
GPU相关的转码命令
查看硬件支持:
ffmpeg -hwaccels
使用CUDA解码:
ffmpeg -hwaccel cuda -i input output
使用CUVID解码:
ffmpeg -c:v h264_cuvid -i input output
转码,使用NVDEC和NVENC
ffmpeg -hwaccel cuda -hwaccel_output_format cuda -i input -c:v h264_nvenc -preset slow output
可以使用-hwaccel_device
指定GPU。GPU id可以通过nvidia-smi
查看
基于Intel QSV加速
该方案一般通过QSV来进行加速。QSV(Quick Sync Video)即Intel的集成加速器,该方便优缺点如下:
优点:
- 不需要额外的硬件支持
- 与现有软编解模式接近,不影响现有架构设计
缺点: - 很多服务器CPU不支持,倒是常见的桌面CPU都支持
- 需要提高码率才能达到与软编解相似的图像效果
查看CPU是否支持的方法如下:
- 查看CPU型号,如linux上
cat /proc/cpuinfo
,如Intel(R) Celeron(R) CPU J3455 @ 1.50GHz
- Google搜索,打开intel官网中对该型号的细节参数介绍。若支持则如下:
该方案支持h264,h265,mjpeg,mpeg2video,vp8,vp9的编解码,和vc1的解码。
更细节的支持情况如下:
解码支持情况:
编码支持情况:
一个简单测试
- 编解码:
- 软编解码 CPU 20% 内存300M
- QSV编解码 CPU 1% 内存200M
- 编码:
- 软编码 CPU 20%+ 内存280M+
- QSV编码 CPU 1% 内存160M+
- 解码:
- 软解码 CPU 2.7% 内存40M
- QSV解码 CPU 0.3% 内存60M
QuickSync对于编码解码在CPU使用率上都有着非常不错的提升。编码提升效果特别明显。
关于抽帧的各种表现,待之后测试补上。TODO
基于API
- DXVA2 / D3D11VA (Windows)
- libmfx (Linux, Windows)
- VAAPI (Linux)
- libmfs (Linux)
- Intel Media SDK (Linux, Windows)
更多细节可以参考官方文档
FFmpeg使用QSV加速的一些命令行参数可参考
常用抽帧及命令
- 抽取视频关键帧(IPB帧)
- 抽取视频场景转换帧
- 按照时间进行均匀抽帧
- 抽取制定时间的视频帧
抽取视频关键帧(IPB帧)
使用ffprobe提取出IPB帧的时间
ffprobe -i 666051400.mp4 -v quiet -select_streams v -show_entries frame=pkt_pts_time,pict_type
抽取IPB帧到jpg图片:
# 抽取I帧
ffmpeg -i 666051400.mp4 -vf "select=eq(pict_type\,I)" -vsync vfr -qscale:v 2 -f image2 ./%08d.jpg
# 抽取P帧
ffmpeg -i 666051400.mp4 -vf "select=eq(pict_type\,P)" -vsync vfr -qscale:v 2 -f image2 ./%08d.jpg
# 抽取B帧
ffmpeg -i 666051400.mp4 -vf "select=eq(pict_type\,B)" -vsync vfr -qscale:v 2 -f image2 ./%08d.jpg
抽取视频场景转换帧
# https://ffmpeg.org/ffmpeg-filters.html#select_002c-aselect
# 其中0.1表示帧为新场景的概率
ffmpeg -i 666051400.mp4 -filter:v "select='gt(scene,0.1)',showinfo" -f null - 2>&1
均匀抽帧
# -r 指定抽取的帧率,即从视频中每秒钟抽取图片的数量。1代表每秒抽取一帧。
ffmpeg -i 666051400.mp4 -r 1 -q:v 2 -f image2 ./%08d.000000.jpg
抽取制定时间的帧
# 耗时0.07s -ss放前面,使用关键帧信息进行索引,所以更快
ffmpeg -ss 00:00:30 -i 666051400.mp4 -vframes 1 0.jpg
# 耗时0.68s
ffmpeg -i 666051400.mp4 -ss 00:00:30 -vframes 1 0.jpg
针对抽帧方向的加速调研
抽帧流程其实与转码类似,一样是输入视频文件,对视频文件进行Demux,解码视频流,得到原始视频帧,然后挑选相应的视频帧按jpeg编码,mux输出到一个个文件中。
那么要加快抽帧的效率,便是想办法加快上面这各个阶段的速率
- 加快数据读取。不确定可行性,视频解码整体还是接近流式读取的情况,这块耗时理论上本身不高,
- 加快解复用,这块耗时占比依旧很小,理论上可优化空间不大
- 加快解码,解码理论上是抽帧过程中消耗占比很大的一块,而通过GPU或者其他一些方式加快,也算有比较大的优化空间
- 加快选帧速度,这块对于场景抽帧意义比较大
- 加快图片编码,编码这块也是抽帧过程中消耗占比比较大的一块,也有通过GPU或者其他一些策略加速,算有比较大的优化空间
- 加快图片输出,
抽帧会伴随着大量的文件输出。在有很多抽帧任务并行时,理论上会对磁盘造成大量的随机写入。
其他途径优化:
- 通过并行
- 算法优化
- 其他
结合以上各节点,这里列出一些进一步的调研方向:
基于Intel QSV的加速方案调研
此方案针对加快解码,加快编码。基于目前的信息来看:
- 支持多种视频格式解码
- 支持jpeg图片编码
- 需要支持QSV的cpu
该方案会有非常大的优化效果,若有足够支持QSV的CPU机器,那么该方案可行性非常大。
待进一步补充相关测试数据
基于Nvidia Cuda的加速方案调研
同上,针对解码编码方面尝试加快进行优化。目前情况:
- 支持多种视频格式解码
- 不支持jpeg图片编码,但网上有其他支持cuda加速的jpeg库,或者可以融入进来
- 需要相应的Nvidia GPU设备
该方案理论上也应该会有不错的优化效果。但就目前的一些简单测试来看,即使能达到好的加速效果,其成本也未必能够接受
待进一步补充相关测试数据
基于内存文件系统进行优化
该方案针对加快数据读取和数据输出。考虑的点主要是整个抽帧过程中是否对视频文件有不小量的随机读取,大量抽帧任务并行时,会有巨量的图片输出。这块是否会产生大量的随机写入,降低效率。
待进一步补充相关测试数据
基于关键帧模式优化
该方案针对加解码(更确切地说是减少不必要的解码操作)
例如对于一秒一帧的均匀抽帧,我们针对这种情况,修改为,尽量抽取每一秒中的关键帧。做个伪的一秒一帧的均匀抽帧。这种模式不一定能为所有抽帧情况加速,但若能为大头的需求加速也不错
待进一步测试验证可行性及补充相关测试数据
基于库选择的优化
针对解码编码方面尝试加快进行优化。
ffmpeg中,同样格式可能有多个编码解码器,比如jpeg的就会有mjpeg,libjpeg等。不同的库实现,性能,质量会有一些差距,通过测试实验,改善参数,或许一定程度上能优化性能。
待进一步补充相关测试数据
基于ffmpeg参数和源码的优化
针对流程的优化。如前面的单帧抽帧,-ss
参数的不同位置会影响 ffmpeg 抽取的时间。理论上,ffmpeg 本身设计是为了通用,而对于专用于抽帧的场景,ffmpeg本身可能存在一些没必要的工作,尝试通过参数和源码级别来优化,或许能省掉这部分的资源消耗。
待进一步补充相关测试数据
针对其他应用的加速调研
TODO
参考
FFmpeg官网
3GP/MP4 视频文件格式解析及其播放原理(转)
FFmpeg视频抽帧那些事
FFmpeg原理和架构
FFmpeg 硬件加速方案概览 (上)
FFmpeg 硬件加速方案概览 (下)
FFmpeg wiki HWAccelIntro
视频和视频帧:FFMPEG+Intel QSV硬解的环境安装篇
视频和视频帧:视频和帧基础知识整理