Loading media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +35 −68 Original line number Diff line number Diff line Loading @@ -1248,15 +1248,25 @@ void NuPlayer::Renderer::postDrainVideoQueue() { 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 realTimeUs; CHECK(entry.mBuffer->meta()->findInt64("timeUs", &realTimeUs)); realTimeUs = mVideoScheduler->schedule(realTimeUs * 1000) / 1000; int64_t twoVsyncsUs = 2 * (mVideoScheduler->getVsyncPeriod() / 1000); 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; } int64_t mediaTimeUs; CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); Loading @@ -1265,18 +1275,6 @@ void NuPlayer::Renderer::postDrainVideoQueue() { 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) { Loading @@ -1284,46 +1282,14 @@ void NuPlayer::Renderer::postDrainVideoQueue() { mMediaClock->updateMaxTimeMedia(mediaTimeUs + 100000); } // 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; } 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; } } realTimeUs = mVideoScheduler->schedule(realTimeUs * 1000) / 1000; if (!mVideoSampleReceived || mediaTimeUs < mAudioFirstAnchorTimeMediaUs) { msg->post(); } else { int64_t twoVsyncsUs = 2 * (mVideoScheduler->getVsyncPeriod() / 1000); delayUs = realTimeUs - nowUs; ALOGW_IF(delayUs > 500000, "unusually high delayUs: %" PRId64, delayUs); // post 2 display refreshes before rendering is due msg->post(delayUs > twoVsyncsUs ? delayUs - twoVsyncsUs : 0); mMediaClock->addTimer(msg, mediaTimeUs, -twoVsyncsUs); } mDrainVideoQueuePending = true; } Loading Loading @@ -1357,6 +1323,7 @@ void NuPlayer::Renderer::onDrainVideoQueue() { realTimeUs = getRealTimeUs(mediaTimeUs, nowUs); } realTimeUs = mVideoScheduler->schedule(realTimeUs * 1000) / 1000; bool tooLate = false; Loading media/libstagefright/MediaClock.cpp +63 −14 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "MediaClock" #include <utils/Log.h> #include <map> #include <media/stagefright/MediaClock.h> Loading @@ -29,6 +30,12 @@ namespace android { // 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), Loading Loading @@ -59,8 +66,8 @@ void MediaClock::reset() { 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; Loading Loading @@ -204,15 +211,26 @@ status_t MediaClock::getRealTimeFor( 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(); } Loading Loading @@ -248,20 +266,51 @@ void MediaClock::processTimers_l() { 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(); 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; } } 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 (it == mTimers.end() || mPlaybackRate == 0.0 || mAnchorTimeMediaUs < 0) { 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 media/libstagefright/include/media/stagefright/MediaClock.h +14 −5 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -30,8 +30,7 @@ struct AMessage; struct MediaClock : public AHandler { enum { TIMER_REASON_REACHED = 0, TIMER_REASON_NO_CLOCK = 1, TIMER_REASON_RESET = 2, TIMER_REASON_RESET = 1, }; MediaClock(); Loading Loading @@ -62,7 +61,10 @@ struct MediaClock : public AHandler { // 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(); Loading @@ -76,6 +78,13 @@ private: 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, Loading @@ -94,7 +103,7 @@ private: float mPlaybackRate; int32_t mGeneration; std::multimap<int64_t, sp<AMessage> > mTimers; std::list<Timer> mTimers; DISALLOW_EVIL_CONSTRUCTORS(MediaClock); }; Loading Loading
media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +35 −68 Original line number Diff line number Diff line Loading @@ -1248,15 +1248,25 @@ void NuPlayer::Renderer::postDrainVideoQueue() { 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 realTimeUs; CHECK(entry.mBuffer->meta()->findInt64("timeUs", &realTimeUs)); realTimeUs = mVideoScheduler->schedule(realTimeUs * 1000) / 1000; int64_t twoVsyncsUs = 2 * (mVideoScheduler->getVsyncPeriod() / 1000); 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; } int64_t mediaTimeUs; CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs)); Loading @@ -1265,18 +1275,6 @@ void NuPlayer::Renderer::postDrainVideoQueue() { 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) { Loading @@ -1284,46 +1282,14 @@ void NuPlayer::Renderer::postDrainVideoQueue() { mMediaClock->updateMaxTimeMedia(mediaTimeUs + 100000); } // 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; } 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; } } realTimeUs = mVideoScheduler->schedule(realTimeUs * 1000) / 1000; if (!mVideoSampleReceived || mediaTimeUs < mAudioFirstAnchorTimeMediaUs) { msg->post(); } else { int64_t twoVsyncsUs = 2 * (mVideoScheduler->getVsyncPeriod() / 1000); delayUs = realTimeUs - nowUs; ALOGW_IF(delayUs > 500000, "unusually high delayUs: %" PRId64, delayUs); // post 2 display refreshes before rendering is due msg->post(delayUs > twoVsyncsUs ? delayUs - twoVsyncsUs : 0); mMediaClock->addTimer(msg, mediaTimeUs, -twoVsyncsUs); } mDrainVideoQueuePending = true; } Loading Loading @@ -1357,6 +1323,7 @@ void NuPlayer::Renderer::onDrainVideoQueue() { realTimeUs = getRealTimeUs(mediaTimeUs, nowUs); } realTimeUs = mVideoScheduler->schedule(realTimeUs * 1000) / 1000; bool tooLate = false; Loading
media/libstagefright/MediaClock.cpp +63 −14 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "MediaClock" #include <utils/Log.h> #include <map> #include <media/stagefright/MediaClock.h> Loading @@ -29,6 +30,12 @@ namespace android { // 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), Loading Loading @@ -59,8 +66,8 @@ void MediaClock::reset() { 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; Loading Loading @@ -204,15 +211,26 @@ status_t MediaClock::getRealTimeFor( 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(); } Loading Loading @@ -248,20 +266,51 @@ void MediaClock::processTimers_l() { 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(); 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; } } 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 (it == mTimers.end() || mPlaybackRate == 0.0 || mAnchorTimeMediaUs < 0) { 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
media/libstagefright/include/media/stagefright/MediaClock.h +14 −5 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -30,8 +30,7 @@ struct AMessage; struct MediaClock : public AHandler { enum { TIMER_REASON_REACHED = 0, TIMER_REASON_NO_CLOCK = 1, TIMER_REASON_RESET = 2, TIMER_REASON_RESET = 1, }; MediaClock(); Loading Loading @@ -62,7 +61,10 @@ struct MediaClock : public AHandler { // 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(); Loading @@ -76,6 +78,13 @@ private: 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, Loading @@ -94,7 +103,7 @@ private: float mPlaybackRate; int32_t mGeneration; std::multimap<int64_t, sp<AMessage> > mTimers; std::list<Timer> mTimers; DISALLOW_EVIL_CONSTRUCTORS(MediaClock); }; Loading