Commit message: None
CVE: CVE-2016-2505
CWE: CWE-119
Vulnerability description: mpeg2ts/ATSParser.cpp in libstagefright in mediaserver in Android 6.x before 2016-07-01 does not validate a certain section length, which allows remote attackers to execute arbitrary code or cause a denial of service (memory corruption) via a crafted media file, aka internal bug 28333006.

Project: Android

ATSParser::ATSParser(uint32_t flags)
 : mFlags(flags),
      mAbsoluteTimeAnchorUs(-1ll),
      mTimeOffsetValid(false),
      mTimeOffsetUs(0ll),
      mLastRecoveredPTS(-1ll),
      mNumTSPacketsParsed(0),
      mNumPCRs(0) {
    mPSISections.add(0 /* PID */, new PSISection);
}

Project: Android

ATSParser::PSISection::PSISection() :
    mSkipBytes(0) {
}

Project: Android

 bool PTSTimeDeltaEstablished() const {
 return mFirstPTSValid;
 }

Project: Android

bool ATSParser::PTSTimeDeltaEstablished() {
 if (mPrograms.isEmpty()) {
 return false;
 }

 return mPrograms.editItemAt(0)->PTSTimeDeltaEstablished();
}

Project: Android

ATSParser::Stream::Stream(
 Program *program,
 unsigned elementaryPID,
 unsigned streamType,
 unsigned PCR_PID)
 : mProgram(program),
      mElementaryPID(elementaryPID),
      mStreamType(streamType),
      mPCR_PID(PCR_PID),
      mExpectedContinuityCounter(-1),
      mPayloadStarted(false),
      mEOSReached(false),
      mPrevPTS(0),
      mQueue(NULL) {
 switch (mStreamType) {
 case STREAMTYPE_H264:
            mQueue = new ElementaryStreamQueue(
 ElementaryStreamQueue::H264,
 (mProgram->parserFlags() & ALIGNED_VIDEO_DATA)
 ? ElementaryStreamQueue::kFlag_AlignedData : 0);
 break;
 case STREAMTYPE_MPEG2_AUDIO_ADTS:
            mQueue = new ElementaryStreamQueue(ElementaryStreamQueue::AAC);
 break;
 case STREAMTYPE_MPEG1_AUDIO:
 case STREAMTYPE_MPEG2_AUDIO:
            mQueue = new ElementaryStreamQueue(
 ElementaryStreamQueue::MPEG_AUDIO);
 break;

 case STREAMTYPE_MPEG1_VIDEO:
 case STREAMTYPE_MPEG2_VIDEO:
            mQueue = new ElementaryStreamQueue(
 ElementaryStreamQueue::MPEG_VIDEO);
 break;

 case STREAMTYPE_MPEG4_VIDEO:
            mQueue = new ElementaryStreamQueue(
 ElementaryStreamQueue::MPEG4_VIDEO);
 break;

 case STREAMTYPE_LPCM_AC3:
 case STREAMTYPE_AC3:
            mQueue = new ElementaryStreamQueue(
 ElementaryStreamQueue::AC3);
 break;

 case STREAMTYPE_METADATA:
            mQueue = new ElementaryStreamQueue(
 ElementaryStreamQueue::METADATA);
 break;

 default:
 break;
 }

    ALOGV("new stream PID 0x%02x, type 0x%02x", elementaryPID, streamType);

 if (mQueue != NULL) {
        mBuffer = new ABuffer(192 * 1024);
        mBuffer->setRange(0, 0);
 }
}

Project: Android

ATSParser::SyncEvent::SyncEvent(off64_t offset)
 : mInit(false), mOffset(offset), mTimeUs(0) {}

Project: Android

