TS即是"Transport Stream"的缩写。他是分包发送的,每一个包长为188字节。在TS流里可以填入很多类型的数据,如视频、音频、自定义信息等。他的包的结构为,包头为4个字节,负载为184个字节(这184个字节不一定都是有效数据,有一些可能为填充数据)。
工作形式:
因为在TS流里可以填入很多种东西,所以有必要有一种机制来确定怎么来标识这些数据。制定TS流标准的机构就规定了一些数据结构来定义。比如: PSI(Program Specific 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    ; ?*.~..??].紿
具体的分析就以这个例子来分析。
  1.  
  2. // Adjust TS packet header
  3. void adjust_TS_packet_header(TS_packet_header* pheader)
  4. {
  5.     unsigned char buf[];
  6.     memcpy(buf, pheader, );
  7.     pheader->transport_error_indicator        = buf[] >> ;
  8.     pheader->payload_unit_start_indicator    = buf[] >>  & 0x01;
  9.     pheader->transport_priority                = buf[] >>  & 0x01;
  10.     pheader->PID                            = (buf[] & 0x1F) <<  | buf[];
  11.     pheader->transport_scrambling_control    = buf[] >> ;
  12.     pheader->adaption_field_control            = buf[] >>  & 0x03;
  13.     pheader->continuity_counter                = buf[] & 0x03;
  14. }
这是一个调整TS流数据包头的函数,这里牵扯到位段调整的问题。现在看一下TS流数据包头的结构的定义:
  1.  
  2. // Transport packet header
  3. typedef struct TS_packet_header
  4. {
  5.     unsigned sync_byte                        : ;
  6.     unsigned transport_error_indicator        : ;
  7.     unsigned payload_unit_start_indicator    : ;
  8.     unsigned transport_priority                : ;
  9.     unsigned PID                            : ;
  10.     unsigned transport_scrambling_control    : ;
  11.     unsigned adaption_field_control            : ;
  12.     unsigned continuity_counter                : ;
  13. } TS_packet_header;
