强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

FFmpeg 多媒体处理教程 / 复用与解复用

复用与解复用

概述

复用(Muxing)是将多个媒体流(视频、音频、字幕等)打包到一个容器文件中的过程。解复用(Demuxing)则是从容器文件中提取各个媒体流的过程。本章详细介绍 FFmpeg 的复用与解复用功能。

复用基础

复用流程

视频流 ──┐
音频流 ──┼──► 复用器 (Muxer) ──► 容器文件
字幕流 ──┘

解复用流程

容器文件 ──► 解复用器 (Demuxer) ──┬──► 视频流
                                 ├──► 音频流
                                 └──► 字幕流

常用复用器

复用器格式说明
mp4MP4最通用格式
matroskaMKV开放格式
flvFLV流媒体格式
mpegtsMPEG-TS传输流
webmWebMWeb 格式
aviAVI传统格式
movMOVApple 格式
oggOGG开源格式

常用解复用器

解复用器格式说明
mov,mp4,m4a,3gpMP4 系列自动检测
matroska,webmMKV/WebM自动检测
flvFLVFlash 视频
mpegtsMPEG-TS传输流
aviAVI传统格式
oggOGG开源格式

多音轨处理

添加多音轨

# 添加两个音轨
ffmpeg -i video.mp4 -i audio1.mp3 -i audio2.aac \
    -map 0:v -map 1:a -map 2:a \
    -c:v copy -c:a copy \
    output.mkv

# 设置音轨语言
ffmpeg -i video.mp4 -i audio_cn.mp3 -i audio_en.mp3 \
    -map 0:v -map 1:a -map 2:a \
    -c:v copy -c:a copy \
    -metadata:s:a:0 language=chi \
    -metadata:s:a:1 language=eng \
    output.mkv

# 设置默认音轨
ffmpeg -i video.mp4 -i audio1.mp3 -i audio2.mp3 \
    -map 0:v -map 1:a -map 2:a \
    -c:v copy -c:a copy \
    -disposition:a:0 default \
    -disposition:a:1 0 \
    output.mkv

音轨选择与映射

# 选择特定音轨
ffmpeg -i input.mkv -map 0:v -map 0:a:1 -c copy output.mp4

# 选择多个音轨
ffmpeg -i input.mkv -map 0:v -map 0:a:0 -map 0:a:2 -c copy output.mkv

# 排除特定音轨
ffmpeg -i input.mkv -map 0 -map -0:a:1 -c copy output.mkv

# 从多个输入选择音轨
ffmpeg -i video.mp4 -i audio1.mp3 -i audio2.mp3 \
    -map 0:v -map 1:a -map 2:a \
    -c copy output.mkv

音轨替换

# 替换音轨
ffmpeg -i video.mp4 -i new_audio.mp3 \
    -map 0:v -map 1:a \
    -c:v copy -c:a copy \
    output.mp4

# 替换特定音轨
ffmpeg -i input.mkv -i new_audio.mp3 \
    -map 0 -map -0:a:0 -map 1:a \
    -c copy output.mkv

音轨合并

# 混合多个音轨为一个
ffmpeg -i video.mp4 -i audio1.mp3 -i audio2.mp3 \
    -filter_complex "[1:a][2:a]amix=inputs=2:duration=longest[a]" \
    -map 0:v -map "[a]" \
    -c:v copy -c:a aac \
    output.mp4

# 保留多个音轨并添加混合音轨
ffmpeg -i video.mp4 -i audio1.mp3 -i audio2.mp3 \
    -filter_complex "[1:a][2:a]amix=inputs=2:duration=longest[mixed]" \
    -map 0:v -map 1:a -map 2:a -map "[mixed]" \
    -c:v copy -c:a copy -c:a:2 aac \
    -metadata:s:a:0 language=chi \
    -metadata:s:a:1 language=eng \
    -metadata:s:a:2 language=und \
    output.mkv

章节处理

章节格式

FFmetadata 格式