status_t ATSParser::PSISection::append(const void *data, size_t size) {
 if (mBuffer == NULL || mBuffer->size() + size > mBuffer->capacity()) {
 size_t newCapacity =
 (mBuffer == NULL) ? size : mBuffer->capacity() + size;

        newCapacity = (newCapacity + 1023) & ~1023;

        sp<ABuffer> newBuffer = new ABuffer(newCapacity);

 if (mBuffer != NULL) {
            memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size());
            newBuffer->setRange(0, mBuffer->size());
 } else {
            newBuffer->setRange(0, 0);
 }

        mBuffer = newBuffer;
 }

    memcpy(mBuffer->data() + mBuffer->size(), data, size);
    mBuffer->setRange(0, mBuffer->size() + size);

 return OK;
}

Project: Android

void ATSParser::PSISection::clear() {
 if (mBuffer != NULL) {
        mBuffer->setRange(0, 0);
 }
    mSkipBytes = 0;
}

Project: Android

int64_t ATSParser::Program::convertPTSToTimestamp(uint64_t PTS) {
    PTS = recoverPTS(PTS);

 if (!(mParser->mFlags & TS_TIMESTAMPS_ARE_ABSOLUTE)) {
 if (!mFirstPTSValid) {
            mFirstPTSValid = true;
            mFirstPTS = PTS;
            PTS = 0;
 } else if (PTS < mFirstPTS) {
            PTS = 0;
 } else {
            PTS -= mFirstPTS;
 }
 }

 int64_t timeUs = (PTS * 100) / 9;

 if (mParser->mAbsoluteTimeAnchorUs >= 0ll) {
        timeUs += mParser->mAbsoluteTimeAnchorUs;
 }

 if (mParser->mTimeOffsetValid) {
        timeUs += mParser->mTimeOffsetUs;
 }

 return timeUs;
}

Project: Android

const uint8_t *ATSParser::PSISection::data() const {
 return mBuffer == NULL ? NULL : mBuffer->data();
}

Project: Android

status_t ATSParser::feedTSPacket(const void *data, size_t size,
 SyncEvent *event) {
 if (size != kTSPacketSize) {
        ALOGE("Wrong TS packet size");
 return BAD_VALUE;
 }

 ABitReader br((const uint8_t *)data, kTSPacketSize);
 return parseTS(&br, event);
}

Project: Android

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());

 ABitReader br(mBuffer->data(), mBuffer->size());

 status_t err = parsePES(&br, event);

    mBuffer->setRange(0, 0);

 return err;
}

Project: Android

sp<MediaSource> ATSParser::Program::getSource(SourceType type) {
 size_t index = (type == AUDIO) ? 0 : 0;

 for (size_t i = 0; i < mStreams.size(); ++i) {
        sp<MediaSource> source = mStreams.editValueAt(i)->getSource(type);
 if (source != NULL) {
 if (index == 0) {
 return source;
 }
 --index;
 }
 }

 return NULL;
}

Project: Android

sp<MediaSource> ATSParser::getSource(SourceType type) {
 int which = -1; // any

 for (size_t i = 0; i < mPrograms.size(); ++i) {
 const sp<Program> &program = mPrograms.editItemAt(i);

 if (which >= 0 && (int)program->number() != which) {
 continue;
 }

        sp<MediaSource> source = program->getSource(type);

 if (source != NULL) {
 return source;
 }
 }

 return NULL;
}

Project: Android

bool ATSParser::hasSource(SourceType type) const {
 for (size_t i = 0; i < mPrograms.size(); ++i) {
 const sp<Program> &program = mPrograms.itemAt(i);
 if (program->hasSource(type)) {
 return true;
 }
 }

 return false;
}

Project: Android

void ATSParser::SyncEvent::init(off64_t offset, const sp<MediaSource> &source,
 int64_t timeUs) {
    mInit = true;
    mOffset = offset;
    mMediaSource = source;
    mTimeUs = timeUs;
}

Project: Android

bool ATSParser::PSISection::isComplete() const {
 if (mBuffer == NULL || mBuffer->size() < 3) {
 return false;
 }

 unsigned sectionLength = U16_AT(mBuffer->data() + 1) & 0xfff;
 return mBuffer->size() >= sectionLength + 3;
}

Project: Android

bool ATSParser::PSISection::isEmpty() const {
 return mBuffer == NULL || mBuffer->size() == 0;
}

