summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/media/AudioSystem.h25
-rw-r--r--include/media/IAudioPolicyService.h2
-rw-r--r--include/media/IAudioPolicyServiceClient.h56
-rw-r--r--media/libcpustats/Android.mk4
-rw-r--r--media/libcpustats/ThreadCpuUsage.cpp3
-rw-r--r--media/libmedia/Android.mk1
-rw-r--r--media/libmedia/AudioSystem.cpp30
-rw-r--r--media/libmedia/IAudioPolicyService.cpp17
-rw-r--r--media/libmedia/IAudioPolicyServiceClient.cpp83
-rw-r--r--media/libstagefright/codecs/aacdec/Android.mk3
-rw-r--r--media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp372
-rw-r--r--media/libstagefright/codecs/aacdec/DrcPresModeWrap.h62
-rw-r--r--media/libstagefright/codecs/aacdec/SoftAAC2.cpp727
-rw-r--r--media/libstagefright/codecs/aacdec/SoftAAC2.h27
-rw-r--r--services/audioflinger/Android.mk1
-rw-r--r--services/audioflinger/AudioMixer.cpp3
-rw-r--r--services/audioflinger/FastCapture.cpp222
-rw-r--r--services/audioflinger/FastCapture.h78
-rw-r--r--services/audioflinger/FastCaptureState.cpp30
-rw-r--r--services/audioflinger/FastCaptureState.h51
-rw-r--r--services/audioflinger/FastMixer.cpp38
-rw-r--r--services/audioflinger/FastMixer.h10
-rw-r--r--services/audioflinger/PatchPanel.cpp48
-rw-r--r--services/audioflinger/StateQueueInstantiations.cpp4
-rw-r--r--services/audioflinger/Threads.cpp18
-rw-r--r--services/audiopolicy/AudioPolicyClientImpl.cpp17
-rw-r--r--services/audiopolicy/AudioPolicyInterface.h24
-rw-r--r--services/audiopolicy/AudioPolicyInterfaceImpl.cpp67
-rw-r--r--services/audiopolicy/AudioPolicyManager.cpp1478
-rw-r--r--services/audiopolicy/AudioPolicyManager.h146
-rw-r--r--services/audiopolicy/AudioPolicyService.cpp162
-rw-r--r--services/audiopolicy/AudioPolicyService.h54
-rw-r--r--services/audiopolicy/audio_policy_conf.h76
33 files changed, 3403 insertions, 536 deletions
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index a79a9ff..6fe0c7f 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -19,6 +19,7 @@
#include <hardware/audio_effect.h>
#include <media/IAudioFlingerClient.h>
+#include <media/IAudioPolicyServiceClient.h>
#include <system/audio.h>
#include <system/audio_policy.h>
#include <utils/Errors.h>
@@ -301,6 +302,21 @@ public:
// ----------------------------------------------------------------------------
+ class AudioPortCallback : public RefBase
+ {
+ public:
+
+ AudioPortCallback() {}
+ virtual ~AudioPortCallback() {}
+
+ virtual void onAudioPortListUpdate() = 0;
+ virtual void onAudioPatchListUpdate() = 0;
+ virtual void onServiceDied() = 0;
+
+ };
+
+ static void setAudioPortCallback(sp<AudioPortCallback> callBack);
+
private:
class AudioFlingerClient: public IBinder::DeathRecipient, public BnAudioFlingerClient
@@ -319,7 +335,8 @@ private:
virtual void ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2);
};
- class AudioPolicyServiceClient: public IBinder::DeathRecipient
+ class AudioPolicyServiceClient: public IBinder::DeathRecipient,
+ public BnAudioPolicyServiceClient
{
public:
AudioPolicyServiceClient() {
@@ -327,6 +344,10 @@ private:
// DeathRecipient
virtual void binderDied(const wp<IBinder>& who);
+
+ // IAudioPolicyServiceClient
+ virtual void onAudioPortListUpdate();
+ virtual void onAudioPatchListUpdate();
};
static sp<AudioFlingerClient> gAudioFlingerClient;
@@ -349,6 +370,8 @@ private:
// list of output descriptors containing cached parameters
// (sampling rate, framecount, channel count...)
static DefaultKeyedVector<audio_io_handle_t, OutputDescriptor *> gOutputs;
+
+ static sp<AudioPortCallback> gAudioPortCallback;
};
}; // namespace android
diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h
index 6b6df6e..d422aa3 100644
--- a/include/media/IAudioPolicyService.h
+++ b/include/media/IAudioPolicyService.h
@@ -25,6 +25,7 @@
#include <utils/Errors.h>
#include <binder/IInterface.h>
#include <media/AudioSystem.h>
+#include <media/IAudioPolicyServiceClient.h>
#include <system/audio_policy.h>
@@ -124,6 +125,7 @@ public:
/* Set audio port configuration */
virtual status_t setAudioPortConfig(const struct audio_port_config *config) = 0;
+ virtual void registerClient(const sp<IAudioPolicyServiceClient>& client) = 0;
};
diff --git a/include/media/IAudioPolicyServiceClient.h b/include/media/IAudioPolicyServiceClient.h
new file mode 100644
index 0000000..59df046
--- /dev/null
+++ b/include/media/IAudioPolicyServiceClient.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 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 ANDROID_IAUDIOPOLICYSERVICECLIENT_H
+#define ANDROID_IAUDIOPOLICYSERVICECLIENT_H
+
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <system/audio.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class IAudioPolicyServiceClient : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(AudioPolicyServiceClient);
+
+ // Notifies a change of audio port configuration.
+ virtual void onAudioPortListUpdate() = 0;
+ // Notifies a change of audio patch configuration.
+ virtual void onAudioPatchListUpdate() = 0;
+};
+
+
+// ----------------------------------------------------------------------------
+
+class BnAudioPolicyServiceClient : public BnInterface<IAudioPolicyServiceClient>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IAUDIOPOLICYSERVICECLIENT_H
diff --git a/media/libcpustats/Android.mk b/media/libcpustats/Android.mk
index b506353..ee283a6 100644
--- a/media/libcpustats/Android.mk
+++ b/media/libcpustats/Android.mk
@@ -1,4 +1,4 @@
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
@@ -8,4 +8,6 @@ LOCAL_SRC_FILES := \
LOCAL_MODULE := libcpustats
+LOCAL_CFLAGS := -std=gnu++11 -Werror
+
include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libcpustats/ThreadCpuUsage.cpp b/media/libcpustats/ThreadCpuUsage.cpp
index 637402a..cfdcb51 100644
--- a/media/libcpustats/ThreadCpuUsage.cpp
+++ b/media/libcpustats/ThreadCpuUsage.cpp
@@ -21,7 +21,6 @@
#include <stdlib.h>
#include <time.h>
-#include <utils/Debug.h>
#include <utils/Log.h>
#include <cpustats/ThreadCpuUsage.h>
@@ -218,7 +217,7 @@ uint32_t ThreadCpuUsage::getCpukHz(int cpuNum)
#define FREQ_SIZE 64
char freq_path[FREQ_SIZE];
#define FREQ_DIGIT 27
- COMPILE_TIME_ASSERT_FUNCTION_SCOPE(MAX_CPU <= 10);
+ static_assert(MAX_CPU <= 10, "MAX_CPU too large");
#define FREQ_PATH "/sys/devices/system/cpu/cpu?/cpufreq/scaling_cur_freq"
strlcpy(freq_path, FREQ_PATH, sizeof(freq_path));
freq_path[FREQ_DIGIT] = cpuNum + '0';
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index f3770e4..69eead3 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -44,6 +44,7 @@ LOCAL_SRC_FILES:= \
JetPlayer.cpp \
IOMX.cpp \
IAudioPolicyService.cpp \
+ IAudioPolicyServiceClient.cpp \
MediaScanner.cpp \
MediaScannerClient.cpp \
CharacterEncodingDetector.cpp \
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 845ee20..eafb3ad 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -45,6 +45,7 @@ audio_format_t AudioSystem::gPrevInFormat;
audio_channel_mask_t AudioSystem::gPrevInChannelMask;
size_t AudioSystem::gInBuffSize = 0; // zero indicates cache is invalid
+sp<AudioSystem::AudioPortCallback> AudioSystem::gAudioPortCallback;
// establish binder interface to AudioFlinger service
const sp<IAudioFlinger>& AudioSystem::get_audio_flinger()
@@ -528,6 +529,7 @@ void AudioSystem::setErrorCallback(audio_error_callback cb)
gAudioErrorCallback = cb;
}
+
bool AudioSystem::routedToA2dpOutput(audio_stream_type_t streamType)
{
switch (streamType) {
@@ -566,6 +568,7 @@ const sp<IAudioPolicyService>& AudioSystem::get_audio_policy_service()
}
binder->linkToDeath(gAudioPolicyServiceClient);
gAudioPolicyService = interface_cast<IAudioPolicyService>(binder);
+ gAudioPolicyService->registerClient(gAudioPolicyServiceClient);
gLock.unlock();
} else {
gLock.unlock();
@@ -880,14 +883,39 @@ status_t AudioSystem::setAudioPortConfig(const struct audio_port_config *config)
return aps->setAudioPortConfig(config);
}
+void AudioSystem::setAudioPortCallback(sp<AudioPortCallback> callBack)
+{
+ Mutex::Autolock _l(gLock);
+ gAudioPortCallback = callBack;
+}
+
// ---------------------------------------------------------------------------
void AudioSystem::AudioPolicyServiceClient::binderDied(const wp<IBinder>& who __unused)
{
- Mutex::Autolock _l(AudioSystem::gLock);
+ Mutex::Autolock _l(gLock);
+ if (gAudioPortCallback != 0) {
+ gAudioPortCallback->onServiceDied();
+ }
AudioSystem::gAudioPolicyService.clear();
ALOGW("AudioPolicyService server died!");
}
+void AudioSystem::AudioPolicyServiceClient::onAudioPortListUpdate()
+{
+ Mutex::Autolock _l(gLock);
+ if (gAudioPortCallback != 0) {
+ gAudioPortCallback->onAudioPortListUpdate();
+ }
+}
+
+void AudioSystem::AudioPolicyServiceClient::onAudioPatchListUpdate()
+{
+ Mutex::Autolock _l(gLock);
+ if (gAudioPortCallback != 0) {
+ gAudioPortCallback->onAudioPatchListUpdate();
+ }
+}
+
}; // namespace android
diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp
index ad2d4eb..eee72c5 100644
--- a/media/libmedia/IAudioPolicyService.cpp
+++ b/media/libmedia/IAudioPolicyService.cpp
@@ -63,7 +63,8 @@ enum {
CREATE_AUDIO_PATCH,
RELEASE_AUDIO_PATCH,
LIST_AUDIO_PATCHES,
- SET_AUDIO_PORT_CONFIG
+ SET_AUDIO_PORT_CONFIG,
+ REGISTER_CLIENT
};
class BpAudioPolicyService : public BpInterface<IAudioPolicyService>
@@ -524,6 +525,13 @@ public:
}
return status;
}
+ virtual void registerClient(const sp<IAudioPolicyServiceClient>& client)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+ data.writeStrongBinder(client->asBinder());
+ remote()->transact(REGISTER_CLIENT, data, &reply);
+ }
};
IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService");
@@ -910,6 +918,13 @@ status_t BnAudioPolicyService::onTransact(
reply->writeInt32(status);
return NO_ERROR;
}
+ case REGISTER_CLIENT: {
+ CHECK_INTERFACE(IAudioPolicyService, data, reply);
+ sp<IAudioPolicyServiceClient> client = interface_cast<IAudioPolicyServiceClient>(
+ data.readStrongBinder());
+ registerClient(client);
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
diff --git a/media/libmedia/IAudioPolicyServiceClient.cpp b/media/libmedia/IAudioPolicyServiceClient.cpp
new file mode 100644
index 0000000..e802277
--- /dev/null
+++ b/media/libmedia/IAudioPolicyServiceClient.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2009 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 "IAudioPolicyServiceClient"
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/Parcel.h>
+
+#include <media/IAudioPolicyServiceClient.h>
+#include <media/AudioSystem.h>
+
+namespace android {
+
+enum {
+ PORT_LIST_UPDATE = IBinder::FIRST_CALL_TRANSACTION,
+ PATCH_LIST_UPDATE
+};
+
+class BpAudioPolicyServiceClient : public BpInterface<IAudioPolicyServiceClient>
+{
+public:
+ BpAudioPolicyServiceClient(const sp<IBinder>& impl)
+ : BpInterface<IAudioPolicyServiceClient>(impl)
+ {
+ }
+
+ void onAudioPortListUpdate()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor());
+ remote()->transact(PORT_LIST_UPDATE, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+
+ void onAudioPatchListUpdate()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioPolicyServiceClient::getInterfaceDescriptor());
+ remote()->transact(PATCH_LIST_UPDATE, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(AudioPolicyServiceClient, "android.media.IAudioPolicyServiceClient");
+
+// ----------------------------------------------------------------------
+
+status_t BnAudioPolicyServiceClient::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch (code) {
+ case PORT_LIST_UPDATE: {
+ CHECK_INTERFACE(IAudioPolicyServiceClient, data, reply);
+ onAudioPortListUpdate();
+ return NO_ERROR;
+ } break;
+ case PATCH_LIST_UPDATE: {
+ CHECK_INTERFACE(IAudioPolicyServiceClient, data, reply);
+ onAudioPatchListUpdate();
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/media/libstagefright/codecs/aacdec/Android.mk b/media/libstagefright/codecs/aacdec/Android.mk
index 49ff238..afb00aa 100644
--- a/media/libstagefright/codecs/aacdec/Android.mk
+++ b/media/libstagefright/codecs/aacdec/Android.mk
@@ -3,7 +3,8 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
- SoftAAC2.cpp
+ SoftAAC2.cpp \
+ DrcPresModeWrap.cpp
LOCAL_C_INCLUDES := \
frameworks/av/media/libstagefright/include \
diff --git a/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp
new file mode 100644
index 0000000..129ad65
--- /dev/null
+++ b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.cpp
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#include "DrcPresModeWrap.h"
+
+#include <assert.h>
+
+#define LOG_TAG "SoftAAC2_DrcWrapper"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+//#define DRC_PRES_MODE_WRAP_DEBUG
+
+#define GPM_ENCODER_TARGET_LEVEL 64
+#define MAX_TARGET_LEVEL 64
+
+CDrcPresModeWrapper::CDrcPresModeWrapper()
+{
+ mDataUpdate = true;
+
+ /* Data from streamInfo. */
+ /* Initialized to the same values as in the aac decoder */
+ mStreamPRL = -1;
+ mStreamDRCPresMode = -1;
+ mStreamNrAACChan = 0;
+ mStreamNrOutChan = 0;
+
+ /* Desired values (set by user). */
+ /* Initialized to the same values as in the aac decoder */
+ mDesTarget = -1;
+ mDesAttFactor = 0;
+ mDesBoostFactor = 0;
+ mDesHeavy = 0;
+
+ mEncoderTarget = -1;
+
+ /* Values from last time. */
+ /* Initialized to the same values as the desired values */
+ mLastTarget = -1;
+ mLastAttFactor = 0;
+ mLastBoostFactor = 0;
+ mLastHeavy = 0;
+}
+
+CDrcPresModeWrapper::~CDrcPresModeWrapper()
+{
+}
+
+void
+CDrcPresModeWrapper::setDecoderHandle(const HANDLE_AACDECODER handle)
+{
+ mHandleDecoder = handle;
+}
+
+void
+CDrcPresModeWrapper::submitStreamData(CStreamInfo* pStreamInfo)
+{
+ assert(pStreamInfo);
+
+ if (mStreamPRL != pStreamInfo->drcProgRefLev) {
+ mStreamPRL = pStreamInfo->drcProgRefLev;
+ mDataUpdate = true;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+ ALOGV("DRC presentation mode wrapper: drcProgRefLev is %d\n", mStreamPRL);
+#endif
+ }
+
+ if (mStreamDRCPresMode != pStreamInfo->drcPresMode) {
+ mStreamDRCPresMode = pStreamInfo->drcPresMode;
+ mDataUpdate = true;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+ ALOGV("DRC presentation mode wrapper: drcPresMode is %d\n", mStreamDRCPresMode);
+#endif
+ }
+
+ if (mStreamNrAACChan != pStreamInfo->aacNumChannels) {
+ mStreamNrAACChan = pStreamInfo->aacNumChannels;
+ mDataUpdate = true;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+ ALOGV("DRC presentation mode wrapper: aacNumChannels is %d\n", mStreamNrAACChan);
+#endif
+ }
+
+ if (mStreamNrOutChan != pStreamInfo->numChannels) {
+ mStreamNrOutChan = pStreamInfo->numChannels;
+ mDataUpdate = true;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+ ALOGV("DRC presentation mode wrapper: numChannels is %d\n", mStreamNrOutChan);
+#endif
+ }
+
+
+
+ if (mStreamNrOutChan<mStreamNrAACChan) {
+ mIsDownmix = true;
+ } else {
+ mIsDownmix = false;
+ }
+
+ if (mIsDownmix && (mStreamNrOutChan == 1)) {
+ mIsMonoDownmix = true;
+ } else {
+ mIsMonoDownmix = false;
+ }
+
+ if (mIsDownmix && mStreamNrOutChan == 2){
+ mIsStereoDownmix = true;
+ } else {
+ mIsStereoDownmix = false;
+ }
+
+}
+
+void
+CDrcPresModeWrapper::setParam(const DRC_PRES_MODE_WRAP_PARAM param, const int value)
+{
+ switch (param) {
+ case DRC_PRES_MODE_WRAP_DESIRED_TARGET:
+ mDesTarget = value;
+ break;
+ case DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR:
+ mDesAttFactor = value;
+ break;
+ case DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR:
+ mDesBoostFactor = value;
+ break;
+ case DRC_PRES_MODE_WRAP_DESIRED_HEAVY:
+ mDesHeavy = value;
+ break;
+ case DRC_PRES_MODE_WRAP_ENCODER_TARGET:
+ mEncoderTarget = value;
+ break;
+ default:
+ break;
+ }
+ mDataUpdate = true;
+}
+
+void
+CDrcPresModeWrapper::update()
+{
+ // Get Data from Decoder
+ int progRefLevel = mStreamPRL;
+ int drcPresMode = mStreamDRCPresMode;
+
+ // by default, do as desired
+ int newTarget = mDesTarget;
+ int newAttFactor = mDesAttFactor;
+ int newBoostFactor = mDesBoostFactor;
+ int newHeavy = mDesHeavy;
+
+ if (mDataUpdate) {
+ // sanity check
+ if (mDesTarget < MAX_TARGET_LEVEL){
+ mDesTarget = MAX_TARGET_LEVEL; // limit target level to -16 dB or below
+ newTarget = MAX_TARGET_LEVEL;
+ }
+
+ if (mEncoderTarget != -1) {
+ if (mDesTarget<124) { // if target level > -31 dB
+ if ((mIsStereoDownmix == false) && (mIsMonoDownmix == false)) {
+ // no stereo or mono downmixing, calculated scaling of light DRC
+ /* use as little compression as possible */
+ newAttFactor = 0;
+ newBoostFactor = 0;
+ if (mDesTarget<progRefLevel) { // if target level > PRL
+ if (mEncoderTarget < mDesTarget) { // if mEncoderTarget > target level
+ // mEncoderTarget > target level > PRL
+ int calcFactor;
+ float calcFactor_norm;
+ // 0.0f < calcFactor_norm < 1.0f
+ calcFactor_norm = (float)(mDesTarget - progRefLevel) /
+ (float)(mEncoderTarget - progRefLevel);
+ calcFactor = (int)(calcFactor_norm*127.0f); // 0 <= calcFactor < 127
+ // calcFactor is the lower limit
+ newAttFactor = (calcFactor>newAttFactor) ? calcFactor : newAttFactor;
+ // new AttFactor will be always = calcFactor, as it is set to 0 before.
+ newBoostFactor = newAttFactor;
+ } else {
+ /* target level > mEncoderTarget > PRL */
+ // newTDLimiterEnable = 1;
+ // the time domain limiter must always be active in this case.
+ // It is assumed that the framework activates it by default
+ newAttFactor = 127;
+ newBoostFactor = 127;
+ }
+ } else { // target level <= PRL
+ // no restrictions required
+ // newAttFactor = newAttFactor;
+ }
+ } else { // downmixing
+ // if target level > -23 dB or mono downmix
+ if ( (mDesTarget<92) || mIsMonoDownmix ) {
+ newHeavy = 1;
+ } else {
+ // we perform a downmix, so, we need at least full light DRC
+ newAttFactor = 127;
+ }
+ }
+ } else { // target level <= -31 dB
+ // playback -31 dB: light DRC only needed if we perform downmixing
+ if (mIsDownmix) { // we do downmixing
+ newAttFactor = 127;
+ }
+ }
+ }
+ else { // handle other used encoder target levels
+
+ // Sanity check: DRC presentation mode is only specified for max. 5.1 channels
+ if (mStreamNrAACChan > 6) {
+ drcPresMode = 0;
+ }
+
+ switch (drcPresMode) {
+ case 0:
+ default: // presentation mode not indicated
+ {
+
+ if (mDesTarget<124) { // if target level > -31 dB
+ // no stereo or mono downmixing
+ if ((mIsStereoDownmix == false) && (mIsMonoDownmix == false)) {
+ if (mDesTarget<progRefLevel) { // if target level > PRL
+ // newTDLimiterEnable = 1;
+ // the time domain limiter must always be active in this case.
+ // It is assumed that the framework activates it by default
+ newAttFactor = 127; // at least, use light compression
+ } else { // target level <= PRL
+ // no restrictions required
+ // newAttFactor = newAttFactor;
+ }
+ } else { // downmixing
+ // newTDLimiterEnable = 1;
+ // the time domain limiter must always be active in this case.
+ // It is assumed that the framework activates it by default
+
+ // if target level > -23 dB or mono downmix
+ if ( (mDesTarget < 92) || mIsMonoDownmix ) {
+ newHeavy = 1;
+ } else{
+ // we perform a downmix, so, we need at least full light DRC
+ newAttFactor = 127;
+ }
+ }
+ } else { // target level <= -31 dB
+ if (mIsDownmix) { // we do downmixing.
+ // newTDLimiterEnable = 1;
+ // the time domain limiter must always be active in this case.
+ // It is assumed that the framework activates it by default
+ newAttFactor = 127;
+ }
+ }
+ }
+ break;
+
+ // Presentation mode 1 and 2 according to ETSI TS 101 154:
+ // Digital Video Broadcasting (DVB); Specification for the use of Video and Audio Coding
+ // in Broadcasting Applications based on the MPEG-2 Transport Stream,
+ // section C.5.4., "Decoding", and Table C.33
+ // ISO DRC -> newHeavy = 0 (Use light compression, MPEG-style)
+ // Compression_value -> newHeavy = 1 (Use heavy compression, DVB-style)
+ // scaling restricted -> newAttFactor = 127
+
+ case 1: // presentation mode 1, Light:-31/Heavy:-23
+ {
+ if (mDesTarget < 124) { // if target level > -31 dB
+ // playback up to -23 dB
+ newHeavy = 1;
+ } else { // target level <= -31 dB
+ // playback -31 dB
+ if (mIsDownmix) { // we do downmixing.
+ newAttFactor = 127;
+ }
+ }
+ }
+ break;
+
+ case 2: // presentation mode 2, Light:-23/Heavy:-23
+ {
+ if (mDesTarget < 124) { // if target level > -31 dB
+ // playback up to -23 dB
+ if (mIsMonoDownmix) { // if mono downmix
+ newHeavy = 1;
+ } else {
+ newHeavy = 0;
+ newAttFactor = 127;
+ }
+ } else { // target level <= -31 dB
+ // playback -31 dB
+ newHeavy = 0;
+ if (mIsDownmix) { // we do downmixing.
+ newAttFactor = 127;
+ }
+ }
+ }
+ break;
+
+ } // switch()
+ } // if (mEncoderTarget == GPM_ENCODER_TARGET_LEVEL)
+
+ // sanity again
+ if (newHeavy == 1) {
+ newBoostFactor=127; // not really needed as the same would be done by the decoder anyway
+ newAttFactor = 127;
+ }
+
+ // update the decoder
+ if (newTarget != mLastTarget) {
+ aacDecoder_SetParam(mHandleDecoder, AAC_DRC_REFERENCE_LEVEL, newTarget);
+ mLastTarget = newTarget;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+ if (newTarget != mDesTarget)
+ ALOGV("DRC presentation mode wrapper: forced target level to %d (from %d)\n", newTarget, mDesTarget);
+ else
+ ALOGV("DRC presentation mode wrapper: set target level to %d\n", newTarget);
+#endif
+ }
+
+ if (newAttFactor != mLastAttFactor) {
+ aacDecoder_SetParam(mHandleDecoder, AAC_DRC_ATTENUATION_FACTOR, newAttFactor);
+ mLastAttFactor = newAttFactor;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+ if (newAttFactor != mDesAttFactor)
+ ALOGV("DRC presentation mode wrapper: forced attenuation factor to %d (from %d)\n", newAttFactor, mDesAttFactor);
+ else
+ ALOGV("DRC presentation mode wrapper: set attenuation factor to %d\n", newAttFactor);
+#endif
+ }
+
+ if (newBoostFactor != mLastBoostFactor) {
+ aacDecoder_SetParam(mHandleDecoder, AAC_DRC_BOOST_FACTOR, newBoostFactor);
+ mLastBoostFactor = newBoostFactor;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+ if (newBoostFactor != mDesBoostFactor)
+ ALOGV("DRC presentation mode wrapper: forced boost factor to %d (from %d)\n",
+ newBoostFactor, mDesBoostFactor);
+ else
+ ALOGV("DRC presentation mode wrapper: set boost factor to %d\n", newBoostFactor);
+#endif
+ }
+
+ if (newHeavy != mLastHeavy) {
+ aacDecoder_SetParam(mHandleDecoder, AAC_DRC_HEAVY_COMPRESSION, newHeavy);
+ mLastHeavy = newHeavy;
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+ if (newHeavy != mDesHeavy)
+ ALOGV("DRC presentation mode wrapper: forced heavy compression to %d (from %d)\n",
+ newHeavy, mDesHeavy);
+ else
+ ALOGV("DRC presentation mode wrapper: set heavy compression to %d\n", newHeavy);
+#endif
+ }
+
+#ifdef DRC_PRES_MODE_WRAP_DEBUG
+ ALOGV("DRC config: tgt_lev: %3d, cut: %3d, boost: %3d, heavy: %d\n", newTarget,
+ newAttFactor, newBoostFactor, newHeavy);
+#endif
+ mDataUpdate = false;
+
+ } // if (mDataUpdate)
+}
diff --git a/media/libstagefright/codecs/aacdec/DrcPresModeWrap.h b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.h
new file mode 100644
index 0000000..f0b6cf2
--- /dev/null
+++ b/media/libstagefright/codecs/aacdec/DrcPresModeWrap.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#pragma once
+#include "aacdecoder_lib.h"
+
+typedef enum
+{
+ DRC_PRES_MODE_WRAP_DESIRED_TARGET = 0x0000,
+ DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR = 0x0001,
+ DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR = 0x0002,
+ DRC_PRES_MODE_WRAP_DESIRED_HEAVY = 0x0003,
+ DRC_PRES_MODE_WRAP_ENCODER_TARGET = 0x0004
+} DRC_PRES_MODE_WRAP_PARAM;
+
+
+class CDrcPresModeWrapper {
+public:
+ CDrcPresModeWrapper();
+ ~CDrcPresModeWrapper();
+ void setDecoderHandle(const HANDLE_AACDECODER handle);
+ void setParam(const DRC_PRES_MODE_WRAP_PARAM param, const int value);
+ void submitStreamData(CStreamInfo*);
+ void update();
+
+protected:
+ HANDLE_AACDECODER mHandleDecoder;
+ int mDesTarget;
+ int mDesAttFactor;
+ int mDesBoostFactor;
+ int mDesHeavy;
+
+ int mEncoderTarget;
+
+ int mLastTarget;
+ int mLastAttFactor;
+ int mLastBoostFactor;
+ int mLastHeavy;
+
+ SCHAR mStreamPRL;
+ SCHAR mStreamDRCPresMode;
+ INT mStreamNrAACChan;
+ INT mStreamNrOutChan;
+
+ bool mIsDownmix;
+ bool mIsMonoDownmix;
+ bool mIsStereoDownmix;
+
+ bool mDataUpdate;
+};
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index 532e36f..a0e3265 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -25,16 +25,22 @@
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/MediaErrors.h>
+#include <math.h>
+
#define FILEREAD_MAX_LAYERS 2
#define DRC_DEFAULT_MOBILE_REF_LEVEL 64 /* 64*-0.25dB = -16 dB below full scale for mobile conf */
#define DRC_DEFAULT_MOBILE_DRC_CUT 127 /* maximum compression of dynamic range for mobile conf */
#define DRC_DEFAULT_MOBILE_DRC_BOOST 127 /* maximum compression of dynamic range for mobile conf */
+#define DRC_DEFAULT_MOBILE_DRC_HEAVY 1 /* switch for heavy compression for mobile conf */
+#define DRC_DEFAULT_MOBILE_ENC_LEVEL -1 /* encoder target level; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */
#define MAX_CHANNEL_COUNT 8 /* maximum number of audio channels that can be decoded */
// names of properties that can be used to override the default DRC settings
#define PROP_DRC_OVERRIDE_REF_LEVEL "aac_drc_reference_level"
#define PROP_DRC_OVERRIDE_CUT "aac_drc_cut"
#define PROP_DRC_OVERRIDE_BOOST "aac_drc_boost"
+#define PROP_DRC_OVERRIDE_HEAVY "aac_drc_heavy"
+#define PROP_DRC_OVERRIDE_ENC_LEVEL "aac_drc_enc_target_level"
namespace android {
@@ -57,18 +63,19 @@ SoftAAC2::SoftAAC2(
mStreamInfo(NULL),
mIsADTS(false),
mInputBufferCount(0),
+ mOutputBufferCount(0),
mSignalledError(false),
- mSawInputEos(false),
- mSignalledOutputEos(false),
- mAnchorTimeUs(0),
- mNumSamplesOutput(0),
mOutputPortSettingsChange(NONE) {
+ for (unsigned int i = 0; i < kNumDelayBlocksMax; i++) {
+ mAnchorTimeUs[i] = 0;
+ }
initPorts();
CHECK_EQ(initDecoder(), (status_t)OK);
}
SoftAAC2::~SoftAAC2() {
aacDecoder_Close(mAACDecoder);
+ delete mOutputDelayRingBuffer;
}
void SoftAAC2::initPorts() {
@@ -121,36 +128,72 @@ status_t SoftAAC2::initDecoder() {
status = OK;
}
}
- mDecoderHasData = false;
- // for streams that contain metadata, use the mobile profile DRC settings unless overridden
- // by platform properties:
+ mEndOfInput = false;
+ mEndOfOutput = false;
+ mOutputDelayCompensated = 0;
+ mOutputDelayRingBufferSize = 2048 * MAX_CHANNEL_COUNT * kNumDelayBlocksMax;
+ mOutputDelayRingBuffer = new short[mOutputDelayRingBufferSize];
+ mOutputDelayRingBufferWritePos = 0;
+ mOutputDelayRingBufferReadPos = 0;
+
+ if (mAACDecoder == NULL) {
+ ALOGE("AAC decoder is null. TODO: Can not call aacDecoder_SetParam in the following code");
+ }
+
+ //aacDecoder_SetParam(mAACDecoder, AAC_PCM_LIMITER_ENABLE, 0);
+
+ //init DRC wrapper
+ mDrcWrap.setDecoderHandle(mAACDecoder);
+ mDrcWrap.submitStreamData(mStreamInfo);
+
+ // for streams that contain metadata, use the mobile profile DRC settings unless overridden by platform properties
+ // TODO: change the DRC settings depending on audio output device type (HDMI, loadspeaker, headphone)
char value[PROPERTY_VALUE_MAX];
- // * AAC_DRC_REFERENCE_LEVEL
+ // DRC_PRES_MODE_WRAP_DESIRED_TARGET
if (property_get(PROP_DRC_OVERRIDE_REF_LEVEL, value, NULL)) {
unsigned refLevel = atoi(value);
- ALOGV("AAC decoder using AAC_DRC_REFERENCE_LEVEL of %d instead of %d",
- refLevel, DRC_DEFAULT_MOBILE_REF_LEVEL);
- aacDecoder_SetParam(mAACDecoder, AAC_DRC_REFERENCE_LEVEL, refLevel);
+ ALOGV("AAC decoder using desired DRC target reference level of %d instead of %d", refLevel,
+ DRC_DEFAULT_MOBILE_REF_LEVEL);
+ mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_TARGET, refLevel);
} else {
- aacDecoder_SetParam(mAACDecoder, AAC_DRC_REFERENCE_LEVEL, DRC_DEFAULT_MOBILE_REF_LEVEL);
+ mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_TARGET, DRC_DEFAULT_MOBILE_REF_LEVEL);
}
- // * AAC_DRC_ATTENUATION_FACTOR
+ // DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR
if (property_get(PROP_DRC_OVERRIDE_CUT, value, NULL)) {
unsigned cut = atoi(value);
- ALOGV("AAC decoder using AAC_DRC_ATTENUATION_FACTOR of %d instead of %d",
- cut, DRC_DEFAULT_MOBILE_DRC_CUT);
- aacDecoder_SetParam(mAACDecoder, AAC_DRC_ATTENUATION_FACTOR, cut);
+ ALOGV("AAC decoder using desired DRC attenuation factor of %d instead of %d", cut,
+ DRC_DEFAULT_MOBILE_DRC_CUT);
+ mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR, cut);
} else {
- aacDecoder_SetParam(mAACDecoder, AAC_DRC_ATTENUATION_FACTOR, DRC_DEFAULT_MOBILE_DRC_CUT);
+ mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR, DRC_DEFAULT_MOBILE_DRC_CUT);
}
- // * AAC_DRC_BOOST_FACTOR (note: no default, using cut)
+ // DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR
if (property_get(PROP_DRC_OVERRIDE_BOOST, value, NULL)) {
unsigned boost = atoi(value);
- ALOGV("AAC decoder using AAC_DRC_BOOST_FACTOR of %d", boost);
- aacDecoder_SetParam(mAACDecoder, AAC_DRC_BOOST_FACTOR, boost);
+ ALOGV("AAC decoder using desired DRC boost factor of %d instead of %d", boost,
+ DRC_DEFAULT_MOBILE_DRC_BOOST);
+ mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR, boost);
+ } else {
+ mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR, DRC_DEFAULT_MOBILE_DRC_BOOST);
+ }
+ // DRC_PRES_MODE_WRAP_DESIRED_HEAVY
+ if (property_get(PROP_DRC_OVERRIDE_HEAVY, value, NULL)) {
+ unsigned heavy = atoi(value);
+ ALOGV("AAC decoder using desried DRC heavy compression switch of %d instead of %d", heavy,
+ DRC_DEFAULT_MOBILE_DRC_HEAVY);
+ mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_HEAVY, heavy);
} else {
- aacDecoder_SetParam(mAACDecoder, AAC_DRC_BOOST_FACTOR, DRC_DEFAULT_MOBILE_DRC_BOOST);
+ mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_HEAVY, DRC_DEFAULT_MOBILE_DRC_HEAVY);
+ }
+ // DRC_PRES_MODE_WRAP_ENCODER_TARGET
+ if (property_get(PROP_DRC_OVERRIDE_ENC_LEVEL, value, NULL)) {
+ unsigned encoderRefLevel = atoi(value);
+ ALOGV("AAC decoder using encoder-side DRC reference level of %d instead of %d",
+ encoderRefLevel, DRC_DEFAULT_MOBILE_ENC_LEVEL);
+ mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET, encoderRefLevel);
+ } else {
+ mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET, DRC_DEFAULT_MOBILE_ENC_LEVEL);
}
return status;
@@ -290,19 +333,101 @@ bool SoftAAC2::isConfigured() const {
return mInputBufferCount > 0;
}
-void SoftAAC2::maybeConfigureDownmix() const {
- if (mStreamInfo->numChannels > 2) {
- char value[PROPERTY_VALUE_MAX];
- if (!(property_get("media.aac_51_output_enabled", value, NULL) &&
- (!strcmp(value, "1") || !strcasecmp(value, "true")))) {
- ALOGI("Downmixing multichannel AAC to stereo");
- aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, 2);
- mStreamInfo->numChannels = 2;
- // By default, the decoder creates a 5.1 channel downmix signal
- // for seven and eight channel input streams. To enable 6.1 and 7.1 channel output
- // use aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, -1)
+void SoftAAC2::configureDownmix() const {
+ char value[PROPERTY_VALUE_MAX];
+ if (!(property_get("media.aac_51_output_enabled", value, NULL)
+ && (!strcmp(value, "1") || !strcasecmp(value, "true")))) {
+ ALOGI("limiting to stereo output");
+ aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, 2);
+ // By default, the decoder creates a 5.1 channel downmix signal
+ // for seven and eight channel input streams. To enable 6.1 and 7.1 channel output
+ // use aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, -1)
+ }
+}
+
+bool SoftAAC2::outputDelayRingBufferPutSamples(INT_PCM *samples, int32_t numSamples) {
+ if (mOutputDelayRingBufferWritePos + numSamples <= mOutputDelayRingBufferSize
+ && (mOutputDelayRingBufferReadPos <= mOutputDelayRingBufferWritePos
+ || mOutputDelayRingBufferReadPos > mOutputDelayRingBufferWritePos + numSamples)) {
+ // faster memcopy loop without checks, if the preconditions allow this
+ for (int32_t i = 0; i < numSamples; i++) {
+ mOutputDelayRingBuffer[mOutputDelayRingBufferWritePos++] = samples[i];
+ }
+
+ if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) {
+ mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize;
+ }
+ if (mOutputDelayRingBufferWritePos == mOutputDelayRingBufferReadPos) {
+ ALOGE("RING BUFFER OVERFLOW");
+ return false;
+ }
+ } else {
+ ALOGV("slow SoftAAC2::outputDelayRingBufferPutSamples()");
+
+ for (int32_t i = 0; i < numSamples; i++) {
+ mOutputDelayRingBuffer[mOutputDelayRingBufferWritePos] = samples[i];
+ mOutputDelayRingBufferWritePos++;
+ if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) {
+ mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize;
+ }
+ if (mOutputDelayRingBufferWritePos == mOutputDelayRingBufferReadPos) {
+ ALOGE("RING BUFFER OVERFLOW");
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+int32_t SoftAAC2::outputDelayRingBufferGetSamples(INT_PCM *samples, int32_t numSamples) {
+ if (mOutputDelayRingBufferReadPos + numSamples <= mOutputDelayRingBufferSize
+ && (mOutputDelayRingBufferWritePos < mOutputDelayRingBufferReadPos
+ || mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferReadPos + numSamples)) {
+ // faster memcopy loop without checks, if the preconditions allow this
+ if (samples != 0) {
+ for (int32_t i = 0; i < numSamples; i++) {
+ samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos++];
+ }
+ } else {
+ mOutputDelayRingBufferReadPos += numSamples;
+ }
+ if (mOutputDelayRingBufferReadPos >= mOutputDelayRingBufferSize) {
+ mOutputDelayRingBufferReadPos -= mOutputDelayRingBufferSize;
+ }
+ } else {
+ ALOGV("slow SoftAAC2::outputDelayRingBufferGetSamples()");
+
+ for (int32_t i = 0; i < numSamples; i++) {
+ if (mOutputDelayRingBufferWritePos == mOutputDelayRingBufferReadPos) {
+ ALOGE("RING BUFFER UNDERRUN");
+ return -1;
+ }
+ if (samples != 0) {
+ samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos];
+ }
+ mOutputDelayRingBufferReadPos++;
+ if (mOutputDelayRingBufferReadPos >= mOutputDelayRingBufferSize) {
+ mOutputDelayRingBufferReadPos -= mOutputDelayRingBufferSize;
+ }
}
}
+ return numSamples;
+}
+
+int32_t SoftAAC2::outputDelayRingBufferSamplesAvailable() {
+ int32_t available = mOutputDelayRingBufferWritePos - mOutputDelayRingBufferReadPos;
+ if (available < 0) {
+ available += mOutputDelayRingBufferSize;
+ }
+ if (available < 0) {
+ ALOGE("FATAL RING BUFFER ERROR");
+ return 0;
+ }
+ return available;
+}
+
+int32_t SoftAAC2::outputDelayRingBufferSamplesLeft() {
+ return mOutputDelayRingBufferSize - outputDelayRingBufferSamplesAvailable();
}
void SoftAAC2::onQueueFilled(OMX_U32 portIndex) {
@@ -318,12 +443,11 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) {
List<BufferInfo *> &outQueue = getPortQueue(1);
if (portIndex == 0 && mInputBufferCount == 0) {
- ++mInputBufferCount;
- BufferInfo *info = *inQueue.begin();
- OMX_BUFFERHEADERTYPE *header = info->mHeader;
+ BufferInfo *inInfo = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
- inBuffer[0] = header->pBuffer + header->nOffset;
- inBufferLength[0] = header->nFilledLen;
+ inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
+ inBufferLength[0] = inHeader->nFilledLen;
AAC_DECODER_ERROR decoderErr =
aacDecoder_ConfigRaw(mAACDecoder,
@@ -331,19 +455,25 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) {
inBufferLength);
if (decoderErr != AAC_DEC_OK) {
+ ALOGW("aacDecoder_ConfigRaw decoderErr = 0x%4.4x", decoderErr);
mSignalledError = true;
notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
return;
}
+ mInputBufferCount++;
+ mOutputBufferCount++; // fake increase of outputBufferCount to keep the counters aligned
+
+ inInfo->mOwnedByUs = false;
inQueue.erase(inQueue.begin());
- info->mOwnedByUs = false;
- notifyEmptyBufferDone(header);
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ configureDownmix();
// Only send out port settings changed event if both sample rate
// and numChannels are valid.
if (mStreamInfo->sampleRate && mStreamInfo->numChannels) {
- maybeConfigureDownmix();
ALOGI("Initially configuring decoder: %d Hz, %d channels",
mStreamInfo->sampleRate,
mStreamInfo->numChannels);
@@ -355,202 +485,304 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) {
return;
}
- while ((!inQueue.empty() || (mSawInputEos && !mSignalledOutputEos)) && !outQueue.empty()) {
- BufferInfo *inInfo = NULL;
- OMX_BUFFERHEADERTYPE *inHeader = NULL;
+ while ((!inQueue.empty() || mEndOfInput) && !outQueue.empty()) {
if (!inQueue.empty()) {
- inInfo = *inQueue.begin();
- inHeader = inInfo->mHeader;
- }
+ INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT];
+ BufferInfo *inInfo = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
- BufferInfo *outInfo = *outQueue.begin();
- OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
- outHeader->nFlags = 0;
-
- if (inHeader) {
if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
- mSawInputEos = true;
+ mEndOfInput = true;
+ } else {
+ mEndOfInput = false;
}
-
- if (inHeader->nOffset == 0 && inHeader->nFilledLen) {
- mAnchorTimeUs = inHeader->nTimeStamp;
- mNumSamplesOutput = 0;
+ if (inHeader->nOffset == 0) { // TODO: does nOffset != 0 happen?
+ mAnchorTimeUs[mInputBufferCount % kNumDelayBlocksMax] =
+ inHeader->nTimeStamp;
}
- if (mIsADTS && inHeader->nFilledLen) {
- size_t adtsHeaderSize = 0;
- // skip 30 bits, aac_frame_length follows.
- // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll?????
+ if (inHeader->nFilledLen == 0) {
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ } else {
+ if (mIsADTS) {
+ size_t adtsHeaderSize = 0;
+ // skip 30 bits, aac_frame_length follows.
+ // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll?????
- const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset;
+ const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset;
- bool signalError = false;
- if (inHeader->nFilledLen < 7) {
- ALOGE("Audio data too short to contain even the ADTS header. "
- "Got %d bytes.", inHeader->nFilledLen);
- hexdump(adtsHeader, inHeader->nFilledLen);
- signalError = true;
- } else {
- bool protectionAbsent = (adtsHeader[1] & 1);
-
- unsigned aac_frame_length =
- ((adtsHeader[3] & 3) << 11)
- | (adtsHeader[4] << 3)
- | (adtsHeader[5] >> 5);
-
- if (inHeader->nFilledLen < aac_frame_length) {
- ALOGE("Not enough audio data for the complete frame. "
- "Got %d bytes, frame size according to the ADTS "
- "header is %u bytes.",
- inHeader->nFilledLen, aac_frame_length);
+ bool signalError = false;
+ if (inHeader->nFilledLen < 7) {
+ ALOGE("Audio data too short to contain even the ADTS header. "
+ "Got %d bytes.", inHeader->nFilledLen);
hexdump(adtsHeader, inHeader->nFilledLen);
signalError = true;
} else {
- adtsHeaderSize = (protectionAbsent ? 7 : 9);
+ bool protectionAbsent = (adtsHeader[1] & 1);
+
+ unsigned aac_frame_length =
+ ((adtsHeader[3] & 3) << 11)
+ | (adtsHeader[4] << 3)
+ | (adtsHeader[5] >> 5);
+
+ if (inHeader->nFilledLen < aac_frame_length) {
+ ALOGE("Not enough audio data for the complete frame. "
+ "Got %d bytes, frame size according to the ADTS "
+ "header is %u bytes.",
+ inHeader->nFilledLen, aac_frame_length);
+ hexdump(adtsHeader, inHeader->nFilledLen);
+ signalError = true;
+ } else {
+ adtsHeaderSize = (protectionAbsent ? 7 : 9);
+
+ inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize;
+ inBufferLength[0] = aac_frame_length - adtsHeaderSize;
+
+ inHeader->nOffset += adtsHeaderSize;
+ inHeader->nFilledLen -= adtsHeaderSize;
+ }
+ }
- inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize;
- inBufferLength[0] = aac_frame_length - adtsHeaderSize;
+ if (signalError) {
+ mSignalledError = true;
- inHeader->nOffset += adtsHeaderSize;
- inHeader->nFilledLen -= adtsHeaderSize;
+ notify(OMX_EventError,
+ OMX_ErrorStreamCorrupt,
+ ERROR_MALFORMED,
+ NULL);
+
+ return;
}
+ } else {
+ inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
+ inBufferLength[0] = inHeader->nFilledLen;
}
- if (signalError) {
- mSignalledError = true;
+ // Fill and decode
+ bytesValid[0] = inBufferLength[0];
+
+ INT prevSampleRate = mStreamInfo->sampleRate;
+ INT prevNumChannels = mStreamInfo->numChannels;
+
+ aacDecoder_Fill(mAACDecoder,
+ inBuffer,
+ inBufferLength,
+ bytesValid);
+
+ // run DRC check
+ mDrcWrap.submitStreamData(mStreamInfo);
+ mDrcWrap.update();
- notify(OMX_EventError,
- OMX_ErrorStreamCorrupt,
- ERROR_MALFORMED,
- NULL);
+ AAC_DECODER_ERROR decoderErr =
+ aacDecoder_DecodeFrame(mAACDecoder,
+ tmpOutBuffer,
+ 2048 * MAX_CHANNEL_COUNT,
+ 0 /* flags */);
+ if (decoderErr != AAC_DEC_OK) {
+ ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
+ }
+
+ if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
+ ALOGE("AAC_DEC_NOT_ENOUGH_BITS should never happen");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
return;
}
- } else {
- inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
- inBufferLength[0] = inHeader->nFilledLen;
- }
- } else {
- inBufferLength[0] = 0;
- }
- // Fill and decode
- INT_PCM *outBuffer = reinterpret_cast<INT_PCM *>(
- outHeader->pBuffer + outHeader->nOffset);
-
- bytesValid[0] = inBufferLength[0];
-
- int prevSampleRate = mStreamInfo->sampleRate;
- int prevNumChannels = mStreamInfo->numChannels;
-
- AAC_DECODER_ERROR decoderErr = AAC_DEC_NOT_ENOUGH_BITS;
- while ((bytesValid[0] > 0 || mSawInputEos) && decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
- mDecoderHasData |= (bytesValid[0] > 0);
- aacDecoder_Fill(mAACDecoder,
- inBuffer,
- inBufferLength,
- bytesValid);
-
- decoderErr = aacDecoder_DecodeFrame(mAACDecoder,
- outBuffer,
- outHeader->nAllocLen,
- 0 /* flags */);
- if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
- if (mSawInputEos && bytesValid[0] <= 0) {
- if (mDecoderHasData) {
- // flush out the decoder's delayed data by calling DecodeFrame
- // one more time, with the AACDEC_FLUSH flag set
- decoderErr = aacDecoder_DecodeFrame(mAACDecoder,
- outBuffer,
- outHeader->nAllocLen,
- AACDEC_FLUSH);
- mDecoderHasData = false;
+ if (bytesValid[0] != 0) {
+ ALOGE("bytesValid[0] != 0 should never happen");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
+
+ size_t numOutBytes =
+ mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
+
+ if (decoderErr == AAC_DEC_OK) {
+ if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
+ mStreamInfo->frameSize * mStreamInfo->numChannels)) {
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+ return;
}
- outHeader->nFlags = OMX_BUFFERFLAG_EOS;
- mSignalledOutputEos = true;
- break;
+ UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0];
+ inHeader->nFilledLen -= inBufferUsedLength;
+ inHeader->nOffset += inBufferUsedLength;
} else {
- ALOGW("Not enough bits, bytesValid %d", bytesValid[0]);
- }
- }
- }
+ ALOGW("AAC decoder returned error 0x%4.4x, substituting silence", decoderErr);
- size_t numOutBytes =
- mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
+ memset(tmpOutBuffer, 0, numOutBytes); // TODO: check for overflow
- if (inHeader) {
- if (decoderErr == AAC_DEC_OK) {
- UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0];
- inHeader->nFilledLen -= inBufferUsedLength;
- inHeader->nOffset += inBufferUsedLength;
- } else {
- ALOGW("AAC decoder returned error %d, substituting silence",
- decoderErr);
+ if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
+ mStreamInfo->frameSize * mStreamInfo->numChannels)) {
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+ return;
+ }
- memset(outHeader->pBuffer + outHeader->nOffset, 0, numOutBytes);
+ // Discard input buffer.
+ inHeader->nFilledLen = 0;
- // Discard input buffer.
- inHeader->nFilledLen = 0;
+ aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
- aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
+ // fall through
+ }
- // fall through
+ /*
+ * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
+ * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
+ * rate system and the sampling rate in the final output is actually
+ * doubled compared with the core AAC decoder sampling rate.
+ *
+ * Explicit signalling is done by explicitly defining SBR audio object
+ * type in the bitstream. Implicit signalling is done by embedding
+ * SBR content in AAC extension payload specific to SBR, and hence
+ * requires an AAC decoder to perform pre-checks on actual audio frames.
+ *
+ * Thus, we could not say for sure whether a stream is
+ * AAC+/eAAC+ until the first data frame is decoded.
+ */
+ if (mOutputBufferCount > 1) {
+ if (mStreamInfo->sampleRate != prevSampleRate ||
+ mStreamInfo->numChannels != prevNumChannels) {
+ ALOGE("can not reconfigure AAC output");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+ return;
+ }
+ }
+ if (mInputBufferCount <= 2) { // TODO: <= 1
+ if (mStreamInfo->sampleRate != prevSampleRate ||
+ mStreamInfo->numChannels != prevNumChannels) {
+ ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels",
+ prevSampleRate, mStreamInfo->sampleRate,
+ prevNumChannels, mStreamInfo->numChannels);
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+
+ if (inHeader->nFilledLen == 0) {
+ inInfo->mOwnedByUs = false;
+ mInputBufferCount++;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ }
+ return;
+ }
+ } else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) {
+ ALOGW("Invalid AAC stream");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+ return;
+ }
+ if (inHeader->nFilledLen == 0) {
+ inInfo->mOwnedByUs = false;
+ mInputBufferCount++;
+ inQueue.erase(inQueue.begin());
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ } else {
+ ALOGW("inHeader->nFilledLen = %d", inHeader->nFilledLen);
+ }
}
+ }
- if (inHeader->nFilledLen == 0) {
- inInfo->mOwnedByUs = false;
- inQueue.erase(inQueue.begin());
- inInfo = NULL;
- notifyEmptyBufferDone(inHeader);
- inHeader = NULL;
+ int32_t outputDelay = mStreamInfo->outputDelay * mStreamInfo->numChannels;
+
+ if (!mEndOfInput && mOutputDelayCompensated < outputDelay) {
+ // discard outputDelay at the beginning
+ int32_t toCompensate = outputDelay - mOutputDelayCompensated;
+ int32_t discard = outputDelayRingBufferSamplesAvailable();
+ if (discard > toCompensate) {
+ discard = toCompensate;
}
+ int32_t discarded = outputDelayRingBufferGetSamples(0, discard);
+ mOutputDelayCompensated += discarded;
+ continue;
}
- /*
- * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
- * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
- * rate system and the sampling rate in the final output is actually
- * doubled compared with the core AAC decoder sampling rate.
- *
- * Explicit signalling is done by explicitly defining SBR audio object
- * type in the bitstream. Implicit signalling is done by embedding
- * SBR content in AAC extension payload specific to SBR, and hence
- * requires an AAC decoder to perform pre-checks on actual audio frames.
- *
- * Thus, we could not say for sure whether a stream is
- * AAC+/eAAC+ until the first data frame is decoded.
- */
- if (mInputBufferCount <= 2) {
- if (mStreamInfo->sampleRate != prevSampleRate ||
- mStreamInfo->numChannels != prevNumChannels) {
- maybeConfigureDownmix();
- ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels",
- prevSampleRate, mStreamInfo->sampleRate,
- prevNumChannels, mStreamInfo->numChannels);
-
- notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
- mOutputPortSettingsChange = AWAITING_DISABLED;
- return;
+ if (mEndOfInput) {
+ while (mOutputDelayCompensated > 0) {
+ // a buffer big enough for MAX_CHANNEL_COUNT channels of decoded HE-AAC
+ INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT];
+
+ // run DRC check
+ mDrcWrap.submitStreamData(mStreamInfo);
+ mDrcWrap.update();
+
+ AAC_DECODER_ERROR decoderErr =
+ aacDecoder_DecodeFrame(mAACDecoder,
+ tmpOutBuffer,
+ 2048 * MAX_CHANNEL_COUNT,
+ AACDEC_FLUSH);
+ if (decoderErr != AAC_DEC_OK) {
+ ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
+ }
+
+ int32_t tmpOutBufferSamples = mStreamInfo->frameSize * mStreamInfo->numChannels;
+ if (tmpOutBufferSamples > mOutputDelayCompensated) {
+ tmpOutBufferSamples = mOutputDelayCompensated;
+ }
+ outputDelayRingBufferPutSamples(tmpOutBuffer, tmpOutBufferSamples);
+ mOutputDelayCompensated -= tmpOutBufferSamples;
}
- } else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) {
- ALOGW("Invalid AAC stream");
- mSignalledError = true;
- notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
- return;
}
- if (decoderErr == AAC_DEC_OK || mNumSamplesOutput > 0) {
- // We'll only output data if we successfully decoded it or
- // we've previously decoded valid data, in the latter case
- // (decode failed) we'll output a silent frame.
- outHeader->nFilledLen = numOutBytes;
+ while (!outQueue.empty()
+ && outputDelayRingBufferSamplesAvailable()
+ >= mStreamInfo->frameSize * mStreamInfo->numChannels) {
+ BufferInfo *outInfo = *outQueue.begin();
+ OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
- outHeader->nTimeStamp =
- mAnchorTimeUs
- + (mNumSamplesOutput * 1000000ll) / mStreamInfo->sampleRate;
+ if (outHeader->nOffset != 0) {
+ ALOGE("outHeader->nOffset != 0 is not handled");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
+
+ INT_PCM *outBuffer =
+ reinterpret_cast<INT_PCM *>(outHeader->pBuffer + outHeader->nOffset);
+ if (outHeader->nOffset
+ + mStreamInfo->frameSize * mStreamInfo->numChannels * sizeof(int16_t)
+ > outHeader->nAllocLen) {
+ ALOGE("buffer overflow");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
- mNumSamplesOutput += mStreamInfo->frameSize;
+ }
+ int32_t ns = outputDelayRingBufferGetSamples(outBuffer,
+ mStreamInfo->frameSize * mStreamInfo->numChannels); // TODO: check for overflow
+ if (ns != mStreamInfo->frameSize * mStreamInfo->numChannels) {
+ ALOGE("not a complete frame of samples available");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
+
+ outHeader->nFilledLen = mStreamInfo->frameSize * mStreamInfo->numChannels
+ * sizeof(int16_t);
+ if (mEndOfInput && !outQueue.empty() && outputDelayRingBufferSamplesAvailable() == 0) {
+ outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+ mEndOfOutput = true;
+ } else {
+ outHeader->nFlags = 0;
+ }
+ outHeader->nTimeStamp = mAnchorTimeUs[mOutputBufferCount
+ % kNumDelayBlocksMax];
+
+ mOutputBufferCount++;
outInfo->mOwnedByUs = false;
outQueue.erase(outQueue.begin());
outInfo = NULL;
@@ -558,8 +790,48 @@ void SoftAAC2::onQueueFilled(OMX_U32 portIndex) {
outHeader = NULL;
}
- if (decoderErr == AAC_DEC_OK) {
- ++mInputBufferCount;
+ if (mEndOfInput) {
+ if (outputDelayRingBufferSamplesAvailable() > 0
+ && outputDelayRingBufferSamplesAvailable()
+ < mStreamInfo->frameSize * mStreamInfo->numChannels) {
+ ALOGE("not a complete frame of samples available");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
+
+ if (mEndOfInput && !outQueue.empty() && outputDelayRingBufferSamplesAvailable() == 0) {
+ if (!mEndOfOutput) {
+ // send empty block signaling EOS
+ mEndOfOutput = true;
+ BufferInfo *outInfo = *outQueue.begin();
+ OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
+
+ if (outHeader->nOffset != 0) {
+ ALOGE("outHeader->nOffset != 0 is not handled");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
+
+ INT_PCM *outBuffer = reinterpret_cast<INT_PCM *>(outHeader->pBuffer
+ + outHeader->nOffset);
+ int32_t ns = 0;
+ outHeader->nFilledLen = 0;
+ outHeader->nFlags = OMX_BUFFERFLAG_EOS;
+
+ outHeader->nTimeStamp = mAnchorTimeUs[mOutputBufferCount
+ % kNumDelayBlocksMax];
+
+ mOutputBufferCount++;
+ outInfo->mOwnedByUs = false;
+ outQueue.erase(outQueue.begin());
+ outInfo = NULL;
+ notifyFillBufferDone(outHeader);
+ outHeader = NULL;
+ }
+ break; // if outQueue not empty but no more output
+ }
}
}
}
@@ -574,34 +846,67 @@ void SoftAAC2::onPortFlushCompleted(OMX_U32 portIndex) {
// but only if initialization has already happened.
if (mInputBufferCount != 0) {
mInputBufferCount = 1;
- mStreamInfo->sampleRate = 0;
}
+ } else {
+ while (outputDelayRingBufferSamplesAvailable() > 0) {
+ int32_t ns = outputDelayRingBufferGetSamples(0,
+ mStreamInfo->frameSize * mStreamInfo->numChannels);
+ if (ns != mStreamInfo->frameSize * mStreamInfo->numChannels) {
+ ALOGE("not a complete frame of samples available");
+ }
+ mOutputBufferCount++;
+ }
+ mOutputDelayRingBufferReadPos = mOutputDelayRingBufferWritePos;
}
}
void SoftAAC2::drainDecoder() {
- // a buffer big enough for 6 channels of decoded HE-AAC
- short buf [2048*6];
- aacDecoder_DecodeFrame(mAACDecoder,
- buf, sizeof(buf), AACDEC_FLUSH | AACDEC_CLRHIST | AACDEC_INTR);
- aacDecoder_DecodeFrame(mAACDecoder,
- buf, sizeof(buf), AACDEC_FLUSH | AACDEC_CLRHIST | AACDEC_INTR);
- aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
- mDecoderHasData = false;
+ int32_t outputDelay = mStreamInfo->outputDelay * mStreamInfo->numChannels;
+
+ // flush decoder until outputDelay is compensated
+ while (mOutputDelayCompensated > 0) {
+ // a buffer big enough for MAX_CHANNEL_COUNT channels of decoded HE-AAC
+ INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT];
+
+ // run DRC check
+ mDrcWrap.submitStreamData(mStreamInfo);
+ mDrcWrap.update();
+
+ AAC_DECODER_ERROR decoderErr =
+ aacDecoder_DecodeFrame(mAACDecoder,
+ tmpOutBuffer,
+ 2048 * MAX_CHANNEL_COUNT,
+ AACDEC_FLUSH);
+ if (decoderErr != AAC_DEC_OK) {
+ ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
+ }
+
+ int32_t tmpOutBufferSamples = mStreamInfo->frameSize * mStreamInfo->numChannels;
+ if (tmpOutBufferSamples > mOutputDelayCompensated) {
+ tmpOutBufferSamples = mOutputDelayCompensated;
+ }
+ outputDelayRingBufferPutSamples(tmpOutBuffer, tmpOutBufferSamples);
+
+ mOutputDelayCompensated -= tmpOutBufferSamples;
+ }
}
void SoftAAC2::onReset() {
drainDecoder();
// reset the "configured" state
mInputBufferCount = 0;
- mNumSamplesOutput = 0;
+ mOutputBufferCount = 0;
+ mOutputDelayCompensated = 0;
+ mOutputDelayRingBufferWritePos = 0;
+ mOutputDelayRingBufferReadPos = 0;
+ mEndOfInput = false;
+ mEndOfOutput = false;
+
// To make the codec behave the same before and after a reset, we need to invalidate the
// streaminfo struct. This does that:
- mStreamInfo->sampleRate = 0;
+ mStreamInfo->sampleRate = 0; // TODO: mStreamInfo is read only
mSignalledError = false;
- mSawInputEos = false;
- mSignalledOutputEos = false;
mOutputPortSettingsChange = NONE;
}
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.h b/media/libstagefright/codecs/aacdec/SoftAAC2.h
index a7ea1e2..5cde03a 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.h
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.h
@@ -20,6 +20,7 @@
#include "SimpleSoftOMXComponent.h"
#include "aacdecoder_lib.h"
+#include "DrcPresModeWrap.h"
namespace android {
@@ -47,18 +48,19 @@ private:
enum {
kNumInputBuffers = 4,
kNumOutputBuffers = 4,
+ kNumDelayBlocksMax = 8,
};
HANDLE_AACDECODER mAACDecoder;
CStreamInfo *mStreamInfo;
bool mIsADTS;
- bool mDecoderHasData;
+ bool mIsFirst;
size_t mInputBufferCount;
+ size_t mOutputBufferCount;
bool mSignalledError;
- bool mSawInputEos;
- bool mSignalledOutputEos;
- int64_t mAnchorTimeUs;
- int64_t mNumSamplesOutput;
+ int64_t mAnchorTimeUs[kNumDelayBlocksMax];
+
+ CDrcPresModeWrapper mDrcWrap;
enum {
NONE,
@@ -69,9 +71,22 @@ private:
void initPorts();
status_t initDecoder();
bool isConfigured() const;
- void maybeConfigureDownmix() const;
+ void configureDownmix() const;
void drainDecoder();
+// delay compensation
+ bool mEndOfInput;
+ bool mEndOfOutput;
+ int32_t mOutputDelayCompensated;
+ int32_t mOutputDelayRingBufferSize;
+ short *mOutputDelayRingBuffer;
+ int32_t mOutputDelayRingBufferWritePos;
+ int32_t mOutputDelayRingBufferReadPos;
+ bool outputDelayRingBufferPutSamples(INT_PCM *samples, int numSamples);
+ int32_t outputDelayRingBufferGetSamples(INT_PCM *samples, int numSamples);
+ int32_t outputDelayRingBufferSamplesAvailable();
+ int32_t outputDelayRingBufferSamplesLeft();
+
DISALLOW_EVIL_CONSTRUCTORS(SoftAAC2);
};
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index f7b6f64..0bdf5a3 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -64,6 +64,7 @@ LOCAL_32_BIT_ONLY := true
LOCAL_SRC_FILES += FastMixer.cpp FastMixerState.cpp AudioWatchdog.cpp
LOCAL_SRC_FILES += FastThread.cpp FastThreadState.cpp
+LOCAL_SRC_FILES += FastCapture.cpp FastCaptureState.cpp
LOCAL_CFLAGS += -DSTATE_QUEUE_INSTANTIATIONS='"StateQueueInstantiations.cpp"'
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 8d57451..ace3bf1 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -750,6 +750,9 @@ void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider
name -= TRACK0;
ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name);
+ if (mState.tracks[name].mInputBufferProvider == bufferProvider) {
+ return; // don't reset any buffer providers if identical.
+ }
if (mState.tracks[name].mReformatBufferProvider != NULL) {
mState.tracks[name].mReformatBufferProvider->reset();
} else if (mState.tracks[name].downmixerBufferProvider != NULL) {
diff --git a/services/audioflinger/FastCapture.cpp b/services/audioflinger/FastCapture.cpp
new file mode 100644
index 0000000..0c9b976
--- /dev/null
+++ b/services/audioflinger/FastCapture.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2014 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 "FastCapture"
+//#define LOG_NDEBUG 0
+
+#define ATRACE_TAG ATRACE_TAG_AUDIO
+
+#include "Configuration.h"
+#include <linux/futex.h>
+#include <sys/syscall.h>
+#include <media/AudioBufferProvider.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include "FastCapture.h"
+
+namespace android {
+
+/*static*/ const FastCaptureState FastCapture::initial;
+
+FastCapture::FastCapture() : FastThread(),
+ inputSource(NULL), inputSourceGen(0), pipeSink(NULL), pipeSinkGen(0),
+ readBuffer(NULL), readBufferState(-1), format(Format_Invalid), sampleRate(0),
+ // dummyDumpState
+ totalNativeFramesRead(0)
+{
+ previous = &initial;
+ current = &initial;
+
+ mDummyDumpState = &dummyDumpState;
+}
+
+FastCapture::~FastCapture()
+{
+}
+
+FastCaptureStateQueue* FastCapture::sq()
+{
+ return &mSQ;
+}
+
+const FastThreadState *FastCapture::poll()
+{
+ return mSQ.poll();
+}
+
+void FastCapture::setLog(NBLog::Writer *logWriter __unused)
+{
+}
+
+void FastCapture::onIdle()
+{
+ preIdle = *(const FastCaptureState *)current;
+ current = &preIdle;
+}
+
+void FastCapture::onExit()
+{
+ delete[] readBuffer;
+}
+
+bool FastCapture::isSubClassCommand(FastThreadState::Command command)
+{
+ switch ((FastCaptureState::Command) command) {
+ case FastCaptureState::READ:
+ case FastCaptureState::WRITE:
+ case FastCaptureState::READ_WRITE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+void FastCapture::onStateChange()
+{
+ const FastCaptureState * const current = (const FastCaptureState *) this->current;
+ const FastCaptureState * const previous = (const FastCaptureState *) this->previous;
+ FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) this->dumpState;
+ const size_t frameCount = current->mFrameCount;
+
+ bool eitherChanged = false;
+
+ // check for change in input HAL configuration
+ NBAIO_Format previousFormat = format;
+ if (current->mInputSourceGen != inputSourceGen) {
+ inputSource = current->mInputSource;
+ inputSourceGen = current->mInputSourceGen;
+ if (inputSource == NULL) {
+ format = Format_Invalid;
+ sampleRate = 0;
+ } else {
+ format = inputSource->format();
+ sampleRate = Format_sampleRate(format);
+ unsigned channelCount = Format_channelCount(format);
+ ALOG_ASSERT(channelCount == 1 || channelCount == 2);
+ }
+ dumpState->mSampleRate = sampleRate;
+ eitherChanged = true;
+ }
+
+ // check for change in pipe
+ if (current->mPipeSinkGen != pipeSinkGen) {
+ pipeSink = current->mPipeSink;
+ pipeSinkGen = current->mPipeSinkGen;
+ eitherChanged = true;
+ }
+
+ // input source and pipe sink must be compatible
+ if (eitherChanged && inputSource != NULL && pipeSink != NULL) {
+ ALOG_ASSERT(Format_isEqual(format, pipeSink->format()));
+ }
+
+ if ((!Format_isEqual(format, previousFormat)) || (frameCount != previous->mFrameCount)) {
+ // FIXME to avoid priority inversion, don't delete here
+ delete[] readBuffer;
+ readBuffer = NULL;
+ if (frameCount > 0 && sampleRate > 0) {
+ // FIXME new may block for unbounded time at internal mutex of the heap
+ // implementation; it would be better to have normal capture thread allocate for
+ // us to avoid blocking here and to prevent possible priority inversion
+ unsigned channelCount = Format_channelCount(format);
+ // FIXME frameSize
+ readBuffer = new short[frameCount * channelCount];
+ periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00
+ underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75
+ overrunNs = (frameCount * 500000000LL) / sampleRate; // 0.50
+ forceNs = (frameCount * 950000000LL) / sampleRate; // 0.95
+ warmupNs = (frameCount * 500000000LL) / sampleRate; // 0.50
+ } else {
+ periodNs = 0;
+ underrunNs = 0;
+ overrunNs = 0;
+ forceNs = 0;
+ warmupNs = 0;
+ }
+ readBufferState = -1;
+ dumpState->mFrameCount = frameCount;
+ }
+
+}
+
+void FastCapture::onWork()
+{
+ const FastCaptureState * const current = (const FastCaptureState *) this->current;
+ FastCaptureDumpState * const dumpState = (FastCaptureDumpState *) this->dumpState;
+ const FastCaptureState::Command command = this->command;
+ const size_t frameCount = current->mFrameCount;
+
+ if ((command & FastCaptureState::READ) /*&& isWarm*/) {
+ ALOG_ASSERT(inputSource != NULL);
+ ALOG_ASSERT(readBuffer != NULL);
+ dumpState->mReadSequence++;
+ ATRACE_BEGIN("read");
+ ssize_t framesRead = inputSource->read(readBuffer, frameCount,
+ AudioBufferProvider::kInvalidPTS);
+ ATRACE_END();
+ dumpState->mReadSequence++;
+ if (framesRead >= 0) {
+ LOG_ALWAYS_FATAL_IF((size_t) framesRead > frameCount);
+ totalNativeFramesRead += framesRead;
+ dumpState->mFramesRead = totalNativeFramesRead;
+ readBufferState = framesRead;
+ } else {
+ dumpState->mReadErrors++;
+ readBufferState = 0;
+ }
+ // FIXME rename to attemptedIO
+ attemptedWrite = true;
+ }
+
+ if (command & FastCaptureState::WRITE) {
+ ALOG_ASSERT(pipeSink != NULL);
+ ALOG_ASSERT(readBuffer != NULL);
+ if (readBufferState < 0) {
+ unsigned channelCount = Format_channelCount(format);
+ // FIXME frameSize
+ memset(readBuffer, 0, frameCount * channelCount * sizeof(short));
+ readBufferState = frameCount;
+ }
+ if (readBufferState > 0) {
+ ssize_t framesWritten = pipeSink->write(readBuffer, readBufferState);
+ // FIXME This supports at most one fast capture client.
+ // To handle multiple clients this could be converted to an array,
+ // or with a lot more work the control block could be shared by all clients.
+ audio_track_cblk_t* cblk = current->mCblk;
+ if (cblk != NULL && framesWritten > 0) {
+ int32_t rear = cblk->u.mStreaming.mRear;
+ android_atomic_release_store(framesWritten + rear, &cblk->u.mStreaming.mRear);
+ cblk->mServer += framesWritten;
+ int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
+ if (!(old & CBLK_FUTEX_WAKE)) {
+ // client is never in server process, so don't use FUTEX_WAKE_PRIVATE
+ (void) syscall(__NR_futex, &cblk->mFutex, FUTEX_WAKE, 1);
+ }
+ }
+ }
+ }
+}
+
+FastCaptureDumpState::FastCaptureDumpState() : FastThreadDumpState(),
+ mReadSequence(0), mFramesRead(0), mReadErrors(0), mSampleRate(0), mFrameCount(0)
+{
+}
+
+FastCaptureDumpState::~FastCaptureDumpState()
+{
+}
+
+} // namespace android
diff --git a/services/audioflinger/FastCapture.h b/services/audioflinger/FastCapture.h
new file mode 100644
index 0000000..e535b9d
--- /dev/null
+++ b/services/audioflinger/FastCapture.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 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 ANDROID_AUDIO_FAST_CAPTURE_H
+#define ANDROID_AUDIO_FAST_CAPTURE_H
+
+#include "FastThread.h"
+#include "StateQueue.h"
+#include "FastCaptureState.h"
+
+namespace android {
+
+typedef StateQueue<FastCaptureState> FastCaptureStateQueue;
+
+struct FastCaptureDumpState : FastThreadDumpState {
+ FastCaptureDumpState();
+ /*virtual*/ ~FastCaptureDumpState();
+
+ // FIXME by renaming, could pull up many of these to FastThreadDumpState
+ uint32_t mReadSequence; // incremented before and after each read()
+ uint32_t mFramesRead; // total number of frames read successfully
+ uint32_t mReadErrors; // total number of read() errors
+ uint32_t mSampleRate;
+ size_t mFrameCount;
+};
+
+class FastCapture : public FastThread {
+
+public:
+ FastCapture();
+ virtual ~FastCapture();
+
+ FastCaptureStateQueue* sq();
+
+private:
+ FastCaptureStateQueue mSQ;
+
+ // callouts
+ virtual const FastThreadState *poll();
+ virtual void setLog(NBLog::Writer *logWriter);
+ virtual void onIdle();
+ virtual void onExit();
+ virtual bool isSubClassCommand(FastThreadState::Command command);
+ virtual void onStateChange();
+ virtual void onWork();
+
+ static const FastCaptureState initial;
+ FastCaptureState preIdle; // copy of state before we went into idle
+ // FIXME by renaming, could pull up many of these to FastThread
+ NBAIO_Source *inputSource;
+ int inputSourceGen;
+ NBAIO_Sink *pipeSink;
+ int pipeSinkGen;
+ short *readBuffer;
+ ssize_t readBufferState; // number of initialized frames in readBuffer, or -1 to clear
+ NBAIO_Format format;
+ unsigned sampleRate;
+ FastCaptureDumpState dummyDumpState;
+ uint32_t totalNativeFramesRead; // copied to dumpState->mFramesRead
+
+}; // class FastCapture
+
+} // namespace android
+
+#endif // ANDROID_AUDIO_FAST_CAPTURE_H
diff --git a/services/audioflinger/FastCaptureState.cpp b/services/audioflinger/FastCaptureState.cpp
new file mode 100644
index 0000000..1d029b7
--- /dev/null
+++ b/services/audioflinger/FastCaptureState.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include "FastCaptureState.h"
+
+namespace android {
+
+FastCaptureState::FastCaptureState() : FastThreadState(),
+ mInputSource(NULL), mInputSourceGen(0), mPipeSink(NULL), mPipeSinkGen(0), mFrameCount(0)
+{
+}
+
+FastCaptureState::~FastCaptureState()
+{
+}
+
+} // android
diff --git a/services/audioflinger/FastCaptureState.h b/services/audioflinger/FastCaptureState.h
new file mode 100644
index 0000000..29c865a
--- /dev/null
+++ b/services/audioflinger/FastCaptureState.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 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 ANDROID_AUDIO_FAST_CAPTURE_STATE_H
+#define ANDROID_AUDIO_FAST_CAPTURE_STATE_H
+
+#include <media/nbaio/NBAIO.h>
+#include "FastThreadState.h"
+#include <private/media/AudioTrackShared.h>
+
+namespace android {
+
+// Represent a single state of the fast capture
+struct FastCaptureState : FastThreadState {
+ FastCaptureState();
+ /*virtual*/ ~FastCaptureState();
+
+ // all pointer fields use raw pointers; objects are owned and ref-counted by RecordThread
+ NBAIO_Source *mInputSource; // HAL input device, must already be negotiated
+ // FIXME by renaming, could pull up these fields to FastThreadState
+ int mInputSourceGen; // increment when mInputSource is assigned
+ NBAIO_Sink *mPipeSink; // after reading from input source, write to this pipe sink
+ int mPipeSinkGen; // increment when mPipeSink is assigned
+ size_t mFrameCount; // number of frames per fast capture buffer
+ audio_track_cblk_t *mCblk; // control block for the single fast client, or NULL
+
+ // Extends FastThreadState::Command
+ static const Command
+ // The following commands also process configuration changes, and can be "or"ed:
+ READ = 0x8, // read from input source
+ WRITE = 0x10, // write to pipe sink
+ READ_WRITE = 0x18; // read from input source and write to pipe sink
+
+}; // struct FastCaptureState
+
+} // namespace android
+
+#endif // ANDROID_AUDIO_FAST_CAPTURE_STATE_H
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index c840418..13b21ec 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -36,6 +36,7 @@
#include <cpustats/ThreadCpuUsage.h>
#endif
#endif
+#include <audio_utils/format.h>
#include "AudioMixer.h"
#include "FastMixer.h"
@@ -52,7 +53,11 @@ FastMixer::FastMixer() : FastThread(),
outputSink(NULL),
outputSinkGen(0),
mixer(NULL),
+ mSinkBuffer(NULL),
+ mSinkBufferSize(0),
mMixerBuffer(NULL),
+ mMixerBufferSize(0),
+ mMixerBufferFormat(AUDIO_FORMAT_PCM_16_BIT),
mMixerBufferState(UNDEFINED),
format(Format_Invalid),
sampleRate(0),
@@ -108,7 +113,8 @@ void FastMixer::onIdle()
void FastMixer::onExit()
{
delete mixer;
- delete[] mMixerBuffer;
+ free(mMixerBuffer);
+ free(mSinkBuffer);
}
bool FastMixer::isSubClassCommand(FastThreadState::Command command)
@@ -154,14 +160,23 @@ void FastMixer::onStateChange()
// FIXME to avoid priority inversion, don't delete here
delete mixer;
mixer = NULL;
- delete[] mMixerBuffer;
+ free(mMixerBuffer);
mMixerBuffer = NULL;
+ free(mSinkBuffer);
+ mSinkBuffer = NULL;
if (frameCount > 0 && sampleRate > 0) {
// FIXME new may block for unbounded time at internal mutex of the heap
// implementation; it would be better to have normal mixer allocate for us
// to avoid blocking here and to prevent possible priority inversion
mixer = new AudioMixer(frameCount, sampleRate, FastMixerState::kMaxFastTracks);
- mMixerBuffer = new short[frameCount * FCC_2];
+ const size_t mixerFrameSize = FCC_2 * audio_bytes_per_sample(mMixerBufferFormat);
+ mMixerBufferSize = mixerFrameSize * frameCount;
+ (void)posix_memalign(&mMixerBuffer, 32, mMixerBufferSize);
+ const size_t sinkFrameSize = FCC_2 * audio_bytes_per_sample(format.mFormat);
+ if (sinkFrameSize > mixerFrameSize) { // need a sink buffer
+ mSinkBufferSize = sinkFrameSize * frameCount;
+ (void)posix_memalign(&mSinkBuffer, 32, mSinkBufferSize);
+ }
periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00
underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75
overrunNs = (frameCount * 500000000LL) / sampleRate; // 0.50
@@ -231,6 +246,10 @@ void FastMixer::onStateChange()
mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
(void *) mMixerBuffer);
// newly allocated track names default to full scale volume
+ mixer->setParameter(
+ name,
+ AudioMixer::TRACK,
+ AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT,
(void *)(uintptr_t)fastTrack->mFormat);
mixer->enable(name);
@@ -261,6 +280,10 @@ void FastMixer::onStateChange()
}
mixer->setParameter(name, AudioMixer::RESAMPLE,
AudioMixer::REMOVE, NULL);
+ mixer->setParameter(
+ name,
+ AudioMixer::TRACK,
+ AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat);
mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::FORMAT,
(void *)(uintptr_t)fastTrack->mFormat);
mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK,
@@ -369,9 +392,14 @@ void FastMixer::onWork()
//bool didFullWrite = false; // dumpsys could display a count of partial writes
if ((command & FastMixerState::WRITE) && (outputSink != NULL) && (mMixerBuffer != NULL)) {
if (mMixerBufferState == UNDEFINED) {
- memset(mMixerBuffer, 0, frameCount * FCC_2 * sizeof(short));
+ memset(mMixerBuffer, 0, mMixerBufferSize);
mMixerBufferState = ZEROED;
}
+ void *buffer = mSinkBuffer != NULL ? mSinkBuffer : mMixerBuffer;
+ if (format.mFormat != mMixerBufferFormat) { // sink format not the same as mixer format
+ memcpy_by_audio_format(buffer, format.mFormat, mMixerBuffer, mMixerBufferFormat,
+ frameCount * Format_channelCount(format));
+ }
// if non-NULL, then duplicate write() to this non-blocking sink
NBAIO_Sink* teeSink;
if ((teeSink = current->mTeeSink) != NULL) {
@@ -381,7 +409,7 @@ void FastMixer::onWork()
// but this code should be modified to handle both non-blocking and blocking sinks
dumpState->mWriteSequence++;
ATRACE_BEGIN("write");
- ssize_t framesWritten = outputSink->write(mMixerBuffer, frameCount);
+ ssize_t framesWritten = outputSink->write(buffer, frameCount);
ATRACE_END();
dumpState->mWriteSequence++;
if (framesWritten >= 0) {
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index db3e2c9..4671670 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -61,7 +61,15 @@ private:
NBAIO_Sink *outputSink;
int outputSinkGen;
AudioMixer* mixer;
- short *mMixerBuffer;
+
+ // mSinkBuffer audio format is stored in format.mFormat.
+ void* mSinkBuffer; // used for mixer output format translation
+ // if sink format is different than mixer output.
+ size_t mSinkBufferSize;
+ void* mMixerBuffer; // mixer output buffer.
+ size_t mMixerBufferSize;
+ audio_format_t mMixerBufferFormat; // mixer output format: AUDIO_FORMAT_PCM_(16_BIT|FLOAT).
+
enum {UNDEFINED, MIXED, ZEROED} mMixerBufferState;
NBAIO_Format format;
unsigned sampleRate;
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index eee74b3..96a8127 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -179,11 +179,11 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa
ALOGW("createAudioPatch() bad src hw module %d", src_module);
return BAD_VALUE;
}
+ AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
for (unsigned int i = 0; i < patch->num_sinks; i++) {
- // limit to connections between devices and output streams
- if (patch->sinks[i].type != AUDIO_PORT_TYPE_MIX) {
- ALOGW("createAudioPatch() invalid sink type %d for device source",
- patch->sinks[i].type);
+ // reject connection to different sink types
+ if (patch->sinks[i].type != patch->sinks[0].type) {
+ ALOGW("createAudioPatch() different sink types in same patch not supported");
return BAD_VALUE;
}
// limit to connections between sinks and sources on same HW module
@@ -192,9 +192,16 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa
"sink on module %d", src_module, patch->sinks[i].ext.mix.hw_module);
return BAD_VALUE;
}
+
+ // limit to connections between devices and output streams for HAL before 3.0
+ if ((audioHwDevice->version() < AUDIO_DEVICE_API_VERSION_3_0) &&
+ (patch->sinks[i].type != AUDIO_PORT_TYPE_MIX)) {
+ ALOGW("createAudioPatch() invalid sink type %d for device source",
+ patch->sinks[i].type);
+ return BAD_VALUE;
+ }
}
- AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
sp<ThreadBase> thread = audioflinger->checkRecordThread_l(
@@ -397,13 +404,38 @@ status_t AudioFlinger::PatchPanel::listAudioPatches(unsigned int *num_patches __
}
/* Set audio port configuration */
-status_t AudioFlinger::PatchPanel::setAudioPortConfig(
- const struct audio_port_config *config __unused)
+status_t AudioFlinger::PatchPanel::setAudioPortConfig(const struct audio_port_config *config)
{
ALOGV("setAudioPortConfig");
+ status_t status = NO_ERROR;
+
+ sp<AudioFlinger> audioflinger = mAudioFlinger.promote();
+ if (audioflinger == 0) {
+ return NO_INIT;
+ }
+
+ audio_module_handle_t module;
+ if (config->type == AUDIO_PORT_TYPE_DEVICE) {
+ module = config->ext.device.hw_module;
+ } else {
+ module = config->ext.mix.hw_module;
+ }
+
+ ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(module);
+ if (index < 0) {
+ ALOGW("setAudioPortConfig() bad hw module %d", module);
+ return BAD_VALUE;
+ }
+
+ AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index);
+ if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) {
+ audio_hw_device_t *hwDevice = audioHwDevice->hwDevice();
+ return hwDevice->set_audio_port_config(hwDevice, config);
+ } else {
+ return INVALID_OPERATION;
+ }
return NO_ERROR;
}
-
}; // namespace android
diff --git a/services/audioflinger/StateQueueInstantiations.cpp b/services/audioflinger/StateQueueInstantiations.cpp
index 0d5cd0c..6f4505e 100644
--- a/services/audioflinger/StateQueueInstantiations.cpp
+++ b/services/audioflinger/StateQueueInstantiations.cpp
@@ -16,12 +16,14 @@
#include "Configuration.h"
#include "FastMixerState.h"
+#include "FastCaptureState.h"
#include "StateQueue.h"
// FIXME hack for gcc
namespace android {
-template class StateQueue<FastMixerState>; // typedef FastMixerStateQueue
+template class StateQueue<FastMixerState>; // typedef FastMixerStateQueue
+template class StateQueue<FastCaptureState>; // typedef FastCaptureStateQueue
}
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 576350e..742163b 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2744,9 +2744,27 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud
break;
}
if (initFastMixer) {
+ audio_format_t fastMixerFormat;
+ if (mMixerBufferEnabled && mEffectBufferEnabled) {
+ fastMixerFormat = AUDIO_FORMAT_PCM_FLOAT;
+ } else {
+ fastMixerFormat = AUDIO_FORMAT_PCM_16_BIT;
+ }
+ if (mFormat != fastMixerFormat) {
+ // change our Sink format to accept our intermediate precision
+ mFormat = fastMixerFormat;
+ free(mSinkBuffer);
+ mFrameSize = mChannelCount * audio_bytes_per_sample(mFormat);
+ const size_t sinkBufferSize = mNormalFrameCount * mFrameSize;
+ (void)posix_memalign(&mSinkBuffer, 32, sinkBufferSize);
+ }
// create a MonoPipe to connect our submix to FastMixer
NBAIO_Format format = mOutputSink->format();
+ // adjust format to match that of the Fast Mixer
+ format.mFormat = fastMixerFormat;
+ format.mFrameSize = audio_bytes_per_sample(format.mFormat) * format.mChannelCount;
+
// This pipe depth compensates for scheduling latency of the normal mixer thread.
// When it wakes up after a maximum latency, it runs a few cycles quickly before
// finally blocking. Note the pipe implementation rounds up the request to a power of 2.
diff --git a/services/audiopolicy/AudioPolicyClientImpl.cpp b/services/audiopolicy/AudioPolicyClientImpl.cpp
index 8225e36..c322d92 100644
--- a/services/audiopolicy/AudioPolicyClientImpl.cpp
+++ b/services/audiopolicy/AudioPolicyClientImpl.cpp
@@ -195,4 +195,21 @@ status_t AudioPolicyService::AudioPolicyClient::releaseAudioPatch(audio_patch_ha
return mAudioPolicyService->clientReleaseAudioPatch(handle, delayMs);
}
+status_t AudioPolicyService::AudioPolicyClient::setAudioPortConfig(
+ const struct audio_port_config *config,
+ int delayMs)
+{
+ return mAudioPolicyService->clientSetAudioPortConfig(config, delayMs);
+}
+
+void AudioPolicyService::AudioPolicyClient::onAudioPortListUpdate()
+{
+ mAudioPolicyService->onAudioPortListUpdate();
+}
+
+void AudioPolicyService::AudioPolicyClient::onAudioPatchListUpdate()
+{
+ mAudioPolicyService->onAudioPatchListUpdate();
+}
+
}; // namespace android
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index bb2deb6..c025a45 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -162,6 +162,24 @@ public:
virtual status_t dump(int fd) = 0;
virtual bool isOffloadSupported(const audio_offload_info_t& offloadInfo) = 0;
+
+ virtual status_t listAudioPorts(audio_port_role_t role,
+ audio_port_type_t type,
+ unsigned int *num_ports,
+ struct audio_port *ports,
+ unsigned int *generation) = 0;
+ virtual status_t getAudioPort(struct audio_port *port) = 0;
+ virtual status_t createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle,
+ uid_t uid) = 0;
+ virtual status_t releaseAudioPatch(audio_patch_handle_t handle,
+ uid_t uid) = 0;
+ virtual status_t listAudioPatches(unsigned int *num_patches,
+ struct audio_patch *patches,
+ unsigned int *generation) = 0;
+ virtual status_t setAudioPortConfig(const struct audio_port_config *config) = 0;
+ virtual void clearAudioPatches(uid_t uid) = 0;
+
};
@@ -255,6 +273,12 @@ public:
virtual status_t releaseAudioPatch(audio_patch_handle_t handle,
int delayMs) = 0;
+ /* Set audio port configuration */
+ virtual status_t setAudioPortConfig(const struct audio_port_config *config, int delayMs) = 0;
+
+ virtual void onAudioPortListUpdate() = 0;
+
+ virtual void onAudioPatchListUpdate() = 0;
};
extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface);
diff --git a/services/audiopolicy/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/AudioPolicyInterfaceImpl.cpp
index 7cd253b..2b33703 100644
--- a/services/audiopolicy/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/AudioPolicyInterfaceImpl.cpp
@@ -463,43 +463,72 @@ bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info)
return mAudioPolicyManager->isOffloadSupported(info);
}
-status_t AudioPolicyService::listAudioPorts(audio_port_role_t role __unused,
- audio_port_type_t type __unused,
+status_t AudioPolicyService::listAudioPorts(audio_port_role_t role,
+ audio_port_type_t type,
unsigned int *num_ports,
- struct audio_port *ports __unused,
- unsigned int *generation __unused)
+ struct audio_port *ports,
+ unsigned int *generation)
{
- *num_ports = 0;
- return INVALID_OPERATION;
+ Mutex::Autolock _l(mLock);
+ if (mAudioPolicyManager == NULL) {
+ return NO_INIT;
+ }
+
+ return mAudioPolicyManager->listAudioPorts(role, type, num_ports, ports, generation);
}
-status_t AudioPolicyService::getAudioPort(struct audio_port *port __unused)
+status_t AudioPolicyService::getAudioPort(struct audio_port *port)
{
- return INVALID_OPERATION;
+ Mutex::Autolock _l(mLock);
+ if (mAudioPolicyManager == NULL) {
+ return NO_INIT;
+ }
+
+ return mAudioPolicyManager->getAudioPort(port);
}
-status_t AudioPolicyService::createAudioPatch(const struct audio_patch *patch __unused,
- audio_patch_handle_t *handle __unused)
+status_t AudioPolicyService::createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle)
{
- return INVALID_OPERATION;
+ Mutex::Autolock _l(mLock);
+ if (mAudioPolicyManager == NULL) {
+ return NO_INIT;
+ }
+ return mAudioPolicyManager->createAudioPatch(patch, handle,
+ IPCThreadState::self()->getCallingUid());
}
-status_t AudioPolicyService::releaseAudioPatch(audio_patch_handle_t handle __unused)
+status_t AudioPolicyService::releaseAudioPatch(audio_patch_handle_t handle)
{
- return INVALID_OPERATION;
+ Mutex::Autolock _l(mLock);
+ if (mAudioPolicyManager == NULL) {
+ return NO_INIT;
+ }
+
+ return mAudioPolicyManager->releaseAudioPatch(handle,
+ IPCThreadState::self()->getCallingUid());
}
status_t AudioPolicyService::listAudioPatches(unsigned int *num_patches,
- struct audio_patch *patches __unused,
- unsigned int *generation __unused)
+ struct audio_patch *patches,
+ unsigned int *generation)
{
- *num_patches = 0;
- return INVALID_OPERATION;
+ Mutex::Autolock _l(mLock);
+ if (mAudioPolicyManager == NULL) {
+ return NO_INIT;
+ }
+
+ return mAudioPolicyManager->listAudioPatches(num_patches, patches, generation);
}
-status_t AudioPolicyService::setAudioPortConfig(const struct audio_port_config *config __unused)
+status_t AudioPolicyService::setAudioPortConfig(const struct audio_port_config *config)
{
- return INVALID_OPERATION;
+ Mutex::Autolock _l(mLock);
+ if (mAudioPolicyManager == NULL) {
+ return NO_INIT;
+ }
+
+ return mAudioPolicyManager->setAudioPortConfig(config);
}
}; // namespace android
diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp
index db0f57d..bf5b9a8 100644
--- a/services/audiopolicy/AudioPolicyManager.cpp
+++ b/services/audiopolicy/AudioPolicyManager.cpp
@@ -38,9 +38,9 @@
#include <utils/Log.h>
#include <hardware/audio.h>
#include <hardware/audio_effect.h>
-#include <hardware_legacy/audio_policy_conf.h>
#include <media/AudioParameter.h>
#include "AudioPolicyManager.h"
+#include "audio_policy_conf.h"
namespace android {
@@ -137,6 +137,12 @@ const StringToEnum sInChannelsNameToEnumTable[] = {
STRING_TO_ENUM(AUDIO_CHANNEL_IN_FRONT_BACK),
};
+const StringToEnum sGainModeNameToEnumTable[] = {
+ STRING_TO_ENUM(AUDIO_GAIN_MODE_JOINT),
+ STRING_TO_ENUM(AUDIO_GAIN_MODE_CHANNELS),
+ STRING_TO_ENUM(AUDIO_GAIN_MODE_RAMP),
+};
+
uint32_t AudioPolicyManager::stringToEnum(const struct StringToEnum *table,
size_t size,
@@ -189,9 +195,8 @@ status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device,
if (audio_is_output_device(device)) {
SortedVector <audio_io_handle_t> outputs;
- sp<DeviceDescriptor> devDesc = new DeviceDescriptor(device,
- address,
- 0);
+ sp<DeviceDescriptor> devDesc = new DeviceDescriptor(String8(""), device);
+ devDesc->mAddress = address;
ssize_t index = mAvailableOutputDevices.indexOf(devDesc);
// save a copy of the opened output descriptors before any output is opened or closed
@@ -210,12 +215,19 @@ status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device,
if (checkOutputsForDevice(device, state, outputs, address) != NO_ERROR) {
return INVALID_OPERATION;
}
+ // outputs should never be empty here
+ ALOG_ASSERT(outputs.size() != 0, "setDeviceConnectionState():"
+ "checkOutputsForDevice() returned no outputs but status OK");
ALOGV("setDeviceConnectionState() checkOutputsForDevice() returned %zu outputs",
outputs.size());
// register new device as available
index = mAvailableOutputDevices.add(devDesc);
if (index >= 0) {
mAvailableOutputDevices[index]->mId = nextUniqueId();
+ HwModule *module = getModuleForDevice(device);
+ ALOG_ASSERT(module != NULL, "setDeviceConnectionState():"
+ "could not find HW module for device %08x", device);
+ mAvailableOutputDevices[index]->mModule = module;
} else {
return NO_MEMORY;
}
@@ -273,25 +285,16 @@ status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device,
0);
}
- if (device == AUDIO_DEVICE_OUT_WIRED_HEADSET) {
- device = AUDIO_DEVICE_IN_WIRED_HEADSET;
- } else if (device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO ||
- device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET ||
- device == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) {
- device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
- } else {
- return NO_ERROR;
- }
+ mpClientInterface->onAudioPortListUpdate();
+ return NO_ERROR;
} // end if is output device
// handle input devices
if (audio_is_input_device(device)) {
SortedVector <audio_io_handle_t> inputs;
- sp<DeviceDescriptor> devDesc = new DeviceDescriptor(device,
- address,
- 0);
-
+ sp<DeviceDescriptor> devDesc = new DeviceDescriptor(String8(""), device);
+ devDesc->mAddress = address;
ssize_t index = mAvailableInputDevices.indexOf(devDesc);
switch (state)
{
@@ -301,6 +304,12 @@ status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device,
ALOGW("setDeviceConnectionState() device already connected: %d", device);
return INVALID_OPERATION;
}
+ HwModule *module = getModuleForDevice(device);
+ if (module == NULL) {
+ ALOGW("setDeviceConnectionState(): could not find HW module for device %08x",
+ device);
+ return INVALID_OPERATION;
+ }
if (checkInputsForDevice(device, state, inputs, address) != NO_ERROR) {
return INVALID_OPERATION;
}
@@ -308,6 +317,7 @@ status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device,
index = mAvailableInputDevices.add(devDesc);
if (index >= 0) {
mAvailableInputDevices[index]->mId = nextUniqueId();
+ mAvailableInputDevices[index]->mModule = module;
} else {
return NO_MEMORY;
}
@@ -330,6 +340,7 @@ status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device,
closeAllInputs();
+ mpClientInterface->onAudioPortListUpdate();
return NO_ERROR;
} // end if is input device
@@ -342,9 +353,8 @@ audio_policy_dev_state_t AudioPolicyManager::getDeviceConnectionState(audio_devi
{
audio_policy_dev_state_t state = AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE;
String8 address = String8(device_address);
- sp<DeviceDescriptor> devDesc = new DeviceDescriptor(device,
- String8(device_address),
- 0);
+ sp<DeviceDescriptor> devDesc = new DeviceDescriptor(String8(""), device);
+ devDesc->mAddress = String8(device_address);
ssize_t index;
DeviceVector *deviceVector;
@@ -741,6 +751,7 @@ audio_io_handle_t AudioPolicyManager::getOutput(audio_stream_type_t stream,
}
mPreviousOutputs = mOutputs;
ALOGV("getOutput() returns new direct output %d", output);
+ mpClientInterface->onAudioPortListUpdate();
return output;
}
@@ -973,6 +984,7 @@ void AudioPolicyManager::releaseOutput(audio_io_handle_t output)
if (dstOutput != mPrimaryOutput) {
mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, mPrimaryOutput, dstOutput);
}
+ mpClientInterface->onAudioPortListUpdate();
}
}
}
@@ -1054,6 +1066,7 @@ audio_io_handle_t AudioPolicyManager::getInput(audio_source_t inputSource,
return 0;
}
addInput(input, inputDesc);
+ mpClientInterface->onAudioPortListUpdate();
return input;
}
@@ -1138,6 +1151,8 @@ void AudioPolicyManager::releaseInput(audio_io_handle_t input)
mpClientInterface->closeInput(input);
delete mInputs.valueAt(index);
mInputs.removeItem(input);
+ nextAudioPortGeneration();
+ mpClientInterface->onAudioPortListUpdate();
ALOGV("releaseInput() exit");
}
@@ -1146,6 +1161,7 @@ void AudioPolicyManager::closeAllInputs() {
mpClientInterface->closeInput(mInputs.keyAt(input_index));
}
mInputs.clear();
+ nextAudioPortGeneration();
}
void AudioPolicyManager::initStreamVolume(audio_stream_type_t stream,
@@ -1466,15 +1482,13 @@ status_t AudioPolicyManager::dump(int fd)
snprintf(buffer, SIZE, " Available output devices:\n");
result.append(buffer);
write(fd, result.string(), result.size());
- DeviceDescriptor::dumpHeader(fd, 2);
for (size_t i = 0; i < mAvailableOutputDevices.size(); i++) {
- mAvailableOutputDevices[i]->dump(fd, 2);
+ mAvailableOutputDevices[i]->dump(fd, 2, i);
}
snprintf(buffer, SIZE, "\n Available input devices:\n");
write(fd, buffer, strlen(buffer));
- DeviceDescriptor::dumpHeader(fd, 2);
for (size_t i = 0; i < mAvailableInputDevices.size(); i++) {
- mAvailableInputDevices[i]->dump(fd, 2);
+ mAvailableInputDevices[i]->dump(fd, 2, i);
}
snprintf(buffer, SIZE, "\nHW Modules dump:\n");
@@ -1595,6 +1609,549 @@ bool AudioPolicyManager::isOffloadSupported(const audio_offload_info_t& offloadI
return (profile != 0);
}
+status_t AudioPolicyManager::listAudioPorts(audio_port_role_t role,
+ audio_port_type_t type,
+ unsigned int *num_ports,
+ struct audio_port *ports,
+ unsigned int *generation)
+{
+ if (num_ports == NULL || (*num_ports != 0 && ports == NULL) ||
+ generation == NULL) {
+ return BAD_VALUE;
+ }
+ ALOGV("listAudioPorts() role %d type %d num_ports %d ports %p", role, type, *num_ports, ports);
+ if (ports == NULL) {
+ *num_ports = 0;
+ }
+
+ size_t portsWritten = 0;
+ size_t portsMax = *num_ports;
+ *num_ports = 0;
+ if (type == AUDIO_PORT_TYPE_NONE || type == AUDIO_PORT_TYPE_DEVICE) {
+ if (role == AUDIO_PORT_ROLE_SINK || role == AUDIO_PORT_ROLE_NONE) {
+ for (size_t i = 0;
+ i < mAvailableOutputDevices.size() && portsWritten < portsMax; i++) {
+ mAvailableOutputDevices[i]->toAudioPort(&ports[portsWritten++]);
+ }
+ *num_ports += mAvailableOutputDevices.size();
+ }
+ if (role == AUDIO_PORT_ROLE_SOURCE || role == AUDIO_PORT_ROLE_NONE) {
+ for (size_t i = 0;
+ i < mAvailableInputDevices.size() && portsWritten < portsMax; i++) {
+ mAvailableInputDevices[i]->toAudioPort(&ports[portsWritten++]);
+ }
+ *num_ports += mAvailableInputDevices.size();
+ }
+ }
+ if (type == AUDIO_PORT_TYPE_NONE || type == AUDIO_PORT_TYPE_MIX) {
+ if (role == AUDIO_PORT_ROLE_SINK || role == AUDIO_PORT_ROLE_NONE) {
+ for (size_t i = 0; i < mInputs.size() && portsWritten < portsMax; i++) {
+ mInputs[i]->toAudioPort(&ports[portsWritten++]);
+ }
+ *num_ports += mInputs.size();
+ }
+ if (role == AUDIO_PORT_ROLE_SOURCE || role == AUDIO_PORT_ROLE_NONE) {
+ for (size_t i = 0; i < mOutputs.size() && portsWritten < portsMax; i++) {
+ mOutputs[i]->toAudioPort(&ports[portsWritten++]);
+ }
+ *num_ports += mOutputs.size();
+ }
+ }
+ *generation = curAudioPortGeneration();
+ ALOGV("listAudioPorts() got %d ports needed %d", portsWritten, *num_ports);
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManager::getAudioPort(struct audio_port *port __unused)
+{
+ return NO_ERROR;
+}
+
+AudioPolicyManager::AudioOutputDescriptor *AudioPolicyManager::getOutputFromId(
+ audio_port_handle_t id) const
+{
+ AudioOutputDescriptor *outputDesc = NULL;
+ for (size_t i = 0; i < mOutputs.size(); i++) {
+ outputDesc = mOutputs.valueAt(i);
+ if (outputDesc->mId == id) {
+ break;
+ }
+ }
+ return outputDesc;
+}
+
+AudioPolicyManager::AudioInputDescriptor *AudioPolicyManager::getInputFromId(
+ audio_port_handle_t id) const
+{
+ AudioInputDescriptor *inputDesc = NULL;
+ for (size_t i = 0; i < mInputs.size(); i++) {
+ inputDesc = mInputs.valueAt(i);
+ if (inputDesc->mId == id) {
+ break;
+ }
+ }
+ return inputDesc;
+}
+
+AudioPolicyManager::HwModule *AudioPolicyManager::getModuleForDevice(audio_devices_t device) const
+{
+ for (size_t i = 0; i < mHwModules.size(); i++) {
+ if (mHwModules[i]->mHandle == 0) {
+ continue;
+ }
+ if (audio_is_output_device(device)) {
+ for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++)
+ {
+ if (mHwModules[i]->mOutputProfiles[j]->mSupportedDevices.types() & device) {
+ return mHwModules[i];
+ }
+ }
+ } else {
+ for (size_t j = 0; j < mHwModules[i]->mInputProfiles.size(); j++) {
+ if (mHwModules[i]->mInputProfiles[j]->mSupportedDevices.types() &
+ device & ~AUDIO_DEVICE_BIT_IN) {
+ return mHwModules[i];
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+AudioPolicyManager::HwModule *AudioPolicyManager::getModuleFromName(const char *name) const
+{
+ for (size_t i = 0; i < mHwModules.size(); i++)
+ {
+ if (strcmp(mHwModules[i]->mName, name) == 0) {
+ return mHwModules[i];
+ }
+ }
+ return NULL;
+}
+
+
+status_t AudioPolicyManager::createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle,
+ uid_t uid)
+{
+ ALOGV("createAudioPatch()");
+
+ if (handle == NULL || patch == NULL) {
+ return BAD_VALUE;
+ }
+ ALOGV("createAudioPatch() num sources %d num sinks %d", patch->num_sources, patch->num_sinks);
+
+ if (patch->num_sources > 1 || patch->num_sinks > 1) {
+ return INVALID_OPERATION;
+ }
+ if (patch->sources[0].role != AUDIO_PORT_ROLE_SOURCE ||
+ patch->sinks[0].role != AUDIO_PORT_ROLE_SINK) {
+ return INVALID_OPERATION;
+ }
+
+ sp<AudioPatch> patchDesc;
+ ssize_t index = mAudioPatches.indexOfKey(*handle);
+
+ ALOGV("createAudioPatch sink id %d role %d type %d", patch->sinks[0].id, patch->sinks[0].role,
+ patch->sinks[0].type);
+ ALOGV("createAudioPatch source id %d role %d type %d", patch->sources[0].id,
+ patch->sources[0].role,
+ patch->sources[0].type);
+
+ if (index >= 0) {
+ patchDesc = mAudioPatches.valueAt(index);
+ ALOGV("createAudioPatch() mUidCached %d patchDesc->mUid %d uid %d",
+ mUidCached, patchDesc->mUid, uid);
+ if (patchDesc->mUid != mUidCached && uid != patchDesc->mUid) {
+ return INVALID_OPERATION;
+ }
+ } else {
+ *handle = 0;
+ }
+
+ if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) {
+ // TODO add support for mix to mix connection
+ if (patch->sinks[0].type != AUDIO_PORT_TYPE_DEVICE) {
+ ALOGV("createAudioPatch() source mix sink not device");
+ return BAD_VALUE;
+ }
+ // output mix to output device connection
+ AudioOutputDescriptor *outputDesc = getOutputFromId(patch->sources[0].id);
+ if (outputDesc == NULL) {
+ ALOGV("createAudioPatch() output not found for id %d", patch->sources[0].id);
+ return BAD_VALUE;
+ }
+ if (patchDesc != 0) {
+ if (patchDesc->mPatch.sources[0].id != patch->sources[0].id) {
+ ALOGV("createAudioPatch() source id differs for patch current id %d new id %d",
+ patchDesc->mPatch.sources[0].id, patch->sources[0].id);
+ return BAD_VALUE;
+ }
+ }
+ sp<DeviceDescriptor> devDesc =
+ mAvailableOutputDevices.getDeviceFromId(patch->sinks[0].id);
+ if (devDesc == 0) {
+ ALOGV("createAudioPatch() out device not found for id %d", patch->sinks[0].id);
+ return BAD_VALUE;
+ }
+
+ if (!outputDesc->mProfile->isCompatibleProfile(devDesc->mType,
+ patch->sources[0].sample_rate,
+ patch->sources[0].format,
+ patch->sources[0].channel_mask,
+ AUDIO_OUTPUT_FLAG_NONE)) {
+ return INVALID_OPERATION;
+ }
+ // TODO: reconfigure output format and channels here
+ ALOGV("createAudioPatch() setting device %08x on output %d",
+ devDesc->mType, outputDesc->mIoHandle);
+ setOutputDevice(outputDesc->mIoHandle,
+ devDesc->mType,
+ true,
+ 0,
+ handle);
+ index = mAudioPatches.indexOfKey(*handle);
+ if (index >= 0) {
+ if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) {
+ ALOGW("createAudioPatch() setOutputDevice() did not reuse the patch provided");
+ }
+ patchDesc = mAudioPatches.valueAt(index);
+ patchDesc->mUid = uid;
+ ALOGV("createAudioPatch() success");
+ } else {
+ ALOGW("createAudioPatch() setOutputDevice() failed to create a patch");
+ return INVALID_OPERATION;
+ }
+ } else if (patch->sources[0].type == AUDIO_PORT_TYPE_DEVICE) {
+ if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
+ // input device to input mix connection
+ AudioInputDescriptor *inputDesc = getInputFromId(patch->sinks[0].id);
+ if (inputDesc == NULL) {
+ return BAD_VALUE;
+ }
+ if (patchDesc != 0) {
+ if (patchDesc->mPatch.sinks[0].id != patch->sinks[0].id) {
+ return BAD_VALUE;
+ }
+ }
+ sp<DeviceDescriptor> devDesc =
+ mAvailableInputDevices.getDeviceFromId(patch->sources[0].id);
+ if (devDesc == 0) {
+ return BAD_VALUE;
+ }
+
+ if (!inputDesc->mProfile->isCompatibleProfile(devDesc->mType,
+ patch->sinks[0].sample_rate,
+ patch->sinks[0].format,
+ patch->sinks[0].channel_mask,
+ AUDIO_OUTPUT_FLAG_NONE)) {
+ return INVALID_OPERATION;
+ }
+ // TODO: reconfigure output format and channels here
+ ALOGV("createAudioPatch() setting device %08x on output %d",
+ devDesc->mType, inputDesc->mIoHandle);
+ setInputDevice(inputDesc->mIoHandle,
+ devDesc->mType,
+ true,
+ handle);
+ index = mAudioPatches.indexOfKey(*handle);
+ if (index >= 0) {
+ if (patchDesc != 0 && patchDesc != mAudioPatches.valueAt(index)) {
+ ALOGW("createAudioPatch() setInputDevice() did not reuse the patch provided");
+ }
+ patchDesc = mAudioPatches.valueAt(index);
+ patchDesc->mUid = uid;
+ ALOGV("createAudioPatch() success");
+ } else {
+ ALOGW("createAudioPatch() setInputDevice() failed to create a patch");
+ return INVALID_OPERATION;
+ }
+ } else if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE) {
+ // device to device connection
+ if (patchDesc != 0) {
+ if (patchDesc->mPatch.sources[0].id != patch->sources[0].id &&
+ patchDesc->mPatch.sinks[0].id != patch->sinks[0].id) {
+ return BAD_VALUE;
+ }
+ }
+
+ sp<DeviceDescriptor> srcDeviceDesc =
+ mAvailableInputDevices.getDeviceFromId(patch->sources[0].id);
+ sp<DeviceDescriptor> sinkDeviceDesc =
+ mAvailableOutputDevices.getDeviceFromId(patch->sinks[0].id);
+ if (srcDeviceDesc == 0 || sinkDeviceDesc == 0) {
+ return BAD_VALUE;
+ }
+ //update source and sink with our own data as the data passed in the patch may
+ // be incomplete.
+ struct audio_patch newPatch = *patch;
+ srcDeviceDesc->toAudioPortConfig(&newPatch.sources[0], &patch->sources[0]);
+ sinkDeviceDesc->toAudioPortConfig(&newPatch.sinks[0], &patch->sinks[0]);
+
+ // TODO: add support for devices on different HW modules
+ if (srcDeviceDesc->mModule != sinkDeviceDesc->mModule) {
+ return INVALID_OPERATION;
+ }
+ // TODO: check from routing capabilities in config file and other conflicting patches
+
+ audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
+ if (index >= 0) {
+ afPatchHandle = patchDesc->mAfPatchHandle;
+ }
+
+ status_t status = mpClientInterface->createAudioPatch(&newPatch,
+ &afPatchHandle,
+ 0);
+ ALOGV("createAudioPatch() patch panel returned %d patchHandle %d",
+ status, afPatchHandle);
+ if (status == NO_ERROR) {
+ if (index < 0) {
+ patchDesc = new AudioPatch((audio_patch_handle_t)nextUniqueId(),
+ &newPatch, uid);
+ addAudioPatch(patchDesc->mHandle, patchDesc);
+ } else {
+ patchDesc->mPatch = newPatch;
+ }
+ patchDesc->mAfPatchHandle = afPatchHandle;
+ *handle = patchDesc->mHandle;
+ nextAudioPortGeneration();
+ mpClientInterface->onAudioPatchListUpdate();
+ } else {
+ ALOGW("createAudioPatch() patch panel could not connect device patch, error %d",
+ status);
+ return INVALID_OPERATION;
+ }
+ } else {
+ return BAD_VALUE;
+ }
+ } else {
+ return BAD_VALUE;
+ }
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManager::releaseAudioPatch(audio_patch_handle_t handle,
+ uid_t uid)
+{
+ ALOGV("releaseAudioPatch() patch %d", handle);
+
+ ssize_t index = mAudioPatches.indexOfKey(handle);
+
+ if (index < 0) {
+ return BAD_VALUE;
+ }
+ sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
+ ALOGV("releaseAudioPatch() mUidCached %d patchDesc->mUid %d uid %d",
+ mUidCached, patchDesc->mUid, uid);
+ if (patchDesc->mUid != mUidCached && uid != patchDesc->mUid) {
+ return INVALID_OPERATION;
+ }
+
+ struct audio_patch *patch = &patchDesc->mPatch;
+ patchDesc->mUid = mUidCached;
+ if (patch->sources[0].type == AUDIO_PORT_TYPE_MIX) {
+ AudioOutputDescriptor *outputDesc = getOutputFromId(patch->sources[0].id);
+ if (outputDesc == NULL) {
+ ALOGV("releaseAudioPatch() output not found for id %d", patch->sources[0].id);
+ return BAD_VALUE;
+ }
+
+ setOutputDevice(outputDesc->mIoHandle,
+ getNewOutputDevice(outputDesc->mIoHandle, true /*fromCache*/),
+ true,
+ 0,
+ NULL);
+ } else if (patch->sources[0].type == AUDIO_PORT_TYPE_DEVICE) {
+ if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
+ AudioInputDescriptor *inputDesc = getInputFromId(patch->sinks[0].id);
+ if (inputDesc == NULL) {
+ ALOGV("releaseAudioPatch() input not found for id %d", patch->sinks[0].id);
+ return BAD_VALUE;
+ }
+ setInputDevice(inputDesc->mIoHandle,
+ getNewInputDevice(inputDesc->mIoHandle),
+ true,
+ NULL);
+ } else if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE) {
+ audio_patch_handle_t afPatchHandle = patchDesc->mAfPatchHandle;
+ status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
+ ALOGV("releaseAudioPatch() patch panel returned %d patchHandle %d",
+ status, patchDesc->mAfPatchHandle);
+ removeAudioPatch(patchDesc->mHandle);
+ nextAudioPortGeneration();
+ mpClientInterface->onAudioPatchListUpdate();
+ } else {
+ return BAD_VALUE;
+ }
+ } else {
+ return BAD_VALUE;
+ }
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManager::listAudioPatches(unsigned int *num_patches,
+ struct audio_patch *patches,
+ unsigned int *generation)
+{
+ if (num_patches == NULL || (*num_patches != 0 && patches == NULL) ||
+ generation == NULL) {
+ return BAD_VALUE;
+ }
+ ALOGV("listAudioPatches() num_patches %d patches %p available patches %d",
+ *num_patches, patches, mAudioPatches.size());
+ if (patches == NULL) {
+ *num_patches = 0;
+ }
+
+ size_t patchesWritten = 0;
+ size_t patchesMax = *num_patches;
+ for (size_t i = 0;
+ i < mAudioPatches.size() && patchesWritten < patchesMax; i++) {
+ patches[patchesWritten] = mAudioPatches[i]->mPatch;
+ patches[patchesWritten++].id = mAudioPatches[i]->mHandle;
+ ALOGV("listAudioPatches() patch %d num_sources %d num_sinks %d",
+ i, mAudioPatches[i]->mPatch.num_sources, mAudioPatches[i]->mPatch.num_sinks);
+ }
+ *num_patches = mAudioPatches.size();
+
+ *generation = curAudioPortGeneration();
+ ALOGV("listAudioPatches() got %d patches needed %d", patchesWritten, *num_patches);
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManager::setAudioPortConfig(const struct audio_port_config *config)
+{
+ ALOGV("setAudioPortConfig()");
+
+ if (config == NULL) {
+ return BAD_VALUE;
+ }
+ ALOGV("setAudioPortConfig() on port handle %d", config->id);
+ // Only support gain configuration for now
+ if (config->config_mask != AUDIO_PORT_CONFIG_GAIN || config->gain.index < 0) {
+ return BAD_VALUE;
+ }
+
+ sp<AudioPort> portDesc;
+ struct audio_port_config portConfig;
+ if (config->type == AUDIO_PORT_TYPE_MIX) {
+ if (config->role == AUDIO_PORT_ROLE_SOURCE) {
+ AudioOutputDescriptor *outputDesc = getOutputFromId(config->id);
+ if (outputDesc == NULL) {
+ return BAD_VALUE;
+ }
+ portDesc = outputDesc->mProfile;
+ outputDesc->toAudioPortConfig(&portConfig);
+ } else if (config->role == AUDIO_PORT_ROLE_SINK) {
+ AudioInputDescriptor *inputDesc = getInputFromId(config->id);
+ if (inputDesc == NULL) {
+ return BAD_VALUE;
+ }
+ portDesc = inputDesc->mProfile;
+ inputDesc->toAudioPortConfig(&portConfig);
+ } else {
+ return BAD_VALUE;
+ }
+ } else if (config->type == AUDIO_PORT_TYPE_DEVICE) {
+ sp<DeviceDescriptor> deviceDesc;
+ if (config->role == AUDIO_PORT_ROLE_SOURCE) {
+ deviceDesc = mAvailableInputDevices.getDeviceFromId(config->id);
+ } else if (config->role == AUDIO_PORT_ROLE_SINK) {
+ deviceDesc = mAvailableOutputDevices.getDeviceFromId(config->id);
+ } else {
+ return BAD_VALUE;
+ }
+ if (deviceDesc == NULL) {
+ return BAD_VALUE;
+ }
+ portDesc = deviceDesc;
+ deviceDesc->toAudioPortConfig(&portConfig);
+ } else {
+ return BAD_VALUE;
+ }
+
+ if ((size_t)config->gain.index >= portDesc->mGains.size()) {
+ return INVALID_OPERATION;
+ }
+ const struct audio_gain *gain = &portDesc->mGains[config->gain.index]->mGain;
+ if ((config->gain.mode & ~gain->mode) != 0) {
+ return BAD_VALUE;
+ }
+ if ((config->gain.mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) {
+ if ((config->gain.values[0] < gain->min_value) ||
+ (config->gain.values[0] > gain->max_value)) {
+ return BAD_VALUE;
+ }
+ } else {
+ if ((config->gain.channel_mask & ~gain->channel_mask) != 0) {
+ return BAD_VALUE;
+ }
+ size_t numValues = popcount(config->gain.channel_mask);
+ for (size_t i = 0; i < numValues; i++) {
+ if ((config->gain.values[i] < gain->min_value) ||
+ (config->gain.values[i] > gain->max_value)) {
+ return BAD_VALUE;
+ }
+ }
+ }
+ if ((config->gain.mode & AUDIO_GAIN_MODE_RAMP) == AUDIO_GAIN_MODE_RAMP) {
+ if ((config->gain.ramp_duration_ms < gain->min_ramp_ms) ||
+ (config->gain.ramp_duration_ms > gain->max_ramp_ms)) {
+ return BAD_VALUE;
+ }
+ }
+
+ portConfig.gain = config->gain;
+
+ status_t status = mpClientInterface->setAudioPortConfig(&portConfig, 0);
+
+ return status;
+}
+
+void AudioPolicyManager::clearAudioPatches(uid_t uid)
+{
+ for (ssize_t i = 0; i < (ssize_t)mAudioPatches.size(); i++) {
+ sp<AudioPatch> patchDesc = mAudioPatches.valueAt(i);
+ if (patchDesc->mUid == uid) {
+ // releaseAudioPatch() removes the patch from mAudioPatches
+ if (releaseAudioPatch(mAudioPatches.keyAt(i), uid) == NO_ERROR) {
+ i--;
+ }
+ }
+ }
+}
+
+status_t AudioPolicyManager::addAudioPatch(audio_patch_handle_t handle,
+ const sp<AudioPatch>& patch)
+{
+ ssize_t index = mAudioPatches.indexOfKey(handle);
+
+ if (index >= 0) {
+ ALOGW("addAudioPatch() patch %d already in", handle);
+ return ALREADY_EXISTS;
+ }
+ mAudioPatches.add(handle, patch);
+ ALOGV("addAudioPatch() handle %d af handle %d num_sources %d num_sinks %d source handle %d"
+ "sink handle %d",
+ handle, patch->mAfPatchHandle, patch->mPatch.num_sources, patch->mPatch.num_sinks,
+ patch->mPatch.sources[0].id, patch->mPatch.sinks[0].id);
+ return NO_ERROR;
+}
+
+status_t AudioPolicyManager::removeAudioPatch(audio_patch_handle_t handle)
+{
+ ssize_t index = mAudioPatches.indexOfKey(handle);
+
+ if (index < 0) {
+ ALOGW("removeAudioPatch() patch %d not in", handle);
+ return ALREADY_EXISTS;
+ }
+ ALOGV("removeAudioPatch() handle %d af handle %d", handle,
+ mAudioPatches.valueAt(index)->mAfPatchHandle);
+ mAudioPatches.removeItemsAt(index);
+ return NO_ERROR;
+}
+
// ----------------------------------------------------------------------------
// AudioPolicyManager
// ----------------------------------------------------------------------------
@@ -1604,6 +2161,11 @@ uint32_t AudioPolicyManager::nextUniqueId()
return android_atomic_inc(&mNextUniqueId);
}
+uint32_t AudioPolicyManager::nextAudioPortGeneration()
+{
+ return android_atomic_inc(&mAudioPortGeneration);
+}
+
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface)
:
#ifdef AUDIO_POLICY_TEST
@@ -1614,15 +2176,17 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa
mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0),
mA2dpSuspended(false),
- mSpeakerDrcEnabled(false), mNextUniqueId(0)
+ mSpeakerDrcEnabled(false), mNextUniqueId(1),
+ mAudioPortGeneration(1)
{
+ mUidCached = getuid();
mpClientInterface = clientInterface;
for (int i = 0; i < AUDIO_POLICY_FORCE_USE_CNT; i++) {
mForceUse[i] = AUDIO_POLICY_FORCE_NONE;
}
- mDefaultOutputDevice = new DeviceDescriptor(AUDIO_DEVICE_OUT_SPEAKER);
+ mDefaultOutputDevice = new DeviceDescriptor(String8(""), AUDIO_DEVICE_OUT_SPEAKER);
if (loadAudioPolicyConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE) != NO_ERROR) {
if (loadAudioPolicyConfig(AUDIO_POLICY_CONFIG_FILE) != NO_ERROR) {
ALOGE("could not load audio policy configuration file, setting defaults");
@@ -1683,6 +2247,7 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa
// give a valid ID to an attached device once confirmed it is reachable
if ((index >= 0) && (mAvailableOutputDevices[index]->mId == 0)) {
mAvailableOutputDevices[index]->mId = nextUniqueId();
+ mAvailableOutputDevices[index]->mModule = mHwModules[i];
}
}
if (mPrimaryOutput == 0 &&
@@ -1690,6 +2255,7 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa
mPrimaryOutput = output;
}
addOutput(output, outputDesc);
+ ALOGI("CSTOR setOutputDevice %08x", outputDesc->mDevice);
setOutputDevice(output,
outputDesc->mDevice,
true);
@@ -1728,6 +2294,7 @@ AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterfa
// give a valid ID to an attached device once confirmed it is reachable
if ((index >= 0) && (mAvailableInputDevices[index]->mId == 0)) {
mAvailableInputDevices[index]->mId = nextUniqueId();
+ mAvailableInputDevices[index]->mModule = mHwModules[i];
}
}
mpClientInterface->closeInput(input);
@@ -1973,6 +2540,7 @@ void AudioPolicyManager::addOutput(audio_io_handle_t output, AudioOutputDescript
outputDesc->mIoHandle = output;
outputDesc->mId = nextUniqueId();
mOutputs.add(output, outputDesc);
+ nextAudioPortGeneration();
}
void AudioPolicyManager::addInput(audio_io_handle_t input, AudioInputDescriptor *inputDesc)
@@ -1980,6 +2548,7 @@ void AudioPolicyManager::addInput(audio_io_handle_t input, AudioInputDescriptor
inputDesc->mIoHandle = input;
inputDesc->mId = nextUniqueId();
mInputs.add(input, inputDesc);
+ nextAudioPortGeneration();
}
String8 AudioPolicyManager::addressToParameter(audio_devices_t device, const String8 address)
@@ -2152,6 +2721,7 @@ status_t AudioPolicyManager::checkOutputsForDevice(audio_devices_t device,
mPrimaryOutput, output);
mpClientInterface->closeOutput(output);
mOutputs.removeItem(output);
+ nextAudioPortGeneration();
output = 0;
}
}
@@ -2438,6 +3008,7 @@ void AudioPolicyManager::closeOutput(audio_io_handle_t output)
delete outputDesc;
mOutputs.removeItem(output);
mPreviousOutputs = mOutputs;
+ nextAudioPortGeneration();
}
SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevice(audio_devices_t device,
@@ -2590,6 +3161,17 @@ audio_devices_t AudioPolicyManager::getNewOutputDevice(audio_io_handle_t output,
audio_devices_t device = AUDIO_DEVICE_NONE;
AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
+
+ ssize_t index = mAudioPatches.indexOfKey(outputDesc->mPatchHandle);
+ if (index >= 0) {
+ sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
+ if (patchDesc->mUid != mUidCached) {
+ ALOGV("getNewOutputDevice() device %08x forced by patch %d",
+ outputDesc->device(), outputDesc->mPatchHandle);
+ return outputDesc->device();
+ }
+ }
+
// check the following by order of priority to request a routing change if necessary:
// 1: the strategy enforced audible is active on the output:
// use device for strategy enforced audible
@@ -2625,6 +3207,17 @@ audio_devices_t AudioPolicyManager::getNewOutputDevice(audio_io_handle_t output,
audio_devices_t AudioPolicyManager::getNewInputDevice(audio_io_handle_t input)
{
AudioInputDescriptor *inputDesc = mInputs.valueFor(input);
+
+ ssize_t index = mAudioPatches.indexOfKey(inputDesc->mPatchHandle);
+ if (index >= 0) {
+ sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index);
+ if (patchDesc->mUid != mUidCached) {
+ ALOGV("getNewInputDevice() device %08x forced by patch %d",
+ inputDesc->mDevice, inputDesc->mPatchHandle);
+ return inputDesc->mDevice;
+ }
+ }
+
audio_devices_t device = getDeviceForInputSource(inputDesc->mInputSource);
ALOGV("getNewInputDevice() selected device %x", device);
@@ -2636,15 +3229,22 @@ uint32_t AudioPolicyManager::getStrategyForStream(audio_stream_type_t stream) {
}
audio_devices_t AudioPolicyManager::getDevicesForStream(audio_stream_type_t stream) {
- audio_devices_t devices;
// By checking the range of stream before calling getStrategy, we avoid
// getStrategy's behavior for invalid streams. getStrategy would do a ALOGE
// and then return STRATEGY_MEDIA, but we want to return the empty set.
if (stream < (audio_stream_type_t) 0 || stream >= AUDIO_STREAM_CNT) {
- devices = AUDIO_DEVICE_NONE;
- } else {
- AudioPolicyManager::routing_strategy strategy = getStrategy(stream);
- devices = getDeviceForStrategy(strategy, true /*fromCache*/);
+ return AUDIO_DEVICE_NONE;
+ }
+ audio_devices_t devices;
+ AudioPolicyManager::routing_strategy strategy = getStrategy(stream);
+ devices = getDeviceForStrategy(strategy, true /*fromCache*/);
+ SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(devices, mOutputs);
+ for (size_t i = 0; i < outputs.size(); i++) {
+ AudioOutputDescriptor *outputDesc = mOutputs.valueFor(outputs[i]);
+ if (outputDesc->isStrategyActive(strategy)) {
+ devices = outputDesc->device();
+ break;
+ }
}
return devices;
}
@@ -2990,7 +3590,8 @@ uint32_t AudioPolicyManager::checkDeviceMuteStrategies(AudioOutputDescriptor *ou
uint32_t AudioPolicyManager::setOutputDevice(audio_io_handle_t output,
audio_devices_t device,
bool force,
- int delayMs)
+ int delayMs,
+ audio_patch_handle_t *patchHandle)
{
ALOGV("setOutputDevice() output %d device %04x delayMs %d", output, device, delayMs);
AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
@@ -3034,7 +3635,7 @@ uint32_t AudioPolicyManager::setOutputDevice(audio_io_handle_t output,
// do the routing
if (device == AUDIO_DEVICE_NONE) {
- resetOutputDevice(output, delayMs);
+ resetOutputDevice(output, delayMs, NULL);
} else {
DeviceVector deviceList = mAvailableOutputDevices.getDevicesFromType(device);
if (!deviceList.isEmpty()) {
@@ -3044,18 +3645,43 @@ uint32_t AudioPolicyManager::setOutputDevice(audio_io_handle_t output,
patch.num_sinks = 0;
for (size_t i = 0; i < deviceList.size() && i < AUDIO_PATCH_PORTS_MAX; i++) {
deviceList.itemAt(i)->toAudioPortConfig(&patch.sinks[i]);
- patch.sinks[i].ext.device.hw_module = patch.sources[0].ext.mix.hw_module;
patch.num_sinks++;
}
- audio_patch_handle_t patchHandle = outputDesc->mPatchHandle;
+ ssize_t index;
+ if (patchHandle && *patchHandle != AUDIO_PATCH_HANDLE_NONE) {
+ index = mAudioPatches.indexOfKey(*patchHandle);
+ } else {
+ index = mAudioPatches.indexOfKey(outputDesc->mPatchHandle);
+ }
+ sp< AudioPatch> patchDesc;
+ audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
+ if (index >= 0) {
+ patchDesc = mAudioPatches.valueAt(index);
+ afPatchHandle = patchDesc->mAfPatchHandle;
+ }
+
status_t status = mpClientInterface->createAudioPatch(&patch,
- &patchHandle,
- delayMs);
+ &afPatchHandle,
+ delayMs);
ALOGV("setOutputDevice() createAudioPatch returned %d patchHandle %d"
"num_sources %d num_sinks %d",
- status, patchHandle, patch.num_sources, patch.num_sinks);
+ status, afPatchHandle, patch.num_sources, patch.num_sinks);
if (status == NO_ERROR) {
- outputDesc->mPatchHandle = patchHandle;
+ if (index < 0) {
+ patchDesc = new AudioPatch((audio_patch_handle_t)nextUniqueId(),
+ &patch, mUidCached);
+ addAudioPatch(patchDesc->mHandle, patchDesc);
+ } else {
+ patchDesc->mPatch = patch;
+ }
+ patchDesc->mAfPatchHandle = afPatchHandle;
+ patchDesc->mUid = mUidCached;
+ if (patchHandle) {
+ *patchHandle = patchDesc->mHandle;
+ }
+ outputDesc->mPatchHandle = patchDesc->mHandle;
+ nextAudioPortGeneration();
+ mpClientInterface->onAudioPatchListUpdate();
}
}
}
@@ -3067,21 +3693,33 @@ uint32_t AudioPolicyManager::setOutputDevice(audio_io_handle_t output,
}
status_t AudioPolicyManager::resetOutputDevice(audio_io_handle_t output,
- int delayMs)
+ int delayMs,
+ audio_patch_handle_t *patchHandle)
{
AudioOutputDescriptor *outputDesc = mOutputs.valueFor(output);
- if (outputDesc->mPatchHandle == 0) {
+ ssize_t index;
+ if (patchHandle) {
+ index = mAudioPatches.indexOfKey(*patchHandle);
+ } else {
+ index = mAudioPatches.indexOfKey(outputDesc->mPatchHandle);
+ }
+ if (index < 0) {
return INVALID_OPERATION;
}
- status_t status = mpClientInterface->releaseAudioPatch(outputDesc->mPatchHandle, delayMs);
+ sp< AudioPatch> patchDesc = mAudioPatches.valueAt(index);
+ status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, delayMs);
ALOGV("resetOutputDevice() releaseAudioPatch returned %d", status);
outputDesc->mPatchHandle = 0;
+ removeAudioPatch(patchDesc->mHandle);
+ nextAudioPortGeneration();
+ mpClientInterface->onAudioPatchListUpdate();
return status;
}
status_t AudioPolicyManager::setInputDevice(audio_io_handle_t input,
audio_devices_t device,
- bool force)
+ bool force,
+ audio_patch_handle_t *patchHandle)
{
status_t status = NO_ERROR;
@@ -3096,31 +3734,67 @@ status_t AudioPolicyManager::setInputDevice(audio_io_handle_t input,
patch.num_sinks = 1;
//only one input device for now
deviceList.itemAt(0)->toAudioPortConfig(&patch.sources[0]);
- patch.sources[0].ext.device.hw_module = patch.sinks[0].ext.mix.hw_module;
patch.num_sources = 1;
- audio_patch_handle_t patchHandle = inputDesc->mPatchHandle;
+ ssize_t index;
+ if (patchHandle && *patchHandle != AUDIO_PATCH_HANDLE_NONE) {
+ index = mAudioPatches.indexOfKey(*patchHandle);
+ } else {
+ index = mAudioPatches.indexOfKey(inputDesc->mPatchHandle);
+ }
+ sp< AudioPatch> patchDesc;
+ audio_patch_handle_t afPatchHandle = AUDIO_PATCH_HANDLE_NONE;
+ if (index >= 0) {
+ patchDesc = mAudioPatches.valueAt(index);
+ afPatchHandle = patchDesc->mAfPatchHandle;
+ }
+
status_t status = mpClientInterface->createAudioPatch(&patch,
- &patchHandle,
+ &afPatchHandle,
0);
ALOGV("setInputDevice() createAudioPatch returned %d patchHandle %d",
- status, patchHandle);
+ status, afPatchHandle);
if (status == NO_ERROR) {
- inputDesc->mPatchHandle = patchHandle;
+ if (index < 0) {
+ patchDesc = new AudioPatch((audio_patch_handle_t)nextUniqueId(),
+ &patch, mUidCached);
+ addAudioPatch(patchDesc->mHandle, patchDesc);
+ } else {
+ patchDesc->mPatch = patch;
+ }
+ patchDesc->mAfPatchHandle = afPatchHandle;
+ patchDesc->mUid = mUidCached;
+ if (patchHandle) {
+ *patchHandle = patchDesc->mHandle;
+ }
+ inputDesc->mPatchHandle = patchDesc->mHandle;
+ nextAudioPortGeneration();
+ mpClientInterface->onAudioPatchListUpdate();
}
}
}
return status;
}
-status_t AudioPolicyManager::resetInputDevice(audio_io_handle_t input)
+status_t AudioPolicyManager::resetInputDevice(audio_io_handle_t input,
+ audio_patch_handle_t *patchHandle)
{
AudioInputDescriptor *inputDesc = mInputs.valueFor(input);
- if (inputDesc->mPatchHandle == 0) {
+ ssize_t index;
+ if (patchHandle) {
+ index = mAudioPatches.indexOfKey(*patchHandle);
+ } else {
+ index = mAudioPatches.indexOfKey(inputDesc->mPatchHandle);
+ }
+ if (index < 0) {
return INVALID_OPERATION;
}
- status_t status = mpClientInterface->releaseAudioPatch(inputDesc->mPatchHandle, 0);
+ sp< AudioPatch> patchDesc = mAudioPatches.valueAt(index);
+ status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0);
ALOGV("resetInputDevice() releaseAudioPatch returned %d", status);
inputDesc->mPatchHandle = 0;
+ removeAudioPatch(patchDesc->mHandle);
+ nextAudioPortGeneration();
+ mpClientInterface->onAudioPatchListUpdate();
return status;
}
@@ -3723,6 +4397,7 @@ uint32_t AudioPolicyManager::getMaxEffectsMemory()
return MAX_EFFECTS_MEMORY;
}
+
// --- AudioOutputDescriptor class implementation
AudioPolicyManager::AudioOutputDescriptor::AudioOutputDescriptor(
@@ -3849,20 +4524,37 @@ bool AudioPolicyManager::AudioOutputDescriptor::isStreamActive(audio_stream_type
}
void AudioPolicyManager::AudioOutputDescriptor::toAudioPortConfig(
- struct audio_port_config *config) const
-{
- config->id = mId;
- config->role = AUDIO_PORT_ROLE_SOURCE;
- config->type = AUDIO_PORT_TYPE_MIX;
- config->sample_rate = mSamplingRate;
- config->channel_mask = mChannelMask;
- config->format = mFormat;
- config->gain.index = -1;
- config->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK|
+ struct audio_port_config *dstConfig,
+ const struct audio_port_config *srcConfig) const
+{
+ dstConfig->id = mId;
+ dstConfig->role = AUDIO_PORT_ROLE_SOURCE;
+ dstConfig->type = AUDIO_PORT_TYPE_MIX;
+ dstConfig->sample_rate = mSamplingRate;
+ dstConfig->channel_mask = mChannelMask;
+ dstConfig->format = mFormat;
+ dstConfig->gain.index = -1;
+ dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK|
AUDIO_PORT_CONFIG_FORMAT;
- config->ext.mix.hw_module = mProfile->mModule->mHandle;
- config->ext.mix.handle = mIoHandle;
- config->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT;
+ // use supplied variable configuration parameters if any
+ if (srcConfig != NULL) {
+ if (srcConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
+ dstConfig->sample_rate = srcConfig->sample_rate;
+ }
+ if (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
+ dstConfig->channel_mask = srcConfig->channel_mask;
+ }
+ if (srcConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT) {
+ dstConfig->format = srcConfig->format;
+ }
+ if (srcConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) {
+ dstConfig->gain = srcConfig->gain;
+ dstConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN;
+ }
+ }
+ dstConfig->ext.mix.hw_module = mProfile->mModule->mHandle;
+ dstConfig->ext.mix.handle = mIoHandle;
+ dstConfig->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT;
}
void AudioPolicyManager::AudioOutputDescriptor::toAudioPort(
@@ -3870,6 +4562,8 @@ void AudioPolicyManager::AudioOutputDescriptor::toAudioPort(
{
mProfile->toAudioPort(port);
port->id = mId;
+ toAudioPortConfig(&port->active_config);
+ port->ext.mix.hw_module = mProfile->mModule->mHandle;
port->ext.mix.handle = mIoHandle;
port->ext.mix.latency_class =
mFlags & AUDIO_OUTPUT_FLAG_FAST ? AUDIO_LATENCY_LOW : AUDIO_LATENCY_NORMAL;
@@ -3921,21 +4615,34 @@ AudioPolicyManager::AudioInputDescriptor::AudioInputDescriptor(const sp<IOProfil
}
void AudioPolicyManager::AudioInputDescriptor::toAudioPortConfig(
- struct audio_port_config *config) const
-{
- config->id = mId;
- config->role = AUDIO_PORT_ROLE_SINK;
- config->type = AUDIO_PORT_TYPE_MIX;
- config->sample_rate = mSamplingRate;
- config->channel_mask = mChannelMask;
- config->format = mFormat;
- config->gain.index = -1;
- config->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK|
- AUDIO_PORT_CONFIG_FORMAT;
- config->ext.mix.hw_module = mProfile->mModule->mHandle;
- config->ext.mix.handle = mIoHandle;
- config->ext.mix.usecase.source = (mInputSource == AUDIO_SOURCE_HOTWORD) ?
- AUDIO_SOURCE_VOICE_RECOGNITION : mInputSource;
+ struct audio_port_config *dstConfig,
+ const struct audio_port_config *srcConfig) const
+{
+ dstConfig->id = mId;
+ dstConfig->role = AUDIO_PORT_ROLE_SINK;
+ dstConfig->type = AUDIO_PORT_TYPE_MIX;
+ dstConfig->sample_rate = mSamplingRate;
+ dstConfig->channel_mask = mChannelMask;
+ dstConfig->format = mFormat;
+ dstConfig->gain.index = -1;
+ dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK|
+ AUDIO_PORT_CONFIG_FORMAT;
+ // use supplied variable configuration parameters if any
+ if (srcConfig != NULL) {
+ if (srcConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
+ dstConfig->sample_rate = srcConfig->sample_rate;
+ }
+ if (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
+ dstConfig->channel_mask = srcConfig->channel_mask;
+ }
+ if (srcConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT) {
+ dstConfig->format = srcConfig->format;
+ }
+ if (srcConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) {
+ dstConfig->gain = srcConfig->gain;
+ dstConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN;
+ }
+ }
}
void AudioPolicyManager::AudioInputDescriptor::toAudioPort(
@@ -3943,6 +4650,8 @@ void AudioPolicyManager::AudioInputDescriptor::toAudioPort(
{
mProfile->toAudioPort(port);
port->id = mId;
+ toAudioPortConfig(&port->active_config);
+ port->ext.mix.hw_module = mProfile->mModule->mHandle;
port->ext.mix.handle = mIoHandle;
port->ext.mix.latency_class = AUDIO_LATENCY_NORMAL;
}
@@ -4047,6 +4756,140 @@ AudioPolicyManager::HwModule::~HwModule()
free((void *)mName);
}
+status_t AudioPolicyManager::HwModule::loadInput(cnode *root)
+{
+ cnode *node = root->first_child;
+
+ sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SINK, this);
+
+ while (node) {
+ if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
+ profile->loadSamplingRates((char *)node->value);
+ } else if (strcmp(node->name, FORMATS_TAG) == 0) {
+ profile->loadFormats((char *)node->value);
+ } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
+ profile->loadInChannels((char *)node->value);
+ } else if (strcmp(node->name, DEVICES_TAG) == 0) {
+ profile->mSupportedDevices.loadDevicesFromName((char *)node->value,
+ mDeclaredDevices);
+ } else if (strcmp(node->name, GAINS_TAG) == 0) {
+ profile->loadGains(node);
+ }
+ node = node->next;
+ }
+ ALOGW_IF(profile->mSupportedDevices.isEmpty(),
+ "loadInput() invalid supported devices");
+ ALOGW_IF(profile->mChannelMasks.size() == 0,
+ "loadInput() invalid supported channel masks");
+ ALOGW_IF(profile->mSamplingRates.size() == 0,
+ "loadInput() invalid supported sampling rates");
+ ALOGW_IF(profile->mFormats.size() == 0,
+ "loadInput() invalid supported formats");
+ if (!profile->mSupportedDevices.isEmpty() &&
+ (profile->mChannelMasks.size() != 0) &&
+ (profile->mSamplingRates.size() != 0) &&
+ (profile->mFormats.size() != 0)) {
+
+ ALOGV("loadInput() adding input Supported Devices %04x",
+ profile->mSupportedDevices.types());
+
+ mInputProfiles.add(profile);
+ return NO_ERROR;
+ } else {
+ return BAD_VALUE;
+ }
+}
+
+status_t AudioPolicyManager::HwModule::loadOutput(cnode *root)
+{
+ cnode *node = root->first_child;
+
+ sp<IOProfile> profile = new IOProfile(String8(root->name), AUDIO_PORT_ROLE_SOURCE, this);
+
+ while (node) {
+ if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
+ profile->loadSamplingRates((char *)node->value);
+ } else if (strcmp(node->name, FORMATS_TAG) == 0) {
+ profile->loadFormats((char *)node->value);
+ } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
+ profile->loadOutChannels((char *)node->value);
+ } else if (strcmp(node->name, DEVICES_TAG) == 0) {
+ profile->mSupportedDevices.loadDevicesFromName((char *)node->value,
+ mDeclaredDevices);
+ } else if (strcmp(node->name, FLAGS_TAG) == 0) {
+ profile->mFlags = parseFlagNames((char *)node->value);
+ } else if (strcmp(node->name, GAINS_TAG) == 0) {
+ profile->loadGains(node);
+ }
+ node = node->next;
+ }
+ ALOGW_IF(profile->mSupportedDevices.isEmpty(),
+ "loadOutput() invalid supported devices");
+ ALOGW_IF(profile->mChannelMasks.size() == 0,
+ "loadOutput() invalid supported channel masks");
+ ALOGW_IF(profile->mSamplingRates.size() == 0,
+ "loadOutput() invalid supported sampling rates");
+ ALOGW_IF(profile->mFormats.size() == 0,
+ "loadOutput() invalid supported formats");
+ if (!profile->mSupportedDevices.isEmpty() &&
+ (profile->mChannelMasks.size() != 0) &&
+ (profile->mSamplingRates.size() != 0) &&
+ (profile->mFormats.size() != 0)) {
+
+ ALOGV("loadOutput() adding output Supported Devices %04x, mFlags %04x",
+ profile->mSupportedDevices.types(), profile->mFlags);
+
+ mOutputProfiles.add(profile);
+ return NO_ERROR;
+ } else {
+ return BAD_VALUE;
+ }
+}
+
+status_t AudioPolicyManager::HwModule::loadDevice(cnode *root)
+{
+ cnode *node = root->first_child;
+
+ audio_devices_t type = AUDIO_DEVICE_NONE;
+ while (node) {
+ if (strcmp(node->name, DEVICE_TYPE) == 0) {
+ type = parseDeviceNames((char *)node->value);
+ break;
+ }
+ node = node->next;
+ }
+ if (type == AUDIO_DEVICE_NONE ||
+ (!audio_is_input_device(type) && !audio_is_output_device(type))) {
+ ALOGW("loadDevice() bad type %08x", type);
+ return BAD_VALUE;
+ }
+ sp<DeviceDescriptor> deviceDesc = new DeviceDescriptor(String8(root->name), type);
+ deviceDesc->mModule = this;
+
+ node = root->first_child;
+ while (node) {
+ if (strcmp(node->name, DEVICE_ADDRESS) == 0) {
+ deviceDesc->mAddress = String8((char *)node->value);
+ } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
+ if (audio_is_input_device(type)) {
+ deviceDesc->loadInChannels((char *)node->value);
+ } else {
+ deviceDesc->loadOutChannels((char *)node->value);
+ }
+ } else if (strcmp(node->name, GAINS_TAG) == 0) {
+ deviceDesc->loadGains(node);
+ }
+ node = node->next;
+ }
+
+ ALOGV("loadDevice() adding device name %s type %08x address %s",
+ deviceDesc->mName.string(), type, deviceDesc->mAddress.string());
+
+ mDeclaredDevices.add(deviceDesc);
+
+ return NO_ERROR;
+}
+
void AudioPolicyManager::HwModule::dump(int fd)
{
const size_t SIZE = 256;
@@ -4074,6 +4917,12 @@ void AudioPolicyManager::HwModule::dump(int fd)
mInputProfiles[i]->dump(fd);
}
}
+ if (mDeclaredDevices.size()) {
+ write(fd, " - devices:\n", strlen(" - devices:\n"));
+ for (size_t i = 0; i < mDeclaredDevices.size(); i++) {
+ mDeclaredDevices[i]->dump(fd, 4, i);
+ }
+ }
}
// --- AudioPort class implementation
@@ -4095,7 +4944,13 @@ void AudioPolicyManager::AudioPort::toAudioPort(struct audio_port *port) const
port->formats[i] = mFormats[i];
}
port->num_formats = i;
- port->num_gains = 0;
+
+ ALOGV("AudioPort::toAudioPort() num gains %d", mGains.size());
+
+ for (i = 0; i < mGains.size() && i < AUDIO_PORT_MAX_GAINS; i++) {
+ port->gains[i] = mGains[i]->mGain;
+ }
+ port->num_gains = i;
}
@@ -4118,7 +4973,6 @@ void AudioPolicyManager::AudioPort::loadSamplingRates(char *name)
}
str = strtok(NULL, "|");
}
- return;
}
void AudioPolicyManager::AudioPort::loadFormats(char *name)
@@ -4141,7 +4995,6 @@ void AudioPolicyManager::AudioPort::loadFormats(char *name)
}
str = strtok(NULL, "|");
}
- return;
}
void AudioPolicyManager::AudioPort::loadInChannels(char *name)
@@ -4166,7 +5019,6 @@ void AudioPolicyManager::AudioPort::loadInChannels(char *name)
}
str = strtok(NULL, "|");
}
- return;
}
void AudioPolicyManager::AudioPort::loadOutChannels(char *name)
@@ -4195,10 +5047,174 @@ void AudioPolicyManager::AudioPort::loadOutChannels(char *name)
return;
}
+audio_gain_mode_t AudioPolicyManager::AudioPort::loadGainMode(char *name)
+{
+ const char *str = strtok(name, "|");
+
+ ALOGV("loadGainMode() %s", name);
+ audio_gain_mode_t mode = 0;
+ while (str != NULL) {
+ mode |= (audio_gain_mode_t)stringToEnum(sGainModeNameToEnumTable,
+ ARRAY_SIZE(sGainModeNameToEnumTable),
+ str);
+ str = strtok(NULL, "|");
+ }
+ return mode;
+}
+
+void AudioPolicyManager::AudioPort::loadGain(cnode *root)
+{
+ cnode *node = root->first_child;
+
+ sp<AudioGain> gain = new AudioGain();
+
+ while (node) {
+ if (strcmp(node->name, GAIN_MODE) == 0) {
+ gain->mGain.mode = loadGainMode((char *)node->value);
+ } else if (strcmp(node->name, GAIN_CHANNELS) == 0) {
+ if ((mType == AUDIO_PORT_TYPE_DEVICE && mRole == AUDIO_PORT_ROLE_SOURCE) ||
+ (mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK)) {
+ gain->mGain.channel_mask =
+ (audio_channel_mask_t)stringToEnum(sInChannelsNameToEnumTable,
+ ARRAY_SIZE(sInChannelsNameToEnumTable),
+ (char *)node->value);
+ } else {
+ gain->mGain.channel_mask =
+ (audio_channel_mask_t)stringToEnum(sOutChannelsNameToEnumTable,
+ ARRAY_SIZE(sOutChannelsNameToEnumTable),
+ (char *)node->value);
+ }
+ } else if (strcmp(node->name, GAIN_MIN_VALUE) == 0) {
+ gain->mGain.min_value = atoi((char *)node->value);
+ } else if (strcmp(node->name, GAIN_MAX_VALUE) == 0) {
+ gain->mGain.max_value = atoi((char *)node->value);
+ } else if (strcmp(node->name, GAIN_DEFAULT_VALUE) == 0) {
+ gain->mGain.default_value = atoi((char *)node->value);
+ } else if (strcmp(node->name, GAIN_STEP_VALUE) == 0) {
+ gain->mGain.step_value = atoi((char *)node->value);
+ } else if (strcmp(node->name, GAIN_MIN_RAMP_MS) == 0) {
+ gain->mGain.min_ramp_ms = atoi((char *)node->value);
+ } else if (strcmp(node->name, GAIN_MAX_RAMP_MS) == 0) {
+ gain->mGain.max_ramp_ms = atoi((char *)node->value);
+ }
+ node = node->next;
+ }
+
+ ALOGV("loadGain() adding new gain mode %08x channel mask %08x min mB %d max mB %d",
+ gain->mGain.mode, gain->mGain.channel_mask, gain->mGain.min_value, gain->mGain.max_value);
+
+ if (gain->mGain.mode == 0) {
+ return;
+ }
+ mGains.add(gain);
+}
+
+void AudioPolicyManager::AudioPort::loadGains(cnode *root)
+{
+ cnode *node = root->first_child;
+ while (node) {
+ ALOGV("loadGains() loading gain %s", node->name);
+ loadGain(node);
+ node = node->next;
+ }
+}
+
+void AudioPolicyManager::AudioPort::dump(int fd, int spaces) const
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ if (mName.size() != 0) {
+ snprintf(buffer, SIZE, "%*s- name: %s\n", spaces, "", mName.string());
+ result.append(buffer);
+ }
+
+ if (mSamplingRates.size() != 0) {
+ snprintf(buffer, SIZE, "%*s- sampling rates: ", spaces, "");
+ result.append(buffer);
+ for (size_t i = 0; i < mSamplingRates.size(); i++) {
+ snprintf(buffer, SIZE, "%d", mSamplingRates[i]);
+ result.append(buffer);
+ result.append(i == (mSamplingRates.size() - 1) ? "" : ", ");
+ }
+ result.append("\n");
+ }
+
+ if (mChannelMasks.size() != 0) {
+ snprintf(buffer, SIZE, "%*s- channel masks: ", spaces, "");
+ result.append(buffer);
+ for (size_t i = 0; i < mChannelMasks.size(); i++) {
+ snprintf(buffer, SIZE, "0x%04x", mChannelMasks[i]);
+ result.append(buffer);
+ result.append(i == (mChannelMasks.size() - 1) ? "" : ", ");
+ }
+ result.append("\n");
+ }
+
+ if (mFormats.size() != 0) {
+ snprintf(buffer, SIZE, "%*s- formats: ", spaces, "");
+ result.append(buffer);
+ for (size_t i = 0; i < mFormats.size(); i++) {
+ snprintf(buffer, SIZE, "%-48s", enumToString(sFormatNameToEnumTable,
+ ARRAY_SIZE(sFormatNameToEnumTable),
+ mFormats[i]));
+ result.append(buffer);
+ result.append(i == (mFormats.size() - 1) ? "" : ", ");
+ }
+ result.append("\n");
+ }
+ write(fd, result.string(), result.size());
+ if (mGains.size() != 0) {
+ snprintf(buffer, SIZE, "%*s- gains:\n", spaces, "");
+ write(fd, buffer, strlen(buffer) + 1);
+ result.append(buffer);
+ for (size_t i = 0; i < mGains.size(); i++) {
+ mGains[i]->dump(fd, spaces + 2, i);
+ }
+ }
+}
+
+// --- AudioGain class implementation
+
+AudioPolicyManager::AudioGain::AudioGain()
+{
+ memset(&mGain, 0, sizeof(struct audio_gain));
+}
+
+void AudioPolicyManager::AudioGain::dump(int fd, int spaces, int index) const
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, "%*sGain %d:\n", spaces, "", index+1);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- mode: %08x\n", spaces, "", mGain.mode);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- channel_mask: %08x\n", spaces, "", mGain.channel_mask);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- min_value: %d mB\n", spaces, "", mGain.min_value);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- max_value: %d mB\n", spaces, "", mGain.max_value);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- default_value: %d mB\n", spaces, "", mGain.default_value);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- step_value: %d mB\n", spaces, "", mGain.step_value);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- min_ramp_ms: %d ms\n", spaces, "", mGain.min_ramp_ms);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "%*s- max_ramp_ms: %d ms\n", spaces, "", mGain.max_ramp_ms);
+ result.append(buffer);
+
+ write(fd, result.string(), result.size());
+}
+
// --- IOProfile class implementation
-AudioPolicyManager::IOProfile::IOProfile(audio_port_role_t role, HwModule *module)
- : AudioPort(AUDIO_PORT_TYPE_MIX, role, module), mFlags((audio_output_flags_t)0)
+AudioPolicyManager::IOProfile::IOProfile(const String8& name, audio_port_role_t role,
+ HwModule *module)
+ : AudioPort(name, AUDIO_PORT_TYPE_MIX, role, module), mFlags((audio_output_flags_t)0)
{
}
@@ -4262,42 +5278,16 @@ void AudioPolicyManager::IOProfile::dump(int fd)
char buffer[SIZE];
String8 result;
- snprintf(buffer, SIZE, " - sampling rates: ");
- result.append(buffer);
- for (size_t i = 0; i < mSamplingRates.size(); i++) {
- snprintf(buffer, SIZE, "%d", mSamplingRates[i]);
- result.append(buffer);
- result.append(i == (mSamplingRates.size() - 1) ? "\n" : ", ");
- }
-
- snprintf(buffer, SIZE, " - channel masks: ");
- result.append(buffer);
- for (size_t i = 0; i < mChannelMasks.size(); i++) {
- snprintf(buffer, SIZE, "0x%04x", mChannelMasks[i]);
- result.append(buffer);
- result.append(i == (mChannelMasks.size() - 1) ? "\n" : ", ");
- }
+ AudioPort::dump(fd, 4);
- snprintf(buffer, SIZE, " - formats: ");
+ snprintf(buffer, SIZE, " - flags: 0x%04x\n", mFlags);
result.append(buffer);
- for (size_t i = 0; i < mFormats.size(); i++) {
- snprintf(buffer, SIZE, "0x%08x", mFormats[i]);
- result.append(buffer);
- result.append(i == (mFormats.size() - 1) ? "\n" : ", ");
- }
-
snprintf(buffer, SIZE, " - devices:\n");
result.append(buffer);
write(fd, result.string(), result.size());
- DeviceDescriptor::dumpHeader(fd, 6);
for (size_t i = 0; i < mSupportedDevices.size(); i++) {
- mSupportedDevices[i]->dump(fd, 6);
+ mSupportedDevices[i]->dump(fd, 6, i);
}
-
- snprintf(buffer, SIZE, " - flags: 0x%04x\n", mFlags);
- result.append(buffer);
-
- write(fd, result.string(), result.size());
}
void AudioPolicyManager::IOProfile::log()
@@ -4402,10 +5392,33 @@ void AudioPolicyManager::DeviceVector::loadDevicesFromType(audio_devices_t types
uint32_t i = 31 - __builtin_clz(types);
uint32_t type = 1 << i;
types &= ~type;
- add(new DeviceDescriptor(type | role_bit));
+ add(new DeviceDescriptor(String8(""), type | role_bit));
}
}
+void AudioPolicyManager::DeviceVector::loadDevicesFromName(char *name,
+ const DeviceVector& declaredDevices)
+{
+ char *devName = strtok(name, "|");
+ while (devName != NULL) {
+ if (strlen(devName) != 0) {
+ audio_devices_t type = stringToEnum(sDeviceNameToEnumTable,
+ ARRAY_SIZE(sDeviceNameToEnumTable),
+ devName);
+ if (type != AUDIO_DEVICE_NONE) {
+ add(new DeviceDescriptor(String8(""), type));
+ } else {
+ sp<DeviceDescriptor> deviceDesc =
+ declaredDevices.getDeviceFromName(String8(devName));
+ if (deviceDesc != 0) {
+ add(deviceDesc);
+ }
+ }
+ }
+ devName = strtok(NULL, "|");
+ }
+}
+
sp<AudioPolicyManager::DeviceDescriptor> AudioPolicyManager::DeviceVector::getDevice(
audio_devices_t type, String8 address) const
{
@@ -4423,6 +5436,20 @@ sp<AudioPolicyManager::DeviceDescriptor> AudioPolicyManager::DeviceVector::getDe
return device;
}
+sp<AudioPolicyManager::DeviceDescriptor> AudioPolicyManager::DeviceVector::getDeviceFromId(
+ audio_port_handle_t id) const
+{
+ sp<DeviceDescriptor> device;
+ for (size_t i = 0; i < size(); i++) {
+ ALOGV("DeviceVector::getDeviceFromId(%d) itemAt(%d)->mId %d", id, i, itemAt(i)->mId);
+ if (itemAt(i)->mId == id) {
+ device = itemAt(i);
+ break;
+ }
+ }
+ return device;
+}
+
AudioPolicyManager::DeviceVector AudioPolicyManager::DeviceVector::getDevicesFromType(
audio_devices_t type) const
{
@@ -4438,51 +5465,83 @@ AudioPolicyManager::DeviceVector AudioPolicyManager::DeviceVector::getDevicesFro
return devices;
}
-void AudioPolicyManager::DeviceDescriptor::toAudioPortConfig(struct audio_port_config *config) const
+sp<AudioPolicyManager::DeviceDescriptor> AudioPolicyManager::DeviceVector::getDeviceFromName(
+ const String8& name) const
{
- config->id = mId;
- config->role = audio_is_output_device(mDeviceType) ?
+ sp<DeviceDescriptor> device;
+ for (size_t i = 0; i < size(); i++) {
+ if (itemAt(i)->mName == name) {
+ device = itemAt(i);
+ break;
+ }
+ }
+ return device;
+}
+
+void AudioPolicyManager::DeviceDescriptor::toAudioPortConfig(
+ struct audio_port_config *dstConfig,
+ const struct audio_port_config *srcConfig) const
+{
+ dstConfig->id = mId;
+ dstConfig->role = audio_is_output_device(mDeviceType) ?
AUDIO_PORT_ROLE_SINK : AUDIO_PORT_ROLE_SOURCE;
- config->type = AUDIO_PORT_TYPE_DEVICE;
- config->sample_rate = 0;
- config->channel_mask = mChannelMask;
- config->format = AUDIO_FORMAT_DEFAULT;
- config->config_mask = AUDIO_PORT_CONFIG_CHANNEL_MASK;
- config->gain.index = -1;
- config->ext.device.type = mDeviceType;
- strncpy(config->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN);
+ dstConfig->type = AUDIO_PORT_TYPE_DEVICE;
+ dstConfig->channel_mask = mChannelMask;
+ dstConfig->gain.index = -1;
+ dstConfig->config_mask = AUDIO_PORT_CONFIG_CHANNEL_MASK;
+ // use supplied variable configuration parameters if any
+ if (srcConfig != NULL) {
+ if (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
+ dstConfig->channel_mask = srcConfig->channel_mask;
+ }
+ if (srcConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) {
+ dstConfig->gain = srcConfig->gain;
+ dstConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN;
+ }
+ }
+ dstConfig->ext.device.type = mDeviceType;
+ dstConfig->ext.device.hw_module = mModule->mHandle;
+ strncpy(dstConfig->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN);
}
void AudioPolicyManager::DeviceDescriptor::toAudioPort(struct audio_port *port) const
{
+ ALOGV("DeviceVector::toAudioPort() handle %d type %x", mId, mDeviceType);
AudioPort::toAudioPort(port);
port->id = mId;
+ toAudioPortConfig(&port->active_config);
port->ext.device.type = mDeviceType;
+ port->ext.device.hw_module = mModule->mHandle;
strncpy(port->ext.device.address, mAddress.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN);
}
-void AudioPolicyManager::DeviceDescriptor::dumpHeader(int fd, int spaces)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
-
- snprintf(buffer, SIZE, "%*s%-48s %-2s %-8s %-32s \n",
- spaces, "", "Type", "ID", "Cnl Mask", "Address");
- write(fd, buffer, strlen(buffer));
-}
-
-status_t AudioPolicyManager::DeviceDescriptor::dump(int fd, int spaces) const
+status_t AudioPolicyManager::DeviceDescriptor::dump(int fd, int spaces, int index) const
{
const size_t SIZE = 256;
char buffer[SIZE];
+ String8 result;
- snprintf(buffer, SIZE, "%*s%-48s %2d %08x %-32s \n",
- spaces, "",
- enumToString(sDeviceNameToEnumTable,
- ARRAY_SIZE(sDeviceNameToEnumTable),
- mDeviceType),
- mId, mChannelMask, mAddress.string());
- write(fd, buffer, strlen(buffer));
+ snprintf(buffer, SIZE, "%*sDevice %d:\n", spaces, "", index+1);
+ result.append(buffer);
+ if (mId != 0) {
+ snprintf(buffer, SIZE, "%*s- id: %2d\n", spaces, "", mId);
+ result.append(buffer);
+ }
+ snprintf(buffer, SIZE, "%*s- type: %-48s\n", spaces, "",
+ enumToString(sDeviceNameToEnumTable,
+ ARRAY_SIZE(sDeviceNameToEnumTable),
+ mDeviceType));
+ result.append(buffer);
+ if (mAddress.size() != 0) {
+ snprintf(buffer, SIZE, "%*s- address: %-32s\n", spaces, "", mAddress.string());
+ result.append(buffer);
+ }
+ if (mChannelMask != AUDIO_CHANNEL_NONE) {
+ snprintf(buffer, SIZE, "%*s- channel mask: %08x\n", spaces, "", mChannelMask);
+ result.append(buffer);
+ }
+ write(fd, result.string(), result.size());
+ AudioPort::dump(fd, spaces);
return NO_ERROR;
}
@@ -4531,102 +5590,30 @@ audio_devices_t AudioPolicyManager::parseDeviceNames(char *name)
return device;
}
-status_t AudioPolicyManager::loadInput(cnode *root, HwModule *module)
-{
- cnode *node = root->first_child;
-
- sp<IOProfile> profile = new IOProfile(AUDIO_PORT_ROLE_SINK, module);
-
- while (node) {
- if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
- profile->loadSamplingRates((char *)node->value);
- } else if (strcmp(node->name, FORMATS_TAG) == 0) {
- profile->loadFormats((char *)node->value);
- } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
- profile->loadInChannels((char *)node->value);
- } else if (strcmp(node->name, DEVICES_TAG) == 0) {
- profile->mSupportedDevices.loadDevicesFromType(parseDeviceNames((char *)node->value));
- }
- node = node->next;
- }
- ALOGW_IF(profile->mSupportedDevices.isEmpty(),
- "loadInput() invalid supported devices");
- ALOGW_IF(profile->mChannelMasks.size() == 0,
- "loadInput() invalid supported channel masks");
- ALOGW_IF(profile->mSamplingRates.size() == 0,
- "loadInput() invalid supported sampling rates");
- ALOGW_IF(profile->mFormats.size() == 0,
- "loadInput() invalid supported formats");
- if (!profile->mSupportedDevices.isEmpty() &&
- (profile->mChannelMasks.size() != 0) &&
- (profile->mSamplingRates.size() != 0) &&
- (profile->mFormats.size() != 0)) {
-
- ALOGV("loadInput() adding input Supported Devices %04x",
- profile->mSupportedDevices.types());
-
- module->mInputProfiles.add(profile);
- return NO_ERROR;
- } else {
- return BAD_VALUE;
- }
-}
-
-status_t AudioPolicyManager::loadOutput(cnode *root, HwModule *module)
-{
- cnode *node = root->first_child;
-
- sp<IOProfile> profile = new IOProfile(AUDIO_PORT_ROLE_SOURCE, module);
-
- while (node) {
- if (strcmp(node->name, SAMPLING_RATES_TAG) == 0) {
- profile->loadSamplingRates((char *)node->value);
- } else if (strcmp(node->name, FORMATS_TAG) == 0) {
- profile->loadFormats((char *)node->value);
- } else if (strcmp(node->name, CHANNELS_TAG) == 0) {
- profile->loadOutChannels((char *)node->value);
- } else if (strcmp(node->name, DEVICES_TAG) == 0) {
- profile->mSupportedDevices.loadDevicesFromType(parseDeviceNames((char *)node->value));
- } else if (strcmp(node->name, FLAGS_TAG) == 0) {
- profile->mFlags = parseFlagNames((char *)node->value);
- }
- node = node->next;
- }
- ALOGW_IF(profile->mSupportedDevices.isEmpty(),
- "loadOutput() invalid supported devices");
- ALOGW_IF(profile->mChannelMasks.size() == 0,
- "loadOutput() invalid supported channel masks");
- ALOGW_IF(profile->mSamplingRates.size() == 0,
- "loadOutput() invalid supported sampling rates");
- ALOGW_IF(profile->mFormats.size() == 0,
- "loadOutput() invalid supported formats");
- if (!profile->mSupportedDevices.isEmpty() &&
- (profile->mChannelMasks.size() != 0) &&
- (profile->mSamplingRates.size() != 0) &&
- (profile->mFormats.size() != 0)) {
-
- ALOGV("loadOutput() adding output Supported Devices %04x, mFlags %04x",
- profile->mSupportedDevices.types(), profile->mFlags);
-
- module->mOutputProfiles.add(profile);
- return NO_ERROR;
- } else {
- return BAD_VALUE;
- }
-}
-
void AudioPolicyManager::loadHwModule(cnode *root)
{
- cnode *node = config_find(root, OUTPUTS_TAG);
status_t status = NAME_NOT_FOUND;
-
+ cnode *node;
HwModule *module = new HwModule(root->name);
+ node = config_find(root, DEVICES_TAG);
+ if (node != NULL) {
+ node = node->first_child;
+ while (node) {
+ ALOGV("loadHwModule() loading device %s", node->name);
+ status_t tmpStatus = module->loadDevice(node);
+ if (status == NAME_NOT_FOUND || status == NO_ERROR) {
+ status = tmpStatus;
+ }
+ node = node->next;
+ }
+ }
+ node = config_find(root, OUTPUTS_TAG);
if (node != NULL) {
node = node->first_child;
while (node) {
ALOGV("loadHwModule() loading output %s", node->name);
- status_t tmpStatus = loadOutput(node, module);
+ status_t tmpStatus = module->loadOutput(node);
if (status == NAME_NOT_FOUND || status == NO_ERROR) {
status = tmpStatus;
}
@@ -4638,13 +5625,15 @@ void AudioPolicyManager::loadHwModule(cnode *root)
node = node->first_child;
while (node) {
ALOGV("loadHwModule() loading input %s", node->name);
- status_t tmpStatus = loadInput(node, module);
+ status_t tmpStatus = module->loadInput(node);
if (status == NAME_NOT_FOUND || status == NO_ERROR) {
status = tmpStatus;
}
node = node->next;
}
}
+ loadGlobalConfig(root, module);
+
if (status == NO_ERROR) {
mHwModules.add(module);
} else {
@@ -4667,16 +5656,22 @@ void AudioPolicyManager::loadHwModules(cnode *root)
}
}
-void AudioPolicyManager::loadGlobalConfig(cnode *root)
+void AudioPolicyManager::loadGlobalConfig(cnode *root, HwModule *module)
{
cnode *node = config_find(root, GLOBAL_CONFIG_TAG);
if (node == NULL) {
return;
}
+ DeviceVector declaredDevices;
+ if (module != NULL) {
+ declaredDevices = module->mDeclaredDevices;
+ }
+
node = node->first_child;
while (node) {
if (strcmp(ATTACHED_OUTPUT_DEVICES_TAG, node->name) == 0) {
- mAvailableOutputDevices.loadDevicesFromType(parseDeviceNames((char *)node->value));
+ mAvailableOutputDevices.loadDevicesFromName((char *)node->value,
+ declaredDevices);
ALOGV("loadGlobalConfig() Attached Output Devices %08x",
mAvailableOutputDevices.types());
} else if (strcmp(DEFAULT_OUTPUT_DEVICE_TAG, node->name) == 0) {
@@ -4684,13 +5679,14 @@ void AudioPolicyManager::loadGlobalConfig(cnode *root)
ARRAY_SIZE(sDeviceNameToEnumTable),
(char *)node->value);
if (device != AUDIO_DEVICE_NONE) {
- mDefaultOutputDevice = new DeviceDescriptor(device);
+ mDefaultOutputDevice = new DeviceDescriptor(String8(""), device);
} else {
ALOGW("loadGlobalConfig() default device not specified");
}
ALOGV("loadGlobalConfig() mDefaultOutputDevice %08x", mDefaultOutputDevice->mDeviceType);
} else if (strcmp(ATTACHED_INPUT_DEVICES_TAG, node->name) == 0) {
- mAvailableInputDevices.loadDevicesFromType(parseDeviceNames((char *)node->value));
+ mAvailableInputDevices.loadDevicesFromName((char *)node->value,
+ declaredDevices);
ALOGV("loadGlobalConfig() Available InputDevices %08x", mAvailableInputDevices.types());
} else if (strcmp(SPEAKER_DRC_ENABLED_TAG, node->name) == 0) {
mSpeakerDrcEnabled = stringToBool((char *)node->value);
@@ -4712,9 +5708,9 @@ status_t AudioPolicyManager::loadAudioPolicyConfig(const char *path)
root = config_node("", "");
config_load(root, data);
- loadGlobalConfig(root);
loadHwModules(root);
-
+ // legacy audio_policy.conf files have one global_configuration section
+ loadGlobalConfig(root, getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY));
config_free(root);
free(root);
free(data);
@@ -4728,13 +5724,13 @@ void AudioPolicyManager::defaultAudioPolicyConfig(void)
{
HwModule *module;
sp<IOProfile> profile;
- sp<DeviceDescriptor> defaultInputDevice = new DeviceDescriptor(AUDIO_DEVICE_IN_BUILTIN_MIC);
+ sp<DeviceDescriptor> defaultInputDevice = new DeviceDescriptor(String8(""), AUDIO_DEVICE_IN_BUILTIN_MIC);
mAvailableOutputDevices.add(mDefaultOutputDevice);
mAvailableInputDevices.add(defaultInputDevice);
module = new HwModule("primary");
- profile = new IOProfile(AUDIO_PORT_ROLE_SOURCE, module);
+ profile = new IOProfile(String8("primary"), AUDIO_PORT_ROLE_SOURCE, module);
profile->mSamplingRates.add(44100);
profile->mFormats.add(AUDIO_FORMAT_PCM_16_BIT);
profile->mChannelMasks.add(AUDIO_CHANNEL_OUT_STEREO);
@@ -4742,7 +5738,7 @@ void AudioPolicyManager::defaultAudioPolicyConfig(void)
profile->mFlags = AUDIO_OUTPUT_FLAG_PRIMARY;
module->mOutputProfiles.add(profile);
- profile = new IOProfile(AUDIO_PORT_ROLE_SINK, module);
+ profile = new IOProfile(String8("primary"), AUDIO_PORT_ROLE_SINK, module);
profile->mSamplingRates.add(8000);
profile->mFormats.add(AUDIO_FORMAT_PCM_16_BIT);
profile->mChannelMasks.add(AUDIO_CHANNEL_IN_MONO);
diff --git a/services/audiopolicy/AudioPolicyManager.h b/services/audiopolicy/AudioPolicyManager.h
index 905a3c8..e012d63 100644
--- a/services/audiopolicy/AudioPolicyManager.h
+++ b/services/audiopolicy/AudioPolicyManager.h
@@ -140,6 +140,23 @@ public:
virtual bool isOffloadSupported(const audio_offload_info_t& offloadInfo);
+ virtual status_t listAudioPorts(audio_port_role_t role,
+ audio_port_type_t type,
+ unsigned int *num_ports,
+ struct audio_port *ports,
+ unsigned int *generation);
+ virtual status_t getAudioPort(struct audio_port *port);
+ virtual status_t createAudioPatch(const struct audio_patch *patch,
+ audio_patch_handle_t *handle,
+ uid_t uid);
+ virtual status_t releaseAudioPatch(audio_patch_handle_t handle,
+ uid_t uid);
+ virtual status_t listAudioPatches(unsigned int *num_patches,
+ struct audio_patch *patches,
+ unsigned int *generation);
+ virtual status_t setAudioPortConfig(const struct audio_port_config *config);
+ virtual void clearAudioPatches(uid_t uid);
+
protected:
enum routing_strategy {
@@ -173,26 +190,25 @@ protected:
DEVICE_CATEGORY_CNT
};
- class IOProfile;
+ class HwModule;
- class HwModule {
+ class AudioGain: public RefBase
+ {
public:
- HwModule(const char *name);
- ~HwModule();
+ AudioGain();
+ virtual ~AudioGain() {}
- void dump(int fd);
+ void dump(int fd, int spaces, int index) const;
- const char *const mName; // base name of the audio HW module (primary, a2dp ...)
- audio_module_handle_t mHandle;
- Vector < sp<IOProfile> > mOutputProfiles; // output profiles exposed by this module
- Vector < sp<IOProfile> > mInputProfiles; // input profiles exposed by this module
+ struct audio_gain mGain;
};
class AudioPort: public RefBase
{
public:
- AudioPort(audio_port_type_t type, audio_port_role_t role, HwModule *module) :
- mType(type), mRole(role), mModule(module) {}
+ AudioPort(const String8& name, audio_port_type_t type,
+ audio_port_role_t role, HwModule *module) :
+ mName(name), mType(type), mRole(role), mModule(module) {}
virtual ~AudioPort() {}
virtual void toAudioPort(struct audio_port *port) const;
@@ -202,32 +218,52 @@ protected:
void loadOutChannels(char *name);
void loadInChannels(char *name);
- audio_port_type_t mType;
- audio_port_role_t mRole;
+ audio_gain_mode_t loadGainMode(char *name);
+ void loadGain(cnode *root);
+ void loadGains(cnode *root);
+
+ void dump(int fd, int spaces) const;
+
+ String8 mName;
+ audio_port_type_t mType;
+ audio_port_role_t mRole;
// by convention, "0' in the first entry in mSamplingRates, mChannelMasks or mFormats
// indicates the supported parameters should be read from the output stream
// after it is opened for the first time
Vector <uint32_t> mSamplingRates; // supported sampling rates
Vector <audio_channel_mask_t> mChannelMasks; // supported channel masks
Vector <audio_format_t> mFormats; // supported audio formats
- HwModule *mModule; // audio HW module exposing this I/O stream
+ Vector < sp<AudioGain> > mGains; // gain controllers
+ HwModule *mModule; // audio HW module exposing this I/O stream
};
+ class AudioPatch: public RefBase
+ {
+ public:
+ AudioPatch(audio_patch_handle_t handle,
+ const struct audio_patch *patch, uid_t uid) :
+ mHandle(handle), mPatch(*patch), mUid(uid), mAfPatchHandle(0) {}
+
+ audio_patch_handle_t mHandle;
+ struct audio_patch mPatch;
+ uid_t mUid;
+ audio_patch_handle_t mAfPatchHandle;
+ };
class DeviceDescriptor: public AudioPort
{
public:
- DeviceDescriptor(audio_devices_t type, String8 address,
+ DeviceDescriptor(const String8& name, audio_devices_t type, String8 address,
audio_channel_mask_t channelMask) :
- AudioPort(AUDIO_PORT_TYPE_DEVICE,
+ AudioPort(name, AUDIO_PORT_TYPE_DEVICE,
audio_is_output_device(type) ? AUDIO_PORT_ROLE_SINK :
AUDIO_PORT_ROLE_SOURCE,
NULL),
mDeviceType(type), mAddress(address),
mChannelMask(channelMask), mId(0) {}
- DeviceDescriptor(audio_devices_t type) :
- AudioPort(AUDIO_PORT_TYPE_DEVICE,
+ DeviceDescriptor(String8 name, audio_devices_t type) :
+ AudioPort(name, AUDIO_PORT_TYPE_DEVICE,
audio_is_output_device(type) ? AUDIO_PORT_ROLE_SINK :
AUDIO_PORT_ROLE_SOURCE,
NULL),
@@ -236,11 +272,12 @@ protected:
virtual ~DeviceDescriptor() {}
bool equals(const sp<DeviceDescriptor>& other) const;
- void toAudioPortConfig(struct audio_port_config *config) const;
+ void toAudioPortConfig(struct audio_port_config *dstConfig,
+ const struct audio_port_config *srcConfig = NULL) const;
+
virtual void toAudioPort(struct audio_port *port) const;
- status_t dump(int fd, int spaces) const;
- static void dumpHeader(int fd, int spaces);
+ status_t dump(int fd, int spaces, int index) const;
audio_devices_t mDeviceType;
String8 mAddress;
@@ -260,8 +297,12 @@ protected:
audio_devices_t types() const { return mDeviceTypes; }
void loadDevicesFromType(audio_devices_t types);
+ void loadDevicesFromName(char *name, const DeviceVector& declaredDevices);
+
sp<DeviceDescriptor> getDevice(audio_devices_t type, String8 address) const;
DeviceVector getDevicesFromType(audio_devices_t types) const;
+ sp<DeviceDescriptor> getDeviceFromId(audio_port_handle_t id) const;
+ sp<DeviceDescriptor> getDeviceFromName(const String8& name) const;
private:
void refreshTypes();
@@ -276,7 +317,7 @@ protected:
class IOProfile : public AudioPort
{
public:
- IOProfile(audio_port_role_t role, HwModule *module);
+ IOProfile(const String8& name, audio_port_role_t role, HwModule *module);
virtual ~IOProfile();
bool isCompatibleProfile(audio_devices_t device,
@@ -294,6 +335,25 @@ protected:
// direct output...). For outputs only.
};
+ class HwModule {
+ public:
+ HwModule(const char *name);
+ ~HwModule();
+
+ status_t loadOutput(cnode *root);
+ status_t loadInput(cnode *root);
+ status_t loadDevice(cnode *root);
+
+ void dump(int fd);
+
+ const char *const mName; // base name of the audio HW module (primary, a2dp ...)
+ audio_module_handle_t mHandle;
+ Vector < sp<IOProfile> > mOutputProfiles; // output profiles exposed by this module
+ Vector < sp<IOProfile> > mInputProfiles; // input profiles exposed by this module
+ DeviceVector mDeclaredDevices; // devices declared in audio_policy.conf
+
+ };
+
// default volume curve
static const VolumeCurvePoint sDefaultVolumeCurve[AudioPolicyManager::VOLCNT];
// default volume curve for media strategy
@@ -335,7 +395,8 @@ protected:
uint32_t inPastMs = 0,
nsecs_t sysTime = 0) const;
- void toAudioPortConfig(struct audio_port_config *config) const;
+ void toAudioPortConfig(struct audio_port_config *dstConfig,
+ const struct audio_port_config *srcConfig = NULL) const;
void toAudioPort(struct audio_port *port) const;
audio_port_handle_t mId;
@@ -379,7 +440,8 @@ protected:
audio_source_t mInputSource; // input source selected by application (mediarecorder.h)
const sp<IOProfile> mProfile; // I/O profile this output derives from
- void toAudioPortConfig(struct audio_port_config *config) const;
+ void toAudioPortConfig(struct audio_port_config *dstConfig,
+ const struct audio_port_config *srcConfig = NULL) const;
void toAudioPort(struct audio_port *port) const;
};
@@ -439,13 +501,17 @@ protected:
uint32_t setOutputDevice(audio_io_handle_t output,
audio_devices_t device,
bool force = false,
- int delayMs = 0);
+ int delayMs = 0,
+ audio_patch_handle_t *patchHandle = NULL);
status_t resetOutputDevice(audio_io_handle_t output,
- int delayMs = 0);
+ int delayMs = 0,
+ audio_patch_handle_t *patchHandle = NULL);
status_t setInputDevice(audio_io_handle_t input,
audio_devices_t device,
- bool force = false);
- status_t resetInputDevice(audio_io_handle_t input);
+ bool force = false,
+ audio_patch_handle_t *patchHandle = NULL);
+ status_t resetInputDevice(audio_io_handle_t input,
+ audio_patch_handle_t *patchHandle = NULL);
// select input device corresponding to requested audio source
virtual audio_devices_t getDeviceForInputSource(audio_source_t inputSource);
@@ -589,6 +655,14 @@ protected:
bool isNonOffloadableEffectEnabled();
+ status_t addAudioPatch(audio_patch_handle_t handle,
+ const sp<AudioPatch>& patch);
+ status_t removeAudioPatch(audio_patch_handle_t handle);
+
+ AudioOutputDescriptor *getOutputFromId(audio_port_handle_t id) const;
+ AudioInputDescriptor *getInputFromId(audio_port_handle_t id) const;
+ HwModule *getModuleForDevice(audio_devices_t device) const;
+ HwModule *getModuleFromName(const char *name) const;
//
// Audio policy configuration file parsing (audio_policy.conf)
//
@@ -601,15 +675,14 @@ protected:
static bool stringToBool(const char *value);
static audio_output_flags_t parseFlagNames(char *name);
static audio_devices_t parseDeviceNames(char *name);
- status_t loadOutput(cnode *root, HwModule *module);
- status_t loadInput(cnode *root, HwModule *module);
void loadHwModule(cnode *root);
void loadHwModules(cnode *root);
- void loadGlobalConfig(cnode *root);
+ void loadGlobalConfig(cnode *root, HwModule *module);
status_t loadAudioPolicyConfig(const char *path);
void defaultAudioPolicyConfig(void);
+ uid_t mUidCached;
AudioPolicyClientInterface *mpClientInterface; // audio policy client interface
audio_io_handle_t mPrimaryOutput; // primary output handle
// list of descriptors for outputs currently opened
@@ -618,10 +691,8 @@ protected:
// reset to mOutputs when updateDevicesAndOutputs() is called.
DefaultKeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mPreviousOutputs;
DefaultKeyedVector<audio_io_handle_t, AudioInputDescriptor *> mInputs; // list of input descriptors
- DeviceVector mAvailableOutputDevices; // bit field of all available output devices
- DeviceVector mAvailableInputDevices; // bit field of all available input devices
- // without AUDIO_DEVICE_BIT_IN to allow direct bit
- // field comparisons
+ DeviceVector mAvailableOutputDevices; // all available output devices
+ DeviceVector mAvailableInputDevices; // all available input devices
int mPhoneState; // current phone state
audio_policy_forced_cfg_t mForceUse[AUDIO_POLICY_FORCE_USE_CNT]; // current forced use configuration
@@ -644,6 +715,9 @@ protected:
Vector <HwModule *> mHwModules;
volatile int32_t mNextUniqueId;
+ volatile int32_t mAudioPortGeneration;
+
+ DefaultKeyedVector<audio_patch_handle_t, sp<AudioPatch> > mAudioPatches;
#ifdef AUDIO_POLICY_TEST
Mutex mLock;
@@ -668,6 +742,8 @@ private:
void handleNotificationRoutingForStream(audio_stream_type_t stream);
static bool isVirtualInputDevice(audio_devices_t device);
uint32_t nextUniqueId();
+ uint32_t nextAudioPortGeneration();
+ uint32_t curAudioPortGeneration() const { return mAudioPortGeneration; }
// converts device address to string sent to audio HAL via setParameters
static String8 addressToParameter(audio_devices_t device, const String8 address);
};
diff --git a/services/audiopolicy/AudioPolicyService.cpp b/services/audiopolicy/AudioPolicyService.cpp
index ea573a4..a2a0461 100644
--- a/services/audiopolicy/AudioPolicyService.cpp
+++ b/services/audiopolicy/AudioPolicyService.cpp
@@ -148,6 +148,61 @@ AudioPolicyService::~AudioPolicyService()
delete mAudioPolicyManager;
delete mAudioPolicyClient;
#endif
+
+ mNotificationClients.clear();
+}
+
+// A notification client is always registered by AudioSystem when the client process
+// connects to AudioPolicyService.
+void AudioPolicyService::registerClient(const sp<IAudioPolicyServiceClient>& client)
+{
+
+ Mutex::Autolock _l(mLock);
+
+ uid_t uid = IPCThreadState::self()->getCallingUid();
+ if (mNotificationClients.indexOfKey(uid) < 0) {
+ sp<NotificationClient> notificationClient = new NotificationClient(this,
+ client,
+ uid);
+ ALOGV("registerClient() client %p, uid %d", client.get(), uid);
+
+ mNotificationClients.add(uid, notificationClient);
+
+ sp<IBinder> binder = client->asBinder();
+ binder->linkToDeath(notificationClient);
+ }
+}
+
+// removeNotificationClient() is called when the client process dies.
+void AudioPolicyService::removeNotificationClient(uid_t uid)
+{
+ Mutex::Autolock _l(mLock);
+
+ mNotificationClients.removeItem(uid);
+
+#ifndef USE_LEGACY_AUDIO_POLICY
+ if (mAudioPolicyManager) {
+ mAudioPolicyManager->clearAudioPatches(uid);
+ }
+#endif
+}
+
+void AudioPolicyService::onAudioPortListUpdate()
+{
+ mOutputCommandThread->updateAudioPortListCommand();
+}
+
+void AudioPolicyService::doOnAudioPortListUpdate()
+{
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mNotificationClients.size(); i++) {
+ mNotificationClients.valueAt(i)->onAudioPortListUpdate();
+ }
+}
+
+void AudioPolicyService::onAudioPatchListUpdate()
+{
+ mOutputCommandThread->updateAudioPatchListCommand();
}
status_t AudioPolicyService::clientCreateAudioPatch(const struct audio_patch *patch,
@@ -163,6 +218,53 @@ status_t AudioPolicyService::clientReleaseAudioPatch(audio_patch_handle_t handle
return mAudioCommandThread->releaseAudioPatchCommand(handle, delayMs);
}
+void AudioPolicyService::doOnAudioPatchListUpdate()
+{
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mNotificationClients.size(); i++) {
+ mNotificationClients.valueAt(i)->onAudioPatchListUpdate();
+ }
+}
+
+status_t AudioPolicyService::clientSetAudioPortConfig(const struct audio_port_config *config,
+ int delayMs)
+{
+ return mAudioCommandThread->setAudioPortConfigCommand(config, delayMs);
+}
+
+AudioPolicyService::NotificationClient::NotificationClient(const sp<AudioPolicyService>& service,
+ const sp<IAudioPolicyServiceClient>& client,
+ uid_t uid)
+ : mService(service), mUid(uid), mAudioPolicyServiceClient(client)
+{
+}
+
+AudioPolicyService::NotificationClient::~NotificationClient()
+{
+}
+
+void AudioPolicyService::NotificationClient::binderDied(const wp<IBinder>& who __unused)
+{
+ sp<NotificationClient> keep(this);
+ sp<AudioPolicyService> service = mService.promote();
+ if (service != 0) {
+ service->removeNotificationClient(mUid);
+ }
+}
+
+void AudioPolicyService::NotificationClient::onAudioPortListUpdate()
+{
+ if (mAudioPolicyServiceClient != 0) {
+ mAudioPolicyServiceClient->onAudioPortListUpdate();
+ }
+}
+
+void AudioPolicyService::NotificationClient::onAudioPatchListUpdate()
+{
+ if (mAudioPolicyServiceClient != 0) {
+ mAudioPolicyServiceClient->onAudioPatchListUpdate();
+ }
+}
void AudioPolicyService::binderDied(const wp<IBinder>& who) {
ALOGW("binderDied() %p, calling pid %d", who.unsafe_get(),
@@ -390,6 +492,36 @@ bool AudioPolicyService::AudioCommandThread::threadLoop()
command->mStatus = af->releaseAudioPatch(data->mHandle);
}
} break;
+ case UPDATE_AUDIOPORT_LIST: {
+ ALOGV("AudioCommandThread() processing update audio port list");
+ sp<AudioPolicyService> svc = mService.promote();
+ if (svc == 0) {
+ break;
+ }
+ mLock.unlock();
+ svc->doOnAudioPortListUpdate();
+ mLock.lock();
+ }break;
+ case UPDATE_AUDIOPATCH_LIST: {
+ ALOGV("AudioCommandThread() processing update audio patch list");
+ sp<AudioPolicyService> svc = mService.promote();
+ if (svc == 0) {
+ break;
+ }
+ mLock.unlock();
+ svc->doOnAudioPatchListUpdate();
+ mLock.lock();
+ }break;
+ case SET_AUDIOPORT_CONFIG: {
+ SetAudioPortConfigData *data = (SetAudioPortConfigData *)command->mParam.get();
+ ALOGV("AudioCommandThread() processing set port config");
+ sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
+ if (af == 0) {
+ command->mStatus = PERMISSION_DENIED;
+ } else {
+ command->mStatus = af->setAudioPortConfig(&data->mConfig);
+ }
+ } break;
default:
ALOGW("AudioCommandThread() unknown command %d", command->mCommand);
}
@@ -584,6 +716,35 @@ status_t AudioPolicyService::AudioCommandThread::releaseAudioPatchCommand(audio_
return sendCommand(command, delayMs);
}
+void AudioPolicyService::AudioCommandThread::updateAudioPortListCommand()
+{
+ sp<AudioCommand> command = new AudioCommand();
+ command->mCommand = UPDATE_AUDIOPORT_LIST;
+ ALOGV("AudioCommandThread() adding update audio port list");
+ sendCommand(command);
+}
+
+void AudioPolicyService::AudioCommandThread::updateAudioPatchListCommand()
+{
+ sp<AudioCommand>command = new AudioCommand();
+ command->mCommand = UPDATE_AUDIOPATCH_LIST;
+ ALOGV("AudioCommandThread() adding update audio patch list");
+ sendCommand(command);
+}
+
+status_t AudioPolicyService::AudioCommandThread::setAudioPortConfigCommand(
+ const struct audio_port_config *config, int delayMs)
+{
+ sp<AudioCommand> command = new AudioCommand();
+ command->mCommand = SET_AUDIOPORT_CONFIG;
+ SetAudioPortConfigData *data = new SetAudioPortConfigData();
+ data->mConfig = *config;
+ command->mParam = data;
+ command->mWaitStatus = true;
+ ALOGV("AudioCommandThread() adding set port config delay %d", delayMs);
+ return sendCommand(command, delayMs);
+}
+
status_t AudioPolicyService::AudioCommandThread::sendCommand(sp<AudioCommand>& command, int delayMs)
{
{
@@ -602,7 +763,6 @@ status_t AudioPolicyService::AudioCommandThread::sendCommand(sp<AudioCommand>& c
return command->mStatus;
}
-
// insertCommand_l() must be called with mLock held
void AudioPolicyService::AudioCommandThread::insertCommand_l(sp<AudioCommand>& command, int delayMs)
{
diff --git a/services/audiopolicy/AudioPolicyService.h b/services/audiopolicy/AudioPolicyService.h
index 9f88b1e..40f589b 100644
--- a/services/audiopolicy/AudioPolicyService.h
+++ b/services/audiopolicy/AudioPolicyService.h
@@ -154,6 +154,8 @@ public:
unsigned int *generation);
virtual status_t setAudioPortConfig(const struct audio_port_config *config);
+ virtual void registerClient(const sp<IAudioPolicyServiceClient>& client);
+
status_t doStopOutput(audio_io_handle_t output,
audio_stream_type_t stream,
int session = 0);
@@ -164,6 +166,14 @@ public:
int delayMs);
status_t clientReleaseAudioPatch(audio_patch_handle_t handle,
int delayMs);
+ virtual status_t clientSetAudioPortConfig(const struct audio_port_config *config,
+ int delayMs);
+
+ void removeNotificationClient(uid_t uid);
+ void onAudioPortListUpdate();
+ void doOnAudioPortListUpdate();
+ void onAudioPatchListUpdate();
+ void doOnAudioPatchListUpdate();
private:
AudioPolicyService() ANDROID_API;
@@ -192,6 +202,9 @@ private:
RELEASE_OUTPUT,
CREATE_AUDIO_PATCH,
RELEASE_AUDIO_PATCH,
+ UPDATE_AUDIOPORT_LIST,
+ UPDATE_AUDIOPATCH_LIST,
+ SET_AUDIOPORT_CONFIG,
};
AudioCommandThread (String8 name, const wp<AudioPolicyService>& service);
@@ -223,7 +236,10 @@ private:
int delayMs);
status_t releaseAudioPatchCommand(audio_patch_handle_t handle,
int delayMs);
-
+ void updateAudioPortListCommand();
+ void updateAudioPatchListCommand();
+ status_t setAudioPortConfigCommand(const struct audio_port_config *config,
+ int delayMs);
void insertCommand_l(AudioCommand *command, int delayMs = 0);
private:
@@ -301,6 +317,11 @@ private:
audio_patch_handle_t mHandle;
};
+ class SetAudioPortConfigData : public AudioCommandData {
+ public:
+ struct audio_port_config mConfig;
+ };
+
Mutex mLock;
Condition mWaitWorkCV;
Vector < sp<AudioCommand> > mAudioCommands; // list of pending commands
@@ -454,10 +475,39 @@ private:
virtual status_t releaseAudioPatch(audio_patch_handle_t handle,
int delayMs);
+ /* Set audio port configuration */
+ virtual status_t setAudioPortConfig(const struct audio_port_config *config, int delayMs);
+
+ virtual void onAudioPortListUpdate();
+ virtual void onAudioPatchListUpdate();
+
private:
AudioPolicyService *mAudioPolicyService;
};
+ // --- Notification Client ---
+ class NotificationClient : public IBinder::DeathRecipient {
+ public:
+ NotificationClient(const sp<AudioPolicyService>& service,
+ const sp<IAudioPolicyServiceClient>& client,
+ uid_t uid);
+ virtual ~NotificationClient();
+
+ void onAudioPortListUpdate();
+ void onAudioPatchListUpdate();
+
+ // IBinder::DeathRecipient
+ virtual void binderDied(const wp<IBinder>& who);
+
+ private:
+ NotificationClient(const NotificationClient&);
+ NotificationClient& operator = (const NotificationClient&);
+
+ const wp<AudioPolicyService> mService;
+ const uid_t mUid;
+ const sp<IAudioPolicyServiceClient> mAudioPolicyServiceClient;
+ };
+
static const char * const kInputSourceNames[AUDIO_SOURCE_CNT -1];
void setPreProcessorEnabled(const InputDesc *inputDesc, bool enabled);
@@ -494,6 +544,8 @@ private:
KeyedVector< audio_source_t, InputSourceDesc* > mInputSources;
KeyedVector< audio_io_handle_t, InputDesc* > mInputs;
+
+ DefaultKeyedVector< uid_t, sp<NotificationClient> > mNotificationClients;
};
}; // namespace android
diff --git a/services/audiopolicy/audio_policy_conf.h b/services/audiopolicy/audio_policy_conf.h
new file mode 100644
index 0000000..79f20f1
--- /dev/null
+++ b/services/audiopolicy/audio_policy_conf.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2012 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 ANDROID_AUDIO_POLICY_CONF_H
+#define ANDROID_AUDIO_POLICY_CONF_H
+
+
+/////////////////////////////////////////////////
+// Definitions for audio policy configuration file (audio_policy.conf)
+/////////////////////////////////////////////////
+
+#define AUDIO_HARDWARE_MODULE_ID_MAX_LEN 32
+
+#define AUDIO_POLICY_CONFIG_FILE "/system/etc/audio_policy.conf"
+#define AUDIO_POLICY_VENDOR_CONFIG_FILE "/vendor/etc/audio_policy.conf"
+
+// global configuration
+#define GLOBAL_CONFIG_TAG "global_configuration"
+
+#define ATTACHED_OUTPUT_DEVICES_TAG "attached_output_devices"
+#define DEFAULT_OUTPUT_DEVICE_TAG "default_output_device"
+#define ATTACHED_INPUT_DEVICES_TAG "attached_input_devices"
+#define SPEAKER_DRC_ENABLED_TAG "speaker_drc_enabled"
+
+// hw modules descriptions
+#define AUDIO_HW_MODULE_TAG "audio_hw_modules"
+
+#define OUTPUTS_TAG "outputs"
+#define INPUTS_TAG "inputs"
+
+#define SAMPLING_RATES_TAG "sampling_rates"
+#define FORMATS_TAG "formats"
+#define CHANNELS_TAG "channel_masks"
+#define DEVICES_TAG "devices"
+#define FLAGS_TAG "flags"
+
+#define DYNAMIC_VALUE_TAG "dynamic" // special value for "channel_masks", "sampling_rates" and
+ // "formats" in outputs descriptors indicating that supported
+ // values should be queried after opening the output.
+
+#define DEVICES_TAG "devices"
+#define DEVICE_TYPE "type"
+#define DEVICE_ADDRESS "address"
+
+#define MIXERS_TAG "mixers"
+#define MIXER_TYPE "type"
+#define MIXER_TYPE_MUX "mux"
+#define MIXER_TYPE_MIX "mix"
+
+#define GAINS_TAG "gains"
+#define GAIN_MODE "mode"
+#define GAIN_CHANNELS "channel_mask"
+#define GAIN_MIN_VALUE "min_value_mB"
+#define GAIN_MAX_VALUE "max_value_mB"
+#define GAIN_DEFAULT_VALUE "default_value_mB"
+#define GAIN_STEP_VALUE "step_value_mB"
+#define GAIN_MIN_RAMP_MS "min_ramp_ms"
+#define GAIN_MAX_RAMP_MS "max_ramp_ms"
+
+
+
+#endif // ANDROID_AUDIO_POLICY_CONF_H