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(&sectionBits);
        } else {
            bool handled = false;
            for (size_t i = 0; i < mPrograms.size(); ++i) {
                status_t err;
          // abc
                if (!mPrograms.editItemAt(i)->parsePSISection(
                            PID, &sectionBits, &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中的内容就很容易理解了,暂时就先写道这里。

 

posted @ 2022-04-13 17:12  青山渺渺  阅读(569)  评论(0编辑  收藏  举报