开发者社区> xumaojun> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

关于TS流的解析

简介: TS即是"TransportStream"的缩写。他是分包发送的,每一个包长为188字节。在TS流里可以填入很多类型的数据,如视频、音频、自定义信息等。
+关注继续查看

TS即是"TransportStream"的缩写。他是分包发送的,每一个包长为188字节。在TS流里可以填入很多类型的数据,如视频、音频、自定义信息等。他的包的结构为,包头为4个字节,负载为184个字节(这184个字节不一定都是有效数据,有一些可能为填充数据)。
工作形式:
因为在TS流里可以填入很多种东西,所以有必要有一种机制来确定怎么来标识这些数据。制定TS流标准的机构就规定了一些数据结构来定义。比如: PSI(ProgramSpecific Information)表,所以解析起来就像这样: 先接收一个负载里为PAT的数据包,在整个数据包里找到一个PMT包的ID。然后再接收一个含有PMT的数据包,在这个数据包里找到有关填入数据类型的ID。之后就在接收到的TS包里找含有这个ID的负载内容,这个内容就是填入的信息。根据填入的数据类型的ID的不同,在TS流复合多种信息是可行的。关键就是找到标识的ID号。
现在以一个例子来说明具体的操作:
在开始之前先给出一片实际TS流例子:
0000f32ch: 47 40 00 17 00 00 B0 0D 00 01 C1 00 00 00 01 E0 ; G@....?..?...?
0000f33ch: 20 A2 C3 29 41 FF FF FF FF FF FF FF FF FF FF FF ;  ⒚)A
0000f34ch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 
0000f35ch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 
0000f36ch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 
0000f37ch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 
0000f38ch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 
0000f39ch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 
0000f3ach: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 
0000f3bch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 
0000f3cch: FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF ; 
0000f3dch: FF FF FF FF FF FF FF FF FF FF FF FF 47 40 20 17 ; G@ .
0000f3ech: 00 02 B0 1B 00 01 C1 00 00 E0 21 F0 00 1B E0 21 ; ..?..?.??.?
0000f3fch: F0 04 2A 02 7E 1F 03 E0 22 F0 00 5D 16 BD 48    ;?*.~..??].紿
具体的分析就以这个例子来分析。
// Adjust TS packet header
void adjust_TS_packet_header(TS_packet_header* pheader)
{
    unsigned char buf[4];
    memcpy(buf, pheader, 4);
   pheader->transport_error_indicator       = buf[1] >> 7;
    pheader->payload_unit_start_indicator    =buf[1] >> 6 & 0x01;
   pheader->transport_priority               = buf[1] >> 5 & 0x01;
   pheader->PID                           = (buf[1] & 0x1F) << 8 | buf[2];
    pheader->transport_scrambling_control    =buf[3] >> 6;
    pheader->adaption_field_control           = buf[3] >> 4 & 0x03;
   pheader->continuity_counter               = buf[3] & 0x03;
}
这是一个调整TS流数据包头的函数,这里牵扯到位段调整的问题。现在看一下TS流数据包头的结构的定义:
// Transport packet header
typedef struct TS_packet_header
{
    unsignedsync_byte                       : 8;
    unsignedtransport_error_indicator        : 1;
    unsigned payload_unit_start_indicator    : 1;
    unsignedtransport_priority               : 1;
    unsignedPID                           : 13;
    unsigned transport_scrambling_control    : 2;
    unsignedadaption_field_control           : 2;
    unsignedcontinuity_counter               : 4;
} TS_packet_header;
下面我们来分析,在ISO/IEC 13818-1里有说明,PAT(ProgramAssociation Table)的PID值为0x00,TS包的标识(即sync_byte)为0x47,并且为了确保这个TS包里的数据有效,所以我们一开始查找47 40 00这三组16进制数,为什么这样?具体的奥秘在TS包的结构上,前面已经说了sync_byte固定为0x47。现在往下看transport_error_indicator、payload_unit_start_indicator、transport_priority和PID这四个元素,PID为0x00,这是PAT的标识。transport_error_indicator为0,transport_priority为0。把他们看成是两组8位16进制数就是:4000。现在看看我们的TS流片断例子,看来正好是47 40 00开头的,一个TS流的头部占据了4个字节。剩下的负载部分的内容由PID来决定,例子看来就是一个PAT表。在这里有个地方需要注意一下,payload_unit_start_indicator为1时,在前4个字节之后会有一个调整字节,它的数值决定了负载内容的具体开始位置。现在看例子中的数据47 40 00 17 00第五个字节是00,说明紧跟着00之后就是具体的负载内容。
下面给出PAT表的结构体:
// PAT table
// Programm Association Table
typedef struct TS_PAT
{
    unsignedtable_id                       : 8;
    unsignedsection_syntax_indicator        : 1;
    unsigned zero                           : 1;
    unsignedreserved_1                       : 2;
    unsignedsection_length                   : 12;
    unsignedtransport_stream_id           : 16;
    unsignedreserved_2                       : 2;
    unsigned version_number                   : 5;
    unsignedcurrent_next_indicator           : 1;
    unsignedsection_number                   : 8;
    unsignedlast_section_number           : 8;
    unsignedprogram_number                   : 16;
    unsignedreserved_3                       : 3;
    unsignednetwork_PID                   : 13;
    unsignedprogram_map_PID               : 13;
    unsignedCRC_32                           : 32;
} TS_PAT;
再给出PAT表字段调整函数:
// Adjust PAT table
void adjust_PAT_table ( TS_PAT * packet, char * buffer )
{
    int n = 0, i = 0;
    int len = 0;
   packet->table_id                   = buffer[0];
    packet->section_syntax_indicator    =buffer[1] >> 7;
   packet->zero                       = buffer[1] >> 6 & 0x1;
   packet->reserved_1                   = buffer[1] >> 4 & 0x3;
   packet->section_length               = (buffer[1] & 0x0F) << 8 | buffer[2];   
   packet->transport_stream_id           = buffer[3] << 8 | buffer[4];
    packet->reserved_2                   = buffer[5] >> 6;
   packet->version_number               = buffer[5] >> 1 &  0x1F;
   packet->current_next_indicator        =(buffer[5] << 7) >> 7;
   packet->section_number               = buffer[6];
    packet->last_section_number           = buffer[7];
    // Get CRC_32
    len = 3 + packet->section_length;
   packet->CRC_32                       = (buffer[len-4] & 0x000000FF) << 24
                                         | (buffer[len-3] & 0x000000FF) << 16
                                         | (buffer[len-2] & 0x000000FF) << 8
                                         | (buffer[len-1] & 0x000000FF);
    // Parse network_PID or program_map_PID
    for ( n = 0; n < packet->section_length - 4; n ++ )
    {
        packet->program_number           = buffer[8] << 8 | buffer[9];
       packet->reserved_3               = buffer[10] >> 5;
        if ( packet->program_number ==0x0 )
           packet->network_PID = (buffer[10] << 3) << 5 | buffer[11];
        else
        {
           packet->program_map_PID = (buffer[10] << 3) << 5 | buffer[11];
        }
        n += 5;
    }
}
通过上面的分析,例子中的数据00 B0 0D 00 01 C1 0000 00 01 E0 20 A2 C3 29 41就是具体的PAT表的内容,然后根据PAT结构体来具体分析PAT表。但是我们需要注意的是在PAT表里有program_number、network_PID的元素不只有一个,这两个元素是通过循环来确定的。循环的次数通过section_length元素的确定。在这个例子中program_map_PID为20,所以下面来PMT分析时,就是查找47 40 20的开头的TS包。
下面来分析PMT表,先给出PMT(ProgramMap Table)的结构体:
// PMT table
// Program Map Table
typedef struct TS_PMT
{
    unsignedtable_id                       : 8;
    unsignedsection_syntax_indicator        : 1;
    unsignedzero                           : 1;
    unsignedreserved_1                       : 2;
    unsignedsection_length                   : 12;
    unsignedprogram_number                   : 16;
    unsignedreserved_2                       : 2;
    unsignedversion_number                   : 5;
    unsignedcurrent_next_indicator           : 1;
    unsignedsection_number                   : 8;
    unsignedlast_section_number           : 8;
    unsignedreserved_3                       : 3;
    unsignedPCR_PID                       : 13;
    unsignedreserved_4                       : 4;
    unsignedprogram_info_length           : 12;
   
    unsignedstream_type                   : 8;
    unsignedreserved_5                       : 3;
    unsignedelementary_PID                   : 13;
    unsignedreserved_6                       : 4;
    unsignedES_info_length                   : 12;
    unsignedCRC_32                           : 32;
} TS_PMT;
在给出调整字段函数:
// Adjust PMT table
void adjust_PMT_table ( TS_PMT * packet, char * buffer )
{
    int pos = 12, len = 0;
    int i = 0;
   packet->table_id                           = buffer[0];
    packet->section_syntax_indicator           = buffer[1] >> 7;
   packet->zero                               = buffer[1] >> 6;
   packet->reserved_1                           = buffer[1] >> 4;
   packet->section_length                       = (buffer[1] & 0x0F) << 8 | buffer[2];   
   packet->program_number                       = buffer[3] << 8 | buffer[4];
   packet->reserved_2                           = buffer[5] >> 6;
   packet->version_number                       = buffer[5] >> 1 & 0x1F;
    packet->current_next_indicator               = (buffer[5] << 7) >> 7;
   packet->section_number                       = buffer[6];
   packet->last_section_number                   = buffer[7];
   packet->reserved_3                           = buffer[8] >> 5;
    packet->PCR_PID                               = ((buffer[8] << 8) | buffer[9]) & 0x1FFF;
   packet->reserved_4                           = buffer[10] >> 4;
   packet->program_info_length                   = (buffer[10] & 0x0F) << 8 | buffer[11];
    // Get CRC_32
    len = packet->section_length + 3;   
   packet->CRC_32               = (buffer[len-4] & 0x000000FF) << 24
                                 | (buffer[len-3] & 0x000000FF) << 16
                                 | (buffer[len-2] & 0x000000FF) << 8
                                 | (buffer[len-1] & 0x000000FF);
    // program info descriptor
    if ( packet->program_info_length != 0 )
        pos +=packet->program_info_length;   
    // Get stream type and PID   
    for ( ; pos <= (packet->section_length + 2 ) - 4; )
    {
       packet->stream_type                           = buffer[pos];
       packet->reserved_5                           = buffer[pos+1] >> 5;
       packet->elementary_PID                       = ((buffer[pos+1] << 8) | buffer[pos+2]) & 0x1FFF;
       packet->reserved_6                           = buffer[pos+3] >> 4;
       packet->ES_info_length                       = (buffer[pos+3] & 0x0F) << 8 | buffer[pos+4];
        // Store in es
        es[i].type = packet->stream_type;
        es[i].pid =packet->elementary_PID;
        if ( packet->ES_info_length != 0)
        {
            pos = pos+5;
            pos +=packet->ES_info_length;
        }
        else
        {
            pos += 5;
        }
        i++;
    }
}
TS流可以复合很多的节目的视频和音频,但是解码器是怎么来区分的呢?答案就在PMT表里,如其名节目映射表。他就是来解决这个问题的。现在看PMT结构体里的stream_type、elementary_PID这两个元素,前一个用来确定后一个作为标识PID的内容具体是什么,音频或视频等。还有要注意他们不只有一个,所以他们是通过循环读取来确保所有的值都被读取了,当然循环也是有规定的(具体看调整函数上)。从例子上来看,我们在倒数第三行找到了上面分析来的PMT表的PID为0x20的TS包。然后就可以把数据是用调整函数填入结构中。然后得到具体节目的PID为视频0x21,音频0x22。
PS. 文章里的PID是用来判断具体TS包是什么包的。分析每个包得到的PID值,都可以复合在TS头部结构体的PID里。

 

 

 

