summaryrefslogtreecommitdiffstats
path: root/media/libmediaplayerservice/nuplayer
diff options
context:
space:
mode:
authorAndreas Huber <andih@google.com>2010-12-15 15:17:42 -0800
committerAndreas Huber <andih@google.com>2010-12-15 15:18:26 -0800
commitf933441648ef6a71dee783d733aac17b9508b452 (patch)
tree240f8068edb362cbea579659a963bbb029a2bac0 /media/libmediaplayerservice/nuplayer
parent60c5b57edd3c8f4bdf6b38cf5b8a193ba770bb72 (diff)
downloadframeworks_av-f933441648ef6a71dee783d733aac17b9508b452.zip
frameworks_av-f933441648ef6a71dee783d733aac17b9508b452.tar.gz
frameworks_av-f933441648ef6a71dee783d733aac17b9508b452.tar.bz2
Initial support for a true streaming player for mpeg2 transport streams.
Change-Id: I153eec439d260a5524b21270e16d36940ec3161a
Diffstat (limited to 'media/libmediaplayerservice/nuplayer')
-rw-r--r--media/libmediaplayerservice/nuplayer/Android.mk22
-rw-r--r--media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp467
-rw-r--r--media/libmediaplayerservice/nuplayer/DecoderWrapper.h81
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.cpp477
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.h108
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp286
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h64
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp136
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDriver.h69
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp496
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h108
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp151
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h74
13 files changed, 2539 insertions, 0 deletions
diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk
new file mode 100644
index 0000000..c4f3764
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/Android.mk
@@ -0,0 +1,22 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ NuPlayer.cpp \
+ NuPlayerDecoder.cpp \
+ NuPlayerDriver.cpp \
+ NuPlayerRenderer.cpp \
+ NuPlayerStreamListener.cpp \
+ DecoderWrapper.cpp \
+
+LOCAL_C_INCLUDES := \
+ $(TOP)/frameworks/base/include/media/stagefright/openmax \
+ $(TOP)/frameworks/base/media/libstagefright/include \
+ $(TOP)/frameworks/base/media/libstagefright/mpeg2ts \
+
+LOCAL_MODULE:= libstagefright_nuplayer
+
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_STATIC_LIBRARY)
+
diff --git a/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp b/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
new file mode 100644
index 0000000..89a5e69
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
@@ -0,0 +1,467 @@
+/*
+ * 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 "DecoderWrapper"
+#include <utils/Log.h>
+
+#include "DecoderWrapper.h"
+
+#include "AACDecoder.h"
+
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/ACodec.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+struct DecoderWrapper::WrapperSource : public MediaSource {
+ WrapperSource(
+ const sp<MetaData> &meta,
+ const sp<AMessage> &notify);
+
+ virtual status_t start(MetaData *params);
+ virtual status_t stop();
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options);
+
+ void queueBuffer(const sp<ABuffer> &buffer);
+ void queueEOS(status_t finalResult);
+ void clear();
+
+protected:
+ virtual ~WrapperSource();
+
+private:
+ Mutex mLock;
+ Condition mCondition;
+
+ sp<MetaData> mMeta;
+ sp<AMessage> mNotify;
+
+ List<sp<ABuffer> > mQueue;
+ status_t mFinalResult;
+
+ DISALLOW_EVIL_CONSTRUCTORS(WrapperSource);
+};
+
+DecoderWrapper::WrapperSource::WrapperSource(
+ const sp<MetaData> &meta, const sp<AMessage> &notify)
+ : mMeta(meta),
+ mNotify(notify),
+ mFinalResult(OK) {
+}
+
+DecoderWrapper::WrapperSource::~WrapperSource() {
+}
+
+status_t DecoderWrapper::WrapperSource::start(MetaData *params) {
+ return OK;
+}
+
+status_t DecoderWrapper::WrapperSource::stop() {
+ return OK;
+}
+
+sp<MetaData> DecoderWrapper::WrapperSource::getFormat() {
+ return mMeta;
+}
+
+status_t DecoderWrapper::WrapperSource::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ Mutex::Autolock autoLock(mLock);
+
+ bool requestedBuffer = false;
+
+ while (mQueue.empty() && mFinalResult == OK) {
+ if (!requestedBuffer) {
+ mNotify->dup()->post();
+ requestedBuffer = true;
+ }
+
+ mCondition.wait(mLock);
+ }
+
+ if (mQueue.empty()) {
+ return mFinalResult;
+ }
+
+ sp<ABuffer> src = *mQueue.begin();
+ mQueue.erase(mQueue.begin());
+
+ MediaBuffer *dst = new MediaBuffer(src->size());
+ memcpy(dst->data(), src->data(), src->size());
+
+ int64_t timeUs;
+ CHECK(src->meta()->findInt64("timeUs", &timeUs));
+
+ dst->meta_data()->setInt64(kKeyTime, timeUs);
+
+ *out = dst;
+
+ return OK;
+}
+
+void DecoderWrapper::WrapperSource::queueBuffer(const sp<ABuffer> &buffer) {
+ Mutex::Autolock autoLock(mLock);
+ mQueue.push_back(buffer);
+ mCondition.broadcast();
+}
+
+void DecoderWrapper::WrapperSource::queueEOS(status_t finalResult) {
+ CHECK_NE(finalResult, (status_t)OK);
+
+ Mutex::Autolock autoLock(mLock);
+ mFinalResult = finalResult;
+ mCondition.broadcast();
+}
+
+void DecoderWrapper::WrapperSource::clear() {
+ Mutex::Autolock autoLock(mLock);
+ mQueue.clear();
+ mFinalResult = OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct DecoderWrapper::WrapperReader : public AHandler {
+ WrapperReader(
+ const sp<MediaSource> &decoder,
+ const sp<AMessage> &notify);
+
+ void start();
+ void readMore(bool flush = false);
+
+protected:
+ virtual ~WrapperReader();
+
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+ enum {
+ kWhatRead
+ };
+
+ sp<MediaSource> mDecoder;
+ sp<AMessage> mNotify;
+ bool mEOS;
+
+ DISALLOW_EVIL_CONSTRUCTORS(WrapperReader);
+};
+
+DecoderWrapper::WrapperReader::WrapperReader(
+ const sp<MediaSource> &decoder, const sp<AMessage> &notify)
+ : mDecoder(decoder),
+ mNotify(notify),
+ mEOS(false) {
+}
+
+DecoderWrapper::WrapperReader::~WrapperReader() {
+}
+
+void DecoderWrapper::WrapperReader::start() {
+ CHECK_EQ(mDecoder->start(), (status_t)OK);
+ readMore();
+}
+
+void DecoderWrapper::WrapperReader::readMore(bool flush) {
+ if (!flush && mEOS) {
+ return;
+ }
+
+ sp<AMessage> msg = new AMessage(kWhatRead, id());
+ msg->setInt32("flush", static_cast<int32_t>(flush));
+ msg->post();
+}
+
+void DecoderWrapper::WrapperReader::onMessageReceived(
+ const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatRead:
+ {
+ int32_t flush;
+ CHECK(msg->findInt32("flush", &flush));
+
+ MediaSource::ReadOptions options;
+ if (flush) {
+ // Dummy seek
+ options.setSeekTo(0);
+ mEOS = false;
+ }
+
+ CHECK(!mEOS);
+
+ MediaBuffer *src;
+ status_t err = mDecoder->read(&src, &options);
+
+ sp<AMessage> notify = mNotify->dup();
+
+ sp<AMessage> realNotify;
+ CHECK(notify->findMessage("real-notify", &realNotify));
+
+ if (err == OK) {
+ realNotify->setInt32("what", ACodec::kWhatDrainThisBuffer);
+
+ sp<ABuffer> dst = new ABuffer(src->range_length());
+ memcpy(dst->data(),
+ (const uint8_t *)src->data() + src->range_offset(),
+ src->range_length());
+
+ int64_t timeUs;
+ CHECK(src->meta_data()->findInt64(kKeyTime, &timeUs));
+ src->release();
+ src = NULL;
+
+ dst->meta()->setInt64("timeUs", timeUs);
+
+ realNotify->setObject("buffer", dst);
+ } else {
+ realNotify->setInt32("what", ACodec::kWhatEOS);
+ mEOS = true;
+ }
+
+ notify->post();
+ break;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+DecoderWrapper::DecoderWrapper()
+ : mNumOutstandingInputBuffers(0),
+ mNumOutstandingOutputBuffers(0),
+ mNumPendingDecodes(0),
+ mFlushing(false) {
+}
+
+DecoderWrapper::~DecoderWrapper() {
+}
+
+void DecoderWrapper::setNotificationMessage(const sp<AMessage> &msg) {
+ mNotify = msg;
+}
+
+void DecoderWrapper::initiateSetup(const sp<AMessage> &msg) {
+ msg->setWhat(kWhatSetup);
+ msg->setTarget(id());
+ msg->post();
+}
+
+void DecoderWrapper::initiateShutdown() {
+ (new AMessage(kWhatShutdown, id()))->post();
+}
+
+void DecoderWrapper::signalFlush() {
+ (new AMessage(kWhatFlush, id()))->post();
+}
+
+void DecoderWrapper::signalResume() {
+ (new AMessage(kWhatResume, id()))->post();
+}
+
+void DecoderWrapper::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatSetup:
+ onSetup(msg);
+ break;
+
+ case kWhatInputDataRequested:
+ {
+ postFillBuffer();
+ ++mNumOutstandingInputBuffers;
+ break;
+ }
+
+ case kWhatInputBufferFilled:
+ {
+ CHECK_GT(mNumOutstandingInputBuffers, 0);
+ --mNumOutstandingInputBuffers;
+
+ if (mFlushing) {
+ mSource->queueEOS(INFO_DISCONTINUITY);
+
+ completeFlushIfPossible();
+ break;
+ }
+
+ sp<RefBase> obj;
+ if (!msg->findObject("buffer", &obj)) {
+ int32_t err = OK;
+ CHECK(msg->findInt32("err", &err));
+
+ mSource->queueEOS(err);
+ break;
+ }
+
+ sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+
+ mSource->queueBuffer(buffer);
+ break;
+ }
+
+ case kWhatFillBufferDone:
+ {
+ CHECK_GT(mNumPendingDecodes, 0);
+ --mNumPendingDecodes;
+
+ if (mFlushing) {
+ completeFlushIfPossible();
+ break;
+ }
+
+ sp<AMessage> notify;
+ CHECK(msg->findMessage("real-notify", &notify));
+
+ sp<AMessage> reply =
+ new AMessage(kWhatOutputBufferDrained, id());
+
+ notify->setMessage("reply", reply);
+ notify->post();
+
+ ++mNumOutstandingOutputBuffers;
+ break;
+ }
+
+ case kWhatOutputBufferDrained:
+ {
+ CHECK_GT(mNumOutstandingOutputBuffers, 0);
+ --mNumOutstandingOutputBuffers;
+
+ if (mFlushing) {
+ completeFlushIfPossible();
+ break;
+ }
+
+ ++mNumPendingDecodes;
+ mReader->readMore();
+ break;
+ }
+
+ case kWhatFlush:
+ {
+ onFlush();
+ break;
+ }
+
+ case kWhatResume:
+ {
+ onResume();
+ break;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+}
+
+void DecoderWrapper::onSetup(const sp<AMessage> &msg) {
+ AString mime;
+ CHECK(msg->findString("mime", &mime));
+
+ CHECK(!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC));
+
+ int32_t numChannels, sampleRate;
+ CHECK(msg->findInt32("channel-count", &numChannels));
+ CHECK(msg->findInt32("sample-rate", &sampleRate));
+
+ sp<RefBase> obj;
+ CHECK(msg->findObject("esds", &obj));
+ sp<ABuffer> esds = static_cast<ABuffer *>(obj.get());
+
+ sp<MetaData> meta = new MetaData;
+ meta->setCString(kKeyMIMEType, mime.c_str());
+ meta->setInt32(kKeySampleRate, sampleRate);
+ meta->setInt32(kKeyChannelCount, numChannels);
+ meta->setData(kKeyESDS, 0, esds->data(), esds->size());
+
+ mSource = new WrapperSource(
+ meta, new AMessage(kWhatInputDataRequested, id()));
+
+ sp<MediaSource> decoder = new AACDecoder(mSource);
+
+ mReaderLooper = new ALooper;
+ mReaderLooper->setName("DecoderWrapper looper");
+
+ mReaderLooper->start(
+ false, /* runOnCallingThread */
+ false, /* canCallJava */
+ PRIORITY_AUDIO);
+
+ sp<AMessage> notify = new AMessage(kWhatFillBufferDone, id());
+ notify->setMessage("real-notify", mNotify);
+
+ mReader = new WrapperReader(decoder, notify);
+ mReaderLooper->registerHandler(mReader);
+
+ mReader->start();
+ ++mNumPendingDecodes;
+}
+
+void DecoderWrapper::postFillBuffer() {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatFillThisBuffer);
+ sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id());
+ notify->setMessage("reply", reply);
+ notify->post();
+}
+
+void DecoderWrapper::onFlush() {
+ CHECK(!mFlushing);
+ mFlushing = true;
+
+ completeFlushIfPossible();
+}
+
+void DecoderWrapper::completeFlushIfPossible() {
+ CHECK(mFlushing);
+
+ if (mNumOutstandingInputBuffers > 0
+ || mNumOutstandingOutputBuffers > 0
+ || mNumPendingDecodes > 0) {
+ return;
+ }
+
+ mFlushing = false;
+
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatFlushCompleted);
+ notify->post();
+}
+
+void DecoderWrapper::onResume() {
+ CHECK(!mFlushing);
+
+ ++mNumPendingDecodes;
+
+ mSource->clear();
+ mReader->readMore(true /* flush */);
+}
+
+} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/DecoderWrapper.h b/media/libmediaplayerservice/nuplayer/DecoderWrapper.h
new file mode 100644
index 0000000..883b356
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/DecoderWrapper.h
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#ifndef DECODER_WRAPPER_H_
+
+#define DECODER_WRAPPER_H_
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct MediaSource;
+
+struct DecoderWrapper : public AHandler {
+ DecoderWrapper();
+
+ void setNotificationMessage(const sp<AMessage> &msg);
+ void initiateSetup(const sp<AMessage> &msg);
+ void initiateShutdown();
+ void signalFlush();
+ void signalResume();
+
+protected:
+ virtual ~DecoderWrapper();
+
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+ struct WrapperSource;
+ struct WrapperReader;
+
+ enum {
+ kWhatSetup,
+ kWhatInputBufferFilled,
+ kWhatOutputBufferDrained,
+ kWhatShutdown,
+ kWhatFillBufferDone,
+ kWhatInputDataRequested,
+ kWhatFlush,
+ kWhatResume,
+ };
+
+ sp<AMessage> mNotify;
+
+ sp<WrapperSource> mSource;
+
+ sp<ALooper> mReaderLooper;
+ sp<WrapperReader> mReader;
+
+ int32_t mNumOutstandingInputBuffers;
+ int32_t mNumOutstandingOutputBuffers;
+ int32_t mNumPendingDecodes;
+ bool mFlushing;
+
+ void onSetup(const sp<AMessage> &msg);
+ void onFlush();
+ void onResume();
+
+ void postFillBuffer();
+ void completeFlushIfPossible();
+
+ DISALLOW_EVIL_CONSTRUCTORS(DecoderWrapper);
+};
+
+} // namespace android
+
+#endif // DECODER_WRAPPER_H_
+
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
new file mode 100644
index 0000000..403029a
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -0,0 +1,477 @@
+/*
+ * 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 "NuPlayer"
+#include <utils/Log.h>
+
+#include "NuPlayer.h"
+#include "NuPlayerDecoder.h"
+#include "NuPlayerRenderer.h"
+#include "NuPlayerStreamListener.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/ACodec.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <surfaceflinger/Surface.h>
+
+namespace android {
+
+////////////////////////////////////////////////////////////////////////////////
+
+NuPlayer::NuPlayer()
+ : mEOS(false),
+ mAudioEOS(false),
+ mVideoEOS(false),
+ mFlushingAudio(NONE),
+ mFlushingVideo(NONE) {
+}
+
+NuPlayer::~NuPlayer() {
+}
+
+void NuPlayer::setListener(const wp<MediaPlayerBase> &listener) {
+ mListener = listener;
+}
+
+void NuPlayer::setDataSource(const sp<IStreamSource> &source) {
+ sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
+
+ source->incStrong(this);
+ msg->setPointer("source", source.get()); // XXX unsafe.
+
+ msg->post();
+}
+
+void NuPlayer::setVideoSurface(const sp<Surface> &surface) {
+ sp<AMessage> msg = new AMessage(kWhatSetVideoSurface, id());
+ msg->setObject("surface", surface);
+ msg->post();
+}
+
+void NuPlayer::setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink) {
+ sp<AMessage> msg = new AMessage(kWhatSetAudioSink, id());
+ msg->setObject("sink", sink);
+ msg->post();
+}
+
+void NuPlayer::start() {
+ (new AMessage(kWhatStart, id()))->post();
+}
+
+void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatSetDataSource:
+ {
+ LOGI("kWhatSetDataSource");
+
+ CHECK(mSource == NULL);
+
+ void *ptr;
+ CHECK(msg->findPointer("source", &ptr));
+
+ mSource = static_cast<IStreamSource *>(ptr);
+ mSource->decStrong(this);
+
+ mStreamListener = new NuPlayerStreamListener(mSource, id());
+ mTSParser = new ATSParser;
+ break;
+ }
+
+ case kWhatSetVideoSurface:
+ {
+ LOGI("kWhatSetVideoSurface");
+
+ sp<RefBase> obj;
+ CHECK(msg->findObject("surface", &obj));
+
+ mSurface = static_cast<Surface *>(obj.get());
+ break;
+ }
+
+ case kWhatSetAudioSink:
+ {
+ LOGI("kWhatSetAudioSink");
+
+ sp<RefBase> obj;
+ CHECK(msg->findObject("sink", &obj));
+
+ mAudioSink = static_cast<MediaPlayerBase::AudioSink *>(obj.get());
+ break;
+ }
+
+ case kWhatStart:
+ {
+ mStreamListener->start();
+
+ mRenderer = new Renderer(
+ mAudioSink,
+ new AMessage(kWhatRendererNotify, id()));
+
+ looper()->registerHandler(mRenderer);
+
+ (new AMessage(kWhatScanSources, id()))->post();
+ break;
+ }
+
+ case kWhatScanSources:
+ {
+ instantiateDecoder(false, &mVideoDecoder);
+
+ if (mAudioSink != NULL) {
+ instantiateDecoder(true, &mAudioDecoder);
+ }
+
+ if (mEOS) {
+ break;
+ }
+
+ feedMoreTSData();
+
+ if (mAudioDecoder == NULL || mVideoDecoder == NULL) {
+ msg->post(100000ll);
+ }
+ break;
+ }
+
+ case kWhatVideoNotify:
+ case kWhatAudioNotify:
+ {
+ bool audio = msg->what() == kWhatAudioNotify;
+
+ sp<AMessage> codecRequest;
+ CHECK(msg->findMessage("codec-request", &codecRequest));
+
+ int32_t what;
+ CHECK(codecRequest->findInt32("what", &what));
+
+ if (what == ACodec::kWhatFillThisBuffer) {
+ status_t err = feedDecoderInputData(
+ audio, codecRequest);
+
+ if (err == -EWOULDBLOCK && !mEOS) {
+ feedMoreTSData();
+ msg->post();
+ }
+ } else if (what == ACodec::kWhatEOS) {
+ mRenderer->queueEOS(audio, ERROR_END_OF_STREAM);
+ } else if (what == ACodec::kWhatFlushCompleted) {
+ if (audio) {
+ CHECK_EQ((int)mFlushingAudio, (int)FLUSHING_DECODER);
+ mFlushingAudio = FLUSHED;
+ } else {
+ CHECK_EQ((int)mFlushingVideo, (int)FLUSHING_DECODER);
+ mFlushingVideo = FLUSHED;
+ }
+
+ LOGI("decoder %s flush completed", audio ? "audio" : "video");
+
+ if (mFlushingAudio == FLUSHED && mFlushingVideo == FLUSHED) {
+ LOGI("both audio and video are flushed now.");
+
+ mRenderer->signalTimeDiscontinuity();
+
+ if (mAudioDecoder != NULL) {
+ mAudioDecoder->signalResume();
+ }
+
+ if (mVideoDecoder != NULL) {
+ mVideoDecoder->signalResume();
+ }
+
+ mFlushingAudio = NONE;
+ mFlushingVideo = NONE;
+ }
+ } else {
+ CHECK_EQ((int)what, (int)ACodec::kWhatDrainThisBuffer);
+
+ renderBuffer(audio, codecRequest);
+ }
+
+ break;
+ }
+
+ case kWhatRendererNotify:
+ {
+ int32_t what;
+ CHECK(msg->findInt32("what", &what));
+
+ if (what == Renderer::kWhatEOS) {
+ int32_t audio;
+ CHECK(msg->findInt32("audio", &audio));
+
+ if (audio) {
+ mAudioEOS = true;
+ } else {
+ mVideoEOS = true;
+ }
+
+ LOGI("reached %s EOS", audio ? "audio" : "video");
+
+ if ((mAudioEOS || mAudioDecoder == NULL)
+ && (mVideoEOS || mVideoDecoder == NULL)) {
+ notifyListener(MEDIA_PLAYBACK_COMPLETE, 0, 0);
+ }
+ } else {
+ CHECK_EQ(what, (int32_t)Renderer::kWhatFlushComplete);
+
+ int32_t audio;
+ CHECK(msg->findInt32("audio", &audio));
+
+ LOGI("renderer %s flush completed.", audio ? "audio" : "video");
+ }
+ break;
+ }
+
+ case kWhatMoreDataQueued:
+ {
+ break;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+}
+
+void NuPlayer::feedMoreTSData() {
+ CHECK(!mEOS);
+
+ for (int32_t i = 0; i < 10; ++i) {
+ char buffer[188];
+ ssize_t n = mStreamListener->read(buffer, sizeof(buffer));
+
+ if (n == 0) {
+ LOGI("input data EOS reached.");
+ mTSParser->signalEOS(ERROR_END_OF_STREAM);
+ mEOS = true;
+ break;
+ } else if (n == INFO_DISCONTINUITY) {
+ mTSParser->signalDiscontinuity(ATSParser::DISCONTINUITY_SEEK);
+ } else if (n < 0) {
+ CHECK_EQ(n, -EWOULDBLOCK);
+ break;
+ } else {
+ if (buffer[0] == 0x00) {
+ // XXX legacy
+ mTSParser->signalDiscontinuity(ATSParser::DISCONTINUITY_SEEK);
+ } else {
+ mTSParser->feedTSPacket(buffer, sizeof(buffer));
+ }
+ }
+ }
+}
+
+status_t NuPlayer::dequeueNextAccessUnit(
+ ATSParser::SourceType *type, sp<ABuffer> *accessUnit) {
+ accessUnit->clear();
+
+ status_t audioErr = -EWOULDBLOCK;
+ int64_t audioTimeUs;
+
+ sp<AnotherPacketSource> audioSource =
+ static_cast<AnotherPacketSource *>(
+ mTSParser->getSource(ATSParser::MPEG2ADTS_AUDIO).get());
+
+ if (audioSource != NULL) {
+ audioErr = audioSource->nextBufferTime(&audioTimeUs);
+ }
+
+ status_t videoErr = -EWOULDBLOCK;
+ int64_t videoTimeUs;
+
+ sp<AnotherPacketSource> videoSource =
+ static_cast<AnotherPacketSource *>(
+ mTSParser->getSource(ATSParser::AVC_VIDEO).get());
+
+ if (videoSource != NULL) {
+ videoErr = videoSource->nextBufferTime(&videoTimeUs);
+ }
+
+ if (audioErr == -EWOULDBLOCK || videoErr == -EWOULDBLOCK) {
+ return -EWOULDBLOCK;
+ }
+
+ if (audioErr != OK && videoErr != OK) {
+ return audioErr;
+ }
+
+ if (videoErr != OK || (audioErr == OK && audioTimeUs < videoTimeUs)) {
+ *type = ATSParser::MPEG2ADTS_AUDIO;
+ return audioSource->dequeueAccessUnit(accessUnit);
+ } else {
+ *type = ATSParser::AVC_VIDEO;
+ return videoSource->dequeueAccessUnit(accessUnit);
+ }
+}
+
+status_t NuPlayer::dequeueAccessUnit(
+ ATSParser::SourceType type, sp<ABuffer> *accessUnit) {
+ sp<AnotherPacketSource> source =
+ static_cast<AnotherPacketSource *>(mTSParser->getSource(type).get());
+
+ if (source == NULL) {
+ return -EWOULDBLOCK;
+ }
+
+ status_t finalResult;
+ if (!source->hasBufferAvailable(&finalResult)) {
+ return finalResult == OK ? -EWOULDBLOCK : finalResult;
+ }
+
+ return source->dequeueAccessUnit(accessUnit);
+}
+
+status_t NuPlayer::instantiateDecoder(
+ bool audio, sp<Decoder> *decoder) {
+ if (*decoder != NULL) {
+ return OK;
+ }
+
+ ATSParser::SourceType type =
+ audio ? ATSParser::MPEG2ADTS_AUDIO : ATSParser::AVC_VIDEO;
+
+ sp<AnotherPacketSource> source =
+ static_cast<AnotherPacketSource *>(
+ mTSParser->getSource(type).get());
+
+ if (source == NULL) {
+ return -EWOULDBLOCK;
+ }
+
+ sp<AMessage> notify =
+ new AMessage(audio ? kWhatAudioNotify : kWhatVideoNotify,
+ id());
+
+ *decoder = new Decoder(notify, audio ? NULL : mSurface);
+ looper()->registerHandler(*decoder);
+
+ const sp<MetaData> &meta = source->getFormat();
+ (*decoder)->configure(meta);
+
+ if (audio) {
+ int32_t sampleRate;
+ int32_t channelCount;
+ CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+ CHECK(meta->findInt32(kKeyChannelCount, &channelCount));
+
+ channelCount = 2; // XXX
+
+ CHECK_EQ(mAudioSink->open(sampleRate, channelCount), (status_t)OK);
+ mAudioSink->start();
+ }
+
+ return OK;
+}
+
+status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) {
+ sp<AMessage> reply;
+ CHECK(msg->findMessage("reply", &reply));
+
+ if ((audio && mFlushingAudio == FLUSHING_DECODER)
+ || (!audio && mFlushingVideo == FLUSHING_DECODER)) {
+ reply->setInt32("err", INFO_DISCONTINUITY);
+ reply->post();
+ return OK;
+ }
+
+ sp<ABuffer> accessUnit;
+ status_t err = dequeueAccessUnit(
+ audio ? ATSParser::MPEG2ADTS_AUDIO : ATSParser::AVC_VIDEO,
+ &accessUnit);
+
+ if (err == -EWOULDBLOCK) {
+ return err;
+ } else if (err != OK) {
+ if (err == INFO_DISCONTINUITY) {
+ LOGI("%s discontinuity", audio ? "audio" : "video");
+ (audio ? mAudioDecoder : mVideoDecoder)->signalFlush();
+ mRenderer->flush(audio);
+
+ if (audio) {
+ CHECK(mFlushingAudio == NONE
+ || mFlushingAudio == AWAITING_DISCONTINUITY);
+ mFlushingAudio = FLUSHING_DECODER;
+ if (mFlushingVideo == NONE) {
+ mFlushingVideo = (mVideoDecoder != NULL)
+ ? AWAITING_DISCONTINUITY
+ : FLUSHED;
+ }
+ } else {
+ CHECK(mFlushingVideo == NONE
+ || mFlushingVideo == AWAITING_DISCONTINUITY);
+ mFlushingVideo = FLUSHING_DECODER;
+ if (mFlushingAudio == NONE) {
+ mFlushingAudio = (mAudioDecoder != NULL)
+ ? AWAITING_DISCONTINUITY
+ : FLUSHED;
+ }
+ }
+ }
+
+ reply->setInt32("err", err);
+ reply->post();
+ return OK;
+ }
+
+ LOGV("returned a valid buffer of %s data", audio ? "audio" : "video");
+
+#if 0
+ int64_t mediaTimeUs;
+ CHECK(accessUnit->meta()->findInt64("timeUs", &mediaTimeUs));
+ LOGI("feeding %s input buffer at media time %.2f secs",
+ audio ? "audio" : "video",
+ mediaTimeUs / 1E6);
+#endif
+
+ reply->setObject("buffer", accessUnit);
+ reply->post();
+
+ return OK;
+}
+
+void NuPlayer::renderBuffer(bool audio, const sp<AMessage> &msg) {
+ LOGV("renderBuffer %s", audio ? "audio" : "video");
+
+ sp<AMessage> reply;
+ CHECK(msg->findMessage("reply", &reply));
+
+ sp<RefBase> obj;
+ CHECK(msg->findObject("buffer", &obj));
+
+ sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+
+ mRenderer->queueBuffer(audio, buffer, reply);
+}
+
+void NuPlayer::notifyListener(int msg, int ext1, int ext2) {
+ if (mListener == NULL) {
+ return;
+ }
+
+ sp<MediaPlayerBase> listener = mListener.promote();
+
+ if (listener == NULL) {
+ return;
+ }
+
+ listener->sendEvent(msg, ext1, ext2);
+}
+
+} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
new file mode 100644
index 0000000..9a5a6c4
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+#ifndef NU_PLAYER_H_
+
+#define NU_PLAYER_H_
+
+#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/foundation/AHandler.h>
+
+#include "ATSParser.h"
+#include "AnotherPacketSource.h"
+
+namespace android {
+
+struct ACodec;
+struct MetaData;
+
+struct NuPlayer : public AHandler {
+ NuPlayer();
+
+ void setListener(const wp<MediaPlayerBase> &listener);
+
+ void setDataSource(const sp<IStreamSource> &source);
+ void setVideoSurface(const sp<Surface> &surface);
+ void setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink);
+ void start();
+
+protected:
+ virtual ~NuPlayer();
+
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+ struct Renderer;
+ struct Decoder;
+ struct NuPlayerStreamListener;
+
+ enum {
+ kWhatSetDataSource,
+ kWhatSetVideoSurface,
+ kWhatSetAudioSink,
+ kWhatMoreDataQueued,
+ kWhatStart,
+ kWhatScanSources,
+ kWhatVideoNotify,
+ kWhatAudioNotify,
+ kWhatRendererNotify,
+ };
+
+ wp<MediaPlayerBase> mListener;
+ sp<IStreamSource> mSource;
+ sp<Surface> mSurface;
+ sp<MediaPlayerBase::AudioSink> mAudioSink;
+ sp<NuPlayerStreamListener> mStreamListener;
+ sp<ATSParser> mTSParser;
+ sp<Decoder> mVideoDecoder;
+ sp<Decoder> mAudioDecoder;
+ sp<Renderer> mRenderer;
+
+ bool mEOS;
+ bool mAudioEOS;
+ bool mVideoEOS;
+
+ enum FlushStatus {
+ NONE,
+ AWAITING_DISCONTINUITY,
+ FLUSHING_DECODER,
+ FLUSHED
+ };
+
+ FlushStatus mFlushingAudio;
+ FlushStatus mFlushingVideo;
+
+ status_t instantiateDecoder(
+ bool audio, sp<Decoder> *decoder);
+
+ status_t feedDecoderInputData(bool audio, const sp<AMessage> &msg);
+ void renderBuffer(bool audio, const sp<AMessage> &msg);
+
+ status_t dequeueNextAccessUnit(
+ ATSParser::SourceType *type, sp<ABuffer> *accessUnit);
+
+ status_t dequeueAccessUnit(
+ ATSParser::SourceType type, sp<ABuffer> *accessUnit);
+
+ void feedMoreTSData();
+ void notifyListener(int msg, int ext1, int ext2);
+
+ DISALLOW_EVIL_CONSTRUCTORS(NuPlayer);
+};
+
+} // namespace android
+
+#endif // NU_PLAYER_H_
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
new file mode 100644
index 0000000..d1ed222
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -0,0 +1,286 @@
+/*
+ * 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 "NuPlayerDecoder"
+#include <utils/Log.h>
+
+#include "NuPlayerDecoder.h"
+
+#include "DecoderWrapper.h"
+#include "ESDS.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/ACodec.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+#include <surfaceflinger/Surface.h>
+
+namespace android {
+
+NuPlayer::Decoder::Decoder(
+ const sp<AMessage> &notify, const sp<Surface> &surface)
+ : mNotify(notify),
+ mSurface(surface) {
+}
+
+NuPlayer::Decoder::~Decoder() {
+}
+
+void NuPlayer::Decoder::configure(const sp<MetaData> &meta) {
+ CHECK(mCodec == NULL);
+ CHECK(mWrapper == NULL);
+
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ sp<AMessage> notifyMsg =
+ new AMessage(kWhatCodecNotify, id());
+
+ sp<AMessage> format = makeFormat(meta);
+
+ if (mSurface != NULL) {
+ format->setObject("surface", mSurface);
+ }
+
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+ mWrapper = new DecoderWrapper;
+ looper()->registerHandler(mWrapper);
+
+ mWrapper->setNotificationMessage(notifyMsg);
+ mWrapper->initiateSetup(format);
+ } else {
+ mCodec = new ACodec;
+ looper()->registerHandler(mCodec);
+
+ mCodec->setNotificationMessage(notifyMsg);
+ mCodec->initiateSetup(format);
+ }
+}
+
+void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatCodecNotify:
+ {
+ int32_t what;
+ CHECK(msg->findInt32("what", &what));
+
+ if (what == ACodec::kWhatFillThisBuffer) {
+ onFillThisBuffer(msg);
+ } else {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setMessage("codec-request", msg);
+ notify->post();
+ }
+ break;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+}
+
+sp<AMessage> NuPlayer::Decoder::makeFormat(const sp<MetaData> &meta) {
+ CHECK(mCSD.isEmpty());
+
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ sp<AMessage> msg = new AMessage;
+ msg->setString("mime", mime);
+
+ if (!strncasecmp("video/", mime, 6)) {
+ int32_t width, height;
+ CHECK(meta->findInt32(kKeyWidth, &width));
+ CHECK(meta->findInt32(kKeyHeight, &height));
+
+ msg->setInt32("width", width);
+ msg->setInt32("height", height);
+ } else {
+ CHECK(!strncasecmp("audio/", mime, 6));
+
+ int32_t numChannels, sampleRate;
+ CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
+ CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+
+ msg->setInt32("channel-count", numChannels);
+ msg->setInt32("sample-rate", sampleRate);
+ }
+
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (meta->findData(kKeyAVCC, &type, &data, &size)) {
+ // Parse the AVCDecoderConfigurationRecord
+
+ const uint8_t *ptr = (const uint8_t *)data;
+
+ CHECK(size >= 7);
+ CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1
+ uint8_t profile = ptr[1];
+ uint8_t level = ptr[3];
+
+ // There is decodable content out there that fails the following
+ // assertion, let's be lenient for now...
+ // CHECK((ptr[4] >> 2) == 0x3f); // reserved
+
+ size_t lengthSize = 1 + (ptr[4] & 3);
+
+ // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
+ // violates it...
+ // CHECK((ptr[5] >> 5) == 7); // reserved
+
+ size_t numSeqParameterSets = ptr[5] & 31;
+
+ ptr += 6;
+ size -= 6;
+
+ sp<ABuffer> buffer = new ABuffer(1024);
+ buffer->setRange(0, 0);
+
+ for (size_t i = 0; i < numSeqParameterSets; ++i) {
+ CHECK(size >= 2);
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ CHECK(size >= length);
+
+ memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+ memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+ buffer->setRange(0, buffer->size() + 4 + length);
+
+ ptr += length;
+ size -= length;
+ }
+
+ buffer->meta()->setInt32("csd", true);
+ mCSD.push(buffer);
+
+ buffer = new ABuffer(1024);
+ buffer->setRange(0, 0);
+
+ CHECK(size >= 1);
+ size_t numPictureParameterSets = *ptr;
+ ++ptr;
+ --size;
+
+ for (size_t i = 0; i < numPictureParameterSets; ++i) {
+ CHECK(size >= 2);
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ CHECK(size >= length);
+
+ memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+ memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+ buffer->setRange(0, buffer->size() + 4 + length);
+
+ ptr += length;
+ size -= length;
+ }
+
+ buffer->meta()->setInt32("csd", true);
+ mCSD.push(buffer);
+
+ msg->setObject("csd", buffer);
+ } else if (meta->findData(kKeyESDS, &type, &data, &size)) {
+#if 0
+ ESDS esds((const char *)data, size);
+ CHECK_EQ(esds.InitCheck(), (status_t)OK);
+
+ const void *codec_specific_data;
+ size_t codec_specific_data_size;
+ esds.getCodecSpecificInfo(
+ &codec_specific_data, &codec_specific_data_size);
+
+ sp<ABuffer> buffer = new ABuffer(codec_specific_data_size);
+
+ memcpy(buffer->data(), codec_specific_data,
+ codec_specific_data_size);
+
+ buffer->meta()->setInt32("csd", true);
+ mCSD.push(buffer);
+#else
+ sp<ABuffer> buffer = new ABuffer(size);
+ memcpy(buffer->data(), data, size);
+
+ msg->setObject("esds", buffer);
+#endif
+ }
+
+ int32_t maxInputSize;
+ if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) {
+ msg->setInt32("max-input-size", maxInputSize);
+ }
+
+ mCSDIndex = 0;
+
+ return msg;
+}
+
+void NuPlayer::Decoder::onFillThisBuffer(const sp<AMessage> &msg) {
+ sp<AMessage> reply;
+ CHECK(msg->findMessage("reply", &reply));
+
+#if 0
+ sp<RefBase> obj;
+ CHECK(msg->findObject("buffer", &obj));
+ sp<ABuffer> outBuffer = static_cast<ABuffer *>(obj.get());
+#else
+ sp<ABuffer> outBuffer;
+#endif
+
+ if (mCSDIndex < mCSD.size()) {
+ outBuffer = mCSD.editItemAt(mCSDIndex++);
+ outBuffer->meta()->setInt64("timeUs", 0);
+
+ reply->setObject("buffer", outBuffer);
+ reply->post();
+ return;
+ }
+
+ sp<AMessage> notify = mNotify->dup();
+ notify->setMessage("codec-request", msg);
+ notify->post();
+}
+
+void NuPlayer::Decoder::signalFlush() {
+ if (mCodec != NULL) {
+ mCodec->signalFlush();
+ } else {
+ CHECK(mWrapper != NULL);
+ mWrapper->signalFlush();
+ }
+}
+
+void NuPlayer::Decoder::signalResume() {
+ if (mCodec != NULL) {
+ mCodec->signalResume();
+ } else {
+ CHECK(mWrapper != NULL);
+ mWrapper->signalResume();
+ }
+}
+
+} // namespace android
+
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
new file mode 100644
index 0000000..77800be
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#ifndef NUPLAYER_DECODER_H_
+
+#define NUPLAYER_DECODER_H_
+
+#include "NuPlayer.h"
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct DecoderWrapper;
+
+struct NuPlayer::Decoder : public AHandler {
+ Decoder(const sp<AMessage> &notify, const sp<Surface> &surface = NULL);
+
+ void configure(const sp<MetaData> &meta);
+ void signalFlush();
+ void signalResume();
+
+protected:
+ virtual ~Decoder();
+
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+ enum {
+ kWhatCodecNotify,
+ };
+
+ sp<AMessage> mNotify;
+ sp<Surface> mSurface;
+
+ sp<ACodec> mCodec;
+ sp<DecoderWrapper> mWrapper;
+
+ Vector<sp<ABuffer> > mCSD;
+ size_t mCSDIndex;
+
+ sp<AMessage> makeFormat(const sp<MetaData> &meta);
+
+ void onFillThisBuffer(const sp<AMessage> &msg);
+
+ DISALLOW_EVIL_CONSTRUCTORS(Decoder);
+};
+
+} // namespace android
+
+#endif // NUPLAYER_DECODER_H_
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
new file mode 100644
index 0000000..b79251a
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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 "NuPlayerDriver"
+#include <utils/Log.h>
+
+#include "NuPlayerDriver.h"
+
+#include "NuPlayer.h"
+
+#include <media/stagefright/foundation/ALooper.h>
+
+namespace android {
+
+NuPlayerDriver::NuPlayerDriver()
+ : mLooper(new ALooper) {
+ mLooper->setName("NuPlayerDriver Looper");
+
+ mLooper->start(
+ false, /* runOnCallingThread */
+ true, /* canCallJava */
+ PRIORITY_AUDIO);
+
+ mPlayer = new NuPlayer;
+ mLooper->registerHandler(mPlayer);
+
+ mPlayer->setListener(this);
+}
+
+NuPlayerDriver::~NuPlayerDriver() {
+ mLooper->stop();
+}
+
+status_t NuPlayerDriver::initCheck() {
+ return OK;
+}
+
+status_t NuPlayerDriver::setDataSource(
+ const char *url, const KeyedVector<String8, String8> *headers) {
+ return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
+ return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::setDataSource(const sp<IStreamSource> &source) {
+ mPlayer->setDataSource(source);
+
+ return OK;
+}
+
+status_t NuPlayerDriver::setVideoSurface(const sp<Surface> &surface) {
+ mPlayer->setVideoSurface(surface);
+
+ return OK;
+}
+
+status_t NuPlayerDriver::prepare() {
+ return OK;
+}
+
+status_t NuPlayerDriver::prepareAsync() {
+ return OK;
+}
+
+status_t NuPlayerDriver::start() {
+ mPlayer->start();
+
+ return OK;
+}
+
+status_t NuPlayerDriver::stop() {
+ return OK;
+}
+
+status_t NuPlayerDriver::pause() {
+ return OK;
+}
+
+bool NuPlayerDriver::isPlaying() {
+ return false;
+}
+
+status_t NuPlayerDriver::seekTo(int msec) {
+ return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::getCurrentPosition(int *msec) {
+ return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::getDuration(int *msec) {
+ return INVALID_OPERATION;
+}
+
+status_t NuPlayerDriver::reset() {
+ return OK;
+}
+
+status_t NuPlayerDriver::setLooping(int loop) {
+ return INVALID_OPERATION;
+}
+
+player_type NuPlayerDriver::playerType() {
+ return NU_PLAYER;
+}
+
+status_t NuPlayerDriver::invoke(const Parcel &request, Parcel *reply) {
+ return INVALID_OPERATION;
+}
+
+void NuPlayerDriver::setAudioSink(const sp<AudioSink> &audioSink) {
+ mPlayer->setAudioSink(audioSink);
+}
+
+status_t NuPlayerDriver::getMetadata(
+ const media::Metadata::Filter& ids, Parcel *records) {
+ return INVALID_OPERATION;
+}
+
+} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
new file mode 100644
index 0000000..245f1dd
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#include <media/MediaPlayerInterface.h>
+
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+struct ALooper;
+struct NuPlayer;
+
+struct NuPlayerDriver : public MediaPlayerInterface {
+ NuPlayerDriver();
+
+ virtual status_t initCheck();
+
+ virtual status_t setDataSource(
+ const char *url, const KeyedVector<String8, String8> *headers);
+
+ virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
+
+ virtual status_t setDataSource(const sp<IStreamSource> &source);
+
+ virtual status_t setVideoSurface(const sp<Surface> &surface);
+ virtual status_t prepare();
+ virtual status_t prepareAsync();
+ virtual status_t start();
+ virtual status_t stop();
+ virtual status_t pause();
+ virtual bool isPlaying();
+ virtual status_t seekTo(int msec);
+ virtual status_t getCurrentPosition(int *msec);
+ virtual status_t getDuration(int *msec);
+ virtual status_t reset();
+ virtual status_t setLooping(int loop);
+ virtual player_type playerType();
+ virtual status_t invoke(const Parcel &request, Parcel *reply);
+ virtual void setAudioSink(const sp<AudioSink> &audioSink);
+
+ virtual status_t getMetadata(
+ const media::Metadata::Filter& ids, Parcel *records);
+
+protected:
+ virtual ~NuPlayerDriver();
+
+private:
+ sp<ALooper> mLooper;
+ sp<NuPlayer> mPlayer;
+
+ DISALLOW_EVIL_CONSTRUCTORS(NuPlayerDriver);
+};
+
+} // namespace android
+
+
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
new file mode 100644
index 0000000..855bc0a
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -0,0 +1,496 @@
+/*
+ * 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 "NuPlayerRenderer"
+#include <utils/Log.h>
+
+#include "NuPlayerRenderer.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+NuPlayer::Renderer::Renderer(
+ const sp<MediaPlayerBase::AudioSink> &sink,
+ const sp<AMessage> &notify)
+ : mAudioSink(sink),
+ mNotify(notify),
+ mNumFramesWritten(0),
+ mDrainAudioQueuePending(false),
+ mDrainVideoQueuePending(false),
+ mAudioQueueGeneration(0),
+ mVideoQueueGeneration(0),
+ mAnchorTimeMediaUs(-1),
+ mAnchorTimeRealUs(-1),
+ mFlushingAudio(false),
+ mFlushingVideo(false),
+ mSyncQueues(true) {
+}
+
+NuPlayer::Renderer::~Renderer() {
+}
+
+void NuPlayer::Renderer::queueBuffer(
+ bool audio,
+ const sp<ABuffer> &buffer,
+ const sp<AMessage> &notifyConsumed) {
+ sp<AMessage> msg = new AMessage(kWhatQueueBuffer, id());
+ msg->setInt32("audio", static_cast<int32_t>(audio));
+ msg->setObject("buffer", buffer);
+ msg->setMessage("notifyConsumed", notifyConsumed);
+ msg->post();
+}
+
+void NuPlayer::Renderer::queueEOS(bool audio, status_t finalResult) {
+ CHECK_NE(finalResult, (status_t)OK);
+
+ sp<AMessage> msg = new AMessage(kWhatQueueEOS, id());
+ msg->setInt32("audio", static_cast<int32_t>(audio));
+ msg->setInt32("finalResult", finalResult);
+ msg->post();
+}
+
+void NuPlayer::Renderer::flush(bool audio) {
+ {
+ Mutex::Autolock autoLock(mFlushLock);
+ if (audio) {
+ CHECK(!mFlushingAudio);
+ mFlushingAudio = true;
+ } else {
+ CHECK(!mFlushingVideo);
+ mFlushingVideo = true;
+ }
+ }
+
+ sp<AMessage> msg = new AMessage(kWhatFlush, id());
+ msg->setInt32("audio", static_cast<int32_t>(audio));
+ msg->post();
+}
+
+void NuPlayer::Renderer::signalTimeDiscontinuity() {
+ CHECK(mAudioQueue.empty());
+ CHECK(mVideoQueue.empty());
+ mAnchorTimeMediaUs = -1;
+ mAnchorTimeRealUs = -1;
+ mSyncQueues = true;
+}
+
+void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatDrainAudioQueue:
+ {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+ if (generation != mAudioQueueGeneration) {
+ break;
+ }
+
+ mDrainAudioQueuePending = false;
+
+ onDrainAudioQueue();
+
+ postDrainAudioQueue();
+ break;
+ }
+
+ case kWhatDrainVideoQueue:
+ {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+ if (generation != mVideoQueueGeneration) {
+ break;
+ }
+
+ mDrainVideoQueuePending = false;
+
+ onDrainVideoQueue();
+
+ postDrainVideoQueue();
+ break;
+ }
+
+ case kWhatQueueBuffer:
+ {
+ onQueueBuffer(msg);
+ break;
+ }
+
+ case kWhatQueueEOS:
+ {
+ onQueueEOS(msg);
+ break;
+ }
+
+ case kWhatFlush:
+ {
+ onFlush(msg);
+ break;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+}
+
+void NuPlayer::Renderer::postDrainAudioQueue() {
+ if (mDrainAudioQueuePending || mSyncQueues) {
+ return;
+ }
+
+ if (mAudioQueue.empty()) {
+ return;
+ }
+
+ mDrainAudioQueuePending = true;
+ sp<AMessage> msg = new AMessage(kWhatDrainAudioQueue, id());
+ msg->setInt32("generation", mAudioQueueGeneration);
+ msg->post(10000);
+}
+
+void NuPlayer::Renderer::onDrainAudioQueue() {
+ uint32_t numFramesPlayed;
+ CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
+
+ ssize_t numFramesAvailableToWrite =
+ mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed);
+
+ CHECK_GE(numFramesAvailableToWrite, 0);
+
+ size_t numBytesAvailableToWrite =
+ numFramesAvailableToWrite * mAudioSink->frameSize();
+
+ while (numBytesAvailableToWrite > 0) {
+ if (mAudioQueue.empty()) {
+ break;
+ }
+
+ QueueEntry *entry = &*mAudioQueue.begin();
+
+ if (entry->mBuffer == NULL) {
+ // EOS
+
+ notifyEOS(true /* audio */);
+
+ mAudioQueue.erase(mAudioQueue.begin());
+ entry = NULL;
+ return;
+ }
+
+ if (entry->mOffset == 0) {
+ int64_t mediaTimeUs;
+ CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
+
+ LOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6);
+
+ mAnchorTimeMediaUs = mediaTimeUs;
+
+ uint32_t numFramesPlayed;
+ CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
+
+ uint32_t numFramesPendingPlayout =
+ mNumFramesWritten - numFramesPlayed;
+
+ int64_t realTimeOffsetUs =
+ (mAudioSink->latency() / 2 /* XXX */
+ + numFramesPendingPlayout
+ * mAudioSink->msecsPerFrame()) * 1000ll;
+
+ // LOGI("realTimeOffsetUs = %lld us", realTimeOffsetUs);
+
+ mAnchorTimeRealUs =
+ ALooper::GetNowUs() + realTimeOffsetUs;
+ }
+
+ size_t copy = entry->mBuffer->size() - entry->mOffset;
+ if (copy > numBytesAvailableToWrite) {
+ copy = numBytesAvailableToWrite;
+ }
+
+ CHECK_EQ(mAudioSink->write(
+ entry->mBuffer->data() + entry->mOffset, copy),
+ (ssize_t)copy);
+
+ entry->mOffset += copy;
+ if (entry->mOffset == entry->mBuffer->size()) {
+ entry->mNotifyConsumed->post();
+ mAudioQueue.erase(mAudioQueue.begin());
+ entry = NULL;
+ }
+
+ numBytesAvailableToWrite -= copy;
+ mNumFramesWritten += copy / mAudioSink->frameSize();
+ }
+}
+
+void NuPlayer::Renderer::postDrainVideoQueue() {
+ if (mDrainVideoQueuePending || mSyncQueues) {
+ return;
+ }
+
+ if (mVideoQueue.empty()) {
+ return;
+ }
+
+ QueueEntry &entry = *mVideoQueue.begin();
+
+ sp<AMessage> msg = new AMessage(kWhatDrainVideoQueue, id());
+ msg->setInt32("generation", mVideoQueueGeneration);
+
+ int64_t delayUs;
+
+ if (entry.mBuffer == NULL) {
+ // EOS doesn't carry a timestamp.
+ delayUs = 0;
+ } else {
+ int64_t mediaTimeUs;
+ CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
+
+ if (mAnchorTimeMediaUs < 0) {
+ delayUs = 0;
+
+ if (mAudioSink == NULL) {
+ mAnchorTimeMediaUs = mediaTimeUs;
+ mAnchorTimeRealUs = ALooper::GetNowUs();
+ }
+ } else {
+ int64_t realTimeUs =
+ (mediaTimeUs - mAnchorTimeMediaUs) + mAnchorTimeRealUs;
+
+ delayUs = realTimeUs - ALooper::GetNowUs();
+ }
+ }
+
+ msg->post(delayUs);
+
+ mDrainVideoQueuePending = true;
+}
+
+void NuPlayer::Renderer::onDrainVideoQueue() {
+ if (mVideoQueue.empty()) {
+ return;
+ }
+
+ QueueEntry *entry = &*mVideoQueue.begin();
+
+ if (entry->mBuffer == NULL) {
+ // EOS
+
+ notifyEOS(false /* audio */);
+
+ mVideoQueue.erase(mVideoQueue.begin());
+ entry = NULL;
+ return;
+ }
+
+#if 0
+ int64_t mediaTimeUs;
+ CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
+
+ LOGI("rendering video at media time %.2f secs", mediaTimeUs / 1E6);
+#endif
+
+ entry->mNotifyConsumed->setInt32("render", true);
+ entry->mNotifyConsumed->post();
+ mVideoQueue.erase(mVideoQueue.begin());
+ entry = NULL;
+}
+
+void NuPlayer::Renderer::notifyEOS(bool audio) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatEOS);
+ notify->setInt32("audio", static_cast<int32_t>(audio));
+ notify->post();
+}
+
+void NuPlayer::Renderer::onQueueBuffer(const sp<AMessage> &msg) {
+ int32_t audio;
+ CHECK(msg->findInt32("audio", &audio));
+
+ if (dropBufferWhileFlushing(audio, msg)) {
+ return;
+ }
+
+ sp<RefBase> obj;
+ CHECK(msg->findObject("buffer", &obj));
+ sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+
+ sp<AMessage> notifyConsumed;
+ CHECK(msg->findMessage("notifyConsumed", &notifyConsumed));
+
+ QueueEntry entry;
+ entry.mBuffer = buffer;
+ entry.mNotifyConsumed = notifyConsumed;
+ entry.mOffset = 0;
+ entry.mFinalResult = OK;
+
+ if (audio) {
+ mAudioQueue.push_back(entry);
+ postDrainAudioQueue();
+ } else {
+ mVideoQueue.push_back(entry);
+ postDrainVideoQueue();
+ }
+
+ if (mSyncQueues && !mAudioQueue.empty() && !mVideoQueue.empty()) {
+ int64_t firstAudioTimeUs;
+ int64_t firstVideoTimeUs;
+ CHECK((*mAudioQueue.begin()).mBuffer->meta()
+ ->findInt64("timeUs", &firstAudioTimeUs));
+ CHECK((*mVideoQueue.begin()).mBuffer->meta()
+ ->findInt64("timeUs", &firstVideoTimeUs));
+
+ int64_t diff = firstVideoTimeUs - firstAudioTimeUs;
+
+ LOGV("queueDiff = %.2f secs", diff / 1E6);
+
+ if (diff > 100000ll) {
+ // Audio data starts More than 0.1 secs before video.
+ // Drop some audio.
+
+ (*mAudioQueue.begin()).mNotifyConsumed->post();
+ mAudioQueue.erase(mAudioQueue.begin());
+ return;
+ }
+
+ syncQueuesDone();
+ }
+}
+
+void NuPlayer::Renderer::syncQueuesDone() {
+ if (!mSyncQueues) {
+ return;
+ }
+
+ mSyncQueues = false;
+
+ if (!mAudioQueue.empty()) {
+ postDrainAudioQueue();
+ }
+
+ if (!mVideoQueue.empty()) {
+ postDrainVideoQueue();
+ }
+}
+
+void NuPlayer::Renderer::onQueueEOS(const sp<AMessage> &msg) {
+ int32_t audio;
+ CHECK(msg->findInt32("audio", &audio));
+
+ if (dropBufferWhileFlushing(audio, msg)) {
+ return;
+ }
+
+ int32_t finalResult;
+ CHECK(msg->findInt32("finalResult", &finalResult));
+
+ QueueEntry entry;
+ entry.mOffset = 0;
+ entry.mFinalResult = finalResult;
+
+ if (audio) {
+ mAudioQueue.push_back(entry);
+ postDrainAudioQueue();
+ } else {
+ mVideoQueue.push_back(entry);
+ postDrainVideoQueue();
+ }
+}
+
+void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) {
+ int32_t audio;
+ CHECK(msg->findInt32("audio", &audio));
+
+ // If we're currently syncing the queues, i.e. dropping audio while
+ // aligning the first audio/video buffer times and only one of the
+ // two queues has data, we may starve that queue by not requesting
+ // more buffers from the decoder. If the other source then encounters
+ // a discontinuity that leads to flushing, we'll never find the
+ // corresponding discontinuity on the other queue.
+ // Therefore we'll stop syncing the queues if at least one of them
+ // is flushed.
+ syncQueuesDone();
+
+ if (audio) {
+ flushQueue(&mAudioQueue);
+
+ Mutex::Autolock autoLock(mFlushLock);
+ mFlushingAudio = false;
+
+ mDrainAudioQueuePending = false;
+ ++mAudioQueueGeneration;
+ } else {
+ flushQueue(&mVideoQueue);
+
+ Mutex::Autolock autoLock(mFlushLock);
+ mFlushingVideo = false;
+
+ mDrainVideoQueuePending = false;
+ ++mVideoQueueGeneration;
+ }
+
+ notifyFlushComplete(audio);
+}
+
+void NuPlayer::Renderer::flushQueue(List<QueueEntry> *queue) {
+ while (!queue->empty()) {
+ QueueEntry *entry = &*queue->begin();
+
+ if (entry->mBuffer != NULL) {
+ entry->mNotifyConsumed->post();
+ }
+
+ queue->erase(queue->begin());
+ entry = NULL;
+ }
+}
+
+void NuPlayer::Renderer::notifyFlushComplete(bool audio) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatFlushComplete);
+ notify->setInt32("audio", static_cast<int32_t>(audio));
+ notify->post();
+}
+
+bool NuPlayer::Renderer::dropBufferWhileFlushing(
+ bool audio, const sp<AMessage> &msg) {
+ bool flushing = false;
+
+ {
+ Mutex::Autolock autoLock(mFlushLock);
+ if (audio) {
+ flushing = mFlushingAudio;
+ } else {
+ flushing = mFlushingVideo;
+ }
+ }
+
+ if (!flushing) {
+ return false;
+ }
+
+ sp<AMessage> notifyConsumed;
+ if (msg->findMessage("notifyConsumed", &notifyConsumed)) {
+ notifyConsumed->post();
+ }
+
+ return true;
+}
+
+} // namespace android
+
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
new file mode 100644
index 0000000..834ddc5
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+#ifndef NUPLAYER_RENDERER_H_
+
+#define NUPLAYER_RENDERER_H_
+
+#include "NuPlayer.h"
+
+namespace android {
+
+struct NuPlayer::Renderer : public AHandler {
+ Renderer(const sp<MediaPlayerBase::AudioSink> &sink,
+ const sp<AMessage> &notify);
+
+ void queueBuffer(
+ bool audio,
+ const sp<ABuffer> &buffer,
+ const sp<AMessage> &notifyConsumed);
+
+ void queueEOS(bool audio, status_t finalResult);
+
+ void flush(bool audio);
+
+ void signalTimeDiscontinuity();
+
+ enum {
+ kWhatEOS,
+ kWhatFlushComplete,
+ };
+
+protected:
+ virtual ~Renderer();
+
+ virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+ enum {
+ kWhatDrainAudioQueue,
+ kWhatDrainVideoQueue,
+ kWhatQueueBuffer,
+ kWhatQueueEOS,
+ kWhatFlush,
+ };
+
+ struct QueueEntry {
+ sp<ABuffer> mBuffer;
+ sp<AMessage> mNotifyConsumed;
+ size_t mOffset;
+ status_t mFinalResult;
+ };
+
+ sp<MediaPlayerBase::AudioSink> mAudioSink;
+ sp<AMessage> mNotify;
+ List<QueueEntry> mAudioQueue;
+ List<QueueEntry> mVideoQueue;
+ uint32_t mNumFramesWritten;
+
+ bool mDrainAudioQueuePending;
+ bool mDrainVideoQueuePending;
+ int32_t mAudioQueueGeneration;
+ int32_t mVideoQueueGeneration;
+
+ int64_t mAnchorTimeMediaUs;
+ int64_t mAnchorTimeRealUs;
+
+ Mutex mFlushLock; // protects the following 2 member vars.
+ bool mFlushingAudio;
+ bool mFlushingVideo;
+
+ bool mSyncQueues;
+
+ void onDrainAudioQueue();
+ void postDrainAudioQueue();
+
+ void onDrainVideoQueue();
+ void postDrainVideoQueue();
+
+ void onQueueBuffer(const sp<AMessage> &msg);
+ void onQueueEOS(const sp<AMessage> &msg);
+ void onFlush(const sp<AMessage> &msg);
+
+ void notifyEOS(bool audio);
+ void notifyFlushComplete(bool audio);
+
+ void flushQueue(List<QueueEntry> *queue);
+ bool dropBufferWhileFlushing(bool audio, const sp<AMessage> &msg);
+ void syncQueuesDone();
+
+ DISALLOW_EVIL_CONSTRUCTORS(Renderer);
+};
+
+} // namespace android
+
+#endif // NUPLAYER_RENDERER_H_
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
new file mode 100644
index 0000000..92642a8
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
@@ -0,0 +1,151 @@
+/*
+ * 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 "NuPlayerStreamListener"
+#include <utils/Log.h>
+
+#include "NuPlayerStreamListener.h"
+
+#include <binder/MemoryDealer.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+namespace android {
+
+NuPlayer::NuPlayerStreamListener::NuPlayerStreamListener(
+ const sp<IStreamSource> &source,
+ ALooper::handler_id id)
+ : mSource(source),
+ mTargetID(id),
+ mEOS(false),
+ mSendDataNotification(true) {
+ mSource->setListener(this);
+
+ mMemoryDealer = new MemoryDealer(kNumBuffers * kBufferSize);
+ for (size_t i = 0; i < kNumBuffers; ++i) {
+ sp<IMemory> mem = mMemoryDealer->allocate(kBufferSize);
+ CHECK(mem != NULL);
+
+ mBuffers.push(mem);
+ }
+ mSource->setBuffers(mBuffers);
+}
+
+void NuPlayer::NuPlayerStreamListener::start() {
+ for (size_t i = 0; i < kNumBuffers; ++i) {
+ mSource->onBufferAvailable(i);
+ }
+}
+
+void NuPlayer::NuPlayerStreamListener::queueBuffer(size_t index, size_t size) {
+ QueueEntry entry;
+ entry.mIsCommand = false;
+ entry.mIndex = index;
+ entry.mSize = size;
+ entry.mOffset = 0;
+
+ Mutex::Autolock autoLock(mLock);
+ mQueue.push_back(entry);
+
+ if (mSendDataNotification) {
+ mSendDataNotification = false;
+ (new AMessage(kWhatMoreDataQueued, mTargetID))->post();
+ }
+}
+
+void NuPlayer::NuPlayerStreamListener::issueCommand(
+ Command cmd, bool synchronous, const sp<AMessage> &extra) {
+ CHECK(!synchronous);
+
+ QueueEntry entry;
+ entry.mIsCommand = true;
+ entry.mCommand = cmd;
+ entry.mExtra = extra;
+
+ Mutex::Autolock autoLock(mLock);
+ mQueue.push_back(entry);
+
+ if (mSendDataNotification) {
+ mSendDataNotification = false;
+ (new AMessage(kWhatMoreDataQueued, mTargetID))->post();
+ }
+}
+
+ssize_t NuPlayer::NuPlayerStreamListener::read(void *data, size_t size) {
+ CHECK_GT(size, 0u);
+
+ Mutex::Autolock autoLock(mLock);
+
+ if (mEOS) {
+ return 0;
+ }
+
+ if (mQueue.empty()) {
+ mSendDataNotification = true;
+
+ return -EWOULDBLOCK;
+ }
+
+ QueueEntry *entry = &*mQueue.begin();
+
+ if (entry->mIsCommand) {
+ switch (entry->mCommand) {
+ case EOS:
+ {
+ mQueue.erase(mQueue.begin());
+ entry = NULL;
+
+ mEOS = true;
+ return 0;
+ }
+
+ case DISCONTINUITY:
+ {
+ mQueue.erase(mQueue.begin());
+ entry = NULL;
+
+ return INFO_DISCONTINUITY;
+ }
+
+ default:
+ TRESPASS();
+ break;
+ }
+ }
+
+ size_t copy = entry->mSize;
+ if (copy > size) {
+ copy = size;
+ }
+
+ memcpy(data,
+ (const uint8_t *)mBuffers.editItemAt(entry->mIndex)->pointer()
+ + entry->mOffset,
+ copy);
+
+ entry->mOffset += copy;
+ entry->mSize -= copy;
+
+ if (entry->mSize == 0) {
+ mSource->onBufferAvailable(entry->mIndex);
+ mQueue.erase(mQueue.begin());
+ entry = NULL;
+ }
+
+ return copy;
+}
+
+} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
new file mode 100644
index 0000000..f88e945
--- /dev/null
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+#ifndef NUPLAYER_STREAM_LISTENER_H_
+
+#define NUPLAYER_STREAM_LISTENER_H_
+
+#include "NuPlayer.h"
+
+#include <media/IStreamSource.h>
+
+namespace android {
+
+struct MemoryDealer;
+
+struct NuPlayer::NuPlayerStreamListener : public BnStreamListener {
+ NuPlayerStreamListener(
+ const sp<IStreamSource> &source,
+ ALooper::handler_id targetID);
+
+ virtual void queueBuffer(size_t index, size_t size);
+
+ virtual void issueCommand(
+ Command cmd, bool synchronous, const sp<AMessage> &extra);
+
+ void start();
+ ssize_t read(void *data, size_t size);
+
+private:
+ enum {
+ kNumBuffers = 16,
+ kBufferSize = 188 * 20
+ };
+
+ struct QueueEntry {
+ bool mIsCommand;
+
+ size_t mIndex;
+ size_t mSize;
+ size_t mOffset;
+
+ Command mCommand;
+ sp<AMessage> mExtra;
+ };
+
+ Mutex mLock;
+
+ sp<IStreamSource> mSource;
+ ALooper::handler_id mTargetID;
+ sp<MemoryDealer> mMemoryDealer;
+ Vector<sp<IMemory> > mBuffers;
+ List<QueueEntry> mQueue;
+ bool mEOS;
+ bool mSendDataNotification;
+
+ DISALLOW_EVIL_CONSTRUCTORS(NuPlayerStreamListener);
+};
+
+} // namespace android
+
+#endif // NUPLAYER_STREAM_LISTENER_H_