Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4041154
  • 博文数量: 366
  • 博客积分: 9916
  • 博客等级: 中将
  • 技术积分: 7195
  • 用 户 组: 普通用户
  • 注册时间: 2011-05-29 23:27
个人简介

简单!

文章分类

全部博文(366)

文章存档

2013年(51)

2012年(269)

2011年(46)

分类: 嵌入式

2012-09-05 09:31:54

         ISO/IEC-13818-1:系统部分;ISO/IEC-13818-2:视频;ISO/IEC-13818-3:音频;ISO /IEC-13818-4:一致性测试;ISO/IEC-13818-5:软件部分;ISO/IEC-13818-6:数字存储媒体命令与控制;ISO /IEC-13818-7:高级音频编码;ISO/IEC-13818-8:系统解码实时接口;

       MPEG2系统任务包括:1. 规定以包传输数据的协议;2. 规定收发两端数据流同步的协议;3. 提供多个数据流的复用和解复用协议;3. 提供数据流加密的协议。以包形式存储和传送数据流是MPEG2系统之要点。ES是直接从编码器出来的数据流,可以是编码过的视频数据流,音频数据流,或其他编码数据流的统称。ES流经过PES打包器之后,被转换成PES包。PES包由包头和payload组成,具体格式摘录如下:


       可以看到PTS/DTS是打在PES包里面的,这两个parameters是解决视音频同步显示,防止解码器输入缓存上溢或下溢的关键。PTS表示 显示单元出现在系统目标解码器(STD: system target decoder)的时间,DTS表示将存取单元全部字节从STD的ES解码缓存器移走的时刻。每个IPB帧的包头都有一个PTSDTS,但PTSDTSB帧都是一样的,无须标出B帧的DTS。对I帧和P帧,显示前一定要存储于视频解码器的重新排序缓存器中,经过延迟(重新排序)后再显示,一定要分别标明PTSDTS

        ES首先需打包成PES流包,然后PES根据需要打包成PS或TS包进行存储或传输。其每路ES只包含一路信源的编码数据流,所以每路PES也只包含相对应信源的数据流。

        对PS流而言,每个PES包头含有PTS和DTS,流识别码,用于区别不同性质ES。然后通过PS复用器将PES包复用成PS包。实际上是将PES 包分解为更细小的PS包。在解码的时候,解复用器将PS分解成一个个PES包,拆包器然后将PES包拆成视频和音频的ES,最后输入至各自解码器进行解 码。一个问题是:各个ES在解码时,如何保证视音频的同步呢?除了PTS和DTS的配合工作外,还有一个重要的参数是SCR(system clock reference)。在编码的时候,PTS,DTS和SCR都是由STC(system time clock)生成的,在解码时,STC会再生,并通过锁相环路(PLL-phase lock loop),用本地SCR相位与输入的瞬时SCR相位锁相比较,以确定解码过程是否同步,若不同步,则用这个瞬时SCR调整27MHz的本地时钟频率。最 后,PTS,DTS和SCR一起配合,解决视音频同步播放的问题。PS格式摘录如下:


        PS包的长度比较长且可变,主要用于无误码环境里,因为越长的话,同步越困难,且在丢包的情况下,重组也越困难。所以,PS适合于节目信息的编辑和本地内容应用的application。

       TS流也是由一个或多个PES组合而来的,他们可以具有相同的时间基准,也可以不同。其基本的复用思想是,对具有相同时间基准的多个PES现进行节目复用,然后再对相互有独立时间基准的各个PS进行传输复用,最终产生出TS。
 
       TS包由包头和包数据2部分组成,其中包头还可以包括扩展的自适用区。包头长度占4bytes,自使用区和包数据共占184bytes,整个TS包长度相当于4个ATM包长。TS包的包头由如下图摘录所示的同步字节、传输误码指示符、有效载荷单元起始指示符、传输优先、包识别(PID-Packet Identification)、传输加扰控制、自适应区控制和连续计数器8个部分组成。

 

       其中,可用同步字节位串的自动相关特性,检测数据流中的包限制,建立包同步;传输误码指示符,是指有不能消除误码时,采用误码校正解码器可表示1bit 的误码,但无法校正;有效载荷单元起始指示符,表示该数据包是否存在确定的起始信息;传输优先,是给TS包分配优先权;PID值是由用户确定的,解码器根据PIDTS上从不同ES来的TS包区别出来,以重建原来的ES;传输加扰控制,可指示数据包内容是否加扰,但包头和自适应区永远不加扰;自适应区控制,用2 bit表示有否自适应区,即(01)表示有有用信息无自适应区,(10)表示无有用信息有自适应区,(11)表示有有用信息有自适应区,(00)无定义;连续计数器可对PID包传送顺序计数,据计数器读数,接收端可判断是否有包丢失及包传送顺序错误。显然,包头对TS包具有同步、识别、检错及加密功能。

       TS包自适应区由自适应区长、各种标志指示符、与插入标志有关的信息和填充数据4部分组成。其中标志部分由间断指示符、随机存取指示符、ES优化指示符、PCR标志、接点标志、传输专用数据标志、原始PCR标志、自适应区扩展标志8个部分组成。重要的是标志部分的PCR字段,可给编解码器的27MHz时钟提供同步资料,进行同步。其过程是,通过PLL,用解码时本地用PCR相位与输入的瞬时PCR相位锁相比较,确定解码过程是否同步,若不同步,则用这个瞬时PCR调整时钟频率。因为,数字图像采用了复杂而不同的压缩编码算法,造成每幅图像的数据各不相同,使直接从压缩编码图像数据的开始部分获取时钟信息成为不可能。为此,选择了某些(而非全部)TS包的自适应区来传送定时信息。于是,被选中的TS包的自适应区,可用于测定包信息的控制bit和重要的控制信息。自适应区无须伴随每个包都发送,发送多少主要由选中的TS包的传输专用时标参数决定。标志中的随机存取指示符和接点标志,在节目变动时,为随机进入I帧压缩的数据流提供随机进入点,也为插入当地节目提供方便。自适应区中的填充数据是由于PES包长不可能正好转为TS包的整数倍,最后的TS包保留一小部分有用容量,通过填充字节加以填补,这样可以防止缓存器下溢,保持总码率恒定不变。

