GenericSource: enable autonomous buffering
Also fix possible racing conditions. Now buffering control is time based
only.
Test: media post submit tests passed
Bug: 66952371
Change-Id: Ia115787511b2f3e612167a61c8df3a9a2b2d9abf
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 4729d59..94e3395 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -49,9 +49,6 @@
static const int kHighWaterMarkMs = 5000; // 5secs
static const int kHighWaterMarkRebufferMs = 15000; // 15secs
-static const int kLowWaterMarkKB = 40;
-static const int kHighWaterMarkKB = 200;
-
NuPlayer::GenericSource::GenericSource(
const sp<AMessage> ¬ify,
bool uidValid,
@@ -62,6 +59,11 @@
mAudioLastDequeueTimeUs(0),
mVideoTimeUs(0),
mVideoLastDequeueTimeUs(0),
+ mPrevBufferPercentage(-1),
+ mPollBufferingGeneration(0),
+ mSentPauseOnBuffering(false),
+ mAudioDataGeneration(0),
+ mVideoDataGeneration(0),
mFetchSubtitleDataGeneration(0),
mFetchTimedTextDataGeneration(0),
mDurationUs(-1ll),
@@ -77,7 +79,7 @@
ALOGV("GenericSource");
CHECK(mediaClock != NULL);
- mBufferingMonitor = new BufferingMonitor(notify);
+ getDefaultBufferingSettings(&mBufferingSettings);
resetDataSource();
}
@@ -95,14 +97,7 @@
mOffset = 0;
mLength = 0;
mStarted = false;
- mStopRead = true;
-
- if (mBufferingMonitorLooper != NULL) {
- mBufferingMonitorLooper->unregisterHandler(mBufferingMonitor->id());
- mBufferingMonitorLooper->stop();
- mBufferingMonitorLooper = NULL;
- }
- mBufferingMonitor->stop();
+ mPreparing = false;
mIsDrmProtected = false;
mIsDrmReleased = false;
@@ -114,6 +109,7 @@
const sp<IMediaHTTPService> &httpService,
const char *url,
const KeyedVector<String8, String8> *headers) {
+ Mutex::Autolock _l(mLock);
ALOGV("setDataSource url: %s", url);
resetDataSource();
@@ -132,6 +128,7 @@
status_t NuPlayer::GenericSource::setDataSource(
int fd, int64_t offset, int64_t length) {
+ Mutex::Autolock _l(mLock);
ALOGV("setDataSource %d/%lld/%lld", fd, (long long)offset, (long long)length);
resetDataSource();
@@ -146,6 +143,7 @@
}
status_t NuPlayer::GenericSource::setDataSource(const sp<DataSource>& source) {
+ Mutex::Autolock _l(mLock);
ALOGV("setDataSource (source: %p)", source.get());
resetDataSource();
@@ -154,6 +152,7 @@
}
sp<MetaData> NuPlayer::GenericSource::getFileFormatMeta() const {
+ Mutex::Autolock _l(mLock);
return mFileMeta;
}
@@ -268,12 +267,36 @@
status_t NuPlayer::GenericSource::getDefaultBufferingSettings(
BufferingSettings* buffering /* nonnull */) {
- mBufferingMonitor->getDefaultBufferingSettings(buffering);
+ buffering->mInitialBufferingMode = BUFFERING_MODE_TIME_ONLY;
+ buffering->mRebufferingMode = BUFFERING_MODE_TIME_ONLY;
+ buffering->mInitialWatermarkMs = kHighWaterMarkMs;
+ buffering->mRebufferingWatermarkLowMs = kLowWaterMarkMs;
+ buffering->mRebufferingWatermarkHighMs = kHighWaterMarkRebufferMs;
+
+ ALOGV("getDefaultBufferingSettings{%s}", buffering->toString().string());
return OK;
}
status_t NuPlayer::GenericSource::setBufferingSettings(const BufferingSettings& buffering) {
- return mBufferingMonitor->setBufferingSettings(buffering);
+ ALOGV("setBufferingSettings{%s}", buffering.toString().string());
+
+ if (buffering.IsSizeBasedBufferingMode(buffering.mInitialBufferingMode)
+ || buffering.IsSizeBasedBufferingMode(buffering.mRebufferingMode)
+ || (buffering.IsTimeBasedBufferingMode(buffering.mRebufferingMode)
+ && buffering.mRebufferingWatermarkLowMs > buffering.mRebufferingWatermarkHighMs)) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock _l(mLock);
+ mBufferingSettings = buffering;
+ if (mBufferingSettings.mInitialBufferingMode == BUFFERING_MODE_NONE) {
+ mBufferingSettings.mInitialWatermarkMs = BufferingSettings::kNoWatermark;
+ }
+ if (mBufferingSettings.mRebufferingMode == BUFFERING_MODE_NONE) {
+ mBufferingSettings.mRebufferingWatermarkLowMs = BufferingSettings::kNoWatermark;
+ mBufferingSettings.mRebufferingWatermarkHighMs = INT32_MAX;
+ }
+ return OK;
}
status_t NuPlayer::GenericSource::startSources() {
@@ -309,6 +332,7 @@
status_t NuPlayer::GenericSource::setBuffers(
bool audio, Vector<MediaBuffer *> &buffers) {
+ Mutex::Autolock _l(mLock);
if (mIsSecure && !audio && mVideoTrack.mSource != NULL) {
return mVideoTrack.mSource->setBuffers(buffers);
}
@@ -316,13 +340,10 @@
}
bool NuPlayer::GenericSource::isStreaming() const {
+ Mutex::Autolock _l(mLock);
return mIsStreaming;
}
-void NuPlayer::GenericSource::setOffloadAudio(bool offload) {
- mBufferingMonitor->setOffloadAudio(offload);
-}
-
NuPlayer::GenericSource::~GenericSource() {
ALOGV("~GenericSource");
if (mLooper != NULL) {
@@ -333,6 +354,7 @@
}
void NuPlayer::GenericSource::prepareAsync() {
+ Mutex::Autolock _l(mLock);
ALOGV("prepareAsync: (looper: %d)", (mLooper != NULL));
if (mLooper == NULL) {
@@ -434,7 +456,7 @@
}
if (mVideoTrack.mSource != NULL) {
- sp<MetaData> meta = doGetFormatMeta(false /* audio */);
+ sp<MetaData> meta = getFormatMeta_l(false /* audio */);
sp<AMessage> msg = new AMessage;
err = convertMetaDataToMessage(meta, &msg);
if(err != OK) {
@@ -468,47 +490,39 @@
}
if (mIsStreaming) {
- if (mBufferingMonitorLooper == NULL) {
- mBufferingMonitor->prepare(mCachedSource, mDurationUs, mBitrate,
- mIsStreaming);
-
- mBufferingMonitorLooper = new ALooper;
- mBufferingMonitorLooper->setName("GSBMonitor");
- mBufferingMonitorLooper->start();
- mBufferingMonitorLooper->registerHandler(mBufferingMonitor);
- }
-
- mBufferingMonitor->ensureCacheIsFetching();
- mBufferingMonitor->restartPollBuffering();
+ mCachedSource->resumeFetchingIfNecessary();
+ mPreparing = true;
+ schedulePollBuffering();
} else {
notifyPrepared();
}
+
+ if (mAudioTrack.mSource != NULL) {
+ postReadBuffer(MEDIA_TRACK_TYPE_AUDIO);
+ }
+
+ if (mVideoTrack.mSource != NULL) {
+ postReadBuffer(MEDIA_TRACK_TYPE_VIDEO);
+ }
}
void NuPlayer::GenericSource::notifyPreparedAndCleanup(status_t err) {
if (err != OK) {
- {
- sp<DataSource> dataSource = mDataSource;
- sp<NuCachedSource2> cachedSource = mCachedSource;
- sp<DataSource> httpSource = mHttpSource;
- {
- Mutex::Autolock _l(mDisconnectLock);
- mDataSource.clear();
- mCachedSource.clear();
- mHttpSource.clear();
- }
- }
- mBitrate = -1;
+ mDataSource.clear();
+ mCachedSource.clear();
+ mHttpSource.clear();
- mBufferingMonitor->cancelPollBuffering();
+ mBitrate = -1;
+ mPrevBufferPercentage = -1;
+ ++mPollBufferingGeneration;
}
notifyPrepared(err);
}
void NuPlayer::GenericSource::start() {
+ Mutex::Autolock _l(mLock);
ALOGI("start");
- mStopRead = false;
if (mAudioTrack.mSource != NULL) {
postReadBuffer(MEDIA_TRACK_TYPE_AUDIO);
}
@@ -518,28 +532,27 @@
}
mStarted = true;
-
- (new AMessage(kWhatStart, this))->post();
}
void NuPlayer::GenericSource::stop() {
+ Mutex::Autolock _l(mLock);
mStarted = false;
}
void NuPlayer::GenericSource::pause() {
+ Mutex::Autolock _l(mLock);
mStarted = false;
}
void NuPlayer::GenericSource::resume() {
+ Mutex::Autolock _l(mLock);
mStarted = true;
-
- (new AMessage(kWhatResume, this))->post();
}
void NuPlayer::GenericSource::disconnect() {
sp<DataSource> dataSource, httpSource;
{
- Mutex::Autolock _l(mDisconnectLock);
+ Mutex::Autolock _l(mLock);
dataSource = mDataSource;
httpSource = mHttpSource;
}
@@ -558,7 +571,24 @@
return OK;
}
+void NuPlayer::GenericSource::sendCacheStats() {
+ int32_t kbps = 0;
+ status_t err = UNKNOWN_ERROR;
+
+ if (mCachedSource != NULL) {
+ err = mCachedSource->getEstimatedBandwidthKbps(&kbps);
+ }
+
+ if (err == OK) {
+ sp<AMessage> notify = dupNotify();
+ notify->setInt32("what", kWhatCacheStats);
+ notify->setInt32("bandwidth", kbps);
+ notify->post();
+ }
+}
+
void NuPlayer::GenericSource::onMessageReceived(const sp<AMessage> &msg) {
+ Mutex::Autolock _l(mLock);
switch (msg->what()) {
case kWhatPrepareAsync:
{
@@ -627,6 +657,8 @@
track->mSource = source;
track->mSource->start();
track->mIndex = trackIndex;
+ ++mAudioDataGeneration;
+ ++mVideoDataGeneration;
int64_t timeUs, actualTimeUs;
const bool formatChange = true;
@@ -644,68 +676,19 @@
break;
}
- case kWhatStart:
- case kWhatResume:
- {
- mBufferingMonitor->restartPollBuffering();
- break;
- }
-
- case kWhatGetFormat:
- {
- onGetFormatMeta(msg);
- break;
- }
-
- case kWhatGetSelectedTrack:
- {
- onGetSelectedTrack(msg);
- break;
- }
-
- case kWhatGetTrackInfo:
- {
- onGetTrackInfo(msg);
- break;
- }
-
- case kWhatSelectTrack:
- {
- onSelectTrack(msg);
- break;
- }
-
- case kWhatSeek:
- {
- onSeek(msg);
- break;
- }
-
case kWhatReadBuffer:
{
onReadBuffer(msg);
break;
}
- case kWhatPrepareDrm:
+ case kWhatPollBuffering:
{
- status_t status = onPrepareDrm(msg);
- sp<AMessage> response = new AMessage;
- response->setInt32("status", status);
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
- response->postReply(replyID);
- break;
- }
-
- case kWhatReleaseDrm:
- {
- status_t status = onReleaseDrm();
- sp<AMessage> response = new AMessage;
- response->setInt32("status", status);
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
- response->postReply(replyID);
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+ if (generation == mPollBufferingGeneration) {
+ onPollBuffering();
+ }
break;
}
@@ -820,34 +803,11 @@
}
sp<MetaData> NuPlayer::GenericSource::getFormatMeta(bool audio) {
- sp<AMessage> msg = new AMessage(kWhatGetFormat, this);
- msg->setInt32("audio", audio);
-
- sp<AMessage> response;
- sp<RefBase> format;
- status_t err = msg->postAndAwaitResponse(&response);
- if (err == OK && response != NULL) {
- CHECK(response->findObject("format", &format));
- return static_cast<MetaData*>(format.get());
- } else {
- return NULL;
- }
+ Mutex::Autolock _l(mLock);
+ return getFormatMeta_l(audio);
}
-void NuPlayer::GenericSource::onGetFormatMeta(const sp<AMessage>& msg) const {
- int32_t audio;
- CHECK(msg->findInt32("audio", &audio));
-
- sp<AMessage> response = new AMessage;
- sp<MetaData> format = doGetFormatMeta(audio);
- response->setObject("format", format);
-
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
- response->postReply(replyID);
-}
-
-sp<MetaData> NuPlayer::GenericSource::doGetFormatMeta(bool audio) const {
+sp<MetaData> NuPlayer::GenericSource::getFormatMeta_l(bool audio) {
sp<IMediaSource> source = audio ? mAudioTrack.mSource : mVideoTrack.mSource;
if (source == NULL) {
@@ -859,6 +819,7 @@
status_t NuPlayer::GenericSource::dequeueAccessUnit(
bool audio, sp<ABuffer> *accessUnit) {
+ Mutex::Autolock _l(mLock);
// If has gone through stop/releaseDrm sequence, we no longer send down any buffer b/c
// the codec's crypto object has gone away (b/37960096).
// Note: This will be unnecessary when stop() changes behavior and releases codec (b/35248283).
@@ -884,10 +845,30 @@
status_t result = track->mPackets->dequeueAccessUnit(accessUnit);
- // start pulling in more buffers if we only have one (or no) buffer left
+ // start pulling in more buffers if cache is running low
// so that decoder has less chance of being starved
- if (track->mPackets->getAvailableBufferCount(&finalResult) < 2) {
- postReadBuffer(audio? MEDIA_TRACK_TYPE_AUDIO : MEDIA_TRACK_TYPE_VIDEO);
+ if (!mIsStreaming) {
+ if (track->mPackets->getAvailableBufferCount(&finalResult) < 2) {
+ postReadBuffer(audio? MEDIA_TRACK_TYPE_AUDIO : MEDIA_TRACK_TYPE_VIDEO);
+ }
+ } else {
+ int64_t durationUs = track->mPackets->getBufferedDurationUs(&finalResult);
+ int64_t restartBufferingMarkUs =
+ mBufferingSettings.mRebufferingWatermarkHighMs * 1000ll / 2;
+ if (finalResult == OK) {
+ if (durationUs < restartBufferingMarkUs) {
+ postReadBuffer(audio? MEDIA_TRACK_TYPE_AUDIO : MEDIA_TRACK_TYPE_VIDEO);
+ }
+ if (track->mPackets->getAvailableBufferCount(&finalResult) < 2
+ && !mSentPauseOnBuffering && !mPreparing) {
+ mCachedSource->resumeFetchingIfNecessary();
+ sendCacheStats();
+ mSentPauseOnBuffering = true;
+ sp<AMessage> notify = dupNotify();
+ notify->setInt32("what", kWhatPauseOnBufferingStart);
+ notify->post();
+ }
+ }
}
if (result != OK) {
@@ -907,7 +888,6 @@
CHECK((*accessUnit)->meta()->findInt64("timeUs", &timeUs));
if (audio) {
mAudioLastDequeueTimeUs = timeUs;
- mBufferingMonitor->updateDequeuedBufferTime(timeUs);
} else {
mVideoLastDequeueTimeUs = timeUs;
}
@@ -932,43 +912,18 @@
}
status_t NuPlayer::GenericSource::getDuration(int64_t *durationUs) {
+ Mutex::Autolock _l(mLock);
*durationUs = mDurationUs;
return OK;
}
size_t NuPlayer::GenericSource::getTrackCount() const {
+ Mutex::Autolock _l(mLock);
return mSources.size();
}
sp<AMessage> NuPlayer::GenericSource::getTrackInfo(size_t trackIndex) const {
- sp<AMessage> msg = new AMessage(kWhatGetTrackInfo, this);
- msg->setSize("trackIndex", trackIndex);
-
- sp<AMessage> response;
- sp<RefBase> format;
- status_t err = msg->postAndAwaitResponse(&response);
- if (err == OK && response != NULL) {
- CHECK(response->findObject("format", &format));
- return static_cast<AMessage*>(format.get());
- } else {
- return NULL;
- }
-}
-
-void NuPlayer::GenericSource::onGetTrackInfo(const sp<AMessage>& msg) const {
- size_t trackIndex;
- CHECK(msg->findSize("trackIndex", &trackIndex));
-
- sp<AMessage> response = new AMessage;
- sp<AMessage> format = doGetTrackInfo(trackIndex);
- response->setObject("format", format);
-
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
- response->postReply(replyID);
-}
-
-sp<AMessage> NuPlayer::GenericSource::doGetTrackInfo(size_t trackIndex) const {
+ Mutex::Autolock _l(mLock);
size_t trackCount = mSources.size();
if (trackIndex >= trackCount) {
return NULL;
@@ -1018,35 +973,7 @@
}
ssize_t NuPlayer::GenericSource::getSelectedTrack(media_track_type type) const {
- sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, this);
- msg->setInt32("type", type);
-
- sp<AMessage> response;
- int32_t index;
- status_t err = msg->postAndAwaitResponse(&response);
- if (err == OK && response != NULL) {
- CHECK(response->findInt32("index", &index));
- return index;
- } else {
- return -1;
- }
-}
-
-void NuPlayer::GenericSource::onGetSelectedTrack(const sp<AMessage>& msg) const {
- int32_t tmpType;
- CHECK(msg->findInt32("type", &tmpType));
- media_track_type type = (media_track_type)tmpType;
-
- sp<AMessage> response = new AMessage;
- ssize_t index = doGetSelectedTrack(type);
- response->setInt32("index", index);
-
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
- response->postReply(replyID);
-}
-
-ssize_t NuPlayer::GenericSource::doGetSelectedTrack(media_track_type type) const {
+ Mutex::Autolock _l(mLock);
const Track *track = NULL;
switch (type) {
case MEDIA_TRACK_TYPE_VIDEO:
@@ -1073,38 +1000,9 @@
}
status_t NuPlayer::GenericSource::selectTrack(size_t trackIndex, bool select, int64_t timeUs) {
+ Mutex::Autolock _l(mLock);
ALOGV("%s track: %zu", select ? "select" : "deselect", trackIndex);
- sp<AMessage> msg = new AMessage(kWhatSelectTrack, this);
- msg->setInt32("trackIndex", trackIndex);
- msg->setInt32("select", select);
- msg->setInt64("timeUs", timeUs);
- sp<AMessage> response;
- status_t err = msg->postAndAwaitResponse(&response);
- if (err == OK && response != NULL) {
- CHECK(response->findInt32("err", &err));
- }
-
- return err;
-}
-
-void NuPlayer::GenericSource::onSelectTrack(const sp<AMessage>& msg) {
- int32_t trackIndex, select;
- int64_t timeUs;
- CHECK(msg->findInt32("trackIndex", &trackIndex));
- CHECK(msg->findInt32("select", &select));
- CHECK(msg->findInt64("timeUs", &timeUs));
-
- sp<AMessage> response = new AMessage;
- status_t err = doSelectTrack(trackIndex, select, timeUs);
- response->setInt32("err", err);
-
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
- response->postReply(replyID);
-}
-
-status_t NuPlayer::GenericSource::doSelectTrack(size_t trackIndex, bool select, int64_t timeUs) {
if (trackIndex >= mSources.size()) {
return BAD_INDEX;
}
@@ -1196,46 +1094,11 @@
}
status_t NuPlayer::GenericSource::seekTo(int64_t seekTimeUs, MediaPlayerSeekMode mode) {
- sp<AMessage> msg = new AMessage(kWhatSeek, this);
- msg->setInt64("seekTimeUs", seekTimeUs);
- msg->setInt32("mode", mode);
-
- sp<AMessage> response;
- status_t err = msg->postAndAwaitResponse(&response);
- if (err == OK && response != NULL) {
- CHECK(response->findInt32("err", &err));
- }
-
- return err;
-}
-
-void NuPlayer::GenericSource::onSeek(const sp<AMessage>& msg) {
- int64_t seekTimeUs;
- int32_t mode;
- CHECK(msg->findInt64("seekTimeUs", &seekTimeUs));
- CHECK(msg->findInt32("mode", &mode));
-
- sp<AMessage> response = new AMessage;
- status_t err = doSeek(seekTimeUs, (MediaPlayerSeekMode)mode);
- response->setInt32("err", err);
-
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
- response->postReply(replyID);
-}
-
-status_t NuPlayer::GenericSource::doSeek(int64_t seekTimeUs, MediaPlayerSeekMode mode) {
- mBufferingMonitor->updateDequeuedBufferTime(-1ll);
-
- // If the Widevine source is stopped, do not attempt to read any
- // more buffers.
- //
- // TODO: revisit after widevine is removed. May be able to
- // combine mStopRead with mStarted.
- if (mStopRead) {
- return INVALID_OPERATION;
- }
+ Mutex::Autolock _l(mLock);
+ ALOGV("seekTo: %lld, %d", (long long)seekTimeUs, mode);
if (mVideoTrack.mSource != NULL) {
+ ++mVideoDataGeneration;
+
int64_t actualTimeUs;
readBuffer(MEDIA_TRACK_TYPE_VIDEO, seekTimeUs, mode, &actualTimeUs);
@@ -1246,6 +1109,7 @@
}
if (mAudioTrack.mSource != NULL) {
+ ++mAudioDataGeneration;
readBuffer(MEDIA_TRACK_TYPE_AUDIO, seekTimeUs, MediaPlayerSeekMode::SEEK_CLOSEST);
mAudioLastDequeueTimeUs = seekTimeUs;
}
@@ -1260,12 +1124,8 @@
mFetchTimedTextDataGeneration++;
}
- // 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.
- mBufferingMonitor->stopBufferingIfNecessary();
- mBufferingMonitor->restartPollBuffering();
-
+ ++mPollBufferingGeneration;
+ schedulePollBuffering();
return OK;
}
@@ -1368,9 +1228,29 @@
return ab;
}
-void NuPlayer::GenericSource::postReadBuffer(media_track_type trackType) {
- Mutex::Autolock _l(mReadBufferLock);
+int32_t NuPlayer::GenericSource::getDataGeneration(media_track_type type) const {
+ int32_t generation = -1;
+ switch (type) {
+ case MEDIA_TRACK_TYPE_VIDEO:
+ generation = mVideoDataGeneration;
+ break;
+ case MEDIA_TRACK_TYPE_AUDIO:
+ generation = mAudioDataGeneration;
+ break;
+ case MEDIA_TRACK_TYPE_TIMEDTEXT:
+ generation = mFetchTimedTextDataGeneration;
+ break;
+ case MEDIA_TRACK_TYPE_SUBTITLE:
+ generation = mFetchSubtitleDataGeneration;
+ break;
+ default:
+ break;
+ }
+ return generation;
+}
+
+void NuPlayer::GenericSource::postReadBuffer(media_track_type trackType) {
if ((mPendingReadBufferTypes & (1 << trackType)) == 0) {
mPendingReadBufferTypes |= (1 << trackType);
sp<AMessage> msg = new AMessage(kWhatReadBuffer, this);
@@ -1383,25 +1263,13 @@
int32_t tmpType;
CHECK(msg->findInt32("trackType", &tmpType));
media_track_type trackType = (media_track_type)tmpType;
+ mPendingReadBufferTypes &= ~(1 << trackType);
readBuffer(trackType);
- {
- // only protect the variable change, as readBuffer may
- // take considerable time.
- Mutex::Autolock _l(mReadBufferLock);
- mPendingReadBufferTypes &= ~(1 << trackType);
- }
}
void NuPlayer::GenericSource::readBuffer(
media_track_type trackType, int64_t seekTimeUs, MediaPlayerSeekMode mode,
int64_t *actualTimeUs, bool formatChange) {
- // Do not read data if Widevine source is stopped
- //
- // TODO: revisit after widevine is removed. May be able to
- // combine mStopRead with mStarted.
- if (mStopRead) {
- return;
- }
Track *track;
size_t maxBuffers = 1;
switch (trackType) {
@@ -1445,10 +1313,12 @@
options.setNonBlocking();
}
+ int32_t generation = getDataGeneration(trackType);
for (size_t numBuffers = 0; numBuffers < maxBuffers; ) {
Vector<MediaBuffer *> mediaBuffers;
status_t err = NO_ERROR;
+ mLock.unlock();
if (couldReadMultiple) {
err = track->mSource->readMultiple(
&mediaBuffers, maxBuffers - numBuffers, &options);
@@ -1459,11 +1329,21 @@
mediaBuffers.push_back(mbuf);
}
}
+ mLock.lock();
options.clearNonPersistent();
size_t id = 0;
size_t count = mediaBuffers.size();
+
+ // in case track has been changed since we don't have lock for some time.
+ if (generation != getDataGeneration(trackType)) {
+ for (; id < count; ++id) {
+ mediaBuffers[id]->release();
+ }
+ break;
+ }
+
for (; id < count; ++id) {
int64_t timeUs;
MediaBuffer *mbuf = mediaBuffers[id];
@@ -1474,10 +1354,8 @@
}
if (trackType == MEDIA_TRACK_TYPE_AUDIO) {
mAudioTimeUs = timeUs;
- mBufferingMonitor->updateQueuedTime(true /* isAudio */, timeUs);
} else if (trackType == MEDIA_TRACK_TYPE_VIDEO) {
mVideoTimeUs = timeUs;
- mBufferingMonitor->updateQueuedTime(false /* isAudio */, timeUs);
}
queueDiscontinuityIfNeeded(seeking, formatChange, trackType, track);
@@ -1524,6 +1402,39 @@
break;
}
}
+
+ if (mIsStreaming
+ && (trackType == MEDIA_TRACK_TYPE_VIDEO || trackType == MEDIA_TRACK_TYPE_AUDIO)) {
+ status_t finalResult;
+ int64_t durationUs = track->mPackets->getBufferedDurationUs(&finalResult);
+
+ int64_t markUs = (mPreparing ? mBufferingSettings.mInitialWatermarkMs
+ : mBufferingSettings.mRebufferingWatermarkHighMs) * 1000ll;
+ if (finalResult == ERROR_END_OF_STREAM || durationUs >= markUs) {
+ if (mPreparing || mSentPauseOnBuffering) {
+ Track *counterTrack =
+ (trackType == MEDIA_TRACK_TYPE_VIDEO ? &mAudioTrack : &mVideoTrack);
+ if (counterTrack->mSource != NULL) {
+ durationUs = counterTrack->mPackets->getBufferedDurationUs(&finalResult);
+ }
+ if (finalResult == ERROR_END_OF_STREAM || durationUs >= markUs) {
+ if (mPreparing) {
+ notifyPrepared();
+ mPreparing = false;
+ } else {
+ sendCacheStats();
+ mSentPauseOnBuffering = false;
+ sp<AMessage> notify = dupNotify();
+ notify->setInt32("what", kWhatResumeOnBufferingEnd);
+ notify->post();
+ }
+ }
+ }
+ return;
+ }
+
+ postReadBuffer(trackType);
+ }
}
void NuPlayer::GenericSource::queueDiscontinuityIfNeeded(
@@ -1541,160 +1452,7 @@
}
}
-NuPlayer::GenericSource::BufferingMonitor::BufferingMonitor(const sp<AMessage> ¬ify)
- : mNotify(notify),
- mDurationUs(-1ll),
- mBitrate(-1ll),
- mIsStreaming(false),
- mAudioTimeUs(0),
- mVideoTimeUs(0),
- mPollBufferingGeneration(0),
- mPrepareBuffering(false),
- mBuffering(false),
- mPrevBufferPercentage(-1),
- mOffloadAudio(false),
- mFirstDequeuedBufferRealUs(-1ll),
- mFirstDequeuedBufferMediaUs(-1ll),
- mlastDequeuedBufferMediaUs(-1ll) {
- getDefaultBufferingSettings(&mSettings);
-}
-
-NuPlayer::GenericSource::BufferingMonitor::~BufferingMonitor() {
-}
-
-void NuPlayer::GenericSource::BufferingMonitor::getDefaultBufferingSettings(
- BufferingSettings *buffering /* nonnull */) {
- buffering->mInitialBufferingMode = BUFFERING_MODE_TIME_ONLY;
- buffering->mRebufferingMode = BUFFERING_MODE_TIME_THEN_SIZE;
- buffering->mInitialWatermarkMs = kHighWaterMarkMs;
- buffering->mRebufferingWatermarkLowMs = kLowWaterMarkMs;
- buffering->mRebufferingWatermarkHighMs = kHighWaterMarkRebufferMs;
- buffering->mRebufferingWatermarkLowKB = kLowWaterMarkKB;
- buffering->mRebufferingWatermarkHighKB = kHighWaterMarkKB;
-
- ALOGV("BufferingMonitor::getDefaultBufferingSettings{%s}",
- buffering->toString().string());
-}
-
-status_t NuPlayer::GenericSource::BufferingMonitor::setBufferingSettings(
- const BufferingSettings &buffering) {
- ALOGV("BufferingMonitor::setBufferingSettings{%s}",
- buffering.toString().string());
-
- Mutex::Autolock _l(mLock);
- if (buffering.IsSizeBasedBufferingMode(buffering.mInitialBufferingMode)
- || (buffering.IsTimeBasedBufferingMode(buffering.mRebufferingMode)
- && buffering.mRebufferingWatermarkLowMs > buffering.mRebufferingWatermarkHighMs)
- || (buffering.IsSizeBasedBufferingMode(buffering.mRebufferingMode)
- && buffering.mRebufferingWatermarkLowKB > buffering.mRebufferingWatermarkHighKB)) {
- return BAD_VALUE;
- }
- mSettings = buffering;
- if (mSettings.mInitialBufferingMode == BUFFERING_MODE_NONE) {
- mSettings.mInitialWatermarkMs = BufferingSettings::kNoWatermark;
- }
- if (!mSettings.IsTimeBasedBufferingMode(mSettings.mRebufferingMode)) {
- mSettings.mRebufferingWatermarkLowMs = BufferingSettings::kNoWatermark;
- mSettings.mRebufferingWatermarkHighMs = INT32_MAX;
- }
- if (!mSettings.IsSizeBasedBufferingMode(mSettings.mRebufferingMode)) {
- mSettings.mRebufferingWatermarkLowKB = BufferingSettings::kNoWatermark;
- mSettings.mRebufferingWatermarkHighKB = INT32_MAX;
- }
- return OK;
-}
-
-void NuPlayer::GenericSource::BufferingMonitor::prepare(
- const sp<NuCachedSource2> &cachedSource,
- int64_t durationUs,
- int64_t bitrate,
- bool isStreaming) {
- Mutex::Autolock _l(mLock);
- prepare_l(cachedSource, durationUs, bitrate, isStreaming);
-}
-
-void NuPlayer::GenericSource::BufferingMonitor::stop() {
- Mutex::Autolock _l(mLock);
- prepare_l(NULL /* cachedSource */, -1 /* durationUs */,
- -1 /* bitrate */, false /* isStreaming */);
-}
-
-void NuPlayer::GenericSource::BufferingMonitor::cancelPollBuffering() {
- Mutex::Autolock _l(mLock);
- cancelPollBuffering_l();
-}
-
-void NuPlayer::GenericSource::BufferingMonitor::restartPollBuffering() {
- Mutex::Autolock _l(mLock);
- if (mIsStreaming) {
- cancelPollBuffering_l();
- onPollBuffering_l();
- }
-}
-
-void NuPlayer::GenericSource::BufferingMonitor::stopBufferingIfNecessary() {
- Mutex::Autolock _l(mLock);
- stopBufferingIfNecessary_l();
-}
-
-void NuPlayer::GenericSource::BufferingMonitor::ensureCacheIsFetching() {
- Mutex::Autolock _l(mLock);
- ensureCacheIsFetching_l();
-}
-
-void NuPlayer::GenericSource::BufferingMonitor::updateQueuedTime(bool isAudio, int64_t timeUs) {
- Mutex::Autolock _l(mLock);
- if (isAudio) {
- mAudioTimeUs = timeUs;
- } else {
- mVideoTimeUs = timeUs;
- }
-}
-
-void NuPlayer::GenericSource::BufferingMonitor::setOffloadAudio(bool offload) {
- Mutex::Autolock _l(mLock);
- mOffloadAudio = offload;
-}
-
-void NuPlayer::GenericSource::BufferingMonitor::updateDequeuedBufferTime(int64_t mediaUs) {
- Mutex::Autolock _l(mLock);
- if (mediaUs < 0) {
- mFirstDequeuedBufferRealUs = -1ll;
- mFirstDequeuedBufferMediaUs = -1ll;
- } else if (mFirstDequeuedBufferRealUs < 0) {
- mFirstDequeuedBufferRealUs = ALooper::GetNowUs();
- mFirstDequeuedBufferMediaUs = mediaUs;
- }
- mlastDequeuedBufferMediaUs = mediaUs;
-}
-
-void NuPlayer::GenericSource::BufferingMonitor::prepare_l(
- const sp<NuCachedSource2> &cachedSource,
- int64_t durationUs,
- int64_t bitrate,
- bool isStreaming) {
-
- mCachedSource = cachedSource;
- mDurationUs = durationUs;
- mBitrate = bitrate;
- mIsStreaming = isStreaming;
- mAudioTimeUs = 0;
- mVideoTimeUs = 0;
- mPrepareBuffering = (cachedSource != NULL);
- cancelPollBuffering_l();
- mOffloadAudio = false;
- mFirstDequeuedBufferRealUs = -1ll;
- mFirstDequeuedBufferMediaUs = -1ll;
- mlastDequeuedBufferMediaUs = -1ll;
-}
-
-void NuPlayer::GenericSource::BufferingMonitor::cancelPollBuffering_l() {
- mBuffering = false;
- ++mPollBufferingGeneration;
- mPrevBufferPercentage = -1;
-}
-
-void NuPlayer::GenericSource::BufferingMonitor::notifyBufferingUpdate_l(int32_t percentage) {
+void NuPlayer::GenericSource::notifyBufferingUpdate(int32_t percentage) {
// Buffering percent could go backward as it's estimated from remaining
// data and last access time. This could cause the buffering position
// drawn on media control to jitter slightly. Remember previously reported
@@ -1707,106 +1465,28 @@
mPrevBufferPercentage = percentage;
- ALOGV("notifyBufferingUpdate_l: buffering %d%%", percentage);
+ ALOGV("notifyBufferingUpdate: buffering %d%%", percentage);
- sp<AMessage> msg = mNotify->dup();
- msg->setInt32("what", kWhatBufferingUpdate);
- msg->setInt32("percentage", percentage);
- msg->post();
+ sp<AMessage> notify = dupNotify();
+ notify->setInt32("what", kWhatBufferingUpdate);
+ notify->setInt32("percentage", percentage);
+ notify->post();
}
-void NuPlayer::GenericSource::BufferingMonitor::startBufferingIfNecessary_l() {
- if (mPrepareBuffering) {
- return;
- }
-
- if (!mBuffering) {
- ALOGD("startBufferingIfNecessary_l");
-
- mBuffering = true;
-
- ensureCacheIsFetching_l();
- sendCacheStats_l();
-
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatPauseOnBufferingStart);
- notify->post();
- }
-}
-
-void NuPlayer::GenericSource::BufferingMonitor::stopBufferingIfNecessary_l() {
- if (mPrepareBuffering) {
- ALOGD("stopBufferingIfNecessary_l, mBuffering=%d", mBuffering);
-
- mPrepareBuffering = false;
-
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatPrepared);
- notify->setInt32("err", OK);
- notify->post();
-
- return;
- }
-
- if (mBuffering) {
- ALOGD("stopBufferingIfNecessary_l");
- mBuffering = false;
-
- sendCacheStats_l();
-
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatResumeOnBufferingEnd);
- notify->post();
- }
-}
-
-void NuPlayer::GenericSource::BufferingMonitor::sendCacheStats_l() {
- int32_t kbps = 0;
- status_t err = UNKNOWN_ERROR;
-
- if (mCachedSource != NULL) {
- err = mCachedSource->getEstimatedBandwidthKbps(&kbps);
- }
-
- if (err == OK) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatCacheStats);
- notify->setInt32("bandwidth", kbps);
- notify->post();
- }
-}
-
-void NuPlayer::GenericSource::BufferingMonitor::ensureCacheIsFetching_l() {
- if (mCachedSource != NULL) {
- mCachedSource->resumeFetchingIfNecessary();
- }
-}
-
-void NuPlayer::GenericSource::BufferingMonitor::schedulePollBuffering_l() {
+void NuPlayer::GenericSource::schedulePollBuffering() {
sp<AMessage> msg = new AMessage(kWhatPollBuffering, this);
msg->setInt32("generation", mPollBufferingGeneration);
// Enquires buffering status every second.
msg->post(1000000ll);
}
-int64_t NuPlayer::GenericSource::BufferingMonitor::getLastReadPosition_l() {
- if (mAudioTimeUs > 0) {
- return mAudioTimeUs;
- } else if (mVideoTimeUs > 0) {
- return mVideoTimeUs;
- } else {
- return 0;
- }
-}
-
-void NuPlayer::GenericSource::BufferingMonitor::onPollBuffering_l() {
+void NuPlayer::GenericSource::onPollBuffering() {
status_t finalStatus = UNKNOWN_ERROR;
int64_t cachedDurationUs = -1ll;
ssize_t cachedDataRemaining = -1;
if (mCachedSource != NULL) {
- cachedDataRemaining =
- mCachedSource->approxDataRemaining(&finalStatus);
+ cachedDataRemaining = mCachedSource->approxDataRemaining(&finalStatus);
if (finalStatus == OK) {
off64_t size;
@@ -1824,157 +1504,49 @@
}
if (finalStatus != OK) {
- ALOGV("onPollBuffering_l: EOS (finalStatus = %d)", finalStatus);
+ ALOGV("onPollBuffering: EOS (finalStatus = %d)", finalStatus);
if (finalStatus == ERROR_END_OF_STREAM) {
- notifyBufferingUpdate_l(100);
+ notifyBufferingUpdate(100);
}
- stopBufferingIfNecessary_l();
return;
}
if (cachedDurationUs >= 0ll) {
if (mDurationUs > 0ll) {
- int64_t cachedPosUs = getLastReadPosition_l() + cachedDurationUs;
+ int64_t cachedPosUs = getLastReadPosition() + cachedDurationUs;
int percentage = 100.0 * cachedPosUs / mDurationUs;
if (percentage > 100) {
percentage = 100;
}
- notifyBufferingUpdate_l(percentage);
+ notifyBufferingUpdate(percentage);
}
- ALOGV("onPollBuffering_l: cachedDurationUs %.1f sec", cachedDurationUs / 1000000.0f);
-
- if (mPrepareBuffering) {
- if (cachedDurationUs > mSettings.mInitialWatermarkMs * 1000) {
- stopBufferingIfNecessary_l();
- }
- } else if (mSettings.IsTimeBasedBufferingMode(mSettings.mRebufferingMode)) {
- if (cachedDurationUs < mSettings.mRebufferingWatermarkLowMs * 1000) {
- // Take into account the data cached in downstream components to try to avoid
- // unnecessary pause.
- if (mOffloadAudio && mFirstDequeuedBufferRealUs >= 0) {
- int64_t downStreamCacheUs =
- mlastDequeuedBufferMediaUs - mFirstDequeuedBufferMediaUs
- - (ALooper::GetNowUs() - mFirstDequeuedBufferRealUs);
- if (downStreamCacheUs > 0) {
- cachedDurationUs += downStreamCacheUs;
- }
- }
-
- if (cachedDurationUs < mSettings.mRebufferingWatermarkLowMs * 1000) {
- startBufferingIfNecessary_l();
- }
- } else if (cachedDurationUs > mSettings.mRebufferingWatermarkHighMs * 1000) {
- stopBufferingIfNecessary_l();
- }
- }
- } else if (cachedDataRemaining >= 0
- && mSettings.IsSizeBasedBufferingMode(mSettings.mRebufferingMode)) {
- ALOGV("onPollBuffering_l: cachedDataRemaining %zd bytes",
- cachedDataRemaining);
-
- if (cachedDataRemaining < (mSettings.mRebufferingWatermarkLowKB << 10)) {
- startBufferingIfNecessary_l();
- } else if (cachedDataRemaining > (mSettings.mRebufferingWatermarkHighKB << 10)) {
- stopBufferingIfNecessary_l();
- }
+ ALOGV("onPollBuffering: cachedDurationUs %.1f sec", cachedDurationUs / 1000000.0f);
}
- schedulePollBuffering_l();
-}
-
-void NuPlayer::GenericSource::BufferingMonitor::onMessageReceived(const sp<AMessage> &msg) {
- switch (msg->what()) {
- case kWhatPollBuffering:
- {
- int32_t generation;
- CHECK(msg->findInt32("generation", &generation));
- Mutex::Autolock _l(mLock);
- if (generation == mPollBufferingGeneration) {
- onPollBuffering_l();
- }
- break;
- }
- default:
- TRESPASS();
- break;
- }
+ schedulePollBuffering();
}
// Modular DRM
status_t NuPlayer::GenericSource::prepareDrm(
- const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId, sp<ICrypto> *crypto)
-{
+ const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId, sp<ICrypto> *outCrypto) {
+ Mutex::Autolock _l(mLock);
ALOGV("prepareDrm");
- sp<AMessage> msg = new AMessage(kWhatPrepareDrm, this);
- // synchronous call so just passing the address but with local copies of "const" args
- uint8_t UUID[16];
- memcpy(UUID, uuid, sizeof(UUID));
- Vector<uint8_t> sessionId = drmSessionId;
- msg->setPointer("uuid", (void*)UUID);
- msg->setPointer("drmSessionId", (void*)&sessionId);
- msg->setPointer("crypto", (void*)crypto);
-
- sp<AMessage> response;
- status_t status = msg->postAndAwaitResponse(&response);
-
- if (status == OK && response != NULL) {
- CHECK(response->findInt32("status", &status));
- ALOGV_IF(status == OK, "prepareDrm: mCrypto: %p (%d)", crypto->get(),
- (*crypto != NULL ? (*crypto)->getStrongCount() : 0));
- ALOGD("prepareDrm ret: %d ", status);
- } else {
- ALOGE("prepareDrm err: %d", status);
- }
-
- return status;
-}
-
-status_t NuPlayer::GenericSource::releaseDrm()
-{
- ALOGV("releaseDrm");
-
- sp<AMessage> msg = new AMessage(kWhatReleaseDrm, this);
-
- // synchronous call to update the source states before the player proceedes with crypto cleanup
- sp<AMessage> response;
- status_t status = msg->postAndAwaitResponse(&response);
-
- if (status == OK && response != NULL) {
- ALOGD("releaseDrm ret: OK ");
- } else {
- ALOGE("releaseDrm err: %d", status);
- }
-
- return status;
-}
-
-status_t NuPlayer::GenericSource::onPrepareDrm(const sp<AMessage> &msg)
-{
- ALOGV("onPrepareDrm ");
-
mIsDrmProtected = false;
mIsDrmReleased = false;
mIsSecure = false;
- uint8_t *uuid;
- Vector<uint8_t> *drmSessionId;
- sp<ICrypto> *outCrypto;
- CHECK(msg->findPointer("uuid", (void**)&uuid));
- CHECK(msg->findPointer("drmSessionId", (void**)&drmSessionId));
- CHECK(msg->findPointer("crypto", (void**)&outCrypto));
-
status_t status = OK;
- sp<ICrypto> crypto = NuPlayerDrm::createCryptoAndPlugin(uuid, *drmSessionId, status);
+ sp<ICrypto> crypto = NuPlayerDrm::createCryptoAndPlugin(uuid, drmSessionId, status);
if (crypto == NULL) {
- ALOGE("onPrepareDrm: createCrypto failed. status: %d", status);
+ ALOGE("prepareDrm: createCrypto failed. status: %d", status);
return status;
}
- ALOGV("onPrepareDrm: createCryptoAndPlugin succeeded for uuid: %s",
+ ALOGV("prepareDrm: createCryptoAndPlugin succeeded for uuid: %s",
DrmUUID::toHexString(uuid).string());
*outCrypto = crypto;
@@ -1983,14 +1555,14 @@
if (mMimes.size() == 0) {
status = UNKNOWN_ERROR;
- ALOGE("onPrepareDrm: Unexpected. Must have at least one track. status: %d", status);
+ ALOGE("prepareDrm: Unexpected. Must have at least one track. status: %d", status);
return status;
}
// first mime in this list is either the video track, or the first audio track
const char *mime = mMimes[0].string();
mIsSecure = crypto->requiresSecureDecoderComponent(mime);
- ALOGV("onPrepareDrm: requiresSecureDecoderComponent mime: %s isSecure: %d",
+ ALOGV("prepareDrm: requiresSecureDecoderComponent mime: %s isSecure: %d",
mime, mIsSecure);
// Checking the member flags while in the looper to send out the notification.
@@ -2004,18 +1576,27 @@
FLAG_CAN_SEEK_FORWARD |
FLAG_CAN_SEEK);
+ if (status == OK) {
+ ALOGV("prepareDrm: mCrypto: %p (%d)", outCrypto->get(),
+ (*outCrypto != NULL ? (*outCrypto)->getStrongCount() : 0));
+ ALOGD("prepareDrm ret: %d ", status);
+ } else {
+ ALOGE("prepareDrm err: %d", status);
+ }
return status;
}
-status_t NuPlayer::GenericSource::onReleaseDrm()
-{
+status_t NuPlayer::GenericSource::releaseDrm() {
+ Mutex::Autolock _l(mLock);
+ ALOGV("releaseDrm");
+
if (mIsDrmProtected) {
mIsDrmProtected = false;
// to prevent returning any more buffer after stop/releaseDrm (b/37960096)
mIsDrmReleased = true;
- ALOGV("onReleaseDrm: mIsDrmProtected is reset.");
+ ALOGV("releaseDrm: mIsDrmProtected is reset.");
} else {
- ALOGE("onReleaseDrm: mIsDrmProtected is already false.");
+ ALOGE("releaseDrm: mIsDrmProtected is already false.");
}
return OK;