TS码流解析(一)TS Header
有一些音视频初学者想要了解TS码流结构,但网上资料不全或者讲得不够清楚,使得学习过程变得异常艰难。这一篇内容将对TS码流结构做详尽解析,争取做到通俗易懂,成为最好的TS码流解析文章。
本篇TS码流解析将会参照Android的ATSParser代码。
首先我们要知道一个标准的TS包一般有188字节,但是也有TS包是192字节或者204字节的情况。同一路TS流中,每个TS包大小是一致的,不会同时出现188字节和192字节的TS包。接下来的文章中,我们只解析标准188字节的TS包。
标准TS包包括一个4字节的包头、n字节自适应域(可选)和184-n字节的负载(可选),可选的意思是它可能在TS包中存在,也可能不存在。TS包的结构如图:

图中橙色部分是包头(header),蓝色部分是自适应域(adaptation field),黄色部分是负载(payload)。
1、TS Header解析
4字节包头共包含有8个字段,接下来依次解析包头字段:
sync_byte:同步字节,值为0x47,关于同步字节有以下几点需要了解:- 同步字节表示一个新的TS包的开始,从同步字节开始往后的188字节为一个完整的TS包;
- 根据以上特点,可以通过检查一个流的188个字节的整数倍位置是否为0x47来判断流是否为TS流;
- 如果一段码流中间丢了一部分数据(丢失同步),可以利用同步字节重新找回同步,但是要注意,码流本身可能包含0x47的数据,所以当找到一个0x47时,需要向前和向后各跳跃188个字节,如果这些位置仍然时0x47,那么就可以确定这个0x47是同步字节,TS流已经对齐成功。
transport_error_indicator:传输误差标志,该数据包在传输过程中是否出现错误,如果该位为1,说明应该立即丢弃该TS包;payload_unit_start_indicator:有效负载单元起始标志,- 值为1,表示该TS包中的负载数据是新的PES包或是PSI包的开始;
- 值为0,表示该TS包中的负载数据是上一个PES包或是PSI包的续传数据。
- 如何理解这个字段的意义:PES和PSI(后面了解这两个数据包的具体内容)数据包的大小往往是大于184字节的,要传输这些数据包肯定要把它们拆开装到多个TS包中,解析TS包时需要知道被拆开数据的头和尾,从而能够正确拼接成为完整的PES和PSI数据,这个标志位就是用来做拆开数据头的作用。
transport_priority:传输优先级,指示TS包的优先级,这个字段不影响TS流解析,暂不做了解;PID:包ID(Packet Identifier),用于标识TS包中的负载是什么类型的数据。视频流、音频流、程序关联表(PAT),节目映射表(PMT),条件接入表(CAT)等信息都会被赋予一个唯一的PID,我们解析TS包时就是根据PID来解析的。在TS流中,有几个预先定义的、有特殊含义的PID值:0:TS包中的数据是PAT(Program Association Table),存储有节目信息;1:TS包中的数据是CAT(Conditional Access Table),存储有流的加密、条件访问信息;0x1FFF:空包,用于保证数据流的连续性;
transport_scrambling_control:传输加扰控制,表示负载的PES数据是否经过了加密处理,要注意的是加密只会对PES数据进行加密,我们这里不会对加密数据解析做了解:00:不进行加扰处理;10:进行偶数密钥加扰;11:进行奇数密钥加扰。
adaptation_field_control:自适应字段控制,用来指示该TS包是否含有自适应字段和负载数据,该字段的取值有四种:00:表示TS包既没有自适应字段,也没有负载数据,就是一个填充包;01:表示TS包没有自适应字段,只有负载数据;10:表示TS包只有自适应字段,没有负载数据;11:表示TS包既有自适应字段,也有负载数据;
continuity_counter:连续计数器,用于检测传输流中的包是否丢失或者错序。连续计数器为4位,每当一个新的TS包(PID相同)被发送出去,该包中的连续计数器就会增加1,最大值为15,到达最大值后又从0开始。如果接收到一个包的连续计数器值比前一个包的值大于1,那么就可以判断出中间丢失了一个或者多个包。
以上就是TS Header中的内容,我们暂且先了解到这,里面一些陌生的术语我们后面再做解释。
2、Adaptation Field解析
继续往后解析,需要先看自适应字段控制的值,值为10和11时TS Header后会跟有一个(自适应域)adaptation field,接下来对这个部分做解析:
adaptation field的结构如图:

结构图中的橙色部分是必选(肯定存在)的控制字段,绿色部分是可选,绿色部分的内容由控制字段的值所决定。接下来先看控制字段有哪些:
adaptation Field Length:自适应域的长度,不包含这个字段本身;discontinuity indicator:非连续指示符,标记当前TS包的数据流在此处是否有间断,我们要注意的是discontinuity indicator和continuity counter是两个不同的概念,前者关注的是数据的连续性,后者关注的是TS包本身的连续性;random access indicator:随机存取指示符,标记当前包的是否为随机存取点,即解码器是否可以从此处开始解码,要注意的是该标志位为1不能说明当前帧为I帧;elementary stream priority indicator:基本流优先级指示符,记录当前的ES数据流的优先级;PCR flag:PCR(Program Clock Reference)标志位,值为1会在自适应域中插入6字节的PCR字段;OPCR flag:OPCR标志位,值为1会在自适应域中插入6字节的OPCR字段(暂不了解);Splicing point flag:剪接点标志,用于记录在传输流中插入或替换媒体数据的位置,广告插播的实现方式,(暂不了解);Transport private data flag:传输私有数据标志,值为1会在自适应域中插入私有数据字段(暂不了解);Adaptation field extension flag:自适应域扩展标志,值为1会在自适应域中插入自适应域扩展字段(暂不了解);
我们先假定控制字段的值都为1,看看后面自适应域的结构是怎么样的:

结构相当的复杂,不过也有规律可循的,这种结构一般包含三个部分:
- 整个字段的长度,这是相当关键的;
- 控制字段,指示存在哪些数据;
- 可选内容,其中可能又嵌套有控制字段和可选内容;
Adaptation Field是否存在取决于TS包的内容和结构:
- 有效负载不足以填充完整个TS包时,利用Adaptation Field可以填充剩余的空间;
- 需要提供额外的控制信息,如PCR(Program Clock Reference)、OPCR(Original Program Clock Reference)、随机访问指示、私有数据等时,这些信息会被放置在Adaptation Field中;
- Adaptation field的长度要小于184字节,不能跨越多个包。
在这里了解一下PCR的作用,PCR是一个全局性的时间基准,在数字视频广播中维护音频和视频的同步,它和PTS有什么区别呢?
我们都知道PTS用于描述音频和视频帧的渲染时间,播放器根据PTS来决定何时将音频或视频帧送到显示设备或者音频设备进行播放,从而确保视频和音频之间的同步性。
PCR是数字电视系统中的概念,数字电视流中可能会有多个不同的节目同时在传输,每个节目流都有自己的PTS,但是这些PTS彼此之间并没有直接的关联,PCR就是提供一个基准用于多个节目流之间的同步。(我也没用过PCR,暂时无法举例)
3、代码解析
接下来看Android是如何解析TS Header和Adaptation Field的:
status_t ATSParser::parseTS(ABitReader *br, SyncEvent *event) {
// 1
unsigned sync_byte = br->getBits(8);
if (sync_byte != 0x47u) {
return BAD_VALUE;
}
// 2
if (br->getBits(1)) { // transport_error_indicator
return OK;
}
unsigned payload_unit_start_indicator = br->getBits(1);
MY_LOGV("transport_priority = %u", br->getBits(1));
unsigned PID = br->getBits(13);
unsigned transport_scrambling_control = br->getBits(2);
unsigned adaptation_field_control = br->getBits(2);
unsigned continuity_counter = br->getBits(4);
status_t err = OK;
// 3
unsigned random_access_indicator = 0;
if (adaptation_field_control == 2 || adaptation_field_control == 3) {
err = parseAdaptationField(br, PID, &random_access_indicator);
}
// 4
if (err == OK) {
if (adaptation_field_control == 1 || adaptation_field_control == 3) {
err = parsePID(br, PID, continuity_counter,
payload_unit_start_indicator,
transport_scrambling_control,
random_access_indicator,
event);
}
}
return err;
}
- 如果同步字节不是0x47则退出该包TS的解析;
- 如果传送误差指示符为1,说明该数据包在传输过程中是否出现错误,丢弃该包数据;
- 如果自适应字段控制值为2或3,说明TS包中包含有自适应域,需要解析部分内容;
- 解析完成自适域中的内容后,需要再查看是否还有负载数据,自适应字段控制值为1或3时,说明有负载数据,需要进一步解析负载数据;
status_t ATSParser::parseAdaptationField(
ABitReader *br, unsigned PID, unsigned *random_access_indicator) {
*random_access_indicator = 0;
// 1
unsigned adaptation_field_length = br->getBits(8);
if (adaptation_field_length > 0) {
if (adaptation_field_length * 8 > br->numBitsLeft()) {
return ERROR_MALFORMED;
}
unsigned discontinuity_indicator = br->getBits(1);
if (discontinuity_indicator) {
}
*random_access_indicator = br->getBits(1);
if (*random_access_indicator) {
}
unsigned elementary_stream_priority_indicator = br->getBits(1);
if (elementary_stream_priority_indicator) {
}
unsigned PCR_flag = br->getBits(1);
// 2
size_t numBitsRead = 4;
if (PCR_flag) {
if (adaptation_field_length * 8 < 52) {
return ERROR_MALFORMED;
}
br->skipBits(4);
uint64_t PCR_base = br->getBits(32);
PCR_base = (PCR_base << 1) | br->getBits(1);
br->skipBits(6);
unsigned PCR_ext = br->getBits(9);
size_t byteOffsetFromStartOfTSPacket =
(188 - br->numBitsLeft() / 8);
uint64_t PCR = PCR_base * 300 + PCR_ext;
uint64_t byteOffsetFromStart =
uint64_t(mNumTSPacketsParsed) * 188 + byteOffsetFromStartOfTSPacket;
for (size_t i = 0; i < mPrograms.size(); ++i) {
updatePCR(PID, PCR, byteOffsetFromStart);
}
numBitsRead += 52;
}
// 3
br->skipBits(adaptation_field_length * 8 - numBitsRead);
}
return OK;
}
- 解析自适应域的长度,长度不包含adaptation_field_length这个字段;
- 用numBitsRead记录已经读取的bit数量;
- 跳过不需要解析的数据;
到这里,TS包的包头和自适应域的解析就完成了,下一节我们来了解TS负载的解析。
TS码流解析(一)TS Header的更多相关文章
- 关于ES、PES、PS/TS 码流
一.基本概念 )ES ES--Elementary Streams (原始流)是直接从编码器出来的数据流,可以是编码过的视频数据流(H.264,MJPEG等),音频数据流(AAC),或其他编码 ...
- 【雷神源码解析】无基础看懂AAC码流解析,看不懂你打我
一 前言 最近在尝试学习一些视频相关的知识,随便一搜才知道原来国内有雷神这么一个真正神级的人物存在,尤其是在这里(传送门)看到他的感言更是对他膜拜不已,雷神这种无私奉献的精神应当被我辈发扬光大.那写这 ...
- H.264/H265码流解析
H.264/H265码流解析 一.H.264码流解析 一个原始的H.264 NALU 单元常由 [StartCode] [NALU Header] [NALU Payload] 三部分组成 一个原始的 ...
- 视音频数据处理入门:AAC音频码流解析
===================================================== 视音频数据处理入门系列文章: 视音频数据处理入门:RGB.YUV像素数据处理 视音频数据处理 ...
- 视音频数据处理入门:H.264视频码流解析
===================================================== 视音频数据处理入门系列文章: 视音频数据处理入门:RGB.YUV像素数据处理 视音频数据处理 ...
- go http 下载视频(TS码流文件)(推荐一个网站学习 go example)
视频 http下载代码 dn.go(注意:代码很ugly,没怎么花时间) 总体感觉特别简单,网上看了下 net/http ,io这2个库的使用, 几分钟就写完了,感觉cpp 在做工具这块 开发效率的 ...
- H264码流解析及NALU
ffmpeg 从mp4上提取H264的nalu http://blog.csdn.net/gavinr/article/details/7183499 639 /* bitstream fil ...
- h.264码流解析_一个SPS的nalu及获取视频的分辨率
00 00 00 01 67 42 00 28 E9 00 A0 0B 77 FE 00 02 00 03 C4 80 00 00 03 00 80 00 00 1A 4D 88 10 94 0 ...
- TS流解析 一
一 从TS流开始 数字电视机顶盒接收到的是一段段的码流,我们称之为TS(Transport Stream,传输流),每个TS流都携带一些信息,如Video.Audio以及我们需要学习的PAT.PMT等 ...
- TS流解析 四
一 从TS流开始 数字电视机顶盒接收到的是一段段的码流,我们称之为TS(Transport Stream,传输流),每个TS流都携带一些信息,如Video.Audio以及我们需要学习的PAT.PMT等 ...
随机推荐
- HDC2021技术分论坛:HarmonyOS本地模拟器重磅来袭!
作者:longjiangyun,模拟器开发工程师 HarmonyOS模拟器是应用开发者使用IDE进行代码开发.调试.测试等活动中必不可少的工具,它分为本地模拟器和远程模拟器,其中远程模拟器又分为单设备 ...
- HDC2021技术分论坛:盘点分布式软总线数据传输技术中的黑科技
作者:houweibo,软总线首席技术专家:lidonghua,软总线技术专家 随着万物互联时代的到来,特别是大量媒体资源的涌入和使用,用户对传输的要求不断提高,怎样的传输技术才能满足未来的用户需求呢 ...
- 重新点亮linux 命令树————文本查看vi和vim[五]
前言 简单整理一下vi和vim,主要介绍一下四种模式. 正文 四种模式分别是: 正常模式(normal-mode) 插入模式(insert-mode) 命令模式(command-mode) 可视模式( ...
- Lattice高速下载器HW-USBN-2B 如何申请 license
如果用的芯片不是停产老旧芯片,Diamond programmer 是不需要 license 绑定支持的. 但是有些需要编程老旧的芯片.需要安装 Diamond programmer stand-al ...
- 低成本FPGA的MIPI测试GOWIN和LATTICE CROSSLINK
本次实验MIPI屏,2.0寸,分辨率是240*320 RGB888, 接口如下: 接上IO就是RST和MIPI的时钟和数据接口,另外就是电源和地. 一:GOWIN的测试方案 Gowin的案例中,首先是 ...
- sm加密前端解析--JS实现国密算法SM2加密
https://gitee.com/houxianzhou/sm2-demo.git 具体相关算法这里不在讲述,网上文章很多,也可以看我之前 iOS-sm2-sm3-sm4-sm9-zuc 里面提供的 ...
- Oracle的主键id自增
Oracle的主键id自增 可以直接用序列加触发器的方式实现 首先表里面要有个主键,没有的话用语句或者在编译器中加一下,都可以 然后创建一个序列,一般来说最常用的有这几个参数 CREATE SEQUE ...
- 力扣577(MySQL)-员工奖金(简单)
题目: 问题:选出所有 bonus < 1000 的员工的 name 及其 bonus.Employee 表单,empId 是这张表单的主关键字 Bonus 表单,empId 是这张表单的主关键 ...
- 力扣350(java&python)-两个数组的交集 II(简单)
题目: 给你两个整数数组 nums1 和 nums2 ,请你以数组形式返回两数组的交集.返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值).可以不 ...
- RocketMQ 千锤百炼--哈啰在分布式消息治理和微服务治理中的实践
简介: 随着公司业务的不断发展,流量也在不断增长.我们发现生产中的一些重大事故,往往是被突发的流量冲跨的,对流量的治理和防护,保障系统高可用就尤为重要. 作者|梁勇 背景 哈啰已进化为包括两轮出 ...