Android 12(S) MultiMedia(十一)从MPEG2TSExtractor到MPEG2-TS
本节主要学习内容是看看MPEG2TSExtractor是如何处理TS流的。
相关代码位置:
frameworks/av/media/extractors/mpeg2/MPEG2TSExtractor.cpp
frameworks/av/media/libstagefright/mpeg2ts/ATSParser.cpp
1、TS Header
MPEG2TSExtractor的构造函数中有个init方法用于加载ts流所包含的信息,而关键方法是feedMore,读完这个方法大致就可以直到ts包的结构以及ts流的组成,再回过头来看MPEG2TSExtractor中的方法就会很简单了。
status_t MPEG2TSExtractor::feedMore(bool isInit) { Mutex::Autolock autoLock(mLock); uint8_t packet[kTSPacketSize]; // 1、通过DataSource读取一个ts packet ssize_t n = mDataSource->readAt(mOffset + mHeaderSkip, packet, kTSPacketSize); // 如果读取到的字节数小于188 if (n < (ssize_t)kTSPacketSize) { // 通知ATSParser读取结束 if (n >= 0) { mParser->signalEOS(ERROR_END_OF_STREAM); } // 如果读取错误,返回-1,否则返回EOS return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM; } // 利用当前的offset,创建一个同步点 ATSParser::SyncEvent event(mOffset); // 2、更新下次读取的offset mOffset += mHeaderSkip + n; // 3、调用ATSParser的方法进行demux status_t err = mParser->feedTSPacket(packet, kTSPacketSize, &event); if (event.hasReturnedData()) { if (isInit) { mLastSyncEvent = event; } else { addSyncPoint_l(event); } } return err; }
看起来feedMore这个方法并不复杂,但是feedTSPacket这个方法实现还是挺复杂的,接下来就来看看。
status_t ATSParser::feedTSPacket(const void *data, size_t size, SyncEvent *event) { if (size != kTSPacketSize) { ALOGE("Wrong TS packet size"); return BAD_VALUE; } // 利用读取的数据创建一个ABitReader ABitReader br((const uint8_t *)data, kTSPacketSize); // 真正开始demux return parseTS(&br, event); }
feedTSPacket中创建了个ABitReader,用它可以实现按位读取,最后就调用parseTS做真正的demux操作。下面先贴一个TS包包头格式图:
status_t ATSParser::parseTS(ABitReader *br, SyncEvent *event) { // 1、读取同步字节 8 bit,不是0x47则丢弃 unsigned sync_byte = br->getBits(8); if (sync_byte != 0x47u) { ALOGE("[error] parseTS: return error as sync_byte=0x%x", sync_byte); return BAD_VALUE; } // 2、读取传输误差指示符 1bit if (br->getBits(1)) { // transport_error_indicator // silently ignore. return OK; } // 3、读取有效载荷单元起始指示符 1 bit unsigned payload_unit_start_indicator = br->getBits(1); // 4、读取传输优先级 1 bit MY_LOGV("transport_priority = %u", br->getBits(1)); // 5、PID 13 bit unsigned PID = br->getBits(13); // 6、传输加扰控制 2 bit unsigned transport_scrambling_control = br->getBits(2); // 7、自适应字段控制 2 bit unsigned adaptation_field_control = br->getBits(2); // 8、连续计数器 4 bit unsigned continuity_counter = br->getBits(4); status_t err = OK; // 9、判断自适应字段控制,如果是2或者3就调用parseAdaptationField unsigned random_access_indicator = 0; if (adaptation_field_control == 2 || adaptation_field_control == 3) { err = parseAdaptationField(br, PID, &random_access_indicator); } if (err == OK) { // 10、如果自适应字段控制为1或者3,则调用parsePID 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); } } ++mNumTSPacketsParsed; return err; }
上面的1 - 8步可以对应上ts包头的结构。后面用到有效载荷单元起始符payload_unit_start_indicator,和传输加扰控制。第9步会根据自适应字段控制的值去做下一步操作,所谓自适应字段控制,意思也就是它的值控制着自适应字段的内容,值与内容的对照表:
自适应字段控制值 | 对应内容 |
00 | 保留 |
01 | 没有调整字段,只含有184bytes的有效载荷 |
10 | 没有有效载荷,只含有183bytes的调整字段 |
11 | 0~182bytes的调整字段后为有效载荷 |
自适应字段控制值为2、3时有调整字段,需要去调用parseAdaptationField,
这段代码不去展开,如果PCR标志位为1需要读取后续的可选字段,后面用到的只有随机读取指示符 random_access_indicator。
到这里TS包的包头就解析结束了,接下来就要解析包中的内容了。
自适应字段控制值为1、3时有有效载荷,需要调用parsePID,这里就比较复杂了(有些内容还没搞懂),先贴代码:
status_t ATSParser::parsePID( ABitReader *br, unsigned PID, unsigned continuity_counter, unsigned payload_unit_start_indicator, unsigned transport_scrambling_control, unsigned random_access_indicator, SyncEvent *event) { // 1、检查PID是否已经被记录到PSISection中 ssize_t sectionIndex = mPSISections.indexOfKey(PID); // 2、如果已经记录 if (sectionIndex >= 0) { sp<PSISection> section = mPSISections.valueAt(sectionIndex); // 2.1、有效载荷单元起始符为1,说明该包承载有PSI数据的第一个字节 if (payload_unit_start_indicator) { // 2.2、清除section中的数据 if (!section->isEmpty()) { ALOGW("parsePID encounters payload_unit_start_indicator when section is not empty"); section->clear(); } // 2.3、获取pointer_field,指示PSI在payload中的位置 unsigned skip = br->getBits(8); section->setSkipBytes(skip + 1); // skip filler bytes + pointer field itself,注意这里是按字节跳过 // 2.4、跳过数据包中payload之前的数据 br->skipBits(skip * 8); } if (br->numBitsLeft() % 8 != 0) { return ERROR_MALFORMED; } // 2.5、将后续PSI分段中的数据 接到前面的PSI数据上 status_t err = section->append(br->data(), br->numBitsLeft() / 8); if (err != OK) { return err; } // 2.6、检查PSI是否读取完毕 if (!section->isComplete()) { return OK; } // 2.7、校验CRC if (!section->isCRCOkay()) { return BAD_VALUE; } ABitReader sectionBits(section->data(), section->size()); if (PID == 0) { // 2.8、读取PAT表 parseProgramAssociationTable(§ionBits); } else { bool handled = false; for (size_t i = 0; i < mPrograms.size(); ++i) { status_t err; // abc if (!mPrograms.editItemAt(i)->parsePSISection( PID, §ionBits, &err)) { continue; } if (err != OK) { return err; } handled = true; break; } if (!handled) { mPSISections.removeItem(PID); section.clear(); } } if (section != NULL) { section->clear(); } return OK; } // cdd bool handled = false; for (size_t i = 0; i < mPrograms.size(); ++i) { status_t err; if (mPrograms.editItemAt(i)->parsePID( PID, continuity_counter, payload_unit_start_indicator, transport_scrambling_control, random_access_indicator, br, &err, event)) { if (err != OK) { return err; } handled = true; break; } } if (!handled) { handled = mCasManager->parsePID(br, PID); } if (!handled) { ALOGV("PID 0x%04x not handled.", PID); } return OK; }
先介绍几个概念:
PSI:Program Specific Infomation 节目专用信息,意思就是保存着流中节目的信息
PAT:Program Associate Tables 节目关联表
PMT:Program Map Tables 节目映射表
进入ATSParser::parsePID这个方法之后会有两个分支:
(1)先判断当前PID是否被记录在了PSISection列表中,如果有记录则说明当前包是一个包含PSI信息的包。接下来会判断 payload_unit_start_indicator,这个值有多种意义,在包含有PSI的包中,值为1说明该包承载有PSI数据的第一个字节(换句话说PSI信息可能分成多个包发送,这个包是第一个包),如果为0则该包不是第一个包。后续的pointer_field会指示PSI数据所在的位置,但是setSkipBytes的作用不太明白,因为在向PSISection写入数据前已经skipBits了。后面append会把后续PSI的数据都组合在一起,根据不同的PID做不同的解析,下面给出PID的意义:
PID值 | TS包中负载内容 |
0x0000 | PAT |
当PID为0,说明当前PSI中包含有有个PAT,PAT节目关联表到底是什么,后面解析parseProgramAssociationTable时就知道了。
如果PID不为0,说明当前PSI中包含有个PMT,PMT节目映射表到底是什么,后面解析Program.parsePSISection时就知道了。
(2)如果PID没有被记录到PSISection中,说明当前包是一个PES包,会调用Program.parsePID解析出包含的数据
这里再记录两个概念
ES:Elementary Streams 原始流,也可以理解为编码后的数据流
PES:Packetized Elementary Streams 分组流,可以理解为将ES流分段打包之后的数据包,由包头和负载组成
2、PAT
下面就先看parseProgramAssociationTable是如何解析PAT表的:
void ATSParser::parseProgramAssociationTable(ABitReader *br) { // 1、表id 8 bit unsigned table_id = br->getBits(8); if (table_id != 0x00u) { ALOGE("PAT data error!"); return ; } // 2、分段句法指示符 1 bit unsigned section_syntax_indictor = br->getBits(1); // '0' 1 bit br->skipBits(1); // '0' // 保留 2 bit MY_LOGV(" reserved = %u", br->getBits(2)); // 3、后续数据长度 12 bit unsigned section_length = br->getBits(12); // 4、传输流id 16 bit MY_LOGV(" transport_stream_id = %u", br->getBits(16)); // 保留 2 bit MY_LOGV(" reserved = %u", br->getBits(2)); // 版本信息 5 bit MY_LOGV(" version_number = %u", br->getBits(5)); // 5、当前、下个PAT指示符 MY_LOGV(" current_next_indicator = %u", br->getBits(1)); // 6、分段号 MY_LOGV(" section_number = %u", br->getBits(8)); // 7、最后一个分段号 MY_LOGV(" last_section_number = %u", br->getBits(8)); // 计算剩余长度 size_t numProgramBytes = (section_length - 5 /* header */ - 4 /* crc */); // 8、循环解析节目号 for (size_t i = 0; i < numProgramBytes / 4; ++i) { // 9、获取节目号 unsigned program_number = br->getBits(16); MY_LOGV(" reserved = %u", br->getBits(3)); if (program_number == 0) { // 10、节目号为0,则后面跟着的是network_PID MY_LOGV(" network_PID = 0x%04x", br->getBits(13)); } else { // 11、节目号不为0,则后面跟着的是节目映射表PMT的id unsigned programMapPID = br->getBits(13); bool found = false; // 12、循环查找PMT_id是否已经被保存 for (size_t index = 0; index < mPrograms.size(); ++index) { const sp<Program> &program = mPrograms.itemAt(index); if (program->number() == program_number) { // 13、如果找到则更新信息 program->updateProgramMapPID(programMapPID); found = true; break; } } // 14、如果没找到则以节目号、PMT_id创建一个Program if (!found) { mPrograms.push( new Program(this, program_number, programMapPID, mLastRecoveredPTS)); if (mSampleAesKeyItem != NULL) { mPrograms.top()->signalNewSampleAesKey(mSampleAesKeyItem); } } // 15、使用PMT_id创建一个PSISection添加到列表当中 if (mPSISections.indexOfKey(programMapPID) < 0) { mPSISections.add(programMapPID, new PSISection); } } } MY_LOGV(" CRC = 0x%08x", br->getBits(32)); }
重要步骤:
3、这里的section_length表示后续的数据长度,单位是byte,后续的5个字节都是表头(暂时无用)
8、循环解析节目号时,看到循环次数是剩余有效长度/4,因为每个节目表信息都是4byte
11、获取到节目号,节目号为0,说明后面跟着的是network_pid(作用未知);如果节目号不为零,说明后面跟着的是programMapPID
12、检查programMapPID是否被记录,如果被记录则更新Program中的id信息;如果没有记录则用节目号和programMapPID创建一个Program
15、使用programMapPID创建一个PSISection添加到列表中(从这里我们可以知道有些带有PSI信息的包的PID 其实就是节目的 programMapPID)
到这儿,大概就可以知道所谓PAT节目关联表,就是保存着program_number 和 programMapPID的表,这些表有什么用,下面就会知道了。
3、PMT
如果说带有PSI信息的包的PID不为0,说明PSI信息都是些节目信息,所以会调用Program.parsePSISection:
bool ATSParser::Program::parsePSISection( unsigned pid, ABitReader *br, status_t *err) { *err = OK; if (pid != mProgramMapPID) { return false; } *err = parseProgramMap(br); return true; }
由于可能会有多个programMapPID,所以解析之前会先找到对应id的Program,然后调用parseProgramMap做真正的解析,这个方法是在是太长了。
status_t ATSParser::Program::parseProgramMap(ABitReader *br) { // 1、表id 8 bit unsigned table_id = br->getBits(8); if (table_id != 0x02u) { ALOGE("PMT data error!"); return ERROR_MALFORMED; } // 2、分段法指示符 1 bit unsigned section_syntax_indicator = br->getBits(1); if (section_syntax_indicator != 1u) { ALOGE("PMT data error!"); return ERROR_MALFORMED; } // 0 1 bit br->skipBits(1); // '0' // 保留 2 bit MY_LOGV(" reserved = %u", br->getBits(2)); // 分段长度 12 bit unsigned section_length = br->getBits(12); // 3、节目号 16 bit MY_LOGV(" program_number = %u", br->getBits(16)); // 保留 2 bit MY_LOGV(" reserved = %u", br->getBits(2)); bool audioPresentationsChanged = false; // 版本号 5 bit unsigned pmtVersion = br->getBits(5); if (pmtVersion != mPMTVersion) { audioPresentationsChanged = true; mPMTVersion = pmtVersion; } // 当前下一个指示符 1 bit MY_LOGV(" current_next_indicator = %u", br->getBits(1)); // 分段序列号 8 bit MY_LOGV(" section_number = %u", br->getBits(8)); // 最后一段编号 8 bit MY_LOGV(" last_section_number = %u", br->getBits(8)); // 保留 3 bit MY_LOGV(" reserved = %u", br->getBits(3)); // PCR_PIC 13 bit unsigned PCR_PID = br->getBits(13); // 保留 4 bit MY_LOGV(" reserved = %u", br->getBits(4)); // 4、CADescriptor信息长度 12 bit unsigned program_info_length = br->getBits(12); // descriptors CADescriptor programCA; // 5、这里比较奇怪并没有找到相关资料 bool hasProgramCA = findCADescriptor(br, program_info_length, &programCA); if (hasProgramCA && !mParser->mCasManager->addProgram( mProgramNumber, programCA)) { return ERROR_MALFORMED; } Vector<StreamInfo> infos; // 计算剩余数据长度 int32_t infoBytesRemaining = section_length - 9 - program_info_length - 4; // 剩余数据长度必须大于5,因为每个信息块的长度至少是5个字节 while (infoBytesRemaining >= 5) { StreamInfo info; // 6、流类型 8 bit info.mType = br->getBits(8); // 保留 3 bit MY_LOGV(" reserved = %u", br->getBits(3)); // 7、流id 13 bit info.mPID = br->getBits(13); // 保留 MY_LOGV(" reserved = %u", br->getBits(4)); // ES信息长度 12 bit unsigned ES_info_length = br->getBits(12); infoBytesRemaining -= 5 + ES_info_length; // 如果ES_info_length,需要减去改长度 CADescriptor streamCA; // 8、扩展类型 info.mTypeExt = EXT_DESCRIPTOR_DVB_RESERVED_MAX; info.mAudioPresentations.clear(); bool hasStreamCA = false; // 确认ES信息长度大于2byte,以及剩余长度大于0(说明有足够空间存储ES信息) while (ES_info_length > 2 && infoBytesRemaining >= 0) { // 9、描述标签 8 bit unsigned descriptor_tag = br->getBits(8); // 10、描述信息长度 8 bit unsigned descriptor_length = br->getBits(8); ES_info_length -= 2; if (descriptor_length > ES_info_length) { return ERROR_MALFORMED; } // 11、接下就不太清楚在做什么了 if (descriptor_tag == DESCRIPTOR_CA && descriptor_length >= 4) { hasStreamCA = true; streamCA.mSystemID = br->getBits(16); streamCA.mPID = br->getBits(16) & 0x1fff; ES_info_length -= descriptor_length; descriptor_length -= 4; streamCA.mPrivateData.assign(br->data(), br->data() + descriptor_length); br->skipBits(descriptor_length * 8); } else if (info.mType == STREAMTYPE_PES_PRIVATE_DATA && descriptor_tag == DESCRIPTOR_DVB_EXTENSION && descriptor_length >= 1) { unsigned descTagExt = br->getBits(8); ALOGV(" tag_ext = 0x%02x", descTagExt); ES_info_length -= descriptor_length; descriptor_length--; // The AC4 descriptor is used in the PSI PMT to identify streams which carry AC4 // audio. if (descTagExt == EXT_DESCRIPTOR_DVB_AC4) { info.mTypeExt = EXT_DESCRIPTOR_DVB_AC4; br->skipBits(descriptor_length * 8); } else if (descTagExt == EXT_DESCRIPTOR_DVB_AUDIO_PRESELECTION && descriptor_length >= 1) { // DVB BlueBook A038 Table 110 unsigned num_preselections = br->getBits(5); br->skipBits(3); // reserved for (unsigned i = 0; i < num_preselections; ++i) { if (br->numBitsLeft() < 16) { ALOGE("Not enough data left in bitreader!"); return ERROR_MALFORMED; } AudioPresentationV1 ap; ap.mPresentationId = br->getBits(5); // preselection_id // audio_rendering_indication ap.mMasteringIndication = static_cast<MasteringIndication>(br->getBits(3)); ap.mAudioDescriptionAvailable = (br->getBits(1) == 1); ap.mSpokenSubtitlesAvailable = (br->getBits(1) == 1); ap.mDialogueEnhancementAvailable = (br->getBits(1) == 1); bool interactivity_enabled = (br->getBits(1) == 1); MY_LOGV(" interactivity_enabled = %d", interactivity_enabled); bool language_code_present = (br->getBits(1) == 1); bool text_label_present = (br->getBits(1) == 1); bool multi_stream_info_present = (br->getBits(1) == 1); bool future_extension = (br->getBits(1) == 1); if (language_code_present) { if (br->numBitsLeft() < 24) { ALOGE("Not enough data left in bitreader!"); return ERROR_MALFORMED; } char language[4]; language[0] = br->getBits(8); language[1] = br->getBits(8); language[2] = br->getBits(8); language[3] = 0; ap.mLanguage = String8(language); } // This maps the presentation id to the message id in the // EXT_DESCRIPTOR_DVB_MESSAGE so that we can get the presentation label. if (text_label_present) { if (br->numBitsLeft() < 8) { ALOGE("Not enough data left in bitreader!"); return ERROR_MALFORMED; } unsigned message_id = br->getBits(8); MY_LOGV(" message_id = %u", message_id); } if (multi_stream_info_present) { if (br->numBitsLeft() < 8) { ALOGE("Not enough data left in bitreader!"); return ERROR_MALFORMED; } unsigned num_aux_components = br->getBits(3); br->skipBits(5); // reserved if (br->numBitsLeft() < (num_aux_components * 8)) { ALOGE("Not enough data left in bitreader!"); return ERROR_MALFORMED; } br->skipBits(num_aux_components * 8); // component_tag } if (future_extension) { if (br->numBitsLeft() < 8) { return ERROR_MALFORMED; } br->skipBits(3); // reserved unsigned future_extension_length = br->getBits(5); if (br->numBitsLeft() < (future_extension_length * 8)) { ALOGE("Not enough data left in bitreader!"); return ERROR_MALFORMED; } br->skipBits(future_extension_length * 8); // future_extension_byte } info.mAudioPresentations.push_back(std::move(ap)); } } else { br->skipBits(descriptor_length * 8); } } else { ES_info_length -= descriptor_length; br->skipBits(descriptor_length * 8); } } if (hasStreamCA && !mParser->mCasManager->addStream( mProgramNumber, info.mPID, streamCA)) { return ERROR_MALFORMED; } if (hasProgramCA) { info.mCADescriptor = programCA; } else if (hasStreamCA) { info.mCADescriptor = streamCA; } // 将所有的info加入到列表中 infos.push(info); } if (infoBytesRemaining != 0) { ALOGW("Section data remains unconsumed"); } unsigned crc = br->getBits(32); if (crc != mPMT_CRC) { audioPresentationsChanged = true; mPMT_CRC = crc; } bool PIDsChanged = false; // 12、检查对应ID流的类型是否有改变 for (size_t i = 0; i < infos.size(); ++i) { StreamInfo &info = infos.editItemAt(i); ssize_t index = mStreams.indexOfKey(info.mPID); if (index >= 0 && mStreams.editValueAt(index)->type() != info.mType) { ALOGI("uh oh. stream PIDs have changed."); PIDsChanged = true; break; } } if (PIDsChanged) { // we can recover if number of streams for each type remain the same bool success = switchPIDs(infos); if (!success) { ALOGI("Stream PIDs changed and we cannot recover."); return ERROR_MALFORMED; } } bool isAddingScrambledStream = false; // 13、检查流是否被创建 for (size_t i = 0; i < infos.size(); ++i) { StreamInfo &info = infos.editItemAt(i); if (mParser->mCasManager->isCAPid(info.mPID)) { // skip CA streams (EMM/ECM) continue; } ssize_t index = mStreams.indexOfKey(info.mPID); if (index < 0) { // 14、使用PCR_PID 和 StreamInfo创建一个新流 sp<Stream> stream = new Stream(this, PCR_PID, info); if (mSampleAesKeyItem != NULL) { stream->signalNewSampleAesKey(mSampleAesKeyItem); } isAddingScrambledStream |= info.mCADescriptor.mSystemID >= 0; // 15、将新流以流id为key加入到列表 mStreams.add(info.mPID, stream); } else if (index >= 0 && mStreams.editValueAt(index)->isAudio() && audioPresentationsChanged) { mStreams.editValueAt(index)->setAudioPresentations(info.mAudioPresentations); } } if (isAddingScrambledStream) { return ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED; } return OK; }
看几个步骤:
5、CADescrpitor暂时还不明白是做什么用的
6、从这一步开始可以提取出有用的信息,打包成是StreamInfo,这一步可以解析出流的类型
7、解析出流的ID
8、mTypeExt 意思好像是扩展类型,比如音频的dobly等
9、descriptor_tag意思可能是扩展类型的标签,根据该标签来确定mTypeExt
12、检查已经保存的流的类型 和 新解析出来的对应id的流的类型是否一致
13、检查对应ID的流有没有被创建,如果没有则以Program、PCR_PID、StreamInfo创建一个Stream,并添加到mStream列表中管理
可以看到上面给的PMT信息表 和 代码解析上有很大的差距,一个是在CADescriptor处的解析上有不同,另一个是在mTypeExt的解析上有不同。
到这儿就知道了PMT节目映射表中保存的是流id以及流的信息比如streamType,以及对应的扩展类型,用解析出的StreamInfo就可以创建出对应的Stream了
4、PES
前面解析的包都是包含PSI信息的包,PID为0时解析的是PAT表,PID为programMapID时解析出的是PMT表,剩下一种PID为StreamID的情况还没看,接下来就来看看。
bool handled = false; for (size_t i = 0; i < mPrograms.size(); ++i) { status_t err; if (mPrograms.editItemAt(i)->parsePID( PID, continuity_counter, payload_unit_start_indicator, transport_scrambling_control, random_access_indicator, br, &err, event)) { if (err != OK) { return err; } handled = true; break; } } bool ATSParser::Program::parsePID( unsigned pid, unsigned continuity_counter, unsigned payload_unit_start_indicator, unsigned transport_scrambling_control, unsigned random_access_indicator, ABitReader *br, status_t *err, SyncEvent *event) { *err = OK; ssize_t index = mStreams.indexOfKey(pid); if (index < 0) { return false; } *err = mStreams.editValueAt(index)->parse( continuity_counter, payload_unit_start_indicator, transport_scrambling_control, random_access_indicator, br, event); return true; }
PID为StreamID时会遍历所有的Program调用其parsePID方法,本质上时查找Program中所有的Stream的id,如果匹配成功则调用对应Stream的parse方法,做真正的解析PES的动作。
status_t ATSParser::Stream::parse( unsigned continuity_counter, unsigned payload_unit_start_indicator, unsigned transport_scrambling_control, unsigned random_access_indicator, ABitReader *br, SyncEvent *event) { if (mQueue == NULL) { return OK; } // 1、检查包中的连续计数 和 预期的连续计数是否相同 if (mExpectedContinuityCounter >= 0 && (unsigned)mExpectedContinuityCounter != continuity_counter) { ALOGI("discontinuity on stream pid 0x%04x", mElementaryPID); mPayloadStarted = false; mPesStartOffsets.clear(); mBuffer->setRange(0, 0); mSubSamples.clear(); mExpectedContinuityCounter = -1; // 负载开始指示符为0说明包中无PES数据 if (!payload_unit_start_indicator) { return OK; } } // 预期计数加1 mExpectedContinuityCounter = (continuity_counter + 1) & 0x0f; // 负载开始指示符为1说明有数据,开始解析数据 if (payload_unit_start_indicator) { off64_t offset = (event != NULL) ? event->getOffset() : 0; // 第一笔数据到来时不会进入到这个判断当中 if (mPayloadStarted) { // 2、处理数据 status_t err = flush(event); if (err != OK) { ALOGW("Error (%08x) happened while flushing; we simply discard " "the PES packet and continue.", err); } } // 第一笔数据到来时会将负载开始置true mPayloadStarted = true; // There should be at most 2 elements in |mPesStartOffsets|. while (mPesStartOffsets.size() >= 2) { mPesStartOffsets.erase(mPesStartOffsets.begin()); } mPesStartOffsets.push_back(offset); } if (!mPayloadStarted) { return OK; } size_t payloadSizeBits = br->numBitsLeft(); if (payloadSizeBits % 8 != 0u) { ALOGE("Wrong value"); return BAD_VALUE; } size_t neededSize = mBuffer->size() + payloadSizeBits / 8; if (!ensureBufferCapacity(neededSize)) { return NO_MEMORY; } // 3、拷贝包中的数据到mBuffer中 memcpy(mBuffer->data() + mBuffer->size(), br->data(), payloadSizeBits / 8); // 设置mBuffer的读写范围 mBuffer->setRange(0, mBuffer->size() + payloadSizeBits / 8); if (mScrambled) { mSubSamples.push_back({payloadSizeBits / 8, transport_scrambling_control, random_access_indicator}); } return OK; }
这里还要先看下 payload_unit_start_indicator 负载开始指示符,之前在PSI包中碰到过,现在PES包中也有这个指示符,当然指代的意思肯定是不一样的。PES包中负载开始指示符为1说明后面的有效负载将从第一个字节开始,为0就没有负载数据。continuity_counter应该就是包中的计数器。接下来看看几个步骤:
1、如果预期计数和实际计数不相同则说明当前数据包不连续
2、第一笔数据到来时并不会进入到flush中去处理数据,因为此时mBuffer中并没有数据,flush方法处理的是上一笔的数据
3、将新送来的包中的PES数据拷贝到mBuffer当中,等待下次调用flush处理
看到这里就知道了,PES数据的解析还要调用flush方法:
status_t ATSParser::Stream::flush(SyncEvent *event) { if (mBuffer == NULL || mBuffer->size() == 0) { return OK; } ALOGV("flushing stream 0x%04x size = %zu", mElementaryPID, mBuffer->size()); status_t err = OK; if (mScrambled) { err = flushScrambled(event); mSubSamples.clear(); } else { ABitReader br(mBuffer->data(), mBuffer->size()); err = parsePES(&br, event); } mBuffer->setRange(0, 0); return err; }
flush方法很简单,如果是普通数据就调用parsePES,如果是加扰数据就调用flushScrambled方法。我们这里只看parsePES:
先看下PES包的结构:
status_t ATSParser::Stream::parsePES(ABitReader *br, SyncEvent *event) { const uint8_t *basePtr = br->data(); // 1、PES开始码 24 bit,固定为1 unsigned packet_startcode_prefix = br->getBits(24); if (packet_startcode_prefix != 1) { ALOGV("Supposedly payload_unit_start=1 unit does not start " "with startcode."); return ERROR_MALFORMED; } // 2、流ID 8 bit unsigned stream_id = br->getBits(8); // 3、PES包长度 16 bit unsigned PES_packet_length = br->getBits(16); // 从这里大概可以知道stream_id有一些固定的取值 if (stream_id != 0xbc // program_stream_map && stream_id != 0xbe // padding_stream && stream_id != 0xbf // private_stream_2 && stream_id != 0xf0 // ECM && stream_id != 0xf1 // EMM && stream_id != 0xff // program_stream_directory && stream_id != 0xf2 // DSMCC && stream_id != 0xf8) { // H.222.1 type E // 4、'10' 2 bit if (br->getBits(2) != 2u) { return ERROR_MALFORMED; } // 5、PES加扰控制 2 bit unsigned PES_scrambling_control = br->getBits(2); // 6、PES优先级 1 bit MY_LOGV("PES_priority = %u", br->getBits(1)); // 7、数据对齐指示符 1 bit MY_LOGV("data_alignment_indicator = %u", br->getBits(1)); // 8、版权 1 bit MY_LOGV("copyright = %u", br->getBits(1)); // 9、原始或拷贝 1 bit MY_LOGV("original_or_copy = %u", br->getBits(1)); // 10、PTS和DTS标志位 2 bit unsigned PTS_DTS_flags = br->getBits(2); // 11、ESCR标志位 1 bit unsigned ESCR_flag = br->getBits(1); // 12、 unsigned ES_rate_flag = br->getBits(1); // 13、 unsigned DSM_trick_mode_flag = br->getBits(1); ALOGV("DSM_trick_mode_flag = %u", DSM_trick_mode_flag); // 14、 unsigned additional_copy_info_flag = br->getBits(1); ALOGV("additional_copy_info_flag = %u", additional_copy_info_flag); // 15、PES CRC校验 MY_LOGV("PES_CRC_flag = %u", br->getBits(1)); MY_LOGV("PES_extension_flag = %u", br->getBits(1)); // 16、PES数据长度 unsigned PES_header_data_length = br->getBits(8); ALOGV("PES_header_data_length = %u", PES_header_data_length); unsigned optional_bytes_remaining = PES_header_data_length; uint64_t PTS = 0, DTS = 0; // 17 、解析PTS/DTS 5 bytes if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) { // 数据至少5byte if (optional_bytes_remaining < 5u) { return ERROR_MALFORMED; } if (br->getBits(4) != PTS_DTS_flags) { return ERROR_MALFORMED; } PTS = ((uint64_t)br->getBits(3)) << 30; if (br->getBits(1) != 1u) { return ERROR_MALFORMED; } PTS |= ((uint64_t)br->getBits(15)) << 15; if (br->getBits(1) != 1u) { return ERROR_MALFORMED; } PTS |= br->getBits(15); if (br->getBits(1) != 1u) { return ERROR_MALFORMED; } ALOGV("PTS = 0x%016" PRIx64 " (%.2f)", PTS, PTS / 90000.0); optional_bytes_remaining -= 5; if (PTS_DTS_flags == 3) { if (optional_bytes_remaining < 5u) { return ERROR_MALFORMED; } if (br->getBits(4) != 1u) { return ERROR_MALFORMED; } DTS = ((uint64_t)br->getBits(3)) << 30; if (br->getBits(1) != 1u) { return ERROR_MALFORMED; } DTS |= ((uint64_t)br->getBits(15)) << 15; if (br->getBits(1) != 1u) { return ERROR_MALFORMED; } DTS |= br->getBits(15); if (br->getBits(1) != 1u) { return ERROR_MALFORMED; } ALOGV("DTS = %" PRIu64, DTS); optional_bytes_remaining -= 5; } } // 18、解析ESCR 6bytes if (ESCR_flag) { if (optional_bytes_remaining < 6u) { return ERROR_MALFORMED; } br->getBits(2); uint64_t ESCR = ((uint64_t)br->getBits(3)) << 30; if (br->getBits(1) != 1u) { return ERROR_MALFORMED; } ESCR |= ((uint64_t)br->getBits(15)) << 15; if (br->getBits(1) != 1u) { return ERROR_MALFORMED; } ESCR |= br->getBits(15); if (br->getBits(1) != 1u) { return ERROR_MALFORMED; } ALOGV("ESCR = %" PRIu64, ESCR); MY_LOGV("ESCR_extension = %u", br->getBits(9)); if (br->getBits(1) != 1u) { return ERROR_MALFORMED; } optional_bytes_remaining -= 6; } // 19、解析ES rate,3bytes if (ES_rate_flag) { if (optional_bytes_remaining < 3u) { return ERROR_MALFORMED; } if (br->getBits(1) != 1u) { return ERROR_MALFORMED; } MY_LOGV("ES_rate = %u", br->getBits(22)); if (br->getBits(1) != 1u) { return ERROR_MALFORMED; } optional_bytes_remaining -= 3; } br->skipBits(optional_bytes_remaining * 8); // 20、到这里信息全部解析结束,下面就是ES数据了 // ES data follows. int32_t pesOffset = br->data() - basePtr; if (PES_packet_length != 0) { // 21、校验数据长度 if (PES_packet_length < PES_header_data_length + 3) { return ERROR_MALFORMED; } unsigned dataLength = PES_packet_length - 3 - PES_header_data_length; if (br->numBitsLeft() < dataLength * 8) { ALOGE("PES packet does not carry enough data to contain " "payload. (numBitsLeft = %zu, required = %u)", br->numBitsLeft(), dataLength * 8); return ERROR_MALFORMED; } ALOGV("There's %u bytes of payload, PES_packet_length=%u, offset=%d", dataLength, PES_packet_length, pesOffset); // 22、调用onPayloadData处理负载ES数据 onPayloadData( PTS_DTS_flags, PTS, DTS, PES_scrambling_control, br->data(), dataLength, pesOffset, event); br->skipBits(dataLength * 8); } else { onPayloadData( PTS_DTS_flags, PTS, DTS, PES_scrambling_control, br->data(), br->numBitsLeft() / 8, pesOffset, event); size_t payloadSizeBits = br->numBitsLeft(); if (payloadSizeBits % 8 != 0u) { return ERROR_MALFORMED; } ALOGV("There's %zu bytes of payload, offset=%d", payloadSizeBits / 8, pesOffset); } } else if (stream_id == 0xbe) { // padding_stream if (PES_packet_length == 0u) { return ERROR_MALFORMED; } br->skipBits(PES_packet_length * 8); } else { if (PES_packet_length == 0u) { return ERROR_MALFORMED; } br->skipBits(PES_packet_length * 8); } return OK; }
这里来看看parsePES中的一些步骤:
2、解析出stream_id,这个stream_id并不是用于创建流的id
3、PES_packet_length 解析出的是PES包后续的总长度
5~15、解析PES包头中的flags
16、PES_header_data_length 解析出的是PES头数据长度,这个头可能包含有四部分PTS/DTS、ESCR、ES_rate、其他,其他包含的是其余flag对应的数据,由于没有用到所以统一划分到其他分类
17~19、分别为PTS/DTS、ESCR、ES_rate的解析过程
21、校验数据长度,PES包的长度应该大于等于标志位的长度 + PES头数据的长度,ES数据等于PES包长度 - 标志位长度(3bytes) - PES头数据长度(标志位对应的数据的长度)
22、剩余的数据就是ES数据了,调用onPayloadData来处理数据
接下来就看看onPayloadData是怎么处理ES数据的吧:
void ATSParser::Stream::onPayloadData( unsigned PTS_DTS_flags, uint64_t PTS, uint64_t /* DTS */, unsigned PES_scrambling_control, const uint8_t *data, size_t size, int32_t payloadOffset, SyncEvent *event) { ALOGV("onPayloadData mStreamType=0x%02x size: %zu", mStreamType, size); int64_t timeUs = 0LL; // no presentation timestamp available. if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) { // 1、将PTS转化为时间戳 timeUs = mProgram->convertPTSToTimestamp(PTS); } // 2、将数据追加到ESQueue当中 status_t err = mQueue->appendData( data, size, timeUs, payloadOffset, PES_scrambling_control); if (mEOSReached) { mQueue->signalEOS(); } if (err != OK) { return; } sp<ABuffer> accessUnit; bool found = false; // 3、从ESQueue中出取出一个buffer while ((accessUnit = mQueue->dequeueAccessUnit()) != NULL) { // 4、如果AnotherPacketSource为空则创建一个,并把format传给它 if (mSource == NULL) { sp<MetaData> meta = mQueue->getFormat(); if (meta != NULL) { ALOGV("Stream PID 0x%08x of type 0x%02x now has data.", mElementaryPID, mStreamType); const char *mime; if (meta->findCString(kKeyMIMEType, &mime) && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { int32_t sync = 0; if (!accessUnit->meta()->findInt32("isSync", &sync) || !sync) { continue; } } mSource = new AnotherPacketSource(meta); if (mAudioPresentations.size() > 0) { addAudioPresentations(accessUnit); } // 5、将取出的buffer保存到AnotherPacketSource中 mSource->queueAccessUnit(accessUnit); ALOGV("onPayloadData: created AnotherPacketSource PID 0x%08x of type 0x%02x", mElementaryPID, mStreamType); } } else if (mQueue->getFormat() != NULL) { // After a discontinuity we invalidate the queue's format // and won't enqueue any access units to the source until // the queue has reestablished the new format. if (mSource->getFormat() == NULL) { mSource->setFormat(mQueue->getFormat()); } if (mAudioPresentations.size() > 0) { addAudioPresentations(accessUnit); } mSource->queueAccessUnit(accessUnit); } // Every access unit has a pesStartOffset queued in |mPesStartOffsets|. // 6、从Offset队列中取出一个值 off64_t pesStartOffset = -1; if (!mPesStartOffsets.empty()) { pesStartOffset = *mPesStartOffsets.begin(); mPesStartOffsets.erase(mPesStartOffsets.begin()); } if (pesStartOffset >= 0 && (event != NULL) && !found && mQueue->getFormat() != NULL) { int32_t sync = 0; if (accessUnit->meta()->findInt32("isSync", &sync) && sync) { int64_t timeUs; if (accessUnit->meta()->findInt64("timeUs", &timeUs)) { found = true; // 7、初始化syncPoint event->init(pesStartOffset, mSource, timeUs, getSourceType()); } } } } }
这个方法的关键步骤不是很多:
2、将ES数据全部写入到ESQueue中,ESQueue是什么?下面会简单了解以下
3、ES数据经过ESQueue处理之后会再被取出来,用AnotherPacketSouce来保存;如果Source还没有创建,则创建一个并且用ESQueue中保存的Format初始化它
6、在ATSParser::Stream::parse中预先将文件偏移量offset保存在mPesStartOffsets当中,这里取出来,协同时间戳一起初始化一个SyncPoint,以后就可以通过时间戳找到对应的文件偏移量了。这里我还有点个人理解:不是每个ts packet都有PTS信息的,所以并不是每次都会去创建SyncPoint,只有对应偏移量的ts packet中有PTS信息时才可以用于seek,第7步上面的两个if判断大概就是做此工作。
下面看看ESQueue是做什么的:
ESQueue的创建最早出现在Stream的构造函数ATSParser::Stream::Stream中。
ATSParser::Stream::Stream( Program *program, unsigned PCR_PID, const StreamInfo &info) : mProgram(program), mElementaryPID(info.mPID), mStreamType(info.mType), // 流类型 mStreamTypeExt(info.mTypeExt), // 流额外类型 mPCR_PID(PCR_PID), mExpectedContinuityCounter(-1), mPayloadStarted(false), mEOSReached(false), mPrevPTS(0), mQueue(NULL), mScrambled(info.mCADescriptor.mSystemID >= 0), mAudioPresentations(info.mAudioPresentations) { mSampleEncrypted = mStreamType == STREAMTYPE_H264_ENCRYPTED || mStreamType == STREAMTYPE_AAC_ENCRYPTED || mStreamType == STREAMTYPE_AC3_ENCRYPTED; ALOGV("new stream PID 0x%02x, type 0x%02x, scrambled %d, SampleEncrypted: %d", info.mPID, info.mType, mScrambled, mSampleEncrypted); uint32_t flags = 0; // 1、生成flags if (((isVideo() || isAudio()) && mScrambled)) { flags = ElementaryStreamQueue::kFlag_ScrambledData; } else if (mSampleEncrypted) { flags = ElementaryStreamQueue::kFlag_SampleEncryptedData; } // 2、初始化mode ElementaryStreamQueue::Mode mode = ElementaryStreamQueue::INVALID; // 3、根据流类型来生成对应的flags和mode switch (mStreamType) { case STREAMTYPE_H264: case STREAMTYPE_H264_ENCRYPTED: mode = ElementaryStreamQueue::H264; flags |= (mProgram->parserFlags() & ALIGNED_VIDEO_DATA) ? ElementaryStreamQueue::kFlag_AlignedData : 0; break; case STREAMTYPE_MPEG2_AUDIO_ADTS: case STREAMTYPE_AAC_ENCRYPTED: mode = ElementaryStreamQueue::AAC; break; case STREAMTYPE_MPEG1_AUDIO: case STREAMTYPE_MPEG2_AUDIO: mode = ElementaryStreamQueue::MPEG_AUDIO; break; case STREAMTYPE_MPEG1_VIDEO: case STREAMTYPE_MPEG2_VIDEO: mode = ElementaryStreamQueue::MPEG_VIDEO; break; case STREAMTYPE_MPEG4_VIDEO: mode = ElementaryStreamQueue::MPEG4_VIDEO; break; case STREAMTYPE_LPCM_AC3: case STREAMTYPE_AC3: case STREAMTYPE_AC3_ENCRYPTED: mode = ElementaryStreamQueue::AC3; break; case STREAMTYPE_EAC3: mode = ElementaryStreamQueue::EAC3; break; case STREAMTYPE_PES_PRIVATE_DATA: if (mStreamTypeExt == EXT_DESCRIPTOR_DVB_AC4) { mode = ElementaryStreamQueue::AC4; } break; case STREAMTYPE_METADATA: mode = ElementaryStreamQueue::METADATA; break; default: ALOGE("stream PID 0x%02x has invalid stream type 0x%02x", info.mPID, info.mType); return; } // 4、利用flags和mode创建ESQueue mQueue = new ElementaryStreamQueue(mode, flags); if (mQueue != NULL) { if (mSampleAesKeyItem != NULL) { mQueue->signalNewSampleAesKey(mSampleAesKeyItem); } ensureBufferCapacity(kInitialStreamBufferSize); // 5、如果是加扰数据则预先初始化format if (mScrambled && (isAudio() || isVideo())) { // Set initial format to scrambled sp<MetaData> meta = new MetaData(); meta->setCString(kKeyMIMEType, isAudio() ? MEDIA_MIMETYPE_AUDIO_SCRAMBLED : MEDIA_MIMETYPE_VIDEO_SCRAMBLED); // for MediaExtractor.CasInfo const CADescriptor &descriptor = info.mCADescriptor; meta->setInt32(kKeyCASystemID, descriptor.mSystemID); meta->setData(kKeyCAPrivateData, 0, descriptor.mPrivateData.data(), descriptor.mPrivateData.size()); mSource = new AnotherPacketSource(meta); } } }
这里比较重要的是mStreamType 和 mStreamTypeExt,他们会影响isAudio和isVideo的返回结果以及 flags 和mode,最终创建的ESQueue也会有所不同。
appendData这里就不作展开了,主要是对编码格式不太了解,appendData大概是在对传入的ES数据做格式加工,保存到mData中;dequeueAcessUnit方法会根据不同StreamType将数据划分为不同的unit返回给Stream,这里暂时不做过多的了解。
到这儿,解析TS包的工作就做完了!!!
了解了如何使用ATSParser去解析TS packet,那么MPEG2TSExtractor中的内容就很容易理解了,暂时就先写道这里。