文 章

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: attachemnt, 附件

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

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视频封装流的输出:

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

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

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

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

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

1Stream #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  Stream #0:2(chi): Audio: ac3, 48000 Hz, stereo, fltp, 192 kb/s
2    Metadata:
3      title           : 天空之城 / 普通话

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

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

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

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

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

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

3. 提取字体 simhei.ttf

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

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

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

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

合并为MKV

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

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

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

在 potplayer 中查看:

audio_stream

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

 1ffmpeg -i input.mp4 \
 2-i eng.m4a \
 3-i jpn.m4a \
 4-map 0 -map 1 -map 2 \
 5-metadata:s:a:0 language="chi" \
 6-metadata:s:a:1 language="eng" \
 7-metadata:s:a:2 language="jpn" \
 8-metadata:s:a:0 title="普通话" \
 9-metadata:s:a:1 title="英语" \
10-metadata:s:a:2 title="日语" \
11-disposition:a:0 default \
12-disposition:a:1 -default \
13-disposition:a:3 none \
14-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
 1default
 2dub
 3original
 4comment
 5lyrics
 6karaoke
 7forced
 8hearing_impaired
 9visual_impaired
10clean_effects
11attached_pic
12timed_thumbnails
13captions
14descriptions
15metadata
16dependent
17still_image

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

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

audio_stream

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

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

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

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

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

对于 MP3 文件类似.

(全文完)