ffmpeg第7篇:数据流选择神器-map指令
自动选择规则
ffmpeg在处理视频时,如果只提供了输入和输出参数,ffmpeg会自动地去选择相应的视频流和音频流来合成文件
自动选择的方式根据如下规则:
- 视频流:选分辨率最高的,比如有两个视频,一个是1080,一个是720,会选择1080的作为默认的视频流
- 音频流:选通道数最多的,比如两个视频,一个无声音,一个有声音,会自动选择有声音的音频流作为最终的输出,如果A视频是1080无声音,B视频是720有声音,那么最终输出的文件画面是A视频的,声音是B视频的
- 字幕:选第一个被检索到的字幕流,如果两个视频都有字幕,那就是第一个输入的第一个字幕流,其它的忽视
为了验证上面的规则,来做个测试:
现有两个视频文件noaudio.mp4和normal.mp4
其中noaudio.mp4是720分辨率,没有声音, normal.mp4是360分辨率,有声音
执行如下命令:
ffmpeg -v quiet -i noaudio.mp4 -i normal.mp4 out.mp4
结果:out的画面是noaudio.mp4的,而声音是normal.mp4的。
map指令
ffmpeg的自动选择规则,显然并不能总是符合需求,所以有了map指令
官方文档描述如下:
When -map is used, only user-mapped streams are included in that output file(只有被用户选择的流才会被用到输出文件中)
同样拿上面的两个文件,做以下测试
ffmpeg -i noaudio.mp4 -i normal.mp4 -map 1:a -acodec copy out.mp4
-map 1:a
的意思是:输出文件的音频使用第二个文件的音频,这次,在out.mp4中,虽然是mp4格式文件,但是只有声音,没有画面
再做个测试
ffmpeg -i noaudio.mp4 -i normal.mp4 -map 1:a -acodec copy -vcodec copy out.mp4
这次指定了视频的编码方式,使用原视频编码方式不重新编码,然而out文件依然只有声音
再做测试
ffmpeg -i noaudio.mp4 -i normal.mp4 -map 1:a -map 0:v -acodec copy -vcodec copy out.mp4
这次用0:v
来指定输出文件使用第一个文件的视频,这次out是第一个文件的画面,第二个文件的声音
那现在再考虑一个问题:如果指定使用第一个文件(noaudio.mp4)的音频会如何呢
继续测试:
ffmpeg -i noaudio.mp4 -i normal.mp4 -map 0:a -map 1:v -acodec copy -vcodec copy out.mp4 -y
执行时出错了,提示如下:
Stream map '0:a' matches no streams.
To ignore this, add a trailling '?' to the map
意思是指定的音频没有匹配到,但是可以加个问题忽略这个map,那加上"?"尝试一下:
ffmpeg -i noaudio.mp4 -i normal.mp4 -map 0:a? -map 1:v -acodec copy -vcodec copy out.mp4 -y
这次成功执行,但是out中只有画面,没有声音,说明问号的功能仅仅是忽视这个map,并不会重新选择新的音频。
未命名滤镜如何处理
现在有两个文件:ftwo.mp4是720分辨率,scale.mp4是360分辨率。
overlay是水印滤镜,咱们之前的文章中已经讲过了,如果不清楚可以去看。
执行下面命令:
ffmpeg -i ftwo.mp4 -i scale.mp4 -filter_complex "overlay" out.mp4 -y
out.mp4文件中,scale.mp4覆盖到ftwo.mp4的左上角,声音用的是scale.mp4的
把两个文件的输入顺序颠倒一下试试:
ffmpeg -i scale.mp4 -i ftwo.mp4 -filter_complex "overlay" out.mp4 -y
这次是ftwo.mp4覆盖住了scale.mp4,由于scale.mp4分辨率小,所以scale.mp4被完全覆盖住,画面也只截了ftwo.mp4左上角640*360的画面,而不是1280*720。
但是声音依然用的是scale的声音,因为scale.mp4时长是是10s,而ftwo.mp4时长是17秒,在合成时ffmpeg会默认用时长较短的那个文件作为输出文件的时长,对应的音频也用这个,所以上面两个输出文件的时长都是10s,ftwo后面的视频就被截断了。
有命名的滤镜如何处理
看这个命令:
ffmpeg -i ftwo.mp4 -i three_scale.mp4 -i oness_scale.mp4 \
-filter_complex "[1:v]hue=s=0[out];overlay;aresample" \
-map '[out]' -an out1.mp4
这里有个命名的滤镜:out,它将第二个输入文件的颜色饱和度s设置为0。
还有两个未命名的滤镜:overlay、aresample
命令执行完,发现out1.mp4是ftwo.mp4的画面和声音。
为什么会这样呢?map和an指令没起作用。
根据官方文档解释,因为有两个未命名滤镜,所以ffmpeg会把这两个未命名的滤镜结果直接输出给第一个输出文件,也就是out1,这时会忽略map指令。
至于an指令,这个指令只作用于自动选择音频流时或手动指定了音频流,对于滤镜输出的流是不起作用的,所以这里也会忽视掉an。
基于以上解释,这里虽然用了out滤镜,但是其并没起作用,第二个输入文件也等于没用。
咱们改一下命令再试试:
ffmpeg -i ftwo.mp4 -i three_scale.mp4 -i oness_scale.mp4 \
-filter_complex "[1:v]hue=s=0[out];overlay;aresample" \
-map '[out]' -an out1.mp4 out2.mp4
结果out1与上面一样,out2是第一个输入文件的画面,第二个输入文件的声音。
这是因为out2前面没有map,所以触发了ffmpeg的自动选择规则,默认用第一个文件的画面,由于第二个文件的时长短,所以将第一个文件截断了,且用的第二个文件的声音。
再改一下命令,既然map和an没用,就去掉
ffmpeg -i ftwo.mp4 -i three_scale.mp4 -i oness_scale.mp4 \
-filter_complex "[1:v]hue=s=0[out];overlay;aresample" \
out1.mp4 out2.mp4
然而这次出错了:
Filter hue has an unconnected output
错误意思是:out滤镜没有指定输出文件。
这说明一旦指定了命名滤镜,就必须用上。
再改下命令
ffmpeg -i ftwo.mp4 -i three_scale.mp4 -i oness_scale.mp4 \
-filter_complex "[1:v]hue=s=0[out];overlay;aresample" \
-map '[out]' -an out1.mp4 out2.mp4 \
-map '[out]' -map 0:a:0 out2.mp4
这次将滤镜用了两次,再次报错:
Output with label 'out' does not exist in any defined filter graph,or was already used elsewhere.
意思是滤镜没用过或者已经用过了,这说明一个滤镜只能使用一次,且必须使用一次
既然如此,咱们只能将滤镜分开了,修改命令如下:
ffmpeg -i ftwo.mp4 -i three_scale.mp4 -i oness_scale.mp4 \
-filter_complex "[1:v]hue=s=0,split=[out1][out2];overlay;aresample" \
-map '[out1]' -an out1.mp4 \
-map '[out2]' -map 0:a:0 out2.mp4
这次用split将同一个滤镜分成了两个,分别使用,第一个上面已经分析过,不起作用。
第二个滤镜起作用了,且根据后面的map,使用了第一个输入文件的第一个音频,所以out2是二个文件的画面,第一个文件的音频,这下怎么用清楚了吗😀。
ffmpeg系列文章目录
ffmpeg第1篇:日志级别控制、保存日志到指定文件、处理进度查询
ffmpeg第2篇:简单滤镜与复杂滤镜的区别
ffmpeg第3篇:为视频添加静态水印
ffmpeg第4篇:为视频添加动态水印
ffmpeg第5篇:让水印图片旋转起来
ffmpeg第6篇:滤镜语法
ffmpeg第7篇:数据流选择神器-map指令