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

Commit ffa3695a authored by Andy Hung's avatar Andy Hung
Browse files

AudioTrack: Limit timestamp time lag after start

Test: Photos with long pause and resume
Bug: 63614330
Bug: 63976584
Change-Id: I81ac650c821600fee507c72c3b0c1be60f5e7ab6
parent 90153cae
Loading
Loading
Loading
Loading
+40 −12
Original line number Diff line number Diff line
@@ -63,6 +63,14 @@ static int64_t convertTimespecToUs(const struct timespec &tv)
    return tv.tv_sec * 1000000ll + tv.tv_nsec / 1000;
}

// TODO move to audio_utils.
static inline struct timespec convertNsToTimespec(int64_t ns) {
    struct timespec tv;
    tv.tv_sec = static_cast<time_t>(ns / NANOS_PER_SECOND);
    tv.tv_nsec = static_cast<long>(ns % NANOS_PER_SECOND);
    return tv;
}

// current monotonic time in microseconds.
static int64_t getNowUs()
{
@@ -539,7 +547,8 @@ status_t AudioTrack::set(
    mUpdatePeriod = 0;
    mPosition = 0;
    mReleased = 0;
    mStartUs = 0;
    mStartNs = 0;
    mStartFromZeroUs = 0;
    AudioSystem::acquireAudioSessionId(mSessionId, mClientPid);
    mSequence = 1;
    mObservedSequence = mSequence;
@@ -587,6 +596,7 @@ status_t AudioTrack::start()
            mStartEts.clear();
        }
    }
    mStartNs = systemTime(); // save this for timestamp adjustment after starting.
    if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) {
        // reset current position as seen by client to 0
        mPosition = 0;
@@ -615,7 +625,7 @@ status_t AudioTrack::start()
        // since the flush is asynchronous and stop may not fully drain.
        // We save the time when the track is started to later verify whether
        // the counters are realistic (i.e. start from zero after this time).
        mStartUs = getNowUs();
        mStartFromZeroUs = mStartNs / 1000;

        // force refresh of remaining frames by processAudioBuffer() as last
        // write before stop could be partial.
@@ -2571,8 +2581,7 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp)
                    if (at < limit) {
                        ALOGV("timestamp pause lag:%lld adjusting from %lld to %lld",
                                (long long)lag, (long long)at, (long long)limit);
                        timestamp.mTime.tv_sec = limit / NANOS_PER_SECOND;
                        timestamp.mTime.tv_nsec = limit % NANOS_PER_SECOND; // compiler opt.
                        timestamp.mTime = convertNsToTimespec(limit);
                    }
                }
                mPreviousLocation = location;
