TS封装格式介绍及解析

ts文件分为三层:ts层(Transport Stream)、pes层(Packet Elemental Stream)、es层(Elementary Stream)。es层就是音视频数据,pes层是在音视频数据上加了时间戳等对数据帧的说明信息,ts层是在pes层上加入了数据流识别和传输的必要信息

 

ts

 ts包大小固定为188字节ts层分为三个部分:ts headeradaptation fieldpayloadts header固定4个字节;adaptation field可能存在也可能不存在,主要作用是给不足188字节的数据做填充;payloadpes数据

 

 

ts header

sync_byte

8bit

同步字节,固定为0x47

transport_error_indicator

1bit

传输错误指示符,表明在ts头的adapt域后由一个无用字节,通常都为0,这个字节算在adapt域长度内

payload_unit_start_indicator

1bit

负载单元起始标示符,一个完整的数据包开始时标记为1

transport_priority

1bit

传输优先级,0为低优先级,1为高优先级,通常取0

pid

13bit

pid(Packet ID号码,唯一的号码对应不同的包)

transport_scrambling_control

2bit

传输加扰控制,00表示未加密

adaptation_field_control

2bit

是否包含自适应区,00保留;01为无自适应域,仅含有效负载;10为仅含自适应域,无有效负载;11为同时带有自适应域和有效负载。

continuity_counter

4bit

递增计数器,从0-f,起始值不一定取0,但必须是连续的

ts层的内容是通过PID值来标识的,主要内容包括:PAT表、PMT表、音频流、视频流。解析ts流要先找到PAT表,只要找到PAT就可以找到PMT,然后就可以找到音视频流了。PAT表的PID值固定为0PAT表和PMT表需要定期插入ts流,因为用户随时可能加入ts流,这个间隔比较小,通常每隔几个视频帧就要加入PATPMTPATPMT表是必须的,还可以加入其它表如SDT(业务描述表)等,不过hls流只要有PATPMT就可以播放了

  • PAT表:他主要的作用就是指明了PMT表的PID
  • PMT表:他主要的作用就是指明了音视频流的PID
  • 音频流/视频流:承载音视频内容

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

PID

PAT

0x0000

CAT

0x0001

TSDT

0x0002

EIT,ST

0x0012

RST,ST

0x0013

TDT,TOT,ST

0x0014

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

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

Packet(十六进制)

4

7

0

7

e

5

1

2

                                          

Packet(二进制)

0

1

0

0

0

1

1

1

0

0

0

0

0

1

1

1

1

1

1

0

0

1

0

1

0

0

0

1

0

0

1

0

Packet Header 信息

1 sync_byte=0x47

2

3

4

5 PID=0x07e5

6

7

8

 

                                           

sync_byte=01000111,                        就是0x47,这是DVB TS规定的同步字节,固定是0x47.

transport_error_indicator=0,              表示当前包没有发生传输错误.

payload_unit_start_indicator=0,        含义参考ISO13818-1标准文

transport_priority=0,                          表示当前包是低优先级.

PID=00111 111001010x07e5,        Video PID

transport_scrambling_control=00,     表示节目没有加

adaptation_field_control=01              0x01,具体含义请参考ISO13818-1

continuity_counte=0010                    0x02,表示当前传送的相同类型的包是第3

 

 

adaption

adaptation_field_length

1B

自适应域长度,后面的字节数

flag

1B

0x50表示包含PCR0x40表示不包含PCR

PCR

5B

Program Clock Reference,节目时钟参考,用于恢复出与编码端一致的系统时序时钟STCSystem Time Clock)。

stuffing_bytes

xB

填充字节,取值0xff

自适应区的长度要包含传输错误指示符标识的一个字节。pcr是节目时钟参考,pcrdtspts都是对同一个系统时钟的采样值,pcr是递增的,因此可以将其设置为dts值,音频数据不需要pcr。如果没有字段,ipad是可以播放的,但vlc无法播放。打包ts流时PATPMT表是没有adaptation field的,不够的长度直接补0xff即可。视频流和音频流都需要加adaptation field,通常加在一个帧的第一个ts包和最后一个ts包里,中间的ts包不加

 

 

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

