欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于FFmpeg源码分析TS数据格式的解析

程序员文章站 2022-07-14 20:02:01
...

原因:前面简单分析了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.

基于FFmpeg源码分析TS数据格式的解析

此时通过解析获取出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源码分析TS数据格式的解析

接下里通过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文件。

基于FFmpeg源码分析TS数据格式的解析基于FFmpeg源码分析TS数据格式的解析

总结:通过ts的协议分析以及FFmpeg的源码实现。可以很清楚的看出ts流的解析,