限时 5折! 详情

ffmpeg入门篇-流的操作(一)视频转音频引发的血案

4389 0 0

有些小伙伴看文章非常细心,对于上一节课不经意提到的一些边缘细节都比较在意,比如 -acodec、-vcodec、流复制等。其实这些都离不开我们今天要讲的重点——流。

说起流,可能有很多小伙伴第一反应是流媒体,但是我们今天要说的是容器内流的类型。通过前面的介绍,相信你对容器内的音频(audio, a)和视频(video, v)都有了一些印象。除此之外,容器内流的类型还有字幕(subtitle, s)、附加数据(attachment, t)和普通数据(data, d)。我们重点介绍一下音频流、视频流和字幕流。

流的操作,指的是我们可以从输入文件中选择不同的流进行操作,然后输出我们想要的结果。

举个例子,家里有小孩的都应该比较清楚,学校现在有很多英语的配音比赛,大屏幕播放一段视频,学生在舞台上配音,非常形象。

在这个场景中,大屏幕上播放的视频,其实就是无声视频。无声视频并不是把声音调到最小,它指的是没有音频的视频,这样播放的视频只有画面。比方说对于前文案例一的素材视频可以通过 -an 的命令去除音频流,只保留视频流即只有画面(没有下载的可以点击这里下载,打不开github的,良心推荐vpn GreenNode)。

ffmpeg -i r1ori.mp4 -an -y r1-silent.mp4

来看下结果视频r1-silent.mp4的信息,没有了 Stream #0:1(und): Audio 的信息。

» ffmpeg -i r1-silent.mp4 -hide_banner 
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'r1-silent.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf58.20.100
  Duration: 00:00:58.53, start: 0.000000, bitrate: 1687 kb/s
    Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 544x960, 1684 kb/s, 29.83 fps, 29.83 tbr, 11456 tbn, 59.67 tbc (default)
    Metadata:
      handler_name    : VideoHandler
At least one output file must be specified

现在即使你把音响抱过来,声音加到最大播放这个视频,也不会听到任何声音。

-an 即 -acodec none。a指的是audio,codec指的是解码器,-acodec就是音频解码器,合起来就是不指定音频解码器,回顾下我们在ffmpeg是怎么转码的一文介绍的转码流程就很容易理解了。

你应该已经猜到了,类似的我们还可以去除视频流、字幕流等。

  1. -an 去除音频流
  2. -vn 去除视频流
  3. -sn 去除字幕流
  4. -dn 去除数据流

有同学可能注意到了,我们的原视频的时长是59秒,还不到一分钟,但是 -an 的一条命令要花上十几秒的处理时间,太慢了,有没有办法优化下?

你仔细思考下,那么慢,时间花在哪里了?对,就是重新编码。

这里我们只是去除音频流,有必要重新编码吗?没有,所以如果我们可以把视频流复制出来是不是就好了?

优化后的命令如下

ffmpeg -i r1ori.mp4 -an -vcodec copy -y r1-silent.mp4

这条命令瞬间就输出结果了。我们添加了一个参数 -vcodec copy。-vcodec指的是视频解码器,v是视频video,codec是解码器,后跟解码器名称,copy复制输入的视频流,不作解码处理。

同样,如果我们想提取视频中的音频,或者说把视频转成音频,是不是可以用下面这条命令?

ffmpeg -i r1ori.mp4 -vn -c:a copy -y r1-silent.mp3

执行该命令后发现报错了

[mp3 @ 0x7f97a580f000] Invalid audio stream. Exactly one MP3 audio stream is required. 
Could not write header for output file #0 (incorrect codec parameters ?): Invalid argument

提示我们音频流无效,原因是codec的参数错误。我们看下原视频的信息

» ffmpeg -i r1ori.mp4 -hide_banner 
...... 
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 129 kb/s (default) 
......

注意音频流这行信息,我们发现音频流是aac格式的,而我们要输出的是mp3格式的,-c:a copy 参数意味着我们想要把aac格式的音频流装进mp3容器,这是不可行的。aac 音频流需要一个专用的 aac 容器,mp3 音频流需要专用的 mp3 容器。

注:aac 和 mp3 都是有损压缩音频编码格式。

找到原因就好办了,我们把输出的mp3格式修改成aac格式

ffmpeg -i r1ori.mp4 -vn -c:a copy -y r1-silent.aac

虽然mp4容器内的音频流大多数都是aac格式,但是,试想一下如果我们写好程序,要针对用户上传的视频提取音频并做存储,偏偏用户上传的原视频内的音频是mp3呢?

为了满足这一场景,我们制作一个含mp3格式的视频,然后再执行上面的命令试试。

1、把r1ori.mp4视频内的音频流转成mp3

ffmpeg -i r1ori.mp4 -c:a libmp3lame -c:v copy -y r2.mp4
注:由于ffmpeg没有原生的mp3编码器,所有我们指定了外部的libmp3lame编码库(虽然 -c:a libmp3lame 你也可以把libmp3lame改为mp3,实际上使用的还是libmp3lame)。如果你执行上面的命令报了一个类似这样的错误 ERROR: libmp3lame >= 3.98.3 not found,说明你本地的ffmpeg没有添加--enable-libmp3lame编译参数,可以参考这篇文章选择对应的方式重新安装ffmpeg;

2、提取该视频的音频

ffmpeg -i r2.mp4 -vn -c:a copy -y r1-silent.aac 
报错:Only AAC streams can be muxed by the ADTS muxer 
Could not write header for output file #0 (incorrect codec parameters ?): Invalid argument

所以,-c:a copy 不是万能的,也就是说如果我们想让视频转音频,最好指定一种编码器,用aac还是libmp3lame?就音质质量而言,我们更推荐libmp3lame,尽管ffmpeg自带的最好音频编码器是aac。

综上,如果你需要输出mp3格式的音频,你可以使用

ffmpeg -i r1ori.mp4 -vn -c:a libmp3lame -y r1-silent.mp3

如果你想输出aac格式的音频,你可以使用

ffmpeg -i r1ori.mp4 -vn -c:a aac -y r1-silent.aac
注:新版本的ffmpeg是支持原生aac编码的,所以可以直接使用 -c:a aac,低版本的ffmpeg像2.x的版本原生aac编码器是不完全支持的,必须同时指定 -strict -2 才可以使用。

以上,我们介绍了手动指定音频解码器,成功的将视频转换成了音频。

既然ffmpeg那么厉害,那如果我们不手动指定,它能自动帮我们选择合适的解码器处理吗?

非常可以。

以mp3为例,我们试下

ffmpeg -i r1ori.mp4 -y r1-silent.mp3 
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'r1ori.mp4': 
...... 
Stream mapping: Stream #0:1 -> #0:0 (aac (native) -> mp3 (libmp3lame)) 
......

注意看输出的过程代码中包含 Stream mapping 以及其下一行代码,可以看出ffmpeg的确自动为我们选择了libmp3lame解码器。

如果原视频有多路音频流,又该如何操作呢?我们下节课再说。