PAT表定义了当前TS流中所有的节目,其PID0x0000,它是PSI的根节点,要查寻找节目必须从PAT表开始查找

table_id

8b

PAT表固定为0x00

section_syntax_indicator

1b

固定为1

zero

1b

固定为0

reserved

2b

固定为11

section_length

12b

后面数据的长度

transport_stream_id

16b

传输流ID,固定为0x0001

reserved

2b

固定为11

version_number

5b

版本号,固定为00000,如果PAT有变化则版本号加1

current_next_indicator

1b

固定为1,表示这个PAT表可以用,如果为0则要等待下一个PAT

section_number

8b

固定为0x00

last_section_number

8b

固定为0x00

开始循环

 

 

program_number

16b

节目号为0x0000时表示这是NIT,节目号为0x0001,表示这是PMT

reserved

3b

固定为111

PID

13b

节目号对应内容的PID

结束循环

 

 

CRC32

32b

前面数据的CRC32校验码

通过一段TS流中一个Packet分析PAT表,这里我们分析一段TS流其中一个PacketPacket Data部分

首先给出一个数据包,其数据如下

Packet Header

Packet Data

0x47 0x40 0x00 0x10

0000 b0 11 00 01 c1 00 00 00 00 e0 1f 00 01 e1 00 24 ac48 84 ff ff…… ff ff

分析Packet Header如下表所示

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

Packet(十六进制)

4

7

4

0

0

0

1

0

Packet(二进制)

0

1

0

0

0

1

1

1

0

1

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

0

1

0

0

0

0

Packet Header Bits

1 sync_byte=0x47

2

3

4

5 PID=0x0000

6

7

8

 根据包头数据格式,我们可以知晓整个数据包的属性,列表如下

sync_byte

0x47

固定同步字节

transport_error_indicator

“0”

没有传输错误

payload_unit_start_indicator

“1”

在前4个字节后会有一个调整字节。所以实际数据应该为去除第一个字节后的数据。即上面数据中红色部分不属于有效数据包。

transport_priority

“0”

传输优先级低

PID

0x0000

PID=0x0000说明数据包是PAT表信息

transport_scrambling_control

“00”

未加密

adaptation_field_control

“01”

附加区域控制

continuity_counte

“0000”

包递增计数器

如上表所示,我们可以知道,首先PacketPacket DataPAT信息表,因为其PID0x0000,并且在包头后需要除去一个字节才是有效数据(payload_unit_start_indicator="1")。这样,Packet Data就应该是“00 b0 11 00 01 c1 00 00 00 00 e0 1f 00 01 e1 00 24 ac48 84 ff ff …… ff ff”

Packet Data分析

第n个字节

  1  

  3  

4

5

6

7

8

9

 10 

 11 

  12 

  13 

14 

15 

16

17

18

19

20

Packet Data(除去开头的0x00)

00

b0

11

00

01

c1

00

00

00

00

e0

1f

00

01

e1

00

24

ac

48

84

字段名

具体值

次序

说明

table_id

8

0000

第1个字节 0000 0000B(0x00)

PAT的table_id只能是0x00

section_syntax_indicator

1

1

 

第2、3个字节

1011 0000 0001 0001B(0xb0 11)

段语法标志位,固定为1

zero

1

0

 

reserved

2

11

 

section_length

12

0000 0001 0001B=0x011=17

段长度为17字节

transport_stream_id

16

0x0001

第4、5个字节 0x00 0x01

 

reserved

2

11

 

第6个字节 1100 0001B(0xc1)

 

version_number

5

00000

一旦PAT有变化,版本号加1

current_next_indicator

1

1

当前传送的PAT表可以使用,若为0则要等待下一个表

section_number

8

0x00

第7个字节0x00

 

last_section_number

8

0x00

第8个字节 0x00

 

开始循环

program_number

16

0x0000-第一次

