Add management of audio effects applied to a specific input or output audio device. A new class DeviceEffectProxy derived from Effectbase is added to represent an effect attached to a particular audio device type and address. This proxy manages one or more actual EffectModule instances automatically added to a playback or capture thread or directly to the audio HAL when the targeted audio device is used by an audio patch. A new DeviceEffectManager class is added to manage creation and release of DeviceEffectProxy instances and monitor creation and release of audio patches and create or release actual effect instances accordingly. Bug: 136294538 Test: make Change-Id: I23b9f9db4459136039c5ee327cf3b1aefa7db5afgugelfrei
parent
9ce5404e8e
commit
b82e6b724e
@ -0,0 +1,277 @@
|
||||
/*
|
||||
**
|
||||
** Copyright 2019, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
#define LOG_TAG "AudioFlinger::DeviceEffectManager"
|
||||
//#define LOG_NDEBUG 0
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <audio_utils/primitives.h>
|
||||
|
||||
#include "AudioFlinger.h"
|
||||
#include <media/audiohal/EffectsFactoryHalInterface.h>
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace android {
|
||||
|
||||
void AudioFlinger::DeviceEffectManager::createAudioPatch(audio_patch_handle_t handle,
|
||||
const PatchPanel::Patch& patch) {
|
||||
ALOGV("%s handle %d mHalHandle %d num sinks %d device sink %08x",
|
||||
__func__, handle, patch.mHalHandle,
|
||||
patch.mAudioPatch.num_sinks,
|
||||
patch.mAudioPatch.num_sinks > 0 ? patch.mAudioPatch.sinks[0].ext.device.type : 0);
|
||||
|
||||
mCommandThread->createAudioPatchCommand(handle, patch);
|
||||
}
|
||||
|
||||
void AudioFlinger::DeviceEffectManager::onCreateAudioPatch(audio_patch_handle_t handle,
|
||||
const PatchPanel::Patch& patch) {
|
||||
ALOGV("%s handle %d mHalHandle %d device sink %08x",
|
||||
__func__, handle, patch.mHalHandle,
|
||||
patch.mAudioPatch.num_sinks > 0 ? patch.mAudioPatch.sinks[0].ext.device.type : 0);
|
||||
Mutex::Autolock _l(mLock);
|
||||
for (auto& effect : mDeviceEffects) {
|
||||
status_t status = effect.second->onCreatePatch(handle, patch);
|
||||
ALOGV("%s Effect onCreatePatch status %d", __func__, status);
|
||||
ALOGW_IF(status == BAD_VALUE, "%s onCreatePatch error %d", __func__, status);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioFlinger::DeviceEffectManager::releaseAudioPatch(audio_patch_handle_t handle) {
|
||||
ALOGV("%s", __func__);
|
||||
mCommandThread->releaseAudioPatchCommand(handle);
|
||||
}
|
||||
|
||||
void AudioFlinger::DeviceEffectManager::onReleaseAudioPatch(audio_patch_handle_t handle) {
|
||||
ALOGV("%s", __func__);
|
||||
Mutex::Autolock _l(mLock);
|
||||
for (auto& effect : mDeviceEffects) {
|
||||
effect.second->onReleasePatch(handle);
|
||||
}
|
||||
}
|
||||
|
||||
// DeviceEffectManager::createEffect_l() must be called with AudioFlinger::mLock held
|
||||
sp<AudioFlinger::EffectHandle> AudioFlinger::DeviceEffectManager::createEffect_l(
|
||||
effect_descriptor_t *descriptor,
|
||||
const AudioDeviceTypeAddr& device,
|
||||
const sp<AudioFlinger::Client>& client,
|
||||
const sp<IEffectClient>& effectClient,
|
||||
const std::map<audio_patch_handle_t, PatchPanel::Patch>& patches,
|
||||
int *enabled,
|
||||
status_t *status) {
|
||||
sp<DeviceEffectProxy> effect;
|
||||
sp<EffectHandle> handle;
|
||||
status_t lStatus;
|
||||
|
||||
lStatus = checkEffectCompatibility(descriptor);
|
||||
if (lStatus != NO_ERROR) {
|
||||
*status = lStatus;
|
||||
return handle;
|
||||
}
|
||||
|
||||
{
|
||||
Mutex::Autolock _l(mLock);
|
||||
auto iter = mDeviceEffects.find(device);
|
||||
if (iter != mDeviceEffects.end()) {
|
||||
effect = iter->second;
|
||||
} else {
|
||||
effect = new DeviceEffectProxy(device, mMyCallback,
|
||||
descriptor, mAudioFlinger.nextUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT));
|
||||
}
|
||||
// create effect handle and connect it to effect module
|
||||
handle = new EffectHandle(effect, client, effectClient, 0 /*priority*/);
|
||||
lStatus = handle->initCheck();
|
||||
if (lStatus == NO_ERROR) {
|
||||
lStatus = effect->addHandle(handle.get());
|
||||
if (lStatus == NO_ERROR) {
|
||||
effect->init(patches);
|
||||
mDeviceEffects.emplace(device, effect);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (enabled != NULL) {
|
||||
*enabled = (int)effect->isEnabled();
|
||||
}
|
||||
*status = lStatus;
|
||||
return handle;
|
||||
}
|
||||
|
||||
status_t AudioFlinger::DeviceEffectManager::checkEffectCompatibility(
|
||||
const effect_descriptor_t *desc) {
|
||||
|
||||
if ((desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_PRE_PROC
|
||||
&& (desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_POST_PROC) {
|
||||
ALOGW("%s() non pre/post processing device effect %s", __func__, desc->name);
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t AudioFlinger::DeviceEffectManager::createEffectHal(
|
||||
const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t deviceId,
|
||||
sp<EffectHalInterface> *effect) {
|
||||
status_t status = NO_INIT;
|
||||
sp<EffectsFactoryHalInterface> effectsFactory = mAudioFlinger.getEffectsFactory();
|
||||
if (effectsFactory != 0) {
|
||||
status = effectsFactory->createEffect(
|
||||
pEffectUuid, sessionId, AUDIO_IO_HANDLE_NONE, deviceId, effect);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void AudioFlinger::DeviceEffectManager::dump(int fd) {
|
||||
const bool locked = dumpTryLock(mLock);
|
||||
if (!locked) {
|
||||
String8 result("DeviceEffectManager may be deadlocked\n");
|
||||
write(fd, result.string(), result.size());
|
||||
}
|
||||
|
||||
write(fd, "\nDevice Effects:\n", sizeof("\nDevice Effects:\n"));
|
||||
for (const auto& iter : mDeviceEffects) {
|
||||
String8 outStr;
|
||||
outStr.appendFormat("%*sEffect for device %s address %s:\n", 2, "",
|
||||
::android::toString(iter.first.mType).c_str(), iter.first.getAddress());
|
||||
write(fd, outStr.string(), outStr.size());
|
||||
iter.second->dump(fd, 4);
|
||||
}
|
||||
|
||||
if (locked) {
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t AudioFlinger::DeviceEffectManager::removeEffect(const sp<DeviceEffectProxy>& effect)
|
||||
{
|
||||
Mutex::Autolock _l(mLock);
|
||||
mDeviceEffects.erase(effect->device());
|
||||
return mDeviceEffects.size();
|
||||
}
|
||||
|
||||
bool AudioFlinger::DeviceEffectManagerCallback::disconnectEffectHandle(
|
||||
EffectHandle *handle, bool unpinIfLast) {
|
||||
sp<EffectBase> effectBase = handle->effect().promote();
|
||||
if (effectBase == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sp<DeviceEffectProxy> effect = effectBase->asDeviceEffectProxy();
|
||||
if (effect == nullptr) {
|
||||
return false;
|
||||
}
|
||||
// restore suspended effects if the disconnected handle was enabled and the last one.
|
||||
bool remove = (effect->removeHandle(handle) == 0) && (!effect->isPinned() || unpinIfLast);
|
||||
if (remove) {
|
||||
mManager.removeEffect(effect);
|
||||
if (handle->enabled()) {
|
||||
effectBase->checkSuspendOnEffectEnabled(false, false /*threadLocked*/);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------- DeviceEffectManager::CommandThread implementation ----------
|
||||
|
||||
|
||||
AudioFlinger::DeviceEffectManager::CommandThread::~CommandThread()
|
||||
{
|
||||
Mutex::Autolock _l(mLock);
|
||||
mCommands.clear();
|
||||
}
|
||||
|
||||
void AudioFlinger::DeviceEffectManager::CommandThread::onFirstRef()
|
||||
{
|
||||
run("DeviceEffectManage_CommandThread", ANDROID_PRIORITY_AUDIO);
|
||||
}
|
||||
|
||||
bool AudioFlinger::DeviceEffectManager::CommandThread::threadLoop()
|
||||
{
|
||||
mLock.lock();
|
||||
while (!exitPending())
|
||||
{
|
||||
while (!mCommands.empty() && !exitPending()) {
|
||||
sp<Command> command = mCommands.front();
|
||||
mCommands.pop_front();
|
||||
mLock.unlock();
|
||||
|
||||
switch (command->mCommand) {
|
||||
case CREATE_AUDIO_PATCH: {
|
||||
CreateAudioPatchData *data = (CreateAudioPatchData *)command->mData.get();
|
||||
ALOGV("CommandThread() processing create audio patch handle %d", data->mHandle);
|
||||
mManager.onCreateAudioPatch(data->mHandle, data->mPatch);
|
||||
} break;
|
||||
case RELEASE_AUDIO_PATCH: {
|
||||
ReleaseAudioPatchData *data = (ReleaseAudioPatchData *)command->mData.get();
|
||||
ALOGV("CommandThread() processing release audio patch handle %d", data->mHandle);
|
||||
mManager.onReleaseAudioPatch(data->mHandle);
|
||||
} break;
|
||||
default:
|
||||
ALOGW("CommandThread() unknown command %d", command->mCommand);
|
||||
}
|
||||
mLock.lock();
|
||||
}
|
||||
|
||||
// At this stage we have either an empty command queue or the first command in the queue
|
||||
// has a finite delay. So unless we are exiting it is safe to wait.
|
||||
if (!exitPending()) {
|
||||
ALOGV("CommandThread() going to sleep");
|
||||
mWaitWorkCV.wait(mLock);
|
||||
}
|
||||
}
|
||||
mLock.unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
void AudioFlinger::DeviceEffectManager::CommandThread::sendCommand(sp<Command> command) {
|
||||
Mutex::Autolock _l(mLock);
|
||||
mCommands.push_back(command);
|
||||
mWaitWorkCV.signal();
|
||||
}
|
||||
|
||||
void AudioFlinger::DeviceEffectManager::CommandThread::createAudioPatchCommand(
|
||||
audio_patch_handle_t handle, const PatchPanel::Patch& patch)
|
||||
{
|
||||
sp<Command> command = new Command(CREATE_AUDIO_PATCH, new CreateAudioPatchData(handle, patch));
|
||||
ALOGV("CommandThread() adding create patch handle %d mHalHandle %d.", handle, patch.mHalHandle);
|
||||
sendCommand(command);
|
||||
}
|
||||
|
||||
void AudioFlinger::DeviceEffectManager::CommandThread::releaseAudioPatchCommand(
|
||||
audio_patch_handle_t handle)
|
||||
{
|
||||
sp<Command> command = new Command(RELEASE_AUDIO_PATCH, new ReleaseAudioPatchData(handle));
|
||||
ALOGV("CommandThread() adding release patch");
|
||||
sendCommand(command);
|
||||
}
|
||||
|
||||
void AudioFlinger::DeviceEffectManager::CommandThread::exit()
|
||||
{
|
||||
ALOGV("CommandThread::exit");
|
||||
{
|
||||
AutoMutex _l(mLock);
|
||||
requestExit();
|
||||
mWaitWorkCV.signal();
|
||||
}
|
||||
// Note that we can call it from the thread loop if all other references have been released
|
||||
// but it will safely return WOULD_BLOCK in this case
|
||||
requestExitAndWait();
|
||||
}
|
||||
|
||||
} // namespace android
|
@ -0,0 +1,203 @@
|
||||
/*
|
||||
**
|
||||
** Copyright 2019, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDING_FROM_AUDIOFLINGER_H
|
||||
#error This header file should only be included from AudioFlinger.h
|
||||
#endif
|
||||
|
||||
// DeviceEffectManager is concealed within AudioFlinger, their lifetimes are the same.
|
||||
class DeviceEffectManager {
|
||||
public:
|
||||
explicit DeviceEffectManager(AudioFlinger* audioFlinger)
|
||||
: mCommandThread(new CommandThread(*this)), mAudioFlinger(*audioFlinger),
|
||||
mMyCallback(new DeviceEffectManagerCallback(this)) {}
|
||||
|
||||
~DeviceEffectManager() {
|
||||
mCommandThread->exit();
|
||||
}
|
||||
|
||||
sp<EffectHandle> createEffect_l(effect_descriptor_t *descriptor,
|
||||
const AudioDeviceTypeAddr& device,
|
||||
const sp<AudioFlinger::Client>& client,
|
||||
const sp<IEffectClient>& effectClient,
|
||||
const std::map<audio_patch_handle_t, PatchPanel::Patch>& patches,
|
||||
int *enabled,
|
||||
status_t *status);
|
||||
void createAudioPatch(audio_patch_handle_t handle, const PatchPanel::Patch& patch);
|
||||
void releaseAudioPatch(audio_patch_handle_t handle);
|
||||
|
||||
size_t removeEffect(const sp<DeviceEffectProxy>& effect);
|
||||
status_t createEffectHal(const effect_uuid_t *pEffectUuid,
|
||||
int32_t sessionId, int32_t deviceId,
|
||||
sp<EffectHalInterface> *effect);
|
||||
status_t addEffectToHal(audio_port_handle_t deviceId, audio_module_handle_t hwModuleId,
|
||||
sp<EffectHalInterface> effect) {
|
||||
return mAudioFlinger.addEffectToHal(deviceId, hwModuleId, effect);
|
||||
};
|
||||
status_t removeEffectFromHal(audio_port_handle_t deviceId, audio_module_handle_t hwModuleId,
|
||||
sp<EffectHalInterface> effect) {
|
||||
return mAudioFlinger.removeEffectFromHal(deviceId, hwModuleId, effect);
|
||||
};
|
||||
|
||||
AudioFlinger& audioFlinger() const { return mAudioFlinger; }
|
||||
|
||||
void dump(int fd);
|
||||
|
||||
private:
|
||||
|
||||
// Thread to execute create and release patch commands asynchronously. This is needed because
|
||||
// PatchPanel::createAudioPatch and releaseAudioPatch are executed from audio policy service
|
||||
// with mutex locked and effect management requires to call back into audio policy service
|
||||
class Command;
|
||||
class CommandThread : public Thread {
|
||||
public:
|
||||
|
||||
enum {
|
||||
CREATE_AUDIO_PATCH,
|
||||
RELEASE_AUDIO_PATCH,
|
||||
};
|
||||
|
||||
CommandThread(DeviceEffectManager& manager)
|
||||
: Thread(false), mManager(manager) {}
|
||||
~CommandThread() override;
|
||||
|
||||
// Thread virtuals
|
||||
void onFirstRef() override;
|
||||
bool threadLoop() override;
|
||||
|
||||
void exit();
|
||||
|
||||
void createAudioPatchCommand(audio_patch_handle_t handle,
|
||||
const PatchPanel::Patch& patch);
|
||||
void releaseAudioPatchCommand(audio_patch_handle_t handle);
|
||||
|
||||
private:
|
||||
class CommandData;
|
||||
|
||||
// descriptor for requested tone playback event
|
||||
class Command: public RefBase {
|
||||
public:
|
||||
Command() = default;
|
||||
Command(int command, sp<CommandData> data)
|
||||
: mCommand(command), mData(data) {}
|
||||
|
||||
int mCommand = -1;
|
||||
sp<CommandData> mData;
|
||||
};
|
||||
|
||||
class CommandData: public RefBase {
|
||||
public:
|
||||
virtual ~CommandData() = default;
|
||||
};
|
||||
|
||||
class CreateAudioPatchData : public CommandData {
|
||||
public:
|
||||
CreateAudioPatchData(audio_patch_handle_t handle, const PatchPanel::Patch& patch)
|
||||
: mHandle(handle), mPatch(patch) {}
|
||||
|
||||
audio_patch_handle_t mHandle;
|
||||
const PatchPanel::Patch mPatch;
|
||||
};
|
||||
|
||||
class ReleaseAudioPatchData : public CommandData {
|
||||
public:
|
||||
ReleaseAudioPatchData(audio_patch_handle_t handle)
|
||||
: mHandle(handle) {}
|
||||
|
||||
audio_patch_handle_t mHandle;
|
||||
};
|
||||
|
||||
void sendCommand(sp<Command> command);
|
||||
|
||||
Mutex mLock;
|
||||
Condition mWaitWorkCV;
|
||||
std::deque <sp<Command>> mCommands; // list of pending commands
|
||||
DeviceEffectManager& mManager;
|
||||
};
|
||||
|
||||
void onCreateAudioPatch(audio_patch_handle_t handle, const PatchPanel::Patch& patch);
|
||||
void onReleaseAudioPatch(audio_patch_handle_t handle);
|
||||
|
||||
status_t checkEffectCompatibility(const effect_descriptor_t *desc);
|
||||
|
||||
Mutex mLock;
|
||||
sp<CommandThread> mCommandThread;
|
||||
AudioFlinger &mAudioFlinger;
|
||||
const sp<DeviceEffectManagerCallback> mMyCallback;
|
||||
std::map<AudioDeviceTypeAddr, sp<DeviceEffectProxy>> mDeviceEffects;
|
||||
};
|
||||
|
||||
class DeviceEffectManagerCallback : public EffectCallbackInterface {
|
||||
public:
|
||||
DeviceEffectManagerCallback(DeviceEffectManager *manager)
|
||||
: mManager(*manager) {}
|
||||
|
||||
status_t createEffectHal(const effect_uuid_t *pEffectUuid,
|
||||
int32_t sessionId, int32_t deviceId,
|
||||
sp<EffectHalInterface> *effect) override {
|
||||
return mManager.createEffectHal(pEffectUuid, sessionId, deviceId, effect);
|
||||
}
|
||||
status_t allocateHalBuffer(size_t size __unused,
|
||||
sp<EffectBufferHalInterface>* buffer __unused) override { return NO_ERROR; }
|
||||
bool updateOrphanEffectChains(const sp<EffectBase>& effect __unused) override { return false; }
|
||||
|
||||
audio_io_handle_t io() const override { return AUDIO_IO_HANDLE_NONE; }
|
||||
bool isOutput() const override { return false; }
|
||||
bool isOffload() const override { return false; }
|
||||
bool isOffloadOrDirect() const override { return false; }
|
||||
bool isOffloadOrMmap() const override { return false; }
|
||||
|
||||
uint32_t sampleRate() const override { return 0; }
|
||||
audio_channel_mask_t channelMask() const override { return AUDIO_CHANNEL_NONE; }
|
||||
uint32_t channelCount() const override { return 0; }
|
||||
size_t frameCount() const override { return 0; }
|
||||
uint32_t latency() const override { return 0; }
|
||||
|
||||
status_t addEffectToHal(sp<EffectHalInterface> effect __unused) override {
|
||||
return NO_ERROR;
|
||||
}
|
||||
status_t removeEffectFromHal(sp<EffectHalInterface> effect __unused) override {
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
bool disconnectEffectHandle(EffectHandle *handle, bool unpinIfLast) override;
|
||||
void setVolumeForOutput(float left __unused, float right __unused) const override {}
|
||||
|
||||
// check if effects should be suspended or restored when a given effect is enable or disabled
|
||||
void checkSuspendOnEffectEnabled(const sp<EffectBase>& effect __unused,
|
||||
bool enabled __unused, bool threadLocked __unused) override {}
|
||||
void resetVolume() override {}
|
||||
uint32_t strategy() const override { return 0; }
|
||||
int32_t activeTrackCnt() const override { return 0; }
|
||||
void onEffectEnable(const sp<EffectBase>& effect __unused) override {}
|
||||
void onEffectDisable(const sp<EffectBase>& effect __unused) override {}
|
||||
|
||||
wp<EffectChain> chain() const override { return nullptr; }
|
||||
|
||||
int newEffectId() { return mManager.audioFlinger().nextUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT); }
|
||||
|
||||
status_t addEffectToHal(audio_port_handle_t deviceId,
|
||||
audio_module_handle_t hwModuleId, sp<EffectHalInterface> effect) {
|
||||
return mManager.addEffectToHal(deviceId, hwModuleId, effect);
|
||||
}
|
||||
status_t removeEffectFromHal(audio_port_handle_t deviceId,
|
||||
audio_module_handle_t hwModuleId, sp<EffectHalInterface> effect) {
|
||||
return mManager.removeEffectFromHal(deviceId, hwModuleId, effect);
|
||||
}
|
||||
private:
|
||||
DeviceEffectManager& mManager;
|
||||
};
|
Loading…
Reference in new issue