啊啊啊啊啊啊啊啊啊啊啊啊…好不容易组出来了section,好几周了吧,其中一周是回学校了,之后就是放空状态了,感觉要是认真思考的话,应该不难的吧…

组section的历程:

  1. 首先,需要清楚ts码流里的层次关系:

    ts码流是由很多的包组成的,即ts码流是包的集合,而section是ts包的集合;

    在码流中会解析出各种table,这些table是实际业务中需要用到的东西,而table是section的集合;

  2. 其次,要清楚section的组成,以下是section头部字段:

    public Section(byte[] container, int sectionStart){this.table_id = container[sectionStart];this.section_syntax_indicator = (byte) ((container[sectionStart + 1] >> 7) & 0x1);this.zero_flag= (byte) ((container[sectionStart+1]>>6)&0x1);this.reserved_1= (byte) (container[sectionStart+1]&0x11);this.section_length = (short) (((container[sectionStart + 1] & 0xf) << 8) | container[sectionStart + 2]);this.ts_id= (short) (container[sectionStart+3]<<8|container[sectionStart+4]);this.reserved_2= (byte) ((container[sectionStart+5]>>6)&0x11);this.version_number = (byte) ((container[sectionStart + 5] >> 1) & 0x1f);this.current_next_indicator = (byte) (container[sectionStart + 5] & 0x1);this.section_number = container[sectionStart + 6];this.last_section_number = container[sectionStart + 7];
    }
    
  3. 接下来就是思考section组成的流程:

    一个个包的获取,首先判断包是不是要找的包(PID),其次这个包能否作为section的包,如果是首包,判断table_id是不是要寻找的。确定了这个包的payload可以作为section的数据部分,将数据部分装入sectioncontainer中。

核心方法:

  • formTable() 作为解目标table中所有section的入口方法,给定ts文件的路径,将其解析为一个TransportStream对象,以及入参的简单处理(这里的入参不需要处理…)
@RequiresApi(api = Build.VERSION_CODES.N)
public static List<Section> formTable(String file, short targetPid, byte targetTable_id) throws Exception {byte[] data = FileUtils.readFile(file);TransportStream ts = DecodeUtils.analysisTransportStream(data);return getTable(ts, targetPid, targetTable_id);
}
  • getTable() 获取给定table_id的所有section。通过调用下一层方法getSection()一次获取一个section,而每一个section都有section_number来对其进行编号,那么想要获取所有section就需要用一个字段来标记哪些section是被获取过的(因为码流中的数据很多都是重复的,对于一些section在解析过程中,可能会存在重复解析的情况)。用sectionCount字段来标记目前拿到了多少个section,从而判断section是不是都拿完了。

    table的解出来的所有section都存入了List<Section>中,并重写了listsort(),根据sectionsection_number字段来进行排序。

@RequiresApi(api = Build.VERSION_CODES.N)
public static List<Section> getTable(TransportStream ts, short targetPid, byte targetTable_id) {Integer a =8;Integer b=9;a.compareTo(b);int lastSectionNumber;int sectionCount = 0;boolean[] sectionNumberRecord = new boolean[256];Section section = new Section();GetSectionParameters parameters;List<Section> sections = new ArrayList<>();sections.sort(new Comparator<Section>() {@Overridepublic int compare(Section s1, Section s2) {return (s1.getSection_number() < s2.getSection_number()) ? -1 : ((s1.getSection_number() == s1.getSection_number()) ? 0 : 1);}});int from = ts.getPosition();int to = ts.getPosition() + ts.getPackageLen();/**transport stream invalid*/if (ts.getTsData() == null) {return null;}while (to < ts.getTsData().length) {parameters = new GetSectionParameters(ts, section, targetTable_id, targetPid, sectionCount, sectionNumberRecord, from);to = getSection(parameters);sectionNumberRecord = parameters.getSectionNumberRecord();sectionCount = parameters.getSectionCount();lastSectionNumber = section.getContainer()[7];/**whether has been form all section*/if (sectionCount > lastSectionNumber) {break;}from = to;to = from + ts.getPackageLen();}return sections;
}
  • getSection()获取一个完整的sectionts码流以ts包的形式一个个读取出来,并判断这个包是不是section的一部分。首先分析这个包是能否作为要找section的首包(payload_unit_start_indicator=1);如果不是首包,继续读取下一个进行判断。如果是要组建section的首包,需要判断section从包的何处开始组建。找到首包后,继续找section剩余的包,根据 tsread 软件中显示的section数据,可以发现,section所有的包的continuity_counter字段是自增的,文档中有提到说section中的package是连续的,或许是这个意思,根据continuity_counter字段找section剩余的包。