Project: Android

bool ATSParser::Stream::isVideo() const {
 switch (mStreamType) {
 case STREAMTYPE_H264:
 case STREAMTYPE_MPEG1_VIDEO:
 case STREAMTYPE_MPEG2_VIDEO:
 case STREAMTYPE_MPEG4_VIDEO:
 return true;

 default:
 return false;
 }
}

Project: Android

 unsigned number() const { return mProgramNumber; }

Project: Android

void ATSParser::Stream::onPayloadData(
 unsigned PTS_DTS_flags, uint64_t PTS, uint64_t /* DTS */,
 const uint8_t *data, size_t size, SyncEvent *event) {
#if 0
    ALOGI("payload streamType 0x%02x, PTS = 0x%016llx, dPTS = %lld",
          mStreamType,
          PTS,
 (int64_t)PTS - mPrevPTS);
    mPrevPTS = PTS;
#endif

    ALOGV("onPayloadData mStreamType=0x%02x", mStreamType);

 int64_t timeUs = 0ll; // no presentation timestamp available.
 if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) {
        timeUs = mProgram->convertPTSToTimestamp(PTS);
 }

 status_t err = mQueue->appendData(data, size, timeUs);

 if (mEOSReached) {
        mQueue->signalEOS();
 }

 if (err != OK) {
 return;
 }

    sp<ABuffer> accessUnit;
 bool found = false;
 while ((accessUnit = mQueue->dequeueAccessUnit()) != NULL) {
 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)
 && !IsIDR(accessUnit)) {
 continue;
 }
                mSource = new AnotherPacketSource(meta);
                mSource->queueAccessUnit(accessUnit);
 }
 } else if (mQueue->getFormat() != NULL) {

 if (mSource->getFormat() == NULL) {
                mSource->setFormat(mQueue->getFormat());
 }
            mSource->queueAccessUnit(accessUnit);
 }

 if ((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;
                    event->init(mPesStartOffset, mSource, timeUs);
 }
 }
 }
 }
}

Project: Android

status_t ATSParser::parseAdaptationField(ABitReader *br, unsigned PID) {
 unsigned adaptation_field_length = br->getBits(8);

 if (adaptation_field_length > 0) {
 if (adaptation_field_length * 8 > br->numBitsLeft()) {
            ALOGV("Adaptation field should be included in a single TS packet.");
 return ERROR_MALFORMED;
 }

 unsigned discontinuity_indicator = br->getBits(1);

 if (discontinuity_indicator) {
            ALOGV("PID 0x%04x: discontinuity_indicator = 1 (!!!)", PID);
 }

        br->skipBits(2);
 unsigned PCR_flag = br->getBits(1);

 size_t numBitsRead = 4;

 if (PCR_flag) {
 if (adaptation_field_length * 8 < 52) {
 return ERROR_MALFORMED;
 }
            br->skipBits(4);
 uint64_t PCR_base = br->getBits(32);
            PCR_base = (PCR_base << 1) | br->getBits(1);

            br->skipBits(6);
 unsigned PCR_ext = br->getBits(9);

 size_t byteOffsetFromStartOfTSPacket =
 (188 - br->numBitsLeft() / 8);

 uint64_t PCR = PCR_base * 300 + PCR_ext;

            ALOGV("PID 0x%04x: PCR = 0x%016" PRIx64 " (%.2f)",
                  PID, PCR, PCR / 27E6);

 size_t byteOffsetFromStart =
                mNumTSPacketsParsed * 188 + byteOffsetFromStartOfTSPacket;

 for (size_t i = 0; i < mPrograms.size(); ++i) {
                updatePCR(PID, PCR, byteOffsetFromStart);
 }

            numBitsRead += 52;
 }

        br->skipBits(adaptation_field_length * 8 - numBitsRead);
 }
 return OK;
}

Project: Android

bool ATSParser::Program::parsePID(
 unsigned pid, unsigned continuity_counter,
 unsigned payload_unit_start_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, br, event);

 return true;
}

Project: Android