根据前一篇中各数据的定义及数据结构,对数据进行分别解析如下:

TS包头定义:

typedef struct TS_packet_header
{
unsigned sync_byte : 8; //同步字节, 固定为0x47,表示后面的是一个TS分组
unsigned transport_error_indicator : 1; //传输误码指示符
unsigned payload_unit_start_indicator : 1; //有效荷载单元起始指示符

unsigned transport_priority : 1; //传输优先, 1表示高优先级,传输机制可能用到,解码用不着
unsigned PID : 13; //PID
unsigned transport_scrambling_control : 2; //传输加扰控制
unsigned adaption_field_control : 2; //自适应控制 01仅含有效负载,10仅含调整字段,11含有调整字段和有效负载。为00解码器不进行处理
unsigned continuity_counter : 4; //连续计数器 一个4bit的计数器,范围0-15
} TS_packet_header;

TS包头解析代码:

HRESULT CTS_Stream_Parse::adjust_TS_packet_header( TS_packet_header*TS_header )
{
unsigned char buf[4];

memcpy(buf, TS_header, 4);
TS_header->transport_error_indicator = buf[1] >> 7;
TS_header->payload_unit_start_indicator = buf[1] >> 6 & 0x01;
TS_header->transport_priority = buf[1] >> 5 & 0x01;
TS_header->PID = (buf[1] & 0x1F) << 8 | buf[2];
TS_header->transport_scrambling_control = buf[3] >> 6;
TS_header->adaption_field_control = buf[3] >> 4 & 0x03;
TS_header->continuity_counter = buf[3] & 0x0F; // 四位数据,应为0x0F xyy 09.03.18

return 0;
}