public static int getSection(GetSectionParameters parameters) {boolean sectionFlag = false;int sectionStart;int sectionPosition = 0;Package packet;int from = parameters.getPosition();int to = from + parameters.getTs().getPackageLen();int continuity_counter = 0;InitSectionReturn initSecReturn;InitSectionParameters initSectionParameters;boolean tag = false;while (to < parameters.getTs().getTsData().length) {packet = new Package(Arrays.copyOfRange(parameters.getTs().getTsData(), from, to));from = to;to = from + parameters.getTs().getPackageLen();if (packet.getPID() == parameters.getTargetPid()) {if (packet.getPayload_unit_start_indicator() == 1) {sectionStart = getSectionStart(packet);if ((sectionStart > parameters.getTs().getPackageLen()) || (sectionStart == 0))continue;parameters.getSection().initSectionHead(packet.getData(), sectionStart);if (parameters.getSection().getTable_id() != parameters.getTargetTable_id())continue;if (parameters.getSectionNumberRecord()[parameters.getSection().getSection_number()]) {continue;}sectionFlag = true;continuity_counter = (packet.getContinuity_counter() + 1) % Package.CONTINUITY_COUNTER_MAX;tag = true;} else if (sectionFlag && packet.getContinuity_counter() == continuity_counter) {continuity_counter = (continuity_counter + 1) % Package.CONTINUITY_COUNTER_MAX;tag = true;}if (tag) {tag = false;initSectionParameters = new InitSectionParameters(parameters, packet, sectionPosition);initSecReturn = initSection(initSectionParameters);if (initSecReturn.isSectionEnd()) {break;}}}}return to;
}
  • initSection()将符合条件packagepayload部分装入sectioncontainer中。装入的时候需要注意,这个package能装入的数据有哪些。并且204的包和188的包相比多的是后面16位的校验码,不需要装入section。在这个方法中还对本次获取的section进行判断,这个section是否已经把数据填充完毕。
public static InitSectionReturn initSection(InitSectionParameters parameters) {boolean sectionEnd = false;int size = 0;int sectionStart = getSectionStart(parameters.getPacket());if (parameters.getParameters().getTs().getPackageLen() == TransportStream.LEN_204) {size = -CRC_LEN;}size += parameters.getParameters().getTs().getPackageLen() - sectionStart;/**the last package in section*/if ((parameters.getParameters().getTs().getPackageLen() - sectionStart) > (parameters.getParameters().getSection().getSection_length() + 3 - parameters.getSectionPosition())) {size = parameters.getParameters().getSection().getSection_length() + 3 - parameters.getSectionPosition();sectionEnd = true;}/**data input */for (int i = sectionStart; i < sectionStart + size; i++) {parameters.getParameters().getSection().getContainer()[parameters.getSectionPosition()] = parameters.getPacket().getData()[i];parameters.setSectionPosition(parameters.getSectionPosition() + 1);}if (sectionEnd) {parameters.getParameters().getSectionNumberRecord()[parameters.getParameters().getSection().getSection_number()] = true;parameters.getParameters().setSectionCount(parameters.getParameters().getSectionCount() + 1);}return new InitSectionReturn(sectionEnd, parameters.getSectionPosition());
}
  • getSectionStart()标记packagepayload的开始位置。packageadaption_field_control判断包的包头后的数据情况,以此来判断payload的实际开始位置。

    00:保留以后使用,(00的数据包在解码的时候应该丢弃)

    01:没有adaption_field, 只有payload

    10:只有adaption,没有payload

    11:adaption_field后面是payload

public static int getSectionStart(Package packet) {int sectionStart = 0;switch (packet.getAdaptation_field_control()) {case 0:case 2:break;case 1:sectionStart = 4;break;case 3:sectionStart = 5 + packet.getData()[4];}if (packet.getPayload_unit_start_indicator() == 1) {sectionStart += packet.getData()[sectionStart] + 1;}return sectionStart;
}

感想:

对于入门新手,组建section看似是最难的,也是最关键的,其实理解了section的组成,再结合tsread软件把section拆分,逆推他是怎么组成的,会发现所有的问题都是小cese啦。
其次在查看资料时,对于section的很多细节都还没注意到,可以对其进行大胆猜想说不定就是你推测的那个意思呢!

在解析之前还参考了这篇博客:https://blog.csdn.net/g742533752/article/details/51272869

如果理解上有什么偏差忘指正…