status_t ATSParser::parsePID(
 ABitReader *br, unsigned PID,
 unsigned continuity_counter,
 unsigned payload_unit_start_indicator,
 SyncEvent *event) {
 ssize_t sectionIndex = mPSISections.indexOfKey(PID);

 if (sectionIndex >= 0) {
        sp<PSISection> section = mPSISections.valueAt(sectionIndex);

 if (payload_unit_start_indicator) {
 if (!section->isEmpty()) {
                ALOGW("parsePID encounters payload_unit_start_indicator when section is not empty");
                section->clear();
 }

 unsigned skip = br->getBits(8);
            section->setSkipBytes(skip + 1); // skip filler bytes + pointer field itself
            br->skipBits(skip * 8);
 }

 if (br->numBitsLeft() % 8 != 0) {
 return ERROR_MALFORMED;
 }
 status_t err = section->append(br->data(), br->numBitsLeft() / 8);

 if (err != OK) {
 return err;
 }

 if (!section->isComplete()) {
 return OK;
 }

 if (!section->isCRCOkay()) {
 return BAD_VALUE;
 }
 ABitReader sectionBits(section->data(), section->size());

 if (PID == 0) {
            parseProgramAssociationTable(&sectionBits);
 } else {
 bool handled = false;
 for (size_t i = 0; i < mPrograms.size(); ++i) {
 status_t err;
 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;
 }

 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,
                    br, &err, event)) {
 if (err != OK) {
 return err;
 }

            handled = true;
 break;
 }
 }

 if (!handled) {
        ALOGV("PID 0x%04x not handled.", PID);
 }

 return OK;
}

Project: Android

bool ATSParser::Program::parsePSISection(
 unsigned pid, ABitReader *br, status_t *err) {
 *err = OK;

 if (pid != mProgramMapPID) {
 return false;
 }

 *err = parseProgramMap(br);

 return true;
}

Project: Android

void ATSParser::parseProgramAssociationTable(ABitReader *br) {
 unsigned table_id = br->getBits(8);
    ALOGV("  table_id = %u", table_id);
 if (table_id != 0x00u) {
        ALOGE("PAT data error!");
 return ;
 }
 unsigned section_syntax_indictor = br->getBits(1);
    ALOGV("  section_syntax_indictor = %u", section_syntax_indictor);

    br->skipBits(1); // '0'
    MY_LOGV("  reserved = %u", br->getBits(2));

 unsigned section_length = br->getBits(12);
    ALOGV("  section_length = %u", section_length);

    MY_LOGV("  transport_stream_id = %u", br->getBits(16));
    MY_LOGV("  reserved = %u", br->getBits(2));
    MY_LOGV("  version_number = %u", br->getBits(5));
    MY_LOGV("  current_next_indicator = %u", br->getBits(1));
    MY_LOGV("  section_number = %u", br->getBits(8));
    MY_LOGV("  last_section_number = %u", br->getBits(8));

 size_t numProgramBytes = (section_length - 5 /* header */ - 4 /* crc */);

 for (size_t i = 0; i < numProgramBytes / 4; ++i) {
 unsigned program_number = br->getBits(16);
        ALOGV("    program_number = %u", program_number);

        MY_LOGV("    reserved = %u", br->getBits(3));

 if (program_number == 0) {
            MY_LOGV("    network_PID = 0x%04x", br->getBits(13));
 } else {
 unsigned programMapPID = br->getBits(13);

            ALOGV("    program_map_PID = 0x%04x", programMapPID);

 bool found = false;
 for (size_t index = 0; index < mPrograms.size(); ++index) {
 const sp<Program> &program = mPrograms.itemAt(index);

 if (program->number() == program_number) {
                    program->updateProgramMapPID(programMapPID);
                    found = true;
 break;
 }
 }

 if (!found) {
                mPrograms.push(
 new Program(this, program_number, programMapPID, mLastRecoveredPTS));
 }

 if (mPSISections.indexOfKey(programMapPID) < 0) {
                mPSISections.add(programMapPID, new PSISection);
 }
 }
 }

    MY_LOGV("  CRC = 0x%08x", br->getBits(32));
}

