summaryrefslogtreecommitdiffstats
path: root/libvideoeditor
diff options
context:
space:
mode:
authorJames Dong <jdong@google.com>2011-05-01 12:36:22 -0700
committerJames Dong <jdong@google.com>2011-05-01 13:52:00 -0700
commitc9dedc4e1d0c8343ab1029cb601253826cd67c81 (patch)
tree08c3e7d3c4c18c7f0dee9b991780b0e117c00b4b /libvideoeditor
parentacb62af8ccf493b38decd91d94eb18395b6f9461 (diff)
downloadframeworks_av-c9dedc4e1d0c8343ab1029cb601253826cd67c81.zip
frameworks_av-c9dedc4e1d0c8343ab1029cb601253826cd67c81.tar.gz
frameworks_av-c9dedc4e1d0c8343ab1029cb601253826cd67c81.tar.bz2
Separate preview player from AwesomePlayer and also video editor audio player from AudioPlayer
Change-Id: I436ac541df3e34ad16be95a50c96d7af67fa15e4
Diffstat (limited to 'libvideoeditor')
-rwxr-xr-xlibvideoeditor/lvpp/Android.mk3
-rw-r--r--libvideoeditor/lvpp/AudioPlayerBase.cpp510
-rw-r--r--libvideoeditor/lvpp/AudioPlayerBase.h119
-rwxr-xr-xlibvideoeditor/lvpp/PreviewPlayer.cpp10
-rwxr-xr-xlibvideoeditor/lvpp/PreviewPlayer.h8
-rw-r--r--libvideoeditor/lvpp/PreviewPlayerBase.cpp1915
-rw-r--r--libvideoeditor/lvpp/PreviewPlayerBase.h285
-rwxr-xr-xlibvideoeditor/lvpp/VideoEditorAudioPlayer.cpp10
-rwxr-xr-xlibvideoeditor/lvpp/VideoEditorAudioPlayer.h9
9 files changed, 2851 insertions, 18 deletions
diff --git a/libvideoeditor/lvpp/Android.mk b/libvideoeditor/lvpp/Android.mk
index 091258d..8b5a6f5 100755
--- a/libvideoeditor/lvpp/Android.mk
+++ b/libvideoeditor/lvpp/Android.mk
@@ -35,6 +35,8 @@ LOCAL_SRC_FILES:= \
DummyAudioSource.cpp \
DummyVideoSource.cpp \
VideoEditorBGAudioProcessing.cpp \
+ AudioPlayerBase.cpp \
+ PreviewPlayerBase.cpp \
PreviewRenderer.cpp
LOCAL_MODULE_TAGS := optional
@@ -50,6 +52,7 @@ LOCAL_SHARED_LIBRARIES := \
libutils \
libcutils \
libmedia \
+ libdrmframework \
libstagefright \
libstagefright_omx \
libstagefright_foundation \
diff --git a/libvideoeditor/lvpp/AudioPlayerBase.cpp b/libvideoeditor/lvpp/AudioPlayerBase.cpp
new file mode 100644
index 0000000..26c6a63
--- /dev/null
+++ b/libvideoeditor/lvpp/AudioPlayerBase.cpp
@@ -0,0 +1,510 @@
+/*
+ * Copyright (C) 2011 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 "AudioPlayerBase"
+#include <utils/Log.h>
+
+#include <binder/IPCThreadState.h>
+#include <media/AudioTrack.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 "AudioPlayerBase.h"
+#include "PreviewPlayerBase.h"
+
+namespace android {
+
+AudioPlayerBase::AudioPlayerBase(
+ const sp<MediaPlayerBase::AudioSink> &audioSink,
+ PreviewPlayerBase *observer)
+ : mAudioTrack(NULL),
+ mInputBuffer(NULL),
+ mSampleRate(0),
+ mLatencyUs(0),
+ mFrameSize(0),
+ mNumFramesPlayed(0),
+ mPositionTimeMediaUs(-1),
+ mPositionTimeRealUs(-1),
+ mSeeking(false),
+ mReachedEOS(false),
+ mFinalStatus(OK),
+ mStarted(false),
+ mIsFirstBuffer(false),
+ mFirstBufferResult(OK),
+ mFirstBuffer(NULL),
+ mAudioSink(audioSink),
+ mObserver(observer) {
+}
+
+AudioPlayerBase::~AudioPlayerBase() {
+ if (mStarted) {
+ reset();
+ }
+}
+
+void AudioPlayerBase::setSource(const sp<MediaSource> &source) {
+ CHECK_EQ(mSource, NULL);
+ mSource = source;
+}
+
+status_t AudioPlayerBase::start(bool sourceAlreadyStarted) {
+ CHECK(!mStarted);
+ CHECK(mSource != NULL);
+
+ status_t err;
+ if (!sourceAlreadyStarted) {
+ err = mSource->start();
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ // We allow an optional INFO_FORMAT_CHANGED at the very beginning
+ // of playback, if there is one, getFormat below will retrieve the
+ // updated format, if there isn't, we'll stash away the valid buffer
+ // of data to be used on the first audio callback.
+
+ CHECK(mFirstBuffer == NULL);
+
+ mFirstBufferResult = mSource->read(&mFirstBuffer);
+ if (mFirstBufferResult == INFO_FORMAT_CHANGED) {
+ LOGV("INFO_FORMAT_CHANGED!!!");
+
+ CHECK(mFirstBuffer == NULL);
+ mFirstBufferResult = OK;
+ mIsFirstBuffer = false;
+ } else {
+ mIsFirstBuffer = true;
+ }
+
+ sp<MetaData> format = mSource->getFormat();
+ const char *mime;
+ bool success = format->findCString(kKeyMIMEType, &mime);
+ CHECK(success);
+ CHECK(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW));
+
+ success = format->findInt32(kKeySampleRate, &mSampleRate);
+ CHECK(success);
+
+ int32_t numChannels;
+ success = format->findInt32(kKeyChannelCount, &numChannels);
+ CHECK(success);
+
+ if (mAudioSink.get() != NULL) {
+ status_t err = mAudioSink->open(
+ mSampleRate, numChannels, AUDIO_FORMAT_PCM_16_BIT,
+ DEFAULT_AUDIOSINK_BUFFERCOUNT,
+ &AudioPlayerBase::AudioSinkCallback, this);
+ if (err != OK) {
+ if (mFirstBuffer != NULL) {
+ mFirstBuffer->release();
+ mFirstBuffer = NULL;
+ }
+
+ if (!sourceAlreadyStarted) {
+ mSource->stop();
+ }
+
+ return err;
+ }
+
+ mLatencyUs = (int64_t)mAudioSink->latency() * 1000;
+ mFrameSize = mAudioSink->frameSize();
+
+ mAudioSink->start();
+ } else {
+ mAudioTrack = new AudioTrack(
+ AUDIO_STREAM_MUSIC, mSampleRate, AUDIO_FORMAT_PCM_16_BIT,
+ (numChannels == 2)
+ ? AUDIO_CHANNEL_OUT_STEREO
+ : AUDIO_CHANNEL_OUT_MONO,
+ 0, 0, &AudioCallback, this, 0);
+
+ if ((err = mAudioTrack->initCheck()) != OK) {
+ delete mAudioTrack;
+ mAudioTrack = NULL;
+
+ if (mFirstBuffer != NULL) {
+ mFirstBuffer->release();
+ mFirstBuffer = NULL;
+ }
+
+ if (!sourceAlreadyStarted) {
+ mSource->stop();
+ }
+
+ return err;
+ }
+
+ mLatencyUs = (int64_t)mAudioTrack->latency() * 1000;
+ mFrameSize = mAudioTrack->frameSize();
+
+ mAudioTrack->start();
+ }
+
+ mStarted = true;
+
+ return OK;
+}
+
+void AudioPlayerBase::pause(bool playPendingSamples) {
+ CHECK(mStarted);
+
+ if (playPendingSamples) {
+ if (mAudioSink.get() != NULL) {
+ mAudioSink->stop();
+ } else {
+ mAudioTrack->stop();
+ }
+ } else {
+ if (mAudioSink.get() != NULL) {
+ mAudioSink->pause();
+ } else {
+ mAudioTrack->pause();
+ }
+ }
+}
+
+void AudioPlayerBase::resume() {
+ CHECK(mStarted);
+
+ if (mAudioSink.get() != NULL) {
+ mAudioSink->start();
+ } else {
+ mAudioTrack->start();
+ }
+}
+
+void AudioPlayerBase::reset() {
+ CHECK(mStarted);
+
+ if (mAudioSink.get() != NULL) {
+ mAudioSink->stop();
+ mAudioSink->close();
+ } else {
+ mAudioTrack->stop();
+
+ delete mAudioTrack;
+ mAudioTrack = NULL;
+ }
+
+ // Make sure to release any buffer we hold onto so that the
+ // source is able to stop().
+
+ if (mFirstBuffer != NULL) {
+ mFirstBuffer->release();
+ mFirstBuffer = NULL;
+ }
+
+ if (mInputBuffer != NULL) {
+ LOGV("AudioPlayerBase releasing input buffer.");
+
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+
+ mSource->stop();
+
+ // The following hack is necessary to ensure that the OMX
+ // component is completely released by the time we may try
+ // to instantiate it again.
+ wp<MediaSource> tmp = mSource;
+ mSource.clear();
+ while (tmp.promote() != NULL) {
+ usleep(1000);
+ }
+ IPCThreadState::self()->flushCommands();
+
+ mNumFramesPlayed = 0;
+ mPositionTimeMediaUs = -1;
+ mPositionTimeRealUs = -1;
+ mSeeking = false;
+ mReachedEOS = false;
+ mFinalStatus = OK;
+ mStarted = false;
+}
+
+// static
+void AudioPlayerBase::AudioCallback(int event, void *user, void *info) {
+ static_cast<AudioPlayerBase *>(user)->AudioCallback(event, info);
+}
+
+bool AudioPlayerBase::isSeeking() {
+ Mutex::Autolock autoLock(mLock);
+ return mSeeking;
+}
+
+bool AudioPlayerBase::reachedEOS(status_t *finalStatus) {
+ *finalStatus = OK;
+
+ Mutex::Autolock autoLock(mLock);
+ *finalStatus = mFinalStatus;
+ return mReachedEOS;
+}
+
+// static
+size_t AudioPlayerBase::AudioSinkCallback(
+ MediaPlayerBase::AudioSink *audioSink,
+ void *buffer, size_t size, void *cookie) {
+ AudioPlayerBase *me = (AudioPlayerBase *)cookie;
+
+ return me->fillBuffer(buffer, size);
+}
+
+void AudioPlayerBase::AudioCallback(int event, void *info) {
+ if (event != AudioTrack::EVENT_MORE_DATA) {
+ return;
+ }
+
+ AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
+ size_t numBytesWritten = fillBuffer(buffer->raw, buffer->size);
+
+ buffer->size = numBytesWritten;
+}
+
+uint32_t AudioPlayerBase::getNumFramesPendingPlayout() const {
+ uint32_t numFramesPlayedOut;
+ status_t err;
+
+ if (mAudioSink != NULL) {
+ err = mAudioSink->getPosition(&numFramesPlayedOut);
+ } else {
+ err = mAudioTrack->getPosition(&numFramesPlayedOut);
+ }
+
+ if (err != OK || mNumFramesPlayed < numFramesPlayedOut) {
+ return 0;
+ }
+
+ // mNumFramesPlayed is the number of frames submitted
+ // to the audio sink for playback, but not all of them
+ // may have played out by now.
+ return mNumFramesPlayed - numFramesPlayedOut;
+}
+
+size_t AudioPlayerBase::fillBuffer(void *data, size_t size) {
+ if (mNumFramesPlayed == 0) {
+ LOGV("AudioCallback");
+ }
+
+ if (mReachedEOS) {
+ return 0;
+ }
+
+ bool postSeekComplete = false;
+ bool postEOS = false;
+ int64_t postEOSDelayUs = 0;
+
+ size_t size_done = 0;
+ size_t size_remaining = size;
+ while (size_remaining > 0) {
+ MediaSource::ReadOptions options;
+
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mSeeking) {
+ if (mIsFirstBuffer) {
+ if (mFirstBuffer != NULL) {
+ mFirstBuffer->release();
+ mFirstBuffer = NULL;
+ }
+ mIsFirstBuffer = false;
+ }
+
+ options.setSeekTo(mSeekTimeUs);
+
+ if (mInputBuffer != NULL) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+
+ mSeeking = false;
+ if (mObserver) {
+ postSeekComplete = true;
+ }
+ }
+ }
+
+ if (mInputBuffer == NULL) {
+ status_t err;
+
+ if (mIsFirstBuffer) {
+ mInputBuffer = mFirstBuffer;
+ mFirstBuffer = NULL;
+ err = mFirstBufferResult;
+
+ mIsFirstBuffer = false;
+ } else {
+ err = mSource->read(&mInputBuffer, &options);
+ }
+
+ CHECK((err == OK && mInputBuffer != NULL)
+ || (err != OK && mInputBuffer == NULL));
+
+ Mutex::Autolock autoLock(mLock);
+
+ if (err != OK) {
+ if (mObserver && !mReachedEOS) {
+ // We don't want to post EOS right away but only
+ // after all frames have actually been played out.
+
+ // These are the number of frames submitted to the
+ // AudioTrack that you haven't heard yet.
+ uint32_t numFramesPendingPlayout =
+ getNumFramesPendingPlayout();
+
+ // These are the number of frames we're going to
+ // submit to the AudioTrack by returning from this
+ // callback.
+ uint32_t numAdditionalFrames = size_done / mFrameSize;
+
+ numFramesPendingPlayout += numAdditionalFrames;
+
+ int64_t timeToCompletionUs =
+ (1000000ll * numFramesPendingPlayout) / mSampleRate;
+
+ LOGV("total number of frames played: %lld (%lld us)",
+ (mNumFramesPlayed + numAdditionalFrames),
+ 1000000ll * (mNumFramesPlayed + numAdditionalFrames)
+ / mSampleRate);
+
+ LOGV("%d frames left to play, %lld us (%.2f secs)",
+ numFramesPendingPlayout,
+ timeToCompletionUs, timeToCompletionUs / 1E6);
+
+ postEOS = true;
+ postEOSDelayUs = timeToCompletionUs + mLatencyUs;
+ }
+
+ mReachedEOS = true;
+ mFinalStatus = err;
+ break;
+ }
+
+ CHECK(mInputBuffer->meta_data()->findInt64(
+ kKeyTime, &mPositionTimeMediaUs));
+
+ mPositionTimeRealUs =
+ ((mNumFramesPlayed + size_done / mFrameSize) * 1000000)
+ / mSampleRate;
+
+ LOGV("buffer->size() = %d, "
+ "mPositionTimeMediaUs=%.2f mPositionTimeRealUs=%.2f",
+ mInputBuffer->range_length(),
+ mPositionTimeMediaUs / 1E6, mPositionTimeRealUs / 1E6);
+ }
+
+ if (mInputBuffer->range_length() == 0) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+
+ continue;
+ }
+
+ size_t copy = size_remaining;
+ if (copy > mInputBuffer->range_length()) {
+ copy = mInputBuffer->range_length();
+ }
+
+ memcpy((char *)data + size_done,
+ (const char *)mInputBuffer->data() + mInputBuffer->range_offset(),
+ copy);
+
+ mInputBuffer->set_range(mInputBuffer->range_offset() + copy,
+ mInputBuffer->range_length() - copy);
+
+ size_done += copy;
+ size_remaining -= copy;
+ }
+
+ {
+ Mutex::Autolock autoLock(mLock);
+ mNumFramesPlayed += size_done / mFrameSize;
+ }
+
+ if (postEOS) {
+ mObserver->postAudioEOS(postEOSDelayUs);
+ }
+
+ if (postSeekComplete) {
+ mObserver->postAudioSeekComplete();
+ }
+
+ return size_done;
+}
+
+int64_t AudioPlayerBase::getRealTimeUs() {
+ Mutex::Autolock autoLock(mLock);
+ return getRealTimeUsLocked();
+}
+
+int64_t AudioPlayerBase::getRealTimeUsLocked() const {
+ return -mLatencyUs + (mNumFramesPlayed * 1000000) / mSampleRate;
+}
+
+int64_t AudioPlayerBase::getMediaTimeUs() {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mPositionTimeMediaUs < 0 || mPositionTimeRealUs < 0) {
+ if (mSeeking) {
+ return mSeekTimeUs;
+ }
+
+ return 0;
+ }
+
+ int64_t realTimeOffset = getRealTimeUsLocked() - mPositionTimeRealUs;
+ if (realTimeOffset < 0) {
+ realTimeOffset = 0;
+ }
+
+ return mPositionTimeMediaUs + realTimeOffset;
+}
+
+bool AudioPlayerBase::getMediaTimeMapping(
+ int64_t *realtime_us, int64_t *mediatime_us) {
+ Mutex::Autolock autoLock(mLock);
+
+ *realtime_us = mPositionTimeRealUs;
+ *mediatime_us = mPositionTimeMediaUs;
+
+ return mPositionTimeRealUs != -1 && mPositionTimeMediaUs != -1;
+}
+
+status_t AudioPlayerBase::seekTo(int64_t time_us) {
+ Mutex::Autolock autoLock(mLock);
+
+ mSeeking = true;
+ mPositionTimeRealUs = mPositionTimeMediaUs = -1;
+ mReachedEOS = false;
+ mSeekTimeUs = time_us;
+
+ if (mAudioSink != NULL) {
+ mAudioSink->flush();
+ } else {
+ mAudioTrack->flush();
+ }
+
+ return OK;
+}
+
+}
diff --git a/libvideoeditor/lvpp/AudioPlayerBase.h b/libvideoeditor/lvpp/AudioPlayerBase.h
new file mode 100644
index 0000000..31b9fa2
--- /dev/null
+++ b/libvideoeditor/lvpp/AudioPlayerBase.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2011 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 AUDIO_PLAYER_BASE_H_
+
+#define AUDIO_PLAYER_BASE_H_
+
+#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/TimeSource.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class MediaSource;
+class AudioTrack;
+class PreviewPlayerBase;
+
+class AudioPlayerBase : public TimeSource {
+public:
+ enum {
+ REACHED_EOS,
+ SEEK_COMPLETE
+ };
+
+ AudioPlayerBase(const sp<MediaPlayerBase::AudioSink> &audioSink,
+ PreviewPlayerBase *audioObserver = NULL);
+
+ virtual ~AudioPlayerBase();
+
+ // Caller retains ownership of "source".
+ void setSource(const sp<MediaSource> &source);
+
+ // Return time in us.
+ virtual int64_t getRealTimeUs();
+
+ status_t start(bool sourceAlreadyStarted = false);
+
+ void pause(bool playPendingSamples = false);
+ void resume();
+
+ // Returns the timestamp of the last buffer played (in us).
+ int64_t getMediaTimeUs();
+
+ // Returns true iff a mapping is established, i.e. the AudioPlayerBase
+ // has played at least one frame of audio.
+ bool getMediaTimeMapping(int64_t *realtime_us, int64_t *mediatime_us);
+
+ status_t seekTo(int64_t time_us);
+
+ bool isSeeking();
+ bool reachedEOS(status_t *finalStatus);
+
+private:
+ friend class VideoEditorAudioPlayer;
+ sp<MediaSource> mSource;
+ AudioTrack *mAudioTrack;
+
+ MediaBuffer *mInputBuffer;
+
+ int mSampleRate;
+ int64_t mLatencyUs;
+ size_t mFrameSize;
+
+ Mutex mLock;
+ int64_t mNumFramesPlayed;
+
+ int64_t mPositionTimeMediaUs;
+ int64_t mPositionTimeRealUs;
+
+ bool mSeeking;
+ bool mReachedEOS;
+ status_t mFinalStatus;
+ int64_t mSeekTimeUs;
+
+ bool mStarted;
+
+ bool mIsFirstBuffer;
+ status_t mFirstBufferResult;
+ MediaBuffer *mFirstBuffer;
+
+ sp<MediaPlayerBase::AudioSink> mAudioSink;
+ PreviewPlayerBase *mObserver;
+
+ static void AudioCallback(int event, void *user, void *info);
+ void AudioCallback(int event, void *info);
+
+ static size_t AudioSinkCallback(
+ MediaPlayerBase::AudioSink *audioSink,
+ void *data, size_t size, void *me);
+
+ size_t fillBuffer(void *data, size_t size);
+
+ int64_t getRealTimeUsLocked() const;
+
+ void reset();
+
+ uint32_t getNumFramesPendingPlayout() const;
+
+ AudioPlayerBase(const AudioPlayerBase &);
+ AudioPlayerBase &operator=(const AudioPlayerBase &);
+};
+
+} // namespace android
+
+#endif // AUDIO_PLAYER_BASE_H_
diff --git a/libvideoeditor/lvpp/PreviewPlayer.cpp b/libvideoeditor/lvpp/PreviewPlayer.cpp
index 552e1cd..8c9ef10 100755
--- a/libvideoeditor/lvpp/PreviewPlayer.cpp
+++ b/libvideoeditor/lvpp/PreviewPlayer.cpp
@@ -170,7 +170,7 @@ int PreviewLocalRenderer::init(
}
PreviewPlayer::PreviewPlayer()
- : AwesomePlayer(),
+ : PreviewPlayerBase(),
mCurrFramingEffectIndex(0) ,
mReportedWidth(0),
mReportedHeight(0),
@@ -202,7 +202,7 @@ PreviewPlayer::PreviewPlayer()
mStreamDoneEventPending = false;
mCheckAudioStatusEvent = new PreviewPlayerEvent(
- this, &AwesomePlayer::onCheckAudioStatus);
+ this, &PreviewPlayerBase::onCheckAudioStatus);
mAudioStatusEventPending = false;
@@ -493,7 +493,7 @@ status_t PreviewPlayer::startAudioPlayer_l() {
return OK;
}
-status_t PreviewPlayer::setAudioPlayer(AudioPlayer *audioPlayer) {
+status_t PreviewPlayer::setAudioPlayer(AudioPlayerBase *audioPlayer) {
Mutex::Autolock autoLock(mLock);
CHECK(!(mFlags & PLAYING));
mAudioPlayer = audioPlayer;
@@ -1513,13 +1513,13 @@ status_t PreviewPlayer::resume() {
status_t err;
if (state->mFileSource != NULL) {
- err = AwesomePlayer::setDataSource_l(state->mFileSource);
+ err = PreviewPlayerBase::setDataSource_l(state->mFileSource);
if (err == OK) {
mFileSource = state->mFileSource;
}
} else {
- err = AwesomePlayer::setDataSource_l(state->mUri, &state->mUriHeaders);
+ err = PreviewPlayerBase::setDataSource_l(state->mUri, &state->mUriHeaders);
}
if (err != OK) {
diff --git a/libvideoeditor/lvpp/PreviewPlayer.h b/libvideoeditor/lvpp/PreviewPlayer.h
index ad34d6c..b793639 100755
--- a/libvideoeditor/lvpp/PreviewPlayer.h
+++ b/libvideoeditor/lvpp/PreviewPlayer.h
@@ -28,12 +28,12 @@
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/TimeSource.h>
#include <utils/threads.h>
-#include <AwesomePlayer.h>
+#include "PreviewPlayerBase.h"
#include "VideoEditorPreviewController.h"
namespace android {
-struct AudioPlayer;
+struct AudioPlayerBase;
struct DataSource;
struct MediaBuffer;
struct MediaExtractor;
@@ -51,7 +51,7 @@ private:
PreviewPlayerRenderer &operator=(const PreviewPlayerRenderer &);
};
-struct PreviewPlayer : public AwesomePlayer {
+struct PreviewPlayer : public PreviewPlayerBase {
PreviewPlayer();
~PreviewPlayer();
@@ -93,7 +93,7 @@ struct PreviewPlayer : public AwesomePlayer {
status_t setImageClipProperties(uint32_t width, uint32_t height);
status_t readFirstVideoFrame();
status_t getLastRenderedTimeMs(uint32_t *lastRenderedTimeMs);
- status_t setAudioPlayer(AudioPlayer *audioPlayer);
+ status_t setAudioPlayer(AudioPlayerBase *audioPlayer);
private:
friend struct PreviewPlayerEvent;
diff --git a/libvideoeditor/lvpp/PreviewPlayerBase.cpp b/libvideoeditor/lvpp/PreviewPlayerBase.cpp
new file mode 100644
index 0000000..62b8a72
--- /dev/null
+++ b/libvideoeditor/lvpp/PreviewPlayerBase.cpp
@@ -0,0 +1,1915 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#undef DEBUG_HDCP
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "PreviewPlayerBase"
+#include <utils/Log.h>
+
+#include <dlfcn.h>
+
+#include "include/ARTSPController.h"
+#include "PreviewPlayerBase.h"
+#include "AudioPlayerBase.h"
+#include "include/SoftwareRenderer.h"
+#include "include/NuCachedSource2.h"
+#include "include/ThrottledSource.h"
+#include "include/MPEG2TSExtractor.h"
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <media/IMediaPlayerService.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/OMXCodec.h>
+
+#include <surfaceflinger/Surface.h>
+#include <gui/ISurfaceTexture.h>
+#include <gui/SurfaceTextureClient.h>
+#include <surfaceflinger/ISurfaceComposer.h>
+
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <cutils/properties.h>
+
+#define USE_SURFACE_ALLOC 1
+
+namespace android {
+
+static int64_t kLowWaterMarkUs = 2000000ll; // 2secs
+static int64_t kHighWaterMarkUs = 10000000ll; // 10secs
+static int64_t kHighWaterMarkRTSPUs = 4000000ll; // 4secs
+static const size_t kLowWaterMarkBytes = 40000;
+static const size_t kHighWaterMarkBytes = 200000;
+
+struct AwesomeEvent : public TimedEventQueue::Event {
+ AwesomeEvent(
+ PreviewPlayerBase *player,
+ void (PreviewPlayerBase::*method)())
+ : mPlayer(player),
+ mMethod(method) {
+ }
+
+protected:
+ virtual ~AwesomeEvent() {}
+
+ virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
+ (mPlayer->*mMethod)();
+ }
+
+private:
+ PreviewPlayerBase *mPlayer;
+ void (PreviewPlayerBase::*mMethod)();
+
+ AwesomeEvent(const AwesomeEvent &);
+ AwesomeEvent &operator=(const AwesomeEvent &);
+};
+
+struct AwesomeLocalRenderer : public AwesomeRenderer {
+ AwesomeLocalRenderer(
+ const sp<ANativeWindow> &nativeWindow, const sp<MetaData> &meta)
+ : mTarget(new SoftwareRenderer(nativeWindow, meta)) {
+ }
+
+ virtual void render(MediaBuffer *buffer) {
+ render((const uint8_t *)buffer->data() + buffer->range_offset(),
+ buffer->range_length());
+ }
+
+ void render(const void *data, size_t size) {
+ mTarget->render(data, size, NULL);
+ }
+
+protected:
+ virtual ~AwesomeLocalRenderer() {
+ delete mTarget;
+ mTarget = NULL;
+ }
+
+private:
+ SoftwareRenderer *mTarget;
+
+ AwesomeLocalRenderer(const AwesomeLocalRenderer &);
+ AwesomeLocalRenderer &operator=(const AwesomeLocalRenderer &);;
+};
+
+struct AwesomeNativeWindowRenderer : public AwesomeRenderer {
+ AwesomeNativeWindowRenderer(
+ const sp<ANativeWindow> &nativeWindow,
+ int32_t rotationDegrees)
+ : mNativeWindow(nativeWindow) {
+ applyRotation(rotationDegrees);
+ }
+
+ virtual void render(MediaBuffer *buffer) {
+ status_t err = mNativeWindow->queueBuffer(
+ mNativeWindow.get(), buffer->graphicBuffer().get());
+ if (err != 0) {
+ LOGE("queueBuffer failed with error %s (%d)", strerror(-err),
+ -err);
+ return;
+ }
+
+ sp<MetaData> metaData = buffer->meta_data();
+ metaData->setInt32(kKeyRendered, 1);
+ }
+
+protected:
+ virtual ~AwesomeNativeWindowRenderer() {}
+
+private:
+ sp<ANativeWindow> mNativeWindow;
+
+ void applyRotation(int32_t rotationDegrees) {
+ uint32_t transform;
+ switch (rotationDegrees) {
+ case 0: transform = 0; break;
+ case 90: transform = HAL_TRANSFORM_ROT_90; break;
+ case 180: transform = HAL_TRANSFORM_ROT_180; break;
+ case 270: transform = HAL_TRANSFORM_ROT_270; break;
+ default: transform = 0; break;
+ }
+
+ if (transform) {
+ CHECK_EQ(0, native_window_set_buffers_transform(
+ mNativeWindow.get(), transform));
+ }
+ }
+
+ AwesomeNativeWindowRenderer(const AwesomeNativeWindowRenderer &);
+ AwesomeNativeWindowRenderer &operator=(
+ const AwesomeNativeWindowRenderer &);
+};
+
+// To collect the decoder usage
+void addBatteryData(uint32_t params) {
+ sp<IBinder> binder =
+ defaultServiceManager()->getService(String16("media.player"));
+ sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
+ CHECK(service.get() != NULL);
+
+ service->addBatteryData(params);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+PreviewPlayerBase::PreviewPlayerBase()
+ : mQueueStarted(false),
+ mTimeSource(NULL),
+ mVideoRendererIsPreview(false),
+ mAudioPlayer(NULL),
+ mDisplayWidth(0),
+ mDisplayHeight(0),
+ mFlags(0),
+ mExtractorFlags(0),
+ mVideoBuffer(NULL),
+ mDecryptHandle(NULL),
+ mLastVideoTimeUs(-1) {
+ CHECK_EQ(mClient.connect(), (status_t)OK);
+
+ DataSource::RegisterDefaultSniffers();
+
+ mVideoEvent = new AwesomeEvent(this, &PreviewPlayerBase::onVideoEvent);
+ mVideoEventPending = false;
+ mStreamDoneEvent = new AwesomeEvent(this, &PreviewPlayerBase::onStreamDone);
+ mStreamDoneEventPending = false;
+ mBufferingEvent = new AwesomeEvent(this, &PreviewPlayerBase::onBufferingUpdate);
+ mBufferingEventPending = false;
+ mVideoLagEvent = new AwesomeEvent(this, &PreviewPlayerBase::onVideoLagUpdate);
+ mVideoEventPending = false;
+
+ mCheckAudioStatusEvent = new AwesomeEvent(
+ this, &PreviewPlayerBase::onCheckAudioStatus);
+
+ mAudioStatusEventPending = false;
+
+ reset();
+}
+
+PreviewPlayerBase::~PreviewPlayerBase() {
+ if (mQueueStarted) {
+ mQueue.stop();
+ }
+
+ reset();
+
+ mClient.disconnect();
+}
+
+void PreviewPlayerBase::cancelPlayerEvents(bool keepBufferingGoing) {
+ mQueue.cancelEvent(mVideoEvent->eventID());
+ mVideoEventPending = false;
+ mQueue.cancelEvent(mStreamDoneEvent->eventID());
+ mStreamDoneEventPending = false;
+ mQueue.cancelEvent(mCheckAudioStatusEvent->eventID());
+ mAudioStatusEventPending = false;
+ mQueue.cancelEvent(mVideoLagEvent->eventID());
+ mVideoLagEventPending = false;
+
+ if (!keepBufferingGoing) {
+ mQueue.cancelEvent(mBufferingEvent->eventID());
+ mBufferingEventPending = false;
+ }
+}
+
+void PreviewPlayerBase::setListener(const wp<MediaPlayerBase> &listener) {
+ Mutex::Autolock autoLock(mLock);
+ mListener = listener;
+}
+
+status_t PreviewPlayerBase::setDataSource(
+ const char *uri, const KeyedVector<String8, String8> *headers) {
+ Mutex::Autolock autoLock(mLock);
+ return setDataSource_l(uri, headers);
+}
+
+status_t PreviewPlayerBase::setDataSource_l(
+ const char *uri, const KeyedVector<String8, String8> *headers) {
+ reset_l();
+
+ mUri = uri;
+
+ if (headers) {
+ mUriHeaders = *headers;
+
+ ssize_t index = mUriHeaders.indexOfKey(String8("x-hide-urls-from-log"));
+ if (index >= 0) {
+ // Browser is in "incognito" mode, suppress logging URLs.
+
+ // This isn't something that should be passed to the server.
+ mUriHeaders.removeItemsAt(index);
+
+ mFlags |= INCOGNITO;
+ }
+ }
+
+ if (!(mFlags & INCOGNITO)) {
+ LOGI("setDataSource_l('%s')", mUri.string());
+ } else {
+ LOGI("setDataSource_l(URL suppressed)");
+ }
+
+ // The actual work will be done during preparation in the call to
+ // ::finishSetDataSource_l to avoid blocking the calling thread in
+ // setDataSource for any significant time.
+
+ return OK;
+}
+
+status_t PreviewPlayerBase::setDataSource(
+ int fd, int64_t offset, int64_t length) {
+ Mutex::Autolock autoLock(mLock);
+
+ reset_l();
+
+ sp<DataSource> dataSource = new FileSource(fd, offset, length);
+
+ status_t err = dataSource->initCheck();
+
+ if (err != OK) {
+ return err;
+ }
+
+ mFileSource = dataSource;
+
+ return setDataSource_l(dataSource);
+}
+
+status_t PreviewPlayerBase::setDataSource(const sp<IStreamSource> &source) {
+ return INVALID_OPERATION;
+}
+
+status_t PreviewPlayerBase::setDataSource_l(
+ const sp<DataSource> &dataSource) {
+ sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
+
+ if (extractor == NULL) {
+ return UNKNOWN_ERROR;
+ }
+
+ dataSource->getDrmInfo(mDecryptHandle, &mDrmManagerClient);
+ if (mDecryptHandle != NULL) {
+ CHECK(mDrmManagerClient);
+ if (RightsStatus::RIGHTS_VALID != mDecryptHandle->status) {
+ notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_DRM_NO_LICENSE);
+ }
+ }
+
+ return setDataSource_l(extractor);
+}
+
+status_t PreviewPlayerBase::setDataSource_l(const sp<MediaExtractor> &extractor) {
+ // Attempt to approximate overall stream bitrate by summing all
+ // tracks' individual bitrates, if not all of them advertise bitrate,
+ // we have to fail.
+
+ int64_t totalBitRate = 0;
+
+ for (size_t i = 0; i < extractor->countTracks(); ++i) {
+ sp<MetaData> meta = extractor->getTrackMetaData(i);
+
+ int32_t bitrate;
+ if (!meta->findInt32(kKeyBitRate, &bitrate)) {
+ totalBitRate = -1;
+ break;
+ }
+
+ totalBitRate += bitrate;
+ }
+
+ mBitrate = totalBitRate;
+
+ LOGV("mBitrate = %lld bits/sec", mBitrate);
+
+ 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)) {
+ setVideoSource(extractor->getTrack(i));
+ haveVideo = true;
+
+ // Set the presentation/display size
+ int32_t displayWidth, displayHeight;
+ bool success = meta->findInt32(kKeyDisplayWidth, &displayWidth);
+ if (success) {
+ success = meta->findInt32(kKeyDisplayHeight, &displayHeight);
+ }
+ if (success) {
+ mDisplayWidth = displayWidth;
+ mDisplayHeight = displayHeight;
+ }
+
+ } else if (!haveAudio && !strncasecmp(mime, "audio/", 6)) {
+ setAudioSource(extractor->getTrack(i));
+ haveAudio = true;
+
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
+ // Only do this for vorbis audio, none of the other audio
+ // formats even support this ringtone specific hack and
+ // retrieving the metadata on some extractors may turn out
+ // to be very expensive.
+ sp<MetaData> fileMeta = extractor->getMetaData();
+ int32_t loop;
+ if (fileMeta != NULL
+ && fileMeta->findInt32(kKeyAutoLoop, &loop) && loop != 0) {
+ mFlags |= AUTO_LOOPING;
+ }
+ }
+ }
+
+ if (haveAudio && haveVideo) {
+ break;
+ }
+ }
+
+ if (!haveAudio && !haveVideo) {
+ return UNKNOWN_ERROR;
+ }
+
+ mExtractorFlags = extractor->flags();
+
+ return OK;
+}
+
+void PreviewPlayerBase::reset() {
+ Mutex::Autolock autoLock(mLock);
+ reset_l();
+}
+
+void PreviewPlayerBase::reset_l() {
+ mDisplayWidth = 0;
+ mDisplayHeight = 0;
+
+ if (mDecryptHandle != NULL) {
+ mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+ Playback::STOP, 0);
+ mDecryptHandle = NULL;
+ mDrmManagerClient = NULL;
+ }
+
+ if (mFlags & PLAYING) {
+ uint32_t params = IMediaPlayerService::kBatteryDataTrackDecoder;
+ if ((mAudioSource != NULL) && (mAudioSource != mAudioTrack)) {
+ params |= IMediaPlayerService::kBatteryDataTrackAudio;
+ }
+ if (mVideoSource != NULL) {
+ params |= IMediaPlayerService::kBatteryDataTrackVideo;
+ }
+ addBatteryData(params);
+ }
+
+ if (mFlags & PREPARING) {
+ mFlags |= PREPARE_CANCELLED;
+ if (mConnectingDataSource != NULL) {
+ LOGI("interrupting the connection process");
+ mConnectingDataSource->disconnect();
+ } else if (mConnectingRTSPController != NULL) {
+ LOGI("interrupting the connection process");
+ mConnectingRTSPController->disconnect();
+ }
+
+ if (mFlags & PREPARING_CONNECTED) {
+ // We are basically done preparing, we're just buffering
+ // enough data to start playback, we can safely interrupt that.
+ finishAsyncPrepare_l();
+ }
+ }
+
+ while (mFlags & PREPARING) {
+ mPreparedCondition.wait(mLock);
+ }
+
+ cancelPlayerEvents();
+
+ mCachedSource.clear();
+ mAudioTrack.clear();
+ mVideoTrack.clear();
+
+ // Shutdown audio first, so that the respone to the reset request
+ // appears to happen instantaneously as far as the user is concerned
+ // If we did this later, audio would continue playing while we
+ // shutdown the video-related resources and the player appear to
+ // not be as responsive to a reset request.
+ if (mAudioPlayer == NULL && mAudioSource != NULL) {
+ // If we had an audio player, it would have effectively
+ // taken possession of the audio source and stopped it when
+ // _it_ is stopped. Otherwise this is still our responsibility.
+ mAudioSource->stop();
+ }
+ mAudioSource.clear();
+
+ mTimeSource = NULL;
+
+ delete mAudioPlayer;
+ mAudioPlayer = NULL;
+
+ mVideoRenderer.clear();
+
+ if (mRTSPController != NULL) {
+ mRTSPController->disconnect();
+ mRTSPController.clear();
+ }
+
+ if (mVideoSource != NULL) {
+ shutdownVideoDecoder_l();
+ }
+
+ mDurationUs = -1;
+ mFlags = 0;
+ mExtractorFlags = 0;
+ mTimeSourceDeltaUs = 0;
+ mVideoTimeUs = 0;
+
+ mSeeking = NO_SEEK;
+ mSeekNotificationSent = false;
+ mSeekTimeUs = 0;
+
+ mUri.setTo("");
+ mUriHeaders.clear();
+
+ mFileSource.clear();
+
+ mBitrate = -1;
+ mLastVideoTimeUs = -1;
+}
+
+void PreviewPlayerBase::notifyListener_l(int msg, int ext1, int ext2) {
+ if (mListener != NULL) {
+ sp<MediaPlayerBase> listener = mListener.promote();
+
+ if (listener != NULL) {
+ listener->sendEvent(msg, ext1, ext2);
+ }
+ }
+}
+
+bool PreviewPlayerBase::getBitrate(int64_t *bitrate) {
+ off64_t size;
+ if (mDurationUs >= 0 && mCachedSource != NULL
+ && mCachedSource->getSize(&size) == OK) {
+ *bitrate = size * 8000000ll / mDurationUs; // in bits/sec
+ return true;
+ }
+
+ if (mBitrate >= 0) {
+ *bitrate = mBitrate;
+ return true;
+ }
+
+ *bitrate = 0;
+
+ return false;
+}
+
+// Returns true iff cached duration is available/applicable.
+bool PreviewPlayerBase::getCachedDuration_l(int64_t *durationUs, bool *eos) {
+ int64_t bitrate;
+
+ if (mRTSPController != NULL) {
+ *durationUs = mRTSPController->getQueueDurationUs(eos);
+ return true;
+ } else if (mCachedSource != NULL && getBitrate(&bitrate)) {
+ status_t finalStatus;
+ size_t cachedDataRemaining = mCachedSource->approxDataRemaining(&finalStatus);
+ *durationUs = cachedDataRemaining * 8000000ll / bitrate;
+ *eos = (finalStatus != OK);
+ return true;
+ }
+
+ return false;
+}
+
+void PreviewPlayerBase::ensureCacheIsFetching_l() {
+ if (mCachedSource != NULL) {
+ mCachedSource->resumeFetchingIfNecessary();
+ }
+}
+
+void PreviewPlayerBase::onVideoLagUpdate() {
+ Mutex::Autolock autoLock(mLock);
+ if (!mVideoLagEventPending) {
+ return;
+ }
+ mVideoLagEventPending = false;
+
+ int64_t audioTimeUs = mAudioPlayer->getMediaTimeUs();
+ int64_t videoLateByUs = audioTimeUs - mVideoTimeUs;
+
+ if (!(mFlags & VIDEO_AT_EOS) && videoLateByUs > 300000ll) {
+ LOGV("video late by %lld ms.", videoLateByUs / 1000ll);
+
+ notifyListener_l(
+ MEDIA_INFO,
+ MEDIA_INFO_VIDEO_TRACK_LAGGING,
+ videoLateByUs / 1000ll);
+ }
+
+ postVideoLagEvent_l();
+}
+
+void PreviewPlayerBase::onBufferingUpdate() {
+ Mutex::Autolock autoLock(mLock);
+ if (!mBufferingEventPending) {
+ return;
+ }
+ mBufferingEventPending = false;
+
+ if (mCachedSource != NULL) {
+ status_t finalStatus;
+ size_t cachedDataRemaining = mCachedSource->approxDataRemaining(&finalStatus);
+ bool eos = (finalStatus != OK);
+
+ if (eos) {
+ if (finalStatus == ERROR_END_OF_STREAM) {
+ notifyListener_l(MEDIA_BUFFERING_UPDATE, 100);
+ }
+ if (mFlags & PREPARING) {
+ LOGV("cache has reached EOS, prepare is done.");
+ finishAsyncPrepare_l();
+ }
+ } else {
+ int64_t bitrate;
+ if (getBitrate(&bitrate)) {
+ size_t cachedSize = mCachedSource->cachedSize();
+ int64_t cachedDurationUs = cachedSize * 8000000ll / bitrate;
+
+ int percentage = 100.0 * (double)cachedDurationUs / mDurationUs;
+ if (percentage > 100) {
+ percentage = 100;
+ }
+
+ notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage);
+ } else {
+ // We don't know the bitrate of the stream, use absolute size
+ // limits to maintain the cache.
+
+ if ((mFlags & PLAYING) && !eos
+ && (cachedDataRemaining < kLowWaterMarkBytes)) {
+ LOGI("cache is running low (< %d) , pausing.",
+ kLowWaterMarkBytes);
+ mFlags |= CACHE_UNDERRUN;
+ pause_l();
+ ensureCacheIsFetching_l();
+ notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
+ } else if (eos || cachedDataRemaining > kHighWaterMarkBytes) {
+ if (mFlags & CACHE_UNDERRUN) {
+ LOGI("cache has filled up (> %d), resuming.",
+ kHighWaterMarkBytes);
+ mFlags &= ~CACHE_UNDERRUN;
+ play_l();
+ notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
+ } else if (mFlags & PREPARING) {
+ LOGV("cache has filled up (> %d), prepare is done",
+ kHighWaterMarkBytes);
+ finishAsyncPrepare_l();
+ }
+ }
+ }
+ }
+ }
+
+ int64_t cachedDurationUs;
+ bool eos;
+ if (getCachedDuration_l(&cachedDurationUs, &eos)) {
+ LOGV("cachedDurationUs = %.2f secs, eos=%d",
+ cachedDurationUs / 1E6, eos);
+
+ int64_t highWaterMarkUs =
+ (mRTSPController != NULL) ? kHighWaterMarkRTSPUs : kHighWaterMarkUs;
+
+ if ((mFlags & PLAYING) && !eos
+ && (cachedDurationUs < kLowWaterMarkUs)) {
+ LOGI("cache is running low (%.2f secs) , pausing.",
+ cachedDurationUs / 1E6);
+ mFlags |= CACHE_UNDERRUN;
+ pause_l();
+ ensureCacheIsFetching_l();
+ notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
+ } else if (eos || cachedDurationUs > highWaterMarkUs) {
+ if (mFlags & CACHE_UNDERRUN) {
+ LOGI("cache has filled up (%.2f secs), resuming.",
+ cachedDurationUs / 1E6);
+ mFlags &= ~CACHE_UNDERRUN;
+ play_l();
+ notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
+ } else if (mFlags & PREPARING) {
+ LOGV("cache has filled up (%.2f secs), prepare is done",
+ cachedDurationUs / 1E6);
+ finishAsyncPrepare_l();
+ }
+ }
+ }
+
+ postBufferingEvent_l();
+}
+
+void PreviewPlayerBase::onStreamDone() {
+ // Posted whenever any stream finishes playing.
+
+ Mutex::Autolock autoLock(mLock);
+ if (!mStreamDoneEventPending) {
+ return;
+ }
+ mStreamDoneEventPending = false;
+
+ if (mStreamDoneStatus != ERROR_END_OF_STREAM) {
+ LOGV("MEDIA_ERROR %d", mStreamDoneStatus);
+
+ notifyListener_l(
+ MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, mStreamDoneStatus);
+
+ pause_l(true /* at eos */);
+
+ mFlags |= AT_EOS;
+ return;
+ }
+
+ const bool allDone =
+ (mVideoSource == NULL || (mFlags & VIDEO_AT_EOS))
+ && (mAudioSource == NULL || (mFlags & AUDIO_AT_EOS));
+
+ if (!allDone) {
+ return;
+ }
+
+ if (mFlags & (LOOPING | AUTO_LOOPING)) {
+ seekTo_l(0);
+
+ if (mVideoSource != NULL) {
+ postVideoEvent_l();
+ }
+ } else {
+ LOGV("MEDIA_PLAYBACK_COMPLETE");
+ notifyListener_l(MEDIA_PLAYBACK_COMPLETE);
+
+ pause_l(true /* at eos */);
+
+ mFlags |= AT_EOS;
+ }
+}
+
+status_t PreviewPlayerBase::play() {
+ Mutex::Autolock autoLock(mLock);
+
+ mFlags &= ~CACHE_UNDERRUN;
+
+ return play_l();
+}
+
+status_t PreviewPlayerBase::play_l() {
+ mFlags &= ~SEEK_PREVIEW;
+
+ if (mFlags & PLAYING) {
+ return OK;
+ }
+
+ if (!(mFlags & PREPARED)) {
+ status_t err = prepare_l();
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ mFlags |= PLAYING;
+ mFlags |= FIRST_FRAME;
+
+ if (mDecryptHandle != NULL) {
+ int64_t position;
+ getPosition(&position);
+ mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+ Playback::START, position / 1000);
+ }
+
+ if (mAudioSource != NULL) {
+ if (mAudioPlayer == NULL) {
+ if (mAudioSink != NULL) {
+ mAudioPlayer = new AudioPlayerBase(mAudioSink, this);
+ mAudioPlayer->setSource(mAudioSource);
+
+ mTimeSource = mAudioPlayer;
+
+ // If there was a seek request before we ever started,
+ // honor the request now.
+ // Make sure to do this before starting the audio player
+ // to avoid a race condition.
+ seekAudioIfNecessary_l();
+ }
+ }
+
+ CHECK(!(mFlags & AUDIO_RUNNING));
+
+ if (mVideoSource == NULL) {
+ status_t err = startAudioPlayer_l();
+
+ if (err != OK) {
+ delete mAudioPlayer;
+ mAudioPlayer = NULL;
+
+ mFlags &= ~(PLAYING | FIRST_FRAME);
+
+ if (mDecryptHandle != NULL) {
+ mDrmManagerClient->setPlaybackStatus(
+ mDecryptHandle, Playback::STOP, 0);
+ }
+
+ return err;
+ }
+ }
+ }
+
+ if (mTimeSource == NULL && mAudioPlayer == NULL) {
+ mTimeSource = &mSystemTimeSource;
+ }
+
+ if (mVideoSource != NULL) {
+ // Kick off video playback
+ postVideoEvent_l();
+
+ if (mAudioSource != NULL && mVideoSource != NULL) {
+ postVideoLagEvent_l();
+ }
+ }
+
+ if (mFlags & AT_EOS) {
+ // Legacy behaviour, if a stream finishes playing and then
+ // is started again, we play from the start...
+ seekTo_l(0);
+ }
+
+ uint32_t params = IMediaPlayerService::kBatteryDataCodecStarted
+ | IMediaPlayerService::kBatteryDataTrackDecoder;
+ if ((mAudioSource != NULL) && (mAudioSource != mAudioTrack)) {
+ params |= IMediaPlayerService::kBatteryDataTrackAudio;
+ }
+ if (mVideoSource != NULL) {
+ params |= IMediaPlayerService::kBatteryDataTrackVideo;
+ }
+ addBatteryData(params);
+
+ return OK;
+}
+
+status_t PreviewPlayerBase::startAudioPlayer_l() {
+ CHECK(!(mFlags & AUDIO_RUNNING));
+
+ if (mAudioSource == NULL || mAudioPlayer == NULL) {
+ return OK;
+ }
+
+ if (!(mFlags & AUDIOPLAYER_STARTED)) {
+ mFlags |= AUDIOPLAYER_STARTED;
+
+ // We've already started the MediaSource in order to enable
+ // the prefetcher to read its data.
+ status_t err = mAudioPlayer->start(
+ true /* sourceAlreadyStarted */);
+
+ if (err != OK) {
+ notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
+ return err;
+ }
+ } else {
+ mAudioPlayer->resume();
+ }
+
+ mFlags |= AUDIO_RUNNING;
+
+ mWatchForAudioEOS = true;
+
+ return OK;
+}
+
+void PreviewPlayerBase::notifyVideoSize_l() {
+ sp<MetaData> meta = mVideoSource->getFormat();
+
+ int32_t cropLeft, cropTop, cropRight, cropBottom;
+ if (!meta->findRect(
+ kKeyCropRect, &cropLeft, &cropTop, &cropRight, &cropBottom)) {
+ int32_t width, height;
+ CHECK(meta->findInt32(kKeyWidth, &width));
+ CHECK(meta->findInt32(kKeyHeight, &height));
+
+ cropLeft = cropTop = 0;
+ cropRight = width - 1;
+ cropBottom = height - 1;
+
+ LOGV("got dimensions only %d x %d", width, height);
+ } else {
+ LOGV("got crop rect %d, %d, %d, %d",
+ cropLeft, cropTop, cropRight, cropBottom);
+ }
+
+ int32_t displayWidth;
+ if (meta->findInt32(kKeyDisplayWidth, &displayWidth)) {
+ LOGV("Display width changed (%d=>%d)", mDisplayWidth, displayWidth);
+ mDisplayWidth = displayWidth;
+ }
+ int32_t displayHeight;
+ if (meta->findInt32(kKeyDisplayHeight, &displayHeight)) {
+ LOGV("Display height changed (%d=>%d)", mDisplayHeight, displayHeight);
+ mDisplayHeight = displayHeight;
+ }
+
+ int32_t usableWidth = cropRight - cropLeft + 1;
+ int32_t usableHeight = cropBottom - cropTop + 1;
+ if (mDisplayWidth != 0) {
+ usableWidth = mDisplayWidth;
+ }
+ if (mDisplayHeight != 0) {
+ usableHeight = mDisplayHeight;
+ }
+
+ int32_t rotationDegrees;
+ if (!mVideoTrack->getFormat()->findInt32(
+ kKeyRotation, &rotationDegrees)) {
+ rotationDegrees = 0;
+ }
+
+ if (rotationDegrees == 90 || rotationDegrees == 270) {
+ notifyListener_l(
+ MEDIA_SET_VIDEO_SIZE, usableHeight, usableWidth);
+ } else {
+ notifyListener_l(
+ MEDIA_SET_VIDEO_SIZE, usableWidth, usableHeight);
+ }
+}
+
+void PreviewPlayerBase::initRenderer_l() {
+ if (mNativeWindow == NULL) {
+ return;
+ }
+
+ 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));
+
+ int32_t rotationDegrees;
+ if (!mVideoTrack->getFormat()->findInt32(
+ kKeyRotation, &rotationDegrees)) {
+ rotationDegrees = 0;
+ }
+
+ mVideoRenderer.clear();
+
+ // Must ensure that mVideoRenderer's destructor is actually executed
+ // before creating a new one.
+ IPCThreadState::self()->flushCommands();
+
+ if (USE_SURFACE_ALLOC && strncmp(component, "OMX.", 4) == 0) {
+ // Hardware decoders avoid the CPU color conversion by decoding
+ // directly to ANativeBuffers, so we must use a renderer that
+ // just pushes those buffers to the ANativeWindow.
+ mVideoRenderer =
+ new AwesomeNativeWindowRenderer(mNativeWindow, rotationDegrees);
+ } else {
+ // Other decoders are instantiated locally and as a consequence
+ // allocate their buffers in local address space. This renderer
+ // then performs a color conversion and copy to get the data
+ // into the ANativeBuffer.
+ mVideoRenderer = new AwesomeLocalRenderer(mNativeWindow, meta);
+ }
+}
+
+status_t PreviewPlayerBase::pause() {
+ Mutex::Autolock autoLock(mLock);
+
+ mFlags &= ~CACHE_UNDERRUN;
+
+ return pause_l();
+}
+
+status_t PreviewPlayerBase::pause_l(bool at_eos) {
+ if (!(mFlags & PLAYING)) {
+ return OK;
+ }
+
+ cancelPlayerEvents(true /* keepBufferingGoing */);
+
+ if (mAudioPlayer != NULL && (mFlags & AUDIO_RUNNING)) {
+ if (at_eos) {
+ // If we played the audio stream to completion we
+ // want to make sure that all samples remaining in the audio
+ // track's queue are played out.
+ mAudioPlayer->pause(true /* playPendingSamples */);
+ } else {
+ mAudioPlayer->pause();
+ }
+
+ mFlags &= ~AUDIO_RUNNING;
+ }
+
+ mFlags &= ~PLAYING;
+
+ if (mDecryptHandle != NULL) {
+ mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+ Playback::PAUSE, 0);
+ }
+
+ uint32_t params = IMediaPlayerService::kBatteryDataTrackDecoder;
+ if ((mAudioSource != NULL) && (mAudioSource != mAudioTrack)) {
+ params |= IMediaPlayerService::kBatteryDataTrackAudio;
+ }
+ if (mVideoSource != NULL) {
+ params |= IMediaPlayerService::kBatteryDataTrackVideo;
+ }
+
+ addBatteryData(params);
+
+ return OK;
+}
+
+bool PreviewPlayerBase::isPlaying() const {
+ return (mFlags & PLAYING) || (mFlags & CACHE_UNDERRUN);
+}
+
+void PreviewPlayerBase::setSurface(const sp<Surface> &surface) {
+ Mutex::Autolock autoLock(mLock);
+
+ mSurface = surface;
+ setNativeWindow_l(surface);
+}
+
+void PreviewPlayerBase::setSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture) {
+ Mutex::Autolock autoLock(mLock);
+
+ mSurface.clear();
+ if (surfaceTexture != NULL) {
+ setNativeWindow_l(new SurfaceTextureClient(surfaceTexture));
+ }
+}
+
+void PreviewPlayerBase::shutdownVideoDecoder_l() {
+ if (mVideoBuffer) {
+ mVideoBuffer->release();
+ mVideoBuffer = NULL;
+ }
+
+ mVideoSource->stop();
+
+ // The following hack is necessary to ensure that the OMX
+ // component is completely released by the time we may try
+ // to instantiate it again.
+ wp<MediaSource> tmp = mVideoSource;
+ mVideoSource.clear();
+ while (tmp.promote() != NULL) {
+ usleep(1000);
+ }
+ IPCThreadState::self()->flushCommands();
+}
+
+void PreviewPlayerBase::setNativeWindow_l(const sp<ANativeWindow> &native) {
+ mNativeWindow = native;
+
+ if (mVideoSource == NULL) {
+ return;
+ }
+
+ LOGI("attempting to reconfigure to use new surface");
+
+ bool wasPlaying = (mFlags & PLAYING) != 0;
+
+ pause_l();
+ mVideoRenderer.clear();
+
+ shutdownVideoDecoder_l();
+
+ CHECK_EQ(initVideoDecoder(), (status_t)OK);
+
+ if (mLastVideoTimeUs >= 0) {
+ mSeeking = SEEK;
+ mSeekNotificationSent = true;
+ mSeekTimeUs = mLastVideoTimeUs;
+ mFlags &= ~(AT_EOS | AUDIO_AT_EOS | VIDEO_AT_EOS);
+ }
+
+ if (wasPlaying) {
+ play_l();
+ }
+}
+
+void PreviewPlayerBase::setAudioSink(
+ const sp<MediaPlayerBase::AudioSink> &audioSink) {
+ Mutex::Autolock autoLock(mLock);
+
+ mAudioSink = audioSink;
+}
+
+status_t PreviewPlayerBase::setLooping(bool shouldLoop) {
+ Mutex::Autolock autoLock(mLock);
+
+ mFlags = mFlags & ~LOOPING;
+
+ if (shouldLoop) {
+ mFlags |= LOOPING;
+ }
+
+ return OK;
+}
+
+status_t PreviewPlayerBase::getDuration(int64_t *durationUs) {
+ Mutex::Autolock autoLock(mMiscStateLock);
+
+ if (mDurationUs < 0) {
+ return UNKNOWN_ERROR;
+ }
+
+ *durationUs = mDurationUs;
+
+ return OK;
+}
+
+status_t PreviewPlayerBase::getPosition(int64_t *positionUs) {
+ if (mRTSPController != NULL) {
+ *positionUs = mRTSPController->getNormalPlayTimeUs();
+ }
+ else if (mSeeking != NO_SEEK) {
+ *positionUs = mSeekTimeUs;
+ } else if (mVideoSource != NULL
+ && (mAudioPlayer == NULL || !(mFlags & VIDEO_AT_EOS))) {
+ Mutex::Autolock autoLock(mMiscStateLock);
+ *positionUs = mVideoTimeUs;
+ } else if (mAudioPlayer != NULL) {
+ *positionUs = mAudioPlayer->getMediaTimeUs();
+ } else {
+ *positionUs = 0;
+ }
+
+ return OK;
+}
+
+status_t PreviewPlayerBase::seekTo(int64_t timeUs) {
+ if (mExtractorFlags & MediaExtractor::CAN_SEEK) {
+ Mutex::Autolock autoLock(mLock);
+ return seekTo_l(timeUs);
+ }
+
+ return OK;
+}
+
+// static
+void PreviewPlayerBase::OnRTSPSeekDoneWrapper(void *cookie) {
+ static_cast<PreviewPlayerBase *>(cookie)->onRTSPSeekDone();
+}
+
+void PreviewPlayerBase::onRTSPSeekDone() {
+ notifyListener_l(MEDIA_SEEK_COMPLETE);
+ mSeekNotificationSent = true;
+}
+
+status_t PreviewPlayerBase::seekTo_l(int64_t timeUs) {
+ if (mRTSPController != NULL) {
+ mRTSPController->seekAsync(timeUs, OnRTSPSeekDoneWrapper, this);
+ return OK;
+ }
+
+ if (mFlags & CACHE_UNDERRUN) {
+ mFlags &= ~CACHE_UNDERRUN;
+ play_l();
+ }
+
+ if ((mFlags & PLAYING) && mVideoSource != NULL && (mFlags & VIDEO_AT_EOS)) {
+ // Video playback completed before, there's no pending
+ // video event right now. In order for this new seek
+ // to be honored, we need to post one.
+
+ postVideoEvent_l();
+ }
+
+ mSeeking = SEEK;
+ mSeekNotificationSent = false;
+ mSeekTimeUs = timeUs;
+ mFlags &= ~(AT_EOS | AUDIO_AT_EOS | VIDEO_AT_EOS);
+
+ seekAudioIfNecessary_l();
+
+ if (!(mFlags & PLAYING)) {
+ LOGV("seeking while paused, sending SEEK_COMPLETE notification"
+ " immediately.");
+
+ notifyListener_l(MEDIA_SEEK_COMPLETE);
+ mSeekNotificationSent = true;
+
+ if ((mFlags & PREPARED) && mVideoSource != NULL) {
+ mFlags |= SEEK_PREVIEW;
+ postVideoEvent_l();
+ }
+ }
+
+ return OK;
+}
+
+void PreviewPlayerBase::seekAudioIfNecessary_l() {
+ if (mSeeking != NO_SEEK && mVideoSource == NULL && mAudioPlayer != NULL) {
+ mAudioPlayer->seekTo(mSeekTimeUs);
+
+ mWatchForAudioSeekComplete = true;
+ mWatchForAudioEOS = true;
+
+ if (mDecryptHandle != NULL) {
+ mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+ Playback::PAUSE, 0);
+ mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+ Playback::START, mSeekTimeUs / 1000);
+ }
+ }
+}
+
+void PreviewPlayerBase::setAudioSource(sp<MediaSource> source) {
+ CHECK(source != NULL);
+
+ mAudioTrack = source;
+}
+
+status_t PreviewPlayerBase::initAudioDecoder() {
+ sp<MetaData> meta = mAudioTrack->getFormat();
+
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) {
+ mAudioSource = mAudioTrack;
+ } else {
+ mAudioSource = OMXCodec::Create(
+ mClient.interface(), mAudioTrack->getFormat(),
+ false, // createEncoder
+ mAudioTrack);
+ }
+
+ if (mAudioSource != NULL) {
+ int64_t durationUs;
+ if (mAudioTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
+ Mutex::Autolock autoLock(mMiscStateLock);
+ if (mDurationUs < 0 || durationUs > mDurationUs) {
+ mDurationUs = durationUs;
+ }
+ }
+
+ status_t err = mAudioSource->start();
+
+ if (err != OK) {
+ mAudioSource.clear();
+ return err;
+ }
+ } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_QCELP)) {
+ // For legacy reasons we're simply going to ignore the absence
+ // of an audio decoder for QCELP instead of aborting playback
+ // altogether.
+ return OK;
+ }
+
+ return mAudioSource != NULL ? OK : UNKNOWN_ERROR;
+}
+
+void PreviewPlayerBase::setVideoSource(sp<MediaSource> source) {
+ CHECK(source != NULL);
+
+ mVideoTrack = source;
+}
+
+status_t PreviewPlayerBase::initVideoDecoder(uint32_t flags) {
+
+ // Either the application or the DRM system can independently say
+ // that there must be a hardware-protected path to an external video sink.
+ // For now we always require a hardware-protected path to external video sink
+ // if content is DRMed, but eventually this could be optional per DRM agent.
+ // When the application wants protection, then
+ // (USE_SURFACE_ALLOC && (mSurface != 0) &&
+ // (mSurface->getFlags() & ISurfaceComposer::eProtectedByApp))
+ // will be true, but that part is already handled by SurfaceFlinger.
+
+#ifdef DEBUG_HDCP
+ // For debugging, we allow a system property to control the protected usage.
+ // In case of uninitialized or unexpected property, we default to "DRM only".
+ bool setProtectionBit = false;
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("persist.sys.hdcp_checking", value, NULL)) {
+ if (!strcmp(value, "never")) {
+ // nop
+ } else if (!strcmp(value, "always")) {
+ setProtectionBit = true;
+ } else if (!strcmp(value, "drm-only")) {
+ if (mDecryptHandle != NULL) {
+ setProtectionBit = true;
+ }
+ // property value is empty, or unexpected value
+ } else {
+ if (mDecryptHandle != NULL) {
+ setProtectionBit = true;
+ }
+ }
+ // can' read property value
+ } else {
+ if (mDecryptHandle != NULL) {
+ setProtectionBit = true;
+ }
+ }
+ // note that usage bit is already cleared, so no need to clear it in the "else" case
+ if (setProtectionBit) {
+ flags |= OMXCodec::kEnableGrallocUsageProtected;
+ }
+#else
+ if (mDecryptHandle != NULL) {
+ flags |= OMXCodec::kEnableGrallocUsageProtected;
+ }
+#endif
+ LOGV("initVideoDecoder flags=0x%x", flags);
+ mVideoSource = OMXCodec::Create(
+ mClient.interface(), mVideoTrack->getFormat(),
+ false, // createEncoder
+ mVideoTrack,
+ NULL, flags, USE_SURFACE_ALLOC ? mNativeWindow : NULL);
+
+ if (mVideoSource != NULL) {
+ int64_t durationUs;
+ if (mVideoTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
+ Mutex::Autolock autoLock(mMiscStateLock);
+ if (mDurationUs < 0 || durationUs > mDurationUs) {
+ mDurationUs = durationUs;
+ }
+ }
+
+ status_t err = mVideoSource->start();
+
+ if (err != OK) {
+ mVideoSource.clear();
+ return err;
+ }
+ }
+
+ return mVideoSource != NULL ? OK : UNKNOWN_ERROR;
+}
+
+void PreviewPlayerBase::finishSeekIfNecessary(int64_t videoTimeUs) {
+ if (mSeeking == SEEK_VIDEO_ONLY) {
+ mSeeking = NO_SEEK;
+ return;
+ }
+
+ if (mSeeking == NO_SEEK || (mFlags & SEEK_PREVIEW)) {
+ return;
+ }
+
+ if (mAudioPlayer != NULL) {
+ LOGV("seeking audio to %lld us (%.2f secs).", videoTimeUs, videoTimeUs / 1E6);
+
+ // If we don't have a video time, seek audio to the originally
+ // requested seek time instead.
+
+ mAudioPlayer->seekTo(videoTimeUs < 0 ? mSeekTimeUs : videoTimeUs);
+ mWatchForAudioSeekComplete = true;
+ mWatchForAudioEOS = true;
+ } else if (!mSeekNotificationSent) {
+ // If we're playing video only, report seek complete now,
+ // otherwise audio player will notify us later.
+ notifyListener_l(MEDIA_SEEK_COMPLETE);
+ mSeekNotificationSent = true;
+ }
+
+ mFlags |= FIRST_FRAME;
+ mSeeking = NO_SEEK;
+
+ if (mDecryptHandle != NULL) {
+ mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+ Playback::PAUSE, 0);
+ mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
+ Playback::START, videoTimeUs / 1000);
+ }
+}
+
+void PreviewPlayerBase::onVideoEvent() {
+ Mutex::Autolock autoLock(mLock);
+ if (!mVideoEventPending) {
+ // The event has been cancelled in reset_l() but had already
+ // been scheduled for execution at that time.
+ return;
+ }
+ mVideoEventPending = false;
+
+ if (mSeeking != NO_SEEK) {
+ if (mVideoBuffer) {
+ mVideoBuffer->release();
+ mVideoBuffer = NULL;
+ }
+
+ if (mSeeking == SEEK && mCachedSource != NULL && mAudioSource != NULL
+ && !(mFlags & SEEK_PREVIEW)) {
+ // We're going to seek the video source first, followed by
+ // the audio source.
+ // In order to avoid jumps in the DataSource offset caused by
+ // the audio codec prefetching data from the old locations
+ // while the video codec is already reading data from the new
+ // locations, we'll "pause" the audio source, causing it to
+ // stop reading input data until a subsequent seek.
+
+ if (mAudioPlayer != NULL && (mFlags & AUDIO_RUNNING)) {
+ mAudioPlayer->pause();
+
+ mFlags &= ~AUDIO_RUNNING;
+ }
+ mAudioSource->pause();
+ }
+ }
+
+ if (!mVideoBuffer) {
+ MediaSource::ReadOptions options;
+ if (mSeeking != NO_SEEK) {
+ LOGV("seeking to %lld us (%.2f secs)", mSeekTimeUs, mSeekTimeUs / 1E6);
+
+ options.setSeekTo(
+ mSeekTimeUs,
+ mSeeking == SEEK_VIDEO_ONLY
+ ? MediaSource::ReadOptions::SEEK_NEXT_SYNC
+ : MediaSource::ReadOptions::SEEK_CLOSEST_SYNC);
+ }
+ for (;;) {
+ status_t err = mVideoSource->read(&mVideoBuffer, &options);
+ options.clearSeekTo();
+
+ if (err != OK) {
+ CHECK(mVideoBuffer == NULL);
+
+ if (err == INFO_FORMAT_CHANGED) {
+ LOGV("VideoSource signalled format change.");
+
+ notifyVideoSize_l();
+
+ if (mVideoRenderer != NULL) {
+ mVideoRendererIsPreview = false;
+ initRenderer_l();
+ }
+ continue;
+ }
+
+ // So video playback is complete, but we may still have
+ // a seek request pending that needs to be applied
+ // to the audio track.
+ if (mSeeking != NO_SEEK) {
+ LOGV("video stream ended while seeking!");
+ }
+ finishSeekIfNecessary(-1);
+
+ if (mAudioPlayer != NULL
+ && !(mFlags & (AUDIO_RUNNING | SEEK_PREVIEW))) {
+ startAudioPlayer_l();
+ }
+
+ mFlags |= VIDEO_AT_EOS;
+ postStreamDoneEvent_l(err);
+ return;
+ }
+
+ if (mVideoBuffer->range_length() == 0) {
+ // Some decoders, notably the PV AVC software decoder
+ // return spurious empty buffers that we just want to ignore.
+
+ mVideoBuffer->release();
+ mVideoBuffer = NULL;
+ continue;
+ }
+
+ break;
+ }
+ }
+
+ int64_t timeUs;
+ CHECK(mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
+
+ mLastVideoTimeUs = timeUs;
+
+ if (mSeeking == SEEK_VIDEO_ONLY) {
+ if (mSeekTimeUs > timeUs) {
+ LOGI("XXX mSeekTimeUs = %lld us, timeUs = %lld us",
+ mSeekTimeUs, timeUs);
+ }
+ }
+
+ {
+ Mutex::Autolock autoLock(mMiscStateLock);
+ mVideoTimeUs = timeUs;
+ }
+
+ SeekType wasSeeking = mSeeking;
+ finishSeekIfNecessary(timeUs);
+
+ if (mAudioPlayer != NULL && !(mFlags & (AUDIO_RUNNING | SEEK_PREVIEW))) {
+ status_t err = startAudioPlayer_l();
+ if (err != OK) {
+ LOGE("Startung the audio player failed w/ err %d", err);
+ return;
+ }
+ }
+
+ TimeSource *ts = (mFlags & AUDIO_AT_EOS) ? &mSystemTimeSource : mTimeSource;
+
+ if (mFlags & FIRST_FRAME) {
+ mFlags &= ~FIRST_FRAME;
+ mTimeSourceDeltaUs = ts->getRealTimeUs() - timeUs;
+ }
+
+ int64_t realTimeUs, mediaTimeUs;
+ if (!(mFlags & AUDIO_AT_EOS) && mAudioPlayer != NULL
+ && mAudioPlayer->getMediaTimeMapping(&realTimeUs, &mediaTimeUs)) {
+ mTimeSourceDeltaUs = realTimeUs - mediaTimeUs;
+ }
+
+ if (wasSeeking == SEEK_VIDEO_ONLY) {
+ int64_t nowUs = ts->getRealTimeUs() - mTimeSourceDeltaUs;
+
+ int64_t latenessUs = nowUs - timeUs;
+
+ if (latenessUs > 0) {
+ LOGI("after SEEK_VIDEO_ONLY we're late by %.2f secs", latenessUs / 1E6);
+ }
+ }
+
+ if (wasSeeking == NO_SEEK) {
+ // Let's display the first frame after seeking right away.
+
+ int64_t nowUs = ts->getRealTimeUs() - mTimeSourceDeltaUs;
+
+ int64_t latenessUs = nowUs - timeUs;
+
+ if (latenessUs > 500000ll
+ && mRTSPController == NULL
+ && mAudioPlayer != NULL
+ && mAudioPlayer->getMediaTimeMapping(
+ &realTimeUs, &mediaTimeUs)) {
+ LOGI("we're much too late (%.2f secs), video skipping ahead",
+ latenessUs / 1E6);
+
+ mVideoBuffer->release();
+ mVideoBuffer = NULL;
+
+ mSeeking = SEEK_VIDEO_ONLY;
+ mSeekTimeUs = mediaTimeUs;
+
+ postVideoEvent_l();
+ return;
+ }
+
+ if (latenessUs > 40000) {
+ // We're more than 40ms late.
+ LOGV("we're late by %lld us (%.2f secs), dropping frame",
+ latenessUs, latenessUs / 1E6);
+ mVideoBuffer->release();
+ mVideoBuffer = NULL;
+
+ postVideoEvent_l();
+ return;
+ }
+
+ if (latenessUs < -10000) {
+ // We're more than 10ms early.
+
+ postVideoEvent_l(10000);
+ return;
+ }
+ }
+
+ if (mVideoRendererIsPreview || mVideoRenderer == NULL) {
+ mVideoRendererIsPreview = false;
+
+ initRenderer_l();
+ }
+
+ if (mVideoRenderer != NULL) {
+ mVideoRenderer->render(mVideoBuffer);
+ }
+
+ mVideoBuffer->release();
+ mVideoBuffer = NULL;
+
+ if (wasSeeking != NO_SEEK && (mFlags & SEEK_PREVIEW)) {
+ mFlags &= ~SEEK_PREVIEW;
+ return;
+ }
+
+ postVideoEvent_l();
+}
+
+void PreviewPlayerBase::postVideoEvent_l(int64_t delayUs) {
+ if (mVideoEventPending) {
+ return;
+ }
+
+ mVideoEventPending = true;
+ mQueue.postEventWithDelay(mVideoEvent, delayUs < 0 ? 10000 : delayUs);
+}
+
+void PreviewPlayerBase::postStreamDoneEvent_l(status_t status) {
+ if (mStreamDoneEventPending) {
+ return;
+ }
+ mStreamDoneEventPending = true;
+
+ mStreamDoneStatus = status;
+ mQueue.postEvent(mStreamDoneEvent);
+}
+
+void PreviewPlayerBase::postBufferingEvent_l() {
+ if (mBufferingEventPending) {
+ return;
+ }
+ mBufferingEventPending = true;
+ mQueue.postEventWithDelay(mBufferingEvent, 1000000ll);
+}
+
+void PreviewPlayerBase::postVideoLagEvent_l() {
+ if (mVideoLagEventPending) {
+ return;
+ }
+ mVideoLagEventPending = true;
+ mQueue.postEventWithDelay(mVideoLagEvent, 1000000ll);
+}
+
+void PreviewPlayerBase::postCheckAudioStatusEvent_l(int64_t delayUs) {
+ if (mAudioStatusEventPending) {
+ return;
+ }
+ mAudioStatusEventPending = true;
+ mQueue.postEventWithDelay(mCheckAudioStatusEvent, delayUs);
+}
+
+void PreviewPlayerBase::onCheckAudioStatus() {
+ Mutex::Autolock autoLock(mLock);
+ if (!mAudioStatusEventPending) {
+ // Event was dispatched and while we were blocking on the mutex,
+ // has already been cancelled.
+ return;
+ }
+
+ mAudioStatusEventPending = false;
+
+ if (mWatchForAudioSeekComplete && !mAudioPlayer->isSeeking()) {
+ mWatchForAudioSeekComplete = false;
+
+ if (!mSeekNotificationSent) {
+ notifyListener_l(MEDIA_SEEK_COMPLETE);
+ mSeekNotificationSent = true;
+ }
+
+ mSeeking = NO_SEEK;
+ }
+
+ status_t finalStatus;
+ if (mWatchForAudioEOS && mAudioPlayer->reachedEOS(&finalStatus)) {
+ mWatchForAudioEOS = false;
+ mFlags |= AUDIO_AT_EOS;
+ mFlags |= FIRST_FRAME;
+ postStreamDoneEvent_l(finalStatus);
+ }
+}
+
+status_t PreviewPlayerBase::prepare() {
+ Mutex::Autolock autoLock(mLock);
+ return prepare_l();
+}
+
+status_t PreviewPlayerBase::prepare_l() {
+ if (mFlags & PREPARED) {
+ return OK;
+ }
+
+ if (mFlags & PREPARING) {
+ return UNKNOWN_ERROR;
+ }
+
+ mIsAsyncPrepare = false;
+ status_t err = prepareAsync_l();
+
+ if (err != OK) {
+ return err;
+ }
+
+ while (mFlags & PREPARING) {
+ mPreparedCondition.wait(mLock);
+ }
+
+ return mPrepareResult;
+}
+
+status_t PreviewPlayerBase::prepareAsync() {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mFlags & PREPARING) {
+ return UNKNOWN_ERROR; // async prepare already pending
+ }
+
+ mIsAsyncPrepare = true;
+ return prepareAsync_l();
+}
+
+status_t PreviewPlayerBase::prepareAsync_l() {
+ if (mFlags & PREPARING) {
+ return UNKNOWN_ERROR; // async prepare already pending
+ }
+
+ if (!mQueueStarted) {
+ mQueue.start();
+ mQueueStarted = true;
+ }
+
+ mFlags |= PREPARING;
+ mAsyncPrepareEvent = new AwesomeEvent(
+ this, &PreviewPlayerBase::onPrepareAsyncEvent);
+
+ mQueue.postEvent(mAsyncPrepareEvent);
+
+ return OK;
+}
+
+status_t PreviewPlayerBase::finishSetDataSource_l() {
+ sp<DataSource> dataSource;
+
+ if (!strncasecmp("http://", mUri.string(), 7)
+ || !strncasecmp("https://", mUri.string(), 8)) {
+ mConnectingDataSource = HTTPBase::Create(
+ (mFlags & INCOGNITO)
+ ? HTTPBase::kFlagIncognito
+ : 0);
+
+ mLock.unlock();
+ status_t err = mConnectingDataSource->connect(mUri, &mUriHeaders);
+ mLock.lock();
+
+ if (err != OK) {
+ mConnectingDataSource.clear();
+
+ LOGI("mConnectingDataSource->connect() returned %d", err);
+ return err;
+ }
+
+#if 0
+ mCachedSource = new NuCachedSource2(
+ new ThrottledSource(
+ mConnectingDataSource, 50 * 1024 /* bytes/sec */));
+#else
+ mCachedSource = new NuCachedSource2(mConnectingDataSource);
+#endif
+ mConnectingDataSource.clear();
+
+ dataSource = mCachedSource;
+
+ String8 contentType = dataSource->getMIMEType();
+
+ if (strncasecmp(contentType.string(), "audio/", 6)) {
+ // We're not doing this for streams that appear to be audio-only
+ // streams to ensure that even low bandwidth streams start
+ // playing back fairly instantly.
+
+ // We're going to prefill the cache before trying to instantiate
+ // the extractor below, as the latter is an operation that otherwise
+ // could block on the datasource for a significant amount of time.
+ // During that time we'd be unable to abort the preparation phase
+ // without this prefill.
+
+ mLock.unlock();
+
+ for (;;) {
+ status_t finalStatus;
+ size_t cachedDataRemaining =
+ mCachedSource->approxDataRemaining(&finalStatus);
+
+ if (finalStatus != OK || cachedDataRemaining >= kHighWaterMarkBytes
+ || (mFlags & PREPARE_CANCELLED)) {
+ break;
+ }
+
+ usleep(200000);
+ }
+
+ mLock.lock();
+ }
+
+ if (mFlags & PREPARE_CANCELLED) {
+ LOGI("Prepare cancelled while waiting for initial cache fill.");
+ return UNKNOWN_ERROR;
+ }
+ } else if (!strncasecmp("rtsp://", mUri.string(), 7)) {
+ if (mLooper == NULL) {
+ mLooper = new ALooper;
+ mLooper->setName("rtsp");
+ mLooper->start();
+ }
+ mRTSPController = new ARTSPController(mLooper);
+ mConnectingRTSPController = mRTSPController;
+
+ mLock.unlock();
+ status_t err = mRTSPController->connect(mUri.string());
+ mLock.lock();
+
+ mConnectingRTSPController.clear();
+
+ LOGI("ARTSPController::connect returned %d", err);
+
+ if (err != OK) {
+ mRTSPController.clear();
+ return err;
+ }
+
+ sp<MediaExtractor> extractor = mRTSPController.get();
+ return setDataSource_l(extractor);
+ } else {
+ dataSource = DataSource::CreateFromURI(mUri.string(), &mUriHeaders);
+ }
+
+ if (dataSource == NULL) {
+ return UNKNOWN_ERROR;
+ }
+
+ sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
+
+ if (extractor == NULL) {
+ return UNKNOWN_ERROR;
+ }
+
+ dataSource->getDrmInfo(mDecryptHandle, &mDrmManagerClient);
+
+ if (mDecryptHandle != NULL) {
+ CHECK(mDrmManagerClient);
+ if (RightsStatus::RIGHTS_VALID != mDecryptHandle->status) {
+ notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_DRM_NO_LICENSE);
+ }
+ }
+
+ return setDataSource_l(extractor);
+}
+
+void PreviewPlayerBase::abortPrepare(status_t err) {
+ CHECK(err != OK);
+
+ if (mIsAsyncPrepare) {
+ notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
+ }
+
+ mPrepareResult = err;
+ mFlags &= ~(PREPARING|PREPARE_CANCELLED|PREPARING_CONNECTED);
+ mAsyncPrepareEvent = NULL;
+ mPreparedCondition.broadcast();
+}
+
+// static
+bool PreviewPlayerBase::ContinuePreparation(void *cookie) {
+ PreviewPlayerBase *me = static_cast<PreviewPlayerBase *>(cookie);
+
+ return (me->mFlags & PREPARE_CANCELLED) == 0;
+}
+
+void PreviewPlayerBase::onPrepareAsyncEvent() {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mFlags & PREPARE_CANCELLED) {
+ LOGI("prepare was cancelled before doing anything");
+ abortPrepare(UNKNOWN_ERROR);
+ return;
+ }
+
+ if (mUri.size() > 0) {
+ status_t err = finishSetDataSource_l();
+
+ if (err != OK) {
+ abortPrepare(err);
+ return;
+ }
+ }
+
+ if (mVideoTrack != NULL && mVideoSource == NULL) {
+ status_t err = initVideoDecoder();
+
+ if (err != OK) {
+ abortPrepare(err);
+ return;
+ }
+ }
+
+ if (mAudioTrack != NULL && mAudioSource == NULL) {
+ status_t err = initAudioDecoder();
+
+ if (err != OK) {
+ abortPrepare(err);
+ return;
+ }
+ }
+
+ mFlags |= PREPARING_CONNECTED;
+
+ if (mCachedSource != NULL || mRTSPController != NULL) {
+ postBufferingEvent_l();
+ } else {
+ finishAsyncPrepare_l();
+ }
+}
+
+void PreviewPlayerBase::finishAsyncPrepare_l() {
+ if (mIsAsyncPrepare) {
+ if (mVideoSource == NULL) {
+ notifyListener_l(MEDIA_SET_VIDEO_SIZE, 0, 0);
+ } else {
+ notifyVideoSize_l();
+ }
+
+ notifyListener_l(MEDIA_PREPARED);
+ }
+
+ mPrepareResult = OK;
+ mFlags &= ~(PREPARING|PREPARE_CANCELLED|PREPARING_CONNECTED);
+ mFlags |= PREPARED;
+ mAsyncPrepareEvent = NULL;
+ mPreparedCondition.broadcast();
+}
+
+uint32_t PreviewPlayerBase::flags() const {
+ return mExtractorFlags;
+}
+
+void PreviewPlayerBase::postAudioEOS(int64_t delayUs) {
+ Mutex::Autolock autoLock(mLock);
+ postCheckAudioStatusEvent_l(delayUs);
+}
+
+void PreviewPlayerBase::postAudioSeekComplete() {
+ Mutex::Autolock autoLock(mLock);
+ postCheckAudioStatusEvent_l(0 /* delayUs */);
+}
+
+status_t PreviewPlayerBase::setParameter(int key, const Parcel &request) {
+ return OK;
+}
+
+status_t PreviewPlayerBase::getParameter(int key, Parcel *reply) {
+ return OK;
+}
+} // namespace android
diff --git a/libvideoeditor/lvpp/PreviewPlayerBase.h b/libvideoeditor/lvpp/PreviewPlayerBase.h
new file mode 100644
index 0000000..a68d53c
--- /dev/null
+++ b/libvideoeditor/lvpp/PreviewPlayerBase.h
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2011 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 PREVIEW_PLAYER_BASE_H_
+
+#define PREVIEW_PLAYER_BASE_H_
+
+#include "HTTPBase.h"
+#include "TimedEventQueue.h"
+
+#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/TimeSource.h>
+#include <utils/threads.h>
+#include <drm/DrmManagerClient.h>
+
+namespace android {
+
+struct AudioPlayerBase;
+struct DataSource;
+struct MediaBuffer;
+struct MediaExtractor;
+struct MediaSource;
+struct NuCachedSource2;
+struct ISurfaceTexture;
+
+struct ALooper;
+struct ARTSPController;
+
+class DrmManagerClinet;
+class DecryptHandle;
+
+struct AwesomeRenderer : public RefBase {
+ AwesomeRenderer() {}
+
+ virtual void render(MediaBuffer *buffer) = 0;
+
+private:
+ AwesomeRenderer(const AwesomeRenderer &);
+ AwesomeRenderer &operator=(const AwesomeRenderer &);
+};
+
+struct PreviewPlayerBase {
+ PreviewPlayerBase();
+ ~PreviewPlayerBase();
+
+ void setListener(const wp<MediaPlayerBase> &listener);
+
+ status_t setDataSource(
+ const char *uri,
+ const KeyedVector<String8, String8> *headers = NULL);
+
+ status_t setDataSource(int fd, int64_t offset, int64_t length);
+
+ status_t setDataSource(const sp<IStreamSource> &source);
+
+ void reset();
+
+ status_t prepare();
+ status_t prepare_l();
+ status_t prepareAsync();
+ status_t prepareAsync_l();
+
+ status_t play();
+ status_t pause();
+
+ bool isPlaying() const;
+
+ void setSurface(const sp<Surface> &surface);
+ void setSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture);
+ void setAudioSink(const sp<MediaPlayerBase::AudioSink> &audioSink);
+ status_t setLooping(bool shouldLoop);
+
+ status_t getDuration(int64_t *durationUs);
+ status_t getPosition(int64_t *positionUs);
+
+ status_t setParameter(int key, const Parcel &request);
+ status_t getParameter(int key, Parcel *reply);
+
+ status_t seekTo(int64_t timeUs);
+
+ // This is a mask of MediaExtractor::Flags.
+ uint32_t flags() const;
+
+ void postAudioEOS(int64_t delayUs = 0ll);
+ void postAudioSeekComplete();
+
+private:
+ friend struct AwesomeEvent;
+ friend struct PreviewPlayer;
+
+ enum {
+ PLAYING = 1,
+ LOOPING = 2,
+ FIRST_FRAME = 4,
+ PREPARING = 8,
+ PREPARED = 16,
+ AT_EOS = 32,
+ PREPARE_CANCELLED = 64,
+ CACHE_UNDERRUN = 128,
+ AUDIO_AT_EOS = 256,
+ VIDEO_AT_EOS = 512,
+ AUTO_LOOPING = 1024,
+
+ // We are basically done preparing but are currently buffering
+ // sufficient data to begin playback and finish the preparation phase
+ // for good.
+ PREPARING_CONNECTED = 2048,
+
+ // We're triggering a single video event to display the first frame
+ // after the seekpoint.
+ SEEK_PREVIEW = 4096,
+
+ AUDIO_RUNNING = 8192,
+ AUDIOPLAYER_STARTED = 16384,
+
+ INCOGNITO = 32768,
+ };
+
+ mutable Mutex mLock;
+ Mutex mMiscStateLock;
+
+ OMXClient mClient;
+ TimedEventQueue mQueue;
+ bool mQueueStarted;
+ wp<MediaPlayerBase> mListener;
+
+ sp<Surface> mSurface;
+ sp<ANativeWindow> mNativeWindow;
+ sp<MediaPlayerBase::AudioSink> mAudioSink;
+
+ SystemTimeSource mSystemTimeSource;
+ TimeSource *mTimeSource;
+
+ String8 mUri;
+ KeyedVector<String8, String8> mUriHeaders;
+
+ sp<DataSource> mFileSource;
+
+ sp<MediaSource> mVideoTrack;
+ sp<MediaSource> mVideoSource;
+ sp<AwesomeRenderer> mVideoRenderer;
+ bool mVideoRendererIsPreview;
+
+ sp<MediaSource> mAudioTrack;
+ sp<MediaSource> mAudioSource;
+ AudioPlayerBase *mAudioPlayer;
+ int64_t mDurationUs;
+
+ int32_t mDisplayWidth;
+ int32_t mDisplayHeight;
+
+ uint32_t mFlags;
+ uint32_t mExtractorFlags;
+
+ int64_t mTimeSourceDeltaUs;
+ int64_t mVideoTimeUs;
+
+ enum SeekType {
+ NO_SEEK,
+ SEEK,
+ SEEK_VIDEO_ONLY
+ };
+ SeekType mSeeking;
+
+ bool mSeekNotificationSent;
+ int64_t mSeekTimeUs;
+
+ int64_t mBitrate; // total bitrate of the file (in bps) or -1 if unknown.
+
+ bool mWatchForAudioSeekComplete;
+ bool mWatchForAudioEOS;
+
+ sp<TimedEventQueue::Event> mVideoEvent;
+ bool mVideoEventPending;
+ sp<TimedEventQueue::Event> mStreamDoneEvent;
+ bool mStreamDoneEventPending;
+ sp<TimedEventQueue::Event> mBufferingEvent;
+ bool mBufferingEventPending;
+ sp<TimedEventQueue::Event> mCheckAudioStatusEvent;
+ bool mAudioStatusEventPending;
+ sp<TimedEventQueue::Event> mVideoLagEvent;
+ bool mVideoLagEventPending;
+
+ sp<TimedEventQueue::Event> mAsyncPrepareEvent;
+ Condition mPreparedCondition;
+ bool mIsAsyncPrepare;
+ status_t mPrepareResult;
+ status_t mStreamDoneStatus;
+
+ void postVideoEvent_l(int64_t delayUs = -1);
+ void postBufferingEvent_l();
+ void postStreamDoneEvent_l(status_t status);
+ void postCheckAudioStatusEvent_l(int64_t delayUs);
+ void postVideoLagEvent_l();
+ status_t play_l();
+
+ MediaBuffer *mVideoBuffer;
+
+ sp<HTTPBase> mConnectingDataSource;
+ sp<NuCachedSource2> mCachedSource;
+
+ sp<ALooper> mLooper;
+ sp<ARTSPController> mRTSPController;
+ sp<ARTSPController> mConnectingRTSPController;
+
+ DrmManagerClient *mDrmManagerClient;
+ sp<DecryptHandle> mDecryptHandle;
+
+ int64_t mLastVideoTimeUs;
+
+ status_t setDataSource_l(
+ const char *uri,
+ const KeyedVector<String8, String8> *headers = NULL);
+
+ status_t setDataSource_l(const sp<DataSource> &dataSource);
+ status_t setDataSource_l(const sp<MediaExtractor> &extractor);
+ void reset_l();
+ status_t seekTo_l(int64_t timeUs);
+ status_t pause_l(bool at_eos = false);
+ void initRenderer_l();
+ void notifyVideoSize_l();
+ void seekAudioIfNecessary_l();
+
+ void cancelPlayerEvents(bool keepBufferingGoing = false);
+
+ void setAudioSource(sp<MediaSource> source);
+ status_t initAudioDecoder();
+
+ void setVideoSource(sp<MediaSource> source);
+ status_t initVideoDecoder(uint32_t flags = 0);
+
+ void onStreamDone();
+
+ void notifyListener_l(int msg, int ext1 = 0, int ext2 = 0);
+
+ void onVideoEvent();
+ void onBufferingUpdate();
+ void onCheckAudioStatus();
+ void onPrepareAsyncEvent();
+ void abortPrepare(status_t err);
+ void finishAsyncPrepare_l();
+ void onVideoLagUpdate();
+
+ bool getCachedDuration_l(int64_t *durationUs, bool *eos);
+
+ status_t finishSetDataSource_l();
+
+ static bool ContinuePreparation(void *cookie);
+
+ static void OnRTSPSeekDoneWrapper(void *cookie);
+ void onRTSPSeekDone();
+
+ bool getBitrate(int64_t *bitrate);
+
+ void finishSeekIfNecessary(int64_t videoTimeUs);
+ void ensureCacheIsFetching_l();
+
+ status_t startAudioPlayer_l();
+
+ void shutdownVideoDecoder_l();
+ void setNativeWindow_l(const sp<ANativeWindow> &native);
+
+ PreviewPlayerBase(const PreviewPlayerBase &);
+ PreviewPlayerBase &operator=(const PreviewPlayerBase &);
+};
+
+} // namespace android
+
+#endif // PREVIEW_PLAYER_BASE_H_
+
diff --git a/libvideoeditor/lvpp/VideoEditorAudioPlayer.cpp b/libvideoeditor/lvpp/VideoEditorAudioPlayer.cpp
index 1628d33..c49328a 100755
--- a/libvideoeditor/lvpp/VideoEditorAudioPlayer.cpp
+++ b/libvideoeditor/lvpp/VideoEditorAudioPlayer.cpp
@@ -35,8 +35,8 @@ namespace android {
VideoEditorAudioPlayer::VideoEditorAudioPlayer(
const sp<MediaPlayerBase::AudioSink> &audioSink,
- AwesomePlayer *observer)
- : AudioPlayer(audioSink, observer) {
+ PreviewPlayerBase *observer)
+ : AudioPlayerBase(audioSink, observer) {
LOGV("VideoEditorAudioPlayer");
mBGAudioPCMFileHandle = NULL;
@@ -97,7 +97,7 @@ sp<MediaSource> VideoEditorAudioPlayer::getSource() {
return mSource;
}
-void VideoEditorAudioPlayer::setObserver(AwesomePlayer *observer) {
+void VideoEditorAudioPlayer::setObserver(PreviewPlayerBase *observer) {
LOGV("setObserver");
//CHECK(!mStarted);
mObserver = observer;
@@ -380,13 +380,13 @@ void VideoEditorAudioPlayer::resume() {
mAudioProcess->veSetAudioProcessingParams(audioMixSettings);
//Call the base class
- AudioPlayer::resume();
+ AudioPlayerBase::resume();
}
void VideoEditorAudioPlayer::reset() {
LOGV("reset");
- AudioPlayer::reset();
+ AudioPlayerBase::reset();
// Capture the current seek point
mBGAudioPCMFileSeekPoint = 0;
diff --git a/libvideoeditor/lvpp/VideoEditorAudioPlayer.h b/libvideoeditor/lvpp/VideoEditorAudioPlayer.h
index f5232cf..73aea65 100755
--- a/libvideoeditor/lvpp/VideoEditorAudioPlayer.h
+++ b/libvideoeditor/lvpp/VideoEditorAudioPlayer.h
@@ -27,7 +27,8 @@
#include "VideoEditorMain.h"
#include "M4OSA_FileReader.h"
#include "VideoEditorBGAudioProcessing.h"
-#include <media/stagefright/AudioPlayer.h>
+#include "AudioPlayerBase.h"
+#include "PreviewPlayerBase.h"
namespace android {
@@ -36,7 +37,7 @@ class AudioTrack;
class PreviewPlayer;
-class VideoEditorAudioPlayer : public AudioPlayer {
+class VideoEditorAudioPlayer : public AudioPlayerBase {
public:
enum {
REACHED_EOS,
@@ -44,7 +45,7 @@ public:
};
VideoEditorAudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink,
- AwesomePlayer *audioObserver = NULL);
+ PreviewPlayerBase *audioObserver = NULL);
virtual ~VideoEditorAudioPlayer();
@@ -58,7 +59,7 @@ public:
M4OSA_UInt32 pBGAudioCurrentMediaBeginCutTS,
M4OSA_UInt32 pBGAudioCurrentMediaVolumeVal);
- void setObserver(AwesomePlayer *observer);
+ void setObserver(PreviewPlayerBase *observer);
void setSource(const sp<MediaSource> &source);
sp<MediaSource> getSource();