FFmpeg 是一个用于音视频处理的自由软件, 被广泛用于音视频开发 FFmpeg 功能强大, 本文主要介绍如何使用 FFmpeg 命令行工具进行简单的视频处理
安装 FFmpeg 可以在官网下载各平台软件包或者静态编译版本, 也可以使用包管理工具安装
基本概念
获得音视频信息
使用 ffmpeg 进行视频处理
选项
- - -y / -n
- - -codec(-c)
- - -ss
- - -t
- - -to
- - -f
- - -filter / -filter_complex
- - -vframes
- - -vn
- - -r
- - -s
- - -an
- - -threads
- - -shortest
- filter
- scale
- crop
- overlay
- drawtext
- fade
- fps
应用示例
视频转码
提取视频流
提取音频
按帧截取图像
按时间截取图像
截取视频片段
截取视频区域
拼接视频
将图片合并为视频
添加音频
添加水印
添加动画
添加文字
添加字幕
旋转视频
基本概念
容器
我们熟悉的 mp4,rmvb,mkv,avi 是多媒体容器文件格式 (或称多媒体封装格式), 所谓容器是指将不同的数据流(视频流, 音频流, 字幕流等) 封装在一个文件 (载体) 中
播放时各种流分别进行解码等处理后, 然后输出到显示器和音响等设备进行播放多媒体容器格式不同于编码格式, 一个容器中可以封装多种编码格式的媒体流
流封装了实际的媒体数据, 如视频流, 音频流和字幕流等一般情况下, 流中的数据只能使用一种编码格式
帧率
帧率 (frames per second, fps) 是每秒画面刷新的次数, 帧率越高视频越流畅一般来说 30fps 就是可以接受的, 60fps 则可以明显提升交互感和逼真感, 但是一般超过 75fps 一般就不容易察觉到有明显的流畅度提升了
分辨率
分辨率表示画面的精细程度, 通常用像素密度来表示, 常用的单位为 ppi(像素每英寸)通常像素密度越高画面越精细, 模糊程度越低
对于视频文件而言, 像素密度是无法控制的 (由播放器和显示设备决定) 我们通常用视频的像素数来表示它的分辨率如 1080x640, 640x320 等
比特率
比特率 (bit rate) 又称码率, 表示多媒体流每秒输出的字节数, 单位为 KB/s, Kbps 等同样的压缩算法下, 比特率越高音视频的质量越好
可变码率 (Variable Bitrate, VBR) 指的是编码器的输出码率可以根据输入源信号的复杂度进行自适应调整, 以在输出质量保持不变的条件下尽可能减少数据量 VBR 适用于存储, 不太适用流式传输
固定码率 (Constant Bitrate, CBR) 指的是编码器输出码率固定, CBR 不适合存储, 对于复杂内容可能没有足够码率进行编码, 从而导致质量下降, 同时会在简单内容部分浪费一些码率
采样率
每秒钟对音频信号的采样次数, 采样频率越高声音还原度越高, 声音更加自然, 单位是赫兹 Hz
音频文件一般使用的采样率是 44.1 kHz, 也就是一秒钟采样 44100 次, 实验发现低于这个值就会有较明显的损失, 而高于这个值人的耳朵已经很难分辨, 而且增大了数字音频所占用的空间
视频编码
视频流可以看做图片的序列, 我们把这个序列中的一张图片称为一帧若存储视频中所有帧则会数据量过大, 不便于存储和传输
所幸统计表明大多数视频相邻帧之间的区别并不大, 所以对于一段变化不大的视频, 我们可以先完整编码帧 A, 其后的 B 帧只需要编码与 A 帧不同的部分, B 帧后的 C 帧则只编码与 B 帧的差异如此递推, 将一段视频编码为一个序列
当某个图像与之前的图像变化很大无法参考前面的帧来生成, 我们就结束上一个序列将该帧完整编码开始一个新的序列
H264 是目前流行的一种视频编码算法, 它定义了三种帧: 完整编码的 I 帧, 参考 I 帧生成只包含差异的 P 帧, 以及以及参考前后帧编码的 B 帧
H264 采用的核心算法是帧内压缩和帧间压缩, 帧内压缩是生成 I 帧的算法, 帧间压缩是生成 B 帧和 P 帧的算法
通常, 我们也把完整编码的 I 帧称为关键帧因为解码非关键帧需要解码其参考的帧, 因此在截图等不需要全部解码的操作中, 经常截取关键帧以提升性能
获得音视频信息
ffprobe 是 FFmpeg 项目提供的用于分析视频信息的命令行工具
随意下载一个测试视频 testmp4, 然后终端中输入指令:
ffprobe -v quiet -print_format json -show_format -show_streams test.mp4
可以获得 json 格式输出的视频信息:
- {
- "streams": [ // 文件中包含的流
- {
- "index": 0, // 流的序号
- "codec_name": "h264", // 流的编码格式
- "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10", // 编码格式的全名
- "profile": "High",
- "codec_type": "video", // video 表示这是一个视频流
- "codec_time_base": "1/60",
- "codec_tag_string": "avc1",
- "codec_tag": "0x31637661",
- "width": 1080, // 视频宽为 1080 像素
- "height": 614, // 视频高为 614 像素
- "coded_width": 1080,
- "coded_height": 614,
- "has_b_frames": 2,
- "sample_aspect_ratio": "0:1",
- "display_aspect_ratio": "0:1",
- "pix_fmt": "yuv420p",
- "level": 31,
- "chroma_location": "left",
- "refs": 1,
- "is_avc": "true",
- "nal_length_size": "4",
- "r_frame_rate": "30/1", // 实际帧率
- "avg_frame_rate": "30/1",
- "time_base": "1/15360",
- "start_pts": 0,
- "start_time": "0.000000",
- "duration_ts": 153093,
- "duration": "9.966992", // 以秒为单位的视频时间
- "bit_rate": "2077265", // 视频的比特率
- "bits_per_raw_sample": "8",
- "nb_frames": "299",
- "tags": { // 流中的附加信息, 其中的字段可能为空
- "rotate": 90, // 视频旋转的角度
- "language": "und",
- "handler_name": "VideoHandler"
- }
- },
- {
- "index": 1, // 流编号
- "codec_name": "aac", // 流的编码格式
- "codec_long_name": "AAC (Advanced Audio Coding)", // 编码格式的全名
- "profile": "LC",
- "codec_type": "audio", // 这是一个音频流
- "codec_time_base": "1/44100",
- "codec_tag_string": "mp4a",
- "codec_tag": "0x6134706d",
- "sample_fmt": "fltp",
- "sample_rate": "44100", // 采样率
- "channels": 2, // 声道数
- "channel_layout": "stereo", // 声道布局, stereo 为立体双声道
- "bits_per_sample": 0,
- "r_frame_rate": "0/0",
- "avg_frame_rate": "0/0",
- "time_base": "1/44100", // 每帧时长
- "start_pts": 0,
- "start_time": "0.000000", // 流开始播放时间
- "duration_ts": 442367,
- "duration": "10.030998", // 流时长
- "bit_rate": "129341", // 比特率
- "max_bit_rate": "129341",
- "nb_frames": "433",
- "tags": {
- "language": "und",
- "handler_name": "SoundHandler"
- }
- }
- ],
- "format": { // 容器信息
- "filename": "test.mp4", // 文件名
- "nb_streams": 2,
- "nb_programs": 0,
- "format_name": "mov,mp4,m4a,3gp,3g2,mj2", // 封装格式名
- "format_long_name": "QuickTime / MOV",
- "start_time": "0.000000",
- "duration": "10.055000",
- "size": "2762615", // 文件字节数
- "bit_rate": "2198002", // 比特率
- "probe_score": 100,
- "tags": {
- "major_brand": "isom",
- "minor_version": "512",
- "compatible_brands": "isomiso2avc1mp41",
- "encoder": "Lavf57.71.100"
- }
- }
- }
示例中使用 - v quiet 选项将日志级别设为 quiet 避免日志信息污染 json,-show_format 显示文件的容器信息,-show_stream 显示容器中流的信息,-show_frames 则可以显示视频中每一帧的信息
更多关于 ffprobe 的内容可以参考官方文档
使用 ffmpeg 进行视频处理
ffmpeg 的命令格式:
- ffmpeg \
- [global_options] \
- [input_file_options] -i input_url \
- [actions] \
- [output_file_options] output_url
我们可以将 ffmpeg 的选项分为全局选项和局部选项, 局部选项用于设置输入输出或者滤镜等, 通常位于被修饰的指令前面
ffmpeg 的基本流程为将容器中的各流进行解码, 然后重新编码为指定的格式在编码之前, 可以使用 filter 对视频进行处理
选项
选项的详细内容请参考官方文档
-y / -n
-y/-n 为全局选项, -y 表示直接覆盖已经存在的输出文件, -n 表示若某个输出文件已经存在则退出
若没有设置 - y 或 - n 选项, 且某个输出文件已经存在 ffmpeg 会询问是否要覆盖输出文件
- ffmpeg -y -i test.mp4 test.mkv
- -codec(-c)
指定输入输出的解码编码器, 可用的编解码器可以参考官方文档:
fmpeg -y -i test.mp4 -c:v libx264 -c:a copy test.mov
codec 指定为 copy 则将输入流直接复制到输出流不进行编解码操作
使用 - c:STREAM_INDEX 方式可以指定某一个流的解码器, STREAM_INDEX 为 stream 对象的 index 属性
-c:v 或 - vcodec 可以为所有视频流指定编码器,-c:v:1 为第 2 个视频流指定编解码器
-c:a 或 - acodec 可以为所有音频流指定编码器,-c:a:12 为第 13 个视频流指定编解码器
-ss
-ss 选项用于设置流的开始时间, 可以设置输入输出或者滤镜在开始时间之前的帧将被跳过不被处理(输入不被解码, 输出不被编码, 滤镜不被处理)
ffmpeg -ss 2 -t 10 -i test.mp4 test.mov
时长有两种方式来表示:
秒数: 如 - t 10, -t 23.167
时分秒: 如 - t 10:23, -t 21:31:00.233
-t
-t 选项用于用于设置输入输出,-t 在 - i 前可以限制输入时长,-t 在输出文件前可以限制输出时长
读入 test.mp4 文件 2s 开始 10s 内的数据, 转码后输出到 test.mov:
ffmpeg -ss 2 -t 10 -i test.mp4 test.mov
读入 test.mp4 全部数据, 全部转码后输出从第 2s 开始 1min10s 内的数据到 test.mov:
- ffmpeg -i test.mp4 -ss 2 -t 01:10 test.mov
- -to
-to 选项类似于 - t 选项, 不同的是 - to 指定结束时刻,-t 指定持续时间
读入 test.mp4 文件 2s 到 12s 内的数据, 转码后输出到 test.mov:
ffmpeg -ss 2 -to 12 -i test.mp4 test.mov
读入 test.mp4 全部数据, 全部转码后输出从 01:00 到 01:30 内的数据到 test.mov:
- ffmpeg -i test.mp4 -ss 01:00 -to 01:30 test.mov
- -f
强制设置输入输出的文件格式, 默认情况下 ffmpeg 会根据文件后缀名判断文件格式
ffmpeg -formats 命令会显示所有支持的编码格式
-filter / -filter_complex
使用过滤器对流进行处理, 下文将简要介绍 filter 的相关内容
可以使用 - vf 代替 - filter:v 处理视频流, -af 代替 - filter:a 处理音频流
-vframes
设置输出文件中包含的总帧数:
- ffmpeg -i test.mp4 -vframes 1 test.mov
- -vn
不将视频流写到输出文件中
- ffmpeg -i test.mp4 -vn -a:c copy out.mp3
- -r
设置某个流的帧率:
- ffmpeg -i test.mp4 -r:v 30 test.mov
- -s
设置帧的大小:
- ffmpeg -i test.mp4 -s 1080x680 out.mp4
- -an
不将音频流写到输出文件中:
- ffmpeg -i test.mp4 -v:c copy -an out.mp4
- -threads
设置处理线程数:
ffmpeg -threads 8 -i test.mp4 out.mp4
可以设置处理
-shortest
当最短的输入流结束后即停止编码和输出
- ffmpeg -i bgm.mp3 -i test.mp4 -shortest output.mp4
- filter
过滤器会对已解码的帧进行处理, 处理后的帧会被重新编码输出, 整个流程可以概括为:
Input -> DecodedFrames -> FilteredFrames -> EncodedData
简单过滤器是单输入单输出的 (只能处理一个流), 而复杂过滤器(filter_complex) 是多输入多输出的可以进行更复杂的操作
ffmpeg 支持的各种滤镜可以参考官方文档 - 滤镜
- scale
- ffmpeg -y -i test.mp4 -vf "scale=2*in_w:2*in_h" test.mov
scale 滤镜用于缩放视频, in_w 和 in_h 代表输入的宽和高
- crop
- ffmpeg -y -i test.mp4 -vf "crop=w=100:h=100:x=in_w/2:y=in_h,scale=400:400" test.mov
crop 滤镜用于截取视频中的一个区域
- overlay
- ffmpeg -y -i test.mp4 -i logo.png -filter_complex 'overlay=10:main_h-overlay_h-10' out.mp4
overlay 滤镜将一个视频叠放在另一个视频上, 可用于在视频中添加水印和动画等操作
overlay 的第一个输入为底层视频流, 第二个输入为叠加视频流 main_w 和 main_h 为底层视频的宽和高, overlay_w 和 overlay_h 为叠加视频的宽和高
- drawtext
- ffmpeg -y -i test.mp4 -vf "drawtext=fontfile=CourierNew.ttf:text='hello world':x=100:y=50:fontsize=24" out.mp4
drawtext 滤镜用于在视频上添加文字
- fade
- ffmpeg -y -i test.mp4 -vf "fade=in:st=0:d=5" out.mp4
fade 滤镜可以制作淡入淡出效果
- fps
- ffmpeg -y -i test.mp4 -vf "fps=60" out.mp4
fps 滤镜通过删除帧或者复制帧的方法强制设置帧率
- ffmpeg -y -i test.mp4 -vf "fps=1" img%3d.png
- ffmpeg -y -i test.mp4 -r 1 img%3d.png
上面两条指令都可以对视频每秒截取一帧图像,-r 选项会截取关键帧并不一定截取 0s1s... 处的帧, fps 滤镜处理的是已经解码的帧因此可以精确的按照时间截取
因为 fps 滤镜会解码要截图的视频片段, 因此这种方式截图会慢很多
应用示例
视频转码
- ffmpeg -y \
- -i test.mp4 \
- -vcodec copy \
- -acodec copy \
- out.mkv
这条指令将容器格式由 MP4 转换到 MKV, 使用 ffprobe 检查输出文件可以发现, 视频流没有发生变化, 但是封装格式改变为 mkv 格式
-vcodec 是一个简单过滤器用于处理视频编码, copy 表示将视频流复制到输出文件中 - acodec 是处理音频编码的过滤器
提取视频流
- ffmpeg -y \
- -i test.mp4 \
- -vcodec copy \
- -an \
- out.mp4
-an 表示不保留音频流
提取音频
- ffmpeg -y \
- -i test.mp4 \
- -ar 44100 -ac 2 -ab 192 \
- -f mp3 \
- output.mp3
分析:
-ar: 指定输出音频采样率
-ac: 指定输出音频通道 (channel) 数, 这里设置为双声道
-ab: 指定输出音频比特率, 单位 kb/s
按帧截取图像
截取第 2s 开始的 10 帧图像, 伸缩为 352x240:
- ffmpeg -y \
- -ss 2 -i test.mp4 \
- -vframes 10 \
- -f image2 \
- -s 352x240 \
- img%03d.png
分析:
-ss 2 -i test.mp4: ss 为开始时间, 用秒数或者 hh:mm:ss[.xxx]格式表示 - i test.mp4 表示输入源
-vframes: 指定截取的帧数, 这里是截取前 10 帧(从 - ss 指定开始时间算起)
-f: 指定输出文件的格式, 如: image2, mjpeg, gif
-s: 对输出画面进行缩放
img%03d.png: 格式化输出文件名, 本示例中输出 img001.png, img002.png 等
-ss 参数也可以放在 vframes 前:
- ffmpeg -y \
- -i test.mp4 \
- -ss 2 -vframes 1 \
- -f image2 \
- -s 352x240 \
- img.png
-ss 参数是局部选项用于设置其后的一个命令,-ss 2 -i test.mp4 表示从输入视频的第 2s 开始处理, 忽略前两秒的内容
而 - ss 2 -vframes 1 表示从第 2s 开始截取, 此时前 2s 的内容已经进行了解码
对不需要处理的部分进行解码会浪费大量时间, 因此建议使用 - ss 2 -i test.mp4 来表示截图开始时间
按时间截取图像
从第 2s 到第 12s 内, 每秒截取 1 帧图像:
- ffmpeg -y \
- -ss 2 -i test.mp4 \
- -r 1 -t 10 \
- -f image2 \
- -s 352x240 \
- img%03d.png
分析:
-t: 指定截取时长, 这里截取 10s
-r 1: -t 的局部选项设置每秒截取的帧数(截取帧率), 若不设置则截取全部帧
和 - vframe 一样 - t 的开始时间也有两种设置方式, 基于同样的理由同样建议将 - ss 放在输入前
截取视频片段
截取视频片段的方法与截图方法类似, 只是将输出格式变为视频:
按时间截取:
- ffmpeg -y \
- -ss 2 -i test.mp4 \
- -r 20 -t 10 \
- -s 352x240 \
- clip.mp4
因为输出为视频,-r 指定的截取帧率即为输出视频帧率
按帧数截取:
- ffmpeg -y \
- -ss 2 -i test.mp4 \
- -vframes 120 \
- -s 352x240 \
- clip.mp4
截取视频区域
截取视频区域:
- ffmpeg -y \
- -ss 2 -i test.mp4 \
- -r 1 \
- -t 10 \
- -filter_complex "[0:v]crop=w=100:h=100:x=12:y=34,scale='400:400'[v]" \
- -map "[v]" \
- img%03d.png
crop 滤镜可以截取视频部分区域,
[0:v]crop=w=100:h=100:x=12:y=34,scale='400:400'[v]
截取了左上角在 (12,34) 处, 宽为 100, 高为 100 的矩形框中的内容, 并将截图放大到 400x400
拼接视频
ffmpeg -i "concat:1.mp4|2.mp4|3.mp4" -c copy output.mp4
将图片合并为视频
- ffmpeg -i img%3d.png output.gif
- ffmpeg -i img%3d.png output.mp4
添加音频
ffmpeg -i bgm.mp3 -i test.mp4 output.mp4
添加水印
- ffmpeg -y \
- -i test.mp4 \
- -i 1.png \
- -filter_complex "[1]scale=w=480:h=280[s];[0][s]overlay=x=main_w-overlay_w-10:y=main_h-overlay_h-10[ov]" \
- -map "[ov]" \
- output.mp4
使用 filter_complex 先将水印图片 (输入 1) 放大到 480x280, 然后使用 overlay 滤镜将放大后的流 [s] 覆盖到视频 (输入 0) 上
若不需要使用 scale 进行缩放, 则可以简化 filter_complex 表达式:
- ffmpeg -y \
- -i test.mp4 \
- -i 1.png \
- -filter_complex "overlay=x=main_w-overlay_w-10:y=main_h-overlay_h-10"
- output.mp4
添加动画
ffmpeg -y -i test.mp4 -t 10 -loop 1 -framerate 6 -i ani%3d.png -filter_complex 'overlay=10:main_h-overlay_h-10' out.mp4
将多张图片 (ani001.png, ani002.png...) 组成动画, 然后将这个动画叠加在视频的左下角 - t 10 -loop 1 会循环播放动画, 持续 10s
该方式也支持 gif 格式的动画
添加文字
ffmpeg -y -i test.mp4 -vf "drawtext=fontfile=CourierNew.ttf:text='hello world':x=100:y=50:fontsize=24" out.mp4
添加字幕
添加字幕有两种方式:
将字幕添加为独立的流, mkv,avi 等封装格式支持此种方式, mp4 格式不支持
将字幕叠加到视频中
添加字幕流:
ffprobe -show_streams -print_format json out.mp4
叠加字幕:
ffmpeg -i test.mp4 -i sub.srt -filter_complex "[0][1]overlay[v]" -map "[v]" out.mp4
HowToBurnSubtitlesIntoVideo 详细介绍了烧录字幕的方法, 作者建议尽量使用字幕流的方法添加字幕
旋转视频
旋转视频有两种方式:
在视频元信息中添加旋转角度信息, 由播放器执行旋转
将每帧图像旋转
添加元信息:
ffmpeg -i test.mp4 -metadata:s:v rotate="90" -codec copy out.mp4
逐帧旋转:
ffmpeg -i test.mp4 -vf "transpose=1" out.mp4
transpose 滤镜的文档
来源: https://www.cnblogs.com/Finley/p/8646711.html