业余整理的工作笔记

mpeg2-ts

2020-04-19  本文已影响0人  龙遁流

ISO/IEC标准13818-1
MPEG2-TS面向的传输介质是地面和卫星等可靠性较低的传输介质 TS通常以固定码率传输

【主要概念】
TS分组(packet) TS的基本传输单位,以固定的同步字节起始0x47,头的一部分。TS分组的必选头长度为4字节,其后为可选部分,
为载荷或适配域。TS分组的头部固定以大端序读写。TS分组长度为188字节。或204字节,最后加了16字节CRC

【PID】
每一种PSI表和每个ES都对应一个PID值,是将TS解复用为ES和PSI的依据。

【PSI】节目专用信息 Program Specific Information,4种表
PAT 节目关联表 Program Association Table
PMT 节目映射表 Program Map Table
CAT 条件访问表 Conditional Access Table
NIT 网络信息表 Network Information Table

【PAT】
列出该TS内所有节目,PID固定为0x0000。每个节目由16bit program_number 指定。每个节目都有一个PID,指定该节目的PMT;
PAT中不包含节目信息时,program_number为0x0000,则应从NIT(PID为0x0010)获取节目信息。
PAT表定义了当前TS流中所有的节目,是PSI的根节点,要查寻找节目必须从PAT表开始查找。

携带的重要信息:
TS流ID(transport_stream_id)该ID标志唯一的流ID

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

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

typedef struct TS_PAT_Program  
{  
    unsigned program_number   :  16; // 节目号  
    unsigned program_map_PID  :  13; // 节目映射表的PID,节目号大于0时对应的PID,每个节目对应一个  
}TS_PAT_Program;

typedef struct TS_PAT  
{  
    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;  // 最后一个分段的号码  
    std::vector<TS_PAT_Program> program;  
    unsigned reserved_3                   : 3;  // 保留位  
    unsigned network_PID                  : 13; // 网络信息表(NIT)的PID,节目号为0时对应的PID为network_PID  
    unsigned CRC_32                       : 32; // CRC32校验码  
} TS_PAT;   

HRESULT CTS_Stream_Parse::adjust_PAT_table( TS_PAT * packet, unsigned char * buffer)  
{  
    packet->table_id                    = buffer[0];  
    packet->section_syntax_indicator    = buffer[1] >> 7;  
    packet->zero                        = buffer[1] >> 6 & 0x1;  
    packet->reserved_1                  = buffer[1] >> 4 & 0x3;  
    packet->section_length              = (buffer[1] & 0x0F) << 8 | buffer[2];   
   
    packet->transport_stream_id           = buffer[3] << 8 | buffer[4];  
   
    packet->reserved_2                    = buffer[5] >> 6;  
    packet->version_number                = buffer[5] >> 1 &  0x1F;  
    packet->current_next_indicator        = (buffer[5] << 7) >> 7;  
    packet->section_number                = buffer[6];  
    packet->last_section_number           = buffer[7];   
  
    int len = 0;  
    len = 3 + packet->section_length;  
    packet->CRC_32                        = (buffer[len-4] & 0x000000FF) << 24 | (buffer[len-3] & 0x000000FF) << 16  
                                          | (buffer[len-2] & 0x000000FF) << 8  | (buffer[len-1] & 0x000000FF);   
   
    int n = 0;  
    for ( n = 0; n < packet->section_length - 12; n += 4 )  
    {  
        unsigned program_num = buffer[8 + n] << 8 | buffer[9 + n];    
        packet->reserved_3   = buffer[10 + n] >> 5;   
    
        packet->network_PID = 0x00;  
        if (program_num == 0x00)  
        {    
            packet->network_PID = (buffer[10 + n] & 0x1F) << 8 | buffer[11 + n];  
  
            TS_network_Pid = packet->network_PID; //记录该TS流的网络PID  
  
            TRACE(" packet->network_PID %0x /n/n", packet->network_PID );  
        }  
        else  
        {  
            TS_PAT_Program PAT_program;  
            PAT_program.program_map_PID = (buffer[10 + n] & 0x1F) << 8 | buffer[11 + n];  
            PAT_program.program_number = program_num;  
            packet->program.push_back( PAT_program );  
            TS_program.push_back( PAT_program ); // 向全局PAT节目数组中添加PAT节目信息       
        }           
    }  
    return 0;  
}

