NuPlayerRenderer: use timer for video rendering
MediaClock: timer can be adjusted by system time offset
Test: plays many media files
Bug: 65204641
Change-Id: Icd73a06d9b2fe720224ed951db94136f399d82f8
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index a568a17..50db4c9 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -1248,82 +1248,48 @@
return;
}
- bool needRepostDrainVideoQueue = false;
- int64_t delayUs;
int64_t nowUs = ALooper::GetNowUs();
- int64_t realTimeUs;
if (mFlags & FLAG_REAL_TIME) {
- int64_t mediaTimeUs;
- CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
- realTimeUs = mediaTimeUs;
- } else {
- int64_t mediaTimeUs;
- CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
+ int64_t realTimeUs;
+ CHECK(entry.mBuffer->meta()->findInt64("timeUs", &realTimeUs));
- {
- Mutex::Autolock autoLock(mLock);
- if (mAnchorTimeMediaUs < 0) {
- mMediaClock->updateAnchor(mediaTimeUs, nowUs, mediaTimeUs);
- mAnchorTimeMediaUs = mediaTimeUs;
- realTimeUs = nowUs;
- } else if (!mVideoSampleReceived) {
- // Always render the first video frame.
- realTimeUs = nowUs;
- } else if (mAudioFirstAnchorTimeMediaUs < 0
- || mMediaClock->getRealTimeFor(mediaTimeUs, &realTimeUs) == OK) {
- realTimeUs = getRealTimeUs(mediaTimeUs, nowUs);
- } else if (mediaTimeUs - mAudioFirstAnchorTimeMediaUs >= 0) {
- needRepostDrainVideoQueue = true;
- realTimeUs = nowUs;
- } else {
- realTimeUs = nowUs;
- }
- }
- if (!mHasAudio) {
- // smooth out videos >= 10fps
- mMediaClock->updateMaxTimeMedia(mediaTimeUs + 100000);
- }
+ realTimeUs = mVideoScheduler->schedule(realTimeUs * 1000) / 1000;
- // 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;
- int64_t postDelayUs = -1;
- if (delayUs > 500000) {
- postDelayUs = 500000;
- if (mHasAudio && (mLastAudioBufferDrained - entry.mBufferOrdinal) <= 0) {
- postDelayUs = 10000;
- }
- } else if (needRepostDrainVideoQueue) {
- // CHECK(mPlaybackRate > 0);
- // CHECK(mAudioFirstAnchorTimeMediaUs >= 0);
- // CHECK(mediaTimeUs - mAudioFirstAnchorTimeMediaUs >= 0);
- postDelayUs = mediaTimeUs - mAudioFirstAnchorTimeMediaUs;
- postDelayUs /= mPlaybackRate;
- }
+ int64_t twoVsyncsUs = 2 * (mVideoScheduler->getVsyncPeriod() / 1000);
- if (postDelayUs >= 0) {
- msg->setWhat(kWhatPostDrainVideoQueue);
- msg->post(postDelayUs);
- mVideoScheduler->restart();
- ALOGI("possible video time jump of %dms (%lld : %lld) or uninitialized media clock,"
- " retrying in %dms",
- (int)(delayUs / 1000), (long long)mediaTimeUs,
- (long long)mAudioFirstAnchorTimeMediaUs, (int)(postDelayUs / 1000));
- mDrainVideoQueuePending = true;
- return;
- }
+ int64_t delayUs = realTimeUs - nowUs;
+
+ ALOGW_IF(delayUs > 500000, "unusually high delayUs: %lld", (long long)delayUs);
+ // post 2 display refreshes before rendering is due
+ msg->post(delayUs > twoVsyncsUs ? delayUs - twoVsyncsUs : 0);
+
+ mDrainVideoQueuePending = true;
+ return;
}
- realTimeUs = mVideoScheduler->schedule(realTimeUs * 1000) / 1000;
- int64_t twoVsyncsUs = 2 * (mVideoScheduler->getVsyncPeriod() / 1000);
+ int64_t mediaTimeUs;
+ CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
- delayUs = realTimeUs - nowUs;
+ {
+ Mutex::Autolock autoLock(mLock);
+ if (mAnchorTimeMediaUs < 0) {
+ mMediaClock->updateAnchor(mediaTimeUs, nowUs, mediaTimeUs);
+ mAnchorTimeMediaUs = mediaTimeUs;
+ }
+ }
+ if (!mHasAudio) {
+ // smooth out videos >= 10fps
+ mMediaClock->updateMaxTimeMedia(mediaTimeUs + 100000);
+ }
- ALOGW_IF(delayUs > 500000, "unusually high delayUs: %" PRId64, delayUs);
- // post 2 display refreshes before rendering is due
- msg->post(delayUs > twoVsyncsUs ? delayUs - twoVsyncsUs : 0);
+ if (!mVideoSampleReceived || mediaTimeUs < mAudioFirstAnchorTimeMediaUs) {
+ msg->post();
+ } else {
+ int64_t twoVsyncsUs = 2 * (mVideoScheduler->getVsyncPeriod() / 1000);
+
+ // post 2 display refreshes before rendering is due
+ mMediaClock->addTimer(msg, mediaTimeUs, -twoVsyncsUs);
+ }
mDrainVideoQueuePending = true;
}
@@ -1357,6 +1323,7 @@
realTimeUs = getRealTimeUs(mediaTimeUs, nowUs);
}
+ realTimeUs = mVideoScheduler->schedule(realTimeUs * 1000) / 1000;
bool tooLate = false;
diff --git a/media/libstagefright/MediaClock.cpp b/media/libstagefright/MediaClock.cpp
index 669e09b..15843a2 100644
--- a/media/libstagefright/MediaClock.cpp
+++ b/media/libstagefright/MediaClock.cpp
@@ -17,6 +17,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaClock"
#include <utils/Log.h>
+#include <map>
#include <media/stagefright/MediaClock.h>
@@ -29,6 +30,12 @@
// If larger than this threshold, it's treated as discontinuity.
static const int64_t kAnchorFluctuationAllowedUs = 10000ll;
+MediaClock::Timer::Timer(const sp<AMessage> ¬ify, int64_t mediaTimeUs, int64_t adjustRealUs)
+ : mNotify(notify),
+ mMediaTimeUs(mediaTimeUs),
+ mAdjustRealUs(adjustRealUs) {
+}
+
MediaClock::MediaClock()
: mAnchorTimeMediaUs(-1),
mAnchorTimeRealUs(-1),
@@ -59,8 +66,8 @@
Mutex::Autolock autoLock(mLock);
auto it = mTimers.begin();
while (it != mTimers.end()) {
- it->second->setInt32("reason", TIMER_REASON_RESET);
- it->second->post();
+ it->mNotify->setInt32("reason", TIMER_REASON_RESET);
+ it->mNotify->post();
it = mTimers.erase(it);
}
mAnchorTimeMediaUs = -1;
@@ -204,15 +211,26 @@
return OK;
}
-void MediaClock::addTimer(const sp<AMessage> ¬ify, int64_t mediaTimeUs) {
+void MediaClock::addTimer(const sp<AMessage> ¬ify, int64_t mediaTimeUs,
+ int64_t adjustRealUs) {
Mutex::Autolock autoLock(mLock);
- int64_t nextMediaTimeUs = INT64_MAX;
- if (!mTimers.empty()) {
- nextMediaTimeUs = mTimers.begin()->first;
+
+ bool updateTimer = (mPlaybackRate != 0.0);
+ if (updateTimer) {
+ auto it = mTimers.begin();
+ while (it != mTimers.end()) {
+ if (((it->mAdjustRealUs - (double)adjustRealUs) * (double)mPlaybackRate
+ + (it->mMediaTimeUs - mediaTimeUs)) <= 0) {
+ updateTimer = false;
+ break;
+ }
+ ++it;
+ }
}
- mTimers.emplace(mediaTimeUs, notify);
- if (mediaTimeUs < nextMediaTimeUs) {
+ mTimers.emplace_back(notify, mediaTimeUs, adjustRealUs);
+
+ if (updateTimer) {
++mGeneration;
processTimers_l();
}
@@ -248,20 +266,51 @@
return;
}
+ int64_t nextLapseRealUs = INT64_MAX;
+ std::multimap<int64_t, Timer> notifyList;
auto it = mTimers.begin();
- while (it != mTimers.end() && it->first <= nowMediaTimeUs) {
- it->second->setInt32("reason", TIMER_REASON_REACHED);
- it->second->post();
- it = mTimers.erase(it);
+ while (it != mTimers.end()) {
+ double diff = it->mAdjustRealUs * (double)mPlaybackRate
+ + it->mMediaTimeUs - nowMediaTimeUs;
+ int64_t diffMediaUs;
+ if (diff > (double)INT64_MAX) {
+ diffMediaUs = INT64_MAX;
+ } else if (diff < (double)INT64_MIN) {
+ diffMediaUs = INT64_MIN;
+ } else {
+ diffMediaUs = diff;
+ }
+
+ if (diffMediaUs <= 0) {
+ notifyList.emplace(diffMediaUs, *it);
+ it = mTimers.erase(it);
+ } else {
+ if (mPlaybackRate != 0.0
+ && (double)diffMediaUs < INT64_MAX * (double)mPlaybackRate) {
+ int64_t targetRealUs = diffMediaUs / (double)mPlaybackRate;
+ if (targetRealUs < nextLapseRealUs) {
+ nextLapseRealUs = targetRealUs;
+ }
+ }
+ ++it;
+ }
}
- if (it == mTimers.end() || mPlaybackRate == 0.0 || mAnchorTimeMediaUs < 0) {
+ auto itNotify = notifyList.begin();
+ while (itNotify != notifyList.end()) {
+ itNotify->second.mNotify->setInt32("reason", TIMER_REASON_REACHED);
+ itNotify->second.mNotify->post();
+ itNotify = notifyList.erase(itNotify);
+ }
+
+ if (mTimers.empty() || mPlaybackRate == 0.0 || mAnchorTimeMediaUs < 0
+ || nextLapseRealUs == INT64_MAX) {
return;
}
sp<AMessage> msg = new AMessage(kWhatTimeIsUp, this);
msg->setInt32("generation", mGeneration);
- msg->post((it->first - nowMediaTimeUs) / (double)mPlaybackRate);
+ msg->post(nextLapseRealUs);
}
} // namespace android
diff --git a/media/libstagefright/include/media/stagefright/MediaClock.h b/media/libstagefright/include/media/stagefright/MediaClock.h
index 59f700a..7511913 100644
--- a/media/libstagefright/include/media/stagefright/MediaClock.h
+++ b/media/libstagefright/include/media/stagefright/MediaClock.h
@@ -18,7 +18,7 @@
#define MEDIA_CLOCK_H_
-#include <map>
+#include <list>
#include <media/stagefright/foundation/AHandler.h>
#include <utils/Mutex.h>
#include <utils/RefBase.h>
@@ -30,8 +30,7 @@
struct MediaClock : public AHandler {
enum {
TIMER_REASON_REACHED = 0,
- TIMER_REASON_NO_CLOCK = 1,
- TIMER_REASON_RESET = 2,
+ TIMER_REASON_RESET = 1,
};
MediaClock();
@@ -62,7 +61,10 @@
// The result is saved in |outRealUs|.
status_t getRealTimeFor(int64_t targetMediaUs, int64_t *outRealUs) const;
- void addTimer(const sp<AMessage> ¬ify, int64_t mediaTimeUs);
+ // request to set up a timer. The target time is |mediaTimeUs|, adjusted by
+ // system time of |adjustRealUs|. In other words, the wake up time is
+ // mediaTimeUs + (adjustRealUs / playbackRate)
+ void addTimer(const sp<AMessage> ¬ify, int64_t mediaTimeUs, int64_t adjustRealUs = 0);
void reset();
@@ -76,6 +78,13 @@
kWhatTimeIsUp = 'tIsU',
};
+ struct Timer {
+ Timer(const sp<AMessage> ¬ify, int64_t mediaTimeUs, int64_t adjustRealUs);
+ const sp<AMessage> mNotify;
+ int64_t mMediaTimeUs;
+ int64_t mAdjustRealUs;
+ };
+
status_t getMediaTime_l(
int64_t realUs,
int64_t *outMediaUs,
@@ -94,7 +103,7 @@
float mPlaybackRate;
int32_t mGeneration;
- std::multimap<int64_t, sp<AMessage> > mTimers;
+ std::list<Timer> mTimers;
DISALLOW_EVIL_CONSTRUCTORS(MediaClock);
};