文 章

FFMpeg与MKV

head

如果要用一个词来形容MKV容器, 大约用"妖孽"比较恰当吧, 相比常见的mp4, mov等容器格式, 它更像一个压缩包, 不管视频流, 音频流, 字幕流, 字体, 都可以封装, 甚至封装一个PDF文件, 一个MKV中封装两个视频流都不是什么问题. 像mkvtoolnix, 小丸工具箱等软件都可以对MKV进行处理, 以下尝试使用ffmpeg处理mkv文件.

ffmpeg 的 map 参数

ffmpeg 的 map 选项是用来从输入中选择流的. 通常单个 mp4 文件只有一路视频流和一路音频流, 使用 ffmpeg 处理时基本上不用指定, 当有多个输入时就有必要使用 map 选项选择输入流了, 由于 mkv 能封装的东西太多了, 故大多数时候 map 选项也是必要参数.

以下是 ffmpeg 官方用来说明 map 参数功能的图片:

ffmpeg-map

两条 ffmpeg 命令行的功能是相同的, 以下对选项的参数做出解释.

map 选项的语法为:

-map input_file_index:stream_type_specifier:stream_index

  • input_file_index: 第几个输入文件(从0开始算起)
  • stream_type_specifier: 流类型(可选参数)
  • stream_index: 流索引

stream_type_specifier有如下几种值:

  • v: video, 视频流
  • a: audio, 音频流
  • s: subtitle, 字幕流
  • d: data, 数据
  • t: attachment, 附件

以上图中的命令行举例说明:

ffmpeg -i input0.mp4 -i input1.mkv -map 0:0 -map 1:3 output.mp4
  • -map 0:0 表示第1个输入(input0.mp4)中的第1个流
  • -map 1:3 表示第2个输入(input1.mkv)中的第4个流
ffmpeg -i input0.mp4 -i input1.mkv -map 0:v:0 -map 1:a:2 output.mp4
  • -map 0:v:0 表示第1个输入中的第1个视频流(v参数)
  • -map 1:a:2 表示第2个输入中的第3个音频流(a参数)

从MKV中抽取流

如果要查看 MKV 中封装的流, 使用如下命令行即可:

ffprobe -i input.mkv

以下是使用 ffprobe 查看某个mkv视频封装流的输出:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
i@flytech:~$ffprobe -i laputa.mkv
...(略)
Input #0, matroska,webm, from 'laputa.mkv':
  Metadata:
    title           : Laputa: Castle in the Sky
    encoder         : libebml v1.2.3 + libmatroska v1.3.0
    creation_time   : 2012-08-15T12:41:14.000000Z
  Duration: 02:04:33.57, start: 0.000000, bitrate: 6397 kb/s
  Chapters:
    Chapter #0:0: start 0.054000, end 0.097000
      Metadata:
        title           : 00:00:00.054
...(略)
  Stream #0:0: Video: h264 (High), yuv420p(progressive), 2000x1080 [SAR 1:1 DAR 50:27], 23.98 fps, 23.98 tbr, 1k tbn (default)
  Stream #0:1(jpn): Audio: dts (DTS-HD MA), 48000 Hz, stereo, s16p (default)
    Metadata:
      title           : 天空の城ラピュタ / 日本語
  Stream #0:2(chi): Audio: ac3, 48000 Hz, stereo, fltp, 192 kb/s
    Metadata:
      title           : 天空之城 / 普通话
...(略)
  Stream #0:8(chi): Subtitle: ass (default)
    Metadata:
      title           : 简体中文字幕
  Stream #0:9(jpn): Subtitle: srt
    Metadata:
      title           : 日文字幕
...(略)
  Stream #0:19: Attachment: ttf
    Metadata:
      filename        : simhei.ttf
      mimetype        : application/x-truetype-font
  Stream #0:20: Attachment: ttf
    Metadata:
      filename        : Geometr706_Md_BT_Medium.ttf
      mimetype        : application/x-truetype-font