如下为一个TS包数据:

0x47 0x40 0x00 0x12 0x00 0x00 0xb0 0x0d 0x00 0x00 0xc1 0x00 0x00 0x00 0x010xe3 0xe8 0xf0 0x0b 0xd7 0x79 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff

分析知道前四位0x47 0x40 0x00 0x12TS头部即为TS包头数据,解析如下:

sync_byte :0x47
transport_error_indicator: 0x00
payload_unit_start_indicator: 0x01
transport_priority : 0x00

PID :0x0000
transport_scrambling_control :0x00
adaptation_field_control :0x01

continuity_counter :0x02

PID = 0x0000,表示此TS包的内容为PSI信息表格的PAT表格数据,在4字节的TS包头之后的第一个字节的Point_field = 0x00, 表示偏移量为0,即紧随其后的即为PAT的数据信息。

PAT表格定义如下:

typedef struct TS_PAT_Program
{
unsigned program_number :16; //节目号
unsigned program_map_PID :13; //节目映射表的PID,节目号大于0时对应的PID,每个节目对应一个
}TS_PAT_Program;

//PAT表结构体
typedef struct TS_PAT
{
unsigned table_id : 8; //固定为0x00 ,标志是该表是PAT
unsigned section_syntax_indicator : 1; //段语法标志位,固定为1
unsigned zero : 1; //0
unsigned reserved_1 : 2; // 保留位
unsigned section_length : 12; //表示这个字节后面有用的字节数,包括CRC32
unsigned transport_stream_id : 16; //该传输流的ID,区别于一个网络中其它多路复用的流
unsigned reserved_2 : 2;// 保留位
unsigned version_number : 5; //范围0-31,表示PAT的版本号
unsigned current_next_indicator : 1; //发送的PAT是当前有效还是下一个PAT有效
unsigned section_number : 8; //分段的号码。PAT可能分为多段传输,第一段为00,以后每个分段加1,最多可能有256个分段
unsigned last_section_number : 8; //最后一个分段的号码

std::vector<TS_PAT_Program> program;
unsigned reserved_3 : 3; // 保留位
unsigned network_PID : 13; //网络信息表(NIT)的PID,节目号为0时对应的PID为network_PID

unsigned CRC_32 : 32; //CRC32校验码
} TS_PAT;

