MPEG-2 学习笔记

这篇博客详细介绍了MPEG-2规范中的传输流和节目流,包括ES、PES、TS和PS等概念,以及PAT、PMT、CAT和NIT等关键表格的作用和数据结构。博主通过学习和理解,阐述了如何从TS分组中解码获取原始流数据,并探讨了搜台过程涉及的技术。同时,还分享了一个用于分析TS流数据的小工具。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

MPEG-2 学习笔记

 

最近有点时间,看了一部分 MPEG-2 的规范,看后想总结点东西,算是做了点作业,另外希望能和大家讨论讨论,请大家指点。

中文版很多概念翻译得很模糊,不易理解,但总体来说还算是不错,适合像我这种入门级别的看,不过建议和英文版对照看,对一些概念能比较准确的理解。整个规范包括三部分:系统,视频编码,音频编码。对应的标准号分别为 ISO/IEC 13818-1 ISO/IEC 13818-2 ISO/IEC 13818-3 ,在规范中经常可以看到这几个字符串。

第一部分“系统”和我们现在的工作关系较紧密,我也主要学习了第一部分。后面两部分主要是讲解编码过程,编码部分看了实在让人犯晕,先偷一下懒吧,把第一部分搞清楚了再看去啃难啃的骨头吧。

下面进入正题了。

 

一、概念

规范中讲述的概念很多,容易让人糊涂,所以先把一些概念理清,弄清楚它们之间的关系,再看后面的就可提高很多的效率。

(1)ES- Elementary Streams ( 原始流 ) ,对视频、音频信号及其他数据进行编码压缩后

的数据流称为原始流。原始流包括访问单元,比如视频原始流的访问单元就是一副图像的编

码数据。

(2) PES- Packetized Elementary Streams ( 分组的原始流 ) ,原始流形成的分组称为 PES 分组,是用来传递原始流的一种数据结构

(3) 节目是节目元素的集合。节目元素可能是原始流,这些原始流有共同的时间基点,用来做同步显示。

(4) 传输流和节目流

TS-Transport Stream 翻译为 传输流

PS-Program Stream  翻译为 节目流

PS 用来传输和保存一道节目的编码数据或其他数据。 PS 的组成单位是 PES 分组。

TS 用来传输和保存多道节目的编码数据或其他数据, TS 的组成单位是节目。

PS 适用于不容易发生错误的环境,以及涉及到软件处理的应用,典型应用如 DVD 光盘的文件存储

TS 适用于容易发生错误的环境,典型应用就是数字电视信号的传输。

TS PS 是可以互相转换的,比如从 TS 中抽取一道节目的内容并产生有效的 PS 是可能。

  (5) 传输流分组和 PES 分组

原始流分成很多 PES 分组,保持串行顺序,一个 PES 分组只包含一个原始流的编码数据。 PES 分组长度很大,最大可为 64K 字节。

PES 分组分为 分组首部 (header)” 有效负载 (payload)” 有效负载 指跟随在首部字节之后的字节。首部的前 4 个字节构成分组的起始码,标识了该分组所属原始流的类型和 ID 号。

 

TS 分组也就是传输流数据形成的数据包。每个 TS 分组长度为 188 字节,包括 分组首部 有效负载,前 4 个字节是分组首部,包含了这个分组的一些信息。有些情况下需要更多的信息时,需在后面添加 调整字段 (adaption field)”

 

 

 


两者之间的关系:

    PES 分组是插入到 TS 分组中的,每个 PES 分组首部的第一字节就是 TS 分组有效负载的第一字节。一个 PID 值的 TS 分组只带有来自一个原始流的数据。

PMT (Program Map Table ) 节目映射表

PMT 所在分组的 PID PAT 指定,所以要先解出 PAT ,再解 PMT

PMT 中包含了属于同一节目的视频、音频和数据原始流的 PID

找到了 PMT ,解多路复用器就可找到一道节目对应的每个原始流的 PID ,再根据原始流

PID ,去获取原始流。如下图: PID1 PID2 分别对应某道节目的视频原始流和音频原始流

PID

 

l        PAT (Program Association  Table ) 节目关联表

l        PAT 所在分组的 PID=0

      PAT 中列出了传输流中存在的节目流

