| /* |
| * 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/DataSource.h> |
| #include <media/stagefright/MediaDebug.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; |
| } |
| } |
| |
| LOGI("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; |
| } else { |
| mParser->feedTSPacket(packet, kTSPacketSize); |
| } |
| |
| mOffset += n; |
| |
| return OK; |
| } |
| |
| 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 |