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

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

章节处理

章节格式

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 的复用与解复用功能,包括:

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

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