作为一种传输流,TS将内容进行打包/复用,让其媒体内容变成TS传输,并最终在解码 端解码。简单来看,TS是一个传输层的协议栈,它可以承载各种内容的传输,比如MPEG,WMV,H264,甚至是IP,那么其中的传输规范是如何定义的 呢?这个即是PSI(节目特定信息)要做的事情。

       PSI由四张表构成:PAT,PMT,CAT和NIT,这四张表分别描述了一个TS所 包括的所有ES流的传输结构。首先的一个概念是,TS是以包形式传播,在编解码端都需要以一定的包ID来标识TS流里承载的内容,比如,PAT表会存在于 一个或多个TS包里,所以要用一个特别的包ID来表示,另外,不同的ES流也需要不同的包ID来标识。我们有了PAT和PMT这两种表,解码器就可以根据 PID,将TS上从不同ES来的TS包区分出来进行解码。

       TS的解码分两步进行,其一,是从PID为0 的TS包里,解析出PAT表,然后从PAT表里找到各个节目源的PID,一般此类节目源都由若干个ES流组成,并描述在PMT表里面,然后通过节目源的 PID,就可以在PMT表里检索到各个ES的PID。其二,解码器根据PMT表里的ES流的PID,将TS流上的包进行区分,并按不同的ES流进行解码。 所以,TS是经过节目复用和传输复用两层完成的,即在节目复用时,加入了PMT,在传输复用时,加入了PAT。同样在节目解复用时,可以得到PMT,在传 输解复用时,可以得到PAT。下图很好地概述了其思想。


TS是支持多路复用的,所以它可用来传输经复用后的多层节目。在复用过程中,要注意的是,解码过程中所需要面对的时间参考和同步问题,因为解复用是需要各种信息同步进行的,所以在复用过程中,就需要插入相关的时间信息:PTS,DTS,PCR。

