/* * Copyright (C) 2011 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 "MPEG2PSExtractor" #include #include "MPEG2PSExtractor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { struct MPEG2PSExtractor::Track : public MediaTrackHelper { Track(MPEG2PSExtractor *extractor, unsigned stream_id, unsigned stream_type); virtual media_status_t start(); virtual media_status_t stop(); virtual media_status_t getFormat(AMediaFormat *); virtual media_status_t read( MediaBufferHelper **buffer, const ReadOptions *options); protected: virtual ~Track(); private: friend struct MPEG2PSExtractor; MPEG2PSExtractor *mExtractor; unsigned mStreamID; unsigned mStreamType; ElementaryStreamQueue *mQueue; sp mSource; status_t appendPESData( unsigned PTS_DTS_flags, uint64_t PTS, uint64_t DTS, const uint8_t *data, size_t size); DISALLOW_EVIL_CONSTRUCTORS(Track); }; struct MPEG2PSExtractor::WrappedTrack : public MediaTrackHelper { WrappedTrack(MPEG2PSExtractor *extractor, Track *track); virtual media_status_t start(); virtual media_status_t stop(); virtual media_status_t getFormat(AMediaFormat *); virtual media_status_t read( MediaBufferHelper **buffer, const ReadOptions *options); protected: virtual ~WrappedTrack(); private: MPEG2PSExtractor *mExtractor; MPEG2PSExtractor::Track *mTrack; DISALLOW_EVIL_CONSTRUCTORS(WrappedTrack); }; //////////////////////////////////////////////////////////////////////////////// MPEG2PSExtractor::MPEG2PSExtractor(DataSourceHelper *source) : mDataSource(source), mOffset(0), mFinalResult(OK), mBuffer(new ABuffer(0)), mScanning(true), mProgramStreamMapValid(false) { for (size_t i = 0; i < 500; ++i) { if (feedMore() != OK) { break; } } // Remove all tracks that were unable to determine their format. AMediaFormat *meta = AMediaFormat_new(); for (size_t i = mTracks.size(); i > 0;) { i--; Track *track = mTracks.valueAt(i); if (track->getFormat(meta) != AMEDIA_OK) { mTracks.removeItemsAt(i); delete track; } } AMediaFormat_delete(meta); mScanning = false; } MPEG2PSExtractor::~MPEG2PSExtractor() { delete mDataSource; for (size_t i = mTracks.size(); i > 0;) { i--; delete mTracks.valueAt(i); } } size_t MPEG2PSExtractor::countTracks() { return mTracks.size(); } MediaTrackHelper *MPEG2PSExtractor::getTrack(size_t index) { if (index >= mTracks.size()) { return NULL; } return new WrappedTrack(this, mTracks.valueAt(index)); } media_status_t MPEG2PSExtractor::getTrackMetaData( AMediaFormat *meta, size_t index, uint32_t /* flags */) { if (index >= mTracks.size()) { return AMEDIA_ERROR_UNKNOWN; } return mTracks.valueAt(index)->getFormat(meta); } media_status_t MPEG2PSExtractor::getMetaData(AMediaFormat *meta) { AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_CONTAINER_MPEG2PS); return AMEDIA_OK; } uint32_t MPEG2PSExtractor::flags() const { return CAN_PAUSE; } status_t MPEG2PSExtractor::feedMore() { Mutex::Autolock autoLock(mLock); // How much data we're reading at a time static const size_t kChunkSize = 8192; for (;;) { status_t err = dequeueChunk(); if (err == -EAGAIN && mFinalResult == OK) { memmove(mBuffer->base(), mBuffer->data(), mBuffer->size()); mBuffer->setRange(0, mBuffer->size()); if (mBuffer->size() + kChunkSize > mBuffer->capacity()) { size_t newCapacity = mBuffer->capacity() + kChunkSize; sp newBuffer = new ABuffer(newCapacity); memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size()); newBuffer->setRange(0, mBuffer->size()); mBuffer = newBuffer; } ssize_t n = mDataSource->readAt( mOffset, mBuffer->data() + mBuffer->size(), kChunkSize); if (n < (ssize_t)kChunkSize) { mFinalResult = (n < 0) ? (status_t)n : ERROR_END_OF_STREAM; return mFinalResult; } mBuffer->setRange(mBuffer->offset(), mBuffer->size() + n); mOffset += n; } else if (err != OK) { mFinalResult = err; return err; } else { return OK; } } } status_t MPEG2PSExtractor::dequeueChunk() { if (mBuffer->size() < 4) { return -EAGAIN; } if (memcmp("\x00\x00\x01", mBuffer->data(), 3)) { return ERROR_MALFORMED; } unsigned chunkType = mBuffer->data()[3]; ssize_t res; switch (chunkType) { case 0xba: { res = dequeuePack(); break; } case 0xbb: { res = dequeueSystemHeader(); break; } default: { res = dequeuePES(); break; } } if (res > 0) { if (mBuffer->size() < (size_t)res) { return -EAGAIN; } mBuffer->setRange(mBuffer->offset() + res, mBuffer->size() - res); res = OK; } return res; } ssize_t MPEG2PSExtractor::dequeuePack() { // 32 + 2 + 3 + 1 + 15 + 1 + 15+ 1 + 9 + 1 + 22 + 1 + 1 | +5 if (mBuffer->size() < 14) { return -EAGAIN; } unsigned pack_stuffing_length = mBuffer->data()[13] & 7; return pack_stuffing_length + 14; } ssize_t MPEG2PSExtractor::dequeueSystemHeader() { if (mBuffer->size() < 6) { return -EAGAIN; } unsigned header_length = U16_AT(mBuffer->data() + 4); return header_length + 6; } ssize_t MPEG2PSExtractor::dequeuePES() { if (mBuffer->size() < 6) { return -EAGAIN; } unsigned PES_packet_length = U16_AT(mBuffer->data() + 4); if (PES_packet_length == 0u) { ALOGE("PES_packet_length is 0"); return -EAGAIN; } size_t n = PES_packet_length + 6; if (mBuffer->size() < n) { return -EAGAIN; } ABitReader br(mBuffer->data(), n); 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; } if (packet_startcode_prefix != 0x000001u) { ALOGE("Wrong PES prefix"); return ERROR_MALFORMED; } unsigned stream_id = br.getBits(8); ALOGV("stream_id = 0x%02x", stream_id); /* unsigned PES_packet_length = */br.getBits(16); if (stream_id == 0xbc) { // program_stream_map if (!mScanning) { return n; } mStreamTypeByESID.clear(); /* unsigned current_next_indicator = */br.getBits(1); /* unsigned reserved = */br.getBits(2); /* unsigned program_stream_map_version = */br.getBits(5); /* unsigned reserved = */br.getBits(7); /* unsigned marker_bit = */br.getBits(1); unsigned program_stream_info_length = br.getBits(16); size_t offset = 0; while (offset < program_stream_info_length) { if (offset + 2 > program_stream_info_length) { return ERROR_MALFORMED; } unsigned descriptor_tag = br.getBits(8); unsigned descriptor_length = br.getBits(8); ALOGI("found descriptor tag 0x%02x of length %u", descriptor_tag, descriptor_length); if (offset + 2 + descriptor_length > program_stream_info_length) { return ERROR_MALFORMED; } br.skipBits(8 * descriptor_length); offset += 2 + descriptor_length; } unsigned elementary_stream_map_length = br.getBits(16); offset = 0; while (offset < elementary_stream_map_length) { if (offset + 4 > elementary_stream_map_length) { return ERROR_MALFORMED; } unsigned stream_type = br.getBits(8); unsigned elementary_stream_id = br.getBits(8); ALOGI("elementary stream id 0x%02x has stream type 0x%02x", elementary_stream_id, stream_type); mStreamTypeByESID.add(elementary_stream_id, stream_type); unsigned elementary_stream_info_length = br.getBits(16); if (offset + 4 + elementary_stream_info_length > elementary_stream_map_length) { return ERROR_MALFORMED; } offset += 4 + elementary_stream_info_length; } /* unsigned CRC32 = */br.getBits(32); mProgramStreamMapValid = true; } else if (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 /* unsigned PES_marker_bits = */br.getBits(2); // should be 0x2(hex) /* unsigned PES_scrambling_control = */br.getBits(2); /* unsigned PES_priority = */br.getBits(1); /* unsigned data_alignment_indicator = */br.getBits(1); /* unsigned copyright = */br.getBits(1); /* unsigned original_or_copy = */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); /* unsigned PES_CRC_flag = */br.getBits(1); /* PES_extension_flag = */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) { if (optional_bytes_remaining < 5u) { return ERROR_MALFORMED; } if (br.getBits(4) != PTS_DTS_flags) { return ERROR_MALFORMED; } PTS = ((uint64_t)br.getBits(3)) << 30; if (br.getBits(1) != 1u) { return ERROR_MALFORMED; } PTS |= ((uint64_t)br.getBits(15)) << 15; if (br.getBits(1) != 1u) { return ERROR_MALFORMED; } PTS |= br.getBits(15); if (br.getBits(1) != 1u) { return ERROR_MALFORMED; } ALOGV("PTS = %" PRIu64, PTS); // ALOGI("PTS = %.2f secs", PTS / 90000.0f); optional_bytes_remaining -= 5; if (PTS_DTS_flags == 3) { if (optional_bytes_remaining < 5u) { return ERROR_MALFORMED; } if (br.getBits(4) != 1u) { return ERROR_MALFORMED; } DTS = ((uint64_t)br.getBits(3)) << 30; if (br.getBits(1) != 1u) { return ERROR_MALFORMED; } DTS |= ((uint64_t)br.getBits(15)) << 15; if (br.getBits(1) != 1u) { return ERROR_MALFORMED; } DTS |= br.getBits(15); if (br.getBits(1) != 1u) { return ERROR_MALFORMED; } ALOGV("DTS = %" PRIu64, DTS); optional_bytes_remaining -= 5; } } if (ESCR_flag) { if (optional_bytes_remaining < 6u) { return ERROR_MALFORMED; } br.getBits(2); uint64_t ESCR = ((uint64_t)br.getBits(3)) << 30; if (br.getBits(1) != 1u) { return ERROR_MALFORMED; } ESCR |= ((uint64_t)br.getBits(15)) << 15; if (br.getBits(1) != 1u) { return ERROR_MALFORMED; } ESCR |= br.getBits(15); if (br.getBits(1) != 1u) { return ERROR_MALFORMED; } ALOGV("ESCR = %" PRIu64, ESCR); /* unsigned ESCR_extension = */br.getBits(9); if (br.getBits(1) != 1u) { return ERROR_MALFORMED; } optional_bytes_remaining -= 6; } if (ES_rate_flag) { if (optional_bytes_remaining < 3u) { return ERROR_MALFORMED; } if (br.getBits(1) != 1u) { return ERROR_MALFORMED; } /* unsigned ES_rate = */br.getBits(22); if (br.getBits(1) != 1u) { return ERROR_MALFORMED; } optional_bytes_remaining -= 3; } if (br.numBitsLeft() < optional_bytes_remaining * 8) { return ERROR_MALFORMED; } br.skipBits(optional_bytes_remaining * 8); // ES data follows. if (PES_packet_length < PES_header_data_length + 3) { return ERROR_MALFORMED; } unsigned dataLength = PES_packet_length - 3 - PES_header_data_length; if (br.numBitsLeft() < dataLength * 8) { ALOGE("PES packet does not carry enough data to contain " "payload. (numBitsLeft = %zu, required = %u)", br.numBitsLeft(), dataLength * 8); return ERROR_MALFORMED; } if (br.numBitsLeft() < dataLength * 8) { return ERROR_MALFORMED; } ssize_t index = mTracks.indexOfKey(stream_id); if (index < 0 && mScanning) { unsigned streamType; ssize_t streamTypeIndex; if (mProgramStreamMapValid && (streamTypeIndex = mStreamTypeByESID.indexOfKey(stream_id)) >= 0) { streamType = mStreamTypeByESID.valueAt(streamTypeIndex); } else if ((stream_id & ~0x1f) == 0xc0) { // ISO/IEC 13818-3 or ISO/IEC 11172-3 or ISO/IEC 13818-7 // or ISO/IEC 14496-3 audio streamType = ATSParser::STREAMTYPE_MPEG2_AUDIO; } else if ((stream_id & ~0x0f) == 0xe0) { // ISO/IEC 13818-2 or ISO/IEC 11172-2 or ISO/IEC 14496-2 video streamType = ATSParser::STREAMTYPE_MPEG2_VIDEO; } else { streamType = ATSParser::STREAMTYPE_RESERVED; } index = mTracks.add( stream_id, new Track(this, stream_id, streamType)); } status_t err = OK; if (index >= 0) { err = mTracks.editValueAt(index)->appendPESData( PTS_DTS_flags, PTS, DTS, br.data(), dataLength); } br.skipBits(dataLength * 8); if (err != OK) { return err; } } else if (stream_id == 0xbe) { // padding_stream if (PES_packet_length == 0u) { return ERROR_MALFORMED; } br.skipBits(PES_packet_length * 8); } else { if (PES_packet_length == 0u) { return ERROR_MALFORMED; } br.skipBits(PES_packet_length * 8); } return n; } //////////////////////////////////////////////////////////////////////////////// MPEG2PSExtractor::Track::Track( MPEG2PSExtractor *extractor, unsigned stream_id, unsigned stream_type) : mExtractor(extractor), mStreamID(stream_id), mStreamType(stream_type), mQueue(NULL) { bool supported = true; ElementaryStreamQueue::Mode mode; switch (mStreamType) { case ATSParser::STREAMTYPE_H264: mode = ElementaryStreamQueue::H264; break; case ATSParser::STREAMTYPE_MPEG2_AUDIO_ADTS: mode = ElementaryStreamQueue::AAC; break; case ATSParser::STREAMTYPE_MPEG1_AUDIO: case ATSParser::STREAMTYPE_MPEG2_AUDIO: mode = ElementaryStreamQueue::MPEG_AUDIO; break; case ATSParser::STREAMTYPE_MPEG1_VIDEO: case ATSParser::STREAMTYPE_MPEG2_VIDEO: mode = ElementaryStreamQueue::MPEG_VIDEO; break; case ATSParser::STREAMTYPE_MPEG4_VIDEO: mode = ElementaryStreamQueue::MPEG4_VIDEO; break; default: supported = false; break; } if (supported) { mQueue = new ElementaryStreamQueue(mode); } else { ALOGI("unsupported stream ID 0x%02x", stream_id); } } MPEG2PSExtractor::Track::~Track() { delete mQueue; mQueue = NULL; } media_status_t MPEG2PSExtractor::Track::start() { if (mSource == NULL) { return AMEDIA_ERROR_UNKNOWN; } // initialize with one small buffer, but allow growth mBufferGroup->init(1 /* one buffer */, 256 /* buffer size */, 64 /* max number of buffers */); if (mSource->start(NULL) == OK) { // AnotherPacketSource::start doesn't use its argument return AMEDIA_OK; } return AMEDIA_ERROR_UNKNOWN; } media_status_t MPEG2PSExtractor::Track::stop() { if (mSource == NULL) { return AMEDIA_ERROR_UNKNOWN; } if (mSource->stop() == OK) { return AMEDIA_OK; } return AMEDIA_ERROR_UNKNOWN; } void copyAMessageToAMediaFormat(AMediaFormat *format, sp msg); media_status_t MPEG2PSExtractor::Track::getFormat(AMediaFormat *meta) { if (mSource == NULL) { return AMEDIA_ERROR_UNKNOWN; } sp sourceMeta = mSource->getFormat(); sp msg; convertMetaDataToMessage(sourceMeta, &msg); copyAMessageToAMediaFormat(meta, msg); return AMEDIA_OK; } media_status_t MPEG2PSExtractor::Track::read( MediaBufferHelper **buffer, const ReadOptions *options) { if (mSource == NULL) { return AMEDIA_ERROR_UNKNOWN; } status_t finalResult; while (!mSource->hasBufferAvailable(&finalResult)) { if (finalResult != OK) { return AMEDIA_ERROR_END_OF_STREAM; } status_t err = mExtractor->feedMore(); if (err != OK) { mSource->signalEOS(err); } } MediaBufferBase *mbuf; mSource->read(&mbuf, (MediaTrack::ReadOptions*) options); size_t length = mbuf->range_length(); MediaBufferHelper *outbuf; mBufferGroup->acquire_buffer(&outbuf, false, length); memcpy(outbuf->data(), mbuf->data(), length); outbuf->set_range(0, length); *buffer = outbuf; MetaDataBase &inMeta = mbuf->meta_data(); AMediaFormat *outMeta = outbuf->meta_data(); int64_t val64; if (inMeta.findInt64(kKeyTime, &val64)) { AMediaFormat_setInt64(outMeta, AMEDIAFORMAT_KEY_TIME_US, val64); } int32_t val32; if (inMeta.findInt32(kKeyIsSyncFrame, &val32)) { AMediaFormat_setInt32(outMeta, AMEDIAFORMAT_KEY_IS_SYNC_FRAME, val32); } if (inMeta.findInt32(kKeyCryptoMode, &val32)) { AMediaFormat_setInt32(outMeta, AMEDIAFORMAT_KEY_CRYPTO_MODE, val32); } uint32_t bufType; const void *bufData; size_t bufSize; if (inMeta.findData(kKeyCryptoIV, &bufType, &bufData, &bufSize)) { AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_CRYPTO_IV, bufData, bufSize); } if (inMeta.findData(kKeyCryptoKey, &bufType, &bufData, &bufSize)) { AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_CRYPTO_KEY, bufData, bufSize); } if (inMeta.findData(kKeyPlainSizes, &bufType, &bufData, &bufSize)) { AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_CRYPTO_PLAIN_SIZES, bufData, bufSize); } if (inMeta.findData(kKeyEncryptedSizes, &bufType, &bufData, &bufSize)) { AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_CRYPTO_ENCRYPTED_SIZES, bufData, bufSize); } if (inMeta.findData(kKeySEI, &bufType, &bufData, &bufSize)) { AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_SEI, bufData, bufSize); } if (inMeta.findData(kKeyAudioPresentationInfo, &bufType, &bufData, &bufSize)) { AMediaFormat_setBuffer(outMeta, AMEDIAFORMAT_KEY_AUDIO_PRESENTATION_INFO, bufData, bufSize); } mbuf->release(); return AMEDIA_OK; } status_t MPEG2PSExtractor::Track::appendPESData( unsigned PTS_DTS_flags, uint64_t PTS, uint64_t /* DTS */, const uint8_t *data, size_t size) { if (mQueue == NULL) { return OK; } int64_t timeUs; if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) { timeUs = (PTS * 100) / 9; } else { timeUs = 0; } status_t err = mQueue->appendData(data, size, timeUs); if (err != OK) { return err; } sp accessUnit; while ((accessUnit = mQueue->dequeueAccessUnit()) != NULL) { if (mSource == NULL) { sp meta = mQueue->getFormat(); if (meta != NULL) { ALOGV("Stream ID 0x%02x now has data.", mStreamID); mSource = new AnotherPacketSource(meta); mSource->queueAccessUnit(accessUnit); } } else if (mQueue->getFormat() != NULL) { mSource->queueAccessUnit(accessUnit); } } return OK; } //////////////////////////////////////////////////////////////////////////////// MPEG2PSExtractor::WrappedTrack::WrappedTrack( MPEG2PSExtractor *extractor, Track *track) : mExtractor(extractor), mTrack(track) { } MPEG2PSExtractor::WrappedTrack::~WrappedTrack() { } media_status_t MPEG2PSExtractor::WrappedTrack::start() { delete mTrack->mBufferGroup; mTrack->mBufferGroup = mBufferGroup; mBufferGroup = nullptr; return mTrack->start(); } media_status_t MPEG2PSExtractor::WrappedTrack::stop() { return mTrack->stop(); } media_status_t MPEG2PSExtractor::WrappedTrack::getFormat(AMediaFormat *meta) { return mTrack->getFormat(meta); } media_status_t MPEG2PSExtractor::WrappedTrack::read( MediaBufferHelper **buffer, const ReadOptions *options) { return mTrack->read(buffer, options); } //////////////////////////////////////////////////////////////////////////////// bool SniffMPEG2PS( DataSourceHelper *source, float *confidence) { uint8_t header[5]; if (source->readAt(0, header, sizeof(header)) < (ssize_t)sizeof(header)) { return false; } if (memcmp("\x00\x00\x01\xba", header, 4) || (header[4] >> 6) != 1) { return false; } *confidence = 0.25f; // Slightly larger than .mp3 extractor's confidence return true; } } // namespace android