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

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

Merge "AudioTrack: Fix timestamp jitter" into nyc-dev

parents 9b04a022 b01faa31
Loading
Loading
Loading
Loading
+10 −4
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ public:

struct ExtendedTimestamp {
    enum Location {
        LOCATION_INVALID = -1,
        LOCATION_CLIENT,   // timestamp of last read frame from client-server track buffer
        LOCATION_SERVER,   // timestamp of newest frame from client-server track buffer
        LOCATION_KERNEL,   // timestamp of newest frame in the kernel (alsa) buffer.
@@ -89,8 +90,10 @@ struct ExtendedTimestamp {
    }

    // Returns the best timestamp as judged from the closest-to-hw stage in the
    // pipeline with a valid timestamp.
    status_t getBestTimestamp(int64_t *position, int64_t *time, int timebase) const {
    // pipeline with a valid timestamp.  If the optional location parameter is non-null,
    // it will be filled with the location where the time was obtained.
    status_t getBestTimestamp(
            int64_t *position, int64_t *time, int timebase, Location *location = nullptr) const {
        if (position == nullptr || time == nullptr
                || timebase < 0 || timebase >= TIMEBASE_MAX) {
            return BAD_VALUE;
@@ -102,18 +105,21 @@ struct ExtendedTimestamp {
            if (mTimeNs[i] > 0) {
                *position = mPosition[i];
                *time = mTimeNs[i] + mTimebaseOffset[timebase];
                if (location != nullptr) {
                    *location = (Location)i;
                }
                return OK;
            }
        }
        return INVALID_OPERATION;
    }

    status_t getBestTimestamp(AudioTimestamp *timestamp) const {
    status_t getBestTimestamp(AudioTimestamp *timestamp, Location *location = nullptr) const {
        if (timestamp == nullptr) {
            return BAD_VALUE;
        }
        int64_t position, time;
        if (getBestTimestamp(&position, &time, TIMEBASE_MONOTONIC) == OK) {
        if (getBestTimestamp(&position, &time, TIMEBASE_MONOTONIC, location) == OK) {
            timestamp->mPosition = position;
            timestamp->mTime.tv_sec = time / 1000000000;
            timestamp->mTime.tv_nsec = time - timestamp->mTime.tv_sec * 1000000000LL;
+2 −0
Original line number Diff line number Diff line
@@ -1046,6 +1046,8 @@ protected:
    bool                    mTimestampStartupGlitchReported; // reduce log spam
    bool                    mRetrogradeMotionReported; // reduce log spam
    AudioTimestamp          mPreviousTimestamp;     // used to detect retrograde motion
    ExtendedTimestamp::Location mPreviousLocation;  // location used for previous timestamp
    double                  mComputedLatencyMs;     // latency between server and kernel

    uint32_t                mUnderrunCountOffset;   // updated when restoring tracks

+38 −1
Original line number Diff line number Diff line
@@ -535,6 +535,8 @@ status_t AudioTrack::set(
    mPreviousTimestampValid = false;
    mTimestampStartupGlitchReported = false;
    mRetrogradeMotionReported = false;
    mPreviousLocation = ExtendedTimestamp::LOCATION_INVALID;
    mComputedLatencyMs = 0.;
    mUnderrunCountOffset = 0;
    mFramesWritten = 0;
    mFramesWrittenServerOffset = 0;
@@ -567,6 +569,8 @@ status_t AudioTrack::start()
        mPreviousTimestampValid = false;
        mTimestampStartupGlitchReported = false;
        mRetrogradeMotionReported = false;
        mPreviousLocation = ExtendedTimestamp::LOCATION_INVALID;
        mComputedLatencyMs = 0.;

        // read last server side position change via timestamp.
        ExtendedTimestamp ets;
@@ -2362,7 +2366,40 @@ status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp)
        ExtendedTimestamp ets;
        status = mProxy->getTimestamp(&ets);
        if (status == OK) {
            status = ets.getBestTimestamp(&timestamp);
            ExtendedTimestamp::Location location;
            status = ets.getBestTimestamp(&timestamp, &location);

            if (status == OK) {
                // It is possible that the best location has moved from the kernel to the server.
                // In this case we adjust the position from the previous computed latency.
                if (location == ExtendedTimestamp::LOCATION_SERVER) {
                    ALOGW_IF(mPreviousLocation == ExtendedTimestamp::LOCATION_KERNEL,
                            "getTimestamp() location moved from kernel to server");
                    const double latencyMs = mComputedLatencyMs > 0.
                            ? mComputedLatencyMs : mAfLatency;
                    const int64_t frames =
                            int64_t(latencyMs * mSampleRate * mPlaybackRate.mSpeed / 1000);
                    ALOGV("mComputedLatencyMs:%lf  mAfLatency:%u  frame adjustment:%lld",
                            mComputedLatencyMs, mAfLatency, (long long)frames);
                    if (frames >= ets.mPosition[location]) {
                        timestamp.mPosition = 0;
                    } else {
                        timestamp.mPosition = (uint32_t)(ets.mPosition[location] - frames);
                    }
                } else if (location == ExtendedTimestamp::LOCATION_KERNEL) {
                    const double bufferDiffMs =
                            (double)(ets.mPosition[ExtendedTimestamp::LOCATION_SERVER]
                                   - ets.mPosition[ExtendedTimestamp::LOCATION_KERNEL])
                                   * 1000 / ((double)mSampleRate * mPlaybackRate.mSpeed);
                    mComputedLatencyMs = bufferDiffMs > 0. ? bufferDiffMs : 0.;
                    ALOGV("mComputedLatencyMs:%lf  mAfLatency:%d",
                            mComputedLatencyMs, mAfLatency);
                }
                mPreviousLocation = location;
            } else {
                // right after AudioTrack is started, one may not find a timestamp
                ALOGV("getBestTimestamp did not find timestamp");
            }
        }
        if (status == INVALID_OPERATION) {
            status = WOULD_BLOCK;