Project: Android

status_t ATSParser::Program::parseProgramMap(ABitReader *br) {
 unsigned table_id = br->getBits(8);
    ALOGV("  table_id = %u", table_id);
 if (table_id != 0x02u) {
        ALOGE("PMT data error!");
 return ERROR_MALFORMED;
 }
 unsigned section_syntax_indicator = br->getBits(1);
    ALOGV("  section_syntax_indicator = %u", section_syntax_indicator);
 if (section_syntax_indicator != 1u) {
        ALOGE("PMT data error!");
 return ERROR_MALFORMED;
 }

    br->skipBits(1); // '0'
    MY_LOGV("  reserved = %u", br->getBits(2));

 unsigned section_length = br->getBits(12);
    ALOGV("  section_length = %u", section_length);

    MY_LOGV("  program_number = %u", br->getBits(16));
    MY_LOGV("  reserved = %u", br->getBits(2));
    MY_LOGV("  version_number = %u", br->getBits(5));
    MY_LOGV("  current_next_indicator = %u", br->getBits(1));
    MY_LOGV("  section_number = %u", br->getBits(8));
    MY_LOGV("  last_section_number = %u", br->getBits(8));
    MY_LOGV("  reserved = %u", br->getBits(3));

 unsigned PCR_PID = br->getBits(13);
    ALOGV("  PCR_PID = 0x%04x", PCR_PID);

    MY_LOGV("  reserved = %u", br->getBits(4));

 unsigned program_info_length = br->getBits(12);
    ALOGV("  program_info_length = %u", program_info_length);

    br->skipBits(program_info_length * 8); // skip descriptors

 Vector<StreamInfo> infos;

 size_t infoBytesRemaining = section_length - 9 - program_info_length - 4;

 while (infoBytesRemaining >= 5) {

 unsigned streamType = br->getBits(8);
        ALOGV("    stream_type = 0x%02x", streamType);

        MY_LOGV("    reserved = %u", br->getBits(3));

 unsigned elementaryPID = br->getBits(13);
        ALOGV("    elementary_PID = 0x%04x", elementaryPID);

        MY_LOGV("    reserved = %u", br->getBits(4));

 unsigned ES_info_length = br->getBits(12);
        ALOGV("    ES_info_length = %u", ES_info_length);

#if 0
        br->skipBits(ES_info_length * 8); // skip descriptors
#else
 unsigned info_bytes_remaining = ES_info_length;
 while (info_bytes_remaining >= 2) {
            MY_LOGV("      tag = 0x%02x", br->getBits(8));

 unsigned descLength = br->getBits(8);
            ALOGV("      len = %u", descLength);

 if (info_bytes_remaining < descLength) {
 return ERROR_MALFORMED;
 }
            br->skipBits(descLength * 8);

            info_bytes_remaining -= descLength + 2;
 }
#endif

 StreamInfo info;
        info.mType = streamType;
        info.mPID = elementaryPID;
        infos.push(info);

        infoBytesRemaining -= 5 + ES_info_length;
 }

 if (infoBytesRemaining != 0) {
        ALOGW("Section data remains unconsumed");
 }
    MY_LOGV("  CRC = 0x%08x", br->getBits(32));

 bool PIDsChanged = false;
 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) {
#if 0
        ALOGI("before:");
 for (size_t i = 0; i < mStreams.size(); ++i) {
            sp<Stream> stream = mStreams.editValueAt(i);

            ALOGI("PID 0x%08x => type 0x%02x", stream->pid(), stream->type());
 }

        ALOGI("after:");
 for (size_t i = 0; i < infos.size(); ++i) {
 StreamInfo &info = infos.editItemAt(i);

            ALOGI("PID 0x%08x => type 0x%02x", info.mPID, info.mType);
 }
#endif

 bool success = switchPIDs(infos);

 if (!success) {
            ALOGI("Stream PIDs changed and we cannot recover.");
 return ERROR_MALFORMED;
 }
 }

 for (size_t i = 0; i < infos.size(); ++i) {
 StreamInfo &info = infos.editItemAt(i);

 ssize_t index = mStreams.indexOfKey(info.mPID);

 if (index < 0) {
            sp<Stream> stream = new Stream(
 this, info.mPID, info.mType, PCR_PID);

            mStreams.add(info.mPID, stream);
 }
 }

 return OK;
}