;FFMETADATA1
title=Example Video
artist=Example Artist

[CHAPTER]
TIMEBASE=1/1000
START=0
END=300000
title=Chapter 1

[CHAPTER]
TIMEBASE=1/1000
START=300000
END=600000
title=Chapter 2

[CHAPTER]
TIMEBASE=1/1000
START=600000
END=900000
title=Chapter 3

XML 章节格式(MKV)

<?xml version="1.0" encoding="UTF-8"?>
<Chapters>
  <EditionEntry>
    <ChapterAtom>
      <ChapterTimeStart>00:00:00.000</ChapterTimeStart>
      <ChapterTimeEnd>00:05:00.000</ChapterTimeEnd>
      <ChapterDisplay>
        <ChapterString>第一章</ChapterString>
      </ChapterDisplay>
    </ChapterAtom>
    <ChapterAtom>
      <ChapterTimeStart>00:05:00.000</ChapterTimeStart>
      <ChapterTimeEnd>00:10:00.000</ChapterTimeEnd>
      <ChapterDisplay>
        <ChapterString>第二章</ChapterString>
      </ChapterDisplay>
    </ChapterAtom>
  </EditionEntry>
</Chapters>

添加章节

# 创建章节元数据文件
cat > chapters.txt << 'EOF'
;FFMETADATA1

[CHAPTER]
TIMEBASE=1/1000
START=0
END=300000
title=Introduction

[CHAPTER]
TIMEBASE=1/1000
START=300000
END=600000
title=Main Content

[CHAPTER]
TIMEBASE=1/1000
START=600000
END=900000
title=Conclusion
EOF

# 添加章节到视频
ffmpeg -i input.mp4 -i chapters.txt -map_metadata 1 -c copy output.mp4

提取章节

# 提取章节信息
ffmpeg -i input.mp4 -f ffmetadata chapters.txt

# 提取章节到 JSON
ffprobe -v quiet -print_format json -show_chapters input.mp4

# 提取章节到 XML(MKV)
ffmpeg -i input.mkv -map_chapters 1 chapters.xml

编辑章节

# 提取现有章节
ffmpeg -i input.mp4 -f ffmetadata chapters.txt

# 编辑 chapters.txt

# 应用修改后的章节
ffmpeg -i input.mp4 -i chapters.txt -map_metadata 1 -c copy output.mp4

删除章节

# 删除所有章节
ffmpeg -i input.mp4 -map_chapters -1 -c copy output.mp4

章节脚本

#!/bin/bash
# create_chapters.sh

INPUT=$1
CHAPTERS_FILE=$2
OUTPUT=$3

# 创建章节文件
cat > "$CHAPTERS_FILE" << 'EOF'
;FFMETADATA1

[CHAPTER]
TIMEBASE=1/1000
START=0
END=300000
title=Introduction

[CHAPTER]
TIMEBASE=1/1000
START=300000
END=600000
title=Main Content

[CHAPTER]
TIMEBASE=1/1000
START=600000
END=900000
title=Conclusion
EOF

# 应用章节
ffmpeg -i "$INPUT" -i "$CHAPTERS_FILE" -map_metadata 1 -c copy "$OUTPUT"

echo "章节添加完成: $OUTPUT"

元数据处理

元数据类型

类型说明示例
全局元数据文件级别信息标题、作者、日期
流元数据特定流信息语言、标题
章节元数据章节信息章节标题
节目元数据节目信息节目标题

查看元数据

# 查看所有元数据
ffprobe -v quiet -print_format json -show_format -show_streams input.mp4

# 查看格式元数据
ffprobe -v quiet -show_entries format_tags input.mp4

# 查看流元数据
ffprobe -v quiet -show_entries stream_tags input.mp4

# 查看特定标签
ffprobe -v quiet -show_entries format_tags=title,artist input.mp4

设置元数据

