有没有办法从 MKV 中提取字幕同时保持原始格式?

有没有办法从 MKV 中提取字幕同时保持原始格式?

我正在尝试使用 ffmpeg 或 mkvextract 从 mkv 文件中提取(如果有很多,则是第一个)字幕轨道(仅通过 CLI,没有 GUI)。

但这样做有一个缺点:我必须手动输入输出名称。在批处理中,你不知道字幕是否是.srt.ass或者甚至是不同的格式,所以盲目地写作subs.srt听起来不是一个好主意。

有没有办法提取字幕同时保持原始格式?

例如,ffmpeg 是否有办法获取字幕格式信息,然后使用该类型执行类似上述命令的操作?(理想情况下是通过工具的本机方法,与操作系统无关)

我可以做这个:

ffmpeg -i video.mkv subs.srt

或这个:

ffmpeg -i video.mkv -map 0:s:0 subs.srt

但是有没有办法避免指定输出名称?例如,使用 mkvextract 我可以这样做:

mkvextract attachments ..\input_video.mkv 1

我会收到警告(不是错误):

“未指定目标文件名,将使用附件名称。”

这实际上是我想要的行为,但这适用于附件。

没有mkvextract subtitlesmkvextract将字幕视为轨道的一部分。因此,如果轨道顺序混乱,字幕不是第 3 条轨道,则使用 mkvextract 的类似命令将不起作用。


总结

  • ffmpeg:我被(可能)错误的文件扩展名困住了
  • mkvextract:我存在提取一些随机音轨而不是字幕的风险。

答案1

我们可以使用 FFprobe 来获取字幕编解码器名称,并相应地应用提取的字幕文件扩展名。

示例批处理文件基于下列答案

假设 MKV 文件名为in1.mkv,我们可以使用以下 Windows 批处理文件:

set fname=in1
set mkv_fname=%fname%.mkv

@echo off
SETLOCAL

::Extract subtitles codec name from file using FFprobe
for /F "tokens=1 delims='\n'" %%a in ('ffprobe.exe "-v" "error" "-select_streams" "s:0" "-show_entries" "stream=codec_name" "-of" "default=noprint_wrappers=1:nokey=1" "%mkv_fname%"') do (
    ENDLOCAL
    set tmp_subs_codec=%%a  
)

::Remove the space from subs_codec
set subs_codec=%tmp_subs_codec:~0,-1%

if %subs_codec%==ass ffmpeg -y -vn -an -dn -i %mkv_fname% -c copy -map 0:s:0 %fname%.ass
if %subs_codec%==subrip ffmpeg -y -vn -an -dn -i %mkv_fname% -c copy -map 0:s:0 %fname%.srt

对于带参数的批处理文件,替换set fname=in1set fname=%1


Linux Bash 示例:

fname=in1
mkv_fname=$fname".mkv"
subs_codec=$(ffprobe -v error -select_streams s:0 -show_entries stream=codec_name -of default=noprint_wrappers=1:nokey=1 $mkv_fname 2>&1)

if [ "$subs_codec" = "ass" ]; then
    ffmpeg -y -vn -an -dn -i $mkv_fname -c copy -map 0:s:0 $fname.ass 
fi

if [ "$subs_codec" = "subrip" ]; then
    ffmpeg -y -vn -an -dn -i $mkv_fname -c copy -map 0:s:0 $fname.srt 
fi

答案2

Rotem 的回答启发了我并给了我技巧,所以我找到了一种更简单的方法来做到这一点并想与大家分享。

使用 PowerShell 中的 ffprobe,我们使用 ffprobe 获取有组织的 json 上的信息,然后使用结构化数组及其访问器,而不是在输出字符串上进行 grep/select:

$ff_info_array = .\ffprobe.exe -v quiet -print_format json -show_format -show_streams "video.mkv" | ConvertFrom-Json

foreach ($entry in $ff_info_array.streams){
if ($entry.codec_type -eq "subtitle") {
                $sub_type=$entry.codec_name #this will contain .srt, .ass ,etc

                if($entry.disposition.default){
                    write-host  "found default sub and its extension is $sub_type "
                    $internal_sub_found = $true
                    $idx = $entry.index
                    $concat= $idx.""
                    #we can now extract the subtitle like above but add the exertion in the name or via mkvextract:
                    .\mkvextract.exe -q tracks  "video.mp4"  "$idx"":myvideo_output.$sub_type"

                }
}

相关内容