blob: e1589b44c8879241964d68f7555473cea953f1ab [file] [log] [blame]
/*
* 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