【ts码流】获取指定section相关推荐

  1. ts码流解析工作总结

    近段时间,由于工作需要,初步研究学习了TS流的解析,多少有些心得体会,这里与大家分享一下(大牛可以无视,呵呵). 已编码的音视频数据,按照PES格式进行第一次打包,即PES包.一系列PES包,也有一种 ...

  2. MPEG-2传输流的码流分析(PS与TS码流)

    0 引言 目前,我国数字电视的信道传输的三种主要传输方式都有了统一明确的国家标准.其主要差别是信道编码.调制方法,但视频和音频信源处理和系统层复用都是以MPEG-2为基础的编码方案.MPEG-2标准自 ...

  3. 关于ES、PES、PS以及TS码流

    from:http://blog.csdn.net/huangblog/article/details/8740571 一.基本概念 1)ES   ES--Elementary  Streams  ( ...

  4. HLS之TS码流封装

    在成功完成了从TS码流中提取出H264和AAC之后,开始一下最终的挑战,就是把H264封装成TS个数.这里想把码流的切片一起实现在里面.这里画了一个大概的实现图. 这个图主要是实现了切片的流程. 我计 ...

  5. TS码流解析-6-解析EIT表

    任务六 利用之前编写的函数,从码流中解析EIT表,主要是获取对应的service_id和EPG标题,时间段等. 相关知识 简述 EIT表按照时间顺序提供每一个业务所包含的事件信息,是事件信息表的简称, ...

  6. 说一说TS码流里面的PCR

    问题的起因是这样的:在我刚开始学习的码流的时候,就有人告诉我TS里面的有一个绝对时间叫做PCR,但是我们因为代码原因,都基本不怎么用.后面分析码流的时候基本都是使用EasyICE,然后看到的数据就是下 ...

  7. JavaCV音视频开发宝典:使用JavaCV读取海康平台或海康网络摄像头sdk回调视频TS码流并解析预览图像

    <JavaCV音视频开发宝典>专栏目录导航 <JavaCV音视频开发宝典>专栏介绍和目录 ​ 前言 两年前博主写了如何利用JavaCV解析各种h264裸流,<JavaCV ...

  8. 截获视频网站 ts流 视频 网络嗅探 IPTV OTT 数据流 音视频数据 码流 ts 流

    忽然想把最近的技术总结一下.做ts流 IP数据流也有5年之久了.接触了大大小小的项目.例如网络嗅探 通过sniffer 获取IP包.ts 码流分析等.今天把二者结合起来可以应用到手机终端.或者工程仪表 ...

  9. 介绍DVB-S码流_PAT_PMT_NIT_CAT

    介绍DVB-S码流,PAT,PMT,NIT,CAT 摘要 本文介绍了电子节目指南信息的构成以及MPEG-2 PSI和DVB-SI信息规范,提出了一种EPG信息的存储结构和节目数据库的生成方案,并据此讲 ...

最新文章

  1. 2022-2028年中国新零售行业深度调研及投资前景预测报告(全卷)
  2. 设计模式 之美 -- 策略模式
  3. javaWeb -- HTTP协议
  4. registry ---------仓库 -----------------镜像
  5. 布局覆盖 超出一部分_Android 布局优化
  6. Arrays 的copyOf() - JDK 6 和操作符 instanceof
  7. python多线程编程(7):线程间通信
  8. STM8学习笔记---Modbus通信协议简单移植
  9. PMP学习系列5:PMBOK(5th)第三章-项目管理过程
  10. poj 3295 Tautology (构造)
  11. 打开运行PS、AI等软件时卡在启动窗口的解决办
  12. 辐射校正(传感器定标+大气校正)
  13. Android 最常用的设计模式九 安卓源码分析—— 适配器模式(Adapter)
  14. 定投复利公式 php,基金定投收益计算公式和复利计算公式
  15. java相关优秀博文收藏
  16. BI工具和数据中台有什么区别?
  17. 【软件测试】软件测试职业规划
  18. 玩转树莓派之ZigBee网关
  19. java读取本地图片_java 怎么才能读取本地磁盘中的图片并传给前端(URL)
  20. IL思路及遇到的问题、解决方法

热门文章

  1. Android项目实战系列—基于博学谷(七)课程模块(上)
  2. 易语言取操作系统类别
  3. 同等学力申硕英语考试2021年备考策略
  4. 无法启动python原因_Pycharm无法运行Python的一些说明
  5. We have awesome remote U.S. jobs waiting for engineers like you.
  6. JAVA_输出5笔购物金额和总金额
  7. Safari访问本地html,safari本地文件读取漏洞之扩展攻击面 | CN-SEC 中文网
  8. AxureRP for chorme 0.62(Axure谷歌浏览器插件)下载安装
  9. 骁龙782g和骁龙778g哪个好
  10. 2010年10月第1期(ACR2010专刊AS篇)_中信国健临床通讯目录