当前位置: 首页 > news >正文

易名域名交易郑州seo怎么做

易名域名交易,郑州seo怎么做,一个专做特卖的网站,电子商务网站建设交印花税吗系列文章目录 ExoPlayer架构详解与源码分析#xff08;1#xff09;——前言 ExoPlayer架构详解与源码分析#xff08;2#xff09;——Player ExoPlayer架构详解与源码分析#xff08;3#xff09;——Timeline ExoPlayer架构详解与源码分析#xff08;4#xff09;—…系列文章目录 ExoPlayer架构详解与源码分析1——前言 ExoPlayer架构详解与源码分析2——Player ExoPlayer架构详解与源码分析3——Timeline ExoPlayer架构详解与源码分析4——整体架构 ExoPlayer架构详解与源码分析5——MediaSource ExoPlayer架构详解与源码分析6——MediaPeriod ExoPlayer架构详解与源码分析7——SampleQueue ExoPlayer架构详解与源码分析8——Loader ExoPlayer架构详解与源码分析9——TsExtractor 文章目录 系列文章目录前言TsExtractorTsDurationReaderSectionReaderPatReaderPmtReaderDefaultTsPayloadReaderFactoryPesReader总结 前言 上篇说完了Extractor的整体结构本篇将详细讲解Extractor的实现主要通过TsExtractor这个实现类来讲解顾名思义TsExtractor是用于TS容器格式的解析器。 TSTransport Stream传输流是一种封装的格式它的全称为MPEG2-TS。 MPEG组织于1994年推出MPEG-2压缩标准以实现视/音频服务与应用互操作的可能性MPEG-2标准是针对标准数字电视和高清晰度电视在各种应用下的压缩方案和系统层的详细规定。在MPEG-2标准中为了将一个或更多的音频、视频或其他的基本数据流合成单个或多个数据流以适应于存储和传送必须对其重新进行打包编码在码流中还需插入各种时间标记、系统控制等信息最后送到信道编码与调制器。这样可以形成两种数据流——传送流TS和节目流PS分别适用于不同的应用。 MPEG2-TS是一种标准数据容器格式传输与存储音视频、节目与系统信息协议数据主要应用于数字广播系统譬如DVB、ATSC与IPTV。TS流是将视频、音频、PSI等数据打包成传输包进行传送。其整体的设计充分考虑了传输过程中的丢包数据干扰等问题特别适合用于节目传输。 科普时间结束回归主线看下ExoPlayer 是如何解析TS结构的 TsExtractor 在看ExoPlayer 源码前必须先来了解下TS的整体结构然后再结合源码看下ExoPlayer是如何实现TS的解析的。 首先看下TS容器的结构网图侵删。 可以看到每个TS包大小为188包含一个header和payload这种固定块大小的结果特别适合网络传输的场景运用的也比较多。 header 名称大小(b)说明sync_byte8同步标记占1个字节固定为0x47当解析器读取到这个字节的时候就知道这是一个包开始位置transport_error_indicator1传输错误指示符1’表示在相关的传输包中至少有一个不可纠正的错误位。当被置1后在错误被纠正之前不能重置为0payload_unit_start_indicator1负载单元起始标示符一个完整的数据包开始时标记为1transport_priority1传输优先级0为低优先级1为高优先级通常取0pid13包的 ID用于区分不同的包注意这个不是唯一的可能相同类型的包都对应同一个PID其中PID有几个固定值用于指定类型的包如PAT包固定值为0x0000transport_scrambling_control2传输加扰控制00表示未加密adaptation_field_control2是否包含自适应区‘00’保留‘01’为无自适应域仅含有效负载‘10’为仅含自适应域无有效负载‘11’为同时带有自适应域和有效负载。continuity_counter4递增计数器从0-f起始值不一定取0但必须是连续的随着每一个具有相同PID的TS流分组而增加当它达到最大值后又回复到0。范围为0~15。接收端可判断是否有包丢失及包传送顺序错误adaptation_field_length8自适应域长度,包含在上图的PRC分段中flag8取0x50表示包含PCR或0x40表示不包含PCR,包含在上图的PRC分段中PCR40Program Clock Reference节目时钟参考用于恢复出与编码端一致的系统时序时钟STCSystem Time Clock。可以理解为当前包的时间戳时间戳一般是以90 kHz 为单位的时间戳所以转化成正常时间戳得除以90000,这段同样包含在上图的PRC分段中 payload payload里主要包含2种类型数据PES和PSI(Program Specific Information:由对于传输流的多路分解以及节目成功再现所必要的标准数据组成) PSI 可以认为属于 6 个表 节目相关表PATTS 节目映射表PMT网络信息表NIT有条件访问表CAT传输流描述表IPMP 控制信息表。 主要介绍下下面3种 PAT表包头的PID固定为0x0000包含一个header和一个body一个payload前3字节为header包含了body的长度后面则为body主要列出所有的PMT表ID可以通过它确定哪些PID的包是PMT表主要要来查询PMT下面是PAT的表结构section_length前为header后面的属于body 名称大小(b)说明table_id8PAT表固定为0x00section_syntax_indicator1固定为1zero1固定为0reserved2固定为11section_length12后面数据的长度transport_stream_id16传输流ID固定为0x0001reserved2固定为11version_number5版本号固定为00000如果PAT有变化则版本号加1current_next_indicator1固定为1表示这个PAT表可以用如果为0则要等待下一个PAT表section_number8固定为0x00last_section_number8固定为0x00开始循环program_number16为0x0000时表示这是NIT网络信息表节目号为0x0001时,表示这是PMTreserved3固定为111PID13PAT对应PMT的包PID值结束循环CRC3232前面数据的CRC32校验码 PMT表包头的PID不固定需要通过PAT获取主要列出了包含的所有流类型及其对于的PID通过它可以确定当前的包对应的是哪种流然后针对性的解析下面是PMT的表结构 名称大小(b)说明table_id8PMT表固定为0x02section_syntax_indicator1固定为1zero1固定为0reserved2固定为11section_length12后面数据的长度program_number16频道号码表示当前的PMT关联到的频道取值0x0001reserved2固定为11version_number5版本号固定为00000如果PAT有变化则版本号加1current_next_indicator1固定为1section_number8固定为0x00last_section_number8固定为0x00reserved3固定为111PCR_PID13PCR(节目参考时钟)所在TS分组的PID指定为视频PIDreserved3固定为111program_info_length12描述信息指定为0x000表示没有开始循环stream_type8流类型标志是Video还是Audio还是其他数据h.264编码对应0x1baac编码对应0x0fmp3编码对应0x03reserved3固定为111elementary_PID13与stream_type对应的PIDreserved4固定为1111ES_info_length12描述信息指定为0x000表示没有结束循环CRC3232前面数据的CRC32校验码 PES 用于承载基本流数据的数据结构可以理解成具体的媒体流数据同样包含header和body看下包结构图 PES的Header结构很复杂这里我们说明下重要的几个 名称大小(b)说明packet_start_code_prefix24固定为0x000001同跟随它的 stream_id 一起组成标识包起始端的包起始码stream_id16流ID音频取值0xc0-0xdf通常为0xc0视频取值0xe0-0xef通常为0xe0具体参照ISO/IEC 13818-1 2.4.3.7PES_packet_length24后面pes数据的长度0表示长度不限制只有视频数据长度会超过0xffffPTS_DTS_flags2当 PTS_DTS_flags 字段设置为‘10’时PES 包头中 PTS 字段存在。设置为‘11’时PES 包头中 PTS 字段和 DTS 字段均存在。设置为‘00’时PES 包头中既无任何 PTS 字段也无任何 DTS 字段存在。值‘01’禁用PES_header_data_length8额外包含的数据长度包含的PTS或者DTS数据PTS33presentation time stamp显示时间戳具体参考ISO/IEC 13818-1 2.4.3.7DTS33decoding time stamp解码时间戳具体参考ISO/IEC 13818-1 2.4.3.7 好了有了上面的知识我们一起来看下源码是如何解析的 首先从初始化看起 public TsExtractor(Mode int mode, Flags int defaultTsPayloadReaderFlags, int timestampSearchBytes) {this(mode,new TimestampAdjuster(0),//创建默认的payload的解析工厂类new DefaultTsPayloadReaderFactory(defaultTsPayloadReaderFlags),timestampSearchBytes);}public TsExtractor(Mode int mode,TimestampAdjuster timestampAdjuster,TsPayloadReader.Factory payloadReaderFactory,int timestampSearchBytes) { ...//初始化缓存数据大小为50个TS包大小tsPacketBuffer new ParsableByteArray(new byte[BUFFER_SIZE], 0); ...//用于从PRC中计算时长durationReader new TsDurationReader(timestampSearchBytes); ...resetPayloadReaders();}//初始化payload解析器private void resetPayloadReaders() {trackIds.clear();tsPayloadReaders.clear();SparseArrayTsPayloadReader initialPayloadReaders payloadReaderFactory.createInitialPayloadReaders();int initialPayloadReadersSize initialPayloadReaders.size();//添加初始化默认的解析器如果没有自定义工厂会使用DefaultTsPayloadReaderFactory此时不包含任何初始化的解析器for (int i 0; i initialPayloadReadersSize; i) {tsPayloadReaders.put(initialPayloadReaders.keyAt(i), initialPayloadReaders.valueAt(i));}//添加包头PAT解析器tsPayloadReaders.put(TS_PAT_PID, new SectionReader(new PatReader()));id3Reader null;}看下初始化后调用的第一个方法主要用来确定当前Extractor是否适用 //在确定使用哪种解析器时会先调用Extractor.sniff决定当前解析器是否可以用于解析上文中也提到了调用点Overridepublic boolean sniff(ExtractorInput input) throws IOException {byte[] buffer tsPacketBuffer.getData();input.peekFully(buffer, 0, TS_PACKET_SIZE * SNIFF_TS_PACKET_COUNT);//填充5*118个数据for (int startPosCandidate 0; startPosCandidate TS_PACKET_SIZE; startPosCandidate) {// Try to identify at least SNIFF_TS_PACKET_COUNT packets starting with TS_SYNC_BYTE.boolean isSyncBytePatternCorrect true;//是否有5个0x47字节连续的间隔188的数据for (int i 0; i SNIFF_TS_PACKET_COUNT; i) {if (buffer[startPosCandidate i * TS_PACKET_SIZE] ! TS_SYNC_BYTE) {isSyncBytePatternCorrect false;break;}}if (isSyncBytePatternCorrect) {input.skipFully(startPosCandidate);return true;}}return false;}然后就开始执行主要的方法read Overridepublic ReadResult int read(ExtractorInput input, PositionHolder seekPosition)throws IOException {long inputLength input.getLength();if (tracksEnded) {//所有PMT表都解析完 tracksEnded//如果tracksEnded了此时数据的总长度已知且不为HLShls有多个ts时长记录在m3u8文件里boolean canReadDuration inputLength ! C.LENGTH_UNSET mode ! MODE_HLS;if (canReadDuration !durationReader.isDurationReadFinished()) {//开始读取时长后面会具体讲到读取方式return durationReader.readDuration(input, seekPosition, pcrPid);}//输出SeekMap表这里保存播放时间戳和数据位置的对应关系可以通过时间戳快速定位到数据位置//这里不深入了maybeOutputSeekMap(inputLength);//是否从头开始这里作用是当Tarck信息解析完毕的时候会返回RESULT_SEEK//回到上面讲的外循环再次从头加载数据if (pendingSeekToStart) {pendingSeekToStart false;seek(/* position */ 0, /* timeUs */ 0);if (input.getPosition() ! 0) {seekPosition.position 0;return RESULT_SEEK;}}if (tsBinarySearchSeeker ! null tsBinarySearchSeeker.isSeeking()) {return tsBinarySearchSeeker.handlePendingSeek(input, seekPosition);}}//读取至少一段Ts包if (!fillBufferWithAtLeastOnePacket(input)) {return RESULT_END_OF_INPUT;}//从上次读取位置查找第一个包的结束位置int endOfPacket findEndOfFirstTsPacketInBuffer();int limit tsPacketBuffer.limit();//如果超过limit其实就是包不足188字节这里会继续加载if (endOfPacket limit) {return RESULT_CONTINUE;}TsPayloadReader.Flags int packetHeaderFlags 0;// Note: See ISO/IEC 13818-1, section 2.4.3.2 for details of the header format.//读取4字节也就是包头的长度int tsPacketHeader tsPacketBuffer.readInt();if ((tsPacketHeader 0x800000) ! 0) { // 获取transport_error_indicator不等0也就是这个包有问题// There are uncorrectable errors in this packet.tsPacketBuffer.setPosition(endOfPacket);//跳过当前包return RESULT_CONTINUE;}//获取payload_unit_start_indicator位负载单元起始标示符一个完整的数据包开始时标记为1packetHeaderFlags | (tsPacketHeader 0x400000) ! 0 ? FLAG_PAYLOAD_UNIT_START_INDICATOR : 0;// Ignoring transport_priority (tsPacketHeader 0x200000)获取包PID 111111111111100000000取4到16位右移8位去除后8位int pid (tsPacketHeader 0x1FFF00) 8;// Ignoring transport_scrambling_control (tsPacketHeader 0xC0)//获取adaptation_field_control第1位判断adaptationField是否存在boolean adaptationFieldExists (tsPacketHeader 0x20) ! 0;//获取adaptation_field_control第2位判断paload是否存在boolean payloadExists (tsPacketHeader 0x10) ! 0;//由于默认只设置了PAT的解析器所以第一次只有当PID为0时才能获取到解析器TsPayloadReader payloadReader payloadExists ? tsPayloadReaders.get(pid) : null;if (payloadReader null) {//不存在payload跳过当前包tsPacketBuffer.setPosition(endOfPacket);return RESULT_CONTINUE;}// 检查连续性if (mode ! MODE_HLS) {//获取continuity_counter字节int continuityCounter tsPacketHeader 0xF;//获取上一个计数int previousCounter continuityCounters.get(pid, continuityCounter - 1);continuityCounters.put(pid, continuityCounter);if (previousCounter continuityCounter) {// 相同的counter可能是重传的数据直接跳过tsPacketBuffer.setPosition(endOfPacket);return RESULT_CONTINUE;} else if (continuityCounter ! ((previousCounter 1) 0xF)) {// 非连续性的数据可能发生了丢包或者seek通知解析器包不连续重置相关标记位payloadReader.seek();}}// 如果存在adaptationField跳过if (adaptationFieldExists) {//获取adaptation_field_length用于跳过相应数据int adaptationFieldLength tsPacketBuffer.readUnsignedByte();int adaptationFieldFlags tsPacketBuffer.readUnsignedByte();packetHeaderFlags |(adaptationFieldFlags 0x40) ! 0 // random_access_indicator.? TsPayloadReader.FLAG_RANDOM_ACCESS_INDICATOR: 0;tsPacketBuffer.skipBytes(adaptationFieldLength - 1 /* flags */);}// 开始读取payloadboolean wereTracksEnded tracksEnded;if (shouldConsumePacketPayload(pid)) {tsPacketBuffer.setLimit(endOfPacket);//设置解析结束位置//将数据喂给相应解析器第一次consume的解析器肯定为PAT解析器接下来会分析payloadReader.consume(tsPacketBuffer, packetHeaderFlags);tsPacketBuffer.setLimit(limit);}//非HLStrack完成PMT已经读取且长度已知非直播流if (mode ! MODE_HLS !wereTracksEnded tracksEnded inputLength ! C.LENGTH_UNSET) {//重新开始再读一遍因为有可能有些媒体数据在PTM等轨道信息数据之前pendingSeekToStart true;}tsPacketBuffer.setPosition(endOfPacket);return RESULT_CONTINUE;}在分析PAT解析器前这里加个插曲讲下上面说到的TsDurationReader看下ExoPlayer是如何计算视频时长的。 TsDurationReader 直入主题readDuration public Extractor.ReadResult int readDuration(ExtractorInput input, PositionHolder seekPositionHolder, int pcrPid) throws IOException {if (pcrPid 0) {return finishReadDuration(input);}if (!isLastPcrValueRead) {return readLastPcrValue(input, seekPositionHolder, pcrPid);//获取最后一个PCR的值}if (lastPcrValue C.TIME_UNSET) {return finishReadDuration(input);}if (!isFirstPcrValueRead) {return readFirstPcrValue(input, seekPositionHolder, pcrPid);//获取最第一个PCR的值}if (firstPcrValue C.TIME_UNSET) {return finishReadDuration(input);}long minPcrPositionUs pcrTimestampAdjuster.adjustTsTimestamp(firstPcrValue);long maxPcrPositionUs pcrTimestampAdjuster.adjustTsTimestamp(lastPcrValue);durationUs maxPcrPositionUs - minPcrPositionUs;//计算差值if (durationUs 0) {Log.w(TAG, Invalid duration: durationUs . Using TIME_UNSET instead.);durationUs C.TIME_UNSET;}return finishReadDuration(input);}private int readFirstPcrValue(ExtractorInput input, PositionHolder seekPositionHolder, int pcrPid)throws IOException {int bytesToSearch (int) min(timestampSearchBytes, input.getLength());int searchStartPosition 0;//从前往后依次读取包相应的获取最后一个时就是从后往前依次读取包if (input.getPosition() ! searchStartPosition) {//回到上面讲的ExtractingLoadable外部循环再次从下面指定位置打开源进行读取seekPositionHolder.position searchStartPosition;return Extractor.RESULT_SEEK;}packetBuffer.reset(bytesToSearch);input.resetPeekPosition();input.peekFully(packetBuffer.getData(), /* offset */ 0, bytesToSearch);firstPcrValue readFirstPcrValueFromBuffer(packetBuffer, pcrPid);isFirstPcrValueRead true;return Extractor.RESULT_CONTINUE;}//最终调用这个读取包头public static long readPcrFromPacket(ParsableByteArray packetBuffer, int startOfPacket, int pcrPid) {packetBuffer.setPosition(startOfPacket);if (packetBuffer.bytesLeft() 5) {// Header 4 bytes, adaptationFieldLength 1 byte.return C.TIME_UNSET;}// Note: See ISO/IEC 13818-1, section 2.4.3.2 for details of the header format.//读取包头4字节int tsPacketHeader packetBuffer.readInt();if ((tsPacketHeader 0x800000) ! 0) {//确保包无错误// transport_error_indicator ! 0 means there are uncorrectable errors in this packet.return C.TIME_UNSET;}//获取包PIDint pid (tsPacketHeader 0x1FFF00) 8;if (pid ! pcrPid) {return C.TIME_UNSET;}//判断adaptationField是否存在boolean adaptationFieldExists (tsPacketHeader 0x20) ! 0;if (!adaptationFieldExists) {return C.TIME_UNSET;}//获取adaptationField长度int adaptationFieldLength packetBuffer.readUnsignedByte();//确认长度if (adaptationFieldLength 7 packetBuffer.bytesLeft() 7) {int flags packetBuffer.readUnsignedByte();//获取是否设置pcrboolean pcrFlagSet (flags 0x10) 0x10;if (pcrFlagSet) {byte[] pcrBytes new byte[6];//解析PCRpacketBuffer.readBytes(pcrBytes, /* offset */ 0, pcrBytes.length);return readPcrValueFromPcrBytes(pcrBytes);}}return C.TIME_UNSET;}//解析PCR 0xFF保持原始字节数据网络数据大端序读取只读取了前33位精度要求不高舍弃后7位private static long readPcrValueFromPcrBytes(byte[] pcrBytes) {return (pcrBytes[0] 0xFFL) 25| (pcrBytes[1] 0xFFL) 17| (pcrBytes[2] 0xFFL) 9| (pcrBytes[3] 0xFFL) 1| (pcrBytes[4] 0xFFL) 7;}public static long ptsToUs(long pts) {return (pts * C.MICROS_PER_SECOND) / 90000;}这里还有个插曲 0xFF这么做的主要原因是因为在java中byte类型为大小为1字节也就是8位而Long整型是8字节64位JVM在将byte转为Long时取byte作为最后1字节其他7字节采用补码的方式填充为0xFFFFFFF 0xFF后就可以将前7位恢复为0x0000000保持原始的字节数据详细可以参考byte为什么要与上0xff这篇文章 TsDurationReader获取的时长主要通过下面几步 当流的长度已知非直播流从TS文件尾部查找第一个包含PCR的包的PCR值从TS文件头部查找第一个包含PCR的包的PCR值获取2者的差值即为时长时间戳一般是以90 kHz 为单位再除以90000就是真实的时间戳了 好了回到主线看下第一次的PAT解析都干了什么由于PAT和PMT有着几乎相同的头结构这里又抽象了一个SectionReader SectionReader 看下公共头的解析过程 Overridepublic void consume(ParsableByteArray data, Flags int flags) {boolean payloadUnitStartIndicator (flags FLAG_PAYLOAD_UNIT_START_INDICATOR) ! 0;int payloadStartPosition C.INDEX_UNSET;if (payloadUnitStartIndicator) {int payloadStartOffset data.readUnsignedByte();payloadStartPosition data.getPosition() payloadStartOffset;}if (waitingForPayloadStart) {if (!payloadUnitStartIndicator) {return;}waitingForPayloadStart false;data.setPosition(payloadStartPosition);bytesRead 0;}while (data.bytesLeft() 0) {//还有剩余数据if (bytesRead SECTION_HEADER_LENGTH) {//解析前3字节// Note: see ISO/IEC 13818-1, section 2.4.4.3 for detailed information on the format of// the header.if (bytesRead 0) {int tableId data.readUnsignedByte();//获取tableIddata.setPosition(data.getPosition() - 1);if (tableId 0xFF /* forbidden value */) {//判断合法性// No more sections in this ts packet.waitingForPayloadStart true;//跳过当前包return;}}int headerBytesToRead min(data.bytesLeft(), SECTION_HEADER_LENGTH - bytesRead);// sectionData is guaranteed to have enough space because its initialized with a 32-element// backing array and headerBytesToRead is at most 3.data.readBytes(sectionData.getData(), bytesRead, headerBytesToRead);bytesRead headerBytesToRead;if (bytesRead SECTION_HEADER_LENGTH) {//已将所有header数据读取到sectionDatasectionData.setPosition(0);sectionData.setLimit(SECTION_HEADER_LENGTH);sectionData.skipBytes(1); //跳过tableidint secondHeaderByte sectionData.readUnsignedByte();//读取头第2个字节int thirdHeaderByte sectionData.readUnsignedByte();//读取头第3个字节sectionSyntaxIndicator (secondHeaderByte 0x80) ! 0;//获取section_syntax_indicatortotalSectionLength //获取section_length(((secondHeaderByte 0x0F) 8) | thirdHeaderByte) SECTION_HEADER_LENGTH;if (sectionData.capacity() totalSectionLength) {//确保缓存够大能够放下body// Ensure there is enough space to keep the whole section.int limit min(MAX_SECTION_LENGTH, max(totalSectionLength, sectionData.capacity() * 2));sectionData.ensureCapacity(limit);}}} else {// 读取bodyint bodyBytesToRead min(data.bytesLeft(), totalSectionLength - bytesRead);// sectionData has been sized large enough for totalSectionLength when reading the header.data.readBytes(sectionData.getData(), bytesRead, bodyBytesToRead);bytesRead bodyBytesToRead;if (bytesRead totalSectionLength) {//已将所有body数据读取到sectionDataif (sectionSyntaxIndicator) {// This section has common syntax as defined in ISO/IEC 13818-1, section 2.4.4.11.if (Util.crc32(sectionData.getData(), 0, totalSectionLength, 0xFFFFFFFF) ! 0) {//首先CRC校验数据完整性// The CRC is invalid so discard the section.waitingForPayloadStart true;return;}sectionData.setLimit(totalSectionLength - 4); // 去除最后的32位校验位} else {// This is a private section with private defined syntax.sectionData.setLimit(totalSectionLength);}sectionData.setPosition(0);reader.consume(sectionData);//将body喂给下个解析器如果是PAT包这里调用PAT解析器解析bytesRead 0;}}}}SectionReader主要做了公共头的解析至于body则交给PatReader或者PmtReader解析 PatReader Overridepublic void consume(ParsableByteArray sectionData) {int tableId sectionData.readUnsignedByte();//PAT tableId 表固定为0x00if (tableId ! 0x00 /* program_association_section */) {// See ISO/IEC 13818-1, section 2.4.4.4 for more information on table id assignment.return;}// section_syntax_indicator(1), 0(1), reserved(2), section_length(4)int secondHeaderByte sectionData.readUnsignedByte();if ((secondHeaderByte 0x80) 0) {// section_syntax_indicator 必须为 1. See ISO/IEC 13818-1, section 2.4.4.5.return;}// 跳过section_length(8), transport_stream_id (16), reserved (2), version_number (5),// current_next_indicator (1), section_number (8), last_section_number (8)sectionData.skipBytes(6);int programCount sectionData.bytesLeft() / 4;//一个PMT描述为4字节计算有多少个PMT表for (int i 0; i programCount; i) {sectionData.readBytes(patScratch, 4);int programNumber patScratch.readBits(16);//program_numberpatScratch.skipBits(3); // reserved (3)if (programNumber 0) {//program_number0则为NIT网络信息表直接跳过patScratch.skipBits(13); // network_PID (13)} else {int pid patScratch.readBits(13);if (tsPayloadReaders.get(pid) null) {//创建PMT解析器当下次读取到PMT的包ID时直接调用PMT解析tsPayloadReaders.put(pid, new SectionReader(new PmtReader(pid)));remainingPmts;}}}if (mode ! MODE_HLS) {tsPayloadReaders.remove(TS_PAT_PID);}}PatReader主要工作就是将PMT表解析出来每个PMT ID对应初始化出一个解析器当下次读取到这些PID的包时采用对于的PmtReader PmtReader Overridepublic void consume(ParsableByteArray sectionData) {int tableId sectionData.readUnsignedByte();//确保是PMT表if (tableId ! 0x02 /* TS_program_map_section */) {// See ISO/IEC 13818-1, section 2.4.4.4 for more information on table id assignment.return;}// 处理时间戳TimestampAdjuster timestampAdjuster;if (mode MODE_SINGLE_PMT || mode MODE_HLS || remainingPmts 1) {timestampAdjuster timestampAdjusters.get(0);} else {timestampAdjuster new TimestampAdjuster(timestampAdjusters.get(0).getFirstSampleTimestampUs());timestampAdjusters.add(timestampAdjuster);}// section_syntax_indicator(1), 0(1), reserved(2), section_length(4)int secondHeaderByte sectionData.readUnsignedByte();if ((secondHeaderByte 0x80) 0) {// section_syntax_indicator 必须为 1. See ISO/IEC 13818-1, section 2.4.4.9.return;}// section_length(8)sectionData.skipBytes(1);int programNumber sectionData.readUnsignedShort();// Skip 3 bytes (24 bits), including:// reserved (2), version_number (5), current_next_indicator (1), section_number (8),// last_section_number (8)sectionData.skipBytes(3);sectionData.readBytes(pmtScratch, 2);// reserved (3), PCR_PID (13)pmtScratch.skipBits(3);pcrPid pmtScratch.readBits(13);// Read program_info_length.sectionData.readBytes(pmtScratch, 2);pmtScratch.skipBits(4);int programInfoLength pmtScratch.readBits(12);// Skip the descriptors.sectionData.skipBytes(programInfoLength);//初始化ID3解析器if (mode MODE_HLS id3Reader null) {// Setup an ID3 track regardless of whether theres a corresponding entry, in case one// appears intermittently during playback. See [Internal: b/20261500].EsInfo id3EsInfo new EsInfo(TS_STREAM_TYPE_ID3, null, null, Util.EMPTY_BYTE_ARRAY);id3Reader payloadReaderFactory.createPayloadReader(TS_STREAM_TYPE_ID3, id3EsInfo);if (id3Reader ! null) {id3Reader.init(timestampAdjuster,output,new TrackIdGenerator(programNumber, TS_STREAM_TYPE_ID3, MAX_PID_PLUS_ONE));}}trackIdToReaderScratch.clear();trackIdToPidScratch.clear();int remainingEntriesLength sectionData.bytesLeft();while (remainingEntriesLength 0) {//开始解析PMT表数据sectionData.readBytes(pmtScratch, 5);int streamType pmtScratch.readBits(8);pmtScratch.skipBits(3); // reservedint elementaryPid pmtScratch.readBits(13);pmtScratch.skipBits(4); // reservedint esInfoLength pmtScratch.readBits(12); // ES_info_length.EsInfo esInfo readEsInfo(sectionData, esInfoLength);//读取ESInfo数据//0x05 private_sections 0x06 PES packets containing private dataif (streamType 0x06 || streamType 0x05) {streamType esInfo.streamType;//使用esInfo的streamType}remainingEntriesLength - esInfoLength 5;int trackId mode MODE_HLS ? streamType : elementaryPid;if (trackIds.get(trackId)) {continue;}NullableTsPayloadReader reader mode MODE_HLS streamType TS_STREAM_TYPE_ID3? id3Reader//根据streamType创建对应的解析器后面会分析: payloadReaderFactory.createPayloadReader(streamType, esInfo);if (mode ! MODE_HLS|| elementaryPid trackIdToPidScratch.get(trackId, MAX_PID_PLUS_ONE)) {trackIdToPidScratch.put(trackId, elementaryPid);trackIdToReaderScratch.put(trackId, reader);//用于后续获取}}int trackIdCount trackIdToPidScratch.size();for (int i 0; i trackIdCount; i) {int trackId trackIdToPidScratch.keyAt(i);int trackPid trackIdToPidScratch.valueAt(i);trackIds.put(trackId, true);trackPids.put(trackPid, true);Nullable TsPayloadReader reader trackIdToReaderScratch.valueAt(i);if (reader ! null) {if (reader ! id3Reader) {//初始化所有解析器reader.init(timestampAdjuster,output,new TrackIdGenerator(programNumber, trackId, MAX_PID_PLUS_ONE));}tsPayloadReaders.put(trackPid, reader);}}if (mode MODE_HLS) {if (!tracksEnded) {output.endTracks();remainingPmts 0;tracksEnded true;}} else {tsPayloadReaders.remove(pid);//解析完成移除当前PMT解析器remainingPmts mode MODE_SINGLE_PMT ? 0 : remainingPmts - 1;if (remainingPmts 0) {//所以PMT表都已读取output.endTracks();//endTracks,这个时候相当于MediaPeriod的prepare过程结束已经获取到播放媒体的相关数据tracksEnded true;}}}PmtReader主要作用就是获取其中的流类型然后创建出对应的解析器最后所以PMT初始化完成后通知上层trackEnded 那么解析器具体是如何创建的呢这部分工作PmtReader交由payloadReaderFactory默认实现了DefaultTsPayloadReaderFactory DefaultTsPayloadReaderFactory 这个createPayloadReader方法里基本上将所有的流类型创建了解析器可以当一个索引看下 OverrideNullablepublic TsPayloadReader createPayloadReader(int streamType, EsInfo esInfo) {switch (streamType) {case TsExtractor.TS_STREAM_TYPE_MPA:case TsExtractor.TS_STREAM_TYPE_MPA_LSF:return new PesReader(new MpegAudioReader(esInfo.language));case TsExtractor.TS_STREAM_TYPE_AAC_ADTS:return isSet(FLAG_IGNORE_AAC_STREAM)? null: new PesReader(new AdtsReader(false, esInfo.language));case TsExtractor.TS_STREAM_TYPE_AAC_LATM:return isSet(FLAG_IGNORE_AAC_STREAM)? null: new PesReader(new LatmReader(esInfo.language));case TsExtractor.TS_STREAM_TYPE_AC3:case TsExtractor.TS_STREAM_TYPE_E_AC3:return new PesReader(new Ac3Reader(esInfo.language));case TsExtractor.TS_STREAM_TYPE_AC4:return new PesReader(new Ac4Reader(esInfo.language));case TsExtractor.TS_STREAM_TYPE_HDMV_DTS:if (!isSet(FLAG_ENABLE_HDMV_DTS_AUDIO_STREAMS)) {return null;}// Fall through.case TsExtractor.TS_STREAM_TYPE_DTS:return new PesReader(new DtsReader(esInfo.language));case TsExtractor.TS_STREAM_TYPE_H262:case TsExtractor.TS_STREAM_TYPE_DC2_H262:return new PesReader(new H262Reader(buildUserDataReader(esInfo)));case TsExtractor.TS_STREAM_TYPE_H263:return new PesReader(new H263Reader(buildUserDataReader(esInfo)));case TsExtractor.TS_STREAM_TYPE_H264:return isSet(FLAG_IGNORE_H264_STREAM)? null: new PesReader(new H264Reader(buildSeiReader(esInfo),isSet(FLAG_ALLOW_NON_IDR_KEYFRAMES),isSet(FLAG_DETECT_ACCESS_UNITS)));case TsExtractor.TS_STREAM_TYPE_H265:return new PesReader(new H265Reader(buildSeiReader(esInfo)));case TsExtractor.TS_STREAM_TYPE_SPLICE_INFO:return isSet(FLAG_IGNORE_SPLICE_INFO_STREAM)? null: new SectionReader(new PassthroughSectionPayloadReader(MimeTypes.APPLICATION_SCTE35));case TsExtractor.TS_STREAM_TYPE_ID3:return new PesReader(new Id3Reader());case TsExtractor.TS_STREAM_TYPE_DVBSUBS:return new PesReader(new DvbSubtitleReader(esInfo.dvbSubtitleInfos));case TsExtractor.TS_STREAM_TYPE_AIT:return new SectionReader(new PassthroughSectionPayloadReader(MimeTypes.APPLICATION_AIT));default:return null;}}这里关注下目前比较主流的H.264,可以看到首先是创建Pes解析器解析PES然后从PES中解析H.264数据组后在H.264数据中解析SEI信息 PesReader 看下PES如何解析 Overridepublic final void consume(ParsableByteArray data, Flags int flags) throws ParserException {Assertions.checkStateNotNull(timestampAdjuster); // Asserts init has been called.//一个状态机 ...while (data.bytesLeft() 0) {switch (state) {case STATE_FINDING_HEADER:data.skipBytes(data.bytesLeft());break;case STATE_READING_HEADER:if (continueRead(data, pesScratch.data, HEADER_SIZE)) {setState(parseHeader() ? STATE_READING_HEADER_EXTENSION : STATE_FINDING_HEADER);}break;case STATE_READING_HEADER_EXTENSION:int readLength min(MAX_HEADER_EXTENSION_SIZE, extendedHeaderLength);// Read as much of the extended header as were interested in, and skip the rest.if (continueRead(data, pesScratch.data, readLength) continueRead(data, /* target */ null, extendedHeaderLength)) {parseHeaderExtension();flags | dataAlignmentIndicator ? FLAG_DATA_ALIGNMENT_INDICATOR : 0;reader.packetStarted(timeUs, flags);setState(STATE_READING_BODY);}break;case STATE_READING_BODY:readLength data.bytesLeft();int padding payloadSize C.LENGTH_UNSET ? 0 : readLength - payloadSize;if (padding 0) {readLength - padding;data.setLimit(data.getPosition() readLength);}reader.consume(data);//调用下层解析器解析body如H264Readerif (payloadSize ! C.LENGTH_UNSET) {payloadSize - readLength;if (payloadSize 0) {reader.packetFinished();setState(STATE_READING_HEADER);}}break;default:throw new IllegalStateException();}}}//解析headerprivate boolean parseHeader() {// Note: see ISO/IEC 13818-1, section 2.4.3.6 for detailed information on the format of// the header.pesScratch.setPosition(0);int startCodePrefix pesScratch.readBits(24);if (startCodePrefix ! 0x000001) {//校验合法性Log.w(TAG, Unexpected start code prefix: startCodePrefix);payloadSize C.LENGTH_UNSET;return false;}pesScratch.skipBits(8); // stream_id.int packetLength pesScratch.readBits(16);//获取长度pesScratch.skipBits(5); // 10 (2), PES_scrambling_control (2), PES_priority (1)dataAlignmentIndicator pesScratch.readBit();pesScratch.skipBits(2); // copyright (1), original_or_copy (1)ptsFlag pesScratch.readBit();//PTS_flagsdtsFlag pesScratch.readBit();//DTS_flags// ESCR_flag (1), ES_rate_flag (1), DSM_trick_mode_flag (1),// additional_copy_info_flag (1), PES_CRC_flag (1), PES_extension_flag (1)pesScratch.skipBits(6);extendedHeaderLength pesScratch.readBits(8);//获取额外数据长度用于DTS PTS解析 ...return true;}RequiresNonNull(timestampAdjuster)private void parseHeaderExtension() {//解析出PTS和DTS用于后续H264解析器pesScratch.setPosition(0);timeUs C.TIME_UNSET;if (ptsFlag) {pesScratch.skipBits(4); // 0010 or 0011long pts (long) pesScratch.readBits(3) 30;pesScratch.skipBits(1); // marker_bitpts | pesScratch.readBits(15) 15;pesScratch.skipBits(1); // marker_bitpts | pesScratch.readBits(15);pesScratch.skipBits(1); // marker_bitif (!seenFirstDts dtsFlag) {pesScratch.skipBits(4); // 0011long dts (long) pesScratch.readBits(3) 30;pesScratch.skipBits(1); // marker_bitdts | pesScratch.readBits(15) 15;pesScratch.skipBits(1); // marker_bitdts | pesScratch.readBits(15);pesScratch.skipBits(1); // marker_bittimestampAdjuster.adjustTsTimestamp(dts);seenFirstDts true;}timeUs timestampAdjuster.adjustTsTimestamp(pts);}}PesReader主要是将Pes的Header解析获取ES数据的长度以及PES与DTS数据然后将这些数据传递给下层ES解析器。 总结 关于TsExtractor的内容先写到这里ES解析器可能比TS更加复杂计划将ES解析的内容单独一篇来解析计划以目前最为普遍的H.264格式作为分析对象也就是对应ExoPlayer中的H264Reader。 版权声明 © 本文为CSDN作者山雨楼原创文章 转载请注明出处 原创不易觉得有用的话收藏转发点赞支持
http://www.dnsts.com.cn/news/19228.html