@@ -2615,18 +2624,18 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp)
        // the previous song under gapless playback.
        // However, we sometimes see zero timestamps, then a glitch of
        // the previous song's position, and then correct timestamps afterwards.
        if (mStartUs != 0 && mSampleRate != 0) {
        if (mStartFromZeroUs != 0 && mSampleRate != 0) {
            static const int kTimeJitterUs = 100000; // 100 ms
            static const int k1SecUs = 1000000;

            const int64_t timeNow = getNowUs();

            if (timeNow < mStartUs + k1SecUs) { // within first second of starting
            if (timeNow < mStartFromZeroUs + k1SecUs) { // within first second of starting
                const int64_t timestampTimeUs = convertTimespecToUs(timestamp.mTime);
                if (timestampTimeUs < mStartUs) {
                if (timestampTimeUs < mStartFromZeroUs) {
                    return WOULD_BLOCK;  // stale timestamp time, occurs before start.
                }
                const int64_t deltaTimeUs = timestampTimeUs - mStartUs;
                const int64_t deltaTimeUs = timestampTimeUs - mStartFromZeroUs;
                const int64_t deltaPositionByUs = (double)timestamp.mPosition * 1000000
                        / ((double)mSampleRate * mPlaybackRate.mSpeed);

@@ -2649,10 +2658,10 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp)
                    return WOULD_BLOCK;
                }
                if (deltaPositionByUs != 0) {
                    mStartUs = 0; // don't check again, we got valid nonzero position.
                    mStartFromZeroUs = 0; // don't check again, we got valid nonzero position.
                }
            } else {
                mStartUs = 0; // don't check again, start time expired.
                mStartFromZeroUs = 0; // don't check again, start time expired.
            }
            mTimestampStartupGlitchReported = false;
        }
@@ -2690,14 +2699,33 @@ status_t AudioTrack::getTimestamp_l(AudioTimestamp& timestamp)
    // Prevent retrograde motion in timestamp.
    // This is sometimes caused by erratic reports of the available space in the ALSA drivers.
    if (status == NO_ERROR) {
        // previousTimestampValid is set to false when starting after a stop or flush.
        if (previousTimestampValid) {
            const int64_t previousTimeNanos =
                    audio_utils_ns_from_timespec(&mPreviousTimestamp.mTime);
            const int64_t currentTimeNanos = audio_utils_ns_from_timespec(&timestamp.mTime);
            int64_t currentTimeNanos = audio_utils_ns_from_timespec(&timestamp.mTime);

            // Fix stale time when checking timestamp right after start().
            //
            // For offload compatibility, use a default lag value here.
            // Any time discrepancy between this update and the pause timestamp is handled
            // by the retrograde check afterwards.
            const int64_t lagNs = int64_t(mAfLatency * 1000000LL);
            const int64_t limitNs = mStartNs - lagNs;
            if (currentTimeNanos < limitNs) {
                ALOGD("correcting timestamp time for pause, "
                        "currentTimeNanos: %lld < limitNs: %lld < mStartNs: %lld",
                        (long long)currentTimeNanos, (long long)limitNs, (long long)mStartNs);
                timestamp.mTime = convertNsToTimespec(limitNs);
                currentTimeNanos = limitNs;
            }

            // retrograde check
            if (currentTimeNanos < previousTimeNanos) {
                ALOGW("retrograde timestamp time corrected, %lld < %lld",
                        (long long)currentTimeNanos, (long long)previousTimeNanos);
                timestamp.mTime = mPreviousTimestamp.mTime;
                // currentTimeNanos not used below.
            }

            // Looking at signed delta will work even when the timestamps
@@ -2907,7 +2935,7 @@ bool AudioTrack::hasStarted()
    case STATE_STOPPED:
        if (isOffloadedOrDirect_l()) {
            // check if we have started in the past to return true.
            return mStartUs > 0;
            return mStartFromZeroUs > 0;
        }
        // A normal audio track may still be draining, so
        // check if stream has ended.  This covers fasttrack position
+3 −1
Original line number Diff line number Diff line
@@ -1085,8 +1085,10 @@ protected:
                                                    // reset by stop() but continues monotonically
                                                    // after new IAudioTrack to restore mPosition,
                                                    // and could be easily widened to uint64_t
    int64_t                 mStartUs;               // the start time after flush or stop.
    int64_t                 mStartFromZeroUs;       // the start time after flush or stop,
                                                    // when position should be 0.
                                                    // only used for offloaded and direct tracks.
    int64_t                 mStartNs;               // the time when start() is called.
    ExtendedTimestamp       mStartEts;              // Extended timestamp at start for normal
                                                    // AudioTracks.
    AudioTimestamp          mStartTs;               // Timestamp at start for offloaded or direct
+12 −1
Original line number Diff line number Diff line
@@ -1152,7 +1152,18 @@ int64_t NuPlayer::Renderer::getPendingAudioPlayoutDurationUs(int64_t nowUs) {
            return writtenAudioDurationUs - (mediaUs - mAudioFirstAnchorTimeMediaUs);
        }
    }
    return writtenAudioDurationUs - mAudioSink->getPlayedOutDurationUs(nowUs);

    const int64_t audioSinkPlayedUs = mAudioSink->getPlayedOutDurationUs(nowUs);
    int64_t pendingUs = writtenAudioDurationUs - audioSinkPlayedUs;
    if (pendingUs < 0) {
        // This shouldn't happen unless the timestamp is stale.
        ALOGW("%s: pendingUs %lld < 0, clamping to zero, potential resume after pause "
                "writtenAudioDurationUs: %lld, audioSinkPlayedUs: %lld",
                __func__, (long long)pendingUs,
                (long long)writtenAudioDurationUs, (long long)audioSinkPlayedUs);
        pendingUs = 0;
    }
    return pendingUs;
}

int64_t NuPlayer::Renderer::getRealTimeUs(int64_t mediaTimeUs, int64_t nowUs) {