FFmpeg 多媒体处理教程 / 复用与解复用
复用与解复用
概述
复用(Muxing)是将多个媒体流(视频、音频、字幕等)打包到一个容器文件中的过程。解复用(Demuxing)则是从容器文件中提取各个媒体流的过程。本章详细介绍 FFmpeg 的复用与解复用功能。
复用基础
复用流程
视频流 ──┐
音频流 ──┼──► 复用器 (Muxer) ──► 容器文件
字幕流 ──┘
解复用流程
容器文件 ──► 解复用器 (Demuxer) ──┬──► 视频流
├──► 音频流
└──► 字幕流
常用复用器
| 复用器 | 格式 | 说明 |
|---|
| mp4 | MP4 | 最通用格式 |
| matroska | MKV | 开放格式 |
| flv | FLV | 流媒体格式 |
| mpegts | MPEG-TS | 传输流 |
| webm | WebM | Web 格式 |
| avi | AVI | 传统格式 |
| mov | MOV | Apple 格式 |
| ogg | OGG | 开源格式 |
常用解复用器
| 解复用器 | 格式 | 说明 |
|---|
| mov,mp4,m4a,3gp | MP4 系列 | 自动检测 |
| matroska,webm | MKV/WebM | 自动检测 |
| flv | FLV | Flash 视频 |
| mpegts | MPEG-TS | 传输流 |
| avi | AVI | 传统格式 |
| ogg | OGG | 开源格式 |
多音轨处理
添加多音轨
# 添加两个音轨
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
章节处理
章节格式
;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"
注意事项
- 格式兼容性:不同容器支持的编解码器不同
- 流映射:使用
-map 明确指定流,避免自动选择问题 - 时间戳:转换格式时注意时间戳处理
- 元数据编码:确保元数据使用正确的字符编码
- 章节格式:不同容器支持不同的章节格式
业务场景
场景 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
扩展阅读
- FFmpeg 格式文档
- FFmpeg 元数据
- FFmpeg 章节
- Matroska 规范
- MP4 规范
总结
本章介绍了 FFmpeg 的复用与解复用功能,包括:
- 多音轨处理
- 章节编辑
- 元数据管理
- 时间戳处理
- 流选择与映射
掌握这些功能可以帮助您更好地组织和管理多媒体文件。