Merge "Properly buffer a certain amount of data on streaming sources before finishing prepare()." into gingerbread
This commit is contained in:
committed by
Android (Google) Code Review
commit
cc4a38c60f
@@ -50,6 +50,9 @@
|
|||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
|
static int64_t kLowWaterMarkUs = 2000000ll; // 2secs
|
||||||
|
static int64_t kHighWaterMarkUs = 10000000ll; // 10secs
|
||||||
|
|
||||||
struct AwesomeEvent : public TimedEventQueue::Event {
|
struct AwesomeEvent : public TimedEventQueue::Event {
|
||||||
AwesomeEvent(
|
AwesomeEvent(
|
||||||
AwesomePlayer *player,
|
AwesomePlayer *player,
|
||||||
@@ -450,6 +453,25 @@ void AwesomePlayer::notifyListener_l(int msg, int ext1, int ext2) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns true iff cached duration is available/applicable.
|
||||||
|
bool AwesomePlayer::getCachedDuration_l(int64_t *durationUs, bool *eos) {
|
||||||
|
off_t totalSize;
|
||||||
|
|
||||||
|
if (mRTSPController != NULL) {
|
||||||
|
*durationUs = mRTSPController->getQueueDurationUs(eos);
|
||||||
|
return true;
|
||||||
|
} else if (mCachedSource != NULL && mDurationUs >= 0
|
||||||
|
&& mCachedSource->getSize(&totalSize) == OK) {
|
||||||
|
int64_t bitrate = totalSize * 8000000ll / mDurationUs; // in bits/sec
|
||||||
|
|
||||||
|
size_t cachedDataRemaining = mCachedSource->approxDataRemaining(eos);
|
||||||
|
*durationUs = cachedDataRemaining * 8000000ll / bitrate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void AwesomePlayer::onBufferingUpdate() {
|
void AwesomePlayer::onBufferingUpdate() {
|
||||||
Mutex::Autolock autoLock(mLock);
|
Mutex::Autolock autoLock(mLock);
|
||||||
if (!mBufferingEventPending) {
|
if (!mBufferingEventPending) {
|
||||||
@@ -457,78 +479,82 @@ void AwesomePlayer::onBufferingUpdate() {
|
|||||||
}
|
}
|
||||||
mBufferingEventPending = false;
|
mBufferingEventPending = false;
|
||||||
|
|
||||||
int kLowWaterMarkSecs = 2;
|
if (mCachedSource != NULL) {
|
||||||
int kHighWaterMarkSecs = 10;
|
|
||||||
|
|
||||||
if (mRTSPController != NULL) {
|
|
||||||
bool eos;
|
bool eos;
|
||||||
int64_t queueDurationUs = mRTSPController->getQueueDurationUs(&eos);
|
size_t cachedDataRemaining = mCachedSource->approxDataRemaining(&eos);
|
||||||
|
|
||||||
LOGV("queueDurationUs = %.2f secs", queueDurationUs / 1E6);
|
if (eos) {
|
||||||
|
notifyListener_l(MEDIA_BUFFERING_UPDATE, 100);
|
||||||
|
} else {
|
||||||
|
off_t size;
|
||||||
|
if (mDurationUs >= 0 && mCachedSource->getSize(&size) == OK) {
|
||||||
|
int64_t bitrate = size * 8000000ll / mDurationUs; // in bits/sec
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
const size_t kLowWaterMarkBytes = 400000;
|
||||||
|
const size_t kHighWaterMarkBytes = 1000000;
|
||||||
|
|
||||||
|
if ((mFlags & PLAYING) && !eos
|
||||||
|
&& (cachedDataRemaining < kLowWaterMarkBytes)) {
|
||||||
|
LOGI("cache is running low (< %d) , pausing.",
|
||||||
|
kLowWaterMarkBytes);
|
||||||
|
mFlags |= CACHE_UNDERRUN;
|
||||||
|
pause_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)) {
|
||||||
if ((mFlags & PLAYING) && !eos
|
if ((mFlags & PLAYING) && !eos
|
||||||
&& (queueDurationUs < kLowWaterMarkSecs * 1000000ll)) {
|
&& (cachedDurationUs < kLowWaterMarkUs)) {
|
||||||
LOGI("rtsp cache is running low, pausing.");
|
LOGI("cache is running low (%.2f secs) , pausing.",
|
||||||
|
cachedDurationUs / 1E6);
|
||||||
mFlags |= CACHE_UNDERRUN;
|
mFlags |= CACHE_UNDERRUN;
|
||||||
pause_l();
|
pause_l();
|
||||||
notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
|
notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
|
||||||
} else if ((mFlags & CACHE_UNDERRUN)
|
} else if (eos || cachedDurationUs > kHighWaterMarkUs) {
|
||||||
&& (eos || queueDurationUs > kHighWaterMarkSecs * 1000000ll)) {
|
if (mFlags & CACHE_UNDERRUN) {
|
||||||
LOGI("rtsp cache has filled up, resuming.");
|
LOGI("cache has filled up (%.2f secs), resuming.",
|
||||||
mFlags &= ~CACHE_UNDERRUN;
|
cachedDurationUs / 1E6);
|
||||||
play_l();
|
mFlags &= ~CACHE_UNDERRUN;
|
||||||
notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
|
play_l();
|
||||||
}
|
notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
|
||||||
|
} else if (mFlags & PREPARING) {
|
||||||
postBufferingEvent_l();
|
LOGV("cache has filled up (%.2f secs), prepare is done",
|
||||||
return;
|
cachedDurationUs / 1E6);
|
||||||
}
|
finishAsyncPrepare_l();
|
||||||
|
|
||||||
if (mCachedSource == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool eos;
|
|
||||||
size_t cachedDataRemaining = mCachedSource->approxDataRemaining(&eos);
|
|
||||||
|
|
||||||
size_t lowWatermark = 400000;
|
|
||||||
size_t highWatermark = 1000000;
|
|
||||||
|
|
||||||
if (eos) {
|
|
||||||
notifyListener_l(MEDIA_BUFFERING_UPDATE, 100);
|
|
||||||
} else {
|
|
||||||
off_t size;
|
|
||||||
if (mDurationUs >= 0 && mCachedSource->getSize(&size) == OK) {
|
|
||||||
int64_t bitrate = size * 8000000ll / mDurationUs; // in bits/sec
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
lowWatermark = kLowWaterMarkSecs * bitrate / 8;
|
|
||||||
highWatermark = kHighWaterMarkSecs * bitrate / 8;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((mFlags & PLAYING) && !eos && (cachedDataRemaining < lowWatermark)) {
|
|
||||||
LOGI("cache is running low (< %d) , pausing.", lowWatermark);
|
|
||||||
mFlags |= CACHE_UNDERRUN;
|
|
||||||
pause_l();
|
|
||||||
notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
|
|
||||||
} else if ((mFlags & CACHE_UNDERRUN)
|
|
||||||
&& (eos || cachedDataRemaining > highWatermark)) {
|
|
||||||
LOGI("cache has filled up (> %d), resuming.", highWatermark);
|
|
||||||
mFlags &= ~CACHE_UNDERRUN;
|
|
||||||
play_l();
|
|
||||||
notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
|
|
||||||
}
|
|
||||||
|
|
||||||
postBufferingEvent_l();
|
postBufferingEvent_l();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1437,45 +1463,49 @@ bool AwesomePlayer::ContinuePreparation(void *cookie) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AwesomePlayer::onPrepareAsyncEvent() {
|
void AwesomePlayer::onPrepareAsyncEvent() {
|
||||||
{
|
Mutex::Autolock autoLock(mLock);
|
||||||
Mutex::Autolock autoLock(mLock);
|
|
||||||
|
|
||||||
if (mFlags & PREPARE_CANCELLED) {
|
if (mFlags & PREPARE_CANCELLED) {
|
||||||
LOGI("prepare was cancelled before doing anything");
|
LOGI("prepare was cancelled before doing anything");
|
||||||
abortPrepare(UNKNOWN_ERROR);
|
abortPrepare(UNKNOWN_ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mUri.size() > 0) {
|
||||||
|
status_t err = finishSetDataSource_l();
|
||||||
|
|
||||||
|
if (err != OK) {
|
||||||
|
abortPrepare(err);
|
||||||
return;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Mutex::Autolock autoLock(mLock);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mCachedSource != NULL || mRTSPController != NULL) {
|
||||||
|
postBufferingEvent_l();
|
||||||
|
} else {
|
||||||
|
finishAsyncPrepare_l();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AwesomePlayer::finishAsyncPrepare_l() {
|
||||||
if (mIsAsyncPrepare) {
|
if (mIsAsyncPrepare) {
|
||||||
if (mVideoWidth < 0 || mVideoHeight < 0) {
|
if (mVideoWidth < 0 || mVideoHeight < 0) {
|
||||||
notifyListener_l(MEDIA_SET_VIDEO_SIZE, 0, 0);
|
notifyListener_l(MEDIA_SET_VIDEO_SIZE, 0, 0);
|
||||||
@@ -1491,8 +1521,6 @@ void AwesomePlayer::onPrepareAsyncEvent() {
|
|||||||
mFlags |= PREPARED;
|
mFlags |= PREPARED;
|
||||||
mAsyncPrepareEvent = NULL;
|
mAsyncPrepareEvent = NULL;
|
||||||
mPreparedCondition.broadcast();
|
mPreparedCondition.broadcast();
|
||||||
|
|
||||||
postBufferingEvent_l();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t AwesomePlayer::suspend() {
|
status_t AwesomePlayer::suspend() {
|
||||||
|
|||||||
@@ -238,6 +238,9 @@ private:
|
|||||||
void onCheckAudioStatus();
|
void onCheckAudioStatus();
|
||||||
void onPrepareAsyncEvent();
|
void onPrepareAsyncEvent();
|
||||||
void abortPrepare(status_t err);
|
void abortPrepare(status_t err);
|
||||||
|
void finishAsyncPrepare_l();
|
||||||
|
|
||||||
|
bool getCachedDuration_l(int64_t *durationUs, bool *eos);
|
||||||
|
|
||||||
status_t finishSetDataSource_l();
|
status_t finishSetDataSource_l();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user