2个字节(0x00 00)

节目号 

reserved

3

111

 

2个字节

1110 0000 0001 1111B(0xe0 1f)

 

network_id(节目号为0时)

program_map_PID(节目号为其他时)

13

0 0000 0001 1111B=31

-第一次

节目号为0x0000时,表示这是NIT,PID=0x001f,即31

节目号为0x0001时,表示这是PMT,PID=0x100,即256

结束循环

CRC_32

32

--

4个字节

 

 

                          

 由以上几个表可以分析出PAT表和PMT表有着内在的联系。也就是之前提到的。PAT表描述了当前流的NITNetwork Information Table,网络信息表)中的PID、当前流中有多少不同类型的PMT表及每个PMT表对应的频道号

 

 

PMT格式( Program Map Table,节目映射表 )

table_id

8b

PMT表取值随意,0x02

section_syntax_indicator

1b

固定为1

zero

1b

固定为0

reserved

2b

固定为11

section_length

12b

后面数据的长度

program_number

16b

频道号码,表示当前的PMT关联到的频道,取值0x0001

reserved

2b

固定为11

version_number

5b

版本号,固定为00000,如果PAT有变化则版本号加1

current_next_indicator

1b

固定为1

section_number

8b

固定为0x00

last_section_number

8b

固定为0x00

reserved

3b

固定为111

PCR_PID

13b

PCR(节目参考时钟)所在TS分组的PID,指定为视频PID

reserved

4b

固定为1111

program_info_length

12b

节目描述信息,指定为0x000表示没有

开始循环

 

 

stream_type

8b

流类型,标志是Video还是Audio还是其他数据,h.264编码对应0x1baac编码对应0x0fmp3编码对应0x03

reserved

3b

固定为111

elementary_PID

13b

stream_type对应的PID

reserved

4b

固定为1111

ES_info_length

12b

描述信息,指定为0x000表示没有

结束循环

 

 

CRC32

32b

前面数据的CRC32校验码