解析代码如下:

HRESULT CTS_Stream_Parse::adjust_PAT_table( TS_PAT * packet, unsigned char* buffer)
{
packet->table_id = buffer[0];
packet->section_syntax_indicator = buffer[1] >> 7;
packet->zero = buffer[1] >> 6 & 0x1;
packet->reserved_1 = buffer[1] >> 4 & 0x3;
packet->section_length = (buffer[1] & 0x0F) << 8 | buffer[2];

packet->transport_stream_id = buffer[3] << 8 | buffer[4];

packet->reserved_2 = buffer[5] >> 6;
packet->version_number = buffer[5] >> 1 & 0x1F;
packet->current_next_indicator = (buffer[5] << 7) >> 7;
packet->section_number = buffer[6];
packet->last_section_number = buffer[7];

int len = 0;
len = 3 + packet->section_length;
packet->CRC_32 = (buffer[len-4] & 0x000000FF) << 24
| (buffer[len-3] & 0x000000FF) << 16
| (buffer[len-2] & 0x000000FF) << 8
| (buffer[len-1] & 0x000000FF);


int n = 0;
for ( n = 0; n < packet->section_length - 12; n += 4 )
{
unsigned program_num = buffer[8 + n ] << 8 | buffer[9 + n ];
packet->reserved_3 = buffer[10 + n ] >> 5;

packet->network_PID = 0x00;
if ( program_num == 0x00)
{
packet->network_PID = (buffer[10 + n ] & 0x1F) << 8 | buffer[11 +n ];

TS_network_Pid = packet->network_PID; //记录该TS流的网络PID

TRACE(" packet->network_PID %0x \n\n", packet->network_PID);
}
else
{
TS_PAT_Program PAT_program;
PAT_program.program_map_PID = (buffer[10 + n] & 0x1F) << 8 |buffer[11 + n];
PAT_program.program_number = program_num;
packet->program.push_back( PAT_program );

TS_program.push_back( PAT_program );//向全局PAT节目数组中添加PAT节目信息
}
}
return 0;
}

