从FFmpeg输出日志中分析问题原因——记一次输出流顺序异常

Posted Jack_Chai

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从FFmpeg输出日志中分析问题原因——记一次输出流顺序异常相关的知识,希望对你有一定的参考价值。

本文出处:http://blog.csdn.net/chaijunkun/article/details/117572832,转载请注明。由于本人不定期会整理相关博文,会对相应内容作出完善。因此强烈建议在原始出处查看此文。

输出流的顺序怎么无法改变

一个视频文件,常规地,会将视频流放在第一个位置,其次将音频流放到第二个位置。对于一些特殊的视频,想要改变其顺序,也是非常方便的,直接使用FFmpeg提供的-map参数来重新映射即可:

ffmpeg -i source.mp4 -c copy -map v -map a out.mp4

由于不涉及重新编解码,因此命令执行速度非常快。

而我接下来遇到的问题,无论是否使用-map来重新映射流顺序,都无法将其改变。

原始文件

原始文件有两个,如下表所示:

文件名文件类型视频流信息音频流信息
source.mp4视频index: 0, codec_name: h264, codec_type: videoindex: 1, codec_name: aac, codec_type: audio
voice.mp3音频N/Aindex: 0, codec_name: pcm_s16le, codec_type: audio

可以看到,视频文件同时具有视频流和音频流,且按前述顺序布局;音频文件比较简单,只有一个音频流。

加工,重现问题

期望实现的功能是将音频混音到原始视频的音频轨道中,保持原有的视频流,以备后续处理。为了降低问题复杂度,我对命令进行了精简:

ffmpeg -i source.mp4 -i voice.mp3 -filter_complex "[0:a][1:a]amix=duration=first" \\
-c:v copy out.mp4

过程很简单,由于在filter_complex中只处理了音频流,因此后面对视频流直接进行了copy处理,这样能减少计算量并加快输出速度。执行命令,文件能正常输出,也能播放,但查看流的顺序,我们发现了问题:

ffprobe -show_streams -of json out.mp4

流信息:

...
"index": 0,
"codec_name": "aac",
...
"index": 1,
"codec_name": "h264",
...

相比于原始视频,流的顺序发生了变化。由于还需要进行拼接处理,流的顺序如果不正确,会影响到后续流程,这个问题必须解决。

老办法,先尝试使用-map进行强制映射:

ffmpeg -i source.mp4 -i voice.mp3 -filter_complex "[0:a][1:a]amix=duration=first" \\
-map v -map a -c:v copy out.mp4

问题依旧,并没有随着该参数的加入而改变结果。

输出日志,或许这里有你想要的

正在苦思冥想问题原因的时候,目光瞄准到了输出日志,希望在这里能够找到蛛丝马迹。由于使用了滤镜,因此注意到了这段对于滤镜图的解析信息:

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'source.mp4':
...
Input #1, wav, from 'voice.mp3':
...
Stream mapping:
  Stream #0:1 (aac) -> amix:input0
  Stream #1:0 (pcm_s16le) -> amix:input1
  amix -> Stream #0:0 (aac)
  Stream #0:0 -> #0:1 (copy)

上文已经描述过原始文件的流顺序,那么将这段滤镜图可视化,应当是这样:

source.mp4 #0:0 视频流 #0:1 音频流 voice.mp3 #1:0 音频流 amix:input0 amix:input1 amix 输出 #0:0? 没办法 被挤到#0:1

在上述滤镜图中,仅仅针对音频流进行了处理。在经过amix滤镜处理后,输出的流就占用了index=0的流位置。而后,原有的视频流还未使用,由于0已经被占用,因此该流向后顺延,就变成了index=1。至此,也就不难理解为什么两个流的位置发生了颠倒。

如何解决

既然已经知道了原因,那么想办法不让音频流抢先占用index=0即可。这里提供两种解决办法。

占位法

既然视频流的位置被抢占,那就先处理视频流,可以使用直通滤镜,不做任何加工:

ffmpeg -i source.mp4 -i voice.mp3 -filter_complex "[0:v]null;[0:a][1:a]amix=duration=first" out.mp4

这样视频流就抢占了index=0的位置。然而,由于滤镜中使用了视频流,因此输出时就不能再使用-c:v copy来进行视频流拷贝了,相当于引入了重新编解码,效率非常低。

更名法

既然滤镜输出的音频流没有被主动声明一个流名称,导致了使用默认策略,如果强制给他起个名字,或许可以:

ffmpeg -i source.mp4 -i voice.mp3 -filter_complex "[0:a][1:a]amix=duration=first[mixed]" \\
-map v -map "[mixed]" -c:v copy out.mp4

从结果来看,是可以的:

Stream mapping:
  Stream #0:1 (aac) -> amix:input0
  Stream #1:0 (pcm_s16le) -> amix:input1
  Stream #0:0 -> #0:0 (copy)
  amix -> Stream #0:1 (aac)

强烈推荐这种方法。由于没有引入针对视频流的滤镜操作,可以直接使用流复制,极大减少了计算量。通过对音频流更名后重新映射,避免了流的抢占。

总结

FFmpeg作为优秀的音视频编解码工具,不仅提供了非常强大的功能,而且还具有非常详尽的日志输出体系。笔者之前遇到的很多问题都是通过日志来反向查找源代码,了解算法原理后,从而有针对性地进行解决。

以上是关于从FFmpeg输出日志中分析问题原因——记一次输出流顺序异常的主要内容,如果未能解决你的问题,请参考以下文章

从FFmpeg输出日志中分析问题原因——记一次输出流顺序异常

从FFmpeg输出日志中分析问题原因——记一次输出流顺序异常

从FFmpeg输出日志中分析问题原因——记一次输出流顺序异常

从源码和日志文件结构中分析Kafka重启失败事件

记一次 .NET 某汽贸店 CPU 爆高分析

记一次Log4j2日志无法输出的 心酸史