通过一段TS流中一个Packet分析PMT表,通过分析一段TS流的数据包Packet来学习PMT表。下面给出了一段TS流数据中的一个Packet(十六进制数

Packet Header

Packet Data

0x47 0x43 0xe8 0x12

00 02 b0 12 00 01 c1 00 00 e3 e9 f0 00 1b e3 e9 f0 00 f0 af b4 4f ff ff…… ff ff

 首先解析Packet Header,分析如下

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

Packet(十六进制)

4

7

4

3

e

8

1

2

Packet(二进制)

0

1

0

0

0

1

1

1

0

1

0

0

0

0

1

1

1

1

1

0

1

0

0

0

0

0

0

1

0

0

1

0

Packet Header Bits

1 sync_byte=0x47

2

3

4

5 PID=0x03e8

6

7

8

 

Packet Header分析

 

Packet Header:0x47 0x40 0x00 0x10

1

sync_byte

0x47

固定同步字节

2

transport_error_indicator

“0”

没有传输错误

3

payload_unit_start_indicator

“1”

在前4个字节后会有一个调整字节。所以实际数据应该为去除第一个字节后的数据。

4

transport_priority

“0”

传输优先级低

5

PID

0x03e8

PID=0x03e8说明数据包是PMT表信息

6

transport_scrambling_control

“00”

未加密

7

adaptation_field_control

“01”

附加区域控制

8

continuity_counte

“0010”

包递增计数器

因为payload_unit_start_indicator=‘1’,在解析数据包的时候需要去除Packet Data的第一个字节。下面是对Packet Data的详细解析

PMT表的Packet Data分析

第n个字节

1

2

3

4

5

6

7

8

9

10   

11   

12   

13   

14  

 15  

16

17

18

19

20

Packet Data

02

b0

12

00

01

c1

00

00

e3

e9

f0

00

1b

e3

e9

f0

00

f0

1b

e3

字段名

位数

具体值

次序

说明

table_id

8

0x02

第1个字节

 

section_syntax_indicator

1

1B

 

第2、3个字节

1011 0000 0001 0010B=0xb012

段语法标志

zero

1

0B

 

reserved

2

11B=0x03

 

section_length

12

0000 0001 0010B=0x12

段长度,从program_number开始,到CRC_32(含)的字节总数

program_number

16

0x0001

第4、5个字节 0x00 01

频道号码,表示当前的PMT关联到的频道

reserved

2

11B=0x03

 

第6个字节

1100 0001B=0xc1

 

version_number

 

5

 

00000B=0x00

版本号码,如果PMT内容有更新,则它会递增1通知解复用程序需要重新接收节目信息

current_next_indicator

1

1B=0x01

当前未来标志符

section_number

8

0x00

第7个字节0x00

当前段号码

last_section_number

8

0x00

第8个字节 0x00

最后段号码,含义和PAT中的对应字段相同

reserved

3

111B=0x07

第9、10个字节

1110 0011 1110 1001B=0xe3e9

 

PCR_PID

13

000111110B=0x3e9

PCR(节目参考时钟)所在TS分组的PID

reserved

4

1111B=0x0f

 

 

 

第11、12个字节

1111 0000 0000 0000=0xf000

 

 

program_info_length

 

12

 

 

 

000000000000B=0x000

节目信息长度(之后的是N个描述符结构,一般可以忽略掉,这个字段就代表描述符总的长度,单位是Bytes)紧接着就是频道内部包含的节目类型和对应的PID号码了

stream_type

8

0x1b

第13个字节 0x1b

流类型,标志是Video还是Audio还是其他数据

reserved

3

111B=0x07

第14、15个字节

1110 0011 1110 1001B=0xe3e9

 

elementary_PID

13

000111110 1001=0x3e9

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

reserved

4

1111B=0x0f

第16、17个字节

1111 0000 0000 0000B=0xf000

 

ES_info_length

12

0000 0000 0000=0x000

 

CRC

32

——

——

 

 

                          

 

pes

有必要说一点,PES层是将ES层(一个片)加入了PES头部,一般ES层的数据是很大的(由PES包长度这个字段指出),所以加入PES头时必须将ES分割,我们只在第一个分割后的ES上加PES,所以就形成了这种情况(即很多TS包并不包含PES头部,直接是裸ES数据,稍后结合工具分析详解):

        PES层主要分为三个部分PES header(固定6个字节),optional header(可选),和pes payload(也就是ES层了)。PES的字段比较多,在此只列出它的句法结构图和几个比较重要的字段(摘自:https://blog.csdn.net/cabbage2008/article/details/49612011):

         可见每个PES包固定了三个字段:起始码流(packet_start_code_prefix,24b),流id(stream id,8b),包长度(PES_packet_length,16b)。这三个字段占据了6个字节,是我们分析PES包的起始标志,请牢记。然后是pes optional header(里面包含了很多字段),最后是pes payload。在此仅介绍几个比较重要的字段(标志flag对应了任选字段的内容是否存在)。

 

pes层是在每一个视频/音频帧上加入了时间戳等信息,pes包内容很多,我们只留下最常用的。 

pes start code

3B

开始码,固定为0x000001

stream id

1B

音频取值(0xc0-0xdf),通常为0xc0
视频取值(0xe0-0xef),通常为0xe0

pes packet length

2B

后面pes数据的长度,0表示长度不限制,
只有视频数据长度会超过0xffff

flag

1B

通常取值0x80,表示数据不加密、无优先级、备份的数据

flag

1B

取值0x80表示只含有pts,取值0xc0表示含有ptsdts

pes data length

1B

后面数据的长度,取值510

pts

5B

33bit

dts

5B

33bit

pts是显示时间戳、dts是解码时间戳,视频数据两种时间戳都需要,音频数据的ptsdts相同,所以只需要pts。有ptsdts两种时间戳是B帧引起的,I帧和P帧的pts等于dts。如果一个视频没有B帧,则pts永远和dts相同。从文件中顺序读取视频帧,取出的帧顺序和dts顺序相同。dts算法比较简单,初始值 + 增量即可,pts计算比较复杂,需要在dts的基础上加偏移量

     音频的pes中只有pts(同dts),视频的IP帧两种时间戳都要有,视频B帧只要pts(同dts)。打包ptsdts就需要知道视频帧类型,但是通过容器格式我们是无法判断帧类型的,必须解析h.264内容才可以获取帧类型

      pes层中很多字段都是有它前面的flag指定,一般情况下很多字段是不存在的,最常见的是PTS/DTS.PES各个字段更加详细的介绍请看相关的介绍文档:https://blog.csdn.net/cabbage2008/article/details/49848937

es

 

es层就是音视频裸数据了,常用的音频编码格式为AAC,视频编码格式为H.264

打包H.264AACTS

 

对于H.264视频而言,每一帧的时间长度

 frame_duration = 1000/fps

fps25时,一帧时间为40ms

对于AAC音频而言,每一帧的时间长度

音频帧的播放时间=一个AAC帧对应的采样样本的个数/采样频率(单位为s)
一帧 1024 sample。采样率 Samplerate 44100KHz,每秒44100sample, 所以根据公式   音频帧的播放时间=一个AAC帧对应的采样样本的个数/采样频率
当前AAC一帧的播放时间是= 1024*1000000/44100= 22.32ms(单位为ms)

理论上的音视频(播放)同步是这样的:
由此得到了每一帧数据的持续时间,音视频交叉存储在容器中:一个时间轴:
时间轴:0   22.32   40     44.62    66.96    80     89.16      111.48    120       ................
    0   22.32            44.62    66.96             89.16      111.48                ................
    0              40                              80                                   120       ................
即视频的持续时间相加 和音频的持续时间相加作比较,谁小写入哪个

(自己的方法)

音频数据(AAC 48k        21.33     42.44  63.99          85.32

视频数据(H264 25fps              40                       80

时间轴                           ------------------------------------------->

ts容器)循环做(写一帧视频,然后写一帧音频,然后视频的时间减去音频的时间,如果大于一帧音频的时间,就多写一帧音频,知道视频多出来的时间小于一帧音频

 

使用 Elecard Stream Analyzer 可以分析TS

参考 

https://www.cnblogs.com/CoderTian/p/7198765.html

https://blog.csdn.net/qq_41786131/article/details/90405715

https://blog.csdn.net/qq_41786131/article/details/90481622

https://www.jianshu.com/p/d6311f03b81f

 

  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue3是一种用于构建用户界面的JavaScript框架,而TypeScript是一种静态类型检查的JavaScript超集。可以使用Vue3和TypeScript来封装一个解析Excel的组件。 首先,我们可以使用Vue3的`defineComponent`函数创建一个组件,并使用TypeScript的类型注来定义组件的props和data。 ```typescript import { defineComponent } from 'vue' export default defineComponent({ props: { file: { type: Object as () => File, required: true } }, data() { return { excelData: [] as any[][] } }, methods: { parseExcel() { // 解析Excel文件的逻辑 } }, mounted() { this.parseExcel() }, template: ` <div> <!-- 组件的HTML模板 --> </div> ` }) ``` 在上面的代码中,我们使用`defineComponent`函数定义了一个名为`ExcelParser`的组件。它接收一个`file`的prop,该prop的类型为`File`。组件的data中有一个名为`excelData`的数组,用来存储解析后的Excel数据。 组件的`parseExcel`方法用于解析Excel文件。你可以使用一些现有的JavaScript库(如xlsx)来帮助解析Excel数据。 当组件被挂载到DOM之后,会调用`mounted`生命周期钩子函数,这里调用了`parseExcel`方法,来解析Excel文件。 最后,我们通过字符串模板定义了组件的HTML结构,你可以根据组件的功能来自定义你需要的HTML。 这只是一个简单的示例,你可以根据自己的需求对组件进行更复杂的封装。通过使用Vue3和TypeScript,你可以在组件中添加类型检查的好处,并且可以更方便地编写和维护代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值