因此,PAT数据解析结果如下:

PAT数据

table_id :0x00 //8
section_syntax_indicator :0x01 // 1
'0' :0x00 // 1
reserved 0x03 // 2
section_length :0x00d // 12
transport_stream_id :0x0000 // 16
reserved :0x03 // 2
version_number :0x00 // 5
current_next_indicator :0x01 // 1
section_number :0x00 // 8
last_section_number :0x00 // 8
program_number :0x0001 // 16
reserved :0x07 // 3
program_map_PID :0x03e8 // 13
CRC :0x f0 0b d7 79

由解析结构可知,该PAT表格中没有网络信息包信息,只包含一个节目,其PID为0x03e8

PMT结构定义:


typedef struct TS_PMT_Stream
{
unsigned stream_type : 8; //指示特定PID的节目元素包的类型。该处PID由elementary PID指定
unsigned elementary_PID : 13; //该域指示TS包的PID值。这些TS包含有相关的节目元素
unsigned ES_info_length : 12; //前两位bit为00。该域指示跟随其后的描述相关节目元素的byte数
unsigned descriptor;
}TS_PMT_Stream;

//PMT 表结构体
typedef struct TS_PMT
{
unsigned table_id : 8; //固定为0x02, 表示PMT表
unsigned section_syntax_indicator : 1; //固定为0x01
unsigned zero : 1; //0x01
unsigned reserved_1 : 2; //0x03
unsigned section_length : 12;//首先两位bit置为00,它指示段的byte数,由段长度域开始,包含CRC。
unsigned program_number : 16;// 指出该节目对应于可应用的Program map PID
unsigned reserved_2 : 2; //0x03
unsigned version_number : 5; //指出TS流中Program map section的版本号
unsigned current_next_indicator : 1; //当该位置1时,当前传送的Program map section可用;
//当该位置0时,指示当前传送的Program map section不可用,下一个TS流的Program map section有效。
unsigned section_number : 8; //固定为0x00
unsigned last_section_number : 8; //固定为0x00
unsigned reserved_3 : 3; //0x07
unsigned PCR_PID : 13; //指明TS包的PID值,该TS包含有PCR域,
//该PCR值对应于由节目号指定的对应节目。
//如果对于私有数据流的节目定义与PCR无关,这个域的值将为0x1FFF。
unsigned reserved_4 : 4; //预留为0x0F
unsigned program_info_length : 12; //前两位bit为00。该域指出跟随其后对节目信息的描述的byte数。

std::vector<TS_PMT_Stream> PMT_Stream; //每个元素包含8位, 指示特定PID的节目元素包的类型。该处PID由elementary PID指定
unsigned reserved_5 : 3; //0x07
unsigned reserved_6 : 4; //0x0F
unsigned CRC_32 : 32;
} TS_PMT;

