audio: add implementation for TV related API
1. TunerConfiguration.
2. AudioDescriptionMixLevel getter/setter.
3. DualMonoMode getter/setter.
4. PlaybackRate getter/setter.
Test: TIS player with instrumented HAL
Test: atest AudioTrackTest#testTunerConfiguration
Test: atest AudioTrackTest#testDualMonoMode
Test: atest AudioTrackTest#testAudioDescriptionMixLevel
Test: atest AudioManagerTest#testGetAdditionalOutputDeviceDelay
Bug: 173482792
Merged-In: Idb22ce6714fa240b2b4b3b8637b16d3a51228fa1
Change-Id: Idb22ce6714fa240b2b4b3b8637b16d3a51228fa1
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 6d2afaa..10d4029 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -645,6 +645,13 @@
virtual sp<media::VolumeShaper::State> getVolumeShaperState(int id) override;
virtual status_t getTimestamp(AudioTimestamp& timestamp);
virtual void signal(); // signal playback thread for a change in control block
+ status_t getDualMonoMode(audio_dual_mono_mode_t* mode) override;
+ status_t setDualMonoMode(audio_dual_mono_mode_t mode) override;
+ status_t getAudioDescriptionMixLevel(float* leveldB) override;
+ status_t setAudioDescriptionMixLevel(float leveldB) override;
+ status_t getPlaybackRateParameters(audio_playback_rate_t* playbackRate) override;
+ status_t setPlaybackRateParameters(
+ const audio_playback_rate_t& playbackRate) override;
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index a23d88c..7804822 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -117,6 +117,12 @@
int auxEffectId() const { return mAuxEffectId; }
virtual status_t getTimestamp(AudioTimestamp& timestamp);
void signal();
+ status_t getDualMonoMode(audio_dual_mono_mode_t* mode);
+ status_t setDualMonoMode(audio_dual_mono_mode_t mode);
+ status_t getAudioDescriptionMixLevel(float* leveldB);
+ status_t setAudioDescriptionMixLevel(float leveldB);
+ status_t getPlaybackRateParameters(audio_playback_rate_t* playbackRate);
+ status_t setPlaybackRateParameters(const audio_playback_rate_t& playbackRate);
// implement FastMixerState::VolumeProvider interface
virtual gain_minifloat_packed_t getVolumeLR();
@@ -281,6 +287,10 @@
/** How many frames should be in the buffer before the track is considered ready */
const size_t mFrameCountToBeReady;
+ audio_dual_mono_mode_t mDualMonoMode = AUDIO_DUAL_MONO_MODE_OFF;
+ float mAudioDescriptionMixLevel = -std::numeric_limits<float>::infinity();
+ audio_playback_rate_t mPlaybackRateParameters = AUDIO_PLAYBACK_RATE_INITIALIZER;
+
private:
void interceptBuffer(const AudioBufferProvider::Buffer& buffer);
template <class F>
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index b17c0bc..ee886d5 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -54,6 +54,60 @@
namespace android {
+// Validation methods for input
+namespace {
+
+status_t validateAudioDescriptionMixLevel(float leveldB)
+{
+ constexpr float MAX_AUDIO_DESCRIPTION_MIX_LEVEL = 48.f;
+ return std::isnan(leveldB) || leveldB > MAX_AUDIO_DESCRIPTION_MIX_LEVEL ? BAD_VALUE : OK;
+}
+
+status_t validateDualMonoMode(audio_dual_mono_mode_t dualMonoMode)
+{
+ switch (dualMonoMode) {
+ case AUDIO_DUAL_MONO_MODE_OFF:
+ case AUDIO_DUAL_MONO_MODE_LR:
+ case AUDIO_DUAL_MONO_MODE_LL:
+ case AUDIO_DUAL_MONO_MODE_RR:
+ return OK;
+ }
+ return BAD_VALUE;
+}
+
+status_t validatePlaybackRateFallbackMode(
+ audio_timestretch_fallback_mode_t fallbackMode)
+{
+ switch (fallbackMode) {
+ case AUDIO_TIMESTRETCH_FALLBACK_CUT_REPEAT:
+ break; // warning if not listed.
+ case AUDIO_TIMESTRETCH_FALLBACK_DEFAULT:
+ case AUDIO_TIMESTRETCH_FALLBACK_MUTE:
+ case AUDIO_TIMESTRETCH_FALLBACK_FAIL:
+ return OK;
+ }
+ return BAD_VALUE;
+}
+
+status_t validatePlaybackRateStretchMode(audio_timestretch_stretch_mode_t stretchMode)
+{
+ switch (stretchMode) {
+ case AUDIO_TIMESTRETCH_STRETCH_DEFAULT:
+ case AUDIO_TIMESTRETCH_STRETCH_VOICE:
+ return OK;
+ }
+ return BAD_VALUE;
+}
+
+status_t validatePlaybackRate(const audio_playback_rate_t& playbackRate)
+{
+ if (playbackRate.mSpeed < 0.f || playbackRate.mPitch < 0.f) return BAD_VALUE;
+ return validatePlaybackRateFallbackMode(playbackRate.mFallbackMode) ?:
+ validatePlaybackRateStretchMode(playbackRate.mStretchMode);
+}
+
+} // namespace
+
using media::VolumeShaper;
// ----------------------------------------------------------------------------
// TrackBase
@@ -367,12 +421,45 @@
return mTrack->getTimestamp(timestamp);
}
-
void AudioFlinger::TrackHandle::signal()
{
return mTrack->signal();
}
+status_t AudioFlinger::TrackHandle::getDualMonoMode(audio_dual_mono_mode_t* mode)
+{
+ return mTrack->getDualMonoMode(mode);
+}
+
+status_t AudioFlinger::TrackHandle::setDualMonoMode(audio_dual_mono_mode_t mode)
+{
+ return validateDualMonoMode(mode) ?: mTrack->setDualMonoMode(mode);
+}
+
+status_t AudioFlinger::TrackHandle::getAudioDescriptionMixLevel(float* leveldB)
+{
+ return mTrack->getAudioDescriptionMixLevel(leveldB);
+}
+
+status_t AudioFlinger::TrackHandle::setAudioDescriptionMixLevel(float leveldB)
+{
+ return validateAudioDescriptionMixLevel(leveldB)
+ ?: mTrack->setAudioDescriptionMixLevel(leveldB);
+}
+
+status_t AudioFlinger::TrackHandle::getPlaybackRateParameters(
+ audio_playback_rate_t* playbackRate)
+{
+ return mTrack->getPlaybackRateParameters(playbackRate);
+}
+
+status_t AudioFlinger::TrackHandle::setPlaybackRateParameters(
+ const audio_playback_rate_t& playbackRate)
+{
+ return validatePlaybackRate(playbackRate)
+ ?: mTrack->setPlaybackRateParameters(playbackRate);
+}
+
status_t AudioFlinger::TrackHandle::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
@@ -1456,6 +1543,108 @@
}
}
+status_t AudioFlinger::PlaybackThread::Track::getDualMonoMode(audio_dual_mono_mode_t* mode)
+{
+ status_t status = INVALID_OPERATION;
+ if (isOffloadedOrDirect()) {
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != nullptr) {
+ PlaybackThread *t = (PlaybackThread *)thread.get();
+ Mutex::Autolock _l(t->mLock);
+ status = t->mOutput->stream->getDualMonoMode(mode);
+ ALOGD_IF((status == NO_ERROR) && (mDualMonoMode != *mode),
+ "%s: mode %d inconsistent", __func__, mDualMonoMode);
+ }
+ }
+ return status;
+}
+
+status_t AudioFlinger::PlaybackThread::Track::setDualMonoMode(audio_dual_mono_mode_t mode)
+{
+ status_t status = INVALID_OPERATION;
+ if (isOffloadedOrDirect()) {
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != nullptr) {
+ auto t = static_cast<PlaybackThread *>(thread.get());
+ Mutex::Autolock lock(t->mLock);
+ status = t->mOutput->stream->setDualMonoMode(mode);
+ if (status == NO_ERROR) {
+ mDualMonoMode = mode;
+ }
+ }
+ }
+ return status;
+}
+
+status_t AudioFlinger::PlaybackThread::Track::getAudioDescriptionMixLevel(float* leveldB)
+{
+ status_t status = INVALID_OPERATION;
+ if (isOffloadedOrDirect()) {
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != nullptr) {
+ auto t = static_cast<PlaybackThread *>(thread.get());
+ Mutex::Autolock lock(t->mLock);
+ status = t->mOutput->stream->getAudioDescriptionMixLevel(leveldB);
+ ALOGD_IF((status == NO_ERROR) && (mAudioDescriptionMixLevel != *leveldB),
+ "%s: level %.3f inconsistent", __func__, mAudioDescriptionMixLevel);
+ }
+ }
+ return status;
+}
+
+status_t AudioFlinger::PlaybackThread::Track::setAudioDescriptionMixLevel(float leveldB)
+{
+ status_t status = INVALID_OPERATION;
+ if (isOffloadedOrDirect()) {
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != nullptr) {
+ auto t = static_cast<PlaybackThread *>(thread.get());
+ Mutex::Autolock lock(t->mLock);
+ status = t->mOutput->stream->setAudioDescriptionMixLevel(leveldB);
+ if (status == NO_ERROR) {
+ mAudioDescriptionMixLevel = leveldB;
+ }
+ }
+ }
+ return status;
+}
+
+status_t AudioFlinger::PlaybackThread::Track::getPlaybackRateParameters(
+ audio_playback_rate_t* playbackRate)
+{
+ status_t status = INVALID_OPERATION;
+ if (isOffloadedOrDirect()) {
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != nullptr) {
+ auto t = static_cast<PlaybackThread *>(thread.get());
+ Mutex::Autolock lock(t->mLock);
+ status = t->mOutput->stream->getPlaybackRateParameters(playbackRate);
+ ALOGD_IF((status == NO_ERROR) &&
+ !isAudioPlaybackRateEqual(mPlaybackRateParameters, *playbackRate),
+ "%s: playbackRate inconsistent", __func__);
+ }
+ }
+ return status;
+}
+
+status_t AudioFlinger::PlaybackThread::Track::setPlaybackRateParameters(
+ const audio_playback_rate_t& playbackRate)
+{
+ status_t status = INVALID_OPERATION;
+ if (isOffloadedOrDirect()) {
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != nullptr) {
+ auto t = static_cast<PlaybackThread *>(thread.get());
+ Mutex::Autolock lock(t->mLock);
+ status = t->mOutput->stream->setPlaybackRateParameters(playbackRate);
+ if (status == NO_ERROR) {
+ mPlaybackRateParameters = playbackRate;
+ }
+ }
+ }
+ return status;
+}
+
//To be called with thread lock held
bool AudioFlinger::PlaybackThread::Track::isResumePending() {
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index 25f7c27..5c47d1b 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -505,6 +505,9 @@
lConfig.offload_info.duration_us = -1;
lConfig.offload_info.has_video = true; // conservative
lConfig.offload_info.is_streaming = true; // likely
+ lConfig.offload_info.encapsulation_mode = lConfig.offload_info.encapsulation_mode;
+ lConfig.offload_info.content_id = lConfig.offload_info.content_id;
+ lConfig.offload_info.sync_id = lConfig.offload_info.sync_id;
}
mFlags = (audio_output_flags_t)(mFlags | flags);