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

Commit 21a1d72b authored by Andy Hung's avatar Andy Hung Committed by Android (Google) Code Review
Browse files

Merge "NuPlayer: update timestamp handling" into lmp-dev

parents 83266677 09e0c364
Loading
Loading
Loading
Loading
+30 −2
Original line number Diff line number Diff line
@@ -271,7 +271,14 @@ status_t NuPlayerDriver::start() {
                mPositionUs = -1;
            } else {
                mPlayer->resume();
                mPositionUs -= ALooper::GetNowUs() - mPauseStartedTimeUs;
                if (mNotifyTimeRealUs != -1) {
                    // Pause time must be set if here by setPauseStartedTimeIfNeeded().
                    //CHECK(mPauseStartedTimeUs != -1);

                    // if no seek occurs, adjust our notify time so that getCurrentPosition()
                    // is continuous if read immediately after calling start().
                    mNotifyTimeRealUs += ALooper::GetNowUs() - mPauseStartedTimeUs;
                }
            }
            break;
        }
@@ -379,15 +386,36 @@ status_t NuPlayerDriver::getCurrentPosition(int *msec) {
    Mutex::Autolock autoLock(mLock);

    if (mPositionUs < 0) {
        // mPositionUs is the media time.
        // It is negative under these cases
        // (1) == -1 after reset, or very first playback, no stream notification yet.
        // (2) == -1 start after end of stream, no stream notification yet.
        // (3) == large negative # after ~292,471 years of continuous playback.

        //CHECK_EQ(mPositionUs, -1);
        *msec = 0;
    } else if (mNotifyTimeRealUs == -1) {
        // A seek has occurred just occurred, no stream notification yet.
        // mPositionUs (>= 0) is the new media position.
        *msec = mPositionUs / 1000;
    } else {
        // mPosition must be valid (i.e. >= 0) by the first check above.
        // We're either playing or have pause time set: mPauseStartedTimeUs is >= 0
        //LOG_ALWAYS_FATAL_IF(
        //        !isPlaying() && mPauseStartedTimeUs < 0,
        //        "Player in non-playing mState(%d) and mPauseStartedTimeUs(%lld) < 0",
        //        mState, (long long)mPauseStartedTimeUs);
        ALOG_ASSERT(mNotifyTimeRealUs >= 0);
        int64_t nowUs =
                (isPlaying() ?  ALooper::GetNowUs() : mPauseStartedTimeUs);
        *msec = (mPositionUs + nowUs - mNotifyTimeRealUs + 500ll) / 1000;
        // It is possible for *msec to be negative if the media position is > 596 hours.
        // but we turn on this checking in NDEBUG == 0 mode.
        ALOG_ASSERT(*msec >= 0);
        ALOGV("getCurrentPosition nowUs(%lld)", (long long)nowUs);
    }

    ALOGV("getCurrentPosition returning(%d) mPositionUs(%lld) mNotifyRealTimeUs(%lld)",
            *msec, (long long)mPositionUs, (long long)mNotifyTimeRealUs);
    return OK;
}

+86 −30
Original line number Diff line number Diff line
@@ -45,7 +45,7 @@ NuPlayer::Renderer::Renderer(
      mDrainVideoQueuePending(false),
      mAudioQueueGeneration(0),
      mVideoQueueGeneration(0),
      mFirstAudioTimeUs(-1),
      mFirstAnchorTimeMediaUs(-1),
      mAnchorTimeMediaUs(-1),
      mAnchorTimeRealUs(-1),
      mFlushingAudio(false),
@@ -54,12 +54,12 @@ NuPlayer::Renderer::Renderer(
      mHasVideo(false),
      mSyncQueues(false),
      mPaused(false),
      mVideoSampleReceived(false),
      mVideoRenderingStarted(false),
      mVideoRenderingStartGeneration(0),
      mAudioRenderingStartGeneration(0),
      mLastPositionUpdateUs(-1ll),
      mVideoLateByUs(0ll),
      mVideoSampleReceived(false) {
      mVideoLateByUs(0ll) {
}

NuPlayer::Renderer::~Renderer() {
@@ -115,6 +115,7 @@ void NuPlayer::Renderer::signalTimeDiscontinuity() {
    Mutex::Autolock autoLock(mLock);
    // CHECK(mAudioQueue.empty());
    // CHECK(mVideoQueue.empty());
    mFirstAnchorTimeMediaUs = -1;
    mAnchorTimeMediaUs = -1;
    mAnchorTimeRealUs = -1;
    mSyncQueues = false;
@@ -339,17 +340,16 @@ size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) {
            int64_t mediaTimeUs;
            CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
            ALOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6);
            if (mFirstAudioTimeUs == -1) {
                mFirstAudioTimeUs = mediaTimeUs;
            if (mFirstAnchorTimeMediaUs == -1) {
                mFirstAnchorTimeMediaUs = mediaTimeUs;
            }

            // TODO: figure out how to calculate initial latency if
            // getTimestamp is not available. Otherwise, the initial time
            // is not correct till the first sample is played.
            int64_t nowUs = ALooper::GetNowUs();
            mAnchorTimeMediaUs =
                mFirstAudioTimeUs + getPlayedOutAudioDurationUs(nowUs);
                mFirstAnchorTimeMediaUs + getPlayedOutAudioDurationUs(nowUs);
            mAnchorTimeRealUs = nowUs;

            notifyPosition();
        }

        size_t copy = entry->mBuffer->size() - entry->mOffset;
@@ -372,10 +372,6 @@ size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) {
        notifyIfMediaRenderingStarted();
    }

    if (sizeCopied != 0) {
        notifyPosition();
    }

    if (hasEOS) {
        (new AMessage(kWhatStopAudioSink, id()))->post();
    }
@@ -424,10 +420,15 @@ bool NuPlayer::Renderer::onDrainAudioQueue() {
            int64_t mediaTimeUs;
            CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
            ALOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6);
            if (mFirstAnchorTimeMediaUs == -1) {
                mFirstAnchorTimeMediaUs = mediaTimeUs;
            }
            mAnchorTimeMediaUs = mediaTimeUs;

            int64_t nowUs = ALooper::GetNowUs();
            mAnchorTimeRealUs = nowUs + getPendingAudioPlayoutDurationUs(nowUs);

            notifyPosition();
        }

        size_t copy = entry->mBuffer->size() - entry->mOffset;
@@ -476,8 +477,6 @@ bool NuPlayer::Renderer::onDrainAudioQueue() {
            break;
        }
    }
    notifyPosition();

    return !mAudioQueue.empty();
}