在TS形成过程中,PTS和DTS是在ES打包成PES时,根据STC的参考,将其时钟信息注入PES包中的,而之后在PES切成TS时,再将 PID和PCR信息注入到TS包中,当多路TS再进行复用的时候,各路TS的PCR将会被提取出来,再进行分析,然后再根据统一的STC参考,将新的 PCR生成并注入到TS中去,最后,因为原来PAT表信息不在适用,所以新的PAT表需要再生成,并附加到新的TS流中去。经过这多层的复用之后,新的 TS流即可以进入调制,传输阶段。过程可参见下图:


解码过程要面对的问题是:解复用,视音频的同步,解码缓存器无上下溢。解复用即是将TS在同一信道里不同时序进行传输的节目分离出来;视音频同步由 DTS, PTS和PCR三者协调完成,并且PCR是重建系统时间基准的绝对时标,而DTS和PTS是解码和重现时刻的相对时标;对解码缓存器无上下溢的问题,必须 借助于系统目标解码器(STD)模型来对其进行实现,基本思想如下:

  1. TS流进入解码器后,首先由换向器,按照一定的时序关系,将各种ES流分解出来(其中也包括PSI信息流)。
  2. 分解过后的ES流会进入各自的传输缓存器,通过之后,其PES流进入各自的主存储器,注意的是:PSI信息流会进入系统缓存器,最后也到达主存储器。
  3. 最后,解码器根据DTS信息,从各个主存储器分别提取媒体或系统信息,进行解码,并根据PTS信息,将媒体内容进行显示处理。

其过程可参见下图:



1. PSI依然是"根",只有定位到PSI表后,才能索引出PAT,和各个节目的PMT表。

2. PAT表记录了什么呢?表头信息和节目信息。

    》表头信息包含若干字段,但最关键的是version_number,why?因为TS流里任何节目信息的变化,这个字段都会往上递加,从而使得TS解码器可以据此让Decoder对不同节目做解码复位。

    》节目信息包括TS流里每路节目的program_number,PMT_PID。由此再结合表头信息,TS解码器即可以定位到具体的有变化的节目流(新增或减少)。如下图:


3. 接下来和PAT表平行的就是PMT表了,因为PAT表已经给出明确的节目信息,所以,解码器可以定位到自己需要的节目。在PMT表里,我们可以看到表头信 息,和音视频的stream_type,PID信息等。这样解码器即可以定位到具体的音视频包上,进行节目解码。如下图:



PAT的定义:
Table_id:为8bit字段,该字段标识节目关联分段,对于PAT,置为0x00。

Section_syntax_indicator:1bit字段,对于PAT,置为
0x01。

Reserved:2bit保留字段,用于将来扩展,置为11。

Section_length:12bit字段,指示当前section的长度,计数值
从分段长度下一个字节开始,包括CRC校验的4个字节,开头两
位置为00,因此其大小不超过1021。

Transport_stream_id:16bit字段,当前TS流的ID,与网络中其
他TS流相区别,由运营商指定。

Reserved:2bit保留字段,用于将来扩展,置为11。

Version_number:5bit字段,指出PAT表的版本号,一旦PAT表有变
化,其版本号增1,当增至31时,恢复至0。

Current_next_indicator:1bit,置为1时,表示传送的PAT当
前有效,置为0表示PAT下一次有效。

Section_number:8bit字段,表示section的数目,从0x00开
始。

Last_section_number:8bit字段,指出最后一个section号,
即PAT表section的最大数目。

Program_number:16bit字段,指出了节目对于哪一个PMT
PID是可用的,当为0x00时,后面的PID对应于NIT。

Reserved:3bit保留字段,用于将来扩展,置为111。

Network_id:13bit字段,NIT PID。

Program_map_PID:13bit字段,对应于program_number
所指定的节目的program_map_section的PID,从上面可看出:一个program用4字节来表示(包括16bit的program_number与13bit的PID)。

CRC:用来证实数据正确性的循环冗余校验码。

(section_number和 last_section_number的功能是当PAT内容>184字节时,PAT表会分成多个段(sections),解复用程序必须在全部接收完成后再进行PAT的分析)

 