...(略)

此 MKV 文件包含了 7 条音轨, 十几条字幕, 5 种字体, 算是比较有代表的 mkv 封装了, 以下以示例方式说明对引 MKV 文件的常用操作.

1. 将此 mkv 文件转换为只包含中文配音的mp4文件:

1
ffmpeg -i laputa.mkv -map 0:0 -map 0:2 -c copy laputa.mp4

其中-map 0:0表示选择第1个输入中的第1个流, 在此输入文件中是 H.264 编码的视频流:

1
Stream #0:0: Video: h264 (High), yuv420p(progressive), 2000x1080 [SAR 1:1 DAR 50:27], 23.98 fps, 23.98 tbr, 1k tbn (default)

-map 0:2表示第1个输入中的第3个流, 在此输入文件中是普通话配音:

1
2
3
  Stream #0:2(chi): Audio: ac3, 48000 Hz, stereo, fltp, 192 kb/s
    Metadata:
      title           : 天空之城 / 普通话

-c copy表示将输入流复制到输出, 以避免重新编码耗费时间.

2. 提取此 mkv 文件中的简体中文字幕:

1
ffmpeg -i laputa.mkv -map 0:8 -c:s ass laputa-chi.ass

其中-map 0:8表示选择第1个输入中的第8个流, 此处是 ass 格式的简体中文字幕:

1
2
3
  Stream #0:8(chi): Subtitle: ass (default)
    Metadata:
      title           : 简体中文字幕

-c:s ass表示对字幕使用ass编码器, 实际上使用-c copy也是可以的, 因为输入和输出均是 ass 文件.

3. 提取字体 simhei.ttf

1
ffmpeg -dump_attachment:19 simhei.ttf -i laputa.mkv

将输出文件名留空的话就可以提取所有附件:

1
ffmpeg -dump_attachment:19 "" -i laputa.mkv

提取所有附件的命令在 Linux 下可正常提取, Windows 下会报参数错误, 原因未知.

合并为MKV

1. 为 MP4 新增2路音频流并封装为MKV

以下命令行将视频input.mp4和英语配音音频流eng.m4a, 日语配音音频流jpn.m4a合并为一个mkv文件:

1
ffmpeg -i input.mp4 -i eng.m4a -i jpn.m4a -map 0 -map 1 -map 2 -c copy out.mkv

在 potplayer 中查看:

audio_stream

虽然音频被添加进去了, 但标题本文是有问题的, 所以还需要为其添加元数据metadata:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
ffmpeg -i input.mp4 \
-i eng.m4a \
-i jpn.m4a \
-map 0 -map 1 -map 2 \
-metadata:s:a:0 language="chi" \
-metadata:s:a:1 language="eng" \
-metadata:s:a:2 language="jpn" \
-metadata:s:a:0 title="普通话" \
-metadata:s:a:1 title="英语" \
-metadata:s:a:2 title="日语" \
-disposition:a:0 default \
-disposition:a:1 -default \
-disposition:a:3 none \
-c copy out.mkv

