diff options
Diffstat (limited to 'media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp')
-rw-r--r-- | media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp new file mode 100644 index 0000000..e1589b4 --- /dev/null +++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp @@ -0,0 +1,266 @@ +/* + * 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 <utils/Log.h> + +#include "include/MPEG2TSExtractor.h" +#include "include/LiveSession.h" +#include "include/NuCachedSource2.h" + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> +#include <utils/String8.h> + +#include "AnotherPacketSource.h" +#include "ATSParser.h" + +namespace android { + +static const size_t kTSPacketSize = 188; + +struct MPEG2TSSource : public MediaSource { + MPEG2TSSource( + const sp<MPEG2TSExtractor> &extractor, + const sp<AnotherPacketSource> &impl, + bool seekable); + + virtual status_t start(MetaData *params = NULL); + virtual status_t stop(); + virtual sp<MetaData> getFormat(); + + virtual status_t read( + MediaBuffer **buffer, const ReadOptions *options = NULL); + +private: + sp<MPEG2TSExtractor> mExtractor; + sp<AnotherPacketSource> 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<MPEG2TSExtractor> &extractor, + const sp<AnotherPacketSource> &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<MetaData> MPEG2TSSource::getFormat() { + sp<MetaData> meta = mImpl->getFormat(); + + int64_t durationUs; + if (mExtractor->mLiveSession != NULL + && mExtractor->mLiveSession->getDuration(&durationUs) == OK) { + meta->setInt64(kKeyDuration, durationUs); + } + + return meta; +} + +status_t MPEG2TSSource::read( + MediaBuffer **out, const ReadOptions *options) { + *out = NULL; + + int64_t seekTimeUs; + ReadOptions::SeekMode seekMode; + if (mSeekable && options && options->getSeekTo(&seekTimeUs, &seekMode)) { + mExtractor->seekTo(seekTimeUs); + } + + 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<DataSource> &source) + : mDataSource(source), + mParser(new ATSParser), + mOffset(0) { + init(); +} + +size_t MPEG2TSExtractor::countTracks() { + return mSourceImpls.size(); +} + +sp<MediaSource> MPEG2TSExtractor::getTrack(size_t index) { + if (index >= mSourceImpls.size()) { + return NULL; + } + + bool seekable = true; + if (mSourceImpls.size() > 1) { + CHECK_EQ(mSourceImpls.size(), 2u); + + sp<MetaData> 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<MetaData> MPEG2TSExtractor::getTrackMetaData( + size_t index, uint32_t flags) { + return index < mSourceImpls.size() + ? mSourceImpls.editItemAt(index)->getFormat() : NULL; +} + +sp<MetaData> MPEG2TSExtractor::getMetaData() { + sp<MetaData> 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<AnotherPacketSource> impl = + (AnotherPacketSource *)mParser->getSource( + ATSParser::VIDEO).get(); + + if (impl != NULL) { + haveVideo = true; + mSourceImpls.push(impl); + } + } + + if (!haveAudio) { + sp<AnotherPacketSource> 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); +} + +void MPEG2TSExtractor::setLiveSession(const sp<LiveSession> &liveSession) { + Mutex::Autolock autoLock(mLock); + + mLiveSession = liveSession; +} + +void MPEG2TSExtractor::seekTo(int64_t seekTimeUs) { + Mutex::Autolock autoLock(mLock); + + if (mLiveSession == NULL) { + return; + } + + mLiveSession->seekTo(seekTimeUs); +} + +uint32_t MPEG2TSExtractor::flags() const { + Mutex::Autolock autoLock(mLock); + + uint32_t flags = CAN_PAUSE; + + if (mLiveSession != NULL && mLiveSession->isSeekable()) { + flags |= CAN_SEEK_FORWARD | CAN_SEEK_BACKWARD | CAN_SEEK; + } + + return flags; +} + +//////////////////////////////////////////////////////////////////////////////// + +bool SniffMPEG2TS( + const sp<DataSource> &source, String8 *mimeType, float *confidence, + sp<AMessage> *) { + 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 |