PMT定义如下: 各字段含义如下:
table_id:8bits的ID,应该是0x02
section_syntax_indicator:1bit的段语法标志,应该是''1'' ''0'':固定是''0'',如果不是说明数据有错.
reserved:2bits保留位,应该是''00''
section_length:16bits段长度,从program_number开始,到CRC_32(包含)的字节总数.
program_number:16bits的频道号码,表示当前的PMT关联到的频道.换句话就是说,当前描述的是program_number频道的信息.
reserved:2bits保留位,应该是''00''
version_number:版本号码,如果PMT内容有更新,则version_number会递增1通知解复用程序需要重新接收节目信息,否则version_number是固定不变的.
current_next_indicator:当前未来标志符,一般是0
section_number:当前段号码
last_section_number:最后段号码,含义和PAT中的对应字段相同,请参考PAT部分.
reserved:3bits保留位,一般是''000''.
PCR_PID:13bits的PCR PID,具体请参考ISO13818-1,解复用程序不使用该参数.
reserved:4bits保留位,一般是''0000''
program_info_length:节目信息长度(之后的是N个描述符结构,一般可以忽略掉,这个字段就代表描述符总的长度,单位是Bytes) 紧接着就是频道内部包含的节目类型和对应的PID号码了.
stream_type:8bits流类型,标志是Video还是Audio还是其他数据.
reserved:3 bits保留位.
elementary_PID:13bits对应的数据PID号码(如果stream_type是Video,那么这个PID就是Video PID,如果stream_type标志是Audio,那么这个PID就是Audio PID)
reserved:4 bits保留位.
ES_info_length:和program_info_length类似的信息长度(其后是N2个描述符号)
CRC_32:32bits段末尾是本段的CRC校验值

 

 

 

SDT的定义
DVB系统提出了一个SDT表格,该表格标志一个节目的名称,并且能和 PMT中的PID联系起来,这样用户就可以通过直接选择节目名称来选择节目了. SDT, Service description section,服务描述段 SDT可以提供的信息包括: (1) 该节目是否在播放中 (2) 该节目是否被加密 (3) 该节目的名称
SDT定义如下: 各字段定义如下:
table_id:8bits的ID,可以是0x42,表示描述的是当前流的信息,也可以是0x46,表示是其他流的信息(EPG使用此参数)
section_syntax_indicator:段语法标志,一般是''1''
reserved_future_used:2bits保留未来使用
reserved:1bit保留位,防止控制字冲突,一般是''0'',也有可能是''1''
section_length:12bits的段长度,单位是Bytes,从transport_stream_id开始,到CRC_32结束(包含)
transport_stream_id:16bits当前描述的流ID
reserved:2bits保留位
version_number:5bits的版本号码,如果数据更新则此字段递增1
current_next_indicator:当前未来标志,一般是''0'',表示当前马上使用.
original_netword_id:16bits的原始网络ID号
reserved_future_use:8bits保留未来使用位
接下来是N个节目信息的循环:
service_id:16 bits的服务器ID,实际上就是PMT段中的program_number.
reserved_future_used:6bits保留未来使用位
EIT_schedule_flag:1bit的EIT信息,1表示当前流实现了该节目的EIT传送
EIT_present_following_flag:1bits的EIT信息,1表示当前流实现了该节目的EIT传送
running_status:3bits的运行状态信息:1-还未播放 2-几分钟后马上开始,3-被暂停播出,4-正在播放,其他---保留
free_CA_mode:1bits的加密信息,''1''表示该节目被加密. 紧 接着的是描述符,一般是Service descriptor,分析此描述符可以获取servive_id指定的节目的节目名称.具体格式请参考 EN300468中的Service descriptor部分.
分析完毕,则节目名称和节目号码已经联系起来了.机顶盒程序就可以用这些节目名称代替 PID让用户选择,从而实现比较友好的用户界面!



H.222规范---PMT的stream_type字段

0x00 

    ITU-T | ISO/IEC Reserved
