Loading include/media/AudioTimestamp.h +11 −0 Original line number Diff line number Diff line Loading @@ -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 Loading include/media/AudioTrack.h +47 −1 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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 Loading include/private/media/AudioTrackShared.h +4 −1 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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; }; Loading media/libmedia/AudioTrack.cpp +56 −0 Original line number Diff line number Diff line Loading @@ -500,6 +500,8 @@ status_t AudioTrack::set( mTimestampStartupGlitchReported = false; mRetrogradeMotionReported = false; mUnderrunCountOffset = 0; mFramesWritten = 0; mFramesWrittenServerOffset = 0; return NO_ERROR; } Loading Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -1923,6 +1936,7 @@ nsecs_t AudioTrack::processAudioBuffer() requested = &timeout; } size_t writtenFrames = 0; while (mRemainingFrames > 0) { Buffer audioBuffer; Loading Loading @@ -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 Loading @@ -2047,6 +2062,10 @@ nsecs_t AudioTrack::processAudioBuffer() #endif } if (writtenFrames > 0) { AutoMutex lock(mLock); mFramesWritten += writtenFrames; } mRemainingFrames = notificationFrames; mRetryOnPartialBuffer = true; Loading Loading @@ -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) { Loading Loading @@ -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); Loading media/libmedia/AudioTrackShared.cpp +2 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading
include/media/AudioTimestamp.h +11 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
include/media/AudioTrack.h +47 −1 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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 Loading
include/private/media/AudioTrackShared.h +4 −1 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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; }; Loading
media/libmedia/AudioTrack.cpp +56 −0 Original line number Diff line number Diff line Loading @@ -500,6 +500,8 @@ status_t AudioTrack::set( mTimestampStartupGlitchReported = false; mRetrogradeMotionReported = false; mUnderrunCountOffset = 0; mFramesWritten = 0; mFramesWrittenServerOffset = 0; return NO_ERROR; } Loading Loading @@ -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; } Loading Loading @@ -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; } Loading Loading @@ -1923,6 +1936,7 @@ nsecs_t AudioTrack::processAudioBuffer() requested = &timeout; } size_t writtenFrames = 0; while (mRemainingFrames > 0) { Buffer audioBuffer; Loading Loading @@ -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 Loading @@ -2047,6 +2062,10 @@ nsecs_t AudioTrack::processAudioBuffer() #endif } if (writtenFrames > 0) { AutoMutex lock(mLock); mFramesWritten += writtenFrames; } mRemainingFrames = notificationFrames; mRetryOnPartialBuffer = true; Loading Loading @@ -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) { Loading Loading @@ -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); Loading
media/libmedia/AudioTrackShared.cpp +2 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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