/* * 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/NuCachedSource2.h" #include #include #include #include #include #include #include #include "AnotherPacketSource.h" #include "ATSParser.h" namespace android { static const size_t kTSPacketSize = 188; struct MPEG2TSSource : public MediaSource { MPEG2TSSource( const sp &extractor, const sp &impl, bool seekable); 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; // If there are both audio and video streams, only the video stream // will be seekable, otherwise the single stream will be seekable. bool mSeekable; DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSSource); }; MPEG2TSSource::MPEG2TSSource( const sp &extractor, const sp &impl, bool seekable) : mExtractor(extractor), mImpl(impl), mSeekable(seekable) { } 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; int64_t seekTimeUs; ReadOptions::SeekMode seekMode; if (mSeekable && options && options->getSeekTo(&seekTimeUs, &seekMode)) { return ERROR_UNSUPPORTED; } 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; } bool seekable = true; if (mSourceImpls.size() > 1) { CHECK_EQ(mSourceImpls.size(), 2u); sp meta = mSourceImpls.editItemAt(index)->getFormat(); const char *mime; CHECK(meta->findCString(kKeyMIMEType, &mime)); if (!strncasecmp("audio/", mime, 6)) { seekable = false; } } return new MPEG2TSSource(this, mSourceImpls.editItemAt(index), seekable); } 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; int numPacketsParsed = 0; while (feedMore() == OK) { ATSParser::SourceType type; if (haveAudio && haveVideo) { break; } if (!haveVideo) { sp impl = (AnotherPacketSource *)mParser->getSource( ATSParser::VIDEO).get(); if (impl != NULL) { haveVideo = true; mSourceImpls.push(impl); } } if (!haveAudio) { sp impl = (AnotherPacketSource *)mParser->getSource( ATSParser::AUDIO).get(); if (impl != NULL) { haveAudio = true; mSourceImpls.push(impl); } } if (++numPacketsParsed > 10000) { break; } } ALOGI("haveAudio=%d, haveVideo=%d", haveAudio, haveVideo); } status_t MPEG2TSExtractor::feedMore() { Mutex::Autolock autoLock(mLock); 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 += n; return mParser->feedTSPacket(packet, kTSPacketSize); } uint32_t MPEG2TSExtractor::flags() const { return CAN_PAUSE; } //////////////////////////////////////////////////////////////////////////////// bool SniffMPEG2TS( const sp &source, String8 *mimeType, float *confidence, sp *) { for (int i = 0; i < 5; ++i) { char header; if (source->readAt(kTSPacketSize * i, &header, 1) != 1 || header != 0x47) { return false; } } *confidence = 0.1f; mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MPEG2TS); return true; } } // namespace android