1 前言
我们在实际工作中有时会遇到一入多出的转码需求,比如在给播放器提供多种网络环境的播放源选择(如流畅、清晰、高清和超清等)时就会需要这种操作。
当然,我们可以有很多种方法来实现这个需求。
在这篇文章中,我们讲一下如何使用一条ffmpeg命令来实现同一个输入源转码输出多路不同视频的功能。
2 转码输出
不使用滤镜
ffmpeg支持使用相同的输入创建多个不同的输出,一般我们可以使用下面这种方式来实现:
ffmpeg -i input -acodec … -vcodec … output1 -acodec … -vcodec … output2 -acodec … -vcodec … output3
比如说将一个相同的视频转码为HD, VGA和QVGA三种分辨率,可以这么来用:
ffmpeg -i input -s 1280x720 -acodec … -vcodec … output1 -s 640x480 -acodec … -vcodec … output2 -s 320x240 -acodec … -vcodec … output3
所有输出使用同一个滤镜
如果你想在转码时使用滤镜,并且所有的输出都用同一个滤镜,那么就增加一个"-filter_complex"参数,使用"split"滤镜。进行多分辨率转码并添加"yadif"滤镜的操作如下所示:
# `split=3`的意思是分成三个流
ffmpeg -i input -filter_complex '[0:v]yadif,split=3[out1][out2][out3]' -map '[out1]' -s 1280x720 -acodec … -vcodec … output1 \
-map '[out2]' -s 640x480 -acodec … -vcodec … output2 -map '[out3]' -s 320x240 -acodec … -vcodec … output3
每路输出使用独立的滤镜
如果你想在转码时每一路都使用一个独立的滤镜,我们可以使用"split"来直接指定输入。
分别添加"boxblur", "negate", "yadif"三个滤镜到三个相应的输出的写法如下所示:
ffmpeg -i input -filter_complex '[0:v]split=3[in1][in2][in3];[in1]boxblur[out1];[in2]negate[out2];[in3]yadif[out3]' -map '[out1]' -acodec … -vcodec … output1 \
-map '[out2]' -acodec … -vcodec … output2 -map '[out3]' -acodec … -vcodec … output3
3 复制输出
这里说的复制输出不是我们常用的比如“-vcodec copy -acodec copy”之类的操作,而是指的将重新编码后的音视频做不同的封装,也就是说用了相同的编码后的音视频数据做不同的封装。
T型虚拟封装器(Tee pseudo-muxer)
T型虚拟封装器是ffmpeg在2013年2月3日添加的新功能,这个功能支持用一行命令将音视频流复制到不同的输出文件中。
下面的例子输出了一路MKV文件流和一路UDP网络流,两个不同的流使用“|”隔开。“[f=mpegts]”这个操作可应用于某一路的单独输出,跟正常使用时的“-f mpegts”是相同的。多个选项可以使用“:”隔开。
ffmpeg -i input.file -c:v libx264 -c:a mp2 -f tee -map 0:v -map 0:a "output.mkv|[f=mpegts]udp://10.0.1.255:1234/"
如果你想选择指定的流,可以使用"select"选项。下一个例子就是将一个视频流切分并缩放为两路不同尺寸的输出,并且两路输出只需对音频编码一次,否则的话得需要对音频编码多次。最后面输出选项中"onfail"的值"ignore"意思是当这一路输出出错失败的话,不会对其他几路输出造成影响。"onfail"的默认值为"abort"。
ffmpeg -i input -filter_complex "[0:v]split=2[s0][s1]; [s0]scale=1280:-2[v0];
[s1]scale=640:-2[v1]" -map "[v0]" -map "[v1]" -map 0:a -c:v libx264 -c:a aac -f tee
"[select=\'v:0,a\']local0.mkv| [select=\'v:0,a\':f=flv]rtmp://server0/app/instance/playpath| [select=\'v:1,a\']local1.mkv|
[select=\'v:1,a\':f=flv:onfail=ignore]rtmp://server1/app/instance/playpath"
管道进程
在一些低版本的ffmpeg中,还可以这样做:使用两个ffmpeg进程,第一个进程用来编码stream(s),第二个进程将其分发为多个不同的输出。
ffmpeg -i input1 -i input2 -acodec … -vcodec … -f mpegts - | ffmpeg -f mpegts -i - -c copy output1 -c copy output2 -c copy output3
如果使用的旧版ffmpeg不支持"-c copy",那么可以使用"-vcodec copy -acodec copy"来代替。
ffmpeg -f v4l2 -i /dev/video0 -vcodec libx264 -f mpegts - | ffmpeg -f mpegts -i - -c copy -f mpegts udp://1.2.3.4:5678 -c copy -f mpegts local.ts
并行编码
在同一个ffmpeg进程中进行多次编码输出会大大降低编码效率。有一些编码器(如libx264)在编码时会使用“多线程后台”,所以有效的允许了并行编码,但是音频编码可能会是串行的,从而成为了瓶颈。似乎是如果你有任何串行编码,ffmpeg会将其视为“真正的串行”,因此你的ffmpeg可能不会使用所有可用的cpu核心。有一种办法是使用多个ffmpeg实例并行运行,通过管道从一个ffmpeg到另一个ffmpeg进行“第二编码”。或者你可以避开编码器限制(例如:使用一个更快的方式【例如:原始格式(raw format)】或者只是做一个原始流复制),希望能对提升效率有所帮助。
4 备注
因ffmpeg版本在不停更新,新版本中参数会存在调整的可能,若您在实际操作中发现有不可用的命令,可通过评论或私信提出,我会尽快修正。