# 设置全局元数据
ffmpeg -i input.mp4 \
    -metadata title="视频标题" \
    -metadata artist="作者" \
    -metadata album="专辑" \
    -metadata date="2024" \
    -metadata comment="备注" \
    -metadata genre="类型" \
    -metadata copyright="版权信息" \
    output.mp4

# 设置流元数据
ffmpeg -i input.mp4 \
    -metadata:s:a:0 language=chi \
    -metadata:s:a:0 title="中文音轨" \
    -metadata:s:v:0 title="主视频" \
    output.mp4

# 设置章节元数据
ffmpeg -i input.mp4 \
    -metadata:s:c:0 title="第一章" \
    output.mp4

删除元数据

# 删除所有元数据
ffmpeg -i input.mp4 -map_metadata -1 -c copy output.mp4

# 删除全局元数据
ffmpeg -i input.mp4 -map_metadata:g -1 -c copy output.mp4

# 删除流元数据
ffmpeg -i input.mp4 -map_metadata:s -1 -c copy output.mp4

# 保留特定元数据
ffmpeg -i input.mp4 -map_metadata 0 -metadata title="" -c copy output.mp4

常用元数据标签

标签说明适用格式
title标题所有
artist艺术家/作者所有
album专辑音频
album_artist专辑艺术家音频
track音轨号音频
disc碟片号音频
date日期所有
genre类型音频
comment备注所有
description描述所有
copyright版权所有
composer作曲家音频
encoder编码器所有
language语言

从文件读取元数据

# 创建元数据文件
cat > metadata.txt << 'EOF'
;FFMETADATA1
title=视频标题
artist=作者
date=2024
comment=备注
EOF

# 应用元数据
ffmpeg -i input.mp4 -i metadata.txt -map_metadata 1 -c copy output.mp4

元数据脚本

#!/bin/bash
# add_metadata.sh

INPUT=$1
TITLE=$2
ARTIST=$3
YEAR=$4
OUTPUT=$5

ffmpeg -i "$INPUT" \
    -metadata title="$TITLE" \
    -metadata artist="$ARTIST" \
    -metadata date="$YEAR" \
    -metadata comment="Processed by FFmpeg" \
    -c copy "$OUTPUT"

echo "元数据添加完成: $OUTPUT"

时间戳处理

时间戳基础

# 查看时间戳信息
ffprobe -v quiet -show_entries stream=start_time,duration input.mp4

# 查看格式时长
ffprobe -v quiet -show_entries format=duration input.mp4

# 查看详细时间戳
ffprobe -v quiet -show_entries packet=pts_time,dts_time input.mp4

时间戳调整

# 重置时间戳(从 0 开始)
ffmpeg -i input.mp4 -c copy -output_ts_offset 0 output.mp4

# 设置起始时间戳
ffmpeg -i input.mp4 -c copy -output_ts_offset 5 output.mp4

# 修复时间戳
ffmpeg -i input.mp4 -c copy -avoid_negative_ts make_zero output.mp4

时间戳模式

# 复制时间戳
ffmpeg -i input.mp4 -c copy -copyts output.mp4

# 重置时间戳
ffmpeg -i input.mp4 -c copy -copytb 1 output.mp4

# 使用输入时间戳
ffmpeg -i input.mp4 -c copy -fflags +genpts output.mp4

时间基(Timebase)

# 设置时间基
ffmpeg -i input.mp4 -c:v libx264 -video_track_timescale 90000 output.mp4

# 设置音频时间基
ffmpeg -i input.mp4 -c:a aac -audio_track_timescale 44100 output.mp4

延迟处理

# 视频延迟
ffmpeg -i input.mp4 -itsoffset 0.5 -i input.mp4 -map 0:v -map 1:a -c copy output.mp4

# 音频延迟
ffmpeg -i input.mp4 -itsoffset -0.5 -i input.mp4 -map 0:v -map 1:a -c copy output.mp4

# 音视频同步
ffmpeg -i input.mp4 -itsoffset 0.1 -i input.mp4 -map 0:v -map 1:a -c copy output.mp4

