From cda17c606b0fe3ccda4dc68a6d43882410ea2462 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Mon, 7 Jun 2010 13:05:37 -0700 Subject: Initial checkin of mpeg2 transport stream parser for stagefright. Change-Id: I328ce77404daf7127933b48c9d58ed504fb8fc6f --- media/libstagefright/Android.mk | 4 +- media/libstagefright/DataSource.cpp | 2 + media/libstagefright/MediaDefs.cpp | 1 + media/libstagefright/MediaExtractor.cpp | 3 + media/libstagefright/StagefrightMediaScanner.cpp | 2 +- media/libstagefright/include/MPEG2TSExtractor.h | 54 ++ media/libstagefright/mpeg2ts/ABitReader.cpp | 98 +++ media/libstagefright/mpeg2ts/ABitReader.h | 53 ++ media/libstagefright/mpeg2ts/ATSParser.cpp | 941 +++++++++++++++++++++ media/libstagefright/mpeg2ts/ATSParser.h | 68 ++ media/libstagefright/mpeg2ts/Android.mk | 22 + .../libstagefright/mpeg2ts/AnotherPacketSource.cpp | 112 +++ media/libstagefright/mpeg2ts/AnotherPacketSource.h | 62 ++ media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp | 189 +++++ 14 files changed, 1609 insertions(+), 2 deletions(-) create mode 100644 media/libstagefright/include/MPEG2TSExtractor.h create mode 100644 media/libstagefright/mpeg2ts/ABitReader.cpp create mode 100644 media/libstagefright/mpeg2ts/ABitReader.h create mode 100644 media/libstagefright/mpeg2ts/ATSParser.cpp create mode 100644 media/libstagefright/mpeg2ts/ATSParser.h create mode 100644 media/libstagefright/mpeg2ts/Android.mk create mode 100644 media/libstagefright/mpeg2ts/AnotherPacketSource.cpp create mode 100644 media/libstagefright/mpeg2ts/AnotherPacketSource.h create mode 100644 media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp (limited to 'media') diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index f00fad8..6286046 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -80,11 +80,13 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_matroska \ libstagefright_vpxdec \ libvpx \ + libstagefright_mpeg2ts \ LOCAL_SHARED_LIBRARIES += \ libstagefright_amrnb_common \ libstagefright_enc_common \ - libstagefright_avc_common + libstagefright_avc_common \ + libstagefright_foundation \ ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true) diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp index 475422d..b569a6b 100644 --- a/media/libstagefright/DataSource.cpp +++ b/media/libstagefright/DataSource.cpp @@ -19,6 +19,7 @@ #include "include/MPEG4Extractor.h" #include "include/WAVExtractor.h" #include "include/OggExtractor.h" +#include "include/MPEG2TSExtractor.h" #include "matroska/MatroskaExtractor.h" @@ -97,6 +98,7 @@ void DataSource::RegisterDefaultSniffers() { RegisterSniffer(SniffWAV); RegisterSniffer(SniffOgg); RegisterSniffer(SniffMatroska); + RegisterSniffer(SniffMPEG2TS); } // static diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp index 327a0ce..39d264c 100644 --- a/media/libstagefright/MediaDefs.cpp +++ b/media/libstagefright/MediaDefs.cpp @@ -38,5 +38,6 @@ const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mpeg4"; const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/wav"; const char *MEDIA_MIMETYPE_CONTAINER_OGG = "application/ogg"; const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA = "video/x-matroska"; +const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS = "video/mp2ts"; } // namespace android diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp index 376d715..56e6136 100644 --- a/media/libstagefright/MediaExtractor.cpp +++ b/media/libstagefright/MediaExtractor.cpp @@ -23,6 +23,7 @@ #include "include/MPEG4Extractor.h" #include "include/WAVExtractor.h" #include "include/OggExtractor.h" +#include "include/MPEG2TSExtractor.h" #include "matroska/MatroskaExtractor.h" @@ -73,6 +74,8 @@ sp MediaExtractor::Create( return new OggExtractor(source); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MATROSKA)) { return new MatroskaExtractor(source); + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) { + return new MPEG2TSExtractor(source); } return NULL; diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index 2829638..1629e9f 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -39,7 +39,7 @@ static bool FileHasAcceptableExtension(const char *extension) { ".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2", ".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac", ".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota", - ".mkv", ".mka", ".webm" + ".mkv", ".mka", ".webm", ".ts" }; static const size_t kNumValidExtensions = sizeof(kValidExtensions) / sizeof(kValidExtensions[0]); diff --git a/media/libstagefright/include/MPEG2TSExtractor.h b/media/libstagefright/include/MPEG2TSExtractor.h new file mode 100644 index 0000000..c96973b --- /dev/null +++ b/media/libstagefright/include/MPEG2TSExtractor.h @@ -0,0 +1,54 @@ +#ifndef MPEG2_TS_EXTRACTOR_H_ + +#define MPEG2_TS_EXTRACTOR_H_ + +#include +#include +#include +#include + +namespace android { + +struct AnotherPacketSource; +struct ATSParser; +struct DataSource; +struct MPEG2TSSource; +struct String8; + +struct MPEG2TSExtractor : public MediaExtractor { + MPEG2TSExtractor(const sp &source); + + virtual size_t countTracks(); + virtual sp getTrack(size_t index); + virtual sp getTrackMetaData(size_t index, uint32_t flags); + + virtual sp getMetaData(); + + virtual uint32_t flags() const { + return CAN_PAUSE; + } + +private: + friend struct MPEG2TSSource; + + Mutex mLock; + + sp mDataSource; + sp mParser; + + Vector > mSourceImpls; + + off_t mOffset; + + void init(); + status_t feedMore(); + + DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSExtractor); +}; + +bool SniffMPEG2TS( + const sp &source, String8 *mimeType, float *confidence); + +} // namespace android + +#endif // MPEG2_TS_EXTRACTOR_H_ diff --git a/media/libstagefright/mpeg2ts/ABitReader.cpp b/media/libstagefright/mpeg2ts/ABitReader.cpp new file mode 100644 index 0000000..24c8df8 --- /dev/null +++ b/media/libstagefright/mpeg2ts/ABitReader.cpp @@ -0,0 +1,98 @@ +/* + * 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. + */ + +#include "ABitReader.h" + +#include + +namespace android { + +ABitReader::ABitReader(const uint8_t *data, size_t size) + : mData(data), + mSize(size), + mReservoir(0), + mNumBitsLeft(0) { +} + +void ABitReader::fillReservoir() { + CHECK_GT(mSize, 0u); + + mReservoir = 0; + size_t i; + for (i = 0; mSize > 0 && i < 4; ++i) { + mReservoir = (mReservoir << 8) | *mData; + + ++mData; + --mSize; + } + + mNumBitsLeft = 8 * i; + mReservoir <<= 32 - mNumBitsLeft; +} + +uint32_t ABitReader::getBits(size_t n) { + CHECK_LE(n, 32u); + + uint32_t result = 0; + while (n > 0) { + if (mNumBitsLeft == 0) { + fillReservoir(); + } + + size_t m = n; + if (m > mNumBitsLeft) { + m = mNumBitsLeft; + } + + result = (result << m) | (mReservoir >> (32 - m)); + mReservoir <<= m; + mNumBitsLeft -= m; + + n -= m; + } + + return result; +} + +void ABitReader::skipBits(size_t n) { + while (n > 32) { + getBits(32); + n -= 32; + } + + if (n > 0) { + getBits(n); + } +} + +void ABitReader::putBits(uint32_t x, size_t n) { + CHECK_LE(mNumBitsLeft + n, 32u); + + mReservoir = (mReservoir >> n) | (x << (32 - n)); + mNumBitsLeft += n; +} + +size_t ABitReader::numBitsLeft() const { + return mSize * 8 + mNumBitsLeft; +} + +const uint8_t *ABitReader::data() const { + CHECK_EQ(mNumBitsLeft % 8, 0u); + + return mData - mNumBitsLeft / 8; +} + +} // namespace android diff --git a/media/libstagefright/mpeg2ts/ABitReader.h b/media/libstagefright/mpeg2ts/ABitReader.h new file mode 100644 index 0000000..5135211 --- /dev/null +++ b/media/libstagefright/mpeg2ts/ABitReader.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#ifndef A_BIT_READER_H_ + +#define A_BIT_READER_H_ + +#include + +#include +#include + +namespace android { + +struct ABitReader { + ABitReader(const uint8_t *data, size_t size); + + uint32_t getBits(size_t n); + void skipBits(size_t n); + + size_t numBitsLeft() const; + + const uint8_t *data() const; + +private: + const uint8_t *mData; + size_t mSize; + + uint32_t mReservoir; // left-aligned bits + size_t mNumBitsLeft; + + void fillReservoir(); + void putBits(uint32_t x, size_t n); + + DISALLOW_EVIL_CONSTRUCTORS(ABitReader); +}; + +} // namespace android + +#endif // A_BIT_READER_H_ diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp new file mode 100644 index 0000000..d05975d --- /dev/null +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -0,0 +1,941 @@ +/* + * 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. + */ + +#include "ATSParser.h" + +#include "ABitReader.h" +#include "AnotherPacketSource.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +static const size_t kTSPacketSize = 188; + +struct ATSParser::Program : public RefBase { + Program(unsigned programMapPID); + + bool parsePID( + unsigned pid, unsigned payload_unit_start_indicator, + ABitReader *br); + + sp getSource(SourceType type); + +private: + unsigned mProgramMapPID; + KeyedVector > mStreams; + + void parseProgramMap(ABitReader *br); + + DISALLOW_EVIL_CONSTRUCTORS(Program); +}; + +struct ATSParser::Stream : public RefBase { + Stream(unsigned elementaryPID, unsigned streamType); + + void parse( + unsigned payload_unit_start_indicator, + ABitReader *br); + + sp getSource(SourceType type); + +protected: + virtual ~Stream(); + +private: + unsigned mElementaryPID; + unsigned mStreamType; + + sp mBuffer; + sp mSource; + bool mPayloadStarted; + + void flush(); + void parsePES(ABitReader *br); + + void onPayloadData( + unsigned PTS_DTS_flags, uint64_t PTS, uint64_t DTS, + const uint8_t *data, size_t size); + + DISALLOW_EVIL_CONSTRUCTORS(Stream); +}; + +//////////////////////////////////////////////////////////////////////////////// + +ATSParser::Program::Program(unsigned programMapPID) + : mProgramMapPID(programMapPID) { +} + +bool ATSParser::Program::parsePID( + unsigned pid, unsigned payload_unit_start_indicator, + ABitReader *br) { + if (pid == mProgramMapPID) { + if (payload_unit_start_indicator) { + unsigned skip = br->getBits(8); + br->skipBits(skip * 8); + } + + parseProgramMap(br); + return true; + } + + ssize_t index = mStreams.indexOfKey(pid); + if (index < 0) { + return false; + } + + mStreams.editValueAt(index)->parse( + payload_unit_start_indicator, br); + + return true; +} + +void ATSParser::Program::parseProgramMap(ABitReader *br) { + unsigned table_id = br->getBits(8); + LOG(VERBOSE) << " table_id = " << table_id; + CHECK_EQ(table_id, 0x02u); + + unsigned section_syntax_indictor = br->getBits(1); + LOG(VERBOSE) << " section_syntax_indictor = " << section_syntax_indictor; + CHECK_EQ(section_syntax_indictor, 1u); + + CHECK_EQ(br->getBits(1), 0u); + LOG(VERBOSE) << " reserved = " << br->getBits(2); + + unsigned section_length = br->getBits(12); + LOG(VERBOSE) << " section_length = " << section_length; + CHECK((section_length & 0xc00) == 0); + CHECK_LE(section_length, 1021u); + + LOG(VERBOSE) << " program_number = " << br->getBits(16); + LOG(VERBOSE) << " reserved = " << br->getBits(2); + LOG(VERBOSE) << " version_number = " << br->getBits(5); + LOG(VERBOSE) << " current_next_indicator = " << br->getBits(1); + LOG(VERBOSE) << " section_number = " << br->getBits(8); + LOG(VERBOSE) << " last_section_number = " << br->getBits(8); + LOG(VERBOSE) << " reserved = " << br->getBits(3); + + LOG(VERBOSE) << " PCR_PID = " + << StringPrintf("0x%04x", br->getBits(13)); + + LOG(VERBOSE) << " reserved = " << br->getBits(4); + + unsigned program_info_length = br->getBits(12); + LOG(VERBOSE) << " program_info_length = " << program_info_length; + CHECK((program_info_length & 0xc00) == 0); + + br->skipBits(program_info_length * 8); // skip descriptors + + // 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); + LOG(VERBOSE) << " stream_type = " + << StringPrintf("0x%02x", streamType); + + LOG(VERBOSE) << " reserved = " << br->getBits(3); + + unsigned elementaryPID = br->getBits(13); + LOG(VERBOSE) << " elementary_PID = " + << StringPrintf("0x%04x", elementaryPID); + + LOG(VERBOSE) << " reserved = " << br->getBits(4); + + unsigned ES_info_length = br->getBits(12); + LOG(VERBOSE) << " ES_info_length = " << ES_info_length; + CHECK((ES_info_length & 0xc00) == 0); + + 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) { + LOG(VERBOSE) << " tag = " << StringPrintf("0x%02x", br->getBits(8)); + + unsigned descLength = br->getBits(8); + LOG(VERBOSE) << " len = " << descLength; + + CHECK_GE(info_bytes_remaining, 2 + descLength); + + br->skipBits(descLength * 8); + + info_bytes_remaining -= descLength + 2; + } + CHECK_EQ(info_bytes_remaining, 0u); +#endif + + ssize_t index = mStreams.indexOfKey(elementaryPID); +#if 0 // XXX revisit + CHECK_LT(index, 0); + mStreams.add(elementaryPID, new Stream(elementaryPID, streamType)); +#else + if (index < 0) { + mStreams.add(elementaryPID, new Stream(elementaryPID, streamType)); + } +#endif + + infoBytesRemaining -= 5 + ES_info_length; + } + + CHECK_EQ(infoBytesRemaining, 0u); + + LOG(VERBOSE) << " CRC = " << StringPrintf("0x%08x", br->getBits(32)); +} + +sp ATSParser::Program::getSource(SourceType type) { + for (size_t i = 0; i < mStreams.size(); ++i) { + sp source = mStreams.editValueAt(i)->getSource(type); + if (source != NULL) { + return source; + } + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// + +ATSParser::Stream::Stream(unsigned elementaryPID, unsigned streamType) + : mElementaryPID(elementaryPID), + mStreamType(streamType), + mBuffer(new ABuffer(65536)), + mPayloadStarted(false) { + mBuffer->setRange(0, 0); +} + +ATSParser::Stream::~Stream() { +} + +void ATSParser::Stream::parse( + unsigned payload_unit_start_indicator, ABitReader *br) { + 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. + + flush(); + } + + mPayloadStarted = true; + } + + if (!mPayloadStarted) { + return; + } + + size_t payloadSizeBits = br->numBitsLeft(); + CHECK_EQ(payloadSizeBits % 8, 0u); + + CHECK_LE(mBuffer->size() + payloadSizeBits / 8, mBuffer->capacity()); + + memcpy(mBuffer->data() + mBuffer->size(), br->data(), payloadSizeBits / 8); + mBuffer->setRange(0, mBuffer->size() + payloadSizeBits / 8); +} + +void ATSParser::Stream::parsePES(ABitReader *br) { + unsigned packet_startcode_prefix = br->getBits(24); + + LOG(VERBOSE) << "packet_startcode_prefix = " + << StringPrintf("0x%08x", packet_startcode_prefix); + + CHECK_EQ(packet_startcode_prefix, 0x000001u); + + unsigned stream_id = br->getBits(8); + LOG(VERBOSE) << "stream_id = " << StringPrintf("0x%02x", stream_id); + + unsigned PES_packet_length = br->getBits(16); + LOG(VERBOSE) << "PES_packet_length = " << 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); + + LOG(VERBOSE) << "PES_scrambling_control = " << br->getBits(2); + LOG(VERBOSE) << "PES_priority = " << br->getBits(1); + LOG(VERBOSE) << "data_alignment_indicator = " << br->getBits(1); + LOG(VERBOSE) << "copyright = " << br->getBits(1); + LOG(VERBOSE) << "original_or_copy = " << br->getBits(1); + + unsigned PTS_DTS_flags = br->getBits(2); + LOG(VERBOSE) << "PTS_DTS_flags = " << PTS_DTS_flags; + + unsigned ESCR_flag = br->getBits(1); + LOG(VERBOSE) << "ESCR_flag = " << ESCR_flag; + + unsigned ES_rate_flag = br->getBits(1); + LOG(VERBOSE) << "ES_rate_flag = " << ES_rate_flag; + + unsigned DSM_trick_mode_flag = br->getBits(1); + LOG(VERBOSE) << "DSM_trick_mode_flag = " << DSM_trick_mode_flag; + + unsigned additional_copy_info_flag = br->getBits(1); + LOG(VERBOSE) << "additional_copy_info_flag = " + << additional_copy_info_flag; + + LOG(VERBOSE) << "PES_CRC_flag = " << br->getBits(1); + LOG(VERBOSE) << "PES_extension_flag = " << br->getBits(1); + + unsigned PES_header_data_length = br->getBits(8); + LOG(VERBOSE) << "PES_header_data_length = " << 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); + + LOG(VERBOSE) << "PTS = " << PTS; + // LOG(INFO) << "PTS = " << PTS / 90000.0f << " secs"; + + 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); + + LOG(VERBOSE) << "DTS = " << 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); + + LOG(VERBOSE) << "ESCR = " << ESCR; + LOG(VERBOSE) << "ESCR_extension = " << 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); + LOG(VERBOSE) << "ES_rate = " << br->getBits(22); + CHECK_EQ(br->getBits(1), 1u); + + optional_bytes_remaining -= 3; + } + + br->skipBits(optional_bytes_remaining * 8); + + // ES data follows. + + onPayloadData( + PTS_DTS_flags, PTS, DTS, + br->data(), br->numBitsLeft() / 8); + + 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; + + CHECK_EQ(br->numBitsLeft(), dataLength * 8); + + br->skipBits(dataLength * 8); + } else { + size_t payloadSizeBits = br->numBitsLeft(); + CHECK((payloadSizeBits % 8) == 0); + + LOG(VERBOSE) << "There's " << (payloadSizeBits / 8) + << " bytes of payload."; + } + } 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); + } +} + +void ATSParser::Stream::flush() { + if (mBuffer->size() == 0) { + return; + } + + LOG(VERBOSE) << "flushing stream " + << StringPrintf("0x%04x", mElementaryPID) + << " size = " << mBuffer->size(); + + ABitReader br(mBuffer->data(), mBuffer->size()); + parsePES(&br); + + mBuffer->setRange(0, 0); +} + +static sp FindNAL( + const uint8_t *data, size_t size, unsigned nalType, + size_t *stopOffset) { + bool foundStart = false; + size_t startOffset = 0; + + size_t offset = 0; + for (;;) { + while (offset + 3 < size + && memcmp("\x00\x00\x00\x01", &data[offset], 4)) { + ++offset; + } + + if (foundStart) { + size_t nalSize; + if (offset + 3 >= size) { + nalSize = size - startOffset; + } else { + nalSize = offset - startOffset; + } + + sp nal = new ABuffer(nalSize); + memcpy(nal->data(), &data[startOffset], nalSize); + + if (stopOffset != NULL) { + *stopOffset = startOffset + nalSize; + } + + return nal; + } + + if (offset + 4 >= size) { + return NULL; + } + + if ((data[offset + 4] & 0x1f) == nalType) { + foundStart = true; + startOffset = offset + 4; + } + + offset += 4; + } +} + +static unsigned parseUE(ABitReader *br) { + unsigned numZeroes = 0; + while (br->getBits(1) == 0) { + ++numZeroes; + } + + unsigned x = br->getBits(numZeroes); + + return x + (1u << numZeroes) - 1; +} + +// Determine video dimensions from the sequence parameterset. +static void FindDimensions( + const sp seqParamSet, int32_t *width, int32_t *height) { + ABitReader br(seqParamSet->data() + 1, seqParamSet->size() - 1); + + unsigned profile_idc = br.getBits(8); + br.skipBits(16); + parseUE(&br); // seq_parameter_set_id + + if (profile_idc == 100 || profile_idc == 110 + || profile_idc == 122 || profile_idc == 144) { + TRESPASS(); + } + + parseUE(&br); // log2_max_frame_num_minus4 + unsigned pic_order_cnt_type = parseUE(&br); + + if (pic_order_cnt_type == 0) { + parseUE(&br); // log2_max_pic_order_cnt_lsb_minus4 + } else if (pic_order_cnt_type == 1) { + br.getBits(1); // delta_pic_order_always_zero_flag + parseUE(&br); // offset_for_non_ref_pic + parseUE(&br); // offset_for_top_to_bottom_field + + unsigned num_ref_frames_in_pic_order_cnt_cycle = parseUE(&br); + for (unsigned i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; ++i) { + parseUE(&br); // offset_for_ref_frame + } + } + + parseUE(&br); // num_ref_frames + br.getBits(1); // gaps_in_frame_num_value_allowed_flag + + unsigned pic_width_in_mbs_minus1 = parseUE(&br); + unsigned pic_height_in_map_units_minus1 = parseUE(&br); + unsigned frame_mbs_only_flag = br.getBits(1); + + *width = pic_width_in_mbs_minus1 * 16 + 16; + + *height = (2 - frame_mbs_only_flag) + * (pic_height_in_map_units_minus1 * 16 + 16); +} + +static sp MakeAVCCodecSpecificData( + const sp &buffer, int32_t *width, int32_t *height) { + const uint8_t *data = buffer->data(); + size_t size = buffer->size(); + + sp seqParamSet = FindNAL(data, size, 7, NULL); + if (seqParamSet == NULL) { + return NULL; + } + + FindDimensions(seqParamSet, width, height); + + size_t stopOffset; + sp picParamSet = FindNAL(data, size, 8, &stopOffset); + CHECK(picParamSet != NULL); + + buffer->setRange(stopOffset, size - stopOffset); + LOG(INFO) << "buffer has " << buffer->size() << " bytes left."; + + size_t csdSize = + 1 + 3 + 1 + 1 + + 2 * 1 + seqParamSet->size() + + 1 + 2 * 1 + picParamSet->size(); + + sp csd = new ABuffer(csdSize); + uint8_t *out = csd->data(); + + *out++ = 0x01; // configurationVersion + memcpy(out, seqParamSet->data() + 1, 3); // profile/level... + out += 3; + *out++ = (0x3f << 2) | 1; // lengthSize == 2 bytes + *out++ = 0xe0 | 1; + + *out++ = seqParamSet->size() >> 8; + *out++ = seqParamSet->size() & 0xff; + memcpy(out, seqParamSet->data(), seqParamSet->size()); + out += seqParamSet->size(); + + *out++ = 1; + + *out++ = picParamSet->size() >> 8; + *out++ = picParamSet->size() & 0xff; + memcpy(out, picParamSet->data(), picParamSet->size()); + + return csd; +} + +static bool getNextNALUnit( + const uint8_t **_data, size_t *_size, + const uint8_t **nalStart, size_t *nalSize) { + const uint8_t *data = *_data; + size_t size = *_size; + + *nalStart = NULL; + *nalSize = 0; + + if (size == 0) { + return false; + } + + size_t offset = 0; + for (;;) { + CHECK_LT(offset + 2, size); + + if (!memcmp("\x00\x00\x01", &data[offset], 3)) { + break; + } + + CHECK_EQ((unsigned)data[offset], 0x00u); + ++offset; + } + + offset += 3; + size_t startOffset = offset; + + while (offset + 2 < size + && memcmp("\x00\x00\x00", &data[offset], 3) + && memcmp("\x00\x00\x01", &data[offset], 3)) { + ++offset; + } + + if (offset + 2 >= size) { + *nalStart = &data[startOffset]; + *nalSize = size - startOffset; + + *_data = NULL; + *_size = 0; + + return true; + } + + size_t endOffset = offset; + + while (offset + 2 < size && memcmp("\x00\x00\x01", &data[offset], 3)) { + CHECK_EQ((unsigned)data[offset], 0x00u); + ++offset; + } + + CHECK_LT(offset + 2, size); + + *nalStart = &data[startOffset]; + *nalSize = endOffset - startOffset; + + *_data = &data[offset]; + *_size = size - offset; + + return true; +} + +sp MakeCleanAVCData(const uint8_t *data, size_t size) { + const uint8_t *tmpData = data; + size_t tmpSize = size; + + size_t totalSize = 0; + const uint8_t *nalStart; + size_t nalSize; + while (getNextNALUnit(&tmpData, &tmpSize, &nalStart, &nalSize)) { + totalSize += 4 + nalSize; + } + + sp buffer = new ABuffer(totalSize); + size_t offset = 0; + while (getNextNALUnit(&data, &size, &nalStart, &nalSize)) { + memcpy(buffer->data() + offset, "\x00\x00\x00\x01", 4); + memcpy(buffer->data() + offset + 4, nalStart, nalSize); + + offset += 4 + nalSize; + } + + return buffer; +} + +static sp FindMPEG2ADTSConfig( + const sp &buffer, int32_t *sampleRate, int32_t *channelCount) { + ABitReader br(buffer->data(), buffer->size()); + + CHECK_EQ(br.getBits(12), 0xfffu); + CHECK_EQ(br.getBits(1), 0u); + CHECK_EQ(br.getBits(2), 0u); + br.getBits(1); // protection_absent + unsigned profile = br.getBits(2); + LOG(INFO) << "profile = " << profile; + CHECK_NE(profile, 3u); + unsigned sampling_freq_index = br.getBits(4); + br.getBits(1); // private_bit + unsigned channel_configuration = br.getBits(3); + CHECK_NE(channel_configuration, 0u); + + LOG(INFO) << "sampling_freq_index = " << sampling_freq_index; + LOG(INFO) << "channel_configuration = " << channel_configuration; + + CHECK_LE(sampling_freq_index, 11u); + static const int32_t kSamplingFreq[] = { + 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000 + }; + *sampleRate = kSamplingFreq[sampling_freq_index]; + + *channelCount = channel_configuration; + + static const uint8_t kStaticESDS[] = { + 0x03, 22, + 0x00, 0x00, // ES_ID + 0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag + + 0x04, 17, + 0x40, // Audio ISO/IEC 14496-3 + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x05, 2, + // AudioSpecificInfo follows + + // oooo offf fccc c000 + // o - audioObjectType + // f - samplingFreqIndex + // c - channelConfig + }; + sp csd = new ABuffer(sizeof(kStaticESDS) + 2); + memcpy(csd->data(), kStaticESDS, sizeof(kStaticESDS)); + + csd->data()[sizeof(kStaticESDS)] = + ((profile + 1) << 3) | (sampling_freq_index >> 1); + + csd->data()[sizeof(kStaticESDS) + 1] = + ((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3); + + hexdump(csd->data(), csd->size()); + return csd; +} + +void ATSParser::Stream::onPayloadData( + unsigned PTS_DTS_flags, uint64_t PTS, uint64_t DTS, + const uint8_t *data, size_t size) { + LOG(VERBOSE) << "onPayloadData mStreamType=" + << StringPrintf("0x%02x", mStreamType); + + sp buffer; + + if (mStreamType == 0x1b) { + buffer = MakeCleanAVCData(data, size); + } else { + // hexdump(data, size); + + buffer = new ABuffer(size); + memcpy(buffer->data(), data, size); + } + + if (mSource == NULL) { + sp meta = new MetaData; + + if (mStreamType == 0x1b) { + meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC); + + int32_t width, height; + sp csd = MakeAVCCodecSpecificData(buffer, &width, &height); + + if (csd == NULL) { + return; + } + + meta->setData(kKeyAVCC, 0, csd->data(), csd->size()); + meta->setInt32(kKeyWidth, width); + meta->setInt32(kKeyHeight, height); + } else { + CHECK_EQ(mStreamType, 0x0fu); + + meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC); + + int32_t sampleRate, channelCount; + sp csd = + FindMPEG2ADTSConfig(buffer, &sampleRate, &channelCount); + + LOG(INFO) << "sampleRate = " << sampleRate; + LOG(INFO) << "channelCount = " << channelCount; + + meta->setInt32(kKeySampleRate, sampleRate); + meta->setInt32(kKeyChannelCount, channelCount); + + meta->setData(kKeyESDS, 0, csd->data(), csd->size()); + } + + LOG(INFO) << "created source!"; + mSource = new AnotherPacketSource(meta); + + // fall through + } + + CHECK(PTS_DTS_flags == 2 || PTS_DTS_flags == 3); + buffer->meta()->setInt64("time", (PTS * 100) / 9); + + if (mStreamType == 0x0f) { + // WHY??? + buffer->setRange(7, buffer->size() - 7); + } + + mSource->queueAccessUnit(buffer); +} + +sp ATSParser::Stream::getSource(SourceType type) { + if ((type == AVC_VIDEO && mStreamType == 0x1b) + || (type == MPEG2ADTS_AUDIO && mStreamType == 0x0f)) { + return mSource; + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// + +ATSParser::ATSParser() { +} + +ATSParser::~ATSParser() { +} + +void ATSParser::feedTSPacket(const void *data, size_t size) { + CHECK_EQ(size, kTSPacketSize); + + ABitReader br((const uint8_t *)data, kTSPacketSize); + parseTS(&br); +} + +void ATSParser::parseProgramAssociationTable(ABitReader *br) { + unsigned table_id = br->getBits(8); + LOG(VERBOSE) << " table_id = " << table_id; + CHECK_EQ(table_id, 0x00u); + + unsigned section_syntax_indictor = br->getBits(1); + LOG(VERBOSE) << " section_syntax_indictor = " << section_syntax_indictor; + CHECK_EQ(section_syntax_indictor, 1u); + + CHECK_EQ(br->getBits(1), 0u); + LOG(VERBOSE) << " reserved = " << br->getBits(2); + + unsigned section_length = br->getBits(12); + LOG(VERBOSE) << " section_length = " << section_length; + CHECK((section_length & 0xc00) == 0); + + LOG(VERBOSE) << " transport_stream_id = " << br->getBits(16); + LOG(VERBOSE) << " reserved = " << br->getBits(2); + LOG(VERBOSE) << " version_number = " << br->getBits(5); + LOG(VERBOSE) << " current_next_indicator = " << br->getBits(1); + LOG(VERBOSE) << " section_number = " << br->getBits(8); + LOG(VERBOSE) << " last_section_number = " << 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); + LOG(VERBOSE) << " program_number = " << program_number; + + LOG(VERBOSE) << " reserved = " << br->getBits(3); + + if (program_number == 0) { + LOG(VERBOSE) << " network_PID = " + << StringPrintf("0x%04x", br->getBits(13)); + } else { + unsigned programMapPID = br->getBits(13); + + LOG(VERBOSE) << " program_map_PID = " + << StringPrintf("0x%04x", programMapPID); + + mPrograms.push(new Program(programMapPID)); + } + } + + LOG(VERBOSE) << " CRC = " << StringPrintf("0x%08x", br->getBits(32)); +} + +void 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; + } + + bool handled = false; + for (size_t i = 0; i < mPrograms.size(); ++i) { + if (mPrograms.editItemAt(i)->parsePID( + PID, payload_unit_start_indicator, br)) { + handled = true; + break; + } + } + + if (!handled) { + LOG(WARNING) << "PID " << StringPrintf("0x%04x", PID) + << " not handled."; + } +} + +void ATSParser::parseAdaptationField(ABitReader *br) { + unsigned adaptation_field_length = br->getBits(8); + if (adaptation_field_length > 0) { + br->skipBits(adaptation_field_length * 8); // XXX + } +} + +void ATSParser::parseTS(ABitReader *br) { + LOG(VERBOSE) << "---"; + + unsigned sync_byte = br->getBits(8); + CHECK_EQ(sync_byte, 0x47u); + + LOG(VERBOSE) << "transport_error_indicator = " << br->getBits(1); + + unsigned payload_unit_start_indicator = br->getBits(1); + LOG(VERBOSE) << "payload_unit_start_indicator = " + << payload_unit_start_indicator; + + LOG(VERBOSE) << "transport_priority = " << br->getBits(1); + + unsigned PID = br->getBits(13); + LOG(VERBOSE) << "PID = " << StringPrintf("0x%04x", PID); + + LOG(VERBOSE) << "transport_scrambling_control = " << br->getBits(2); + + unsigned adaptation_field_control = br->getBits(2); + LOG(VERBOSE) << "adaptation_field_control = " << adaptation_field_control; + + LOG(VERBOSE) << "continuity_counter = " << br->getBits(4); + + if (adaptation_field_control == 2 || adaptation_field_control == 3) { + parseAdaptationField(br); + } + + if (adaptation_field_control == 1 || adaptation_field_control == 3) { + parsePID(br, PID, payload_unit_start_indicator); + } +} + +sp ATSParser::getSource(SourceType type) { + for (size_t i = 0; i < mPrograms.size(); ++i) { + sp source = mPrograms.editItemAt(i)->getSource(type); + + if (source != NULL) { + return source; + } + } + + return NULL; +} + +} // namespace android diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h new file mode 100644 index 0000000..1e22e7b --- /dev/null +++ b/media/libstagefright/mpeg2ts/ATSParser.h @@ -0,0 +1,68 @@ +/* + * 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. + */ + +#ifndef A_TS_PARSER_H_ + +#define A_TS_PARSER_H_ + +#include + +#include +#include +#include + +namespace android { + +struct ABitReader; +struct MediaSource; + +struct ATSParser : public RefBase { + ATSParser(); + + void feedTSPacket(const void *data, size_t size); + + enum SourceType { + AVC_VIDEO, + MPEG2ADTS_AUDIO + }; + sp getSource(SourceType type); + +protected: + virtual ~ATSParser(); + +private: + struct Program; + struct Stream; + + Vector > mPrograms; + + void parseProgramAssociationTable(ABitReader *br); + void parseProgramMap(ABitReader *br); + void parsePES(ABitReader *br); + + void parsePID( + ABitReader *br, unsigned PID, + unsigned payload_unit_start_indicator); + + void parseAdaptationField(ABitReader *br); + void parseTS(ABitReader *br); + + DISALLOW_EVIL_CONSTRUCTORS(ATSParser); +}; + +} // namespace android + +#endif // A_TS_PARSER_H_ diff --git a/media/libstagefright/mpeg2ts/Android.mk b/media/libstagefright/mpeg2ts/Android.mk new file mode 100644 index 0000000..b6772eb --- /dev/null +++ b/media/libstagefright/mpeg2ts/Android.mk @@ -0,0 +1,22 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + ABitReader.cpp \ + AnotherPacketSource.cpp \ + ATSParser.cpp \ + MPEG2TSExtractor.cpp \ + +LOCAL_C_INCLUDES:= \ + $(JNI_H_INCLUDE) \ + $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \ + $(TOP)/frameworks/base/media/libstagefright + +LOCAL_MODULE:= libstagefright_mpeg2ts + +ifeq ($(TARGET_ARCH),arm) + LOCAL_CFLAGS += -Wno-psabi +endif + +include $(BUILD_STATIC_LIBRARY) diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp new file mode 100644 index 0000000..3d51177 --- /dev/null +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp @@ -0,0 +1,112 @@ +/* + * 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. + */ + +#include "AnotherPacketSource.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { + +AnotherPacketSource::AnotherPacketSource(const sp &meta) + : mFormat(meta), + mEOSResult(OK) { +} + +AnotherPacketSource::~AnotherPacketSource() { +} + +status_t AnotherPacketSource::start(MetaData *params) { + return OK; +} + +status_t AnotherPacketSource::stop() { + return OK; +} + +sp AnotherPacketSource::getFormat() { + return mFormat; +} + +status_t AnotherPacketSource::read( + MediaBuffer **out, const ReadOptions *) { + *out = NULL; + + Mutex::Autolock autoLock(mLock); + while (mEOSResult == OK && mBuffers.empty()) { + mCondition.wait(mLock); + } + + if (!mBuffers.empty()) { + const sp buffer = *mBuffers.begin(); + + uint64_t timeUs; + CHECK(buffer->meta()->findInt64( + "time", (int64_t *)&timeUs)); + + MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size()); + mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs); + + // hexdump(buffer->data(), buffer->size()); + + memcpy(mediaBuffer->data(), buffer->data(), buffer->size()); + *out = mediaBuffer; + + mBuffers.erase(mBuffers.begin()); + return OK; + } + + return mEOSResult; +} + +void AnotherPacketSource::queueAccessUnit(const sp &buffer) { + int32_t damaged; + if (buffer->meta()->findInt32("damaged", &damaged) && damaged) { + // LOG(VERBOSE) << "discarding damaged AU"; + return; + } + + Mutex::Autolock autoLock(mLock); + mBuffers.push_back(buffer); + mCondition.signal(); +} + +void AnotherPacketSource::signalEOS(status_t result) { + CHECK(result != OK); + + Mutex::Autolock autoLock(mLock); + mEOSResult = result; + mCondition.signal(); +} + +bool AnotherPacketSource::hasBufferAvailable(status_t *finalResult) { + Mutex::Autolock autoLock(mLock); + if (!mBuffers.empty()) { + return true; + } + + *finalResult = mEOSResult; + return false; +} + +} // namespace android diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h new file mode 100644 index 0000000..ce83d21 --- /dev/null +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#ifndef ANOTHER_PACKET_SOURCE_H_ + +#define ANOTHER_PACKET_SOURCE_H_ + +#include +#include +#include +#include + +namespace android { + +struct ABuffer; + +struct AnotherPacketSource : public MediaSource { + AnotherPacketSource(const sp &meta); + + virtual status_t start(MetaData *params = NULL); + virtual status_t stop(); + virtual sp getFormat(); + + virtual status_t read( + MediaBuffer **buffer, const ReadOptions *options = NULL); + + bool hasBufferAvailable(status_t *finalResult); + + void queueAccessUnit(const sp &buffer); + void signalEOS(status_t result); + +protected: + virtual ~AnotherPacketSource(); + +private: + Mutex mLock; + Condition mCondition; + + sp mFormat; + List > mBuffers; + status_t mEOSResult; + + DISALLOW_EVIL_CONSTRUCTORS(AnotherPacketSource); +}; + + +} // namespace android + +#endif // ANOTHER_PACKET_SOURCE_H_ diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp new file mode 100644 index 0000000..b798273 --- /dev/null +++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp @@ -0,0 +1,189 @@ +/* + * 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 "MPEG2TSExtractor" +#include + +#include "include/MPEG2TSExtractor.h" + +#include +#include +#include +#include +#include +#include + +#include "AnotherPacketSource.h" +#include "ATSParser.h" + +namespace android { + +struct MPEG2TSSource : public MediaSource { + MPEG2TSSource( + const sp &extractor, + const sp &impl); + + virtual status_t start(MetaData *params = NULL); + virtual status_t stop(); + virtual sp getFormat(); + + virtual status_t read( + MediaBuffer **buffer, const ReadOptions *options = NULL); + +private: + sp mExtractor; + sp mImpl; + + DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSSource); +}; + +MPEG2TSSource::MPEG2TSSource( + const sp &extractor, + const sp &impl) + : mExtractor(extractor), + mImpl(impl) { +} + +status_t MPEG2TSSource::start(MetaData *params) { + return mImpl->start(params); +} + +status_t MPEG2TSSource::stop() { + return mImpl->stop(); +} + +sp MPEG2TSSource::getFormat() { + return mImpl->getFormat(); +} + +status_t MPEG2TSSource::read( + MediaBuffer **out, const ReadOptions *options) { + *out = NULL; + + status_t finalResult; + while (!mImpl->hasBufferAvailable(&finalResult)) { + if (finalResult != OK) { + return ERROR_END_OF_STREAM; + } + + status_t err = mExtractor->feedMore(); + if (err != OK) { + mImpl->signalEOS(err); + } + } + + return mImpl->read(out, options); +} + +//////////////////////////////////////////////////////////////////////////////// + +MPEG2TSExtractor::MPEG2TSExtractor(const sp &source) + : mDataSource(source), + mParser(new ATSParser), + mOffset(0) { + init(); +} + +size_t MPEG2TSExtractor::countTracks() { + return mSourceImpls.size(); +} + +sp MPEG2TSExtractor::getTrack(size_t index) { + if (index >= mSourceImpls.size()) { + return NULL; + } + + return new MPEG2TSSource(this, mSourceImpls.editItemAt(index)); +} + +sp MPEG2TSExtractor::getTrackMetaData( + size_t index, uint32_t flags) { + return index < mSourceImpls.size() + ? mSourceImpls.editItemAt(index)->getFormat() : NULL; +} + +sp MPEG2TSExtractor::getMetaData() { + sp meta = new MetaData; + meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG2TS); + + return meta; +} + +void MPEG2TSExtractor::init() { + bool haveAudio = false; + bool haveVideo = false; + + while (feedMore() == OK) { + ATSParser::SourceType type; + if (haveAudio && haveVideo) { + break; + } + if (haveVideo) { + type = ATSParser::MPEG2ADTS_AUDIO; + } else { + type = ATSParser::AVC_VIDEO; + } + sp impl = + (AnotherPacketSource *)mParser->getSource(type).get(); + + if (impl != NULL) { + if (type == ATSParser::MPEG2ADTS_AUDIO) { + haveAudio = true; + } else { + haveVideo = true; + } + mSourceImpls.push(impl); + } + } + + LOGI("haveAudio=%d, haveVideo=%d", haveAudio, haveVideo); +} + +status_t MPEG2TSExtractor::feedMore() { + Mutex::Autolock autoLock(mLock); + + static const size_t kTSPacketSize = 188; + + uint8_t packet[kTSPacketSize]; + ssize_t n = mDataSource->readAt(mOffset, packet, kTSPacketSize); + + if (n < (ssize_t)kTSPacketSize) { + return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM; + } + + mOffset += kTSPacketSize; + mParser->feedTSPacket(packet, kTSPacketSize); + + return OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +bool SniffMPEG2TS( + const sp &source, String8 *mimeType, float *confidence) { + char header; + if (source->readAt(0, &header, 1) != 1 || header != 0x47) { + return false; + } + + *confidence = 0.05f; + mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MPEG2TS); + + return true; +} + +} // namespace android -- cgit v1.1