0x01     ISO/IEC 11172-2 Video
0x02     ITU-T Rec. H.262 | ISO/IEC 13818-2 Video or ISO/IEC 11172-2 constrained parameter video stream
0x03     ISO/IEC 11172-3 Audio
0x04     ISO/IEC 13818-3 Audio
0x05     ITU-T Rec. H.222.0 | ISO/IEC 13818-1 private_sections
0x06     ITU-T Rec. H.222.0 | ISO/IEC 13818-1 PES packets containing private data
0x07     ISO/IEC 13522 MHEG
0x08     ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Annex A DSM-CC
0x09     ITU-T Rec. H.222.1
0x0A     ISO/IEC 13818-6 type A
0x0B     ISO/IEC 13818-6 type B
0x0C     ISO/IEC 13818-6 type C
0x0D     ISO/IEC 13818-6 type D
0x0E     ITU-T Rec. H.222.0 | ISO/IEC 13818-1 auxiliary
0x0F     ISO/IEC 13818-7 Audio with ADTS transport syntax
0x10     ISO/IEC 14496-2 Visual
0x11     ISO/IEC 14496-3 Audio with the LATM transport syntax as defined in ISO/IEC 14496-3/Amd.1
0x12     ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried in PES packets
0x13     ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried in ISO/IEC 14496_sections
0x14     ISO/IEC 13818-6 Synchronized Download Protocol
0x15     Metadata carried in PES packets
0x16     Metadata carried in metadata_sections
0x17     Metadata carried in ISO/IEC 13818-6 Data Carousel
0x18     Metadata carried in ISO/IEC 13818-6 Object Carousel
0x19     Metadata carried in ISO/IEC 13818-6 Synchronized Download Protocol
0x1A     IPMP stream (defined in ISO/IEC 13818-11, MPEG-2 IPMP)
0x1B     AVC video stream as defined in ITU-T Rec. H.264 | ISO/IEC 14496-10 Video
0x1C     ISO/IEC 14496-3 Audio, without using any additional transport syntax, such as DST, ALS and SLS
0x1D     ISO/IEC 14496-17 Text
0x1E     Auxiliary video stream as defined in ISO/IEC 23002-3
0x1F-0x7E ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved
0x7F     IPMP stream
0x80-0xFF User Private

点击(此处)折叠或打开

  1. /*****************************************************************************
  2.  * dvbpsi_GenPATSections
  3.  *****************************************************************************
  4.  * 用传入的p_pat信息构建PAT,i_max_pps参数用于限制每个pat里携带节目信息的数量(最多253个)
  5.  *****************************************************************************/
  6. dvbpsi_psi_section_t* dvbpsi_GenPATSections(dvbpsi_pat_t* p_pat,
  7.                                             int i_max_pps)
  8. {
  9.   dvbpsi_psi_section_t* p_result = dvbpsi_NewPSISection(1024); /*保存返回值*/
  10.   dvbpsi_psi_section_t* p_current = p_result;
  11.   dvbpsi_psi_section_t* p_prev;
  12.   dvbpsi_pat_program_t* p_program = p_pat->p_first_program; /*传入的不同节目PMT所使用的PID号的结构链*/
  13.   int i_count = 0; /*存储已经写入的节目数*/

  14.   /* 一个PAT表最多能携带253个节目信息 */
  15.   if((i_max_pps <= 0) || (i_max_pps > 253))
  16.     i_max_pps = 253;

  17.   p_current->i_table_id = 0; /*标志本表格的类型,PAT应该是0x00*/
  18.   p_current->b_syntax_indicator = 1; /*段语法标志*/
  19.   p_current->b_private_indicator = 0;
  20.   p_current->i_length = 9; /* header + CRC_32 */
  21.   p_current->i_extension = p_pat->i_ts_id; /*16bits的当前流ID,DVB内唯一.(事实上很多都是自定义的TS ID)*/
  22.   p_current->i_version = p_pat->i_version; /*5bits版本号码,标注当前节目的版本.这是个非常有用的参数,当检测到这个字段改变时,说明TS流中的节目已经变化了,程序必须重新搜索节目*/
  23.   p_current->b_current_next = p_pat->b_current_next; /*当前还是未来使用标志符,一般情况下为'0'*/
  24.   p_current->i_number = 0; /*当前段号码*/
  25.   p_current->p_payload_end += 8; /* 跳过8字节的表头信息*/
  26.   p_current->p_payload_start = p_current->p_payload_end; /*未写入数据时,开始和结束在同一位置*/

  27.   /* 向PAT中填入节目信息 */
  28.   while(p_program != NULL)
  29.   {
  30.     /* 如果一个PAT表保存不完,则需新建一个PAT表*/
  31.     if(++i_count > i_max_pps)
  32.     {
  33.       p_prev = p_current;
  34.       p_current = dvbpsi_NewPSISection(1024);
  35.       p_prev->p_next = p_current;
  36.       i_count = 1; /*下面将在新段写入第一个节目信息*/

  37.       p_current->i_table_id = 0;
  38.       p_current->b_syntax_indicator = 1;
  39.       p_current->b_private_indicator = 0;
  40.       p_current->i_length = 9; /* header + CRC_32 */
  41.       p_current->i_extension = p_pat->i_ts_id;
  42.       p_current->i_version = p_pat->i_version;
  43.       p_current->b_current_next = p_pat->b_current_next;
  44.       p_current->i_number = p_prev->i_number + 1;
  45.       p_current->p_payload_end += 8; /* 跳过头,指向节目信息开始*/
  46.       p_current->p_payload_start = p_current->p_payload_end;
  47.     }

  48.     /* p_payload_end 指向节目信息开始处,从这里开始填充数据*/
  49.     p_current->p_payload_end[0] = p_program->i_number >> 8; /*填入16bits的节目号*/
  50.     p_current->p_payload_end[1] = p_program->i_number;
  51.     p_current->p_payload_end[2] = (p_program->i_pid >> 8) | 0xe0; /*0xe0=11100000 保留的三位置1,填入PMT的PID的高5位*/
  52.     p_current->p_payload_end[3] = p_program->i_pid; /*PID的低8位*/

  53.     /* 每个PMT占4字节,所以按4字节递增*/
  54.     p_current->p_payload_end += 4;
  55.     p_current->i_length += 4;

  56.     p_program = p_program->p_next;
  57.   }

  58.   /* 将所有PAT表中的最后段号码设置成最后一个PAT的号码*/
  59.   p_prev = p_result;
  60.   while(p_prev != NULL)
  61.   {
  62.     p_prev->i_last_number = p_current->i_number;
  63.     dvbpsi_BuildPSISection(p_prev); /*调用dvbpsi_BuildPSISection构建PSI包*/
  64.     p_prev = p_prev->p_next;
  65.   }

  66.   return p_result;
  67. }


