diff options
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 |