使用 ffmpeg 从视频中提取每个音频和字幕
Extract every audio and subtitles from a video with ffmpeg
我有多个音轨和字幕要提取到单个 .mkv 文件中。我是 ffmpeg
命令的新手,这是我尝试过的(音频):
ffmpeg -i VIDEO.mkv -vn -acodec copy AUDIO.aac
它只是提取了 1 个音频。我想要的是告诉 ffmpeg
将每个音频文件和字幕文件提取到一个目的地,并保留每个文件和扩展名的原始名称。 (因为我不知道音频文件的扩展名是什么,有时可能是 .flac 或 .aac)。
我不确定我在网上找到的解决方案,因为它很复杂,我需要解释才能知道它是如何工作的,以便我以后可以操纵命令。顺便说一下,我计划 运行 来自 Windows CMD 的代码。
谢谢。
您首先要列出所有音频流:
ffmpeg -i VIDEO.mkv
然后根据输出,您可以编译命令以单独提取音轨。
使用一些 shell 脚本,您可以在脚本文件中自动执行此操作,以便您可以对任何 mkv 文件进行通用操作。
字幕都差不多。字幕会打印在info中,然后你可以提取它们,类似于:
ffmpeg -threads 4 -i VIDEO.mkv -vn -an -codec:s:0.2 srt myLangSubtitle.srt
0.2 是您必须从信息中读取的标识符。
ffmpeg
中还没有选项可以自动将所有流提取到适当的容器中,但是手动操作肯定是可以的。
您只需要知道适合您要提取的格式的容器。
默认 stream selection 每种流类型只选择一个流,因此您必须使用 -map
选项手动映射每个流。
1。获取输入信息
使用 ffmpeg
或 ffprobe
你可以在每个单独的流中获取信息,并且有一个 wide variety of formats (xml, json, cvs等)可满足您的需求。
ffmpeg
例子
ffmpeg -i input.mkv
结果输出(我删掉了一些额外的东西,流编号和格式信息很重要):
Input #0, matroska,webm, from 'input.mkv':
Metadata:
Duration: 00:00:05.00, start: 0.000000, bitrate: 106 kb/s
Stream #0:0: Video: h264 (High 4:4:4 Predictive), yuv444p, 320x240 [SAR 1:1 DAR 4:3], 25 fps, 25 tbr, 1k tbn, 50 tbc (default)
Stream #0:1: Audio: vorbis, 44100 Hz, mono, fltp (default)
Stream #0:2: Audio: aac, 44100 Hz, mono, fltp (default)
Stream #0:3: Audio: flac, 44100 Hz, mono, fltp (default)
Stream #0:4: Subtitle: ass (default)
ffprobe
例子
ffprobe -v error -show_entries stream=index,codec_name,codec_type input.mkv
结果输出:
[STREAM]
index=0
codec_name=h264
codec_type=video
[/STREAM]
[STREAM]
index=1
codec_name=vorbis
codec_type=audio
[/STREAM]
[STREAM]
index=2
codec_name=aac
codec_type=audio
[/STREAM]
[STREAM]
index=3
codec_name=flac
codec_type=audio
[/STREAM]
[STREAM]
index=4
codec_name=ass
codec_type=subtitle
[/STREAM]
2。提取流
使用上述命令之一的信息:
ffmpeg -i input.mkv \
-map 0:v -c copy video_h264.mkv \
-map 0:a:0 -c copy audio0_vorbis.oga \
-map 0:a:1 -c copy audio1_aac.m4a \
-map 0:a:2 -c copy audio2.flac \
-map 0:s -c copy subtitles.ass
在这种情况下,上面的例子是一样的:
ffmpeg -i input.mkv \
-map 0:0 -c copy video_h264.mkv \
-map 0:1 -c copy audio0_vorbis.oga \
-map 0:2 -c copy audio1_aac.m4a \
-map 0:3 -c copy audio2.flac \
-map 0:4 -c copy subtitles.ass
我更喜欢第一个例子,因为input file index:stream specifier:stream index
更灵活高效;它也不太容易出现不正确的映射。
请参阅有关 stream specifiers and the -map
option to fully understand the syntax. Additional info is in the answer to FFmpeg mux video and audio (from another video) - mapping issue 的文档。
这些示例将 stream copy(重新混合),因此不会发生重新编码。
容器格式
用于将流与某些常见格式的输出扩展名相匹配的部分列表:
Video Format
Extensions
H.264
.mp4, .m4v, .mov, .h264, .264
H.265/HEVC
.mp4, .h265, .265
VP8/VP9
.webm
AV1
.mp4
MPEG-4
.mp4, .avi
MPEG-2
.mpg, .vob, .ts
DV
.dv, .avi, .mov
Theora
.ogv/.ogg
FFV1
.mkv
Almost anything
.mkv, .nut
Audio Format
Extensions
AAC
.m4a, .aac
MP3
.mp3
PCM
.wav
Vorbis
.oga/.ogg
Opus
.opus, .oga/.ogg, .mp4
FLAC
.flac, .oga/.ogg
Almost anything
.mka, .nut
Subtitle Format
Extensions
Subrip/SRT
.srt
SubStation Alpha/ASS
.ass
以下脚本从当前目录中的文件中提取所有音频流
ls |parallel "ffmpeg -i {} 2>&1 |\
sed -n 's/.*Stream \#\(.\+\)\:\(.\+\)\: Audio\: \([a-zA-Z0-9]\+\).*$/-map : -c copy \"{.}..\"/p' |\
xargs -n5 ffmpeg -i {} "
我是这样解决的:
ffprobe -show_entries stream=index,codec_type:stream_tags=language -of compact $video1 2>&1 | { while read line; do if $(echo "$line" | grep -q -i "stream #"); then echo "$line"; fi; done; while read -d $'\x0D' line; do if $(echo "$line" | grep -q "time="); then echo "$line" | awk '{ printf "%s\r", }'; fi; done; }
输出:
仅在命令前设置 $video1 变量。
尽情享受吧!
如果有人使用 ffmpeg
的现代版本解决这个问题,it looks like 他们会在此处添加选项。
我需要通过维护所有轨道来转换文件:
ffmpeg -i "${input_file}" -vcodec hevc -crf 28 -map 0 "${output_file}"
要实现原始问题的要求,可能可以使用:
mappings="`ffmpeg -i \"${filein}\" |& awk 'BEGIN { i = 1 }; /Stream.*Audio/ {gsub(/^ *Stream #/, \"-map \"); gsub(/\(.*$/, \" -acodec mp3 audio\"i\".mp3\"); print; i +=1}'`"
ffmpeg -i "${input_file}" ${mappings}
第一行 (mappings=...) 提取现有音频流并将它们转换为“-map X:Y -acodec mp3 FILENAME”,而第二行执行提取
我有多个音轨和字幕要提取到单个 .mkv 文件中。我是 ffmpeg
命令的新手,这是我尝试过的(音频):
ffmpeg -i VIDEO.mkv -vn -acodec copy AUDIO.aac
它只是提取了 1 个音频。我想要的是告诉 ffmpeg
将每个音频文件和字幕文件提取到一个目的地,并保留每个文件和扩展名的原始名称。 (因为我不知道音频文件的扩展名是什么,有时可能是 .flac 或 .aac)。
我不确定我在网上找到的解决方案,因为它很复杂,我需要解释才能知道它是如何工作的,以便我以后可以操纵命令。顺便说一下,我计划 运行 来自 Windows CMD 的代码。
谢谢。
您首先要列出所有音频流:
ffmpeg -i VIDEO.mkv
然后根据输出,您可以编译命令以单独提取音轨。
使用一些 shell 脚本,您可以在脚本文件中自动执行此操作,以便您可以对任何 mkv 文件进行通用操作。
字幕都差不多。字幕会打印在info中,然后你可以提取它们,类似于:
ffmpeg -threads 4 -i VIDEO.mkv -vn -an -codec:s:0.2 srt myLangSubtitle.srt
0.2 是您必须从信息中读取的标识符。
ffmpeg
中还没有选项可以自动将所有流提取到适当的容器中,但是手动操作肯定是可以的。
您只需要知道适合您要提取的格式的容器。
默认 stream selection 每种流类型只选择一个流,因此您必须使用 -map
选项手动映射每个流。
1。获取输入信息
使用 ffmpeg
或 ffprobe
你可以在每个单独的流中获取信息,并且有一个 wide variety of formats (xml, json, cvs等)可满足您的需求。
ffmpeg
例子
ffmpeg -i input.mkv
结果输出(我删掉了一些额外的东西,流编号和格式信息很重要):
Input #0, matroska,webm, from 'input.mkv':
Metadata:
Duration: 00:00:05.00, start: 0.000000, bitrate: 106 kb/s
Stream #0:0: Video: h264 (High 4:4:4 Predictive), yuv444p, 320x240 [SAR 1:1 DAR 4:3], 25 fps, 25 tbr, 1k tbn, 50 tbc (default)
Stream #0:1: Audio: vorbis, 44100 Hz, mono, fltp (default)
Stream #0:2: Audio: aac, 44100 Hz, mono, fltp (default)
Stream #0:3: Audio: flac, 44100 Hz, mono, fltp (default)
Stream #0:4: Subtitle: ass (default)
ffprobe
例子
ffprobe -v error -show_entries stream=index,codec_name,codec_type input.mkv
结果输出:
[STREAM]
index=0
codec_name=h264
codec_type=video
[/STREAM]
[STREAM]
index=1
codec_name=vorbis
codec_type=audio
[/STREAM]
[STREAM]
index=2
codec_name=aac
codec_type=audio
[/STREAM]
[STREAM]
index=3
codec_name=flac
codec_type=audio
[/STREAM]
[STREAM]
index=4
codec_name=ass
codec_type=subtitle
[/STREAM]
2。提取流
使用上述命令之一的信息:
ffmpeg -i input.mkv \
-map 0:v -c copy video_h264.mkv \
-map 0:a:0 -c copy audio0_vorbis.oga \
-map 0:a:1 -c copy audio1_aac.m4a \
-map 0:a:2 -c copy audio2.flac \
-map 0:s -c copy subtitles.ass
在这种情况下,上面的例子是一样的:
ffmpeg -i input.mkv \
-map 0:0 -c copy video_h264.mkv \
-map 0:1 -c copy audio0_vorbis.oga \
-map 0:2 -c copy audio1_aac.m4a \
-map 0:3 -c copy audio2.flac \
-map 0:4 -c copy subtitles.ass
我更喜欢第一个例子,因为
input file index:stream specifier:stream index
更灵活高效;它也不太容易出现不正确的映射。请参阅有关 stream specifiers and the
-map
option to fully understand the syntax. Additional info is in the answer to FFmpeg mux video and audio (from another video) - mapping issue 的文档。这些示例将 stream copy(重新混合),因此不会发生重新编码。
容器格式
用于将流与某些常见格式的输出扩展名相匹配的部分列表:
Video Format | Extensions |
---|---|
H.264 | .mp4, .m4v, .mov, .h264, .264 |
H.265/HEVC | .mp4, .h265, .265 |
VP8/VP9 | .webm |
AV1 | .mp4 |
MPEG-4 | .mp4, .avi |
MPEG-2 | .mpg, .vob, .ts |
DV | .dv, .avi, .mov |
Theora | .ogv/.ogg |
FFV1 | .mkv |
Almost anything | .mkv, .nut |
Audio Format | Extensions |
---|---|
AAC | .m4a, .aac |
MP3 | .mp3 |
PCM | .wav |
Vorbis | .oga/.ogg |
Opus | .opus, .oga/.ogg, .mp4 |
FLAC | .flac, .oga/.ogg |
Almost anything | .mka, .nut |
Subtitle Format | Extensions |
---|---|
Subrip/SRT | .srt |
SubStation Alpha/ASS | .ass |
以下脚本从当前目录中的文件中提取所有音频流
ls |parallel "ffmpeg -i {} 2>&1 |\
sed -n 's/.*Stream \#\(.\+\)\:\(.\+\)\: Audio\: \([a-zA-Z0-9]\+\).*$/-map : -c copy \"{.}..\"/p' |\
xargs -n5 ffmpeg -i {} "
我是这样解决的:
ffprobe -show_entries stream=index,codec_type:stream_tags=language -of compact $video1 2>&1 | { while read line; do if $(echo "$line" | grep -q -i "stream #"); then echo "$line"; fi; done; while read -d $'\x0D' line; do if $(echo "$line" | grep -q "time="); then echo "$line" | awk '{ printf "%s\r", }'; fi; done; }
输出:
仅在命令前设置 $video1 变量。
尽情享受吧!
如果有人使用 ffmpeg
的现代版本解决这个问题,it looks like 他们会在此处添加选项。
我需要通过维护所有轨道来转换文件:
ffmpeg -i "${input_file}" -vcodec hevc -crf 28 -map 0 "${output_file}"
要实现原始问题的要求,可能可以使用:
mappings="`ffmpeg -i \"${filein}\" |& awk 'BEGIN { i = 1 }; /Stream.*Audio/ {gsub(/^ *Stream #/, \"-map \"); gsub(/\(.*$/, \" -acodec mp3 audio\"i\".mp3\"); print; i +=1}'`"
ffmpeg -i "${input_file}" ${mappings}
第一行 (mappings=...) 提取现有音频流并将它们转换为“-map X:Y -acodec mp3 FILENAME”,而第二行执行提取