@@ -517,12 +516,16 @@ void NuPlayer::Renderer::postDrainVideoQueue() {
        int64_t mediaTimeUs;
        CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));

        if (mFirstAnchorTimeMediaUs == -1 && !mHasAudio) {
            mFirstAnchorTimeMediaUs = mediaTimeUs;
        }
        if (mAnchorTimeMediaUs < 0) {
            delayUs = 0;

            if (!mHasAudio) {
                mAnchorTimeMediaUs = mediaTimeUs;
                mAnchorTimeRealUs = ALooper::GetNowUs();
                notifyPosition();
            }
        } else {
            int64_t realTimeUs =
@@ -554,8 +557,6 @@ void NuPlayer::Renderer::onDrainVideoQueue() {
        entry = NULL;

        mVideoLateByUs = 0ll;

        notifyPosition();
        return;
    }

@@ -601,8 +602,6 @@ void NuPlayer::Renderer::onDrainVideoQueue() {
        }
        notifyIfMediaRenderingStarted();
    }

    notifyPosition();
}

void NuPlayer::Renderer::notifyVideoRenderingStart() {
@@ -779,7 +778,7 @@ void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) {
            prepareForMediaRenderingStart();

            if (offloadingAudio()) {
                mFirstAudioTimeUs = -1;
                mFirstAnchorTimeMediaUs = -1;
            }
        }

@@ -867,9 +866,11 @@ void NuPlayer::Renderer::onDisableOffloadAudio() {
}