流选择与映射

流选择语法

# 选择所有流
ffmpeg -i input.mp4 -map 0 output.mkv

# 选择视频流
ffmpeg -i input.mp4 -map 0:v output.mkv

# 选择音频流
ffmpeg -i input.mp4 -map 0:a output.mkv

# 选择字幕流
ffmpeg -i input.mp4 -map 0:s output.mkv

# 选择特定流
ffmpeg -i input.mp4 -map 0:v:0 -map 0:a:0 output.mkv

# 选择多个特定流
ffmpeg -i input.mp4 -map 0:v:0 -map 0:a:0 -map 0:a:1 output.mkv

流排除

# 排除字幕流
ffmpeg -i input.mkv -map 0 -map -0:s output.mp4

# 排除特定音频流
ffmpeg -i input.mkv -map 0 -map -0:a:1 output.mkv

# 排除所有视频流
ffmpeg -i input.mkv -map 0 -map -0:v output.mp3

多输入流映射

# 从不同输入选择流
ffmpeg -i video.mp4 -i audio.mp3 -i subtitle.srt \
    -map 0:v -map 1:a -map 2:s \
    -c copy output.mkv

# 组合多个输入
ffmpeg -i video1.mp4 -i video2.mp4 \
    -map 0:v -map 0:a -map 1:v -map 1:a \
    -c copy output.mkv

默认流选择

# 设置默认视频流
ffmpeg -i input.mp4 \
    -disposition:v:0 default \
    -c copy output.mkv

# 设置默认音频流
ffmpeg -i input.mp4 \
    -disposition:a:0 default \
    -disposition:a:1 0 \
    -c copy output.mkv

# 设置默认字幕流
ffmpeg -i input.mp4 \
    -disposition:s:0 default \
    -disposition:s:1 0 \
    -c copy output.mkv

流处置(Disposition)

# 查看流处置
ffprobe -v quiet -show_entries stream_disposition input.mkv

# 设置流处置
ffmpeg -i input.mp4 \
    -disposition:v:0 default \
    -disposition:a:0 default \
    -disposition:a:1 0 \
    -disposition:s:0 default \
    -c copy output.mkv

# 处置选项
# default - 默认流
# dub - 配音
# original - 原始
# comment - 评论
# lyrics - 歌词
# karaoke - 卡拉OK
# forced - 强制
# hearing_impaired - 听力障碍
# visual_impaired - 视觉障碍
# clean_effects - 干净效果
# attached_pic - 附带图片
# timed_lyrics - 定时歌词

容器格式转换

无损转换

# MP4 转 MKV
ffmpeg -i input.mp4 -c copy output.mkv

# MKV 转 MP4
ffmpeg -i input.mkv -c copy output.mp4

# AVI 转 MP4
ffmpeg -i input.avi -c copy output.mp4

# 转换并选择流
ffmpeg -i input.mkv -map 0:v -map 0:a:0 -c copy output.mp4

带处理的转换

# 转换并重新编码
ffmpeg -i input.avi -c:v libx264 -c:a aac output.mp4

# 转换并调整质量
ffmpeg -i input.mkv -c:v libx264 -crf 23 -c:a copy output.mp4

# 转换并裁剪
ffmpeg -i input.mp4 -ss 00:01:00 -t 60 -c copy output.mkv

复用选项

MP4 选项

# 快速启动
ffmpeg -i input.mp4 -c copy -movflags +faststart output.mp4

# 分片 MP4
ffmpeg -i input.mp4 -c copy -movflags +frag_keyframe+empty_moov output.mp4

# 碎片化 MP4
ffmpeg -i input.mp4 -c copy -movflags +frag_keyframe+separate_moof output.mp4

# DASH 兼容
ffmpeg -i input.mp4 -c copy -movflags +dash output.mp4

MKV 选项

# 基本 MKV
ffmpeg -i input.mp4 -c copy output.mkv

