ffmpeg提取与合并音视频和字幕

发布时间 2024-01-12 11:49:45作者: DeviLeo

提取

假设有一个视频文件名字叫demo.mkv,有两条音频,一条字幕。

# 打印视频信息
$ ffprobe -i demo.mkv

# 以下是简化后的视频信息
视频1:Stream 0:0 hevc
音频1:Stream 0:1 eac3
音频2:Stream 0:2 aac
字幕1:Stream 0:3 subrip

# `Stream [i]:[j]`是指第i个输入中的第j个流。
# 视频、音频、字幕、缩略图等等都称为流。
# 此处的i=0即`demo.mkv`,j=0即视频1。

# 导出视频,无音频
# 参数`-map [i]:[j]`是指第i个输入中的第j个流(Stream)
# 参数`-vcodec copy`和`-c:v copy`表示不重新编码视频,直接复制。
# 参数`-acodec copy`和`-c:a copy`表示不重新编码音频,直接复制。
# 参数`-scodec copy`和`-c:s copy`表示不转换字幕,直接复制。
# 参数`-an`和`-vn`表示不导出音频和视频。
# 参数`-y`是指如果输出文件已存在,不询问直接覆盖。
$ ffmpeg -i demo.mkv -map 0:0 -vcodec copy -an -y "video.mp4"

# 导出第1条音频,无视频
$ ffmpeg -i demo.mkv -map 0:1 -acodec copy -vn -y "audio_1.ac3"

# 导出第2条音频,无视频
$ ffmpeg -i demo.mkv -map 0:2 -c:a copy -vn -y "audio_2.m4a"

# 导出字幕
$ ffmpeg -i demo.mkv -map 0:3 -c:s copy -an -vn -y "subtitle.srt"


# 合并为mkv
# 参数`-map [i]:v`是指第i个输入作为视频流
# 参数`-map [i]:a`是指第i个输入作为音频流
# 参数`-map [i]:s`是指第i个输入作为字幕流
# 参数`-metadata:s:a:1`表示指定第2个音频流的metadata
# 参数`-disposition:a:0 default`表示默认使用第1个音频流
# 参数`-disposition:a:1 0`表示取消默认使用第2个音频流
# 参数`-disposition:s:0 default`表示默认使用第1个字幕流
# 输出视频中流的位置按添加顺序编号
$ ffmpeg -i "video.mp4" \
    -i "audio_1.ac3" -i "audio_2.m4a" \
    -i "subtitle.srt" \
    -map 0:v -map 1:a -map 2:a -map 3:s \
    -c:v copy -c:a copy -c:s copy \
    -metadata:s:a:0 title="中文" -metadata:s:a:0 language=Chinese \
    -metadata:s:a:1 title="英语" -metadata:s:a:1 language=English \
    -metadata:s:s:0 title="简体中文" -metadata:s:s:0 language="Simplified Chinese" \
    -disposition:a:0 default \
    -disposition:a:1 0 \
    -disposition:s:0 default \
    -y "output.mkv"

根据音频格式返回扩展名和流索引

$ get_audio() {
    INFO="$1"
    
    MANDARIN=`cat "${INFO}" | grep "Audio.*default"`
    MANDARIN_STREAM_INDEX="${MANDARIN#*:}"
    MANDARIN_STREAM_INDEX="${MANDARIN_STREAM_INDEX%%:*}"
    MANDARIN_STREAM_INDEX="${MANDARIN_STREAM_INDEX%%(*}"
    
    MANDARIN_STREAM_CODEC="${MANDARIN%%,*}"
    MANDARIN_STREAM_CODEC="${MANDARIN_STREAM_CODEC##*: }"
    if [[ "${MANDARIN_STREAM_CODEC}" == "eac3" ]]; then
        MANDARIN_STREAM_CODEC="ac3"
    else
        MANDARIN_STREAM_CODEC="m4a"
    fi
    
    SHANGHAI=`cat "${INFO}" | grep "Audio" | grep -v "default"`
    SHANGHAI_STREAM_INDEX="${SHANGHAI#*:}"
    SHANGHAI_STREAM_INDEX="${SHANGHAI_STREAM_INDEX%%:*}"
    SHANGHAI_STREAM_INDEX="${SHANGHAI_STREAM_INDEX%%(*}"
    
    SHANGHAI_STREAM_CODEC="${SHANGHAI%%,*}"
    SHANGHAI_STREAM_CODEC="${SHANGHAI_STREAM_CODEC##*: }"
    SHANGHAI_STREAM_CODEC="${SHANGHAI_STREAM_CODEC%% *}"
    if [[ "${SHANGHAI_STREAM_CODEC}" == "eac3" ]]; then
        SHANGHAI_STREAM_CODEC="ac3"
    else
        SHANGHAI_STREAM_CODEC="m4a"
    fi
    
    echo "${SHANGHAI_STREAM_INDEX}" "${SHANGHAI_STREAM_CODEC}" "${MANDARIN_STREAM_INDEX}" "${MANDARIN_STREAM_CODEC}"
}

$ INFO="info.log"
$ ffprobe -i "demo.mkv" > "${INFO}" 2>&1
$ INDEX_CODEC=($(get_audio "$INFO"))

$ SH_I="${INDEX_CODEC[0]}"
$ SH_C="${INDEX_CODEC[1]}"
$ CN_I="${INDEX_CODEC[2]}"
$ CN_C="${INDEX_CODEC[3]}"

# echo "audio: ${SH_I}, ext: ${SH_C}"
# echo "audio: ${CN_I}, ext: ${CN_C}"