Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 17648f36 authored by Wei Jia's avatar Wei Jia
Browse files

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
parent 75e61dba
Loading
Loading
Loading
Loading
+35 −68
Original line number Diff line number Diff line
@@ -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));

@@ -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) {
@@ -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;
}
@@ -1357,6 +1323,7 @@ void NuPlayer::Renderer::onDrainVideoQueue() {

        realTimeUs = getRealTimeUs(mediaTimeUs, nowUs);
    }
    realTimeUs = mVideoScheduler->schedule(realTimeUs * 1000) / 1000;

    bool tooLate = false;

+63 −14
Original line number Diff line number Diff line
@@ -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 @@ namespace android {
// If larger than this threshold, it's treated as discontinuity.
static const int64_t kAnchorFluctuationAllowedUs = 10000ll;

MediaClock::Timer::Timer(const sp<AMessage> &notify, int64_t mediaTimeUs, int64_t adjustRealUs)
    : mNotify(notify),
      mMediaTimeUs(mediaTimeUs),
      mAdjustRealUs(adjustRealUs) {
}

MediaClock::MediaClock()
    : mAnchorTimeMediaUs(-1),
      mAnchorTimeRealUs(-1),
@@ -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;
@@ -204,15 +211,26 @@ status_t MediaClock::getRealTimeFor(
    return OK;
}

void MediaClock::addTimer(const sp<AMessage> &notify, int64_t mediaTimeUs) {
void MediaClock::addTimer(const sp<AMessage> &notify, 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 @@ 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
+14 −5
Original line number Diff line number Diff line
@@ -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 AMessage;
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 @@ 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> &notify, 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> &notify, int64_t mediaTimeUs, int64_t adjustRealUs = 0);

    void reset();

@@ -76,6 +78,13 @@ private:
        kWhatTimeIsUp = 'tIsU',
    };

    struct Timer {
        Timer(const sp<AMessage> &notify, 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 @@ private:
    float mPlaybackRate;

    int32_t mGeneration;
    std::multimap<int64_t, sp<AMessage> > mTimers;
    std::list<Timer> mTimers;

    DISALLOW_EVIL_CONSTRUCTORS(MediaClock);
};