相关文章:

  • 怎么把文件放到网站的根目录网站做代理服务器
  • 如何建设营销型的网站用什么做响应式网站
  • 哪个做问卷网站佣金高做网站需要提供些什么页面
  • app网站样式建网站用什么系统好
  • 外贸网站的特色做网站应规避的风险
  • 公司建站后还要录入网页吗宝塔搭建wordpress网站
  • 使用angular2框架做的网站wordpress页面响应慢
  • 四川省住房与城乡建设厅网站关键词seo排名怎么做的
  • 网页设计与网站规划建网站的公司大全
  • 重庆网站推广公司哪家好网站动图怎么做
  • 设置网站默认首页c语言入门自学零基础
  • 泰安建站哪家好网络推广 推广
  • 怎么做像知乎一样的网站做自己网站
  • 建设市政务信息共享网站wordpress需要先安装数据库
  • wordpress国内打开速度慢福州seo结算
  • 此网站域名三天更换做外贸的人经常用什么网站
  • 做网站买什么服务器 便宜网站制作文章
  • 网站建设会计帐务处理做地推的网站
  • 网站开发培训什么ps设计师网站有哪些
  • 如何自己做网站模版3d建模培训班有用吗
  • 模板网站建设 报价余杭门户网站
  • 现在网站前台用什么做dedecms网站地图
  • 做淘客的网站名称wordpress手机编辑器插件下载地址
  • 李连杰做的功夫网站南宁网站建设 超博网络
  • 关键词没有排名的网站怎么做app制作教程下载
  • 北京监理建设协会网站荆门网站建设
  • 直邮网站的推广活动怎么做企业管理定制软件
  • 2022年最火的电商平台企业网站seo服务
  • 企业手机版网站外国做袜子的网站
  • 做网站必须学php吗wordpress 首页调用页面标题