summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/AwesomePlayer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/libstagefright/AwesomePlayer.cpp')
-rw-r--r--media/libstagefright/AwesomePlayer.cpp626
1 files changed, 626 insertions, 0 deletions
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
new file mode 100644
index 0000000..ed46cea
--- /dev/null
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -0,0 +1,626 @@
+/*
+ * Copyright (C) 2009 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 "AwesomePlayer"
+#include <utils/Log.h>
+
+#include "include/AwesomePlayer.h"
+
+#include <media/stagefright/AudioPlayer.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaDebug.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/OMXCodec.h>
+
+namespace android {
+
+struct AwesomeEvent : public TimedEventQueue::Event {
+ AwesomeEvent(AwesomePlayer *player, int32_t code)
+ : mPlayer(player),
+ mCode(code) {
+ }
+
+protected:
+ virtual ~AwesomeEvent() {}
+
+ virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
+ mPlayer->onEvent(mCode);
+ }
+
+private:
+ AwesomePlayer *mPlayer;
+ int32_t mCode;
+
+ AwesomeEvent(const AwesomeEvent &);
+ AwesomeEvent &operator=(const AwesomeEvent &);
+};
+
+AwesomePlayer::AwesomePlayer()
+ : mTimeSource(NULL),
+ mAudioPlayer(NULL),
+ mLastVideoBuffer(NULL),
+ mVideoBuffer(NULL) {
+ CHECK_EQ(mClient.connect(), OK);
+
+ DataSource::RegisterDefaultSniffers();
+
+ mVideoEvent = new AwesomeEvent(this, 0);
+ mVideoEventPending = false;
+ mStreamDoneEvent = new AwesomeEvent(this, 1);
+ mStreamDoneEventPending = false;
+
+ mQueue.start();
+
+ reset();
+}
+
+AwesomePlayer::~AwesomePlayer() {
+ mQueue.stop();
+
+ reset();
+
+ mClient.disconnect();
+}
+
+void AwesomePlayer::cancelPlayerEvents() {
+ mQueue.cancelEvent(mVideoEvent->eventID());
+ mVideoEventPending = false;
+ mQueue.cancelEvent(mStreamDoneEvent->eventID());
+ mStreamDoneEventPending = false;
+}
+
+void AwesomePlayer::setListener(const sp<MediaPlayerBase> &listener) {
+ Mutex::Autolock autoLock(mLock);
+ mListener = listener;
+}
+
+status_t AwesomePlayer::setDataSource(const char *uri) {
+ Mutex::Autolock autoLock(mLock);
+
+ reset_l();
+
+ sp<MediaExtractor> extractor = MediaExtractor::CreateFromURI(uri);
+
+ if (extractor == NULL) {
+ return UNKNOWN_ERROR;
+ }
+
+ return setDataSource_l(extractor);
+}
+
+status_t AwesomePlayer::setDataSource(
+ int fd, int64_t offset, int64_t length) {
+ Mutex::Autolock autoLock(mLock);
+
+ reset_l();
+
+ sp<DataSource> source = new FileSource(fd, offset, length);
+
+ status_t err = source->initCheck();
+
+ if (err != OK) {
+ return err;
+ }
+
+ sp<MediaExtractor> extractor = MediaExtractor::Create(source);
+
+ if (extractor == NULL) {
+ return UNKNOWN_ERROR;
+ }
+
+ return setDataSource_l(extractor);
+}
+
+status_t AwesomePlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
+ reset_l();
+
+ bool haveAudio = false;
+ bool haveVideo = false;
+ for (size_t i = 0; i < extractor->countTracks(); ++i) {
+ sp<MetaData> meta = extractor->getTrackMetaData(i);
+
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ if (!haveVideo && !strncasecmp(mime, "video/", 6)) {
+ if (setVideoSource(extractor->getTrack(i)) == OK) {
+ haveVideo = true;
+ }
+ } else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) {
+ if (setAudioSource(extractor->getTrack(i)) == OK) {
+ haveAudio = true;
+ }
+ }
+
+ if (haveAudio && haveVideo) {
+ break;
+ }
+ }
+
+ return !haveAudio && !haveVideo ? UNKNOWN_ERROR : OK;
+}
+
+void AwesomePlayer::reset() {
+ Mutex::Autolock autoLock(mLock);
+ reset_l();
+}
+
+void AwesomePlayer::reset_l() {
+ cancelPlayerEvents();
+
+ if (mLastVideoBuffer) {
+ mLastVideoBuffer->release();
+ mLastVideoBuffer = NULL;
+ }
+
+ if (mVideoBuffer) {
+ mVideoBuffer->release();
+ mVideoBuffer = NULL;
+ }
+
+ if (mVideoSource != NULL) {
+ mVideoSource->stop();
+ mVideoSource.clear();
+ }
+
+ mAudioSource.clear();
+
+ if (mTimeSource != mAudioPlayer) {
+ delete mTimeSource;
+ }
+ mTimeSource = NULL;
+
+ delete mAudioPlayer;
+ mAudioPlayer = NULL;
+
+ mVideoRenderer.clear();
+
+ mDurationUs = -1;
+ mFlags = 0;
+ mVideoWidth = mVideoHeight = -1;
+ mTimeSourceDeltaUs = 0;
+ mVideoTimeUs = 0;
+
+ mSeeking = false;
+ mSeekTimeUs = 0;
+}
+
+// static
+void AwesomePlayer::AudioNotify(void *_me, int what) {
+ AwesomePlayer *me = (AwesomePlayer *)_me;
+
+ Mutex::Autolock autoLock(me->mLock);
+
+ switch (what) {
+ case AudioPlayer::REACHED_EOS:
+ me->postStreamDoneEvent_l();
+ break;
+
+ case AudioPlayer::SEEK_COMPLETE:
+ {
+ if (me->mListener != NULL) {
+ me->mListener->sendEvent(MEDIA_SEEK_COMPLETE);
+ }
+
+ break;
+ }
+
+ default:
+ CHECK(!"should not be here.");
+ break;
+ }
+}
+
+void AwesomePlayer::onStreamDone() {
+ // Posted whenever any stream finishes playing.
+
+ Mutex::Autolock autoLock(mLock);
+ mStreamDoneEventPending = false;
+
+ if (mFlags & LOOPING) {
+ seekTo_l(0);
+
+ if (mVideoRenderer != NULL) {
+ postVideoEvent_l();
+ }
+ } else {
+ if (mListener != NULL) {
+ mListener->sendEvent(MEDIA_PLAYBACK_COMPLETE);
+ }
+
+ pause_l();
+ }
+}
+
+status_t AwesomePlayer::play() {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mFlags & PLAYING) {
+ return OK;
+ }
+
+ mFlags |= PLAYING;
+ mFlags |= FIRST_FRAME;
+
+ if (mAudioSource != NULL) {
+ if (mAudioPlayer == NULL) {
+ if (mAudioSink != NULL) {
+ mAudioPlayer = new AudioPlayer(mAudioSink);
+
+ mAudioPlayer->setListenerCallback(
+ &AwesomePlayer::AudioNotify, this);
+
+ mAudioPlayer->setSource(mAudioSource);
+ mAudioPlayer->start();
+
+ delete mTimeSource;
+ mTimeSource = mAudioPlayer;
+
+ // If there was a seek request while we were paused
+ // and we're just starting up again, honor the request now.
+ seekAudioIfNecessary_l();
+ }
+ } else {
+ mAudioPlayer->resume();
+ }
+ }
+
+ if (mTimeSource == NULL && mAudioPlayer == NULL) {
+ mTimeSource = new SystemTimeSource;
+ }
+
+ if (mVideoSource != NULL) {
+ if (mVideoRenderer == NULL) {
+ initRenderer_l();
+ }
+
+ if (mVideoRenderer != NULL) {
+ // Kick off video playback
+ postVideoEvent_l();
+ }
+ }
+
+ return OK;
+}
+
+void AwesomePlayer::initRenderer_l() {
+ if (mISurface != NULL) {
+ sp<MetaData> meta = mVideoSource->getFormat();
+
+ int32_t format;
+ const char *component;
+ int32_t decodedWidth, decodedHeight;
+ CHECK(meta->findInt32(kKeyColorFormat, &format));
+ CHECK(meta->findCString(kKeyDecoderComponent, &component));
+ CHECK(meta->findInt32(kKeyWidth, &decodedWidth));
+ CHECK(meta->findInt32(kKeyHeight, &decodedHeight));
+
+ mVideoRenderer =
+ mClient.interface()->createRenderer(
+ mISurface, component,
+ (OMX_COLOR_FORMATTYPE)format,
+ decodedWidth, decodedHeight,
+ mVideoWidth, mVideoHeight);
+ }
+}
+
+status_t AwesomePlayer::pause() {
+ Mutex::Autolock autoLock(mLock);
+ return pause_l();
+}
+
+status_t AwesomePlayer::pause_l() {
+ if (!(mFlags & PLAYING)) {
+ return OK;
+ }
+
+ cancelPlayerEvents();
+
+ if (mAudioPlayer != NULL) {
+ mAudioPlayer->pause();
+ }
+
+ mFlags &= ~PLAYING;
+
+ return OK;
+}
+
+bool AwesomePlayer::isPlaying() const {
+ Mutex::Autolock autoLock(mLock);
+
+ return mFlags & PLAYING;
+}
+
+void AwesomePlayer::setISurface(const sp<ISurface> &isurface) {
+ Mutex::Autolock autoLock(mLock);
+
+ mISurface = isurface;
+}
+
+void AwesomePlayer::setAudioSink(
+ const sp<MediaPlayerBase::AudioSink> &audioSink) {
+ Mutex::Autolock autoLock(mLock);
+
+ mAudioSink = audioSink;
+}
+
+status_t AwesomePlayer::setLooping(bool shouldLoop) {
+ Mutex::Autolock autoLock(mLock);
+
+ mFlags = mFlags & ~LOOPING;
+
+ if (shouldLoop) {
+ mFlags |= LOOPING;
+ }
+
+ return OK;
+}
+
+status_t AwesomePlayer::getDuration(int64_t *durationUs) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mDurationUs < 0) {
+ return UNKNOWN_ERROR;
+ }
+
+ *durationUs = mDurationUs;
+
+ return OK;
+}
+
+status_t AwesomePlayer::getPosition(int64_t *positionUs) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mVideoRenderer != NULL) {
+ *positionUs = mVideoTimeUs;
+ } else if (mAudioPlayer != NULL) {
+ *positionUs = mAudioPlayer->getMediaTimeUs();
+ } else {
+ *positionUs = 0;
+ }
+
+ return OK;
+}
+
+status_t AwesomePlayer::seekTo(int64_t timeUs) {
+ Mutex::Autolock autoLock(mLock);
+ return seekTo_l(timeUs);
+}
+
+status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
+ mSeeking = true;
+ mSeekTimeUs = timeUs;
+
+ seekAudioIfNecessary_l();
+
+ return OK;
+}
+
+void AwesomePlayer::seekAudioIfNecessary_l() {
+ if (mSeeking && mVideoRenderer == NULL && mAudioPlayer != NULL) {
+ mAudioPlayer->seekTo(mSeekTimeUs);
+
+ mSeeking = false;
+ }
+}
+
+status_t AwesomePlayer::getVideoDimensions(
+ int32_t *width, int32_t *height) const {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mVideoWidth < 0 || mVideoHeight < 0) {
+ return UNKNOWN_ERROR;
+ }
+
+ *width = mVideoWidth;
+ *height = mVideoHeight;
+
+ return OK;
+}
+
+status_t AwesomePlayer::setAudioSource(const sp<MediaSource> &source) {
+ if (source == NULL) {
+ return UNKNOWN_ERROR;
+ }
+
+ mAudioSource = OMXCodec::Create(
+ mClient.interface(), source->getFormat(),
+ false, // createEncoder
+ source);
+
+ if (mAudioSource != NULL) {
+ int64_t durationUs;
+ if (source->getFormat()->findInt64(kKeyDuration, &durationUs)) {
+ if (mDurationUs < 0 || durationUs > mDurationUs) {
+ mDurationUs = durationUs;
+ }
+ }
+ }
+
+ return mAudioSource != NULL ? OK : UNKNOWN_ERROR;
+}
+
+status_t AwesomePlayer::setVideoSource(const sp<MediaSource> &source) {
+ if (source == NULL) {
+ return UNKNOWN_ERROR;
+ }
+
+ mVideoSource = OMXCodec::Create(
+ mClient.interface(), source->getFormat(),
+ false, // createEncoder
+ source);
+
+ if (mVideoSource != NULL) {
+ int64_t durationUs;
+ if (source->getFormat()->findInt64(kKeyDuration, &durationUs)) {
+ if (mDurationUs < 0 || durationUs > mDurationUs) {
+ mDurationUs = durationUs;
+ }
+ }
+
+ CHECK(source->getFormat()->findInt32(kKeyWidth, &mVideoWidth));
+ CHECK(source->getFormat()->findInt32(kKeyHeight, &mVideoHeight));
+
+ mVideoSource->start();
+ }
+
+ return mVideoSource != NULL ? OK : UNKNOWN_ERROR;
+}
+
+void AwesomePlayer::onEvent(int32_t code) {
+ if (code == 1) {
+ onStreamDone();
+ return;
+ }
+
+ Mutex::Autolock autoLock(mLock);
+ mVideoEventPending = false;
+
+ if (mSeeking) {
+ if (mLastVideoBuffer) {
+ mLastVideoBuffer->release();
+ mLastVideoBuffer = NULL;
+ }
+
+ if (mVideoBuffer) {
+ mVideoBuffer->release();
+ mVideoBuffer = NULL;
+ }
+ }
+
+ if (!mVideoBuffer) {
+ MediaSource::ReadOptions options;
+ if (mSeeking) {
+ LOGV("seeking to %lld us (%.2f secs)", mSeekTimeUs, mSeekTimeUs / 1E6);
+
+ options.setSeekTo(mSeekTimeUs);
+ }
+ for (;;) {
+ status_t err = mVideoSource->read(&mVideoBuffer, &options);
+
+ if (err != OK) {
+ CHECK_EQ(mVideoBuffer, NULL);
+
+ if (err == INFO_FORMAT_CHANGED) {
+ LOGV("VideoSource signalled format change.");
+
+ initRenderer_l();
+ continue;
+ }
+
+ postStreamDoneEvent_l();
+ return;
+ }
+
+ break;
+ }
+ }
+
+ int64_t timeUs;
+ CHECK(mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
+
+ mVideoTimeUs = timeUs;
+
+ if (mSeeking) {
+ if (mAudioPlayer != NULL) {
+ LOGV("seeking audio to %lld us (%.2f secs).", timeUs, timeUs / 1E6);
+
+ mAudioPlayer->seekTo(timeUs);
+ } else {
+ // If we're playing video only, report seek complete now,
+ // otherwise audio player will notify us later.
+ if (mListener != NULL) {
+ mListener->sendEvent(MEDIA_SEEK_COMPLETE);
+ }
+ }
+
+ mFlags |= FIRST_FRAME;
+ mSeeking = false;
+ }
+
+ if (mFlags & FIRST_FRAME) {
+ mFlags &= ~FIRST_FRAME;
+
+ mTimeSourceDeltaUs = mTimeSource->getRealTimeUs() - timeUs;
+ }
+
+ int64_t realTimeUs, mediaTimeUs;
+ if (mAudioPlayer != NULL
+ && mAudioPlayer->getMediaTimeMapping(&realTimeUs, &mediaTimeUs)) {
+ mTimeSourceDeltaUs = realTimeUs - mediaTimeUs;
+ }
+
+ int64_t nowUs = mTimeSource->getRealTimeUs() - mTimeSourceDeltaUs;
+
+ int64_t latenessUs = nowUs - timeUs;
+
+ if (latenessUs > 20000) {
+ // We're more than 20ms late.
+ LOGV("we're late by %lld us (%.2f secs)", latenessUs, latenessUs / 1E6);
+
+ mVideoBuffer->release();
+ mVideoBuffer = NULL;
+
+ postVideoEvent_l();
+ return;
+ }
+
+ if (latenessUs < -10000) {
+ // We're more than 10ms early.
+
+ postVideoEvent_l(10000);
+ return;
+ }
+
+ void *id;
+ if (mVideoBuffer->meta_data()->findPointer(kKeyBufferID, &id)) {
+ mVideoRenderer->render((IOMX::buffer_id)id);
+ }
+
+ if (mLastVideoBuffer) {
+ mLastVideoBuffer->release();
+ mLastVideoBuffer = NULL;
+ }
+ mLastVideoBuffer = mVideoBuffer;
+ mVideoBuffer = NULL;
+
+ postVideoEvent_l();
+}
+
+void AwesomePlayer::postVideoEvent_l(int64_t delayUs) {
+ if (mVideoEventPending) {
+ return;
+ }
+
+ mVideoEventPending = true;
+ mQueue.postEventWithDelay(mVideoEvent, delayUs < 0 ? 10000 : delayUs);
+}
+
+void AwesomePlayer::postStreamDoneEvent_l() {
+ if (mStreamDoneEventPending) {
+ return;
+ }
+ mStreamDoneEventPending = true;
+ mQueue.postEvent(mStreamDoneEvent);
+}
+
+} // namespace android
+