void NuPlayer::Renderer::notifyPosition() {
    if (mAnchorTimeRealUs < 0 || mAnchorTimeMediaUs < 0) {
        return;
    }
    // notifyPosition() must be called only after setting mAnchorTimeRealUs
    // and mAnchorTimeMediaUs, and must not be paused as it extrapolates position.
    //CHECK_GE(mAnchorTimeRealUs, 0);
    //CHECK_GE(mAnchorTimeMediaUs, 0);
    //CHECK(!mPaused || !mHasAudio);  // video-only does display in paused mode.

    int64_t nowUs = ALooper::GetNowUs();

@@ -881,6 +882,18 @@ void NuPlayer::Renderer::notifyPosition() {

    int64_t positionUs = (nowUs - mAnchorTimeRealUs) + mAnchorTimeMediaUs;

    //ALOGD("notifyPosition: positionUs(%lld) nowUs(%lld) mAnchorTimeRealUs(%lld)"
    //        " mAnchorTimeMediaUs(%lld) mFirstAnchorTimeMediaUs(%lld)",
    //        (long long)positionUs, (long long)nowUs, (long long)mAnchorTimeRealUs,
    //        (long long)mAnchorTimeMediaUs, (long long)mFirstAnchorTimeMediaUs);

    // Due to adding the latency to mAnchorTimeRealUs in onDrainAudioQueue(),
    // positionUs may be less than the first media time.  This is avoided
    // here to prevent potential retrograde motion of the position bar
    // when starting up after a seek.
    if (positionUs < mFirstAnchorTimeMediaUs) {
        positionUs = mFirstAnchorTimeMediaUs;
    }
    sp<AMessage> notify = mNotify->dup();
    notify->setInt32("what", kWhatPosition);
    notify->setInt64("positionUs", positionUs);
@@ -933,33 +946,76 @@ void NuPlayer::Renderer::onResume() {
    }
}

// TODO: Remove unnecessary calls to getPlayedOutAudioDurationUs()
// as it acquires locks and may query the audio driver.
//
// Some calls are not needed since notifyPosition() doesn't always deliver a message.
// Some calls could conceivably retrieve extrapolated data instead of
// accessing getTimestamp() or getPosition() every time a data buffer with
// a media time is received.
//
int64_t NuPlayer::Renderer::getPlayedOutAudioDurationUs(int64_t nowUs) {
    // FIXME: getTimestamp sometimes returns negative frame count.
    // Since we do not handle the rollover at this point (which can
    // happen every 14 hours), simply treat the timestamp as signed.
    uint32_t numFramesPlayed;
    int64_t numFramesPlayedAt;
    AudioTimestamp ts;
    static const int64_t kStaleTimestamp100ms = 100000;

    status_t res = mAudioSink->getTimestamp(ts);
    if (res == OK) {
    if (res == OK) {                 // case 1: mixing audio tracks and offloaded tracks.
        numFramesPlayed = ts.mPosition;
        numFramesPlayedAt =
            ts.mTime.tv_sec * 1000000LL + ts.mTime.tv_nsec / 1000;
    } else {
        const int64_t timestampAge = nowUs - numFramesPlayedAt;
        if (timestampAge > kStaleTimestamp100ms) {
            // This is an audio FIXME.
            // getTimestamp returns a timestamp which may come from audio mixing threads.
            // After pausing, the MixerThread may go idle, thus the mTime estimate may
            // become stale. Assuming that the MixerThread runs 20ms, with FastMixer at 5ms,
            // the max latency should be about 25ms with an average around 12ms (to be verified).
            // For safety we use 100ms.
            ALOGW("getTimestamp: returned stale timestamp nowUs(%lld) numFramesPlayedAt(%lld)",
                    (long long)nowUs, (long long)numFramesPlayedAt);
            numFramesPlayedAt = nowUs - kStaleTimestamp100ms;
        }
        //ALOGD("getTimestamp: OK %d %lld", numFramesPlayed, (long long)numFramesPlayedAt);
    } else if (res == WOULD_BLOCK) { // case 2: transitory state on start of a new track
        numFramesPlayed = 0;
        numFramesPlayedAt = nowUs;
        //ALOGD("getTimestamp: WOULD_BLOCK %d %lld",
        //        numFramesPlayed, (long long)numFramesPlayedAt);
    } else {                         // case 3: transitory at new track or audio fast tracks.
        res = mAudioSink->getPosition(&numFramesPlayed);
        CHECK_EQ(res, (status_t)OK);
        numFramesPlayedAt = nowUs;
        numFramesPlayedAt += 1000LL * mAudioSink->latency() / 2; /* XXX */
        //ALOGD("getPosition: %d %lld", numFramesPlayed, numFramesPlayedAt);
    }
    return (int32_t)numFramesPlayed * 1000LL * mAudioSink->msecsPerFrame()

    // TODO: remove the (int32_t) casting below as it may overflow at 12.4 hours.
    //CHECK_EQ(numFramesPlayed & (1 << 31), 0);  // can't be negative until 12.4 hrs, test
    int64_t durationUs = (int32_t)numFramesPlayed * 1000LL * mAudioSink->msecsPerFrame()
            + nowUs - numFramesPlayedAt;
    if (durationUs < 0) {
        // Occurs when numFramesPlayed position is very small and the following:
        // (1) In case 1, the time nowUs is computed before getTimestamp() is called and
        //     numFramesPlayedAt is greater than nowUs by time more than numFramesPlayed.
        // (2) In case 3, using getPosition and adding mAudioSink->latency() to
        //     numFramesPlayedAt, by a time amount greater than numFramesPlayed.
        //
        // Both of these are transitory conditions.
        ALOGW("getPlayedOutAudioDurationUs: negative timestamp %lld set to zero", (long long)durationUs);
        durationUs = 0;
    }
    ALOGV("getPlayedOutAudioDurationUs(%lld) nowUs(%lld) frames(%u) framesAt(%lld)",
            (long long)durationUs, (long long)nowUs, numFramesPlayed, (long long)numFramesPlayedAt);
    return durationUs;
}

void NuPlayer::Renderer::onAudioOffloadTearDown() {
    int64_t firstAudioTimeUs;
    {
        Mutex::Autolock autoLock(mLock);
        firstAudioTimeUs = mFirstAudioTimeUs;
        firstAudioTimeUs = mFirstAnchorTimeMediaUs;
    }

    int64_t currentPositionUs =
+1 −1
Original line number Diff line number Diff line
@@ -106,7 +106,7 @@ private:
    int32_t mAudioQueueGeneration;
    int32_t mVideoQueueGeneration;

    int64_t mFirstAudioTimeUs;
    int64_t mFirstAnchorTimeMediaUs;
    int64_t mAnchorTimeMediaUs;
    int64_t mAnchorTimeRealUs;