Support bit-perfect PCM playback.
This is part of USB audio improvement. Bit-perfect PCM playback
indicates that the PCM data will be sent as is down to the audio HAL.
When the track is bit-perfect, there will not be volume control
applied in the audio mixer. Only effects without processing or hw
accelerated effects will be allowed to attach to bit-perfect tracks.
Bug: 239435816
Test: manually
Test: atest audiopolicy_tests
Change-Id: I0bad4d7d78d4eaf741754d01bc5422ba15374782
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 474c5be..c307c94 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -621,13 +621,15 @@
fullConfig.format = config->format;
std::vector<audio_io_handle_t> secondaryOutputs;
bool isSpatialized;
+ bool isBitPerfect;
ret = AudioSystem::getOutputForAttr(&localAttr, &io,
actualSessionId,
&streamType, adjAttributionSource,
&fullConfig,
(audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ |
AUDIO_OUTPUT_FLAG_DIRECT),
- deviceId, &portId, &secondaryOutputs, &isSpatialized);
+ deviceId, &portId, &secondaryOutputs, &isSpatialized,
+ &isBitPerfect);
if (ret != NO_ERROR) {
config->sample_rate = fullConfig.sample_rate;
config->channel_mask = fullConfig.channel_mask;
@@ -1083,7 +1085,8 @@
audio_stream_type_t streamType;
audio_port_handle_t portId = AUDIO_PORT_HANDLE_NONE;
std::vector<audio_io_handle_t> secondaryOutputs;
- bool isSpatialized = false;;
+ bool isSpatialized = false;
+ bool isBitPerfect = false;
// TODO b/182392553: refactor or make clearer
pid_t clientPid =
@@ -1130,7 +1133,7 @@
lStatus = AudioSystem::getOutputForAttr(&localAttr, &output.outputId, sessionId, &streamType,
adjAttributionSource, &input.config, input.flags,
&output.selectedDeviceId, &portId, &secondaryOutputs,
- &isSpatialized);
+ &isSpatialized, &isBitPerfect);
if (lStatus != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) {
ALOGE("createTrack() getOutputForAttr() return error %d or invalid output handle", lStatus);
@@ -1196,7 +1199,8 @@
input.notificationsPerBuffer, input.speed,
input.sharedBuffer, sessionId, &output.flags,
callingPid, adjAttributionSource, input.clientInfo.clientTid,
- &lStatus, portId, input.audioTrackCallback, isSpatialized);
+ &lStatus, portId, input.audioTrackCallback, isSpatialized,
+ isBitPerfect);
LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (track == 0));
// we don't abort yet if lStatus != NO_ERROR; there is still work to be done regardless
@@ -2912,7 +2916,11 @@
return thread;
} else {
sp<PlaybackThread> thread;
- if (flags & AUDIO_OUTPUT_FLAG_SPATIALIZER) {
+ if (flags & AUDIO_OUTPUT_FLAG_BIT_PERFECT) {
+ thread = sp<BitPerfectThread>::make(this, outputStream, *output, mSystemReady);
+ ALOGV("%s() created bit-perfect output: ID %d thread %p",
+ __func__, *output, thread.get());
+ } else if (flags & AUDIO_OUTPUT_FLAG_SPATIALIZER) {
thread = new SpatializerThread(this, outputStream, *output,
mSystemReady, mixerConfig);
ALOGV("openOutput_l() created spatializer output: ID %d thread %p",
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index a2cde70..333c57b 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -590,6 +590,7 @@
class OffloadThread;
class DuplicatingThread;
class AsyncCallbackThread;
+ class BitPerfectThread;
class Track;
class RecordTrack;
class EffectBase;
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index fb03812..84b9c40 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -2961,6 +2961,9 @@
if ((*flags & AUDIO_OUTPUT_FLAG_FAST) != 0 && !isFastCompatible()) {
*flags = (audio_output_flags_t)(*flags & ~AUDIO_OUTPUT_FLAG_FAST);
}
+ if ((*flags & AUDIO_OUTPUT_FLAG_BIT_PERFECT) != 0 && !isBitPerfectCompatible()) {
+ *flags = (audio_output_flags_t)(*flags & ~AUDIO_OUTPUT_FLAG_BIT_PERFECT);
+ }
}
void AudioFlinger::EffectChain::checkInputFlagCompatibility(audio_input_flags_t *flags) const
@@ -2998,6 +3001,18 @@
return true;
}
+bool AudioFlinger::EffectChain::isBitPerfectCompatible() const {
+ Mutex::Autolock _l(mLock);
+ for (const auto &effect : mEffects) {
+ if (effect->isProcessImplemented()
+ && effect->isImplementationSoftware()) {
+ return false;
+ }
+ }
+ // Allow effects without processing or hw accelerated effects.
+ return true;
+}
+
// isCompatibleWithThread_l() must be called with thread->mLock held
bool AudioFlinger::EffectChain::isCompatibleWithThread_l(const sp<ThreadBase>& thread) const
{
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 72ec0e5..7b71a85 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -545,6 +545,9 @@
// Is this EffectChain compatible with the FAST audio flag.
bool isFastCompatible() const;
+ // Is this EffectChain compatible with the bit-perfect audio flag.
+ bool isBitPerfectCompatible() const;
+
// isCompatibleWithThread_l() must be called with thread->mLock held
bool isCompatibleWithThread_l(const sp<ThreadBase>& thread) const;
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 0f1373d..ebbdf56 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -83,7 +83,8 @@
* ready as possible (aka. Buffer is full). */
size_t frameCountToBeReady = SIZE_MAX,
float speed = 1.0f,
- bool isSpatialized = false);
+ bool isSpatialized = false,
+ bool isBitPerfect = false);
virtual ~Track();
virtual status_t initCheck() const;
@@ -203,6 +204,7 @@
audio_output_flags_t getOutputFlags() const { return mFlags; }
float getSpeed() const { return mSpeed; }
bool isSpatialized() const override { return mIsSpatialized; }
+ bool isBitPerfect() const override { return mIsBitPerfect; }
/**
* Updates the mute state and notifies the audio service. Call this only when holding player
@@ -361,6 +363,7 @@
TeePatches mTeePatches;
const float mSpeed;
const bool mIsSpatialized;
+ const bool mIsBitPerfect;
// TODO: replace PersistableBundle with own struct
// access these two variables only when holding player thread lock.
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index e06d2cd..f917527 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -536,6 +536,8 @@
return "MMAP_CAPTURE";
case SPATIALIZER:
return "SPATIALIZER";
+ case BIT_PERFECT:
+ return "BIT_PERFECT";
default:
return "unknown";
}
@@ -1519,6 +1521,26 @@
}
}
break;
+ case BIT_PERFECT:
+ if ((desc->flags & EFFECT_FLAG_HW_ACC_TUNNEL) != 0) {
+ // Allow HW accelerated effects of tunnel type
+ break;
+ }
+ // As bit-perfect tracks will not be allowed to apply audio effect that will touch the audio
+ // data, effects will not be allowed on 1) global effects (AUDIO_SESSION_OUTPUT_MIX),
+ // 2) post-processing effects (AUDIO_SESSION_OUTPUT_STAGE or AUDIO_SESSION_DEVICE) and
+ // 3) there is any bit-perfect track with the given session id.
+ if (sessionId == AUDIO_SESSION_OUTPUT_MIX || sessionId == AUDIO_SESSION_OUTPUT_STAGE ||
+ sessionId == AUDIO_SESSION_DEVICE) {
+ ALOGW("%s: effect %s not supported on bit-perfect thread %s",
+ __func__, desc->name, mThreadName);
+ return BAD_VALUE;
+ } else if ((hasAudioSession_l(sessionId) & ThreadBase::BIT_PERFECT_SESSION) != 0) {
+ ALOGW("%s: effect %s not supported as there is a bit-perfect track with session as %d",
+ __func__, desc->name, sessionId);
+ return BAD_VALUE;
+ }
+ break;
default:
LOG_ALWAYS_FATAL("checkEffectCompatibility_l(): wrong thread type %d", mType);
}
@@ -2283,7 +2305,8 @@
status_t *status,
audio_port_handle_t portId,
const sp<media::IAudioTrackCallback>& callback,
- bool isSpatialized)
+ bool isSpatialized,
+ bool isBitPerfect)
{
size_t frameCount = *pFrameCount;
size_t notificationFrameCount = *pNotificationFrameCount;
@@ -2315,6 +2338,25 @@
*flags = (audio_output_flags_t)(*flags & outputFlags);
}
+ if (isBitPerfect) {
+ sp<EffectChain> chain = getEffectChain_l(sessionId);
+ if (chain.get() != nullptr) {
+ // Bit-perfect is required according to the configuration and preferred mixer
+ // attributes, but it is not in the output flag from the client's request. Explicitly
+ // adding bit-perfect flag to check the compatibility
+ audio_output_flags_t flagsToCheck =
+ (audio_output_flags_t)(*flags & AUDIO_OUTPUT_FLAG_BIT_PERFECT);
+ chain->checkOutputFlagCompatibility(&flagsToCheck);
+ if ((flagsToCheck & AUDIO_OUTPUT_FLAG_BIT_PERFECT) == AUDIO_OUTPUT_FLAG_NONE) {
+ ALOGE("%s cannot create track as there is data-processing effect attached to "
+ "given session id(%d)", __func__, sessionId);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ *flags = flagsToCheck;
+ }
+ }
+
// client expresses a preference for FAST, but we get the final say
if (*flags & AUDIO_OUTPUT_FLAG_FAST) {
if (
@@ -2495,6 +2537,18 @@
*pNotificationFrameCount = notificationFrameCount;
switch (mType) {
+ case BIT_PERFECT:
+ if (isBitPerfect) {
+ if (sampleRate != mSampleRate || format != mFormat || channelMask != mChannelMask) {
+ ALOGE("%s, bad parameter when request streaming bit-perfect, sampleRate=%u, "
+ "format=%#x, channelMask=%#x, mSampleRate=%u, mFormat=%#x, mChannelMask=%#x",
+ __func__, sampleRate, format, channelMask, mSampleRate, mFormat,
+ mChannelMask);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ }
+ break;
case DIRECT:
if (audio_is_linear_pcm(format)) { // TODO maybe use audio_has_proportional_frames()?
@@ -2576,7 +2630,7 @@
nullptr /* buffer */, (size_t)0 /* bufferSize */, sharedBuffer,
sessionId, creatorPid, attributionSource, trackFlags,
TrackBase::TYPE_DEFAULT, portId, SIZE_MAX /*frameCountToBeReady*/,
- speed, isSpatialized);
+ speed, isSpatialized, isBitPerfect);
lStatus = track != 0 ? track->initCheck() : (status_t) NO_MEMORY;
if (lStatus != NO_ERROR) {
@@ -4020,7 +4074,8 @@
// Either threadLoop_mix() or threadLoop_sleepTime() should have set
// mMixerBuffer with data if mMixerBufferValid is true and mSleepTimeUs == 0.
// Merge mMixerBuffer data into mEffectBuffer (if any effects are valid)
- // or mSinkBuffer (if there are no effects).
+ // or mSinkBuffer (if there are no effects and there is no data already copied to
+ // mSinkBuffer).
//
// This is done pre-effects computation; if effects change to
// support higher precision, this needs to move.
@@ -4029,7 +4084,7 @@
// TODO use mSleepTimeUs == 0 as an additional condition.
uint32_t mixerChannelCount = mEffectBufferValid ?
audio_channel_count_from_out_mask(mMixerChannelMask) : mChannelCount;
- if (mMixerBufferValid) {
+ if (mMixerBufferValid && (mEffectBufferValid || !mHasDataCopiedToSinkBuffer)) {
void *buffer = mEffectBufferValid ? mEffectBuffer : mSinkBuffer;
audio_format_t format = mEffectBufferValid ? mEffectBufferFormat : mFormat;
@@ -4120,7 +4175,7 @@
// Effects buffer (buffer valid), we need to
// copy into the sink buffer.
// TODO use mSleepTimeUs == 0 as an additional condition.
- if (mEffectBufferValid) {
+ if (mEffectBufferValid && !mHasDataCopiedToSinkBuffer) {
//ALOGV("writing effect buffer to sink buffer format %#x", mFormat);
void *effectBuffer = (mType == SPATIALIZER) ? mPostSpatializerBuffer : mEffectBuffer;
if (requireMonoBlend()) {
@@ -4801,7 +4856,7 @@
// initialize fast mixer depending on configuration
bool initFastMixer;
- if (mType == SPATIALIZER) {
+ if (mType == SPATIALIZER || mType == BIT_PERFECT) {
initFastMixer = false;
} else {
switch (kUseFastMixer) {
@@ -9781,6 +9836,7 @@
audio_port_handle_t deviceId = mDeviceId;
std::vector<audio_io_handle_t> secondaryOutputs;
bool isSpatialized;
+ bool isBitPerfect;
ret = AudioSystem::getOutputForAttr(&mAttr, &io,
mSessionId,
&stream,
@@ -9790,7 +9846,8 @@
&deviceId,
&portId,
&secondaryOutputs,
- &isSpatialized);
+ &isSpatialized,
+ &isBitPerfect);
ALOGD_IF(!secondaryOutputs.empty(),
"MmapThread::start does not support secondary outputs, ignoring them");
} else {
@@ -10737,4 +10794,40 @@
return mInput->getCapturePosition((int64_t*)position, timeNanos);
}
+// ----------------------------------------------------------------------------
+
+AudioFlinger::BitPerfectThread::BitPerfectThread(const sp<AudioFlinger> &audioflinger,
+ AudioStreamOut *output, audio_io_handle_t id, bool systemReady)
+ : MixerThread(audioflinger, output, id, systemReady, BIT_PERFECT) {}
+
+AudioFlinger::PlaybackThread::mixer_state AudioFlinger::BitPerfectThread::prepareTracks_l(
+ Vector<sp<Track>> *tracksToRemove) {
+ mixer_state result = MixerThread::prepareTracks_l(tracksToRemove);
+ // If there is only one active track and it is bit-perfect, enable tee buffer.
+ if (mActiveTracks.size() == 1 && mActiveTracks[0]->isBitPerfect()) {
+ const int trackId = mActiveTracks[0]->id();
+ mAudioMixer->setParameter(
+ trackId, AudioMixer::TRACK, AudioMixer::TEE_BUFFER, (void *)mSinkBuffer);
+ mAudioMixer->setParameter(
+ trackId, AudioMixer::TRACK, AudioMixer::TEE_BUFFER_FRAME_COUNT,
+ (void *)(uintptr_t)mNormalFrameCount);
+ mIsBitPerfect = true;
+ } else {
+ mIsBitPerfect = false;
+ // No need to copy bit-perfect data directly to sink buffer given there are multiple tracks
+ // active.
+ for (const auto& track : mActiveTracks) {
+ const int trackId = track->id();
+ mAudioMixer->setParameter(
+ trackId, AudioMixer::TRACK, AudioMixer::TEE_BUFFER, nullptr);
+ }
+ }
+ return result;
+}
+
+void AudioFlinger::BitPerfectThread::threadLoop_mix() {
+ MixerThread::threadLoop_mix();
+ mHasDataCopiedToSinkBuffer = mIsBitPerfect;
+}
+
} // namespace android
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 8bf629b..f1b82e4 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -33,6 +33,7 @@
MMAP_PLAYBACK, // Thread class for MMAP playback stream
MMAP_CAPTURE, // Thread class for MMAP capture stream
SPATIALIZER, //
+ BIT_PERFECT, // Thread class for BitPerfectThread
// If you add any values here, also update ThreadBase::threadTypeToString()
};
@@ -430,8 +431,10 @@
// track
FAST_SESSION = 0x4, // the audio session corresponds to at least one
// fast track
- SPATIALIZED_SESSION = 0x8 // the audio session corresponds to at least one
- // spatialized track
+ SPATIALIZED_SESSION = 0x8, // the audio session corresponds to at least one
+ // spatialized track
+ BIT_PERFECT_SESSION = 0x10 // the audio session corresponds to at least one
+ // bit-perfect track
};
// get effect chain corresponding to session Id.
@@ -497,6 +500,9 @@
if (track->isSpatialized()) {
result |= SPATIALIZED_SESSION; // caution, only first track.
}
+ if (track->isBitPerfect()) {
+ result |= BIT_PERFECT_SESSION;
+ }
break;
}
}
@@ -982,7 +988,8 @@
status_t *status /*non-NULL*/,
audio_port_handle_t portId,
const sp<media::IAudioTrackCallback>& callback,
- bool isSpatialized);
+ bool isSpatialized,
+ bool isBitPerfect);
AudioStreamOut* getOutput() const;
AudioStreamOut* clearOutput();
@@ -1163,6 +1170,9 @@
// for any processing (including output processing).
bool mEffectBufferValid;
+ // Set to "true" to enable when data has already copied to sink
+ bool mHasDataCopiedToSinkBuffer = false;
+
// Frame size aligned buffer used as input and output to all post processing effects
// except the Spatializer in a SPATIALIZER thread. Non spatialized tracks are mixed into
// this buffer so that post processing effects can be applied.
@@ -2273,3 +2283,16 @@
AudioStreamIn* mInput;
};
+
+class BitPerfectThread : public MixerThread {
+public:
+ BitPerfectThread(const sp<AudioFlinger>& audioflinger, AudioStreamOut *output,
+ audio_io_handle_t id, bool systemReady);
+
+protected:
+ mixer_state prepareTracks_l(Vector<sp<Track>> *tracksToRemove) override;
+ void threadLoop_mix() override;
+
+private:
+ bool mIsBitPerfect;
+};
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 20bfbb0..f305aa8 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -109,6 +109,8 @@
virtual bool isSpatialized() const { return false; }
+ virtual bool isBitPerfect() const { return false; }
+
#ifdef TEE_SINK
void dumpTee(int fd, const std::string &reason) const {
mTee.dump(fd, reason);
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 6493e2a..950d555 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -633,7 +633,8 @@
audio_port_handle_t portId,
size_t frameCountToBeReady,
float speed,
- bool isSpatialized)
+ bool isSpatialized,
+ bool isBitPerfect)
: TrackBase(thread, client, attr, sampleRate, format, channelMask, frameCount,
// TODO: Using unsecurePointer() has some associated security pitfalls
// (see declaration for details).
@@ -668,7 +669,8 @@
mFlushHwPending(false),
mFlags(flags),
mSpeed(speed),
- mIsSpatialized(isSpatialized)
+ mIsSpatialized(isSpatialized),
+ mIsBitPerfect(isBitPerfect)
{
// client == 0 implies sharedBuffer == 0
ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));