Project: Android

status_t ATSParser::parseTS(ABitReader *br, SyncEvent *event) {
    ALOGV("---");

 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;
 }

 if (br->getBits(1)) { // transport_error_indicator
 return OK;
 }

 unsigned payload_unit_start_indicator = br->getBits(1);
    ALOGV("payload_unit_start_indicator = %u", payload_unit_start_indicator);

    MY_LOGV("transport_priority = %u", br->getBits(1));

 unsigned PID = br->getBits(13);
    ALOGV("PID = 0x%04x", PID);

    MY_LOGV("transport_scrambling_control = %u", br->getBits(2));

 unsigned adaptation_field_control = br->getBits(2);
    ALOGV("adaptation_field_control = %u", adaptation_field_control);

 unsigned continuity_counter = br->getBits(4);
    ALOGV("PID = 0x%04x, continuity_counter = %u", PID, continuity_counter);


 status_t err = OK;

 if (adaptation_field_control == 2 || adaptation_field_control == 3) {
        err = parseAdaptationField(br, PID);
 }
 if (err == OK) {
 if (adaptation_field_control == 1 || adaptation_field_control == 3) {
            err = parsePID(br, PID, continuity_counter,
                    payload_unit_start_indicator, event);
 }
 }

 ++mNumTSPacketsParsed;

 return err;
}

Project: Android

 unsigned pid() const { return mElementaryPID; }

Project: Android

 unsigned programMapPID() const {
 return mProgramMapPID;
 }

Project: Android

int64_t ATSParser::Program::recoverPTS(uint64_t PTS_33bit) {
 if (mLastRecoveredPTS < 0ll) {
        mLastRecoveredPTS = static_cast<int64_t>(PTS_33bit);
 } else {
        mLastRecoveredPTS = static_cast<int64_t>(
 ((mLastRecoveredPTS - PTS_33bit + 0x100000000ll)
 & 0xfffffffe00000000ull) | PTS_33bit);
 if (mLastRecoveredPTS < 0ll) {
            ALOGI("Clamping negative recovered PTS (%" PRId64 ") to 0", mLastRecoveredPTS);
            mLastRecoveredPTS = 0ll;
 }
 }

 return mLastRecoveredPTS;
}

Project: Android

void ATSParser::Program::signalDiscontinuity(
 DiscontinuityType type, const sp<AMessage> &extra) {
 int64_t mediaTimeUs;
 if ((type & DISCONTINUITY_TIME)
 && extra != NULL
 && extra->findInt64(
 IStreamListener::kKeyMediaTimeUs, &mediaTimeUs)) {
        mFirstPTSValid = false;
 }

 for (size_t i = 0; i < mStreams.size(); ++i) {
        mStreams.editValueAt(i)->signalDiscontinuity(type, extra);
 }
}

Project: Android

void ATSParser::Stream::signalDiscontinuity(
 DiscontinuityType type, const sp<AMessage> &extra) {
    mExpectedContinuityCounter = -1;

 if (mQueue == NULL) {
 return;
 }

    mPayloadStarted = false;
    mEOSReached = false;
    mBuffer->setRange(0, 0);

 bool clearFormat = false;
 if (isAudio()) {
 if (type & DISCONTINUITY_AUDIO_FORMAT) {
            clearFormat = true;
 }
 } else {
 if (type & DISCONTINUITY_VIDEO_FORMAT) {
            clearFormat = true;
 }
 }

    mQueue->clear(clearFormat);

 if (type & DISCONTINUITY_TIME) {
 uint64_t resumeAtPTS;
 if (extra != NULL
 && extra->findInt64(
 IStreamListener::kKeyResumeAtPTS,
 (int64_t *)&resumeAtPTS)) {
 int64_t resumeAtMediaTimeUs =
                mProgram->convertPTSToTimestamp(resumeAtPTS);

            extra->setInt64("resume-at-mediaTimeUs", resumeAtMediaTimeUs);
 }
 }

 if (mSource != NULL) {
        mSource->queueDiscontinuity(type, extra, true);
 }
}

