Audio V4: Send volume and duplicated track attr in update metadata
Only the volume of MMAP thread was previously sent and
the attributes of duplicated tracks were skipped.
Bug: 38184704
Bug: 69623109
Test: check that the HAL receives correct track metadata volume
Test: check that the HAL receives correct duplicated track metadata
Signed-off-by: Kevin Rocard <krocard@google.com>
Change-Id: I1e3b14edd8c9a5a65d2b9f4cf8660997c81c701c
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 0e2da4e..3302868 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -19,8 +19,11 @@
#define ANDROID_AUDIO_FLINGER_H
#include "Configuration.h"
+#include <atomic>
+#include <mutex>
#include <deque>
#include <map>
+#include <vector>
#include <stdint.h>
#include <sys/types.h>
#include <limits.h>
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index ea01a25..a78be99 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -93,6 +93,23 @@
const sp<media::VolumeShaper::Operation>& operation);
sp<media::VolumeShaper::State> getVolumeShaperState(int id);
sp<media::VolumeHandler> 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<playback_track_metadata_t>;
+ using MetadataInserter = std::back_insert_iterator<SourceMetadatas>;
+ /** Copy the track metadata in the provided iterator. Thread safe. */
+ virtual void copyMetadataTo(MetadataInserter& backInserter) const;
protected:
// for numerous
@@ -133,6 +150,8 @@
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 @@
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<AudioTrackServerProxy> 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 @@
bool isActive() const { return mActive; }
const wp<ThreadBase>& 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 @@
bool mActive;
DuplicatingThread* const mSourceThread; // for waitTimeMs() in write()
sp<AudioTrackClientProxy> 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 160aa45..43a8b50 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2623,27 +2623,33 @@
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> &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> &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 @@
didModify = true;
// no acknowledgement required for newly active tracks
}
+ sp<AudioTrackServerProxy> 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 @@
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 @@
}
if (lastTrack) {
+ track->setFinalVolume((left + right) / 2.f);
if (left != mLeftVolFloat || right != mRightVolFloat) {
mLeftVolFloat = left;
mRightVolFloat = right;
@@ -6153,14 +6168,12 @@
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 bb81224..5a5961a 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -566,8 +566,8 @@
// periodically called in the threadLoop() to update power state uids.
void updatePowerState(sp<ThreadBase> 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 @@
int mLastActiveTracksGeneration;
wp<T> 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 @@
void removeTrack_l(const sp<Track>& 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<String16>& args);
void dumpTracks(int fd, const Vector<String16>& args);
@@ -1287,7 +1288,8 @@
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 236412b..ee9ce84 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -407,6 +407,9 @@
// 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 @@
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 @@
return outputBufferFull;
}
+void AudioFlinger::PlaybackThread::OutputTrack::copyMetadataTo(MetadataInserter& backInserter) const
+{
+ std::lock_guard<std::mutex> lock(mTrackMetadatasMutex);
+ backInserter = std::copy(mTrackMetadatas.begin(), mTrackMetadatas.end(), backInserter);
+}
+
+void AudioFlinger::PlaybackThread::OutputTrack::setMetadatas(const SourceMetadatas& metadatas) {
+ {
+ std::lock_guard<std::mutex> 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)
{