int Video_PID=0x07e5,Audio_PID=0x07e6;  
void Process_Packet(unsigned char*buff)  
{ 
    int I; 
    int PID=GETPID(buff);  
    if(PID==0x0000) // 如果PID为0x0000,则该Packet Data为PAT信息,因此调用处理PAT表的函数  
    { 
        Process_PAT(buff+4); 
    }  
    else // 这里buff+4 意味着从Packet Header之后进行解析(包头占4个字节)  
    {                     
        ……  
    }  
}

【PMT】
包含特定节目的program_number,以及该节目对应的所有ES的PID
(1) 当前频道中包含的所有Video数据的PID

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

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

typedef struct TS_PMT_Stream  
{  
    unsigned stream_type          : 8;  // 指示特定PID的节目元素包的类型。该处PID由elementary PID指定  
    unsigned elementary_PID       : 13; // 该域指示TS包的PID值。这些TS包含有相关的节目元素  
    unsigned ES_info_length       : 12; // 前两位bit为00。该域指示跟随其后的描述相关节目元素的byte数  
    unsigned descriptor;  
}TS_PMT_Stream;

typedef struct TS_PMT  
{  
    unsigned table_id                        : 8;  // 固定为0x02, 表示PMT表  
    unsigned section_syntax_indicator        : 1;  // 固定为0x01  
    unsigned zero                            : 1;  // 0x01  
    unsigned reserved_1                      : 2;  // 0x03  
    unsigned section_length                  : 12; // 首先两位bit置为00,它指示段的byte数,由段长度域开始,包含CRC。  
    unsigned program_number                  : 16; // 指出该节目对应于可应用的Program map PID  
    unsigned reserved_2                      : 2;  // 0x03  
    unsigned version_number                  : 5;  // 指出TS流中Program map section的版本号  
    unsigned current_next_indicator          : 1;  // 当该位置1时,当前传送的Program map section可用;  
                                                   // 当该位置0时,指示当前传送的Program map section不可用,下一个TS流的Program map section有效。  
    unsigned section_number                  : 8;  // 固定为0x00  
    unsigned last_section_number             : 8;  // 固定为0x00  
    unsigned reserved_3                      : 3;  // 0x07  
    unsigned PCR_PID                         : 13; // 指明TS包的PID值,该TS包含有PCR域,  
                                                   // 该PCR值对应于由节目号指定的对应节目。  
                                                   // 如果对于私有数据流的节目定义与PCR无关,这个域的值将为0x1FFF。  
    unsigned reserved_4                      : 4;  // 预留为0x0F  
    unsigned program_info_length             : 12; // 前两位bit为00。该域指出跟随其后对节目信息的描述的byte数。  
      
    std::vector<TS_PMT_Stream> PMT_Stream;         // 每个元素包含8位, 指示特定PID的节目元素包的类型。该处PID由elementary PID指定  
    unsigned reserved_5                      : 3;  // 0x07  
    unsigned reserved_6                      : 4;  // 0x0F  
    unsigned CRC_32                          : 32;   
} TS_PMT;

