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

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

Implement client playback timestamps with 64 bit accuracy

Provide server timestamps if the HAL doesn't provide it.
Provide monotonic - boottime translation.

Bug: 17472992
Bug: 26682703
Bug: 27749434
Change-Id: I6c9b213d9f9284092e34d57f52870e02c72df62a
parent 1b9e52cd
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -69,12 +69,23 @@ struct ExtendedTimestamp {
    // or NTP adjustment.
    int64_t mTimebaseOffset[TIMEBASE_MAX];

    // Playback only:
    // mFlushed is number of flushed frames before entering the server mix;
    // hence not included in mPosition. This is used for adjusting server positions
    // information for frames "dropped".
    // FIXME: This variable should be eliminated, with the offset added on the server side
    // before sending to client, but differences in legacy position offset handling
    // and new extended timestamps require this to be maintained as a separate quantity.
    int64_t mFlushed;

    // Call to reset the timestamp to the original (invalid) state
    void clear() {
        memset(mPosition, 0, sizeof(mPosition)); // actually not necessary if time is -1
        for (int i = 0; i < LOCATION_MAX; ++i) {
            mTimeNs[i] = -1;
        }
        memset(mTimebaseOffset, 0, sizeof(mTimebaseOffset));
        mFlushed = 0;
    }

    // Returns the best timestamp as judged from the closest-to-hw stage in the
+47 −1
Original line number Diff line number Diff line
@@ -727,12 +727,51 @@ public:
     *                     because the audio device changed or AudioFlinger died.
     *                     This typically occurs for direct or offload tracks
     *                     or if mDoNotReconnect is true.
     *         INVALID_OPERATION  if called on a FastTrack, wrong state, or some other error.
     *         INVALID_OPERATION  wrong state, or some other error.
     *
     * The timestamp parameter is undefined on return, if status is not NO_ERROR.
     */
            status_t    getTimestamp(AudioTimestamp& timestamp);

    /* Return the extended timestamp, with additional timebase info and improved drain behavior.
     *
     * This is similar to the AudioTrack.java API:
     * getTimestamp(@NonNull AudioTimestamp timestamp, @AudioTimestamp.Timebase int timebase)
     *
     * Some differences between this method and the getTimestamp(AudioTimestamp& timestamp) method
     *
     *   1. stop() by itself does not reset the frame position.
     *      A following start() resets the frame position to 0.
     *   2. flush() by itself does not reset the frame position.
     *      The frame position advances by the number of frames flushed,
     *      when the first frame after flush reaches the audio sink.
     *   3. BOOTTIME clock offsets are provided to help synchronize with
     *      non-audio streams, e.g. sensor data.
     *   4. Position is returned with 64 bits of resolution.
     *
     * Parameters:
     *  timestamp: A pointer to the caller allocated ExtendedTimestamp.
     *
     * Returns NO_ERROR    on success; timestamp is filled with valid data.
     *         BAD_VALUE   if timestamp is NULL.
     *         WOULD_BLOCK if called immediately after start() when the number
     *                     of frames consumed is less than the
     *                     overall hardware latency to physical output. In WOULD_BLOCK cases,
     *                     one might poll again, or use getPosition(), or use 0 position and
     *                     current time for the timestamp.
     *                     If WOULD_BLOCK is returned, the timestamp is still
     *                     modified with the LOCATION_CLIENT portion filled.
     *         DEAD_OBJECT if AudioFlinger dies or the output device changes and
     *                     the track cannot be automatically restored.
     *                     The application needs to recreate the AudioTrack
     *                     because the audio device changed or AudioFlinger died.
     *                     This typically occurs for direct or offloaded tracks
     *                     or if mDoNotReconnect is true.
     *         INVALID_OPERATION  if called on a offloaded or direct track.
     *                     Use getTimestamp(AudioTimestamp& timestamp) instead.
     */
            status_t getTimestamp(ExtendedTimestamp *timestamp);

    /* Add an AudioDeviceCallback. The caller will be notified when the audio device to which this
     * AudioTrack is routed is updated.
     * Replaces any previously installed callback.
@@ -956,6 +995,13 @@ protected:

    uint32_t                mUnderrunCountOffset;   // updated when restoring tracks

    int64_t                 mFramesWritten;         // total frames written. reset to zero after
                                                    // the start() following stop(). It is not
                                                    // changed after restoring the track or
                                                    // after flush.
    int64_t                 mFramesWrittenServerOffset; // An offset to server frames due to
                                                    // restoring AudioTrack, or stop/start.

    audio_output_flags_t    mFlags;                 // same as mOrigFlags, except for bits that may
                                                    // be denied by client or server, such as
                                                    // AUDIO_OUTPUT_FLAG_FAST.  mLock must be
+4 −1
Original line number Diff line number Diff line
@@ -522,6 +522,9 @@ public:
        mTimestampMutator.push(timestamp);
    }

    // Total count of the number of flushed frames since creation (never reset).
    virtual int64_t     framesFlushed() const { return mFlushed; }

    // Get dynamic buffer size from the shared control block.
    uint32_t            getBufferSizeInFrames() const {
        return android_atomic_acquire_load((int32_t *)&mCblk->mBufferSizeInFrames);
@@ -531,7 +534,7 @@ protected:
    size_t      mAvailToClient; // estimated frames available to client prior to releaseBuffer()
    int32_t     mFlush;         // our copy of cblk->u.mStreaming.mFlush, for streaming output only
    int64_t     mReleased;      // our copy of cblk->mServer, at 64 bit resolution

    int64_t     mFlushed;       // flushed frames to account for client-server discrepancy
    ExtendedTimestampQueue::Mutator mTimestampMutator;
};

+56 −0
Original line number Diff line number Diff line
@@ -500,6 +500,8 @@ status_t AudioTrack::set(
    mTimestampStartupGlitchReported = false;
    mRetrogradeMotionReported = false;
    mUnderrunCountOffset = 0;
    mFramesWritten = 0;
    mFramesWrittenServerOffset = 0;

    return NO_ERROR;
}
@@ -537,6 +539,14 @@ status_t AudioTrack::start()
        // Note: the if is technically unnecessary because previousState == STATE_FLUSHED
        // is only for streaming tracks, and mMarkerReached is already set to false.
        if (previousState == STATE_STOPPED) {
            // read last server side position change via timestamp
            ExtendedTimestamp ets;
            if (mProxy->getTimestamp(&ets) == OK &&
                    ets.mTimeNs[ExtendedTimestamp::LOCATION_SERVER] > 0) {
                mFramesWrittenServerOffset = -(ets.mPosition[ExtendedTimestamp::LOCATION_SERVER]
                                                             + ets.mFlushed);
            }
            mFramesWritten = 0;
            mProxy->clearTimestamp(); // need new server push for valid timestamp
            mMarkerReached = false;
        }
@@ -1657,6 +1667,9 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking)
        releaseBuffer(&audioBuffer);
    }

    if (written > 0) {
        mFramesWritten += written / mFrameSize;
    }
    return written;
}

@@ -1923,6 +1936,7 @@ nsecs_t AudioTrack::processAudioBuffer()
        requested = &timeout;
    }

    size_t writtenFrames = 0;
    while (mRemainingFrames > 0) {

        Buffer audioBuffer;
@@ -2024,6 +2038,7 @@ nsecs_t AudioTrack::processAudioBuffer()
        }

        releaseBuffer(&audioBuffer);
        writtenFrames += releasedFrames;

        // FIXME here is where we would repeat EVENT_MORE_DATA again on same advanced buffer
        // if callback doesn't like to accept the full chunk
@@ -2047,6 +2062,10 @@ nsecs_t AudioTrack::processAudioBuffer()
#endif

    }
    if (writtenFrames > 0) {
        AutoMutex lock(mLock);
        mFramesWritten += writtenFrames;
    }
    mRemainingFrames = notificationFrames;
    mRetryOnPartialBuffer = true;

@@ -2109,6 +2128,7 @@ status_t AudioTrack::restoreTrack_l(const char *from)
        }
        if (mState == STATE_ACTIVE) {
            result = mAudioTrack->start();
            mFramesWrittenServerOffset = mFramesWritten; // server resets to zero so we offset
        }
    }
    if (result != NO_ERROR) {
@@ -2162,6 +2182,42 @@ status_t AudioTrack::setParameters(const String8& keyValuePairs)
    return mAudioTrack->setParameters(keyValuePairs);
}

status_t AudioTrack::getTimestamp(ExtendedTimestamp *timestamp)
{
    if (timestamp == nullptr) {
        return BAD_VALUE;
    }
    AutoMutex lock(mLock);
    if (mCblk->mFlags & CBLK_INVALID) {
        const status_t status = restoreTrack_l("getTimestampExtended");
        if (status != OK) {
            // per getTimestamp() API doc in header, we return DEAD_OBJECT here,
            // recommending that the track be recreated.
            return DEAD_OBJECT;
        }
    }
    // check for offloaded/direct here in case restoring somehow changed those flags.
    if (isOffloadedOrDirect_l()) {
        return INVALID_OPERATION; // not supported
    }
    status_t status = mProxy->getTimestamp(timestamp);
    bool found = false;
    if (status == OK) {
        timestamp->mPosition[ExtendedTimestamp::LOCATION_CLIENT] = mFramesWritten;
        timestamp->mTimeNs[ExtendedTimestamp::LOCATION_CLIENT] = 0;
        // server side frame offset in case AudioTrack has been restored.
        for (int i = ExtendedTimestamp::LOCATION_SERVER;
                i < ExtendedTimestamp::LOCATION_MAX; ++i) {
            if (timestamp->mTimeNs[i] >= 0) {
                // apply server offset and the "flush frame correction here"
                timestamp->mPosition[i] += mFramesWrittenServerOffset + timestamp->mFlushed;
                found = true;
            }
        }
    }
    return found ? OK : WOULD_BLOCK;
}

status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp)
{
    AutoMutex lock(mLock);
+2 −1
Original line number Diff line number Diff line
@@ -615,7 +615,7 @@ void StaticAudioTrackClientProxy::getBufferPositionAndLoopCount(
ServerProxy::ServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
        size_t frameSize, bool isOut, bool clientInServer)
    : Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer),
      mAvailToClient(0), mFlush(0), mReleased(0)
      mAvailToClient(0), mFlush(0), mReleased(0), mFlushed(0)
    , mTimestampMutator(&cblk->mExtendedTimestampQueue)
{
    cblk->mBufferSizeInFrames = frameCount;
@@ -671,6 +671,7 @@ status_t ServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush)
                            mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1);
                }
            }
            mFlushed += (newFront - front) & mask;
            front = newFront;
        }
    } else {
Loading