# 设置默认模式
ffmpeg -i input.mp4 -c copy -default_mode passthrough output.mkv

# 设置语言
ffmpeg -i input.mp4 -c copy -metadata:s:a:0 language=chi output.mkv

MPEG-TS 选项

# 基本 MPEG-TS
ffmpeg -i input.mp4 -c copy output.ts

# 设置 PCR
ffmpeg -i input.mp4 -c copy -mpegts_pcr_period 20 output.ts

# 设置起始时间戳
ffmpeg -i input.mp4 -c copy -mpegts_start_pid 256 output.ts

# 重发头部
ffmpeg -i input.mp4 -c copy -mpegts_flags +resend_headers output.ts

FLV 选项

# 基本 FLV
ffmpeg -i input.mp4 -c copy output.flv

# 无持续时间信息
ffmpeg -i input.mp4 -c copy -flvflags no_duration_filesize output.flv

流信息检测

使用 ffprobe

# 基本信息
ffprobe input.mp4

# JSON 格式
ffprobe -v quiet -print_format json -show_format -show_streams input.mp4

# 仅显示格式
ffprobe -v quiet -show_format input.mp4

# 仅显示流
ffprobe -v quiet -show_streams input.mp4

# 选择特定流
ffprobe -v quiet -select_streams v:0 -show_streams input.mp4

# 显示特定信息
ffprobe -v quiet -show_entries format=duration,size input.mp4

流信息脚本

#!/bin/bash
# show_streams.sh

FILE=$1

echo "=== 文件信息 ==="
echo "格式: $(ffprobe -v quiet -show_entries format=format_name -of csv=p=0 "$FILE")"
echo "时长: $(ffprobe -v quiet -show_entries format=duration -of csv=p=0 "$FILE") 秒"
echo "大小: $(ls -lh "$FILE" | awk '{print $5}')"

echo -e "\n=== 视频流 ==="
ffprobe -v quiet -select_streams v:0 \
    -show_entries stream=codec_name,width,height,r_frame_rate,pix_fmt \
    -of csv=p=0 "$FILE"

echo -e "\n=== 音频流 ==="
ffprobe -v quiet -select_streams a:0 \
    -show_entries stream=codec_name,sample_rate,channels,channel_layout \
    -of csv=p=0 "$FILE"

echo -e "\n=== 字幕流 ==="
ffprobe -v quiet -select_streams s \
    -show_entries stream=codec_name,codec_type \
    -of csv=p=0 "$FILE"

高级用法

流复制与重编码混合

# 复制视频流,重新编码音频流
ffmpeg -i input.mp4 -c:v copy -c:a aac -b:a 192k output.mp4

# 重新编码视频流,复制音频流
ffmpeg -i input.mp4 -c:v libx264 -crf 23 -c:a copy output.mp4

# 不同流使用不同编解码器
ffmpeg -i input.mkv -c:v libx264 -c:a aac -c:s mov_text output.mp4

流过滤

# 仅保留视频流
ffmpeg -i input.mp4 -vn output.mp3

# 仅保留音频流
ffmpeg -i input.mp4 -an output.mp4

# 仅保留字幕流
ffmpeg -i input.mkv -sn output.srt

流复制与滤镜混合

# 视频使用滤镜,音频直接复制
ffmpeg -i input.mp4 -vf "scale=1280:720" -c:v libx264 -c:a copy output.mp4

# 音频使用滤镜,视频直接复制
ffmpeg -i input.mp4 -af "volume=2.0" -c:v copy -c:a aac output.mp4

批量处理

批量添加音轨

#!/bin/bash
# batch_add_audio.sh

VIDEO_DIR=$1
AUDIO_DIR=$2
OUTPUT_DIR=${3:-output}

mkdir -p "$OUTPUT_DIR"

