【问题标题】:Extract every audio and subtitles from a video with ffmpeg使用 ffmpeg 从视频中提取每个音频和字幕
【发布时间】:2015-12-31 13:16:00
【问题描述】:

我有多个音轨和字幕要提取到一个 .mkv 文件中。我是ffmpeg 命令的新手,这是我尝试过的(音频):

ffmpeg -i VIDEO.mkv -vn -acodec copy AUDIO.aac

它只提取 1 个音频。我想要的是告诉ffmpeg 将每个音频文件和字幕文件提取到一个目的地,并保留每个文件和扩展名的原始名称。 (因为我不知道音频文件是哪个扩展名,有时可能是 .flac.aac)。

我不确定我在网上找到的解决方案,因为它非常复杂,我需要解释才能知道它是如何工作的,以便我将来可以操作该命令。顺便说一句,我计划从 Windows CMD 运行代码。

谢谢。

【问题讨论】:

  • 对于 Matroska (MKV) 格式,使用 MKVToolNix 很容易做到这一点。

标签: audio video cmd ffmpeg mkv


【解决方案1】:

ffmpeg 中还没有选项可以自动将所有流提取到适当的容器中,但当然可以手动执行。

您只需要知道要提取的格式的适当容器。

默认stream selection 只为每种流类型选择一个流,因此您必须使用-map 选项手动映射每个流。

1.获取输入信息

使用ffmpegffprobe,您可以在每个单独的流中获取信息,并且有一个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

容器格式

将流与某些常见格式的输出扩展匹配的部分列表:

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

【讨论】:

  • -map 0:a:0 中的零前缀是什么?我们可以有多个“索引”(例如0:a:01:a:0)吗?我认为后缀(0)实际上是流索引。这些“超级”索引是否有任何文档?抱歉有点跑题了。
  • @themihai 这些值代表input id:stream specifier:stream id。例如,2:a:5 将是来自第 3 个输入的第 6 个 a音频流(ffmpeg 从 0 开始计数)。它比简单地声明2:5 更灵活,因为流索引 值可能并不总是代表所需的流。通过让您从第二个输入中选择第 8 个视频流之类的东西,它可以让您更加懒惰:1:v:8。请参阅-map docsthis answer 了解更多信息。
  • 感谢您的回答!有道理,我已经阅读了 -map 文档,但我不知道为什么我错过了。
  • @themihai 主要优点是您不必提前知道特定流的确切数字。
  • 值得注意的是,在 libavformat 版本 57.82.100 之前,无法使用此命令提取 *.sup 格式的蓝光字幕 (hdmv_pgs_subtitle),因此如果您需要这样做,请确保您正在使用最新版本的 ffmpeg 可执行文件。
【解决方案2】:

您将首先列出所有音频流:

ffmpeg -i VIDEO.mkv

然后根据输出,您可以编译命令以单独提取音轨。

使用一些 shell 脚本,您可以在脚本文件中自动执行此操作,这样您就可以对任何 mkv 文件通用地执行此操作。

字幕几乎相同。字幕会打印在信息中,然后你可以提取它们,类似于:

ffmpeg -threads 4 -i VIDEO.mkv -vn -an -codec:s:0.2 srt myLangSubtitle.srt

0.2 是您必须从信息中读取的标识符。

【讨论】:

    【解决方案3】:

    我是这样解决的:

    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", $8 }'; fi; done; }
    

    输出:

    仅在命令前设置 $video1 var。

    尽情享受吧!

    【讨论】:

    • 感谢您的提示非常有用。
    【解决方案4】:

    如果有人使用现代版本的ffmpegit 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}
    

    第 1 行 (mappings=...) 提取现有音频流并将其转换为“-map X:Y -acodec mp3 FILENAME”,而第 2 行执行提取

    【讨论】:

      【解决方案5】:

      以下脚本从当前目录中的文件中提取所有音频流

      ls |parallel "ffmpeg -i {} 2>&1 |\
       sed -n 's/.*Stream \#\(.\+\)\:\(.\+\)\: Audio\: \([a-zA-Z0-9]\+\).*$/-map \1:\2 -c copy \"{.}.\1\2.\3\"/p' |\
       xargs -n5 ffmpeg -i {} "
      

      【讨论】:

      • ffmpeg 的 stderr 输出不适用于机器解析。这就是ffprobe 的用途。
      • 如果我对 ffmpeg 有更好的了解,我当然可以设计出更好的解决方案。因此,这是一种有效的早期解决方案。
      猜你喜欢
      • 2012-04-12
      • 1970-01-01
      • 2015-08-25
      • 2018-05-09
      • 1970-01-01
      • 1970-01-01
      • 2021-12-17
      • 2021-02-26
      相关资源
      最近更新 更多