Project: Android

void ATSParser::Program::signalEOS(status_t finalResult) {
 for (size_t i = 0; i < mStreams.size(); ++i) {
        mStreams.editValueAt(i)->signalEOS(finalResult);
 }
}

Project: Android

void ATSParser::Stream::signalEOS(status_t finalResult) {
 if (mSource != NULL) {
        mSource->signalEOS(finalResult);
 }
    mEOSReached = true;
    flush(NULL);
}

Project: Android

void ATSParser::signalEOS(status_t finalResult) {
 if (finalResult == (status_t) OK) {
        ALOGE("finalResult not OK");
 return;
 }

 for (size_t i = 0; i < mPrograms.size(); ++i) {
        mPrograms.editItemAt(i)->signalEOS(finalResult);
 }
}

Project: Android

size_t ATSParser::PSISection::size() const {
 return mBuffer == NULL ? 0 : mBuffer->size();
}

Project: Android

bool ATSParser::Program::switchPIDs(const Vector<StreamInfo> &infos) {
 bool success = false;

 if (mStreams.size() == infos.size()) {
 size_t i;
 KeyedVector<int32_t, Vector<int32_t> > oldType2PIDs, newType2PIDs;
 for (i = 0; i < mStreams.size(); ++i) {
 ssize_t index = oldType2PIDs.indexOfKey(mStreams[i]->type());
 if (index < 0) {
                oldType2PIDs.add(mStreams[i]->type(), Vector<int32_t>());
 }
            oldType2PIDs.editValueFor(mStreams[i]->type()).push_back(mStreams[i]->pid());
 }
 for (i = 0; i < infos.size(); ++i) {
 ssize_t index = newType2PIDs.indexOfKey(infos[i].mType);
 if (index < 0) {
                newType2PIDs.add(infos[i].mType, Vector<int32_t>());
 }
            newType2PIDs.editValueFor(infos[i].mType).push_back(infos[i].mPID);
 }

 if (oldType2PIDs.size() == newType2PIDs.size()) {
            success = true;
 for (i = 0; i < oldType2PIDs.size(); ++i) {
 if (oldType2PIDs.keyAt(i) != newType2PIDs.keyAt(i)
 || oldType2PIDs[i].size() != newType2PIDs[i].size()) {
                     success = false;
 break;
 }
 }
 }

 if (success) {
 KeyedVector<int32_t, sp<Stream> > temp;
 for (i = 0; i < mStreams.size(); ++i) {
                 temp.add(mStreams.keyAt(i), mStreams.editValueAt(i));
 }

            mStreams.clear();
 for (i = 0; i < temp.size(); ++i) {
 ssize_t index = newType2PIDs.indexOfKey(temp[i]->type());
 if (index < 0) {
 return false;
 }
 Vector<int32_t> &newPIDs = newType2PIDs.editValueAt(index);
 if (newPIDs.isEmpty()) {
 return false;
 }

 Vector<int32_t>::iterator it = newPIDs.begin();

                temp.editValueAt(i)->setPID(*it);
                mStreams.add(temp[i]->pid(), temp.editValueAt(i));

                newPIDs.erase(it);
 }
 }
 }
 return success;
}

Project: Android

 unsigned type() const { return mStreamType; }

Project: Android

 void updateProgramMapPID(unsigned programMapPID) {
        mProgramMapPID = programMapPID;
 }

Project: Android

ATSParser::~ATSParser() {
}

Project: Android

ATSParser::PSISection::~PSISection() {
}

Project: Android

ATSParser::Stream::~Stream() {
 delete mQueue;
    mQueue = NULL;
}