zoukankan      html  css  js  c++  java
  • 精解PSISI(二)

    DVB SI/PSI分析和处理

     

    SI是Specific Information的简称,PSI是program Specific Information.该机制允许DVB传送各种各样的讯息,比如节目名称,电视台名称,各种PID,私有信息,甚至单独传送数据实现数据通信等.这些功能的实现都归功于SI/PSI.

     在DVB 标准中,定义了一个标准的PID用来实现SI/PSI.这些PID是系统保留的,因此DVB编码的时候并不会用这些PID做为Video PID或者 Audio PID或者其他PID.在一个简单的解复用程序中,只需要提供处理PAT,PMT表格的程序即可实现解复用,当然如果需要更友好的界面和实现 更复杂的功能(如CA)则必须处理其他的SI表.在这里仅仅分析PAT,PMT,SDT表格,其他SI表格的分析,请参考ISO13818-1(MPEG-2系统层标准)和EN300468(DVB SI标准)文档.

     DVB定义的SI保留的PID分别是:

      

    上表格的PID就是DVB保留的PID,分配的其他PID一定不会占用这些PID.解复用程序需要使用到的表格只有PAT,PMT,SDT,而CA应用还需要使用CAT,EPG应用还需要使用NIT,EIT,TDT,TOT等表格.所以在需要解复用的时候,伪代码需要这样写:

      void Process_Packet(unsigned char*buff)

      {

       int PID=GETPID(buff);

       if(PID==0x0000) 

       {

        Process_PAT(buff+4);

       }

       else if(PID==......)

       {

       }

       else

       {

        printf("Unknown PID!");

       }

      }

     所 有的表格都开始于Packet中的184字节的数据部分,但有的时候一个表格没有184字节,这时在Packet中就可能插入一些无效信息用来填充使整个 Packet依然保持是188字节.也可能用头信息中的payload_unit_start_indicator标志表格有个偏移位置(当 payload_unit_start_indicator=0表示表格数据直接从Packet区的第四个字节开始,否则表示有一个偏移量位置开始,具体 请参考ISO13818-1,第4字节到偏移量间的数据是系统填充的无效数据).

     下面针对解复用程序详细分析一下PAT,PMT和SDT三类表格的格式.

    PAT, Program Association Table,节目关联表

     PAT表携带以下信息:

    (1) TS流ID--- transport_stream_id,该ID标志唯一的流ID

    (2) 节目频道号-- program_number,该号码标志TS流中的一个频道,该频道可以包含很多的节目(即可以包含多个Video PID和Audio PID)

    (3) PMT的PID--- program_map_PID,表示本频道使用的哪个PID做为PMT的PID,因为可以有很多的频道,因此DVB规定PMT的PID可以由用户自己定义.

    PAT表定义如下:

     

    各字段含义如下:

     table_id:8 bits,标志本表格的类型,应该是0x00

     section_syntax_indicator:1 bit,段语法标志,应该是'1'

     '0':固定的'0',这是为了防止和ISO13818Video流格式中的控制字冲突而设置的.

     Reserved:保留的2bits,保留位一般都是'0'

     section_length:12bits的段大小,单位是Bytes.

     transport_stream_id:16bits的当前流ID,DVB内唯一.(事实上很多都是自定义的TS ID)

     version_number:5bits版本号码,标注当前节目的版本.这是个非常有用的参数,当检测到这个字段改变时,说明TS流中的节目已经变化了,程序必须重新搜索节目.

     current_next_indicator:1bit:当前还是未来使用标志符,一般情况下为'0'

     section_number:8bits当前段号码

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

     从for()开始,就是描述了当前流中的频道数目(N),每一个频道对应的PMT PID是什么.解复用程序需要和上图类似的循环来接收所有的频道号码和对应的PMT PID,并把这些信息在缓冲区中保存起来.在后部的处理中需要使用到PMT PID.

     CRC_32:本段的CRC校验值,一般是会忽略的.N是一个变量,计算方法是N=(section_length-9)/4.

     从以上分析我们可以发现,PAT表主要包含频道号码和每一个频道对应的PMT的PID号码,这些信息我们在处理PAT表格的时候会保存起来,以后会使用到这些数据.例如我们可以定义这样的数据结构保存这些信息:

     typedef struct

     {

      int channel_number;

      int pmt_pid;

     }PMT_ITEM;

     PMT_ITEM pmt[64];

    PMT, Program Map Table,节目影射表

     如果一个TS流中含有多个频道,那么就会包含多个PID不同的PMT表.检测是否PMT的伪代码如下:

     void Process_Packet(unsigned char*buff)

     {

      int I;

      int PID=GETPID(buff);

      if(PID==0x0000)

      {

       Process_PAT(buff+4);

      }

      else if(PID==.....)

      {

      }

      else

      {

       

       for(i=0;i<64;i++)

       {

        if(PID==pmt[i].pmt_pid)

        {

         Process_PMT(buff+4);

         break;

        

       }

      }

     }

    PMT表中包含的数据如下:

    (1) 当前频道中包含的所有Video数据的PID

    (2) 当前频道中包含的所有Audio数据的PID

    (3) 和当前频道关联在一起的其他数据的PID(如数字广播,数据通讯等使用的PID)

    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校验值,一般忽略.

    从以上的分析可以看出,只要我们处理了PMT,那么我们就可以获取频道中所有的PID信息,例如当前频道包含多少个Video,共多少个Audio,和其他数据,还能知道每种数据对应的PID分别是什么.

    这样如果我们要选择其中一个Video和Audio收看,那么只需要把要收看的节目的Video PID和Audio PID保存起来,在处理Packet的时候进行过滤即可实现.

    比较全面实现解复用的伪代码如下:

    int Video_PID=0x07e5,Audio_PID=0x07e6;

    void Process_Packet(unsigned char*buff)

    {

     int I;

     int PID=GETPID(buff);

     if(PID==0x0000)

     {

      Process_PAT(buff+4);

     }

     else if(PID==Video_PID)

     {

      SaveToVideoBuffer(buff+4);

     }

     else if(PID==Audio_PID)

     {

      SaveToAudioBuffer(buff+4);

     }

     else

    {

     for( i=0;i<64;i++)

     {

      if(PID==pmt[i].pmt_pid)

      {

       Process_PMT(buff+4);

       Break;

      }

     }

     

    }

    以上伪代码可以实现基本的解复用:检测所有的频道,检测所有stream的PID,选择特定的节目进行播放.只要读取每个Packet的188字节的内容,然后每次都调用Process_Packet()即可实现简单的解复用.

    介绍到这里,我们就可以总结一下DVB搜台的原理了.(好!洗耳恭听!)

      机 顶盒先调整高频头到一个固定的频率(如498MHZ),如果此频率有数字信号,则COFDM芯片(如MT352)会自动把TS流数据传送给MPEG- decoder. MPEG-2 decoder先进行数据的同步,也就是等待完整的Packet的到来.然后循环查找是否出现PID== 0x0000的Packet,如果出现了,则马上进入分析PAT的处理,获取了所有的PMT的PID.接着循环查找是否出现PMT,如果发现了,则自动进 入PMT分析,获取该频段所有的频道数据并保存.如果没有发现PAT或者没有发现PMT,说明该频段没有信号,进入下一个频率扫描.

      从以上描述可以看出,机顶盒搜索频率是随机发生的,要使每次机顶盒都能搜索到信号,则要求TS流每隔一段时间就发送一次PAT和PMT.事实上DVB传输系统就是这么做的.因此无论何时接入终端系统,系统都能马上搜索到节目并正确解复用实现播放.不仅仅如此,其他数据也都是交替传送的.比如第一个Packet可能是PAT,第二个Packet可能是PMT,而第三个Packet可能是Video 1,第四个Packet可能是Video 2,

    只要系统传输速度足够快(就是称之为"码率"的东东),实现实时播放是没有任何问题的.

     到这里虽然实现了解复用,但可以看出,使用的PID都是枯燥的数字,如果调台要用户自己输入数字那可是太麻烦了,而且还容易输入错误,操作非常不直观,即使做成一个菜单让用户选择也是非常的呆板.针对这个问题,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让用户选择,从而实现比较友好的用户界面!

     下面参考一下<<Seekfor MPEG2 decoder>>中的界面和显示信息.

     

    上 图是<<Seekfor MPEG2 decoder>>打开三个不同的码流文件(*.ts)形成的PID信息和节目名称.用户 可以通过切换节目名称的下拉列表框切换节目,也可以通过"视频流"和"音频流"下拉列表框切换Video和Audio!这些数据都是通过分析PAT, PMT和SDT得到的.

  • 相关阅读:
    智能语音
    设置View大小随屏幕自动放大
    程序经常在第一次启动时崩溃
    苹果员工的休假时间记录
    未解决知识点:edgesForExtendedLayout
    关于iOS的autolayout中导航栏的疑问
    iOS比较好用的抽屉第三方:JASidePanels
    versions使用心得
    使用Xcode7上传app的error整理:ERROR ITMS-90535,ERROR ITMS-90529,ERROR ITMS-90049
    subversions上传新文件Xcode中不显示
  • 原文地址:https://www.cnblogs.com/KUDO/p/1311855.html
Copyright © 2011-2022 走看看