目录

一、TS流相关知识

1.1 TS流、PS流、PES流和ES流都是什么?

1.2 TS流是如何产生的?

1.3TS流的格式

二、从TS流到PAT、PMT

2.1 PAT表(Program Association Table,节目关联表)

2.1.1PAT表的描述(表格+分析)

2.1.2 PAT表的定义(代码+分析)

2.1.3 PAT表的结构(代码+分析)

2.1.4 PAT表的解析(代码+分析)

2.1.5 通过一段TS流中一个Packet分析PAT表(表格+分析)

2.1.6 过滤PAT表信息的伪代码

三、 PMT表(Program Map Table,节目映射表)(Service Descriptor Table)

3.1 PMT表的描述

3.2 PMT表的定义(代码)

3.3 PMT表的结构体定义(代码)

3.4 PMT表的解析(代码)

3.5 通过一端TS流中的一个packet分析PMT表(表格+分析)

四、解复用模型

五、DVB搜台原理以及SDT表(Service Descriptor Table,业务描述表)

六、从PAT开始,走向更远

参考


一、TS流相关知识

数字电视机顶盒接收到的是一段段的码流,我们称之为TS(Transport Stream,传输流),每个TS流都携带一些信息,如Video、Audio以及我们需要学习的PAT、PMT等信息。因此,我们首先需要了解TS流是什么,以及TS流是怎样形成、有着怎样的结构。

1.1 TS流、PS流、PES流和ES流都是什么?

ES流(Elementary Stream):基本码流,不分段的音频、视频或其他信息的连续码流。

PES流:把基本流ES分割成段,并加上相应头文件打包成形的打包基本码流。

PS流(Program Stream):节目流,将具有共同时间基准的一个或多个PES组合(复合)而成的单一数据流(用于播放或编辑系统,如m2p)。

TS流(Transport Stream):传输流,将具有共同时间基准或独立时间基准的一个或多个PES组合(复合)而成的单一数据流(用于数据传输)。

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

由于TS码流具有较强的抵抗传输误码的能力,因此目前在传输媒体中进行传输的MPEG-2码流基本上都采用了TS码流的包格。

1.2 TS流是如何产生的?

从上图可以看出,视频ES和音频ES通过打包器和共同或独立的系统时间基准形成一个个PES,通过TS复用器复用形成的传输流。注意这里的TS流是位流格式(分析Packet的时候会解释),也即是说TS流是可以按位读取的。

1.3TS流的格式

PID是TS流中唯一识别标志,Packet Data是什么内容就是由PID决定的。如果一个TS流中的一个Packet的Packet Header中的PID是0x0000,那么这个Packet的Packet Data就是DVB的PAT表而非其他类型数据(如Video、Audio或其他业务信息)。下表给出了一些表的PID值,这些值是固定的,不允许用于更改。

回顾一下,TS流是一种位流(当然就是数字的),它是由ES流分割成PES后复用而成的;它经过网络传输被机顶盒接收到;数字电视机顶盒接收到TS流后将解析TS流。

下面以一个TS流的其中一个Packet中的 packet header 为例进行说明。

TS流是由一个个Packet(包)构成的,每个包都是由Packet Header(包头)和Packet Data(包数据)组成的。其中Packet Header指示了该Packet是什么属性的,并给出了该Packet Data的数据的唯一网络标识符PID。

到这里,我们对TS流已经有了一定的了解,下面将从TS流转向PAT表和PMT表的学习。

二、从TS流到PAT、PMT

说完了TS流的基本概念,就该开始对TS流进行更深入的研究了。首先需要想一想:TS流的本质是什么?它的确是一段码流,并且是一段由数据包(Packet)组成的码流。那么这些数据包究竟是怎样的呢?它和我们收看的电视节目之间又有什么区别?这些都是这部分需要了解的内容。

在上一节中,我们可以看到PID这个被标红的字段频繁地出现。PID是当前TS流的Packet区别于其他Packet类型的唯一识别符,通过读取每个包的Packet Header,我们可以知道这个Packet的数据属于何种类型。上一节列出了几项固定的PID值,它们用于识别存储了特殊信息的Packet。下面要谈的PAT表的PID值就是固定的0x0000。

2.1 PAT表(Program Association Table,节目关联表)

2.1.1PAT表的描述(表格+分析)

PAT表定义了当前TS流中所有的节目,其PID为0x0000,它是PSI(节目特定信息,Program Specific Information)的根节点,要查寻找节目必须从PAT表开始查找。

PAT表携带以下信息:

标准ISO13818-1中,有PAT的个字段详细描述:

2.1.2 PAT表的定义(代码+分析)

PAT表主要包含频道号码和每一个频道对应的PMT的PID号码,这些信息我们在处理PAT表格的时候会保存起来,以后会使用到这些数据。下面将PAT表的定义给出:

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

2.1.3 PAT表的结构(代码+分析)

再将PAT表的结构体给出:

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;   

