Loading include/private/media/AudioTrackShared.h +83 −0 Original line number Diff line number Diff line Loading @@ -53,6 +53,83 @@ namespace android { //EL_FIXME 20 seconds may not be enough and must be reconciled with new obtainBuffer implementation #define MAX_RUN_OFFLOADED_TIMEOUT_MS 20000 // assuming up to a maximum of 20 seconds of offloaded // for audio_track_cblk_t::mState, to match TrackBase.h static inline constexpr int CBLK_STATE_IDLE = 0; static inline constexpr int CBLK_STATE_PAUSING = 7; /** * MirroredVariable is a local variable which simultaneously updates * a mirrored storage location. This is useful for server side variables * where a local copy is kept, but a client visible copy is offered through shared memory. * * We use std::atomic as the default container class to access this memory. */ template <typename T, template <typename> class Container = std::atomic> class MirroredVariable { template <typename C> struct Constraints { // If setMirror is used with a different type U != T passed in, // as a general rule, the Container must issue a memcpy to read or write // (or its equivalent) to avoid possible strict aliasing issues. // The memcpy also avoids gaps in structs and alignment issues with different types. static constexpr bool ok_ = false; // Containers must specify constraints. }; template <typename X> struct Constraints<std::atomic<X>> { // Atomics force read and write to memory. static constexpr bool ok = std::is_same_v<X, T> || (std::atomic<X>::is_always_lock_free // no additional locking && sizeof(std::atomic<X>) == sizeof(X) // layout identical to X. && (std::is_arithmetic_v<X> || std::is_enum_v<X>)); // No gaps in the layout. }; static_assert(Constraints<Container<T>>::ok); public: explicit MirroredVariable(const T& t) : t_{t} {} // implicit conversion operator operator T() const { return t_; } MirroredVariable& operator=(const T& t) { t_ = t; if (mirror_ != nullptr) { *mirror_ = t; } return *this; } template <typename U> void setMirror(Container<U> *other_mirror) { // Much of the concern is with T != U, however there are additional concerns // when storage uses shared memory between processes. For atomics, it must be // lock free. static_assert(sizeof(U) == sizeof(T)); static_assert(alignof(U) == alignof(T)); static_assert(Constraints<Container<U>>::ok); static_assert(sizeof(Container<U>) == sizeof(Container<T>)); static_assert(alignof(Container<U>) == alignof(Container<T>)); auto mirror = reinterpret_cast<Container<T>*>(other_mirror); if (mirror_ != mirror) { mirror_ = mirror; if (mirror != nullptr) { *mirror = t_; } } } void clear() { mirror_ = nullptr; } MirroredVariable& operator&() const = delete; protected: T t_{}; Container<T>* mirror_ = nullptr; }; struct AudioTrackSharedStreaming { // similar to NBAIO MonoPipe // in continuously incrementing frame units, take modulo buffer size, which must be a power of 2 Loading Loading @@ -188,6 +265,8 @@ public: volatile int32_t mFlags; // combinations of CBLK_* std::atomic<int32_t> mState; // current TrackBase state. public: union { AudioTrackSharedStreaming mStreaming; Loading @@ -198,6 +277,9 @@ public: // Cache line boundary (32 bytes) }; // TODO: ensure standard layout. // static_assert(std::is_standard_layout_v<audio_track_cblk_t>); // ---------------------------------------------------------------------------- // Proxy for shared memory control block, to isolate callers from needing to know the details. Loading Loading @@ -323,6 +405,7 @@ public: return mEpoch; } int32_t getState() const { return mCblk->mState; } uint32_t getBufferSizeInFrames() const { return mBufferSizeInFrames; } // See documentation for AudioTrack::setBufferSizeInFrames() uint32_t setBufferSizeInFrames(uint32_t requestedSize); Loading media/libaudioclient/AudioTrack.cpp +39 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <inttypes.h> #include <math.h> #include <sys/resource.h> #include <thread> #include <android/media/IAudioPolicyService.h> #include <android-base/macros.h> Loading Loading @@ -947,6 +948,44 @@ void AudioTrack::flush_l() mAudioTrack->flush(); } bool AudioTrack::pauseAndWait(const std::chrono::milliseconds& timeout) { using namespace std::chrono_literals; pause(); AutoMutex lock(mLock); // offload and direct tracks do not wait because pause volume ramp is handled by hardware. if (isOffloadedOrDirect_l()) return true; // Wait for the track state to be anything besides pausing. // This ensures that the volume has ramped down. constexpr auto SLEEP_INTERVAL_MS = 10ms; auto begin = std::chrono::steady_clock::now(); while (true) { // wait for state to change const int state = mProxy->getState(); mLock.unlock(); // only local variables accessed until lock. auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::steady_clock::now() - begin); if (state != CBLK_STATE_PAUSING) { ALOGV("%s: success state:%d after %lld ms", __func__, state, elapsed.count()); return true; } std::chrono::milliseconds remaining = timeout - elapsed; if (remaining.count() <= 0) { ALOGW("%s: timeout expired state:%d still pausing:%d after %lld ms", __func__, state, CBLK_STATE_PAUSING, elapsed.count()); return false; } // It is conceivable that the track is restored while sleeping; // as this logic is advisory, we allow that. std::this_thread::sleep_for(std::min(remaining, SLEEP_INTERVAL_MS)); mLock.lock(); } } void AudioTrack::pause() { const int64_t beginNs = systemTime(); Loading media/libaudioclient/include/media/AudioTrack.h +9 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ #include <utils/threads.h> #include <android/content/AttributionSourceState.h> #include <chrono> #include <string> #include "android/media/BnAudioTrackCallback.h" Loading Loading @@ -510,6 +511,14 @@ public: */ void pause(); /* Pause and wait (with timeout) for the audio track to ramp to silence. * * \param timeout is the time limit to wait before returning. * A negative number is treated as 0. * \return true if the track is ramped to silence, false if the timeout occurred. */ bool pauseAndWait(const std::chrono::milliseconds& timeout); /* Set volume for this track, mostly used for games' sound effects * left and right volumes. Levels must be >= 0.0 and <= 1.0. * This is the older API. New applications should use setVolume(float) when possible. Loading media/libmediaplayerservice/MediaPlayerService.cpp +7 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #define LOG_TAG "MediaPlayerService" #include <utils/Log.h> #include <chrono> #include <sys/types.h> #include <sys/stat.h> #include <sys/time.h> Loading Loading @@ -2467,8 +2468,13 @@ void MediaPlayerService::AudioOutput::flush() void MediaPlayerService::AudioOutput::pause() { ALOGV("pause"); // We use pauseAndWait() instead of pause() to ensure tracks ramp to silence before // any flush. We choose 40 ms timeout to allow 1 deep buffer mixer period // to occur. Often waiting is 0 - 20 ms. using namespace std::chrono_literals; constexpr auto TIMEOUT_MS = 40ms; Mutex::Autolock lock(mLock); if (mTrack != 0) mTrack->pause(); if (mTrack != 0) mTrack->pauseAndWait(TIMEOUT_MS); } void MediaPlayerService::AudioOutput::close() Loading services/audioflinger/Threads.cpp +4 −4 Original line number Diff line number Diff line Loading @@ -5089,7 +5089,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac break; case TrackBase::IDLE: default: LOG_ALWAYS_FATAL("unexpected track state %d", track->mState); LOG_ALWAYS_FATAL("unexpected track state %d", (int)track->mState); } if (isActive) { Loading Loading @@ -5148,7 +5148,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac // TODO Remove the ALOGW when this theory is confirmed. ALOGW("fast track %d should have been active; " "mState=%d, mTrackMask=%#x, recentUnderruns=%u, isShared=%d", j, track->mState, state->mTrackMask, recentUnderruns, j, (int)track->mState, state->mTrackMask, recentUnderruns, track->sharedBuffer() != 0); // Since the FastMixer state already has the track inactive, do nothing here. } Loading Loading @@ -8041,7 +8041,7 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac ALOGV("active record track PAUSING -> ACTIVE"); recordTrack->mState = TrackBase::ACTIVE; } else { ALOGV("active record track state %d", recordTrack->mState); ALOGV("active record track state %d", (int)recordTrack->mState); } return status; } Loading @@ -8067,7 +8067,7 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac } if (recordTrack->mState != TrackBase::STARTING_1) { ALOGW("%s(%d): unsynchronized mState:%d change", __func__, recordTrack->id(), recordTrack->mState); __func__, recordTrack->id(), (int)recordTrack->mState); // Someone else has changed state, let them take over, // leave mState in the new state. recordTrack->clearSyncStartEvent(); Loading Loading
include/private/media/AudioTrackShared.h +83 −0 Original line number Diff line number Diff line Loading @@ -53,6 +53,83 @@ namespace android { //EL_FIXME 20 seconds may not be enough and must be reconciled with new obtainBuffer implementation #define MAX_RUN_OFFLOADED_TIMEOUT_MS 20000 // assuming up to a maximum of 20 seconds of offloaded // for audio_track_cblk_t::mState, to match TrackBase.h static inline constexpr int CBLK_STATE_IDLE = 0; static inline constexpr int CBLK_STATE_PAUSING = 7; /** * MirroredVariable is a local variable which simultaneously updates * a mirrored storage location. This is useful for server side variables * where a local copy is kept, but a client visible copy is offered through shared memory. * * We use std::atomic as the default container class to access this memory. */ template <typename T, template <typename> class Container = std::atomic> class MirroredVariable { template <typename C> struct Constraints { // If setMirror is used with a different type U != T passed in, // as a general rule, the Container must issue a memcpy to read or write // (or its equivalent) to avoid possible strict aliasing issues. // The memcpy also avoids gaps in structs and alignment issues with different types. static constexpr bool ok_ = false; // Containers must specify constraints. }; template <typename X> struct Constraints<std::atomic<X>> { // Atomics force read and write to memory. static constexpr bool ok = std::is_same_v<X, T> || (std::atomic<X>::is_always_lock_free // no additional locking && sizeof(std::atomic<X>) == sizeof(X) // layout identical to X. && (std::is_arithmetic_v<X> || std::is_enum_v<X>)); // No gaps in the layout. }; static_assert(Constraints<Container<T>>::ok); public: explicit MirroredVariable(const T& t) : t_{t} {} // implicit conversion operator operator T() const { return t_; } MirroredVariable& operator=(const T& t) { t_ = t; if (mirror_ != nullptr) { *mirror_ = t; } return *this; } template <typename U> void setMirror(Container<U> *other_mirror) { // Much of the concern is with T != U, however there are additional concerns // when storage uses shared memory between processes. For atomics, it must be // lock free. static_assert(sizeof(U) == sizeof(T)); static_assert(alignof(U) == alignof(T)); static_assert(Constraints<Container<U>>::ok); static_assert(sizeof(Container<U>) == sizeof(Container<T>)); static_assert(alignof(Container<U>) == alignof(Container<T>)); auto mirror = reinterpret_cast<Container<T>*>(other_mirror); if (mirror_ != mirror) { mirror_ = mirror; if (mirror != nullptr) { *mirror = t_; } } } void clear() { mirror_ = nullptr; } MirroredVariable& operator&() const = delete; protected: T t_{}; Container<T>* mirror_ = nullptr; }; struct AudioTrackSharedStreaming { // similar to NBAIO MonoPipe // in continuously incrementing frame units, take modulo buffer size, which must be a power of 2 Loading Loading @@ -188,6 +265,8 @@ public: volatile int32_t mFlags; // combinations of CBLK_* std::atomic<int32_t> mState; // current TrackBase state. public: union { AudioTrackSharedStreaming mStreaming; Loading @@ -198,6 +277,9 @@ public: // Cache line boundary (32 bytes) }; // TODO: ensure standard layout. // static_assert(std::is_standard_layout_v<audio_track_cblk_t>); // ---------------------------------------------------------------------------- // Proxy for shared memory control block, to isolate callers from needing to know the details. Loading Loading @@ -323,6 +405,7 @@ public: return mEpoch; } int32_t getState() const { return mCblk->mState; } uint32_t getBufferSizeInFrames() const { return mBufferSizeInFrames; } // See documentation for AudioTrack::setBufferSizeInFrames() uint32_t setBufferSizeInFrames(uint32_t requestedSize); Loading
media/libaudioclient/AudioTrack.cpp +39 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <inttypes.h> #include <math.h> #include <sys/resource.h> #include <thread> #include <android/media/IAudioPolicyService.h> #include <android-base/macros.h> Loading Loading @@ -947,6 +948,44 @@ void AudioTrack::flush_l() mAudioTrack->flush(); } bool AudioTrack::pauseAndWait(const std::chrono::milliseconds& timeout) { using namespace std::chrono_literals; pause(); AutoMutex lock(mLock); // offload and direct tracks do not wait because pause volume ramp is handled by hardware. if (isOffloadedOrDirect_l()) return true; // Wait for the track state to be anything besides pausing. // This ensures that the volume has ramped down. constexpr auto SLEEP_INTERVAL_MS = 10ms; auto begin = std::chrono::steady_clock::now(); while (true) { // wait for state to change const int state = mProxy->getState(); mLock.unlock(); // only local variables accessed until lock. auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::steady_clock::now() - begin); if (state != CBLK_STATE_PAUSING) { ALOGV("%s: success state:%d after %lld ms", __func__, state, elapsed.count()); return true; } std::chrono::milliseconds remaining = timeout - elapsed; if (remaining.count() <= 0) { ALOGW("%s: timeout expired state:%d still pausing:%d after %lld ms", __func__, state, CBLK_STATE_PAUSING, elapsed.count()); return false; } // It is conceivable that the track is restored while sleeping; // as this logic is advisory, we allow that. std::this_thread::sleep_for(std::min(remaining, SLEEP_INTERVAL_MS)); mLock.lock(); } } void AudioTrack::pause() { const int64_t beginNs = systemTime(); Loading
media/libaudioclient/include/media/AudioTrack.h +9 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ #include <utils/threads.h> #include <android/content/AttributionSourceState.h> #include <chrono> #include <string> #include "android/media/BnAudioTrackCallback.h" Loading Loading @@ -510,6 +511,14 @@ public: */ void pause(); /* Pause and wait (with timeout) for the audio track to ramp to silence. * * \param timeout is the time limit to wait before returning. * A negative number is treated as 0. * \return true if the track is ramped to silence, false if the timeout occurred. */ bool pauseAndWait(const std::chrono::milliseconds& timeout); /* Set volume for this track, mostly used for games' sound effects * left and right volumes. Levels must be >= 0.0 and <= 1.0. * This is the older API. New applications should use setVolume(float) when possible. Loading
media/libmediaplayerservice/MediaPlayerService.cpp +7 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #define LOG_TAG "MediaPlayerService" #include <utils/Log.h> #include <chrono> #include <sys/types.h> #include <sys/stat.h> #include <sys/time.h> Loading Loading @@ -2467,8 +2468,13 @@ void MediaPlayerService::AudioOutput::flush() void MediaPlayerService::AudioOutput::pause() { ALOGV("pause"); // We use pauseAndWait() instead of pause() to ensure tracks ramp to silence before // any flush. We choose 40 ms timeout to allow 1 deep buffer mixer period // to occur. Often waiting is 0 - 20 ms. using namespace std::chrono_literals; constexpr auto TIMEOUT_MS = 40ms; Mutex::Autolock lock(mLock); if (mTrack != 0) mTrack->pause(); if (mTrack != 0) mTrack->pauseAndWait(TIMEOUT_MS); } void MediaPlayerService::AudioOutput::close() Loading
services/audioflinger/Threads.cpp +4 −4 Original line number Diff line number Diff line Loading @@ -5089,7 +5089,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac break; case TrackBase::IDLE: default: LOG_ALWAYS_FATAL("unexpected track state %d", track->mState); LOG_ALWAYS_FATAL("unexpected track state %d", (int)track->mState); } if (isActive) { Loading Loading @@ -5148,7 +5148,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac // TODO Remove the ALOGW when this theory is confirmed. ALOGW("fast track %d should have been active; " "mState=%d, mTrackMask=%#x, recentUnderruns=%u, isShared=%d", j, track->mState, state->mTrackMask, recentUnderruns, j, (int)track->mState, state->mTrackMask, recentUnderruns, track->sharedBuffer() != 0); // Since the FastMixer state already has the track inactive, do nothing here. } Loading Loading @@ -8041,7 +8041,7 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac ALOGV("active record track PAUSING -> ACTIVE"); recordTrack->mState = TrackBase::ACTIVE; } else { ALOGV("active record track state %d", recordTrack->mState); ALOGV("active record track state %d", (int)recordTrack->mState); } return status; } Loading @@ -8067,7 +8067,7 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac } if (recordTrack->mState != TrackBase::STARTING_1) { ALOGW("%s(%d): unsynchronized mState:%d change", __func__, recordTrack->id(), recordTrack->mState); __func__, recordTrack->id(), (int)recordTrack->mState); // Someone else has changed state, let them take over, // leave mState in the new state. recordTrack->clearSyncStartEvent(); Loading