命令行结尾的符号 \ 是换行符, 在 Linux 的 bash 中是 \, 在 Windows 的 cmd 中是 ^, 在 powershell 中是 `(反引号).

-metadata 选项用来写入元数据, 可为mkv文件或其中封装的流写入元数据, 其语法如下:

1
-metadata[:metadata_specifier] key=value

metadata_specifier 可取如下几种值:

  • g: 全局元数据, 也就是 mkv 文件的元数据
  • s[:stream_spec] 数据流的元数据, 比如 mkv 中音频流的元数据; stream_spec 取值范围同 map 选项的 stream_type_specifier
  • c: chapter_index 章节的元数据
  • p: program_index 节目的元数据

此参数如果不指定, 默认为 g, 即全局元数据

-disposition选项用来调整文件布局的, 此处用来设置默认音频流. 在上述命令行中将第 1 条音频流设置为默认音频流, -disposition:a:1 -default-disposition:a:3 none 的作用是一样的, 即不要将其设置为默认流(否则会出现所有音频流均是默认流的情况), 其语法如下:

1
-disposition[:stream_specifier] value
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
default
dub
original
comment
lyrics
karaoke
forced
hearing_impaired
visual_impaired
clean_effects
attached_pic
timed_thumbnails
captions
descriptions
metadata
dependent
still_image

default 用来设置默认流, attached_pic 可用来为视频或 mp3 设置封面.

使用上述命令行再次生成 mkv 后使用 potplayer 查看:

audio_stream

2. 使用 ffmpeg 将多个视频, 音频, 字幕, 字体封装为MKV

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
ffmpeg -i input.mp4 \
-i input_2.mp4 \
-i eng.m4a \
-i jpn.m4a \
-i chi.ass \
-i eng.ass \
-i jpn.ass \
-attach SmileySans-Oblique.ttf \
-map 0 -map 1 -map 2 -map 3 -map 4 -map 5 -map 6 \
-metadata:s:v:0 language="" \
-metadata:s:v:1 language="" \
-metadata:s:v:0 title="视频1" \
-metadata:s:v:1 title="视频2" \
-disposition:v:0 default \
-disposition:v:1 none \
-metadata:s:a:0 language="chi" \
-metadata:s:a:1 language="eng" \
-metadata:s:a:2 language="jpn" \
-metadata:s:a:0 title="普通话" \
-metadata:s:a:1 title="英语" \
-metadata:s:a:2 title="日语" \
-disposition:a:0 default \
-disposition:a:1 none \
-disposition:a:3 none \
-metadata:s:s:0 language="chi" \
-metadata:s:s:1 language="eng" \
-metadata:s:s:2 language="jpn" \
-metadata:s:s:0 title="中文字幕" \
-metadata:s:s:1 title="英文字幕" \
-metadata:s:s:2 title="日语字幕" \
-disposition:s:0 default \
-disposition:s:1 none \
-disposition:s:3 none \
-metadata:s:t:0 mimetype="application/x-truetype-font" \
-c copy out.mkv

这条命令行将两个视频, 2个音频, 3个字幕和1个在字幕中用到的字体封装为 mkv 文件, 且设置默认视频流为第1个视频流, 默认音频流为第1个音频流, 默认字幕流为第1个 ass 字幕. 为了防止在其它电脑上播放时找不到 ass 字幕的字体, 所以将字体也一并封装到 mkv 文件中.

在命令行中使用 -attach 选项添加字体为附件, 并且使用 -metadata:s:t:0 mimetype="application/x-truetype-font" 为附件指定 mimetype.

使用 potplayer 查看:

视频流

视频流

字幕流

字幕流

3. 为文件添加封面

默认资源管理器会提取视频中的某一帧来作为缩略图, 如果给视频文件设置了封面,则会使用封面作为文件缩略图.

以上面输入的 out.mkv 为例添加封面:

1
2
3
4
5
6
 ffmpeg -i out.mkv 
 -attach cover.jpg 
 -map 0 
 -metadata:s:t:1 mimetype="image/jpeg" 
 -disposition:t:1 attached_pic 
 -c copy out2.mkv

这里使用 -attach cover.jpg 添加封面图片, -map 0 的意思是选择第1个输入(out.mkv)中所有的流. 因为输入文件中已经有了一个附件(字体), 故此为封面设置metadata使用-metadata:s:t:1.

以上是对 mkv 文件添加封面的方法, 如果要对 MP4 或 MP3 文件添加封面, 由于 MP4 容器不支持附件, 故要使用如下命令行:

1
2
3
4
5
6
7
ffmpeg -i input.mp4 \
-i cover.jpg \
-map 0 \
-map 1 \
-disposition:v:1 attached_pic \
-c copy
out.mp4

对于 MP3 文件类似.

(全文完)