HRESULT CTS_Stream_Parse::adjust_PMT_table ( TS_PMT * packet, unsigned char * buffer )  
{   
    packet->table_id                            = buffer[0];  
    packet->section_syntax_indicator            = buffer[1] >> 7;  
    packet->zero                                = buffer[1] >> 6 & 0x01;   
    packet->reserved_1                          = buffer[1] >> 4 & 0x03;  
    packet->section_length                      = (buffer[1] & 0x0F) << 8 | buffer[2];      
    packet->program_number                      = buffer[3] << 8 | buffer[4];  
    packet->reserved_2                          = buffer[5] >> 6;  
    packet->version_number                      = buffer[5] >> 1 & 0x1F;  
    packet->current_next_indicator              = (buffer[5] << 7) >> 7;  
    packet->section_number                      = buffer[6];  
    packet->last_section_number                 = buffer[7];  
    packet->reserved_3                          = buffer[8] >> 5;  
    packet->PCR_PID                             = ((buffer[8] << 8) | buffer[9]) & 0x1FFF;  
  
    PCRID = packet->PCR_PID;  
  
    packet->reserved_4                          = buffer[10] >> 4;  
    packet->program_info_length                 = (buffer[10] & 0x0F) << 8 | buffer[11];   
    // Get CRC_32  
    int len = 0;  
    len = packet->section_length + 3;      
    packet->CRC_32                = (buffer[len-4] & 0x000000FF) << 24 | (buffer[len-3] & 0x000000FF) << 16  
                                  | (buffer[len-2] & 0x000000FF) << 8  | (buffer[len-1] & 0x000000FF);   
  
    int pos = 12;  
    // program info descriptor  
    if ( packet->program_info_length != 0 )  
        pos += packet->program_info_length;      
    // Get stream type and PID      
    for ( ; pos <= (packet->section_length + 2 ) -  4; )  
    {  
        TS_PMT_Stream pmt_stream;  
        pmt_stream.stream_type =  buffer[pos];  
        packet->reserved_5  =   buffer[pos+1] >> 5;  
        pmt_stream.elementary_PID =  ((buffer[pos+1] << 8) | buffer[pos+2]) & 0x1FFF;  
        packet->reserved_6     =   buffer[pos+3] >> 4;  
        pmt_stream.ES_info_length =   (buffer[pos+3] & 0x0F) << 8 | buffer[pos+4];  

        pmt_stream.descriptor = 0x00;  
        if (pmt_stream.ES_info_length != 0)  
        {  
            pmt_stream.descriptor = buffer[pos + 5];  
             
            for( int len = 2; len <= pmt_stream.ES_info_length; len ++ )  
            {  
                pmt_stream.descriptor = pmt_stream.descriptor<< 8 | buffer[pos + 4 + len];  
            }  
                pos += pmt_stream.ES_info_length;  
            }  
            pos += 5;  
            packet->PMT_Stream.push_back( pmt_stream );  
            TS_Stream_type.push_back( pmt_stream );  
        }  
        return 0;  
}

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); }              //PAT表的PID为0x0000  
  else if(PID==Video_PID) { SaveToVideoBuffer(buff+4); }  //PID指示该数据包为视频包  
  else if(PID==Audio_PID) { SaveToAudioBuffer(buff+4); }  //PID指示该数据包为音频包  
  else{                               // buff+4 意味着要除去buff前4个字节(即包头)  
      for( i=0;i<64;i++)  
      { if(PID==pmt[i].pmt_pid) { Process_PMT(buff+4); Break; } }
    } 
}  

【CAT】
用于节目的加密,PID为0x0001

【NIT】
提供TS的相关信息,如频率、调制方式。在扫描所有频道的节目时,关键信息来自各频道的TS中的NIT。

【PCR】
节目时钟参考 Program Clock Reference,使得解码后的内容可以正确地同步播放。最多每100ms,接收方会从TS分组的适配域中得到特定节目的PCR值,
PCR的PID由该节目的PMT中的PCR_PID域指定。解码系统应当基于PCR生成高精度的系统校时时钟(System Timing Clock,STC),用于同步声音ES和视频ES的内容。
STC是MPEG-2系统里校时的基准。例如,表示时间戳(Presentation timestamp,PTS)的值即是以PCR值为基准的偏移量。PCR包括一个33比特的低精度部分(90kHz)
和一个9比特的高精度部分(27MHz,取值为0-299)。PCR容许的最大抖动为+/-500ns。

ES流(Elementary Stream):基本码流,不分段的音频、视频或其他信息的连续码流。
PES流:把基本流ES分割成段,并加上相应头打包成形的打包基本码流。
PS流(Program Stream):节目流,将具有共同时间基准的一个或多个PES组合(复合)而成的单一数据流(用于播放或编辑系统,如m2p)。
TS流(Transport Stream):传输流,将具有共同时间基准或独立时间基准的一个或多个PES组合(复合)而成的单一数据流(用于数据传输)。

【TS流和PS流的区别】
TS流的包结构是长度是固定的;PS流的包结构是可变长度的。这导致了TS流的抵抗传输误码的能力强于PS流(TS码流由于采用了固定长度的包结构,当传输误码破坏了某一TS包的同步信息时,
接收机可在固定的位置检测它后面包中的同步信息,从而恢复同步,避免了信息丢失。而PS包由于长度是变化的,一旦某一 PS包的同步信息丢失,接收机无法确定下一包的同步位置,
就会造成失步,导致严重的信息丢失。因此,在信道环境较为恶劣,传输误码较高时,一般采用TS码流;而在信道环境较好,传输误码较低时,一般采用PS码流。)
由于TS码流具有较强的抵抗传输误码的能力,因此目前在传输媒体中进行传输的MPEG-2码流基本上都采用了TS码流的包格式。

