这是本文档旧的修订版!
FFmpeg
- Official site:ffmpeg.org
FFmpeg使用
- FFmpeg 的命令可以分成五个部分:
ffmpeg [$1] {[$2] -i $3} ... {[$4] $5} ...
- $1: 全局参数
- $2: 输入文件参数
- $3: 输入文件
- $4: 输出文件参数
- $5: 输出文件
- 其中 输入部分
{[$2] -i $3}
可多个, 输出部分{[$4] $5}
也可多个
- 常用命令行参数:
-c
: 指定编码器-c copy
:直接复制原编码,不重新编码(这样比较快)-c:v
:指定视频编码器-c:a
:指定音频编码器-i
: 指定输入文件-an
: 去除音频流-vn
: 去除视频流-preset
:指定输出的视频质量,会影响文件的生成速度,有以下几个可用的值 ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow。-y
: 不经过确认,输出时直接覆盖同名文件。
常用指令
- 列出支持的容器类型:
ffmpeg -formats
- 列出支持的编码格式:
ffmpeg –codecs
- 列出安装的编码器:
ffmpeg -encoders
- 列出视频信息:
ffmpeg -i video.mp4
- 隐藏ffpmeg 本身的信息:
ffmpeg -hide_banner -i video.mp4
- 提取音频:
ffmpeg -i input.mp4 -vn -c:a copy output.aac
- 合并音频:
ffmpeg -i input.aac -i input.mp4 output.mp4
- 解码为YUV:
ffmpeg -i input.mp4 -c:v rawvideo -pix_fmt yuv420p output.yuv
- 单张图转视频,方法之一:
ffmpeg -i 1.jpg -filter_complex color=s=1280x720:c=black[vbg];[0:v]scale=1280x720[sv];[vbg][sv]overlay[vout] -map [vout] -ss 0 -to 10 -y 1.mp4
- 生成画面带时间戳的测试视频:
ffmpeg -f lavfi -i testsrc=duration=100:size=1280x720:rate=30:decimals=2 -pix_fmt yuv420p -vcodec libx264 output.mp4
生成空画面测试视频:
ffmpeg -f lavfi -i color=c=blue:s=1280x720:r=30 -pix_fmt yuv420p ...
详细看 ffmpeg 滤镜文档ffmpeg-filters。如果是实时视频流,比如往v4l2推流,加
-re
参数来以视频原始速度来生成:ffmpeg -re -f lavfi -i color=c=blue:s=1280x720:r=30 -pix_fmt yuv420p -f v4l2 /dev/video0
FFmpeg编程
硬解
- VAAPI: ffmpeg, 测试方式:
ffmpeg -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 -hwaccel_output_format vaapi -i input.mp4 -f null -
注意,此条命令只测试硬解效率,实际硬解时时间主要耗费在从GPU拷贝数据
av_hwframe_transfer_data
速率慢。详见 采取GPU-COPY技术做Memory-Mapping, Intel 在 ffmpeg 上关于 QSV GPU-COPY 的提交:lavc/qsvdec: Add GPU-accelerated memory copy support - 这有个关于硬解解码后内存拷贝性能的讨论串,看懂它
- Ubuntu18.04里的
i965_dri_video.so
有点老不能支持比较新的intel cpu, 判断方法:- 执行
lspci
,记住 VGA 该行的 id, 比如下面的3e98
lspci | grep VGA 00:02.0 VGA compatible controller: Intel Corporation Device 3e98 (rev 02)
- 在 i965_pciids.h 里查找上面所记的 0x3e98 ,如果有对应的id, 则可以编译对应新版本的 intel-vaapi-driver 来解决。如果没有,则大概需要使用 media-driver
- 更新libva 及 i965_dri_video.so 步骤:
# 编译对应版本的libva sudo apt install autoconf libtool git clone https://github.com/intel/libva.git cd libva git checkout 2.13.0 # 改为你需要的对应版本 ./autogen.sh --prefix=/opt/intel/libva --libdir=/opt/intel/libva/lib make sudo make install # 编译 intel-vaapi-driver git cone https://github.com/intel/intel-vaapi-driver.git cd intel-vaapi-driver git checkout 2.4.1 # 改为你需要的对应版本 export PKG_CONFIG_PATH=/opt/intel/libva/lib/pkgconfig ./autogen.sh make sudo make install # 设置库路径就可以使用新驱动来硬解 export LD_LIBRARY_PATH=/opt/intel/libva/lib:$LD_LIBRARY_PATH ffmpeg -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 -hwaccel_output_format vaapi -i input.mp4 -f null -
Tips
- 网络上流行的 ffmpeg 示例代码(包括上面的leixiaohua博客), 都未做错误处理,部分函数在 ffmpeg3.0 后都被标注为废弃,不要直接拷贝用在生产环境中。看懂流程后自己按官方文档或参考 ffplay 源码来写。
- 解码第一帧前把解码器pkt_timebase 设置为流的timebase
decctx→pkt_timebase = stream→time_base
, 可抛弃一些样本,优化部分延时问题。设置为无缓存AVFMT_FLAG_NOBUFFER
也可解决部分延时:AVFormatContext* pFormatCtx = avformat_alloc_context(); pFormatCtx->flags = AVFMT_FLAG_NOBUFFER; int ret = avformat_open_input(&pFormatCtx, ...);
avformat_open_input
默认是阻塞的,如果是 tcp 连接,可设置listen_timeout
(单位秒) 或者stimeout
(单位微秒) 来达到超时处理;如果是 udp 连接,则可以设置中断回调pFormatCtx→interrupt_callback
- 关于视频流初始解析时间:
avformat_find_stream_info
的返回时间和pFormatCtx→probesize
与pFormatCtx→max_analyze_duration
相关,哪个先达到就返回。- 需要第一个关键帧信息的解码器(比如h264), 如果在
avformat_find_stream_info
期间内遇不到关键帧, 则pix_fmt
、宽高等信息将为空, 需要等到实际解码循环时才能得到 - 所以h264解码时,如果流协议没有让发送端主动发送关键帧的功能,那么初始解析时间就取决于流对关键帧间隔的设置
- h264软解时, 得手动在
AVCodecContext
设置多线程才能利用多核CPU,这样1080P以上视频解码才不卡codec_ctx_->thread_count = av_cpu_count(); codec_ctx_->thread_type = FF_THREAD_FRAME;
错误处理
- ffmpeg 函数的返回值为
AVERROR
修饰,定义见libavutil/error.h
- 错误值可用
av_strerror
函数来得到文本描述字符串;参考 ffplay 代码:string AvStrError(int err) { char errbuf[128]; const char *errbuf_ptr = errbuf; if (av_strerror(err, errbuf, sizeof(errbuf)) < 0) { errbuf_ptr = strerror(AVUNERROR(err)); } return string(errbuf_ptr); }
- 大部分错误码为PIOSIX标准中错误码的负值
- 日志:提供了
av_log_set_callback
函数来设置日志回调函数,自行输出各等级日志,方便查看具体信息。回调函数必须线程安全。