任务二
根据指定的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;