2.1.4 PAT表的解析(代码+分析)

接下来给出的是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;
}  

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

2.1.5 通过一段TS流中一个Packet分析PAT表(表格+分析)

如上图所示,PAT表描述了当前流的NIT(Network Information Table,网络信息表)中的PID、当前流中有多少不同类型的PMT表及每个PMT表对应的频道号。上图当中PAT包含一个节目,其对应的PMT为0x1000.

2.1.6 过滤PAT表信息的伪代码

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); }  // 如果PID为0x0000,则该Packet Data为PAT信息,因此调用处理PAT表的函数  else{                                     // 这里buff+4 意味着从Packet Header之后进行解析(包头占4个字节)  ……  }
}  

三、 PMT表(Program Map Table,节目映射表)(Service Descriptor Table)

3.1 PMT表的描述

如果一个TS流中含有多个频道,那么就会包含多个PID不同的PMT表。PMT表中包含的数据如下:

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

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

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

只要我们处理了PMT,那么我们就可以获取频道中所有的PID信息,如当前频道包含多少个Video、共多少个Audio和其他数据,还能知道每种数据对应的PID分别是什么。这样如果我们要选择其中一个Video和Audio收看,那么只需要把要收看的节目的Video PID和Audio PID保存起来,在处理Packet的时候进行过滤即可实现。

3.2 PMT表的定义(代码)

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;

3.3 PMT表的结构体定义(代码)

//PMT 表结构体
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;  

3.4 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_32int 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;
}

3.5 通过一端TS流中的一个packet分析PMT表(表格+分析)

四、解复用模型

int Video_PID=0x07e5,Audio_PID=0x07e6;
void Process_Packet(unsigned char*buff)
{int i; int PID=GETPID(buff);  if(PID==0x0000)       //PAT表的PID为0x0000{ Process_PAT(buff+4); }              else if(PID==Video_PID) //PID指示该数据包为视频包  { SaveToVideoBuffer(buff+4); }  else if(PID==Audio_PID) //PID指示该数据包为音频包{ SaveToAudioBuffer(buff+4); }  else                  // buff+4 意味着要除去buff前4个字节(即包头){                               for( i=0;i<64;i++){ if(PID==pmt[i].pmt_pid) {Process_PMT(buff+4); Break; }} }
}  

五、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,说明该频段没有信号,进入下一个频率扫描。

在解析TS流的时候,首先寻找PAT表,根据PAT获取所有PMT表的PID;再寻找PMT表,获取该频段所有节目数据并保存。这样,只需要知道节目的PID就可以根据PacketHeade给出的PID过滤出不同的Packet,从而观看不同的节目。这些就是PAT表和PMT表之间的关系。而由于PID是一串枯燥的数字,用户不方便记忆、且容易输错,所以需要有一张表将节目名称和该节目的PID对应起来,DVB设计了SDT表来解决这个问题。 该表格标志一个节目的名称,并且能和PMT中的PID联系起来,这样用户就可以通过直接选择节目名称来选择节目了。

SDT可以提供的信息包括:

(1) 该节目是否在播放中

(2) 该节目是否被加密

(3) 该节目的名称

六、从PAT开始,走向更远

在本章的学习中,我们发现了一个特点:所有的TS流的解析都是从寻找PAT表开始的,只有找到了PAT表,我们才能继续下一步的解析。因此,在进行了TS流、PAT表和PMT表的初步知识储备后,在接下来的学习中将从PAT表开始,学习更多的PSI/SI相关的表,将走得更远。

七、海思Hi3798 AVPlAY模块

AVPLAY(Audio Video Player)模块整合SDK 内部音视频播放相关模块,提供给用户基本播放业务相关的接口。AVPLAY 在典型媒体播放终端方案中的位置如图所示。AVPLAY 主要依赖ADEC/VDEC/DEMUX 等模块,其向应用或中间件播放器提供基本的播放业务相关接口。

首先在官方给的sample_ipplayer例程中。首先需要进行一系列的初始化工作,在这里就不一一讲述。主要是讲述如何使用Demux模块来进行节目搜索。

可以按照以上步骤去分析,发现海思官方的编程也是按照先搜寻PAT,搜寻节目,查找PMT,然后再讲解码器与对应的视频相绑定,从而进行解码。

注意在解析PMT的时候,会设置3层filter,第一层是PMT TABLE数据(table id =0x02);第二层和第三层为program id(节目号);

参考

【1】https://blog.csdn.net/qq_31213433/article/details/50571499