点击(此处)折叠或打开

  1. /*****************************************************************************
  2.  * dvbpsi_GenPMTSections
  3.  *****************************************************************************
  4.  * 用传入的p_pmt信息构建PMT
  5.  *****************************************************************************/
  6. dvbpsi_psi_section_t* dvbpsi_GenPMTSections(dvbpsi_pmt_t* p_pmt)
  7. {
  8.   dvbpsi_psi_section_t* p_result = dvbpsi_NewPSISection(1024); /*分配空间,用来存放最后结果*/
  9.   dvbpsi_psi_section_t* p_current = p_result;
  10.   dvbpsi_psi_section_t* p_prev;
  11.   dvbpsi_descriptor_t* p_descriptor = p_pmt->p_first_descriptor;
  12.   dvbpsi_pmt_es_t* p_es = p_pmt->p_first_es;
  13.   uint16_t i_info_length; /*存储段或描述符长度*/

  14.   p_current->i_table_id = 0x02; /*表ID为0x02表示此表为PMT*/
  15.   p_current->b_syntax_indicator = 1;
  16.   p_current->b_private_indicator = 0; /*私有标志:0 不是私有数据*/
  17.   p_current->i_length = 13; /* 表头 + 4bytes CRC_32校验数据,从program_number开始,到CRC_32(包含)的字节总数 */
  18.   p_current->i_extension = p_pmt->i_program_number; /*16bits的频道号码,表示当前的PMT关联到的频道*/
  19.   p_current->i_version = p_pmt->i_version; /*版本号码,如果PMT内容有更新,则version_number会递增1通知解复用程序需要重新接收节目信息,否则version_number是固定不变的.*/
  20.   p_current->b_current_next = p_pmt->b_current_next;
  21.   p_current->i_number = 0; /*当前未来标志符,一般是0*/
  22.   p_current->p_payload_end += 12; /* 指向频道描述符开始 */
  23.   p_current->p_payload_start = p_current->p_data + 8; /*跳过表头,指向数据开始*/

  24.   /* 填入PCR信息*/
  25.   p_current->p_data[8] = (p_pmt->i_pcr_pid >> 8) | 0xe0; /*68~81bit的13bit位存储PCR信息,先填入高5位数据*/
  26.   p_current->p_data[9] = p_pmt->i_pcr_pid;

  27.   /* PMT descriptors */
  28.   while(p_descriptor != NULL)
  29.   {
  30.     /* 判断是否需要新的PMT表*/
  31.     /* 已写入的数据长度 + 描述符长度 + 2 > 1024 - CRC_32_length ,2字节用于存储描述符长度*/
  32.     if( (p_current->p_payload_end - p_current->p_data)
  33.                                 + p_descriptor->i_length > 1018)
  34.     {
  35.       /* 节目信息长度,就是紧接着的若干个描述符的总长度*/
  36.       i_info_length = (p_current->p_payload_end - p_current->p_data) - 12; /*减去表头的12个字节*/
  37.       p_current->p_data[10] = (i_info_length >> 8) | 0xf0; /*将长度写入到77~88bit的14bit中,先写入高4位*/
  38.       p_current->p_data[11] = i_info_length;

  39.   /*一个PMT存储不完,构建新的PMT继续存储*/
  40.       p_prev = p_current;
  41.       p_current = dvbpsi_NewPSISection(1024);
  42.       p_prev->p_next = p_current;

  43.       p_current->i_table_id = 0x02;
  44.       p_current->b_syntax_indicator = 1;
  45.       p_current->b_private_indicator = 0;
  46.       p_current->i_length = 13; /* header + CRC_32 */
  47.       p_current->i_extension = p_pmt->i_program_number;
  48.       p_current->i_version = p_pmt->i_version;
  49.       p_current->b_current_next = p_pmt->b_current_next;
  50.       p_current->i_number = p_prev->i_number + 1;
  51.       p_current->p_payload_end += 12; /* just after the header */
  52.       p_current->p_payload_start = p_current->p_data + 8;

  53.       /* PCR_PID */
  54.       p_current->p_data[8] = (p_pmt->i_pcr_pid >> 8) | 0xe0;
  55.       p_current->p_data[9] = p_pmt->i_pcr_pid;
  56.     }

  57.     /* p_payload_end 已经指向描述符开始处 */
  58.     p_current->p_payload_end[0] = p_descriptor->i_tag; /*设置描述符标志*/
  59.     p_current->p_payload_end[1] = p_descriptor->i_length; /*设置描述符长度*/
  60.     memcpy(p_current->p_payload_end + 2, /*将描述符数据填充入p_data中*/
  61.            p_descriptor->p_data,
  62.            p_descriptor->i_length);

  63.     /* 每个描述符只用2个字节来存储标志和长度,所以以2递增*/
  64.     p_current->p_payload_end += p_descriptor->i_length + 2;
  65.     p_current->i_length += p_descriptor->i_length + 2;

  66.     p_descriptor = p_descriptor->p_next;
  67.   }

  68.   /* 写入描述符总长度信息*/
  69.   i_info_length = (p_current->p_payload_end - p_current->p_data) - 12;
  70.   p_current->p_data[10] = (i_info_length >> 8) | 0xf0;
  71.   p_current->p_data[11] = i_info_length;

  72.   /* PMT ESs */
  73.   while(p_es != NULL)
  74.   {
  75.     uint8_t* p_es_start = p_current->p_payload_end;
  76.     uint16_t i_es_length = 5;

  77.     /* 测试当前PMT表能否存储所有描述符 */
  78.     p_descriptor = p_es->p_first_descriptor;
  79.     while( (p_descriptor != NULL)
  80.            && ((p_es_start - p_current->p_data) + i_es_length <= 1020)) /*1020=1024-4 留出4字节的CRC校验数据空间*/
  81.     {
  82.       i_es_length += p_descriptor->i_length + 2; /*2是指存储描述符标志和长度把占用的2字节*/
  83.       p_descriptor = p_descriptor->p_next;
  84.     }

  85.     /* 如果描述符链末空,当前表不是空表,则创建新表*/
  86.     if( (p_descriptor != NULL) /*链未空,仍有描述符未被存储*/
  87.         && (p_es_start - p_current->p_data != 12) /*当前表不是空表*/
  88.         && (i_es_length <= 1008)) /*1008=1024-12-4 ,12字节的表头和4字节的CRC数据,我觉得该判断会始终为真???*/
  89.     {
  90.       /* 将剩余的描述符存入新表中 */
  91.       DVBPSI_DEBUG("PMT generator",
  92.                    "create a new section to carry more ES descriptors");
  93.       p_prev = p_current;
  94.       p_current = dvbpsi_NewPSISection(1024);
  95.       p_prev->p_next = p_current;

  96.       p_current->i_table_id = 0x02;
  97.       p_current->b_syntax_indicator = 1;
  98.       p_current->b_private_indicator = 0;
  99.       p_current->i_length = 13; /* header + CRC_32 */
  100.       p_current->i_extension = p_pmt->i_program_number;
  101.       p_current->i_version = p_pmt->i_version;
  102.       p_current->b_current_next = p_pmt->b_current_next;
  103.       p_current->i_number = p_prev->i_number + 1;
  104.       p_current->p_payload_end += 12; /* just after the header */
  105.       p_current->p_payload_start = p_current->p_data + 8;

  106.       /* PCR_PID */
  107.       p_current->p_data[8] = (p_pmt->i_pcr_pid >> 8) | 0xe0;
  108.       p_current->p_data[9] = p_pmt->i_pcr_pid;

  109.       /* program_info_length */
  110.       i_info_length = 0; /*只携带ES描述符,所以节目信息长度设置为0*/
  111.       p_current->p_data[10] = 0xf0; /*保留的4位置1*/
  112.       p_current->p_data[11] = 0x00;

  113.       p_es_start = p_current->p_payload_end;
  114.     }

  115.     /* p_es_start 已经指向ES数据开始处,写入ES相关信息 */
  116.     p_es_start[0] = p_es->i_type;
  117.     p_es_start[1] = (p_es->i_pid >> 8) | 0xe0; /*用14bit位存储ES PID,先写入高4位*/
  118.     p_es_start[2] = p_es->i_pid;

  119.     /* 占用了5个字节 */
  120.     p_current->p_payload_end += 5;
  121.     p_current->i_length += 5;

  122.     /* ES descriptors */
  123.     p_descriptor = p_es->p_first_descriptor;
  124.     while( (p_descriptor != NULL)
  125.            && ( (p_current->p_payload_end - p_current->p_data)
  126.                 + p_descriptor->i_length <= 1018)) /*ES描述符不为空及空间足够,已用空间+4字节CRC+2字节(tag和length)<1018*/
  127.     {
  128.       /* p_payload_end is where the descriptor begins */
  129.       p_current->p_payload_end[0] = p_descriptor->i_tag;
  130.       p_current->p_payload_end[1] = p_descriptor->i_length;
  131.       memcpy(p_current->p_payload_end + 2,
  132.              p_descriptor->p_data,
  133.              p_descriptor->i_length);

  134.       /* 向后移动 descriptor_length + 2 */
  135.       p_current->p_payload_end += p_descriptor->i_length + 2;
  136.       p_current->i_length += p_descriptor->i_length + 2;

  137.       p_descriptor = p_descriptor->p_next;
  138.     }

  139.     if(p_descriptor != NULL)
  140.       DVBPSI_ERROR("PMT generator", "unable to carry all the ES descriptors");

  141.     /* ES_info_length */
  142.     i_es_length = p_current->p_payload_end - p_es_start - 5;
  143.     p_es_start[3] = (i_es_length >> 8) | 0xf0; /*写入ES描述符的总长度*/
  144.     p_es_start[4] = i_es_length;

  145.     p_es = p_es->p_next;
  146.   }

  147.   /* Finalization */
  148.   p_prev = p_result;
  149.   while(p_prev != NULL)
  150.   {
  151.     p_prev->i_last_number = p_current->i_number;
  152.     dvbpsi_BuildPSISection(p_prev);
  153.     p_prev = p_prev->p_next;
  154.   }

  155.   return p_result;
  156. }


阅读(9039) | 评论(0) | 转发(2) |
1

上一篇:视频播放的基本原理

下一篇:ECM & EMM

给主人留下些什么吧!~~