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

TS码流解析-2-获取完整的TABLE

程序员文章站 2022-03-05 12:20:59
...

任务二

根据指定的table id 和 pid 获取完整的table。这是比较重要的一个环节,后面涉及到相关表的解析,都需要用到这里的函数。

相关知识

1.首先了解TS数据包包头的结构,在这里贴一张ts包头的结构图

TS码流解析-2-获取完整的TABLE

各字段的解释:

# 标识 位数 说明
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头部信息结构图

TS码流解析-2-获取完整的TABLE

# 字段名 占位 说明
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之间的关系

TS码流解析-2-获取完整的TABLE
简单说,就是数据(table)在传输之前,被切分成一段一段的数据(section),然后段之前加上标识,接着再进行切分,并且加上流的标识符(ts package),这时候就变成了一个个固定包长的流,然后我们就通过该流进行传输。接受方收到数据后,按照相关的规则把它们拼接起来,就可以看到原先的样子了。
关系与区别:

  1. 三者之中,只有TS package是固定长度的,section和table的长度都是不固定的
  2. 一个section可能包含一个或者多个TS package
  3. 一个table可能包含一个或者多个section,section之间用section number区分
  4. 一个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 流程图

TS码流解析-2-获取完整的TABLE

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, &section_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, &section_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时的运算解释,看图

TS码流解析-2-获取完整的TABLE
pointer_field的解释
TS码流解析-2-获取完整的TABLE

3 将section拼接成table

1流程图

TS码流解析-2-获取完整的TABLE

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(&section, 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, &section_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;
}

有了这一步的基础后,解析后面的表,只要调用相关的接口就行了。如果以上代码还有什么地方可以优化的话,麻烦告知一声,谢谢!!!!!