TS流基本知识【HI3798 AVPLAY播放TS流】相关推荐

  1. 流处理器知识概述:什么是流处理器?

    什么是流处理器(Stream processor)? 在我们介绍流处理器这个概念之前,首先让我们来了解一下流处理器这个概念是如何演变而来的.早在微软推出的DirectX 7.0当中就曾经提出过一个概念 ...

  2. ffmpeg 3.2版本播放ts流正常,但是录制成为MP4的文件播放黑屏

    终于把黑屏的问题解决了. 场景:用ffmpeg 3.2的库播放ts流或者m3u8文件正常,但是录制成为MP4文件的时候,只有声音,是黑屏的. 解决过程:Step1:采用ffmpeg 2.7进行ts流的 ...

  3. 基于IP播放TS流的码率控制策略

    前言 在数字电视的应用中,TS流数据的播放是其中一个重要环节.TS流的播放从接收端的角度来考虑,是如何保证解码器的缓冲器不出现溢出:从发送端来考虑,是如何保证码流按照其自身的码率较为均匀地离开发送设备 ...

  4. Web端直接播放 .ts 视频及mux.js播放ts视频没有声音

    最近项目中需要前端播放 .ts 格式视频,捣鼓了几天学习到很多知识,也发掘了一种优秀的解决方案,分享给有同样需求的同学. 常见方案 在网上查找的大部分解决方案都是用诸如videojs等网页播放器,接收 ...

  5. [html] HTML5如何播放ts视频流?

    [html] HTML5如何播放ts视频流? 引入mux.js转化,然后video展示 个人简介 我是歌谣,欢迎和大家一起交流前后端知识.放弃很容易, 但坚持一定很酷.欢迎大家一起讨论 主目录 与歌谣 ...

  6. 视频流分片后的ts片,H265压缩。复制时间流命令

    视频太大,想让压缩后播放,怎样压缩叻?一般改变视频屏幕大小或者码率都不能很好的压缩视频,会出现变形失真等情况. 本文说的视频压缩是H265压缩既视频转换格式的方法来达到的. 要求做一个视频上传,前台播 ...

  7. Web端 Html5 直接播放 .ts 视频

    常见方案 在网上查找的大部分解决方案都是用诸如videojs等网页播放器,接收 .m3u8索引文件的方式来播放ts切片.这种方案的缺点是需要后端对原始ts切片做处理,生成 .m3u8索引文件 ffmp ...

  8. PES,TS,PS,RTP等流的打包格式解析之PS流

    本篇描述PS流的封装格式 1.PS头封装格式 PS流是对PES的进一步封装,是将具有共同时间基准的一个或多个PES包组合而成的单一的数据流:其基本单位是PS包,PS流由很多个PS包组成,PS包主要由固 ...

  9. AI云边调度EasyCVR播放HLS流时出现闪屏是什么原因?如何解决?

    随着安防市场的规模不断扩大与发展,EasyCVR快速纵深的视频能力使其已经成为安防行业的主流需求平台,在视频能力上,支持海量视频的汇聚与管理.转码与分发.鉴权管理.智能分析等.平台支持多协议方式接入, ...

  10. 实现h5端播放rtsp流视频--通过ffmpeg转流实现

    实现h5端播放rtsp流视频–通过ffmpeg转流实现 安装包可通过官网下载,也可以评论邮箱发给你 #1. 安装 ffmpeg 解压 ffmpeg.zip 添加系统环境变量 cmd 输入 ffmpeg ...

最新文章

  1. HTML5 localStorage本地儲存
  2. Deep Zoom Composer 正式版发布!
  3. help.hybris.com和help.sap.com网站的搜索实现
  4. vuex的命名空间有哪些_Python3 命名空间和作用域
  5. 一次性说清楚秒验(本机号码一键登录)基本原理、优势、场景、交互过程和常见的问题
  6. select count(*) from返回的类型_数据分析面试题类型汇总
  7. php使用curl发送请求时 添加header失效
  8. 7-34 点赞 (20 分)
  9. java 路由器接口的作用是什么_路由器的接口和用途
  10. android sepolicy报错解决
  11. SAP ALV 负号前置
  12. 斐讯K2刷华硕固件教程
  13. RPGMAKER游戏引擎基于JavaScript的插件制作(一)——前期准备以及RPGMAKER内建逻辑
  14. Type-C PD充电器诱骗取电5V9V12V15V20V,XSP06+锂电池(筋膜枪)充电
  15. 机器学习笔记(八):强化学习
  16. 陶  朱  商  经
  17. Mapreduce Wordcount白名单 Python实现
  18. Material studio 中如何构建方形晶胞
  19. 【Gem5】有关gem5模拟器的资料导航
  20. 鸿蒙适配机型小米,华为鸿蒙适配机型公布 P50首发/小米10S才是真正至尊版

热门文章

  1. 初学Linux的简单命令(一)
  2. 华为机顶盒视频播放代码
  3. Alibaba代码检查工具插件
  4. 客户端的云桌面平台配置与开启(附,登录“云电脑”与切换登录账号)
  5. GO PDF资源 汇总!
  6. iOS手势全屏滑动返回
  7. LabVIEW安装多个NI软件产品时的安装顺序
  8. 现代控制理论-6李雅普诺夫稳定性
  9. 2022大连理工887软件工程初试
  10. Labview操作sqlite数据库