From d5923409bbcbb22954a92c2b497ef4492d7cb6a5 Mon Sep 17 00:00:00 2001 From: Lajos Molnar Date: Mon, 20 Oct 2014 17:47:54 -0700 Subject: mediaplayer: limit scheduling video frames into the future This addresses when video timestamps jumps before an audio timestamp, but still works on slideshow video clips (<=1fps). This, however, will not skip time-changes on video-only live video streams, as we cannot distinguish live slideshow video clips from non-slideshow ones. Bug: 18032127 Change-Id: I959a714edfe1c8cf3b84704c693dcd1b3e5b7855 --- .../nuplayer/NuPlayerRenderer.cpp | 39 +++++++++++++++++++++- .../nuplayer/NuPlayerRenderer.h | 5 +++ 2 files changed, 43 insertions(+), 1 deletion(-) (limited to 'media') diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 46a2590..638d9bc 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -82,7 +82,9 @@ NuPlayer::Renderer::Renderer( mAudioRenderingStartGeneration(0), mAudioOffloadPauseTimeoutGeneration(0), mAudioOffloadTornDown(false), - mCurrentOffloadInfo(AUDIO_INFO_INITIALIZER) { + mCurrentOffloadInfo(AUDIO_INFO_INITIALIZER), + mTotalBuffersQueued(0), + mLastAudioBufferDrained(0) { readProperties(); } @@ -361,6 +363,19 @@ void NuPlayer::Renderer::onMessageReceived(const sp &msg) { break; } + case kWhatPostDrainVideoQueue: + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + if (generation != mVideoQueueGeneration) { + break; + } + + mDrainVideoQueuePending = false; + postDrainVideoQueue(); + break; + } + case kWhatQueueBuffer: { onQueueBuffer(msg); @@ -580,6 +595,8 @@ bool NuPlayer::Renderer::onDrainAudioQueue() { while (numBytesAvailableToWrite > 0 && !mAudioQueue.empty()) { QueueEntry *entry = &*mAudioQueue.begin(); + mLastAudioBufferDrained = entry->mBufferOrdinal; + if (entry->mBuffer == NULL) { // EOS int64_t postEOSDelayUs = 0; @@ -716,6 +733,25 @@ void NuPlayer::Renderer::postDrainVideoQueue() { } else { realTimeUs = getRealTimeUs(mediaTimeUs, nowUs); } + + // Heuristics to handle situation when media time changed without a + // discontinuity. If we have not drained an audio buffer that was + // received after this buffer, repost in 10 msec. Otherwise repost + // in 500 msec. + delayUs = realTimeUs - nowUs; + if (delayUs > 500000) { + int64_t postDelayUs = 500000; + if (mHasAudio && (mLastAudioBufferDrained - entry.mBufferOrdinal) <= 0) { + postDelayUs = 10000; + } + msg->setWhat(kWhatPostDrainVideoQueue); + msg->post(postDelayUs); + mVideoScheduler->restart(); + ALOGI("possible video time jump of %dms, retrying in %dms", + (int)(delayUs / 1000), (int)(postDelayUs / 1000)); + mDrainVideoQueuePending = true; + return; + } } realTimeUs = mVideoScheduler->schedule(realTimeUs * 1000) / 1000; @@ -855,6 +891,7 @@ void NuPlayer::Renderer::onQueueBuffer(const sp &msg) { entry.mNotifyConsumed = notifyConsumed; entry.mOffset = 0; entry.mFinalResult = OK; + entry.mBufferOrdinal = ++mTotalBuffersQueued; if (audio) { Mutex::Autolock autoLock(mLock); diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h index 7079f85..b15a266 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h @@ -101,6 +101,7 @@ private: enum { kWhatDrainAudioQueue = 'draA', kWhatDrainVideoQueue = 'draV', + kWhatPostDrainVideoQueue = 'pDVQ', kWhatQueueBuffer = 'queB', kWhatQueueEOS = 'qEOS', kWhatFlush = 'flus', @@ -119,6 +120,7 @@ private: sp mNotifyConsumed; size_t mOffset; status_t mFinalResult; + int32_t mBufferOrdinal; }; static const int64_t kMinPositionUpdateDelayUs; @@ -169,6 +171,9 @@ private: bool mAudioOffloadTornDown; audio_offload_info_t mCurrentOffloadInfo; + int32_t mTotalBuffersQueued; + int32_t mLastAudioBufferDrained; + size_t fillAudioBuffer(void *buffer, size_t size); bool onDrainAudioQueue(); -- cgit v1.1