TS格式解析
1.TS格式介绍
TS:全称为MPEG2-TS。TS即"Transport Stream"的缩写。它是分包发送的,每一个包长为188字节(还有192和204个字节的包)。包的结构为,包头为4个字节(第一个字节为0x47),负载为184个字节。在TS流里可以填入很多类型的数据,如视频、音频、自定义信息等。MPEG2-TS主要应用于实时传送的节目,比如实时广播的电视节目。MPEG2-TS格式的特点就是要求从视频流的任一片段开始都是可以独立解码的。简单地说,将DVD上的VOB文件的前面一截cut掉(或者是数据损坏数据)就会导致整个文件无法解码,而电视节目是任何时候打开电视机都能解码(收看)的。
TS解析需要参考:ISO/IEC 13818-1的2.4 Transport Stream bitstream requirements
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 header typedef 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 header int 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; } |
TS包头解析需要参考:ISO/IEC 13818-1的2.4.3.2 Transport Stream packet layer
4.TS负载格式解析
4.1 PAT解析
TS_header包头中的PID值为0x0,表示当前负载为PAT(Program Association Table)。PAT数据的信息可以理解为整个TS流包含的节目信息。
// Program Association Table typedef 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<N; i++) // { unsigned program_number : 16; unsigned reserved_3 : 3; unsigned network_PID : 16; // 或者program_map_PID // }
unsigned CRC_32 : 32; } PAT_Packet; // Parse PAT int Parse_PAT(unsigned char *pTSBuf, PAT_Packet *packet) { TS_header TSheader; if (Parse_TS_packet_header(pTSBuf, &TSheader) != 0) return -1; if (TSheader.payload_unit_start_indicator == 0x01) // 表示含有PSI或者PES头 { if (TSheader.PID == 0x0) // 表示PAT { int iBeginlen = 4; int adaptation_field_length = pTSBuf[4]; switch (TSheader.adaption_field_control) { case 0x0: // reserved for future use by ISO/IEC return -1; case 0x1: // 无调整字段,仅含有效负载 iBeginlen += pTSBuf[iBeginlen] + 1; // + pointer_field break ; case 0x2: // 仅含调整字段,无有效负载 return -1; case 0x3: // 调整字段后含有效负载 if (adaptation_field_length > 0) { iBeginlen += 1; // adaptation_field_length占8位 iBeginlen += adaptation_field_length; // + adaptation_field_length } else { iBeginlen += 1; // adaptation_field_length占8位 } iBeginlen += pTSBuf[iBeginlen] + 1; // + pointer_field break ; default : break ; } unsigned char *pPAT = pTSBuf + iBeginlen; packet->table_id = pPAT[0]; packet->section_syntax_indicator = pPAT[1] >> 7; packet->zero = pPAT[1] >> 6 & 0x1; packet->reserved_1 = pPAT[1] >> 4 & 0x3; packet->section_length = (pPAT[1] & 0x0F) << 8 |pPAT[2]; packet->transport_stream_id = pPAT[3] << 8 | pPAT[4]; packet->reserved_2 = pPAT[5] >> 6; packet->version_number = pPAT[5] >> 1 & 0x1F; packet->current_next_indicator = (pPAT[5] << 7) >> 7; packet->section_number = pPAT[6]; packet->last_section_number = pPAT[7]; int len = 0; len = 3 + packet->section_length; packet->CRC_32 = (pPAT[len-4] & 0x000000FF) << 24 | (pPAT[len-3] & 0x000000FF) << 16 | (pPAT[len-2] & 0x000000FF) << 8 | (pPAT[len-1] & 0x000000FF); int n = 0; for ( n = 0; n < (packet->section_length - 12); n += 4 ) { packet->program_number = pPAT[8 + n ] << 8 | pPAT[9 + n ]; packet->reserved_3 = pPAT[10 + n ] >> 5; if ( packet->program_number == 0x00) { packet->network_PID = (pPAT[10 + n ] & 0x1F) << 8 |pPAT[11 + n ]; } else { // 有效的PMT的PID,然后通过这个PID值去查找PMT包 program_map_PID = (pPAT[10 + n] & 0x1F) << 8 |pPAT[11 + n]; } } return 0; } } return -1; } |
PAT数据解析需要参考:ISO/IEC 13818-1的2.4.4.3 Program Association Table
4.2 PMT解析
由PAT包中的program_map_PID可以确定PMT(Program Map Table)的PID。PMT数据的信息可以理解为这个节目包含的音频和视频信息。
// Program Map Table typedef 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<N; i++) // { unsigned stream_type : 8; unsigned reserved_5 : 3; unsigned elementary_PID : 13; unsigned reserved_6 : 4; unsigned ES_info_length : 12; // } unsigned CRC_32 : 32; } PMT_Packet; // Parse PMT int Parse_PMT(unsigned char *pTSBuf, PMT_Packet *packet) { // 参考Parse_PAT()来做就行了 // ... return 0; } |
PMT数据解析需要参考:ISO/IEC 13818-1的2.4.4.8 Program Map Table
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是Packetized Elementary Stream的简称,是将原始ES流打包后形成的,再将PES经过不同的打包方式可以组成MPEG program stream 和 MPEG transport stream,即PS流和TS流。 PES的组成结构如图,包括6个字节的包头字段,加上3个字节基本流信息字段,根据信息字段的设置可在之后附加其他字段。 <a href="http://www.yunlipiao.com/wp-content/uploads/2013/08/pes.jpg" class="cboxElement" rel="example4" 208"="" style="text-decoration: none; color: rgb(1, 150, 227);">
![]() PES结构 前三字节是包头起始标识字段,内容为0x000001 第四个字节是流ID字段,不同的流ID有不用的意义,如图,音频流ID范围从0xC0到0xDF,视频流ID范围从0xE0到0xEF。 ![]() PES流ID字段 第五六个字节是PES包长度,表示PES包头部在该字段之后的长度,单位是字节 接下来的第七八九字节是PES的扩展头部字段,用于设置流的基本信息,结构如图 ![]() PES可选扩展 第六字节的高两位是标识位,值为10b 第七字节的高两位是PTS和DTS指示位,00表示无PTS无DTS,01禁止使用,10表示PES头部字段会附加PTS结构 ![]() pts结构 11表示PES头部字段会附加PTS和DTS结构 ![]() pts和dts结构 其中PTS和DTS使用的是90KHZ时钟单位,即1PTS表示1/90000秒,PTS和DTS虽然是33位,但占用了5个字节 ESCR FLAG字段设为1,会在头部附加6个字节的ESCR结构,ES RATE FLAG字段设置为1,会在头部附加3个字节ES rate结构,其他标识位如果设置为1也会相应的在头部附加对应字段。 ![]() ES rate结构 ![]() ESCR结构 本文地址:http://www.yunlipiao.com/208.html,出自云里飘博客,转载请保留链接 |
TS格式解析的更多相关文章
- 多媒体文件格式(四):TS 格式
一.TS 格式标准介绍 TS是一种音视频封装格式,全称为MPEG2-TS.其中TS即"Transport Stream"的缩写. 先简要介绍一下什么是MPEG2-TS: DVD的音 ...
- 视音频数据处理入门:FLV封装格式解析
===================================================== 视音频数据处理入门系列文章: 视音频数据处理入门:RGB.YUV像素数据处理 视音频数据处理 ...
- TS流解析 二 *****
1.TS格式介绍 TS:全称为MPEG2-TS.TS即"Transport Stream"的缩写.它是分包发送的,每一个包长为188字节(还有192和204个字节的包).包的结构为 ...
- plist文件、NSUserDefault 对文件进行存储的类、json格式解析
========================== 文件操作 ========================== Δ一 .plist文件 .plist文件是一个属性字典数组的一个文件: .plis ...
- ts 协议解析
pes : http://wenku.baidu.com/link?url=KjcA0qXqZ1bWVQTa8i1YOmygofldSQL7Pjj-zGRw1e_6_LFmVLo5DIWF0SNwVn ...
- MySQL binlog的格式解析
我搜集到了一些资料,对理解代码比较有帮助. 在头文件中binlog_event.h中,有描述 class Log_event_header class Log_event_footer 参见[Myst ...
- JSON格式解析和libjson使用简介(关于cjson的使用示例)
JSON格式解析和libjson使用简介 在阅读本文之前,请先阅读下<Rss Reader实例开发之系统设计>一文. Rss Reader实例开发中,进行网络数据交换时主要使用到了两种数据 ...
- 转:YUV RGB 常见视频格式解析
转: http://www.cnblogs.com/qinjunni/archive/2012/02/23/2364446.html YUV RGB 常见视频格式解析 I420是YUV格式的一种,而Y ...
- 虚拟机VHD格式解析到NTFS文件系统解析
本来的需求是XEN下的镜像取证,但这篇仅包括他支持的一种格式,就是VHD,此项目从头开始大概用了两周时间,中间遇到了很多让人头大的问题,光是思考的笔记就写了十几页纸,不过实际上并没有那么难,主要是很久 ...
随机推荐
- seleniumRC启动及浏览器实例配置
一.firefox浏览器实例配置 1.启动用户配置文件管理器 重要:在启动用户配置文件管理器之前,Firefox必须完全关闭. 1)按 support.cdn.mozilla.net/medi ...
- mongodb创建数据库和配置用户
上一篇我们说了mongodb远程连接配置,今天给大家说下mongodb怎么创建数据库和配置用户,毕竟光有远程连接是不够的,我们还要上去操作和放数据的. 系统:centos 5.x 环境:mon ...
- 【转】Installing OpenCV on Debian Linux
In this post I will describe the process of installing OpenCV(both versions 2.4.2 and 2.4.3) on Debi ...
- HDU POJ 1015 Jury Compromise(陪审团的人选,DP)
题意: 在遥远的国家佛罗布尼亚,嫌犯是否有罪,须由陪审团决定.陪审团是由法官从公众中挑选的.先随机挑选n个人作为陪审团的候选人,然后再从这n个人中选m人组成陪审团.选m人的办法是:控方和辩方会根据对候 ...
- Struts2使用拦截器完成权限控制示例
http://aumy2008.iteye.com/blog/146952 Struts2使用拦截器完成权限控制示例 示例需求: 要求用户登录,且必须为指定用户名才可以查看系统中某个视图资源:否 ...
- 如何进行Hadoop二次开发指导视频下载
本视频适合对Java有一定了解,熟悉java se的Hadoop爱好者,想对Hadoop进行二次开发.下面是以伪分布为例: 想对Hadoop二次开发:一.首先需要Hadoop和Java之间搭建Ecli ...
- BZOJ1901 - Dynamic Rankings(树状数组套主席树)
题目大意 给定一个有N个数字的序列,然后又m个指令,指令种类只有两种,形式如下: Q l r k 要求你查询区间[l,r]第k小的数是哪个 C i t 要求你把第i个数修改为t 题解 动态的区间第k ...
- 从java程序员到CTO的成长路线图
很多新人不知道从事java开发,具体的发展路径是怎么样的,甚至很多人都不能区分程序猿和攻城师的区别.包括不少小白,从事java开发都半年,甚至1年了,对职业发展还没有清晰的认证.这非常不利于自己的发展 ...
- 使用 IntelliJ IDEA 导入 Spark 最新源码及编译 Spark 源代码
前言 其实啊,无论你是初学者还是具备了有一定spark编程经验,都需要对spark源码足够重视起来. 本人,肺腑之己见,想要成为大数据的大牛和顶尖专家,多结合源码和操练编程. 准备工作 1.sca ...
- Java+7入门经典 -2 数据
第2章 程序,数据,变量和计算 2.1 数据和变量 变量是一段有名字的内存, 存储程序中的信息, 描述事物的数据项; 每段定义了名字的内存只能存储一种特定类型的数据. Type; 编译器会检测错误的类 ...