下面我们来分析,在ISO/IEC 13818-1里有说明,PAT(Program Association 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进制数就是:40 00。现在看看我们的TS流片断例子,看来正好是47 40 00开头的,一个TS流的头部占据了4个字节。剩下的负载部分的内容由PID来决定,例子看来就是一个PAT表。在这里有个地方需要注意一下,payload_unit_start_indicator为1时,在前4个字节之后会有一个调整字节,它的数值决定了负载内容的具体开始位置。现在看例子中的数据47 40 00 17 00第五个字节是00,说明紧跟着00之后就是具体的负载内容。
下面给出PAT表的结构体:
  1.  
  2. // PAT table
  3. // Programm Association Table
  4. typedef struct TS_PAT
  5. {
  6.     unsigned table_id                        : ;
  7.     unsigned section_syntax_indicator        : ;
  8.     unsigned zero                            : ;
  9.     unsigned reserved_1                        : ;
  10.     unsigned section_length                    : ;
  11.     unsigned transport_stream_id            : ;
  12.     unsigned reserved_2                        : ;
  13.     unsigned version_number                    : ;
  14.     unsigned current_next_indicator            : ;
  15.     unsigned section_number                    : ;
  16.     unsigned last_section_number            : ;
  17.     unsigned program_number                    : ;
  18.     unsigned reserved_3                        : ;
  19.     unsigned network_PID                    : ;
  20.     unsigned program_map_PID                : ;
  21.     unsigned CRC_32                            : ;
  22. } TS_PAT;
再给出PAT表字段调整函数:
  1.  
  2. // Adjust PAT table
  3. void adjust_PAT_table ( TS_PAT * packet, char * buffer )
  4. {
  5.     int n = , i = ;
  6.     int len = ;
  7.     packet->table_id                    = buffer[];
  8.     packet->section_syntax_indicator    = buffer[] >> ;
  9.     packet->zero                        = buffer[] >>  & 0x1;
  10.     packet->reserved_1                    = buffer[] >>  & 0x3;
  11.     packet->section_length                = (buffer[] & 0x0F) <<  | buffer[];   
  12.     packet->transport_stream_id            = buffer[] <<  | buffer[];
  13.     packet->reserved_2                    = buffer[] >> ;
  14.     packet->version_number                = buffer[] >>  &  0x1F;
  15.     packet->current_next_indicator        = (buffer[] << ) >> ;
  16.     packet->section_number                = buffer[];
  17.     packet->last_section_number            = buffer[];
  18.     // Get CRC_32
  19.     len =  + packet->section_length;
  20.     packet->CRC_32                        = (buffer[len-] & 0x000000FF) << 
  21.                                           | (buffer[len-] & 0x000000FF) << 
  22.                                           | (buffer[len-] & 0x000000FF) << 
  23.                                           | (buffer[len-] & 0x000000FF);
  24.     // Parse network_PID or program_map_PID
  25.     for ( n = ; n < packet->section_length - ; n ++ )
  26.     {
  27.         packet->program_number            = buffer[] <<  | buffer[];
  28.         packet->reserved_3                = buffer[] >> ;
  29.         if ( packet->program_number == 0x0 )
  30.             packet->network_PID = (buffer[] << ) <<  | buffer[];
  31.         else
  32.         {
  33.             packet->program_map_PID = (buffer[] << ) <<  | buffer[];
  34.         }
  35.         n += ;
  36.     }
  37. }
通过上面的分析,例子中的数据00 B0 0D 00 01 C1 00 00 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(Program Map Table)的结构体:
  1.  
  2. // PMT table
  3. // Program Map Table
  4. typedef struct TS_PMT
  5. {
  6.     unsigned table_id                        : ;
  7.     unsigned section_syntax_indicator        : ;
  8.     unsigned zero                            : ;
  9.     unsigned reserved_1                        : ;
  10.     unsigned section_length                    : ;
  11.     unsigned program_number                    : ;
  12.     unsigned reserved_2                        : ;
  13.     unsigned version_number                    : ;
  14.     unsigned current_next_indicator            : ;
  15.     unsigned section_number                    : ;
  16.     unsigned last_section_number            : ;
  17.     unsigned reserved_3                        : ;
  18.     unsigned PCR_PID                        : ;
  19.     unsigned reserved_4                        : ;
  20.     unsigned program_info_length            : ;
  21.    
  22.     unsigned stream_type                    : ;
  23.     unsigned reserved_5                        : ;
  24.     unsigned elementary_PID                    : ;
  25.     unsigned reserved_6                        : ;
  26.     unsigned ES_info_length                    : ;
  27.     unsigned CRC_32                            : ;
  28. } TS_PMT;
在给出调整字段函数:
  1.  
  2. // Adjust PMT table
  3. void adjust_PMT_table ( TS_PMT * packet, char * buffer )
  4. {
  5.     int pos = , len = ;
  6.     int i = ;
  7.     packet->table_id                            = buffer[];
  8.     packet->section_syntax_indicator            = buffer[] >> ;
  9.     packet->zero                                = buffer[] >> ;
  10.     packet->reserved_1                            = buffer[] >> ;
  11.     packet->section_length                        = (buffer[] & 0x0F) <<  | buffer[];   
  12.     packet->program_number                        = buffer[] <<  | buffer[];
  13.     packet->reserved_2                            = buffer[] >> ;
  14.     packet->version_number                        = buffer[] >>  & 0x1F;
  15.     packet->current_next_indicator                = (buffer[] << ) >> ;
  16.     packet->section_number                        = buffer[];
  17.     packet->last_section_number                    = buffer[];
  18.     packet->reserved_3                            = buffer[] >> ;
  19.     packet->PCR_PID                                = ((buffer[] << ) | buffer[]) & 0x1FFF;
  20.     packet->reserved_4                            = buffer[] >> ;
  21.     packet->program_info_length                    = (buffer[] & 0x0F) <<  | buffer[];
  22.     // Get CRC_32
  23.     len = packet->section_length + ;   
  24.     packet->CRC_32                = (buffer[len-] & 0x000000FF) << 
  25.                                   | (buffer[len-] & 0x000000FF) << 
  26.                                   | (buffer[len-] & 0x000000FF) << 
  27.                                   | (buffer[len-] & 0x000000FF);
  28.     // program info descriptor
  29.     if ( packet->program_info_length !=  )
  30.         pos += packet->program_info_length;   
  31.     // Get stream type and PID   
  32.     for ( ; pos <= (packet->section_length +  ) -  ; )
  33.     {
  34.         packet->stream_type                            = buffer[pos];
  35.         packet->reserved_5                            = buffer[pos+] >> ;
  36.         packet->elementary_PID                        = ((buffer[pos+] << ) | buffer[pos+]) & 0x1FFF;
  37.         packet->reserved_6                            = buffer[pos+] >> ;
  38.         packet->ES_info_length                        = (buffer[pos+] & 0x0F) <<  | buffer[pos+];
  39.         // Store in es
  40.         es[i].type = packet->stream_type;
  41.         es[i].pid = packet->elementary_PID;
  42.         if ( packet->ES_info_length !=  )
  43.         {
  44.             pos = pos+;
  45.             pos += packet->ES_info_length;
  46.         }
  47.         else
  48.         {
  49.             pos += ;
  50.         }
  51.         i++;
  52.     }
  53. }
TS流可以复合很多的节目的视频和音频,但是解码器是怎么来区分的呢?答案就在PMT表里,如其名节目映射表。他就是来解决这个问题的。现在看PMT结构体里的stream_type、elementary_PID这两个元素,前一个用来确定后一个作为标识PID的内容具体是什么,音频或视频等。还有要注意他们不只有一个,所以他们是通过循环读取来确保所有的值都被读取了,当然循环也是有规定的(具体看调整函数上)。从例子上来看,我们在倒数第三行找到了上面分析来的PMT表的PID为0x20的TS包。然后就可以把数据是用调整函数填入结构中。然后得到具体节目的PID为视频0x21, 音频0x22。
PS. 文章里的PID是用来判断具体TS包是什么包的。分析每个包得到的PID值,都可以复合在TS头部结构体的PID里。

TS流的解析的更多相关文章

  1. 关于TS流的解析

    字节.在TS流里可以填入很多类型的数据,如视频.音频.自定义信息等.他的包的结构为,包头为4个字节,负载为184个字节(这184个字节不一定都是有效数据,有一些可能为填充数据). 工作形式: 因为在T ...

  2. TS流解析 一

    一 从TS流开始 数字电视机顶盒接收到的是一段段的码流,我们称之为TS(Transport Stream,传输流),每个TS流都携带一些信息,如Video.Audio以及我们需要学习的PAT.PMT等 ...

  3. TS流解析 四

    一 从TS流开始 数字电视机顶盒接收到的是一段段的码流,我们称之为TS(Transport Stream,传输流),每个TS流都携带一些信息,如Video.Audio以及我们需要学习的PAT.PMT等 ...

  4. TS流PAT/PMT详解

    一 从TS流开始 从MPEG-2到DVB,看着看着突然就出现了一大堆表格,什么PAT.PMT.CAT……如此多的表该怎样深入了解呢? 我们知道,数字电视机顶盒接收到的是一段段的码流,我们称之为TS(T ...

  5. TS流分析

    http://blog.csdn.net/zxh821112/article/details/17587215 一 从TS流开始 数字电视机顶盒接收到的是一段段的码流,我们称之为TS(Transpor ...

  6. 从TS流到PAT和PMT

    转自:https://blog.csdn.net/rongdeguoqian/article/details/18214627 一 从TS流开始 最近开始学习数字电视机顶盒的开发,从MPEG-2到DV ...

  7. 分析ffmpeg解析ts流信息的源码

    花费一些时间,然后全部扔了.为了不忘记和抛砖引玉,特发此贴. ffmpeg解析ts流 1.目的     打算软件方式解析出pat,pmt等码流信息 2.源代码所在位置         下载ffmpeg ...

  8. TS流解析 二 *****

    1.TS格式介绍 TS:全称为MPEG2-TS.TS即"Transport Stream"的缩写.它是分包发送的,每一个包长为188字节(还有192和204个字节的包).包的结构为 ...

  9. ffmpeg解析TS流

    介绍:  MPEG的系统层编码为不同的应用场景设计了两种格式:  TS(Transport Stream) 和PS(Program Stream), 它们两者之间不具有层级关系, 在逻辑上,它们两者都 ...

随机推荐

  1. 如何解决请求URL长度超过配置的maxurlLength值问题

    当我们批量请求的数据太多时,会出现请求的url长度超过配置maxurllength值的问题(比如一次性操作1000条数据) 1.问题描述: 我在进行批量选择单据进行发送时,出现这个问题(批量500条) ...

  2. Windows各种各种消息投递函数

    1.SendMessage:发送消息给指定的窗口过程:直到窗口过程处理了消息才返回. 2.PostMessage:将消息放入消息队列(与指定窗口创建的线程相关)中:无需等待消息处理,立即返回.   不 ...

  3. 关于双网卡双宽带Http及Socks代理的配置

    1.[硬件环境] a, 1台宿主(win7)+几十台虚拟机(xp)(vm10的版本,估计可打开52台以上的虚拟机) b, 双网卡,其中一个网卡通过路由连接电信ADSL,一个直连集线器,可直接连接移动m ...

  4. Python Django 之 Template 模板语言简介

    一.什么事模板语言 html+逻辑控制语句 二.模板语言的作用 帮助前端处理后端发来的数据,方便前端展示(杂糅渲染) 三.模板语言语法 1.{{变量}} 变量使用双大括号{{}} 2.万能的句点号. ...

  5. webpack-dev-server将文件产出到指定目录

    默认情况下webpack-dev-server是将文件产出到内存中,写了一个插件 将文件产出到指定目录, 比较简易啊 哈哈哈 使用代码如下 const WebpackDevServerOutput = ...

  6. 2.spring 学习

    1.spring简单工程搭建 http://www.cnblogs.com/yun965861480/p/6278193.html 2.spring数据源 http://www.cnblogs.com ...

  7. STL标准库-迭代器适配器

    技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性 这次主要介绍一下迭代器适配器.以reverse_iterator(反向迭代器),insert_iterator(插入迭代器),o ...

  8. Python 字典的嵌套

    wf = { "name":"汪峰", "age":52, "hobby":["唱歌", " ...

  9. 《TensorFlow实战》读书笔记(完结)

    1 TensorFlow基础 ---1.1TensorFlow概要 TensorFlow使用数据流图进行计算,一次编写,各处运行. ---1.2 TensorFlow编程模型简介 TensorFlow ...

  10. Mac OS 基于 VirtualEnv 的安装 tensorflow 1.3.0

    如果不行的话,就用conda装吧 https://www.jianshu.com/p/d54546ab315e 推荐使用 virtualenv 创建一个隔离的容器, 来安装 TensorFlow. 这 ...