l   

l        PAT 指定了传输流中每个节目对应 PMT 所在分组的 PID

l        PAT 的第一条数据指定了 NIT 所在分组的 PID ,其他数据指定了 PMT 所在分组的 PID ,如下图所示:

 

 

 

l  CAT (Conditional Access Table ) 条件访问表

l  CAT 所在分组的 PID=1

l  CAT 中列出了条件控制信息 (ECM) 和条件管理信息 (EMM) 所在分组的 PID

l  CAT 用于节目的加密和解密

 

l  NIT( Network Information Table) 网络信息表

l  NIT 所在分组的 PID PAT 指定

l  NIT 提供一组传输流的相关信息,以及于网络自身特性相关的信息,比如网络名称,传输参数 ( 如频率 , 调制方式等 )

l  NIT 一般是解码器内部使用的数据,当然也可以做为 EPG 的一个显示数据提供给用户做为参考。

 

 

几种 PSI 之间的关系,如下图所示:首先 PAT 中指定了传输流中所存在的节目,及每个节目对应的 PMT PID 号。   比如 Program 1 对应的 PMT PID=22, 然后找到 PID=22 TS 分组,解出 PMT ,得到这个节目中包含的原始流的 PID ,再根据原始流的 PID 去找相应的 TS 分组,获取原始流的数据,然后就可以送入解码器解码了。


 

(5)PSI

     全称 Program Specific Information ,意为节目专用信息。传输流中是多路节目复用的,那么,怎么知道这些节目在传输流中的位置,区分属于不同节目呢?所以就还需要一些附加信息,这就是 PSI PSI 也是插入到 TS 分组中的,它们的 PID 是特定值。

         MPEG-2 中规定了 4 PSI ,包括 PAT( 节目关联表 ) CAT( 条件访问表 ) PMT( 节目映射表 )

NIT( 网络信息表 ) ,这些 PSI 包含了进行多路解调和显示节目的必要的和足够的信

息。                             

         具体的应用中可能包括更多的信息,比如 DVB-T 中定义了 SDT( 服务描述表 ),EIT( 环境信息表 ),BAT( 节目组相关表 ),TDT( 时间日期表 ) 等,统称为 DVB-SI( 服务信息 )

    

l      PSI PID 是特定的,含 PSI 的数据包必须周期性的出现在传输流中。

二、数据结构

1 TS 分组

前面提到, TS 分组由 188 个字节构成,其结构如下:  

 

 

transport_packet()

{  
 sync_byte                                                                    // 8 
 transport_error_indicator                                          //1 
 payload_unit_start_indicator                                    //1 
 transport_priority                                                       // 1  

PID                                                                             //13 
 transport_scrambling_control                                  // 2
 adaptation_field_control                                            //2 
 continuity_counter                                                      //4 
 if(adaptation_field_control=='10'  || adaptation_field_control=='11'){  
  adaptation_field()  
 }  
 if(adaptation_field_control=='01' || adaptation_field_control=='11') {  
  for (i=0;i<N;i++){  
   data_byte                                                                   //8 
  }  
 }  
}

 

 

前面 32bit 的数据即 TS 分组首部,它指出了这个分组的属性。

sync_byte   同步字节,固定为 0x47 ,表示后面的是一个 TS 分组,当然,后面包中的数据是不会出现 0x47

transport_error_indicator 传输错误标志位,一般传输错误的话就不会处理这个包了

payload_unit_start_indicator 这个位功能有点复杂,字面意思是有效负载的开始标志,根据后面有效负载的内容不同功能也不同,后面用到的时候再说。

transport_priority   传输优先级位, 1 表示高优先级,传输机制可能用到,解码好像用不着。

