ffmpeg使用笔记
记录ffmpeg的一些使用方法。
注:以下内容中,一行前有$\$$符号表示该行内容中$\$$之后的部分为一个命令,没有$\$$符合则该行内容为普通文本
使用ffmpeg快速剪辑视频
出门在外,突然需要某一个视频片段,但是原视频文件储存在家里NAS中,将文件全部传出来再剪辑片段可能消耗大量流量,或网络速度较慢会花费大量时间。如果登录到NAS上,使用ffmpeg直接将文件裁剪出来,只传输裁剪出的文件即可。
假设原始文件为source.mp4,需要取出第7分钟(0:07:00.00)到第10分钟(0:10:00.00)的片段,则可以使用如下命令:
$ffmpeg -ss 0:07:00.00 -t 3:00.00 -i source.mp4 -c:v copy -c:a copy output.mp4
除了通过持续时间(-t)参数,也可以用目标时间(-to)来指定截取的片段:
$ffmpeg -ss 0:07:00.00 -to 0:10:00.00 -i source.mp4 -c:v copy -c:a copy output.mp4
由于编码器使用仅复制模式,速度非常快。但是,原视频编码时可能采用关键帧模式,即每经过几十帧插入一个关键帧,关键帧保存完整的画幅内容,而关键帧之间的帧则只保存相对关键帧的增量。这个时候,ffmpeg默认会保证关键帧被尽可能加入到输出的片段中,使用上述命令则可能无法确保剪辑的时间(实际会略长于设置的时间,以包含完整的关键帧及其间隔内容),强行要求时间准确则可能导致开头或结尾的片段出现黑屏的情况。因此可以先将视频转换成每一帧均为关键帧的格式,再进行剪辑(注意:这将导致文件非常大)。命令如下:
$ffmpeg -i source.mp4 -strict -2 -qscale 0 -intra source_intra.mp4
当然,也可以先剪辑出所需要的片段,之后再插帧进行进一步操作,或是将目标片段先粗略减成3个片段,对开头和结尾片段插帧,再进一步剪辑,最后重新拼合3个片段即可。
拼合片段的命令如下:
$ffmpeg -f concat -i list.txt -c copy concat.mp4
其中list.txt保存需要拼合的片段的文件名。例子如下:
file split1.mp4
file split2.mp4
file split3.mp4
执行以上命令后,concat.mp4即为依次连接split1.mp4至split3.mp4的视频文件。
硬件加速转码
一些老的视频格式(比如.avi格式的视频文件)可能占用较大的空间,而使用H264编码的后缀为.mp4的文件可以在基本保持视频质量的情况下使用更小的容量保存视频。使用H265编码则能得到更高的效率,但较老的硬件可能会没有硬件解码器支持。
一个使用ffmpeg进行软件转码的例子如下:
ffmpeg -i source.mkv -map 0 -c:v libx265 -q:v 0 -c:a copy -c:s copy output.mkv
该例子中保持源视频的音频轨道和字幕轨道不变(-map 0参数保持各个数据流,-c:a copy参数拷贝音频流,-c:s copy参数拷贝字幕流),视频用H265编码,并尽可能保持高视频质量(-q:v 0参数)。
此外,对于libx265编码器有一组预设参数,平衡了不同的编码速度和质量。详细可参考https://x265.readthedocs.io/en/stable/presets.html。使用ffmpeg时,添加-preset可以选择不同的预设参数,其中0是最快速度,9是最优质量,编码器默认设置为5。将preset设置为8(veryslow)的例子如下:
ffmpeg -i source.mkv -map 0 -c:v libx265 -q:v 0 -preset 8 -c:a copy -c:s copy output.mkv
使用libx264或libx265软件编码器时,虽然得到的输出视频具有很高的编码效率,但是编码过程会非常消耗CPU性能,转码的速度相对较慢,并可能占用较大的内存。在有独立显卡的设备上,可以使用显卡的编码器进行视频编码,这可以大大提高视频转码的速度(注意,编码效率和质量一般会逊色于软件编码)。使用-hwaccels参数可以查看当前ffmpeg版本所支持的硬件加速功能:
$ffmpeg -hwaccels
Hardware acceleration methods:
cuda
dxva2
qsv
d3d11va
opencl
vulkan
如果有cuda或者cuvid,则该版本支持调用Nvidia的显卡的硬件加速功能。调用Nvidia显卡使用硬件编码器的例子如下:
$ffmpeg -hwaccel cuvid -i source.avi -c:v h264_nvenc output.mp4
如果该Nvidia GPU支持H265编码(HEVC),以下例子调用Nvidia显卡使用硬件解码器读取H264的视频,并使用硬件编码器转码为H265:
$ffmpeg -hwaccel cuvid -c:v h264_cuvid -i source.mkv -c:v hevc_nvenc output.mkv
如果输入视频中有多个流(视频流、音频流、字幕流),可以通过-map 0将其全部选中,否则输出视频中只会保留1个视频流、1个音频流和1个字幕流:
$ffmpeg -hwaccel cuvid -c:v h264_cuvid -i source.mkv -map 0 -c:v hevc_nvenc -c:a copy -c:s copy output.mkv
需要注意,调用nvenc时,在不指定码率的情况下,默认编码的码率为2000kbps。如果需要指定视频的码率,可以使用-b:v参数,如:
$ffmpeg -hwaccel cuvid -c:v h264_cuvid -i source.mkv -map 0 -c:v hevc_nvenc -b:v 6000k -c:a copy -c:s copy output.mkv
如果有qsv则说明该版本ffmpeg支持Intel GPU加速。调用Intel集显使用硬件解码(H264)和硬件编码(H264)的例子如下:
$ffmpeg -hwaccel qsv -c:v hevc_qsv -i source.avi -c:v h264_qsv output.mp4
如果需要进行视频压制,可以使用-vf参数并附加需要的视频参数。以上述例子为例,要求目标视频为1080p、60fps:
$ffmpeg -hwaccel qsv -c:v hevc_qsv -i source.avi -vf 'vpp_qsv=framerate=60,scale_qsv=w=1920:h=1080' -c:v h264_qsv output.mp4
关于ffmpeg硬件加速的更多用法可以参考https://trac.ffmpeg.org/wiki/Hardware/QuickSync和https://docs.nvidia.com/video-technologies/video-codec-sdk/ffmpeg-with-nvidia-gpu/
视频转gif
ffmpeg支持gif格式,直接指定输出格式为gif即可。但由于gif本身不对数据进行压缩,空间利用率很低,有时受到文件大小限制,需要在视频转换时对原始视频进行等比例缩放或降低帧率。
该参数指定方法也可以用于视频格式互转。
以下例子将source.avi转换为分辨率为640x480的、帧数为24的gif图片。
$ffmpeg -i source.avi -s 640x480 -r 24 output.gif
以下例子将source.avi转换为分辨率为基于原始分辨率等比例缩放到1/4(长和宽均为1/2)的gif图片。
$ffmpeg -i source.avi -vf scale=iw/2:ih/2 output.gif
字幕提取
有的文件会包含字幕流,仅使用-i参数读取源视频文件,或者在对源视频文件用ffmpeg进行操作时会提示该视频中包含的数据流。比如:
Input #0, matroska,webm, from 'source.mkv':
Metadata:
encoder : libebml v1.3.0 + libmatroska v1.4.1
creation_time : 2016-10-12T12:35:43.000000Z
Duration: 01:55:25.95, start: 0.000000, bitrate: 5083 kb/s
Stream #0:0(eng): Video: h264 (High), yuv420p(progressive), 1280x720 [SAR 1:1 DAR 16:9], 23.98 fps, 23.98 tbr, 1k tbn, 47.95 tbc (default)
Stream #0:1: Audio: ac3, 48000 Hz, 5.1(side), fltp, 640 kb/s (default)
Metadata:
title : 英语原声
Stream #0:2: Subtitle: ass (default)
Metadata:
title : 中英字幕
Stream #0:3: Subtitle: ass
Metadata:
title : 英中字幕
可以看到这个文件中有#0:2和#0:3两个字幕流,格式为ass。将第一个字幕流(0:s:0)提取为单独文件(文件名保存为subtitle.ass)的例子如下:
$ffmpeg -i source.mkv -map 0:s:0 subtitle.ass
使用ffmpeg批量转码(使用bash脚本实现)
一个将当前目录下的所有.flv格式的视频的音频提取为mp3的bash脚本例子如下:
#!/bin/bash
for i in *.flv
do
echo "File $i selected"
ffmpeg -i "$i" "$i.mp3"
done
需要修改所选择的文件,则在for所在那一行修改末尾处选取文件的语法即可,需要添加其他参数则按照命令格式在ffmpeg所在行修改即可。之后执行脚本即可。