解析代码为:

HRESULT CTS_Stream_Parse::adjust_PMT_table ( TS_PMT * packet, unsigned char* buffer )
{
packet->table_id = buffer[0];
packet->section_syntax_indicator = buffer[1] >> 7;
packet->zero = buffer[1] >> 6 & 0x01;
packet->reserved_1 = buffer[1] >> 4 & 0x03;
packet->section_length = (buffer[1] & 0x0F) << 8 | buffer[2];
packet->program_number = buffer[3] << 8 | buffer[4];
packet->reserved_2 = buffer[5] >> 6;
packet->version_number = buffer[5] >> 1 & 0x1F;
packet->current_next_indicator = (buffer[5] << 7) >> 7;
packet->section_number = buffer[6];
packet->last_section_number = buffer[7];
packet->reserved_3 = buffer[8] >> 5;
packet->PCR_PID = ((buffer[8] << 8) | buffer[9]) & 0x1FFF;

PCRID = packet->PCR_PID;

packet->reserved_4 = buffer[10] >> 4;
packet->program_info_length = (buffer[10] & 0x0F) << 8 |buffer[11];
// Get CRC_32
int len = 0;
len = packet->section_length + 3;
packet->CRC_32 = (buffer[len-4] & 0x000000FF) << 24
| (buffer[len-3] & 0x000000FF) << 16
| (buffer[len-2] & 0x000000FF) << 8
| (buffer[len-1] & 0x000000FF);

int pos = 12;
// program info descriptor
if ( packet->program_info_length != 0 )
pos += packet->program_info_length;
// Get stream type and PID
for ( ; pos <= (packet->section_length + 2 ) - 4; )
{
TS_PMT_Stream pmt_stream;
pmt_stream.stream_type = buffer[pos];
packet->reserved_5 = buffer[pos+1] >> 5;
pmt_stream.elementary_PID = ((buffer[pos+1] << 8) | buffer[pos+2]) &0x1FFF;
packet->reserved_6 = buffer[pos+3] >> 4;
pmt_stream.ES_info_length = (buffer[pos+3] & 0x0F) << 8 |buffer[pos+4];

pmt_stream.descriptor = 0x00;
if (pmt_stream.ES_info_length != 0)
{
pmt_stream.descriptor = buffer[pos + 5];

for( int len = 2; len <= pmt_stream.ES_info_length; len ++ )
{
pmt_stream.descriptor = pmt_stream.descriptor<< 8 | buffer[pos + 4 +len];
}
pos += pmt_stream.ES_info_length;
}
pos += 5;
packet->PMT_Stream.push_back( pmt_stream );
TS_Stream_type.push_back( pmt_stream );
}
return 0;
}

举例如下:

0x47 0x43 0xe8 0x12 0x00 0x02 0xb0 0x12 0x00 0x01 0xc1 0x00 0x00 0xe3 0xe90xf0 0x00 0x1b 0xe3 0xe9 0xf0 0x00 0xf0 0xaf 0xb4 0x4f 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff 0xff

TS头部

