基于FFmpeg源码分析TS数据格式的解析
原因:前面简单分析了FFmpeg针对HLS协议和m3u8文件的简单调用,但是TS数据流的解析却没有介绍,故在此介绍一下TS流的数据格式及解析.
概况:ts(transport stream)可以简单理解为传输文件,内部封装pes(packet elemental stream),而pes内部封装es(elemental stream)数据,而我们用于解码的数据即原始的es数据。可以简单理解为pes比pes多了一些数据头,而数据头内部包含了pts和dts等信息。故重点就是如何找到es数据的起始地址。
流程介绍:为了想要找到es数据,首先需要了解PAT表和PMT表,PAT(program association table)节目关联表:内部包含 TS流中所有的节目。PMT(program map table)节目映射表:内部包含对应节目中包含的音视频数据流描述信息。那么如何关联PAT和PMT以及真实数据流?那么就使用到了PID。
TS包类型解析如下:总共占用4个字节.重点就是获取pid数据,通过获取的pid可以得ts包类型.
typedef struct transport_packet
{
//占用8bit,同步字节,默认为0x47
unsigned sync_byte : 8;
//占用1bit,传输错误指示位,1:表示传输包中至少有一个不可纠正的错误位,一般为0.
unsigned transport_error_indicator : 1;
//占用1bit,负载单元起始标识位,1:表示起始数据 0:表示累加数据.一个TS包固定为188字节,而一帧数据将会被分割为多个包,故通过起始标识符可以得知是否为新的数据帧.
unsigned payload_unit_start_indicator : 1;
//占用1bit,传输优先级
unsigned transport_priority : 1;
//占用13bit,pid
unsigned PID : 13;
//占用2bit,加密标识.一般为00
unsigned transport_scrambling_control : 2;
//占用2bit,负载自适应字节标识符.00:保留值,01:负载中只有有效载荷.10:负载中只有自适应字段.11:负载中既包含有效载荷有包含自适应字段.
unsigned adaptation_field_control : 2;
//占用4bit,连续计数器.随着相同pid的ts包增加而增加.可用于重复包的丢弃.
unsigned continuity_counter : 4;
if (adaptaion_field_control == '10' || adaptation_field_control == '11')
{
adaptation_field()
}
if (adaptaion_field_control == '01' || adaptation_field_control == '11')
{
for (i = 0; i < N; i++)
{
unsigned data_byte : 8;
}
}
}transport_packet;
下面的表格介绍了PID的对应关系,故重点就是PAT的pid值为0000.
此时通过解析获取出pid,如果pid的值对应的是PAT,则接下来分析PAT的协议如下:重点获取到PMT个数和PMT对应的PID信息.
typedef struct program_association_section
{
//table_id:默认00
unsigned table_id : 8;
//默认1
unsigned section_syntax_indicator : 1;
//默认0
'0';
unsigned reserved : 2;
unsigned section_length : 12;
unsigned transport_stream_id : 16;
unsigned reserved : 2;
unsigned version_number : 5;
unsigned current_next_indicator : 1;
unsigned section_number : 8;
unsigned last_secion_number : 8;
for (i = 0; i < N; i++)
{
unsigned program_number : 16;//PMT个数
unsigned reserved : 3;
if (program_number == '0')
{
network_PID;
}
else {
unsigned program_map_PID:13 //PMT的pid
}
}
unsigned CRC_32 : 32;
}program_association_section;
接下来通过获取的PMT的pid信息,则找到对应的ts包进行数据分析如下:此时的重点为获取对应的stream_type和pid.则通过找到的pid获取对应的数据流.
接下里通过FFmpeg源码进行分析.主要实现文件为mpegts.c,可以看出通过读取ts数据包分别获取对应的pat,pmt,然后通过pmt获取到对应的音视频pid,通过音视频的pid来获取pes中的pts和dts数据。然后获取原始的es数据。
static int mpegts_read_header(AVFormatContext *s)
{
mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1);
}
static void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
{
mpegts_open_section_filter(ts, pmt_pid, pmt_cb, ts, 1);
}
static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
{
pes = add_pes_stream(ts, pid, pcr_pid);
}
/* return non zero if a packet could be constructed */
static int mpegts_push_data(MpegTSFilter *filter,
const uint8_t *buf, int buf_size, int is_start,
int64_t pos)
{
while (buf_size > 0) {
switch (pes->state) {
case MPEGTS_HEADER:
break;
}
}
通过av_read_frame获取到原始的es数据流对比可以看出.和ts原始数据缺少了ts和pes的头.左边为.ts文件,右边为.264文件。
总结:通过ts的协议分析以及FFmpeg的源码实现。可以很清楚的看出ts流的解析,