summaryrefslogtreecommitdiffstats
path: root/media/libmediaplayerservice
diff options
context:
space:
mode:
authorChong Zhang <chz@google.com>2015-02-04 15:04:04 +0000
committerAndroid Git Automerger <android-git-automerger@android.com>2015-02-04 15:04:04 +0000
commitc9fcfb923bb891d980238d47f33fdf488f758dc2 (patch)
tree546704e694b862ad8ded7f0e0899fa562c468424 /media/libmediaplayerservice
parente074a93046ebe5cea0b55c3a479e082a426e1e07 (diff)
parent07840f7544de883fe5c00cbd18bc6458a20e2691 (diff)
downloadframeworks_av-c9fcfb923bb891d980238d47f33fdf488f758dc2.zip
frameworks_av-c9fcfb923bb891d980238d47f33fdf488f758dc2.tar.gz
frameworks_av-c9fcfb923bb891d980238d47f33fdf488f758dc2.tar.bz2
am 07840f75: am 3a7ed550: am efbb6195: NuPlayer: pause playback when buffering is low
* commit '07840f7544de883fe5c00cbd18bc6458a20e2691': NuPlayer: pause playback when buffering is low
Diffstat (limited to 'media/libmediaplayerservice')
-rw-r--r--media/libmediaplayerservice/nuplayer/GenericSource.cpp189
-rw-r--r--media/libmediaplayerservice/nuplayer/GenericSource.h13
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.cpp69
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.h9
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerSource.h3
5 files changed, 250 insertions, 33 deletions
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 76b80bb..9b446b8 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -40,6 +40,11 @@
namespace android {
+static int64_t kLowWaterMarkUs = 2000000ll; // 2secs
+static int64_t kHighWaterMarkUs = 5000000ll; // 5secs
+static const ssize_t kLowWaterMarkBytes = 40000;
+static const ssize_t kHighWaterMarkBytes = 200000;
+
NuPlayer::GenericSource::GenericSource(
const sp<AMessage> &notify,
bool uidValid,
@@ -55,6 +60,7 @@ NuPlayer::GenericSource::GenericSource(
mAudioIsVorbis(false),
mIsWidevine(false),
mIsSecure(false),
+ mIsStreaming(false),
mUIDValid(uidValid),
mUID(uid),
mFd(-1),
@@ -62,7 +68,9 @@ NuPlayer::GenericSource::GenericSource(
mMetaDataSize(-1ll),
mBitrate(-1ll),
mPollBufferingGeneration(0),
- mPendingReadBufferTypes(0) {
+ mPendingReadBufferTypes(0),
+ mBuffering(false),
+ mPrepareBuffering(false) {
resetDataSource();
DataSource::RegisterDefaultSniffers();
}
@@ -254,6 +262,20 @@ status_t NuPlayer::GenericSource::initFromDataSource() {
}
}
+ // Start the selected A/V tracks now before we start buffering.
+ // Widevine sources might re-initialize crypto when starting, if we delay
+ // this to start(), all data buffered during prepare would be wasted.
+ // (We don't actually start reading until start().)
+ if (mAudioTrack.mSource != NULL && mAudioTrack.mSource->start() != OK) {
+ ALOGE("failed to start audio track!");
+ return UNKNOWN_ERROR;
+ }
+
+ if (mVideoTrack.mSource != NULL && mVideoTrack.mSource->start() != OK) {
+ ALOGE("failed to start video track!");
+ return UNKNOWN_ERROR;
+ }
+
mBitrate = totalBitrate;
return OK;
@@ -352,9 +374,13 @@ void NuPlayer::GenericSource::onPrepareAsync() {
mCachedSource = static_cast<NuCachedSource2 *>(mDataSource.get());
}
- if (mIsWidevine || mCachedSource != NULL) {
- schedulePollBuffering();
- }
+ // For widevine or other cached streaming cases, we need to wait for
+ // enough buffering before reporting prepared.
+ // Note that even when URL doesn't start with widevine://, mIsWidevine
+ // could still be set to true later, if the streaming or file source
+ // is sniffed to be widevine. We don't want to buffer for file source
+ // in that case, so must check the flag now.
+ mIsStreaming = (mIsWidevine || mCachedSource != NULL);
}
// check initial caching status
@@ -397,7 +423,14 @@ void NuPlayer::GenericSource::onPrepareAsync() {
| FLAG_CAN_SEEK_FORWARD
| FLAG_CAN_SEEK);
- notifyPrepared();
+ if (mIsStreaming) {
+ mPrepareBuffering = true;
+
+ ensureCacheIsFetching();
+ restartPollBuffering();
+ } else {
+ notifyPrepared();
+ }
}
void NuPlayer::GenericSource::notifyPreparedAndCleanup(status_t err) {
@@ -489,19 +522,17 @@ void NuPlayer::GenericSource::start() {
mStopRead = false;
if (mAudioTrack.mSource != NULL) {
- CHECK_EQ(mAudioTrack.mSource->start(), (status_t)OK);
-
postReadBuffer(MEDIA_TRACK_TYPE_AUDIO);
}
if (mVideoTrack.mSource != NULL) {
- CHECK_EQ(mVideoTrack.mSource->start(), (status_t)OK);
-
postReadBuffer(MEDIA_TRACK_TYPE_VIDEO);
}
setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000);
mStarted = true;
+
+ (new AMessage(kWhatStart, id()))->post();
}
void NuPlayer::GenericSource::stop() {
@@ -526,6 +557,8 @@ void NuPlayer::GenericSource::resume() {
// nothing to do, just account for DRM playback status
setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000);
mStarted = true;
+
+ (new AMessage(kWhatResume, id()))->post();
}
void NuPlayer::GenericSource::disconnect() {
@@ -558,22 +591,98 @@ void NuPlayer::GenericSource::schedulePollBuffering() {
}
void NuPlayer::GenericSource::cancelPollBuffering() {
+ mBuffering = false;
++mPollBufferingGeneration;
}
+void NuPlayer::GenericSource::restartPollBuffering() {
+ if (mIsStreaming) {
+ cancelPollBuffering();
+ onPollBuffering();
+ }
+}
+
void NuPlayer::GenericSource::notifyBufferingUpdate(int percentage) {
+ ALOGV("notifyBufferingUpdate: buffering %d%%", percentage);
+
sp<AMessage> msg = dupNotify();
msg->setInt32("what", kWhatBufferingUpdate);
msg->setInt32("percentage", percentage);
msg->post();
}
+void NuPlayer::GenericSource::startBufferingIfNecessary() {
+ ALOGV("startBufferingIfNecessary: mPrepareBuffering=%d, mBuffering=%d",
+ mPrepareBuffering, mBuffering);
+
+ if (mPrepareBuffering) {
+ return;
+ }
+
+ if (!mBuffering) {
+ mBuffering = true;
+
+ ensureCacheIsFetching();
+ sendCacheStats();
+
+ sp<AMessage> notify = dupNotify();
+ notify->setInt32("what", kWhatPauseOnBufferingStart);
+ notify->post();
+ }
+}
+
+void NuPlayer::GenericSource::stopBufferingIfNecessary() {
+ ALOGV("stopBufferingIfNecessary: mPrepareBuffering=%d, mBuffering=%d",
+ mPrepareBuffering, mBuffering);
+
+ if (mPrepareBuffering) {
+ mPrepareBuffering = false;
+ notifyPrepared();
+ return;
+ }
+
+ if (mBuffering) {
+ mBuffering = false;
+
+ sendCacheStats();
+
+ sp<AMessage> notify = dupNotify();
+ notify->setInt32("what", kWhatResumeOnBufferingEnd);
+ notify->post();
+ }
+}
+
+void NuPlayer::GenericSource::sendCacheStats() {
+ int32_t kbps = 0;
+ status_t err = UNKNOWN_ERROR;
+
+ if (mCachedSource != NULL) {
+ err = mCachedSource->getEstimatedBandwidthKbps(&kbps);
+ } else if (mWVMExtractor != NULL) {
+ err = mWVMExtractor->getEstimatedBandwidthKbps(&kbps);
+ }
+
+ if (err == OK) {
+ sp<AMessage> notify = dupNotify();
+ notify->setInt32("what", kWhatCacheStats);
+ notify->setInt32("bandwidth", kbps);
+ notify->post();
+ }
+}
+
+void NuPlayer::GenericSource::ensureCacheIsFetching() {
+ if (mCachedSource != NULL) {
+ mCachedSource->resumeFetchingIfNecessary();
+ }
+}
+
void NuPlayer::GenericSource::onPollBuffering() {
status_t finalStatus = UNKNOWN_ERROR;
- int64_t cachedDurationUs = 0ll;
+ int64_t cachedDurationUs = -1ll;
+ ssize_t cachedDataRemaining = -1;
if (mCachedSource != NULL) {
- size_t cachedDataRemaining =
+ cachedDataRemaining =
mCachedSource->approxDataRemaining(&finalStatus);
if (finalStatus == OK) {
@@ -593,23 +702,48 @@ void NuPlayer::GenericSource::onPollBuffering() {
= mWVMExtractor->getCachedDurationUs(&finalStatus);
}
- if (finalStatus == ERROR_END_OF_STREAM) {
- notifyBufferingUpdate(100);
- cancelPollBuffering();
+ if (finalStatus != OK) {
+ ALOGV("onPollBuffering: EOS (finalStatus = %d)", finalStatus);
+
+ if (finalStatus == ERROR_END_OF_STREAM) {
+ notifyBufferingUpdate(100);
+ }
+
+ stopBufferingIfNecessary();
return;
- } else if (cachedDurationUs > 0ll && mDurationUs > 0ll) {
- int percentage = 100.0 * cachedDurationUs / mDurationUs;
- if (percentage > 100) {
- percentage = 100;
+ } else if (cachedDurationUs >= 0ll) {
+ if (mDurationUs > 0ll) {
+ int64_t cachedPosUs = getLastReadPosition() + cachedDurationUs;
+ int percentage = 100.0 * cachedPosUs / mDurationUs;
+ if (percentage > 100) {
+ percentage = 100;
+ }
+
+ notifyBufferingUpdate(percentage);
}
- notifyBufferingUpdate(percentage);
+ ALOGV("onPollBuffering: cachedDurationUs %.1f sec",
+ cachedDurationUs / 1000000.0f);
+
+ if (cachedDurationUs < kLowWaterMarkUs) {
+ startBufferingIfNecessary();
+ } else if (cachedDurationUs > kHighWaterMarkUs) {
+ stopBufferingIfNecessary();
+ }
+ } else if (cachedDataRemaining >= 0) {
+ ALOGV("onPollBuffering: cachedDataRemaining %d bytes",
+ cachedDataRemaining);
+
+ if (cachedDataRemaining < kLowWaterMarkBytes) {
+ startBufferingIfNecessary();
+ } else if (cachedDataRemaining > kHighWaterMarkBytes) {
+ stopBufferingIfNecessary();
+ }
}
schedulePollBuffering();
}
-
void NuPlayer::GenericSource::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatPrepareAsync:
@@ -688,6 +822,14 @@ void NuPlayer::GenericSource::onMessageReceived(const sp<AMessage> &msg) {
break;
}
+
+ case kWhatStart:
+ case kWhatResume:
+ {
+ restartPollBuffering();
+ break;
+ }
+
case kWhatPollBuffering:
{
int32_t generation;
@@ -1201,6 +1343,13 @@ status_t NuPlayer::GenericSource::doSeek(int64_t seekTimeUs) {
if (!mStarted) {
setDrmPlaybackStatusIfNeeded(Playback::PAUSE, 0);
}
+
+ // If currently buffering, post kWhatBufferingEnd first, so that
+ // NuPlayer resumes. Otherwise, if cache hits high watermark
+ // before new polling happens, no one will resume the playback.
+ stopBufferingIfNecessary();
+ restartPollBuffering();
+
return OK;
}
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 1b63a1f..385d73a 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -94,16 +94,17 @@ private:
kWhatSeek,
kWhatReadBuffer,
kWhatStopWidevine,
+ kWhatStart,
+ kWhatResume,
};
- Vector<sp<MediaSource> > mSources;
-
struct Track {
size_t mIndex;
sp<MediaSource> mSource;
sp<AnotherPacketSource> mPackets;
};
+ Vector<sp<MediaSource> > mSources;
Track mAudioTrack;
int64_t mAudioTimeUs;
int64_t mAudioLastDequeueTimeUs;
@@ -119,6 +120,7 @@ private:
bool mAudioIsVorbis;
bool mIsWidevine;
bool mIsSecure;
+ bool mIsStreaming;
bool mUIDValid;
uid_t mUID;
sp<IMediaHTTPService> mHTTPService;
@@ -143,6 +145,8 @@ private:
int64_t mBitrate;
int32_t mPollBufferingGeneration;
uint32_t mPendingReadBufferTypes;
+ bool mBuffering;
+ bool mPrepareBuffering;
mutable Mutex mReadBufferLock;
sp<ALooper> mLooper;
@@ -194,8 +198,13 @@ private:
void schedulePollBuffering();
void cancelPollBuffering();
+ void restartPollBuffering();
void onPollBuffering();
void notifyBufferingUpdate(int percentage);
+ void startBufferingIfNecessary();
+ void stopBufferingIfNecessary();
+ void sendCacheStats();
+ void ensureCacheIsFetching();
DISALLOW_EVIL_CONSTRUCTORS(GenericSource);
};
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index e02a2d5..1f55706 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -180,7 +180,9 @@ NuPlayer::NuPlayer()
mFlushingVideo(NONE),
mResumePending(false),
mVideoScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW),
- mStarted(false) {
+ mStarted(false),
+ mPaused(false),
+ mPausedByClient(false) {
clearFlushComplete();
}
@@ -598,6 +600,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
} else {
onStart();
}
+ mPausedByClient = false;
break;
}
@@ -956,16 +959,8 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
case kWhatPause:
{
- if (mSource != NULL) {
- mSource->pause();
- } else {
- ALOGW("pause called when source is gone or not set");
- }
- if (mRenderer != NULL) {
- mRenderer->pause();
- } else {
- ALOGW("pause called when renderer is gone or not set");
- }
+ onPause();
+ mPausedByClient = true;
break;
}
@@ -988,6 +983,10 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
}
void NuPlayer::onResume() {
+ if (!mPaused) {
+ return;
+ }
+ mPaused = false;
if (mSource != NULL) {
mSource->resume();
} else {
@@ -1072,6 +1071,23 @@ void NuPlayer::onStart() {
postScanSources();
}
+void NuPlayer::onPause() {
+ if (mPaused) {
+ return;
+ }
+ mPaused = true;
+ if (mSource != NULL) {
+ mSource->pause();
+ } else {
+ ALOGW("pause called when source is gone or not set");
+ }
+ if (mRenderer != NULL) {
+ mRenderer->pause();
+ } else {
+ ALOGW("pause called when renderer is gone or not set");
+ }
+}
+
bool NuPlayer::audioDecoderStillNeeded() {
// Audio decoder is no longer needed if it's in shut/shutting down status.
return ((mFlushingAudio != SHUT_DOWN) && (mFlushingAudio != SHUTTING_DOWN_DECODER));
@@ -1709,18 +1725,49 @@ void NuPlayer::onSourceNotify(const sp<AMessage> &msg) {
break;
}
+ case Source::kWhatPauseOnBufferingStart:
+ {
+ // ignore if not playing
+ if (mStarted && !mPausedByClient) {
+ ALOGI("buffer low, pausing...");
+
+ onPause();
+ }
+ // fall-thru
+ }
+
case Source::kWhatBufferingStart:
{
notifyListener(MEDIA_INFO, MEDIA_INFO_BUFFERING_START, 0);
break;
}
+ case Source::kWhatResumeOnBufferingEnd:
+ {
+ // ignore if not playing
+ if (mStarted && !mPausedByClient) {
+ ALOGI("buffer ready, resuming...");
+
+ onResume();
+ }
+ // fall-thru
+ }
+
case Source::kWhatBufferingEnd:
{
notifyListener(MEDIA_INFO, MEDIA_INFO_BUFFERING_END, 0);
break;
}
+ case Source::kWhatCacheStats:
+ {
+ int32_t kbps;
+ CHECK(msg->findInt32("bandwidth", &kbps));
+
+ notifyListener(MEDIA_INFO, MEDIA_INFO_NETWORK_BANDWIDTH, kbps);
+ break;
+ }
+
case Source::kWhatSubtitleData:
{
sp<ABuffer> buffer;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index bb32eac..57eaf74 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -177,6 +177,14 @@ private:
bool mStarted;
+ // Actual pause state, either as requested by client or due to buffering.
+ bool mPaused;
+
+ // Pause state as requested by client. Note that if mPausedByClient is
+ // true, mPaused is always true; if mPausedByClient is false, mPaused could
+ // still become true, when we pause internally due to buffering.
+ bool mPausedByClient;
+
inline const sp<DecoderBase> &getDecoder(bool audio) {
return audio ? mAudioDecoder : mVideoDecoder;
}
@@ -204,6 +212,7 @@ private:
void onStart();
void onResume();
+ void onPause();
bool audioDecoderStillNeeded();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index c81d3b9..5a8beb1 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -48,6 +48,9 @@ struct NuPlayer::Source : public AHandler {
kWhatBufferingUpdate,
kWhatBufferingStart,
kWhatBufferingEnd,
+ kWhatPauseOnBufferingStart,
+ kWhatResumeOnBufferingEnd,
+ kWhatCacheStats,
kWhatSubtitleData,
kWhatTimedTextData,
kWhatQueueDecoderShutdown,