diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 0e2da4efb8..33028688a7 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -19,8 +19,11 @@ #define ANDROID_AUDIO_FLINGER_H #include "Configuration.h" +#include +#include #include #include +#include #include #include #include diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index ea01a25985..a78be99294 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -93,6 +93,23 @@ public: const sp& operation); sp getVolumeShaperState(int id); sp getVolumeHandler() { return mVolumeHandler; } + /** Set the computed normalized final volume of the track. + * !masterMute * masterVolume * streamVolume * averageLRVolume */ + void setFinalVolume(float volume); + float getFinalVolume() const { return mFinalVolume; } + + /** @return true if the track has changed (metadata or volume) since + * the last time this function was called, + * true if this function was never called since the track creation, + * false otherwise. + * Thread safe. + */ + bool readAndClearHasChanged() { return !mChangeNotified.test_and_set(); } + + using SourceMetadatas = std::vector; + using MetadataInserter = std::back_insert_iterator; + /** Copy the track metadata in the provided iterator. Thread safe. */ + virtual void copyMetadataTo(MetadataInserter& backInserter) const; protected: // for numerous @@ -133,6 +150,8 @@ protected: bool presentationComplete(int64_t framesWritten, size_t audioHalFrames); void signalClientFlag(int32_t flag); + /** Set that a metadata has changed and needs to be notified to backend. Thread safe. */ + void setMetadataHasChanged() { mChangeNotified.clear(); } public: void triggerEvents(AudioSystem::sync_event_t type); virtual void invalidate(); @@ -182,10 +201,13 @@ private: volatile float mCachedVolume; // combined master volume and stream type volume; // 'volatile' means accessed without lock or // barrier, but is read/written atomically + float mFinalVolume; // combine master volume, stream type volume and track volume sp mAudioTrackServerProxy; bool mResumeToStopping; // track was paused in stopping state. bool mFlushHwPending; // track requests for thread flush audio_output_flags_t mFlags; + // If the last track change was notified to the client with readAndClearHasChanged + std::atomic_flag mChangeNotified = ATOMIC_FLAG_INIT; }; // end of Track @@ -216,8 +238,11 @@ public: bool isActive() const { return mActive; } const wp& thread() const { return mThread; } -private: + void copyMetadataTo(MetadataInserter& backInserter) const override; + /** Set the metadatas of the upstream tracks. Thread safe. */ + void setMetadatas(const SourceMetadatas& metadatas); +private: status_t obtainBuffer(AudioBufferProvider::Buffer* buffer, uint32_t waitTimeMs); void clearBufferQueue(); @@ -232,6 +257,20 @@ private: bool mActive; DuplicatingThread* const mSourceThread; // for waitTimeMs() in write() sp mClientProxy; + /** Attributes of the source tracks. + * + * This member must be accessed with mTrackMetadatasMutex taken. + * There is one writer (duplicating thread) and one reader (downstream mixer). + * + * That means that the duplicating thread can block the downstream mixer + * thread and vice versa for the time of the copy. + * If this becomes an issue, the metadata could be stored in an atomic raw pointer, + * and a exchange with nullptr and delete can be used. + * Alternatively a read-copy-update might be implemented. + */ + SourceMetadatas mTrackMetadatas; + /** Protects mTrackMetadatas against concurrent access. */ + mutable std::mutex mTrackMetadatasMutex; }; // end of OutputTrack // playback track, used by PatchPanel diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 160aa45bea..43a8b50ae4 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -2623,27 +2623,33 @@ void AudioFlinger::PlaybackThread::readOutputParameters_l() void AudioFlinger::PlaybackThread::updateMetadata_l() { - // TODO: add volume support - if (mOutput == nullptr || mOutput->stream == nullptr || - !mActiveTracks.readAndClearHasChanged()) { - return; + if (mOutput == nullptr || mOutput->stream == nullptr ) { + return; // That should not happen + } + bool hasChanged = mActiveTracks.readAndClearHasChanged(); + for (const sp &track : mActiveTracks) { + // Do not short-circuit as all hasChanged states must be reset + // as all the metadata are going to be sent + hasChanged |= track->readAndClearHasChanged(); + } + if (!hasChanged) { + return; // nothing to do } StreamOutHalInterface::SourceMetadata metadata; + auto backInserter = std::back_inserter(metadata.tracks); for (const sp &track : mActiveTracks) { // No track is invalid as this is called after prepareTrack_l in the same critical section - if (track->isOutputTrack()) { - // TODO: OutputTrack (used for duplication) are currently not supported - continue; - } - metadata.tracks.push_back({ - .usage = track->attributes().usage, - .content_type = track->attributes().content_type, - .gain = 1, - }); + track->copyMetadataTo(backInserter); } - mOutput->stream->updateSourceMetadata(metadata); + sendMetadataToBackend_l(metadata); } +void AudioFlinger::PlaybackThread::sendMetadataToBackend_l( + const StreamOutHalInterface::SourceMetadata& metadata) +{ + mOutput->stream->updateSourceMetadata(metadata); +}; + status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames) { if (halFrames == NULL || dspFrames == NULL) { @@ -4381,13 +4387,19 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac didModify = true; // no acknowledgement required for newly active tracks } + sp proxy = track->mAudioTrackServerProxy; // cache the combined master volume and stream type volume for fast mixer; this // lacks any synchronization or barrier so VolumeProvider may read a stale value const float vh = track->getVolumeHandler()->getVolume( - track->mAudioTrackServerProxy->framesReleased()).first; - track->mCachedVolume = masterVolume + proxy->framesReleased()).first; + float volume = masterVolume * mStreamTypes[track->streamType()].volume * vh; + track->mCachedVolume = masterVolume; + gain_minifloat_packed_t vlr = proxy->getVolumeLR(); + float vlf = volume * float_from_gain(gain_minifloat_unpack_left(vlr)); + float vrf = volume * float_from_gain(gain_minifloat_unpack_right(vlr)); + track->setFinalVolume((vlf + vrf) / 2.f); ++fastTracks; } else { // was it previously active? @@ -4564,6 +4576,8 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac vaf = v * sendLevel * (1. / MAX_GAIN_INT); } + track->setFinalVolume((vrf + vlf) / 2.f); + // Delegate volume control to effect in track effect chain if needed if (chain != 0 && chain->setVolume_l(&vl, &vr)) { // Do not ramp volume if volume is controlled by effect @@ -5096,6 +5110,7 @@ void AudioFlinger::DirectOutputThread::processVolume_l(Track *track, bool lastTr } if (lastTrack) { + track->setFinalVolume((left + right) / 2.f); if (left != mLeftVolFloat || right != mRightVolFloat) { mLeftVolFloat = left; mRightVolFloat = right; @@ -6153,14 +6168,12 @@ bool AudioFlinger::DuplicatingThread::outputsReady( return true; } -void AudioFlinger::DuplicatingThread::updateMetadata_l() +void AudioFlinger::DuplicatingThread::sendMetadataToBackend_l( + const StreamOutHalInterface::SourceMetadata& metadata) { - // TODO: The duplicated track metadata needs to be pushed to downstream - // but this information can be read at any time by the downstream threads. - // Taking the lock of any downstream threads is no possible due to cross deadlock risks - // (eg: during effect move). - // A lock-free structure needs to be used to shared the metadata, probably an atomic - // pointer to a metadata vector in each output tracks. + for (auto& outputTrack : outputTracks) { // not mOutputTracks + outputTrack->setMetadatas(metadata.tracks); + } } uint32_t AudioFlinger::DuplicatingThread::activeSleepTimeUs() const diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index bb81224358..5a5961a989 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -566,8 +566,8 @@ protected: // periodically called in the threadLoop() to update power state uids. void updatePowerState(sp thread, bool force = false); - /** @return true if the active tracks have changed since the last time - * this function was called or the vector was created. */ + /** @return true if one or move active tracks was added or removed since the + * last time this function was called or the vector was created. */ bool readAndClearHasChanged(); private: @@ -588,7 +588,7 @@ protected: int mLastActiveTracksGeneration; wp mLatestActiveTrack; // latest track added to ActiveTracks SimpleLog * const mLocalLog; - // If the active tracks have changed since last call to readAndClearHasChanged + // If the vector has changed since last call to readAndClearHasChanged bool mHasChanged = false; }; @@ -927,7 +927,8 @@ private: void removeTrack_l(const sp& track); void readOutputParameters_l(); - void updateMetadata_l() override; + void updateMetadata_l() final; + virtual void sendMetadataToBackend_l(const StreamOutHalInterface::SourceMetadata& metadata); virtual void dumpInternals(int fd, const Vector& args); void dumpTracks(int fd, const Vector& args); @@ -1287,7 +1288,8 @@ public: void removeOutputTrack(MixerThread* thread); uint32_t waitTimeMs() const { return mWaitTimeMs; } - void updateMetadata_l() override; + void sendMetadataToBackend_l( + const StreamOutHalInterface::SourceMetadata& metadata) override; protected: virtual uint32_t activeSleepTimeUs() const; diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 236412b0c2..ee9ce84bca 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -407,6 +407,9 @@ AudioFlinger::PlaybackThread::Track::Track( // mSinkTimestamp mFastIndex(-1), mCachedVolume(1.0), + /* The track might not play immediately after being active, similarly as if its volume was 0. + * When the track starts playing, its volume will be computed. */ + mFinalVolume(0.f), mResumeToStopping(false), mFlushHwPending(false), mFlags(flags) @@ -997,6 +1000,23 @@ sp AudioFlinger::PlaybackThread::Track::getVolumeShaperStat return mVolumeHandler->getVolumeShaperState(id); } +void AudioFlinger::PlaybackThread::Track::setFinalVolume(float volume) +{ + if (mFinalVolume != volume) { // Compare to an epsilon if too many meaningless updates + mFinalVolume = volume; + setMetadataHasChanged(); + } +} + +void AudioFlinger::PlaybackThread::Track::copyMetadataTo(MetadataInserter& backInserter) const +{ + *backInserter++ = { + .usage = mAttr.usage, + .content_type = mAttr.content_type, + .gain = mFinalVolume, + }; +} + status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& timestamp) { if (!isOffloaded() && !isDirect()) { @@ -1427,6 +1447,21 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(void* data, uint32_t frame return outputBufferFull; } +void AudioFlinger::PlaybackThread::OutputTrack::copyMetadataTo(MetadataInserter& backInserter) const +{ + std::lock_guard lock(mTrackMetadatasMutex); + backInserter = std::copy(mTrackMetadatas.begin(), mTrackMetadatas.end(), backInserter); +} + +void AudioFlinger::PlaybackThread::OutputTrack::setMetadatas(const SourceMetadatas& metadatas) { + { + std::lock_guard lock(mTrackMetadatasMutex); + mTrackMetadatas = metadatas; + } + // No need to adjust metadata track volumes as OutputTrack volumes are always 0dBFS. + setMetadataHasChanged(); +} + status_t AudioFlinger::PlaybackThread::OutputTrack::obtainBuffer( AudioBufferProvider::Buffer* buffer, uint32_t waitTimeMs) {