1.TS格式介绍
TS:全称为MPEG2-TS。TS即"Transport Stream"的缩写。它是分包发送的,每一个包长为188字节(还有192和204个字节的包)。包的结构为,包头为4个字节(第一个字节为0x47),负载为184个字节。在TS流里可以填入很多类型的数据,如视频、音频、自定义信息等。MPEG2-TS主要应用于实时传送的节目,比如实时广播的电视节目。MPEG2-TS格式的特点就是要求从视频流的任一片段开始都是可以独立解码的。简单地说,将DVD上的VOB文件的前面一截cut掉(或者是数据损坏数据)就会导致整个文件无法解码,而电视节目是任何时候打开电视机都能解码(收看)的。
2.TS流包含的内容
一段TS流,必须包含PAT包、PMT包、多个音频包、多个视频包、多个PCR包、以及其他信息包。
解析TS流数据的流程:查找PID为0x0的包,解析PAT,PAT包中的program_map_PID表示PMT的PID;查找PMT,PMT包中的elementary_PID表示音视频包的PID,PMT包中的PCR_PID表示PCR的PID,有的时候PCR的PID跟音频或者视频的PID相同,说明PCR会融进音视频的包,注意解析,有的时候PCR是自己单独的包;CAT、NIT、SDT、EIT的PID分别为: 0x01、0x10、0x11、0x12。
3.TS包头解析
TS包头有4个字节
//Transport Stream headertypedef struct TS_header{ unsigned sync_byte :8; //同步字节,固定为0x47 ,表示后面的是一个TS分组,当然,后面包中的数据是不会出现0x47的 unsigned transport_error_indicator :1; //传输错误标志位,一般传输错误的话就不会处理这个包了 unsigned payload_unit_start_indicator :1; //有效负载的开始标志,根据后面有效负载的内容不同功能也不同 // payload_unit_start_indicator为1时,在前4个字节之后会有一个调整字节,它的数值决定了负载内容的具体开始位置。 unsigned transport_priority :1; //传输优先级位,1表示高优先级 unsigned PID :13; //有效负载数据的类型 unsigned transport_scrambling_control :2; //加密标志位,00表示未加密 unsigned adaption_field_control :2; //调整字段控制,。01仅含有效负载,10仅含调整字段,11含有调整字段和有效负载。为00的话解码器不进行处理。 unsigned continuity_counter :4; //一个4bit的计数器,范围0-15} TS_header; //特殊参数说明: //sync_byte:0x47 //payload_unit_start_indicator:0x01表示含有PSI或者PES头 //PID:0x0表示后面负载内容为PAT,不同的PID表示不同的负载 //adaption_field_control: // 0x0: // reserved for future use by ISO/IEC // 0x1: // 无调整字段,仅含有效负载 // 0x2: // 仅含调整字段,无有效负载 // 0x3: // 调整字段后含有效负载 // Parse TS headerint Parse_TS_header(unsigned char *pTSBuf, TS_header *pheader){ pheader->sync_byte = pTSBuf[0]; if (pheader->sync_byte != 0x47) return -1; pheader->transport_error_indicator = pTSBuf[1] >> 7; pheader->payload_unit_start_indicator = pTSBuf[1] >> 6 & 0x01; pheader->transport_priority = pTSBuf[1] >> 5 & 0x01; pheader->PID = (pTSBuf[1] & 0x1F) << 8 | pTSBuf[2]; pheader->transport_scrambling_control = pTSBuf[3] >> 6; pheader->adaption_field_control = pTSBuf[3] >> 4 & 0x03; pheader->continuity_counter = pTSBuf[3] & 0x0F; return 0;}4.TS负载格式解析
4.1 PAT解析
领取音视频开发学习资料:
TS_header包头中的PID值为0x0,表示当前负载为PAT(Program Association Table)。PAT数据的信息可以理解为整个TS流包含的节目信息。
// Program Association Tabletypedef struct PAT_Packet_tag{ 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; //最后一个分段的号码 // for(i=0; i
4.2 PMT解析
由PAT包中的program_map_PID可以确定PMT(Program Map Table)的PID。PMT数据的信息可以理解为这个节目包含的音频和视频信息。
// Program Map Tabletypedef struct PMT_Packet_tag{ unsigned table_id : 8; unsigned section_syntax_indicator : 1; unsigned zero : 1; unsigned reserved_1 : 2; unsigned section_length : 12; unsigned program_number : 16; unsigned reserved_2 : 2; unsigned version_number : 5; unsigned current_next_indicator : 1; unsigned section_number : 8; unsigned last_section_number : 8; unsigned reserved_3 : 3; unsigned PCR_PID : 13; unsigned reserved_4 : 4; unsigned program_info_length : 12; // for(i=0; i 4.3 PES解析 根据文档参考PAT、PMT的解析流程就能完成PES的解析了。 需要注意的是PES中PTS的解析,一般来说在90 kHz 中,PTS/9000的值为秒单位。 unsigned long long Parse_PTS(unsigned *pBuf){ unsigned long long llpts = (((unsigned long long )(pBuf[0] & 0x0E)) << 29) | (unsigned long long )(pBuf[1] << 22) | (((unsigned long long )(pBuf[2] & 0xFE)) << 14) | (unsigned long long )(pBuf[3] << 7) | (unsigned long long )(pBuf[4] >> 1); return llpts;}PES结构详解 领取音视频开发学习资料: PES前面几个字段填写方法: // 生成 pes 头数据 buffer[0] = 0x00; buffer[1] = 0x00; buffer[2] = 0x01; buffer[3] = (unsigned char)(pes->stream_id); buffer[4] = (unsigned char)((pes_header_len_syt >> 8) & 0xff); buffer[5] = (unsigned char)(pes_header_len_syt & 0xff); buffer[6] = (1<<7)// 2 - check bits '10' | ((prc->encrypt & 0x03) << 4)// 2 - PES_scrambling_control(0) | ((pes_priority & 1) << 3)// 1 - PES_priority(0) | (((~stuff_flag) << 1) & 0x04) // 1 - data_alignment_indicator(0) | 0 // 1 - copyright(0) | 0; // 1 - original_or_copy(0) buffer[7] = (((unsigned char)pes->add_pts) << 7) | ((unsigned char)pes->add_user_data);// buffer[7] = ((unsigned char)pes->add_pts) << 7; // 2 - PTS_DTS_flags() // 1 - ESCR_flag(0) // 1 - ES_rate_flag(0) // 1 - DSM_trick_mode_flag(0) // 1 - additional_copy_info_flag(0) // 1 - PES_CRC_flag(0) // 1 - PES_extension_flag() buffer[8] = (unsigned char)pes_header_ext_len; // 8 - PES_header_data_length pos = 9; if (pes->add_pts) { pts = prc->ptime_stamp; buffer[pos++] = (pts >> 28 & 0x0e) | 0x21; // 4 - '0010' // 3 - PTS [32..30] // 1 - marker_bit buffer[pos++] = (pts >> 21); // 8 - PTS [29..22] buffer[pos++] = (pts >> 13 & 0xfe) | 0x01; // 7 - PTS [21..15] // 1 - marker_bit buffer[pos++] = (pts >> 6); // 8 - PTS [14..7] buffer[pos++] = (pts << 2 & 0xfc) | 0x01; // 7 - PTS [6..0] // 1 - marker_bit } 接收方PTS时间戳的获取 PTS和DTS字段,本来有效信息是33位,扩展至5个字节,也就是40位。扩展方式: 通过上边这个图可以清楚地看到,PTS的33位的各部分,如果从左至右的绿色部分分别为PTS1,PTS2,PTS3的话,那么PTS的计算: PTS = (PTS1 & 0x0e) << 29 + (PTS2 & 0xfffe) << 14 + (PTS3 & 0xfffe ) >> 1; DTS(如果有)紧接着后边读取5个字节的数据,也是这样求出来的。
标签: unsigned
②文章观点仅代表原作者本人不代表本站立场,并不完全代表本站赞同其观点和对其真实性负责。
③文章版权归原作者所有,部分转载文章仅为传播更多信息、受益服务用户之目的,如信息标记有误,请联系站长修正。
④本站一律禁止以任何方式发布或转载任何违法违规的相关信息,如发现本站上有涉嫌侵权/违规及任何不妥的内容,请第一时间反馈。发送邮件到 88667178@qq.com,经核实立即修正或删除。