PID   这个比较重要,指出了这个包的有效负载数据的类型,告诉我们这个包传输的是什么内容。前面已经叙述过。

     transport_scrambling_control 加密标志位,表示 TS 分组有效负载的加密模式。 TS 分组首部 ( 也就是前面这 32bit) 是不应被加密的, 00 表示未加密。

 

       adaption_field_control   翻译为 调整字段控制 ,表示 TS 分组首部后面是否跟随有调整字段和有效负载。 01 仅含有效负载, 10 仅含调整字段, 11 含有调整字段和有效负载。为 00 的话解码器不进行处理。空 分组没有调整字段。

        continuity_counter    一个 4bit 的计数器,范围 0-15 ,具有相同的 PID TS 分组传输时每次加 1 ,到 15 后清 0 。不过,有些情况下是不计数的。如下: (1)TS 分组无有效负载 (2) 复制的 TS 分组和原分组这个值一样 (3) 后面讲到的一个标志 discontinuity_indicator 1

       adaptation_field()          调整字段的处理

     data_byte              有效负载的剩余部分,可能为 PES 分组, PSI ,或一些自定义的数据。

 

(2)PAT

   PAT 数据结构如下:

program_association_section() { 
 table_id                                                                    // 8
 section_syntax_indicator                                      //1
 '0'                                                                           //1
 reserved                                                              // 2
 section_length                                                    //12
 transport_stream_id                                         // 16
 reserved                                                           // 2
 version_number                                                 // 5
 current_next_indicator                                     //1
 section_number                                                //8
 last_section_number                                         // 8
 for (i=0; i<N;i++) { 
  program_number                                              // 16
  reserved                                                          // 3
  if(program_number == '0') { 
   network_PID                                                  //  13
  } 
  else { 
   program_map_PID                                           // 13
  } 
 } 
 CRC_32                                                            //  32

 

table_id   固定为 0x00 ,标志是该表是 PAT

section_syntax_indicator 段语法标志位,固定为 1

section_length          表示这个字节后面有用的字节数,包括 CRC32 。假如后面的字节加上前面的字节数少于 188 ,后面会用 0XFF 填充。假如这个数值比较大,则 PAT 会分成几部分来传输。

transport_stream_id     该传输流的 ID ,区别于一个网络中其它多路复用的流。

 

version_number        范围 0-31 ,表示 PAT 的版本号,标注当前节目的版本.这是个非常有用的参数,当检测到这个字段改变时,说明 TS 流中的节目已经变化了,程序必须重新搜索节目.

current_next_indicator 表示发送的 PAT 是当前有效还是下一个 PAT 有效。

       section_number       分段的号码。 PAT 可能分为多段传输,第一段为 00 ,以后每个分段

1 ,最多可能有 256 个分段

       last_section_number   最后一个分段的号码

       program_number  节目号

 

       network_PID   网络信息表( NIT )的 PID ,网络信息表提供了该物理网络的一些信息,和电视台相关的。节目号为 0 时对应的 PID network_PID

program_map_PID  节目映射表的 PID ,节目号大于 0 时对应的 PID ,每个节目对应一个

CRC_32    CRC32 校验码

 

上面 program_number network_PID program_map_PID 是循环出现的。 program_number 等于 0 时对应 network_PID program_number 等于其它值时对应 program_map_PID

举个例子,下述流为带 PAT TS 分组:

47 40 00 1c 00 00 b0 15 13 f6 e7 00 00 00 00 e0 10 00 01 e0 20 00 02 e0 21 1a 34 b4 77 ff…………..ff

其中红色的四个字节是 TS 分组头部,用数据结构解出首部,得到 PID=0x00 ,表示为该分组的有效负载是 PAT 。蓝色的 00 称为 指针域 ”----Pointer field ,表示了一个偏移量,即从后面第几个字节开始是 PAT 部分。为 00 表示后面紧接着的就是 PAT 00 b0 15 13 f6 e7 00 00 00 00 e0 10 00 01 e0 20 00 02 e0 21 1a 34 b4 77

再利用 PAT 的数据结构解出 PAT ,得到如下信息:

---------------PAT Information-------------

table_id: 00

section_syntax_indicator: 01

section_length: 0015

transport_stream_id: 13f6

version_number: 13

current_next_indicator: 01

section_number: 00

last_section_number: 00

program_number: 0000

network_PID : 0010

program_number: 0001

program_map_PID: 0020

program_number: 0002

program_map_PID: 0021

CRC_32: 1a34b477

 

 

可以看出,此 PAT 只有一段,包含了三个节目,节目号 0000 对应于 network_PID=0010 ,节目号 0001 对应于 program_map_PID =0020 ,节目号 0002 对应于 program_map_PID =0021 ,从实际的角度,我们应该把这三个节目号理解为三个频道,第一个频道中的内容是网络信息,第二、三个频道包含了节目信息。在数字电视中,一个频道即对应于一个频点,如 498MHZ ,一个频道上可以有多个节目,后面的 PMT 即是告诉了我们某个频道中所有节目对应的 PID

于是现在就搜寻 PID=0x0020 TS 分组,即是频道 2 对应的 PMT 信息。

 

(3)PMT

   PMT 数据结构如下:

TS_program_map_section() { 
 table_id                                                                              // 8
 section_syntax_indicator                                                  //1
 '0'                                                                                      //  1
 reserved                                                                           //    2
 section_length                                                                   //  12
 program_number                                                               //16
 reserved                                                                              // 2
 version_number                                                                  //5
 current_next_indicator                                                       //1
 section_number                                                                  // 8
 last_section_number                                                          //8
 reserved                                                                               //3
 PCR_PID                                                                             //13
 reserved 4
 program_info_length                                                          //12
 for (i=0; i<N; i++) { 
  descriptor() 
 } 
 for (i=0;i<N1;i++) { 
  stream_type                                                                        //8
  reserved                                                                               //3
  elementary_PID                                                                  //13
  reserved                                                                               //4
  ES_info_length                                                                   //12
  for (i=0; i<N2; i++) { 
   descriptor() 
  } 
 } 
 CRC_32                                                                               //32
}

 

table_id   固定为 0x02 ,标志是该表是 PMT

section_syntax_indicator

section_length      

version_number       

current_next_indicator  以上四个字段意思和 PAT 相同,可参考上面解释

       section_number      

       last_section_number   以上两个字段意思和 PAT 相同,不过值都固定为 0x00 ,我觉得这样的原因可能是因为 PMT 不需要有先后顺序,因为先定义哪个节目都是无所谓。

       program_number  节目号,表示该 PMT 对应的节目

       PCR_PID            PCR (节目时钟参考)所在 TS 分组的 PID ,根据 PID 可以去搜索相应的 TS 分组,解出 PCR 信息。

program_info_length  该节目的信息长度,在此字段之后可能会有一些字节描述该节目的信息

stream_type          指示了 PID elementary_PID PES 分组中原始流的类型,比如视频流,音频流等,见后面的表

elementary_PID      该节目中包括的视频流,音频流等对应的 TS 分组的 PID

ES_info_length       该节目相关原始流的描述符的信息长度。

 

stream_type 对应的类型:

 

还是举个例子,下述是一个包含 PMT TS 分组,

   47 40 20 1c 00 02 b0 1f 00 01 e7 00 00 e1 00 f0 00 02 e1 00 f0 05 02 03 b2 44 5f 04 e1 10 f0 03 03 01 67 c9 ab c8 d2

红色的四个字节是 TS 分组头部,蓝色的 00 指针域 ,意义同 PAT 中的指针域。所以下面的数据就是 PMT 的内容: 02 b0 1f 00 01 e7 00 00 e1 00 f0 00 02 e1 00 f0 05 02 03 b2 44 5f 04 e1 10 f0 03 03 01 67 c9 ab c8 d2

再解出 PMT ,得到下列信息:

table_id: 02

section_syntax_indicator: 01

section_length: 01f

program_number: 0001

version_number: 13

current_next_indicator: 01

section_number: 00

last_section_number: 00

PCR_PID: 0100

program_info_length: 000

descriptor:

steam_type: 00

elementary_PID: 0001

ES_info_length: 000

descriptor:

steam_type: 02

elementary_PID: 0001

ES_info_length: 005

descriptor: 02 03 b2 44 5f

steam_type: 04

elementary_PID: 0011

ES_info_length: 003

descriptor: 03 01 67

CRC_32: c9abc8d2

 

可以看出,该节目号 0001 包含了三个流的信息,流类型分别为 00 02 04 00 的流为保留值,可以不考虑, 02 表示原始流为视频流,其 elementary_PID 0001 04 表示原始流为音频流,其 elementary_PID 0011 ,两个流分别还带有 descriptor (描述符),说明了该原始流的一些信息。

得到了这个 elementary_PID ,再从后面的传输流中找到 PID 为这个值的 TS 分组,其有效负载即为这个原始流的数据,获取数据送到解码器,即可还原这个视频或音频了。

 

三、总结

上面的都是一些零散的知识,跟我们实际应用有什么关系呢?下面就是一个简易的应用过程 --- 搜台。搜台过程大致如下:

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

上述过程主要涉及到 PAT PMT 的一些解码和解复用知识,这也是目前我学习到的,当然,数字电视涉及到的知识远远不止这些,解码方面就还包括调整字段的处理, SI( 业务信息 ) 应用,时钟的处理, CA 加密解 MI 系统等,还需要继续的学习和实践。

 

附:自己做了一个小工具,可以简单分析一下 TS 流数据,点击 "OPEN" 选择 TS 流文件,或直接拷贝文本数据到文本框中,点击 "Parse" ,可以得到各个字段的值,学习过程中可以算一个小帮手,欢迎试用!

 

 

 

NIT 格式各字段含义如下 :
table_id:8 bits
标志 , 应该是 0x40 0x41. table_id==0x40 时候 , 这个 NIT 描述的是当前流的网络信息 , 否则描述的是其他流的网络信息 ( 一般是电视台同步播放的其他 TS 流信息 ).
section_syntax_incicator:1bit
的段语法标志 , 应该是 '1'
reserved_future_use:1bit
保留未来使用位 , 一般是 '0'
reserved:2bits
保留位 , 一般是 '00', 这是防止控制字冲突而设置的 .
section_length:12bits
段长度 , network_id 开始 , CRC_32( 包含 ) 结束的字节总数 .
network_id:16bits
的网络 ID 号码 ,DVB 内唯一的一个号码 , 标志不同的电视台 .
Reserved:2bits
保留意见位 .
version_number:5bits
的版本号码 , NIT 内容有任何改变时 , 该字段会递增 1( 提醒解码器更新 NIT 信息 ).
current_next_indicator:1bit
的当前下次使用标志 , 一般是 '0'
section_number:8bits
的当前段号码 .
last_section_number:8bits
的当前段号码 .
reserved_future_use:4bits
保留未来使用 , 现在应该是 '0000'.
network_descriptors_length:12bits
网络描述符长度 , 单位是字节 .
descriptor():N
个不同的描述符结构 , 一般是网络名称描述符 , 解码器在此获取当前的网络名称 ( 即电视台名称 )
reserved_future_use:4bits
未来保留位 , 当前应是 '0000'.
transport_stream_loop_length:12bits
的字节总数 , 就是随后的循环的字节总数 .
transport_stream_id:16bits
的网络 ID
original_network_id:16bits
原始网络 ID. 如果 original_network_id== transport_stream_id 说明该 TS 流是直播节目 , 否则说明该 TS 流是转播节目 .
transport_descriptors_length:12bits
的描述符长度 , 随后的 N 个描述符占用的字节总数 .
descriptor(),N
个描述符 , 可以有多个连续但不相同的描述符号 , 如网络名称描述符 , 传输系统参数描述符 , 解码器分析这些描述符获取网络的不同信息 .
CRC_32:
整个段的 CRC 校验值 , 一般可以忽略

 

 

.
EIT, Event Information Table,
环境信息表
环境信息表提供如下信息:节目段的标识号、起始时间、节目长度、播放状态、是否加密;指向特定信息的链接信息;节目段多语种的简短介绍;节目段的详细介绍;两段同样节目段的时间偏移;基本码流类型,如视频的幅型比、伴音的类型、字幕的类型等;使用的加密系统;节目类型,如电影 / 戏剧、新闻、综艺、体育、少儿、音乐、艺术、社会政治、文教等;节目限定年龄的级别;给出实现交互式回传信道的电话号码;为满足各节目段的码率而提供的缓存大小信息及私有数据等 .
环境信息表中提供了类似于广播电视报所提供的节目表的内容,在 SI 中,只有 EIT 才有可能被加密。根据 EIT 及其它表所提供的信息,可以出五花八门的电子节目指南。 - 如:按节目类型检索、按时间检索及对某类节目的锁定等 .

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值