diff options
Diffstat (limited to 'media/libstagefright/mpeg2ts/ATSParser.cpp')
-rw-r--r-- | media/libstagefright/mpeg2ts/ATSParser.cpp | 1012 |
1 files changed, 1012 insertions, 0 deletions
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp new file mode 100644 index 0000000..3f4de1f --- /dev/null +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -0,0 +1,1012 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "ATSParser" +#include <utils/Log.h> + +#include "ATSParser.h" + +#include "AnotherPacketSource.h" +#include "ESQueue.h" +#include "include/avc_utils.h" + +#include <media/stagefright/foundation/ABitReader.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/foundation/hexdump.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MetaData.h> +#include <media/IStreamSource.h> +#include <utils/KeyedVector.h> + +namespace android { + +// I want the expression "y" evaluated even if verbose logging is off. +#define MY_LOGV(x, y) \ + do { unsigned tmp = y; ALOGV(x, tmp); } while (0) + +static const size_t kTSPacketSize = 188; + +struct ATSParser::Program : public RefBase { + Program(ATSParser *parser, unsigned programNumber, unsigned programMapPID); + + bool parsePID( + unsigned pid, unsigned payload_unit_start_indicator, + ABitReader *br, status_t *err); + + void signalDiscontinuity( + DiscontinuityType type, const sp<AMessage> &extra); + + void signalEOS(status_t finalResult); + + sp<MediaSource> getSource(SourceType type); + + int64_t convertPTSToTimestamp(uint64_t PTS); + + bool PTSTimeDeltaEstablished() const { + return mFirstPTSValid; + } + + unsigned number() const { return mProgramNumber; } + + void updateProgramMapPID(unsigned programMapPID) { + mProgramMapPID = programMapPID; + } + +private: + ATSParser *mParser; + unsigned mProgramNumber; + unsigned mProgramMapPID; + KeyedVector<unsigned, sp<Stream> > mStreams; + bool mFirstPTSValid; + uint64_t mFirstPTS; + + status_t parseProgramMap(ABitReader *br); + + DISALLOW_EVIL_CONSTRUCTORS(Program); +}; + +struct ATSParser::Stream : public RefBase { + Stream(Program *program, unsigned elementaryPID, unsigned streamType); + + unsigned type() const { return mStreamType; } + unsigned pid() const { return mElementaryPID; } + void setPID(unsigned pid) { mElementaryPID = pid; } + + status_t parse( + unsigned payload_unit_start_indicator, + ABitReader *br); + + void signalDiscontinuity( + DiscontinuityType type, const sp<AMessage> &extra); + + void signalEOS(status_t finalResult); + + sp<MediaSource> getSource(SourceType type); + +protected: + virtual ~Stream(); + +private: + Program *mProgram; + unsigned mElementaryPID; + unsigned mStreamType; + + sp<ABuffer> mBuffer; + sp<AnotherPacketSource> mSource; + bool mPayloadStarted; + + ElementaryStreamQueue *mQueue; + + status_t flush(); + status_t parsePES(ABitReader *br); + + void onPayloadData( + unsigned PTS_DTS_flags, uint64_t PTS, uint64_t DTS, + const uint8_t *data, size_t size); + + void extractAACFrames(const sp<ABuffer> &buffer); + + bool isAudio() const; + bool isVideo() const; + + DISALLOW_EVIL_CONSTRUCTORS(Stream); +}; + +//////////////////////////////////////////////////////////////////////////////// + +ATSParser::Program::Program( + ATSParser *parser, unsigned programNumber, unsigned programMapPID) + : mParser(parser), + mProgramNumber(programNumber), + mProgramMapPID(programMapPID), + mFirstPTSValid(false), + mFirstPTS(0) { + ALOGV("new program number %u", programNumber); +} + +bool ATSParser::Program::parsePID( + unsigned pid, unsigned payload_unit_start_indicator, + ABitReader *br, status_t *err) { + *err = OK; + + if (pid == mProgramMapPID) { + if (payload_unit_start_indicator) { + unsigned skip = br->getBits(8); + br->skipBits(skip * 8); + } + + *err = parseProgramMap(br); + + return true; + } + + ssize_t index = mStreams.indexOfKey(pid); + if (index < 0) { + return false; + } + + *err = mStreams.editValueAt(index)->parse( + payload_unit_start_indicator, br); + + return true; +} + +void ATSParser::Program::signalDiscontinuity( + DiscontinuityType type, const sp<AMessage> &extra) { + for (size_t i = 0; i < mStreams.size(); ++i) { + mStreams.editValueAt(i)->signalDiscontinuity(type, extra); + } +} + +void ATSParser::Program::signalEOS(status_t finalResult) { + for (size_t i = 0; i < mStreams.size(); ++i) { + mStreams.editValueAt(i)->signalEOS(finalResult); + } +} + +struct StreamInfo { + unsigned mType; + unsigned mPID; +}; + +status_t ATSParser::Program::parseProgramMap(ABitReader *br) { + unsigned table_id = br->getBits(8); + ALOGV(" table_id = %u", table_id); + CHECK_EQ(table_id, 0x02u); + + unsigned section_syntax_indicator = br->getBits(1); + ALOGV(" section_syntax_indicator = %u", section_syntax_indicator); + CHECK_EQ(section_syntax_indicator, 1u); + + CHECK_EQ(br->getBits(1), 0u); + MY_LOGV(" reserved = %u", br->getBits(2)); + + unsigned section_length = br->getBits(12); + ALOGV(" section_length = %u", section_length); + CHECK_EQ(section_length & 0xc00, 0u); + CHECK_LE(section_length, 1021u); + + 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)); + MY_LOGV(" PCR_PID = 0x%04x", br->getBits(13)); + MY_LOGV(" reserved = %u", br->getBits(4)); + + unsigned program_info_length = br->getBits(12); + ALOGV(" program_info_length = %u", program_info_length); + CHECK_EQ(program_info_length & 0xc00, 0u); + + br->skipBits(program_info_length * 8); // skip descriptors + + Vector<StreamInfo> infos; + + // infoBytesRemaining is the number of bytes that make up the + // variable length section of ES_infos. It does not include the + // final CRC. + size_t infoBytesRemaining = section_length - 9 - program_info_length - 4; + + while (infoBytesRemaining > 0) { + CHECK_GE(infoBytesRemaining, 5u); + + 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); + CHECK_EQ(ES_info_length & 0xc00, 0u); + + CHECK_GE(infoBytesRemaining - 5, 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); + + CHECK_GE(info_bytes_remaining, 2 + descLength); + + br->skipBits(descLength * 8); + + info_bytes_remaining -= descLength + 2; + } + CHECK_EQ(info_bytes_remaining, 0u); +#endif + + StreamInfo info; + info.mType = streamType; + info.mPID = elementaryPID; + infos.push(info); + + infoBytesRemaining -= 5 + ES_info_length; + } + + CHECK_EQ(infoBytesRemaining, 0u); + 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 + + // The only case we can recover from is if we have two streams + // and they switched PIDs. + + bool success = false; + + if (mStreams.size() == 2 && infos.size() == 2) { + const StreamInfo &info1 = infos.itemAt(0); + const StreamInfo &info2 = infos.itemAt(1); + + sp<Stream> s1 = mStreams.editValueAt(0); + sp<Stream> s2 = mStreams.editValueAt(1); + + bool caseA = + info1.mPID == s1->pid() && info1.mType == s2->type() + && info2.mPID == s2->pid() && info2.mType == s1->type(); + + bool caseB = + info1.mPID == s2->pid() && info1.mType == s1->type() + && info2.mPID == s1->pid() && info2.mType == s2->type(); + + if (caseA || caseB) { + unsigned pid1 = s1->pid(); + unsigned pid2 = s2->pid(); + s1->setPID(pid2); + s2->setPID(pid1); + + mStreams.clear(); + mStreams.add(s1->pid(), s1); + mStreams.add(s2->pid(), s2); + + success = true; + } + } + + 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); + mStreams.add(info.mPID, stream); + } + } + + return OK; +} + +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; +} + +int64_t ATSParser::Program::convertPTSToTimestamp(uint64_t 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; + } + } + + return (PTS * 100) / 9; +} + +//////////////////////////////////////////////////////////////////////////////// + +ATSParser::Stream::Stream( + Program *program, unsigned elementaryPID, unsigned streamType) + : mProgram(program), + mElementaryPID(elementaryPID), + mStreamType(streamType), + mPayloadStarted(false), + mQueue(NULL) { + switch (mStreamType) { + case STREAMTYPE_H264: + mQueue = new ElementaryStreamQueue(ElementaryStreamQueue::H264); + 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; + + 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); + } +} + +ATSParser::Stream::~Stream() { + delete mQueue; + mQueue = NULL; +} + +status_t ATSParser::Stream::parse( + unsigned payload_unit_start_indicator, ABitReader *br) { + if (mQueue == NULL) { + return OK; + } + + if (payload_unit_start_indicator) { + if (mPayloadStarted) { + // Otherwise we run the danger of receiving the trailing bytes + // of a PES packet that we never saw the start of and assuming + // we have a a complete PES packet. + + status_t err = flush(); + + if (err != OK) { + return err; + } + } + + mPayloadStarted = true; + } + + if (!mPayloadStarted) { + return OK; + } + + size_t payloadSizeBits = br->numBitsLeft(); + CHECK_EQ(payloadSizeBits % 8, 0u); + + size_t neededSize = mBuffer->size() + payloadSizeBits / 8; + if (mBuffer->capacity() < neededSize) { + // Increment in multiples of 64K. + neededSize = (neededSize + 65535) & ~65535; + + ALOGI("resizing buffer to %d bytes", neededSize); + + sp<ABuffer> newBuffer = new ABuffer(neededSize); + memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size()); + newBuffer->setRange(0, mBuffer->size()); + mBuffer = newBuffer; + } + + memcpy(mBuffer->data() + mBuffer->size(), br->data(), payloadSizeBits / 8); + mBuffer->setRange(0, mBuffer->size() + payloadSizeBits / 8); + + return OK; +} + +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; + } +} + +bool ATSParser::Stream::isAudio() const { + switch (mStreamType) { + case STREAMTYPE_MPEG1_AUDIO: + case STREAMTYPE_MPEG2_AUDIO: + case STREAMTYPE_MPEG2_AUDIO_ADTS: + return true; + + default: + return false; + } +} + +void ATSParser::Stream::signalDiscontinuity( + DiscontinuityType type, const sp<AMessage> &extra) { + if (mQueue == NULL) { + return; + } + + mPayloadStarted = 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); + } +} + +void ATSParser::Stream::signalEOS(status_t finalResult) { + if (mSource != NULL) { + mSource->signalEOS(finalResult); + } +} + +status_t ATSParser::Stream::parsePES(ABitReader *br) { + unsigned packet_startcode_prefix = br->getBits(24); + + ALOGV("packet_startcode_prefix = 0x%08x", packet_startcode_prefix); + + if (packet_startcode_prefix != 1) { + ALOGV("Supposedly payload_unit_start=1 unit does not start " + "with startcode."); + + return ERROR_MALFORMED; + } + + CHECK_EQ(packet_startcode_prefix, 0x000001u); + + unsigned stream_id = br->getBits(8); + ALOGV("stream_id = 0x%02x", stream_id); + + unsigned PES_packet_length = br->getBits(16); + ALOGV("PES_packet_length = %u", PES_packet_length); + + 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 + CHECK_EQ(br->getBits(2), 2u); + + MY_LOGV("PES_scrambling_control = %u", br->getBits(2)); + MY_LOGV("PES_priority = %u", br->getBits(1)); + MY_LOGV("data_alignment_indicator = %u", br->getBits(1)); + MY_LOGV("copyright = %u", br->getBits(1)); + MY_LOGV("original_or_copy = %u", br->getBits(1)); + + unsigned PTS_DTS_flags = br->getBits(2); + ALOGV("PTS_DTS_flags = %u", PTS_DTS_flags); + + unsigned ESCR_flag = br->getBits(1); + ALOGV("ESCR_flag = %u", ESCR_flag); + + unsigned ES_rate_flag = br->getBits(1); + ALOGV("ES_rate_flag = %u", ES_rate_flag); + + unsigned DSM_trick_mode_flag = br->getBits(1); + ALOGV("DSM_trick_mode_flag = %u", DSM_trick_mode_flag); + + unsigned additional_copy_info_flag = br->getBits(1); + ALOGV("additional_copy_info_flag = %u", additional_copy_info_flag); + + MY_LOGV("PES_CRC_flag = %u", br->getBits(1)); + MY_LOGV("PES_extension_flag = %u", br->getBits(1)); + + 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; + + if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) { + CHECK_GE(optional_bytes_remaining, 5u); + + CHECK_EQ(br->getBits(4), PTS_DTS_flags); + + PTS = ((uint64_t)br->getBits(3)) << 30; + CHECK_EQ(br->getBits(1), 1u); + PTS |= ((uint64_t)br->getBits(15)) << 15; + CHECK_EQ(br->getBits(1), 1u); + PTS |= br->getBits(15); + CHECK_EQ(br->getBits(1), 1u); + + ALOGV("PTS = %llu", PTS); + // ALOGI("PTS = %.2f secs", PTS / 90000.0f); + + optional_bytes_remaining -= 5; + + if (PTS_DTS_flags == 3) { + CHECK_GE(optional_bytes_remaining, 5u); + + CHECK_EQ(br->getBits(4), 1u); + + DTS = ((uint64_t)br->getBits(3)) << 30; + CHECK_EQ(br->getBits(1), 1u); + DTS |= ((uint64_t)br->getBits(15)) << 15; + CHECK_EQ(br->getBits(1), 1u); + DTS |= br->getBits(15); + CHECK_EQ(br->getBits(1), 1u); + + ALOGV("DTS = %llu", DTS); + + optional_bytes_remaining -= 5; + } + } + + if (ESCR_flag) { + CHECK_GE(optional_bytes_remaining, 6u); + + br->getBits(2); + + uint64_t ESCR = ((uint64_t)br->getBits(3)) << 30; + CHECK_EQ(br->getBits(1), 1u); + ESCR |= ((uint64_t)br->getBits(15)) << 15; + CHECK_EQ(br->getBits(1), 1u); + ESCR |= br->getBits(15); + CHECK_EQ(br->getBits(1), 1u); + + ALOGV("ESCR = %llu", ESCR); + MY_LOGV("ESCR_extension = %u", br->getBits(9)); + + CHECK_EQ(br->getBits(1), 1u); + + optional_bytes_remaining -= 6; + } + + if (ES_rate_flag) { + CHECK_GE(optional_bytes_remaining, 3u); + + CHECK_EQ(br->getBits(1), 1u); + MY_LOGV("ES_rate = %u", br->getBits(22)); + CHECK_EQ(br->getBits(1), 1u); + + optional_bytes_remaining -= 3; + } + + br->skipBits(optional_bytes_remaining * 8); + + // ES data follows. + + if (PES_packet_length != 0) { + CHECK_GE(PES_packet_length, PES_header_data_length + 3); + + 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 = %d, required = %d)", + br->numBitsLeft(), dataLength * 8); + + return ERROR_MALFORMED; + } + + CHECK_GE(br->numBitsLeft(), dataLength * 8); + + onPayloadData( + PTS_DTS_flags, PTS, DTS, br->data(), dataLength); + + br->skipBits(dataLength * 8); + } else { + onPayloadData( + PTS_DTS_flags, PTS, DTS, + br->data(), br->numBitsLeft() / 8); + + size_t payloadSizeBits = br->numBitsLeft(); + CHECK_EQ(payloadSizeBits % 8, 0u); + + ALOGV("There's %d bytes of payload.", payloadSizeBits / 8); + } + } else if (stream_id == 0xbe) { // padding_stream + CHECK_NE(PES_packet_length, 0u); + br->skipBits(PES_packet_length * 8); + } else { + CHECK_NE(PES_packet_length, 0u); + br->skipBits(PES_packet_length * 8); + } + + return OK; +} + +status_t ATSParser::Stream::flush() { + if (mBuffer->size() == 0) { + return OK; + } + + ALOGV("flushing stream 0x%04x size = %d", mElementaryPID, mBuffer->size()); + + ABitReader br(mBuffer->data(), mBuffer->size()); + + status_t err = parsePES(&br); + + mBuffer->setRange(0, 0); + + return err; +} + +void ATSParser::Stream::onPayloadData( + unsigned PTS_DTS_flags, uint64_t PTS, uint64_t DTS, + const uint8_t *data, size_t size) { + 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 (err != OK) { + return; + } + + sp<ABuffer> accessUnit; + 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); + + mSource = new AnotherPacketSource(meta); + mSource->queueAccessUnit(accessUnit); + } + } 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()); + } + mSource->queueAccessUnit(accessUnit); + } + } +} + +sp<MediaSource> ATSParser::Stream::getSource(SourceType type) { + switch (type) { + case VIDEO: + { + if (isVideo()) { + return mSource; + } + break; + } + + case AUDIO: + { + if (isAudio()) { + return mSource; + } + break; + } + + default: + break; + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// + +ATSParser::ATSParser(uint32_t flags) + : mFlags(flags) { +} + +ATSParser::~ATSParser() { +} + +status_t ATSParser::feedTSPacket(const void *data, size_t size) { + CHECK_EQ(size, kTSPacketSize); + + ABitReader br((const uint8_t *)data, kTSPacketSize); + return parseTS(&br); +} + +void ATSParser::signalDiscontinuity( + DiscontinuityType type, const sp<AMessage> &extra) { + for (size_t i = 0; i < mPrograms.size(); ++i) { + mPrograms.editItemAt(i)->signalDiscontinuity(type, extra); + } +} + +void ATSParser::signalEOS(status_t finalResult) { + CHECK_NE(finalResult, (status_t)OK); + + for (size_t i = 0; i < mPrograms.size(); ++i) { + mPrograms.editItemAt(i)->signalEOS(finalResult); + } +} + +void ATSParser::parseProgramAssociationTable(ABitReader *br) { + unsigned table_id = br->getBits(8); + ALOGV(" table_id = %u", table_id); + CHECK_EQ(table_id, 0x00u); + + unsigned section_syntax_indictor = br->getBits(1); + ALOGV(" section_syntax_indictor = %u", section_syntax_indictor); + CHECK_EQ(section_syntax_indictor, 1u); + + CHECK_EQ(br->getBits(1), 0u); + MY_LOGV(" reserved = %u", br->getBits(2)); + + unsigned section_length = br->getBits(12); + ALOGV(" section_length = %u", section_length); + CHECK_EQ(section_length & 0xc00, 0u); + + 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 */); + CHECK_EQ((numProgramBytes % 4), 0u); + + 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)); + } + } + } + + MY_LOGV(" CRC = 0x%08x", br->getBits(32)); +} + +status_t ATSParser::parsePID( + ABitReader *br, unsigned PID, + unsigned payload_unit_start_indicator) { + if (PID == 0) { + if (payload_unit_start_indicator) { + unsigned skip = br->getBits(8); + br->skipBits(skip * 8); + } + parseProgramAssociationTable(br); + return OK; + } + + bool handled = false; + for (size_t i = 0; i < mPrograms.size(); ++i) { + status_t err; + if (mPrograms.editItemAt(i)->parsePID( + PID, payload_unit_start_indicator, br, &err)) { + if (err != OK) { + return err; + } + + handled = true; + break; + } + } + + if (!handled) { + ALOGV("PID 0x%04x not handled.", PID); + } + + return OK; +} + +void ATSParser::parseAdaptationField(ABitReader *br) { + unsigned adaptation_field_length = br->getBits(8); + if (adaptation_field_length > 0) { + br->skipBits(adaptation_field_length * 8); // XXX + } +} + +status_t ATSParser::parseTS(ABitReader *br) { + ALOGV("---"); + + unsigned sync_byte = br->getBits(8); + CHECK_EQ(sync_byte, 0x47u); + + MY_LOGV("transport_error_indicator = %u", br->getBits(1)); + + 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("continuity_counter = %u", continuity_counter); + + // ALOGI("PID = 0x%04x, continuity_counter = %u", PID, continuity_counter); + + if (adaptation_field_control == 2 || adaptation_field_control == 3) { + parseAdaptationField(br); + } + + if (adaptation_field_control == 1 || adaptation_field_control == 3) { + return parsePID(br, PID, payload_unit_start_indicator); + } + + return OK; +} + +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; +} + +bool ATSParser::PTSTimeDeltaEstablished() { + if (mPrograms.isEmpty()) { + return false; + } + + return mPrograms.editItemAt(0)->PTSTimeDeltaEstablished(); +} + +} // namespace android |