TS码流解析-2-获取完整的TABLE
任务二
根据指定的table id 和 pid 获取完整的table。这是比较重要的一个环节,后面涉及到相关表的解析,都需要用到这里的函数。
相关知识
1.首先了解TS数据包包头的结构,在这里贴一张ts包头的结构图
各字段的解释:
# | 标识 | 位数 | 说明 |
---|---|---|---|
0 | sync_byte | 8 bits | 同步字节,固定是0x47 |
1 | transport_error_indicator | 1 bits | 错误指示信息(1:该包至少有1bits传输错误) |
2 | payload_unit_start_indicator | 1 bits | 负载单元开始标志(packet不满188字节时需填充) |
3 | transport_priority | 1 bits | 传输优先级标志(1:优先级高) |
4 | PID | 13 bits | Packet ID号码,唯一的号码对应不同的包 |
5 | transport_scrambling_control | 2 bits | 加密标志(00:未加密;其他表示已加密) |
6 | adaptation_field_control | 2 bits | 附加区域控制 |
7 | continuity_counter | 4 bits | 包递增计数器 |
在目前阶段,我们主要关注的字段有
payload_unit_start_indicator->用于判断是否为一个section的开始,值为1表示section开头,0表示后续的section数据
PID ->用于判断是否为我们需要的ts包
adaptation_field_control ->用于判断是否有效载荷的开始位置
值 | 描述 |
---|---|
00 | 保留,为将来ISO/IEC使用 |
01 | 没有自适应域,仅仅只有有效载荷 |
10 | 仅有自适应域,没有有效载荷 |
11 | 自适应域之后紧跟着有效载荷 |
2.了解section的头部结构,在这里同样也贴一张section头部信息结构图
# | 字段名 | 占位 | 说明 |
---|---|---|---|
0 | table_id | 8 bits | 用于标识是什么table |
1 | section_syntax_indicator | 1 bit | 段语法标志位,固定为1 |
2 | reserved_future_use | 1 bit | 保留 |
3 | reserved | 2 bits | 保留 |
4 | section_length | 12 bits | 标识section的长度,不包括前三个字节 |
5 | transport_stream_id | 16 bits | TS的识别号 |
6 | reserved | 2 bits | 保留 |
7 | version_number | 5 bits | 一旦TABLE有变化,版本号加1 |
8 | current_next_indicator | 1 bit | 当前传送的表可以使用,若为0则要等待下一个表 |
9 | section_number | 8 bits | 给出section号,在sub_table中,第一个section其section_number为"0x00",每增加一个section,section_number加一 |
10 | last_section_number | 8 bits | sub_table中最后一个section的section_number |
注意:这里给的字段以及section头的在所有表中前8个字节是适用的,不管是PAT还是PMT等,前八个字节都是一样的,命名可能不一样,但是大小是一样的。
同样,在这里我们重点关注的有:
table_id->表类型的唯一标识符
section_length->section的长度
version_number->版本号,变化时候,意味着table的内容发生改变
section_number->section的编号
last_section_number->最后一个section的编号
3.package、section、table之间的关系
简单说,就是数据(table)在传输之前,被切分成一段一段的数据(section),然后段之前加上标识,接着再进行切分,并且加上流的标识符(ts package),这时候就变成了一个个固定包长的流,然后我们就通过该流进行传输。接受方收到数据后,按照相关的规则把它们拼接起来,就可以看到原先的样子了。
关系与区别:
- 三者之中,只有TS package是固定长度的,section和table的长度都是不固定的
- 一个section可能包含一个或者多个TS package
- 一个table可能包含一个或者多个section,section之间用section number区分
- 一个EIT section的最大长度是4096字节,其他的section最大长度是1024字节
解析过程
1 数据结构的定义
#define MAX_SECTION_LENGTH 4096
typedef struct
{
unsigned int table_id :8;
unsigned int section_syntax_indicator :1;
unsigned int zero :1;
unsigned int reserved1 :2;
unsigned int section_length :12;
unsigned int service_id :16;
unsigned int reserved2 :2;
unsigned int version_number :5;
unsigned int current_next_indicator :1;
unsigned int section_number :8;
unsigned int last_section_number :8;
}TS_SECTION_HEAD;
typedef struct section
{
unsigned char section_buffer[MAX_SECTION_LENGTH];
unsigned int section_length;
unsigned int section_number;
struct section *next;
}SECTION;
typedef int(*TABLE_CHECK)(unsigned char table_id);
2 获取对应的section
1 流程图
2 相关代码
//获取有效载荷的开始位置
static unsigned int get_section_start(TS_PACKAGE_HEAD ts_package_head, unsigned char *package_buffer)
{
int section_start_position = 0;
switch (ts_package_head.adaptation_field_control)
{
case 0:
break;
case 1:
section_start_position = 4;
break;
case 2:
break;
case 3:
/*---------------------------------------------------------*/
/* ts head(4) + adaptation_field_length(1) = 5 */
/* package_buffer[4] is the length of adaptation_field */
/*---------------------------------------------------------*/
section_start_position = 5 + package_buffer[4];
//adaptation_field_length is the length after package_buffer[4]
break;
}
if (1 == ts_package_head.payload_unit_start_indicator)
{
section_start_position += package_buffer[section_start_position] + 1;
//why? look the capture picture after the code
}
return section_start_position;
}
//把section 的头数据赋值到对应的结构体
static void get_section_head(TS_SECTION_HEAD *ts_section_head, unsigned int section_start_position, unsigned char *package_buffer)
{
ts_section_head->table_id = package_buffer[section_start_position];
ts_section_head->section_syntax_indicator = package_buffer[section_start_position + 1] >> 7;
ts_section_head->zero = (package_buffer[section_start_position + 1] >> 6) & 0x1;
ts_section_head->reserved1 = (package_buffer[section_start_position + 1] >> 4) & 0x3;
ts_section_head->section_length = ((package_buffer[section_start_position + 1] & 0xf) << 8) | package_buffer[section_start_position + 2];
ts_section_head->service_id = (package_buffer[section_start_position + 3] << 8) | package_buffer[section_start_position + 4];
ts_section_head->reserved2 = (package_buffer[section_start_position + 5] >> 6) & 0x3;
ts_section_head->version_number = (package_buffer[section_start_position + 5] >> 1) & 0x1f;
ts_section_head->section_syntax_indicator = package_buffer[section_start_position + 5] >> 7;
ts_section_head->section_number = package_buffer[section_start_position + 6];
ts_section_head->last_section_number = package_buffer[section_start_position + 7];
}
//把有效载荷的数据写入到section
static int write_load_data_to_section(unsigned char *section_buffer, unsigned char *package_buffer, int *section_writed_length,
unsigned int package_length, unsigned int section_start_position, TS_SECTION_HEAD *ts_section_head)
{
int copy_size = 0;
int different_length = 0;
int end_flag = 0;
int remain = 0;
if (MAX_TS_PACKAGE_LEN == package_length)
{
different_length = 16;
}
copy_size = package_length - section_start_position - different_length;
remain = ts_section_head->section_length + 3 - *section_writed_length;
if (copy_size >= remain)
{
copy_size = remain;
end_flag = 1;
}
memcpy(section_buffer + *section_writed_length, package_buffer + section_start_position, copy_size);
*section_writed_length += copy_size;
return end_flag;
}
//获取section的函数
static unsigned int get_section(FILE *ts_file, unsigned int package_length, unsigned char table_id, unsigned short input_pid,
unsigned char *section_buffer, TS_SECTION_HEAD *ts_section_head)
{
unsigned char package_buffer[MAX_TS_PACKAGE_LEN] = {0};
unsigned int section_writed_length = 0;
unsigned int section_start_flag = 0;
unsigned int section_start_position = 0;
TS_PACKAGE_HEAD ts_package_head = {0};
input_pid &= 0x1fff; //set the hight three bit 0,in case for compare with package pid
while (package_length == fread(package_buffer, 1, package_length, ts_file))
{
get_package_head(package_buffer, &ts_package_head);
if ((input_pid!=ts_package_head.pid) || (SYNC_BYTE!=ts_package_head.sync_byte))
{
continue;
}
section_start_position = get_section_start(ts_package_head, package_buffer);
if (1 == ts_package_head.payload_unit_start_indicator)
{
if ((0==section_start_position) || (section_start_position>package_length))
{
log("section start position error!!\n");
continue;
}
get_section_head(ts_section_head, section_start_position, package_buffer);
if (table_id != ts_section_head->table_id)
{
continue;
}
if (1 == write_load_data_to_section(section_buffer, package_buffer, §ion_writed_length, package_length, section_start_position, ts_section_head))
{
log("success get one section!!\n");
return section_writed_length;
}
section_start_flag = 1;
continue;
}
if (1 == section_start_flag)
{
if (1 == write_load_data_to_section(section_buffer, package_buffer, §ion_writed_length, package_length, section_start_position, ts_section_head))
{
log("success get one section!!\n");
return section_writed_length;
}
}
}
return section_writed_length;
}
注意:获取到的section_number的值并不是从0开始的,这是因为单向传输的特点导致的,我们并不知道接受方什么时候开始接受,我们只能不停的发送数据包。
对于payload_unit_start_indicator为1时的运算解释,看图
pointer_field的解释
3 将section拼接成table
1流程图
2 相关代码
//把section加入到链表
static SECTION *add_section_to_link(SECTION *section_head, SECTION section, unsigned int *section_count, unsigned int *section_array, unsigned char section_number)
{
SECTION *new_node = NULL;
if (1 == section_array[section_number])
{
return section_head;;
}
if (0 == section.section_length)
{
log("section length is zero error!\n");
return section_head;
}
new_node = (SECTION*)malloc(sizeof(SECTION));
if (NULL == new_node)
{
log("malloc memory error!\n");
return section_head;
}
new_node->section_number = section.section_number;
new_node->section_length = section.section_length;
memset(new_node->section_buffer, 0, MAX_SECTION_LENGTH);
memcpy(new_node->section_buffer, section.section_buffer, section.section_length);
//head insert
new_node->next = section_head;
(*section_count)++;
section_array[section_number] = 1;
return new_node;
}
//判断是否为EIT表
int eit_check(unsigned char table_id)
{
if ((table_id<0x4E) && (table_id>0x6F))
{
return 1;
}
return 0;
}
//当version_number改变时执行的操作
static int version_number_change(TS_SECTION_HEAD ts_section_head, unsigned char *version_number)
{
if (INIT_VERSION_NUMBER == *version_number)
{
*version_number = ts_section_head.version_number;
}
if (ts_section_head.version_number != *version_number)
{
//log("version_number change!!!\n");
return 1;
}
return 0;
}
//创建section的链表,也就是创建一个table
SECTION *create_section_link(FILE *ts_file, unsigned int package_length, int first_sync_position, unsigned char table_id, unsigned short input_pid,
TABLE_CHECK eit_check_callback)
{
SECTION section = {0};
SECTION *section_head = NULL;
unsigned char version_number = INIT_VERSION_NUMBER;
unsigned int section_number_array[MAX_SECTION_COUNT] = {0};
unsigned int section_count = 0;
TS_SECTION_HEAD ts_section_head = {0};
if ((NULL==ts_file) || (first_sync_position<0) || (0==package_length))
{
log("function write_to_file parameter error");
return section_head;
}
if (fseek(ts_file, first_sync_position - 1, SEEK_SET) != 0)
{
log("Fseek to first position to 0x47 error!\n");
return section_head;
}
while (0 == feof(ts_file))
{
memset(§ion, 0, sizeof(SECTION));
section.section_length = get_section(ts_file, package_length, table_id, input_pid, section.section_buffer, &ts_section_head);
if (0 == section.section_length)
{
log("Get section error!!!\n");
continue;
}
section.section_number = section.section_buffer[SECTION_NUMBER_OFFSET];
if (version_number_change(ts_section_head, &version_number) != 0)
{
if (eit_check_callback == NULL)
{
memset(section_number_array, 0, MAX_SECTION_COUNT); //version number change and then init
version_number = INIT_VERSION_NUMBER;
destroy_section_link(section_head);
section_count = 0;
}
}
section_head = add_section_to_link(section_head, section, §ion_count, section_number_array, ts_section_head.section_number);
if (section_count > ts_section_head.last_section_number)
{
log("success get one table!!\n");
break;
}
}
return section_head;
}
有了这一步的基础后,解析后面的表,只要调用相关的接口就行了。如果以上代码还有什么地方可以优化的话,麻烦告知一声,谢谢!!!!!
下一篇: Chapter2-力学
推荐阅读