TS流是基于Packet的位流格式,每个包是188个字节(或204个字节,在188个字节后加上了16字节的CRC校验数据,其他格式一样)

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

而由于PID是一串枯燥的数字,用户不方便记忆、且容易输错,所以需要有一张表将节目名称和该节目的PID对应起来,DVB设计了SDT表来解决这个问题。
该表格标志一个节目的名称,并且能和PMT中的PID联系起来,这样用户就可以通过直接选择节目名称来选择节目了。

SDT可以提供的信息包括: Service Descriptor Table,业务描述表
(1) 该节目是否在播放中
(2) 该节目是否被加密
(3) 该节目的名称

payload_unit_start_indicator:负载开始标志位,针对不同的负载,有不同的含义
(1)PES:
置为1,标识TS包的有效净荷以PES包的第一个字节开始,即此TS包为PES包的起始包,且此TS分组中有且只有一个PES包的起始字段;置为0,表示TS包不是PES包的起始包,是后面的数据包。
(2)PSI:
置为1,表示TS包中带有PSI数据分段的第一个字节,即这个TS包是PSI Section的起始包,则此TS包的负载(payload)的第一个字节带有pointer_field,用来指示PSI数据的在payload 中的位置;
置为0,表示TS包不带有PSI Section的第一个字节,即此TS包不是PSI的起始包,即在有效负载中没有point_field,有效负载的开始就是PSI的数据内容。point_field的定义将在下面的
PSI节中进行介绍;对于空包的包,payload_unit_start_indicator应该置为0。
例如:若TS包载荷为PAT,则当接收到的TS包的payload_unit_start_indicator为1时,表明这个TS包包含了PAT头信息,从这个包里面解析出section_length和continuity_counter,
然后继续收集后面的payload_unit_start_indicator = 0的TS包,并判断continuity_counter的连续性,不断读出TS包中的净载荷(也就是PAT数据),用section_length作为收集TS包结束条件。

调整字段,一般在以下两种情况会在TS流中添加自适应段,并且此时的TS header 中的 adaptation_field_control == 1x时,以下字段才会存在 :
(1)封装TS数据的时候,视频或者音频数据不够184个字节的时候,使用该段来指明调整字段0xFF的长度,此时的自适应的区的PCR_flag 标志为0;
(2)对于每一帧视频数据进行封装的开始,需要并且是必须在TS header 之后添加自适应区间,此时,自适应区间中最重要的部分是PCR相关数据,PCR主要用来实现解码端的时钟同步,
具有很重要的意义。此时的自适应区的PCT_flag 标志为1;

ts文件分为三层:ts层(Transport Stream)、pes层(Packet Elemental Stream)、es层(Elementary Stream)。
es层就是音视频数据,pes层是在音视频数据上加了时间戳等对数据帧的说明信息,ts层是在pes层上加入了数据流识别和传输的必要信息。
TS文件判断的方法一般为前5个包都是188字节(0x47开头,2..5*188都是0x47),则认为是包大小为188字节。\color{red}
{注意,204字节的包最后16字节是FEC前向纠错,他们都不计入段长的}

【ffmpeg 时间概念】
time_base :时间基,度量时间,将1s分成90000份,每一个刻度就是1/90000s,则time_base = {1, 90000}。 pts的值就是多少个时间基

时间基的转换:不同的封装格式,time_base不一样,对应的pts值是不一样的。
在ffmpeg中,非压缩的数据AVFrame,时间基为AVCodecContext的time_base, AVRational{1,25}。压缩后的数据,AVPacket,时间基为AVStream的time_base,AVRational{1,90000}。

pcr是节目时钟参考,pcr、dts、pts都是对同一个系统时钟的采样值,pcr是递增的,因此可以将其设置为dts值,音频数据不需要pcr

Program association section
Conditional access section
Transport Stream program map section
Private section
【PES】
PTS/DTS = (PTS1 & 0x0e) << 29 + (PTS2 & 0xfffe) << 14 + (PTS3 & 0xfffe ) >> 1;

ts_dvb搜台原理.png ts_packet结构.png ts_packet结构_适配域.png ts_pat实例.png ts_pes_PTSDTS.png ts_pes结构.png ts_pes结构图.png ts_pmt_streamtype.png ts_pmt实例.png ts_psi结构.png ts_ts-pes-es各层结构.png ts_可用pid值.png ts_流产生流程.png ts_流产生流程1.png ts_流结构.png
上一篇 下一篇

猜你喜欢

热点阅读