sync_byte :0x47
transport_error_indicator: 0x00
payload_unit_start_indicator: 0x01
transport_priority : 0x00

PID :0x03e8
transport_scrambling_control :0x00
adaptation_field_control :0x01

continuity_counter :0x02

PMT数据               

table_id :0x02 // 8
section_syntax_indicator :0x01 // 1
'0' :0x00 // 1
reserved :0x03 // 2
section_length : 0x012 // 12
program_number :0x00 01 // 16
reserved :0x03 // 2
version_number :0x00 // 5
current_next_indicator 0x01 // 1
section_number :0x00 // 8
last_section_number :0x00 // 8
reserved 0x07 // 3
PCR_PID :0x03 e9 // PCR(节目参考时钟)所在TS分组的PID // 13
reserved :0x0f //4
program_info_length :0x000 // 12
stream_type :0x1b // 8
reserved 0x07 // 3
elementary_PID :0x03 e9 // 13//该节目中包括的视频流,音频流等对应的TS分组的PID
reserved :0x0f // 4
ES_info_length :0x000 // 12
CRC : 0xf0 af b4 4f

 

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
H265(HEVC) nal 单元头介绍及rtp发送中的fu分组发送详解
首先来介绍下h265(HEVC)nal单元头,与h264的nal层相比,h265的nal unit header有两个字节构成,如下图所示: 从图中可以看出hHEVC的nal包结构与h264有明显的不同,hevc加入了nal所在的时间层的ID,取去除了nal_ref_idc,此信息合并到了naltype中,通常情况下F为0,layerid为0,TID为1。
2546 0
RTP协议全解析(H264码流和PS流)
写在前面:RTP的解析,网上找了很多资料,但是都不全,所以我力图整理出一个比较全面的解析, 其中借鉴了很多文章,我都列在了文章最后,在此表示感谢。 互联网的发展离不开大家的无私奉献,我决定从我做起,希望大家支持。
1168 0
RTP 包格式 详细解析
H.264 视频 RTP 负载格式 1. 网络抽象层单元类型 (NALU) NALU 头由一个字节组成, 它的语法如下:       +---------------+      |0|1|2|3|4|5|6|7|      +-+-+-+-+-+-+-+-+      |F|NRI|  Type   |      +---------------+ F: 1 个比特.
1017 0
FU-A分包方式,以及从RTP包里面得到H.264数据和AAC数据的方法。。
【原创】 RFC3984是H.264的baseline码流在RTP方式下传输的规范,这里只讨论FU-A分包方式,以及从RTP包里面得到H.264数据和AAC数据的方法。 1、单个NAL包单元 12字节的RTP头后面的就是音视频数据,比较简单。
632 0
FU-A分包方式,以及从RTP包里面得到H.264数据和AAC数据的方法
FU-A分包方式,以及从RTP包里面得到H.264数据和AAC数据的方法 RFC3984是H.264的baseline码流在RTP方式下传输的规范,这里只讨论FU-A分包方式,以及从RTP包里面得到H.264数据和AAC数据的方法。
1061 0
TS流文件
<h2 class="headline-1" style="margin: 35px 0px 15px -30px; padding: 0px; font-size: 24px; font-family: 'Microsoft YaHei', SimHei, Verdana; font-weight: 500; line-height: 22px; clear: both; zoom: 1;
1927 0
RTP头结构解析
RTP包头前12个固定字节机构图: 0                   1                   2                   3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1    ...
590 0
自己动手写RTP服务器——用RTP协议传输TS流
上一篇文章我们介绍了关于RTP协议的知识,那么我们现在就自己写一个简单的传输TS流媒体的RTP服务器吧。 预备知识 关于TS流的格式:TS流封装的具体格式请参考文档ISO/IEC 13818-1。
1375 0
关于TS流的解析
TS即是"TransportStream"的缩写。他是分包发送的,每一个包长为188字节。在TS流里可以填入很多类型的数据,如视频、音频、自定义信息等。
594 0
+关注
xumaojun
乐于学习与分析
文章
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载