for video in "$VIDEO_DIR"/*.mp4; do
    filename=$(basename "$video" .mp4)
    audio="$AUDIO_DIR/${filename}.mp3"
    
    if [ -f "$audio" ]; then
        echo "添加音轨: $filename"
        ffmpeg -y -i "$video" -i "$audio" \
            -map 0:v -map 1:a \
            -c:v copy -c:a aac \
            "$OUTPUT_DIR/$filename.mp4"
    else
        echo "未找到音轨: $filename"
    fi
done

批量添加章节

#!/bin/bash
# batch_add_chapters.sh

INPUT_DIR=$1
CHAPTERS_FILE=$2
OUTPUT_DIR=${3:-output}

mkdir -p "$OUTPUT_DIR"

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file")
    output="$OUTPUT_DIR/$filename"
    
    echo "添加章节: $filename"
    ffmpeg -y -i "$file" -i "$CHAPTERS_FILE" \
        -map_metadata 1 -c copy "$output"
done

批量提取元数据

#!/bin/bash
# batch_extract_metadata.sh

INPUT_DIR=${1:-.}
OUTPUT_FILE=${2:-metadata.csv}

echo "文件名,标题,艺术家,日期,时长" > "$OUTPUT_FILE"

for file in "$INPUT_DIR"/*.mp4; do
    filename=$(basename "$file")
    title=$(ffprobe -v quiet -show_entries format_tags=title -of csv=p=0 "$file")
    artist=$(ffprobe -v quiet -show_entries format_tags=artist -of csv=p=0 "$file")
    date=$(ffprobe -v quiet -show_entries format_tags=date -of csv=p=0 "$file")
    duration=$(ffprobe -v quiet -show_entries format=duration -of csv=p=0 "$file")
    
    echo "$filename,$title,$artist,$date,$duration" >> "$OUTPUT_FILE"
done

echo "元数据提取完成: $OUTPUT_FILE"

注意事项

  1. 格式兼容性:不同容器支持的编解码器不同
  2. 流映射:使用 -map 明确指定流,避免自动选择问题
  3. 时间戳:转换格式时注意时间戳处理
  4. 元数据编码:确保元数据使用正确的字符编码
  5. 章节格式:不同容器支持不同的章节格式

业务场景

场景 1:蓝光光盘备份

# 提取主视频和音轨
ffmpeg -i bluray.iso \
    -map 0:v:0 -map 0:a:0 -map 0:a:1 \
    -c:v copy -c:a copy \
    -metadata:s:a:0 language=jpn \
    -metadata:s:a:1 language=chi \
    output.mkv

场景 2:多语言视频制作

# 合并多语言音轨
ffmpeg -i video.mp4 -i audio_cn.mp3 -i audio_en.mp3 -i audio_jp.mp3 \
    -map 0:v -map 1:a -map 2:a -map 3:a \
    -c:v copy -c:a aac \
    -metadata:s:a:0 language=chi \
    -metadata:s:a:1 language=eng \
    -metadata:s:a:2 language=jpn \
    -disposition:a:0 default \
    output.mkv

场景 3:视频章节制作

# 创建章节文件
cat > chapters.txt << 'EOF'
;FFMETADATA1

[CHAPTER]
TIMEBASE=1/1000
START=0
END=120000
title=Opening

[CHAPTER]
TIMEBASE=1/1000
START=120000
END=300000
title=Part 1

[CHAPTER]
TIMEBASE=1/1000
START=300000
END=480000
title=Part 2

[CHAPTER]
TIMEBASE=1/1000
START=480000
END=600000
title=Ending
EOF

# 添加章节
ffmpeg -i input.mp4 -i chapters.txt -map_metadata 1 -c copy output.mp4

扩展阅读

  1. FFmpeg 格式文档
  2. FFmpeg 元数据
  3. FFmpeg 章节
  4. Matroska 规范
  5. MP4 规范

总结

本章介绍了 FFmpeg 的复用与解复用功能,包括:

  • 多音轨处理
  • 章节编辑
  • 元数据管理
  • 时间戳处理
  • 流选择与映射

掌握这些功能可以帮助您更好地组织和管理多媒体文件。