FFmpeg解复用过程详解
1.解复用概述
解复用(Demux)是多媒体处理中的重要步骤,主要负责将封装格式(如MP4、MKV等)中的音视频流分离出来。FFmpeg提供了完整的解复用API,可以处理各种常见的媒体格式。
2.核心API调用流程
3.详细步骤解析
3.1 函数介绍
avformat_open_input
int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options);
- 功能: 打开媒体文件并初始化AVFormatContext
- 参数:
ps
: 指向AVFormatContext指针的指针url
: 媒体文件路径fmt
: 指定输入格式,通常为NULL以自动检测options
: 额外的选项字典
- 返回值: 成功返回0,失败返回负值错误码
- 示例:
AVFormatContext *fmt_ctx = NULL;
if (avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL) < 0) {
fprintf(stderr, "无法打开文件\n");
return -1;
}
avformat_find_stream_info
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
- 功能: 读取媒体文件数据包以获取流信息
- 参数:
ic
: AVFormatContext指针options
: 额外的选项字典
- 返回值: 成功返回0,失败返回负值错误码
- 示例:
if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
fprintf(stderr, "无法获取流信息\n");
return -1;
}
av_find_best_stream
int av_find_best_stream(AVFormatContext *ic, enum AVMediaType type, int wanted_stream_nb, int related_stream, AVCodec **decoder_ret, int flags);
- 功能: 查找指定类型的最佳流
- 参数:
ic
: AVFormatContext指针type
: 流类型(如AVMEDIA_TYPE_VIDEO, AVMEDIA_TYPE_AUDIO)wanted_stream_nb
: 期望的流索引,-1表示自动选择related_stream
: 相关流索引,-1表示无decoder_ret
: 返回解码器指针flags
: 标志位
- 返回值: 成功返回流索引,失败返回负值错误码
- 示例:
int video_stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if (video_stream_index < 0) {
fprintf(stderr, "未找到视频流\n");
return -1;
}
av_packet_alloc
AVPacket *av_packet_alloc(void);
- 功能: 分配AVPacket
- 返回值: 成功返回AVPacket指针,失败返回NULL
- 示例:
AVPacket *pkt = av_packet_alloc();
if (!pkt) {
fprintf(stderr, "无法分配数据包\n");
return -1;
}
av_read_frame
int av_read_frame(AVFormatContext *s, AVPacket *pkt);
- 功能: 从媒体文件中读取一个数据包
- 参数:
s
: AVFormatContext指针pkt
: AVPacket指针
- 返回值: 成功返回0,失败返回负值错误码
- 示例:
while (av_read_frame(fmt_ctx, pkt) >= 0) {
if (pkt->stream_index == video_stream_index) {
// 处理视频数据包
}
av_packet_unref(pkt);
}
av_packet_free
void av_packet_free(AVPacket **pkt);
- 功能: 释放AVPacket
- 参数:
pkt
: 指向AVPacket指针的指针
- 示例:
av_packet_free(&pkt);
avformat_close_input
void avformat_close_input(AVFormatContext **s);
- 功能: 关闭输入文件并释放相关资源
- 参数:
s
: 指向AVFormatContext指针的指针
- 示例:
avformat_close_input(&fmt_ctx);
3.2 完整示例流程
AVFormatContext *fmt_ctx = NULL;
AVPacket *pkt = NULL;
// 打开文件
if (avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL) < 0) {
fprintf(stderr, "无法打开文件\n");
return -1;
}
// 获取流信息
if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {
fprintf(stderr, "无法获取流信息\n");
return -1;
}
// 查找视频流
int video_stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if (video_stream_index < 0) {
fprintf(stderr, "未找到视频流\n");
return -1;
}
// 分配数据包
pkt = av_packet_alloc();
if (!pkt) {
fprintf(stderr, "无法分配数据包\n");
return -1;
}
// 读取并处理数据包
while (av_read_frame(fmt_ctx, pkt) >= 0) {
if (pkt->stream_index == video_stream_index) {
// 处理视频数据包
}
av_packet_unref(pkt);
}
// 清理
av_packet_free(&pkt);
avformat_close_input(&fmt_ctx);
4.容器介绍
在我们开始学习FFmpeg之前,我们先来了解一下容器的概念。说到容器大家一定会想到水杯、箱子、罐子等,这些都是容器的例子。在计算机领域,容器也是一个类似的概念。
在计算机领域,容器是一种用于存储和传输多媒体数据的文件格式。容器可以包含多种类型的数据流,如视频流、音频流、字幕流等。常见的容器格式包括MP4、MKV、AVI、FLV等。容器本身并不包含实际的媒体数据,而是包含了媒体数据的元数据和索引信息。实际的媒体数据通常是经过编码的,例如视频数据可能是H.264编码的,音频数据可能是AAC编码的。容器的作用是将这些编码后的数据流组合在一起,并提供必要的元数据和索引信息,以便播放器能够正确解码和播放这些数据。
4.1MP4介绍
MP4(MPEG-4 Part 14)是一种常见的多媒体容器格式,广泛用于存储和传输视频、音频和其他多媒体数据。MP4容器的设计目的是提供高效的数据存储和传输,同时支持多种编码格式。MP4容器的结构相对复杂,包含了多个层次的元数据和索引信息,以便播放器能够快速定位和解码媒体数据。MP4容器的设计考虑了多种应用场景,如流媒体、存储和传输,因此具有较高的兼容性和灵活性。MP4容器的广泛应用使其成为多媒体数据存储和传输的重要格式。
4.2MP4文件结构
MP4文件结构详解
1.主要Box及其作用
1.1 MP4
- 全程: MP4 File Structure
- 作用: MP4文件的根容器,包含所有其他Box。
1.2 FTYP
- 全程: File Type Box
- 作用: 描述文件的类型和兼容性。
1.3 MOOV
- 全程: Movie Box
- 作用: 包含电影的元数据,如音频信息、视频信息、时间信息、轨道信息等。
1.4 MDAT
- 全程: Media Data Box
- 作用: 存储实际的媒体数据,如视频帧和音频帧。音视频数据是以chuck为单位存储的,每个chuck包含多个sample。存储的位置可以通过stco获取
2.MOOV Box的子Box
2.1 MVHD
- 全程: Movie Header Box
- 作用: 包含电影的全局信息,如时间刻度、时长等。
2.2 TRAK
- 全程: Track Box
- 作用: 描述一个轨道的信息,如视频轨道或音频轨道。
3.TRAK Box的子Box
3.1 TKHD
- 全程: Track Header Box
- 作用: 包含轨道的头信息,如轨道ID、时长、音量、视频宽高等。
3.2 MDIA
- 全程: Media Box
- 作用: 包含媒体的信息,如媒体类型、媒体头等。
4.MDIA Box的子Box
4.1 MDHD
- 全程: Media Header Box
- 作用: 包含媒体的头信息,如时间刻度、时长等。这个时长指的是改轨道的时长
4.2 HDLR
- 全程: Handler Reference Box
- 作用: 描述媒体的处理程序,如视频处理程序或音频处理程序。
4.3 MINF
- 全程: Media Information Box
- 作用: 包含媒体的详细信息,如视频媒体信息或音频媒体信息。
5.MINF Box的子Box
5.1 VMHD
- 全程: Video Media Header Box
- 作用: 包含视频媒体的头信息,如图形模式等。
5.2 SMHD
- 全程: Sound Media Header Box
- 作用: 包含音频媒体的头信息,如平衡等。
5.3 DINF
- 全程: Data Information Box
- 作用: 包含数据的信息,如数据引用等。
5.4 STBL
- 全程: Sample Table Box
- 作用: 包含样本表的信息,如样本描述、时间戳等。
6.STBL Box的子Box
6.1 STSD
- 全程: Sample Description Box
- 作用: 包含样本的描述信息,如编码类型等。
6.2 STTS
- 全程: Time-to-Sample Box
- 作用: 包含样本的sample个数与每个sample持续的时间。已time_scale为单位
6.3 STSC
- 全程: Sample-to-Chunk Box
- 作用: 描述样本到块的映射关系。
6.4 STSZ
- 全程: Sample Size Box
- 作用: 包含样本的大小信息。
6.5 STCO
- 全程: Chunk Offset Box
- 作用: 包含块的偏移量信息。
-
6.6 STSS
- 全程: Sync Sample Box
- 作用: 包含关键帧在哪个Chuck的信息。
-
6.7 CTTX
- 全程: Composition Time to Sample Box
- 作用: 用于描述显示时间戳(PTS)和解码时间戳(DTS)之间的关系。它的主要作用是处理B帧(双向预测帧)的时间戳问题。
MP4数据存储为什么用chuck为单位,而不是使用sample为单位?
1.1 提高数据读取效率
- 减少I/O操作:将多个
sample
打包成一个chunk
,可以减少磁盘I/O操作的次数。读取一个chunk
(包含多个sample
)比逐个读取sample
更高效。 - 连续存储:
chunk
的设计使得数据在磁盘上连续存储,减少了文件碎片化,从而提高了读取效率。
1.2 优化随机访问
- 快速定位:通过
STCO
(Chunk Offset Box)可以快速定位到某个chunk
的起始位置,结合STSC
(Sample-to-Chunk Box)可以快速找到目标sample
。 - 关键帧定位:
STSS
(Sync Sample Box)记录了关键帧所在的chunk
,便于实现视频的随机访问和快速定位。
1.3 简化数据管理
- 减少元数据开销:将多个
sample
打包成一个chunk
,可以减少元数据的存储开销。例如,STCO
只需要记录chunk
的偏移量,而不需要为每个sample
单独记录偏移量。 - 便于流媒体传输:
chunk
的设计使得流媒体传输更加高效,可以按chunk
为单位进行传输,而不是逐个sample
传输。
1.4 优化存储空间
- 压缩效率:将多个
sample
打包成一个chunk
,可以提高数据的压缩效率,减少存储空间的占用。 - 减少冗余:
chunk
的设计可以减少文件中的冗余数据,使得文件更加紧凑。
ts文件结构详解
ts包可以包含多种不同的table,比如:pat、pmt、sdt、nit、tot等。这些table大多数是与DTMB有关我们不做重点分析。
ts包也可以包含多媒体数据,比如:视频、音频、字幕等。这些数据以ES(Encrypted Stream)的形式存储在pes包中,在由ts包中进行封装。结构如下图
从上图中我们可以看出音视频数据会被编码器封装为ES结构,然后通过PES打包器生成为PES结构,在通过TS打包器封装为TS包。
1.1 TS包结构
以ts为后缀名字的文件通常是TS文件。TS文件是由一个个TS包组成的。TS包是有固定大小的,一般为188字节。TS包的结构如下:
可以看一个TS包是固定字节188,并且由TS包头、TS包负载、CRC组成。我们关注的多媒体数据就在这个TS包的负载中。
1.2 PES包结构
PES包是TS包负载中包含的多媒体数据,PES包的结构如下:
PES由包起始码前缀、流id、PES长度、PES头、PES数据组成。PES数据数据包含的就是ES数据了。
1.3 如何查找到PES包(查找指定的audio/video数据)
1.3.1 问题引入
大家可能会有疑问:既然可以通过PES包的起始码前缀判断是否为PES包,为什么还需要专门讨论如何查找指定的PES包呢?要回答这个问题,我们需要先了解TS流在DTMB(地面数字多媒体广播)中的应用场景。
1.3.2 DTMB背景介绍
DTMB(GB 20600-2006,全称Digital Terrestrial Multimedia Broadcast,即地面数字多媒体广播),原名DMB-T/H(Digital Multimedia Broadcast-Terrestrial/Handheld,即数字多媒体广播-地面/手持),是中国制定的数字电视和流动数字广播标准。简单来说,DTMB就是我们以前使用的广播电视系统。
1.3.3 TS流的多路复用特性
在DTMB中,一个频点下可能包含多个频道,每个频道又包含多个节目,而每个节目可能包含多个流(如音频流、视频流、字幕流等)。因此,接收到的TS流实际上是多个频道数据的混合体,其中包含了大量不同频道的PES包。
1.3.4 如何查找指定的PES包?
为了从混合的TS流中提取出我们需要的PES包,需要使用PID(Packet Identifier)。PID是在PMT(Program Map Table)中定义的,用于标识不同的媒体流。例如:
一个节目可能包含一个音频流、一个视频流和一个字幕流,分别对应三个不同的PID。
在接收TS流时,通过PID可以快速定位到目标TS包,进而提取出对应的PES包。
1.3.5 解复用过程
解复用的核心步骤如下:
根据PID过滤TS包:从混合的TS流中提取出目标PID对应的TS包。
合并TS包:由于音视频帧的数据量较大,通常会被分割成多个TS包传输,因此需要将这些TS包合并成一个完整的PES包。
提取PES包:从合并后的TS包中剥离出PES包,供后续解码使用。
6. FFmpeg的简化处理
在使用FFmpeg时,解复用过程已经被封装在API中。例如,av_read_frame函数可以直接返回一帧完整的可解码数据,无需手动处理TS包的合并和PES包的提取。
TS与MP4的区别与使用场景
1. TS格式的特点与使用场景
定义与用途
TS格式主要用于流媒体传输,如数字电视广播、网络直播和卫星电视等场景。它支持多路复用、错误恢复和时间戳等功能,适合在带宽有限的情况下进行高效传输。
技术特点
- TS文件由多个固定大小的包组成(每个包188字节),每个包包含媒体元数据,这使得TS文件即使损坏也能部分播放。
- TS格式支持边下载边播放,具有较好的容错能力。
- TS文件通常使用MPEG-2编码,但也可以支持其他编码格式。
兼容性
TS格式的兼容性较差,许多现代设备(如iPhone、Android手机)不支持直接播放TS文件,需要额外的软件或插件。
适用场景
TS格式广泛应用于广播和流媒体传输领域,例如HLS(HTTP Live Streaming)协议中常使用TS作为容器格式。
2. MP4格式的特点与使用场景
定义与用途
MP4是一种通用的多媒体容器格式,适用于存储各种类型的媒体数据,包括视频、音频、字幕和图像等。
技术特点
- MP4文件是基于文件的,所有媒体信息存储在一个文件中,便于离线播放。
- MP4支持多种编码格式,如H.264、H.265、AAC等,且文件体积相对较小。
- MP4格式的索引信息存储在文件尾部,这可能导致部分文件无法在浏览器中直接播放。
兼容性
MP4格式的兼容性极强,几乎所有平台和设备都支持MP4文件,包括PC、手机、平板电脑和流媒体设备。
适用场景
MP4格式适用于个人视频制作、网络视频分享、电影存储和在线流媒体服务等场景。
3. TS与MP4的主要区别
特性 | TS格式 | MP4格式 |
---|---|---|
用途 | 主要用于流媒体传输(如广播、直播)。 | 通用多媒体存储和播放(如个人视频、电影、网络视频)。 |
技术特点 | 支持多路复用、错误恢复、时间戳;包大小固定(188字节)。 | 文件式存储,索引信息在文件尾部;支持多种编码格式。 |
兼容性 | 兼容性较差,需额外软件支持。 | 兼容性极强,几乎所有平台和设备支持。 |
适用场景 | 广播、流媒体传输(如HLS协议)。 | 个人视频制作、网络视频分享、电影存储等。 |
文件大小 | 文件较大,占用更多空间。 | 文件较小,体积更高效。 |
播放方式 | 支持边下载边播放,适合网络传输。 | 文件加载完成后才能播放。 |
4. 总结
TS格式更适合流媒体传输和广播场景,具有高效的传输能力和较强的容错能力,但兼容性较差;而MP4格式则更通用,适用于个人视频制作和离线播放,兼容性极强。根据具体需求选择合适的容器格式是关键。