FFMpeg与MKV

序
如果要用一个词来形容MKV容器, 大约用"妖孽"比较恰当吧, 相比常见的mp4, mov等容器格式, 它更像一个压缩包, 不管视频流, 音频流, 字幕流, 字体, 都可以封装, 甚至封装一个PDF文件, 一个MKV中封装两个视频流都不是什么问题. 像mkvtoolnix, 小丸工具箱等软件都可以对MKV进行处理, 以下尝试使用ffmpeg处理mkv文件.
ffmpeg 的 map 参数
ffmpeg 的 map 选项是用来从输入中选择流的. 通常单个 mp4 文件只有一路视频流和一路音频流, 使用 ffmpeg 处理时基本上不用指定, 当有多个输入时就有必要使用 map 选项选择输入流了, 由于 mkv 能封装的东西太多了, 故大多数时候 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视频封装流的输出:
|
|
此 MKV 文件包含了 7 条音轨, 十几条字幕, 5 种字体, 算是比较有代表的 mkv 封装了, 以下以示例方式说明对引 MKV 文件的常用操作.
1. 将此 mkv 文件转换为只包含中文配音的mp4文件:
|
|
其中-map 0:0表示选择第1个输入中的第1个流, 在此输入文件中是 H.264 编码的视频流:
|
|
-map 0:2表示第1个输入中的第3个流, 在此输入文件中是普通话配音:
|
|
-c copy表示将输入流复制到输出, 以避免重新编码耗费时间.
2. 提取此 mkv 文件中的简体中文字幕:
|
|
其中-map 0:8表示选择第1个输入中的第8个流, 此处是 ass 格式的简体中文字幕:
|
|
-c:s ass表示对字幕使用ass编码器, 实际上使用-c copy也是可以的, 因为输入和输出均是 ass 文件.
3. 提取字体 simhei.ttf
|
|
将输出文件名留空的话就可以提取所有附件:
|
|
提取所有附件的命令在 Linux 下可正常提取, Windows 下会报参数错误, 原因未知.
合并为MKV
1. 为 MP4 新增2路音频流并封装为MKV
以下命令行将视频input.mp4和英语配音音频流eng.m4a, 日语配音音频流jpn.m4a合并为一个mkv文件:
|
|
在 potplayer 中查看:

虽然音频被添加进去了, 但标题本文是有问题的, 所以还需要为其添加元数据metadata:
|
|
命令行结尾的符号 \ 是换行符, 在 Linux 的 bash 中是 \, 在 Windows 的 cmd 中是 ^, 在 powershell 中是 `(反引号).
-metadata 选项用来写入元数据, 可为mkv文件或其中封装的流写入元数据, 其语法如下:
|
|
metadata_specifier 可取如下几种值:
g: 全局元数据, 也就是 mkv 文件的元数据-
s[:stream_spec]数据流的元数据, 比如 mkv 中音频流的元数据;stream_spec取值范围同 map 选项的stream_type_specifier c: chapter_index章节的元数据p: program_index节目的元数据
此参数如果不指定, 默认为 g, 即全局元数据
-
language的值可参考 ISO 638-1 代码表.
-disposition选项用来调整文件布局的, 此处用来设置默认音频流. 在上述命令行中将第 1 条音频流设置为默认音频流, -disposition:a:1 -default 与 -disposition:a:3 none 的作用是一样的, 即不要将其设置为默认流(否则会出现所有音频流均是默认流的情况), 其语法如下:
|
|
-
stream_specifier与 map 选项的stream_type_specifier类似, 此处不再赘述 - value 可用的值可以使用命令行
ffmpeg -dispositions查看, 如下:
|
|
default 用来设置默认流, attached_pic 可用来为视频或 mp3 设置封面.
使用上述命令行再次生成 mkv 后使用 potplayer 查看:

2. 使用 ffmpeg 将多个视频, 音频, 字幕, 字体封装为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 为例添加封面:
|
|
这里使用 -attach cover.jpg 添加封面图片, -map 0 的意思是选择第1个输入(out.mkv)中所有的流. 因为输入文件中已经有了一个附件(字体), 故此为封面设置metadata使用-metadata:s:t:1.
以上是对 mkv 文件添加封面的方法, 如果要对 MP4 或 MP3 文件添加封面, 由于 MP4 容器不支持附件, 故要使用如下命令行:
|
|
对于 MP3 文件类似.
(全文完)