diff options
Diffstat (limited to 'services')
126 files changed, 30130 insertions, 6122 deletions
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index b895027..697fb37 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -13,18 +13,28 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) +LOCAL_SRC_FILES := \ + ServiceUtilities.cpp + +# FIXME Move this library to frameworks/native +LOCAL_MODULE := libserviceutility + +include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) + LOCAL_SRC_FILES:= \ AudioFlinger.cpp \ Threads.cpp \ Tracks.cpp \ Effects.cpp \ AudioMixer.cpp.arm \ - AudioPolicyService.cpp \ - ServiceUtilities.cpp \ + PatchPanel.cpp LOCAL_SRC_FILES += StateQueue.cpp LOCAL_C_INCLUDES := \ + $(TOPDIR)frameworks/av/services/audiopolicy \ $(call include-path-for, audio-effects) \ $(call include-path-for, audio-utils) @@ -46,12 +56,15 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_STATIC_LIBRARIES := \ libscheduling_policy \ libcpustats \ - libmedia_helper + libmedia_helper \ + libserviceutility LOCAL_MODULE:= libaudioflinger 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"' @@ -72,10 +85,21 @@ include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - test-resample.cpp \ + test-resample.cpp \ + +LOCAL_C_INCLUDES := \ + $(call include-path-for, audio-utils) + +LOCAL_STATIC_LIBRARIES := \ + libsndfile LOCAL_SHARED_LIBRARIES := \ libaudioresampler \ + libaudioutils \ + libdl \ + libcutils \ + libutils \ + liblog LOCAL_MODULE:= test-resample @@ -88,7 +112,11 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ AudioResampler.cpp.arm \ AudioResamplerCubic.cpp.arm \ - AudioResamplerSinc.cpp.arm + AudioResamplerSinc.cpp.arm \ + AudioResamplerDyn.cpp.arm + +LOCAL_C_INCLUDES := \ + $(call include-path-for, audio-utils) LOCAL_SHARED_LIBRARIES := \ libcutils \ diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 33b19a4..1843722 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -82,6 +82,7 @@ namespace android { static const char kDeadlockedString[] = "AudioFlinger may be deadlocked\n"; static const char kHardwareLockedString[] = "Hardware lock is taken\n"; +static const char kClientLockedString[] = "Client lock is taken\n"; nsecs_t AudioFlinger::mStandbyTimeInNsecs = kDefaultStandbyTimeInNsecs; @@ -104,6 +105,36 @@ static const nsecs_t kMinGlobalEffectEnabletimeNs = seconds(7200); // ---------------------------------------------------------------------------- +const char *formatToString(audio_format_t format) { + switch (format & AUDIO_FORMAT_MAIN_MASK) { + case AUDIO_FORMAT_PCM: + switch (format) { + case AUDIO_FORMAT_PCM_16_BIT: return "pcm16"; + case AUDIO_FORMAT_PCM_8_BIT: return "pcm8"; + case AUDIO_FORMAT_PCM_32_BIT: return "pcm32"; + case AUDIO_FORMAT_PCM_8_24_BIT: return "pcm8.24"; + case AUDIO_FORMAT_PCM_FLOAT: return "pcmfloat"; + case AUDIO_FORMAT_PCM_24_BIT_PACKED: return "pcm24"; + default: + break; + } + break; + case AUDIO_FORMAT_MP3: return "mp3"; + case AUDIO_FORMAT_AMR_NB: return "amr-nb"; + case AUDIO_FORMAT_AMR_WB: return "amr-wb"; + case AUDIO_FORMAT_AAC: return "aac"; + case AUDIO_FORMAT_HE_AAC_V1: return "he-aac-v1"; + case AUDIO_FORMAT_HE_AAC_V2: return "he-aac-v2"; + case AUDIO_FORMAT_VORBIS: return "vorbis"; + case AUDIO_FORMAT_OPUS: return "opus"; + case AUDIO_FORMAT_AC3: return "ac-3"; + case AUDIO_FORMAT_E_AC3: return "e-ac-3"; + default: + break; + } + return "unknown"; +} + static int load_audio_interface(const char *if_name, audio_hw_device_t **dev) { const hw_module_t *mod; @@ -121,7 +152,7 @@ static int load_audio_interface(const char *if_name, audio_hw_device_t **dev) if (rc) { goto out; } - if ((*dev)->common.version != AUDIO_DEVICE_API_VERSION_CURRENT) { + if ((*dev)->common.version < AUDIO_DEVICE_API_VERSION_MIN) { ALOGE("%s wrong audio hw device version %04x", __func__, (*dev)->common.version); rc = BAD_VALUE; goto out; @@ -138,6 +169,7 @@ out: AudioFlinger::AudioFlinger() : BnAudioFlinger(), mPrimaryHardwareDev(NULL), + mAudioHwDevs(NULL), mHardwareStatus(AUDIO_HW_IDLE), mMasterVolume(1.0f), mMasterMute(false), @@ -146,14 +178,16 @@ AudioFlinger::AudioFlinger() mBtNrecIsOff(false), mIsLowRamDevice(true), mIsDeviceTypeKnown(false), - mGlobalEffectEnableTime(0) + mGlobalEffectEnableTime(0), + mPrimaryOutputSampleRate(0) { getpid_cached = getpid(); char value[PROPERTY_VALUE_MAX]; bool doLog = (property_get("ro.test_harness", value, "0") > 0) && (atoi(value) == 1); if (doLog) { - mLogMemoryDealer = new MemoryDealer(kLogMemorySize, "LogWriters"); + mLogMemoryDealer = new MemoryDealer(kLogMemorySize, "LogWriters", MemoryHeapBase::READ_ONLY); } + #ifdef TEE_SINK (void) property_get("ro.debuggable", value, "0"); int debuggable = atoi(value); @@ -162,12 +196,16 @@ AudioFlinger::AudioFlinger() (void) property_get("af.tee", value, "0"); teeEnabled = atoi(value); } - if (teeEnabled & 1) + // FIXME symbolic constants here + if (teeEnabled & 1) { mTeeSinkInputEnabled = true; - if (teeEnabled & 2) + } + if (teeEnabled & 2) { mTeeSinkOutputEnabled = true; - if (teeEnabled & 4) + } + if (teeEnabled & 4) { mTeeSinkTrackEnabled = true; + } #endif } @@ -191,6 +229,8 @@ void AudioFlinger::onFirstRef() } } + mPatchPanel = new PatchPanel(this); + mMode = AUDIO_MODE_NORMAL; } @@ -210,6 +250,18 @@ AudioFlinger::~AudioFlinger() audio_hw_device_close(mAudioHwDevs.valueAt(i)->hwDevice()); delete mAudioHwDevs.valueAt(i); } + + // Tell media.log service about any old writers that still need to be unregistered + sp<IBinder> binder = defaultServiceManager()->getService(String16("media.log")); + if (binder != 0) { + sp<IMediaLogService> mediaLogService(interface_cast<IMediaLogService>(binder)); + for (size_t count = mUnregisteredWriters.size(); count > 0; count--) { + sp<IMemory> iMemory(mUnregisteredWriters.top()->getIMemory()); + mUnregisteredWriters.pop(); + mediaLogService->unregisterWriter(iMemory); + } + } + } static const char * const audio_interfaces[] = { @@ -249,7 +301,7 @@ AudioFlinger::AudioHwDevice* AudioFlinger::findSuitableHwDev_l( return NULL; } -void AudioFlinger::dumpClients(int fd, const Vector<String16>& args) +void AudioFlinger::dumpClients(int fd, const Vector<String16>& args __unused) { const size_t SIZE = 256; char buffer[SIZE]; @@ -271,17 +323,17 @@ void AudioFlinger::dumpClients(int fd, const Vector<String16>& args) } result.append("Global session refs:\n"); - result.append(" session pid count\n"); + result.append(" session pid count\n"); for (size_t i = 0; i < mAudioSessionRefs.size(); i++) { AudioSessionRef *r = mAudioSessionRefs[i]; - snprintf(buffer, SIZE, " %7d %3d %3d\n", r->mSessionid, r->mPid, r->mCnt); + snprintf(buffer, SIZE, " %7d %5d %5d\n", r->mSessionid, r->mPid, r->mCnt); result.append(buffer); } write(fd, result.string(), result.size()); } -void AudioFlinger::dumpInternals(int fd, const Vector<String16>& args) +void AudioFlinger::dumpInternals(int fd, const Vector<String16>& args __unused) { const size_t SIZE = 256; char buffer[SIZE]; @@ -296,7 +348,7 @@ void AudioFlinger::dumpInternals(int fd, const Vector<String16>& args) write(fd, result.string(), result.size()); } -void AudioFlinger::dumpPermissionDenial(int fd, const Vector<String16>& args) +void AudioFlinger::dumpPermissionDenial(int fd, const Vector<String16>& args __unused) { const size_t SIZE = 256; char buffer[SIZE]; @@ -344,7 +396,16 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args) write(fd, result.string(), result.size()); } + bool clientLocked = dumpTryLock(mClientLock); + if (!clientLocked) { + String8 result(kClientLockedString); + write(fd, result.string(), result.size()); + } dumpClients(fd, args); + if (clientLocked) { + mClientLock.unlock(); + } + dumpInternals(fd, args); // dump playback threads @@ -388,8 +449,9 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args) return NO_ERROR; } -sp<AudioFlinger::Client> AudioFlinger::registerPid_l(pid_t pid) +sp<AudioFlinger::Client> AudioFlinger::registerPid(pid_t pid) { + Mutex::Autolock _cl(mClientLock); // If pid is already in the mClients wp<> map, then use that entry // (for which promote() is always != 0), otherwise create a new entry and Client. sp<Client> client = mClients.valueFor(pid).promote(); @@ -403,16 +465,44 @@ sp<AudioFlinger::Client> AudioFlinger::registerPid_l(pid_t pid) sp<NBLog::Writer> AudioFlinger::newWriter_l(size_t size, const char *name) { + // If there is no memory allocated for logs, return a dummy writer that does nothing if (mLogMemoryDealer == 0) { return new NBLog::Writer(); } - sp<IMemory> shared = mLogMemoryDealer->allocate(NBLog::Timeline::sharedSize(size)); - sp<NBLog::Writer> writer = new NBLog::Writer(size, shared); sp<IBinder> binder = defaultServiceManager()->getService(String16("media.log")); - if (binder != 0) { - interface_cast<IMediaLogService>(binder)->registerWriter(shared, size, name); + // Similarly if we can't contact the media.log service, also return a dummy writer + if (binder == 0) { + return new NBLog::Writer(); + } + sp<IMediaLogService> mediaLogService(interface_cast<IMediaLogService>(binder)); + sp<IMemory> shared = mLogMemoryDealer->allocate(NBLog::Timeline::sharedSize(size)); + // If allocation fails, consult the vector of previously unregistered writers + // and garbage-collect one or more them until an allocation succeeds + if (shared == 0) { + Mutex::Autolock _l(mUnregisteredWritersLock); + for (size_t count = mUnregisteredWriters.size(); count > 0; count--) { + { + // Pick the oldest stale writer to garbage-collect + sp<IMemory> iMemory(mUnregisteredWriters[0]->getIMemory()); + mUnregisteredWriters.removeAt(0); + mediaLogService->unregisterWriter(iMemory); + // Now the media.log remote reference to IMemory is gone. When our last local + // reference to IMemory also drops to zero at end of this block, + // the IMemory destructor will deallocate the region from mLogMemoryDealer. + } + // Re-attempt the allocation + shared = mLogMemoryDealer->allocate(NBLog::Timeline::sharedSize(size)); + if (shared != 0) { + goto success; + } + } + // Even after garbage-collecting all old writers, there is still not enough memory, + // so return a dummy writer + return new NBLog::Writer(); } - return writer; +success: + mediaLogService->registerWriter(shared, size, name); + return new NBLog::Writer(size, shared); } void AudioFlinger::unregisterWriter(const sp<NBLog::Writer>& writer) @@ -424,13 +514,10 @@ void AudioFlinger::unregisterWriter(const sp<NBLog::Writer>& writer) if (iMemory == 0) { return; } - sp<IBinder> binder = defaultServiceManager()->getService(String16("media.log")); - if (binder != 0) { - interface_cast<IMediaLogService>(binder)->unregisterWriter(iMemory); - // Now the media.log remote reference to IMemory is gone. - // When our last local reference to IMemory also drops to zero, - // the IMemory destructor will deallocate the region from mMemoryDealer. - } + // Rather than removing the writer immediately, append it to a queue of old writers to + // be garbage-collected later. This allows us to continue to view old logs for a while. + Mutex::Autolock _l(mUnregisteredWritersLock); + mUnregisteredWriters.push(writer); } // IAudioFlinger interface @@ -441,13 +528,12 @@ sp<IAudioTrack> AudioFlinger::createTrack( uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, - size_t frameCount, + size_t *frameCount, IAudioFlinger::track_flags_t *flags, const sp<IMemory>& sharedBuffer, audio_io_handle_t output, pid_t tid, int *sessionId, - String8& name, int clientUid, status_t *status) { @@ -465,10 +551,29 @@ sp<IAudioTrack> AudioFlinger::createTrack( goto Exit; } - // client is responsible for conversion of 8-bit PCM to 16-bit PCM, - // and we don't yet support 8.24 or 32-bit PCM - if (audio_is_linear_pcm(format) && format != AUDIO_FORMAT_PCM_16_BIT) { - ALOGE("createTrack() invalid format %d", format); + // further sample rate checks are performed by createTrack_l() depending on the thread type + if (sampleRate == 0) { + ALOGE("createTrack() invalid sample rate %u", sampleRate); + lStatus = BAD_VALUE; + goto Exit; + } + + // further channel mask checks are performed by createTrack_l() depending on the thread type + if (!audio_is_output_channel(channelMask)) { + ALOGE("createTrack() invalid channel mask %#x", channelMask); + lStatus = BAD_VALUE; + goto Exit; + } + + // further format checks are performed by createTrack_l() depending on the thread type + if (!audio_is_valid_format(format)) { + ALOGE("createTrack() invalid format %#x", format); + lStatus = BAD_VALUE; + goto Exit; + } + + if (sharedBuffer != 0 && sharedBuffer->pointer() == NULL) { + ALOGE("createTrack() sharedBuffer is non-0 but has NULL pointer()"); lStatus = BAD_VALUE; goto Exit; } @@ -476,7 +581,6 @@ sp<IAudioTrack> AudioFlinger::createTrack( { Mutex::Autolock _l(mLock); PlaybackThread *thread = checkPlaybackThread_l(output); - PlaybackThread *effectThread = NULL; if (thread == NULL) { ALOGE("no playback thread found for output handle %d", output); lStatus = BAD_VALUE; @@ -484,24 +588,23 @@ sp<IAudioTrack> AudioFlinger::createTrack( } pid_t pid = IPCThreadState::self()->getCallingPid(); + client = registerPid(pid); - client = registerPid_l(pid); - - ALOGV("createTrack() sessionId: %d", (sessionId == NULL) ? -2 : *sessionId); - if (sessionId != NULL && *sessionId != AUDIO_SESSION_OUTPUT_MIX) { + PlaybackThread *effectThread = NULL; + if (sessionId != NULL && *sessionId != AUDIO_SESSION_ALLOCATE) { + lSessionId = *sessionId; // check if an effect chain with the same session ID is present on another // output thread and move it here. for (size_t i = 0; i < mPlaybackThreads.size(); i++) { sp<PlaybackThread> t = mPlaybackThreads.valueAt(i); if (mPlaybackThreads.keyAt(i) != output) { - uint32_t sessions = t->hasAudioSession(*sessionId); + uint32_t sessions = t->hasAudioSession(lSessionId); if (sessions & PlaybackThread::EFFECT_SESSION) { effectThread = t.get(); break; } } } - lSessionId = *sessionId; } else { // if no audio session id is provided, create one here lSessionId = nextUniqueId(); @@ -519,6 +622,7 @@ sp<IAudioTrack> AudioFlinger::createTrack( // move effect chain to this output thread if an effect on same session was waiting // for a track to be created if (lStatus == NO_ERROR && effectThread != NULL) { + // no risk of deadlock because AudioFlinger::mLock is held Mutex::Autolock _dl(thread->mLock); Mutex::Autolock _sl(effectThread->mLock); moveEffectChain_l(lSessionId, effectThread, thread, true); @@ -538,23 +642,27 @@ sp<IAudioTrack> AudioFlinger::createTrack( } } } + } - if (lStatus == NO_ERROR) { - // s for server's pid, n for normal mixer name, f for fast index - name = String8::format("s:%d;n:%d;f:%d", getpid_cached, track->name() - AudioMixer::TRACK0, - track->fastIndex()); - trackHandle = new TrackHandle(track); - } else { - // remove local strong reference to Client before deleting the Track so that the Client - // destructor is called by the TrackBase destructor with mLock held - client.clear(); + + if (lStatus != NO_ERROR) { + // remove local strong reference to Client before deleting the Track so that the + // Client destructor is called by the TrackBase destructor with mClientLock held + // Don't hold mClientLock when releasing the reference on the track as the + // destructor will acquire it. + { + Mutex::Autolock _cl(mClientLock); + client.clear(); + } track.clear(); + goto Exit; } + // return handle to client + trackHandle = new TrackHandle(track); + Exit: - if (status != NULL) { - *status = lStatus; - } + *status = lStatus; return trackHandle; } @@ -569,17 +677,6 @@ uint32_t AudioFlinger::sampleRate(audio_io_handle_t output) const return thread->sampleRate(); } -int AudioFlinger::channelCount(audio_io_handle_t output) const -{ - Mutex::Autolock _l(mLock); - PlaybackThread *thread = checkPlaybackThread_l(output); - if (thread == NULL) { - ALOGW("channelCount() unknown thread %d", output); - return 0; - } - return thread->channelCount(); -} - audio_format_t AudioFlinger::format(audio_io_handle_t output) const { Mutex::Autolock _l(mLock); @@ -796,7 +893,7 @@ status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value, AutoMutex lock(mLock); PlaybackThread *thread = NULL; - if (output) { + if (output != AUDIO_IO_HANDLE_NONE) { thread = checkPlaybackThread_l(output); if (thread == NULL) { return BAD_VALUE; @@ -845,7 +942,7 @@ float AudioFlinger::streamVolume(audio_stream_type_t stream, audio_io_handle_t o AutoMutex lock(mLock); float volume; - if (output) { + if (output != AUDIO_IO_HANDLE_NONE) { PlaybackThread *thread = checkPlaybackThread_l(output); if (thread == NULL) { return 0.0f; @@ -878,8 +975,8 @@ status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8& return PERMISSION_DENIED; } - // ioHandle == 0 means the parameters are global to the audio hardware interface - if (ioHandle == 0) { + // AUDIO_IO_HANDLE_NONE means the parameters are global to the audio hardware interface + if (ioHandle == AUDIO_IO_HANDLE_NONE) { Mutex::Autolock _l(mLock); status_t final_result = NO_ERROR; { @@ -961,7 +1058,7 @@ String8 AudioFlinger::getParameters(audio_io_handle_t ioHandle, const String8& k Mutex::Autolock _l(mLock); - if (ioHandle == 0) { + if (ioHandle == AUDIO_IO_HANDLE_NONE) { String8 out_s8; for (size_t i = 0; i < mAudioHwDevs.size(); i++) { @@ -1000,7 +1097,7 @@ size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t form AutoMutex lock(mHardwareLock); mHardwareStatus = AUDIO_HW_GET_INPUT_BUFFER_SIZE; - struct audio_config config; + audio_config_t config; memset(&config, 0, sizeof(config)); config.sample_rate = sampleRate; config.channel_mask = channelMask; @@ -1061,21 +1158,32 @@ status_t AudioFlinger::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrame void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client) { - Mutex::Autolock _l(mLock); + if (client == 0) { + return; + } + bool clientAdded = false; + { + Mutex::Autolock _cl(mClientLock); - pid_t pid = IPCThreadState::self()->getCallingPid(); - if (mNotificationClients.indexOfKey(pid) < 0) { - sp<NotificationClient> notificationClient = new NotificationClient(this, - client, - pid); - ALOGV("registerClient() client %p, pid %d", notificationClient.get(), pid); + pid_t pid = IPCThreadState::self()->getCallingPid(); + if (mNotificationClients.indexOfKey(pid) < 0) { + sp<NotificationClient> notificationClient = new NotificationClient(this, + client, + pid); + ALOGV("registerClient() client %p, pid %d", notificationClient.get(), pid); - mNotificationClients.add(pid, notificationClient); + mNotificationClients.add(pid, notificationClient); - sp<IBinder> binder = client->asBinder(); - binder->linkToDeath(notificationClient); + sp<IBinder> binder = client->asBinder(); + binder->linkToDeath(notificationClient); + clientAdded = true; + } + } + // mClientLock should not be held here because ThreadBase::sendIoConfigEvent() will lock the + // ThreadBase mutex and the locking order is ThreadBase::mLock then AudioFlinger::mClientLock. + if (clientAdded) { // the config change is always sent from playback or record threads to avoid deadlock // with AudioSystem::gLock for (size_t i = 0; i < mPlaybackThreads.size(); i++) { @@ -1091,8 +1199,10 @@ void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client) void AudioFlinger::removeNotificationClient(pid_t pid) { Mutex::Autolock _l(mLock); - - mNotificationClients.removeItem(pid); + { + Mutex::Autolock _cl(mClientLock); + mNotificationClients.removeItem(pid); + } ALOGV("%d died, releasing its sessions", pid); size_t num = mAudioSessionRefs.size(); @@ -1115,17 +1225,18 @@ void AudioFlinger::removeNotificationClient(pid_t pid) } } -// audioConfigChanged_l() must be called with AudioFlinger::mLock held -void AudioFlinger::audioConfigChanged_l(int event, audio_io_handle_t ioHandle, const void *param2) +void AudioFlinger::audioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2) { + Mutex::Autolock _l(mClientLock); size_t size = mNotificationClients.size(); for (size_t i = 0; i < size; i++) { - mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event, ioHandle, - param2); + mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event, + ioHandle, + param2); } } -// removeClient_l() must be called with AudioFlinger::mLock held +// removeClient_l() must be called with AudioFlinger::mClientLock held void AudioFlinger::removeClient_l(pid_t pid) { ALOGV("removeClient_l() pid %d, calling pid %d", pid, @@ -1163,7 +1274,7 @@ AudioFlinger::Client::Client(const sp<AudioFlinger>& audioFlinger, pid_t pid) // 1 MB of address space is good for 32 tracks, 8 buffers each, 4 KB/buffer } -// Client destructor must be called with AudioFlinger::mLock held +// Client destructor must be called with AudioFlinger::mClientLock held AudioFlinger::Client::~Client() { mAudioFlinger->removeClient_l(mPid); @@ -1212,7 +1323,7 @@ AudioFlinger::NotificationClient::~NotificationClient() { } -void AudioFlinger::NotificationClient::binderDied(const wp<IBinder>& who) +void AudioFlinger::NotificationClient::binderDied(const wp<IBinder>& who __unused) { sp<NotificationClient> keep(this); mAudioFlinger->removeNotificationClient(mPid); @@ -1230,20 +1341,24 @@ sp<IAudioRecord> AudioFlinger::openRecord( uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, - size_t frameCount, + size_t *frameCount, IAudioFlinger::track_flags_t *flags, pid_t tid, int *sessionId, + size_t *notificationFrames, + sp<IMemory>& cblk, + sp<IMemory>& buffers, status_t *status) { sp<RecordThread::RecordTrack> recordTrack; sp<RecordHandle> recordHandle; sp<Client> client; status_t lStatus; - RecordThread *thread; - size_t inFrameCount; int lSessionId; + cblk.clear(); + buffers.clear(); + // check calling permissions if (!recordingAllowed()) { ALOGE("openRecord() permission denied: recording not allowed"); @@ -1251,16 +1366,31 @@ sp<IAudioRecord> AudioFlinger::openRecord( goto Exit; } - if (format != AUDIO_FORMAT_PCM_16_BIT) { - ALOGE("openRecord() invalid format %d", format); + // further sample rate checks are performed by createRecordTrack_l() + if (sampleRate == 0) { + ALOGE("openRecord() invalid sample rate %u", sampleRate); + lStatus = BAD_VALUE; + goto Exit; + } + + // we don't yet support anything other than 16-bit PCM + if (!(audio_is_valid_format(format) && + audio_is_linear_pcm(format) && format == AUDIO_FORMAT_PCM_16_BIT)) { + ALOGE("openRecord() invalid format %#x", format); lStatus = BAD_VALUE; goto Exit; } - // add client to list - { // scope for mLock + // further channel mask checks are performed by createRecordTrack_l() + if (!audio_is_input_channel(channelMask)) { + ALOGE("openRecord() invalid channel mask %#x", channelMask); + lStatus = BAD_VALUE; + goto Exit; + } + + { Mutex::Autolock _l(mLock); - thread = checkRecordThread_l(input); + RecordThread *thread = checkRecordThread_l(input); if (thread == NULL) { ALOGE("openRecord() checkRecordThread_l failed"); lStatus = BAD_VALUE; @@ -1275,42 +1405,48 @@ sp<IAudioRecord> AudioFlinger::openRecord( } pid_t pid = IPCThreadState::self()->getCallingPid(); - client = registerPid_l(pid); + client = registerPid(pid); - // If no audio session id is provided, create one here - if (sessionId != NULL && *sessionId != AUDIO_SESSION_OUTPUT_MIX) { + if (sessionId != NULL && *sessionId != AUDIO_SESSION_ALLOCATE) { lSessionId = *sessionId; } else { + // if no audio session id is provided, create one here lSessionId = nextUniqueId(); if (sessionId != NULL) { *sessionId = lSessionId; } } - // create new record track. - // The record track uses one track in mHardwareMixerThread by convention. + ALOGV("openRecord() lSessionId: %d", lSessionId); + // TODO: the uid should be passed in as a parameter to openRecord recordTrack = thread->createRecordTrack_l(client, sampleRate, format, channelMask, - frameCount, lSessionId, + frameCount, lSessionId, notificationFrames, IPCThreadState::self()->getCallingUid(), flags, tid, &lStatus); LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (recordTrack == 0)); } + if (lStatus != NO_ERROR) { // remove local strong reference to Client before deleting the RecordTrack so that the - // Client destructor is called by the TrackBase destructor with mLock held - client.clear(); + // Client destructor is called by the TrackBase destructor with mClientLock held + // Don't hold mClientLock when releasing the reference on the track as the + // destructor will acquire it. + { + Mutex::Autolock _cl(mClientLock); + client.clear(); + } recordTrack.clear(); goto Exit; } - // return to handle to client + cblk = recordTrack->getCblk(); + buffers = recordTrack->getBuffers(); + + // return handle to client recordHandle = new RecordHandle(recordTrack); - lStatus = NO_ERROR; Exit: - if (status) { - *status = lStatus; - } + *status = lStatus; return recordHandle; } @@ -1320,6 +1456,9 @@ Exit: audio_module_handle_t AudioFlinger::loadHwModule(const char *name) { + if (name == NULL) { + return 0; + } if (!settingsAllowed()) { return 0; } @@ -1398,7 +1537,7 @@ audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name) } audio_module_handle_t handle = nextUniqueId(); - mAudioHwDevs.add(handle, new AudioHwDevice(name, dev, flags)); + mAudioHwDevs.add(handle, new AudioHwDevice(handle, name, dev, flags)); ALOGI("loadHwModule() Loaded %s audio interface from %s (%s) handle %d", name, dev->common.module->name, dev->common.module->id, handle); @@ -1440,117 +1579,155 @@ status_t AudioFlinger::setLowRamDevice(bool isLowRamDevice) return NO_ERROR; } -// ---------------------------------------------------------------------------- - -audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module, - audio_devices_t *pDevices, - uint32_t *pSamplingRate, - audio_format_t *pFormat, - audio_channel_mask_t *pChannelMask, - uint32_t *pLatencyMs, - audio_output_flags_t flags, - const audio_offload_info_t *offloadInfo) +audio_hw_sync_t AudioFlinger::getAudioHwSyncForSession(audio_session_t sessionId) { - PlaybackThread *thread = NULL; - struct audio_config config; - config.sample_rate = (pSamplingRate != NULL) ? *pSamplingRate : 0; - config.channel_mask = (pChannelMask != NULL) ? *pChannelMask : 0; - config.format = (pFormat != NULL) ? *pFormat : AUDIO_FORMAT_DEFAULT; - if (offloadInfo) { - config.offload_info = *offloadInfo; + Mutex::Autolock _l(mLock); + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + sp<PlaybackThread> thread = mPlaybackThreads.valueAt(i); + if ((thread->hasAudioSession(sessionId) & ThreadBase::TRACK_SESSION) != 0) { + // A session can only be on one thread, so exit after first match + String8 reply = thread->getParameters(String8(AUDIO_PARAMETER_STREAM_HW_AV_SYNC)); + AudioParameter param = AudioParameter(reply); + int value; + if (param.getInt(String8(AUDIO_PARAMETER_STREAM_HW_AV_SYNC), value) == NO_ERROR) { + return value; + } + break; + } } + return AUDIO_HW_SYNC_INVALID; +} - audio_stream_out_t *outStream = NULL; - AudioHwDevice *outHwDev; +// ---------------------------------------------------------------------------- - ALOGV("openOutput(), module %d Device %x, SamplingRate %d, Format %#08x, Channels %x, flags %x", - module, - (pDevices != NULL) ? *pDevices : 0, - config.sample_rate, - config.format, - config.channel_mask, - flags); - ALOGV("openOutput(), offloadInfo %p version 0x%04x", - offloadInfo, offloadInfo == NULL ? -1 : offloadInfo->version ); - if (pDevices == NULL || *pDevices == 0) { +sp<AudioFlinger::PlaybackThread> AudioFlinger::openOutput_l(audio_module_handle_t module, + audio_io_handle_t *output, + audio_config_t *config, + audio_devices_t devices, + const String8& address, + audio_output_flags_t flags) +{ + AudioHwDevice *outHwDev = findSuitableHwDev_l(module, devices); + if (outHwDev == NULL) { return 0; } - Mutex::Autolock _l(mLock); - - outHwDev = findSuitableHwDev_l(module, *pDevices); - if (outHwDev == NULL) - return 0; - audio_hw_device_t *hwDevHal = outHwDev->hwDevice(); - audio_io_handle_t id = nextUniqueId(); + if (*output == AUDIO_IO_HANDLE_NONE) { + *output = nextUniqueId(); + } mHardwareStatus = AUDIO_HW_OUTPUT_OPEN; + audio_stream_out_t *outStream = NULL; + + // FOR TESTING ONLY: + // This if statement allows overriding the audio policy settings + // and forcing a specific format or channel mask to the HAL/Sink device for testing. + if (!(flags & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD | AUDIO_OUTPUT_FLAG_DIRECT))) { + // Check only for Normal Mixing mode + if (kEnableExtendedPrecision) { + // Specify format (uncomment one below to choose) + //config->format = AUDIO_FORMAT_PCM_FLOAT; + //config->format = AUDIO_FORMAT_PCM_24_BIT_PACKED; + //config->format = AUDIO_FORMAT_PCM_32_BIT; + //config->format = AUDIO_FORMAT_PCM_8_24_BIT; + // ALOGV("openOutput_l() upgrading format to %#08x", config->format); + } + if (kEnableExtendedChannels) { + // Specify channel mask (uncomment one below to choose) + //config->channel_mask = audio_channel_out_mask_from_count(4); // for USB 4ch + //config->channel_mask = audio_channel_mask_from_representation_and_bits( + // AUDIO_CHANNEL_REPRESENTATION_INDEX, (1 << 4) - 1); // another 4ch example + } + } + status_t status = hwDevHal->open_output_stream(hwDevHal, - id, - *pDevices, - (audio_output_flags_t)flags, - &config, - &outStream); + *output, + devices, + flags, + config, + &outStream, + address.string()); mHardwareStatus = AUDIO_HW_IDLE; - ALOGV("openOutput() openOutputStream returned output %p, SamplingRate %d, Format %#08x, " - "Channels %x, status %d", + ALOGV("openOutput_l() openOutputStream returned output %p, sampleRate %d, Format %#x, " + "channelMask %#x, status %d", outStream, - config.sample_rate, - config.format, - config.channel_mask, + config->sample_rate, + config->format, + config->channel_mask, status); if (status == NO_ERROR && outStream != NULL) { - AudioStreamOut *output = new AudioStreamOut(outHwDev, outStream, flags); + AudioStreamOut *outputStream = new AudioStreamOut(outHwDev, outStream, flags); + PlaybackThread *thread; if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) { - thread = new OffloadThread(this, output, id, *pDevices); - ALOGV("openOutput() created offload output: ID %d thread %p", id, thread); - } else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT) || - (config.format != AUDIO_FORMAT_PCM_16_BIT) || - (config.channel_mask != AUDIO_CHANNEL_OUT_STEREO)) { - thread = new DirectOutputThread(this, output, id, *pDevices); - ALOGV("openOutput() created direct output: ID %d thread %p", id, thread); + thread = new OffloadThread(this, outputStream, *output, devices); + ALOGV("openOutput_l() created offload output: ID %d thread %p", *output, thread); + } else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT) + || !isValidPcmSinkFormat(config->format) + || !isValidPcmSinkChannelMask(config->channel_mask)) { + thread = new DirectOutputThread(this, outputStream, *output, devices); + ALOGV("openOutput_l() created direct output: ID %d thread %p", *output, thread); } else { - thread = new MixerThread(this, output, id, *pDevices); - ALOGV("openOutput() created mixer output: ID %d thread %p", id, thread); + thread = new MixerThread(this, outputStream, *output, devices); + ALOGV("openOutput_l() created mixer output: ID %d thread %p", *output, thread); } - mPlaybackThreads.add(id, thread); + mPlaybackThreads.add(*output, thread); + return thread; + } - if (pSamplingRate != NULL) { - *pSamplingRate = config.sample_rate; - } - if (pFormat != NULL) { - *pFormat = config.format; - } - if (pChannelMask != NULL) { - *pChannelMask = config.channel_mask; - } - if (pLatencyMs != NULL) { - *pLatencyMs = thread->latency(); - } + return 0; +} + +status_t AudioFlinger::openOutput(audio_module_handle_t module, + audio_io_handle_t *output, + audio_config_t *config, + audio_devices_t *devices, + const String8& address, + uint32_t *latencyMs, + audio_output_flags_t flags) +{ + ALOGV("openOutput(), module %d Device %x, SamplingRate %d, Format %#08x, Channels %x, flags %x", + module, + (devices != NULL) ? *devices : 0, + config->sample_rate, + config->format, + config->channel_mask, + flags); + + if (*devices == AUDIO_DEVICE_NONE) { + return BAD_VALUE; + } + + Mutex::Autolock _l(mLock); + + sp<PlaybackThread> thread = openOutput_l(module, output, config, *devices, address, flags); + if (thread != 0) { + *latencyMs = thread->latency(); // notify client processes of the new output creation - thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED); + thread->audioConfigChanged(AudioSystem::OUTPUT_OPENED); // the first primary output opened designates the primary hw device if ((mPrimaryHardwareDev == NULL) && (flags & AUDIO_OUTPUT_FLAG_PRIMARY)) { ALOGI("Using module %d has the primary audio interface", module); - mPrimaryHardwareDev = outHwDev; + mPrimaryHardwareDev = thread->getOutput()->audioHwDev; AutoMutex lock(mHardwareLock); mHardwareStatus = AUDIO_HW_SET_MODE; - hwDevHal->set_mode(hwDevHal, mMode); + mPrimaryHardwareDev->hwDevice()->set_mode(mPrimaryHardwareDev->hwDevice(), mMode); mHardwareStatus = AUDIO_HW_IDLE; + + mPrimaryOutputSampleRate = config->sample_rate; } - return id; + return NO_ERROR; } - return 0; + return NO_INIT; } audio_io_handle_t AudioFlinger::openDuplicateOutput(audio_io_handle_t output1, @@ -1563,7 +1740,7 @@ audio_io_handle_t AudioFlinger::openDuplicateOutput(audio_io_handle_t output1, if (thread1 == NULL || thread2 == NULL) { ALOGW("openDuplicateOutput() wrong output mixer type for output %d or %d", output1, output2); - return 0; + return AUDIO_IO_HANDLE_NONE; } audio_io_handle_t id = nextUniqueId(); @@ -1571,7 +1748,7 @@ audio_io_handle_t AudioFlinger::openDuplicateOutput(audio_io_handle_t output1, thread->addOutputTrack(thread2); mPlaybackThreads.add(id, thread); // notify client processes of the new output creation - thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED); + thread->audioConfigChanged(AudioSystem::OUTPUT_OPENED); return id; } @@ -1621,22 +1798,35 @@ status_t AudioFlinger::closeOutput_nonvirtual(audio_io_handle_t output) } } } - audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, NULL); + audioConfigChanged(AudioSystem::OUTPUT_CLOSED, output, NULL); } thread->exit(); // The thread entity (active unit of execution) is no longer running here, // but the ThreadBase container still exists. if (thread->type() != ThreadBase::DUPLICATING) { - AudioStreamOut *out = thread->clearOutput(); - ALOG_ASSERT(out != NULL, "out shouldn't be NULL"); - // from now on thread->mOutput is NULL - out->hwDev()->close_output_stream(out->hwDev(), out->stream); - delete out; + closeOutputFinish(thread); } + return NO_ERROR; } +void AudioFlinger::closeOutputFinish(sp<PlaybackThread> thread) +{ + AudioStreamOut *out = thread->clearOutput(); + ALOG_ASSERT(out != NULL, "out shouldn't be NULL"); + // from now on thread->mOutput is NULL + out->hwDev()->close_output_stream(out->hwDev(), out->stream); + delete out; +} + +void AudioFlinger::closeOutputInternal_l(sp<PlaybackThread> thread) +{ + mPlaybackThreads.removeItem(thread->mId); + thread->exit(); + closeOutputFinish(thread); +} + status_t AudioFlinger::suspendOutput(audio_io_handle_t output) { Mutex::Autolock _l(mLock); @@ -1668,58 +1858,76 @@ status_t AudioFlinger::restoreOutput(audio_io_handle_t output) return NO_ERROR; } -audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, - audio_devices_t *pDevices, - uint32_t *pSamplingRate, - audio_format_t *pFormat, - audio_channel_mask_t *pChannelMask) +status_t AudioFlinger::openInput(audio_module_handle_t module, + audio_io_handle_t *input, + audio_config_t *config, + audio_devices_t *device, + const String8& address, + audio_source_t source, + audio_input_flags_t flags) { - status_t status; - RecordThread *thread = NULL; - struct audio_config config; - config.sample_rate = (pSamplingRate != NULL) ? *pSamplingRate : 0; - config.channel_mask = (pChannelMask != NULL) ? *pChannelMask : 0; - config.format = (pFormat != NULL) ? *pFormat : AUDIO_FORMAT_DEFAULT; - - uint32_t reqSamplingRate = config.sample_rate; - audio_format_t reqFormat = config.format; - audio_channel_mask_t reqChannels = config.channel_mask; - audio_stream_in_t *inStream = NULL; - AudioHwDevice *inHwDev; + Mutex::Autolock _l(mLock); - if (pDevices == NULL || *pDevices == 0) { - return 0; + if (*device == AUDIO_DEVICE_NONE) { + return BAD_VALUE; } - Mutex::Autolock _l(mLock); + sp<RecordThread> thread = openInput_l(module, input, config, *device, address, source, flags); + + if (thread != 0) { + // notify client processes of the new input creation + thread->audioConfigChanged(AudioSystem::INPUT_OPENED); + return NO_ERROR; + } + return NO_INIT; +} - inHwDev = findSuitableHwDev_l(module, *pDevices); - if (inHwDev == NULL) +sp<AudioFlinger::RecordThread> AudioFlinger::openInput_l(audio_module_handle_t module, + audio_io_handle_t *input, + audio_config_t *config, + audio_devices_t device, + const String8& address, + audio_source_t source, + audio_input_flags_t flags) +{ + AudioHwDevice *inHwDev = findSuitableHwDev_l(module, device); + if (inHwDev == NULL) { + *input = AUDIO_IO_HANDLE_NONE; return 0; + } - audio_hw_device_t *inHwHal = inHwDev->hwDevice(); - audio_io_handle_t id = nextUniqueId(); + if (*input == AUDIO_IO_HANDLE_NONE) { + *input = nextUniqueId(); + } - status = inHwHal->open_input_stream(inHwHal, id, *pDevices, &config, - &inStream); - ALOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %d, Channels %x, " - "status %d", + audio_config_t halconfig = *config; + audio_hw_device_t *inHwHal = inHwDev->hwDevice(); + audio_stream_in_t *inStream = NULL; + status_t status = inHwHal->open_input_stream(inHwHal, *input, device, &halconfig, + &inStream, flags, address.string(), source); + ALOGV("openInput_l() openInputStream returned input %p, SamplingRate %d" + ", Format %#x, Channels %x, flags %#x, status %d", inStream, - config.sample_rate, - config.format, - config.channel_mask, + halconfig.sample_rate, + halconfig.format, + halconfig.channel_mask, + flags, status); // If the input could not be opened with the requested parameters and we can handle the // conversion internally, try to open again with the proposed parameters. The AudioFlinger can // resample the input and do mono to stereo or stereo to mono conversions on 16 bit PCM inputs. if (status == BAD_VALUE && - reqFormat == config.format && config.format == AUDIO_FORMAT_PCM_16_BIT && - (config.sample_rate <= 2 * reqSamplingRate) && - (popcount(config.channel_mask) <= FCC_2) && (popcount(reqChannels) <= FCC_2)) { - ALOGV("openInput() reopening with proposed sampling rate and channel mask"); + config->format == halconfig.format && halconfig.format == AUDIO_FORMAT_PCM_16_BIT && + (halconfig.sample_rate <= 2 * config->sample_rate) && + (audio_channel_count_from_in_mask(halconfig.channel_mask) <= FCC_2) && + (audio_channel_count_from_in_mask(config->channel_mask) <= FCC_2)) { + // FIXME describe the change proposed by HAL (save old values so we can log them here) + ALOGV("openInput_l() reopening with proposed sampling rate and channel mask"); inStream = NULL; - status = inHwHal->open_input_stream(inHwHal, id, *pDevices, &config, &inStream); + status = inHwHal->open_input_stream(inHwHal, *input, device, &halconfig, + &inStream, flags, address.string(), source); + // FIXME log this new status; HAL should not propose any further changes } if (status == NO_ERROR && inStream != NULL) { @@ -1733,17 +1941,17 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, TEE_SINK_NEW, // copy input using a new pipe TEE_SINK_OLD, // copy input using an existing pipe } kind; - NBAIO_Format format = Format_from_SR_C(inStream->common.get_sample_rate(&inStream->common), - popcount(inStream->common.get_channels(&inStream->common))); + NBAIO_Format format = Format_from_SR_C(halconfig.sample_rate, + audio_channel_count_from_in_mask(halconfig.channel_mask), halconfig.format); if (!mTeeSinkInputEnabled) { kind = TEE_SINK_NO; - } else if (format == Format_Invalid) { + } else if (!Format_isValid(format)) { kind = TEE_SINK_NO; } else if (mRecordTeeSink == 0) { kind = TEE_SINK_NEW; } else if (mRecordTeeSink->getStrongCount() != 1) { kind = TEE_SINK_NO; - } else if (format == mRecordTeeSink->format()) { + } else if (Format_isEqual(format, mRecordTeeSink->format())) { kind = TEE_SINK_OLD; } else { kind = TEE_SINK_NEW; @@ -1773,39 +1981,26 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, } #endif - AudioStreamIn *input = new AudioStreamIn(inHwDev, inStream); + AudioStreamIn *inputStream = new AudioStreamIn(inHwDev, inStream); // Start record thread // RecordThread requires both input and output device indication to forward to audio // pre processing modules - thread = new RecordThread(this, - input, - reqSamplingRate, - reqChannels, - id, + sp<RecordThread> thread = new RecordThread(this, + inputStream, + *input, primaryOutputDevice_l(), - *pDevices + device #ifdef TEE_SINK , teeSink #endif ); - mRecordThreads.add(id, thread); - ALOGV("openInput() created record thread: ID %d thread %p", id, thread); - if (pSamplingRate != NULL) { - *pSamplingRate = reqSamplingRate; - } - if (pFormat != NULL) { - *pFormat = config.format; - } - if (pChannelMask != NULL) { - *pChannelMask = reqChannels; - } - - // notify client processes of the new input creation - thread->audioConfigChanged_l(AudioSystem::INPUT_OPENED); - return id; + mRecordThreads.add(*input, thread); + ALOGV("openInput_l() created record thread: ID %d thread %p", *input, thread.get()); + return thread; } + *input = AUDIO_IO_HANDLE_NONE; return 0; } @@ -1827,26 +2022,35 @@ status_t AudioFlinger::closeInput_nonvirtual(audio_io_handle_t input) } ALOGV("closeInput() %d", input); - audioConfigChanged_l(AudioSystem::INPUT_CLOSED, input, NULL); + audioConfigChanged(AudioSystem::INPUT_CLOSED, input, NULL); mRecordThreads.removeItem(input); } - thread->exit(); - // The thread entity (active unit of execution) is no longer running here, - // but the ThreadBase container still exists. + // FIXME: calling thread->exit() without mLock held should not be needed anymore now that + // we have a different lock for notification client + closeInputFinish(thread); + return NO_ERROR; +} +void AudioFlinger::closeInputFinish(sp<RecordThread> thread) +{ + thread->exit(); AudioStreamIn *in = thread->clearInput(); ALOG_ASSERT(in != NULL, "in shouldn't be NULL"); // from now on thread->mInput is NULL in->hwDev()->close_input_stream(in->hwDev(), in->stream); delete in; +} - return NO_ERROR; +void AudioFlinger::closeInputInternal_l(sp<RecordThread> thread) +{ + mRecordThreads.removeItem(thread->mId); + closeInputFinish(thread); } -status_t AudioFlinger::setStreamOutput(audio_stream_type_t stream, audio_io_handle_t output) +status_t AudioFlinger::invalidateStream(audio_stream_type_t stream) { Mutex::Autolock _l(mLock); - ALOGV("setStreamOutput() stream %d to output %d", stream, output); + ALOGV("invalidateStream() stream %d", stream); for (size_t i = 0; i < mPlaybackThreads.size(); i++) { PlaybackThread *thread = mPlaybackThreads.valueAt(i).get(); @@ -1857,24 +2061,30 @@ status_t AudioFlinger::setStreamOutput(audio_stream_type_t stream, audio_io_hand } -int AudioFlinger::newAudioSessionId() +audio_unique_id_t AudioFlinger::newAudioUniqueId() { return nextUniqueId(); } -void AudioFlinger::acquireAudioSessionId(int audioSession) +void AudioFlinger::acquireAudioSessionId(int audioSession, pid_t pid) { Mutex::Autolock _l(mLock); pid_t caller = IPCThreadState::self()->getCallingPid(); - ALOGV("acquiring %d from %d", audioSession, caller); - - // Ignore requests received from processes not known as notification client. The request - // is likely proxied by mediaserver (e.g CameraService) and releaseAudioSessionId() can be - // called from a different pid leaving a stale session reference. Also we don't know how - // to clear this reference if the client process dies. - if (mNotificationClients.indexOfKey(caller) < 0) { - ALOGV("acquireAudioSessionId() unknown client %d for session %d", caller, audioSession); - return; + ALOGV("acquiring %d from %d, for %d", audioSession, caller, pid); + if (pid != -1 && (caller == getpid_cached)) { + caller = pid; + } + + { + Mutex::Autolock _cl(mClientLock); + // Ignore requests received from processes not known as notification client. The request + // is likely proxied by mediaserver (e.g CameraService) and releaseAudioSessionId() can be + // called from a different pid leaving a stale session reference. Also we don't know how + // to clear this reference if the client process dies. + if (mNotificationClients.indexOfKey(caller) < 0) { + ALOGW("acquireAudioSessionId() unknown client %d for session %d", caller, audioSession); + return; + } } size_t num = mAudioSessionRefs.size(); @@ -1890,11 +2100,14 @@ void AudioFlinger::acquireAudioSessionId(int audioSession) ALOGV(" added new entry for %d", audioSession); } -void AudioFlinger::releaseAudioSessionId(int audioSession) +void AudioFlinger::releaseAudioSessionId(int audioSession, pid_t pid) { Mutex::Autolock _l(mLock); pid_t caller = IPCThreadState::self()->getCallingPid(); - ALOGV("releasing %d from %d", audioSession, caller); + ALOGV("releasing %d from %d for %d", audioSession, caller, pid); + if (pid != -1 && (caller == getpid_cached)) { + caller = pid; + } size_t num = mAudioSessionRefs.size(); for (size_t i = 0; i< num; i++) { AudioSessionRef *ref = mAudioSessionRefs.itemAt(i); @@ -1956,7 +2169,7 @@ void AudioFlinger::purgeStaleEffects_l() { } } if (!found) { - Mutex::Autolock _l (t->mLock); + Mutex::Autolock _l(t->mLock); // remove all effects from the chain while (ec->mEffects.size()) { sp<EffectModule> effect = ec->mEffects[0]; @@ -1993,7 +2206,7 @@ AudioFlinger::RecordThread *AudioFlinger::checkRecordThread_l(audio_io_handle_t uint32_t AudioFlinger::nextUniqueId() { - return android_atomic_inc(&mNextUniqueId); + return (uint32_t) android_atomic_inc(&mNextUniqueId); } AudioFlinger::PlaybackThread *AudioFlinger::primaryPlaybackThread_l() const @@ -2023,7 +2236,7 @@ sp<AudioFlinger::SyncEvent> AudioFlinger::createSyncEvent(AudioSystem::sync_even int triggerSession, int listenerSession, sync_event_callback_t callBack, - void *cookie) + wp<RefBase> cookie) { Mutex::Autolock _l(mLock); @@ -2185,7 +2398,7 @@ sp<IEffect> AudioFlinger::createEffect( // return effect descriptor *pDesc = desc; - if (io == 0 && sessionId == AUDIO_SESSION_OUTPUT_MIX) { + if (io == AUDIO_IO_HANDLE_NONE && sessionId == AUDIO_SESSION_OUTPUT_MIX) { // if the output returned by getOutputForEffect() is removed before we lock the // mutex below, the call to checkPlaybackThread_l(io) below will detect it // and we will exit safely @@ -2200,7 +2413,7 @@ sp<IEffect> AudioFlinger::createEffect( // If output is 0 here, sessionId is neither SESSION_OUTPUT_STAGE nor SESSION_OUTPUT_MIX // because of code checking output when entering the function. // Note: io is never 0 when creating an effect on an input - if (io == 0) { + if (io == AUDIO_IO_HANDLE_NONE) { if (sessionId == AUDIO_SESSION_OUTPUT_STAGE) { // output must be specified by AudioPolicyManager when using session // AUDIO_SESSION_OUTPUT_STAGE @@ -2225,7 +2438,7 @@ sp<IEffect> AudioFlinger::createEffect( // If no output thread contains the requested session ID, default to // first output. The effect chain will be moved to the correct output // thread when a track with the same session ID is created - if (io == 0 && mPlaybackThreads.size()) { + if (io == AUDIO_IO_HANDLE_NONE && mPlaybackThreads.size() > 0) { io = mPlaybackThreads.keyAt(0); } ALOGV("createEffect() got io %d for effect %s", io, desc.name); @@ -2240,7 +2453,7 @@ sp<IEffect> AudioFlinger::createEffect( } } - sp<Client> client = registerPid_l(pid); + sp<Client> client = registerPid(pid); // create effect on selected output thread handle = thread->createEffect_l(client, effectClient, priority, sessionId, @@ -2248,12 +2461,15 @@ sp<IEffect> AudioFlinger::createEffect( if (handle != 0 && id != NULL) { *id = handle->id(); } + if (handle == 0) { + // remove local strong reference to Client with mClientLock held + Mutex::Autolock _cl(mClientLock); + client.clear(); + } } Exit: - if (status != NULL) { - *status = lStatus; - } + *status = lStatus; return handle; } @@ -2299,6 +2515,16 @@ status_t AudioFlinger::moveEffectChain_l(int sessionId, return INVALID_OPERATION; } + // Check whether the destination thread has a channel count of FCC_2, which is + // currently required for (most) effects. Prevent moving the effect chain here rather + // than disabling the addEffect_l() call in dstThread below. + if (dstThread->mChannelCount != FCC_2) { + ALOGW("moveEffectChain_l() effect chain failed because" + " destination thread %p channel count(%u) != %u", + dstThread, dstThread->mChannelCount, FCC_2); + return INVALID_OPERATION; + } + // remove chain first. This is useful only if reconfiguring effect chain on same output thread, // so that a new chain is created with correct parameters when first effect is added. This is // otherwise unnecessary as removeEffect_l() will remove the chain when last effect is @@ -2473,24 +2699,26 @@ void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_hand // if 2 dumpsys are done within 1 second, and rotation didn't work, then discard 2nd int teeFd = open(teePath, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, S_IRUSR | S_IWUSR); if (teeFd >= 0) { + // FIXME use libsndfile char wavHeader[44]; memcpy(wavHeader, "RIFF\0\0\0\0WAVEfmt \20\0\0\0\1\0\2\0\104\254\0\0\0\0\0\0\4\0\20\0data\0\0\0\0", sizeof(wavHeader)); NBAIO_Format format = teeSource->format(); unsigned channelCount = Format_channelCount(format); - ALOG_ASSERT(channelCount <= FCC_2); uint32_t sampleRate = Format_sampleRate(format); + size_t frameSize = Format_frameSize(format); wavHeader[22] = channelCount; // number of channels wavHeader[24] = sampleRate; // sample rate wavHeader[25] = sampleRate >> 8; - wavHeader[32] = channelCount * 2; // block alignment + wavHeader[32] = frameSize; // block alignment + wavHeader[33] = frameSize >> 8; write(teeFd, wavHeader, sizeof(wavHeader)); size_t total = 0; bool firstRead = true; +#define TEE_SINK_READ 1024 // frames per I/O operation + void *buffer = malloc(TEE_SINK_READ * frameSize); for (;;) { -#define TEE_SINK_READ 1024 - short buffer[TEE_SINK_READ * FCC_2]; size_t count = TEE_SINK_READ; ssize_t actual = teeSource->read(buffer, count, AudioBufferProvider::kInvalidPTS); @@ -2503,14 +2731,17 @@ void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_hand break; } ALOG_ASSERT(actual <= (ssize_t)count); - write(teeFd, buffer, actual * channelCount * sizeof(short)); + write(teeFd, buffer, actual * frameSize); total += actual; } + free(buffer); lseek(teeFd, (off_t) 4, SEEK_SET); - uint32_t temp = 44 + total * channelCount * sizeof(short) - 8; + uint32_t temp = 44 + total * frameSize - 8; + // FIXME not big-endian safe write(teeFd, &temp, sizeof(temp)); lseek(teeFd, (off_t) 40, SEEK_SET); - temp = total * channelCount * sizeof(short); + temp = total * frameSize; + // FIXME not big-endian safe write(teeFd, &temp, sizeof(temp)); close(teeFd); if (fd >= 0) { diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 7320144..753314f 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -18,6 +18,7 @@ #ifndef ANDROID_AUDIO_FLINGER_H #define ANDROID_AUDIO_FLINGER_H +#include "Configuration.h" #include <stdint.h> #include <sys/types.h> #include <limits.h> @@ -49,9 +50,12 @@ #include <media/AudioBufferProvider.h> #include <media/ExtendedAudioBufferProvider.h> + +#include "FastCapture.h" #include "FastMixer.h" #include <media/nbaio/NBAIO.h> #include "AudioWatchdog.h" +#include "AudioMixer.h" #include <powermanager/IPowerManager.h> @@ -60,8 +64,8 @@ namespace android { -class audio_track_cblk_t; -class effect_param_cblk_t; +struct audio_track_cblk_t; +struct effect_param_cblk_t; class AudioMixer; class AudioBuffer; class AudioResampler; @@ -81,9 +85,6 @@ class ServerProxy; static const nsecs_t kDefaultStandbyTimeInNsecs = seconds(3); -#define MAX_GAIN 4096.0f -#define MAX_GAIN_INT 0x1000 - #define INCLUDING_FROM_AUDIOFLINGER_H class AudioFlinger : @@ -102,29 +103,30 @@ public: uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, - size_t frameCount, + size_t *pFrameCount, IAudioFlinger::track_flags_t *flags, const sp<IMemory>& sharedBuffer, audio_io_handle_t output, pid_t tid, int *sessionId, - String8& name, int clientUid, - status_t *status); + status_t *status /*non-NULL*/); virtual sp<IAudioRecord> openRecord( audio_io_handle_t input, uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, - size_t frameCount, + size_t *pFrameCount, IAudioFlinger::track_flags_t *flags, pid_t tid, int *sessionId, - status_t *status); + size_t *notificationFrames, + sp<IMemory>& cblk, + sp<IMemory>& buffers, + status_t *status /*non-NULL*/); virtual uint32_t sampleRate(audio_io_handle_t output) const; - virtual int channelCount(audio_io_handle_t output) const; virtual audio_format_t format(audio_io_handle_t output) const; virtual size_t frameCount(audio_io_handle_t output) const; virtual uint32_t latency(audio_io_handle_t output) const; @@ -156,14 +158,13 @@ public: virtual size_t getInputBufferSize(uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask) const; - virtual audio_io_handle_t openOutput(audio_module_handle_t module, - audio_devices_t *pDevices, - uint32_t *pSamplingRate, - audio_format_t *pFormat, - audio_channel_mask_t *pChannelMask, - uint32_t *pLatencyMs, - audio_output_flags_t flags, - const audio_offload_info_t *offloadInfo); + virtual status_t openOutput(audio_module_handle_t module, + audio_io_handle_t *output, + audio_config_t *config, + audio_devices_t *devices, + const String8& address, + uint32_t *latencyMs, + audio_output_flags_t flags); virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1, audio_io_handle_t output2); @@ -174,15 +175,17 @@ public: virtual status_t restoreOutput(audio_io_handle_t output); - virtual audio_io_handle_t openInput(audio_module_handle_t module, - audio_devices_t *pDevices, - uint32_t *pSamplingRate, - audio_format_t *pFormat, - audio_channel_mask_t *pChannelMask); + virtual status_t openInput(audio_module_handle_t module, + audio_io_handle_t *input, + audio_config_t *config, + audio_devices_t *device, + const String8& address, + audio_source_t source, + audio_input_flags_t flags); virtual status_t closeInput(audio_io_handle_t input); - virtual status_t setStreamOutput(audio_stream_type_t stream, audio_io_handle_t output); + virtual status_t invalidateStream(audio_stream_type_t stream); virtual status_t setVoiceVolume(float volume); @@ -191,11 +194,11 @@ public: virtual uint32_t getInputFramesLost(audio_io_handle_t ioHandle) const; - virtual int newAudioSessionId(); + virtual audio_unique_id_t newAudioUniqueId(); - virtual void acquireAudioSessionId(int audioSession); + virtual void acquireAudioSessionId(int audioSession, pid_t pid); - virtual void releaseAudioSessionId(int audioSession); + virtual void releaseAudioSessionId(int audioSession, pid_t pid); virtual status_t queryNumberEffects(uint32_t *numEffects) const; @@ -210,7 +213,7 @@ public: int32_t priority, audio_io_handle_t io, int sessionId, - status_t *status, + status_t *status /*non-NULL*/, int *id, int *enabled); @@ -224,6 +227,30 @@ public: virtual status_t setLowRamDevice(bool isLowRamDevice); + /* List available audio ports and their attributes */ + virtual status_t listAudioPorts(unsigned int *num_ports, + struct audio_port *ports); + + /* Get attributes for a given audio port */ + virtual status_t getAudioPort(struct audio_port *port); + + /* Create an audio patch between several source and sink ports */ + virtual status_t createAudioPatch(const struct audio_patch *patch, + audio_patch_handle_t *handle); + + /* Release an audio patch */ + virtual status_t releaseAudioPatch(audio_patch_handle_t handle); + + /* List existing audio patches */ + virtual status_t listAudioPatches(unsigned int *num_patches, + struct audio_patch *patches); + + /* Set audio port configuration */ + virtual status_t setAudioPortConfig(const struct audio_port_config *config); + + /* Get the HW synchronization source used for an audio session */ + virtual audio_hw_sync_t getAudioHwSyncForSession(audio_session_t sessionId); + virtual status_t onTransact( uint32_t code, const Parcel& data, @@ -235,8 +262,12 @@ public: sp<NBLog::Writer> newWriter_l(size_t size, const char *name); void unregisterWriter(const sp<NBLog::Writer>& writer); private: - static const size_t kLogMemorySize = 10 * 1024; + static const size_t kLogMemorySize = 40 * 1024; sp<MemoryDealer> mLogMemoryDealer; // == 0 when NBLog is disabled + // When a log writer is unregistered, it is done lazily so that media.log can continue to see it + // for as long as possible. The memory is only freed when it is needed for another log writer. + Vector< sp<NBLog::Writer> > mUnregisteredWriters; + Mutex mUnregisteredWritersLock; public: class SyncEvent; @@ -249,7 +280,7 @@ public: int triggerSession, int listenerSession, sync_event_callback_t callBack, - void *cookie) + wp<RefBase> cookie) : mType(type), mTriggerSession(triggerSession), mListenerSession(listenerSession), mCallback(callBack), mCookie(cookie) {} @@ -262,14 +293,14 @@ public: AudioSystem::sync_event_t type() const { return mType; } int triggerSession() const { return mTriggerSession; } int listenerSession() const { return mListenerSession; } - void *cookie() const { return mCookie; } + wp<RefBase> cookie() const { return mCookie; } private: const AudioSystem::sync_event_t mType; const int mTriggerSession; const int mListenerSession; sync_event_callback_t mCallback; - void * const mCookie; + const wp<RefBase> mCookie; mutable Mutex mLock; }; @@ -277,7 +308,7 @@ public: int triggerSession, int listenerSession, sync_event_callback_t callBack, - void *cookie); + wp<RefBase> cookie); private: class AudioHwDevice; // fwd declaration for findSuitableHwDev_l @@ -300,6 +331,49 @@ private: audio_devices_t devices); void purgeStaleEffects_l(); + // Set kEnableExtendedChannels to true to enable greater than stereo output + // for the MixerThread and device sink. Number of channels allowed is + // FCC_2 <= channels <= AudioMixer::MAX_NUM_CHANNELS. + static const bool kEnableExtendedChannels = true; + + // Returns true if channel mask is permitted for the PCM sink in the MixerThread + static inline bool isValidPcmSinkChannelMask(audio_channel_mask_t channelMask) { + switch (audio_channel_mask_get_representation(channelMask)) { + case AUDIO_CHANNEL_REPRESENTATION_POSITION: { + uint32_t channelCount = FCC_2; // stereo is default + if (kEnableExtendedChannels) { + channelCount = audio_channel_count_from_out_mask(channelMask); + if (channelCount < FCC_2 // mono is not supported at this time + || channelCount > AudioMixer::MAX_NUM_CHANNELS) { + return false; + } + } + // check that channelMask is the "canonical" one we expect for the channelCount. + return channelMask == audio_channel_out_mask_from_count(channelCount); + } + default: + return false; + } + } + + // Set kEnableExtendedPrecision to true to use extended precision in MixerThread + static const bool kEnableExtendedPrecision = true; + + // Returns true if format is permitted for the PCM sink in the MixerThread + static inline bool isValidPcmSinkFormat(audio_format_t format) { + switch (format) { + case AUDIO_FORMAT_PCM_16_BIT: + return true; + case AUDIO_FORMAT_PCM_FLOAT: + case AUDIO_FORMAT_PCM_24_BIT_PACKED: + case AUDIO_FORMAT_PCM_32_BIT: + case AUDIO_FORMAT_PCM_8_24_BIT: + return kEnableExtendedPrecision; + default: + return false; + } + } + // standby delay for MIXER and DUPLICATING playback threads is read from property // ro.audio.flinger_standbytime_ms or defaults to kDefaultStandbyTimeInNsecs static nsecs_t mStandbyTimeInNsecs; @@ -394,6 +468,8 @@ private: #include "Effects.h" +#include "PatchPanel.h" + // server side of the client's IAudioTrack class TrackHandle : public android::BnAudioTrack { public: @@ -427,7 +503,6 @@ private: public: RecordHandle(const sp<RecordThread::RecordTrack>& recordTrack); virtual ~RecordHandle(); - virtual sp<IMemory> getCblk() const; virtual status_t start(int /*AudioSystem::sync_event_t*/ event, int triggerSession); virtual void stop(); virtual status_t onTransact( @@ -443,15 +518,39 @@ private: PlaybackThread *checkPlaybackThread_l(audio_io_handle_t output) const; MixerThread *checkMixerThread_l(audio_io_handle_t output) const; RecordThread *checkRecordThread_l(audio_io_handle_t input) const; + sp<RecordThread> openInput_l(audio_module_handle_t module, + audio_io_handle_t *input, + audio_config_t *config, + audio_devices_t device, + const String8& address, + audio_source_t source, + audio_input_flags_t flags); + sp<PlaybackThread> openOutput_l(audio_module_handle_t module, + audio_io_handle_t *output, + audio_config_t *config, + audio_devices_t devices, + const String8& address, + audio_output_flags_t flags); + + void closeOutputFinish(sp<PlaybackThread> thread); + void closeInputFinish(sp<RecordThread> thread); + // no range check, AudioFlinger::mLock held bool streamMute_l(audio_stream_type_t stream) const { return mStreamTypes[stream].mute; } // no range check, doesn't check per-thread stream volume, AudioFlinger::mLock held float streamVolume_l(audio_stream_type_t stream) const { return mStreamTypes[stream].volume; } - void audioConfigChanged_l(int event, audio_io_handle_t ioHandle, const void *param2); - - // allocate an audio_io_handle_t, session ID, or effect ID + void audioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2); + + // Allocate an audio_io_handle_t, session ID, effect ID, or audio_module_handle_t. + // They all share the same ID space, but the namespaces are actually independent + // because there are separate KeyedVectors for each kind of ID. + // The return value is uint32_t, but is cast to signed for some IDs. + // FIXME This API does not handle rollover to zero (for unsigned IDs), + // or from positive to negative (for signed IDs). + // Thus it may fail by returning an ID of the wrong sign, + // or by returning a non-unique ID. uint32_t nextUniqueId(); status_t moveEffectChain_l(int sessionId, @@ -467,7 +566,6 @@ private: void removeClient_l(pid_t pid); void removeNotificationClient(pid_t pid); - bool isNonOffloadableGlobalEffectEnabled_l(); void onNonOffloadableGlobalEffectEnable(); @@ -478,10 +576,11 @@ private: AHWD_CAN_SET_MASTER_MUTE = 0x2, }; - AudioHwDevice(const char *moduleName, + AudioHwDevice(audio_module_handle_t handle, + const char *moduleName, audio_hw_device_t *hwDevice, Flags flags) - : mModuleName(strdup(moduleName)) + : mHandle(handle), mModuleName(strdup(moduleName)) , mHwDevice(hwDevice) , mFlags(flags) { } /*virtual*/ ~AudioHwDevice() { free((void *)mModuleName); } @@ -494,12 +593,16 @@ private: return (0 != (mFlags & AHWD_CAN_SET_MASTER_MUTE)); } + audio_module_handle_t handle() const { return mHandle; } const char *moduleName() const { return mModuleName; } audio_hw_device_t *hwDevice() const { return mHwDevice; } + uint32_t version() const { return mHwDevice->common.version; } + private: + const audio_module_handle_t mHandle; const char * const mModuleName; audio_hw_device_t * const mHwDevice; - Flags mFlags; + const Flags mFlags; }; // AudioStreamOut and AudioStreamIn are immutable, so their fields are const. @@ -509,7 +612,7 @@ private: struct AudioStreamOut { AudioHwDevice* const audioHwDev; audio_stream_out_t* const stream; - audio_output_flags_t flags; + const audio_output_flags_t flags; audio_hw_device_t* hwDev() const { return audioHwDev->hwDevice(); } @@ -537,7 +640,11 @@ private: }; mutable Mutex mLock; - + // protects mClients and mNotificationClients. + // must be locked after mLock and ThreadBase::mLock if both must be locked + // avoids acquiring AudioFlinger::mLock from inside thread loop. + mutable Mutex mClientLock; + // protected by mClientLock DefaultKeyedVector< pid_t, wp<Client> > mClients; // see ~Client() mutable Mutex mHardwareLock; @@ -586,8 +693,13 @@ private: DefaultKeyedVector< audio_io_handle_t, sp<RecordThread> > mRecordThreads; + // protected by mClientLock DefaultKeyedVector< pid_t, sp<NotificationClient> > mNotificationClients; + volatile int32_t mNextUniqueId; // updated by android_atomic_inc + // nextUniqueId() returns uint32_t, but this is declared int32_t + // because the atomic operations require an int32_t + audio_mode_t mMode; bool mBtNrecIsOff; @@ -602,11 +714,13 @@ private: // to be created private: - sp<Client> registerPid_l(pid_t pid); // always returns non-0 + sp<Client> registerPid(pid_t pid); // always returns non-0 // for use from destructor status_t closeOutput_nonvirtual(audio_io_handle_t output); + void closeOutputInternal_l(sp<PlaybackThread> thread); status_t closeInput_nonvirtual(audio_io_handle_t input); + void closeInputInternal_l(sp<RecordThread> thread); #ifdef TEE_SINK // all record threads serially share a common tee sink, which is re-created on format change @@ -634,7 +748,7 @@ public: // 0x200000 stereo 16-bit PCM frames = 47.5 seconds at 44.1 kHz, 8 megabytes static const size_t kTeeSinkInputFramesDefault = 0x200000; static const size_t kTeeSinkOutputFramesDefault = 0x200000; - static const size_t kTeeSinkTrackFramesDefault = 0x1000; + static const size_t kTeeSinkTrackFramesDefault = 0x200000; #endif // This method reads from a variable without mLock, but the variable is updated under mLock. So @@ -647,10 +761,17 @@ private: bool mIsLowRamDevice; bool mIsDeviceTypeKnown; nsecs_t mGlobalEffectEnableTime; // when a global effect was last enabled + + sp<PatchPanel> mPatchPanel; + + uint32_t mPrimaryOutputSampleRate; // sample rate of the primary output, or zero if none + // protected by mHardwareLock }; #undef INCLUDING_FROM_AUDIOFLINGER_H +const char *formatToString(audio_format_t format); + // ---------------------------------------------------------------------------- }; // namespace android diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp index f92421e..fd28ea1 100644 --- a/services/audioflinger/AudioMixer.cpp +++ b/services/audioflinger/AudioMixer.cpp @@ -22,6 +22,7 @@ #include <stdint.h> #include <string.h> #include <stdlib.h> +#include <math.h> #include <sys/types.h> #include <utils/Errors.h> @@ -34,65 +35,345 @@ #include <system/audio.h> #include <audio_utils/primitives.h> +#include <audio_utils/format.h> #include <common_time/local_clock.h> #include <common_time/cc_helper.h> #include <media/EffectsFactoryApi.h> +#include <audio_effects/effect_downmix.h> +#include "AudioMixerOps.h" #include "AudioMixer.h" +// The FCC_2 macro refers to the Fixed Channel Count of 2 for the legacy integer mixer. +#ifndef FCC_2 +#define FCC_2 2 +#endif + +// Look for MONO_HACK for any Mono hack involving legacy mono channel to +// stereo channel conversion. + +/* VERY_VERY_VERBOSE_LOGGING will show exactly which process hook and track hook is + * being used. This is a considerable amount of log spam, so don't enable unless you + * are verifying the hook based code. + */ +//#define VERY_VERY_VERBOSE_LOGGING +#ifdef VERY_VERY_VERBOSE_LOGGING +#define ALOGVV ALOGV +//define ALOGVV printf // for test-mixer.cpp +#else +#define ALOGVV(a...) do { } while (0) +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) +#endif + +// Set kUseNewMixer to true to use the new mixer engine. Otherwise the +// original code will be used. This is false for now. +static const bool kUseNewMixer = false; + +// Set kUseFloat to true to allow floating input into the mixer engine. +// If kUseNewMixer is false, this is ignored or may be overridden internally +// because of downmix/upmix support. +static const bool kUseFloat = true; + +// Set to default copy buffer size in frames for input processing. +static const size_t kCopyBufferFrameCount = 256; + namespace android { // ---------------------------------------------------------------------------- -AudioMixer::DownmixerBufferProvider::DownmixerBufferProvider() : AudioBufferProvider(), - mTrackBufferProvider(NULL), mDownmixHandle(NULL) + +template <typename T> +T min(const T& a, const T& b) { + return a < b ? a : b; } -AudioMixer::DownmixerBufferProvider::~DownmixerBufferProvider() +AudioMixer::CopyBufferProvider::CopyBufferProvider(size_t inputFrameSize, + size_t outputFrameSize, size_t bufferFrameCount) : + mInputFrameSize(inputFrameSize), + mOutputFrameSize(outputFrameSize), + mLocalBufferFrameCount(bufferFrameCount), + mLocalBufferData(NULL), + mConsumed(0) { - ALOGV("AudioMixer deleting DownmixerBufferProvider (%p)", this); - EffectRelease(mDownmixHandle); + ALOGV("CopyBufferProvider(%p)(%zu, %zu, %zu)", this, + inputFrameSize, outputFrameSize, bufferFrameCount); + LOG_ALWAYS_FATAL_IF(inputFrameSize < outputFrameSize && bufferFrameCount == 0, + "Requires local buffer if inputFrameSize(%zu) < outputFrameSize(%zu)", + inputFrameSize, outputFrameSize); + if (mLocalBufferFrameCount) { + (void)posix_memalign(&mLocalBufferData, 32, mLocalBufferFrameCount * mOutputFrameSize); + } + mBuffer.frameCount = 0; +} + +AudioMixer::CopyBufferProvider::~CopyBufferProvider() +{ + ALOGV("~CopyBufferProvider(%p)", this); + if (mBuffer.frameCount != 0) { + mTrackBufferProvider->releaseBuffer(&mBuffer); + } + free(mLocalBufferData); } -status_t AudioMixer::DownmixerBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer, - int64_t pts) { - //ALOGV("DownmixerBufferProvider::getNextBuffer()"); - if (this->mTrackBufferProvider != NULL) { +status_t AudioMixer::CopyBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer, + int64_t pts) +{ + //ALOGV("CopyBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)", + // this, pBuffer, pBuffer->frameCount, pts); + if (mLocalBufferFrameCount == 0) { status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts); if (res == OK) { - mDownmixConfig.inputCfg.buffer.frameCount = pBuffer->frameCount; - mDownmixConfig.inputCfg.buffer.raw = pBuffer->raw; - mDownmixConfig.outputCfg.buffer.frameCount = pBuffer->frameCount; - mDownmixConfig.outputCfg.buffer.raw = mDownmixConfig.inputCfg.buffer.raw; - // in-place so overwrite the buffer contents, has been set in prepareTrackForDownmix() - //mDownmixConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE; - - res = (*mDownmixHandle)->process(mDownmixHandle, - &mDownmixConfig.inputCfg.buffer, &mDownmixConfig.outputCfg.buffer); - //ALOGV("getNextBuffer is downmixing"); + copyFrames(pBuffer->raw, pBuffer->raw, pBuffer->frameCount); } return res; - } else { - ALOGE("DownmixerBufferProvider::getNextBuffer() error: NULL track buffer provider"); - return NO_INIT; } + if (mBuffer.frameCount == 0) { + mBuffer.frameCount = pBuffer->frameCount; + status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts); + // At one time an upstream buffer provider had + // res == OK and mBuffer.frameCount == 0, doesn't seem to happen now 7/18/2014. + // + // By API spec, if res != OK, then mBuffer.frameCount == 0. + // but there may be improper implementations. + ALOG_ASSERT(res == OK || mBuffer.frameCount == 0); + if (res != OK || mBuffer.frameCount == 0) { // not needed by API spec, but to be safe. + pBuffer->raw = NULL; + pBuffer->frameCount = 0; + return res; + } + mConsumed = 0; + } + ALOG_ASSERT(mConsumed < mBuffer.frameCount); + size_t count = min(mLocalBufferFrameCount, mBuffer.frameCount - mConsumed); + count = min(count, pBuffer->frameCount); + pBuffer->raw = mLocalBufferData; + pBuffer->frameCount = count; + copyFrames(pBuffer->raw, (uint8_t*)mBuffer.raw + mConsumed * mInputFrameSize, + pBuffer->frameCount); + return OK; } -void AudioMixer::DownmixerBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer) { - //ALOGV("DownmixerBufferProvider::releaseBuffer()"); - if (this->mTrackBufferProvider != NULL) { +void AudioMixer::CopyBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer) +{ + //ALOGV("CopyBufferProvider(%p)::releaseBuffer(%p(%zu))", + // this, pBuffer, pBuffer->frameCount); + if (mLocalBufferFrameCount == 0) { mTrackBufferProvider->releaseBuffer(pBuffer); - } else { - ALOGE("DownmixerBufferProvider::releaseBuffer() error: NULL track buffer provider"); + return; } + // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount"); + mConsumed += pBuffer->frameCount; // TODO: update for efficiency to reuse existing content + if (mConsumed != 0 && mConsumed >= mBuffer.frameCount) { + mTrackBufferProvider->releaseBuffer(&mBuffer); + ALOG_ASSERT(mBuffer.frameCount == 0); + } + pBuffer->raw = NULL; + pBuffer->frameCount = 0; } +void AudioMixer::CopyBufferProvider::reset() +{ + if (mBuffer.frameCount != 0) { + mTrackBufferProvider->releaseBuffer(&mBuffer); + } + mConsumed = 0; +} -// ---------------------------------------------------------------------------- -bool AudioMixer::isMultichannelCapable = false; +AudioMixer::DownmixerBufferProvider::DownmixerBufferProvider( + audio_channel_mask_t inputChannelMask, + audio_channel_mask_t outputChannelMask, audio_format_t format, + uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount) : + CopyBufferProvider( + audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(inputChannelMask), + audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(outputChannelMask), + bufferFrameCount) // set bufferFrameCount to 0 to do in-place +{ + ALOGV("DownmixerBufferProvider(%p)(%#x, %#x, %#x %u %d)", + this, inputChannelMask, outputChannelMask, format, + sampleRate, sessionId); + if (!sIsMultichannelCapable + || EffectCreate(&sDwnmFxDesc.uuid, + sessionId, + SESSION_ID_INVALID_AND_IGNORED, + &mDownmixHandle) != 0) { + ALOGE("DownmixerBufferProvider() error creating downmixer effect"); + mDownmixHandle = NULL; + return; + } + // channel input configuration will be overridden per-track + mDownmixConfig.inputCfg.channels = inputChannelMask; // FIXME: Should be bits + mDownmixConfig.outputCfg.channels = outputChannelMask; // FIXME: should be bits + mDownmixConfig.inputCfg.format = format; + mDownmixConfig.outputCfg.format = format; + mDownmixConfig.inputCfg.samplingRate = sampleRate; + mDownmixConfig.outputCfg.samplingRate = sampleRate; + mDownmixConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; + mDownmixConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE; + // input and output buffer provider, and frame count will not be used as the downmix effect + // process() function is called directly (see DownmixerBufferProvider::getNextBuffer()) + mDownmixConfig.inputCfg.mask = EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS | + EFFECT_CONFIG_FORMAT | EFFECT_CONFIG_ACC_MODE; + mDownmixConfig.outputCfg.mask = mDownmixConfig.inputCfg.mask; + + int cmdStatus; + uint32_t replySize = sizeof(int); + + // Configure downmixer + status_t status = (*mDownmixHandle)->command(mDownmixHandle, + EFFECT_CMD_SET_CONFIG /*cmdCode*/, sizeof(effect_config_t) /*cmdSize*/, + &mDownmixConfig /*pCmdData*/, + &replySize, &cmdStatus /*pReplyData*/); + if (status != 0 || cmdStatus != 0) { + ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while configuring downmixer", + status, cmdStatus); + EffectRelease(mDownmixHandle); + mDownmixHandle = NULL; + return; + } + + // Enable downmixer + replySize = sizeof(int); + status = (*mDownmixHandle)->command(mDownmixHandle, + EFFECT_CMD_ENABLE /*cmdCode*/, 0 /*cmdSize*/, NULL /*pCmdData*/, + &replySize, &cmdStatus /*pReplyData*/); + if (status != 0 || cmdStatus != 0) { + ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while enabling downmixer", + status, cmdStatus); + EffectRelease(mDownmixHandle); + mDownmixHandle = NULL; + return; + } + + // Set downmix type + // parameter size rounded for padding on 32bit boundary + const int psizePadded = ((sizeof(downmix_params_t) - 1)/sizeof(int) + 1) * sizeof(int); + const int downmixParamSize = + sizeof(effect_param_t) + psizePadded + sizeof(downmix_type_t); + effect_param_t * const param = (effect_param_t *) malloc(downmixParamSize); + param->psize = sizeof(downmix_params_t); + const downmix_params_t downmixParam = DOWNMIX_PARAM_TYPE; + memcpy(param->data, &downmixParam, param->psize); + const downmix_type_t downmixType = DOWNMIX_TYPE_FOLD; + param->vsize = sizeof(downmix_type_t); + memcpy(param->data + psizePadded, &downmixType, param->vsize); + replySize = sizeof(int); + status = (*mDownmixHandle)->command(mDownmixHandle, + EFFECT_CMD_SET_PARAM /* cmdCode */, downmixParamSize /* cmdSize */, + param /*pCmdData*/, &replySize, &cmdStatus /*pReplyData*/); + free(param); + if (status != 0 || cmdStatus != 0) { + ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while setting downmix type", + status, cmdStatus); + EffectRelease(mDownmixHandle); + mDownmixHandle = NULL; + return; + } + ALOGV("DownmixerBufferProvider() downmix type set to %d", (int) downmixType); +} + +AudioMixer::DownmixerBufferProvider::~DownmixerBufferProvider() +{ + ALOGV("~DownmixerBufferProvider (%p)", this); + EffectRelease(mDownmixHandle); + mDownmixHandle = NULL; +} -effect_descriptor_t AudioMixer::dwnmFxDesc; +void AudioMixer::DownmixerBufferProvider::copyFrames(void *dst, const void *src, size_t frames) +{ + mDownmixConfig.inputCfg.buffer.frameCount = frames; + mDownmixConfig.inputCfg.buffer.raw = const_cast<void *>(src); + mDownmixConfig.outputCfg.buffer.frameCount = frames; + mDownmixConfig.outputCfg.buffer.raw = dst; + // may be in-place if src == dst. + status_t res = (*mDownmixHandle)->process(mDownmixHandle, + &mDownmixConfig.inputCfg.buffer, &mDownmixConfig.outputCfg.buffer); + ALOGE_IF(res != OK, "DownmixBufferProvider error %d", res); +} + +/* call once in a pthread_once handler. */ +/*static*/ status_t AudioMixer::DownmixerBufferProvider::init() +{ + // find multichannel downmix effect if we have to play multichannel content + uint32_t numEffects = 0; + int ret = EffectQueryNumberEffects(&numEffects); + if (ret != 0) { + ALOGE("AudioMixer() error %d querying number of effects", ret); + return NO_INIT; + } + ALOGV("EffectQueryNumberEffects() numEffects=%d", numEffects); + + for (uint32_t i = 0 ; i < numEffects ; i++) { + if (EffectQueryEffect(i, &sDwnmFxDesc) == 0) { + ALOGV("effect %d is called %s", i, sDwnmFxDesc.name); + if (memcmp(&sDwnmFxDesc.type, EFFECT_UIID_DOWNMIX, sizeof(effect_uuid_t)) == 0) { + ALOGI("found effect \"%s\" from %s", + sDwnmFxDesc.name, sDwnmFxDesc.implementor); + sIsMultichannelCapable = true; + break; + } + } + } + ALOGW_IF(!sIsMultichannelCapable, "unable to find downmix effect"); + return NO_INIT; +} + +/*static*/ bool AudioMixer::DownmixerBufferProvider::sIsMultichannelCapable = false; +/*static*/ effect_descriptor_t AudioMixer::DownmixerBufferProvider::sDwnmFxDesc; + +AudioMixer::RemixBufferProvider::RemixBufferProvider(audio_channel_mask_t inputChannelMask, + audio_channel_mask_t outputChannelMask, audio_format_t format, + size_t bufferFrameCount) : + CopyBufferProvider( + audio_bytes_per_sample(format) + * audio_channel_count_from_out_mask(inputChannelMask), + audio_bytes_per_sample(format) + * audio_channel_count_from_out_mask(outputChannelMask), + bufferFrameCount), + mFormat(format), + mSampleSize(audio_bytes_per_sample(format)), + mInputChannels(audio_channel_count_from_out_mask(inputChannelMask)), + mOutputChannels(audio_channel_count_from_out_mask(outputChannelMask)) +{ + ALOGV("RemixBufferProvider(%p)(%#x, %#x, %#x) %zu %zu", + this, format, inputChannelMask, outputChannelMask, + mInputChannels, mOutputChannels); + // TODO: consider channel representation in index array formulation + // We ignore channel representation, and just use the bits. + memcpy_by_index_array_initialization(mIdxAry, ARRAY_SIZE(mIdxAry), + audio_channel_mask_get_bits(outputChannelMask), + audio_channel_mask_get_bits(inputChannelMask)); +} + +void AudioMixer::RemixBufferProvider::copyFrames(void *dst, const void *src, size_t frames) +{ + memcpy_by_index_array(dst, mOutputChannels, + src, mInputChannels, mIdxAry, mSampleSize, frames); +} + +AudioMixer::ReformatBufferProvider::ReformatBufferProvider(int32_t channels, + audio_format_t inputFormat, audio_format_t outputFormat, + size_t bufferFrameCount) : + CopyBufferProvider( + channels * audio_bytes_per_sample(inputFormat), + channels * audio_bytes_per_sample(outputFormat), + bufferFrameCount), + mChannels(channels), + mInputFormat(inputFormat), + mOutputFormat(outputFormat) +{ + ALOGV("ReformatBufferProvider(%p)(%d, %#x, %#x)", this, channels, inputFormat, outputFormat); +} + +void AudioMixer::ReformatBufferProvider::copyFrames(void *dst, const void *src, size_t frames) +{ + memcpy_by_audio_format(dst, mOutputFormat, src, mInputFormat, frames * mChannels); +} + +// ---------------------------------------------------------------------------- // Ensure mConfiguredNames bitmask is initialized properly on all architectures. // The value of 1 << x is undefined in C when x >= 32. @@ -101,20 +382,12 @@ AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate, uint32_t maxNumTr : mTrackNames(0), mConfiguredNames((maxNumTracks >= 32 ? 0 : 1 << maxNumTracks) - 1), mSampleRate(sampleRate) { - // AudioMixer is not yet capable of multi-channel beyond stereo - COMPILE_TIME_ASSERT_FUNCTION_SCOPE(2 == MAX_NUM_CHANNELS); - ALOG_ASSERT(maxNumTracks <= MAX_NUM_TRACKS, "maxNumTracks %u > MAX_NUM_TRACKS %u", maxNumTracks, MAX_NUM_TRACKS); // AudioMixer is not yet capable of more than 32 active track inputs ALOG_ASSERT(32 >= MAX_NUM_TRACKS, "bad MAX_NUM_TRACKS %d", MAX_NUM_TRACKS); - // AudioMixer is not yet capable of multi-channel output beyond stereo - ALOG_ASSERT(2 == MAX_NUM_CHANNELS, "bad MAX_NUM_CHANNELS %d", MAX_NUM_CHANNELS); - - LocalClock lc; - pthread_once(&sOnceControl, &sInitRoutine); mState.enabledTracks= 0; @@ -133,30 +406,10 @@ AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate, uint32_t maxNumTr for (unsigned i=0 ; i < MAX_NUM_TRACKS ; i++) { t->resampler = NULL; t->downmixerBufferProvider = NULL; + t->mReformatBufferProvider = NULL; t++; } - // find multichannel downmix effect if we have to play multichannel content - uint32_t numEffects = 0; - int ret = EffectQueryNumberEffects(&numEffects); - if (ret != 0) { - ALOGE("AudioMixer() error %d querying number of effects", ret); - return; - } - ALOGV("EffectQueryNumberEffects() numEffects=%d", numEffects); - - for (uint32_t i = 0 ; i < numEffects ; i++) { - if (EffectQueryEffect(i, &dwnmFxDesc) == 0) { - ALOGV("effect %d is called %s", i, dwnmFxDesc.name); - if (memcmp(&dwnmFxDesc.type, EFFECT_UIID_DOWNMIX, sizeof(effect_uuid_t)) == 0) { - ALOGI("found effect \"%s\" from %s", - dwnmFxDesc.name, dwnmFxDesc.implementor); - isMultichannelCapable = true; - break; - } - } - } - ALOGE_IF(!isMultichannelCapable, "unable to find downmix effect"); } AudioMixer::~AudioMixer() @@ -165,6 +418,7 @@ AudioMixer::~AudioMixer() for (unsigned i=0 ; i < MAX_NUM_TRACKS ; i++) { delete t->resampler; delete t->downmixerBufferProvider; + delete t->mReformatBufferProvider; t++; } delete [] mState.outputTemp; @@ -176,32 +430,52 @@ void AudioMixer::setLog(NBLog::Writer *log) mState.mLog = log; } -int AudioMixer::getTrackName(audio_channel_mask_t channelMask, int sessionId) +int AudioMixer::getTrackName(audio_channel_mask_t channelMask, + audio_format_t format, int sessionId) { + if (!isValidPcmTrackFormat(format)) { + ALOGE("AudioMixer::getTrackName invalid format (%#x)", format); + return -1; + } uint32_t names = (~mTrackNames) & mConfiguredNames; if (names != 0) { int n = __builtin_ctz(names); ALOGV("add track (%d)", n); - mTrackNames |= 1 << n; // assume default parameters for the track, except where noted below track_t* t = &mState.tracks[n]; t->needs = 0; - t->volume[0] = UNITY_GAIN; - t->volume[1] = UNITY_GAIN; - // no initialization needed - // t->prevVolume[0] - // t->prevVolume[1] + + // Integer volume. + // Currently integer volume is kept for the legacy integer mixer. + // Will be removed when the legacy mixer path is removed. + t->volume[0] = UNITY_GAIN_INT; + t->volume[1] = UNITY_GAIN_INT; + t->prevVolume[0] = UNITY_GAIN_INT << 16; + t->prevVolume[1] = UNITY_GAIN_INT << 16; t->volumeInc[0] = 0; t->volumeInc[1] = 0; t->auxLevel = 0; t->auxInc = 0; + t->prevAuxLevel = 0; + + // Floating point volume. + t->mVolume[0] = UNITY_GAIN_FLOAT; + t->mVolume[1] = UNITY_GAIN_FLOAT; + t->mPrevVolume[0] = UNITY_GAIN_FLOAT; + t->mPrevVolume[1] = UNITY_GAIN_FLOAT; + t->mVolumeInc[0] = 0.; + t->mVolumeInc[1] = 0.; + t->mAuxLevel = 0.; + t->mAuxInc = 0.; + t->mPrevAuxLevel = 0.; + // no initialization needed - // t->prevAuxLevel // t->frameCount - t->channelCount = 2; + t->channelCount = audio_channel_count_from_out_mask(channelMask); t->enabled = false; - t->format = 16; - t->channelMask = AUDIO_CHANNEL_OUT_STEREO; + ALOGV_IF(audio_channel_mask_get_bits(channelMask) != AUDIO_CHANNEL_OUT_STEREO, + "Non-stereo channel mask: %d\n", channelMask); + t->channelMask = channelMask; t->sessionId = sessionId; // setBufferProvider(name, AudioBufferProvider *) is required before enable(name) t->bufferProvider = NULL; @@ -215,52 +489,116 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask, int sessionId) // setParameter(name, TRACK, MAIN_BUFFER, mixBuffer) is required before enable(name) t->mainBuffer = NULL; t->auxBuffer = NULL; + t->mInputBufferProvider = NULL; + t->mReformatBufferProvider = NULL; t->downmixerBufferProvider = NULL; - - status_t status = initTrackDownmix(&mState.tracks[n], n, channelMask); - if (status == OK) { - return TRACK0 + n; + t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT; + t->mFormat = format; + t->mMixerInFormat = kUseFloat && kUseNewMixer + ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; + t->mMixerChannelMask = audio_channel_mask_from_representation_and_bits( + AUDIO_CHANNEL_REPRESENTATION_POSITION, AUDIO_CHANNEL_OUT_STEREO); + t->mMixerChannelCount = audio_channel_count_from_out_mask(t->mMixerChannelMask); + // Check the downmixing (or upmixing) requirements. + status_t status = initTrackDownmix(t, n); + if (status != OK) { + ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask); + return -1; } - ALOGE("AudioMixer::getTrackName(0x%x) failed, error preparing track for downmix", - channelMask); + // initTrackDownmix() may change the input format requirement. + // If you desire floating point input to the mixer, it may change + // to integer because the downmixer requires integer to process. + ALOGVV("mMixerFormat:%#x mMixerInFormat:%#x\n", t->mMixerFormat, t->mMixerInFormat); + prepareTrackForReformat(t, n); + mTrackNames |= 1 << n; + return TRACK0 + n; } + ALOGE("AudioMixer::getTrackName out of available tracks"); return -1; } void AudioMixer::invalidateState(uint32_t mask) { - if (mask) { + if (mask != 0) { mState.needsChanged |= mask; mState.hook = process__validate; } } -status_t AudioMixer::initTrackDownmix(track_t* pTrack, int trackNum, audio_channel_mask_t mask) +// Called when channel masks have changed for a track name +// TODO: Fix Downmixbufferprofider not to (possibly) change mixer input format, +// which will simplify this logic. +bool AudioMixer::setChannelMasks(int name, + audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask) { + track_t &track = mState.tracks[name]; + + if (trackChannelMask == track.channelMask + && mixerChannelMask == track.mMixerChannelMask) { + return false; // no need to change + } + // always recompute for both channel masks even if only one has changed. + const uint32_t trackChannelCount = audio_channel_count_from_out_mask(trackChannelMask); + const uint32_t mixerChannelCount = audio_channel_count_from_out_mask(mixerChannelMask); + const bool mixerChannelCountChanged = track.mMixerChannelCount != mixerChannelCount; + + ALOG_ASSERT((trackChannelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX) + && trackChannelCount + && mixerChannelCount); + track.channelMask = trackChannelMask; + track.channelCount = trackChannelCount; + track.mMixerChannelMask = mixerChannelMask; + track.mMixerChannelCount = mixerChannelCount; + + // channel masks have changed, does this track need a downmixer? + // update to try using our desired format (if we aren't already using it) + const audio_format_t prevMixerInFormat = track.mMixerInFormat; + track.mMixerInFormat = kUseFloat && kUseNewMixer + ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; + const status_t status = initTrackDownmix(&mState.tracks[name], name); + ALOGE_IF(status != OK, + "initTrackDownmix error %d, track channel mask %#x, mixer channel mask %#x", + status, track.channelMask, track.mMixerChannelMask); + + const bool mixerInFormatChanged = prevMixerInFormat != track.mMixerInFormat; + if (mixerInFormatChanged) { + prepareTrackForReformat(&track, name); // because of downmixer, track format may change! + } + + if (track.resampler && (mixerInFormatChanged || mixerChannelCountChanged)) { + // resampler input format or channels may have changed. + const uint32_t resetToSampleRate = track.sampleRate; + delete track.resampler; + track.resampler = NULL; + track.sampleRate = mSampleRate; // without resampler, track rate is device sample rate. + // recreate the resampler with updated format, channels, saved sampleRate. + track.setResampler(resetToSampleRate /*trackSampleRate*/, mSampleRate /*devSampleRate*/); + } + return true; +} + +status_t AudioMixer::initTrackDownmix(track_t* pTrack, int trackName) { - uint32_t channelCount = popcount(mask); - ALOG_ASSERT((channelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX) && channelCount); - status_t status = OK; - if (channelCount > MAX_NUM_CHANNELS) { - pTrack->channelMask = mask; - pTrack->channelCount = channelCount; - ALOGV("initTrackDownmix(track=%d, mask=0x%x) calls prepareTrackForDownmix()", - trackNum, mask); - status = prepareTrackForDownmix(pTrack, trackNum); - } else { - unprepareTrackForDownmix(pTrack, trackNum); + // Only remix (upmix or downmix) if the track and mixer/device channel masks + // are not the same and not handled internally, as mono -> stereo currently is. + if (pTrack->channelMask != pTrack->mMixerChannelMask + && !(pTrack->channelMask == AUDIO_CHANNEL_OUT_MONO + && pTrack->mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO)) { + return prepareTrackForDownmix(pTrack, trackName); } - return status; + // no remix necessary + unprepareTrackForDownmix(pTrack, trackName); + return NO_ERROR; } -void AudioMixer::unprepareTrackForDownmix(track_t* pTrack, int trackName) { +void AudioMixer::unprepareTrackForDownmix(track_t* pTrack, int trackName __unused) { ALOGV("AudioMixer::unprepareTrackForDownmix(%d)", trackName); if (pTrack->downmixerBufferProvider != NULL) { // this track had previously been configured with a downmixer, delete it ALOGV(" deleting old downmixer"); - pTrack->bufferProvider = pTrack->downmixerBufferProvider->mTrackBufferProvider; delete pTrack->downmixerBufferProvider; pTrack->downmixerBufferProvider = NULL; + reconfigureBufferProviders(pTrack); } else { ALOGV(" nothing to do, no downmixer to delete"); } @@ -272,101 +610,66 @@ status_t AudioMixer::prepareTrackForDownmix(track_t* pTrack, int trackName) // discard the previous downmixer if there was one unprepareTrackForDownmix(pTrack, trackName); - - DownmixerBufferProvider* pDbp = new DownmixerBufferProvider(); - int32_t status; - - if (!isMultichannelCapable) { - ALOGE("prepareTrackForDownmix(%d) fails: mixer doesn't support multichannel content", - trackName); - goto noDownmixForActiveTrack; - } - - if (EffectCreate(&dwnmFxDesc.uuid, - pTrack->sessionId /*sessionId*/, -2 /*ioId not relevant here, using random value*/, - &pDbp->mDownmixHandle/*pHandle*/) != 0) { - ALOGE("prepareTrackForDownmix(%d) fails: error creating downmixer effect", trackName); - goto noDownmixForActiveTrack; - } - - // channel input configuration will be overridden per-track - pDbp->mDownmixConfig.inputCfg.channels = pTrack->channelMask; - pDbp->mDownmixConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; - pDbp->mDownmixConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT; - pDbp->mDownmixConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; - pDbp->mDownmixConfig.inputCfg.samplingRate = pTrack->sampleRate; - pDbp->mDownmixConfig.outputCfg.samplingRate = pTrack->sampleRate; - pDbp->mDownmixConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; - pDbp->mDownmixConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE; - // input and output buffer provider, and frame count will not be used as the downmix effect - // process() function is called directly (see DownmixerBufferProvider::getNextBuffer()) - pDbp->mDownmixConfig.inputCfg.mask = EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS | - EFFECT_CONFIG_FORMAT | EFFECT_CONFIG_ACC_MODE; - pDbp->mDownmixConfig.outputCfg.mask = pDbp->mDownmixConfig.inputCfg.mask; - - {// scope for local variables that are not used in goto label "noDownmixForActiveTrack" - int cmdStatus; - uint32_t replySize = sizeof(int); - - // Configure and enable downmixer - status = (*pDbp->mDownmixHandle)->command(pDbp->mDownmixHandle, - EFFECT_CMD_SET_CONFIG /*cmdCode*/, sizeof(effect_config_t) /*cmdSize*/, - &pDbp->mDownmixConfig /*pCmdData*/, - &replySize /*replySize*/, &cmdStatus /*pReplyData*/); - if ((status != 0) || (cmdStatus != 0)) { - ALOGE("error %d while configuring downmixer for track %d", status, trackName); - goto noDownmixForActiveTrack; - } - replySize = sizeof(int); - status = (*pDbp->mDownmixHandle)->command(pDbp->mDownmixHandle, - EFFECT_CMD_ENABLE /*cmdCode*/, 0 /*cmdSize*/, NULL /*pCmdData*/, - &replySize /*replySize*/, &cmdStatus /*pReplyData*/); - if ((status != 0) || (cmdStatus != 0)) { - ALOGE("error %d while enabling downmixer for track %d", status, trackName); - goto noDownmixForActiveTrack; + if (DownmixerBufferProvider::isMultichannelCapable()) { + DownmixerBufferProvider* pDbp = new DownmixerBufferProvider(pTrack->channelMask, + pTrack->mMixerChannelMask, + AUDIO_FORMAT_PCM_16_BIT /* TODO: use pTrack->mMixerInFormat, now only PCM 16 */, + pTrack->sampleRate, pTrack->sessionId, kCopyBufferFrameCount); + + if (pDbp->isValid()) { // if constructor completed properly + pTrack->mMixerInFormat = AUDIO_FORMAT_PCM_16_BIT; // PCM 16 bit required for downmix + pTrack->downmixerBufferProvider = pDbp; + reconfigureBufferProviders(pTrack); + return NO_ERROR; } + delete pDbp; + } - // Set downmix type - // parameter size rounded for padding on 32bit boundary - const int psizePadded = ((sizeof(downmix_params_t) - 1)/sizeof(int) + 1) * sizeof(int); - const int downmixParamSize = - sizeof(effect_param_t) + psizePadded + sizeof(downmix_type_t); - effect_param_t * const param = (effect_param_t *) malloc(downmixParamSize); - param->psize = sizeof(downmix_params_t); - const downmix_params_t downmixParam = DOWNMIX_PARAM_TYPE; - memcpy(param->data, &downmixParam, param->psize); - const downmix_type_t downmixType = DOWNMIX_TYPE_FOLD; - param->vsize = sizeof(downmix_type_t); - memcpy(param->data + psizePadded, &downmixType, param->vsize); - - status = (*pDbp->mDownmixHandle)->command(pDbp->mDownmixHandle, - EFFECT_CMD_SET_PARAM /* cmdCode */, downmixParamSize/* cmdSize */, - param /*pCmndData*/, &replySize /*replySize*/, &cmdStatus /*pReplyData*/); - - free(param); - - if ((status != 0) || (cmdStatus != 0)) { - ALOGE("error %d while setting downmix type for track %d", status, trackName); - goto noDownmixForActiveTrack; - } else { - ALOGV("downmix type set to %d for track %d", (int) downmixType, trackName); - } - }// end of scope for local variables that are not used in goto label "noDownmixForActiveTrack" + // Effect downmixer does not accept the channel conversion. Let's use our remixer. + RemixBufferProvider* pRbp = new RemixBufferProvider(pTrack->channelMask, + pTrack->mMixerChannelMask, pTrack->mMixerInFormat, kCopyBufferFrameCount); + // Remix always finds a conversion whereas Downmixer effect above may fail. + pTrack->downmixerBufferProvider = pRbp; + reconfigureBufferProviders(pTrack); + return NO_ERROR; +} - // initialization successful: - // - keep track of the real buffer provider in case it was set before - pDbp->mTrackBufferProvider = pTrack->bufferProvider; - // - we'll use the downmix effect integrated inside this - // track's buffer provider, and we'll use it as the track's buffer provider - pTrack->downmixerBufferProvider = pDbp; - pTrack->bufferProvider = pDbp; +void AudioMixer::unprepareTrackForReformat(track_t* pTrack, int trackName __unused) { + ALOGV("AudioMixer::unprepareTrackForReformat(%d)", trackName); + if (pTrack->mReformatBufferProvider != NULL) { + delete pTrack->mReformatBufferProvider; + pTrack->mReformatBufferProvider = NULL; + reconfigureBufferProviders(pTrack); + } +} +status_t AudioMixer::prepareTrackForReformat(track_t* pTrack, int trackName) +{ + ALOGV("AudioMixer::prepareTrackForReformat(%d) with format %#x", trackName, pTrack->mFormat); + // discard the previous reformatter if there was one + unprepareTrackForReformat(pTrack, trackName); + // only configure reformatter if needed + if (pTrack->mFormat != pTrack->mMixerInFormat) { + pTrack->mReformatBufferProvider = new ReformatBufferProvider( + audio_channel_count_from_out_mask(pTrack->channelMask), + pTrack->mFormat, pTrack->mMixerInFormat, + kCopyBufferFrameCount); + reconfigureBufferProviders(pTrack); + } return NO_ERROR; +} -noDownmixForActiveTrack: - delete pDbp; - pTrack->downmixerBufferProvider = NULL; - return NO_INIT; +void AudioMixer::reconfigureBufferProviders(track_t* pTrack) +{ + pTrack->bufferProvider = pTrack->mInputBufferProvider; + if (pTrack->mReformatBufferProvider) { + pTrack->mReformatBufferProvider->setBufferProvider(pTrack->bufferProvider); + pTrack->bufferProvider = pTrack->mReformatBufferProvider; + } + if (pTrack->downmixerBufferProvider) { + pTrack->downmixerBufferProvider->setBufferProvider(pTrack->bufferProvider); + pTrack->bufferProvider = pTrack->downmixerBufferProvider; + } } void AudioMixer::deleteTrackName(int name) @@ -385,6 +688,8 @@ void AudioMixer::deleteTrackName(int name) track.resampler = NULL; // delete the downmixer unprepareTrackForDownmix(&mState.tracks[name], name); + // delete the reformatter + unprepareTrackForReformat(&mState.tracks[name], name); mTrackNames &= ~(1<<name); } @@ -415,6 +720,73 @@ void AudioMixer::disable(int name) } } +/* Sets the volume ramp variables for the AudioMixer. + * + * The volume ramp variables are used to transition from the previous + * volume to the set volume. ramp controls the duration of the transition. + * Its value is typically one state framecount period, but may also be 0, + * meaning "immediate." + * + * FIXME: 1) Volume ramp is enabled only if there is a nonzero integer increment + * even if there is a nonzero floating point increment (in that case, the volume + * change is immediate). This restriction should be changed when the legacy mixer + * is removed (see #2). + * FIXME: 2) Integer volume variables are used for Legacy mixing and should be removed + * when no longer needed. + * + * @param newVolume set volume target in floating point [0.0, 1.0]. + * @param ramp number of frames to increment over. if ramp is 0, the volume + * should be set immediately. Currently ramp should not exceed 65535 (frames). + * @param pIntSetVolume pointer to the U4.12 integer target volume, set on return. + * @param pIntPrevVolume pointer to the U4.28 integer previous volume, set on return. + * @param pIntVolumeInc pointer to the U4.28 increment per output audio frame, set on return. + * @param pSetVolume pointer to the float target volume, set on return. + * @param pPrevVolume pointer to the float previous volume, set on return. + * @param pVolumeInc pointer to the float increment per output audio frame, set on return. + * @return true if the volume has changed, false if volume is same. + */ +static inline bool setVolumeRampVariables(float newVolume, int32_t ramp, + int16_t *pIntSetVolume, int32_t *pIntPrevVolume, int32_t *pIntVolumeInc, + float *pSetVolume, float *pPrevVolume, float *pVolumeInc) { + if (newVolume == *pSetVolume) { + return false; + } + /* set the floating point volume variables */ + if (ramp != 0) { + *pVolumeInc = (newVolume - *pSetVolume) / ramp; + *pPrevVolume = *pSetVolume; + } else { + *pVolumeInc = 0; + *pPrevVolume = newVolume; + } + *pSetVolume = newVolume; + + /* set the legacy integer volume variables */ + int32_t intVolume = newVolume * AudioMixer::UNITY_GAIN_INT; + if (intVolume > AudioMixer::UNITY_GAIN_INT) { + intVolume = AudioMixer::UNITY_GAIN_INT; + } else if (intVolume < 0) { + ALOGE("negative volume %.7g", newVolume); + intVolume = 0; // should never happen, but for safety check. + } + if (intVolume == *pIntSetVolume) { + *pIntVolumeInc = 0; + /* TODO: integer/float workaround: ignore floating volume ramp */ + *pVolumeInc = 0; + *pPrevVolume = newVolume; + return true; + } + if (ramp != 0) { + *pIntVolumeInc = ((intVolume - *pIntSetVolume) << 16) / ramp; + *pIntPrevVolume = (*pIntVolumeInc == 0 ? intVolume : *pIntSetVolume) << 16; + } else { + *pIntVolumeInc = 0; + *pIntPrevVolume = intVolume << 16; + } + *pIntSetVolume = intVolume; + return true; +} + void AudioMixer::setParameter(int name, int target, int param, void *value) { name -= TRACK0; @@ -429,16 +801,10 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) case TRACK: switch (param) { case CHANNEL_MASK: { - audio_channel_mask_t mask = - static_cast<audio_channel_mask_t>(reinterpret_cast<uintptr_t>(value)); - if (track.channelMask != mask) { - uint32_t channelCount = popcount(mask); - ALOG_ASSERT((channelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX) && channelCount); - track.channelMask = mask; - track.channelCount = channelCount; - // the mask has changed, does this track need a downmixer? - initTrackDownmix(&mState.tracks[name], name, mask); - ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", mask); + const audio_channel_mask_t trackChannelMask = + static_cast<audio_channel_mask_t>(valueInt); + if (setChannelMasks(name, trackChannelMask, track.mMixerChannelMask)) { + ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", trackChannelMask); invalidateState(1 << name); } } break; @@ -456,15 +822,37 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) invalidateState(1 << name); } break; - case FORMAT: - ALOG_ASSERT(valueInt == AUDIO_FORMAT_PCM_16_BIT); - break; + case FORMAT: { + audio_format_t format = static_cast<audio_format_t>(valueInt); + if (track.mFormat != format) { + ALOG_ASSERT(audio_is_linear_pcm(format), "Invalid format %#x", format); + track.mFormat = format; + ALOGV("setParameter(TRACK, FORMAT, %#x)", format); + prepareTrackForReformat(&track, name); + invalidateState(1 << name); + } + } break; // FIXME do we want to support setting the downmix type from AudioFlinger? // for a specific track? or per mixer? /* case DOWNMIX_TYPE: break */ + case MIXER_FORMAT: { + audio_format_t format = static_cast<audio_format_t>(valueInt); + if (track.mMixerFormat != format) { + track.mMixerFormat = format; + ALOGV("setParameter(TRACK, MIXER_FORMAT, %#x)", format); + } + } break; + case MIXER_CHANNEL_MASK: { + const audio_channel_mask_t mixerChannelMask = + static_cast<audio_channel_mask_t>(valueInt); + if (setChannelMasks(name, track.channelMask, mixerChannelMask)) { + ALOGV("setParameter(TRACK, MIXER_CHANNEL_MASK, %#x)", mixerChannelMask); + invalidateState(1 << name); + } + } break; default: - LOG_FATAL("bad param"); + LOG_ALWAYS_FATAL("setParameter track: bad param %d", param); } break; @@ -489,85 +877,77 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) invalidateState(1 << name); break; default: - LOG_FATAL("bad param"); + LOG_ALWAYS_FATAL("setParameter resample: bad param %d", param); } break; case RAMP_VOLUME: case VOLUME: switch (param) { - case VOLUME0: - case VOLUME1: - if (track.volume[param-VOLUME0] != valueInt) { - ALOGV("setParameter(VOLUME, VOLUME0/1: %04x)", valueInt); - track.prevVolume[param-VOLUME0] = track.volume[param-VOLUME0] << 16; - track.volume[param-VOLUME0] = valueInt; - if (target == VOLUME) { - track.prevVolume[param-VOLUME0] = valueInt << 16; - track.volumeInc[param-VOLUME0] = 0; - } else { - int32_t d = (valueInt<<16) - track.prevVolume[param-VOLUME0]; - int32_t volInc = d / int32_t(mState.frameCount); - track.volumeInc[param-VOLUME0] = volInc; - if (volInc == 0) { - track.prevVolume[param-VOLUME0] = valueInt << 16; - } - } - invalidateState(1 << name); - } - break; case AUXLEVEL: - //ALOG_ASSERT(0 <= valueInt && valueInt <= MAX_GAIN_INT, "bad aux level %d", valueInt); - if (track.auxLevel != valueInt) { - ALOGV("setParameter(VOLUME, AUXLEVEL: %04x)", valueInt); - track.prevAuxLevel = track.auxLevel << 16; - track.auxLevel = valueInt; - if (target == VOLUME) { - track.prevAuxLevel = valueInt << 16; - track.auxInc = 0; - } else { - int32_t d = (valueInt<<16) - track.prevAuxLevel; - int32_t volInc = d / int32_t(mState.frameCount); - track.auxInc = volInc; - if (volInc == 0) { - track.prevAuxLevel = valueInt << 16; - } - } + if (setVolumeRampVariables(*reinterpret_cast<float*>(value), + target == RAMP_VOLUME ? mState.frameCount : 0, + &track.auxLevel, &track.prevAuxLevel, &track.auxInc, + &track.mAuxLevel, &track.mPrevAuxLevel, &track.mAuxInc)) { + ALOGV("setParameter(%s, AUXLEVEL: %04x)", + target == VOLUME ? "VOLUME" : "RAMP_VOLUME", track.auxLevel); invalidateState(1 << name); } break; default: - LOG_FATAL("bad param"); + if ((unsigned)param >= VOLUME0 && (unsigned)param < VOLUME0 + MAX_NUM_VOLUMES) { + if (setVolumeRampVariables(*reinterpret_cast<float*>(value), + target == RAMP_VOLUME ? mState.frameCount : 0, + &track.volume[param - VOLUME0], &track.prevVolume[param - VOLUME0], + &track.volumeInc[param - VOLUME0], + &track.mVolume[param - VOLUME0], &track.mPrevVolume[param - VOLUME0], + &track.mVolumeInc[param - VOLUME0])) { + ALOGV("setParameter(%s, VOLUME%d: %04x)", + target == VOLUME ? "VOLUME" : "RAMP_VOLUME", param - VOLUME0, + track.volume[param - VOLUME0]); + invalidateState(1 << name); + } + } else { + LOG_ALWAYS_FATAL("setParameter volume: bad param %d", param); + } } break; default: - LOG_FATAL("bad target"); + LOG_ALWAYS_FATAL("setParameter: bad target %d", target); } } -bool AudioMixer::track_t::setResampler(uint32_t value, uint32_t devSampleRate) +bool AudioMixer::track_t::setResampler(uint32_t trackSampleRate, uint32_t devSampleRate) { - if (value != devSampleRate || resampler != NULL) { - if (sampleRate != value) { - sampleRate = value; + if (trackSampleRate != devSampleRate || resampler != NULL) { + if (sampleRate != trackSampleRate) { + sampleRate = trackSampleRate; if (resampler == NULL) { - ALOGV("creating resampler from track %d Hz to device %d Hz", value, devSampleRate); + ALOGV("Creating resampler from track %d Hz to device %d Hz", + trackSampleRate, devSampleRate); AudioResampler::src_quality quality; // force lowest quality level resampler if use case isn't music or video // FIXME this is flawed for dynamic sample rates, as we choose the resampler // quality level based on the initial ratio, but that could change later. // Should have a way to distinguish tracks with static ratios vs. dynamic ratios. - if (!((value == 44100 && devSampleRate == 48000) || - (value == 48000 && devSampleRate == 44100))) { - quality = AudioResampler::LOW_QUALITY; + if (!((trackSampleRate == 44100 && devSampleRate == 48000) || + (trackSampleRate == 48000 && devSampleRate == 44100))) { + quality = AudioResampler::DYN_LOW_QUALITY; } else { quality = AudioResampler::DEFAULT_QUALITY; } + + // TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer + // but if none exists, it is the channel count (1 for mono). + const int resamplerChannelCount = downmixerBufferProvider != NULL + ? mMixerChannelCount : channelCount; + ALOGVV("Creating resampler:" + " format(%#x) channels(%d) devSampleRate(%u) quality(%d)\n", + mMixerInFormat, resamplerChannelCount, devSampleRate, quality); resampler = AudioResampler::create( - format, - // the resampler sees the number of channels after the downmixer, if any - downmixerBufferProvider != NULL ? MAX_NUM_CHANNELS : channelCount, + mMixerInFormat, + resamplerChannelCount, devSampleRate, quality); resampler->setLocalTimeFreq(sLocalTimeFreq); } @@ -577,21 +957,57 @@ bool AudioMixer::track_t::setResampler(uint32_t value, uint32_t devSampleRate) return false; } -inline -void AudioMixer::track_t::adjustVolumeRamp(bool aux) +/* Checks to see if the volume ramp has completed and clears the increment + * variables appropriately. + * + * FIXME: There is code to handle int/float ramp variable switchover should it not + * complete within a mixer buffer processing call, but it is preferred to avoid switchover + * due to precision issues. The switchover code is included for legacy code purposes + * and can be removed once the integer volume is removed. + * + * It is not sufficient to clear only the volumeInc integer variable because + * if one channel requires ramping, all channels are ramped. + * + * There is a bit of duplicated code here, but it keeps backward compatibility. + */ +inline void AudioMixer::track_t::adjustVolumeRamp(bool aux, bool useFloat) { - for (uint32_t i=0 ; i<MAX_NUM_CHANNELS ; i++) { - if (((volumeInc[i]>0) && (((prevVolume[i]+volumeInc[i])>>16) >= volume[i])) || - ((volumeInc[i]<0) && (((prevVolume[i]+volumeInc[i])>>16) <= volume[i]))) { - volumeInc[i] = 0; - prevVolume[i] = volume[i]<<16; + if (useFloat) { + for (uint32_t i = 0; i < MAX_NUM_VOLUMES; i++) { + if (mVolumeInc[i] != 0 && fabs(mVolume[i] - mPrevVolume[i]) <= fabs(mVolumeInc[i])) { + volumeInc[i] = 0; + prevVolume[i] = volume[i] << 16; + mVolumeInc[i] = 0.; + mPrevVolume[i] = mVolume[i]; + } else { + //ALOGV("ramp: %f %f %f", mVolume[i], mPrevVolume[i], mVolumeInc[i]); + prevVolume[i] = u4_28_from_float(mPrevVolume[i]); + } + } + } else { + for (uint32_t i = 0; i < MAX_NUM_VOLUMES; i++) { + if (((volumeInc[i]>0) && (((prevVolume[i]+volumeInc[i])>>16) >= volume[i])) || + ((volumeInc[i]<0) && (((prevVolume[i]+volumeInc[i])>>16) <= volume[i]))) { + volumeInc[i] = 0; + prevVolume[i] = volume[i] << 16; + mVolumeInc[i] = 0.; + mPrevVolume[i] = mVolume[i]; + } else { + //ALOGV("ramp: %d %d %d", volume[i] << 16, prevVolume[i], volumeInc[i]); + mPrevVolume[i] = float_from_u4_28(prevVolume[i]); + } } } + /* TODO: aux is always integer regardless of output buffer type */ if (aux) { if (((auxInc>0) && (((prevAuxLevel+auxInc)>>16) >= auxLevel)) || - ((auxInc<0) && (((prevAuxLevel+auxInc)>>16) <= auxLevel))) { + ((auxInc<0) && (((prevAuxLevel+auxInc)>>16) <= auxLevel))) { auxInc = 0; - prevAuxLevel = auxLevel<<16; + prevAuxLevel = auxLevel << 16; + mAuxInc = 0.; + mPrevAuxLevel = mAuxLevel; + } else { + //ALOGV("aux ramp: %d %d %d", auxLevel << 16, prevAuxLevel, auxInc); } } } @@ -610,21 +1026,16 @@ 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].downmixerBufferProvider != NULL) { - // update required? - if (mState.tracks[name].downmixerBufferProvider->mTrackBufferProvider != bufferProvider) { - ALOGV("AudioMixer::setBufferProvider(%p) for downmix", bufferProvider); - // setting the buffer provider for a track that gets downmixed consists in: - // 1/ setting the buffer provider to the "downmix / buffer provider" wrapper - // so it's the one that gets called when the buffer provider is needed, - mState.tracks[name].bufferProvider = mState.tracks[name].downmixerBufferProvider; - // 2/ saving the buffer provider for the track so the wrapper can use it - // when it downmixes. - mState.tracks[name].downmixerBufferProvider->mTrackBufferProvider = bufferProvider; - } - } else { - mState.tracks[name].bufferProvider = bufferProvider; + 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) { } + + mState.tracks[name].mInputBufferProvider = bufferProvider; + reconfigureBufferProviders(&mState.tracks[name]); } @@ -657,6 +1068,9 @@ void AudioMixer::process__validate(state_t* state, int64_t pts) // compute everything we need... int countActiveTracks = 0; + // TODO: fix all16BitsStereNoResample logic to + // either properly handle muted tracks (it should ignore them) + // or remove altogether as an obsolete optimization. bool all16BitsStereoNoResample = true; bool resampling = false; bool volumeRamp = false; @@ -668,39 +1082,47 @@ void AudioMixer::process__validate(state_t* state, int64_t pts) countActiveTracks++; track_t& t = state->tracks[i]; uint32_t n = 0; + // FIXME can overflow (mask is only 3 bits) n |= NEEDS_CHANNEL_1 + t.channelCount - 1; - n |= NEEDS_FORMAT_16; - n |= t.doesResample() ? NEEDS_RESAMPLE_ENABLED : NEEDS_RESAMPLE_DISABLED; + if (t.doesResample()) { + n |= NEEDS_RESAMPLE; + } if (t.auxLevel != 0 && t.auxBuffer != NULL) { - n |= NEEDS_AUX_ENABLED; + n |= NEEDS_AUX; } if (t.volumeInc[0]|t.volumeInc[1]) { volumeRamp = true; } else if (!t.doesResample() && t.volumeRL == 0) { - n |= NEEDS_MUTE_ENABLED; + n |= NEEDS_MUTE; } t.needs = n; - if ((n & NEEDS_MUTE__MASK) == NEEDS_MUTE_ENABLED) { + if (n & NEEDS_MUTE) { t.hook = track__nop; } else { - if ((n & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) { + if (n & NEEDS_AUX) { all16BitsStereoNoResample = false; } - if ((n & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) { + if (n & NEEDS_RESAMPLE) { all16BitsStereoNoResample = false; resampling = true; - t.hook = track__genericResample; + t.hook = getTrackHook(TRACKTYPE_RESAMPLE, t.mMixerChannelCount, + t.mMixerInFormat, t.mMixerFormat); ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2, "Track %d needs downmix + resample", i); } else { if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){ - t.hook = track__16BitsMono; + t.hook = getTrackHook( + t.mMixerChannelCount == 2 // TODO: MONO_HACK. + ? TRACKTYPE_NORESAMPLEMONO : TRACKTYPE_NORESAMPLE, + t.mMixerChannelCount, + t.mMixerInFormat, t.mMixerFormat); all16BitsStereoNoResample = false; } if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2){ - t.hook = track__16BitsStereo; + t.hook = getTrackHook(TRACKTYPE_NORESAMPLE, t.mMixerChannelCount, + t.mMixerInFormat, t.mMixerFormat); ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2, "Track %d needs downmix", i); } @@ -710,7 +1132,7 @@ void AudioMixer::process__validate(state_t* state, int64_t pts) // select the processing hooks state->hook = process__nop; - if (countActiveTracks) { + if (countActiveTracks > 0) { if (resampling) { if (!state->outputTemp) { state->outputTemp = new int32_t[MAX_NUM_CHANNELS * state->frameCount]; @@ -731,7 +1153,17 @@ void AudioMixer::process__validate(state_t* state, int64_t pts) state->hook = process__genericNoResampling; if (all16BitsStereoNoResample && !volumeRamp) { if (countActiveTracks == 1) { - state->hook = process__OneTrack16BitsStereoNoResampling; + const int i = 31 - __builtin_clz(state->enabledTracks); + track_t& t = state->tracks[i]; + if ((t.needs & NEEDS_MUTE) == 0) { + // The check prevents a muted track from acquiring a process hook. + // + // This is dangerous if the track is MONO as that requires + // special case handling due to implicit channel duplication. + // Stereo or Multichannel should actually be fine here. + state->hook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK, + t.mMixerChannelCount, t.mMixerInFormat, t.mMixerFormat); + } } } } @@ -746,16 +1178,15 @@ void AudioMixer::process__validate(state_t* state, int64_t pts) // Now that the volume ramp has been done, set optimal state and // track hooks for subsequent mixer process - if (countActiveTracks) { + if (countActiveTracks > 0) { bool allMuted = true; uint32_t en = state->enabledTracks; while (en) { const int i = 31 - __builtin_clz(en); en &= ~(1<<i); track_t& t = state->tracks[i]; - if (!t.doesResample() && t.volumeRL == 0) - { - t.needs |= NEEDS_MUTE_ENABLED; + if (!t.doesResample() && t.volumeRL == 0) { + t.needs |= NEEDS_MUTE; t.hook = track__nop; } else { allMuted = false; @@ -765,7 +1196,11 @@ void AudioMixer::process__validate(state_t* state, int64_t pts) state->hook = process__nop; } else if (all16BitsStereoNoResample) { if (countActiveTracks == 1) { - state->hook = process__OneTrack16BitsStereoNoResampling; + const int i = 31 - __builtin_clz(state->enabledTracks); + track_t& t = state->tracks[i]; + // Muted single tracks handled by allMuted above. + state->hook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK, + t.mMixerChannelCount, t.mMixerInFormat, t.mMixerFormat); } } } @@ -775,15 +1210,15 @@ void AudioMixer::process__validate(state_t* state, int64_t pts) void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp, int32_t* aux) { + ALOGVV("track__genericResample\n"); t->resampler->setSampleRate(t->sampleRate); // ramp gain - resample to temp buffer and scale/mix in 2nd step if (aux != NULL) { // always resample with unity gain when sending to auxiliary buffer to be able // to apply send level after resampling - // TODO: modify each resampler to support aux channel? - t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN); - memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t)); + t->resampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT); + memset(temp, 0, outFrameCount * t->mMixerChannelCount * sizeof(int32_t)); t->resampler->resample(temp, outFrameCount, t->bufferProvider); if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc)) { volumeRampStereo(t, out, outFrameCount, temp, aux); @@ -792,7 +1227,7 @@ void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFram } } else { if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1])) { - t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN); + t->resampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT); memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t)); t->resampler->resample(temp, outFrameCount, t->bufferProvider); volumeRampStereo(t, out, outFrameCount, temp, aux); @@ -800,14 +1235,14 @@ void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFram // constant gain else { - t->resampler->setVolume(t->volume[0], t->volume[1]); + t->resampler->setVolume(t->mVolume[0], t->mVolume[1]); t->resampler->resample(out, outFrameCount, t->bufferProvider); } } } -void AudioMixer::track__nop(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp, - int32_t* aux) +void AudioMixer::track__nop(track_t* t __unused, int32_t* out __unused, + size_t outFrameCount __unused, int32_t* temp __unused, int32_t* aux __unused) { } @@ -883,9 +1318,10 @@ void AudioMixer::volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32 } } -void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, - int32_t* aux) +void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, + int32_t* temp __unused, int32_t* aux) { + ALOGVV("track__16BitsStereo\n"); const int16_t *in = static_cast<const int16_t *>(t->in); if (CC_UNLIKELY(aux != NULL)) { @@ -974,9 +1410,10 @@ void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount t->in = in; } -void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, - int32_t* aux) +void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, + int32_t* temp __unused, int32_t* aux) { + ALOGVV("track__16BitsMono\n"); const int16_t *in = static_cast<int16_t const *>(t->in); if (CC_UNLIKELY(aux != NULL)) { @@ -1064,8 +1501,8 @@ void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, // no-op case void AudioMixer::process__nop(state_t* state, int64_t pts) { + ALOGVV("process__nop\n"); uint32_t e0 = state->enabledTracks; - size_t bufSize = state->frameCount * sizeof(int16_t) * MAX_NUM_CHANNELS; while (e0) { // process by group of tracks with same output buffer to // avoid multiple memset() on same buffer @@ -1084,7 +1521,8 @@ void AudioMixer::process__nop(state_t* state, int64_t pts) } e0 &= ~(e1); - memset(t1.mainBuffer, 0, bufSize); + memset(t1.mainBuffer, 0, state->frameCount * t1.mMixerChannelCount + * audio_bytes_per_sample(t1.mMixerFormat)); } while (e1) { @@ -1110,6 +1548,7 @@ void AudioMixer::process__nop(state_t* state, int64_t pts) // generic code without resampling void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts) { + ALOGVV("process__genericNoResampling\n"); int32_t outTemp[BLOCKSIZE * MAX_NUM_CHANNELS] __attribute__((aligned(32))); // acquire each track's buffer @@ -1154,7 +1593,7 @@ void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts) track_t& t = state->tracks[i]; size_t outFrames = BLOCKSIZE; int32_t *aux = NULL; - if (CC_UNLIKELY((t.needs & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED)) { + if (CC_UNLIKELY(t.needs & NEEDS_AUX)) { aux = t.auxBuffer + numFrames; } while (outFrames) { @@ -1166,9 +1605,9 @@ void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts) break; } size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount; - if (inFrames) { - t.hook(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, - state->resampleTemp, aux); + if (inFrames > 0) { + t.hook(&t, outTemp + (BLOCKSIZE - outFrames) * t.mMixerChannelCount, + inFrames, state->resampleTemp, aux); t.frameCount -= inFrames; outFrames -= inFrames; if (CC_UNLIKELY(aux != NULL)) { @@ -1192,8 +1631,13 @@ void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts) } } } - ditherAndClamp(out, outTemp, BLOCKSIZE); - out += BLOCKSIZE; + + convertMixerFormat(out, t1.mMixerFormat, outTemp, t1.mMixerInFormat, + BLOCKSIZE * t1.mMixerChannelCount); + // TODO: fix ugly casting due to choice of out pointer type + out = reinterpret_cast<int32_t*>((uint8_t*)out + + BLOCKSIZE * t1.mMixerChannelCount + * audio_bytes_per_sample(t1.mMixerFormat)); numFrames += BLOCKSIZE; } while (numFrames < state->frameCount); } @@ -1212,10 +1656,9 @@ void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts) // generic code with resampling void AudioMixer::process__genericResampling(state_t* state, int64_t pts) { + ALOGVV("process__genericResampling\n"); // this const just means that local variable outTemp doesn't change int32_t* const outTemp = state->outputTemp; - const size_t size = sizeof(int32_t) * MAX_NUM_CHANNELS * state->frameCount; - size_t numFrames = state->frameCount; uint32_t e0 = state->enabledTracks; @@ -1236,20 +1679,20 @@ void AudioMixer::process__genericResampling(state_t* state, int64_t pts) } e0 &= ~(e1); int32_t *out = t1.mainBuffer; - memset(outTemp, 0, size); + memset(outTemp, 0, sizeof(*outTemp) * t1.mMixerChannelCount * state->frameCount); while (e1) { const int i = 31 - __builtin_clz(e1); e1 &= ~(1<<i); track_t& t = state->tracks[i]; int32_t *aux = NULL; - if (CC_UNLIKELY((t.needs & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED)) { + if (CC_UNLIKELY(t.needs & NEEDS_AUX)) { aux = t.auxBuffer; } // this is a little goofy, on the resampling case we don't // acquire/release the buffers because it's done by // the resampler. - if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) { + if (t.needs & NEEDS_RESAMPLE) { t.resampler->setPTS(pts); t.hook(&t, outTemp, numFrames, state->resampleTemp, aux); } else { @@ -1268,14 +1711,15 @@ void AudioMixer::process__genericResampling(state_t* state, int64_t pts) if (CC_UNLIKELY(aux != NULL)) { aux += outFrames; } - t.hook(&t, outTemp + outFrames*MAX_NUM_CHANNELS, t.buffer.frameCount, + t.hook(&t, outTemp + outFrames * t.mMixerChannelCount, t.buffer.frameCount, state->resampleTemp, aux); outFrames += t.buffer.frameCount; t.bufferProvider->releaseBuffer(&t.buffer); } } } - ditherAndClamp(out, outTemp, numFrames); + convertMixerFormat(out, t1.mMixerFormat, + outTemp, t1.mMixerInFormat, numFrames * t1.mMixerChannelCount); } } @@ -1283,6 +1727,7 @@ void AudioMixer::process__genericResampling(state_t* state, int64_t pts) void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, int64_t pts) { + ALOGVV("process__OneTrack16BitsStereoNoResampling\n"); // This method is only called when state->enabledTracks has exactly // one bit set. The asserts below would verify this, but are commented out // since the whole point of this method is to optimize performance. @@ -1294,6 +1739,7 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, AudioBufferProvider::Buffer& b(t.buffer); int32_t* out = t.mainBuffer; + float *fout = reinterpret_cast<float*>(out); size_t numFrames = state->frameCount; const int16_t vl = t.volume[0]; @@ -1307,161 +1753,486 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, // in == NULL can happen if the track was flushed just after having // been enabled for mixing. - if (in == NULL || ((unsigned long)in & 3)) { - memset(out, 0, numFrames*MAX_NUM_CHANNELS*sizeof(int16_t)); - ALOGE_IF(((unsigned long)in & 3), "process stereo track: input buffer alignment pb: " - "buffer %p track %d, channels %d, needs %08x", - in, i, t.channelCount, t.needs); + if (in == NULL || (((uintptr_t)in) & 3)) { + memset(out, 0, numFrames + * t.mMixerChannelCount * audio_bytes_per_sample(t.mMixerFormat)); + ALOGE_IF((((uintptr_t)in) & 3), + "process__OneTrack16BitsStereoNoResampling: misaligned buffer" + " %p track %d, channels %d, needs %08x, volume %08x vfl %f vfr %f", + in, i, t.channelCount, t.needs, vrl, t.mVolume[0], t.mVolume[1]); return; } size_t outFrames = b.frameCount; - if (CC_UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) { - // volume is boosted, so we might need to clamp even though - // we process only one track. - do { - uint32_t rl = *reinterpret_cast<const uint32_t *>(in); - in += 2; - int32_t l = mulRL(1, rl, vrl) >> 12; - int32_t r = mulRL(0, rl, vrl) >> 12; - // clamping... - l = clamp16(l); - r = clamp16(r); - *out++ = (r<<16) | (l & 0xFFFF); - } while (--outFrames); - } else { + switch (t.mMixerFormat) { + case AUDIO_FORMAT_PCM_FLOAT: do { uint32_t rl = *reinterpret_cast<const uint32_t *>(in); in += 2; - int32_t l = mulRL(1, rl, vrl) >> 12; - int32_t r = mulRL(0, rl, vrl) >> 12; - *out++ = (r<<16) | (l & 0xFFFF); + int32_t l = mulRL(1, rl, vrl); + int32_t r = mulRL(0, rl, vrl); + *fout++ = float_from_q4_27(l); + *fout++ = float_from_q4_27(r); + // Note: In case of later int16_t sink output, + // conversion and clamping is done by memcpy_to_i16_from_float(). } while (--outFrames); + break; + case AUDIO_FORMAT_PCM_16_BIT: + if (CC_UNLIKELY(uint32_t(vl) > UNITY_GAIN_INT || uint32_t(vr) > UNITY_GAIN_INT)) { + // volume is boosted, so we might need to clamp even though + // we process only one track. + do { + uint32_t rl = *reinterpret_cast<const uint32_t *>(in); + in += 2; + int32_t l = mulRL(1, rl, vrl) >> 12; + int32_t r = mulRL(0, rl, vrl) >> 12; + // clamping... + l = clamp16(l); + r = clamp16(r); + *out++ = (r<<16) | (l & 0xFFFF); + } while (--outFrames); + } else { + do { + uint32_t rl = *reinterpret_cast<const uint32_t *>(in); + in += 2; + int32_t l = mulRL(1, rl, vrl) >> 12; + int32_t r = mulRL(0, rl, vrl) >> 12; + *out++ = (r<<16) | (l & 0xFFFF); + } while (--outFrames); + } + break; + default: + LOG_ALWAYS_FATAL("bad mixer format: %d", t.mMixerFormat); } numFrames -= b.frameCount; t.bufferProvider->releaseBuffer(&b); } } -#if 0 -// 2 tracks is also a common case -// NEVER used in current implementation of process__validate() -// only use if the 2 tracks have the same output buffer -void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, - int64_t pts) +int64_t AudioMixer::calculateOutputPTS(const track_t& t, int64_t basePTS, + int outputFrameIndex) { - int i; - uint32_t en = state->enabledTracks; - - i = 31 - __builtin_clz(en); - const track_t& t0 = state->tracks[i]; - AudioBufferProvider::Buffer& b0(t0.buffer); + if (AudioBufferProvider::kInvalidPTS == basePTS) { + return AudioBufferProvider::kInvalidPTS; + } - en &= ~(1<<i); - i = 31 - __builtin_clz(en); - const track_t& t1 = state->tracks[i]; - AudioBufferProvider::Buffer& b1(t1.buffer); + return basePTS + ((outputFrameIndex * sLocalTimeFreq) / t.sampleRate); +} - const int16_t *in0; - const int16_t vl0 = t0.volume[0]; - const int16_t vr0 = t0.volume[1]; - size_t frameCount0 = 0; +/*static*/ uint64_t AudioMixer::sLocalTimeFreq; +/*static*/ pthread_once_t AudioMixer::sOnceControl = PTHREAD_ONCE_INIT; - const int16_t *in1; - const int16_t vl1 = t1.volume[0]; - const int16_t vr1 = t1.volume[1]; - size_t frameCount1 = 0; +/*static*/ void AudioMixer::sInitRoutine() +{ + LocalClock lc; + sLocalTimeFreq = lc.getLocalFreq(); // for the resampler - //FIXME: only works if two tracks use same buffer - int32_t* out = t0.mainBuffer; - size_t numFrames = state->frameCount; - const int16_t *buff = NULL; + DownmixerBufferProvider::init(); // for the downmixer +} +/* TODO: consider whether this level of optimization is necessary. + * Perhaps just stick with a single for loop. + */ + +// Needs to derive a compile time constant (constexpr). Could be targeted to go +// to a MONOVOL mixtype based on MAX_NUM_VOLUMES, but that's an unnecessary complication. +#define MIXTYPE_MONOVOL(mixtype) (mixtype == MIXTYPE_MULTI ? MIXTYPE_MULTI_MONOVOL : \ + mixtype == MIXTYPE_MULTI_SAVEONLY ? MIXTYPE_MULTI_SAVEONLY_MONOVOL : mixtype) + +/* MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration) + * TO: int32_t (Q4.27) or float + * TI: int32_t (Q4.27) or int16_t (Q0.15) or float + * TA: int32_t (Q4.27) + */ +template <int MIXTYPE, + typename TO, typename TI, typename TV, typename TA, typename TAV> +static void volumeRampMulti(uint32_t channels, TO* out, size_t frameCount, + const TI* in, TA* aux, TV *vol, const TV *volinc, TAV *vola, TAV volainc) +{ + switch (channels) { + case 1: + volumeRampMulti<MIXTYPE, 1>(out, frameCount, in, aux, vol, volinc, vola, volainc); + break; + case 2: + volumeRampMulti<MIXTYPE, 2>(out, frameCount, in, aux, vol, volinc, vola, volainc); + break; + case 3: + volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 3>(out, + frameCount, in, aux, vol, volinc, vola, volainc); + break; + case 4: + volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 4>(out, + frameCount, in, aux, vol, volinc, vola, volainc); + break; + case 5: + volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 5>(out, + frameCount, in, aux, vol, volinc, vola, volainc); + break; + case 6: + volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 6>(out, + frameCount, in, aux, vol, volinc, vola, volainc); + break; + case 7: + volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 7>(out, + frameCount, in, aux, vol, volinc, vola, volainc); + break; + case 8: + volumeRampMulti<MIXTYPE_MONOVOL(MIXTYPE), 8>(out, + frameCount, in, aux, vol, volinc, vola, volainc); + break; + } +} - while (numFrames) { +/* MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration) + * TO: int32_t (Q4.27) or float + * TI: int32_t (Q4.27) or int16_t (Q0.15) or float + * TA: int32_t (Q4.27) + */ +template <int MIXTYPE, + typename TO, typename TI, typename TV, typename TA, typename TAV> +static void volumeMulti(uint32_t channels, TO* out, size_t frameCount, + const TI* in, TA* aux, const TV *vol, TAV vola) +{ + switch (channels) { + case 1: + volumeMulti<MIXTYPE, 1>(out, frameCount, in, aux, vol, vola); + break; + case 2: + volumeMulti<MIXTYPE, 2>(out, frameCount, in, aux, vol, vola); + break; + case 3: + volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 3>(out, frameCount, in, aux, vol, vola); + break; + case 4: + volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 4>(out, frameCount, in, aux, vol, vola); + break; + case 5: + volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 5>(out, frameCount, in, aux, vol, vola); + break; + case 6: + volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 6>(out, frameCount, in, aux, vol, vola); + break; + case 7: + volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 7>(out, frameCount, in, aux, vol, vola); + break; + case 8: + volumeMulti<MIXTYPE_MONOVOL(MIXTYPE), 8>(out, frameCount, in, aux, vol, vola); + break; + } +} - if (frameCount0 == 0) { - b0.frameCount = numFrames; - int64_t outputPTS = calculateOutputPTS(t0, pts, - out - t0.mainBuffer); - t0.bufferProvider->getNextBuffer(&b0, outputPTS); - if (b0.i16 == NULL) { - if (buff == NULL) { - buff = new int16_t[MAX_NUM_CHANNELS * state->frameCount]; - } - in0 = buff; - b0.frameCount = numFrames; - } else { - in0 = b0.i16; +/* MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration) + * USEFLOATVOL (set to true if float volume is used) + * ADJUSTVOL (set to true if volume ramp parameters needs adjustment afterwards) + * TO: int32_t (Q4.27) or float + * TI: int32_t (Q4.27) or int16_t (Q0.15) or float + * TA: int32_t (Q4.27) + */ +template <int MIXTYPE, bool USEFLOATVOL, bool ADJUSTVOL, + typename TO, typename TI, typename TA> +void AudioMixer::volumeMix(TO *out, size_t outFrames, + const TI *in, TA *aux, bool ramp, AudioMixer::track_t *t) +{ + if (USEFLOATVOL) { + if (ramp) { + volumeRampMulti<MIXTYPE>(t->mMixerChannelCount, out, outFrames, in, aux, + t->mPrevVolume, t->mVolumeInc, &t->prevAuxLevel, t->auxInc); + if (ADJUSTVOL) { + t->adjustVolumeRamp(aux != NULL, true); } - frameCount0 = b0.frameCount; + } else { + volumeMulti<MIXTYPE>(t->mMixerChannelCount, out, outFrames, in, aux, + t->mVolume, t->auxLevel); } - if (frameCount1 == 0) { - b1.frameCount = numFrames; - int64_t outputPTS = calculateOutputPTS(t1, pts, - out - t0.mainBuffer); - t1.bufferProvider->getNextBuffer(&b1, outputPTS); - if (b1.i16 == NULL) { - if (buff == NULL) { - buff = new int16_t[MAX_NUM_CHANNELS * state->frameCount]; - } - in1 = buff; - b1.frameCount = numFrames; - } else { - in1 = b1.i16; + } else { + if (ramp) { + volumeRampMulti<MIXTYPE>(t->mMixerChannelCount, out, outFrames, in, aux, + t->prevVolume, t->volumeInc, &t->prevAuxLevel, t->auxInc); + if (ADJUSTVOL) { + t->adjustVolumeRamp(aux != NULL); } - frameCount1 = b1.frameCount; + } else { + volumeMulti<MIXTYPE>(t->mMixerChannelCount, out, outFrames, in, aux, + t->volume, t->auxLevel); } + } +} - size_t outFrames = frameCount0 < frameCount1?frameCount0:frameCount1; - - numFrames -= outFrames; - frameCount0 -= outFrames; - frameCount1 -= outFrames; +/* This process hook is called when there is a single track without + * aux buffer, volume ramp, or resampling. + * TODO: Update the hook selection: this can properly handle aux and ramp. + * + * MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration) + * TO: int32_t (Q4.27) or float + * TI: int32_t (Q4.27) or int16_t (Q0.15) or float + * TA: int32_t (Q4.27) + */ +template <int MIXTYPE, typename TO, typename TI, typename TA> +void AudioMixer::process_NoResampleOneTrack(state_t* state, int64_t pts) +{ + ALOGVV("process_NoResampleOneTrack\n"); + // CLZ is faster than CTZ on ARM, though really not sure if true after 31 - clz. + const int i = 31 - __builtin_clz(state->enabledTracks); + ALOG_ASSERT((1 << i) == state->enabledTracks, "more than 1 track enabled"); + track_t *t = &state->tracks[i]; + const uint32_t channels = t->mMixerChannelCount; + TO* out = reinterpret_cast<TO*>(t->mainBuffer); + TA* aux = reinterpret_cast<TA*>(t->auxBuffer); + const bool ramp = t->needsRamp(); + + for (size_t numFrames = state->frameCount; numFrames; ) { + AudioBufferProvider::Buffer& b(t->buffer); + // get input buffer + b.frameCount = numFrames; + const int64_t outputPTS = calculateOutputPTS(*t, pts, state->frameCount - numFrames); + t->bufferProvider->getNextBuffer(&b, outputPTS); + const TI *in = reinterpret_cast<TI*>(b.raw); - do { - int32_t l0 = *in0++; - int32_t r0 = *in0++; - l0 = mul(l0, vl0); - r0 = mul(r0, vr0); - int32_t l = *in1++; - int32_t r = *in1++; - l = mulAdd(l, vl1, l0) >> 12; - r = mulAdd(r, vr1, r0) >> 12; - // clamping... - l = clamp16(l); - r = clamp16(r); - *out++ = (r<<16) | (l & 0xFFFF); - } while (--outFrames); - - if (frameCount0 == 0) { - t0.bufferProvider->releaseBuffer(&b0); + // in == NULL can happen if the track was flushed just after having + // been enabled for mixing. + if (in == NULL || (((uintptr_t)in) & 3)) { + memset(out, 0, numFrames + * channels * audio_bytes_per_sample(t->mMixerFormat)); + ALOGE_IF((((uintptr_t)in) & 3), "process_NoResampleOneTrack: bus error: " + "buffer %p track %p, channels %d, needs %#x", + in, t, t->channelCount, t->needs); + return; } - if (frameCount1 == 0) { - t1.bufferProvider->releaseBuffer(&b1); + + const size_t outFrames = b.frameCount; + volumeMix<MIXTYPE, is_same<TI, float>::value, false> ( + out, outFrames, in, aux, ramp, t); + + out += outFrames * channels; + if (aux != NULL) { + aux += channels; } + numFrames -= b.frameCount; + + // release buffer + t->bufferProvider->releaseBuffer(&b); } + if (ramp) { + t->adjustVolumeRamp(aux != NULL, is_same<TI, float>::value); + } +} - delete [] buff; +/* This track hook is called to do resampling then mixing, + * pulling from the track's upstream AudioBufferProvider. + * + * MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration) + * TO: int32_t (Q4.27) or float + * TI: int32_t (Q4.27) or int16_t (Q0.15) or float + * TA: int32_t (Q4.27) + */ +template <int MIXTYPE, typename TO, typename TI, typename TA> +void AudioMixer::track__Resample(track_t* t, TO* out, size_t outFrameCount, TO* temp, TA* aux) +{ + ALOGVV("track__Resample\n"); + t->resampler->setSampleRate(t->sampleRate); + const bool ramp = t->needsRamp(); + if (ramp || aux != NULL) { + // if ramp: resample with unity gain to temp buffer and scale/mix in 2nd step. + // if aux != NULL: resample with unity gain to temp buffer then apply send level. + + t->resampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT); + memset(temp, 0, outFrameCount * t->mMixerChannelCount * sizeof(TO)); + t->resampler->resample((int32_t*)temp, outFrameCount, t->bufferProvider); + + volumeMix<MIXTYPE, is_same<TI, float>::value, true>( + out, outFrameCount, temp, aux, ramp, t); + + } else { // constant volume gain + t->resampler->setVolume(t->mVolume[0], t->mVolume[1]); + t->resampler->resample((int32_t*)out, outFrameCount, t->bufferProvider); + } } -#endif -int64_t AudioMixer::calculateOutputPTS(const track_t& t, int64_t basePTS, - int outputFrameIndex) +/* This track hook is called to mix a track, when no resampling is required. + * The input buffer should be present in t->in. + * + * MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration) + * TO: int32_t (Q4.27) or float + * TI: int32_t (Q4.27) or int16_t (Q0.15) or float + * TA: int32_t (Q4.27) + */ +template <int MIXTYPE, typename TO, typename TI, typename TA> +void AudioMixer::track__NoResample(track_t* t, TO* out, size_t frameCount, + TO* temp __unused, TA* aux) { - if (AudioBufferProvider::kInvalidPTS == basePTS) - return AudioBufferProvider::kInvalidPTS; + ALOGVV("track__NoResample\n"); + const TI *in = static_cast<const TI *>(t->in); - return basePTS + ((outputFrameIndex * sLocalTimeFreq) / t.sampleRate); + volumeMix<MIXTYPE, is_same<TI, float>::value, true>( + out, frameCount, in, aux, t->needsRamp(), t); + + // MIXTYPE_MONOEXPAND reads a single input channel and expands to NCHAN output channels. + // MIXTYPE_MULTI reads NCHAN input channels and places to NCHAN output channels. + in += (MIXTYPE == MIXTYPE_MONOEXPAND) ? frameCount : frameCount * t->mMixerChannelCount; + t->in = in; } -/*static*/ uint64_t AudioMixer::sLocalTimeFreq; -/*static*/ pthread_once_t AudioMixer::sOnceControl = PTHREAD_ONCE_INIT; +/* The Mixer engine generates either int32_t (Q4_27) or float data. + * We use this function to convert the engine buffers + * to the desired mixer output format, either int16_t (Q.15) or float. + */ +void AudioMixer::convertMixerFormat(void *out, audio_format_t mixerOutFormat, + void *in, audio_format_t mixerInFormat, size_t sampleCount) +{ + switch (mixerInFormat) { + case AUDIO_FORMAT_PCM_FLOAT: + switch (mixerOutFormat) { + case AUDIO_FORMAT_PCM_FLOAT: + memcpy(out, in, sampleCount * sizeof(float)); // MEMCPY. TODO optimize out + break; + case AUDIO_FORMAT_PCM_16_BIT: + memcpy_to_i16_from_float((int16_t*)out, (float*)in, sampleCount); + break; + default: + LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat); + break; + } + break; + case AUDIO_FORMAT_PCM_16_BIT: + switch (mixerOutFormat) { + case AUDIO_FORMAT_PCM_FLOAT: + memcpy_to_float_from_q4_27((float*)out, (int32_t*)in, sampleCount); + break; + case AUDIO_FORMAT_PCM_16_BIT: + // two int16_t are produced per iteration + ditherAndClamp((int32_t*)out, (int32_t*)in, sampleCount >> 1); + break; + default: + LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat); + break; + } + break; + default: + LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat); + break; + } +} -/*static*/ void AudioMixer::sInitRoutine() +/* Returns the proper track hook to use for mixing the track into the output buffer. + */ +AudioMixer::hook_t AudioMixer::getTrackHook(int trackType, uint32_t channelCount, + audio_format_t mixerInFormat, audio_format_t mixerOutFormat __unused) { - LocalClock lc; - sLocalTimeFreq = lc.getLocalFreq(); + if (!kUseNewMixer && channelCount == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) { + switch (trackType) { + case TRACKTYPE_NOP: + return track__nop; + case TRACKTYPE_RESAMPLE: + return track__genericResample; + case TRACKTYPE_NORESAMPLEMONO: + return track__16BitsMono; + case TRACKTYPE_NORESAMPLE: + return track__16BitsStereo; + default: + LOG_ALWAYS_FATAL("bad trackType: %d", trackType); + break; + } + } + LOG_ALWAYS_FATAL_IF(channelCount > MAX_NUM_CHANNELS); + switch (trackType) { + case TRACKTYPE_NOP: + return track__nop; + case TRACKTYPE_RESAMPLE: + switch (mixerInFormat) { + case AUDIO_FORMAT_PCM_FLOAT: + return (AudioMixer::hook_t) + track__Resample<MIXTYPE_MULTI, float /*TO*/, float /*TI*/, int32_t /*TA*/>; + case AUDIO_FORMAT_PCM_16_BIT: + return (AudioMixer::hook_t)\ + track__Resample<MIXTYPE_MULTI, int32_t, int16_t, int32_t>; + default: + LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat); + break; + } + break; + case TRACKTYPE_NORESAMPLEMONO: + switch (mixerInFormat) { + case AUDIO_FORMAT_PCM_FLOAT: + return (AudioMixer::hook_t) + track__NoResample<MIXTYPE_MONOEXPAND, float, float, int32_t>; + case AUDIO_FORMAT_PCM_16_BIT: + return (AudioMixer::hook_t) + track__NoResample<MIXTYPE_MONOEXPAND, int32_t, int16_t, int32_t>; + default: + LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat); + break; + } + break; + case TRACKTYPE_NORESAMPLE: + switch (mixerInFormat) { + case AUDIO_FORMAT_PCM_FLOAT: + return (AudioMixer::hook_t) + track__NoResample<MIXTYPE_MULTI, float, float, int32_t>; + case AUDIO_FORMAT_PCM_16_BIT: + return (AudioMixer::hook_t) + track__NoResample<MIXTYPE_MULTI, int32_t, int16_t, int32_t>; + default: + LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat); + break; + } + break; + default: + LOG_ALWAYS_FATAL("bad trackType: %d", trackType); + break; + } + return NULL; +} + +/* Returns the proper process hook for mixing tracks. Currently works only for + * PROCESSTYPE_NORESAMPLEONETRACK, a mix involving one track, no resampling. + * + * TODO: Due to the special mixing considerations of duplicating to + * a stereo output track, the input track cannot be MONO. This should be + * prevented by the caller. + */ +AudioMixer::process_hook_t AudioMixer::getProcessHook(int processType, uint32_t channelCount, + audio_format_t mixerInFormat, audio_format_t mixerOutFormat) +{ + if (processType != PROCESSTYPE_NORESAMPLEONETRACK) { // Only NORESAMPLEONETRACK + LOG_ALWAYS_FATAL("bad processType: %d", processType); + return NULL; + } + if (!kUseNewMixer && channelCount == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) { + return process__OneTrack16BitsStereoNoResampling; + } + LOG_ALWAYS_FATAL_IF(channelCount > MAX_NUM_CHANNELS); + switch (mixerInFormat) { + case AUDIO_FORMAT_PCM_FLOAT: + switch (mixerOutFormat) { + case AUDIO_FORMAT_PCM_FLOAT: + return process_NoResampleOneTrack<MIXTYPE_MULTI_SAVEONLY, + float /*TO*/, float /*TI*/, int32_t /*TA*/>; + case AUDIO_FORMAT_PCM_16_BIT: + return process_NoResampleOneTrack<MIXTYPE_MULTI_SAVEONLY, + int16_t, float, int32_t>; + default: + LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat); + break; + } + break; + case AUDIO_FORMAT_PCM_16_BIT: + switch (mixerOutFormat) { + case AUDIO_FORMAT_PCM_FLOAT: + return process_NoResampleOneTrack<MIXTYPE_MULTI_SAVEONLY, + float, int16_t, int32_t>; + case AUDIO_FORMAT_PCM_16_BIT: + return process_NoResampleOneTrack<MIXTYPE_MULTI_SAVEONLY, + int16_t, int16_t, int32_t>; + default: + LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat); + break; + } + break; + default: + LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat); + break; + } + return NULL; } // ---------------------------------------------------------------------------- diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h index 43aeb86..3b972bb 100644 --- a/services/audioflinger/AudioMixer.h +++ b/services/audioflinger/AudioMixer.h @@ -26,10 +26,13 @@ #include <media/AudioBufferProvider.h> #include "AudioResampler.h" -#include <audio_effects/effect_downmix.h> +#include <hardware/audio_effect.h> #include <system/audio.h> #include <media/nbaio/NBLog.h> +// FIXME This is actually unity gain, which might not be max in future, expressed in U.12 +#define MAX_GAIN_INT AudioMixer::UNITY_GAIN_INT + namespace android { // ---------------------------------------------------------------------------- @@ -48,14 +51,14 @@ public: static const uint32_t MAX_NUM_TRACKS = 32; // maximum number of channels supported by the mixer - // This mixer has a hard-coded upper limit of 2 channels for output. - // There is support for > 2 channel tracks down-mixed to 2 channel output via a down-mix effect. - // Adding support for > 2 channel output would require more than simply changing this value. - static const uint32_t MAX_NUM_CHANNELS = 2; + // This mixer has a hard-coded upper limit of 8 channels for output. + static const uint32_t MAX_NUM_CHANNELS = 8; + static const uint32_t MAX_NUM_VOLUMES = 2; // stereo volume only // maximum number of channels supported for the content - static const uint32_t MAX_NUM_CHANNELS_TO_DOWNMIX = 8; + static const uint32_t MAX_NUM_CHANNELS_TO_DOWNMIX = AUDIO_CHANNEL_COUNT_MAX; - static const uint16_t UNITY_GAIN = 0x1000; + static const uint16_t UNITY_GAIN_INT = 0x1000; + static const float UNITY_GAIN_FLOAT = 1.0f; enum { // names @@ -77,6 +80,8 @@ public: MAIN_BUFFER = 0x4002, AUX_BUFFER = 0x4003, DOWNMIX_TYPE = 0X4004, + MIXER_FORMAT = 0x4005, // AUDIO_FORMAT_PCM_(FLOAT|16_BIT) + MIXER_CHANNEL_MASK = 0x4006, // Channel mask for mixer output // for target RESAMPLE SAMPLE_RATE = 0x4100, // Configure sample rate conversion on this track name; // parameter 'value' is the new sample rate in Hz. @@ -90,6 +95,7 @@ public: REMOVE = 0x4102, // Remove the sample rate converter on this track name; // the track is restored to the mix sample rate. // for target RAMP_VOLUME and VOLUME (8 channels max) + // FIXME use float for these 3 to improve the dynamic range VOLUME0 = 0x4200, VOLUME1 = 0x4201, AUXLEVEL = 0x4210, @@ -99,7 +105,10 @@ public: // For all APIs with "name": TRACK0 <= name < TRACK0 + MAX_NUM_TRACKS // Allocate a track name. Returns new track name if successful, -1 on failure. - int getTrackName(audio_channel_mask_t channelMask, int sessionId); + // The failure could be because of an invalid channelMask or format, or that + // the track capacity of the mixer is exceeded. + int getTrackName(audio_channel_mask_t channelMask, + audio_format_t format, int sessionId); // Free an allocated track by name void deleteTrackName(int name); @@ -117,35 +126,34 @@ public: size_t getUnreleasedFrames(int name) const; + static inline bool isValidPcmTrackFormat(audio_format_t format) { + return format == AUDIO_FORMAT_PCM_16_BIT || + format == AUDIO_FORMAT_PCM_24_BIT_PACKED || + format == AUDIO_FORMAT_PCM_32_BIT || + format == AUDIO_FORMAT_PCM_FLOAT; + } + private: enum { + // FIXME this representation permits up to 8 channels NEEDS_CHANNEL_COUNT__MASK = 0x00000007, - NEEDS_FORMAT__MASK = 0x000000F0, - NEEDS_MUTE__MASK = 0x00000100, - NEEDS_RESAMPLE__MASK = 0x00001000, - NEEDS_AUX__MASK = 0x00010000, }; enum { - NEEDS_CHANNEL_1 = 0x00000000, - NEEDS_CHANNEL_2 = 0x00000001, - - NEEDS_FORMAT_16 = 0x00000010, + NEEDS_CHANNEL_1 = 0x00000000, // mono + NEEDS_CHANNEL_2 = 0x00000001, // stereo - NEEDS_MUTE_DISABLED = 0x00000000, - NEEDS_MUTE_ENABLED = 0x00000100, + // sample format is not explicitly specified, and is assumed to be AUDIO_FORMAT_PCM_16_BIT - NEEDS_RESAMPLE_DISABLED = 0x00000000, - NEEDS_RESAMPLE_ENABLED = 0x00001000, - - NEEDS_AUX_DISABLED = 0x00000000, - NEEDS_AUX_ENABLED = 0x00010000, + NEEDS_MUTE = 0x00000100, + NEEDS_RESAMPLE = 0x00001000, + NEEDS_AUX = 0x00010000, }; struct state_t; struct track_t; - class DownmixerBufferProvider; + class CopyBufferProvider; typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux); @@ -154,16 +162,17 @@ private: struct track_t { uint32_t needs; + // TODO: Eventually remove legacy integer volume settings union { - int16_t volume[MAX_NUM_CHANNELS]; // [0]3.12 fixed point + int16_t volume[MAX_NUM_VOLUMES]; // U4.12 fixed point (top bit should be zero) int32_t volumeRL; }; - int32_t prevVolume[MAX_NUM_CHANNELS]; + int32_t prevVolume[MAX_NUM_VOLUMES]; // 16-byte boundary - int32_t volumeInc[MAX_NUM_CHANNELS]; + int32_t volumeInc[MAX_NUM_VOLUMES]; int32_t auxInc; int32_t prevAuxLevel; @@ -173,7 +182,7 @@ private: uint16_t frameCount; uint8_t channelCount; // 1 or 2, redundant with (needs & NEEDS_CHANNEL_COUNT__MASK) - uint8_t format; // always 16 + uint8_t unused_padding; // formerly format, was always 16 uint16_t enabled; // actually bool audio_channel_mask_t channelMask; @@ -196,48 +205,159 @@ private: int32_t* auxBuffer; // 16-byte boundary - - DownmixerBufferProvider* downmixerBufferProvider; // 4 bytes + AudioBufferProvider* mInputBufferProvider; // externally provided buffer provider. + CopyBufferProvider* mReformatBufferProvider; // provider wrapper for reformatting. + CopyBufferProvider* downmixerBufferProvider; // wrapper for channel conversion. int32_t sessionId; - int32_t padding[2]; + // 16-byte boundary + audio_format_t mMixerFormat; // output mix format: AUDIO_FORMAT_PCM_(FLOAT|16_BIT) + audio_format_t mFormat; // input track format + audio_format_t mMixerInFormat; // mix internal format AUDIO_FORMAT_PCM_(FLOAT|16_BIT) + // each track must be converted to this format. + + float mVolume[MAX_NUM_VOLUMES]; // floating point set volume + float mPrevVolume[MAX_NUM_VOLUMES]; // floating point previous volume + float mVolumeInc[MAX_NUM_VOLUMES]; // floating point volume increment + + float mAuxLevel; // floating point set aux level + float mPrevAuxLevel; // floating point prev aux level + float mAuxInc; // floating point aux increment // 16-byte boundary + audio_channel_mask_t mMixerChannelMask; + uint32_t mMixerChannelCount; - bool setResampler(uint32_t sampleRate, uint32_t devSampleRate); + bool needsRamp() { return (volumeInc[0] | volumeInc[1] | auxInc) != 0; } + bool setResampler(uint32_t trackSampleRate, uint32_t devSampleRate); bool doesResample() const { return resampler != NULL; } void resetResampler() { if (resampler != NULL) resampler->reset(); } - void adjustVolumeRamp(bool aux); + void adjustVolumeRamp(bool aux, bool useFloat = false); size_t getUnreleasedFrames() const { return resampler != NULL ? resampler->getUnreleasedFrames() : 0; }; }; + typedef void (*process_hook_t)(state_t* state, int64_t pts); + // pad to 32-bytes to fill cache line struct state_t { uint32_t enabledTracks; uint32_t needsChanged; size_t frameCount; - void (*hook)(state_t* state, int64_t pts); // one of process__*, never NULL + process_hook_t hook; // one of process__*, never NULL int32_t *outputTemp; int32_t *resampleTemp; NBLog::Writer* mLog; int32_t reserved[1]; // FIXME allocate dynamically to save some memory when maxNumTracks < MAX_NUM_TRACKS - track_t tracks[MAX_NUM_TRACKS]; __attribute__((aligned(32))); + track_t tracks[MAX_NUM_TRACKS] __attribute__((aligned(32))); }; - // AudioBufferProvider that wraps a track AudioBufferProvider by a call to a downmix effect - class DownmixerBufferProvider : public AudioBufferProvider { + // Base AudioBufferProvider class used for DownMixerBufferProvider, RemixBufferProvider, + // and ReformatBufferProvider. + // It handles a private buffer for use in converting format or channel masks from the + // input data to a form acceptable by the mixer. + // TODO: Make a ResamplerBufferProvider when integers are entirely removed from the + // processing pipeline. + class CopyBufferProvider : public AudioBufferProvider { public: + // Use a private buffer of bufferFrameCount frames (each frame is outputFrameSize bytes). + // If bufferFrameCount is 0, no private buffer is created and in-place modification of + // the upstream buffer provider's buffers is performed by copyFrames(). + CopyBufferProvider(size_t inputFrameSize, size_t outputFrameSize, + size_t bufferFrameCount); + virtual ~CopyBufferProvider(); + + // Overrides AudioBufferProvider methods virtual status_t getNextBuffer(Buffer* buffer, int64_t pts); virtual void releaseBuffer(Buffer* buffer); - DownmixerBufferProvider(); - virtual ~DownmixerBufferProvider(); + // Other public methods + + // call this to release the buffer to the upstream provider. + // treat it as an audio discontinuity for future samples. + virtual void reset(); + + // this function should be supplied by the derived class. It converts + // #frames in the *src pointer to the *dst pointer. It is public because + // some providers will allow this to work on arbitrary buffers outside + // of the internal buffers. + virtual void copyFrames(void *dst, const void *src, size_t frames) = 0; + + // set the upstream buffer provider. Consider calling "reset" before this function. + void setBufferProvider(AudioBufferProvider *p) { + mTrackBufferProvider = p; + } + + protected: AudioBufferProvider* mTrackBufferProvider; + const size_t mInputFrameSize; + const size_t mOutputFrameSize; + private: + AudioBufferProvider::Buffer mBuffer; + const size_t mLocalBufferFrameCount; + void* mLocalBufferData; + size_t mConsumed; + }; + + // DownmixerBufferProvider wraps a track AudioBufferProvider to provide + // position dependent downmixing by an Audio Effect. + class DownmixerBufferProvider : public CopyBufferProvider { + public: + DownmixerBufferProvider(audio_channel_mask_t inputChannelMask, + audio_channel_mask_t outputChannelMask, audio_format_t format, + uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount); + virtual ~DownmixerBufferProvider(); + virtual void copyFrames(void *dst, const void *src, size_t frames); + bool isValid() const { return mDownmixHandle != NULL; } + + static status_t init(); + static bool isMultichannelCapable() { return sIsMultichannelCapable; } + + protected: effect_handle_t mDownmixHandle; effect_config_t mDownmixConfig; + + // effect descriptor for the downmixer used by the mixer + static effect_descriptor_t sDwnmFxDesc; + // indicates whether a downmix effect has been found and is usable by this mixer + static bool sIsMultichannelCapable; + // FIXME: should we allow effects outside of the framework? + // We need to here. A special ioId that must be <= -2 so it does not map to a session. + static const int32_t SESSION_ID_INVALID_AND_IGNORED = -2; + }; + + // RemixBufferProvider wraps a track AudioBufferProvider to perform an + // upmix or downmix to the proper channel count and mask. + class RemixBufferProvider : public CopyBufferProvider { + public: + RemixBufferProvider(audio_channel_mask_t inputChannelMask, + audio_channel_mask_t outputChannelMask, audio_format_t format, + size_t bufferFrameCount); + virtual void copyFrames(void *dst, const void *src, size_t frames); + + protected: + const audio_format_t mFormat; + const size_t mSampleSize; + const size_t mInputChannels; + const size_t mOutputChannels; + int8_t mIdxAry[sizeof(uint32_t)*8]; // 32 bits => channel indices + }; + + // ReformatBufferProvider wraps a track AudioBufferProvider to convert the input data + // to an acceptable mixer input format type. + class ReformatBufferProvider : public CopyBufferProvider { + public: + ReformatBufferProvider(int32_t channels, + audio_format_t inputFormat, audio_format_t outputFormat, + size_t bufferFrameCount); + virtual void copyFrames(void *dst, const void *src, size_t frames); + + protected: + const int32_t mChannels; + const audio_format_t mInputFormat; + const audio_format_t mOutputFormat; }; // bitmask of allocated track names, where bit 0 corresponds to TRACK0 etc. @@ -255,18 +375,20 @@ public: private: state_t mState __attribute__((aligned(32))); - // effect descriptor for the downmixer used by the mixer - static effect_descriptor_t dwnmFxDesc; - // indicates whether a downmix effect has been found and is usable by this mixer - static bool isMultichannelCapable; - // Call after changing either the enabled status of a track, or parameters of an enabled track. // OK to call more often than that, but unnecessary. void invalidateState(uint32_t mask); - static status_t initTrackDownmix(track_t* pTrack, int trackNum, audio_channel_mask_t mask); + bool setChannelMasks(int name, + audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask); + + // TODO: remove unused trackName/trackNum from functions below. + static status_t initTrackDownmix(track_t* pTrack, int trackName); static status_t prepareTrackForDownmix(track_t* pTrack, int trackNum); static void unprepareTrackForDownmix(track_t* pTrack, int trackName); + static status_t prepareTrackForReformat(track_t* pTrack, int trackNum); + static void unprepareTrackForReformat(track_t* pTrack, int trackName); + static void reconfigureBufferProviders(track_t* pTrack); static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux); @@ -286,10 +408,6 @@ private: static void process__genericResampling(state_t* state, int64_t pts); static void process__OneTrack16BitsStereoNoResampling(state_t* state, int64_t pts); -#if 0 - static void process__TwoTracks16BitsStereoNoResampling(state_t* state, - int64_t pts); -#endif static int64_t calculateOutputPTS(const track_t& t, int64_t basePTS, int outputFrameIndex); @@ -297,6 +415,53 @@ private: static uint64_t sLocalTimeFreq; static pthread_once_t sOnceControl; static void sInitRoutine(); + + /* multi-format volume mixing function (calls template functions + * in AudioMixerOps.h). The template parameters are as follows: + * + * MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration) + * USEFLOATVOL (set to true if float volume is used) + * ADJUSTVOL (set to true if volume ramp parameters needs adjustment afterwards) + * TO: int32_t (Q4.27) or float + * TI: int32_t (Q4.27) or int16_t (Q0.15) or float + * TA: int32_t (Q4.27) + */ + template <int MIXTYPE, bool USEFLOATVOL, bool ADJUSTVOL, + typename TO, typename TI, typename TA> + static void volumeMix(TO *out, size_t outFrames, + const TI *in, TA *aux, bool ramp, AudioMixer::track_t *t); + + // multi-format process hooks + template <int MIXTYPE, typename TO, typename TI, typename TA> + static void process_NoResampleOneTrack(state_t* state, int64_t pts); + + // multi-format track hooks + template <int MIXTYPE, typename TO, typename TI, typename TA> + static void track__Resample(track_t* t, TO* out, size_t frameCount, + TO* temp __unused, TA* aux); + template <int MIXTYPE, typename TO, typename TI, typename TA> + static void track__NoResample(track_t* t, TO* out, size_t frameCount, + TO* temp __unused, TA* aux); + + static void convertMixerFormat(void *out, audio_format_t mixerOutFormat, + void *in, audio_format_t mixerInFormat, size_t sampleCount); + + // hook types + enum { + PROCESSTYPE_NORESAMPLEONETRACK, + }; + enum { + TRACKTYPE_NOP, + TRACKTYPE_RESAMPLE, + TRACKTYPE_NORESAMPLE, + TRACKTYPE_NORESAMPLEMONO, + }; + + // functions for determining the proper process and track hooks. + static process_hook_t getProcessHook(int processType, uint32_t channelCount, + audio_format_t mixerInFormat, audio_format_t mixerOutFormat); + static hook_t getTrackHook(int trackType, uint32_t channelCount, + audio_format_t mixerInFormat, audio_format_t mixerOutFormat); }; // ---------------------------------------------------------------------------- diff --git a/services/audioflinger/AudioMixerOps.h b/services/audioflinger/AudioMixerOps.h new file mode 100644 index 0000000..f7376a8 --- /dev/null +++ b/services/audioflinger/AudioMixerOps.h @@ -0,0 +1,454 @@ +/* + * 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_MIXER_OPS_H +#define ANDROID_AUDIO_MIXER_OPS_H + +namespace android { + +/* Behavior of is_same<>::value is true if the types are identical, + * false otherwise. Identical to the STL std::is_same. + */ +template<typename T, typename U> +struct is_same +{ + static const bool value = false; +}; + +template<typename T> +struct is_same<T, T> // partial specialization +{ + static const bool value = true; +}; + + +/* MixMul is a multiplication operator to scale an audio input signal + * by a volume gain, with the formula: + * + * O(utput) = I(nput) * V(olume) + * + * The output, input, and volume may have different types. + * There are 27 variants, of which 14 are actually defined in an + * explicitly templated class. + * + * The following type variables and the underlying meaning: + * + * Output type TO: int32_t (Q4.27) or int16_t (Q.15) or float [-1,1] + * Input signal type TI: int32_t (Q4.27) or int16_t (Q.15) or float [-1,1] + * Volume type TV: int32_t (U4.28) or int16_t (U4.12) or float [-1,1] + * + * For high precision audio, only the <TO, TI, TV> = <float, float, float> + * needs to be accelerated. This is perhaps the easiest form to do quickly as well. + */ + +template <typename TO, typename TI, typename TV> +inline TO MixMul(TI value, TV volume) { + COMPILE_TIME_ASSERT_FUNCTION_SCOPE(false); + // should not be here :-). + // To avoid mistakes, this template is always specialized. + return value * volume; +} + +template <> +inline int32_t MixMul<int32_t, int16_t, int16_t>(int16_t value, int16_t volume) { + return value * volume; +} + +template <> +inline int32_t MixMul<int32_t, int32_t, int16_t>(int32_t value, int16_t volume) { + return (value >> 12) * volume; +} + +template <> +inline int32_t MixMul<int32_t, int16_t, int32_t>(int16_t value, int32_t volume) { + return value * (volume >> 16); +} + +template <> +inline int32_t MixMul<int32_t, int32_t, int32_t>(int32_t value, int32_t volume) { + return (value >> 12) * (volume >> 16); +} + +template <> +inline float MixMul<float, float, int16_t>(float value, int16_t volume) { + static const float norm = 1. / (1 << 12); + return value * volume * norm; +} + +template <> +inline float MixMul<float, float, int32_t>(float value, int32_t volume) { + static const float norm = 1. / (1 << 28); + return value * volume * norm; +} + +template <> +inline int16_t MixMul<int16_t, float, int16_t>(float value, int16_t volume) { + return clamp16_from_float(MixMul<float, float, int16_t>(value, volume)); +} + +template <> +inline int16_t MixMul<int16_t, float, int32_t>(float value, int32_t volume) { + return clamp16_from_float(MixMul<float, float, int32_t>(value, volume)); +} + +template <> +inline float MixMul<float, int16_t, int16_t>(int16_t value, int16_t volume) { + static const float norm = 1. / (1 << (15 + 12)); + return static_cast<float>(value) * static_cast<float>(volume) * norm; +} + +template <> +inline float MixMul<float, int16_t, int32_t>(int16_t value, int32_t volume) { + static const float norm = 1. / (1ULL << (15 + 28)); + return static_cast<float>(value) * static_cast<float>(volume) * norm; +} + +template <> +inline int16_t MixMul<int16_t, int16_t, int16_t>(int16_t value, int16_t volume) { + return clamp16(MixMul<int32_t, int16_t, int16_t>(value, volume) >> 12); +} + +template <> +inline int16_t MixMul<int16_t, int32_t, int16_t>(int32_t value, int16_t volume) { + return clamp16(MixMul<int32_t, int32_t, int16_t>(value, volume) >> 12); +} + +template <> +inline int16_t MixMul<int16_t, int16_t, int32_t>(int16_t value, int32_t volume) { + return clamp16(MixMul<int32_t, int16_t, int32_t>(value, volume) >> 12); +} + +template <> +inline int16_t MixMul<int16_t, int32_t, int32_t>(int32_t value, int32_t volume) { + return clamp16(MixMul<int32_t, int32_t, int32_t>(value, volume) >> 12); +} + +/* Required for floating point volume. Some are needed for compilation but + * are not needed in execution and should be removed from the final build by + * an optimizing compiler. + */ +template <> +inline float MixMul<float, float, float>(float value, float volume) { + return value * volume; +} + +template <> +inline float MixMul<float, int16_t, float>(int16_t value, float volume) { + static const float float_from_q_15 = 1. / (1 << 15); + return value * volume * float_from_q_15; +} + +template <> +inline int32_t MixMul<int32_t, int32_t, float>(int32_t value, float volume) { + LOG_ALWAYS_FATAL("MixMul<int32_t, int32_t, float> Runtime Should not be here"); + return value * volume; +} + +template <> +inline int32_t MixMul<int32_t, int16_t, float>(int16_t value, float volume) { + LOG_ALWAYS_FATAL("MixMul<int32_t, int16_t, float> Runtime Should not be here"); + static const float u4_12_from_float = (1 << 12); + return value * volume * u4_12_from_float; +} + +template <> +inline int16_t MixMul<int16_t, int16_t, float>(int16_t value, float volume) { + LOG_ALWAYS_FATAL("MixMul<int16_t, int16_t, float> Runtime Should not be here"); + return value * volume; +} + +template <> +inline int16_t MixMul<int16_t, float, float>(float value, float volume) { + static const float q_15_from_float = (1 << 15); + return value * volume * q_15_from_float; +} + +/* + * MixAccum is used to add into an accumulator register of a possibly different + * type. The TO and TI types are the same as MixMul. + */ + +template <typename TO, typename TI> +inline void MixAccum(TO *auxaccum, TI value) { + if (!is_same<TO, TI>::value) { + LOG_ALWAYS_FATAL("MixAccum type not properly specialized: %zu %zu\n", + sizeof(TO), sizeof(TI)); + } + *auxaccum += value; +} + +template<> +inline void MixAccum<float, int16_t>(float *auxaccum, int16_t value) { + static const float norm = 1. / (1 << 15); + *auxaccum += norm * value; +} + +template<> +inline void MixAccum<float, int32_t>(float *auxaccum, int32_t value) { + static const float norm = 1. / (1 << 27); + *auxaccum += norm * value; +} + +template<> +inline void MixAccum<int32_t, int16_t>(int32_t *auxaccum, int16_t value) { + *auxaccum += value << 12; +} + +template<> +inline void MixAccum<int32_t, float>(int32_t *auxaccum, float value) { + *auxaccum += clampq4_27_from_float(value); +} + +/* MixMulAux is just like MixMul except it combines with + * an accumulator operation MixAccum. + */ + +template <typename TO, typename TI, typename TV, typename TA> +inline TO MixMulAux(TI value, TV volume, TA *auxaccum) { + MixAccum<TA, TI>(auxaccum, value); + return MixMul<TO, TI, TV>(value, volume); +} + +/* MIXTYPE is used to determine how the samples in the input frame + * are mixed with volume gain into the output frame. + * See the volumeRampMulti functions below for more details. + */ +enum { + MIXTYPE_MULTI, + MIXTYPE_MONOEXPAND, + MIXTYPE_MULTI_SAVEONLY, + MIXTYPE_MULTI_MONOVOL, + MIXTYPE_MULTI_SAVEONLY_MONOVOL, +}; + +/* + * The volumeRampMulti and volumeRamp functions take a MIXTYPE + * which indicates the per-frame mixing and accumulation strategy. + * + * MIXTYPE_MULTI: + * NCHAN represents number of input and output channels. + * TO: int32_t (Q4.27) or float + * TI: int32_t (Q4.27) or int16_t (Q0.15) or float + * TV: int32_t (U4.28) or int16_t (U4.12) or float + * vol: represents a volume array. + * + * This accumulates into the out pointer. + * + * MIXTYPE_MONOEXPAND: + * Single input channel. NCHAN represents number of output channels. + * TO: int32_t (Q4.27) or float + * TI: int32_t (Q4.27) or int16_t (Q0.15) or float + * TV: int32_t (U4.28) or int16_t (U4.12) or float + * Input channel count is 1. + * vol: represents volume array. + * + * This accumulates into the out pointer. + * + * MIXTYPE_MULTI_SAVEONLY: + * NCHAN represents number of input and output channels. + * TO: int16_t (Q.15) or float + * TI: int32_t (Q4.27) or int16_t (Q0.15) or float + * TV: int32_t (U4.28) or int16_t (U4.12) or float + * vol: represents a volume array. + * + * MIXTYPE_MULTI_SAVEONLY does not accumulate into the out pointer. + * + * MIXTYPE_MULTI_MONOVOL: + * Same as MIXTYPE_MULTI, but uses only volume[0]. + * + * MIXTYPE_MULTI_SAVEONLY_MONOVOL: + * Same as MIXTYPE_MULTI_SAVEONLY, but uses only volume[0]. + * + */ + +template <int MIXTYPE, int NCHAN, + typename TO, typename TI, typename TV, typename TA, typename TAV> +inline void volumeRampMulti(TO* out, size_t frameCount, + const TI* in, TA* aux, TV *vol, const TV *volinc, TAV *vola, TAV volainc) +{ +#ifdef ALOGVV + ALOGVV("volumeRampMulti, MIXTYPE:%d\n", MIXTYPE); +#endif + if (aux != NULL) { + do { + TA auxaccum = 0; + switch (MIXTYPE) { + case MIXTYPE_MULTI: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum); + vol[i] += volinc[i]; + } + break; + case MIXTYPE_MONOEXPAND: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum); + vol[i] += volinc[i]; + } + in++; + break; + case MIXTYPE_MULTI_SAVEONLY: + for (int i = 0; i < NCHAN; ++i) { + *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum); + vol[i] += volinc[i]; + } + break; + case MIXTYPE_MULTI_MONOVOL: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum); + } + vol[0] += volinc[0]; + break; + case MIXTYPE_MULTI_SAVEONLY_MONOVOL: + for (int i = 0; i < NCHAN; ++i) { + *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum); + } + vol[0] += volinc[0]; + break; + default: + LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE); + break; + } + auxaccum /= NCHAN; + *aux++ += MixMul<TA, TA, TAV>(auxaccum, *vola); + vola[0] += volainc; + } while (--frameCount); + } else { + do { + switch (MIXTYPE) { + case MIXTYPE_MULTI: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMul<TO, TI, TV>(*in++, vol[i]); + vol[i] += volinc[i]; + } + break; + case MIXTYPE_MONOEXPAND: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMul<TO, TI, TV>(*in, vol[i]); + vol[i] += volinc[i]; + } + in++; + break; + case MIXTYPE_MULTI_SAVEONLY: + for (int i = 0; i < NCHAN; ++i) { + *out++ = MixMul<TO, TI, TV>(*in++, vol[i]); + vol[i] += volinc[i]; + } + break; + case MIXTYPE_MULTI_MONOVOL: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMul<TO, TI, TV>(*in++, vol[0]); + } + vol[0] += volinc[0]; + break; + case MIXTYPE_MULTI_SAVEONLY_MONOVOL: + for (int i = 0; i < NCHAN; ++i) { + *out++ = MixMul<TO, TI, TV>(*in++, vol[0]); + } + vol[0] += volinc[0]; + break; + default: + LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE); + break; + } + } while (--frameCount); + } +} + +template <int MIXTYPE, int NCHAN, + typename TO, typename TI, typename TV, typename TA, typename TAV> +inline void volumeMulti(TO* out, size_t frameCount, + const TI* in, TA* aux, const TV *vol, TAV vola) +{ +#ifdef ALOGVV + ALOGVV("volumeMulti MIXTYPE:%d\n", MIXTYPE); +#endif + if (aux != NULL) { + do { + TA auxaccum = 0; + switch (MIXTYPE) { + case MIXTYPE_MULTI: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum); + } + break; + case MIXTYPE_MONOEXPAND: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMulAux<TO, TI, TV, TA>(*in, vol[i], &auxaccum); + } + in++; + break; + case MIXTYPE_MULTI_SAVEONLY: + for (int i = 0; i < NCHAN; ++i) { + *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[i], &auxaccum); + } + break; + case MIXTYPE_MULTI_MONOVOL: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum); + } + break; + case MIXTYPE_MULTI_SAVEONLY_MONOVOL: + for (int i = 0; i < NCHAN; ++i) { + *out++ = MixMulAux<TO, TI, TV, TA>(*in++, vol[0], &auxaccum); + } + break; + default: + LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE); + break; + } + auxaccum /= NCHAN; + *aux++ += MixMul<TA, TA, TAV>(auxaccum, vola); + } while (--frameCount); + } else { + do { + switch (MIXTYPE) { + case MIXTYPE_MULTI: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMul<TO, TI, TV>(*in++, vol[i]); + } + break; + case MIXTYPE_MONOEXPAND: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMul<TO, TI, TV>(*in, vol[i]); + } + in++; + break; + case MIXTYPE_MULTI_SAVEONLY: + for (int i = 0; i < NCHAN; ++i) { + *out++ = MixMul<TO, TI, TV>(*in++, vol[i]); + } + break; + case MIXTYPE_MULTI_MONOVOL: + for (int i = 0; i < NCHAN; ++i) { + *out++ += MixMul<TO, TI, TV>(*in++, vol[0]); + } + break; + case MIXTYPE_MULTI_SAVEONLY_MONOVOL: + for (int i = 0; i < NCHAN; ++i) { + *out++ = MixMul<TO, TI, TV>(*in++, vol[0]); + } + break; + default: + LOG_ALWAYS_FATAL("invalid mixtype %d", MIXTYPE); + break; + } + } while (--frameCount); + } +} + +}; + +#endif /* ANDROID_AUDIO_MIXER_OPS_H */ diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp deleted file mode 100644 index 6ea5324..0000000 --- a/services/audioflinger/AudioPolicyService.cpp +++ /dev/null @@ -1,1691 +0,0 @@ -/* - * 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 "AudioPolicyService" -//#define LOG_NDEBUG 0 - -#include "Configuration.h" -#undef __STRICT_ANSI__ -#define __STDINT_LIMITS -#define __STDC_LIMIT_MACROS -#include <stdint.h> - -#include <sys/time.h> -#include <binder/IServiceManager.h> -#include <utils/Log.h> -#include <cutils/properties.h> -#include <binder/IPCThreadState.h> -#include <utils/String16.h> -#include <utils/threads.h> -#include "AudioPolicyService.h" -#include "ServiceUtilities.h" -#include <hardware_legacy/power.h> -#include <media/AudioEffect.h> -#include <media/EffectsFactoryApi.h> - -#include <hardware/hardware.h> -#include <system/audio.h> -#include <system/audio_policy.h> -#include <hardware/audio_policy.h> -#include <audio_effects/audio_effects_conf.h> -#include <media/AudioParameter.h> - -namespace android { - -static const char kDeadlockedString[] = "AudioPolicyService may be deadlocked\n"; -static const char kCmdDeadlockedString[] = "AudioPolicyService command thread may be deadlocked\n"; - -static const int kDumpLockRetries = 50; -static const int kDumpLockSleepUs = 20000; - -static const nsecs_t kAudioCommandTimeout = 3000000000LL; // 3 seconds - -namespace { - extern struct audio_policy_service_ops aps_ops; -}; - -// ---------------------------------------------------------------------------- - -AudioPolicyService::AudioPolicyService() - : BnAudioPolicyService() , mpAudioPolicyDev(NULL) , mpAudioPolicy(NULL) -{ - char value[PROPERTY_VALUE_MAX]; - const struct hw_module_t *module; - int forced_val; - int rc; - - Mutex::Autolock _l(mLock); - - // start tone playback thread - mTonePlaybackThread = new AudioCommandThread(String8("ApmTone"), this); - // start audio commands thread - mAudioCommandThread = new AudioCommandThread(String8("ApmAudio"), this); - // start output activity command thread - mOutputCommandThread = new AudioCommandThread(String8("ApmOutput"), this); - /* instantiate the audio policy manager */ - rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module); - if (rc) - return; - - rc = audio_policy_dev_open(module, &mpAudioPolicyDev); - ALOGE_IF(rc, "couldn't open audio policy device (%s)", strerror(-rc)); - if (rc) - return; - - rc = mpAudioPolicyDev->create_audio_policy(mpAudioPolicyDev, &aps_ops, this, - &mpAudioPolicy); - ALOGE_IF(rc, "couldn't create audio policy (%s)", strerror(-rc)); - if (rc) - return; - - rc = mpAudioPolicy->init_check(mpAudioPolicy); - ALOGE_IF(rc, "couldn't init_check the audio policy (%s)", strerror(-rc)); - if (rc) - return; - - ALOGI("Loaded audio policy from %s (%s)", module->name, module->id); - - // load audio pre processing modules - if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) { - loadPreProcessorConfig(AUDIO_EFFECT_VENDOR_CONFIG_FILE); - } else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) { - loadPreProcessorConfig(AUDIO_EFFECT_DEFAULT_CONFIG_FILE); - } -} - -AudioPolicyService::~AudioPolicyService() -{ - mTonePlaybackThread->exit(); - mTonePlaybackThread.clear(); - mAudioCommandThread->exit(); - mAudioCommandThread.clear(); - - - // release audio pre processing resources - for (size_t i = 0; i < mInputSources.size(); i++) { - delete mInputSources.valueAt(i); - } - mInputSources.clear(); - - for (size_t i = 0; i < mInputs.size(); i++) { - mInputs.valueAt(i)->mEffects.clear(); - delete mInputs.valueAt(i); - } - mInputs.clear(); - - if (mpAudioPolicy != NULL && mpAudioPolicyDev != NULL) - mpAudioPolicyDev->destroy_audio_policy(mpAudioPolicyDev, mpAudioPolicy); - if (mpAudioPolicyDev != NULL) - audio_policy_dev_close(mpAudioPolicyDev); -} - -status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device, - audio_policy_dev_state_t state, - const char *device_address) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - if (!settingsAllowed()) { - return PERMISSION_DENIED; - } - if (!audio_is_output_device(device) && !audio_is_input_device(device)) { - return BAD_VALUE; - } - if (state != AUDIO_POLICY_DEVICE_STATE_AVAILABLE && - state != AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) { - return BAD_VALUE; - } - - ALOGV("setDeviceConnectionState()"); - Mutex::Autolock _l(mLock); - return mpAudioPolicy->set_device_connection_state(mpAudioPolicy, device, - state, device_address); -} - -audio_policy_dev_state_t AudioPolicyService::getDeviceConnectionState( - audio_devices_t device, - const char *device_address) -{ - if (mpAudioPolicy == NULL) { - return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; - } - return mpAudioPolicy->get_device_connection_state(mpAudioPolicy, device, - device_address); -} - -status_t AudioPolicyService::setPhoneState(audio_mode_t state) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - if (!settingsAllowed()) { - return PERMISSION_DENIED; - } - if (uint32_t(state) >= AUDIO_MODE_CNT) { - return BAD_VALUE; - } - - ALOGV("setPhoneState()"); - - // TODO: check if it is more appropriate to do it in platform specific policy manager - AudioSystem::setMode(state); - - Mutex::Autolock _l(mLock); - mpAudioPolicy->set_phone_state(mpAudioPolicy, state); - return NO_ERROR; -} - -status_t AudioPolicyService::setForceUse(audio_policy_force_use_t usage, - audio_policy_forced_cfg_t config) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - if (!settingsAllowed()) { - return PERMISSION_DENIED; - } - if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) { - return BAD_VALUE; - } - if (config < 0 || config >= AUDIO_POLICY_FORCE_CFG_CNT) { - return BAD_VALUE; - } - ALOGV("setForceUse()"); - Mutex::Autolock _l(mLock); - mpAudioPolicy->set_force_use(mpAudioPolicy, usage, config); - return NO_ERROR; -} - -audio_policy_forced_cfg_t AudioPolicyService::getForceUse(audio_policy_force_use_t usage) -{ - if (mpAudioPolicy == NULL) { - return AUDIO_POLICY_FORCE_NONE; - } - if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) { - return AUDIO_POLICY_FORCE_NONE; - } - return mpAudioPolicy->get_force_use(mpAudioPolicy, usage); -} - -audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream, - uint32_t samplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - audio_output_flags_t flags, - const audio_offload_info_t *offloadInfo) -{ - if (mpAudioPolicy == NULL) { - return 0; - } - ALOGV("getOutput()"); - Mutex::Autolock _l(mLock); - return mpAudioPolicy->get_output(mpAudioPolicy, stream, samplingRate, - format, channelMask, flags, offloadInfo); -} - -status_t AudioPolicyService::startOutput(audio_io_handle_t output, - audio_stream_type_t stream, - int session) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - ALOGV("startOutput()"); - Mutex::Autolock _l(mLock); - return mpAudioPolicy->start_output(mpAudioPolicy, output, stream, session); -} - -status_t AudioPolicyService::stopOutput(audio_io_handle_t output, - audio_stream_type_t stream, - int session) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - ALOGV("stopOutput()"); - mOutputCommandThread->stopOutputCommand(output, stream, session); - return NO_ERROR; -} - -status_t AudioPolicyService::doStopOutput(audio_io_handle_t output, - audio_stream_type_t stream, - int session) -{ - ALOGV("doStopOutput from tid %d", gettid()); - Mutex::Autolock _l(mLock); - return mpAudioPolicy->stop_output(mpAudioPolicy, output, stream, session); -} - -void AudioPolicyService::releaseOutput(audio_io_handle_t output) -{ - if (mpAudioPolicy == NULL) { - return; - } - ALOGV("releaseOutput()"); - mOutputCommandThread->releaseOutputCommand(output); -} - -void AudioPolicyService::doReleaseOutput(audio_io_handle_t output) -{ - ALOGV("doReleaseOutput from tid %d", gettid()); - Mutex::Autolock _l(mLock); - mpAudioPolicy->release_output(mpAudioPolicy, output); -} - -audio_io_handle_t AudioPolicyService::getInput(audio_source_t inputSource, - uint32_t samplingRate, - audio_format_t format, - audio_channel_mask_t channelMask, - int audioSession) -{ - if (mpAudioPolicy == NULL) { - return 0; - } - // already checked by client, but double-check in case the client wrapper is bypassed - if (inputSource >= AUDIO_SOURCE_CNT && inputSource != AUDIO_SOURCE_HOTWORD) { - return 0; - } - - if ((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) { - return 0; - } - - Mutex::Autolock _l(mLock); - // the audio_in_acoustics_t parameter is ignored by get_input() - audio_io_handle_t input = mpAudioPolicy->get_input(mpAudioPolicy, inputSource, samplingRate, - format, channelMask, (audio_in_acoustics_t) 0); - - if (input == 0) { - return input; - } - // create audio pre processors according to input source - audio_source_t aliasSource = (inputSource == AUDIO_SOURCE_HOTWORD) ? - AUDIO_SOURCE_VOICE_RECOGNITION : inputSource; - - ssize_t index = mInputSources.indexOfKey(aliasSource); - if (index < 0) { - return input; - } - ssize_t idx = mInputs.indexOfKey(input); - InputDesc *inputDesc; - if (idx < 0) { - inputDesc = new InputDesc(audioSession); - mInputs.add(input, inputDesc); - } else { - inputDesc = mInputs.valueAt(idx); - } - - Vector <EffectDesc *> effects = mInputSources.valueAt(index)->mEffects; - for (size_t i = 0; i < effects.size(); i++) { - EffectDesc *effect = effects[i]; - sp<AudioEffect> fx = new AudioEffect(NULL, &effect->mUuid, -1, 0, 0, audioSession, input); - status_t status = fx->initCheck(); - if (status != NO_ERROR && status != ALREADY_EXISTS) { - ALOGW("Failed to create Fx %s on input %d", effect->mName, input); - // fx goes out of scope and strong ref on AudioEffect is released - continue; - } - for (size_t j = 0; j < effect->mParams.size(); j++) { - fx->setParameter(effect->mParams[j]); - } - inputDesc->mEffects.add(fx); - } - setPreProcessorEnabled(inputDesc, true); - return input; -} - -status_t AudioPolicyService::startInput(audio_io_handle_t input) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - Mutex::Autolock _l(mLock); - - return mpAudioPolicy->start_input(mpAudioPolicy, input); -} - -status_t AudioPolicyService::stopInput(audio_io_handle_t input) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - Mutex::Autolock _l(mLock); - - return mpAudioPolicy->stop_input(mpAudioPolicy, input); -} - -void AudioPolicyService::releaseInput(audio_io_handle_t input) -{ - if (mpAudioPolicy == NULL) { - return; - } - Mutex::Autolock _l(mLock); - mpAudioPolicy->release_input(mpAudioPolicy, input); - - ssize_t index = mInputs.indexOfKey(input); - if (index < 0) { - return; - } - InputDesc *inputDesc = mInputs.valueAt(index); - setPreProcessorEnabled(inputDesc, false); - delete inputDesc; - mInputs.removeItemsAt(index); -} - -status_t AudioPolicyService::initStreamVolume(audio_stream_type_t stream, - int indexMin, - int indexMax) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - if (!settingsAllowed()) { - return PERMISSION_DENIED; - } - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { - return BAD_VALUE; - } - Mutex::Autolock _l(mLock); - mpAudioPolicy->init_stream_volume(mpAudioPolicy, stream, indexMin, indexMax); - return NO_ERROR; -} - -status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream, - int index, - audio_devices_t device) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - if (!settingsAllowed()) { - return PERMISSION_DENIED; - } - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { - return BAD_VALUE; - } - Mutex::Autolock _l(mLock); - if (mpAudioPolicy->set_stream_volume_index_for_device) { - return mpAudioPolicy->set_stream_volume_index_for_device(mpAudioPolicy, - stream, - index, - device); - } else { - return mpAudioPolicy->set_stream_volume_index(mpAudioPolicy, stream, index); - } -} - -status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream, - int *index, - audio_devices_t device) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - if (uint32_t(stream) >= AUDIO_STREAM_CNT) { - return BAD_VALUE; - } - Mutex::Autolock _l(mLock); - if (mpAudioPolicy->get_stream_volume_index_for_device) { - return mpAudioPolicy->get_stream_volume_index_for_device(mpAudioPolicy, - stream, - index, - device); - } else { - return mpAudioPolicy->get_stream_volume_index(mpAudioPolicy, stream, index); - } -} - -uint32_t AudioPolicyService::getStrategyForStream(audio_stream_type_t stream) -{ - if (mpAudioPolicy == NULL) { - return 0; - } - return mpAudioPolicy->get_strategy_for_stream(mpAudioPolicy, stream); -} - -//audio policy: use audio_device_t appropriately - -audio_devices_t AudioPolicyService::getDevicesForStream(audio_stream_type_t stream) -{ - if (mpAudioPolicy == NULL) { - return (audio_devices_t)0; - } - return mpAudioPolicy->get_devices_for_stream(mpAudioPolicy, stream); -} - -audio_io_handle_t AudioPolicyService::getOutputForEffect(const effect_descriptor_t *desc) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - Mutex::Autolock _l(mLock); - return mpAudioPolicy->get_output_for_effect(mpAudioPolicy, desc); -} - -status_t AudioPolicyService::registerEffect(const effect_descriptor_t *desc, - audio_io_handle_t io, - uint32_t strategy, - int session, - int id) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - return mpAudioPolicy->register_effect(mpAudioPolicy, desc, io, strategy, session, id); -} - -status_t AudioPolicyService::unregisterEffect(int id) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - return mpAudioPolicy->unregister_effect(mpAudioPolicy, id); -} - -status_t AudioPolicyService::setEffectEnabled(int id, bool enabled) -{ - if (mpAudioPolicy == NULL) { - return NO_INIT; - } - return mpAudioPolicy->set_effect_enabled(mpAudioPolicy, id, enabled); -} - -bool AudioPolicyService::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const -{ - if (mpAudioPolicy == NULL) { - return 0; - } - Mutex::Autolock _l(mLock); - return mpAudioPolicy->is_stream_active(mpAudioPolicy, stream, inPastMs); -} - -bool AudioPolicyService::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const -{ - if (mpAudioPolicy == NULL) { - return 0; - } - Mutex::Autolock _l(mLock); - return mpAudioPolicy->is_stream_active_remotely(mpAudioPolicy, stream, inPastMs); -} - -bool AudioPolicyService::isSourceActive(audio_source_t source) const -{ - if (mpAudioPolicy == NULL) { - return false; - } - if (mpAudioPolicy->is_source_active == 0) { - return false; - } - Mutex::Autolock _l(mLock); - return mpAudioPolicy->is_source_active(mpAudioPolicy, source); -} - -status_t AudioPolicyService::queryDefaultPreProcessing(int audioSession, - effect_descriptor_t *descriptors, - uint32_t *count) -{ - - if (mpAudioPolicy == NULL) { - *count = 0; - return NO_INIT; - } - Mutex::Autolock _l(mLock); - status_t status = NO_ERROR; - - size_t index; - for (index = 0; index < mInputs.size(); index++) { - if (mInputs.valueAt(index)->mSessionId == audioSession) { - break; - } - } - if (index == mInputs.size()) { - *count = 0; - return BAD_VALUE; - } - Vector< sp<AudioEffect> > effects = mInputs.valueAt(index)->mEffects; - - for (size_t i = 0; i < effects.size(); i++) { - effect_descriptor_t desc = effects[i]->descriptor(); - if (i < *count) { - descriptors[i] = desc; - } - } - if (effects.size() > *count) { - status = NO_MEMORY; - } - *count = effects.size(); - return status; -} - -void AudioPolicyService::binderDied(const wp<IBinder>& who) { - ALOGW("binderDied() %p, calling pid %d", who.unsafe_get(), - IPCThreadState::self()->getCallingPid()); -} - -static bool tryLock(Mutex& mutex) -{ - bool locked = false; - for (int i = 0; i < kDumpLockRetries; ++i) { - if (mutex.tryLock() == NO_ERROR) { - locked = true; - break; - } - usleep(kDumpLockSleepUs); - } - return locked; -} - -status_t AudioPolicyService::dumpInternals(int fd) -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - snprintf(buffer, SIZE, "PolicyManager Interface: %p\n", mpAudioPolicy); - result.append(buffer); - snprintf(buffer, SIZE, "Command Thread: %p\n", mAudioCommandThread.get()); - result.append(buffer); - snprintf(buffer, SIZE, "Tones Thread: %p\n", mTonePlaybackThread.get()); - result.append(buffer); - - write(fd, result.string(), result.size()); - return NO_ERROR; -} - -status_t AudioPolicyService::dump(int fd, const Vector<String16>& args) -{ - if (!dumpAllowed()) { - dumpPermissionDenial(fd); - } else { - bool locked = tryLock(mLock); - if (!locked) { - String8 result(kDeadlockedString); - write(fd, result.string(), result.size()); - } - - dumpInternals(fd); - if (mAudioCommandThread != 0) { - mAudioCommandThread->dump(fd); - } - if (mTonePlaybackThread != 0) { - mTonePlaybackThread->dump(fd); - } - - if (mpAudioPolicy) { - mpAudioPolicy->dump(mpAudioPolicy, fd); - } - - if (locked) mLock.unlock(); - } - return NO_ERROR; -} - -status_t AudioPolicyService::dumpPermissionDenial(int fd) -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - snprintf(buffer, SIZE, "Permission Denial: " - "can't dump AudioPolicyService from pid=%d, uid=%d\n", - IPCThreadState::self()->getCallingPid(), - IPCThreadState::self()->getCallingUid()); - result.append(buffer); - write(fd, result.string(), result.size()); - return NO_ERROR; -} - -void AudioPolicyService::setPreProcessorEnabled(const InputDesc *inputDesc, bool enabled) -{ - const Vector<sp<AudioEffect> > &fxVector = inputDesc->mEffects; - for (size_t i = 0; i < fxVector.size(); i++) { - fxVector.itemAt(i)->setEnabled(enabled); - } -} - -status_t AudioPolicyService::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - return BnAudioPolicyService::onTransact(code, data, reply, flags); -} - - -// ----------- AudioPolicyService::AudioCommandThread implementation ---------- - -AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name, - const wp<AudioPolicyService>& service) - : Thread(false), mName(name), mService(service) -{ - mpToneGenerator = NULL; -} - - -AudioPolicyService::AudioCommandThread::~AudioCommandThread() -{ - if (!mAudioCommands.isEmpty()) { - release_wake_lock(mName.string()); - } - for (size_t k=0; k < mAudioCommands.size(); k++) { - delete mAudioCommands[k]->mParam; - delete mAudioCommands[k]; - } - mAudioCommands.clear(); - delete mpToneGenerator; -} - -void AudioPolicyService::AudioCommandThread::onFirstRef() -{ - run(mName.string(), ANDROID_PRIORITY_AUDIO); -} - -bool AudioPolicyService::AudioCommandThread::threadLoop() -{ - nsecs_t waitTime = INT64_MAX; - - mLock.lock(); - while (!exitPending()) - { - while (!mAudioCommands.isEmpty()) { - nsecs_t curTime = systemTime(); - // commands are sorted by increasing time stamp: execute them from index 0 and up - if (mAudioCommands[0]->mTime <= curTime) { - AudioCommand *command = mAudioCommands[0]; - mAudioCommands.removeAt(0); - mLastCommand = *command; - - switch (command->mCommand) { - case START_TONE: { - mLock.unlock(); - ToneData *data = (ToneData *)command->mParam; - ALOGV("AudioCommandThread() processing start tone %d on stream %d", - data->mType, data->mStream); - delete mpToneGenerator; - mpToneGenerator = new ToneGenerator(data->mStream, 1.0); - mpToneGenerator->startTone(data->mType); - delete data; - mLock.lock(); - }break; - case STOP_TONE: { - mLock.unlock(); - ALOGV("AudioCommandThread() processing stop tone"); - if (mpToneGenerator != NULL) { - mpToneGenerator->stopTone(); - delete mpToneGenerator; - mpToneGenerator = NULL; - } - mLock.lock(); - }break; - case SET_VOLUME: { - VolumeData *data = (VolumeData *)command->mParam; - ALOGV("AudioCommandThread() processing set volume stream %d, \ - volume %f, output %d", data->mStream, data->mVolume, data->mIO); - command->mStatus = AudioSystem::setStreamVolume(data->mStream, - data->mVolume, - data->mIO); - if (command->mWaitStatus) { - command->mCond.signal(); - command->mCond.waitRelative(mLock, kAudioCommandTimeout); - } - delete data; - }break; - case SET_PARAMETERS: { - ParametersData *data = (ParametersData *)command->mParam; - ALOGV("AudioCommandThread() processing set parameters string %s, io %d", - data->mKeyValuePairs.string(), data->mIO); - command->mStatus = AudioSystem::setParameters(data->mIO, data->mKeyValuePairs); - if (command->mWaitStatus) { - command->mCond.signal(); - command->mCond.waitRelative(mLock, kAudioCommandTimeout); - } - delete data; - }break; - case SET_VOICE_VOLUME: { - VoiceVolumeData *data = (VoiceVolumeData *)command->mParam; - ALOGV("AudioCommandThread() processing set voice volume volume %f", - data->mVolume); - command->mStatus = AudioSystem::setVoiceVolume(data->mVolume); - if (command->mWaitStatus) { - command->mCond.signal(); - command->mCond.waitRelative(mLock, kAudioCommandTimeout); - } - delete data; - }break; - case STOP_OUTPUT: { - StopOutputData *data = (StopOutputData *)command->mParam; - ALOGV("AudioCommandThread() processing stop output %d", - data->mIO); - sp<AudioPolicyService> svc = mService.promote(); - if (svc == 0) { - break; - } - mLock.unlock(); - svc->doStopOutput(data->mIO, data->mStream, data->mSession); - mLock.lock(); - delete data; - }break; - case RELEASE_OUTPUT: { - ReleaseOutputData *data = (ReleaseOutputData *)command->mParam; - ALOGV("AudioCommandThread() processing release output %d", - data->mIO); - sp<AudioPolicyService> svc = mService.promote(); - if (svc == 0) { - break; - } - mLock.unlock(); - svc->doReleaseOutput(data->mIO); - mLock.lock(); - delete data; - }break; - default: - ALOGW("AudioCommandThread() unknown command %d", command->mCommand); - } - delete command; - waitTime = INT64_MAX; - } else { - waitTime = mAudioCommands[0]->mTime - curTime; - break; - } - } - // release delayed commands wake lock - if (mAudioCommands.isEmpty()) { - release_wake_lock(mName.string()); - } - ALOGV("AudioCommandThread() going to sleep"); - mWaitWorkCV.waitRelative(mLock, waitTime); - ALOGV("AudioCommandThread() waking up"); - } - mLock.unlock(); - return false; -} - -status_t AudioPolicyService::AudioCommandThread::dump(int fd) -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - snprintf(buffer, SIZE, "AudioCommandThread %p Dump\n", this); - result.append(buffer); - write(fd, result.string(), result.size()); - - bool locked = tryLock(mLock); - if (!locked) { - String8 result2(kCmdDeadlockedString); - write(fd, result2.string(), result2.size()); - } - - snprintf(buffer, SIZE, "- Commands:\n"); - result = String8(buffer); - result.append(" Command Time Wait pParam\n"); - for (size_t i = 0; i < mAudioCommands.size(); i++) { - mAudioCommands[i]->dump(buffer, SIZE); - result.append(buffer); - } - result.append(" Last Command\n"); - mLastCommand.dump(buffer, SIZE); - result.append(buffer); - - write(fd, result.string(), result.size()); - - if (locked) mLock.unlock(); - - return NO_ERROR; -} - -void AudioPolicyService::AudioCommandThread::startToneCommand(ToneGenerator::tone_type type, - audio_stream_type_t stream) -{ - AudioCommand *command = new AudioCommand(); - command->mCommand = START_TONE; - ToneData *data = new ToneData(); - data->mType = type; - data->mStream = stream; - command->mParam = data; - Mutex::Autolock _l(mLock); - insertCommand_l(command); - ALOGV("AudioCommandThread() adding tone start type %d, stream %d", type, stream); - mWaitWorkCV.signal(); -} - -void AudioPolicyService::AudioCommandThread::stopToneCommand() -{ - AudioCommand *command = new AudioCommand(); - command->mCommand = STOP_TONE; - command->mParam = NULL; - Mutex::Autolock _l(mLock); - insertCommand_l(command); - ALOGV("AudioCommandThread() adding tone stop"); - mWaitWorkCV.signal(); -} - -status_t AudioPolicyService::AudioCommandThread::volumeCommand(audio_stream_type_t stream, - float volume, - audio_io_handle_t output, - int delayMs) -{ - status_t status = NO_ERROR; - - AudioCommand *command = new AudioCommand(); - command->mCommand = SET_VOLUME; - VolumeData *data = new VolumeData(); - data->mStream = stream; - data->mVolume = volume; - data->mIO = output; - command->mParam = data; - Mutex::Autolock _l(mLock); - insertCommand_l(command, delayMs); - ALOGV("AudioCommandThread() adding set volume stream %d, volume %f, output %d", - stream, volume, output); - mWaitWorkCV.signal(); - if (command->mWaitStatus) { - command->mCond.wait(mLock); - status = command->mStatus; - command->mCond.signal(); - } - return status; -} - -status_t AudioPolicyService::AudioCommandThread::parametersCommand(audio_io_handle_t ioHandle, - const char *keyValuePairs, - int delayMs) -{ - status_t status = NO_ERROR; - - AudioCommand *command = new AudioCommand(); - command->mCommand = SET_PARAMETERS; - ParametersData *data = new ParametersData(); - data->mIO = ioHandle; - data->mKeyValuePairs = String8(keyValuePairs); - command->mParam = data; - Mutex::Autolock _l(mLock); - insertCommand_l(command, delayMs); - ALOGV("AudioCommandThread() adding set parameter string %s, io %d ,delay %d", - keyValuePairs, ioHandle, delayMs); - mWaitWorkCV.signal(); - if (command->mWaitStatus) { - command->mCond.wait(mLock); - status = command->mStatus; - command->mCond.signal(); - } - return status; -} - -status_t AudioPolicyService::AudioCommandThread::voiceVolumeCommand(float volume, int delayMs) -{ - status_t status = NO_ERROR; - - AudioCommand *command = new AudioCommand(); - command->mCommand = SET_VOICE_VOLUME; - VoiceVolumeData *data = new VoiceVolumeData(); - data->mVolume = volume; - command->mParam = data; - Mutex::Autolock _l(mLock); - insertCommand_l(command, delayMs); - ALOGV("AudioCommandThread() adding set voice volume volume %f", volume); - mWaitWorkCV.signal(); - if (command->mWaitStatus) { - command->mCond.wait(mLock); - status = command->mStatus; - command->mCond.signal(); - } - return status; -} - -void AudioPolicyService::AudioCommandThread::stopOutputCommand(audio_io_handle_t output, - audio_stream_type_t stream, - int session) -{ - AudioCommand *command = new AudioCommand(); - command->mCommand = STOP_OUTPUT; - StopOutputData *data = new StopOutputData(); - data->mIO = output; - data->mStream = stream; - data->mSession = session; - command->mParam = data; - Mutex::Autolock _l(mLock); - insertCommand_l(command); - ALOGV("AudioCommandThread() adding stop output %d", output); - mWaitWorkCV.signal(); -} - -void AudioPolicyService::AudioCommandThread::releaseOutputCommand(audio_io_handle_t output) -{ - AudioCommand *command = new AudioCommand(); - command->mCommand = RELEASE_OUTPUT; - ReleaseOutputData *data = new ReleaseOutputData(); - data->mIO = output; - command->mParam = data; - Mutex::Autolock _l(mLock); - insertCommand_l(command); - ALOGV("AudioCommandThread() adding release output %d", output); - mWaitWorkCV.signal(); -} - -// insertCommand_l() must be called with mLock held -void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *command, int delayMs) -{ - ssize_t i; // not size_t because i will count down to -1 - Vector <AudioCommand *> removedCommands; - command->mTime = systemTime() + milliseconds(delayMs); - - // acquire wake lock to make sure delayed commands are processed - if (mAudioCommands.isEmpty()) { - acquire_wake_lock(PARTIAL_WAKE_LOCK, mName.string()); - } - - // check same pending commands with later time stamps and eliminate them - for (i = mAudioCommands.size()-1; i >= 0; i--) { - AudioCommand *command2 = mAudioCommands[i]; - // commands are sorted by increasing time stamp: no need to scan the rest of mAudioCommands - if (command2->mTime <= command->mTime) break; - if (command2->mCommand != command->mCommand) continue; - - switch (command->mCommand) { - case SET_PARAMETERS: { - ParametersData *data = (ParametersData *)command->mParam; - ParametersData *data2 = (ParametersData *)command2->mParam; - if (data->mIO != data2->mIO) break; - ALOGV("Comparing parameter command %s to new command %s", - data2->mKeyValuePairs.string(), data->mKeyValuePairs.string()); - AudioParameter param = AudioParameter(data->mKeyValuePairs); - AudioParameter param2 = AudioParameter(data2->mKeyValuePairs); - for (size_t j = 0; j < param.size(); j++) { - String8 key; - String8 value; - param.getAt(j, key, value); - for (size_t k = 0; k < param2.size(); k++) { - String8 key2; - String8 value2; - param2.getAt(k, key2, value2); - if (key2 == key) { - param2.remove(key2); - ALOGV("Filtering out parameter %s", key2.string()); - break; - } - } - } - // if all keys have been filtered out, remove the command. - // otherwise, update the key value pairs - if (param2.size() == 0) { - removedCommands.add(command2); - } else { - data2->mKeyValuePairs = param2.toString(); - } - command->mTime = command2->mTime; - // force delayMs to non 0 so that code below does not request to wait for - // command status as the command is now delayed - delayMs = 1; - } break; - - case SET_VOLUME: { - VolumeData *data = (VolumeData *)command->mParam; - VolumeData *data2 = (VolumeData *)command2->mParam; - if (data->mIO != data2->mIO) break; - if (data->mStream != data2->mStream) break; - ALOGV("Filtering out volume command on output %d for stream %d", - data->mIO, data->mStream); - removedCommands.add(command2); - command->mTime = command2->mTime; - // force delayMs to non 0 so that code below does not request to wait for - // command status as the command is now delayed - delayMs = 1; - } break; - case START_TONE: - case STOP_TONE: - default: - break; - } - } - - // remove filtered commands - for (size_t j = 0; j < removedCommands.size(); j++) { - // removed commands always have time stamps greater than current command - for (size_t k = i + 1; k < mAudioCommands.size(); k++) { - if (mAudioCommands[k] == removedCommands[j]) { - ALOGV("suppressing command: %d", mAudioCommands[k]->mCommand); - // for commands that are not filtered, - // command->mParam is deleted in threadLoop - delete mAudioCommands[k]->mParam; - delete mAudioCommands[k]; - mAudioCommands.removeAt(k); - break; - } - } - } - removedCommands.clear(); - - // wait for status only if delay is 0 - if (delayMs == 0) { - command->mWaitStatus = true; - } else { - command->mWaitStatus = false; - } - - // insert command at the right place according to its time stamp - ALOGV("inserting command: %d at index %d, num commands %d", - command->mCommand, (int)i+1, mAudioCommands.size()); - mAudioCommands.insertAt(command, i + 1); -} - -void AudioPolicyService::AudioCommandThread::exit() -{ - ALOGV("AudioCommandThread::exit"); - { - AutoMutex _l(mLock); - requestExit(); - mWaitWorkCV.signal(); - } - requestExitAndWait(); -} - -void AudioPolicyService::AudioCommandThread::AudioCommand::dump(char* buffer, size_t size) -{ - snprintf(buffer, size, " %02d %06d.%03d %01u %p\n", - mCommand, - (int)ns2s(mTime), - (int)ns2ms(mTime)%1000, - mWaitStatus, - mParam); -} - -/******* helpers for the service_ops callbacks defined below *********/ -void AudioPolicyService::setParameters(audio_io_handle_t ioHandle, - const char *keyValuePairs, - int delayMs) -{ - mAudioCommandThread->parametersCommand(ioHandle, keyValuePairs, - delayMs); -} - -int AudioPolicyService::setStreamVolume(audio_stream_type_t stream, - float volume, - audio_io_handle_t output, - int delayMs) -{ - return (int)mAudioCommandThread->volumeCommand(stream, volume, - output, delayMs); -} - -int AudioPolicyService::startTone(audio_policy_tone_t tone, - audio_stream_type_t stream) -{ - if (tone != AUDIO_POLICY_TONE_IN_CALL_NOTIFICATION) - ALOGE("startTone: illegal tone requested (%d)", tone); - if (stream != AUDIO_STREAM_VOICE_CALL) - ALOGE("startTone: illegal stream (%d) requested for tone %d", stream, - tone); - mTonePlaybackThread->startToneCommand(ToneGenerator::TONE_SUP_CALL_WAITING, - AUDIO_STREAM_VOICE_CALL); - return 0; -} - -int AudioPolicyService::stopTone() -{ - mTonePlaybackThread->stopToneCommand(); - return 0; -} - -int AudioPolicyService::setVoiceVolume(float volume, int delayMs) -{ - return (int)mAudioCommandThread->voiceVolumeCommand(volume, delayMs); -} - -bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info) -{ - if (mpAudioPolicy == NULL) { - ALOGV("mpAudioPolicy == NULL"); - return false; - } - - if (mpAudioPolicy->is_offload_supported == NULL) { - ALOGV("HAL does not implement is_offload_supported"); - return false; - } - - return mpAudioPolicy->is_offload_supported(mpAudioPolicy, &info); -} - -// ---------------------------------------------------------------------------- -// Audio pre-processing configuration -// ---------------------------------------------------------------------------- - -/*static*/ const char * const AudioPolicyService::kInputSourceNames[AUDIO_SOURCE_CNT -1] = { - MIC_SRC_TAG, - VOICE_UL_SRC_TAG, - VOICE_DL_SRC_TAG, - VOICE_CALL_SRC_TAG, - CAMCORDER_SRC_TAG, - VOICE_REC_SRC_TAG, - VOICE_COMM_SRC_TAG -}; - -// returns the audio_source_t enum corresponding to the input source name or -// AUDIO_SOURCE_CNT is no match found -audio_source_t AudioPolicyService::inputSourceNameToEnum(const char *name) -{ - int i; - for (i = AUDIO_SOURCE_MIC; i < AUDIO_SOURCE_CNT; i++) { - if (strcmp(name, kInputSourceNames[i - AUDIO_SOURCE_MIC]) == 0) { - ALOGV("inputSourceNameToEnum found source %s %d", name, i); - break; - } - } - return (audio_source_t)i; -} - -size_t AudioPolicyService::growParamSize(char *param, - size_t size, - size_t *curSize, - size_t *totSize) -{ - // *curSize is at least sizeof(effect_param_t) + 2 * sizeof(int) - size_t pos = ((*curSize - 1 ) / size + 1) * size; - - if (pos + size > *totSize) { - while (pos + size > *totSize) { - *totSize += ((*totSize + 7) / 8) * 4; - } - param = (char *)realloc(param, *totSize); - } - *curSize = pos + size; - return pos; -} - -size_t AudioPolicyService::readParamValue(cnode *node, - char *param, - size_t *curSize, - size_t *totSize) -{ - if (strncmp(node->name, SHORT_TAG, sizeof(SHORT_TAG) + 1) == 0) { - size_t pos = growParamSize(param, sizeof(short), curSize, totSize); - *(short *)((char *)param + pos) = (short)atoi(node->value); - ALOGV("readParamValue() reading short %d", *(short *)((char *)param + pos)); - return sizeof(short); - } else if (strncmp(node->name, INT_TAG, sizeof(INT_TAG) + 1) == 0) { - size_t pos = growParamSize(param, sizeof(int), curSize, totSize); - *(int *)((char *)param + pos) = atoi(node->value); - ALOGV("readParamValue() reading int %d", *(int *)((char *)param + pos)); - return sizeof(int); - } else if (strncmp(node->name, FLOAT_TAG, sizeof(FLOAT_TAG) + 1) == 0) { - size_t pos = growParamSize(param, sizeof(float), curSize, totSize); - *(float *)((char *)param + pos) = (float)atof(node->value); - ALOGV("readParamValue() reading float %f",*(float *)((char *)param + pos)); - return sizeof(float); - } else if (strncmp(node->name, BOOL_TAG, sizeof(BOOL_TAG) + 1) == 0) { - size_t pos = growParamSize(param, sizeof(bool), curSize, totSize); - if (strncmp(node->value, "false", strlen("false") + 1) == 0) { - *(bool *)((char *)param + pos) = false; - } else { - *(bool *)((char *)param + pos) = true; - } - ALOGV("readParamValue() reading bool %s",*(bool *)((char *)param + pos) ? "true" : "false"); - return sizeof(bool); - } else if (strncmp(node->name, STRING_TAG, sizeof(STRING_TAG) + 1) == 0) { - size_t len = strnlen(node->value, EFFECT_STRING_LEN_MAX); - if (*curSize + len + 1 > *totSize) { - *totSize = *curSize + len + 1; - param = (char *)realloc(param, *totSize); - } - strncpy(param + *curSize, node->value, len); - *curSize += len; - param[*curSize] = '\0'; - ALOGV("readParamValue() reading string %s", param + *curSize - len); - return len; - } - ALOGW("readParamValue() unknown param type %s", node->name); - return 0; -} - -effect_param_t *AudioPolicyService::loadEffectParameter(cnode *root) -{ - cnode *param; - cnode *value; - size_t curSize = sizeof(effect_param_t); - size_t totSize = sizeof(effect_param_t) + 2 * sizeof(int); - effect_param_t *fx_param = (effect_param_t *)malloc(totSize); - - param = config_find(root, PARAM_TAG); - value = config_find(root, VALUE_TAG); - if (param == NULL && value == NULL) { - // try to parse simple parameter form {int int} - param = root->first_child; - if (param != NULL) { - // Note: that a pair of random strings is read as 0 0 - int *ptr = (int *)fx_param->data; - int *ptr2 = (int *)((char *)param + sizeof(effect_param_t)); - ALOGW("loadEffectParameter() ptr %p ptr2 %p", ptr, ptr2); - *ptr++ = atoi(param->name); - *ptr = atoi(param->value); - fx_param->psize = sizeof(int); - fx_param->vsize = sizeof(int); - return fx_param; - } - } - if (param == NULL || value == NULL) { - ALOGW("loadEffectParameter() invalid parameter description %s", root->name); - goto error; - } - - fx_param->psize = 0; - param = param->first_child; - while (param) { - ALOGV("loadEffectParameter() reading param of type %s", param->name); - size_t size = readParamValue(param, (char *)fx_param, &curSize, &totSize); - if (size == 0) { - goto error; - } - fx_param->psize += size; - param = param->next; - } - - // align start of value field on 32 bit boundary - curSize = ((curSize - 1 ) / sizeof(int) + 1) * sizeof(int); - - fx_param->vsize = 0; - value = value->first_child; - while (value) { - ALOGV("loadEffectParameter() reading value of type %s", value->name); - size_t size = readParamValue(value, (char *)fx_param, &curSize, &totSize); - if (size == 0) { - goto error; - } - fx_param->vsize += size; - value = value->next; - } - - return fx_param; - -error: - free(fx_param); - return NULL; -} - -void AudioPolicyService::loadEffectParameters(cnode *root, Vector <effect_param_t *>& params) -{ - cnode *node = root->first_child; - while (node) { - ALOGV("loadEffectParameters() loading param %s", node->name); - effect_param_t *param = loadEffectParameter(node); - if (param == NULL) { - node = node->next; - continue; - } - params.add(param); - node = node->next; - } -} - -AudioPolicyService::InputSourceDesc *AudioPolicyService::loadInputSource( - cnode *root, - const Vector <EffectDesc *>& effects) -{ - cnode *node = root->first_child; - if (node == NULL) { - ALOGW("loadInputSource() empty element %s", root->name); - return NULL; - } - InputSourceDesc *source = new InputSourceDesc(); - while (node) { - size_t i; - for (i = 0; i < effects.size(); i++) { - if (strncmp(effects[i]->mName, node->name, EFFECT_STRING_LEN_MAX) == 0) { - ALOGV("loadInputSource() found effect %s in list", node->name); - break; - } - } - if (i == effects.size()) { - ALOGV("loadInputSource() effect %s not in list", node->name); - node = node->next; - continue; - } - EffectDesc *effect = new EffectDesc(*effects[i]); // deep copy - loadEffectParameters(node, effect->mParams); - ALOGV("loadInputSource() adding effect %s uuid %08x", effect->mName, effect->mUuid.timeLow); - source->mEffects.add(effect); - node = node->next; - } - if (source->mEffects.size() == 0) { - ALOGW("loadInputSource() no valid effects found in source %s", root->name); - delete source; - return NULL; - } - return source; -} - -status_t AudioPolicyService::loadInputSources(cnode *root, const Vector <EffectDesc *>& effects) -{ - cnode *node = config_find(root, PREPROCESSING_TAG); - if (node == NULL) { - return -ENOENT; - } - node = node->first_child; - while (node) { - audio_source_t source = inputSourceNameToEnum(node->name); - if (source == AUDIO_SOURCE_CNT) { - ALOGW("loadInputSources() invalid input source %s", node->name); - node = node->next; - continue; - } - ALOGV("loadInputSources() loading input source %s", node->name); - InputSourceDesc *desc = loadInputSource(node, effects); - if (desc == NULL) { - node = node->next; - continue; - } - mInputSources.add(source, desc); - node = node->next; - } - return NO_ERROR; -} - -AudioPolicyService::EffectDesc *AudioPolicyService::loadEffect(cnode *root) -{ - cnode *node = config_find(root, UUID_TAG); - if (node == NULL) { - return NULL; - } - effect_uuid_t uuid; - if (AudioEffect::stringToGuid(node->value, &uuid) != NO_ERROR) { - ALOGW("loadEffect() invalid uuid %s", node->value); - return NULL; - } - return new EffectDesc(root->name, uuid); -} - -status_t AudioPolicyService::loadEffects(cnode *root, Vector <EffectDesc *>& effects) -{ - cnode *node = config_find(root, EFFECTS_TAG); - if (node == NULL) { - return -ENOENT; - } - node = node->first_child; - while (node) { - ALOGV("loadEffects() loading effect %s", node->name); - EffectDesc *effect = loadEffect(node); - if (effect == NULL) { - node = node->next; - continue; - } - effects.add(effect); - node = node->next; - } - return NO_ERROR; -} - -status_t AudioPolicyService::loadPreProcessorConfig(const char *path) -{ - cnode *root; - char *data; - - data = (char *)load_file(path, NULL); - if (data == NULL) { - return -ENODEV; - } - root = config_node("", ""); - config_load(root, data); - - Vector <EffectDesc *> effects; - loadEffects(root, effects); - loadInputSources(root, effects); - - // delete effects to fix memory leak. - // as effects is local var and valgrind would treat this as memory leak - // and although it only did in mediaserver init, but free it in case mediaserver reboot - size_t i; - for (i = 0; i < effects.size(); i++) { - delete effects[i]; - } - - config_free(root); - free(root); - free(data); - - return NO_ERROR; -} - -/* implementation of the interface to the policy manager */ -extern "C" { - - -static audio_module_handle_t aps_load_hw_module(void *service, - const char *name) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) { - ALOGW("%s: could not get AudioFlinger", __func__); - return 0; - } - - return af->loadHwModule(name); -} - -// deprecated: replaced by aps_open_output_on_module() -static audio_io_handle_t aps_open_output(void *service, - audio_devices_t *pDevices, - uint32_t *pSamplingRate, - audio_format_t *pFormat, - audio_channel_mask_t *pChannelMask, - uint32_t *pLatencyMs, - audio_output_flags_t flags) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) { - ALOGW("%s: could not get AudioFlinger", __func__); - return 0; - } - - return af->openOutput((audio_module_handle_t)0, pDevices, pSamplingRate, pFormat, pChannelMask, - pLatencyMs, flags); -} - -static audio_io_handle_t aps_open_output_on_module(void *service, - audio_module_handle_t module, - audio_devices_t *pDevices, - uint32_t *pSamplingRate, - audio_format_t *pFormat, - audio_channel_mask_t *pChannelMask, - uint32_t *pLatencyMs, - audio_output_flags_t flags, - const audio_offload_info_t *offloadInfo) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) { - ALOGW("%s: could not get AudioFlinger", __func__); - return 0; - } - return af->openOutput(module, pDevices, pSamplingRate, pFormat, pChannelMask, - pLatencyMs, flags, offloadInfo); -} - -static audio_io_handle_t aps_open_dup_output(void *service, - audio_io_handle_t output1, - audio_io_handle_t output2) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) { - ALOGW("%s: could not get AudioFlinger", __func__); - return 0; - } - return af->openDuplicateOutput(output1, output2); -} - -static int aps_close_output(void *service, audio_io_handle_t output) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) - return PERMISSION_DENIED; - - return af->closeOutput(output); -} - -static int aps_suspend_output(void *service, audio_io_handle_t output) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) { - ALOGW("%s: could not get AudioFlinger", __func__); - return PERMISSION_DENIED; - } - - return af->suspendOutput(output); -} - -static int aps_restore_output(void *service, audio_io_handle_t output) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) { - ALOGW("%s: could not get AudioFlinger", __func__); - return PERMISSION_DENIED; - } - - return af->restoreOutput(output); -} - -// deprecated: replaced by aps_open_input_on_module(), and acoustics parameter is ignored -static audio_io_handle_t aps_open_input(void *service, - audio_devices_t *pDevices, - uint32_t *pSamplingRate, - audio_format_t *pFormat, - audio_channel_mask_t *pChannelMask, - audio_in_acoustics_t acoustics) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) { - ALOGW("%s: could not get AudioFlinger", __func__); - return 0; - } - - return af->openInput((audio_module_handle_t)0, pDevices, pSamplingRate, pFormat, pChannelMask); -} - -static audio_io_handle_t aps_open_input_on_module(void *service, - audio_module_handle_t module, - audio_devices_t *pDevices, - uint32_t *pSamplingRate, - audio_format_t *pFormat, - audio_channel_mask_t *pChannelMask) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) { - ALOGW("%s: could not get AudioFlinger", __func__); - return 0; - } - - return af->openInput(module, pDevices, pSamplingRate, pFormat, pChannelMask); -} - -static int aps_close_input(void *service, audio_io_handle_t input) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) - return PERMISSION_DENIED; - - return af->closeInput(input); -} - -static int aps_set_stream_output(void *service, audio_stream_type_t stream, - audio_io_handle_t output) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) - return PERMISSION_DENIED; - - return af->setStreamOutput(stream, output); -} - -static int aps_move_effects(void *service, int session, - audio_io_handle_t src_output, - audio_io_handle_t dst_output) -{ - sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); - if (af == 0) - return PERMISSION_DENIED; - - return af->moveEffects(session, src_output, dst_output); -} - -static char * aps_get_parameters(void *service, audio_io_handle_t io_handle, - const char *keys) -{ - String8 result = AudioSystem::getParameters(io_handle, String8(keys)); - return strdup(result.string()); -} - -static void aps_set_parameters(void *service, audio_io_handle_t io_handle, - const char *kv_pairs, int delay_ms) -{ - AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; - - audioPolicyService->setParameters(io_handle, kv_pairs, delay_ms); -} - -static int aps_set_stream_volume(void *service, audio_stream_type_t stream, - float volume, audio_io_handle_t output, - int delay_ms) -{ - AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; - - return audioPolicyService->setStreamVolume(stream, volume, output, - delay_ms); -} - -static int aps_start_tone(void *service, audio_policy_tone_t tone, - audio_stream_type_t stream) -{ - AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; - - return audioPolicyService->startTone(tone, stream); -} - -static int aps_stop_tone(void *service) -{ - AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; - - return audioPolicyService->stopTone(); -} - -static int aps_set_voice_volume(void *service, float volume, int delay_ms) -{ - AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; - - return audioPolicyService->setVoiceVolume(volume, delay_ms); -} - -}; // extern "C" - -namespace { - struct audio_policy_service_ops aps_ops = { - open_output : aps_open_output, - open_duplicate_output : aps_open_dup_output, - close_output : aps_close_output, - suspend_output : aps_suspend_output, - restore_output : aps_restore_output, - open_input : aps_open_input, - close_input : aps_close_input, - set_stream_volume : aps_set_stream_volume, - set_stream_output : aps_set_stream_output, - set_parameters : aps_set_parameters, - get_parameters : aps_get_parameters, - start_tone : aps_start_tone, - stop_tone : aps_stop_tone, - set_voice_volume : aps_set_voice_volume, - move_effects : aps_move_effects, - load_hw_module : aps_load_hw_module, - open_output_on_module : aps_open_output_on_module, - open_input_on_module : aps_open_input_on_module, - }; -}; // namespace <unnamed> - -}; // namespace android diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h deleted file mode 100644 index a38160f..0000000 --- a/services/audioflinger/AudioPolicyService.h +++ /dev/null @@ -1,362 +0,0 @@ -/* - * 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_AUDIOPOLICYSERVICE_H -#define ANDROID_AUDIOPOLICYSERVICE_H - -#include <cutils/misc.h> -#include <cutils/config_utils.h> -#include <cutils/compiler.h> -#include <utils/String8.h> -#include <utils/Vector.h> -#include <utils/SortedVector.h> -#include <binder/BinderService.h> -#include <system/audio.h> -#include <system/audio_policy.h> -#include <hardware/audio_policy.h> -#include <media/IAudioPolicyService.h> -#include <media/ToneGenerator.h> -#include <media/AudioEffect.h> - -namespace android { - -// ---------------------------------------------------------------------------- - -class AudioPolicyService : - public BinderService<AudioPolicyService>, - public BnAudioPolicyService, -// public AudioPolicyClientInterface, - public IBinder::DeathRecipient -{ - friend class BinderService<AudioPolicyService>; - -public: - // for BinderService - static const char *getServiceName() ANDROID_API { return "media.audio_policy"; } - - virtual status_t dump(int fd, const Vector<String16>& args); - - // - // BnAudioPolicyService (see AudioPolicyInterface for method descriptions) - // - - virtual status_t setDeviceConnectionState(audio_devices_t device, - audio_policy_dev_state_t state, - const char *device_address); - virtual audio_policy_dev_state_t getDeviceConnectionState( - audio_devices_t device, - const char *device_address); - virtual status_t setPhoneState(audio_mode_t state); - virtual status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config); - virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage); - virtual audio_io_handle_t getOutput(audio_stream_type_t stream, - uint32_t samplingRate = 0, - audio_format_t format = AUDIO_FORMAT_DEFAULT, - audio_channel_mask_t channelMask = 0, - audio_output_flags_t flags = - AUDIO_OUTPUT_FLAG_NONE, - const audio_offload_info_t *offloadInfo = NULL); - virtual status_t startOutput(audio_io_handle_t output, - audio_stream_type_t stream, - int session = 0); - virtual status_t stopOutput(audio_io_handle_t output, - audio_stream_type_t stream, - int session = 0); - virtual void releaseOutput(audio_io_handle_t output); - virtual audio_io_handle_t getInput(audio_source_t inputSource, - uint32_t samplingRate = 0, - audio_format_t format = AUDIO_FORMAT_DEFAULT, - audio_channel_mask_t channelMask = 0, - int audioSession = 0); - virtual status_t startInput(audio_io_handle_t input); - virtual status_t stopInput(audio_io_handle_t input); - virtual void releaseInput(audio_io_handle_t input); - virtual status_t initStreamVolume(audio_stream_type_t stream, - int indexMin, - int indexMax); - virtual status_t setStreamVolumeIndex(audio_stream_type_t stream, - int index, - audio_devices_t device); - virtual status_t getStreamVolumeIndex(audio_stream_type_t stream, - int *index, - audio_devices_t device); - - virtual uint32_t getStrategyForStream(audio_stream_type_t stream); - virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream); - - virtual audio_io_handle_t getOutputForEffect(const effect_descriptor_t *desc); - virtual status_t registerEffect(const effect_descriptor_t *desc, - audio_io_handle_t io, - uint32_t strategy, - int session, - int id); - virtual status_t unregisterEffect(int id); - virtual status_t setEffectEnabled(int id, bool enabled); - virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const; - virtual bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs = 0) const; - virtual bool isSourceActive(audio_source_t source) const; - - virtual status_t queryDefaultPreProcessing(int audioSession, - effect_descriptor_t *descriptors, - uint32_t *count); - virtual status_t onTransact( - uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags); - - // IBinder::DeathRecipient - virtual void binderDied(const wp<IBinder>& who); - - // - // Helpers for the struct audio_policy_service_ops implementation. - // This is used by the audio policy manager for certain operations that - // are implemented by the policy service. - // - virtual void setParameters(audio_io_handle_t ioHandle, - const char *keyValuePairs, - int delayMs); - - virtual status_t setStreamVolume(audio_stream_type_t stream, - float volume, - audio_io_handle_t output, - int delayMs = 0); - virtual status_t startTone(audio_policy_tone_t tone, audio_stream_type_t stream); - virtual status_t stopTone(); - virtual status_t setVoiceVolume(float volume, int delayMs = 0); - virtual bool isOffloadSupported(const audio_offload_info_t &config); - - status_t doStopOutput(audio_io_handle_t output, - audio_stream_type_t stream, - int session = 0); - void doReleaseOutput(audio_io_handle_t output); - -private: - AudioPolicyService() ANDROID_API; - virtual ~AudioPolicyService(); - - status_t dumpInternals(int fd); - - // Thread used for tone playback and to send audio config commands to audio flinger - // For tone playback, using a separate thread is necessary to avoid deadlock with mLock because - // startTone() and stopTone() are normally called with mLock locked and requesting a tone start - // or stop will cause calls to AudioPolicyService and an attempt to lock mLock. - // For audio config commands, it is necessary because audio flinger requires that the calling - // process (user) has permission to modify audio settings. - class AudioCommandThread : public Thread { - class AudioCommand; - public: - - // commands for tone AudioCommand - enum { - START_TONE, - STOP_TONE, - SET_VOLUME, - SET_PARAMETERS, - SET_VOICE_VOLUME, - STOP_OUTPUT, - RELEASE_OUTPUT - }; - - AudioCommandThread (String8 name, const wp<AudioPolicyService>& service); - virtual ~AudioCommandThread(); - - status_t dump(int fd); - - // Thread virtuals - virtual void onFirstRef(); - virtual bool threadLoop(); - - void exit(); - void startToneCommand(ToneGenerator::tone_type type, - audio_stream_type_t stream); - void stopToneCommand(); - status_t volumeCommand(audio_stream_type_t stream, float volume, - audio_io_handle_t output, int delayMs = 0); - status_t parametersCommand(audio_io_handle_t ioHandle, - const char *keyValuePairs, int delayMs = 0); - status_t voiceVolumeCommand(float volume, int delayMs = 0); - void stopOutputCommand(audio_io_handle_t output, - audio_stream_type_t stream, - int session); - void releaseOutputCommand(audio_io_handle_t output); - - void insertCommand_l(AudioCommand *command, int delayMs = 0); - - private: - class AudioCommandData; - - // descriptor for requested tone playback event - class AudioCommand { - - public: - AudioCommand() - : mCommand(-1) {} - - void dump(char* buffer, size_t size); - - int mCommand; // START_TONE, STOP_TONE ... - nsecs_t mTime; // time stamp - Condition mCond; // condition for status return - status_t mStatus; // command status - bool mWaitStatus; // true if caller is waiting for status - AudioCommandData *mParam; // command specific parameter data - }; - - class AudioCommandData { - public: - virtual ~AudioCommandData() {} - protected: - AudioCommandData() {} - }; - - class ToneData : public AudioCommandData { - public: - ToneGenerator::tone_type mType; // tone type (START_TONE only) - audio_stream_type_t mStream; // stream type (START_TONE only) - }; - - class VolumeData : public AudioCommandData { - public: - audio_stream_type_t mStream; - float mVolume; - audio_io_handle_t mIO; - }; - - class ParametersData : public AudioCommandData { - public: - audio_io_handle_t mIO; - String8 mKeyValuePairs; - }; - - class VoiceVolumeData : public AudioCommandData { - public: - float mVolume; - }; - - class StopOutputData : public AudioCommandData { - public: - audio_io_handle_t mIO; - audio_stream_type_t mStream; - int mSession; - }; - - class ReleaseOutputData : public AudioCommandData { - public: - audio_io_handle_t mIO; - }; - - Mutex mLock; - Condition mWaitWorkCV; - Vector <AudioCommand *> mAudioCommands; // list of pending commands - ToneGenerator *mpToneGenerator; // the tone generator - AudioCommand mLastCommand; // last processed command (used by dump) - String8 mName; // string used by wake lock fo delayed commands - wp<AudioPolicyService> mService; - }; - - class EffectDesc { - public: - EffectDesc(const char *name, const effect_uuid_t& uuid) : - mName(strdup(name)), - mUuid(uuid) { } - EffectDesc(const EffectDesc& orig) : - mName(strdup(orig.mName)), - mUuid(orig.mUuid) { - // deep copy mParams - for (size_t k = 0; k < orig.mParams.size(); k++) { - effect_param_t *origParam = orig.mParams[k]; - // psize and vsize are rounded up to an int boundary for allocation - size_t origSize = sizeof(effect_param_t) + - ((origParam->psize + 3) & ~3) + - ((origParam->vsize + 3) & ~3); - effect_param_t *dupParam = (effect_param_t *) malloc(origSize); - memcpy(dupParam, origParam, origSize); - // This works because the param buffer allocation is also done by - // multiples of 4 bytes originally. In theory we should memcpy only - // the actual param size, that is without rounding vsize. - mParams.add(dupParam); - } - } - /*virtual*/ ~EffectDesc() { - free(mName); - for (size_t k = 0; k < mParams.size(); k++) { - free(mParams[k]); - } - } - char *mName; - effect_uuid_t mUuid; - Vector <effect_param_t *> mParams; - }; - - class InputSourceDesc { - public: - InputSourceDesc() {} - /*virtual*/ ~InputSourceDesc() { - for (size_t j = 0; j < mEffects.size(); j++) { - delete mEffects[j]; - } - } - Vector <EffectDesc *> mEffects; - }; - - - class InputDesc { - public: - InputDesc(int session) : mSessionId(session) {} - /*virtual*/ ~InputDesc() {} - const int mSessionId; - Vector< sp<AudioEffect> >mEffects; - }; - - static const char * const kInputSourceNames[AUDIO_SOURCE_CNT -1]; - - void setPreProcessorEnabled(const InputDesc *inputDesc, bool enabled); - status_t loadPreProcessorConfig(const char *path); - status_t loadEffects(cnode *root, Vector <EffectDesc *>& effects); - EffectDesc *loadEffect(cnode *root); - status_t loadInputSources(cnode *root, const Vector <EffectDesc *>& effects); - audio_source_t inputSourceNameToEnum(const char *name); - InputSourceDesc *loadInputSource(cnode *root, const Vector <EffectDesc *>& effects); - void loadEffectParameters(cnode *root, Vector <effect_param_t *>& params); - effect_param_t *loadEffectParameter(cnode *root); - size_t readParamValue(cnode *node, - char *param, - size_t *curSize, - size_t *totSize); - size_t growParamSize(char *param, - size_t size, - size_t *curSize, - size_t *totSize); - - // Internal dump utilities. - status_t dumpPermissionDenial(int fd); - - - mutable Mutex mLock; // prevents concurrent access to AudioPolicy manager functions changing - // device connection state or routing - sp<AudioCommandThread> mAudioCommandThread; // audio commands thread - sp<AudioCommandThread> mTonePlaybackThread; // tone playback thread - sp<AudioCommandThread> mOutputCommandThread; // process stop and release output - struct audio_policy_device *mpAudioPolicyDev; - struct audio_policy *mpAudioPolicy; - KeyedVector< audio_source_t, InputSourceDesc* > mInputSources; - KeyedVector< audio_io_handle_t, InputDesc* > mInputs; -}; - -}; // namespace android - -#endif // ANDROID_AUDIOPOLICYSERVICE_H diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp index e5cceb1..1f7a613 100644 --- a/services/audioflinger/AudioResampler.cpp +++ b/services/audioflinger/AudioResampler.cpp @@ -22,9 +22,11 @@ #include <sys/types.h> #include <cutils/log.h> #include <cutils/properties.h> +#include <audio_utils/primitives.h> #include "AudioResampler.h" #include "AudioResamplerSinc.h" #include "AudioResamplerCubic.h" +#include "AudioResamplerDyn.h" #ifdef __arm__ #include <machine/cpu-features.h> @@ -39,8 +41,8 @@ namespace android { class AudioResamplerOrder1 : public AudioResampler { public: - AudioResamplerOrder1(int bitDepth, int inChannelCount, int32_t sampleRate) : - AudioResampler(bitDepth, inChannelCount, sampleRate, LOW_QUALITY), mX0L(0), mX0R(0) { + AudioResamplerOrder1(int inChannelCount, int32_t sampleRate) : + AudioResampler(inChannelCount, sampleRate, LOW_QUALITY), mX0L(0), mX0R(0) { } virtual void resample(int32_t* out, size_t outFrameCount, AudioBufferProvider* provider); @@ -77,6 +79,9 @@ private: int mX0R; }; +/*static*/ +const double AudioResampler::kPhaseMultiplier = 1L << AudioResampler::kNumPhaseBits; + bool AudioResampler::qualityIsSupported(src_quality quality) { switch (quality) { @@ -85,6 +90,9 @@ bool AudioResampler::qualityIsSupported(src_quality quality) case MED_QUALITY: case HIGH_QUALITY: case VERY_HIGH_QUALITY: + case DYN_LOW_QUALITY: + case DYN_MED_QUALITY: + case DYN_HIGH_QUALITY: return true; default: return false; @@ -105,7 +113,7 @@ void AudioResampler::init_routine() if (*endptr == '\0') { defaultQuality = (src_quality) l; ALOGD("forcing AudioResampler quality to %d", defaultQuality); - if (defaultQuality < DEFAULT_QUALITY || defaultQuality > VERY_HIGH_QUALITY) { + if (defaultQuality < DEFAULT_QUALITY || defaultQuality > DYN_HIGH_QUALITY) { defaultQuality = DEFAULT_QUALITY; } } @@ -125,6 +133,12 @@ uint32_t AudioResampler::qualityMHz(src_quality quality) return 20; case VERY_HIGH_QUALITY: return 34; + case DYN_LOW_QUALITY: + return 4; + case DYN_MED_QUALITY: + return 6; + case DYN_HIGH_QUALITY: + return 12; } } @@ -132,7 +146,7 @@ static const uint32_t maxMHz = 130; // an arbitrary number that permits 3 VHQ, s static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static uint32_t currentMHz = 0; -AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount, +AudioResampler* AudioResampler::create(audio_format_t format, int inChannelCount, int32_t sampleRate, src_quality quality) { bool atFinalQuality; @@ -148,6 +162,16 @@ AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount, atFinalQuality = true; } + /* if the caller requests DEFAULT_QUALITY and af.resampler.property + * has not been set, the target resampler quality is set to DYN_MED_QUALITY, + * and allowed to "throttle" down to DYN_LOW_QUALITY if necessary + * due to estimated CPU load of having too many active resamplers + * (the code below the if). + */ + if (quality == DEFAULT_QUALITY) { + quality = DYN_MED_QUALITY; + } + // naive implementation of CPU load throttling doesn't account for whether resampler is active pthread_mutex_lock(&mutex); for (;;) { @@ -162,7 +186,6 @@ AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount, // not enough CPU available for proposed quality level, so try next lowest level switch (quality) { default: - case DEFAULT_QUALITY: case LOW_QUALITY: atFinalQuality = true; break; @@ -175,6 +198,15 @@ AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount, case VERY_HIGH_QUALITY: quality = HIGH_QUALITY; break; + case DYN_LOW_QUALITY: + atFinalQuality = true; + break; + case DYN_MED_QUALITY: + quality = DYN_LOW_QUALITY; + break; + case DYN_HIGH_QUALITY: + quality = DYN_MED_QUALITY; + break; } } pthread_mutex_unlock(&mutex); @@ -183,22 +215,43 @@ AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount, switch (quality) { default: - case DEFAULT_QUALITY: case LOW_QUALITY: ALOGV("Create linear Resampler"); - resampler = new AudioResamplerOrder1(bitDepth, inChannelCount, sampleRate); + LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT); + resampler = new AudioResamplerOrder1(inChannelCount, sampleRate); break; case MED_QUALITY: ALOGV("Create cubic Resampler"); - resampler = new AudioResamplerCubic(bitDepth, inChannelCount, sampleRate); + LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT); + resampler = new AudioResamplerCubic(inChannelCount, sampleRate); break; case HIGH_QUALITY: ALOGV("Create HIGH_QUALITY sinc Resampler"); - resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate); + LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT); + resampler = new AudioResamplerSinc(inChannelCount, sampleRate); break; case VERY_HIGH_QUALITY: ALOGV("Create VERY_HIGH_QUALITY sinc Resampler = %d", quality); - resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate, quality); + LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT); + resampler = new AudioResamplerSinc(inChannelCount, sampleRate, quality); + break; + case DYN_LOW_QUALITY: + case DYN_MED_QUALITY: + case DYN_HIGH_QUALITY: + ALOGV("Create dynamic Resampler = %d", quality); + if (format == AUDIO_FORMAT_PCM_FLOAT) { + resampler = new AudioResamplerDyn<float, float, float>(inChannelCount, + sampleRate, quality); + } else { + LOG_ALWAYS_FATAL_IF(format != AUDIO_FORMAT_PCM_16_BIT); + if (quality == DYN_HIGH_QUALITY) { + resampler = new AudioResamplerDyn<int32_t, int16_t, int32_t>(inChannelCount, + sampleRate, quality); + } else { + resampler = new AudioResamplerDyn<int16_t, int16_t, int32_t>(inChannelCount, + sampleRate, quality); + } + } break; } @@ -207,26 +260,26 @@ AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount, return resampler; } -AudioResampler::AudioResampler(int bitDepth, int inChannelCount, +AudioResampler::AudioResampler(int inChannelCount, int32_t sampleRate, src_quality quality) : - mBitDepth(bitDepth), mChannelCount(inChannelCount), - mSampleRate(sampleRate), mInSampleRate(sampleRate), mInputIndex(0), - mPhaseFraction(0), mLocalTimeFreq(0), - mPTS(AudioBufferProvider::kInvalidPTS), mQuality(quality) { - // sanity check on format - if ((bitDepth != 16) ||(inChannelCount < 1) || (inChannelCount > 2)) { - ALOGE("Unsupported sample format, %d bits, %d channels", bitDepth, - inChannelCount); - // ALOG_ASSERT(0); + mChannelCount(inChannelCount), + mSampleRate(sampleRate), mInSampleRate(sampleRate), mInputIndex(0), + mPhaseFraction(0), mLocalTimeFreq(0), + mPTS(AudioBufferProvider::kInvalidPTS), mQuality(quality) { + + const int maxChannels = quality < DYN_LOW_QUALITY ? 2 : 8; + if (inChannelCount < 1 + || inChannelCount > maxChannels) { + LOG_ALWAYS_FATAL("Unsupported sample format %d quality %d channels", + quality, inChannelCount); } if (sampleRate <= 0) { - ALOGE("Unsupported sample rate %d Hz", sampleRate); + LOG_ALWAYS_FATAL("Unsupported sample rate %d Hz", sampleRate); } // initialize common members mVolume[0] = mVolume[1] = 0; mBuffer.frameCount = 0; - } AudioResampler::~AudioResampler() { @@ -246,10 +299,12 @@ void AudioResampler::setSampleRate(int32_t inSampleRate) { mPhaseIncrement = (uint32_t)((kPhaseMultiplier * inSampleRate) / mSampleRate); } -void AudioResampler::setVolume(int16_t left, int16_t right) { +void AudioResampler::setVolume(float left, float right) { // TODO: Implement anti-zipper filter - mVolume[0] = left; - mVolume[1] = right; + // convert to U4.12 for internal integer use (round down) + // integer volume values are clamped to 0 to UNITY_GAIN. + mVolume[0] = u4_12_from_float(clampFloatVol(left)); + mVolume[1] = u4_12_from_float(clampFloatVol(right)); } void AudioResampler::setLocalTimeFreq(uint64_t freq) { @@ -305,7 +360,7 @@ void AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount, uint32_t phaseIncrement = mPhaseIncrement; size_t outputIndex = 0; size_t outputSampleCount = outFrameCount * 2; - size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + size_t inFrameCount = getInFrameCountRequired(outFrameCount); // ALOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d", // outFrameCount, inputIndex, phaseFraction, phaseIncrement); @@ -339,8 +394,9 @@ void AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount, out[outputIndex++] += vl * Interp(mX0L, in[0], phaseFraction); out[outputIndex++] += vr * Interp(mX0R, in[1], phaseFraction); Advance(&inputIndex, &phaseFraction, phaseIncrement); - if (outputIndex == outputSampleCount) + if (outputIndex == outputSampleCount) { break; + } } // process input samples @@ -402,7 +458,7 @@ void AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount, uint32_t phaseIncrement = mPhaseIncrement; size_t outputIndex = 0; size_t outputSampleCount = outFrameCount * 2; - size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + size_t inFrameCount = getInFrameCountRequired(outFrameCount); // ALOGE("starting resample %d frames, inputIndex=%d, phaseFraction=%d, phaseIncrement=%d", // outFrameCount, inputIndex, phaseFraction, phaseIncrement); @@ -434,8 +490,9 @@ void AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount, out[outputIndex++] += vl * sample; out[outputIndex++] += vr * sample; Advance(&inputIndex, &phaseFraction, phaseIncrement); - if (outputIndex == outputSampleCount) + if (outputIndex == outputSampleCount) { break; + } } // process input samples @@ -514,6 +571,16 @@ void AudioResamplerOrder1::AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, uint32_t &phaseFraction, uint32_t phaseIncrement) { + (void)maxOutPt; // remove unused parameter warnings + (void)maxInIdx; + (void)outputIndex; + (void)out; + (void)inputIndex; + (void)vl; + (void)vr; + (void)phaseFraction; + (void)phaseIncrement; + (void)in; #define MO_PARAM5 "36" // offset of parameter 5 (outputIndex) asm( @@ -625,6 +692,16 @@ void AudioResamplerOrder1::AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32 size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, uint32_t &phaseFraction, uint32_t phaseIncrement) { + (void)maxOutPt; // remove unused parameter warnings + (void)maxInIdx; + (void)outputIndex; + (void)out; + (void)inputIndex; + (void)vl; + (void)vr; + (void)phaseFraction; + (void)phaseIncrement; + (void)in; #define ST_PARAM5 "40" // offset of parameter 5 (outputIndex) asm( "stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r12, lr}\n" diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h index 33e64ce..cdc6d92 100644 --- a/services/audioflinger/AudioResampler.h +++ b/services/audioflinger/AudioResampler.h @@ -22,6 +22,7 @@ #include <cutils/compiler.h> #include <media/AudioBufferProvider.h> +#include <system/audio.h> namespace android { // ---------------------------------------------------------------------------- @@ -41,16 +42,21 @@ public: MED_QUALITY=2, HIGH_QUALITY=3, VERY_HIGH_QUALITY=4, + DYN_LOW_QUALITY=5, + DYN_MED_QUALITY=6, + DYN_HIGH_QUALITY=7, }; - static AudioResampler* create(int bitDepth, int inChannelCount, + static const float UNITY_GAIN_FLOAT = 1.0f; + + static AudioResampler* create(audio_format_t format, int inChannelCount, int32_t sampleRate, src_quality quality=DEFAULT_QUALITY); virtual ~AudioResampler(); virtual void init() = 0; virtual void setSampleRate(int32_t inSampleRate); - virtual void setVolume(int16_t left, int16_t right); + virtual void setVolume(float left, float right); virtual void setLocalTimeFreq(uint64_t freq); // set the PTS of the next buffer output by the resampler @@ -60,7 +66,7 @@ public: // A mono provider delivers a sequence of samples. // A stereo provider delivers a sequence of interleaved pairs of samples. // Multi-channel providers are not supported. - // In either case, 'out' holds interleaved pairs of fixed-point signed Q19.12. + // In either case, 'out' holds interleaved pairs of fixed-point Q4.27. // That is, for a mono provider, there is an implicit up-channeling. // Since this method accumulates, the caller is responsible for clearing 'out' initially. // FIXME assumes provider is always successful; it should return the actual frame count. @@ -81,9 +87,9 @@ protected: static const uint32_t kPhaseMask = (1LU<<kNumPhaseBits)-1; // multiplier to calculate fixed point phase increment - static const double kPhaseMultiplier = 1L << kNumPhaseBits; + static const double kPhaseMultiplier; - AudioResampler(int bitDepth, int inChannelCount, int32_t sampleRate, src_quality quality); + AudioResampler(int inChannelCount, int32_t sampleRate, src_quality quality); // prevent copying AudioResampler(const AudioResampler&); @@ -91,7 +97,6 @@ protected: int64_t calculateOutputPTS(int outputFrameIndex); - const int32_t mBitDepth; const int32_t mChannelCount; const int32_t mSampleRate; int32_t mInSampleRate; @@ -107,6 +112,47 @@ protected: uint64_t mLocalTimeFreq; int64_t mPTS; + // returns the inFrameCount required to generate outFrameCount frames. + // + // Placed here to be a consistent for all resamplers. + // + // Right now, we use the upper bound without regards to the current state of the + // input buffer using integer arithmetic, as follows: + // + // (static_cast<uint64_t>(outFrameCount)*mInSampleRate + (mSampleRate - 1))/mSampleRate; + // + // The double precision equivalent (float may not be precise enough): + // ceil(static_cast<double>(outFrameCount) * mInSampleRate / mSampleRate); + // + // this relies on the fact that the mPhaseIncrement is rounded down from + // #phases * mInSampleRate/mSampleRate and the fact that Sum(Floor(x)) <= Floor(Sum(x)). + // http://www.proofwiki.org/wiki/Sum_of_Floors_Not_Greater_Than_Floor_of_Sums + // + // (so long as double precision is computed accurately enough to be considered + // greater than or equal to the Floor(x) value in int32_t arithmetic; thus this + // will not necessarily hold for floats). + // + // TODO: + // Greater accuracy and a tight bound is obtained by: + // 1) subtract and adjust for the current state of the AudioBufferProvider buffer. + // 2) using the exact integer formula where (ignoring 64b casting) + // inFrameCount = (mPhaseIncrement * (outFrameCount - 1) + mPhaseFraction) / phaseWrapLimit; + // phaseWrapLimit is the wraparound (1 << kNumPhaseBits), if not specified explicitly. + // + inline size_t getInFrameCountRequired(size_t outFrameCount) { + return (static_cast<uint64_t>(outFrameCount)*mInSampleRate + + (mSampleRate - 1))/mSampleRate; + } + + inline float clampFloatVol(float volume) { + if (volume > UNITY_GAIN_FLOAT) { + return UNITY_GAIN_FLOAT; + } else if (volume >= 0.) { + return volume; + } + return 0.; // NaN or negative volume maps to 0. + } + private: const src_quality mQuality; diff --git a/services/audioflinger/AudioResamplerCubic.cpp b/services/audioflinger/AudioResamplerCubic.cpp index 18e59e9..8f14ff9 100644 --- a/services/audioflinger/AudioResamplerCubic.cpp +++ b/services/audioflinger/AudioResamplerCubic.cpp @@ -60,14 +60,15 @@ void AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount, uint32_t phaseIncrement = mPhaseIncrement; size_t outputIndex = 0; size_t outputSampleCount = outFrameCount * 2; - size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + size_t inFrameCount = getInFrameCountRequired(outFrameCount); // fetch first buffer if (mBuffer.frameCount == 0) { mBuffer.frameCount = inFrameCount; provider->getNextBuffer(&mBuffer, mPTS); - if (mBuffer.raw == NULL) + if (mBuffer.raw == NULL) { return; + } // ALOGW("New buffer: offset=%p, frames=%dn", mBuffer.raw, mBuffer.frameCount); } int16_t *in = mBuffer.i16; @@ -97,8 +98,9 @@ void AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount, mBuffer.frameCount = inFrameCount; provider->getNextBuffer(&mBuffer, calculateOutputPTS(outputIndex / 2)); - if (mBuffer.raw == NULL) + if (mBuffer.raw == NULL) { goto save_state; // ugly, but efficient + } in = mBuffer.i16; // ALOGW("New buffer: offset=%p, frames=%d", mBuffer.raw, mBuffer.frameCount); } @@ -126,14 +128,15 @@ void AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount, uint32_t phaseIncrement = mPhaseIncrement; size_t outputIndex = 0; size_t outputSampleCount = outFrameCount * 2; - size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + size_t inFrameCount = getInFrameCountRequired(outFrameCount); // fetch first buffer if (mBuffer.frameCount == 0) { mBuffer.frameCount = inFrameCount; provider->getNextBuffer(&mBuffer, mPTS); - if (mBuffer.raw == NULL) + if (mBuffer.raw == NULL) { return; + } // ALOGW("New buffer: offset=%p, frames=%d", mBuffer.raw, mBuffer.frameCount); } int16_t *in = mBuffer.i16; @@ -163,8 +166,9 @@ void AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount, mBuffer.frameCount = inFrameCount; provider->getNextBuffer(&mBuffer, calculateOutputPTS(outputIndex / 2)); - if (mBuffer.raw == NULL) + if (mBuffer.raw == NULL) { goto save_state; // ugly, but efficient + } // ALOGW("New buffer: offset=%p, frames=%dn", mBuffer.raw, mBuffer.frameCount); in = mBuffer.i16; } diff --git a/services/audioflinger/AudioResamplerCubic.h b/services/audioflinger/AudioResamplerCubic.h index 203b933..b315da5 100644 --- a/services/audioflinger/AudioResamplerCubic.h +++ b/services/audioflinger/AudioResamplerCubic.h @@ -28,8 +28,8 @@ namespace android { class AudioResamplerCubic : public AudioResampler { public: - AudioResamplerCubic(int bitDepth, int inChannelCount, int32_t sampleRate) : - AudioResampler(bitDepth, inChannelCount, sampleRate, MED_QUALITY) { + AudioResamplerCubic(int inChannelCount, int32_t sampleRate) : + AudioResampler(inChannelCount, sampleRate, MED_QUALITY) { } virtual void resample(int32_t* out, size_t outFrameCount, AudioBufferProvider* provider); diff --git a/services/audioflinger/AudioResamplerDyn.cpp b/services/audioflinger/AudioResamplerDyn.cpp new file mode 100644 index 0000000..0eeb201 --- /dev/null +++ b/services/audioflinger/AudioResamplerDyn.cpp @@ -0,0 +1,621 @@ +/* + * Copyright (C) 2013 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 "AudioResamplerDyn" +//#define LOG_NDEBUG 0 + +#include <malloc.h> +#include <string.h> +#include <stdlib.h> +#include <dlfcn.h> +#include <math.h> + +#include <cutils/compiler.h> +#include <cutils/properties.h> +#include <utils/Debug.h> +#include <utils/Log.h> +#include <audio_utils/primitives.h> + +#include "AudioResamplerFirOps.h" // USE_NEON and USE_INLINE_ASSEMBLY defined here +#include "AudioResamplerFirProcess.h" +#include "AudioResamplerFirProcessNeon.h" +#include "AudioResamplerFirGen.h" // requires math.h +#include "AudioResamplerDyn.h" + +//#define DEBUG_RESAMPLER + +namespace android { + +/* + * InBuffer is a type agnostic input buffer. + * + * Layout of the state buffer for halfNumCoefs=8. + * + * [rrrrrrppppppppnnnnnnnnrrrrrrrrrrrrrrrrrrr.... rrrrrrr] + * S I R + * + * S = mState + * I = mImpulse + * R = mRingFull + * p = past samples, convoluted with the (p)ositive side of sinc() + * n = future samples, convoluted with the (n)egative side of sinc() + * r = extra space for implementing the ring buffer + */ + +template<typename TC, typename TI, typename TO> +AudioResamplerDyn<TC, TI, TO>::InBuffer::InBuffer() + : mState(NULL), mImpulse(NULL), mRingFull(NULL), mStateCount(0) +{ +} + +template<typename TC, typename TI, typename TO> +AudioResamplerDyn<TC, TI, TO>::InBuffer::~InBuffer() +{ + init(); +} + +template<typename TC, typename TI, typename TO> +void AudioResamplerDyn<TC, TI, TO>::InBuffer::init() +{ + free(mState); + mState = NULL; + mImpulse = NULL; + mRingFull = NULL; + mStateCount = 0; +} + +// resizes the state buffer to accommodate the appropriate filter length +template<typename TC, typename TI, typename TO> +void AudioResamplerDyn<TC, TI, TO>::InBuffer::resize(int CHANNELS, int halfNumCoefs) +{ + // calculate desired state size + size_t stateCount = halfNumCoefs * CHANNELS * 2 * kStateSizeMultipleOfFilterLength; + + // check if buffer needs resizing + if (mState + && stateCount == mStateCount + && mRingFull-mState == (ssize_t) (mStateCount-halfNumCoefs*CHANNELS)) { + return; + } + + // create new buffer + TI* state = NULL; + (void)posix_memalign(reinterpret_cast<void**>(&state), 32, stateCount*sizeof(*state)); + memset(state, 0, stateCount*sizeof(*state)); + + // attempt to preserve state + if (mState) { + TI* srcLo = mImpulse - halfNumCoefs*CHANNELS; + TI* srcHi = mImpulse + halfNumCoefs*CHANNELS; + TI* dst = state; + + if (srcLo < mState) { + dst += mState-srcLo; + srcLo = mState; + } + if (srcHi > mState + mStateCount) { + srcHi = mState + mStateCount; + } + memcpy(dst, srcLo, (srcHi - srcLo) * sizeof(*srcLo)); + free(mState); + } + + // set class member vars + mState = state; + mStateCount = stateCount; + mImpulse = state + halfNumCoefs*CHANNELS; // actually one sample greater than needed + mRingFull = state + mStateCount - halfNumCoefs*CHANNELS; +} + +// copy in the input data into the head (impulse+halfNumCoefs) of the buffer. +template<typename TC, typename TI, typename TO> +template<int CHANNELS> +void AudioResamplerDyn<TC, TI, TO>::InBuffer::readAgain(TI*& impulse, const int halfNumCoefs, + const TI* const in, const size_t inputIndex) +{ + TI* head = impulse + halfNumCoefs*CHANNELS; + for (size_t i=0 ; i<CHANNELS ; i++) { + head[i] = in[inputIndex*CHANNELS + i]; + } +} + +// advance the impulse pointer, and load in data into the head (impulse+halfNumCoefs) +template<typename TC, typename TI, typename TO> +template<int CHANNELS> +void AudioResamplerDyn<TC, TI, TO>::InBuffer::readAdvance(TI*& impulse, const int halfNumCoefs, + const TI* const in, const size_t inputIndex) +{ + impulse += CHANNELS; + + if (CC_UNLIKELY(impulse >= mRingFull)) { + const size_t shiftDown = mRingFull - mState - halfNumCoefs*CHANNELS; + memcpy(mState, mState+shiftDown, halfNumCoefs*CHANNELS*2*sizeof(TI)); + impulse -= shiftDown; + } + readAgain<CHANNELS>(impulse, halfNumCoefs, in, inputIndex); +} + +template<typename TC, typename TI, typename TO> +void AudioResamplerDyn<TC, TI, TO>::Constants::set( + int L, int halfNumCoefs, int inSampleRate, int outSampleRate) +{ + int bits = 0; + int lscale = inSampleRate/outSampleRate < 2 ? L - 1 : + static_cast<int>(static_cast<uint64_t>(L)*inSampleRate/outSampleRate); + for (int i=lscale; i; ++bits, i>>=1) + ; + mL = L; + mShift = kNumPhaseBits - bits; + mHalfNumCoefs = halfNumCoefs; +} + +template<typename TC, typename TI, typename TO> +AudioResamplerDyn<TC, TI, TO>::AudioResamplerDyn( + int inChannelCount, int32_t sampleRate, src_quality quality) + : AudioResampler(inChannelCount, sampleRate, quality), + mResampleFunc(0), mFilterSampleRate(0), mFilterQuality(DEFAULT_QUALITY), + mCoefBuffer(NULL) +{ + mVolumeSimd[0] = mVolumeSimd[1] = 0; + // The AudioResampler base class assumes we are always ready for 1:1 resampling. + // We reset mInSampleRate to 0, so setSampleRate() will calculate filters for + // setSampleRate() for 1:1. (May be removed if precalculated filters are used.) + mInSampleRate = 0; + mConstants.set(128, 8, mSampleRate, mSampleRate); // TODO: set better +} + +template<typename TC, typename TI, typename TO> +AudioResamplerDyn<TC, TI, TO>::~AudioResamplerDyn() +{ + free(mCoefBuffer); +} + +template<typename TC, typename TI, typename TO> +void AudioResamplerDyn<TC, TI, TO>::init() +{ + mFilterSampleRate = 0; // always trigger new filter generation + mInBuffer.init(); +} + +template<typename TC, typename TI, typename TO> +void AudioResamplerDyn<TC, TI, TO>::setVolume(float left, float right) +{ + AudioResampler::setVolume(left, right); + if (is_same<TO, float>::value || is_same<TO, double>::value) { + mVolumeSimd[0] = static_cast<TO>(left); + mVolumeSimd[1] = static_cast<TO>(right); + } else { // integer requires scaling to U4_28 (rounding down) + // integer volumes are clamped to 0 to UNITY_GAIN so there + // are no issues with signed overflow. + mVolumeSimd[0] = u4_28_from_float(clampFloatVol(left)); + mVolumeSimd[1] = u4_28_from_float(clampFloatVol(right)); + } +} + +template<typename T> T max(T a, T b) {return a > b ? a : b;} + +template<typename T> T absdiff(T a, T b) {return a > b ? a - b : b - a;} + +template<typename TC, typename TI, typename TO> +void AudioResamplerDyn<TC, TI, TO>::createKaiserFir(Constants &c, + double stopBandAtten, int inSampleRate, int outSampleRate, double tbwCheat) +{ + TC* buf = NULL; + static const double atten = 0.9998; // to avoid ripple overflow + double fcr; + double tbw = firKaiserTbw(c.mHalfNumCoefs, stopBandAtten); + + (void)posix_memalign(reinterpret_cast<void**>(&buf), 32, (c.mL+1)*c.mHalfNumCoefs*sizeof(TC)); + if (inSampleRate < outSampleRate) { // upsample + fcr = max(0.5*tbwCheat - tbw/2, tbw/2); + } else { // downsample + fcr = max(0.5*tbwCheat*outSampleRate/inSampleRate - tbw/2, tbw/2); + } + // create and set filter + firKaiserGen(buf, c.mL, c.mHalfNumCoefs, stopBandAtten, fcr, atten); + c.mFirCoefs = buf; + if (mCoefBuffer) { + free(mCoefBuffer); + } + mCoefBuffer = buf; +#ifdef DEBUG_RESAMPLER + // print basic filter stats + printf("L:%d hnc:%d stopBandAtten:%lf fcr:%lf atten:%lf tbw:%lf\n", + c.mL, c.mHalfNumCoefs, stopBandAtten, fcr, atten, tbw); + // test the filter and report results + double fp = (fcr - tbw/2)/c.mL; + double fs = (fcr + tbw/2)/c.mL; + double passMin, passMax, passRipple; + double stopMax, stopRipple; + testFir(buf, c.mL, c.mHalfNumCoefs, fp, fs, /*passSteps*/ 1000, /*stopSteps*/ 100000, + passMin, passMax, passRipple, stopMax, stopRipple); + printf("passband(%lf, %lf): %.8lf %.8lf %.8lf\n", 0., fp, passMin, passMax, passRipple); + printf("stopband(%lf, %lf): %.8lf %.3lf\n", fs, 0.5, stopMax, stopRipple); +#endif +} + +// recursive gcd. Using objdump, it appears the tail recursion is converted to a while loop. +static int gcd(int n, int m) +{ + if (m == 0) { + return n; + } + return gcd(m, n % m); +} + +static bool isClose(int32_t newSampleRate, int32_t prevSampleRate, + int32_t filterSampleRate, int32_t outSampleRate) +{ + + // different upsampling ratios do not need a filter change. + if (filterSampleRate != 0 + && filterSampleRate < outSampleRate + && newSampleRate < outSampleRate) + return true; + + // check design criteria again if downsampling is detected. + int pdiff = absdiff(newSampleRate, prevSampleRate); + int adiff = absdiff(newSampleRate, filterSampleRate); + + // allow up to 6% relative change increments. + // allow up to 12% absolute change increments (from filter design) + return pdiff < prevSampleRate>>4 && adiff < filterSampleRate>>3; +} + +template<typename TC, typename TI, typename TO> +void AudioResamplerDyn<TC, TI, TO>::setSampleRate(int32_t inSampleRate) +{ + if (mInSampleRate == inSampleRate) { + return; + } + int32_t oldSampleRate = mInSampleRate; + int32_t oldHalfNumCoefs = mConstants.mHalfNumCoefs; + uint32_t oldPhaseWrapLimit = mConstants.mL << mConstants.mShift; + bool useS32 = false; + + mInSampleRate = inSampleRate; + + // TODO: Add precalculated Equiripple filters + + if (mFilterQuality != getQuality() || + !isClose(inSampleRate, oldSampleRate, mFilterSampleRate, mSampleRate)) { + mFilterSampleRate = inSampleRate; + mFilterQuality = getQuality(); + + // Begin Kaiser Filter computation + // + // The quantization floor for S16 is about 96db - 10*log_10(#length) + 3dB. + // Keep the stop band attenuation no greater than 84-85dB for 32 length S16 filters + // + // For s32 we keep the stop band attenuation at the same as 16b resolution, about + // 96-98dB + // + + double stopBandAtten; + double tbwCheat = 1.; // how much we "cheat" into aliasing + int halfLength; + if (mFilterQuality == DYN_HIGH_QUALITY) { + // 32b coefficients, 64 length + useS32 = true; + stopBandAtten = 98.; + if (inSampleRate >= mSampleRate * 4) { + halfLength = 48; + } else if (inSampleRate >= mSampleRate * 2) { + halfLength = 40; + } else { + halfLength = 32; + } + } else if (mFilterQuality == DYN_LOW_QUALITY) { + // 16b coefficients, 16-32 length + useS32 = false; + stopBandAtten = 80.; + if (inSampleRate >= mSampleRate * 4) { + halfLength = 24; + } else if (inSampleRate >= mSampleRate * 2) { + halfLength = 16; + } else { + halfLength = 8; + } + if (inSampleRate <= mSampleRate) { + tbwCheat = 1.05; + } else { + tbwCheat = 1.03; + } + } else { // DYN_MED_QUALITY + // 16b coefficients, 32-64 length + // note: > 64 length filters with 16b coefs can have quantization noise problems + useS32 = false; + stopBandAtten = 84.; + if (inSampleRate >= mSampleRate * 4) { + halfLength = 32; + } else if (inSampleRate >= mSampleRate * 2) { + halfLength = 24; + } else { + halfLength = 16; + } + if (inSampleRate <= mSampleRate) { + tbwCheat = 1.03; + } else { + tbwCheat = 1.01; + } + } + + // determine the number of polyphases in the filterbank. + // for 16b, it is desirable to have 2^(16/2) = 256 phases. + // https://ccrma.stanford.edu/~jos/resample/Relation_Interpolation_Error_Quantization.html + // + // We are a bit more lax on this. + + int phases = mSampleRate / gcd(mSampleRate, inSampleRate); + + // TODO: Once dynamic sample rate change is an option, the code below + // should be modified to execute only when dynamic sample rate change is enabled. + // + // as above, #phases less than 63 is too few phases for accurate linear interpolation. + // we increase the phases to compensate, but more phases means more memory per + // filter and more time to compute the filter. + // + // if we know that the filter will be used for dynamic sample rate changes, + // that would allow us skip this part for fixed sample rate resamplers. + // + while (phases<63) { + phases *= 2; // this code only needed to support dynamic rate changes + } + + if (phases>=256) { // too many phases, always interpolate + phases = 127; + } + + // create the filter + mConstants.set(phases, halfLength, inSampleRate, mSampleRate); + createKaiserFir(mConstants, stopBandAtten, + inSampleRate, mSampleRate, tbwCheat); + } // End Kaiser filter + + // update phase and state based on the new filter. + const Constants& c(mConstants); + mInBuffer.resize(mChannelCount, c.mHalfNumCoefs); + const uint32_t phaseWrapLimit = c.mL << c.mShift; + // try to preserve as much of the phase fraction as possible for on-the-fly changes + mPhaseFraction = static_cast<unsigned long long>(mPhaseFraction) + * phaseWrapLimit / oldPhaseWrapLimit; + mPhaseFraction %= phaseWrapLimit; // should not do anything, but just in case. + mPhaseIncrement = static_cast<uint32_t>(static_cast<uint64_t>(phaseWrapLimit) + * inSampleRate / mSampleRate); + + // determine which resampler to use + // check if locked phase (works only if mPhaseIncrement has no "fractional phase bits") + int locked = (mPhaseIncrement << (sizeof(mPhaseIncrement)*8 - c.mShift)) == 0; + if (locked) { + mPhaseFraction = mPhaseFraction >> c.mShift << c.mShift; // remove fractional phase + } + + // stride is the minimum number of filter coefficients processed per loop iteration. + // We currently only allow a stride of 16 to match with SIMD processing. + // This means that the filter length must be a multiple of 16, + // or half the filter length (mHalfNumCoefs) must be a multiple of 8. + // + // Note: A stride of 2 is achieved with non-SIMD processing. + int stride = ((c.mHalfNumCoefs & 7) == 0) ? 16 : 2; + LOG_ALWAYS_FATAL_IF(stride < 16, "Resampler stride must be 16 or more"); + LOG_ALWAYS_FATAL_IF(mChannelCount < 1 || mChannelCount > 8, + "Resampler channels(%d) must be between 1 to 8", mChannelCount); + // stride 16 (falls back to stride 2 for machines that do not support NEON) + if (locked) { + switch (mChannelCount) { + case 1: + mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<1, true, 16>; + break; + case 2: + mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<2, true, 16>; + break; + case 3: + mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<3, true, 16>; + break; + case 4: + mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<4, true, 16>; + break; + case 5: + mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<5, true, 16>; + break; + case 6: + mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<6, true, 16>; + break; + case 7: + mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<7, true, 16>; + break; + case 8: + mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<8, true, 16>; + break; + } + } else { + switch (mChannelCount) { + case 1: + mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<1, false, 16>; + break; + case 2: + mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<2, false, 16>; + break; + case 3: + mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<3, false, 16>; + break; + case 4: + mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<4, false, 16>; + break; + case 5: + mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<5, false, 16>; + break; + case 6: + mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<6, false, 16>; + break; + case 7: + mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<7, false, 16>; + break; + case 8: + mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<8, false, 16>; + break; + } + } +#ifdef DEBUG_RESAMPLER + printf("channels:%d %s stride:%d %s coef:%d shift:%d\n", + mChannelCount, locked ? "locked" : "interpolated", + stride, useS32 ? "S32" : "S16", 2*c.mHalfNumCoefs, c.mShift); +#endif +} + +template<typename TC, typename TI, typename TO> +void AudioResamplerDyn<TC, TI, TO>::resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider) +{ + (this->*mResampleFunc)(reinterpret_cast<TO*>(out), outFrameCount, provider); +} + +template<typename TC, typename TI, typename TO> +template<int CHANNELS, bool LOCKED, int STRIDE> +void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount, + AudioBufferProvider* provider) +{ + // TODO Mono -> Mono is not supported. OUTPUT_CHANNELS reflects minimum of stereo out. + const int OUTPUT_CHANNELS = (CHANNELS < 2) ? 2 : CHANNELS; + const Constants& c(mConstants); + const TC* const coefs = mConstants.mFirCoefs; + TI* impulse = mInBuffer.getImpulse(); + size_t inputIndex = 0; + uint32_t phaseFraction = mPhaseFraction; + const uint32_t phaseIncrement = mPhaseIncrement; + size_t outputIndex = 0; + size_t outputSampleCount = outFrameCount * OUTPUT_CHANNELS; + const uint32_t phaseWrapLimit = c.mL << c.mShift; + size_t inFrameCount = (phaseIncrement * (uint64_t)outFrameCount + phaseFraction) + / phaseWrapLimit; + // sanity check that inFrameCount is in signed 32 bit integer range. + ALOG_ASSERT(0 <= inFrameCount && inFrameCount < (1U << 31)); + + //ALOGV("inFrameCount:%d outFrameCount:%d" + // " phaseIncrement:%u phaseFraction:%u phaseWrapLimit:%u", + // inFrameCount, outFrameCount, phaseIncrement, phaseFraction, phaseWrapLimit); + + // NOTE: be very careful when modifying the code here. register + // pressure is very high and a small change might cause the compiler + // to generate far less efficient code. + // Always sanity check the result with objdump or test-resample. + + // the following logic is a bit convoluted to keep the main processing loop + // as tight as possible with register allocation. + while (outputIndex < outputSampleCount) { + //ALOGV("LOOP: inFrameCount:%d outputIndex:%d outFrameCount:%d" + // " phaseFraction:%u phaseWrapLimit:%u", + // inFrameCount, outputIndex, outFrameCount, phaseFraction, phaseWrapLimit); + + // check inputIndex overflow + ALOG_ASSERT(inputIndex <= mBuffer.frameCount, "inputIndex%d > frameCount%d", + inputIndex, mBuffer.frameCount); + // Buffer is empty, fetch a new one if necessary (inFrameCount > 0). + // We may not fetch a new buffer if the existing data is sufficient. + while (mBuffer.frameCount == 0 && inFrameCount > 0) { + mBuffer.frameCount = inFrameCount; + provider->getNextBuffer(&mBuffer, + calculateOutputPTS(outputIndex / OUTPUT_CHANNELS)); + if (mBuffer.raw == NULL) { + goto resample_exit; + } + inFrameCount -= mBuffer.frameCount; + if (phaseFraction >= phaseWrapLimit) { // read in data + mInBuffer.template readAdvance<CHANNELS>( + impulse, c.mHalfNumCoefs, + reinterpret_cast<TI*>(mBuffer.raw), inputIndex); + inputIndex++; + phaseFraction -= phaseWrapLimit; + while (phaseFraction >= phaseWrapLimit) { + if (inputIndex >= mBuffer.frameCount) { + inputIndex = 0; + provider->releaseBuffer(&mBuffer); + break; + } + mInBuffer.template readAdvance<CHANNELS>( + impulse, c.mHalfNumCoefs, + reinterpret_cast<TI*>(mBuffer.raw), inputIndex); + inputIndex++; + phaseFraction -= phaseWrapLimit; + } + } + } + const TI* const in = reinterpret_cast<const TI*>(mBuffer.raw); + const size_t frameCount = mBuffer.frameCount; + const int coefShift = c.mShift; + const int halfNumCoefs = c.mHalfNumCoefs; + const TO* const volumeSimd = mVolumeSimd; + + // main processing loop + while (CC_LIKELY(outputIndex < outputSampleCount)) { + // caution: fir() is inlined and may be large. + // output will be loaded with the appropriate values + // + // from the input samples in impulse[-halfNumCoefs+1]... impulse[halfNumCoefs] + // from the polyphase filter of (phaseFraction / phaseWrapLimit) in coefs. + // + //ALOGV("LOOP2: inFrameCount:%d outputIndex:%d outFrameCount:%d" + // " phaseFraction:%u phaseWrapLimit:%u", + // inFrameCount, outputIndex, outFrameCount, phaseFraction, phaseWrapLimit); + ALOG_ASSERT(phaseFraction < phaseWrapLimit); + fir<CHANNELS, LOCKED, STRIDE>( + &out[outputIndex], + phaseFraction, phaseWrapLimit, + coefShift, halfNumCoefs, coefs, + impulse, volumeSimd); + + outputIndex += OUTPUT_CHANNELS; + + phaseFraction += phaseIncrement; + while (phaseFraction >= phaseWrapLimit) { + if (inputIndex >= frameCount) { + goto done; // need a new buffer + } + mInBuffer.template readAdvance<CHANNELS>(impulse, halfNumCoefs, in, inputIndex); + inputIndex++; + phaseFraction -= phaseWrapLimit; + } + } +done: + // We arrive here when we're finished or when the input buffer runs out. + // Regardless we need to release the input buffer if we've acquired it. + if (inputIndex > 0) { // we've acquired a buffer (alternatively could check frameCount) + ALOG_ASSERT(inputIndex == frameCount, "inputIndex(%d) != frameCount(%d)", + inputIndex, frameCount); // must have been fully read. + inputIndex = 0; + provider->releaseBuffer(&mBuffer); + ALOG_ASSERT(mBuffer.frameCount == 0); + } + } + +resample_exit: + // inputIndex must be zero in all three cases: + // (1) the buffer never was been acquired; (2) the buffer was + // released at "done:"; or (3) getNextBuffer() failed. + ALOG_ASSERT(inputIndex == 0, "Releasing: inputindex:%d frameCount:%d phaseFraction:%u", + inputIndex, mBuffer.frameCount, phaseFraction); + ALOG_ASSERT(mBuffer.frameCount == 0); // there must be no frames in the buffer + mInBuffer.setImpulse(impulse); + mPhaseFraction = phaseFraction; +} + +/* instantiate templates used by AudioResampler::create */ +template class AudioResamplerDyn<float, float, float>; +template class AudioResamplerDyn<int16_t, int16_t, int32_t>; +template class AudioResamplerDyn<int32_t, int16_t, int32_t>; + +// ---------------------------------------------------------------------------- +}; // namespace android diff --git a/services/audioflinger/AudioResamplerDyn.h b/services/audioflinger/AudioResamplerDyn.h new file mode 100644 index 0000000..e886a68 --- /dev/null +++ b/services/audioflinger/AudioResamplerDyn.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2013 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_RESAMPLER_DYN_H +#define ANDROID_AUDIO_RESAMPLER_DYN_H + +#include <stdint.h> +#include <sys/types.h> +#include <cutils/log.h> + +#include "AudioResampler.h" + +namespace android { + +/* AudioResamplerDyn + * + * This class template is used for floating point and integer resamplers. + * + * Type variables: + * TC = filter coefficient type (one of int16_t, int32_t, or float) + * TI = input data type (one of int16_t or float) + * TO = output data type (one of int32_t or float) + * + * For integer input data types TI, the coefficient type TC is either int16_t or int32_t. + * For float input data types TI, the coefficient type TC is float. + */ + +template<typename TC, typename TI, typename TO> +class AudioResamplerDyn: public AudioResampler { +public: + AudioResamplerDyn(int inChannelCount, + int32_t sampleRate, src_quality quality); + + virtual ~AudioResamplerDyn(); + + virtual void init(); + + virtual void setSampleRate(int32_t inSampleRate); + + virtual void setVolume(float left, float right); + + virtual void resample(int32_t* out, size_t outFrameCount, + AudioBufferProvider* provider); + +private: + + class Constants { // stores the filter constants. + public: + Constants() : + mL(0), mShift(0), mHalfNumCoefs(0), mFirCoefs(NULL) + {} + void set(int L, int halfNumCoefs, + int inSampleRate, int outSampleRate); + + int mL; // interpolation phases in the filter. + int mShift; // right shift to get polyphase index + unsigned int mHalfNumCoefs; // filter half #coefs + const TC* mFirCoefs; // polyphase filter bank + }; + + class InBuffer { // buffer management for input type TI + public: + InBuffer(); + ~InBuffer(); + void init(); + + void resize(int CHANNELS, int halfNumCoefs); + + // used for direct management of the mImpulse pointer + inline TI* getImpulse() { + return mImpulse; + } + + inline void setImpulse(TI *impulse) { + mImpulse = impulse; + } + + template<int CHANNELS> + inline void readAgain(TI*& impulse, const int halfNumCoefs, + const TI* const in, const size_t inputIndex); + + template<int CHANNELS> + inline void readAdvance(TI*& impulse, const int halfNumCoefs, + const TI* const in, const size_t inputIndex); + + private: + // tuning parameter guidelines: 2 <= multiple <= 8 + static const int kStateSizeMultipleOfFilterLength = 4; + + // in general, mRingFull = mState + mStateSize - halfNumCoefs*CHANNELS. + TI* mState; // base pointer for the input buffer storage + TI* mImpulse; // current location of the impulse response (centered) + TI* mRingFull; // mState <= mImpulse < mRingFull + size_t mStateCount; // size of state in units of TI. + }; + + void createKaiserFir(Constants &c, double stopBandAtten, + int inSampleRate, int outSampleRate, double tbwCheat); + + template<int CHANNELS, bool LOCKED, int STRIDE> + void resample(TO* out, size_t outFrameCount, AudioBufferProvider* provider); + + // define a pointer to member function type for resample + typedef void (AudioResamplerDyn<TC, TI, TO>::*resample_ABP_t)(TO* out, + size_t outFrameCount, AudioBufferProvider* provider); + + // data - the contiguous storage and layout of these is important. + InBuffer mInBuffer; + Constants mConstants; // current set of coefficient parameters + TO __attribute__ ((aligned (8))) mVolumeSimd[2]; // must be aligned or NEON may crash + resample_ABP_t mResampleFunc; // called function for resampling + int32_t mFilterSampleRate; // designed filter sample rate. + src_quality mFilterQuality; // designed filter quality. + void* mCoefBuffer; // if a filter is created, this is not null +}; + +}; // namespace android + +#endif /*ANDROID_AUDIO_RESAMPLER_DYN_H*/ diff --git a/services/audioflinger/AudioResamplerFirGen.h b/services/audioflinger/AudioResamplerFirGen.h new file mode 100644 index 0000000..d024b2f --- /dev/null +++ b/services/audioflinger/AudioResamplerFirGen.h @@ -0,0 +1,709 @@ +/* + * Copyright (C) 2013 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_RESAMPLER_FIR_GEN_H +#define ANDROID_AUDIO_RESAMPLER_FIR_GEN_H + +namespace android { + +/* + * generates a sine wave at equal steps. + * + * As most of our functions use sine or cosine at equal steps, + * it is very efficient to compute them that way (single multiply and subtract), + * rather than invoking the math library sin() or cos() each time. + * + * SineGen uses Goertzel's Algorithm (as a generator not a filter) + * to calculate sine(wstart + n * wstep) or cosine(wstart + n * wstep) + * by stepping through 0, 1, ... n. + * + * e^i(wstart+wstep) = 2cos(wstep) * e^i(wstart) - e^i(wstart-wstep) + * + * or looking at just the imaginary sine term, as the cosine follows identically: + * + * sin(wstart+wstep) = 2cos(wstep) * sin(wstart) - sin(wstart-wstep) + * + * Goertzel's algorithm is more efficient than the angle addition formula, + * e^i(wstart+wstep) = e^i(wstart) * e^i(wstep), which takes up to + * 4 multiplies and 2 adds (or 3* and 3+) and requires both sine and + * cosine generation due to the complex * complex multiply (full rotation). + * + * See: http://en.wikipedia.org/wiki/Goertzel_algorithm + * + */ + +class SineGen { +public: + SineGen(double wstart, double wstep, bool cosine = false) { + if (cosine) { + mCurrent = cos(wstart); + mPrevious = cos(wstart - wstep); + } else { + mCurrent = sin(wstart); + mPrevious = sin(wstart - wstep); + } + mTwoCos = 2.*cos(wstep); + } + SineGen(double expNow, double expPrev, double twoCosStep) { + mCurrent = expNow; + mPrevious = expPrev; + mTwoCos = twoCosStep; + } + inline double value() const { + return mCurrent; + } + inline void advance() { + double tmp = mCurrent; + mCurrent = mCurrent*mTwoCos - mPrevious; + mPrevious = tmp; + } + inline double valueAdvance() { + double tmp = mCurrent; + mCurrent = mCurrent*mTwoCos - mPrevious; + mPrevious = tmp; + return tmp; + } + +private: + double mCurrent; // current value of sine/cosine + double mPrevious; // previous value of sine/cosine + double mTwoCos; // stepping factor +}; + +/* + * generates a series of sine generators, phase offset by fixed steps. + * + * This is used to generate polyphase sine generators, one per polyphase + * in the filter code below. + * + * The SineGen returned by value() starts at innerStart = outerStart + n*outerStep; + * increments by innerStep. + * + */ + +class SineGenGen { +public: + SineGenGen(double outerStart, double outerStep, double innerStep, bool cosine = false) + : mSineInnerCur(outerStart, outerStep, cosine), + mSineInnerPrev(outerStart-innerStep, outerStep, cosine) + { + mTwoCos = 2.*cos(innerStep); + } + inline SineGen value() { + return SineGen(mSineInnerCur.value(), mSineInnerPrev.value(), mTwoCos); + } + inline void advance() { + mSineInnerCur.advance(); + mSineInnerPrev.advance(); + } + inline SineGen valueAdvance() { + return SineGen(mSineInnerCur.valueAdvance(), mSineInnerPrev.valueAdvance(), mTwoCos); + } + +private: + SineGen mSineInnerCur; // generate the inner sine values (stepped by outerStep). + SineGen mSineInnerPrev; // generate the inner sine previous values + // (behind by innerStep, stepped by outerStep). + double mTwoCos; // the inner stepping factor for the returned SineGen. +}; + +static inline double sqr(double x) { + return x * x; +} + +/* + * rounds a double to the nearest integer for FIR coefficients. + * + * One variant uses noise shaping, which must keep error history + * to work (the err parameter, initialized to 0). + * The other variant is a non-noise shaped version for + * S32 coefficients (noise shaping doesn't gain much). + * + * Caution: No bounds saturation is applied, but isn't needed in this case. + * + * @param x is the value to round. + * + * @param maxval is the maximum integer scale factor expressed as an int64 (for headroom). + * Typically this may be the maximum positive integer+1 (using the fact that double precision + * FIR coefficients generated here are never that close to 1.0 to pose an overflow condition). + * + * @param err is the previous error (actual - rounded) for the previous rounding op. + * For 16b coefficients this can improve stopband dB performance by up to 2dB. + * + * Many variants exist for the noise shaping: http://en.wikipedia.org/wiki/Noise_shaping + * + */ + +static inline int64_t toint(double x, int64_t maxval, double& err) { + double val = x * maxval; + double ival = floor(val + 0.5 + err*0.2); + err = val - ival; + return static_cast<int64_t>(ival); +} + +static inline int64_t toint(double x, int64_t maxval) { + return static_cast<int64_t>(floor(x * maxval + 0.5)); +} + +/* + * Modified Bessel function of the first kind + * http://en.wikipedia.org/wiki/Bessel_function + * + * The formulas are taken from Abramowitz and Stegun, + * _Handbook of Mathematical Functions_ (links below): + * + * http://people.math.sfu.ca/~cbm/aands/page_375.htm + * http://people.math.sfu.ca/~cbm/aands/page_378.htm + * + * http://dlmf.nist.gov/10.25 + * http://dlmf.nist.gov/10.40 + * + * Note we assume x is nonnegative (the function is symmetric, + * pass in the absolute value as needed). + * + * Constants are compile time derived with templates I0Term<> and + * I0ATerm<> to the precision of the compiler. The series can be expanded + * to any precision needed, but currently set around 24b precision. + * + * We use a bit of template math here, constexpr would probably be + * more appropriate for a C++11 compiler. + * + * For the intermediate range 3.75 < x < 15, we use minimax polynomial fit. + * + */ + +template <int N> +struct I0Term { + static const double value = I0Term<N-1>::value / (4. * N * N); +}; + +template <> +struct I0Term<0> { + static const double value = 1.; +}; + +template <int N> +struct I0ATerm { + static const double value = I0ATerm<N-1>::value * (2.*N-1.) * (2.*N-1.) / (8. * N); +}; + +template <> +struct I0ATerm<0> { // 1/sqrt(2*PI); + static const double value = 0.398942280401432677939946059934381868475858631164934657665925; +}; + +#if USE_HORNERS_METHOD +/* Polynomial evaluation of A + Bx + Cx^2 + Dx^3 + ... + * using Horner's Method: http://en.wikipedia.org/wiki/Horner's_method + * + * This has fewer multiplications than Estrin's method below, but has back to back + * floating point dependencies. + * + * On ARM this appears to work slower, so USE_HORNERS_METHOD is not default enabled. + */ + +inline double Poly2(double A, double B, double x) { + return A + x * B; +} + +inline double Poly4(double A, double B, double C, double D, double x) { + return A + x * (B + x * (C + x * (D))); +} + +inline double Poly7(double A, double B, double C, double D, double E, double F, double G, + double x) { + return A + x * (B + x * (C + x * (D + x * (E + x * (F + x * (G)))))); +} + +inline double Poly9(double A, double B, double C, double D, double E, double F, double G, + double H, double I, double x) { + return A + x * (B + x * (C + x * (D + x * (E + x * (F + x * (G + x * (H + x * (I)))))))); +} + +#else +/* Polynomial evaluation of A + Bx + Cx^2 + Dx^3 + ... + * using Estrin's Method: http://en.wikipedia.org/wiki/Estrin's_scheme + * + * This is typically faster, perhaps gains about 5-10% overall on ARM processors + * over Horner's method above. + */ + +inline double Poly2(double A, double B, double x) { + return A + B * x; +} + +inline double Poly3(double A, double B, double C, double x, double x2) { + return Poly2(A, B, x) + C * x2; +} + +inline double Poly3(double A, double B, double C, double x) { + return Poly2(A, B, x) + C * x * x; +} + +inline double Poly4(double A, double B, double C, double D, double x, double x2) { + return Poly2(A, B, x) + Poly2(C, D, x) * x2; // same as poly2(poly2, poly2, x2); +} + +inline double Poly4(double A, double B, double C, double D, double x) { + return Poly4(A, B, C, D, x, x * x); +} + +inline double Poly7(double A, double B, double C, double D, double E, double F, double G, + double x) { + double x2 = x * x; + return Poly4(A, B, C, D, x, x2) + Poly3(E, F, G, x, x2) * (x2 * x2); +} + +inline double Poly8(double A, double B, double C, double D, double E, double F, double G, + double H, double x, double x2, double x4) { + return Poly4(A, B, C, D, x, x2) + Poly4(E, F, G, H, x, x2) * x4; +} + +inline double Poly9(double A, double B, double C, double D, double E, double F, double G, + double H, double I, double x) { + double x2 = x * x; +#if 1 + // It does not seem faster to explicitly decompose Poly8 into Poly4, but + // could depend on compiler floating point scheduling. + double x4 = x2 * x2; + return Poly8(A, B, C, D, E, F, G, H, x, x2, x4) + I * (x4 * x4); +#else + double val = Poly4(A, B, C, D, x, x2); + double x4 = x2 * x2; + return val + Poly4(E, F, G, H, x, x2) * x4 + I * (x4 * x4); +#endif +} +#endif + +static inline double I0(double x) { + if (x < 3.75) { + x *= x; + return Poly7(I0Term<0>::value, I0Term<1>::value, + I0Term<2>::value, I0Term<3>::value, + I0Term<4>::value, I0Term<5>::value, + I0Term<6>::value, x); // e < 1.6e-7 + } + if (1) { + /* + * Series expansion coefs are easy to calculate, but are expanded around 0, + * so error is unequal over the interval 0 < x < 3.75, the error being + * significantly better near 0. + * + * A better solution is to use precise minimax polynomial fits. + * + * We use a slightly more complicated solution for 3.75 < x < 15, based on + * the tables in Blair and Edwards, "Stable Rational Minimax Approximations + * to the Modified Bessel Functions I0(x) and I1(x)", Chalk Hill Nuclear Laboratory, + * AECL-4928. + * + * http://www.iaea.org/inis/collection/NCLCollectionStore/_Public/06/178/6178667.pdf + * + * See Table 11 for 0 < x < 15; e < 10^(-7.13). + * + * Note: Beta cannot exceed 15 (hence Stopband cannot exceed 144dB = 24b). + * + * This speeds up overall computation by about 40% over using the else clause below, + * which requires sqrt and exp. + * + */ + + x *= x; + double num = Poly9(-0.13544938430e9, -0.33153754512e8, + -0.19406631946e7, -0.48058318783e5, + -0.63269783360e3, -0.49520779070e1, + -0.24970910370e-1, -0.74741159550e-4, + -0.18257612460e-6, x); + double y = x - 225.; // reflection around 15 (squared) + double den = Poly4(-0.34598737196e8, 0.23852643181e6, + -0.70699387620e3, 0.10000000000e1, y); + return num / den; + +#if IO_EXTENDED_BETA + /* Table 42 for x > 15; e < 10^(-8.11). + * This is used for Beta>15, but is disabled here as + * we never use Beta that high. + * + * NOTE: This should be enabled only for x > 15. + */ + + double y = 1./x; + double z = y - (1./15); + double num = Poly2(0.415079861746e1, -0.5149092496e1, z); + double den = Poly3(0.103150763823e2, -0.14181687413e2, + 0.1000000000e1, z); + return exp(x) * sqrt(y) * num / den; +#endif + } else { + /* + * NOT USED, but reference for large Beta. + * + * Abramowitz and Stegun asymptotic formula. + * works for x > 3.75. + */ + double y = 1./x; + return exp(x) * sqrt(y) * + // note: reciprocal squareroot may be easier! + // http://en.wikipedia.org/wiki/Fast_inverse_square_root + Poly9(I0ATerm<0>::value, I0ATerm<1>::value, + I0ATerm<2>::value, I0ATerm<3>::value, + I0ATerm<4>::value, I0ATerm<5>::value, + I0ATerm<6>::value, I0ATerm<7>::value, + I0ATerm<8>::value, y); // (... e) < 1.9e-7 + } +} + +/* A speed optimized version of the Modified Bessel I0() which incorporates + * the sqrt and numerator multiply and denominator divide into the computation. + * This speeds up filter computation by about 10-15%. + */ +static inline double I0SqrRat(double x2, double num, double den) { + if (x2 < (3.75 * 3.75)) { + return Poly7(I0Term<0>::value, I0Term<1>::value, + I0Term<2>::value, I0Term<3>::value, + I0Term<4>::value, I0Term<5>::value, + I0Term<6>::value, x2) * num / den; // e < 1.6e-7 + } + num *= Poly9(-0.13544938430e9, -0.33153754512e8, + -0.19406631946e7, -0.48058318783e5, + -0.63269783360e3, -0.49520779070e1, + -0.24970910370e-1, -0.74741159550e-4, + -0.18257612460e-6, x2); // e < 10^(-7.13). + double y = x2 - 225.; // reflection around 15 (squared) + den *= Poly4(-0.34598737196e8, 0.23852643181e6, + -0.70699387620e3, 0.10000000000e1, y); + return num / den; +} + +/* + * calculates the transition bandwidth for a Kaiser filter + * + * Formula 3.2.8, Vaidyanathan, _Multirate Systems and Filter Banks_, p. 48 + * Formula 7.76, Oppenheim and Schafer, _Discrete-time Signal Processing, 3e_, p. 542 + * + * @param halfNumCoef is half the number of coefficients per filter phase. + * + * @param stopBandAtten is the stop band attenuation desired. + * + * @return the transition bandwidth in normalized frequency (0 <= f <= 0.5) + */ +static inline double firKaiserTbw(int halfNumCoef, double stopBandAtten) { + return (stopBandAtten - 7.95)/((2.*14.36)*halfNumCoef); +} + +/* + * calculates the fir transfer response of the overall polyphase filter at w. + * + * Calculates the DTFT transfer coefficient H(w) for 0 <= w <= PI, utilizing the + * fact that h[n] is symmetric (cosines only, no complex arithmetic). + * + * We use Goertzel's algorithm to accelerate the computation to essentially + * a single multiply and 2 adds per filter coefficient h[]. + * + * Be careful be careful to consider that h[n] is the overall polyphase filter, + * with L phases, so rescaling H(w)/L is probably what you expect for "unity gain", + * as you only use one of the polyphases at a time. + */ +template <typename T> +static inline double firTransfer(const T* coef, int L, int halfNumCoef, double w) { + double accum = static_cast<double>(coef[0])*0.5; // "center coefficient" from first bank + coef += halfNumCoef; // skip first filterbank (picked up by the last filterbank). +#if SLOW_FIRTRANSFER + /* Original code for reference. This is equivalent to the code below, but slower. */ + for (int i=1 ; i<=L ; ++i) { + for (int j=0, ix=i ; j<halfNumCoef ; ++j, ix+=L) { + accum += cos(ix*w)*static_cast<double>(*coef++); + } + } +#else + /* + * Our overall filter is stored striped by polyphases, not a contiguous h[n]. + * We could fetch coefficients in a non-contiguous fashion + * but that will not scale to vector processing. + * + * We apply Goertzel's algorithm directly to each polyphase filter bank instead of + * using cosine generation/multiplication, thereby saving one multiply per inner loop. + * + * See: http://en.wikipedia.org/wiki/Goertzel_algorithm + * Also: Oppenheim and Schafer, _Discrete Time Signal Processing, 3e_, p. 720. + * + * We use the basic recursion to incorporate the cosine steps into real sequence x[n]: + * s[n] = x[n] + (2cosw)*s[n-1] + s[n-2] + * + * y[n] = s[n] - e^(iw)s[n-1] + * = sum_{k=-\infty}^{n} x[k]e^(-iw(n-k)) + * = e^(-iwn) sum_{k=0}^{n} x[k]e^(iwk) + * + * The summation contains the frequency steps we want multiplied by the source + * (similar to a DTFT). + * + * Using symmetry, and just the real part (be careful, this must happen + * after any internal complex multiplications), the polyphase filterbank + * transfer function is: + * + * Hpp[n, w, w_0] = sum_{k=0}^{n} x[k] * cos(wk + w_0) + * = Re{ e^(iwn + iw_0) y[n]} + * = cos(wn+w_0) * s[n] - cos(w(n+1)+w_0) * s[n-1] + * + * using the fact that s[n] of real x[n] is real. + * + */ + double dcos = 2. * cos(L*w); + int start = ((halfNumCoef)*L + 1); + SineGen cc((start - L) * w, w, true); // cosine + SineGen cp(start * w, w, true); // cosine + for (int i=1 ; i<=L ; ++i) { + double sc = 0; + double sp = 0; + for (int j=0 ; j<halfNumCoef ; ++j) { + double tmp = sc; + sc = static_cast<double>(*coef++) + dcos*sc - sp; + sp = tmp; + } + // If we are awfully clever, we can apply Goertzel's algorithm + // again on the sc and sp sequences returned here. + accum += cc.valueAdvance() * sc - cp.valueAdvance() * sp; + } +#endif + return accum*2.; +} + +/* + * evaluates the minimum and maximum |H(f)| bound in a band region. + * + * This is usually done with equally spaced increments in the target band in question. + * The passband is often very small, and sampled that way. The stopband is often much + * larger. + * + * We use the fact that the overall polyphase filter has an additional bank at the end + * for interpolation; hence it is overspecified for the H(f) computation. Thus the + * first polyphase is never actually checked, excepting its first term. + * + * In this code we use the firTransfer() evaluator above, which uses Goertzel's + * algorithm to calculate the transfer function at each point. + * + * TODO: An alternative with equal spacing is the FFT/DFT. An alternative with unequal + * spacing is a chirp transform. + * + * @param coef is the designed polyphase filter banks + * + * @param L is the number of phases (for interpolation) + * + * @param halfNumCoef should be half the number of coefficients for a single + * polyphase. + * + * @param fstart is the normalized frequency start. + * + * @param fend is the normalized frequency end. + * + * @param steps is the number of steps to take (sampling) between frequency start and end + * + * @param firMin returns the minimum transfer |H(f)| found + * + * @param firMax returns the maximum transfer |H(f)| found + * + * 0 <= f <= 0.5. + * This is used to test passband and stopband performance. + */ +template <typename T> +static void testFir(const T* coef, int L, int halfNumCoef, + double fstart, double fend, int steps, double &firMin, double &firMax) { + double wstart = fstart*(2.*M_PI); + double wend = fend*(2.*M_PI); + double wstep = (wend - wstart)/steps; + double fmax, fmin; + double trf = firTransfer(coef, L, halfNumCoef, wstart); + if (trf<0) { + trf = -trf; + } + fmin = fmax = trf; + wstart += wstep; + for (int i=1; i<steps; ++i) { + trf = firTransfer(coef, L, halfNumCoef, wstart); + if (trf<0) { + trf = -trf; + } + if (trf>fmax) { + fmax = trf; + } + else if (trf<fmin) { + fmin = trf; + } + wstart += wstep; + } + // renormalize - this is only needed for integer filter types + double norm = 1./((1ULL<<(sizeof(T)*8-1))*L); + + firMin = fmin * norm; + firMax = fmax * norm; +} + +/* + * evaluates the |H(f)| lowpass band characteristics. + * + * This function tests the lowpass characteristics for the overall polyphase filter, + * and is used to verify the design. For this case, fp should be set to the + * passband normalized frequency from 0 to 0.5 for the overall filter (thus it + * is the designed polyphase bank value / L). Likewise for fs. + * + * @param coef is the designed polyphase filter banks + * + * @param L is the number of phases (for interpolation) + * + * @param halfNumCoef should be half the number of coefficients for a single + * polyphase. + * + * @param fp is the passband normalized frequency, 0 < fp < fs < 0.5. + * + * @param fs is the stopband normalized frequency, 0 < fp < fs < 0.5. + * + * @param passSteps is the number of passband sampling steps. + * + * @param stopSteps is the number of stopband sampling steps. + * + * @param passMin is the minimum value in the passband + * + * @param passMax is the maximum value in the passband (useful for scaling). This should + * be less than 1., to avoid sine wave test overflow. + * + * @param passRipple is the passband ripple. Typically this should be less than 0.1 for + * an audio filter. Generally speaker/headphone device characteristics will dominate + * the passband term. + * + * @param stopMax is the maximum value in the stopband. + * + * @param stopRipple is the stopband ripple, also known as stopband attenuation. + * Typically this should be greater than ~80dB for low quality, and greater than + * ~100dB for full 16b quality, otherwise aliasing may become noticeable. + * + */ +template <typename T> +static void testFir(const T* coef, int L, int halfNumCoef, + double fp, double fs, int passSteps, int stopSteps, + double &passMin, double &passMax, double &passRipple, + double &stopMax, double &stopRipple) { + double fmin, fmax; + testFir(coef, L, halfNumCoef, 0., fp, passSteps, fmin, fmax); + double d1 = (fmax - fmin)/2.; + passMin = fmin; + passMax = fmax; + passRipple = -20.*log10(1. - d1); // passband ripple + testFir(coef, L, halfNumCoef, fs, 0.5, stopSteps, fmin, fmax); + // fmin is really not important for the stopband. + stopMax = fmax; + stopRipple = -20.*log10(fmax); // stopband ripple/attenuation +} + +/* + * Calculates the overall polyphase filter based on a windowed sinc function. + * + * The windowed sinc is an odd length symmetric filter of exactly L*halfNumCoef*2+1 + * taps for the entire kernel. This is then decomposed into L+1 polyphase filterbanks. + * The last filterbank is used for interpolation purposes (and is mostly composed + * of the first bank shifted by one sample), and is unnecessary if one does + * not do interpolation. + * + * We use the last filterbank for some transfer function calculation purposes, + * so it needs to be generated anyways. + * + * @param coef is the caller allocated space for coefficients. This should be + * exactly (L+1)*halfNumCoef in size. + * + * @param L is the number of phases (for interpolation) + * + * @param halfNumCoef should be half the number of coefficients for a single + * polyphase. + * + * @param stopBandAtten is the stopband value, should be >50dB. + * + * @param fcr is cutoff frequency/sampling rate (<0.5). At this point, the energy + * should be 6dB less. (fcr is where the amplitude drops by half). Use the + * firKaiserTbw() to calculate the transition bandwidth. fcr is the midpoint + * between the stop band and the pass band (fstop+fpass)/2. + * + * @param atten is the attenuation (generally slightly less than 1). + */ + +template <typename T> +static inline void firKaiserGen(T* coef, int L, int halfNumCoef, + double stopBandAtten, double fcr, double atten) { + // + // Formula 3.2.5, 3.2.7, Vaidyanathan, _Multirate Systems and Filter Banks_, p. 48 + // Formula 7.75, Oppenheim and Schafer, _Discrete-time Signal Processing, 3e_, p. 542 + // + // See also: http://melodi.ee.washington.edu/courses/ee518/notes/lec17.pdf + // + // Kaiser window and beta parameter + // + // | 0.1102*(A - 8.7) A > 50 + // beta = | 0.5842*(A - 21)^0.4 + 0.07886*(A - 21) 21 <= A <= 50 + // | 0. A < 21 + // + // with A is the desired stop-band attenuation in dBFS + // + // 30 dB 2.210 + // 40 dB 3.384 + // 50 dB 4.538 + // 60 dB 5.658 + // 70 dB 6.764 + // 80 dB 7.865 + // 90 dB 8.960 + // 100 dB 10.056 + + const int N = L * halfNumCoef; // non-negative half + const double beta = 0.1102 * (stopBandAtten - 8.7); // >= 50dB always + const double xstep = (2. * M_PI) * fcr / L; + const double xfrac = 1. / N; + const double yscale = atten * L / (I0(beta) * M_PI); + const double sqrbeta = sqr(beta); + + // We use sine generators, which computes sines on regular step intervals. + // This speeds up overall computation about 40% from computing the sine directly. + + SineGenGen sgg(0., xstep, L*xstep); // generates sine generators (one per polyphase) + + for (int i=0 ; i<=L ; ++i) { // generate an extra set of coefs for interpolation + + // computation for a single polyphase of the overall filter. + SineGen sg = sgg.valueAdvance(); // current sine generator for "j" inner loop. + double err = 0; // for noise shaping on int16_t coefficients (over each polyphase) + + for (int j=0, ix=i ; j<halfNumCoef ; ++j, ix+=L) { + double y; + if (CC_LIKELY(ix)) { + double x = static_cast<double>(ix); + + // sine generator: sg.valueAdvance() returns sin(ix*xstep); + // y = I0(beta * sqrt(1.0 - sqr(x * xfrac))) * yscale * sg.valueAdvance() / x; + y = I0SqrRat(sqrbeta * (1.0 - sqr(x * xfrac)), yscale * sg.valueAdvance(), x); + } else { + y = 2. * atten * fcr; // center of filter, sinc(0) = 1. + sg.advance(); + } + + if (is_same<T, int16_t>::value) { // int16_t needs noise shaping + *coef++ = static_cast<T>(toint(y, 1ULL<<(sizeof(T)*8-1), err)); + } else if (is_same<T, int32_t>::value) { + *coef++ = static_cast<T>(toint(y, 1ULL<<(sizeof(T)*8-1))); + } else { // assumed float or double + *coef++ = static_cast<T>(y); + } + } + } +} + +}; // namespace android + +#endif /*ANDROID_AUDIO_RESAMPLER_FIR_GEN_H*/ diff --git a/services/audioflinger/AudioResamplerFirOps.h b/services/audioflinger/AudioResamplerFirOps.h new file mode 100644 index 0000000..bf2163f --- /dev/null +++ b/services/audioflinger/AudioResamplerFirOps.h @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2013 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_RESAMPLER_FIR_OPS_H +#define ANDROID_AUDIO_RESAMPLER_FIR_OPS_H + +namespace android { + +#if defined(__arm__) && !defined(__thumb__) +#define USE_INLINE_ASSEMBLY (true) +#else +#define USE_INLINE_ASSEMBLY (false) +#endif + +#if USE_INLINE_ASSEMBLY && defined(__ARM_NEON__) +#define USE_NEON (true) +#include <arm_neon.h> +#else +#define USE_NEON (false) +#endif + +template<typename T, typename U> +struct is_same +{ + static const bool value = false; +}; + +template<typename T> +struct is_same<T, T> // partial specialization +{ + static const bool value = true; +}; + +static inline +int32_t mulRL(int left, int32_t in, uint32_t vRL) +{ +#if USE_INLINE_ASSEMBLY + int32_t out; + if (left) { + asm( "smultb %[out], %[in], %[vRL] \n" + : [out]"=r"(out) + : [in]"%r"(in), [vRL]"r"(vRL) + : ); + } else { + asm( "smultt %[out], %[in], %[vRL] \n" + : [out]"=r"(out) + : [in]"%r"(in), [vRL]"r"(vRL) + : ); + } + return out; +#else + int16_t v = left ? static_cast<int16_t>(vRL) : static_cast<int16_t>(vRL>>16); + return static_cast<int32_t>((static_cast<int64_t>(in) * v) >> 16); +#endif +} + +static inline +int32_t mulAdd(int16_t in, int16_t v, int32_t a) +{ +#if USE_INLINE_ASSEMBLY + int32_t out; + asm( "smlabb %[out], %[v], %[in], %[a] \n" + : [out]"=r"(out) + : [in]"%r"(in), [v]"r"(v), [a]"r"(a) + : ); + return out; +#else + return a + v * in; +#endif +} + +static inline +int32_t mulAdd(int16_t in, int32_t v, int32_t a) +{ +#if USE_INLINE_ASSEMBLY + int32_t out; + asm( "smlawb %[out], %[v], %[in], %[a] \n" + : [out]"=r"(out) + : [in]"%r"(in), [v]"r"(v), [a]"r"(a) + : ); + return out; +#else + return a + static_cast<int32_t>((static_cast<int64_t>(v) * in) >> 16); +#endif +} + +static inline +int32_t mulAdd(int32_t in, int32_t v, int32_t a) +{ +#if USE_INLINE_ASSEMBLY + int32_t out; + asm( "smmla %[out], %[v], %[in], %[a] \n" + : [out]"=r"(out) + : [in]"%r"(in), [v]"r"(v), [a]"r"(a) + : ); + return out; +#else + return a + static_cast<int32_t>((static_cast<int64_t>(v) * in) >> 32); +#endif +} + +static inline +int32_t mulAddRL(int left, uint32_t inRL, int16_t v, int32_t a) +{ +#if USE_INLINE_ASSEMBLY + int32_t out; + if (left) { + asm( "smlabb %[out], %[v], %[inRL], %[a] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a) + : ); + } else { + asm( "smlabt %[out], %[v], %[inRL], %[a] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a) + : ); + } + return out; +#else + int16_t s = left ? static_cast<int16_t>(inRL) : static_cast<int16_t>(inRL>>16); + return a + v * s; +#endif +} + +static inline +int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a) +{ +#if USE_INLINE_ASSEMBLY + int32_t out; + if (left) { + asm( "smlawb %[out], %[v], %[inRL], %[a] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a) + : ); + } else { + asm( "smlawt %[out], %[v], %[inRL], %[a] \n" + : [out]"=r"(out) + : [inRL]"%r"(inRL), [v]"r"(v), [a]"r"(a) + : ); + } + return out; +#else + int16_t s = left ? static_cast<int16_t>(inRL) : static_cast<int16_t>(inRL>>16); + return a + static_cast<int32_t>((static_cast<int64_t>(v) * s) >> 16); +#endif +} + +}; // namespace android + +#endif /*ANDROID_AUDIO_RESAMPLER_FIR_OPS_H*/ diff --git a/services/audioflinger/AudioResamplerFirProcess.h b/services/audioflinger/AudioResamplerFirProcess.h new file mode 100644 index 0000000..efc8055 --- /dev/null +++ b/services/audioflinger/AudioResamplerFirProcess.h @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2013 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_RESAMPLER_FIR_PROCESS_H +#define ANDROID_AUDIO_RESAMPLER_FIR_PROCESS_H + +namespace android { + +// depends on AudioResamplerFirOps.h + +/* variant for input type TI = int16_t input samples */ +template<typename TC> +static inline +void mac(int32_t& l, int32_t& r, TC coef, const int16_t* samples) +{ + uint32_t rl = *reinterpret_cast<const uint32_t*>(samples); + l = mulAddRL(1, rl, coef, l); + r = mulAddRL(0, rl, coef, r); +} + +template<typename TC> +static inline +void mac(int32_t& l, TC coef, const int16_t* samples) +{ + l = mulAdd(samples[0], coef, l); +} + +/* variant for input type TI = float input samples */ +template<typename TC> +static inline +void mac(float& l, float& r, TC coef, const float* samples) +{ + l += *samples++ * coef; + r += *samples * coef; +} + +template<typename TC> +static inline +void mac(float& l, TC coef, const float* samples) +{ + l += *samples * coef; +} + +/* variant for output type TO = int32_t output samples */ +static inline +int32_t volumeAdjust(int32_t value, int32_t volume) +{ + return 2 * mulRL(0, value, volume); // Note: only use top 16b +} + +/* variant for output type TO = float output samples */ +static inline +float volumeAdjust(float value, float volume) +{ + return value * volume; +} + +/* + * Helper template functions for loop unrolling accumulator operations. + * + * Unrolling the loops achieves about 2x gain. + * Using a recursive template rather than an array of TO[] for the accumulator + * values is an additional 10-20% gain. + */ + +template<int CHANNELS, typename TO> +class Accumulator : public Accumulator<CHANNELS-1, TO> // recursive +{ +public: + inline void clear() { + value = 0; + Accumulator<CHANNELS-1, TO>::clear(); + } + template<typename TC, typename TI> + inline void acc(TC coef, const TI*& data) { + mac(value, coef, data++); + Accumulator<CHANNELS-1, TO>::acc(coef, data); + } + inline void volume(TO*& out, TO gain) { + *out++ = volumeAdjust(value, gain); + Accumulator<CHANNELS-1, TO>::volume(out, gain); + } + + TO value; // one per recursive inherited base class +}; + +template<typename TO> +class Accumulator<0, TO> { +public: + inline void clear() { + } + template<typename TC, typename TI> + inline void acc(TC coef __unused, const TI*& data __unused) { + } + inline void volume(TO*& out __unused, TO gain __unused) { + } +}; + +template<typename TC, typename TINTERP> +inline +TC interpolate(TC coef_0, TC coef_1, TINTERP lerp) +{ + return lerp * (coef_1 - coef_0) + coef_0; +} + +template<> +inline +int16_t interpolate<int16_t, uint32_t>(int16_t coef_0, int16_t coef_1, uint32_t lerp) +{ // in some CPU architectures 16b x 16b multiplies are faster. + return (static_cast<int16_t>(lerp) * static_cast<int16_t>(coef_1 - coef_0) >> 15) + coef_0; +} + +template<> +inline +int32_t interpolate<int32_t, uint32_t>(int32_t coef_0, int32_t coef_1, uint32_t lerp) +{ + return (lerp * static_cast<int64_t>(coef_1 - coef_0) >> 31) + coef_0; +} + +/* class scope for passing in functions into templates */ +struct InterpCompute { + template<typename TC, typename TINTERP> + static inline + TC interpolatep(TC coef_0, TC coef_1, TINTERP lerp) { + return interpolate(coef_0, coef_1, lerp); + } + + template<typename TC, typename TINTERP> + static inline + TC interpolaten(TC coef_0, TC coef_1, TINTERP lerp) { + return interpolate(coef_0, coef_1, lerp); + } +}; + +struct InterpNull { + template<typename TC, typename TINTERP> + static inline + TC interpolatep(TC coef_0, TC coef_1 __unused, TINTERP lerp __unused) { + return coef_0; + } + + template<typename TC, typename TINTERP> + static inline + TC interpolaten(TC coef_0 __unused, TC coef_1, TINTERP lerp __unused) { + return coef_1; + } +}; + +/* + * Calculates a single output frame (two samples). + * + * The Process*() functions compute both the positive half FIR dot product and + * the negative half FIR dot product, accumulates, and then applies the volume. + * + * Use fir() to compute the proper coefficient pointers for a polyphase + * filter bank. + * + * ProcessBase() is the fundamental processing template function. + * + * ProcessL() calls ProcessBase() with TFUNC = InterpNull, for fixed/locked phase. + * Process() calls ProcessBase() with TFUNC = InterpCompute, for interpolated phase. + */ + +template <int CHANNELS, int STRIDE, typename TFUNC, typename TC, typename TI, typename TO, typename TINTERP> +static inline +void ProcessBase(TO* const out, + size_t count, + const TC* coefsP, + const TC* coefsN, + const TI* sP, + const TI* sN, + TINTERP lerpP, + const TO* const volumeLR) +{ + COMPILE_TIME_ASSERT_FUNCTION_SCOPE(CHANNELS > 0) + + if (CHANNELS > 2) { + // TO accum[CHANNELS]; + Accumulator<CHANNELS, TO> accum; + + // for (int j = 0; j < CHANNELS; ++j) accum[j] = 0; + accum.clear(); + for (size_t i = 0; i < count; ++i) { + TC c = TFUNC::interpolatep(coefsP[0], coefsP[count], lerpP); + + // for (int j = 0; j < CHANNELS; ++j) mac(accum[j], c, sP + j); + const TI *tmp_data = sP; // tmp_ptr seems to work better + accum.acc(c, tmp_data); + + coefsP++; + sP -= CHANNELS; + c = TFUNC::interpolaten(coefsN[count], coefsN[0], lerpP); + + // for (int j = 0; j < CHANNELS; ++j) mac(accum[j], c, sN + j); + tmp_data = sN; // tmp_ptr seems faster than directly using sN + accum.acc(c, tmp_data); + + coefsN++; + sN += CHANNELS; + } + // for (int j = 0; j < CHANNELS; ++j) out[j] += volumeAdjust(accum[j], volumeLR[0]); + TO *tmp_out = out; // may remove if const out definition changes. + accum.volume(tmp_out, volumeLR[0]); + } else if (CHANNELS == 2) { + TO l = 0; + TO r = 0; + for (size_t i = 0; i < count; ++i) { + mac(l, r, TFUNC::interpolatep(coefsP[0], coefsP[count], lerpP), sP); + coefsP++; + sP -= CHANNELS; + mac(l, r, TFUNC::interpolaten(coefsN[count], coefsN[0], lerpP), sN); + coefsN++; + sN += CHANNELS; + } + out[0] += volumeAdjust(l, volumeLR[0]); + out[1] += volumeAdjust(r, volumeLR[1]); + } else { /* CHANNELS == 1 */ + TO l = 0; + for (size_t i = 0; i < count; ++i) { + mac(l, TFUNC::interpolatep(coefsP[0], coefsP[count], lerpP), sP); + coefsP++; + sP -= CHANNELS; + mac(l, TFUNC::interpolaten(coefsN[count], coefsN[0], lerpP), sN); + coefsN++; + sN += CHANNELS; + } + out[0] += volumeAdjust(l, volumeLR[0]); + out[1] += volumeAdjust(l, volumeLR[1]); + } +} + +template <int CHANNELS, int STRIDE, typename TC, typename TI, typename TO> +static inline +void ProcessL(TO* const out, + int count, + const TC* coefsP, + const TC* coefsN, + const TI* sP, + const TI* sN, + const TO* const volumeLR) +{ + ProcessBase<CHANNELS, STRIDE, InterpNull>(out, count, coefsP, coefsN, sP, sN, 0, volumeLR); +} + +template <int CHANNELS, int STRIDE, typename TC, typename TI, typename TO, typename TINTERP> +static inline +void Process(TO* const out, + int count, + const TC* coefsP, + const TC* coefsN, + const TC* coefsP1 __unused, + const TC* coefsN1 __unused, + const TI* sP, + const TI* sN, + TINTERP lerpP, + const TO* const volumeLR) +{ + ProcessBase<CHANNELS, STRIDE, InterpCompute>(out, count, coefsP, coefsN, sP, sN, lerpP, volumeLR); +} + +/* + * Calculates a single output frame (two samples) from input sample pointer. + * + * This sets up the params for the accelerated Process() and ProcessL() + * functions to do the appropriate dot products. + * + * @param out should point to the output buffer with space for at least one output frame. + * + * @param phase is the fractional distance between input frames for interpolation: + * phase >= 0 && phase < phaseWrapLimit. It can be thought of as a rational fraction + * of phase/phaseWrapLimit. + * + * @param phaseWrapLimit is #polyphases<<coefShift, where #polyphases is the number of polyphases + * in the polyphase filter. Likewise, #polyphases can be obtained as (phaseWrapLimit>>coefShift). + * + * @param coefShift gives the bit alignment of the polyphase index in the phase parameter. + * + * @param halfNumCoefs is the half the number of coefficients per polyphase filter. Since the + * overall filterbank is odd-length symmetric, only halfNumCoefs need be stored. + * + * @param coefs is the polyphase filter bank, starting at from polyphase index 0, and ranging to + * and including the #polyphases. Each polyphase of the filter has half-length halfNumCoefs + * (due to symmetry). The total size of the filter bank in coefficients is + * (#polyphases+1)*halfNumCoefs. + * + * The filter bank coefs should be aligned to a minimum of 16 bytes (preferrably to cache line). + * + * The coefs should be attenuated (to compensate for passband ripple) + * if storing back into the native format. + * + * @param samples are unaligned input samples. The position is in the "middle" of the + * sample array with respect to the FIR filter: + * the negative half of the filter is dot product from samples+1 to samples+halfNumCoefs; + * the positive half of the filter is dot product from samples to samples-halfNumCoefs+1. + * + * @param volumeLR is a pointer to an array of two 32 bit volume values, one per stereo channel, + * expressed as a S32 integer. A negative value inverts the channel 180 degrees. + * The pointer volumeLR should be aligned to a minimum of 8 bytes. + * A typical value for volume is 0x1000 to align to a unity gain output of 20.12. + * + * In between calls to filterCoefficient, the phase is incremented by phaseIncrement, where + * phaseIncrement is calculated as inputSampling * phaseWrapLimit / outputSampling. + * + * The filter polyphase index is given by indexP = phase >> coefShift. Due to + * odd length symmetric filter, the polyphase index of the negative half depends on + * whether interpolation is used. + * + * The fractional siting between the polyphase indices is given by the bits below coefShift: + * + * lerpP = phase << 32 - coefShift >> 1; // for 32 bit unsigned phase multiply + * lerpP = phase << 32 - coefShift >> 17; // for 16 bit unsigned phase multiply + * + * For integer types, this is expressed as: + * + * lerpP = phase << sizeof(phase)*8 - coefShift + * >> (sizeof(phase)-sizeof(*coefs))*8 + 1; + * + * For floating point, lerpP is the fractional phase scaled to [0.0, 1.0): + * + * lerpP = (phase << 32 - coefShift) / (1 << 32); // floating point equivalent + */ + +template<int CHANNELS, bool LOCKED, int STRIDE, typename TC, typename TI, typename TO> +static inline +void fir(TO* const out, + const uint32_t phase, const uint32_t phaseWrapLimit, + const int coefShift, const int halfNumCoefs, const TC* const coefs, + const TI* const samples, const TO* const volumeLR) +{ + // NOTE: be very careful when modifying the code here. register + // pressure is very high and a small change might cause the compiler + // to generate far less efficient code. + // Always sanity check the result with objdump or test-resample. + + if (LOCKED) { + // locked polyphase (no interpolation) + // Compute the polyphase filter index on the positive and negative side. + uint32_t indexP = phase >> coefShift; + uint32_t indexN = (phaseWrapLimit - phase) >> coefShift; + const TC* coefsP = coefs + indexP*halfNumCoefs; + const TC* coefsN = coefs + indexN*halfNumCoefs; + const TI* sP = samples; + const TI* sN = samples + CHANNELS; + + // dot product filter. + ProcessL<CHANNELS, STRIDE>(out, + halfNumCoefs, coefsP, coefsN, sP, sN, volumeLR); + } else { + // interpolated polyphase + // Compute the polyphase filter index on the positive and negative side. + uint32_t indexP = phase >> coefShift; + uint32_t indexN = (phaseWrapLimit - phase - 1) >> coefShift; // one's complement. + const TC* coefsP = coefs + indexP*halfNumCoefs; + const TC* coefsN = coefs + indexN*halfNumCoefs; + const TC* coefsP1 = coefsP + halfNumCoefs; + const TC* coefsN1 = coefsN + halfNumCoefs; + const TI* sP = samples; + const TI* sN = samples + CHANNELS; + + // Interpolation fraction lerpP derived by shifting all the way up and down + // to clear the appropriate bits and align to the appropriate level + // for the integer multiply. The constants should resolve in compile time. + // + // The interpolated filter coefficient is derived as follows for the pos/neg half: + // + // interpolated[P] = index[P]*lerpP + index[P+1]*(1-lerpP) + // interpolated[N] = index[N+1]*lerpP + index[N]*(1-lerpP) + + // on-the-fly interpolated dot product filter + if (is_same<TC, float>::value || is_same<TC, double>::value) { + static const TC scale = 1. / (65536. * 65536.); // scale phase bits to [0.0, 1.0) + TC lerpP = TC(phase << (sizeof(phase)*8 - coefShift)) * scale; + + Process<CHANNELS, STRIDE>(out, + halfNumCoefs, coefsP, coefsN, coefsP1, coefsN1, sP, sN, lerpP, volumeLR); + } else { + uint32_t lerpP = phase << (sizeof(phase)*8 - coefShift) + >> ((sizeof(phase)-sizeof(*coefs))*8 + 1); + + Process<CHANNELS, STRIDE>(out, + halfNumCoefs, coefsP, coefsN, coefsP1, coefsN1, sP, sN, lerpP, volumeLR); + } + } +} + +}; // namespace android + +#endif /*ANDROID_AUDIO_RESAMPLER_FIR_PROCESS_H*/ diff --git a/services/audioflinger/AudioResamplerFirProcessNeon.h b/services/audioflinger/AudioResamplerFirProcessNeon.h new file mode 100644 index 0000000..f311cef --- /dev/null +++ b/services/audioflinger/AudioResamplerFirProcessNeon.h @@ -0,0 +1,1149 @@ +/* + * Copyright (C) 2013 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_RESAMPLER_FIR_PROCESS_NEON_H +#define ANDROID_AUDIO_RESAMPLER_FIR_PROCESS_NEON_H + +namespace android { + +// depends on AudioResamplerFirOps.h, AudioResamplerFirProcess.h + +#if USE_NEON +// +// NEON specializations are enabled for Process() and ProcessL() +// +// TODO: Stride 16 and Stride 8 can be combined with one pass stride 8 (if necessary) +// and looping stride 16 (or vice versa). This has some polyphase coef data alignment +// issues with S16 coefs. Consider this later. + +// Macros to save a mono/stereo accumulator sample in q0 (and q4) as stereo out. +#define ASSEMBLY_ACCUMULATE_MONO \ + "vld1.s32 {d2}, [%[vLR]:64] \n"/* (1) load volumes */\ + "vld1.s32 {d3}, %[out] \n"/* (2) unaligned load the output */\ + "vpadd.s32 d0, d0, d1 \n"/* (1) add all 4 partial sums */\ + "vpadd.s32 d0, d0, d0 \n"/* (1+4d) and replicate L/R */\ + "vqrdmulh.s32 d0, d0, d2 \n"/* (2+3d) apply volume */\ + "vqadd.s32 d3, d3, d0 \n"/* (1+4d) accumulate result (saturating) */\ + "vst1.s32 {d3}, %[out] \n"/* (2+2d) store result */ + +#define ASSEMBLY_ACCUMULATE_STEREO \ + "vld1.s32 {d2}, [%[vLR]:64] \n"/* (1) load volumes*/\ + "vld1.s32 {d3}, %[out] \n"/* (2) unaligned load the output*/\ + "vpadd.s32 d0, d0, d1 \n"/* (1) add all 4 partial sums from q0*/\ + "vpadd.s32 d8, d8, d9 \n"/* (1) add all 4 partial sums from q4*/\ + "vpadd.s32 d0, d0, d8 \n"/* (1+4d) combine into L/R*/\ + "vqrdmulh.s32 d0, d0, d2 \n"/* (2+3d) apply volume*/\ + "vqadd.s32 d3, d3, d0 \n"/* (1+4d) accumulate result (saturating)*/\ + "vst1.s32 {d3}, %[out] \n"/* (2+2d)store result*/ + +template <> +inline void ProcessL<1, 16>(int32_t* const out, + int count, + const int16_t* coefsP, + const int16_t* coefsN, + const int16_t* sP, + const int16_t* sN, + const int32_t* const volumeLR) +{ + const int CHANNELS = 1; // template specialization does not preserve params + const int STRIDE = 16; + sP -= CHANNELS*((STRIDE>>1)-1); + asm ( + "veor q0, q0, q0 \n"// (0 - combines+) accumulator = 0 + + "1: \n" + + "vld1.16 {q2}, [%[sP]] \n"// (2+0d) load 8 16-bits mono samples + "vld1.16 {q3}, [%[sN]]! \n"// (2) load 8 16-bits mono samples + "vld1.16 {q8}, [%[coefsP0]:128]! \n"// (1) load 8 16-bits coefs + "vld1.16 {q10}, [%[coefsN0]:128]! \n"// (1) load 8 16-bits coefs + + "vrev64.16 q2, q2 \n"// (1) reverse s3, s2, s1, s0, s7, s6, s5, s4 + + // reordering the vmal to do d6, d7 before d4, d5 is slower(?) + "vmlal.s16 q0, d4, d17 \n"// (1+0d) multiply (reversed)samples by coef + "vmlal.s16 q0, d5, d16 \n"// (1) multiply (reversed)samples by coef + "vmlal.s16 q0, d6, d20 \n"// (1) multiply neg samples + "vmlal.s16 q0, d7, d21 \n"// (1) multiply neg samples + + // moving these ARM instructions before neon above seems to be slower + "subs %[count], %[count], #8 \n"// (1) update loop counter + "sub %[sP], %[sP], #16 \n"// (0) move pointer to next set of samples + + // sP used after branch (warning) + "bne 1b \n"// loop + + ASSEMBLY_ACCUMULATE_MONO + + : [out] "=Uv" (out[0]), + [count] "+r" (count), + [coefsP0] "+r" (coefsP), + [coefsN0] "+r" (coefsN), + [sP] "+r" (sP), + [sN] "+r" (sN) + : [vLR] "r" (volumeLR) + : "cc", "memory", + "q0", "q1", "q2", "q3", + "q8", "q10" + ); +} + +template <> +inline void ProcessL<2, 16>(int32_t* const out, + int count, + const int16_t* coefsP, + const int16_t* coefsN, + const int16_t* sP, + const int16_t* sN, + const int32_t* const volumeLR) +{ + const int CHANNELS = 2; // template specialization does not preserve params + const int STRIDE = 16; + sP -= CHANNELS*((STRIDE>>1)-1); + asm ( + "veor q0, q0, q0 \n"// (1) acc_L = 0 + "veor q4, q4, q4 \n"// (0 combines+) acc_R = 0 + + "1: \n" + + "vld2.16 {q2, q3}, [%[sP]] \n"// (3+0d) load 8 16-bits stereo samples + "vld2.16 {q5, q6}, [%[sN]]! \n"// (3) load 8 16-bits stereo samples + "vld1.16 {q8}, [%[coefsP0]:128]! \n"// (1) load 8 16-bits coefs + "vld1.16 {q10}, [%[coefsN0]:128]! \n"// (1) load 8 16-bits coefs + + "vrev64.16 q2, q2 \n"// (1) reverse 8 frames of the left positive + "vrev64.16 q3, q3 \n"// (0 combines+) reverse right positive + + "vmlal.s16 q0, d4, d17 \n"// (1) multiply (reversed) samples left + "vmlal.s16 q0, d5, d16 \n"// (1) multiply (reversed) samples left + "vmlal.s16 q4, d6, d17 \n"// (1) multiply (reversed) samples right + "vmlal.s16 q4, d7, d16 \n"// (1) multiply (reversed) samples right + "vmlal.s16 q0, d10, d20 \n"// (1) multiply samples left + "vmlal.s16 q0, d11, d21 \n"// (1) multiply samples left + "vmlal.s16 q4, d12, d20 \n"// (1) multiply samples right + "vmlal.s16 q4, d13, d21 \n"// (1) multiply samples right + + // moving these ARM before neon seems to be slower + "subs %[count], %[count], #8 \n"// (1) update loop counter + "sub %[sP], %[sP], #32 \n"// (0) move pointer to next set of samples + + // sP used after branch (warning) + "bne 1b \n"// loop + + ASSEMBLY_ACCUMULATE_STEREO + + : [out] "=Uv" (out[0]), + [count] "+r" (count), + [coefsP0] "+r" (coefsP), + [coefsN0] "+r" (coefsN), + [sP] "+r" (sP), + [sN] "+r" (sN) + : [vLR] "r" (volumeLR) + : "cc", "memory", + "q0", "q1", "q2", "q3", + "q4", "q5", "q6", + "q8", "q10" + ); +} + +template <> +inline void Process<1, 16>(int32_t* const out, + int count, + const int16_t* coefsP, + const int16_t* coefsN, + const int16_t* coefsP1, + const int16_t* coefsN1, + const int16_t* sP, + const int16_t* sN, + uint32_t lerpP, + const int32_t* const volumeLR) +{ + const int CHANNELS = 1; // template specialization does not preserve params + const int STRIDE = 16; + sP -= CHANNELS*((STRIDE>>1)-1); + asm ( + "vmov.32 d2[0], %[lerpP] \n"// load the positive phase S32 Q15 + "veor q0, q0, q0 \n"// (0 - combines+) accumulator = 0 + + "1: \n" + + "vld1.16 {q2}, [%[sP]] \n"// (2+0d) load 8 16-bits mono samples + "vld1.16 {q3}, [%[sN]]! \n"// (2) load 8 16-bits mono samples + "vld1.16 {q8}, [%[coefsP0]:128]! \n"// (1) load 8 16-bits coefs + "vld1.16 {q9}, [%[coefsP1]:128]! \n"// (1) load 8 16-bits coefs for interpolation + "vld1.16 {q10}, [%[coefsN1]:128]! \n"// (1) load 8 16-bits coefs + "vld1.16 {q11}, [%[coefsN0]:128]! \n"// (1) load 8 16-bits coefs for interpolation + + "vsub.s16 q9, q9, q8 \n"// (1) interpolate (step1) 1st set of coefs + "vsub.s16 q11, q11, q10 \n"// (1) interpolate (step1) 2nd set of coets + + "vqrdmulh.s16 q9, q9, d2[0] \n"// (2) interpolate (step2) 1st set of coefs + "vqrdmulh.s16 q11, q11, d2[0] \n"// (2) interpolate (step2) 2nd set of coefs + + "vrev64.16 q2, q2 \n"// (1) reverse s3, s2, s1, s0, s7, s6, s5, s4 + + "vadd.s16 q8, q8, q9 \n"// (1+2d) interpolate (step3) 1st set + "vadd.s16 q10, q10, q11 \n"// (1+1d) interpolate (step3) 2nd set + + // reordering the vmal to do d6, d7 before d4, d5 is slower(?) + "vmlal.s16 q0, d4, d17 \n"// (1+0d) multiply reversed samples by coef + "vmlal.s16 q0, d5, d16 \n"// (1) multiply reversed samples by coef + "vmlal.s16 q0, d6, d20 \n"// (1) multiply neg samples + "vmlal.s16 q0, d7, d21 \n"// (1) multiply neg samples + + // moving these ARM instructions before neon above seems to be slower + "subs %[count], %[count], #8 \n"// (1) update loop counter + "sub %[sP], %[sP], #16 \n"// (0) move pointer to next set of samples + + // sP used after branch (warning) + "bne 1b \n"// loop + + ASSEMBLY_ACCUMULATE_MONO + + : [out] "=Uv" (out[0]), + [count] "+r" (count), + [coefsP0] "+r" (coefsP), + [coefsN0] "+r" (coefsN), + [coefsP1] "+r" (coefsP1), + [coefsN1] "+r" (coefsN1), + [sP] "+r" (sP), + [sN] "+r" (sN) + : [lerpP] "r" (lerpP), + [vLR] "r" (volumeLR) + : "cc", "memory", + "q0", "q1", "q2", "q3", + "q8", "q9", "q10", "q11" + ); +} + +template <> +inline void Process<2, 16>(int32_t* const out, + int count, + const int16_t* coefsP, + const int16_t* coefsN, + const int16_t* coefsP1, + const int16_t* coefsN1, + const int16_t* sP, + const int16_t* sN, + uint32_t lerpP, + const int32_t* const volumeLR) +{ + const int CHANNELS = 2; // template specialization does not preserve params + const int STRIDE = 16; + sP -= CHANNELS*((STRIDE>>1)-1); + asm ( + "vmov.32 d2[0], %[lerpP] \n"// load the positive phase + "veor q0, q0, q0 \n"// (1) acc_L = 0 + "veor q4, q4, q4 \n"// (0 combines+) acc_R = 0 + + "1: \n" + + "vld2.16 {q2, q3}, [%[sP]] \n"// (3+0d) load 8 16-bits stereo samples + "vld2.16 {q5, q6}, [%[sN]]! \n"// (3) load 8 16-bits stereo samples + "vld1.16 {q8}, [%[coefsP0]:128]! \n"// (1) load 8 16-bits coefs + "vld1.16 {q9}, [%[coefsP1]:128]! \n"// (1) load 8 16-bits coefs for interpolation + "vld1.16 {q10}, [%[coefsN1]:128]! \n"// (1) load 8 16-bits coefs + "vld1.16 {q11}, [%[coefsN0]:128]! \n"// (1) load 8 16-bits coefs for interpolation + + "vsub.s16 q9, q9, q8 \n"// (1) interpolate (step1) 1st set of coefs + "vsub.s16 q11, q11, q10 \n"// (1) interpolate (step1) 2nd set of coets + + "vqrdmulh.s16 q9, q9, d2[0] \n"// (2) interpolate (step2) 1st set of coefs + "vqrdmulh.s16 q11, q11, d2[0] \n"// (2) interpolate (step2) 2nd set of coefs + + "vrev64.16 q2, q2 \n"// (1) reverse 8 frames of the left positive + "vrev64.16 q3, q3 \n"// (1) reverse 8 frames of the right positive + + "vadd.s16 q8, q8, q9 \n"// (1+1d) interpolate (step3) 1st set + "vadd.s16 q10, q10, q11 \n"// (1+1d) interpolate (step3) 2nd set + + "vmlal.s16 q0, d4, d17 \n"// (1) multiply reversed samples left + "vmlal.s16 q0, d5, d16 \n"// (1) multiply reversed samples left + "vmlal.s16 q4, d6, d17 \n"// (1) multiply reversed samples right + "vmlal.s16 q4, d7, d16 \n"// (1) multiply reversed samples right + "vmlal.s16 q0, d10, d20 \n"// (1) multiply samples left + "vmlal.s16 q0, d11, d21 \n"// (1) multiply samples left + "vmlal.s16 q4, d12, d20 \n"// (1) multiply samples right + "vmlal.s16 q4, d13, d21 \n"// (1) multiply samples right + + // moving these ARM before neon seems to be slower + "subs %[count], %[count], #8 \n"// (1) update loop counter + "sub %[sP], %[sP], #32 \n"// (0) move pointer to next set of samples + + // sP used after branch (warning) + "bne 1b \n"// loop + + ASSEMBLY_ACCUMULATE_STEREO + + : [out] "=Uv" (out[0]), + [count] "+r" (count), + [coefsP0] "+r" (coefsP), + [coefsN0] "+r" (coefsN), + [coefsP1] "+r" (coefsP1), + [coefsN1] "+r" (coefsN1), + [sP] "+r" (sP), + [sN] "+r" (sN) + : [lerpP] "r" (lerpP), + [vLR] "r" (volumeLR) + : "cc", "memory", + "q0", "q1", "q2", "q3", + "q4", "q5", "q6", + "q8", "q9", "q10", "q11" + ); +} + +template <> +inline void ProcessL<1, 16>(int32_t* const out, + int count, + const int32_t* coefsP, + const int32_t* coefsN, + const int16_t* sP, + const int16_t* sN, + const int32_t* const volumeLR) +{ + const int CHANNELS = 1; // template specialization does not preserve params + const int STRIDE = 16; + sP -= CHANNELS*((STRIDE>>1)-1); + asm ( + "veor q0, q0, q0 \n"// result, initialize to 0 + + "1: \n" + + "vld1.16 {q2}, [%[sP]] \n"// load 8 16-bits mono samples + "vld1.16 {q3}, [%[sN]]! \n"// load 8 16-bits mono samples + "vld1.32 {q8, q9}, [%[coefsP0]:128]! \n"// load 8 32-bits coefs + "vld1.32 {q10, q11}, [%[coefsN0]:128]! \n"// load 8 32-bits coefs + + "vrev64.16 q2, q2 \n"// reverse 8 frames of the positive side + + "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits + "vshll.s16 q13, d5, #15 \n"// extend samples to 31 bits + + "vshll.s16 q14, d6, #15 \n"// extend samples to 31 bits + "vshll.s16 q15, d7, #15 \n"// extend samples to 31 bits + + "vqrdmulh.s32 q12, q12, q9 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q15, q15, q11 \n"// multiply samples by interpolated coef + + "vadd.s32 q0, q0, q12 \n"// accumulate result + "vadd.s32 q13, q13, q14 \n"// accumulate result + "vadd.s32 q0, q0, q15 \n"// accumulate result + "vadd.s32 q0, q0, q13 \n"// accumulate result + + "sub %[sP], %[sP], #16 \n"// move pointer to next set of samples + "subs %[count], %[count], #8 \n"// update loop counter + + "bne 1b \n"// loop + + ASSEMBLY_ACCUMULATE_MONO + + : [out] "=Uv" (out[0]), + [count] "+r" (count), + [coefsP0] "+r" (coefsP), + [coefsN0] "+r" (coefsN), + [sP] "+r" (sP), + [sN] "+r" (sN) + : [vLR] "r" (volumeLR) + : "cc", "memory", + "q0", "q1", "q2", "q3", + "q8", "q9", "q10", "q11", + "q12", "q13", "q14", "q15" + ); +} + +template <> +inline void ProcessL<2, 16>(int32_t* const out, + int count, + const int32_t* coefsP, + const int32_t* coefsN, + const int16_t* sP, + const int16_t* sN, + const int32_t* const volumeLR) +{ + const int CHANNELS = 2; // template specialization does not preserve params + const int STRIDE = 16; + sP -= CHANNELS*((STRIDE>>1)-1); + asm ( + "veor q0, q0, q0 \n"// result, initialize to 0 + "veor q4, q4, q4 \n"// result, initialize to 0 + + "1: \n" + + "vld2.16 {q2, q3}, [%[sP]] \n"// load 4 16-bits stereo samples + "vld2.16 {q5, q6}, [%[sN]]! \n"// load 4 16-bits stereo samples + "vld1.32 {q8, q9}, [%[coefsP0]:128]! \n"// load 4 32-bits coefs + "vld1.32 {q10, q11}, [%[coefsN0]:128]! \n"// load 4 32-bits coefs + + "vrev64.16 q2, q2 \n"// reverse 8 frames of the positive side + "vrev64.16 q3, q3 \n"// reverse 8 frames of the positive side + + "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits + "vshll.s16 q13, d5, #15 \n"// extend samples to 31 bits + + "vshll.s16 q14, d10, #15 \n"// extend samples to 31 bits + "vshll.s16 q15, d11, #15 \n"// extend samples to 31 bits + + "vqrdmulh.s32 q12, q12, q9 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q15, q15, q11 \n"// multiply samples by interpolated coef + + "vadd.s32 q0, q0, q12 \n"// accumulate result + "vadd.s32 q13, q13, q14 \n"// accumulate result + "vadd.s32 q0, q0, q15 \n"// (+1) accumulate result + "vadd.s32 q0, q0, q13 \n"// (+1) accumulate result + + "vshll.s16 q12, d6, #15 \n"// extend samples to 31 bits + "vshll.s16 q13, d7, #15 \n"// extend samples to 31 bits + + "vshll.s16 q14, d12, #15 \n"// extend samples to 31 bits + "vshll.s16 q15, d13, #15 \n"// extend samples to 31 bits + + "vqrdmulh.s32 q12, q12, q9 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q15, q15, q11 \n"// multiply samples by interpolated coef + + "vadd.s32 q4, q4, q12 \n"// accumulate result + "vadd.s32 q13, q13, q14 \n"// accumulate result + "vadd.s32 q4, q4, q15 \n"// (+1) accumulate result + "vadd.s32 q4, q4, q13 \n"// (+1) accumulate result + + "subs %[count], %[count], #8 \n"// update loop counter + "sub %[sP], %[sP], #32 \n"// move pointer to next set of samples + + "bne 1b \n"// loop + + ASSEMBLY_ACCUMULATE_STEREO + + : [out] "=Uv" (out[0]), + [count] "+r" (count), + [coefsP0] "+r" (coefsP), + [coefsN0] "+r" (coefsN), + [sP] "+r" (sP), + [sN] "+r" (sN) + : [vLR] "r" (volumeLR) + : "cc", "memory", + "q0", "q1", "q2", "q3", + "q4", "q5", "q6", + "q8", "q9", "q10", "q11", + "q12", "q13", "q14", "q15" + ); +} + +template <> +inline void Process<1, 16>(int32_t* const out, + int count, + const int32_t* coefsP, + const int32_t* coefsN, + const int32_t* coefsP1, + const int32_t* coefsN1, + const int16_t* sP, + const int16_t* sN, + uint32_t lerpP, + const int32_t* const volumeLR) +{ + const int CHANNELS = 1; // template specialization does not preserve params + const int STRIDE = 16; + sP -= CHANNELS*((STRIDE>>1)-1); + asm ( + "vmov.32 d2[0], %[lerpP] \n"// load the positive phase + "veor q0, q0, q0 \n"// result, initialize to 0 + + "1: \n" + + "vld1.16 {q2}, [%[sP]] \n"// load 8 16-bits mono samples + "vld1.16 {q3}, [%[sN]]! \n"// load 8 16-bits mono samples + "vld1.32 {q8, q9}, [%[coefsP0]:128]! \n"// load 8 32-bits coefs + "vld1.32 {q12, q13}, [%[coefsP1]:128]! \n"// load 8 32-bits coefs + "vld1.32 {q10, q11}, [%[coefsN1]:128]! \n"// load 8 32-bits coefs + "vld1.32 {q14, q15}, [%[coefsN0]:128]! \n"// load 8 32-bits coefs + + "vsub.s32 q12, q12, q8 \n"// interpolate (step1) + "vsub.s32 q13, q13, q9 \n"// interpolate (step1) + "vsub.s32 q14, q14, q10 \n"// interpolate (step1) + "vsub.s32 q15, q15, q11 \n"// interpolate (step1) + + "vqrdmulh.s32 q12, q12, d2[0] \n"// interpolate (step2) + "vqrdmulh.s32 q13, q13, d2[0] \n"// interpolate (step2) + "vqrdmulh.s32 q14, q14, d2[0] \n"// interpolate (step2) + "vqrdmulh.s32 q15, q15, d2[0] \n"// interpolate (step2) + + "vadd.s32 q8, q8, q12 \n"// interpolate (step3) + "vadd.s32 q9, q9, q13 \n"// interpolate (step3) + "vadd.s32 q10, q10, q14 \n"// interpolate (step3) + "vadd.s32 q11, q11, q15 \n"// interpolate (step3) + + "vrev64.16 q2, q2 \n"// reverse 8 frames of the positive side + + "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits + "vshll.s16 q13, d5, #15 \n"// extend samples to 31 bits + + "vshll.s16 q14, d6, #15 \n"// extend samples to 31 bits + "vshll.s16 q15, d7, #15 \n"// extend samples to 31 bits + + "vqrdmulh.s32 q12, q12, q9 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q15, q15, q11 \n"// multiply samples by interpolated coef + + "vadd.s32 q0, q0, q12 \n"// accumulate result + "vadd.s32 q13, q13, q14 \n"// accumulate result + "vadd.s32 q0, q0, q15 \n"// accumulate result + "vadd.s32 q0, q0, q13 \n"// accumulate result + + "sub %[sP], %[sP], #16 \n"// move pointer to next set of samples + "subs %[count], %[count], #8 \n"// update loop counter + + "bne 1b \n"// loop + + ASSEMBLY_ACCUMULATE_MONO + + : [out] "=Uv" (out[0]), + [count] "+r" (count), + [coefsP0] "+r" (coefsP), + [coefsN0] "+r" (coefsN), + [coefsP1] "+r" (coefsP1), + [coefsN1] "+r" (coefsN1), + [sP] "+r" (sP), + [sN] "+r" (sN) + : [lerpP] "r" (lerpP), + [vLR] "r" (volumeLR) + : "cc", "memory", + "q0", "q1", "q2", "q3", + "q8", "q9", "q10", "q11", + "q12", "q13", "q14", "q15" + ); +} + +template <> +inline void Process<2, 16>(int32_t* const out, + int count, + const int32_t* coefsP, + const int32_t* coefsN, + const int32_t* coefsP1, + const int32_t* coefsN1, + const int16_t* sP, + const int16_t* sN, + uint32_t lerpP, + const int32_t* const volumeLR) +{ + const int CHANNELS = 2; // template specialization does not preserve params + const int STRIDE = 16; + sP -= CHANNELS*((STRIDE>>1)-1); + asm ( + "vmov.32 d2[0], %[lerpP] \n"// load the positive phase + "veor q0, q0, q0 \n"// result, initialize to 0 + "veor q4, q4, q4 \n"// result, initialize to 0 + + "1: \n" + + "vld2.16 {q2, q3}, [%[sP]] \n"// load 4 16-bits stereo samples + "vld2.16 {q5, q6}, [%[sN]]! \n"// load 4 16-bits stereo samples + "vld1.32 {q8, q9}, [%[coefsP0]:128]! \n"// load 8 32-bits coefs + "vld1.32 {q12, q13}, [%[coefsP1]:128]! \n"// load 8 32-bits coefs + "vld1.32 {q10, q11}, [%[coefsN1]:128]! \n"// load 8 32-bits coefs + "vld1.32 {q14, q15}, [%[coefsN0]:128]! \n"// load 8 32-bits coefs + + "vsub.s32 q12, q12, q8 \n"// interpolate (step1) + "vsub.s32 q13, q13, q9 \n"// interpolate (step1) + "vsub.s32 q14, q14, q10 \n"// interpolate (step1) + "vsub.s32 q15, q15, q11 \n"// interpolate (step1) + + "vqrdmulh.s32 q12, q12, d2[0] \n"// interpolate (step2) + "vqrdmulh.s32 q13, q13, d2[0] \n"// interpolate (step2) + "vqrdmulh.s32 q14, q14, d2[0] \n"// interpolate (step2) + "vqrdmulh.s32 q15, q15, d2[0] \n"// interpolate (step2) + + "vadd.s32 q8, q8, q12 \n"// interpolate (step3) + "vadd.s32 q9, q9, q13 \n"// interpolate (step3) + "vadd.s32 q10, q10, q14 \n"// interpolate (step3) + "vadd.s32 q11, q11, q15 \n"// interpolate (step3) + + "vrev64.16 q2, q2 \n"// reverse 8 frames of the positive side + "vrev64.16 q3, q3 \n"// reverse 8 frames of the positive side + + "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits + "vshll.s16 q13, d5, #15 \n"// extend samples to 31 bits + + "vshll.s16 q14, d10, #15 \n"// extend samples to 31 bits + "vshll.s16 q15, d11, #15 \n"// extend samples to 31 bits + + "vqrdmulh.s32 q12, q12, q9 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q15, q15, q11 \n"// multiply samples by interpolated coef + + "vadd.s32 q0, q0, q12 \n"// accumulate result + "vadd.s32 q13, q13, q14 \n"// accumulate result + "vadd.s32 q0, q0, q15 \n"// (+1) accumulate result + "vadd.s32 q0, q0, q13 \n"// (+1) accumulate result + + "vshll.s16 q12, d6, #15 \n"// extend samples to 31 bits + "vshll.s16 q13, d7, #15 \n"// extend samples to 31 bits + + "vshll.s16 q14, d12, #15 \n"// extend samples to 31 bits + "vshll.s16 q15, d13, #15 \n"// extend samples to 31 bits + + "vqrdmulh.s32 q12, q12, q9 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q15, q15, q11 \n"// multiply samples by interpolated coef + + "vadd.s32 q4, q4, q12 \n"// accumulate result + "vadd.s32 q13, q13, q14 \n"// accumulate result + "vadd.s32 q4, q4, q15 \n"// (+1) accumulate result + "vadd.s32 q4, q4, q13 \n"// (+1) accumulate result + + "subs %[count], %[count], #8 \n"// update loop counter + "sub %[sP], %[sP], #32 \n"// move pointer to next set of samples + + "bne 1b \n"// loop + + ASSEMBLY_ACCUMULATE_STEREO + + : [out] "=Uv" (out[0]), + [count] "+r" (count), + [coefsP0] "+r" (coefsP), + [coefsN0] "+r" (coefsN), + [coefsP1] "+r" (coefsP1), + [coefsN1] "+r" (coefsN1), + [sP] "+r" (sP), + [sN] "+r" (sN) + : [lerpP] "r" (lerpP), + [vLR] "r" (volumeLR) + : "cc", "memory", + "q0", "q1", "q2", "q3", + "q4", "q5", "q6", + "q8", "q9", "q10", "q11", + "q12", "q13", "q14", "q15" + ); +} + +template <> +inline void ProcessL<1, 8>(int32_t* const out, + int count, + const int16_t* coefsP, + const int16_t* coefsN, + const int16_t* sP, + const int16_t* sN, + const int32_t* const volumeLR) +{ + const int CHANNELS = 1; // template specialization does not preserve params + const int STRIDE = 8; + sP -= CHANNELS*((STRIDE>>1)-1); + asm ( + "veor q0, q0, q0 \n"// (0 - combines+) accumulator = 0 + + "1: \n" + + "vld1.16 {d4}, [%[sP]] \n"// (2+0d) load 4 16-bits mono samples + "vld1.16 {d6}, [%[sN]]! \n"// (2) load 4 16-bits mono samples + "vld1.16 {d16}, [%[coefsP0]:64]! \n"// (1) load 4 16-bits coefs + "vld1.16 {d20}, [%[coefsN0]:64]! \n"// (1) load 4 16-bits coefs + + "vrev64.16 d4, d4 \n"// (1) reversed s3, s2, s1, s0, s7, s6, s5, s4 + + // reordering the vmal to do d6, d7 before d4, d5 is slower(?) + "vmlal.s16 q0, d4, d16 \n"// (1) multiply (reversed)samples by coef + "vmlal.s16 q0, d6, d20 \n"// (1) multiply neg samples + + // moving these ARM instructions before neon above seems to be slower + "subs %[count], %[count], #4 \n"// (1) update loop counter + "sub %[sP], %[sP], #8 \n"// (0) move pointer to next set of samples + + // sP used after branch (warning) + "bne 1b \n"// loop + + ASSEMBLY_ACCUMULATE_MONO + + : [out] "=Uv" (out[0]), + [count] "+r" (count), + [coefsP0] "+r" (coefsP), + [coefsN0] "+r" (coefsN), + [sP] "+r" (sP), + [sN] "+r" (sN) + : [vLR] "r" (volumeLR) + : "cc", "memory", + "q0", "q1", "q2", "q3", + "q8", "q10" + ); +} + +template <> +inline void ProcessL<2, 8>(int32_t* const out, + int count, + const int16_t* coefsP, + const int16_t* coefsN, + const int16_t* sP, + const int16_t* sN, + const int32_t* const volumeLR) +{ + const int CHANNELS = 2; // template specialization does not preserve params + const int STRIDE = 8; + sP -= CHANNELS*((STRIDE>>1)-1); + asm ( + "veor q0, q0, q0 \n"// (1) acc_L = 0 + "veor q4, q4, q4 \n"// (0 combines+) acc_R = 0 + + "1: \n" + + "vld2.16 {d4, d5}, [%[sP]] \n"// (2+0d) load 8 16-bits stereo samples + "vld2.16 {d6, d7}, [%[sN]]! \n"// (2) load 8 16-bits stereo samples + "vld1.16 {d16}, [%[coefsP0]:64]! \n"// (1) load 8 16-bits coefs + "vld1.16 {d20}, [%[coefsN0]:64]! \n"// (1) load 8 16-bits coefs + + "vrev64.16 q2, q2 \n"// (1) reverse 8 frames of the left positive + + "vmlal.s16 q0, d4, d16 \n"// (1) multiply (reversed) samples left + "vmlal.s16 q4, d5, d16 \n"// (1) multiply (reversed) samples right + "vmlal.s16 q0, d6, d20 \n"// (1) multiply samples left + "vmlal.s16 q4, d7, d20 \n"// (1) multiply samples right + + // moving these ARM before neon seems to be slower + "subs %[count], %[count], #4 \n"// (1) update loop counter + "sub %[sP], %[sP], #16 \n"// (0) move pointer to next set of samples + + // sP used after branch (warning) + "bne 1b \n"// loop + + ASSEMBLY_ACCUMULATE_STEREO + + : [out] "=Uv" (out[0]), + [count] "+r" (count), + [coefsP0] "+r" (coefsP), + [coefsN0] "+r" (coefsN), + [sP] "+r" (sP), + [sN] "+r" (sN) + : [vLR] "r" (volumeLR) + : "cc", "memory", + "q0", "q1", "q2", "q3", + "q4", "q5", "q6", + "q8", "q10" + ); +} + +template <> +inline void Process<1, 8>(int32_t* const out, + int count, + const int16_t* coefsP, + const int16_t* coefsN, + const int16_t* coefsP1, + const int16_t* coefsN1, + const int16_t* sP, + const int16_t* sN, + uint32_t lerpP, + const int32_t* const volumeLR) +{ + const int CHANNELS = 1; // template specialization does not preserve params + const int STRIDE = 8; + sP -= CHANNELS*((STRIDE>>1)-1); + asm ( + "vmov.32 d2[0], %[lerpP] \n"// load the positive phase S32 Q15 + "veor q0, q0, q0 \n"// (0 - combines+) accumulator = 0 + + "1: \n" + + "vld1.16 {d4}, [%[sP]] \n"// (2+0d) load 4 16-bits mono samples + "vld1.16 {d6}, [%[sN]]! \n"// (2) load 4 16-bits mono samples + "vld1.16 {d16}, [%[coefsP0]:64]! \n"// (1) load 4 16-bits coefs + "vld1.16 {d17}, [%[coefsP1]:64]! \n"// (1) load 4 16-bits coefs for interpolation + "vld1.16 {d20}, [%[coefsN1]:64]! \n"// (1) load 4 16-bits coefs + "vld1.16 {d21}, [%[coefsN0]:64]! \n"// (1) load 4 16-bits coefs for interpolation + + "vsub.s16 d17, d17, d16 \n"// (1) interpolate (step1) 1st set of coefs + "vsub.s16 d21, d21, d20 \n"// (1) interpolate (step1) 2nd set of coets + + "vqrdmulh.s16 d17, d17, d2[0] \n"// (2) interpolate (step2) 1st set of coefs + "vqrdmulh.s16 d21, d21, d2[0] \n"// (2) interpolate (step2) 2nd set of coefs + + "vrev64.16 d4, d4 \n"// (1) reverse s3, s2, s1, s0, s7, s6, s5, s4 + + "vadd.s16 d16, d16, d17 \n"// (1+2d) interpolate (step3) 1st set + "vadd.s16 d20, d20, d21 \n"// (1+1d) interpolate (step3) 2nd set + + // reordering the vmal to do d6, d7 before d4, d5 is slower(?) + "vmlal.s16 q0, d4, d16 \n"// (1+0d) multiply (reversed)by coef + "vmlal.s16 q0, d6, d20 \n"// (1) multiply neg samples + + // moving these ARM instructions before neon above seems to be slower + "subs %[count], %[count], #4 \n"// (1) update loop counter + "sub %[sP], %[sP], #8 \n"// move pointer to next set of samples + + // sP used after branch (warning) + "bne 1b \n"// loop + + ASSEMBLY_ACCUMULATE_MONO + + : [out] "=Uv" (out[0]), + [count] "+r" (count), + [coefsP0] "+r" (coefsP), + [coefsN0] "+r" (coefsN), + [coefsP1] "+r" (coefsP1), + [coefsN1] "+r" (coefsN1), + [sP] "+r" (sP), + [sN] "+r" (sN) + : [lerpP] "r" (lerpP), + [vLR] "r" (volumeLR) + : "cc", "memory", + "q0", "q1", "q2", "q3", + "q8", "q9", "q10", "q11" + ); +} + +template <> +inline void Process<2, 8>(int32_t* const out, + int count, + const int16_t* coefsP, + const int16_t* coefsN, + const int16_t* coefsP1, + const int16_t* coefsN1, + const int16_t* sP, + const int16_t* sN, + uint32_t lerpP, + const int32_t* const volumeLR) +{ + const int CHANNELS = 2; // template specialization does not preserve params + const int STRIDE = 8; + sP -= CHANNELS*((STRIDE>>1)-1); + asm ( + "vmov.32 d2[0], %[lerpP] \n"// load the positive phase + "veor q0, q0, q0 \n"// (1) acc_L = 0 + "veor q4, q4, q4 \n"// (0 combines+) acc_R = 0 + + "1: \n" + + "vld2.16 {d4, d5}, [%[sP]] \n"// (3+0d) load 8 16-bits stereo samples + "vld2.16 {d6, d7}, [%[sN]]! \n"// (3) load 8 16-bits stereo samples + "vld1.16 {d16}, [%[coefsP0]:64]! \n"// (1) load 8 16-bits coefs + "vld1.16 {d17}, [%[coefsP1]:64]! \n"// (1) load 8 16-bits coefs for interpolation + "vld1.16 {d20}, [%[coefsN1]:64]! \n"// (1) load 8 16-bits coefs + "vld1.16 {d21}, [%[coefsN0]:64]! \n"// (1) load 8 16-bits coefs for interpolation + + "vsub.s16 d17, d17, d16 \n"// (1) interpolate (step1) 1st set of coefs + "vsub.s16 d21, d21, d20 \n"// (1) interpolate (step1) 2nd set of coets + + "vqrdmulh.s16 d17, d17, d2[0] \n"// (2) interpolate (step2) 1st set of coefs + "vqrdmulh.s16 d21, d21, d2[0] \n"// (2) interpolate (step2) 2nd set of coefs + + "vrev64.16 q2, q2 \n"// (1) reverse 8 frames of the left positive + + "vadd.s16 d16, d16, d17 \n"// (1+1d) interpolate (step3) 1st set + "vadd.s16 d20, d20, d21 \n"// (1+1d) interpolate (step3) 2nd set + + "vmlal.s16 q0, d4, d16 \n"// (1) multiply (reversed) samples left + "vmlal.s16 q4, d5, d16 \n"// (1) multiply (reversed) samples right + "vmlal.s16 q0, d6, d20 \n"// (1) multiply samples left + "vmlal.s16 q4, d7, d20 \n"// (1) multiply samples right + + // moving these ARM before neon seems to be slower + "subs %[count], %[count], #4 \n"// (1) update loop counter + "sub %[sP], %[sP], #16 \n"// move pointer to next set of samples + + // sP used after branch (warning) + "bne 1b \n"// loop + + ASSEMBLY_ACCUMULATE_STEREO + + : [out] "=Uv" (out[0]), + [count] "+r" (count), + [coefsP0] "+r" (coefsP), + [coefsN0] "+r" (coefsN), + [coefsP1] "+r" (coefsP1), + [coefsN1] "+r" (coefsN1), + [sP] "+r" (sP), + [sN] "+r" (sN) + : [lerpP] "r" (lerpP), + [vLR] "r" (volumeLR) + : "cc", "memory", + "q0", "q1", "q2", "q3", + "q4", "q5", "q6", + "q8", "q9", "q10", "q11" + ); +} + +template <> +inline void ProcessL<1, 8>(int32_t* const out, + int count, + const int32_t* coefsP, + const int32_t* coefsN, + const int16_t* sP, + const int16_t* sN, + const int32_t* const volumeLR) +{ + const int CHANNELS = 1; // template specialization does not preserve params + const int STRIDE = 8; + sP -= CHANNELS*((STRIDE>>1)-1); + asm ( + "veor q0, q0, q0 \n"// result, initialize to 0 + + "1: \n" + + "vld1.16 {d4}, [%[sP]] \n"// load 4 16-bits mono samples + "vld1.16 {d6}, [%[sN]]! \n"// load 4 16-bits mono samples + "vld1.32 {q8}, [%[coefsP0]:128]! \n"// load 4 32-bits coefs + "vld1.32 {q10}, [%[coefsN0]:128]! \n"// load 4 32-bits coefs + + "vrev64.16 d4, d4 \n"// reverse 2 frames of the positive side + + "vshll.s16 q12, d4, #15 \n"// (stall) extend samples to 31 bits + "vshll.s16 q14, d6, #15 \n"// extend samples to 31 bits + + "vqrdmulh.s32 q12, q12, q8 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef + + "vadd.s32 q0, q0, q12 \n"// accumulate result + "vadd.s32 q0, q0, q14 \n"// (stall) accumulate result + + "subs %[count], %[count], #4 \n"// update loop counter + "sub %[sP], %[sP], #8 \n"// move pointer to next set of samples + + "bne 1b \n"// loop + + ASSEMBLY_ACCUMULATE_MONO + + : [out] "=Uv" (out[0]), + [count] "+r" (count), + [coefsP0] "+r" (coefsP), + [coefsN0] "+r" (coefsN), + [sP] "+r" (sP), + [sN] "+r" (sN) + : [vLR] "r" (volumeLR) + : "cc", "memory", + "q0", "q1", "q2", "q3", + "q8", "q9", "q10", "q11", + "q12", "q14" + ); +} + +template <> +inline void ProcessL<2, 8>(int32_t* const out, + int count, + const int32_t* coefsP, + const int32_t* coefsN, + const int16_t* sP, + const int16_t* sN, + const int32_t* const volumeLR) +{ + const int CHANNELS = 2; // template specialization does not preserve params + const int STRIDE = 8; + sP -= CHANNELS*((STRIDE>>1)-1); + asm ( + "veor q0, q0, q0 \n"// result, initialize to 0 + "veor q4, q4, q4 \n"// result, initialize to 0 + + "1: \n" + + "vld2.16 {d4, d5}, [%[sP]] \n"// load 4 16-bits stereo samples + "vld2.16 {d6, d7}, [%[sN]]! \n"// load 4 16-bits stereo samples + "vld1.32 {q8}, [%[coefsP0]:128]! \n"// load 4 32-bits coefs + "vld1.32 {q10}, [%[coefsN0]:128]! \n"// load 4 32-bits coefs + + "vrev64.16 q2, q2 \n"// reverse 2 frames of the positive side + + "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits + "vshll.s16 q13, d5, #15 \n"// extend samples to 31 bits + + "vshll.s16 q14, d6, #15 \n"// extend samples to 31 bits + "vshll.s16 q15, d7, #15 \n"// extend samples to 31 bits + + "vqrdmulh.s32 q12, q12, q8 \n"// multiply samples by coef + "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by coef + "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by coef + "vqrdmulh.s32 q15, q15, q10 \n"// multiply samples by coef + + "vadd.s32 q0, q0, q12 \n"// accumulate result + "vadd.s32 q4, q4, q13 \n"// accumulate result + "vadd.s32 q0, q0, q14 \n"// accumulate result + "vadd.s32 q4, q4, q15 \n"// accumulate result + + "subs %[count], %[count], #4 \n"// update loop counter + "sub %[sP], %[sP], #16 \n"// move pointer to next set of samples + + "bne 1b \n"// loop + + ASSEMBLY_ACCUMULATE_STEREO + + : [out] "=Uv" (out[0]), + [count] "+r" (count), + [coefsP0] "+r" (coefsP), + [coefsN0] "+r" (coefsN), + [sP] "+r" (sP), + [sN] "+r" (sN) + : [vLR] "r" (volumeLR) + : "cc", "memory", + "q0", "q1", "q2", "q3", "q4", + "q8", "q9", "q10", "q11", + "q12", "q13", "q14", "q15" + ); +} + +template <> +inline void Process<1, 8>(int32_t* const out, + int count, + const int32_t* coefsP, + const int32_t* coefsN, + const int32_t* coefsP1, + const int32_t* coefsN1, + const int16_t* sP, + const int16_t* sN, + uint32_t lerpP, + const int32_t* const volumeLR) +{ + const int CHANNELS = 1; // template specialization does not preserve params + const int STRIDE = 8; + sP -= CHANNELS*((STRIDE>>1)-1); + asm ( + "vmov.32 d2[0], %[lerpP] \n"// load the positive phase + "veor q0, q0, q0 \n"// result, initialize to 0 + + "1: \n" + + "vld1.16 {d4}, [%[sP]] \n"// load 4 16-bits mono samples + "vld1.16 {d6}, [%[sN]]! \n"// load 4 16-bits mono samples + "vld1.32 {q8}, [%[coefsP0]:128]! \n"// load 4 32-bits coefs + "vld1.32 {q9}, [%[coefsP1]:128]! \n"// load 4 32-bits coefs for interpolation + "vld1.32 {q10}, [%[coefsN1]:128]! \n"// load 4 32-bits coefs + "vld1.32 {q11}, [%[coefsN0]:128]! \n"// load 4 32-bits coefs for interpolation + + "vrev64.16 d4, d4 \n"// reverse 2 frames of the positive side + + "vsub.s32 q9, q9, q8 \n"// interpolate (step1) 1st set of coefs + "vsub.s32 q11, q11, q10 \n"// interpolate (step1) 2nd set of coets + "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits + + "vqrdmulh.s32 q9, q9, d2[0] \n"// interpolate (step2) 1st set of coefs + "vqrdmulh.s32 q11, q11, d2[0] \n"// interpolate (step2) 2nd set of coefs + "vshll.s16 q14, d6, #15 \n"// extend samples to 31 bits + + "vadd.s32 q8, q8, q9 \n"// interpolate (step3) 1st set + "vadd.s32 q10, q10, q11 \n"// interpolate (step4) 2nd set + + "vqrdmulh.s32 q12, q12, q8 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef + + "vadd.s32 q0, q0, q12 \n"// accumulate result + "vadd.s32 q0, q0, q14 \n"// accumulate result + + "subs %[count], %[count], #4 \n"// update loop counter + "sub %[sP], %[sP], #8 \n"// move pointer to next set of samples + + "bne 1b \n"// loop + + ASSEMBLY_ACCUMULATE_MONO + + : [out] "=Uv" (out[0]), + [count] "+r" (count), + [coefsP0] "+r" (coefsP), + [coefsP1] "+r" (coefsP1), + [coefsN0] "+r" (coefsN), + [coefsN1] "+r" (coefsN1), + [sP] "+r" (sP), + [sN] "+r" (sN) + : [lerpP] "r" (lerpP), + [vLR] "r" (volumeLR) + : "cc", "memory", + "q0", "q1", "q2", "q3", + "q8", "q9", "q10", "q11", + "q12", "q14" + ); +} + +template <> +inline +void Process<2, 8>(int32_t* const out, + int count, + const int32_t* coefsP, + const int32_t* coefsN, + const int32_t* coefsP1, + const int32_t* coefsN1, + const int16_t* sP, + const int16_t* sN, + uint32_t lerpP, + const int32_t* const volumeLR) +{ + const int CHANNELS = 2; // template specialization does not preserve params + const int STRIDE = 8; + sP -= CHANNELS*((STRIDE>>1)-1); + asm ( + "vmov.32 d2[0], %[lerpP] \n"// load the positive phase + "veor q0, q0, q0 \n"// result, initialize to 0 + "veor q4, q4, q4 \n"// result, initialize to 0 + + "1: \n" + "vld2.16 {d4, d5}, [%[sP]] \n"// load 4 16-bits stereo samples + "vld2.16 {d6, d7}, [%[sN]]! \n"// load 4 16-bits stereo samples + "vld1.32 {q8}, [%[coefsP0]:128]! \n"// load 4 32-bits coefs + "vld1.32 {q9}, [%[coefsP1]:128]! \n"// load 4 32-bits coefs for interpolation + "vld1.32 {q10}, [%[coefsN1]:128]! \n"// load 4 32-bits coefs + "vld1.32 {q11}, [%[coefsN0]:128]! \n"// load 4 32-bits coefs for interpolation + + "vrev64.16 q2, q2 \n"// (reversed) 2 frames of the positive side + + "vsub.s32 q9, q9, q8 \n"// interpolate (step1) 1st set of coefs + "vsub.s32 q11, q11, q10 \n"// interpolate (step1) 2nd set of coets + "vshll.s16 q12, d4, #15 \n"// extend samples to 31 bits + "vshll.s16 q13, d5, #15 \n"// extend samples to 31 bits + + "vqrdmulh.s32 q9, q9, d2[0] \n"// interpolate (step2) 1st set of coefs + "vqrdmulh.s32 q11, q11, d2[1] \n"// interpolate (step3) 2nd set of coefs + "vshll.s16 q14, d6, #15 \n"// extend samples to 31 bits + "vshll.s16 q15, d7, #15 \n"// extend samples to 31 bits + + "vadd.s32 q8, q8, q9 \n"// interpolate (step3) 1st set + "vadd.s32 q10, q10, q11 \n"// interpolate (step4) 2nd set + + "vqrdmulh.s32 q12, q12, q8 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q13, q13, q8 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q14, q14, q10 \n"// multiply samples by interpolated coef + "vqrdmulh.s32 q15, q15, q10 \n"// multiply samples by interpolated coef + + "vadd.s32 q0, q0, q12 \n"// accumulate result + "vadd.s32 q4, q4, q13 \n"// accumulate result + "vadd.s32 q0, q0, q14 \n"// accumulate result + "vadd.s32 q4, q4, q15 \n"// accumulate result + + "subs %[count], %[count], #4 \n"// update loop counter + "sub %[sP], %[sP], #16 \n"// move pointer to next set of samples + + "bne 1b \n"// loop + + ASSEMBLY_ACCUMULATE_STEREO + + : [out] "=Uv" (out[0]), + [count] "+r" (count), + [coefsP0] "+r" (coefsP), + [coefsP1] "+r" (coefsP1), + [coefsN0] "+r" (coefsN), + [coefsN1] "+r" (coefsN1), + [sP] "+r" (sP), + [sN] "+r" (sN) + : [lerpP] "r" (lerpP), + [vLR] "r" (volumeLR) + : "cc", "memory", + "q0", "q1", "q2", "q3", "q4", + "q8", "q9", "q10", "q11", + "q12", "q13", "q14", "q15" + ); +} + +#endif //USE_NEON + +}; // namespace android + +#endif /*ANDROID_AUDIO_RESAMPLER_FIR_PROCESS_NEON_H*/ diff --git a/services/audioflinger/AudioResamplerSinc.cpp b/services/audioflinger/AudioResamplerSinc.cpp index e50b192..d03e578 100644 --- a/services/audioflinger/AudioResamplerSinc.cpp +++ b/services/audioflinger/AudioResamplerSinc.cpp @@ -27,6 +27,7 @@ #include <cutils/properties.h> #include <utils/Log.h> +#include <audio_utils/primitives.h> #include "AudioResamplerSinc.h" @@ -452,9 +453,9 @@ int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a) // ---------------------------------------------------------------------------- -AudioResamplerSinc::AudioResamplerSinc(int bitDepth, +AudioResamplerSinc::AudioResamplerSinc( int inChannelCount, int32_t sampleRate, src_quality quality) - : AudioResampler(bitDepth, inChannelCount, sampleRate, quality), + : AudioResampler(inChannelCount, sampleRate, quality), mState(0), mImpulse(0), mRingFull(0), mFirCoefs(0) { /* @@ -500,10 +501,12 @@ void AudioResamplerSinc::init() { mRingFull = mImpulse + (numCoefs+1)*mChannelCount; } -void AudioResamplerSinc::setVolume(int16_t left, int16_t right) { +void AudioResamplerSinc::setVolume(float left, float right) { AudioResampler::setVolume(left, right); - mVolumeSIMD[0] = int32_t(left)<<16; - mVolumeSIMD[1] = int32_t(right)<<16; + // convert to U4_28 (rounding down). + // integer volume values are clamped to 0 to UNITY_GAIN. + mVolumeSIMD[0] = u4_28_from_float(clampFloatVol(left)); + mVolumeSIMD[1] = u4_28_from_float(clampFloatVol(right)); } void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount, @@ -543,7 +546,7 @@ void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount, uint32_t phaseIncrement = mPhaseIncrement; size_t outputIndex = 0; size_t outputSampleCount = outFrameCount * 2; - size_t inFrameCount = (outFrameCount*mInSampleRate)/mSampleRate; + size_t inFrameCount = getInFrameCountRequired(outFrameCount); while (outputIndex < outputSampleCount) { // buffer is empty, fetch a new one diff --git a/services/audioflinger/AudioResamplerSinc.h b/services/audioflinger/AudioResamplerSinc.h index 1ea4474..4691d0a 100644 --- a/services/audioflinger/AudioResamplerSinc.h +++ b/services/audioflinger/AudioResamplerSinc.h @@ -34,7 +34,7 @@ typedef int32_t (*readResampleFirLerpIntBitsFn)(); class AudioResamplerSinc : public AudioResampler { public: - AudioResamplerSinc(int bitDepth, int inChannelCount, int32_t sampleRate, + AudioResamplerSinc(int inChannelCount, int32_t sampleRate, src_quality quality = HIGH_QUALITY); virtual ~AudioResamplerSinc(); @@ -44,7 +44,7 @@ public: private: void init(); - virtual void setVolume(int16_t left, int16_t right); + virtual void setVolume(float left, float right); template<int CHANNELS> void resample(int32_t* out, size_t outFrameCount, diff --git a/services/audioflinger/Configuration.h b/services/audioflinger/Configuration.h index 0754d9d..6a8aeb1 100644 --- a/services/audioflinger/Configuration.h +++ b/services/audioflinger/Configuration.h @@ -31,6 +31,7 @@ // uncomment to enable fast mixer to take performance samples for later statistical analysis #define FAST_MIXER_STATISTICS +// FIXME rename to FAST_THREAD_STATISTICS // uncomment for debugging timing problems related to StateQueue::push() //#define STATE_QUEUE_DUMP diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp index 010e233..365f271 100644 --- a/services/audioflinger/Effects.cpp +++ b/services/audioflinger/Effects.cpp @@ -44,6 +44,8 @@ #define ALOGVV(a...) do { } while(0) #endif +#define min(a, b) ((a) < (b) ? (a) : (b)) + namespace android { // ---------------------------------------------------------------------------- @@ -116,8 +118,9 @@ status_t AudioFlinger::EffectModule::addHandle(EffectHandle *handle) continue; } // first non destroyed handle is considered in control - if (controlHandle == NULL) + if (controlHandle == NULL) { controlHandle = h; + } if (h->priority() <= priority) { break; } @@ -804,7 +807,112 @@ bool AudioFlinger::EffectModule::isOffloaded() const return mOffloaded; } -void AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args) +String8 effectFlagsToString(uint32_t flags) { + String8 s; + + s.append("conn. mode: "); + switch (flags & EFFECT_FLAG_TYPE_MASK) { + case EFFECT_FLAG_TYPE_INSERT: s.append("insert"); break; + case EFFECT_FLAG_TYPE_AUXILIARY: s.append("auxiliary"); break; + case EFFECT_FLAG_TYPE_REPLACE: s.append("replace"); break; + case EFFECT_FLAG_TYPE_PRE_PROC: s.append("preproc"); break; + case EFFECT_FLAG_TYPE_POST_PROC: s.append("postproc"); break; + default: s.append("unknown/reserved"); break; + } + s.append(", "); + + s.append("insert pref: "); + switch (flags & EFFECT_FLAG_INSERT_MASK) { + case EFFECT_FLAG_INSERT_ANY: s.append("any"); break; + case EFFECT_FLAG_INSERT_FIRST: s.append("first"); break; + case EFFECT_FLAG_INSERT_LAST: s.append("last"); break; + case EFFECT_FLAG_INSERT_EXCLUSIVE: s.append("exclusive"); break; + default: s.append("unknown/reserved"); break; + } + s.append(", "); + + s.append("volume mgmt: "); + switch (flags & EFFECT_FLAG_VOLUME_MASK) { + case EFFECT_FLAG_VOLUME_NONE: s.append("none"); break; + case EFFECT_FLAG_VOLUME_CTRL: s.append("implements control"); break; + case EFFECT_FLAG_VOLUME_IND: s.append("requires indication"); break; + default: s.append("unknown/reserved"); break; + } + s.append(", "); + + uint32_t devind = flags & EFFECT_FLAG_DEVICE_MASK; + if (devind) { + s.append("device indication: "); + switch (devind) { + case EFFECT_FLAG_DEVICE_IND: s.append("requires updates"); break; + default: s.append("unknown/reserved"); break; + } + s.append(", "); + } + + s.append("input mode: "); + switch (flags & EFFECT_FLAG_INPUT_MASK) { + case EFFECT_FLAG_INPUT_DIRECT: s.append("direct"); break; + case EFFECT_FLAG_INPUT_PROVIDER: s.append("provider"); break; + case EFFECT_FLAG_INPUT_BOTH: s.append("direct+provider"); break; + default: s.append("not set"); break; + } + s.append(", "); + + s.append("output mode: "); + switch (flags & EFFECT_FLAG_OUTPUT_MASK) { + case EFFECT_FLAG_OUTPUT_DIRECT: s.append("direct"); break; + case EFFECT_FLAG_OUTPUT_PROVIDER: s.append("provider"); break; + case EFFECT_FLAG_OUTPUT_BOTH: s.append("direct+provider"); break; + default: s.append("not set"); break; + } + s.append(", "); + + uint32_t accel = flags & EFFECT_FLAG_HW_ACC_MASK; + if (accel) { + s.append("hardware acceleration: "); + switch (accel) { + case EFFECT_FLAG_HW_ACC_SIMPLE: s.append("non-tunneled"); break; + case EFFECT_FLAG_HW_ACC_TUNNEL: s.append("tunneled"); break; + default: s.append("unknown/reserved"); break; + } + s.append(", "); + } + + uint32_t modeind = flags & EFFECT_FLAG_AUDIO_MODE_MASK; + if (modeind) { + s.append("mode indication: "); + switch (modeind) { + case EFFECT_FLAG_AUDIO_MODE_IND: s.append("required"); break; + default: s.append("unknown/reserved"); break; + } + s.append(", "); + } + + uint32_t srcind = flags & EFFECT_FLAG_AUDIO_SOURCE_MASK; + if (srcind) { + s.append("source indication: "); + switch (srcind) { + case EFFECT_FLAG_AUDIO_SOURCE_IND: s.append("required"); break; + default: s.append("unknown/reserved"); break; + } + s.append(", "); + } + + if (flags & EFFECT_FLAG_OFFLOAD_MASK) { + s.append("offloadable, "); + } + + int len = s.length(); + if (s.length() > 2) { + char *str = s.lockBuffer(len); + s.unlockBuffer(len - 2); + } + return s; +} + + +void AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args __unused) { const size_t SIZE = 256; char buffer[SIZE]; @@ -838,9 +946,10 @@ void AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args) mDescriptor.type.node[2], mDescriptor.type.node[3],mDescriptor.type.node[4],mDescriptor.type.node[5]); result.append(buffer); - snprintf(buffer, SIZE, "\t\t- apiVersion: %08X\n\t\t- flags: %08X\n", + snprintf(buffer, SIZE, "\t\t- apiVersion: %08X\n\t\t- flags: %08X (%s)\n", mDescriptor.apiVersion, - mDescriptor.flags); + mDescriptor.flags, + effectFlagsToString(mDescriptor.flags).string()); result.append(buffer); snprintf(buffer, SIZE, "\t\t- name: %s\n", mDescriptor.name); @@ -851,37 +960,37 @@ void AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args) result.append("\t\t- Input configuration:\n"); result.append("\t\t\tFrames Smp rate Channels Format Buffer\n"); - snprintf(buffer, SIZE, "\t\t\t%05zu %05d %08x %6d %p\n", + snprintf(buffer, SIZE, "\t\t\t%05zu %05d %08x %6d (%s) %p\n", mConfig.inputCfg.buffer.frameCount, mConfig.inputCfg.samplingRate, mConfig.inputCfg.channels, mConfig.inputCfg.format, + formatToString((audio_format_t)mConfig.inputCfg.format), mConfig.inputCfg.buffer.raw); result.append(buffer); result.append("\t\t- Output configuration:\n"); result.append("\t\t\tBuffer Frames Smp rate Channels Format\n"); - snprintf(buffer, SIZE, "\t\t\t%p %05zu %05d %08x %d\n", + snprintf(buffer, SIZE, "\t\t\t%p %05zu %05d %08x %d (%s)\n", mConfig.outputCfg.buffer.raw, mConfig.outputCfg.buffer.frameCount, mConfig.outputCfg.samplingRate, mConfig.outputCfg.channels, - mConfig.outputCfg.format); + mConfig.outputCfg.format, + formatToString((audio_format_t)mConfig.outputCfg.format)); result.append(buffer); snprintf(buffer, SIZE, "\t\t%zu Clients:\n", mHandles.size()); result.append(buffer); - result.append("\t\t\tPid Priority Ctrl Locked client server\n"); + result.append("\t\t\t Pid Priority Ctrl Locked client server\n"); for (size_t i = 0; i < mHandles.size(); ++i) { EffectHandle *handle = mHandles[i]; if (handle != NULL && !handle->destroyed_l()) { - handle->dump(buffer, SIZE); + handle->dumpToBuffer(buffer, SIZE); result.append(buffer); } } - result.append("\n"); - write(fd, result.string(), result.length()); if (locked) { @@ -911,18 +1020,15 @@ AudioFlinger::EffectHandle::EffectHandle(const sp<EffectModule>& effect, } int bufOffset = ((sizeof(effect_param_cblk_t) - 1) / sizeof(int) + 1) * sizeof(int); mCblkMemory = client->heap()->allocate(EFFECT_PARAM_BUFFER_SIZE + bufOffset); - if (mCblkMemory != 0) { - mCblk = static_cast<effect_param_cblk_t *>(mCblkMemory->pointer()); - - if (mCblk != NULL) { - new(mCblk) effect_param_cblk_t(); - mBuffer = (uint8_t *)mCblk + bufOffset; - } - } else { + if (mCblkMemory == 0 || + (mCblk = static_cast<effect_param_cblk_t *>(mCblkMemory->pointer())) == NULL) { ALOGE("not enough memory for Effect size=%u", EFFECT_PARAM_BUFFER_SIZE + sizeof(effect_param_cblk_t)); + mCblkMemory.clear(); return; } + new(mCblk) effect_param_cblk_t(); + mBuffer = (uint8_t *)mCblk + bufOffset; } AudioFlinger::EffectHandle::~EffectHandle() @@ -939,6 +1045,11 @@ AudioFlinger::EffectHandle::~EffectHandle() disconnect(false); } +status_t AudioFlinger::EffectHandle::initCheck() +{ + return mClient == 0 || mCblkMemory != 0 ? OK : NO_MEMORY; +} + status_t AudioFlinger::EffectHandle::enable() { ALOGV("enable %p", this); @@ -1053,8 +1164,8 @@ void AudioFlinger::EffectHandle::disconnect(bool unpinIfLast) mCblk->~effect_param_cblk_t(); // destroy our shared-structure. } mCblkMemory.clear(); // free the shared memory before releasing the heap it belongs to - // Client destructor must run with AudioFlinger mutex locked - Mutex::Autolock _l(mClient->audioFlinger()->mLock); + // Client destructor must run with AudioFlinger client mutex locked + Mutex::Autolock _l(mClient->audioFlinger()->mClientLock); mClient.clear(); } } @@ -1179,15 +1290,15 @@ status_t AudioFlinger::EffectHandle::onTransact( } -void AudioFlinger::EffectHandle::dump(char* buffer, size_t size) +void AudioFlinger::EffectHandle::dumpToBuffer(char* buffer, size_t size) { bool locked = mCblk != NULL && AudioFlinger::dumpTryLock(mCblk->lock); - snprintf(buffer, size, "\t\t\t%05d %05d %01u %01u %05u %05u\n", + snprintf(buffer, size, "\t\t\t%5d %5d %3s %3s %5u %5u\n", (mClient == 0) ? getpid_cached : mClient->pid(), mPriority, - mHasControl, - !locked, + mHasControl ? "yes" : "no", + locked ? "yes" : "no", mCblk ? mCblk->clientIndex : 0, mCblk ? mCblk->serverIndex : 0 ); @@ -1278,7 +1389,13 @@ void AudioFlinger::EffectChain::clearInputBuffer() // Must be called with EffectChain::mLock locked void AudioFlinger::EffectChain::clearInputBuffer_l(sp<ThreadBase> thread) { - memset(mInBuffer, 0, thread->frameCount() * thread->frameSize()); + // TODO: This will change in the future, depending on multichannel + // and sample format changes for effects. + // Currently effects processing is only available for stereo, AUDIO_FORMAT_PCM_16_BIT + // (4 bytes frame size) + const size_t frameSize = + audio_bytes_per_sample(AUDIO_FORMAT_PCM_16_BIT) * min(FCC_2, thread->channelCount()); + memset(mInBuffer, 0, thread->frameCount() * frameSize); } // Must be called with EffectChain::mLock locked @@ -1568,33 +1685,35 @@ void AudioFlinger::EffectChain::dump(int fd, const Vector<String16>& args) char buffer[SIZE]; String8 result; - snprintf(buffer, SIZE, "Effects for session %d:\n", mSessionId); + size_t numEffects = mEffects.size(); + snprintf(buffer, SIZE, " %d effects for session %d\n", numEffects, mSessionId); result.append(buffer); - bool locked = AudioFlinger::dumpTryLock(mLock); - // failed to lock - AudioFlinger is probably deadlocked - if (!locked) { - result.append("\tCould not lock mutex:\n"); - } + if (numEffects) { + bool locked = AudioFlinger::dumpTryLock(mLock); + // failed to lock - AudioFlinger is probably deadlocked + if (!locked) { + result.append("\tCould not lock mutex:\n"); + } - result.append("\tNum fx In buffer Out buffer Active tracks:\n"); - snprintf(buffer, SIZE, "\t%02zu %p %p %d\n", - mEffects.size(), - mInBuffer, - mOutBuffer, - mActiveTrackCnt); - result.append(buffer); - write(fd, result.string(), result.size()); + result.append("\tIn buffer Out buffer Active tracks:\n"); + snprintf(buffer, SIZE, "\t%p %p %d\n", + mInBuffer, + mOutBuffer, + mActiveTrackCnt); + result.append(buffer); + write(fd, result.string(), result.size()); - for (size_t i = 0; i < mEffects.size(); ++i) { - sp<EffectModule> effect = mEffects[i]; - if (effect != 0) { - effect->dump(fd, args); + for (size_t i = 0; i < numEffects; ++i) { + sp<EffectModule> effect = mEffects[i]; + if (effect != 0) { + effect->dump(fd, args); + } } - } - if (locked) { - mLock.unlock(); + if (locked) { + mLock.unlock(); + } } } diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h index b717857..4170fd4 100644 --- a/services/audioflinger/Effects.h +++ b/services/audioflinger/Effects.h @@ -169,6 +169,7 @@ public: const sp<IEffectClient>& effectClient, int32_t priority); virtual ~EffectHandle(); + virtual status_t initCheck(); // IEffect virtual status_t enable(); @@ -208,7 +209,7 @@ public: // destroyed_l() must be called with the associated EffectModule mLock held bool destroyed_l() const { return mDestroyed; } - void dump(char* buffer, size_t size); + void dumpToBuffer(char* buffer, size_t size); protected: friend class AudioFlinger; // for mEffect, mHasControl, mEnabled @@ -269,6 +270,7 @@ public: sp<EffectModule> getEffectFromDesc_l(effect_descriptor_t *descriptor); sp<EffectModule> getEffectFromId_l(int id); sp<EffectModule> getEffectFromType_l(const effect_uuid_t *type); + // FIXME use float to improve the dynamic range bool setVolume_l(uint32_t *left, uint32_t *right); void setDevice_l(audio_devices_t device); void setMode_l(audio_mode_t mode); 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 6d87838..2678cbf 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -36,620 +36,436 @@ #include <cpustats/ThreadCpuUsage.h> #endif #endif +#include <audio_utils/format.h> #include "AudioMixer.h" #include "FastMixer.h" -#define FAST_HOT_IDLE_NS 1000000L // 1 ms: time to sleep while hot idling -#define FAST_DEFAULT_NS 999999999L // ~1 sec: default time to sleep -#define MIN_WARMUP_CYCLES 2 // minimum number of loop cycles to wait for warmup -#define MAX_WARMUP_CYCLES 10 // maximum number of loop cycles to wait for warmup - #define FCC_2 2 // fixed channel count assumption namespace android { -// Fast mixer thread -bool FastMixer::threadLoop() +/*static*/ const FastMixerState FastMixer::initial; + +FastMixer::FastMixer() : FastThread(), + slopNs(0), + // fastTrackNames + // generations + outputSink(NULL), + outputSinkGen(0), + mixer(NULL), + mSinkBuffer(NULL), + mSinkBufferSize(0), + mSinkChannelCount(FCC_2), + mMixerBuffer(NULL), + mMixerBufferSize(0), + mMixerBufferFormat(AUDIO_FORMAT_PCM_16_BIT), + mMixerBufferState(UNDEFINED), + format(Format_Invalid), + sampleRate(0), + fastTracksGen(0), + totalNativeFramesWritten(0), + // timestamp + nativeFramesWrittenButNotPresented(0) // the = 0 is to silence the compiler { - static const FastMixerState initial; - const FastMixerState *previous = &initial, *current = &initial; - FastMixerState preIdle; // copy of state before we went into idle - struct timespec oldTs = {0, 0}; - bool oldTsValid = false; - long slopNs = 0; // accumulated time we've woken up too early (> 0) or too late (< 0) - long sleepNs = -1; // -1: busy wait, 0: sched_yield, > 0: nanosleep - int fastTrackNames[FastMixerState::kMaxFastTracks]; // handles used by mixer to identify tracks - int generations[FastMixerState::kMaxFastTracks]; // last observed mFastTracks[i].mGeneration + // FIXME pass initial as parameter to base class constructor, and make it static local + previous = &initial; + current = &initial; + + mDummyDumpState = &dummyDumpState; + // TODO: Add channel mask to NBAIO_Format. + // We assume that the channel mask must be a valid positional channel mask. + mSinkChannelMask = audio_channel_out_mask_from_count(mSinkChannelCount); + unsigned i; for (i = 0; i < FastMixerState::kMaxFastTracks; ++i) { fastTrackNames[i] = -1; generations[i] = 0; } - NBAIO_Sink *outputSink = NULL; - int outputSinkGen = 0; - AudioMixer* mixer = NULL; - short *mixBuffer = NULL; - enum {UNDEFINED, MIXED, ZEROED} mixBufferState = UNDEFINED; - NBAIO_Format format = Format_Invalid; - unsigned sampleRate = 0; - int fastTracksGen = 0; - long periodNs = 0; // expected period; the time required to render one mix buffer - long underrunNs = 0; // underrun likely when write cycle is greater than this value - long overrunNs = 0; // overrun likely when write cycle is less than this value - long forceNs = 0; // if overrun detected, force the write cycle to take this much time - long warmupNs = 0; // warmup complete when write cycle is greater than to this value - FastMixerDumpState dummyDumpState, *dumpState = &dummyDumpState; - bool ignoreNextOverrun = true; // used to ignore initial overrun and first after an underrun #ifdef FAST_MIXER_STATISTICS - struct timespec oldLoad = {0, 0}; // previous value of clock_gettime(CLOCK_THREAD_CPUTIME_ID) - bool oldLoadValid = false; // whether oldLoad is valid - uint32_t bounds = 0; - bool full = false; // whether we have collected at least mSamplingN samples -#ifdef CPU_FREQUENCY_STATISTICS - ThreadCpuUsage tcu; // for reading the current CPU clock frequency in kHz -#endif + oldLoad.tv_sec = 0; + oldLoad.tv_nsec = 0; #endif - unsigned coldGen = 0; // last observed mColdGen - bool isWarm = false; // true means ready to mix, false means wait for warmup before mixing - struct timespec measuredWarmupTs = {0, 0}; // how long did it take for warmup to complete - uint32_t warmupCycles = 0; // counter of number of loop cycles required to warmup - NBAIO_Sink* teeSink = NULL; // if non-NULL, then duplicate write() to this non-blocking sink - NBLog::Writer dummyLogWriter, *logWriter = &dummyLogWriter; - uint32_t totalNativeFramesWritten = 0; // copied to dumpState->mFramesWritten - - // next 2 fields are valid only when timestampStatus == NO_ERROR - AudioTimestamp timestamp; - uint32_t nativeFramesWrittenButNotPresented = 0; // the = 0 is to silence the compiler - status_t timestampStatus = INVALID_OPERATION; - - for (;;) { - - // either nanosleep, sched_yield, or busy wait - if (sleepNs >= 0) { - if (sleepNs > 0) { - ALOG_ASSERT(sleepNs < 1000000000); - const struct timespec req = {0, sleepNs}; - nanosleep(&req, NULL); - } else { - sched_yield(); - } - } - // default to long sleep for next cycle - sleepNs = FAST_DEFAULT_NS; - - // poll for state change - const FastMixerState *next = mSQ.poll(); - if (next == NULL) { - // continue to use the default initial state until a real state is available - ALOG_ASSERT(current == &initial && previous == &initial); - next = current; - } +} - FastMixerState::Command command = next->mCommand; - if (next != current) { +FastMixer::~FastMixer() +{ +} - // As soon as possible of learning of a new dump area, start using it - dumpState = next->mDumpState != NULL ? next->mDumpState : &dummyDumpState; - teeSink = next->mTeeSink; - logWriter = next->mNBLogWriter != NULL ? next->mNBLogWriter : &dummyLogWriter; - if (mixer != NULL) { - mixer->setLog(logWriter); - } +FastMixerStateQueue* FastMixer::sq() +{ + return &mSQ; +} - // We want to always have a valid reference to the previous (non-idle) state. - // However, the state queue only guarantees access to current and previous states. - // So when there is a transition from a non-idle state into an idle state, we make a - // copy of the last known non-idle state so it is still available on return from idle. - // The possible transitions are: - // non-idle -> non-idle update previous from current in-place - // non-idle -> idle update previous from copy of current - // idle -> idle don't update previous - // idle -> non-idle don't update previous - if (!(current->mCommand & FastMixerState::IDLE)) { - if (command & FastMixerState::IDLE) { - preIdle = *current; - current = &preIdle; - oldTsValid = false; -#ifdef FAST_MIXER_STATISTICS - oldLoadValid = false; -#endif - ignoreNextOverrun = true; - } - previous = current; - } - current = next; - } -#if !LOG_NDEBUG - next = NULL; // not referenced again -#endif +const FastThreadState *FastMixer::poll() +{ + return mSQ.poll(); +} - dumpState->mCommand = command; - - switch (command) { - case FastMixerState::INITIAL: - case FastMixerState::HOT_IDLE: - sleepNs = FAST_HOT_IDLE_NS; - continue; - case FastMixerState::COLD_IDLE: - // only perform a cold idle command once - // FIXME consider checking previous state and only perform if previous != COLD_IDLE - if (current->mColdGen != coldGen) { - int32_t *coldFutexAddr = current->mColdFutexAddr; - ALOG_ASSERT(coldFutexAddr != NULL); - int32_t old = android_atomic_dec(coldFutexAddr); - if (old <= 0) { - (void) syscall(__NR_futex, coldFutexAddr, FUTEX_WAIT_PRIVATE, old - 1, NULL); - } - int policy = sched_getscheduler(0); - if (!(policy == SCHED_FIFO || policy == SCHED_RR)) { - ALOGE("did not receive expected priority boost"); - } - // This may be overly conservative; there could be times that the normal mixer - // requests such a brief cold idle that it doesn't require resetting this flag. - isWarm = false; - measuredWarmupTs.tv_sec = 0; - measuredWarmupTs.tv_nsec = 0; - warmupCycles = 0; - sleepNs = -1; - coldGen = current->mColdGen; -#ifdef FAST_MIXER_STATISTICS - bounds = 0; - full = false; -#endif - oldTsValid = !clock_gettime(CLOCK_MONOTONIC, &oldTs); - timestampStatus = INVALID_OPERATION; - } else { - sleepNs = FAST_HOT_IDLE_NS; - } - continue; - case FastMixerState::EXIT: - delete mixer; - delete[] mixBuffer; - return false; - case FastMixerState::MIX: - case FastMixerState::WRITE: - case FastMixerState::MIX_WRITE: - break; - default: - LOG_FATAL("bad command %d", command); +void FastMixer::setLog(NBLog::Writer *logWriter) +{ + if (mixer != NULL) { + mixer->setLog(logWriter); + } +} + +void FastMixer::onIdle() +{ + preIdle = *(const FastMixerState *)current; + current = &preIdle; +} + +void FastMixer::onExit() +{ + delete mixer; + free(mMixerBuffer); + free(mSinkBuffer); +} + +bool FastMixer::isSubClassCommand(FastThreadState::Command command) +{ + switch ((FastMixerState::Command) command) { + case FastMixerState::MIX: + case FastMixerState::WRITE: + case FastMixerState::MIX_WRITE: + return true; + default: + return false; + } +} + +void FastMixer::onStateChange() +{ + const FastMixerState * const current = (const FastMixerState *) this->current; + const FastMixerState * const previous = (const FastMixerState *) this->previous; + FastMixerDumpState * const dumpState = (FastMixerDumpState *) this->dumpState; + const size_t frameCount = current->mFrameCount; + + // handle state change here, but since we want to diff the state, + // we're prepared for previous == &initial the first time through + unsigned previousTrackMask; + + // check for change in output HAL configuration + NBAIO_Format previousFormat = format; + if (current->mOutputSinkGen != outputSinkGen) { + outputSink = current->mOutputSink; + outputSinkGen = current->mOutputSinkGen; + if (outputSink == NULL) { + format = Format_Invalid; + sampleRate = 0; + mSinkChannelCount = 0; + mSinkChannelMask = AUDIO_CHANNEL_NONE; + } else { + format = outputSink->format(); + sampleRate = Format_sampleRate(format); + mSinkChannelCount = Format_channelCount(format); + LOG_ALWAYS_FATAL_IF(mSinkChannelCount > AudioMixer::MAX_NUM_CHANNELS); + + // TODO: Add channel mask to NBAIO_Format + // We assume that the channel mask must be a valid positional channel mask. + mSinkChannelMask = audio_channel_out_mask_from_count(mSinkChannelCount); } + dumpState->mSampleRate = sampleRate; + } - // there is a non-idle state available to us; did the state change? - size_t frameCount = current->mFrameCount; - if (current != previous) { - - // handle state change here, but since we want to diff the state, - // we're prepared for previous == &initial the first time through - unsigned previousTrackMask; - - // check for change in output HAL configuration - NBAIO_Format previousFormat = format; - if (current->mOutputSinkGen != outputSinkGen) { - outputSink = current->mOutputSink; - outputSinkGen = current->mOutputSinkGen; - if (outputSink == NULL) { - format = Format_Invalid; - sampleRate = 0; - } else { - format = outputSink->format(); - sampleRate = Format_sampleRate(format); - ALOG_ASSERT(Format_channelCount(format) == FCC_2); - } + if ((!Format_isEqual(format, previousFormat)) || (frameCount != previous->mFrameCount)) { + // FIXME to avoid priority inversion, don't delete here + delete mixer; + mixer = NULL; + 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); + const size_t mixerFrameSize = mSinkChannelCount + * audio_bytes_per_sample(mMixerBufferFormat); + mMixerBufferSize = mixerFrameSize * frameCount; + (void)posix_memalign(&mMixerBuffer, 32, mMixerBufferSize); + const size_t sinkFrameSize = mSinkChannelCount + * audio_bytes_per_sample(format.mFormat); + if (sinkFrameSize > mixerFrameSize) { // need a sink buffer + mSinkBufferSize = sinkFrameSize * frameCount; + (void)posix_memalign(&mSinkBuffer, 32, mSinkBufferSize); } - - if ((format != previousFormat) || (frameCount != previous->mFrameCount)) { - // FIXME to avoid priority inversion, don't delete here - delete mixer; - mixer = NULL; - delete[] mixBuffer; - mixBuffer = 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); - mixBuffer = new short[frameCount * FCC_2]; - 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; - } - mixBufferState = UNDEFINED; + 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; + } + mMixerBufferState = UNDEFINED; #if !LOG_NDEBUG - for (i = 0; i < FastMixerState::kMaxFastTracks; ++i) { - fastTrackNames[i] = -1; - } + for (unsigned i = 0; i < FastMixerState::kMaxFastTracks; ++i) { + fastTrackNames[i] = -1; + } #endif - // we need to reconfigure all active tracks - previousTrackMask = 0; - fastTracksGen = current->mFastTracksGen - 1; - dumpState->mFrameCount = frameCount; - } else { - previousTrackMask = previous->mTrackMask; - } + // we need to reconfigure all active tracks + previousTrackMask = 0; + fastTracksGen = current->mFastTracksGen - 1; + dumpState->mFrameCount = frameCount; + } else { + previousTrackMask = previous->mTrackMask; + } - // check for change in active track set - unsigned currentTrackMask = current->mTrackMask; - dumpState->mTrackMask = currentTrackMask; - if (current->mFastTracksGen != fastTracksGen) { - ALOG_ASSERT(mixBuffer != NULL); - int name; - - // process removed tracks first to avoid running out of track names - unsigned removedTracks = previousTrackMask & ~currentTrackMask; - while (removedTracks != 0) { - i = __builtin_ctz(removedTracks); - removedTracks &= ~(1 << i); - const FastTrack* fastTrack = ¤t->mFastTracks[i]; - ALOG_ASSERT(fastTrack->mBufferProvider == NULL); - if (mixer != NULL) { - name = fastTrackNames[i]; - ALOG_ASSERT(name >= 0); - mixer->deleteTrackName(name); - } + // check for change in active track set + const unsigned currentTrackMask = current->mTrackMask; + dumpState->mTrackMask = currentTrackMask; + if (current->mFastTracksGen != fastTracksGen) { + ALOG_ASSERT(mMixerBuffer != NULL); + int name; + + // process removed tracks first to avoid running out of track names + unsigned removedTracks = previousTrackMask & ~currentTrackMask; + while (removedTracks != 0) { + int i = __builtin_ctz(removedTracks); + removedTracks &= ~(1 << i); + const FastTrack* fastTrack = ¤t->mFastTracks[i]; + ALOG_ASSERT(fastTrack->mBufferProvider == NULL); + if (mixer != NULL) { + name = fastTrackNames[i]; + ALOG_ASSERT(name >= 0); + mixer->deleteTrackName(name); + } #if !LOG_NDEBUG - fastTrackNames[i] = -1; + fastTrackNames[i] = -1; #endif - // don't reset track dump state, since other side is ignoring it - generations[i] = fastTrack->mGeneration; - } + // don't reset track dump state, since other side is ignoring it + generations[i] = fastTrack->mGeneration; + } - // now process added tracks - unsigned addedTracks = currentTrackMask & ~previousTrackMask; - while (addedTracks != 0) { - i = __builtin_ctz(addedTracks); - addedTracks &= ~(1 << i); - const FastTrack* fastTrack = ¤t->mFastTracks[i]; - AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider; - ALOG_ASSERT(bufferProvider != NULL && fastTrackNames[i] == -1); - if (mixer != NULL) { - // calling getTrackName with default channel mask and a random invalid - // sessionId (no effects here) - name = mixer->getTrackName(AUDIO_CHANNEL_OUT_STEREO, -555); - ALOG_ASSERT(name >= 0); - fastTrackNames[i] = name; - mixer->setBufferProvider(name, bufferProvider); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, - (void *) mixBuffer); - // newly allocated track names default to full scale volume - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, - (void *)(uintptr_t)fastTrack->mChannelMask); - mixer->enable(name); - } - generations[i] = fastTrack->mGeneration; - } + // now process added tracks + unsigned addedTracks = currentTrackMask & ~previousTrackMask; + while (addedTracks != 0) { + int i = __builtin_ctz(addedTracks); + addedTracks &= ~(1 << i); + const FastTrack* fastTrack = ¤t->mFastTracks[i]; + AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider; + ALOG_ASSERT(bufferProvider != NULL && fastTrackNames[i] == -1); + if (mixer != NULL) { + name = mixer->getTrackName(fastTrack->mChannelMask, + fastTrack->mFormat, AUDIO_SESSION_OUTPUT_MIX); + ALOG_ASSERT(name >= 0); + fastTrackNames[i] = name; + mixer->setBufferProvider(name, bufferProvider); + 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->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, + (void *)(uintptr_t)fastTrack->mChannelMask); + mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, + (void *)(uintptr_t)mSinkChannelMask); + mixer->enable(name); + } + generations[i] = fastTrack->mGeneration; + } - // finally process (potentially) modified tracks; these use the same slot - // but may have a different buffer provider or volume provider - unsigned modifiedTracks = currentTrackMask & previousTrackMask; - while (modifiedTracks != 0) { - i = __builtin_ctz(modifiedTracks); - modifiedTracks &= ~(1 << i); - const FastTrack* fastTrack = ¤t->mFastTracks[i]; - if (fastTrack->mGeneration != generations[i]) { - // this track was actually modified - AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider; - ALOG_ASSERT(bufferProvider != NULL); - if (mixer != NULL) { - name = fastTrackNames[i]; - ALOG_ASSERT(name >= 0); - mixer->setBufferProvider(name, bufferProvider); - if (fastTrack->mVolumeProvider == NULL) { - mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, - (void *)0x1000); - mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, - (void *)0x1000); - } - mixer->setParameter(name, AudioMixer::RESAMPLE, - AudioMixer::REMOVE, NULL); - mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, - (void *)(uintptr_t) fastTrack->mChannelMask); - // already enabled - } - generations[i] = fastTrack->mGeneration; + // finally process (potentially) modified tracks; these use the same slot + // but may have a different buffer provider or volume provider + unsigned modifiedTracks = currentTrackMask & previousTrackMask; + while (modifiedTracks != 0) { + int i = __builtin_ctz(modifiedTracks); + modifiedTracks &= ~(1 << i); + const FastTrack* fastTrack = ¤t->mFastTracks[i]; + if (fastTrack->mGeneration != generations[i]) { + // this track was actually modified + AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider; + ALOG_ASSERT(bufferProvider != NULL); + if (mixer != NULL) { + name = fastTrackNames[i]; + ALOG_ASSERT(name >= 0); + mixer->setBufferProvider(name, bufferProvider); + if (fastTrack->mVolumeProvider == NULL) { + float f = AudioMixer::UNITY_GAIN_FLOAT; + mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f); + mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f); } + 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, + (void *)(uintptr_t)fastTrack->mChannelMask); + mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, + (void *)(uintptr_t)mSinkChannelMask); + // already enabled } - - fastTracksGen = current->mFastTracksGen; - - dumpState->mNumTracks = popcount(currentTrackMask); + generations[i] = fastTrack->mGeneration; } - -#if 1 // FIXME shouldn't need this - // only process state change once - previous = current; -#endif } - // do work using current state here - if ((command & FastMixerState::MIX) && (mixer != NULL) && isWarm) { - ALOG_ASSERT(mixBuffer != NULL); - // for each track, update volume and check for underrun - unsigned currentTrackMask = current->mTrackMask; - while (currentTrackMask != 0) { - i = __builtin_ctz(currentTrackMask); - currentTrackMask &= ~(1 << i); - const FastTrack* fastTrack = ¤t->mFastTracks[i]; - - // Refresh the per-track timestamp - if (timestampStatus == NO_ERROR) { - uint32_t trackFramesWrittenButNotPresented = - nativeFramesWrittenButNotPresented; - uint32_t trackFramesWritten = fastTrack->mBufferProvider->framesReleased(); - // Can't provide an AudioTimestamp before first frame presented, - // or during the brief 32-bit wraparound window - if (trackFramesWritten >= trackFramesWrittenButNotPresented) { - AudioTimestamp perTrackTimestamp; - perTrackTimestamp.mPosition = - trackFramesWritten - trackFramesWrittenButNotPresented; - perTrackTimestamp.mTime = timestamp.mTime; - fastTrack->mBufferProvider->onTimestamp(perTrackTimestamp); - } - } + fastTracksGen = current->mFastTracksGen; - int name = fastTrackNames[i]; - ALOG_ASSERT(name >= 0); - if (fastTrack->mVolumeProvider != NULL) { - uint32_t vlr = fastTrack->mVolumeProvider->getVolumeLR(); - mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, - (void *)(uintptr_t)(vlr & 0xFFFF)); - mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, - (void *)(uintptr_t)(vlr >> 16)); - } - // FIXME The current implementation of framesReady() for fast tracks - // takes a tryLock, which can block - // up to 1 ms. If enough active tracks all blocked in sequence, this would result - // in the overall fast mix cycle being delayed. Should use a non-blocking FIFO. - size_t framesReady = fastTrack->mBufferProvider->framesReady(); - if (ATRACE_ENABLED()) { - // I wish we had formatted trace names - char traceName[16]; - strcpy(traceName, "fRdy"); - traceName[4] = i + (i < 10 ? '0' : 'A' - 10); - traceName[5] = '\0'; - ATRACE_INT(traceName, framesReady); - } - FastTrackDump *ftDump = &dumpState->mTracks[i]; - FastTrackUnderruns underruns = ftDump->mUnderruns; - if (framesReady < frameCount) { - if (framesReady == 0) { - underruns.mBitFields.mEmpty++; - underruns.mBitFields.mMostRecent = UNDERRUN_EMPTY; - mixer->disable(name); - } else { - // allow mixing partial buffer - underruns.mBitFields.mPartial++; - underruns.mBitFields.mMostRecent = UNDERRUN_PARTIAL; - mixer->enable(name); - } - } else { - underruns.mBitFields.mFull++; - underruns.mBitFields.mMostRecent = UNDERRUN_FULL; - mixer->enable(name); + dumpState->mNumTracks = popcount(currentTrackMask); + } +} + +void FastMixer::onWork() +{ + const FastMixerState * const current = (const FastMixerState *) this->current; + FastMixerDumpState * const dumpState = (FastMixerDumpState *) this->dumpState; + const FastMixerState::Command command = this->command; + const size_t frameCount = current->mFrameCount; + + if ((command & FastMixerState::MIX) && (mixer != NULL) && isWarm) { + ALOG_ASSERT(mMixerBuffer != NULL); + // for each track, update volume and check for underrun + unsigned currentTrackMask = current->mTrackMask; + while (currentTrackMask != 0) { + int i = __builtin_ctz(currentTrackMask); + currentTrackMask &= ~(1 << i); + const FastTrack* fastTrack = ¤t->mFastTracks[i]; + + // Refresh the per-track timestamp + if (timestampStatus == NO_ERROR) { + uint32_t trackFramesWrittenButNotPresented = + nativeFramesWrittenButNotPresented; + uint32_t trackFramesWritten = fastTrack->mBufferProvider->framesReleased(); + // Can't provide an AudioTimestamp before first frame presented, + // or during the brief 32-bit wraparound window + if (trackFramesWritten >= trackFramesWrittenButNotPresented) { + AudioTimestamp perTrackTimestamp; + perTrackTimestamp.mPosition = + trackFramesWritten - trackFramesWrittenButNotPresented; + perTrackTimestamp.mTime = timestamp.mTime; + fastTrack->mBufferProvider->onTimestamp(perTrackTimestamp); } - ftDump->mUnderruns = underruns; - ftDump->mFramesReady = framesReady; } - int64_t pts; - if (outputSink == NULL || (OK != outputSink->getNextWriteTimestamp(&pts))) - pts = AudioBufferProvider::kInvalidPTS; + int name = fastTrackNames[i]; + ALOG_ASSERT(name >= 0); + if (fastTrack->mVolumeProvider != NULL) { + gain_minifloat_packed_t vlr = fastTrack->mVolumeProvider->getVolumeLR(); + float vlf = float_from_gain(gain_minifloat_unpack_left(vlr)); + float vrf = float_from_gain(gain_minifloat_unpack_right(vlr)); - // process() is CPU-bound - mixer->process(pts); - mixBufferState = MIXED; - } else if (mixBufferState == MIXED) { - mixBufferState = UNDEFINED; - } - bool attemptedWrite = false; - //bool didFullWrite = false; // dumpsys could display a count of partial writes - if ((command & FastMixerState::WRITE) && (outputSink != NULL) && (mixBuffer != NULL)) { - if (mixBufferState == UNDEFINED) { - memset(mixBuffer, 0, frameCount * FCC_2 * sizeof(short)); - mixBufferState = ZEROED; - } - if (teeSink != NULL) { - (void) teeSink->write(mixBuffer, frameCount); + mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &vlf); + mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &vrf); } - // FIXME write() is non-blocking and lock-free for a properly implemented NBAIO sink, - // but this code should be modified to handle both non-blocking and blocking sinks - dumpState->mWriteSequence++; - ATRACE_BEGIN("write"); - ssize_t framesWritten = outputSink->write(mixBuffer, frameCount); - ATRACE_END(); - dumpState->mWriteSequence++; - if (framesWritten >= 0) { - ALOG_ASSERT((size_t) framesWritten <= frameCount); - totalNativeFramesWritten += framesWritten; - dumpState->mFramesWritten = totalNativeFramesWritten; - //if ((size_t) framesWritten == frameCount) { - // didFullWrite = true; - //} - } else { - dumpState->mWriteErrors++; + // FIXME The current implementation of framesReady() for fast tracks + // takes a tryLock, which can block + // up to 1 ms. If enough active tracks all blocked in sequence, this would result + // in the overall fast mix cycle being delayed. Should use a non-blocking FIFO. + size_t framesReady = fastTrack->mBufferProvider->framesReady(); + if (ATRACE_ENABLED()) { + // I wish we had formatted trace names + char traceName[16]; + strcpy(traceName, "fRdy"); + traceName[4] = i + (i < 10 ? '0' : 'A' - 10); + traceName[5] = '\0'; + ATRACE_INT(traceName, framesReady); } - attemptedWrite = true; - // FIXME count # of writes blocked excessively, CPU usage, etc. for dump - - timestampStatus = outputSink->getTimestamp(timestamp); - if (timestampStatus == NO_ERROR) { - uint32_t totalNativeFramesPresented = timestamp.mPosition; - if (totalNativeFramesPresented <= totalNativeFramesWritten) { - nativeFramesWrittenButNotPresented = - totalNativeFramesWritten - totalNativeFramesPresented; + FastTrackDump *ftDump = &dumpState->mTracks[i]; + FastTrackUnderruns underruns = ftDump->mUnderruns; + if (framesReady < frameCount) { + if (framesReady == 0) { + underruns.mBitFields.mEmpty++; + underruns.mBitFields.mMostRecent = UNDERRUN_EMPTY; + mixer->disable(name); } else { - // HAL reported that more frames were presented than were written - timestampStatus = INVALID_OPERATION; + // allow mixing partial buffer + underruns.mBitFields.mPartial++; + underruns.mBitFields.mMostRecent = UNDERRUN_PARTIAL; + mixer->enable(name); } + } else { + underruns.mBitFields.mFull++; + underruns.mBitFields.mMostRecent = UNDERRUN_FULL; + mixer->enable(name); } + ftDump->mUnderruns = underruns; + ftDump->mFramesReady = framesReady; } - // To be exactly periodic, compute the next sleep time based on current time. - // This code doesn't have long-term stability when the sink is non-blocking. - // FIXME To avoid drift, use the local audio clock or watch the sink's fill status. - struct timespec newTs; - int rc = clock_gettime(CLOCK_MONOTONIC, &newTs); - if (rc == 0) { - //logWriter->logTimestamp(newTs); - if (oldTsValid) { - time_t sec = newTs.tv_sec - oldTs.tv_sec; - long nsec = newTs.tv_nsec - oldTs.tv_nsec; - ALOGE_IF(sec < 0 || (sec == 0 && nsec < 0), - "clock_gettime(CLOCK_MONOTONIC) failed: was %ld.%09ld but now %ld.%09ld", - oldTs.tv_sec, oldTs.tv_nsec, newTs.tv_sec, newTs.tv_nsec); - if (nsec < 0) { - --sec; - nsec += 1000000000; - } - // To avoid an initial underrun on fast tracks after exiting standby, - // do not start pulling data from tracks and mixing until warmup is complete. - // Warmup is considered complete after the earlier of: - // MIN_WARMUP_CYCLES write() attempts and last one blocks for at least warmupNs - // MAX_WARMUP_CYCLES write() attempts. - // This is overly conservative, but to get better accuracy requires a new HAL API. - if (!isWarm && attemptedWrite) { - measuredWarmupTs.tv_sec += sec; - measuredWarmupTs.tv_nsec += nsec; - if (measuredWarmupTs.tv_nsec >= 1000000000) { - measuredWarmupTs.tv_sec++; - measuredWarmupTs.tv_nsec -= 1000000000; - } - ++warmupCycles; - if ((nsec > warmupNs && warmupCycles >= MIN_WARMUP_CYCLES) || - (warmupCycles >= MAX_WARMUP_CYCLES)) { - isWarm = true; - dumpState->mMeasuredWarmupTs = measuredWarmupTs; - dumpState->mWarmupCycles = warmupCycles; - } - } - sleepNs = -1; - if (isWarm) { - if (sec > 0 || nsec > underrunNs) { - ATRACE_NAME("underrun"); - // FIXME only log occasionally - ALOGV("underrun: time since last cycle %d.%03ld sec", - (int) sec, nsec / 1000000L); - dumpState->mUnderruns++; - ignoreNextOverrun = true; - } else if (nsec < overrunNs) { - if (ignoreNextOverrun) { - ignoreNextOverrun = false; - } else { - // FIXME only log occasionally - ALOGV("overrun: time since last cycle %d.%03ld sec", - (int) sec, nsec / 1000000L); - dumpState->mOverruns++; - } - // This forces a minimum cycle time. It: - // - compensates for an audio HAL with jitter due to sample rate conversion - // - works with a variable buffer depth audio HAL that never pulls at a - // rate < than overrunNs per buffer. - // - recovers from overrun immediately after underrun - // It doesn't work with a non-blocking audio HAL. - sleepNs = forceNs - nsec; - } else { - ignoreNextOverrun = false; - } - } -#ifdef FAST_MIXER_STATISTICS - if (isWarm) { - // advance the FIFO queue bounds - size_t i = bounds & (dumpState->mSamplingN - 1); - bounds = (bounds & 0xFFFF0000) | ((bounds + 1) & 0xFFFF); - if (full) { - bounds += 0x10000; - } else if (!(bounds & (dumpState->mSamplingN - 1))) { - full = true; - } - // compute the delta value of clock_gettime(CLOCK_MONOTONIC) - uint32_t monotonicNs = nsec; - if (sec > 0 && sec < 4) { - monotonicNs += sec * 1000000000; - } - // compute raw CPU load = delta value of clock_gettime(CLOCK_THREAD_CPUTIME_ID) - uint32_t loadNs = 0; - struct timespec newLoad; - rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &newLoad); - if (rc == 0) { - if (oldLoadValid) { - sec = newLoad.tv_sec - oldLoad.tv_sec; - nsec = newLoad.tv_nsec - oldLoad.tv_nsec; - if (nsec < 0) { - --sec; - nsec += 1000000000; - } - loadNs = nsec; - if (sec > 0 && sec < 4) { - loadNs += sec * 1000000000; - } - } else { - // first time through the loop - oldLoadValid = true; - } - oldLoad = newLoad; - } -#ifdef CPU_FREQUENCY_STATISTICS - // get the absolute value of CPU clock frequency in kHz - int cpuNum = sched_getcpu(); - uint32_t kHz = tcu.getCpukHz(cpuNum); - kHz = (kHz << 4) | (cpuNum & 0xF); -#endif - // save values in FIFO queues for dumpsys - // these stores #1, #2, #3 are not atomic with respect to each other, - // or with respect to store #4 below - dumpState->mMonotonicNs[i] = monotonicNs; - dumpState->mLoadNs[i] = loadNs; -#ifdef CPU_FREQUENCY_STATISTICS - dumpState->mCpukHz[i] = kHz; -#endif - // this store #4 is not atomic with respect to stores #1, #2, #3 above, but - // the newest open & oldest closed halves are atomic with respect to each other - dumpState->mBounds = bounds; - ATRACE_INT("cycle_ms", monotonicNs / 1000000); - ATRACE_INT("load_us", loadNs / 1000); - } -#endif + int64_t pts; + if (outputSink == NULL || (OK != outputSink->getNextWriteTimestamp(&pts))) { + pts = AudioBufferProvider::kInvalidPTS; + } + + // process() is CPU-bound + mixer->process(pts); + mMixerBufferState = MIXED; + } else if (mMixerBufferState == MIXED) { + mMixerBufferState = UNDEFINED; + } + //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, 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) { + (void) teeSink->write(buffer, frameCount); + } + // FIXME write() is non-blocking and lock-free for a properly implemented NBAIO sink, + // but this code should be modified to handle both non-blocking and blocking sinks + dumpState->mWriteSequence++; + ATRACE_BEGIN("write"); + ssize_t framesWritten = outputSink->write(buffer, frameCount); + ATRACE_END(); + dumpState->mWriteSequence++; + if (framesWritten >= 0) { + ALOG_ASSERT((size_t) framesWritten <= frameCount); + totalNativeFramesWritten += framesWritten; + dumpState->mFramesWritten = totalNativeFramesWritten; + //if ((size_t) framesWritten == frameCount) { + // didFullWrite = true; + //} + } else { + dumpState->mWriteErrors++; + } + attemptedWrite = true; + // FIXME count # of writes blocked excessively, CPU usage, etc. for dump + + timestampStatus = outputSink->getTimestamp(timestamp); + if (timestampStatus == NO_ERROR) { + uint32_t totalNativeFramesPresented = timestamp.mPosition; + if (totalNativeFramesPresented <= totalNativeFramesWritten) { + nativeFramesWrittenButNotPresented = + totalNativeFramesWritten - totalNativeFramesPresented; } else { - // first time through the loop - oldTsValid = true; - sleepNs = periodNs; - ignoreNextOverrun = true; + // HAL reported that more frames were presented than were written + timestampStatus = INVALID_OPERATION; } - oldTs = newTs; - } else { - // monotonic clock is broken - oldTsValid = false; - sleepNs = periodNs; } - - - } // for (;;) - - // never return 'true'; Thread::_threadLoop() locks mutex which can result in priority inversion + } } FastMixerDumpState::FastMixerDumpState( #ifdef FAST_MIXER_STATISTICS uint32_t samplingN #endif - ) : - mCommand(FastMixerState::INITIAL), mWriteSequence(0), mFramesWritten(0), - mNumTracks(0), mWriteErrors(0), mUnderruns(0), mOverruns(0), - mSampleRate(0), mFrameCount(0), /* mMeasuredWarmupTs({0, 0}), */ mWarmupCycles(0), + ) : FastThreadDumpState(), + mWriteSequence(0), mFramesWritten(0), + mNumTracks(0), mWriteErrors(0), + mSampleRate(0), mFrameCount(0), mTrackMask(0) -#ifdef FAST_MIXER_STATISTICS - , mSamplingN(0), mBounds(0) -#endif { - mMeasuredWarmupTs.tv_sec = 0; - mMeasuredWarmupTs.tv_nsec = 0; #ifdef FAST_MIXER_STATISTICS increaseSamplingN(samplingN); #endif @@ -694,7 +510,7 @@ static int compare_uint32_t(const void *pa, const void *pb) void FastMixerDumpState::dump(int fd) const { if (mCommand == FastMixerState::INITIAL) { - dprintf(fd, "FastMixer not initialized\n"); + dprintf(fd, " FastMixer not initialized\n"); return; } #define COMMAND_MAX 32 @@ -728,10 +544,10 @@ void FastMixerDumpState::dump(int fd) const double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1000.0) + (mMeasuredWarmupTs.tv_nsec / 1000000.0); double mixPeriodSec = (double) mFrameCount / (double) mSampleRate; - dprintf(fd, "FastMixer command=%s writeSequence=%u framesWritten=%u\n" - " numTracks=%u writeErrors=%u underruns=%u overruns=%u\n" - " sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n" - " mixPeriod=%.2f ms\n", + dprintf(fd, " FastMixer command=%s writeSequence=%u framesWritten=%u\n" + " numTracks=%u writeErrors=%u underruns=%u overruns=%u\n" + " sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n" + " mixPeriod=%.2f ms\n", string, mWriteSequence, mFramesWritten, mNumTracks, mWriteErrors, mUnderruns, mOverruns, mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles, @@ -782,14 +598,20 @@ void FastMixerDumpState::dump(int fd) const previousCpukHz = sampleCpukHz; #endif } - dprintf(fd, "Simple moving statistics over last %.1f seconds:\n", wall.n() * mixPeriodSec); - dprintf(fd, " wall clock time in ms per mix cycle:\n" - " mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", - wall.mean()*1e-6, wall.minimum()*1e-6, wall.maximum()*1e-6, wall.stddev()*1e-6); - dprintf(fd, " raw CPU load in us per mix cycle:\n" - " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", - loadNs.mean()*1e-3, loadNs.minimum()*1e-3, loadNs.maximum()*1e-3, - loadNs.stddev()*1e-3); + if (n) { + dprintf(fd, " Simple moving statistics over last %.1f seconds:\n", + wall.n() * mixPeriodSec); + dprintf(fd, " wall clock time in ms per mix cycle:\n" + " mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", + wall.mean()*1e-6, wall.minimum()*1e-6, wall.maximum()*1e-6, + wall.stddev()*1e-6); + dprintf(fd, " raw CPU load in us per mix cycle:\n" + " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", + loadNs.mean()*1e-3, loadNs.minimum()*1e-3, loadNs.maximum()*1e-3, + loadNs.stddev()*1e-3); + } else { + dprintf(fd, " No FastMixer statistics available currently\n"); + } #ifdef CPU_FREQUENCY_STATISTICS dprintf(fd, " CPU clock frequency in MHz:\n" " mean=%.0f min=%.0f max=%.0f stddev=%.0f\n", @@ -807,9 +629,9 @@ void FastMixerDumpState::dump(int fd) const left.sample(tail[i]); right.sample(tail[n - (i + 1)]); } - dprintf(fd, "Distribution of mix cycle times in ms for the tails (> ~3 stddev outliers):\n" - " left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n" - " right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", + dprintf(fd, " Distribution of mix cycle times in ms for the tails (> ~3 stddev outliers):\n" + " left tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n" + " right tail: mean=%.2f min=%.2f max=%.2f stddev=%.2f\n", left.mean()*1e-6, left.minimum()*1e-6, left.maximum()*1e-6, left.stddev()*1e-6, right.mean()*1e-6, right.minimum()*1e-6, right.maximum()*1e-6, right.stddev()*1e-6); @@ -822,9 +644,9 @@ void FastMixerDumpState::dump(int fd) const // Instead we always display all tracks, with an indication // of whether we think the track is active. uint32_t trackMask = mTrackMask; - dprintf(fd, "Fast tracks: kMaxFastTracks=%u activeMask=%#x\n", + dprintf(fd, " Fast tracks: kMaxFastTracks=%u activeMask=%#x\n", FastMixerState::kMaxFastTracks, trackMask); - dprintf(fd, "Index Active Full Partial Empty Recent Ready\n"); + dprintf(fd, " Index Active Full Partial Empty Recent Ready\n"); for (uint32_t i = 0; i < FastMixerState::kMaxFastTracks; ++i, trackMask >>= 1) { bool isActive = trackMask & 1; const FastTrackDump *ftDump = &mTracks[i]; @@ -844,7 +666,7 @@ void FastMixerDumpState::dump(int fd) const mostRecent = "?"; break; } - dprintf(fd, "%5u %6s %4u %7u %5u %7s %5zu\n", i, isActive ? "yes" : "no", + dprintf(fd, " %5u %6s %4u %7u %5u %7s %5zu\n", i, isActive ? "yes" : "no", (underruns.mBitFields.mFull) & UNDERRUN_MASK, (underruns.mBitFields.mPartial) & UNDERRUN_MASK, (underruns.mBitFields.mEmpty) & UNDERRUN_MASK, diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h index c356d31..fde8c2b 100644 --- a/services/audioflinger/FastMixer.h +++ b/services/audioflinger/FastMixer.h @@ -20,119 +20,70 @@ #include <linux/futex.h> #include <sys/syscall.h> #include <utils/Debug.h> +#include "FastThread.h" #include <utils/Thread.h> #include "StateQueue.h" #include "FastMixerState.h" +#include "FastMixerDumpState.h" namespace android { +class AudioMixer; + typedef StateQueue<FastMixerState> FastMixerStateQueue; -class FastMixer : public Thread { +class FastMixer : public FastThread { public: - FastMixer() : Thread(false /*canCallJava*/) { } - virtual ~FastMixer() { } + FastMixer(); + virtual ~FastMixer(); - FastMixerStateQueue* sq() { return &mSQ; } + FastMixerStateQueue* sq(); private: - virtual bool threadLoop(); FastMixerStateQueue mSQ; -}; // class FastMixer + // 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(); + + // FIXME these former local variables need comments and to be renamed to have "m" prefix + static const FastMixerState initial; + FastMixerState preIdle; // copy of state before we went into idle + long slopNs; // accumulated time we've woken up too early (> 0) or too late (< 0) + int fastTrackNames[FastMixerState::kMaxFastTracks]; // handles used by mixer to identify tracks + int generations[FastMixerState::kMaxFastTracks]; // last observed mFastTracks[i].mGeneration + NBAIO_Sink *outputSink; + int outputSinkGen; + AudioMixer* mixer; + + // 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; + uint32_t mSinkChannelCount; + audio_channel_mask_t mSinkChannelMask; + 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; + int fastTracksGen; + FastMixerDumpState dummyDumpState; + uint32_t totalNativeFramesWritten; // copied to dumpState->mFramesWritten + + // next 2 fields are valid only when timestampStatus == NO_ERROR + AudioTimestamp timestamp; + uint32_t nativeFramesWrittenButNotPresented; -// Describes the underrun status for a single "pull" attempt -enum FastTrackUnderrunStatus { - UNDERRUN_FULL, // framesReady() is full frame count, no underrun - UNDERRUN_PARTIAL, // framesReady() is non-zero but < full frame count, partial underrun - UNDERRUN_EMPTY, // framesReady() is zero, total underrun -}; - -// Underrun counters are not reset to zero for new tracks or if track generation changes. -// This packed representation is used to keep the information atomic. -union FastTrackUnderruns { - FastTrackUnderruns() { mAtomic = 0; - COMPILE_TIME_ASSERT_FUNCTION_SCOPE(sizeof(FastTrackUnderruns) == sizeof(uint32_t)); } - FastTrackUnderruns(const FastTrackUnderruns& copyFrom) : mAtomic(copyFrom.mAtomic) { } - FastTrackUnderruns& operator=(const FastTrackUnderruns& rhs) - { if (this != &rhs) mAtomic = rhs.mAtomic; return *this; } - struct { -#define UNDERRUN_BITS 10 -#define UNDERRUN_MASK ((1 << UNDERRUN_BITS) - 1) - uint32_t mFull : UNDERRUN_BITS; // framesReady() is full frame count - uint32_t mPartial : UNDERRUN_BITS; // framesReady() is non-zero but < full frame count - uint32_t mEmpty : UNDERRUN_BITS; // framesReady() is zero - FastTrackUnderrunStatus mMostRecent : 2; // status of most recent framesReady() - } mBitFields; -private: - uint32_t mAtomic; -}; - -// Represents the dump state of a fast track -struct FastTrackDump { - FastTrackDump() : mFramesReady(0) { } - /*virtual*/ ~FastTrackDump() { } - FastTrackUnderruns mUnderruns; - size_t mFramesReady; // most recent value only; no long-term statistics kept -}; - -// The FastMixerDumpState keeps a cache of FastMixer statistics that can be logged by dumpsys. -// Each individual native word-sized field is accessed atomically. But the -// overall structure is non-atomic, that is there may be an inconsistency between fields. -// No barriers or locks are used for either writing or reading. -// Only POD types are permitted, and the contents shouldn't be trusted (i.e. do range checks). -// It has a different lifetime than the FastMixer, and so it can't be a member of FastMixer. -struct FastMixerDumpState { - FastMixerDumpState( -#ifdef FAST_MIXER_STATISTICS - uint32_t samplingN = kSamplingNforLowRamDevice -#endif - ); - /*virtual*/ ~FastMixerDumpState(); - - void dump(int fd) const; // should only be called on a stable copy, not the original - - FastMixerState::Command mCommand; // current command - uint32_t mWriteSequence; // incremented before and after each write() - uint32_t mFramesWritten; // total number of frames written successfully - uint32_t mNumTracks; // total number of active fast tracks - uint32_t mWriteErrors; // total number of write() errors - uint32_t mUnderruns; // total number of underruns - uint32_t mOverruns; // total number of overruns - uint32_t mSampleRate; - size_t mFrameCount; - struct timespec mMeasuredWarmupTs; // measured warmup time - uint32_t mWarmupCycles; // number of loop cycles required to warmup - uint32_t mTrackMask; // mask of active tracks - FastTrackDump mTracks[FastMixerState::kMaxFastTracks]; - -#ifdef FAST_MIXER_STATISTICS - // Recently collected samples of per-cycle monotonic time, thread CPU time, and CPU frequency. - // kSamplingN is max size of sampling frame (statistics), and must be a power of 2 <= 0x8000. - // The sample arrays are virtually allocated based on this compile-time constant, - // but are only initialized and used based on the runtime parameter mSamplingN. - static const uint32_t kSamplingN = 0x8000; - // Compile-time constant for a "low RAM device", must be a power of 2 <= kSamplingN. - // This value was chosen such that each array uses 1 small page (4 Kbytes). - static const uint32_t kSamplingNforLowRamDevice = 0x400; - // Corresponding runtime maximum size of sample arrays, must be a power of 2 <= kSamplingN. - uint32_t mSamplingN; - // The bounds define the interval of valid samples, and are represented as follows: - // newest open (excluded) endpoint = lower 16 bits of bounds, modulo N - // oldest closed (included) endpoint = upper 16 bits of bounds, modulo N - // Number of valid samples is newest - oldest. - uint32_t mBounds; // bounds for mMonotonicNs, mThreadCpuNs, and mCpukHz - // The elements in the *Ns arrays are in units of nanoseconds <= 3999999999. - uint32_t mMonotonicNs[kSamplingN]; // delta monotonic (wall clock) time - uint32_t mLoadNs[kSamplingN]; // delta CPU load in time -#ifdef CPU_FREQUENCY_STATISTICS - uint32_t mCpukHz[kSamplingN]; // absolute CPU clock frequency in kHz, bits 0-3 are CPU# -#endif - // Increase sampling window after construction, must be a power of 2 <= kSamplingN - void increaseSamplingN(uint32_t samplingN); -#endif -}; +}; // class FastMixer } // namespace android diff --git a/services/audioflinger/FastMixerDumpState.h b/services/audioflinger/FastMixerDumpState.h new file mode 100644 index 0000000..6a1e464 --- /dev/null +++ b/services/audioflinger/FastMixerDumpState.h @@ -0,0 +1,95 @@ +/* + * 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_MIXER_DUMP_STATE_H +#define ANDROID_AUDIO_FAST_MIXER_DUMP_STATE_H + +#include "Configuration.h" + +namespace android { + +// Describes the underrun status for a single "pull" attempt +enum FastTrackUnderrunStatus { + UNDERRUN_FULL, // framesReady() is full frame count, no underrun + UNDERRUN_PARTIAL, // framesReady() is non-zero but < full frame count, partial underrun + UNDERRUN_EMPTY, // framesReady() is zero, total underrun +}; + +// Underrun counters are not reset to zero for new tracks or if track generation changes. +// This packed representation is used to keep the information atomic. +union FastTrackUnderruns { + FastTrackUnderruns() { mAtomic = 0; + COMPILE_TIME_ASSERT_FUNCTION_SCOPE(sizeof(FastTrackUnderruns) == sizeof(uint32_t)); } + FastTrackUnderruns(const FastTrackUnderruns& copyFrom) : mAtomic(copyFrom.mAtomic) { } + FastTrackUnderruns& operator=(const FastTrackUnderruns& rhs) + { if (this != &rhs) mAtomic = rhs.mAtomic; return *this; } + struct { +#define UNDERRUN_BITS 10 +#define UNDERRUN_MASK ((1 << UNDERRUN_BITS) - 1) + uint32_t mFull : UNDERRUN_BITS; // framesReady() is full frame count + uint32_t mPartial : UNDERRUN_BITS; // framesReady() is non-zero but < full frame count + uint32_t mEmpty : UNDERRUN_BITS; // framesReady() is zero + FastTrackUnderrunStatus mMostRecent : 2; // status of most recent framesReady() + } mBitFields; +private: + uint32_t mAtomic; +}; + +// Represents the dump state of a fast track +struct FastTrackDump { + FastTrackDump() : mFramesReady(0) { } + /*virtual*/ ~FastTrackDump() { } + FastTrackUnderruns mUnderruns; + size_t mFramesReady; // most recent value only; no long-term statistics kept +}; + +// The FastMixerDumpState keeps a cache of FastMixer statistics that can be logged by dumpsys. +// Each individual native word-sized field is accessed atomically. But the +// overall structure is non-atomic, that is there may be an inconsistency between fields. +// No barriers or locks are used for either writing or reading. +// Only POD types are permitted, and the contents shouldn't be trusted (i.e. do range checks). +// It has a different lifetime than the FastMixer, and so it can't be a member of FastMixer. +struct FastMixerDumpState : FastThreadDumpState { + FastMixerDumpState( +#ifdef FAST_MIXER_STATISTICS + uint32_t samplingN = kSamplingNforLowRamDevice +#endif + ); + /*virtual*/ ~FastMixerDumpState(); + + void dump(int fd) const; // should only be called on a stable copy, not the original + + uint32_t mWriteSequence; // incremented before and after each write() + uint32_t mFramesWritten; // total number of frames written successfully + uint32_t mNumTracks; // total number of active fast tracks + uint32_t mWriteErrors; // total number of write() errors + uint32_t mSampleRate; + size_t mFrameCount; + uint32_t mTrackMask; // mask of active tracks + FastTrackDump mTracks[FastMixerState::kMaxFastTracks]; + +#ifdef FAST_MIXER_STATISTICS + // Compile-time constant for a "low RAM device", must be a power of 2 <= kSamplingN. + // This value was chosen such that each array uses 1 small page (4 Kbytes). + static const uint32_t kSamplingNforLowRamDevice = 0x400; + // Increase sampling window after construction, must be a power of 2 <= kSamplingN + void increaseSamplingN(uint32_t samplingN); +#endif +}; + +} // android + +#endif // ANDROID_AUDIO_FAST_MIXER_DUMP_STATE_H diff --git a/services/audioflinger/FastMixerState.cpp b/services/audioflinger/FastMixerState.cpp index 43ff233..3aa8dad 100644 --- a/services/audioflinger/FastMixerState.cpp +++ b/services/audioflinger/FastMixerState.cpp @@ -14,14 +14,13 @@ * limitations under the License. */ -#include "Configuration.h" #include "FastMixerState.h" namespace android { FastTrack::FastTrack() : mBufferProvider(NULL), mVolumeProvider(NULL), - mChannelMask(AUDIO_CHANNEL_OUT_STEREO), mGeneration(0) + mChannelMask(AUDIO_CHANNEL_OUT_STEREO), mFormat(AUDIO_FORMAT_INVALID), mGeneration(0) { } @@ -29,10 +28,10 @@ FastTrack::~FastTrack() { } -FastMixerState::FastMixerState() : +FastMixerState::FastMixerState() : FastThreadState(), + // mFastTracks mFastTracksGen(0), mTrackMask(0), mOutputSink(NULL), mOutputSinkGen(0), - mFrameCount(0), mCommand(INITIAL), mColdFutexAddr(NULL), mColdGen(0), - mDumpState(NULL), mTeeSink(NULL), mNBLogWriter(NULL) + mFrameCount(0), mTeeSink(NULL) { } diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h index 9739fe9..661c9ca 100644 --- a/services/audioflinger/FastMixerState.h +++ b/services/audioflinger/FastMixerState.h @@ -17,10 +17,12 @@ #ifndef ANDROID_AUDIO_FAST_MIXER_STATE_H #define ANDROID_AUDIO_FAST_MIXER_STATE_H +#include <audio_utils/minifloat.h> #include <system/audio.h> #include <media/ExtendedAudioBufferProvider.h> #include <media/nbaio/NBAIO.h> #include <media/nbaio/NBLog.h> +#include "FastThreadState.h" namespace android { @@ -28,9 +30,8 @@ struct FastMixerDumpState; class VolumeProvider { public: - // Return the track volume in U4_12 format: left in lower half, right in upper half. The - // provider implementation is responsible for validating that the return value is in range. - virtual uint32_t getVolumeLR() = 0; + // The provider implementation is responsible for validating that the return value is in range. + virtual gain_minifloat_packed_t getVolumeLR() = 0; protected: VolumeProvider() { } virtual ~VolumeProvider() { } @@ -44,11 +45,12 @@ struct FastTrack { ExtendedAudioBufferProvider* mBufferProvider; // must be NULL if inactive, or non-NULL if active VolumeProvider* mVolumeProvider; // optional; if NULL then full-scale audio_channel_mask_t mChannelMask; // AUDIO_CHANNEL_OUT_MONO or AUDIO_CHANNEL_OUT_STEREO + audio_format_t mFormat; // track format int mGeneration; // increment when any field is assigned }; // Represents a single state of the fast mixer -struct FastMixerState { +struct FastMixerState : FastThreadState { FastMixerState(); /*virtual*/ ~FastMixerState(); @@ -61,23 +63,16 @@ struct FastMixerState { NBAIO_Sink* mOutputSink; // HAL output device, must already be negotiated int mOutputSinkGen; // increment when mOutputSink is assigned size_t mFrameCount; // number of frames per fast mix buffer - enum Command { - INITIAL = 0, // used only for the initial state - HOT_IDLE = 1, // do nothing - COLD_IDLE = 2, // wait for the futex - IDLE = 3, // either HOT_IDLE or COLD_IDLE - EXIT = 4, // exit from thread + + // Extends FastThreadState::Command + static const Command // The following commands also process configuration changes, and can be "or"ed: MIX = 0x8, // mix tracks WRITE = 0x10, // write to output sink - MIX_WRITE = 0x18, // mix tracks and write to output sink - } mCommand; - int32_t* mColdFutexAddr; // for COLD_IDLE only, pointer to the associated futex - unsigned mColdGen; // increment when COLD_IDLE is requested so it's only performed once + MIX_WRITE = 0x18; // mix tracks and write to output sink + // This might be a one-time configuration rather than per-state - FastMixerDumpState* mDumpState; // if non-NULL, then update dump state periodically NBAIO_Sink* mTeeSink; // if non-NULL, then duplicate write()s to this non-blocking sink - NBLog::Writer* mNBLogWriter; // non-blocking logger }; // struct FastMixerState } // namespace android diff --git a/services/audioflinger/FastThread.cpp b/services/audioflinger/FastThread.cpp new file mode 100644 index 0000000..216dace --- /dev/null +++ b/services/audioflinger/FastThread.cpp @@ -0,0 +1,347 @@ +/* + * 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 "FastThread" +//#define LOG_NDEBUG 0 + +#define ATRACE_TAG ATRACE_TAG_AUDIO + +#include "Configuration.h" +#include <linux/futex.h> +#include <sys/syscall.h> +#include <utils/Log.h> +#include <utils/Trace.h> +#include "FastThread.h" + +#define FAST_DEFAULT_NS 999999999L // ~1 sec: default time to sleep +#define FAST_HOT_IDLE_NS 1000000L // 1 ms: time to sleep while hot idling +#define MIN_WARMUP_CYCLES 2 // minimum number of loop cycles to wait for warmup +#define MAX_WARMUP_CYCLES 10 // maximum number of loop cycles to wait for warmup + +namespace android { + +FastThread::FastThread() : Thread(false /*canCallJava*/), + // re-initialized to &initial by subclass constructor + previous(NULL), current(NULL), + /* oldTs({0, 0}), */ + oldTsValid(false), + sleepNs(-1), + periodNs(0), + underrunNs(0), + overrunNs(0), + forceNs(0), + warmupNs(0), + // re-initialized to &dummyDumpState by subclass constructor + mDummyDumpState(NULL), + dumpState(NULL), + ignoreNextOverrun(true), +#ifdef FAST_MIXER_STATISTICS + // oldLoad + oldLoadValid(false), + bounds(0), + full(false), + // tcu +#endif + coldGen(0), + isWarm(false), + /* measuredWarmupTs({0, 0}), */ + warmupCycles(0), + // dummyLogWriter + logWriter(&dummyLogWriter), + timestampStatus(INVALID_OPERATION), + + command(FastThreadState::INITIAL), +#if 0 + frameCount(0), +#endif + attemptedWrite(false) +{ + oldTs.tv_sec = 0; + oldTs.tv_nsec = 0; + measuredWarmupTs.tv_sec = 0; + measuredWarmupTs.tv_nsec = 0; +} + +FastThread::~FastThread() +{ +} + +bool FastThread::threadLoop() +{ + for (;;) { + + // either nanosleep, sched_yield, or busy wait + if (sleepNs >= 0) { + if (sleepNs > 0) { + ALOG_ASSERT(sleepNs < 1000000000); + const struct timespec req = {0, sleepNs}; + nanosleep(&req, NULL); + } else { + sched_yield(); + } + } + // default to long sleep for next cycle + sleepNs = FAST_DEFAULT_NS; + + // poll for state change + const FastThreadState *next = poll(); + if (next == NULL) { + // continue to use the default initial state until a real state is available + // FIXME &initial not available, should save address earlier + //ALOG_ASSERT(current == &initial && previous == &initial); + next = current; + } + + command = next->mCommand; + if (next != current) { + + // As soon as possible of learning of a new dump area, start using it + dumpState = next->mDumpState != NULL ? next->mDumpState : mDummyDumpState; + logWriter = next->mNBLogWriter != NULL ? next->mNBLogWriter : &dummyLogWriter; + setLog(logWriter); + + // We want to always have a valid reference to the previous (non-idle) state. + // However, the state queue only guarantees access to current and previous states. + // So when there is a transition from a non-idle state into an idle state, we make a + // copy of the last known non-idle state so it is still available on return from idle. + // The possible transitions are: + // non-idle -> non-idle update previous from current in-place + // non-idle -> idle update previous from copy of current + // idle -> idle don't update previous + // idle -> non-idle don't update previous + if (!(current->mCommand & FastThreadState::IDLE)) { + if (command & FastThreadState::IDLE) { + onIdle(); + oldTsValid = false; +#ifdef FAST_MIXER_STATISTICS + oldLoadValid = false; +#endif + ignoreNextOverrun = true; + } + previous = current; + } + current = next; + } +#if !LOG_NDEBUG + next = NULL; // not referenced again +#endif + + dumpState->mCommand = command; + + // << current, previous, command, dumpState >> + + switch (command) { + case FastThreadState::INITIAL: + case FastThreadState::HOT_IDLE: + sleepNs = FAST_HOT_IDLE_NS; + continue; + case FastThreadState::COLD_IDLE: + // only perform a cold idle command once + // FIXME consider checking previous state and only perform if previous != COLD_IDLE + if (current->mColdGen != coldGen) { + int32_t *coldFutexAddr = current->mColdFutexAddr; + ALOG_ASSERT(coldFutexAddr != NULL); + int32_t old = android_atomic_dec(coldFutexAddr); + if (old <= 0) { + syscall(__NR_futex, coldFutexAddr, FUTEX_WAIT_PRIVATE, old - 1, NULL); + } + int policy = sched_getscheduler(0); + if (!(policy == SCHED_FIFO || policy == SCHED_RR)) { + ALOGE("did not receive expected priority boost"); + } + // This may be overly conservative; there could be times that the normal mixer + // requests such a brief cold idle that it doesn't require resetting this flag. + isWarm = false; + measuredWarmupTs.tv_sec = 0; + measuredWarmupTs.tv_nsec = 0; + warmupCycles = 0; + sleepNs = -1; + coldGen = current->mColdGen; +#ifdef FAST_MIXER_STATISTICS + bounds = 0; + full = false; +#endif + oldTsValid = !clock_gettime(CLOCK_MONOTONIC, &oldTs); + timestampStatus = INVALID_OPERATION; + } else { + sleepNs = FAST_HOT_IDLE_NS; + } + continue; + case FastThreadState::EXIT: + onExit(); + return false; + default: + LOG_ALWAYS_FATAL_IF(!isSubClassCommand(command)); + break; + } + + // there is a non-idle state available to us; did the state change? + if (current != previous) { + onStateChange(); +#if 1 // FIXME shouldn't need this + // only process state change once + previous = current; +#endif + } + + // do work using current state here + attemptedWrite = false; + onWork(); + + // To be exactly periodic, compute the next sleep time based on current time. + // This code doesn't have long-term stability when the sink is non-blocking. + // FIXME To avoid drift, use the local audio clock or watch the sink's fill status. + struct timespec newTs; + int rc = clock_gettime(CLOCK_MONOTONIC, &newTs); + if (rc == 0) { + //logWriter->logTimestamp(newTs); + if (oldTsValid) { + time_t sec = newTs.tv_sec - oldTs.tv_sec; + long nsec = newTs.tv_nsec - oldTs.tv_nsec; + ALOGE_IF(sec < 0 || (sec == 0 && nsec < 0), + "clock_gettime(CLOCK_MONOTONIC) failed: was %ld.%09ld but now %ld.%09ld", + oldTs.tv_sec, oldTs.tv_nsec, newTs.tv_sec, newTs.tv_nsec); + if (nsec < 0) { + --sec; + nsec += 1000000000; + } + // To avoid an initial underrun on fast tracks after exiting standby, + // do not start pulling data from tracks and mixing until warmup is complete. + // Warmup is considered complete after the earlier of: + // MIN_WARMUP_CYCLES write() attempts and last one blocks for at least warmupNs + // MAX_WARMUP_CYCLES write() attempts. + // This is overly conservative, but to get better accuracy requires a new HAL API. + if (!isWarm && attemptedWrite) { + measuredWarmupTs.tv_sec += sec; + measuredWarmupTs.tv_nsec += nsec; + if (measuredWarmupTs.tv_nsec >= 1000000000) { + measuredWarmupTs.tv_sec++; + measuredWarmupTs.tv_nsec -= 1000000000; + } + ++warmupCycles; + if ((nsec > warmupNs && warmupCycles >= MIN_WARMUP_CYCLES) || + (warmupCycles >= MAX_WARMUP_CYCLES)) { + isWarm = true; + dumpState->mMeasuredWarmupTs = measuredWarmupTs; + dumpState->mWarmupCycles = warmupCycles; + } + } + sleepNs = -1; + if (isWarm) { + if (sec > 0 || nsec > underrunNs) { + ATRACE_NAME("underrun"); + // FIXME only log occasionally + ALOGV("underrun: time since last cycle %d.%03ld sec", + (int) sec, nsec / 1000000L); + dumpState->mUnderruns++; + ignoreNextOverrun = true; + } else if (nsec < overrunNs) { + if (ignoreNextOverrun) { + ignoreNextOverrun = false; + } else { + // FIXME only log occasionally + ALOGV("overrun: time since last cycle %d.%03ld sec", + (int) sec, nsec / 1000000L); + dumpState->mOverruns++; + } + // This forces a minimum cycle time. It: + // - compensates for an audio HAL with jitter due to sample rate conversion + // - works with a variable buffer depth audio HAL that never pulls at a + // rate < than overrunNs per buffer. + // - recovers from overrun immediately after underrun + // It doesn't work with a non-blocking audio HAL. + sleepNs = forceNs - nsec; + } else { + ignoreNextOverrun = false; + } + } +#ifdef FAST_MIXER_STATISTICS + if (isWarm) { + // advance the FIFO queue bounds + size_t i = bounds & (dumpState->mSamplingN - 1); + bounds = (bounds & 0xFFFF0000) | ((bounds + 1) & 0xFFFF); + if (full) { + bounds += 0x10000; + } else if (!(bounds & (dumpState->mSamplingN - 1))) { + full = true; + } + // compute the delta value of clock_gettime(CLOCK_MONOTONIC) + uint32_t monotonicNs = nsec; + if (sec > 0 && sec < 4) { + monotonicNs += sec * 1000000000; + } + // compute raw CPU load = delta value of clock_gettime(CLOCK_THREAD_CPUTIME_ID) + uint32_t loadNs = 0; + struct timespec newLoad; + rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &newLoad); + if (rc == 0) { + if (oldLoadValid) { + sec = newLoad.tv_sec - oldLoad.tv_sec; + nsec = newLoad.tv_nsec - oldLoad.tv_nsec; + if (nsec < 0) { + --sec; + nsec += 1000000000; + } + loadNs = nsec; + if (sec > 0 && sec < 4) { + loadNs += sec * 1000000000; + } + } else { + // first time through the loop + oldLoadValid = true; + } + oldLoad = newLoad; + } +#ifdef CPU_FREQUENCY_STATISTICS + // get the absolute value of CPU clock frequency in kHz + int cpuNum = sched_getcpu(); + uint32_t kHz = tcu.getCpukHz(cpuNum); + kHz = (kHz << 4) | (cpuNum & 0xF); +#endif + // save values in FIFO queues for dumpsys + // these stores #1, #2, #3 are not atomic with respect to each other, + // or with respect to store #4 below + dumpState->mMonotonicNs[i] = monotonicNs; + dumpState->mLoadNs[i] = loadNs; +#ifdef CPU_FREQUENCY_STATISTICS + dumpState->mCpukHz[i] = kHz; +#endif + // this store #4 is not atomic with respect to stores #1, #2, #3 above, but + // the newest open & oldest closed halves are atomic with respect to each other + dumpState->mBounds = bounds; + ATRACE_INT("cycle_ms", monotonicNs / 1000000); + ATRACE_INT("load_us", loadNs / 1000); + } +#endif + } else { + // first time through the loop + oldTsValid = true; + sleepNs = periodNs; + ignoreNextOverrun = true; + } + oldTs = newTs; + } else { + // monotonic clock is broken + oldTsValid = false; + sleepNs = periodNs; + } + + } // for (;;) + + // never return 'true'; Thread::_threadLoop() locks mutex which can result in priority inversion +} + +} // namespace android diff --git a/services/audioflinger/FastThread.h b/services/audioflinger/FastThread.h new file mode 100644 index 0000000..1330334 --- /dev/null +++ b/services/audioflinger/FastThread.h @@ -0,0 +1,92 @@ +/* + * 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_THREAD_H +#define ANDROID_AUDIO_FAST_THREAD_H + +#include "Configuration.h" +#ifdef CPU_FREQUENCY_STATISTICS +#include <cpustats/ThreadCpuUsage.h> +#endif +#include <utils/Thread.h> +#include "FastThreadState.h" + +namespace android { + +// FastThread is the common abstract base class of FastMixer and FastCapture +class FastThread : public Thread { + +public: + FastThread(); + virtual ~FastThread(); + +private: + // implement Thread::threadLoop() + virtual bool threadLoop(); + +protected: + // callouts to subclass in same lexical order as they were in original FastMixer.cpp + // FIXME need comments + virtual const FastThreadState *poll() = 0; + virtual void setLog(NBLog::Writer *logWriter __unused) { } + virtual void onIdle() = 0; + virtual void onExit() = 0; + virtual bool isSubClassCommand(FastThreadState::Command command) = 0; + virtual void onStateChange() = 0; + virtual void onWork() = 0; + + // FIXME these former local variables need comments and to be renamed to have an "m" prefix + const FastThreadState *previous; + const FastThreadState *current; + struct timespec oldTs; + bool oldTsValid; + long sleepNs; // -1: busy wait, 0: sched_yield, > 0: nanosleep + long periodNs; // expected period; the time required to render one mix buffer + long underrunNs; // underrun likely when write cycle is greater than this value + long overrunNs; // overrun likely when write cycle is less than this value + long forceNs; // if overrun detected, force the write cycle to take this much time + long warmupNs; // warmup complete when write cycle is greater than to this value + FastThreadDumpState *mDummyDumpState; + FastThreadDumpState *dumpState; + bool ignoreNextOverrun; // used to ignore initial overrun and first after an underrun +#ifdef FAST_MIXER_STATISTICS + struct timespec oldLoad; // previous value of clock_gettime(CLOCK_THREAD_CPUTIME_ID) + bool oldLoadValid; // whether oldLoad is valid + uint32_t bounds; + bool full; // whether we have collected at least mSamplingN samples +#ifdef CPU_FREQUENCY_STATISTICS + ThreadCpuUsage tcu; // for reading the current CPU clock frequency in kHz +#endif +#endif + unsigned coldGen; // last observed mColdGen + bool isWarm; // true means ready to mix, false means wait for warmup before mixing + struct timespec measuredWarmupTs; // how long did it take for warmup to complete + uint32_t warmupCycles; // counter of number of loop cycles required to warmup + NBLog::Writer dummyLogWriter; + NBLog::Writer *logWriter; + status_t timestampStatus; + + FastThreadState::Command command; +#if 0 + size_t frameCount; +#endif + bool attemptedWrite; + +}; // class FastThread + +} // android + +#endif // ANDROID_AUDIO_FAST_THREAD_H diff --git a/services/audioflinger/FastThreadState.cpp b/services/audioflinger/FastThreadState.cpp new file mode 100644 index 0000000..6994872 --- /dev/null +++ b/services/audioflinger/FastThreadState.cpp @@ -0,0 +1,49 @@ +/* + * 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 "Configuration.h" +#include "FastThreadState.h" + +namespace android { + +FastThreadState::FastThreadState() : + mCommand(INITIAL), mColdFutexAddr(NULL), mColdGen(0), mDumpState(NULL), mNBLogWriter(NULL) + +{ +} + +FastThreadState::~FastThreadState() +{ +} + + +FastThreadDumpState::FastThreadDumpState() : + mCommand(FastThreadState::INITIAL), mUnderruns(0), mOverruns(0), + /* mMeasuredWarmupTs({0, 0}), */ + mWarmupCycles(0) +#ifdef FAST_MIXER_STATISTICS + , mSamplingN(1), mBounds(0) +#endif +{ + mMeasuredWarmupTs.tv_sec = 0; + mMeasuredWarmupTs.tv_nsec = 0; +} + +FastThreadDumpState::~FastThreadDumpState() +{ +} + +} // namespace android diff --git a/services/audioflinger/FastThreadState.h b/services/audioflinger/FastThreadState.h new file mode 100644 index 0000000..1ab8a0a --- /dev/null +++ b/services/audioflinger/FastThreadState.h @@ -0,0 +1,88 @@ +/* + * 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_THREAD_STATE_H +#define ANDROID_AUDIO_FAST_THREAD_STATE_H + +#include "Configuration.h" +#include <stdint.h> +#include <media/nbaio/NBLog.h> + +namespace android { + +struct FastThreadDumpState; + +// Represents a single state of a FastThread +struct FastThreadState { + FastThreadState(); + /*virtual*/ ~FastThreadState(); + + typedef uint32_t Command; + static const Command + INITIAL = 0, // used only for the initial state + HOT_IDLE = 1, // do nothing + COLD_IDLE = 2, // wait for the futex + IDLE = 3, // either HOT_IDLE or COLD_IDLE + EXIT = 4; // exit from thread + // additional values defined per subclass + Command mCommand; // current command + int32_t* mColdFutexAddr; // for COLD_IDLE only, pointer to the associated futex + unsigned mColdGen; // increment when COLD_IDLE is requested so it's only performed once + + // This might be a one-time configuration rather than per-state + FastThreadDumpState* mDumpState; // if non-NULL, then update dump state periodically + NBLog::Writer* mNBLogWriter; // non-blocking logger + +}; // struct FastThreadState + + +// FIXME extract common part of comment at FastMixerDumpState +struct FastThreadDumpState { + FastThreadDumpState(); + /*virtual*/ ~FastThreadDumpState(); + + FastThreadState::Command mCommand; // current command + uint32_t mUnderruns; // total number of underruns + uint32_t mOverruns; // total number of overruns + struct timespec mMeasuredWarmupTs; // measured warmup time + uint32_t mWarmupCycles; // number of loop cycles required to warmup + +#ifdef FAST_MIXER_STATISTICS + // Recently collected samples of per-cycle monotonic time, thread CPU time, and CPU frequency. + // kSamplingN is max size of sampling frame (statistics), and must be a power of 2 <= 0x8000. + // The sample arrays are virtually allocated based on this compile-time constant, + // but are only initialized and used based on the runtime parameter mSamplingN. + static const uint32_t kSamplingN = 0x8000; + // Corresponding runtime maximum size of sample arrays, must be a power of 2 <= kSamplingN. + uint32_t mSamplingN; + // The bounds define the interval of valid samples, and are represented as follows: + // newest open (excluded) endpoint = lower 16 bits of bounds, modulo N + // oldest closed (included) endpoint = upper 16 bits of bounds, modulo N + // Number of valid samples is newest - oldest. + uint32_t mBounds; // bounds for mMonotonicNs, mThreadCpuNs, and mCpukHz + // The elements in the *Ns arrays are in units of nanoseconds <= 3999999999. + uint32_t mMonotonicNs[kSamplingN]; // delta monotonic (wall clock) time + uint32_t mLoadNs[kSamplingN]; // delta CPU load in time +#ifdef CPU_FREQUENCY_STATISTICS + uint32_t mCpukHz[kSamplingN]; // absolute CPU clock frequency in kHz, bits 0-3 are CPU# +#endif +#endif + +}; // struct FastThreadDumpState + +} // android + +#endif // ANDROID_AUDIO_FAST_THREAD_STATE_H diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp new file mode 100644 index 0000000..7544052 --- /dev/null +++ b/services/audioflinger/PatchPanel.cpp @@ -0,0 +1,695 @@ +/* +** +** Copyright 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 "AudioFlinger::PatchPanel" +//#define LOG_NDEBUG 0 + +#include "Configuration.h" +#include <utils/Log.h> +#include <audio_utils/primitives.h> + +#include "AudioFlinger.h" +#include "ServiceUtilities.h" +#include <media/AudioParameter.h> + +// ---------------------------------------------------------------------------- + +// Note: the following macro is used for extremely verbose logging message. In +// order to run with ALOG_ASSERT turned on, we need to have LOG_NDEBUG set to +// 0; but one side effect of this is to turn all LOGV's as well. Some messages +// are so verbose that we want to suppress them even when we have ALOG_ASSERT +// turned on. Do not uncomment the #def below unless you really know what you +// are doing and want to see all of the extremely verbose messages. +//#define VERY_VERY_VERBOSE_LOGGING +#ifdef VERY_VERY_VERBOSE_LOGGING +#define ALOGVV ALOGV +#else +#define ALOGVV(a...) do { } while(0) +#endif + +namespace android { + +/* List connected audio ports and their attributes */ +status_t AudioFlinger::listAudioPorts(unsigned int *num_ports, + struct audio_port *ports) +{ + Mutex::Autolock _l(mLock); + if (mPatchPanel != 0) { + return mPatchPanel->listAudioPorts(num_ports, ports); + } + return NO_INIT; +} + +/* Get supported attributes for a given audio port */ +status_t AudioFlinger::getAudioPort(struct audio_port *port) +{ + Mutex::Autolock _l(mLock); + if (mPatchPanel != 0) { + return mPatchPanel->getAudioPort(port); + } + return NO_INIT; +} + + +/* Connect a patch between several source and sink ports */ +status_t AudioFlinger::createAudioPatch(const struct audio_patch *patch, + audio_patch_handle_t *handle) +{ + Mutex::Autolock _l(mLock); + if (mPatchPanel != 0) { + return mPatchPanel->createAudioPatch(patch, handle); + } + return NO_INIT; +} + +/* Disconnect a patch */ +status_t AudioFlinger::releaseAudioPatch(audio_patch_handle_t handle) +{ + Mutex::Autolock _l(mLock); + if (mPatchPanel != 0) { + return mPatchPanel->releaseAudioPatch(handle); + } + return NO_INIT; +} + + +/* List connected audio ports and they attributes */ +status_t AudioFlinger::listAudioPatches(unsigned int *num_patches, + struct audio_patch *patches) +{ + Mutex::Autolock _l(mLock); + if (mPatchPanel != 0) { + return mPatchPanel->listAudioPatches(num_patches, patches); + } + return NO_INIT; +} + +/* Set audio port configuration */ +status_t AudioFlinger::setAudioPortConfig(const struct audio_port_config *config) +{ + Mutex::Autolock _l(mLock); + if (mPatchPanel != 0) { + return mPatchPanel->setAudioPortConfig(config); + } + return NO_INIT; +} + + +AudioFlinger::PatchPanel::PatchPanel(const sp<AudioFlinger>& audioFlinger) + : mAudioFlinger(audioFlinger) +{ +} + +AudioFlinger::PatchPanel::~PatchPanel() +{ +} + +/* List connected audio ports and their attributes */ +status_t AudioFlinger::PatchPanel::listAudioPorts(unsigned int *num_ports __unused, + struct audio_port *ports __unused) +{ + ALOGV("listAudioPorts"); + return NO_ERROR; +} + +/* Get supported attributes for a given audio port */ +status_t AudioFlinger::PatchPanel::getAudioPort(struct audio_port *port __unused) +{ + ALOGV("getAudioPort"); + return NO_ERROR; +} + + +/* Connect a patch between several source and sink ports */ +status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *patch, + audio_patch_handle_t *handle) +{ + ALOGV("createAudioPatch() num_sources %d num_sinks %d handle %d", + patch->num_sources, patch->num_sinks, *handle); + status_t status = NO_ERROR; + audio_patch_handle_t halHandle = AUDIO_PATCH_HANDLE_NONE; + sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); + if (audioflinger == 0) { + return NO_INIT; + } + + if (handle == NULL || patch == NULL) { + return BAD_VALUE; + } + if (patch->num_sources == 0 || patch->num_sources > AUDIO_PATCH_PORTS_MAX || + patch->num_sinks == 0 || patch->num_sinks > AUDIO_PATCH_PORTS_MAX) { + return BAD_VALUE; + } + // limit number of sources to 1 for now or 2 sources for special cross hw module case. + // only the audio policy manager can request a patch creation with 2 sources. + if (patch->num_sources > 2) { + return INVALID_OPERATION; + } + + if (*handle != AUDIO_PATCH_HANDLE_NONE) { + for (size_t index = 0; *handle != 0 && index < mPatches.size(); index++) { + if (*handle == mPatches[index]->mHandle) { + ALOGV("createAudioPatch() removing patch handle %d", *handle); + halHandle = mPatches[index]->mHalHandle; + mPatches.removeAt(index); + break; + } + } + } + + Patch *newPatch = new Patch(patch); + + switch (patch->sources[0].type) { + case AUDIO_PORT_TYPE_DEVICE: { + audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module; + ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); + if (index < 0) { + ALOGW("createAudioPatch() bad src hw module %d", srcModule); + status = BAD_VALUE; + goto exit; + } + AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); + for (unsigned int i = 0; i < patch->num_sinks; i++) { + // support only one sink if connection to a mix or across HW modules + if ((patch->sinks[i].type == AUDIO_PORT_TYPE_MIX || + patch->sinks[i].ext.mix.hw_module != srcModule) && + patch->num_sinks > 1) { + status = INVALID_OPERATION; + goto exit; + } + // 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"); + status = BAD_VALUE; + goto exit; + } + // limit to connections between devices and input streams for HAL before 3.0 + if (patch->sinks[i].ext.mix.hw_module == srcModule && + (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); + status = BAD_VALUE; + goto exit; + } + } + + if (patch->sinks[0].ext.device.hw_module != srcModule) { + // limit to device to device connection if not on same hw module + if (patch->sinks[0].type != AUDIO_PORT_TYPE_DEVICE) { + ALOGW("createAudioPatch() invalid sink type for cross hw module"); + status = INVALID_OPERATION; + goto exit; + } + // special case num sources == 2 -=> reuse an exiting output mix to connect to the + // sink + if (patch->num_sources == 2) { + if (patch->sources[1].type != AUDIO_PORT_TYPE_MIX || + patch->sinks[0].ext.device.hw_module != + patch->sources[1].ext.mix.hw_module) { + ALOGW("createAudioPatch() invalid source combination"); + status = INVALID_OPERATION; + goto exit; + } + + sp<ThreadBase> thread = + audioflinger->checkPlaybackThread_l(patch->sources[1].ext.mix.handle); + newPatch->mPlaybackThread = (MixerThread *)thread.get(); + if (thread == 0) { + ALOGW("createAudioPatch() cannot get playback thread"); + status = INVALID_OPERATION; + goto exit; + } + } else { + audio_config_t config = AUDIO_CONFIG_INITIALIZER; + audio_devices_t device = patch->sinks[0].ext.device.type; + String8 address = String8(patch->sinks[0].ext.device.address); + audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; + newPatch->mPlaybackThread = audioflinger->openOutput_l( + patch->sinks[0].ext.device.hw_module, + &output, + &config, + device, + address, + AUDIO_OUTPUT_FLAG_NONE); + ALOGV("audioflinger->openOutput_l() returned %p", + newPatch->mPlaybackThread.get()); + if (newPatch->mPlaybackThread == 0) { + status = NO_MEMORY; + goto exit; + } + } + uint32_t channelCount = newPatch->mPlaybackThread->channelCount(); + audio_devices_t device = patch->sources[0].ext.device.type; + String8 address = String8(patch->sources[0].ext.device.address); + audio_config_t config = AUDIO_CONFIG_INITIALIZER; + audio_channel_mask_t inChannelMask = audio_channel_in_mask_from_count(channelCount); + config.sample_rate = newPatch->mPlaybackThread->sampleRate(); + config.channel_mask = inChannelMask; + config.format = newPatch->mPlaybackThread->format(); + audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; + newPatch->mRecordThread = audioflinger->openInput_l(srcModule, + &input, + &config, + device, + address, + AUDIO_SOURCE_MIC, + AUDIO_INPUT_FLAG_NONE); + ALOGV("audioflinger->openInput_l() returned %p inChannelMask %08x", + newPatch->mRecordThread.get(), inChannelMask); + if (newPatch->mRecordThread == 0) { + status = NO_MEMORY; + goto exit; + } + status = createPatchConnections(newPatch, patch); + if (status != NO_ERROR) { + goto exit; + } + } else { + if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) { + if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) { + sp<ThreadBase> thread = audioflinger->checkRecordThread_l( + patch->sinks[0].ext.mix.handle); + if (thread == 0) { + ALOGW("createAudioPatch() bad capture I/O handle %d", + patch->sinks[0].ext.mix.handle); + status = BAD_VALUE; + goto exit; + } + status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle); + } else { + audio_hw_device_t *hwDevice = audioHwDevice->hwDevice(); + status = hwDevice->create_audio_patch(hwDevice, + patch->num_sources, + patch->sources, + patch->num_sinks, + patch->sinks, + &halHandle); + } + } else { + sp<ThreadBase> thread = audioflinger->checkRecordThread_l( + patch->sinks[0].ext.mix.handle); + if (thread == 0) { + ALOGW("createAudioPatch() bad capture I/O handle %d", + patch->sinks[0].ext.mix.handle); + status = BAD_VALUE; + goto exit; + } + char *address; + if (strcmp(patch->sources[0].ext.device.address, "") != 0) { + address = audio_device_address_to_parameter( + patch->sources[0].ext.device.type, + patch->sources[0].ext.device.address); + } else { + address = (char *)calloc(1, 1); + } + AudioParameter param = AudioParameter(String8(address)); + free(address); + param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), + (int)patch->sources[0].ext.device.type); + param.addInt(String8(AUDIO_PARAMETER_STREAM_INPUT_SOURCE), + (int)patch->sinks[0].ext.mix.usecase.source); + ALOGV("createAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s", + param.toString().string()); + status = thread->setParameters(param.toString()); + } + } + } break; + case AUDIO_PORT_TYPE_MIX: { + audio_module_handle_t srcModule = patch->sources[0].ext.mix.hw_module; + ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); + if (index < 0) { + ALOGW("createAudioPatch() bad src hw module %d", srcModule); + status = BAD_VALUE; + goto exit; + } + // limit to connections between devices and output streams + for (unsigned int i = 0; i < patch->num_sinks; i++) { + if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) { + ALOGW("createAudioPatch() invalid sink type %d for mix source", + patch->sinks[i].type); + status = BAD_VALUE; + goto exit; + } + // limit to connections between sinks and sources on same HW module + if (patch->sinks[i].ext.device.hw_module != srcModule) { + status = BAD_VALUE; + goto exit; + } + } + AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); + sp<ThreadBase> thread = + audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle); + if (thread == 0) { + ALOGW("createAudioPatch() bad playback I/O handle %d", + patch->sources[0].ext.mix.handle); + status = BAD_VALUE; + goto exit; + } + if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) { + status = thread->sendCreateAudioPatchConfigEvent(patch, &halHandle); + } else { + audio_devices_t type = AUDIO_DEVICE_NONE; + for (unsigned int i = 0; i < patch->num_sinks; i++) { + type |= patch->sinks[i].ext.device.type; + } + char *address; + if (strcmp(patch->sinks[0].ext.device.address, "") != 0) { + //FIXME: we only support address on first sink with HAL version < 3.0 + address = audio_device_address_to_parameter( + patch->sinks[0].ext.device.type, + patch->sinks[0].ext.device.address); + } else { + address = (char *)calloc(1, 1); + } + AudioParameter param = AudioParameter(String8(address)); + free(address); + param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), (int)type); + status = thread->setParameters(param.toString()); + } + + } break; + default: + status = BAD_VALUE; + goto exit; + } +exit: + ALOGV("createAudioPatch() status %d", status); + if (status == NO_ERROR) { + *handle = audioflinger->nextUniqueId(); + newPatch->mHandle = *handle; + newPatch->mHalHandle = halHandle; + mPatches.add(newPatch); + ALOGV("createAudioPatch() added new patch handle %d halHandle %d", *handle, halHandle); + } else { + clearPatchConnections(newPatch); + delete newPatch; + } + return status; +} + +status_t AudioFlinger::PatchPanel::createPatchConnections(Patch *patch, + const struct audio_patch *audioPatch) +{ + // create patch from source device to record thread input + struct audio_patch subPatch; + subPatch.num_sources = 1; + subPatch.sources[0] = audioPatch->sources[0]; + subPatch.num_sinks = 1; + + patch->mRecordThread->getAudioPortConfig(&subPatch.sinks[0]); + subPatch.sinks[0].ext.mix.usecase.source = AUDIO_SOURCE_MIC; + + status_t status = createAudioPatch(&subPatch, &patch->mRecordPatchHandle); + if (status != NO_ERROR) { + patch->mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE; + return status; + } + + // create patch from playback thread output to sink device + patch->mPlaybackThread->getAudioPortConfig(&subPatch.sources[0]); + subPatch.sinks[0] = audioPatch->sinks[0]; + status = createAudioPatch(&subPatch, &patch->mPlaybackPatchHandle); + if (status != NO_ERROR) { + patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; + return status; + } + + // use a pseudo LCM between input and output framecount + size_t playbackFrameCount = patch->mPlaybackThread->frameCount(); + int playbackShift = __builtin_ctz(playbackFrameCount); + size_t recordFramecount = patch->mRecordThread->frameCount(); + int shift = __builtin_ctz(recordFramecount); + if (playbackShift < shift) { + shift = playbackShift; + } + size_t frameCount = (playbackFrameCount * recordFramecount) >> shift; + ALOGV("createPatchConnections() playframeCount %d recordFramecount %d frameCount %d ", + playbackFrameCount, recordFramecount, frameCount); + + // create a special record track to capture from record thread + uint32_t channelCount = patch->mPlaybackThread->channelCount(); + audio_channel_mask_t inChannelMask = audio_channel_in_mask_from_count(channelCount); + audio_channel_mask_t outChannelMask = patch->mPlaybackThread->channelMask(); + uint32_t sampleRate = patch->mPlaybackThread->sampleRate(); + audio_format_t format = patch->mPlaybackThread->format(); + + patch->mPatchRecord = new RecordThread::PatchRecord( + patch->mRecordThread.get(), + sampleRate, + inChannelMask, + format, + frameCount, + NULL, + IAudioFlinger::TRACK_DEFAULT); + if (patch->mPatchRecord == 0) { + return NO_MEMORY; + } + status = patch->mPatchRecord->initCheck(); + if (status != NO_ERROR) { + return status; + } + patch->mRecordThread->addPatchRecord(patch->mPatchRecord); + + // create a special playback track to render to playback thread. + // this track is given the same buffer as the PatchRecord buffer + patch->mPatchTrack = new PlaybackThread::PatchTrack( + patch->mPlaybackThread.get(), + sampleRate, + outChannelMask, + format, + frameCount, + patch->mPatchRecord->buffer(), + IAudioFlinger::TRACK_DEFAULT); + if (patch->mPatchTrack == 0) { + return NO_MEMORY; + } + status = patch->mPatchTrack->initCheck(); + if (status != NO_ERROR) { + return status; + } + patch->mPlaybackThread->addPatchTrack(patch->mPatchTrack); + + // tie playback and record tracks together + patch->mPatchRecord->setPeerProxy(patch->mPatchTrack.get()); + patch->mPatchTrack->setPeerProxy(patch->mPatchRecord.get()); + + // start capture and playback + patch->mPatchRecord->start(AudioSystem::SYNC_EVENT_NONE, 0); + patch->mPatchTrack->start(); + + return status; +} + +void AudioFlinger::PatchPanel::clearPatchConnections(Patch *patch) +{ + sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); + if (audioflinger == 0) { + return; + } + + ALOGV("clearPatchConnections() patch->mRecordPatchHandle %d patch->mPlaybackPatchHandle %d", + patch->mRecordPatchHandle, patch->mPlaybackPatchHandle); + + if (patch->mPatchRecord != 0) { + patch->mPatchRecord->stop(); + } + if (patch->mPatchTrack != 0) { + patch->mPatchTrack->stop(); + } + if (patch->mRecordPatchHandle != AUDIO_PATCH_HANDLE_NONE) { + releaseAudioPatch(patch->mRecordPatchHandle); + patch->mRecordPatchHandle = AUDIO_PATCH_HANDLE_NONE; + } + if (patch->mPlaybackPatchHandle != AUDIO_PATCH_HANDLE_NONE) { + releaseAudioPatch(patch->mPlaybackPatchHandle); + patch->mPlaybackPatchHandle = AUDIO_PATCH_HANDLE_NONE; + } + if (patch->mRecordThread != 0) { + if (patch->mPatchRecord != 0) { + patch->mRecordThread->deletePatchRecord(patch->mPatchRecord); + patch->mPatchRecord.clear(); + } + audioflinger->closeInputInternal_l(patch->mRecordThread); + patch->mRecordThread.clear(); + } + if (patch->mPlaybackThread != 0) { + if (patch->mPatchTrack != 0) { + patch->mPlaybackThread->deletePatchTrack(patch->mPatchTrack); + patch->mPatchTrack.clear(); + } + // if num sources == 2 we are reusing an existing playback thread so we do not close it + if (patch->mAudioPatch.num_sources != 2) { + audioflinger->closeOutputInternal_l(patch->mPlaybackThread); + } + patch->mPlaybackThread.clear(); + } +} + +/* Disconnect a patch */ +status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle) +{ + ALOGV("releaseAudioPatch handle %d", handle); + status_t status = NO_ERROR; + size_t index; + + sp<AudioFlinger> audioflinger = mAudioFlinger.promote(); + if (audioflinger == 0) { + return NO_INIT; + } + + for (index = 0; index < mPatches.size(); index++) { + if (handle == mPatches[index]->mHandle) { + break; + } + } + if (index == mPatches.size()) { + return BAD_VALUE; + } + Patch *removedPatch = mPatches[index]; + mPatches.removeAt(index); + + struct audio_patch *patch = &removedPatch->mAudioPatch; + + switch (patch->sources[0].type) { + case AUDIO_PORT_TYPE_DEVICE: { + audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module; + ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); + if (index < 0) { + ALOGW("releaseAudioPatch() bad src hw module %d", srcModule); + status = BAD_VALUE; + break; + } + + if (patch->sinks[0].type == AUDIO_PORT_TYPE_DEVICE && + patch->sinks[0].ext.device.hw_module != srcModule) { + clearPatchConnections(removedPatch); + break; + } + + 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( + patch->sinks[0].ext.mix.handle); + if (thread == 0) { + ALOGW("releaseAudioPatch() bad capture I/O handle %d", + patch->sinks[0].ext.mix.handle); + status = BAD_VALUE; + break; + } + status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle); + } else { + audio_hw_device_t *hwDevice = audioHwDevice->hwDevice(); + status = hwDevice->release_audio_patch(hwDevice, removedPatch->mHalHandle); + } + } else { + sp<ThreadBase> thread = audioflinger->checkRecordThread_l( + patch->sinks[0].ext.mix.handle); + if (thread == 0) { + ALOGW("releaseAudioPatch() bad capture I/O handle %d", + patch->sinks[0].ext.mix.handle); + status = BAD_VALUE; + break; + } + AudioParameter param; + param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), 0); + ALOGV("releaseAudioPatch() AUDIO_PORT_TYPE_DEVICE setParameters %s", + param.toString().string()); + status = thread->setParameters(param.toString()); + } + } break; + case AUDIO_PORT_TYPE_MIX: { + audio_module_handle_t srcModule = patch->sources[0].ext.mix.hw_module; + ssize_t index = audioflinger->mAudioHwDevs.indexOfKey(srcModule); + if (index < 0) { + ALOGW("releaseAudioPatch() bad src hw module %d", srcModule); + status = BAD_VALUE; + break; + } + sp<ThreadBase> thread = + audioflinger->checkPlaybackThread_l(patch->sources[0].ext.mix.handle); + if (thread == 0) { + ALOGW("releaseAudioPatch() bad playback I/O handle %d", + patch->sources[0].ext.mix.handle); + status = BAD_VALUE; + break; + } + AudioHwDevice *audioHwDevice = audioflinger->mAudioHwDevs.valueAt(index); + if (audioHwDevice->version() >= AUDIO_DEVICE_API_VERSION_3_0) { + status = thread->sendReleaseAudioPatchConfigEvent(removedPatch->mHalHandle); + } else { + AudioParameter param; + param.addInt(String8(AUDIO_PARAMETER_STREAM_ROUTING), 0); + status = thread->setParameters(param.toString()); + } + } break; + default: + status = BAD_VALUE; + break; + } + + delete removedPatch; + return status; +} + + +/* List connected audio ports and they attributes */ +status_t AudioFlinger::PatchPanel::listAudioPatches(unsigned int *num_patches __unused, + struct audio_patch *patches __unused) +{ + ALOGV("listAudioPatches"); + return NO_ERROR; +} + +/* Set audio port configuration */ +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/PatchPanel.h b/services/audioflinger/PatchPanel.h new file mode 100644 index 0000000..e31179c --- /dev/null +++ b/services/audioflinger/PatchPanel.h @@ -0,0 +1,78 @@ +/* +** +** Copyright 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 INCLUDING_FROM_AUDIOFLINGER_H + #error This header file should only be included from AudioFlinger.h +#endif + +class PatchPanel : public RefBase { +public: + + class Patch; + + PatchPanel(const sp<AudioFlinger>& audioFlinger); + virtual ~PatchPanel(); + + /* List connected audio ports and their attributes */ + status_t listAudioPorts(unsigned int *num_ports, + struct audio_port *ports); + + /* Get supported attributes for a given audio port */ + status_t getAudioPort(struct audio_port *port); + + /* Create a patch between several source and sink ports */ + status_t createAudioPatch(const struct audio_patch *patch, + audio_patch_handle_t *handle); + + /* Release a patch */ + status_t releaseAudioPatch(audio_patch_handle_t handle); + + /* List connected audio devices and they attributes */ + status_t listAudioPatches(unsigned int *num_patches, + struct audio_patch *patches); + + /* Set audio port configuration */ + status_t setAudioPortConfig(const struct audio_port_config *config); + + status_t createPatchConnections(Patch *patch, + const struct audio_patch *audioPatch); + void clearPatchConnections(Patch *patch); + + class Patch { + public: + Patch(const struct audio_patch *patch) : + mAudioPatch(*patch), mHandle(AUDIO_PATCH_HANDLE_NONE), + mHalHandle(AUDIO_PATCH_HANDLE_NONE), mRecordPatchHandle(AUDIO_PATCH_HANDLE_NONE), + mPlaybackPatchHandle(AUDIO_PATCH_HANDLE_NONE) {} + ~Patch() {} + + struct audio_patch mAudioPatch; + audio_patch_handle_t mHandle; + audio_patch_handle_t mHalHandle; + sp<PlaybackThread> mPlaybackThread; + sp<PlaybackThread::PatchTrack> mPatchTrack; + sp<RecordThread> mRecordThread; + sp<RecordThread::PatchRecord> mPatchRecord; + audio_patch_handle_t mRecordPatchHandle; + audio_patch_handle_t mPlaybackPatchHandle; + + }; + +private: + const wp<AudioFlinger> mAudioFlinger; + SortedVector <Patch *> mPatches; +}; diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index 43b77f3..ee48276 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -29,14 +29,17 @@ public: audio_format_t format, audio_channel_mask_t channelMask, size_t frameCount, + void *buffer, const sp<IMemory>& sharedBuffer, int sessionId, int uid, - IAudioFlinger::track_flags_t flags); + IAudioFlinger::track_flags_t flags, + track_type type); virtual ~Track(); + virtual status_t initCheck() const; static void appendDumpHeader(String8& result); - void dump(char* buffer, size_t size); + void dump(char* buffer, size_t size, bool active); virtual status_t start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE, int triggerSession = 0); @@ -53,6 +56,7 @@ public: return mStreamType; } bool isOffloaded() const { return (mFlags & IAudioFlinger::TRACK_OFFLOAD) != 0; } + bool isDirect() const { return (mFlags & IAudioFlinger::TRACK_DIRECT) != 0; } status_t setParameters(const String8& keyValuePairs); status_t attachAuxEffect(int EffectId); void setAuxBuffer(int EffectId, int32_t *buffer); @@ -64,7 +68,7 @@ public: void signal(); // implement FastMixerState::VolumeProvider interface - virtual uint32_t getVolumeLR(); + virtual gain_minifloat_packed_t getVolumeLR(); virtual status_t setSyncEvent(const sp<SyncEvent>& event); @@ -93,10 +97,10 @@ protected: bool isReady() const; void setPaused() { mState = PAUSED; } void reset(); - - bool isOutputTrack() const { - return (mStreamType == AUDIO_STREAM_CNT); - } + bool isFlushPending() const { return mFlushHwPending; } + void flushAck(); + bool isResumePending(); + void resumeAck(); sp<IMemory> sharedBuffer() const { return mSharedBuffer; } @@ -109,8 +113,6 @@ public: void triggerEvents(AudioSystem::sync_event_t type); void invalidate(); bool isInvalid() const { return mIsInvalid; } - virtual bool isTimedTrack() const { return false; } - bool isFastTrack() const { return (mFlags & IAudioFlinger::TRACK_FAST) != 0; } int fastIndex() const { return mFastIndex; } protected: @@ -137,8 +139,6 @@ protected: // audio HAL when this track will be fully rendered // zero means not monitoring private: - IAudioFlinger::track_flags_t mFlags; - // The following fields are only for fast tracks, and should be in a subclass int mFastIndex; // index within FastMixerState::mFastTracks[]; // either mFastIndex == -1 if not isFastTrack() @@ -154,6 +154,12 @@ private: bool mIsInvalid; // non-resettable latch, set by invalidate() AudioTrackServerProxy* mAudioTrackServerProxy; bool mResumeToStopping; // track was paused in stopping state. + bool mFlushHwPending; // track requests for thread flush + + // for last call to getTimestamp + bool mPreviousValid; + uint32_t mPreviousFramesWritten; + AudioTimestamp mPreviousTimestamp; }; // end of Track class TimedTrack : public Track { @@ -185,7 +191,6 @@ class TimedTrack : public Track { }; // Mixer facing methods. - virtual bool isTimedTrack() const { return true; } virtual size_t framesReady() const; // AudioBufferProvider interface @@ -286,3 +291,34 @@ private: DuplicatingThread* const mSourceThread; // for waitTimeMs() in write() AudioTrackClientProxy* mClientProxy; }; // end of OutputTrack + +// playback track, used by PatchPanel +class PatchTrack : public Track, public PatchProxyBufferProvider { +public: + + PatchTrack(PlaybackThread *playbackThread, + uint32_t sampleRate, + audio_channel_mask_t channelMask, + audio_format_t format, + size_t frameCount, + void *buffer, + IAudioFlinger::track_flags_t flags); + virtual ~PatchTrack(); + + // AudioBufferProvider interface + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer, + int64_t pts); + virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer); + + // PatchProxyBufferProvider interface + virtual status_t obtainBuffer(Proxy::Buffer* buffer, + const struct timespec *timeOut = NULL); + virtual void releaseBuffer(Proxy::Buffer* buffer); + + void setPeerProxy(PatchProxyBufferProvider *proxy) { mPeerProxy = proxy; } + +private: + sp<ClientProxy> mProxy; + PatchProxyBufferProvider* mPeerProxy; + struct timespec mPeerTimeout; +}; // end of PatchTrack diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h index 57de568..204a9d6 100644 --- a/services/audioflinger/RecordTracks.h +++ b/services/audioflinger/RecordTracks.h @@ -28,8 +28,11 @@ public: audio_format_t format, audio_channel_mask_t channelMask, size_t frameCount, + void *buffer, int sessionId, - int uid); + int uid, + IAudioFlinger::track_flags_t flags, + track_type type); virtual ~RecordTrack(); virtual status_t start(AudioSystem::sync_event_t event, int triggerSession); @@ -45,7 +48,10 @@ public: return tmp; } static void appendDumpHeader(String8& result); - void dump(char* buffer, size_t size); + void dump(char* buffer, size_t size, bool active); + + void handleSyncStartEvent(const sp<SyncEvent>& event); + void clearSyncStartEvent(); private: friend class AudioFlinger; // for mState @@ -59,5 +65,64 @@ private: // releaseBuffer() not overridden bool mOverflow; // overflow on most recent attempt to fill client buffer - AudioRecordServerProxy* mAudioRecordServerProxy; + + // updated by RecordThread::readInputParameters_l() + AudioResampler *mResampler; + + // interleaved stereo pairs of fixed-point Q4.27 + int32_t *mRsmpOutBuffer; + // current allocated frame count for the above, which may be larger than needed + size_t mRsmpOutFrameCount; + + size_t mRsmpInUnrel; // unreleased frames remaining from + // most recent getNextBuffer + // for debug only + + // rolling counter that is never cleared + int32_t mRsmpInFront; // next available frame + + AudioBufferProvider::Buffer mSink; // references client's buffer sink in shared memory + + // sync event triggering actual audio capture. Frames read before this event will + // be dropped and therefore not read by the application. + sp<SyncEvent> mSyncStartEvent; + + // number of captured frames to drop after the start sync event has been received. + // when < 0, maximum frames to drop before starting capture even if sync event is + // not received + ssize_t mFramesToDrop; + + // used by resampler to find source frames + ResamplerBufferProvider *mResamplerBufferProvider; }; + +// playback track, used by PatchPanel +class PatchRecord : virtual public RecordTrack, public PatchProxyBufferProvider { +public: + + PatchRecord(RecordThread *recordThread, + uint32_t sampleRate, + audio_channel_mask_t channelMask, + audio_format_t format, + size_t frameCount, + void *buffer, + IAudioFlinger::track_flags_t flags); + virtual ~PatchRecord(); + + // AudioBufferProvider interface + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer, + int64_t pts); + virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer); + + // PatchProxyBufferProvider interface + virtual status_t obtainBuffer(Proxy::Buffer *buffer, + const struct timespec *timeOut = NULL); + virtual void releaseBuffer(Proxy::Buffer *buffer); + + void setPeerProxy(PatchProxyBufferProvider *proxy) { mPeerProxy = proxy; } + +private: + sp<ClientProxy> mProxy; + PatchProxyBufferProvider* mPeerProxy; + struct timespec mPeerTimeout; +}; // end of PatchRecord diff --git a/services/audioflinger/ServiceUtilities.cpp b/services/audioflinger/ServiceUtilities.cpp index 152455d..8246fef 100644 --- a/services/audioflinger/ServiceUtilities.cpp +++ b/services/audioflinger/ServiceUtilities.cpp @@ -59,6 +59,13 @@ bool settingsAllowed() { return ok; } +bool modifyAudioRoutingAllowed() { + static const String16 sModifyAudioRoutingAllowed("android.permission.MODIFY_AUDIO_ROUTING"); + bool ok = checkCallingPermission(sModifyAudioRoutingAllowed); + if (!ok) ALOGE("android.permission.MODIFY_AUDIO_ROUTING"); + return ok; +} + bool dumpAllowed() { // don't optimize for same pid, since mediaserver never dumps itself static const String16 sDump("android.permission.DUMP"); diff --git a/services/audioflinger/ServiceUtilities.h b/services/audioflinger/ServiceUtilities.h index 531bc56..df6f6f4 100644 --- a/services/audioflinger/ServiceUtilities.h +++ b/services/audioflinger/ServiceUtilities.h @@ -24,6 +24,7 @@ bool recordingAllowed(); bool captureAudioOutputAllowed(); bool captureHotwordAllowed(); bool settingsAllowed(); +bool modifyAudioRoutingAllowed(); bool dumpAllowed(); } diff --git a/services/audioflinger/StateQueue.h b/services/audioflinger/StateQueue.h index ef01df7..27f6a28 100644 --- a/services/audioflinger/StateQueue.h +++ b/services/audioflinger/StateQueue.h @@ -91,6 +91,8 @@ // arithmetic on the state pointers. However to the mutator, the state pointers // are in a definite circular order. +#include "Configuration.h" + namespace android { #ifdef STATE_QUEUE_DUMP 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 23a2174..97b1753 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -26,6 +26,7 @@ #include <sys/stat.h> #include <cutils/properties.h> #include <media/AudioParameter.h> +#include <media/AudioResamplerPublic.h> #include <utils/Log.h> #include <utils/Trace.h> @@ -34,8 +35,11 @@ #include <audio_effects/effect_ns.h> #include <audio_effects/effect_aec.h> #include <audio_utils/primitives.h> +#include <audio_utils/format.h> +#include <audio_utils/minifloat.h> // NBAIO implementations +#include <media/nbaio/AudioStreamInSource.h> #include <media/nbaio/AudioStreamOutSink.h> #include <media/nbaio/MonoPipe.h> #include <media/nbaio/MonoPipeReader.h> @@ -51,6 +55,7 @@ #include "AudioFlinger.h" #include "AudioMixer.h" #include "FastMixer.h" +#include "FastCapture.h" #include "ServiceUtilities.h" #include "SchedulingPolicyService.h" @@ -79,6 +84,8 @@ #define ALOGVV(a...) do { } while(0) #endif +#define max(a, b) ((a) > (b) ? (a) : (b)) + namespace android { // retry counts for buffer fill timeout @@ -96,18 +103,18 @@ static const nsecs_t kWarningThrottleNs = seconds(5); // RecordThread loop sleep time upon application overrun or audio HAL read error static const int kRecordThreadSleepUs = 5000; -// maximum time to wait for setParameters to complete -static const nsecs_t kSetParametersTimeoutNs = seconds(2); +// maximum time to wait in sendConfigEvent_l() for a status to be received +static const nsecs_t kConfigEventTimeoutNs = seconds(2); // minimum sleep time for the mixer thread loop when tracks are active but in underrun static const uint32_t kMinThreadSleepTimeUs = 5000; // maximum divider applied to the active sleep time in the mixer thread loop static const uint32_t kMaxThreadSleepTimeShift = 2; -// minimum normal mix buffer size, expressed in milliseconds rather than frames -static const uint32_t kMinNormalMixBufferSizeMs = 20; -// maximum normal mix buffer size -static const uint32_t kMaxNormalMixBufferSizeMs = 24; +// minimum normal sink buffer size, expressed in milliseconds rather than frames +static const uint32_t kMinNormalSinkBufferSizeMs = 20; +// maximum normal sink buffer size +static const uint32_t kMaxNormalSinkBufferSizeMs = 24; // Offloaded output thread standby delay: allows track transition without going to standby static const nsecs_t kOffloadStandbyDelayNs = seconds(1); @@ -129,9 +136,17 @@ static const enum { // up large writes into smaller ones, and the wrapper would need to deal with scheduler. } kUseFastMixer = FastMixer_Static; +// Whether to use fast capture +static const enum { + FastCapture_Never, // never initialize or use: for debugging only + FastCapture_Always, // always initialize and use, even if not needed: for debugging only + FastCapture_Static, // initialize if needed, then use all the time if initialized +} kUseFastCapture = FastCapture_Static; + // Priorities for requestPriority static const int kPriorityAudioApp = 2; static const int kPriorityFastMixer = 3; +static const int kPriorityFastCapture = 3; // IAudioFlinger::createTrack() reports back to client the total size of shared memory area // for the track. The client then sub-divides this into smaller buffers for its use. @@ -140,8 +155,39 @@ static const int kPriorityFastMixer = 3; // FIXME It would be better for client to tell AudioFlinger the value of N, // so AudioFlinger could allocate the right amount of memory. // See the client's minBufCount and mNotificationFramesAct calculations for details. + +// This is the default value, if not specified by property. static const int kFastTrackMultiplier = 2; +// The minimum and maximum allowed values +static const int kFastTrackMultiplierMin = 1; +static const int kFastTrackMultiplierMax = 2; + +// The actual value to use, which can be specified per-device via property af.fast_track_multiplier. +static int sFastTrackMultiplier = kFastTrackMultiplier; + +// See Thread::readOnlyHeap(). +// Initially this heap is used to allocate client buffers for "fast" AudioRecord. +// Eventually it will be the single buffer that FastCapture writes into via HAL read(), +// and that all "fast" AudioRecord clients read from. In either case, the size can be small. +static const size_t kRecordThreadReadOnlyHeapSize = 0x2000; + +// ---------------------------------------------------------------------------- + +static pthread_once_t sFastTrackMultiplierOnce = PTHREAD_ONCE_INIT; + +static void sFastTrackMultiplierInit() +{ + char value[PROPERTY_VALUE_MAX]; + if (property_get("af.fast_track_multiplier", value, NULL) > 0) { + char *endptr; + unsigned long ul = strtoul(value, &endptr, 0); + if (*endptr == '\0' && kFastTrackMultiplierMin <= ul && ul <= kFastTrackMultiplierMax) { + sFastTrackMultiplier = (int) ul; + } + } +} + // ---------------------------------------------------------------------------- #ifdef ADD_BATTERY_DATA @@ -185,7 +231,11 @@ CpuStats::CpuStats() { } -void CpuStats::sample(const String8 &title) { +void CpuStats::sample(const String8 &title +#ifndef DEBUG_CPU_USAGE + __unused +#endif + ) { #ifdef DEBUG_CPU_USAGE // get current thread's delta CPU time in wall clock ns double wcNs; @@ -269,9 +319,9 @@ AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio : Thread(false /*canCallJava*/), mType(type), mAudioFlinger(audioFlinger), - // mSampleRate, mFrameCount, mChannelMask, mChannelCount, mFrameSize, and mFormat are - // set by PlaybackThread::readOutputParameters() or RecordThread::readInputParameters() - mParamStatus(NO_ERROR), + // mSampleRate, mFrameCount, mChannelMask, mChannelCount, mFrameSize, mFormat, mBufferSize + // are set by PlaybackThread::readOutputParameters_l() or + // RecordThread::readInputParameters_l() //FIXME: mStandby should be true here. Is this some kind of hack? mStandby(false), mOutDevice(outDevice), mInDevice(inDevice), mAudioSource(AUDIO_SOURCE_DEFAULT), mId(id), @@ -283,12 +333,8 @@ AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio AudioFlinger::ThreadBase::~ThreadBase() { // mConfigEvents should be empty, but just in case it isn't, free the memory it owns - for (size_t i = 0; i < mConfigEvents.size(); i++) { - delete mConfigEvents[i]; - } mConfigEvents.clear(); - mParamCond.broadcast(); // do not lock the mutex in destructor releaseWakeLock_l(); if (mPowerManager != 0) { @@ -297,6 +343,17 @@ AudioFlinger::ThreadBase::~ThreadBase() } } +status_t AudioFlinger::ThreadBase::readyToRun() +{ + status_t status = initCheck(); + if (status == NO_ERROR) { + ALOGI("AudioFlinger's thread %p ready to run", this); + } else { + ALOGE("No working audio driver found."); + } + return status; +} + void AudioFlinger::ThreadBase::exit() { ALOGV("ThreadBase::exit"); @@ -328,16 +385,30 @@ status_t AudioFlinger::ThreadBase::setParameters(const String8& keyValuePairs) ALOGV("ThreadBase::setParameters() %s", keyValuePairs.string()); Mutex::Autolock _l(mLock); - mNewParameters.add(keyValuePairs); + return sendSetParameterConfigEvent_l(keyValuePairs); +} + +// sendConfigEvent_l() must be called with ThreadBase::mLock held +// Can temporarily release the lock if waiting for a reply from processConfigEvents_l(). +status_t AudioFlinger::ThreadBase::sendConfigEvent_l(sp<ConfigEvent>& event) +{ + status_t status = NO_ERROR; + + mConfigEvents.add(event); + ALOGV("sendConfigEvent_l() num events %d event %d", mConfigEvents.size(), event->mType); mWaitWorkCV.signal(); - // wait condition with timeout in case the thread loop has exited - // before the request could be processed - if (mParamCond.waitRelative(mLock, kSetParametersTimeoutNs) == NO_ERROR) { - status = mParamStatus; - mWaitWorkCV.signal(); - } else { - status = TIMED_OUT; + mLock.unlock(); + { + Mutex::Autolock _l(event->mLock); + while (event->mWaitStatus) { + if (event->mCond.waitRelative(event->mLock, kConfigEventTimeoutNs) != NO_ERROR) { + event->mStatus = TIMED_OUT; + event->mWaitStatus = false; + } + } + status = event->mStatus; } + mLock.lock(); return status; } @@ -350,62 +421,155 @@ void AudioFlinger::ThreadBase::sendIoConfigEvent(int event, int param) // sendIoConfigEvent_l() must be called with ThreadBase::mLock held void AudioFlinger::ThreadBase::sendIoConfigEvent_l(int event, int param) { - IoConfigEvent *ioEvent = new IoConfigEvent(event, param); - mConfigEvents.add(static_cast<ConfigEvent *>(ioEvent)); - ALOGV("sendIoConfigEvent() num events %d event %d, param %d", mConfigEvents.size(), event, - param); - mWaitWorkCV.signal(); + sp<ConfigEvent> configEvent = (ConfigEvent *)new IoConfigEvent(event, param); + sendConfigEvent_l(configEvent); } // sendPrioConfigEvent_l() must be called with ThreadBase::mLock held void AudioFlinger::ThreadBase::sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio) { - PrioConfigEvent *prioEvent = new PrioConfigEvent(pid, tid, prio); - mConfigEvents.add(static_cast<ConfigEvent *>(prioEvent)); - ALOGV("sendPrioConfigEvent_l() num events %d pid %d, tid %d prio %d", - mConfigEvents.size(), pid, tid, prio); - mWaitWorkCV.signal(); + sp<ConfigEvent> configEvent = (ConfigEvent *)new PrioConfigEvent(pid, tid, prio); + sendConfigEvent_l(configEvent); } -void AudioFlinger::ThreadBase::processConfigEvents() +// sendSetParameterConfigEvent_l() must be called with ThreadBase::mLock held +status_t AudioFlinger::ThreadBase::sendSetParameterConfigEvent_l(const String8& keyValuePair) { - mLock.lock(); - while (!mConfigEvents.isEmpty()) { - ALOGV("processConfigEvents() remaining events %d", mConfigEvents.size()); - ConfigEvent *event = mConfigEvents[0]; - mConfigEvents.removeAt(0); - // release mLock before locking AudioFlinger mLock: lock order is always - // AudioFlinger then ThreadBase to avoid cross deadlock - mLock.unlock(); - switch(event->type()) { - case CFG_EVENT_PRIO: { - PrioConfigEvent *prioEvent = static_cast<PrioConfigEvent *>(event); - // FIXME Need to understand why this has be done asynchronously - int err = requestPriority(prioEvent->pid(), prioEvent->tid(), prioEvent->prio(), - true /*asynchronous*/); - if (err != 0) { - ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; " - "error %d", - prioEvent->prio(), prioEvent->pid(), prioEvent->tid(), err); - } - } break; - case CFG_EVENT_IO: { - IoConfigEvent *ioEvent = static_cast<IoConfigEvent *>(event); - mAudioFlinger->mLock.lock(); - audioConfigChanged_l(ioEvent->event(), ioEvent->param()); - mAudioFlinger->mLock.unlock(); - } break; - default: - ALOGE("processConfigEvents() unknown event type %d", event->type()); - break; - } - delete event; - mLock.lock(); + sp<ConfigEvent> configEvent = (ConfigEvent *)new SetParameterConfigEvent(keyValuePair); + return sendConfigEvent_l(configEvent); +} + +status_t AudioFlinger::ThreadBase::sendCreateAudioPatchConfigEvent( + const struct audio_patch *patch, + audio_patch_handle_t *handle) +{ + Mutex::Autolock _l(mLock); + sp<ConfigEvent> configEvent = (ConfigEvent *)new CreateAudioPatchConfigEvent(*patch, *handle); + status_t status = sendConfigEvent_l(configEvent); + if (status == NO_ERROR) { + CreateAudioPatchConfigEventData *data = + (CreateAudioPatchConfigEventData *)configEvent->mData.get(); + *handle = data->mHandle; } - mLock.unlock(); + return status; } -void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args) +status_t AudioFlinger::ThreadBase::sendReleaseAudioPatchConfigEvent( + const audio_patch_handle_t handle) +{ + Mutex::Autolock _l(mLock); + sp<ConfigEvent> configEvent = (ConfigEvent *)new ReleaseAudioPatchConfigEvent(handle); + return sendConfigEvent_l(configEvent); +} + + +// post condition: mConfigEvents.isEmpty() +void AudioFlinger::ThreadBase::processConfigEvents_l() +{ + bool configChanged = false; + + while (!mConfigEvents.isEmpty()) { + ALOGV("processConfigEvents_l() remaining events %d", mConfigEvents.size()); + sp<ConfigEvent> event = mConfigEvents[0]; + mConfigEvents.removeAt(0); + switch (event->mType) { + case CFG_EVENT_PRIO: { + PrioConfigEventData *data = (PrioConfigEventData *)event->mData.get(); + // FIXME Need to understand why this has to be done asynchronously + int err = requestPriority(data->mPid, data->mTid, data->mPrio, + true /*asynchronous*/); + if (err != 0) { + ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d", + data->mPrio, data->mPid, data->mTid, err); + } + } break; + case CFG_EVENT_IO: { + IoConfigEventData *data = (IoConfigEventData *)event->mData.get(); + audioConfigChanged(data->mEvent, data->mParam); + } break; + case CFG_EVENT_SET_PARAMETER: { + SetParameterConfigEventData *data = (SetParameterConfigEventData *)event->mData.get(); + if (checkForNewParameter_l(data->mKeyValuePairs, event->mStatus)) { + configChanged = true; + } + } break; + case CFG_EVENT_CREATE_AUDIO_PATCH: { + CreateAudioPatchConfigEventData *data = + (CreateAudioPatchConfigEventData *)event->mData.get(); + event->mStatus = createAudioPatch_l(&data->mPatch, &data->mHandle); + } break; + case CFG_EVENT_RELEASE_AUDIO_PATCH: { + ReleaseAudioPatchConfigEventData *data = + (ReleaseAudioPatchConfigEventData *)event->mData.get(); + event->mStatus = releaseAudioPatch_l(data->mHandle); + } break; + default: + ALOG_ASSERT(false, "processConfigEvents_l() unknown event type %d", event->mType); + break; + } + { + Mutex::Autolock _l(event->mLock); + if (event->mWaitStatus) { + event->mWaitStatus = false; + event->mCond.signal(); + } + } + ALOGV_IF(mConfigEvents.isEmpty(), "processConfigEvents_l() DONE thread %p", this); + } + + if (configChanged) { + cacheParameters_l(); + } +} + +String8 channelMaskToString(audio_channel_mask_t mask, bool output) { + String8 s; + if (output) { + if (mask & AUDIO_CHANNEL_OUT_FRONT_LEFT) s.append("front-left, "); + if (mask & AUDIO_CHANNEL_OUT_FRONT_RIGHT) s.append("front-right, "); + if (mask & AUDIO_CHANNEL_OUT_FRONT_CENTER) s.append("front-center, "); + if (mask & AUDIO_CHANNEL_OUT_LOW_FREQUENCY) s.append("low freq, "); + if (mask & AUDIO_CHANNEL_OUT_BACK_LEFT) s.append("back-left, "); + if (mask & AUDIO_CHANNEL_OUT_BACK_RIGHT) s.append("back-right, "); + if (mask & AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER) s.append("front-left-of-center, "); + if (mask & AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER) s.append("front-right-of-center, "); + if (mask & AUDIO_CHANNEL_OUT_BACK_CENTER) s.append("back-center, "); + if (mask & AUDIO_CHANNEL_OUT_SIDE_LEFT) s.append("side-left, "); + if (mask & AUDIO_CHANNEL_OUT_SIDE_RIGHT) s.append("side-right, "); + if (mask & AUDIO_CHANNEL_OUT_TOP_CENTER) s.append("top-center ,"); + if (mask & AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT) s.append("top-front-left, "); + if (mask & AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER) s.append("top-front-center, "); + if (mask & AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT) s.append("top-front-right, "); + if (mask & AUDIO_CHANNEL_OUT_TOP_BACK_LEFT) s.append("top-back-left, "); + if (mask & AUDIO_CHANNEL_OUT_TOP_BACK_CENTER) s.append("top-back-center, " ); + if (mask & AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT) s.append("top-back-right, " ); + if (mask & ~AUDIO_CHANNEL_OUT_ALL) s.append("unknown, "); + } else { + if (mask & AUDIO_CHANNEL_IN_LEFT) s.append("left, "); + if (mask & AUDIO_CHANNEL_IN_RIGHT) s.append("right, "); + if (mask & AUDIO_CHANNEL_IN_FRONT) s.append("front, "); + if (mask & AUDIO_CHANNEL_IN_BACK) s.append("back, "); + if (mask & AUDIO_CHANNEL_IN_LEFT_PROCESSED) s.append("left-processed, "); + if (mask & AUDIO_CHANNEL_IN_RIGHT_PROCESSED) s.append("right-processed, "); + if (mask & AUDIO_CHANNEL_IN_FRONT_PROCESSED) s.append("front-processed, "); + if (mask & AUDIO_CHANNEL_IN_BACK_PROCESSED) s.append("back-processed, "); + if (mask & AUDIO_CHANNEL_IN_PRESSURE) s.append("pressure, "); + if (mask & AUDIO_CHANNEL_IN_X_AXIS) s.append("X, "); + if (mask & AUDIO_CHANNEL_IN_Y_AXIS) s.append("Y, "); + if (mask & AUDIO_CHANNEL_IN_Z_AXIS) s.append("Z, "); + if (mask & AUDIO_CHANNEL_IN_VOICE_UPLINK) s.append("voice-uplink, "); + if (mask & AUDIO_CHANNEL_IN_VOICE_DNLINK) s.append("voice-dnlink, "); + if (mask & ~AUDIO_CHANNEL_IN_ALL) s.append("unknown, "); + } + int len = s.length(); + if (s.length() > 2) { + char *str = s.lockBuffer(len); + s.unlockBuffer(len - 2); + } + return s; +} + +void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args __unused) { const size_t SIZE = 256; char buffer[SIZE]; @@ -413,47 +577,31 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args) bool locked = AudioFlinger::dumpTryLock(mLock); if (!locked) { - snprintf(buffer, SIZE, "thread %p maybe dead locked\n", this); - write(fd, buffer, strlen(buffer)); - } - - snprintf(buffer, SIZE, "io handle: %d\n", mId); - result.append(buffer); - snprintf(buffer, SIZE, "TID: %d\n", getTid()); - result.append(buffer); - snprintf(buffer, SIZE, "standby: %d\n", mStandby); - result.append(buffer); - snprintf(buffer, SIZE, "Sample rate: %u\n", mSampleRate); - result.append(buffer); - snprintf(buffer, SIZE, "HAL frame count: %zu\n", mFrameCount); - result.append(buffer); - snprintf(buffer, SIZE, "Channel Count: %u\n", mChannelCount); - result.append(buffer); - snprintf(buffer, SIZE, "Channel Mask: 0x%08x\n", mChannelMask); - result.append(buffer); - snprintf(buffer, SIZE, "Format: %d\n", mFormat); - result.append(buffer); - snprintf(buffer, SIZE, "Frame size: %zu\n", mFrameSize); - result.append(buffer); - - snprintf(buffer, SIZE, "\nPending setParameters commands: \n"); - result.append(buffer); - result.append(" Index Command"); - for (size_t i = 0; i < mNewParameters.size(); ++i) { - snprintf(buffer, SIZE, "\n %02zu ", i); - result.append(buffer); - result.append(mNewParameters[i]); - } - - snprintf(buffer, SIZE, "\n\nPending config events: \n"); - result.append(buffer); - for (size_t i = 0; i < mConfigEvents.size(); i++) { - mConfigEvents[i]->dump(buffer, SIZE); - result.append(buffer); + dprintf(fd, "thread %p maybe dead locked\n", this); + } + + dprintf(fd, " I/O handle: %d\n", mId); + dprintf(fd, " TID: %d\n", getTid()); + dprintf(fd, " Standby: %s\n", mStandby ? "yes" : "no"); + dprintf(fd, " Sample rate: %u\n", mSampleRate); + dprintf(fd, " HAL frame count: %zu\n", mFrameCount); + dprintf(fd, " HAL buffer size: %u bytes\n", mBufferSize); + dprintf(fd, " Channel Count: %u\n", mChannelCount); + dprintf(fd, " Channel Mask: 0x%08x (%s)\n", mChannelMask, + channelMaskToString(mChannelMask, mType != RECORD).string()); + dprintf(fd, " Format: 0x%x (%s)\n", mHALFormat, formatToString(mHALFormat)); + dprintf(fd, " Frame size: %zu\n", mFrameSize); + dprintf(fd, " Pending config events:"); + size_t numConfig = mConfigEvents.size(); + if (numConfig) { + for (size_t i = 0; i < numConfig; i++) { + mConfigEvents[i]->dump(buffer, SIZE); + dprintf(fd, "\n %s", buffer); + } + dprintf(fd, "\n"); + } else { + dprintf(fd, " none\n"); } - result.append("\n"); - - write(fd, result.string(), result.size()); if (locked) { mLock.unlock(); @@ -466,10 +614,11 @@ void AudioFlinger::ThreadBase::dumpEffectChains(int fd, const Vector<String16>& char buffer[SIZE]; String8 result; - snprintf(buffer, SIZE, "\n- %zu Effect Chains:\n", mEffectChains.size()); + size_t numEffectChains = mEffectChains.size(); + snprintf(buffer, SIZE, " %zu Effect Chains\n", numEffectChains); write(fd, buffer, strlen(buffer)); - for (size_t i = 0; i < mEffectChains.size(); ++i) { + for (size_t i = 0; i < numEffectChains; ++i) { sp<EffectChain> chain = mEffectChains[i]; if (chain != 0) { chain->dump(fd, args); @@ -513,12 +662,14 @@ void AudioFlinger::ThreadBase::acquireWakeLock_l(int uid) binder, getWakeLockTag(), String16("media"), - uid); + uid, + true /* FIXME force oneway contrary to .aidl */); } else { status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK, binder, getWakeLockTag(), - String16("media")); + String16("media"), + true /* FIXME force oneway contrary to .aidl */); } if (status == NO_ERROR) { mWakeLockToken = binder; @@ -538,7 +689,8 @@ void AudioFlinger::ThreadBase::releaseWakeLock_l() if (mWakeLockToken != 0) { ALOGV("releaseWakeLock_l() %s", mName); if (mPowerManager != 0) { - mPowerManager->releaseWakeLock(mWakeLockToken, 0); + mPowerManager->releaseWakeLock(mWakeLockToken, 0, + true /* FIXME force oneway contrary to .aidl */); } mWakeLockToken.clear(); } @@ -574,7 +726,8 @@ void AudioFlinger::ThreadBase::updateWakeLockUids_l(const SortedVector<int> &uid if (mPowerManager != 0) { sp<IBinder> binder = new BBinder(); status_t status; - status = mPowerManager->updateWakeLockUids(mWakeLockToken, uids.size(), uids.array()); + status = mPowerManager->updateWakeLockUids(mWakeLockToken, uids.size(), uids.array(), + true /* FIXME force oneway contrary to .aidl */); ALOGV("acquireWakeLock_l() %s status %d", mName, status); } } @@ -586,7 +739,7 @@ void AudioFlinger::ThreadBase::clearPowerManager() mPowerManager.clear(); } -void AudioFlinger::ThreadBase::PMDeathRecipient::binderDied(const wp<IBinder>& who) +void AudioFlinger::ThreadBase::PMDeathRecipient::binderDied(const wp<IBinder>& who __unused) { sp<ThreadBase> thread = mThread.promote(); if (thread != 0) { @@ -739,8 +892,7 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l( int sessionId, effect_descriptor_t *desc, int *enabled, - status_t *status - ) + status_t *status) { sp<EffectModule> effect; sp<EffectHandle> handle; @@ -756,6 +908,24 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l( goto Exit; } + // Reject any effect on Direct output threads for now, since the format of + // mSinkBuffer is not guaranteed to be compatible with effect processing (PCM 16 stereo). + if (mType == DIRECT) { + ALOGW("createEffect_l() Cannot add effect %s on Direct output type thread %s", + desc->name, mName); + lStatus = BAD_VALUE; + goto Exit; + } + + // Reject any effect on mixer or duplicating multichannel sinks. + // TODO: fix both format and multichannel issues with effects. + if ((mType == MIXER || mType == DUPLICATING) && mChannelCount != FCC_2) { + ALOGW("createEffect_l() Cannot add effect %s for multichannel(%d) %s threads", + desc->name, mChannelCount, mType == MIXER ? "MIXER" : "DUPLICATING"); + lStatus = BAD_VALUE; + goto Exit; + } + // Allow global effects only on offloaded and mixer threads if (sessionId == AUDIO_SESSION_OUTPUT_MIX) { switch (mType) { @@ -829,7 +999,10 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l( } // create effect handle and connect it to effect module handle = new EffectHandle(effect, client, effectClient, priority); - lStatus = effect->addHandle(handle.get()); + lStatus = handle->initCheck(); + if (lStatus == OK) { + lStatus = effect->addHandle(handle.get()); + } if (enabled != NULL) { *enabled = (int)effect->isEnabled(); } @@ -850,9 +1023,7 @@ Exit: handle.clear(); } - if (status != NULL) { - *status = lStatus; - } + *status = lStatus; return handle; } @@ -991,6 +1162,18 @@ void AudioFlinger::ThreadBase::disconnectEffect(const sp<EffectModule>& effect, } } +void AudioFlinger::ThreadBase::getAudioPortConfig(struct audio_port_config *config) +{ + config->type = AUDIO_PORT_TYPE_MIX; + config->ext.mix.handle = mId; + config->sample_rate = mSampleRate; + config->format = mFormat; + config->channel_mask = mChannelMask; + config->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK| + AUDIO_PORT_CONFIG_FORMAT; +} + + // ---------------------------------------------------------------------------- // Playback // ---------------------------------------------------------------------------- @@ -1001,8 +1184,18 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge audio_devices_t device, type_t type) : ThreadBase(audioFlinger, id, device, AUDIO_DEVICE_NONE, type), - mNormalFrameCount(0), mMixBuffer(NULL), - mAllocMixBuffer(NULL), mSuspended(0), mBytesWritten(0), + mNormalFrameCount(0), mSinkBuffer(NULL), + mMixerBufferEnabled(AudioFlinger::kEnableExtendedPrecision), + mMixerBuffer(NULL), + mMixerBufferSize(0), + mMixerBufferFormat(AUDIO_FORMAT_INVALID), + mMixerBufferValid(false), + mEffectBufferEnabled(AudioFlinger::kEnableExtendedPrecision), + mEffectBuffer(NULL), + mEffectBufferSize(0), + mEffectBufferFormat(AUDIO_FORMAT_INVALID), + mEffectBufferValid(false), + mSuspended(0), mBytesWritten(0), mActiveTracksGeneration(0), // mStreamTypes[] initialized in constructor body mOutput(output), @@ -1044,11 +1237,11 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge } } - readOutputParameters(); + readOutputParameters_l(); // mStreamTypes[AUDIO_STREAM_CNT] is initialized by stream_type_t default constructor // There is no AUDIO_STREAM_MIN, and ++ operator does not compile - for (audio_stream_type_t stream = (audio_stream_type_t) 0; stream < AUDIO_STREAM_CNT; + for (audio_stream_type_t stream = AUDIO_STREAM_MIN; stream < AUDIO_STREAM_CNT; stream = (audio_stream_type_t) (stream + 1)) { mStreamTypes[stream].volume = mAudioFlinger->streamVolume_l(stream); mStreamTypes[stream].mute = mAudioFlinger->streamMute_l(stream); @@ -1060,7 +1253,9 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge AudioFlinger::PlaybackThread::~PlaybackThread() { mAudioFlinger->unregisterWriter(mNBLogWriter); - delete [] mAllocMixBuffer; + free(mSinkBuffer); + free(mMixerBuffer); + free(mEffectBuffer); } void AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args) @@ -1070,13 +1265,13 @@ void AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args) dumpEffectChains(fd, args); } -void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& args) +void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& args __unused) { const size_t SIZE = 256; char buffer[SIZE]; String8 result; - result.appendFormat("Output thread %p stream volumes in dB:\n ", this); + result.appendFormat(" Stream volumes in dB: "); for (int i = 0; i < AUDIO_STREAM_CNT; ++i) { const stream_type_t *st = &mStreamTypes[i]; if (i > 0) { @@ -1091,75 +1286,68 @@ void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& ar write(fd, result.string(), result.length()); result.clear(); - snprintf(buffer, SIZE, "Output thread %p tracks\n", this); - result.append(buffer); - Track::appendDumpHeader(result); - for (size_t i = 0; i < mTracks.size(); ++i) { - sp<Track> track = mTracks[i]; - if (track != 0) { - track->dump(buffer, SIZE); - result.append(buffer); + // These values are "raw"; they will wrap around. See prepareTracks_l() for a better way. + FastTrackUnderruns underruns = getFastTrackUnderruns(0); + dprintf(fd, " Normal mixer raw underrun counters: partial=%u empty=%u\n", + underruns.mBitFields.mPartial, underruns.mBitFields.mEmpty); + + size_t numtracks = mTracks.size(); + size_t numactive = mActiveTracks.size(); + dprintf(fd, " %d Tracks", numtracks); + size_t numactiveseen = 0; + if (numtracks) { + dprintf(fd, " of which %d are active\n", numactive); + Track::appendDumpHeader(result); + for (size_t i = 0; i < numtracks; ++i) { + sp<Track> track = mTracks[i]; + if (track != 0) { + bool active = mActiveTracks.indexOf(track) >= 0; + if (active) { + numactiveseen++; + } + track->dump(buffer, SIZE, active); + result.append(buffer); + } } + } else { + result.append("\n"); } - - snprintf(buffer, SIZE, "Output thread %p active tracks\n", this); - result.append(buffer); - Track::appendDumpHeader(result); - for (size_t i = 0; i < mActiveTracks.size(); ++i) { - sp<Track> track = mActiveTracks[i].promote(); - if (track != 0) { - track->dump(buffer, SIZE); - result.append(buffer); + if (numactiveseen != numactive) { + // some tracks in the active list were not in the tracks list + snprintf(buffer, SIZE, " The following tracks are in the active list but" + " not in the track list\n"); + result.append(buffer); + Track::appendDumpHeader(result); + for (size_t i = 0; i < numactive; ++i) { + sp<Track> track = mActiveTracks[i].promote(); + if (track != 0 && mTracks.indexOf(track) < 0) { + track->dump(buffer, SIZE, true); + result.append(buffer); + } } } - write(fd, result.string(), result.size()); - // These values are "raw"; they will wrap around. See prepareTracks_l() for a better way. - FastTrackUnderruns underruns = getFastTrackUnderruns(0); - dprintf(fd, "Normal mixer raw underrun counters: partial=%u empty=%u\n", - underruns.mBitFields.mPartial, underruns.mBitFields.mEmpty); + write(fd, result.string(), result.size()); } void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args) { - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - - snprintf(buffer, SIZE, "\nOutput thread %p internals\n", this); - result.append(buffer); - snprintf(buffer, SIZE, "Normal frame count: %zu\n", mNormalFrameCount); - result.append(buffer); - snprintf(buffer, SIZE, "last write occurred (msecs): %llu\n", - ns2ms(systemTime() - mLastWriteTime)); - result.append(buffer); - snprintf(buffer, SIZE, "total writes: %d\n", mNumWrites); - result.append(buffer); - snprintf(buffer, SIZE, "delayed writes: %d\n", mNumDelayedWrites); - result.append(buffer); - snprintf(buffer, SIZE, "blocked in write: %d\n", mInWrite); - result.append(buffer); - snprintf(buffer, SIZE, "suspend count: %d\n", mSuspended); - result.append(buffer); - snprintf(buffer, SIZE, "mix buffer : %p\n", mMixBuffer); - result.append(buffer); - write(fd, result.string(), result.size()); - dprintf(fd, "Fast track availMask=%#x\n", mFastTrackAvailMask); + dprintf(fd, "\nOutput thread %p:\n", this); + dprintf(fd, " Normal frame count: %zu\n", mNormalFrameCount); + dprintf(fd, " Last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime)); + dprintf(fd, " Total writes: %d\n", mNumWrites); + dprintf(fd, " Delayed writes: %d\n", mNumDelayedWrites); + dprintf(fd, " Blocked in write: %s\n", mInWrite ? "yes" : "no"); + dprintf(fd, " Suspend count: %d\n", mSuspended); + dprintf(fd, " Sink buffer : %p\n", mSinkBuffer); + dprintf(fd, " Mixer buffer: %p\n", mMixerBuffer); + dprintf(fd, " Effect buffer: %p\n", mEffectBuffer); + dprintf(fd, " Fast track availMask=%#x\n", mFastTrackAvailMask); dumpBase(fd, args); } // Thread virtuals -status_t AudioFlinger::PlaybackThread::readyToRun() -{ - status_t status = initCheck(); - if (status == NO_ERROR) { - ALOGI("AudioFlinger's thread %p ready to run", this); - } else { - ALOGE("No working audio driver found."); - } - return status; -} void AudioFlinger::PlaybackThread::onFirstRef() { @@ -1182,7 +1370,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, - size_t frameCount, + size_t *pFrameCount, const sp<IMemory>& sharedBuffer, int sessionId, IAudioFlinger::track_flags_t *flags, @@ -1190,6 +1378,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac int uid, status_t *status) { + size_t frameCount = *pFrameCount; sp<Track> track; status_t lStatus; @@ -1215,9 +1404,10 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac ) && // PCM data audio_is_linear_pcm(format) && - // mono or stereo - ( (channelMask == AUDIO_CHANNEL_OUT_MONO) || - (channelMask == AUDIO_CHANNEL_OUT_STEREO) ) && + // identical channel mask to sink, or mono in and stereo sink + (channelMask == mChannelMask || + (channelMask == AUDIO_CHANNEL_OUT_MONO && + mChannelMask == AUDIO_CHANNEL_OUT_STEREO)) && // hardware sample rate (sampleRate == mSampleRate) && // normal mixer has an associated fast mixer @@ -1229,15 +1419,21 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac ) { // if frameCount not specified, then it defaults to fast mixer (HAL) frame count if (frameCount == 0) { - frameCount = mFrameCount * kFastTrackMultiplier; + // read the fast track multiplier property the first time it is needed + int ok = pthread_once(&sFastTrackMultiplierOnce, sFastTrackMultiplierInit); + if (ok != 0) { + ALOGE("%s pthread_once failed: %d", __func__, ok); + } + frameCount = mFrameCount * sFastTrackMultiplier; } ALOGV("AUDIO_OUTPUT_FLAG_FAST accepted: frameCount=%d mFrameCount=%d", frameCount, mFrameCount); } else { ALOGV("AUDIO_OUTPUT_FLAG_FAST denied: isTimed=%d sharedBuffer=%p frameCount=%d " - "mFrameCount=%d format=%d isLinear=%d channelMask=%#x sampleRate=%u mSampleRate=%u " + "mFrameCount=%d format=%#x mFormat=%#x isLinear=%d channelMask=%#x " + "sampleRate=%u mSampleRate=%u " "hasFastMixer=%d tid=%d fastTrackAvailMask=%#x", - isTimed, sharedBuffer.get(), frameCount, mFrameCount, format, + isTimed, sharedBuffer.get(), frameCount, mFrameCount, format, mFormat, audio_is_linear_pcm(format), channelMask, sampleRate, mSampleRate, hasFastMixer(), tid, mFastTrackAvailMask); *flags &= ~IAudioFlinger::TRACK_FAST; @@ -1256,44 +1452,52 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac } } } + *pFrameCount = frameCount; - if (mType == DIRECT) { - if ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM) { + switch (mType) { + + case DIRECT: + if (audio_is_linear_pcm(format)) { if (sampleRate != mSampleRate || format != mFormat || channelMask != mChannelMask) { - ALOGE("createTrack_l() Bad parameter: sampleRate %u format %d, channelMask 0x%08x " - "for output %p with format %d", + ALOGE("createTrack_l() Bad parameter: sampleRate %u format %#x, channelMask 0x%08x " + "for output %p with format %#x", sampleRate, format, channelMask, mOutput, mFormat); lStatus = BAD_VALUE; goto Exit; } } - } else if (mType == OFFLOAD) { + break; + + case OFFLOAD: if (sampleRate != mSampleRate || format != mFormat || channelMask != mChannelMask) { - ALOGE("createTrack_l() Bad parameter: sampleRate %d format %d, channelMask 0x%08x \"" - "for output %p with format %d", + ALOGE("createTrack_l() Bad parameter: sampleRate %d format %#x, channelMask 0x%08x \"" + "for output %p with format %#x", sampleRate, format, channelMask, mOutput, mFormat); lStatus = BAD_VALUE; goto Exit; } - } else { - if ((format & AUDIO_FORMAT_MAIN_MASK) != AUDIO_FORMAT_PCM) { - ALOGE("createTrack_l() Bad parameter: format %d \"" - "for output %p with format %d", + break; + + default: + if (!audio_is_linear_pcm(format)) { + ALOGE("createTrack_l() Bad parameter: format %#x \"" + "for output %p with format %#x", format, mOutput, mFormat); lStatus = BAD_VALUE; goto Exit; } - // Resampler implementation limits input sampling rate to 2 x output sampling rate. - if (sampleRate > mSampleRate*2) { + if (sampleRate > mSampleRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX) { ALOGE("Sample rate out of range: %u mSampleRate %u", sampleRate, mSampleRate); lStatus = BAD_VALUE; goto Exit; } + break; + } lStatus = initCheck(); if (lStatus != NO_ERROR) { - ALOGE("Audio driver not initialized."); + ALOGE("createTrack_l() audio driver not initialized"); goto Exit; } @@ -1306,7 +1510,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac uint32_t strategy = AudioSystem::getStrategyForStream(streamType); for (size_t i = 0; i < mTracks.size(); ++i) { sp<Track> t = mTracks[i]; - if (t != 0 && !t->isOutputTrack()) { + if (t != 0 && t->isExternalTrack()) { uint32_t actual = AudioSystem::getStrategyForStream(t->streamType()); if (sessionId == t->sessionId() && strategy != actual) { ALOGE("createTrack_l() mismatched strategy; expected %u but found %u", @@ -1319,18 +1523,21 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac if (!isTimed) { track = new Track(this, client, streamType, sampleRate, format, - channelMask, frameCount, sharedBuffer, sessionId, uid, *flags); + channelMask, frameCount, NULL, sharedBuffer, + sessionId, uid, *flags, TrackBase::TYPE_DEFAULT); } else { track = TimedTrack::create(this, client, streamType, sampleRate, format, channelMask, frameCount, sharedBuffer, sessionId, uid); } - if (track == 0 || track->getCblk() == NULL || track->name() < 0) { - lStatus = NO_MEMORY; + // new Track always returns non-NULL, + // but TimedTrack::create() is a factory that could fail by returning NULL + lStatus = track != 0 ? track->initCheck() : (status_t) NO_MEMORY; + if (lStatus != NO_ERROR) { + ALOGE("createTrack_l() initCheck failed %d; no control block?", lStatus); // track must be cleared from the caller as the caller has the AF lock goto Exit; } - mTracks.add(track); sp<EffectChain> chain = getEffectChain_l(sessionId); @@ -1352,9 +1559,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac lStatus = NO_ERROR; Exit: - if (status) { - *status = lStatus; - } + *status = lStatus; return track; } @@ -1432,7 +1637,7 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track) // the track is newly added, make sure it fills up all its // buffers before playing. This is to ensure the client will // effectively get the latency it requested. - if (!track->isOutputTrack()) { + if (track->isExternalTrack()) { TrackBase::track_state state = track->mState; mLock.unlock(); status = AudioSystem::startOutput(mId, track->streamType(), track->sessionId()); @@ -1473,9 +1678,7 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track) status = NO_ERROR; } - ALOGV("signal playback thread"); - broadcast_l(); - + onAddNewTrack_l(); return status; } @@ -1487,7 +1690,7 @@ bool AudioFlinger::PlaybackThread::destroyTrack_l(const sp<Track>& track) track->mState = TrackBase::STOPPED; if (!trackActive) { removeTrack_l(track); - } else if (track->isFastTrack() || track->isOffloaded()) { + } else if (track->isFastTrack() || track->isOffloaded() || track->isDirect()) { track->mState = TrackBase::STOPPING_1; } @@ -1538,12 +1741,11 @@ String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys) return out_s8; } -// audioConfigChanged_l() must be called with AudioFlinger::mLock held -void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) { +void AudioFlinger::PlaybackThread::audioConfigChanged(int event, int param) { AudioSystem::OutputDescriptor desc; void *param2 = NULL; - ALOGV("PlaybackThread::audioConfigChanged_l, thread %p, event %d, param %d", this, event, + ALOGV("PlaybackThread::audioConfigChanged, thread %p, event %d, param %d", this, event, param); switch (event) { @@ -1554,7 +1756,7 @@ void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) { desc.format = mFormat; desc.frameCount = mNormalFrameCount; // FIXME see // AudioFlinger::frameCount(audio_io_handle_t) - desc.latency = latency(); + desc.latency = latency_l(); param2 = &desc; break; @@ -1564,7 +1766,7 @@ void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) { default: break; } - mAudioFlinger->audioConfigChanged_l(event, mId, param2); + mAudioFlinger->audioConfigChanged(event, mId, param2); } void AudioFlinger::PlaybackThread::writeCallback() @@ -1601,7 +1803,7 @@ void AudioFlinger::PlaybackThread::resetDraining(uint32_t sequence) // static int AudioFlinger::PlaybackThread::asyncCallback(stream_callback_event_t event, - void *param, + void *param __unused, void *cookie) { AudioFlinger::PlaybackThread *me = (AudioFlinger::PlaybackThread *)cookie; @@ -1620,29 +1822,33 @@ int AudioFlinger::PlaybackThread::asyncCallback(stream_callback_event_t event, return 0; } -void AudioFlinger::PlaybackThread::readOutputParameters() +void AudioFlinger::PlaybackThread::readOutputParameters_l() { - // unfortunately we have no way of recovering from errors here, hence the LOG_FATAL + // unfortunately we have no way of recovering from errors here, hence the LOG_ALWAYS_FATAL mSampleRate = mOutput->stream->common.get_sample_rate(&mOutput->stream->common); mChannelMask = mOutput->stream->common.get_channels(&mOutput->stream->common); if (!audio_is_output_channel(mChannelMask)) { - LOG_FATAL("HAL channel mask %#x not valid for output", mChannelMask); + LOG_ALWAYS_FATAL("HAL channel mask %#x not valid for output", mChannelMask); } - if ((mType == MIXER || mType == DUPLICATING) && mChannelMask != AUDIO_CHANNEL_OUT_STEREO) { - LOG_FATAL("HAL channel mask %#x not supported for mixed output; " - "must be AUDIO_CHANNEL_OUT_STEREO", mChannelMask); + if ((mType == MIXER || mType == DUPLICATING) + && !isValidPcmSinkChannelMask(mChannelMask)) { + LOG_ALWAYS_FATAL("HAL channel mask %#x not supported for mixed output", + mChannelMask); } - mChannelCount = popcount(mChannelMask); - mFormat = mOutput->stream->common.get_format(&mOutput->stream->common); + mChannelCount = audio_channel_count_from_out_mask(mChannelMask); + mHALFormat = mOutput->stream->common.get_format(&mOutput->stream->common); + mFormat = mHALFormat; if (!audio_is_valid_format(mFormat)) { - LOG_FATAL("HAL format %d not valid for output", mFormat); + LOG_ALWAYS_FATAL("HAL format %#x not valid for output", mFormat); } - if ((mType == MIXER || mType == DUPLICATING) && mFormat != AUDIO_FORMAT_PCM_16_BIT) { - LOG_FATAL("HAL format %d not supported for mixed output; must be AUDIO_FORMAT_PCM_16_BIT", + if ((mType == MIXER || mType == DUPLICATING) + && !isValidPcmSinkFormat(mFormat)) { + LOG_FATAL("HAL format %#x not supported for mixed output", mFormat); } - mFrameSize = audio_stream_frame_size(&mOutput->stream->common); - mFrameCount = mOutput->stream->common.get_buffer_size(&mOutput->stream->common) / mFrameSize; + mFrameSize = audio_stream_out_frame_size(mOutput->stream); + mBufferSize = mOutput->stream->common.get_buffer_size(&mOutput->stream->common); + mFrameCount = mBufferSize / mFrameSize; if (mFrameCount & 15) { ALOGW("HAL output buffer size is %u frames but AudioMixer requires multiples of 16 frames", mFrameCount); @@ -1657,12 +1863,12 @@ void AudioFlinger::PlaybackThread::readOutputParameters() } } - // Calculate size of normal mix buffer relative to the HAL output buffer size + // Calculate size of normal sink buffer relative to the HAL output buffer size double multiplier = 1.0; if (mType == MIXER && (kUseFastMixer == FastMixer_Static || kUseFastMixer == FastMixer_Dynamic)) { - size_t minNormalFrameCount = (kMinNormalMixBufferSizeMs * mSampleRate) / 1000; - size_t maxNormalFrameCount = (kMaxNormalMixBufferSizeMs * mSampleRate) / 1000; + size_t minNormalFrameCount = (kMinNormalSinkBufferSizeMs * mSampleRate) / 1000; + size_t maxNormalFrameCount = (kMaxNormalSinkBufferSizeMs * mSampleRate) / 1000; // round up minimum and round down maximum to nearest 16 frames to satisfy AudioMixer minNormalFrameCount = (minNormalFrameCount + 15) & ~15; maxNormalFrameCount = maxNormalFrameCount & ~15; @@ -1680,7 +1886,7 @@ void AudioFlinger::PlaybackThread::readOutputParameters() } } else { // prefer an even multiplier, for compatibility with doubling of fast tracks due to HAL - // SRC (it would be unusual for the normal mix buffer size to not be a multiple of fast + // SRC (it would be unusual for the normal sink buffer size to not be a multiple of fast // track, but we sometimes have to do this to satisfy the maximum frame count // constraint) // FIXME this rounding up should not be done if no HAL SRC @@ -1695,19 +1901,43 @@ void AudioFlinger::PlaybackThread::readOutputParameters() } mNormalFrameCount = multiplier * mFrameCount; // round up to nearest 16 frames to satisfy AudioMixer - mNormalFrameCount = (mNormalFrameCount + 15) & ~15; - ALOGI("HAL output buffer size %u frames, normal mix buffer size %u frames", mFrameCount, + if (mType == MIXER || mType == DUPLICATING) { + mNormalFrameCount = (mNormalFrameCount + 15) & ~15; + } + ALOGI("HAL output buffer size %u frames, normal sink buffer size %u frames", mFrameCount, mNormalFrameCount); - delete[] mAllocMixBuffer; - size_t align = (mFrameSize < sizeof(int16_t)) ? sizeof(int16_t) : mFrameSize; - mAllocMixBuffer = new int8_t[mNormalFrameCount * mFrameSize + align - 1]; - mMixBuffer = (int16_t *) ((((size_t)mAllocMixBuffer + align - 1) / align) * align); - memset(mMixBuffer, 0, mNormalFrameCount * mFrameSize); + // mSinkBuffer is the sink buffer. Size is always multiple-of-16 frames. + // Originally this was int16_t[] array, need to remove legacy implications. + free(mSinkBuffer); + mSinkBuffer = NULL; + // For sink buffer size, we use the frame size from the downstream sink to avoid problems + // with non PCM formats for compressed music, e.g. AAC, and Offload threads. + const size_t sinkBufferSize = mNormalFrameCount * mFrameSize; + (void)posix_memalign(&mSinkBuffer, 32, sinkBufferSize); + + // We resize the mMixerBuffer according to the requirements of the sink buffer which + // drives the output. + free(mMixerBuffer); + mMixerBuffer = NULL; + if (mMixerBufferEnabled) { + mMixerBufferFormat = AUDIO_FORMAT_PCM_FLOAT; // also valid: AUDIO_FORMAT_PCM_16_BIT. + mMixerBufferSize = mNormalFrameCount * mChannelCount + * audio_bytes_per_sample(mMixerBufferFormat); + (void)posix_memalign(&mMixerBuffer, 32, mMixerBufferSize); + } + free(mEffectBuffer); + mEffectBuffer = NULL; + if (mEffectBufferEnabled) { + mEffectBufferFormat = AUDIO_FORMAT_PCM_16_BIT; // Note: Effects support 16b only + mEffectBufferSize = mNormalFrameCount * mChannelCount + * audio_bytes_per_sample(mEffectBufferFormat); + (void)posix_memalign(&mEffectBuffer, 32, mEffectBufferSize); + } // force reconfiguration of effect chains and engines to take new buffer size and audio // parameters into account - // Note that mLock is not held when readOutputParameters() is called from the constructor + // Note that mLock is not held when readOutputParameters_l() is called from the constructor // but in this case nothing is done below as no audio sessions have effect yet so it doesn't // matter. // create a copy of mEffectChains as calling moveEffectChain_l() can reorder some effect chains @@ -1841,10 +2071,10 @@ void AudioFlinger::PlaybackThread::threadLoop_removeTracks( const Vector< sp<Track> >& tracksToRemove) { size_t count = tracksToRemove.size(); - if (count) { + if (count > 0) { for (size_t i = 0 ; i < count ; i++) { const sp<Track>& track = tracksToRemove.itemAt(i); - if (!track->isOutputTrack()) { + if (track->isExternalTrack()) { AudioSystem::stopOutput(mId, track->streamType(), track->sessionId()); #ifdef ADD_BATTERY_DATA // to track the speaker usage @@ -1882,12 +2112,12 @@ ssize_t AudioFlinger::PlaybackThread::threadLoop_write() mLastWriteTime = systemTime(); mInWrite = true; ssize_t bytesWritten; + const size_t offset = mCurrentWriteLength - mBytesRemaining; // If an NBAIO sink is present, use it to write the normal mixer's submix if (mNormalSink != 0) { -#define mBitShift 2 // FIXME - size_t count = mBytesRemaining >> mBitShift; - size_t offset = (mCurrentWriteLength - mBytesRemaining) >> 1; + const size_t count = mBytesRemaining / mFrameSize; + ATRACE_BEGIN("write"); // update the setpoint when AudioFlinger::mScreenState changes uint32_t screenState = AudioFlinger::mScreenState; @@ -1899,10 +2129,10 @@ ssize_t AudioFlinger::PlaybackThread::threadLoop_write() (pipe->maxFrames() * 7) / 8 : mNormalFrameCount * 2); } } - ssize_t framesWritten = mNormalSink->write(mMixBuffer + offset, count); + ssize_t framesWritten = mNormalSink->write((char *)mSinkBuffer + offset, count); ATRACE_END(); if (framesWritten > 0) { - bytesWritten = framesWritten << mBitShift; + bytesWritten = framesWritten * mFrameSize; } else { bytesWritten = framesWritten; } @@ -1917,7 +2147,7 @@ ssize_t AudioFlinger::PlaybackThread::threadLoop_write() // otherwise use the HAL / AudioStreamOut directly } else { // Direct output and offload threads - size_t offset = (mCurrentWriteLength - mBytesRemaining); + if (mUseAsyncWrite) { ALOGW_IF(mWriteAckSequence & 1, "threadLoop_write(): out of sequence write request"); mWriteAckSequence += 2; @@ -1928,7 +2158,7 @@ ssize_t AudioFlinger::PlaybackThread::threadLoop_write() // FIXME We should have an implementation of timestamps for direct output threads. // They are used e.g for multichannel PCM playback over HDMI. bytesWritten = mOutput->stream->write(mOutput->stream, - (char *)mMixBuffer + offset, mBytesRemaining); + (char *)mSinkBuffer + offset, mBytesRemaining); if (mUseAsyncWrite && ((bytesWritten < 0) || (bytesWritten == (ssize_t)mBytesRemaining))) { // do not wait for async callback in case of error of full write @@ -1967,7 +2197,7 @@ void AudioFlinger::PlaybackThread::threadLoop_exit() /* The derived values that are cached: - - mixBufferSize from frame count * frame size + - mSinkBufferSize from frame count * frame size - activeSleepTime from activeSleepTimeUs() - idleSleepTime from idleSleepTimeUs() - standbyDelay from mActiveSleepTimeUs (DIRECT only) @@ -1986,7 +2216,7 @@ The parameters that affect these derived values are: void AudioFlinger::PlaybackThread::cacheParameters_l() { - mixBufferSize = mNormalFrameCount * mFrameSize; + mSinkBufferSize = mNormalFrameCount * mFrameSize; activeSleepTime = activeSleepTimeUs(); idleSleepTime = idleSleepTimeUs(); } @@ -2009,13 +2239,14 @@ void AudioFlinger::PlaybackThread::invalidateTracks(audio_stream_type_t streamTy status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& chain) { int session = chain->sessionId(); - int16_t *buffer = mMixBuffer; + int16_t* buffer = reinterpret_cast<int16_t*>(mEffectBufferEnabled + ? mEffectBuffer : mSinkBuffer); bool ownsBuffer = false; ALOGV("addEffectChain_l() %p on thread %p for session %d", chain.get(), this, session); if (session > 0) { // Only one effect chain can be present in direct output thread and it uses - // the mix buffer as input + // the sink buffer as input if (mType != DIRECT) { size_t numSamples = mNormalFrameCount * mChannelCount; buffer = new int16_t[numSamples]; @@ -2049,7 +2280,8 @@ status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& c } chain->setInBuffer(buffer, ownsBuffer); - chain->setOutBuffer(mMixBuffer); + chain->setOutBuffer(reinterpret_cast<int16_t*>(mEffectBufferEnabled + ? mEffectBuffer : mSinkBuffer)); // Effect chain for session AUDIO_SESSION_OUTPUT_STAGE is inserted at end of effect // chains list in order to be processed last as it contains output stage effects // Effect chain for session AUDIO_SESSION_OUTPUT_MIX is inserted before @@ -2099,7 +2331,7 @@ size_t AudioFlinger::PlaybackThread::removeEffectChain_l(const sp<EffectChain>& for (size_t i = 0; i < mTracks.size(); ++i) { sp<Track> track = mTracks[i]; if (session == track->sessionId()) { - track->setMainBuffer(mMixBuffer); + track->setMainBuffer(reinterpret_cast<int16_t*>(mSinkBuffer)); chain->decTrackCnt(); } } @@ -2189,12 +2421,12 @@ bool AudioFlinger::PlaybackThread::threadLoop() Vector< sp<EffectChain> > effectChains; - processConfigEvents(); - { // scope for mLock Mutex::Autolock _l(mLock); + processConfigEvents_l(); + if (logString != NULL) { mNBLogWriter->logTimestamp(); mNBLogWriter->log(logString); @@ -2207,10 +2439,6 @@ bool AudioFlinger::PlaybackThread::threadLoop() mLatchQValid = true; } - if (checkForNewParameters_l()) { - cacheParameters_l(); - } - saveOutputTracks(); if (mSignalPending) { // A signal was raised while we were unlocked @@ -2302,14 +2530,32 @@ bool AudioFlinger::PlaybackThread::threadLoop() // must be written to HAL threadLoop_sleepTime(); if (sleepTime == 0) { - mCurrentWriteLength = mixBufferSize; + mCurrentWriteLength = mSinkBufferSize; } } + // Either threadLoop_mix() or threadLoop_sleepTime() should have set + // mMixerBuffer with data if mMixerBufferValid is true and sleepTime == 0. + // Merge mMixerBuffer data into mEffectBuffer (if any effects are valid) + // or mSinkBuffer (if there are no effects). + // + // This is done pre-effects computation; if effects change to + // support higher precision, this needs to move. + // + // mMixerBufferValid is only set true by MixerThread::prepareTracks_l(). + // TODO use sleepTime == 0 as an additional condition. + if (mMixerBufferValid) { + void *buffer = mEffectBufferValid ? mEffectBuffer : mSinkBuffer; + audio_format_t format = mEffectBufferValid ? mEffectBufferFormat : mFormat; + + memcpy_by_audio_format(buffer, format, mMixerBuffer, mMixerBufferFormat, + mNormalFrameCount * mChannelCount); + } + mBytesRemaining = mCurrentWriteLength; if (isSuspended()) { sleepTime = suspendSleepTimeUs(); // simulate write to HAL when suspended - mBytesWritten += mixBufferSize; + mBytesWritten += mSinkBufferSize; mBytesRemaining = 0; } @@ -2330,6 +2576,16 @@ bool AudioFlinger::PlaybackThread::threadLoop() } } + // Only if the Effects buffer is enabled and there is data in the + // Effects buffer (buffer valid), we need to + // copy into the sink buffer. + // TODO use sleepTime == 0 as an additional condition. + if (mEffectBufferValid) { + //ALOGV("writing effect buffer to sink buffer format %#x", mFormat); + memcpy_by_audio_format(mSinkBuffer, mFormat, mEffectBuffer, mEffectBufferFormat, + mNormalFrameCount * mChannelCount); + } + // enable changes in effect chain unlockEffectChains(effectChains); @@ -2348,20 +2604,20 @@ bool AudioFlinger::PlaybackThread::threadLoop() (mMixerStatus == MIXER_DRAIN_ALL)) { threadLoop_drain(); } -if (mType == MIXER) { - // write blocked detection - nsecs_t now = systemTime(); - nsecs_t delta = now - mLastWriteTime; - if (!mStandby && delta > maxPeriod) { - mNumDelayedWrites++; - if ((now - lastWarning) > kWarningThrottleNs) { - ATRACE_NAME("underrun"); - ALOGW("write blocked for %llu msecs, %d delayed writes, thread %p", - ns2ms(delta), mNumDelayedWrites, this); - lastWarning = now; + if (mType == MIXER) { + // write blocked detection + nsecs_t now = systemTime(); + nsecs_t delta = now - mLastWriteTime; + if (!mStandby && delta > maxPeriod) { + mNumDelayedWrites++; + if ((now - lastWarning) > kWarningThrottleNs) { + ATRACE_NAME("underrun"); + ALOGW("write blocked for %llu msecs, %d delayed writes, thread %p", + ns2ms(delta), mNumDelayedWrites, this); + lastWarning = now; + } } } -} } else { usleep(sleepTime); @@ -2389,12 +2645,9 @@ if (mType == MIXER) { threadLoop_exit(); - // for DuplicatingThread, standby mode is handled by the outputTracks, otherwise ... - if (mType == MIXER || mType == DIRECT || mType == OFFLOAD) { - // put output stream into standby mode - if (!mStandby) { - mOutput->stream->common.standby(&mOutput->stream->common); - } + if (!mStandby) { + threadLoop_standby(); + mStandby = true; } releaseWakeLock(); @@ -2409,7 +2662,7 @@ if (mType == MIXER) { void AudioFlinger::PlaybackThread::removeTracks_l(const Vector< sp<Track> >& tracksToRemove) { size_t count = tracksToRemove.size(); - if (count) { + if (count > 0) { for (size_t i=0 ; i<count ; i++) { const sp<Track>& track = tracksToRemove.itemAt(i); mActiveTracks.remove(track); @@ -2435,7 +2688,7 @@ status_t AudioFlinger::PlaybackThread::getTimestamp_l(AudioTimestamp& timestamp) if (mNormalSink != 0) { return mNormalSink->getTimestamp(timestamp); } - if (mType == OFFLOAD && mOutput->stream->get_presentation_position) { + if ((mType == OFFLOAD || mType == DIRECT) && mOutput->stream->get_presentation_position) { uint64_t position64; int ret = mOutput->stream->get_presentation_position( mOutput->stream, &position64, ×tamp.mTime); @@ -2446,6 +2699,67 @@ status_t AudioFlinger::PlaybackThread::getTimestamp_l(AudioTimestamp& timestamp) } return INVALID_OPERATION; } + +status_t AudioFlinger::PlaybackThread::createAudioPatch_l(const struct audio_patch *patch, + audio_patch_handle_t *handle) +{ + status_t status = NO_ERROR; + if (mOutput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) { + // store new device and send to effects + audio_devices_t type = AUDIO_DEVICE_NONE; + for (unsigned int i = 0; i < patch->num_sinks; i++) { + type |= patch->sinks[i].ext.device.type; + } + mOutDevice = type; + for (size_t i = 0; i < mEffectChains.size(); i++) { + mEffectChains[i]->setDevice_l(mOutDevice); + } + + audio_hw_device_t *hwDevice = mOutput->audioHwDev->hwDevice(); + status = hwDevice->create_audio_patch(hwDevice, + patch->num_sources, + patch->sources, + patch->num_sinks, + patch->sinks, + handle); + } else { + ALOG_ASSERT(false, "createAudioPatch_l() called on a pre 3.0 HAL"); + } + return status; +} + +status_t AudioFlinger::PlaybackThread::releaseAudioPatch_l(const audio_patch_handle_t handle) +{ + status_t status = NO_ERROR; + if (mOutput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) { + audio_hw_device_t *hwDevice = mOutput->audioHwDev->hwDevice(); + status = hwDevice->release_audio_patch(hwDevice, handle); + } else { + ALOG_ASSERT(false, "releaseAudioPatch_l() called on a pre 3.0 HAL"); + } + return status; +} + +void AudioFlinger::PlaybackThread::addPatchTrack(const sp<PatchTrack>& track) +{ + Mutex::Autolock _l(mLock); + mTracks.add(track); +} + +void AudioFlinger::PlaybackThread::deletePatchTrack(const sp<PatchTrack>& track) +{ + Mutex::Autolock _l(mLock); + destroyTrack_l(track); +} + +void AudioFlinger::PlaybackThread::getAudioPortConfig(struct audio_port_config *config) +{ + ThreadBase::getAudioPortConfig(config); + config->role = AUDIO_PORT_ROLE_SOURCE; + config->ext.mix.hw_module = mOutput->audioHwDev->handle(); + config->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT; +} + // ---------------------------------------------------------------------------- AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, @@ -2465,15 +2779,10 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud mNormalFrameCount); mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate); - // FIXME - Current mixer implementation only supports stereo output - if (mChannelCount != FCC_2) { - ALOGE("Invalid audio hardware channel count %d", mChannelCount); - } - // create an NBAIO sink for the HAL output stream, and negotiate mOutputSink = new AudioStreamOutSink(output->stream); size_t numCounterOffers = 0; - const NBAIO_Format offers[1] = {Format_from_SR_C(mSampleRate, mChannelCount)}; + const NBAIO_Format offers[1] = {Format_from_SR_C(mSampleRate, mChannelCount, mFormat)}; ssize_t index = mOutputSink->negotiate(offers, 1, NULL, numCounterOffers); ALOG_ASSERT(index == 0); @@ -2492,9 +2801,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. @@ -2535,6 +2862,8 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud // wrap the source side of the MonoPipe to make it an AudioBufferProvider fastTrack->mBufferProvider = new SourceAudioBufferProvider(new MonoPipeReader(monoPipe)); fastTrack->mVolumeProvider = NULL; + fastTrack->mChannelMask = mChannelMask; // mPipeSink channel mask for audio to FastMixer + fastTrack->mFormat = mFormat; // mPipeSink format for audio to FastMixer fastTrack->mGeneration++; state->mFastTracksGen++; state->mTrackMask = 1; @@ -2578,8 +2907,6 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud } #endif - } else { - mFastMixer = NULL; } switch (kUseFastMixer) { @@ -2598,7 +2925,7 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud AudioFlinger::MixerThread::~MixerThread() { - if (mFastMixer != NULL) { + if (mFastMixer != 0) { FastMixerStateQueue *sq = mFastMixer->sq(); FastMixerState *state = sq->begin(); if (state->mCommand == FastMixerState::COLD_IDLE) { @@ -2620,7 +2947,7 @@ AudioFlinger::MixerThread::~MixerThread() ALOG_ASSERT(fastTrack->mBufferProvider != NULL); delete fastTrack->mBufferProvider; sq->end(false /*didModify*/); - delete mFastMixer; + mFastMixer.clear(); #ifdef AUDIO_WATCHDOG if (mAudioWatchdog != 0) { mAudioWatchdog->requestExit(); @@ -2636,7 +2963,7 @@ AudioFlinger::MixerThread::~MixerThread() uint32_t AudioFlinger::MixerThread::correctLatency_l(uint32_t latency) const { - if (mFastMixer != NULL) { + if (mFastMixer != 0) { MonoPipe *pipe = (MonoPipe *)mPipeSink.get(); latency += (pipe->getAvgFrames() * 1000) / mSampleRate; } @@ -2653,7 +2980,7 @@ ssize_t AudioFlinger::MixerThread::threadLoop_write() { // FIXME we should only do one push per cycle; confirm this is true // Start the fast mixer if it's not already running - if (mFastMixer != NULL) { + if (mFastMixer != 0) { FastMixerStateQueue *sq = mFastMixer->sq(); FastMixerState *state = sq->begin(); if (state->mCommand != FastMixerState::MIX_WRITE && @@ -2687,7 +3014,7 @@ ssize_t AudioFlinger::MixerThread::threadLoop_write() void AudioFlinger::MixerThread::threadLoop_standby() { // Idle the fast mixer if it's currently running - if (mFastMixer != NULL) { + if (mFastMixer != 0) { FastMixerStateQueue *sq = mFastMixer->sq(); FastMixerState *state = sq->begin(); if (!(state->mCommand & FastMixerState::IDLE)) { @@ -2713,12 +3040,6 @@ void AudioFlinger::MixerThread::threadLoop_standby() PlaybackThread::threadLoop_standby(); } -// Empty implementation for standard mixer -// Overridden for offloaded playback -void AudioFlinger::PlaybackThread::flushOutput_l() -{ -} - bool AudioFlinger::PlaybackThread::waitingAsyncCallback_l() { return false; @@ -2750,6 +3071,12 @@ void AudioFlinger::PlaybackThread::threadLoop_standby() } } +void AudioFlinger::PlaybackThread::onAddNewTrack_l() +{ + ALOGV("signal playback thread"); + broadcast_l(); +} + void AudioFlinger::MixerThread::threadLoop_mix() { // obtain the presentation timestamp of the next output buffer @@ -2768,7 +3095,7 @@ void AudioFlinger::MixerThread::threadLoop_mix() // mix buffers... mAudioMixer->process(pts); - mCurrentWriteLength = mixBufferSize; + mCurrentWriteLength = mSinkBufferSize; // increase sleep time progressively when application underrun condition clears. // Only increase sleep time if the mixer is ready for two consecutive times to avoid // that a steady state of alternating ready/not ready conditions keeps the sleep time @@ -2802,7 +3129,13 @@ void AudioFlinger::MixerThread::threadLoop_sleepTime() sleepTime = idleSleepTime; } } else if (mBytesWritten != 0 || (mMixerStatus == MIXER_TRACKS_ENABLED)) { - memset (mMixBuffer, 0, mixBufferSize); + // clear out mMixerBuffer or mSinkBuffer, to ensure buffers are cleared + // before effects processing or output. + if (mMixerBufferValid) { + memset(mMixerBuffer, 0, mMixerBufferSize); + } else { + memset(mSinkBuffer, 0, mSinkBufferSize); + } sleepTime = 0; ALOGV_IF(mBytesWritten == 0 && (mMixerStatus == MIXER_TRACKS_ENABLED), "anticipated start"); @@ -2844,11 +3177,14 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac FastMixerState *state = NULL; bool didModify = false; FastMixerStateQueue::block_t block = FastMixerStateQueue::BLOCK_UNTIL_PUSHED; - if (mFastMixer != NULL) { + if (mFastMixer != 0) { sq = mFastMixer->sq(); state = sq->begin(); } + mMixerBufferValid = false; // mMixerBuffer has no valid data until appropriate tracks found. + mEffectBufferValid = false; // mEffectBuffer has no valid data until tracks found. + for (size_t i=0 ; i<count ; i++) { const sp<Track> t = mActiveTracks[i].promote(); if (t == 0) { @@ -2967,7 +3303,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac break; case TrackBase::IDLE: default: - LOG_FATAL("unexpected track state %d", track->mState); + LOG_ALWAYS_FATAL("unexpected track state %d", track->mState); } if (isActive) { @@ -2978,6 +3314,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac fastTrack->mBufferProvider = eabp; fastTrack->mVolumeProvider = vp; fastTrack->mChannelMask = track->mChannelMask; + fastTrack->mFormat = track->mFormat; fastTrack->mGeneration++; state->mTrackMask |= 1 << j; didModify = true; @@ -2998,7 +3335,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac // because we're about to decrement the last sp<> on those tracks. block = FastMixerStateQueue::BLOCK_UNTIL_ACKED; } else { - LOG_FATAL("fast track %d should have been active", j); + LOG_ALWAYS_FATAL("fast track %d should have been active", j); } tracksToRemove->add(track); // Avoids a misleading display in dumpsys @@ -3027,12 +3364,14 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac // +1 for rounding and +1 for additional sample needed for interpolation desiredFrames = (mNormalFrameCount * sr) / mSampleRate + 1 + 1; // add frames already consumed but not yet released by the resampler - // because cblk->framesReady() will include these frames + // because mAudioTrackServerProxy->framesReady() will include these frames desiredFrames += mAudioMixer->getUnreleasedFrames(track->name()); +#if 0 // the minimum track buffer size is normally twice the number of frames necessary // to fill one buffer and the resampler should not leave more than one buffer worth // of unreleased frames after each pass, but just in case... ALOG_ASSERT(desiredFrames <= cblk->frameCount_); +#endif } uint32_t minFrames = 1; if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() && @@ -3048,10 +3387,14 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac mixedTracks++; - // track->mainBuffer() != mMixBuffer means there is an effect chain - // connected to the track + // track->mainBuffer() != mSinkBuffer or mMixerBuffer means + // there is an effect chain connected to the track chain.clear(); - if (track->mainBuffer() != mMixBuffer) { + if (track->mainBuffer() != mSinkBuffer && + track->mainBuffer() != mMixerBuffer) { + if (mEffectBufferEnabled) { + mEffectBufferValid = true; // Later can set directly. + } chain = getEffectChain_l(track->sessionId()); // Delegate volume control to effect in track effect chain if needed if (chain != 0) { @@ -3081,9 +3424,11 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac } // compute volume for this track - uint32_t vl, vr, va; + uint32_t vl, vr; // in U8.24 integer format + float vlf, vrf, vaf; // in [0.0, 1.0] float format if (track->isPausing() || mStreamTypes[track->streamType()].mute) { - vl = vr = va = 0; + vl = vr = 0; + vlf = vrf = vaf = 0.; if (track->isPausing()) { track->setPaused(); } @@ -3093,37 +3438,44 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac float typeVolume = mStreamTypes[track->streamType()].volume; float v = masterVolume * typeVolume; AudioTrackServerProxy *proxy = track->mAudioTrackServerProxy; - uint32_t vlr = proxy->getVolumeLR(); - vl = vlr & 0xFFFF; - vr = vlr >> 16; + gain_minifloat_packed_t vlr = proxy->getVolumeLR(); + vlf = float_from_gain(gain_minifloat_unpack_left(vlr)); + vrf = float_from_gain(gain_minifloat_unpack_right(vlr)); // track volumes come from shared memory, so can't be trusted and must be clamped - if (vl > MAX_GAIN_INT) { - ALOGV("Track left volume out of range: %04X", vl); - vl = MAX_GAIN_INT; + if (vlf > GAIN_FLOAT_UNITY) { + ALOGV("Track left volume out of range: %.3g", vlf); + vlf = GAIN_FLOAT_UNITY; } - if (vr > MAX_GAIN_INT) { - ALOGV("Track right volume out of range: %04X", vr); - vr = MAX_GAIN_INT; + if (vrf > GAIN_FLOAT_UNITY) { + ALOGV("Track right volume out of range: %.3g", vrf); + vrf = GAIN_FLOAT_UNITY; } // now apply the master volume and stream type volume - vl = (uint32_t)(v * vl) << 12; - vr = (uint32_t)(v * vr) << 12; + vlf *= v; + vrf *= v; // assuming master volume and stream type volume each go up to 1.0, - // vl and vr are now in 8.24 format - + // then derive vl and vr as U8.24 versions for the effect chain + const float scaleto8_24 = MAX_GAIN_INT * MAX_GAIN_INT; + vl = (uint32_t) (scaleto8_24 * vlf); + vr = (uint32_t) (scaleto8_24 * vrf); + // vl and vr are now in U8.24 format uint16_t sendLevel = proxy->getSendLevel_U4_12(); // send level comes from shared memory and so may be corrupt if (sendLevel > MAX_GAIN_INT) { ALOGV("Track send level out of range: %04X", sendLevel); sendLevel = MAX_GAIN_INT; } - va = (uint32_t)(v * sendLevel); + // vaf is represented as [0.0, 1.0] float by rescaling sendLevel + vaf = v * sendLevel * (1. / MAX_GAIN_INT); } // Delegate volume control to effect in track effect chain if needed if (chain != 0 && chain->setVolume_l(&vl, &vr)) { // Do not ramp volume if volume is controlled by effect param = AudioMixer::VOLUME; + // Update remaining floating point volume levels + vlf = (float)vl / (1 << 24); + vrf = (float)vr / (1 << 24); track->mHasVolumeController = true; } else { // force no volume ramp when volume controller was just disabled or removed @@ -3134,28 +3486,13 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac track->mHasVolumeController = false; } - // Convert volumes from 8.24 to 4.12 format - // This additional clamping is needed in case chain->setVolume_l() overshot - vl = (vl + (1 << 11)) >> 12; - if (vl > MAX_GAIN_INT) { - vl = MAX_GAIN_INT; - } - vr = (vr + (1 << 11)) >> 12; - if (vr > MAX_GAIN_INT) { - vr = MAX_GAIN_INT; - } - - if (va > MAX_GAIN_INT) { - va = MAX_GAIN_INT; // va is uint32_t, so no need to check for - - } - // XXX: these things DON'T need to be done each time mAudioMixer->setBufferProvider(name, track); mAudioMixer->enable(name); - mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, (void *)(uintptr_t)vl); - mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, (void *)(uintptr_t)vr); - mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, (void *)(uintptr_t)va); + mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, &vlf); + mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, &vrf); + mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, &vaf); mAudioMixer->setParameter( name, AudioMixer::TRACK, @@ -3164,8 +3501,12 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK, (void *)(uintptr_t)track->channelMask()); + mAudioMixer->setParameter( + name, + AudioMixer::TRACK, + AudioMixer::MIXER_CHANNEL_MASK, (void *)(uintptr_t)mChannelMask); // limit track sample rate to 2 x output sample rate, which changes at re-configuration - uint32_t maxSampleRate = mSampleRate * 2; + uint32_t maxSampleRate = mSampleRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX; uint32_t reqSampleRate = track->mAudioTrackServerProxy->getSampleRate(); if (reqSampleRate == 0) { reqSampleRate = mSampleRate; @@ -3177,10 +3518,41 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac AudioMixer::RESAMPLE, AudioMixer::SAMPLE_RATE, (void *)(uintptr_t)reqSampleRate); - mAudioMixer->setParameter( - name, - AudioMixer::TRACK, - AudioMixer::MAIN_BUFFER, (void *)track->mainBuffer()); + /* + * Select the appropriate output buffer for the track. + * + * Tracks with effects go into their own effects chain buffer + * and from there into either mEffectBuffer or mSinkBuffer. + * + * Other tracks can use mMixerBuffer for higher precision + * channel accumulation. If this buffer is enabled + * (mMixerBufferEnabled true), then selected tracks will accumulate + * into it. + * + */ + if (mMixerBufferEnabled + && (track->mainBuffer() == mSinkBuffer + || track->mainBuffer() == mMixerBuffer)) { + mAudioMixer->setParameter( + name, + AudioMixer::TRACK, + AudioMixer::MIXER_FORMAT, (void *)mMixerBufferFormat); + mAudioMixer->setParameter( + name, + AudioMixer::TRACK, + AudioMixer::MAIN_BUFFER, (void *)mMixerBuffer); + // TODO: override track->mainBuffer()? + mMixerBufferValid = true; + } else { + mAudioMixer->setParameter( + name, + AudioMixer::TRACK, + AudioMixer::MIXER_FORMAT, (void *)AUDIO_FORMAT_PCM_16_BIT); + mAudioMixer->setParameter( + name, + AudioMixer::TRACK, + AudioMixer::MAIN_BUFFER, (void *)track->mainBuffer()); + } mAudioMixer->setParameter( name, AudioMixer::TRACK, @@ -3294,13 +3666,34 @@ track_is_ready: ; // remove all the tracks that need to be... removeTracks_l(*tracksToRemove); - // mix buffer must be cleared if all tracks are connected to an - // effect chain as in this case the mixer will not write to - // mix buffer and track effects will accumulate into it + if (getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX) != 0) { + mEffectBufferValid = true; + } + + // sink or mix buffer must be cleared if all tracks are connected to an + // effect chain as in this case the mixer will not write to the sink or mix buffer + // and track effects will accumulate into it if ((mBytesRemaining == 0) && ((mixedTracks != 0 && mixedTracks == tracksWithEffect) || (mixedTracks == 0 && fastTracks > 0))) { // FIXME as a performance optimization, should remember previous zero status - memset(mMixBuffer, 0, mNormalFrameCount * mChannelCount * sizeof(int16_t)); + if (mMixerBufferValid) { + memset(mMixerBuffer, 0, mMixerBufferSize); + // TODO: In testing, mSinkBuffer below need not be cleared because + // the PlaybackThread::threadLoop() copies mMixerBuffer into mSinkBuffer + // after mixing. + // + // To enforce this guarantee: + // ((mixedTracks != 0 && mixedTracks == tracksWithEffect) || + // (mixedTracks == 0 && fastTracks > 0)) + // must imply MIXER_TRACKS_READY. + // Later, we may clear buffers regardless, and skip much of this logic. + } + // TODO - either mEffectBuffer or mSinkBuffer needs to be cleared. + if (mEffectBufferValid) { + memset(mEffectBuffer, 0, mEffectBufferSize); + } + // FIXME as a performance optimization, should remember previous zero status + memset(mSinkBuffer, 0, mNormalFrameCount * mFrameSize); } // if any fast tracks, then status is ready @@ -3312,9 +3705,10 @@ track_is_ready: ; } // getTrackName_l() must be called with ThreadBase::mLock held -int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask, int sessionId) +int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask, + audio_format_t format, int sessionId) { - return mAudioMixer->getTrackName(channelMask, sessionId); + return mAudioMixer->getTrackName(channelMask, format, sessionId); } // deleteTrackName_l() must be called with ThreadBase::mLock held @@ -3324,130 +3718,122 @@ void AudioFlinger::MixerThread::deleteTrackName_l(int name) mAudioMixer->deleteTrackName(name); } -// checkForNewParameters_l() must be called with ThreadBase::mLock held -bool AudioFlinger::MixerThread::checkForNewParameters_l() +// checkForNewParameter_l() must be called with ThreadBase::mLock held +bool AudioFlinger::MixerThread::checkForNewParameter_l(const String8& keyValuePair, + status_t& status) { - // if !&IDLE, holds the FastMixer state to restore after new parameters processed - FastMixerState::Command previousCommand = FastMixerState::HOT_IDLE; bool reconfig = false; - while (!mNewParameters.isEmpty()) { + status = NO_ERROR; - if (mFastMixer != NULL) { - FastMixerStateQueue *sq = mFastMixer->sq(); - FastMixerState *state = sq->begin(); - if (!(state->mCommand & FastMixerState::IDLE)) { - previousCommand = state->mCommand; - state->mCommand = FastMixerState::HOT_IDLE; - sq->end(); - sq->push(FastMixerStateQueue::BLOCK_UNTIL_ACKED); - } else { - sq->end(false /*didModify*/); - } + // if !&IDLE, holds the FastMixer state to restore after new parameters processed + FastMixerState::Command previousCommand = FastMixerState::HOT_IDLE; + if (mFastMixer != 0) { + FastMixerStateQueue *sq = mFastMixer->sq(); + FastMixerState *state = sq->begin(); + if (!(state->mCommand & FastMixerState::IDLE)) { + previousCommand = state->mCommand; + state->mCommand = FastMixerState::HOT_IDLE; + sq->end(); + sq->push(FastMixerStateQueue::BLOCK_UNTIL_ACKED); + } else { + sq->end(false /*didModify*/); } + } - status_t status = NO_ERROR; - String8 keyValuePair = mNewParameters[0]; - AudioParameter param = AudioParameter(keyValuePair); - int value; - - if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) { + AudioParameter param = AudioParameter(keyValuePair); + int value; + if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) { + reconfig = true; + } + if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) { + if (!isValidPcmSinkFormat((audio_format_t) value)) { + status = BAD_VALUE; + } else { + // no need to save value, since it's constant reconfig = true; } - if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) { - if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) { - status = BAD_VALUE; - } else { - reconfig = true; - } - } - if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) { - if ((audio_channel_mask_t) value != AUDIO_CHANNEL_OUT_STEREO) { - status = BAD_VALUE; - } else { - reconfig = true; - } + } + if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) { + if (!isValidPcmSinkChannelMask((audio_channel_mask_t) value)) { + status = BAD_VALUE; + } else { + // no need to save value, since it's constant + reconfig = true; } - if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { - // do not accept frame count changes if tracks are open as the track buffer - // size depends on frame count and correct behavior would not be guaranteed - // if frame count is changed after track creation - if (!mTracks.isEmpty()) { - status = INVALID_OPERATION; - } else { - reconfig = true; - } + } + if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { + // do not accept frame count changes if tracks are open as the track buffer + // size depends on frame count and correct behavior would not be guaranteed + // if frame count is changed after track creation + if (!mTracks.isEmpty()) { + status = INVALID_OPERATION; + } else { + reconfig = true; } - if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) { + } + if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) { #ifdef ADD_BATTERY_DATA - // when changing the audio output device, call addBatteryData to notify - // the change - if (mOutDevice != value) { - uint32_t params = 0; - // check whether speaker is on - if (value & AUDIO_DEVICE_OUT_SPEAKER) { - params |= IMediaPlayerService::kBatteryDataSpeakerOn; - } + // when changing the audio output device, call addBatteryData to notify + // the change + if (mOutDevice != value) { + uint32_t params = 0; + // check whether speaker is on + if (value & AUDIO_DEVICE_OUT_SPEAKER) { + params |= IMediaPlayerService::kBatteryDataSpeakerOn; + } - audio_devices_t deviceWithoutSpeaker - = AUDIO_DEVICE_OUT_ALL & ~AUDIO_DEVICE_OUT_SPEAKER; - // check if any other device (except speaker) is on - if (value & deviceWithoutSpeaker ) { - params |= IMediaPlayerService::kBatteryDataOtherAudioDeviceOn; - } + audio_devices_t deviceWithoutSpeaker + = AUDIO_DEVICE_OUT_ALL & ~AUDIO_DEVICE_OUT_SPEAKER; + // check if any other device (except speaker) is on + if (value & deviceWithoutSpeaker ) { + params |= IMediaPlayerService::kBatteryDataOtherAudioDeviceOn; + } - if (params != 0) { - addBatteryData(params); - } + if (params != 0) { + addBatteryData(params); } + } #endif - // forward device change to effects that have requested to be - // aware of attached audio device. - if (value != AUDIO_DEVICE_NONE) { - mOutDevice = value; - for (size_t i = 0; i < mEffectChains.size(); i++) { - mEffectChains[i]->setDevice_l(mOutDevice); - } + // forward device change to effects that have requested to be + // aware of attached audio device. + if (value != AUDIO_DEVICE_NONE) { + mOutDevice = value; + for (size_t i = 0; i < mEffectChains.size(); i++) { + mEffectChains[i]->setDevice_l(mOutDevice); } } + } - if (status == NO_ERROR) { + if (status == NO_ERROR) { + status = mOutput->stream->common.set_parameters(&mOutput->stream->common, + keyValuePair.string()); + if (!mStandby && status == INVALID_OPERATION) { + mOutput->stream->common.standby(&mOutput->stream->common); + mStandby = true; + mBytesWritten = 0; status = mOutput->stream->common.set_parameters(&mOutput->stream->common, - keyValuePair.string()); - if (!mStandby && status == INVALID_OPERATION) { - mOutput->stream->common.standby(&mOutput->stream->common); - mStandby = true; - mBytesWritten = 0; - status = mOutput->stream->common.set_parameters(&mOutput->stream->common, - keyValuePair.string()); - } - if (status == NO_ERROR && reconfig) { - readOutputParameters(); - delete mAudioMixer; - mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate); - for (size_t i = 0; i < mTracks.size() ; i++) { - int name = getTrackName_l(mTracks[i]->mChannelMask, mTracks[i]->mSessionId); - if (name < 0) { - break; - } - mTracks[i]->mName = name; + keyValuePair.string()); + } + if (status == NO_ERROR && reconfig) { + readOutputParameters_l(); + delete mAudioMixer; + mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate); + for (size_t i = 0; i < mTracks.size() ; i++) { + int name = getTrackName_l(mTracks[i]->mChannelMask, + mTracks[i]->mFormat, mTracks[i]->mSessionId); + if (name < 0) { + break; } - sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED); + mTracks[i]->mName = name; } + sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED); } - - mNewParameters.removeAt(0); - - mParamStatus = status; - mParamCond.signal(); - // wait for condition with time out in case the thread calling ThreadBase::setParameters() - // already timed out waiting for the status and will never signal the condition. - mWaitWorkCV.waitRelative(mLock, kSetParametersTimeoutNs); } if (!(previousCommand & FastMixerState::IDLE)) { - ALOG_ASSERT(mFastMixer != NULL); + ALOG_ASSERT(mFastMixer != 0); FastMixerStateQueue *sq = mFastMixer->sq(); FastMixerState *state = sq->begin(); ALOG_ASSERT(state->mCommand == FastMixerState::HOT_IDLE); @@ -3468,9 +3854,7 @@ void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& ar PlaybackThread::dumpInternals(fd, args); - snprintf(buffer, SIZE, "AudioMixer tracks: %08x\n", mAudioMixer->trackNames()); - result.append(buffer); - write(fd, result.string(), result.size()); + dprintf(fd, " AudioMixer tracks: 0x%08x\n", mAudioMixer->trackNames()); // Make a non-atomic copy of fast mixer dump state so it won't change underneath us const FastMixerDumpState copy(mFastMixerDumpState); @@ -3551,13 +3935,17 @@ void AudioFlinger::DirectOutputThread::processVolume_l(Track *track, bool lastTr float typeVolume = mStreamTypes[track->streamType()].volume; float v = mMasterVolume * typeVolume; AudioTrackServerProxy *proxy = track->mAudioTrackServerProxy; - uint32_t vlr = proxy->getVolumeLR(); - float v_clamped = v * (vlr & 0xFFFF); - if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; - left = v_clamped/MAX_GAIN; - v_clamped = v * (vlr >> 16); - if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; - right = v_clamped/MAX_GAIN; + gain_minifloat_packed_t vlr = proxy->getVolumeLR(); + left = float_from_gain(gain_minifloat_unpack_left(vlr)); + if (left > GAIN_FLOAT_UNITY) { + left = GAIN_FLOAT_UNITY; + } + left *= v; + right = float_from_gain(gain_minifloat_unpack_right(vlr)); + if (right > GAIN_FLOAT_UNITY) { + right = GAIN_FLOAT_UNITY; + } + right *= v; } if (lastTrack) { @@ -3612,14 +4000,16 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep // The first time a track is added we wait // for all its buffers to be filled before processing it uint32_t minFrames; - if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing()) { + if ((track->sharedBuffer() == 0) && !track->isStopping_1() && !track->isPausing()) { minFrames = mNormalFrameCount; } else { minFrames = 1; } - if ((track->framesReady() >= minFrames) && track->isReady() && - !track->isPaused() && !track->isTerminated()) + ALOGI("prepareTracks_l minFrames %d state %d frames ready %d, ", + minFrames, track->mState, track->framesReady()); + if ((track->framesReady() >= minFrames) && track->isReady() && !track->isPaused() && + !track->isStopping_2() && !track->isStopped()) { ALOGVV("track %d s=%08x [OK]", track->name(), cblk->mServer); @@ -3646,17 +4036,26 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep if (!mEffectChains.isEmpty() && last) { mEffectChains[0]->clearInputBuffer(); } - - ALOGVV("track %d s=%08x [NOT READY]", track->name(), cblk->mServer); - if ((track->sharedBuffer() != 0) || track->isTerminated() || - track->isStopped() || track->isPaused()) { + if (track->isStopping_1()) { + track->mState = TrackBase::STOPPING_2; + } + if ((track->sharedBuffer() != 0) || track->isStopped() || + track->isStopping_2() || track->isPaused()) { // We have consumed all the buffers of this track. // Remove it from the list of active tracks. - // TODO: implement behavior for compressed audio - size_t audioHALFrames = (latency_l() * mSampleRate) / 1000; + size_t audioHALFrames; + if (audio_is_linear_pcm(mFormat)) { + audioHALFrames = (latency_l() * mSampleRate) / 1000; + } else { + audioHALFrames = 0; + } + size_t framesWritten = mBytesWritten / mFrameSize; if (mStandby || !last || track->presentationComplete(framesWritten, audioHALFrames)) { + if (track->isStopping_2()) { + track->mState = TrackBase::STOPPED; + } if (track->isStopped()) { track->reset(); } @@ -3688,7 +4087,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep void AudioFlinger::DirectOutputThread::threadLoop_mix() { size_t frameCount = mFrameCount; - int8_t *curBuf = (int8_t *)mMixBuffer; + int8_t *curBuf = (int8_t *)mSinkBuffer; // output audio to hardware while (frameCount) { AudioBufferProvider::Buffer buffer; @@ -3703,7 +4102,7 @@ void AudioFlinger::DirectOutputThread::threadLoop_mix() curBuf += buffer.frameCount * mFrameSize; mActiveTrack->releaseBuffer(&buffer); } - mCurrentWriteLength = curBuf - (int8_t *)mMixBuffer; + mCurrentWriteLength = curBuf - (int8_t *)mSinkBuffer; sleepTime = 0; standbyTime = systemTime() + standbyDelay; mActiveTrack.clear(); @@ -3718,68 +4117,69 @@ void AudioFlinger::DirectOutputThread::threadLoop_sleepTime() sleepTime = idleSleepTime; } } else if (mBytesWritten != 0 && audio_is_linear_pcm(mFormat)) { - memset(mMixBuffer, 0, mFrameCount * mFrameSize); + memset(mSinkBuffer, 0, mFrameCount * mFrameSize); sleepTime = 0; } } // getTrackName_l() must be called with ThreadBase::mLock held -int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask, - int sessionId) +int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask __unused, + audio_format_t format __unused, int sessionId __unused) { return 0; } // deleteTrackName_l() must be called with ThreadBase::mLock held -void AudioFlinger::DirectOutputThread::deleteTrackName_l(int name) +void AudioFlinger::DirectOutputThread::deleteTrackName_l(int name __unused) { } -// checkForNewParameters_l() must be called with ThreadBase::mLock held -bool AudioFlinger::DirectOutputThread::checkForNewParameters_l() +// checkForNewParameter_l() must be called with ThreadBase::mLock held +bool AudioFlinger::DirectOutputThread::checkForNewParameter_l(const String8& keyValuePair, + status_t& status) { bool reconfig = false; - while (!mNewParameters.isEmpty()) { - status_t status = NO_ERROR; - String8 keyValuePair = mNewParameters[0]; - AudioParameter param = AudioParameter(keyValuePair); - int value; - - if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { - // do not accept frame count changes if tracks are open as the track buffer - // size depends on frame count and correct behavior would not be garantied - // if frame count is changed after track creation - if (!mTracks.isEmpty()) { - status = INVALID_OPERATION; - } else { - reconfig = true; + status = NO_ERROR; + + AudioParameter param = AudioParameter(keyValuePair); + int value; + if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) { + // forward device change to effects that have requested to be + // aware of attached audio device. + if (value != AUDIO_DEVICE_NONE) { + mOutDevice = value; + for (size_t i = 0; i < mEffectChains.size(); i++) { + mEffectChains[i]->setDevice_l(mOutDevice); } } - if (status == NO_ERROR) { + } + if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { + // do not accept frame count changes if tracks are open as the track buffer + // size depends on frame count and correct behavior would not be garantied + // if frame count is changed after track creation + if (!mTracks.isEmpty()) { + status = INVALID_OPERATION; + } else { + reconfig = true; + } + } + if (status == NO_ERROR) { + status = mOutput->stream->common.set_parameters(&mOutput->stream->common, + keyValuePair.string()); + if (!mStandby && status == INVALID_OPERATION) { + mOutput->stream->common.standby(&mOutput->stream->common); + mStandby = true; + mBytesWritten = 0; status = mOutput->stream->common.set_parameters(&mOutput->stream->common, - keyValuePair.string()); - if (!mStandby && status == INVALID_OPERATION) { - mOutput->stream->common.standby(&mOutput->stream->common); - mStandby = true; - mBytesWritten = 0; - status = mOutput->stream->common.set_parameters(&mOutput->stream->common, - keyValuePair.string()); - } - if (status == NO_ERROR && reconfig) { - readOutputParameters(); - sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED); - } + keyValuePair.string()); + } + if (status == NO_ERROR && reconfig) { + readOutputParameters_l(); + sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED); } - - mNewParameters.removeAt(0); - - mParamStatus = status; - mParamCond.signal(); - // wait for condition with time out in case the thread calling ThreadBase::setParameters() - // already timed out waiting for the status and will never signal the condition. - mWaitWorkCV.waitRelative(mLock, kSetParametersTimeoutNs); } + return reconfig; } @@ -3987,6 +4387,17 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTr sp<Track> l = mLatestActiveTrack.promote(); bool last = l.get() == track; + if (track->isInvalid()) { + ALOGW("An invalidated track shouldn't be in active list"); + tracksToRemove->add(track); + continue; + } + + if (track->mState == TrackBase::IDLE) { + ALOGW("An idle track shouldn't be in active list"); + continue; + } + if (track->isPausing()) { track->setPaused(); if (last) { @@ -4005,32 +4416,39 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTr mBytesRemaining = 0; // stop writing } tracksToRemove->add(track); - } else if (track->framesReady() && track->isReady() && + } else if (track->isFlushPending()) { + track->flushAck(); + if (last) { + mFlushPending = true; + } + } else if (track->isResumePending()){ + track->resumeAck(); + if (last) { + if (mPausedBytesRemaining) { + // Need to continue write that was interrupted + mCurrentWriteLength = mPausedWriteLength; + mBytesRemaining = mPausedBytesRemaining; + mPausedBytesRemaining = 0; + } + if (mHwPaused) { + doHwResume = true; + mHwPaused = false; + // threadLoop_mix() will handle the case that we need to + // resume an interrupted write + } + // enable write to audio HAL + sleepTime = 0; + + // Do not handle new data in this iteration even if track->framesReady() + mixerStatus = MIXER_TRACKS_ENABLED; + } + } else if (track->framesReady() && track->isReady() && !track->isPaused() && !track->isTerminated() && !track->isStopping_2()) { ALOGVV("OffloadThread: track %d s=%08x [OK]", track->name(), cblk->mServer); if (track->mFillingUpStatus == Track::FS_FILLED) { track->mFillingUpStatus = Track::FS_ACTIVE; // make sure processVolume_l() will apply new volume even if 0 mLeftVolFloat = mRightVolFloat = -1.0; - if (track->mState == TrackBase::RESUMING) { - track->mState = TrackBase::ACTIVE; - if (last) { - if (mPausedBytesRemaining) { - // Need to continue write that was interrupted - mCurrentWriteLength = mPausedWriteLength; - mBytesRemaining = mPausedBytesRemaining; - mPausedBytesRemaining = 0; - } - if (mHwPaused) { - doHwResume = true; - mHwPaused = false; - // threadLoop_mix() will handle the case that we need to - // resume an interrupted write - } - // enable write to audio HAL - sleepTime = 0; - } - } } if (last) { @@ -4054,7 +4472,6 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTr // seek when resuming. if (previousTrack->sessionId() != track->sessionId()) { previousTrack->invalidate(); - mFlushPending = true; } } } @@ -4100,7 +4517,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTr size_t audioHALFrames = (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000; size_t framesWritten = - mBytesWritten / audio_stream_frame_size(&mOutput->stream->common); + mBytesWritten / audio_stream_out_frame_size(mOutput->stream); track->presentationComplete(framesWritten, audioHALFrames); track->reset(); tracksToRemove->add(track); @@ -4130,9 +4547,6 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTr // if resume is received before pause is executed. if (!mStandby && (doHwPause || (mFlushPending && !mHwPaused && (count != 0)))) { mOutput->stream->pause(mOutput->stream); - if (!doHwPause) { - doHwResume = true; - } } if (mFlushPending) { flushHw_l(); @@ -4148,11 +4562,6 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTr return mixerStatus; } -void AudioFlinger::OffloadThread::flushOutput_l() -{ - mFlushPending = true; -} - // must be called with thread mutex locked bool AudioFlinger::OffloadThread::waitingAsyncCallback_l() { @@ -4167,15 +4576,15 @@ bool AudioFlinger::OffloadThread::waitingAsyncCallback_l() // must be called with thread mutex locked bool AudioFlinger::OffloadThread::shouldStandby_l() { - bool TrackPaused = false; + bool trackPaused = false; // do not put the HAL in standby when paused. AwesomePlayer clear the offloaded AudioTrack // after a timeout and we will enter standby then. if (mTracks.size() > 0) { - TrackPaused = mTracks[mTracks.size() - 1]->isPaused(); + trackPaused = mTracks[mTracks.size() - 1]->isPaused(); } - return !mStandby && !TrackPaused; + return !mStandby && !trackPaused; } @@ -4193,6 +4602,8 @@ void AudioFlinger::OffloadThread::flushHw_l() mBytesRemaining = 0; mPausedWriteLength = 0; mPausedBytesRemaining = 0; + mHwPaused = false; + if (mUseAsyncWrite) { // discard any pending drain or write ack by incrementing sequence mWriteAckSequence = (mWriteAckSequence + 2) & ~1; @@ -4203,6 +4614,18 @@ void AudioFlinger::OffloadThread::flushHw_l() } } +void AudioFlinger::OffloadThread::onAddNewTrack_l() +{ + sp<Track> previousTrack = mPreviousTrack.promote(); + sp<Track> latestTrack = mLatestActiveTrack.promote(); + + if (previousTrack != 0 && latestTrack != 0 && + (previousTrack->sessionId() != latestTrack->sessionId())) { + mFlushPending = true; + } + PlaybackThread::onAddNewTrack_l(); +} + // ---------------------------------------------------------------------------- AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, @@ -4227,11 +4650,11 @@ void AudioFlinger::DuplicatingThread::threadLoop_mix() if (outputsReady(outputTracks)) { mAudioMixer->process(AudioBufferProvider::kInvalidPTS); } else { - memset(mMixBuffer, 0, mixBufferSize); + memset(mSinkBuffer, 0, mSinkBufferSize); } sleepTime = 0; writeFrames = mNormalFrameCount; - mCurrentWriteLength = mixBufferSize; + mCurrentWriteLength = mSinkBufferSize; standbyTime = systemTime() + standbyDelay; } @@ -4246,7 +4669,7 @@ void AudioFlinger::DuplicatingThread::threadLoop_sleepTime() } else if (mBytesWritten != 0) { if (mMixerStatus == MIXER_TRACKS_ENABLED) { writeFrames = mNormalFrameCount; - memset(mMixBuffer, 0, mixBufferSize); + memset(mSinkBuffer, 0, mSinkBufferSize); } else { // flush remaining overflow buffers in output tracks writeFrames = 0; @@ -4258,10 +4681,18 @@ void AudioFlinger::DuplicatingThread::threadLoop_sleepTime() ssize_t AudioFlinger::DuplicatingThread::threadLoop_write() { for (size_t i = 0; i < outputTracks.size(); i++) { - outputTracks[i]->write(mMixBuffer, writeFrames); + // We convert the duplicating thread format to AUDIO_FORMAT_PCM_16_BIT + // for delivery downstream as needed. This in-place conversion is safe as + // AUDIO_FORMAT_PCM_16_BIT is smaller than any other supported format + // (AUDIO_FORMAT_PCM_8_BIT is not allowed here). + if (mFormat != AUDIO_FORMAT_PCM_16_BIT) { + memcpy_by_audio_format(mSinkBuffer, AUDIO_FORMAT_PCM_16_BIT, + mSinkBuffer, mFormat, writeFrames * mChannelCount); + } + outputTracks[i]->write(reinterpret_cast<int16_t*>(mSinkBuffer), writeFrames); } mStandby = false; - return (ssize_t)mixBufferSize; + return (ssize_t)mSinkBufferSize; } void AudioFlinger::DuplicatingThread::threadLoop_standby() @@ -4287,10 +4718,16 @@ void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread) Mutex::Autolock _l(mLock); // FIXME explain this formula size_t frameCount = (3 * mNormalFrameCount * mSampleRate) / thread->sampleRate(); + // OutputTrack is forced to AUDIO_FORMAT_PCM_16_BIT regardless of mFormat + // due to current usage case and restrictions on the AudioBufferProvider. + // Actual buffer conversion is done in threadLoop_write(). + // + // TODO: This may change in the future, depending on multichannel + // (and non int16_t*) support on AF::PlaybackThread::OutputTrack OutputTrack *outputTrack = new OutputTrack(thread, this, mSampleRate, - mFormat, + AUDIO_FORMAT_PCM_16_BIT, mChannelMask, frameCount, IPCThreadState::self()->getCallingUid()); @@ -4372,8 +4809,6 @@ void AudioFlinger::DuplicatingThread::cacheParameters_l() AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, AudioStreamIn *input, - uint32_t sampleRate, - audio_channel_mask_t channelMask, audio_io_handle_t id, audio_devices_t outDevice, audio_devices_t inDevice @@ -4382,27 +4817,162 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, #endif ) : ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD), - mInput(input), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL), - // mRsmpInIndex and mBufferSize set by readInputParameters() - mReqChannelCount(popcount(channelMask)), - mReqSampleRate(sampleRate) - // mBytesRead is only meaningful while active, and so is cleared in start() - // (but might be better to also clear here for dump?) + mInput(input), mActiveTracksGen(0), mRsmpInBuffer(NULL), + // mRsmpInFrames and mRsmpInFramesP2 are set by readInputParameters_l() + mRsmpInRear(0) #ifdef TEE_SINK , mTeeSink(teeSink) #endif + , mReadOnlyHeap(new MemoryDealer(kRecordThreadReadOnlyHeapSize, + "RecordThreadRO", MemoryHeapBase::READ_ONLY)) + // mFastCapture below + , mFastCaptureFutex(0) + // mInputSource + // mPipeSink + // mPipeSource + , mPipeFramesP2(0) + // mPipeMemory + // mFastCaptureNBLogWriter + , mFastTrackAvail(false) { snprintf(mName, kNameLength, "AudioIn_%X", id); + mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mName); + + readInputParameters_l(); + + // create an NBAIO source for the HAL input stream, and negotiate + mInputSource = new AudioStreamInSource(input->stream); + size_t numCounterOffers = 0; + const NBAIO_Format offers[1] = {Format_from_SR_C(mSampleRate, mChannelCount, mFormat)}; + ssize_t index = mInputSource->negotiate(offers, 1, NULL, numCounterOffers); + ALOG_ASSERT(index == 0); - readInputParameters(); + // initialize fast capture depending on configuration + bool initFastCapture; + switch (kUseFastCapture) { + case FastCapture_Never: + initFastCapture = false; + break; + case FastCapture_Always: + initFastCapture = true; + break; + case FastCapture_Static: + uint32_t primaryOutputSampleRate; + { + AutoMutex _l(audioFlinger->mHardwareLock); + primaryOutputSampleRate = audioFlinger->mPrimaryOutputSampleRate; + } + initFastCapture = + // either capture sample rate is same as (a reasonable) primary output sample rate + (((primaryOutputSampleRate == 44100 || primaryOutputSampleRate == 48000) && + (mSampleRate == primaryOutputSampleRate)) || + // or primary output sample rate is unknown, and capture sample rate is reasonable + ((primaryOutputSampleRate == 0) && + ((mSampleRate == 44100 || mSampleRate == 48000)))) && + // and the buffer size is < 12 ms + (mFrameCount * 1000) / mSampleRate < 12; + break; + // case FastCapture_Dynamic: + } + + if (initFastCapture) { + // create a Pipe for FastMixer to write to, and for us and fast tracks to read from + NBAIO_Format format = mInputSource->format(); + size_t pipeFramesP2 = roundup(mSampleRate / 25); // double-buffering of 20 ms each + size_t pipeSize = pipeFramesP2 * Format_frameSize(format); + void *pipeBuffer; + const sp<MemoryDealer> roHeap(readOnlyHeap()); + sp<IMemory> pipeMemory; + if ((roHeap == 0) || + (pipeMemory = roHeap->allocate(pipeSize)) == 0 || + (pipeBuffer = pipeMemory->pointer()) == NULL) { + ALOGE("not enough memory for pipe buffer size=%zu", pipeSize); + goto failed; + } + // pipe will be shared directly with fast clients, so clear to avoid leaking old information + memset(pipeBuffer, 0, pipeSize); + Pipe *pipe = new Pipe(pipeFramesP2, format, pipeBuffer); + const NBAIO_Format offers[1] = {format}; + size_t numCounterOffers = 0; + ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers); + ALOG_ASSERT(index == 0); + mPipeSink = pipe; + PipeReader *pipeReader = new PipeReader(*pipe); + numCounterOffers = 0; + index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers); + ALOG_ASSERT(index == 0); + mPipeSource = pipeReader; + mPipeFramesP2 = pipeFramesP2; + mPipeMemory = pipeMemory; + + // create fast capture + mFastCapture = new FastCapture(); + FastCaptureStateQueue *sq = mFastCapture->sq(); +#ifdef STATE_QUEUE_DUMP + // FIXME +#endif + FastCaptureState *state = sq->begin(); + state->mCblk = NULL; + state->mInputSource = mInputSource.get(); + state->mInputSourceGen++; + state->mPipeSink = pipe; + state->mPipeSinkGen++; + state->mFrameCount = mFrameCount; + state->mCommand = FastCaptureState::COLD_IDLE; + // already done in constructor initialization list + //mFastCaptureFutex = 0; + state->mColdFutexAddr = &mFastCaptureFutex; + state->mColdGen++; + state->mDumpState = &mFastCaptureDumpState; +#ifdef TEE_SINK + // FIXME +#endif + mFastCaptureNBLogWriter = audioFlinger->newWriter_l(kFastCaptureLogSize, "FastCapture"); + state->mNBLogWriter = mFastCaptureNBLogWriter.get(); + sq->end(); + sq->push(FastCaptureStateQueue::BLOCK_UNTIL_PUSHED); + + // start the fast capture + mFastCapture->run("FastCapture", ANDROID_PRIORITY_URGENT_AUDIO); + pid_t tid = mFastCapture->getTid(); + int err = requestPriority(getpid_cached, tid, kPriorityFastMixer); + if (err != 0) { + ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d", + kPriorityFastCapture, getpid_cached, tid, err); + } + +#ifdef AUDIO_WATCHDOG + // FIXME +#endif + + mFastTrackAvail = true; + } +failed: ; + + // FIXME mNormalSource } AudioFlinger::RecordThread::~RecordThread() { + if (mFastCapture != 0) { + FastCaptureStateQueue *sq = mFastCapture->sq(); + FastCaptureState *state = sq->begin(); + if (state->mCommand == FastCaptureState::COLD_IDLE) { + int32_t old = android_atomic_inc(&mFastCaptureFutex); + if (old == -1) { + (void) syscall(__NR_futex, &mFastCaptureFutex, FUTEX_WAKE_PRIVATE, 1); + } + } + state->mCommand = FastCaptureState::EXIT; + sq->end(); + sq->push(FastCaptureStateQueue::BLOCK_UNTIL_PUSHED); + mFastCapture->join(); + mFastCapture.clear(); + } + mAudioFlinger->unregisterWriter(mFastCaptureNBLogWriter); + mAudioFlinger->unregisterWriter(mNBLogWriter); delete[] mRsmpInBuffer; - delete mResampler; - delete[] mRsmpOutBuffer; } void AudioFlinger::RecordThread::onFirstRef() @@ -4410,230 +4980,482 @@ void AudioFlinger::RecordThread::onFirstRef() run(mName, PRIORITY_URGENT_AUDIO); } -status_t AudioFlinger::RecordThread::readyToRun() -{ - status_t status = initCheck(); - ALOGW_IF(status != NO_ERROR,"RecordThread %p could not initialize", this); - return status; -} - bool AudioFlinger::RecordThread::threadLoop() { - AudioBufferProvider::Buffer buffer; - sp<RecordTrack> activeTrack; - Vector< sp<EffectChain> > effectChains; - nsecs_t lastWarning = 0; inputStandBy(); + +reacquire_wakelock: + sp<RecordTrack> activeTrack; + int activeTracksGen; { Mutex::Autolock _l(mLock); - activeTrack = mActiveTrack; - acquireWakeLock_l(activeTrack != 0 ? activeTrack->uid() : -1); + size_t size = mActiveTracks.size(); + activeTracksGen = mActiveTracksGen; + if (size > 0) { + // FIXME an arbitrary choice + activeTrack = mActiveTracks[0]; + acquireWakeLock_l(activeTrack->uid()); + if (size > 1) { + SortedVector<int> tmp; + for (size_t i = 0; i < size; i++) { + tmp.add(mActiveTracks[i]->uid()); + } + updateWakeLockUids_l(tmp); + } + } else { + acquireWakeLock_l(-1); + } } - // used to verify we've read at least once before evaluating how many bytes were read - bool readOnce = false; + // used to request a deferred sleep, to be executed later while mutex is unlocked + uint32_t sleepUs = 0; - // start recording - while (!exitPending()) { + // loop while there is work to do + for (;;) { + Vector< sp<EffectChain> > effectChains; + + // sleep with mutex unlocked + if (sleepUs > 0) { + usleep(sleepUs); + sleepUs = 0; + } - processConfigEvents(); + // activeTracks accumulates a copy of a subset of mActiveTracks + Vector< sp<RecordTrack> > activeTracks; + + // reference to the (first and only) active fast track + sp<RecordTrack> fastTrack; + + // reference to a fast track which is about to be removed + sp<RecordTrack> fastTrackToRemove; { // scope for mLock Mutex::Autolock _l(mLock); - checkForNewParameters_l(); - if (mActiveTrack != 0 && activeTrack != mActiveTrack) { - SortedVector<int> tmp; - tmp.add(mActiveTrack->uid()); - updateWakeLockUids_l(tmp); - } - activeTrack = mActiveTrack; - if (mActiveTrack == 0 && mConfigEvents.isEmpty()) { - standby(); - if (exitPending()) { - break; - } + processConfigEvents_l(); + // check exitPending here because checkForNewParameters_l() and + // checkForNewParameters_l() can temporarily release mLock + if (exitPending()) { + break; + } + + // if no active track(s), then standby and release wakelock + size_t size = mActiveTracks.size(); + if (size == 0) { + standbyIfNotAlreadyInStandby(); + // exitPending() can't become true here releaseWakeLock_l(); ALOGV("RecordThread: loop stopping"); // go to sleep mWaitWorkCV.wait(mLock); ALOGV("RecordThread: loop starting"); - acquireWakeLock_l(mActiveTrack != 0 ? mActiveTrack->uid() : -1); - continue; + goto reacquire_wakelock; } - if (mActiveTrack != 0) { - if (mActiveTrack->isTerminated()) { - removeTrack_l(mActiveTrack); - mActiveTrack.clear(); - } else if (mActiveTrack->mState == TrackBase::PAUSING) { - standby(); - mActiveTrack.clear(); - mStartStopCond.broadcast(); - } else if (mActiveTrack->mState == TrackBase::RESUMING) { - if (mReqChannelCount != mActiveTrack->channelCount()) { - mActiveTrack.clear(); - mStartStopCond.broadcast(); - } else if (readOnce) { - // record start succeeds only if first read from audio input - // succeeds - if (mBytesRead >= 0) { - mActiveTrack->mState = TrackBase::ACTIVE; - } else { - mActiveTrack.clear(); - } - mStartStopCond.broadcast(); + + if (mActiveTracksGen != activeTracksGen) { + activeTracksGen = mActiveTracksGen; + SortedVector<int> tmp; + for (size_t i = 0; i < size; i++) { + tmp.add(mActiveTracks[i]->uid()); + } + updateWakeLockUids_l(tmp); + } + + bool doBroadcast = false; + for (size_t i = 0; i < size; ) { + + activeTrack = mActiveTracks[i]; + if (activeTrack->isTerminated()) { + if (activeTrack->isFastTrack()) { + ALOG_ASSERT(fastTrackToRemove == 0); + fastTrackToRemove = activeTrack; } + removeTrack_l(activeTrack); + mActiveTracks.remove(activeTrack); + mActiveTracksGen++; + size--; + continue; + } + + TrackBase::track_state activeTrackState = activeTrack->mState; + switch (activeTrackState) { + + case TrackBase::PAUSING: + mActiveTracks.remove(activeTrack); + mActiveTracksGen++; + doBroadcast = true; + size--; + continue; + + case TrackBase::STARTING_1: + sleepUs = 10000; + i++; + continue; + + case TrackBase::STARTING_2: + doBroadcast = true; mStandby = false; + activeTrack->mState = TrackBase::ACTIVE; + break; + + case TrackBase::ACTIVE: + break; + + case TrackBase::IDLE: + i++; + continue; + + default: + LOG_ALWAYS_FATAL("Unexpected activeTrackState %d", activeTrackState); + } + + activeTracks.add(activeTrack); + i++; + + if (activeTrack->isFastTrack()) { + ALOG_ASSERT(!mFastTrackAvail); + ALOG_ASSERT(fastTrack == 0); + fastTrack = activeTrack; } } + if (doBroadcast) { + mStartStopCond.broadcast(); + } + + // sleep if there are no active tracks to process + if (activeTracks.size() == 0) { + if (sleepUs == 0) { + sleepUs = kRecordThreadSleepUs; + } + continue; + } + sleepUs = 0; lockEffectChains_l(effectChains); } - if (mActiveTrack != 0) { - if (mActiveTrack->mState != TrackBase::ACTIVE && - mActiveTrack->mState != TrackBase::RESUMING) { - unlockEffectChains(effectChains); - usleep(kRecordThreadSleepUs); - continue; + // thread mutex is now unlocked, mActiveTracks unknown, activeTracks.size() > 0 + + size_t size = effectChains.size(); + for (size_t i = 0; i < size; i++) { + // thread mutex is not locked, but effect chain is locked + effectChains[i]->process_l(); + } + + // Push a new fast capture state if fast capture is not already running, or cblk change + if (mFastCapture != 0) { + FastCaptureStateQueue *sq = mFastCapture->sq(); + FastCaptureState *state = sq->begin(); + bool didModify = false; + FastCaptureStateQueue::block_t block = FastCaptureStateQueue::BLOCK_UNTIL_PUSHED; + if (state->mCommand != FastCaptureState::READ_WRITE /* FIXME && + (kUseFastMixer != FastMixer_Dynamic || state->mTrackMask > 1)*/) { + if (state->mCommand == FastCaptureState::COLD_IDLE) { + int32_t old = android_atomic_inc(&mFastCaptureFutex); + if (old == -1) { + (void) syscall(__NR_futex, &mFastCaptureFutex, FUTEX_WAKE_PRIVATE, 1); + } + } + state->mCommand = FastCaptureState::READ_WRITE; +#if 0 // FIXME + mFastCaptureDumpState.increaseSamplingN(mAudioFlinger->isLowRamDevice() ? + FastCaptureDumpState::kSamplingNforLowRamDevice : FastMixerDumpState::kSamplingN); +#endif + didModify = true; } - for (size_t i = 0; i < effectChains.size(); i ++) { - effectChains[i]->process_l(); + audio_track_cblk_t *cblkOld = state->mCblk; + audio_track_cblk_t *cblkNew = fastTrack != 0 ? fastTrack->cblk() : NULL; + if (cblkNew != cblkOld) { + state->mCblk = cblkNew; + // block until acked if removing a fast track + if (cblkOld != NULL) { + block = FastCaptureStateQueue::BLOCK_UNTIL_ACKED; + } + didModify = true; + } + sq->end(didModify); + if (didModify) { + sq->push(block); +#if 0 + if (kUseFastCapture == FastCapture_Dynamic) { + mNormalSource = mPipeSource; + } +#endif } + } - buffer.frameCount = mFrameCount; - status_t status = mActiveTrack->getNextBuffer(&buffer); - if (status == NO_ERROR) { - readOnce = true; - size_t framesOut = buffer.frameCount; - if (mResampler == NULL) { + // now run the fast track destructor with thread mutex unlocked + fastTrackToRemove.clear(); + + // Read from HAL to keep up with fastest client if multiple active tracks, not slowest one. + // Only the client(s) that are too slow will overrun. But if even the fastest client is too + // slow, then this RecordThread will overrun by not calling HAL read often enough. + // If destination is non-contiguous, first read past the nominal end of buffer, then + // copy to the right place. Permitted because mRsmpInBuffer was over-allocated. + + int32_t rear = mRsmpInRear & (mRsmpInFramesP2 - 1); + ssize_t framesRead; + + // If an NBAIO source is present, use it to read the normal capture's data + if (mPipeSource != 0) { + size_t framesToRead = mBufferSize / mFrameSize; + framesRead = mPipeSource->read(&mRsmpInBuffer[rear * mChannelCount], + framesToRead, AudioBufferProvider::kInvalidPTS); + if (framesRead == 0) { + // since pipe is non-blocking, simulate blocking input + sleepUs = (framesToRead * 1000000LL) / mSampleRate; + } + // otherwise use the HAL / AudioStreamIn directly + } else { + ssize_t bytesRead = mInput->stream->read(mInput->stream, + &mRsmpInBuffer[rear * mChannelCount], mBufferSize); + if (bytesRead < 0) { + framesRead = bytesRead; + } else { + framesRead = bytesRead / mFrameSize; + } + } + + if (framesRead < 0 || (framesRead == 0 && mPipeSource == 0)) { + ALOGE("read failed: framesRead=%d", framesRead); + // Force input into standby so that it tries to recover at next read attempt + inputStandBy(); + sleepUs = kRecordThreadSleepUs; + } + if (framesRead <= 0) { + goto unlock; + } + ALOG_ASSERT(framesRead > 0); + + if (mTeeSink != 0) { + (void) mTeeSink->write(&mRsmpInBuffer[rear * mChannelCount], framesRead); + } + // If destination is non-contiguous, we now correct for reading past end of buffer. + { + size_t part1 = mRsmpInFramesP2 - rear; + if ((size_t) framesRead > part1) { + memcpy(mRsmpInBuffer, &mRsmpInBuffer[mRsmpInFramesP2 * mChannelCount], + (framesRead - part1) * mFrameSize); + } + } + rear = mRsmpInRear += framesRead; + + size = activeTracks.size(); + // loop over each active track + for (size_t i = 0; i < size; i++) { + activeTrack = activeTracks[i]; + + // skip fast tracks, as those are handled directly by FastCapture + if (activeTrack->isFastTrack()) { + continue; + } + + enum { + OVERRUN_UNKNOWN, + OVERRUN_TRUE, + OVERRUN_FALSE + } overrun = OVERRUN_UNKNOWN; + + // loop over getNextBuffer to handle circular sink + for (;;) { + + activeTrack->mSink.frameCount = ~0; + status_t status = activeTrack->getNextBuffer(&activeTrack->mSink); + size_t framesOut = activeTrack->mSink.frameCount; + LOG_ALWAYS_FATAL_IF((status == OK) != (framesOut > 0)); + + int32_t front = activeTrack->mRsmpInFront; + ssize_t filled = rear - front; + size_t framesIn; + + if (filled < 0) { + // should not happen, but treat like a massive overrun and re-sync + framesIn = 0; + activeTrack->mRsmpInFront = rear; + overrun = OVERRUN_TRUE; + } else if ((size_t) filled <= mRsmpInFrames) { + framesIn = (size_t) filled; + } else { + // client is not keeping up with server, but give it latest data + framesIn = mRsmpInFrames; + activeTrack->mRsmpInFront = front = rear - framesIn; + overrun = OVERRUN_TRUE; + } + + if (framesOut == 0 || framesIn == 0) { + break; + } + + if (activeTrack->mResampler == NULL) { // no resampling - while (framesOut) { - size_t framesIn = mFrameCount - mRsmpInIndex; - if (framesIn) { - int8_t *src = (int8_t *)mRsmpInBuffer + mRsmpInIndex * mFrameSize; - int8_t *dst = buffer.i8 + (buffer.frameCount - framesOut) * - mActiveTrack->mFrameSize; - if (framesIn > framesOut) - framesIn = framesOut; - mRsmpInIndex += framesIn; - framesOut -= framesIn; - if (mChannelCount == mReqChannelCount) { - memcpy(dst, src, framesIn * mFrameSize); - } else { - if (mChannelCount == 1) { - upmix_to_stereo_i16_from_mono_i16((int16_t *)dst, - (int16_t *)src, framesIn); - } else { - downmix_to_mono_i16_from_stereo_i16((int16_t *)dst, - (int16_t *)src, framesIn); - } - } + if (framesIn > framesOut) { + framesIn = framesOut; + } else { + framesOut = framesIn; + } + int8_t *dst = activeTrack->mSink.i8; + while (framesIn > 0) { + front &= mRsmpInFramesP2 - 1; + size_t part1 = mRsmpInFramesP2 - front; + if (part1 > framesIn) { + part1 = framesIn; } - if (framesOut && mFrameCount == mRsmpInIndex) { - void *readInto; - if (framesOut == mFrameCount && mChannelCount == mReqChannelCount) { - readInto = buffer.raw; - framesOut = 0; - } else { - readInto = mRsmpInBuffer; - mRsmpInIndex = 0; - } - mBytesRead = mInput->stream->read(mInput->stream, readInto, - mBufferSize); - if (mBytesRead <= 0) { - if ((mBytesRead < 0) && (mActiveTrack->mState == TrackBase::ACTIVE)) - { - ALOGE("Error reading audio input"); - // Force input into standby so that it tries to - // recover at next read attempt - inputStandBy(); - usleep(kRecordThreadSleepUs); - } - mRsmpInIndex = mFrameCount; - framesOut = 0; - buffer.frameCount = 0; - } -#ifdef TEE_SINK - else if (mTeeSink != 0) { - (void) mTeeSink->write(readInto, - mBytesRead >> Format_frameBitShift(mTeeSink->format())); - } -#endif + int8_t *src = (int8_t *)mRsmpInBuffer + (front * mFrameSize); + if (mChannelCount == activeTrack->mChannelCount) { + memcpy(dst, src, part1 * mFrameSize); + } else if (mChannelCount == 1) { + upmix_to_stereo_i16_from_mono_i16((int16_t *)dst, (const int16_t *)src, + part1); + } else { + downmix_to_mono_i16_from_stereo_i16((int16_t *)dst, (const int16_t *)src, + part1); } + dst += part1 * activeTrack->mFrameSize; + front += part1; + framesIn -= part1; } + activeTrack->mRsmpInFront += framesOut; + } else { // resampling + // FIXME framesInNeeded should really be part of resampler API, and should + // depend on the SRC ratio + // to keep mRsmpInBuffer full so resampler always has sufficient input + size_t framesInNeeded; + // FIXME only re-calculate when it changes, and optimize for common ratios + // Do not precompute in/out because floating point is not associative + // e.g. a*b/c != a*(b/c). + const double in(mSampleRate); + const double out(activeTrack->mSampleRate); + framesInNeeded = ceil(framesOut * in / out) + 1; + ALOGV("need %u frames in to produce %u out given in/out ratio of %.4g", + framesInNeeded, framesOut, in / out); + // Although we theoretically have framesIn in circular buffer, some of those are + // unreleased frames, and thus must be discounted for purpose of budgeting. + size_t unreleased = activeTrack->mRsmpInUnrel; + framesIn = framesIn > unreleased ? framesIn - unreleased : 0; + if (framesIn < framesInNeeded) { + ALOGV("not enough to resample: have %u frames in but need %u in to " + "produce %u out given in/out ratio of %.4g", + framesIn, framesInNeeded, framesOut, in / out); + size_t newFramesOut = framesIn > 0 ? floor((framesIn - 1) * out / in) : 0; + LOG_ALWAYS_FATAL_IF(newFramesOut >= framesOut); + if (newFramesOut == 0) { + break; + } + framesInNeeded = ceil(newFramesOut * in / out) + 1; + ALOGV("now need %u frames in to produce %u out given out/in ratio of %.4g", + framesInNeeded, newFramesOut, out / in); + LOG_ALWAYS_FATAL_IF(framesIn < framesInNeeded); + ALOGV("success 2: have %u frames in and need %u in to produce %u out " + "given in/out ratio of %.4g", + framesIn, framesInNeeded, newFramesOut, in / out); + framesOut = newFramesOut; + } else { + ALOGV("success 1: have %u in and need %u in to produce %u out " + "given in/out ratio of %.4g", + framesIn, framesInNeeded, framesOut, in / out); + } - // resampler accumulates, but we only have one source track - memset(mRsmpOutBuffer, 0, framesOut * FCC_2 * sizeof(int32_t)); - // alter output frame count as if we were expecting stereo samples - if (mChannelCount == 1 && mReqChannelCount == 1) { - framesOut >>= 1; + // reallocate mRsmpOutBuffer as needed; we will grow but never shrink + if (activeTrack->mRsmpOutFrameCount < framesOut) { + // FIXME why does each track need it's own mRsmpOutBuffer? can't they share? + delete[] activeTrack->mRsmpOutBuffer; + // resampler always outputs stereo + activeTrack->mRsmpOutBuffer = new int32_t[framesOut * FCC_2]; + activeTrack->mRsmpOutFrameCount = framesOut; } - mResampler->resample(mRsmpOutBuffer, framesOut, - this /* AudioBufferProvider* */); + + // resampler accumulates, but we only have one source track + memset(activeTrack->mRsmpOutBuffer, 0, framesOut * FCC_2 * sizeof(int32_t)); + activeTrack->mResampler->resample(activeTrack->mRsmpOutBuffer, framesOut, + // FIXME how about having activeTrack implement this interface itself? + activeTrack->mResamplerBufferProvider + /*this*/ /* AudioBufferProvider* */); // ditherAndClamp() works as long as all buffers returned by - // mActiveTrack->getNextBuffer() are 32 bit aligned which should be always true. - if (mChannelCount == 2 && mReqChannelCount == 1) { - // temporarily type pun mRsmpOutBuffer from Q19.12 to int16_t - ditherAndClamp(mRsmpOutBuffer, mRsmpOutBuffer, framesOut); + // activeTrack->getNextBuffer() are 32 bit aligned which should be always true. + if (activeTrack->mChannelCount == 1) { + // temporarily type pun mRsmpOutBuffer from Q4.27 to int16_t + ditherAndClamp(activeTrack->mRsmpOutBuffer, activeTrack->mRsmpOutBuffer, + framesOut); // the resampler always outputs stereo samples: // do post stereo to mono conversion - downmix_to_mono_i16_from_stereo_i16(buffer.i16, (int16_t *)mRsmpOutBuffer, - framesOut); + downmix_to_mono_i16_from_stereo_i16(activeTrack->mSink.i16, + (const int16_t *)activeTrack->mRsmpOutBuffer, framesOut); } else { - ditherAndClamp((int32_t *)buffer.raw, mRsmpOutBuffer, framesOut); + ditherAndClamp((int32_t *)activeTrack->mSink.raw, + activeTrack->mRsmpOutBuffer, framesOut); } // now done with mRsmpOutBuffer } - if (mFramestoDrop == 0) { - mActiveTrack->releaseBuffer(&buffer); + + if (framesOut > 0 && (overrun == OVERRUN_UNKNOWN)) { + overrun = OVERRUN_FALSE; + } + + if (activeTrack->mFramesToDrop == 0) { + if (framesOut > 0) { + activeTrack->mSink.frameCount = framesOut; + activeTrack->releaseBuffer(&activeTrack->mSink); + } } else { - if (mFramestoDrop > 0) { - mFramestoDrop -= buffer.frameCount; - if (mFramestoDrop <= 0) { - clearSyncStartEvent(); + // FIXME could do a partial drop of framesOut + if (activeTrack->mFramesToDrop > 0) { + activeTrack->mFramesToDrop -= framesOut; + if (activeTrack->mFramesToDrop <= 0) { + activeTrack->clearSyncStartEvent(); } } else { - mFramestoDrop += buffer.frameCount; - if (mFramestoDrop >= 0 || mSyncStartEvent == 0 || - mSyncStartEvent->isCancelled()) { + activeTrack->mFramesToDrop += framesOut; + if (activeTrack->mFramesToDrop >= 0 || activeTrack->mSyncStartEvent == 0 || + activeTrack->mSyncStartEvent->isCancelled()) { ALOGW("Synced record %s, session %d, trigger session %d", - (mFramestoDrop >= 0) ? "timed out" : "cancelled", - mActiveTrack->sessionId(), - (mSyncStartEvent != 0) ? mSyncStartEvent->triggerSession() : 0); - clearSyncStartEvent(); + (activeTrack->mFramesToDrop >= 0) ? "timed out" : "cancelled", + activeTrack->sessionId(), + (activeTrack->mSyncStartEvent != 0) ? + activeTrack->mSyncStartEvent->triggerSession() : 0); + activeTrack->clearSyncStartEvent(); } } } - mActiveTrack->clearOverflow(); + + if (framesOut == 0) { + break; + } } - // client isn't retrieving buffers fast enough - else { - if (!mActiveTrack->setOverflow()) { + + switch (overrun) { + case OVERRUN_TRUE: + // client isn't retrieving buffers fast enough + if (!activeTrack->setOverflow()) { nsecs_t now = systemTime(); + // FIXME should lastWarning per track? if ((now - lastWarning) > kWarningThrottleNs) { ALOGW("RecordThread: buffer overflow"); lastWarning = now; } } - // Release the processor for a while before asking for a new buffer. - // This will give the application more chance to read from the buffer and - // clear the overflow. - usleep(kRecordThreadSleepUs); + break; + case OVERRUN_FALSE: + activeTrack->clearOverflow(); + break; + case OVERRUN_UNKNOWN: + break; } + } + +unlock: // enable changes in effect chain unlockEffectChains(effectChains); - effectChains.clear(); + // effectChains doesn't need to be cleared, since it is cleared by destructor at scope end } - standby(); + standbyIfNotAlreadyInStandby(); { Mutex::Autolock _l(mLock); @@ -4641,7 +5463,8 @@ bool AudioFlinger::RecordThread::threadLoop() sp<RecordTrack> track = mTracks[i]; track->invalidate(); } - mActiveTrack.clear(); + mActiveTracks.clear(); + mActiveTracksGen++; mStartStopCond.broadcast(); } @@ -4651,7 +5474,7 @@ bool AudioFlinger::RecordThread::threadLoop() return false; } -void AudioFlinger::RecordThread::standby() +void AudioFlinger::RecordThread::standbyIfNotAlreadyInStandby() { if (!mStandby) { inputStandBy(); @@ -4661,91 +5484,130 @@ void AudioFlinger::RecordThread::standby() void AudioFlinger::RecordThread::inputStandBy() { + // Idle the fast capture if it's currently running + if (mFastCapture != 0) { + FastCaptureStateQueue *sq = mFastCapture->sq(); + FastCaptureState *state = sq->begin(); + if (!(state->mCommand & FastCaptureState::IDLE)) { + state->mCommand = FastCaptureState::COLD_IDLE; + state->mColdFutexAddr = &mFastCaptureFutex; + state->mColdGen++; + mFastCaptureFutex = 0; + sq->end(); + // BLOCK_UNTIL_PUSHED would be insufficient, as we need it to stop doing I/O now + sq->push(FastCaptureStateQueue::BLOCK_UNTIL_ACKED); +#if 0 + if (kUseFastCapture == FastCapture_Dynamic) { + // FIXME + } +#endif +#ifdef AUDIO_WATCHDOG + // FIXME +#endif + } else { + sq->end(false /*didModify*/); + } + } mInput->stream->common.standby(&mInput->stream->common); } -sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createRecordTrack_l( +// RecordThread::createRecordTrack_l() must be called with AudioFlinger::mLock held +sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createRecordTrack_l( const sp<AudioFlinger::Client>& client, uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, - size_t frameCount, + size_t *pFrameCount, int sessionId, + size_t *notificationFrames, int uid, IAudioFlinger::track_flags_t *flags, pid_t tid, status_t *status) { + size_t frameCount = *pFrameCount; sp<RecordTrack> track; status_t lStatus; - lStatus = initCheck(); - if (lStatus != NO_ERROR) { - ALOGE("createRecordTrack_l() audio driver not initialized"); - goto Exit; - } // client expresses a preference for FAST, but we get the final say if (*flags & IAudioFlinger::TRACK_FAST) { if ( - // use case: callback handler and frame count is default or at least as large as HAL - ( - (tid != -1) && - ((frameCount == 0) || - (frameCount >= mFrameCount)) - ) && - // FIXME when record supports non-PCM data, also check for audio_is_linear_pcm(format) - // mono or stereo - ( (channelMask == AUDIO_CHANNEL_OUT_MONO) || - (channelMask == AUDIO_CHANNEL_OUT_STEREO) ) && - // hardware sample rate + // use case: callback handler + (tid != -1) && + // frame count is not specified, or is exactly the pipe depth + ((frameCount == 0) || (frameCount == mPipeFramesP2)) && + // PCM data + audio_is_linear_pcm(format) && + // native format + (format == mFormat) && + // native channel mask + (channelMask == mChannelMask) && + // native hardware sample rate (sampleRate == mSampleRate) && - // record thread has an associated fast recorder - hasFastRecorder() - // FIXME test that RecordThread for this fast track has a capable output HAL - // FIXME add a permission test also? + // record thread has an associated fast capture + hasFastCapture() && + // there are sufficient fast track slots available + mFastTrackAvail ) { - // if frameCount not specified, then it defaults to fast recorder (HAL) frame count - if (frameCount == 0) { - frameCount = mFrameCount * kFastTrackMultiplier; - } - ALOGV("AUDIO_INPUT_FLAG_FAST accepted: frameCount=%d mFrameCount=%d", + ALOGV("AUDIO_INPUT_FLAG_FAST accepted: frameCount=%u mFrameCount=%u", frameCount, mFrameCount); } else { - ALOGV("AUDIO_INPUT_FLAG_FAST denied: frameCount=%d " - "mFrameCount=%d format=%d isLinear=%d channelMask=%#x sampleRate=%u mSampleRate=%u " - "hasFastRecorder=%d tid=%d", - frameCount, mFrameCount, format, - audio_is_linear_pcm(format), - channelMask, sampleRate, mSampleRate, hasFastRecorder(), tid); + ALOGV("AUDIO_INPUT_FLAG_FAST denied: frameCount=%u mFrameCount=%u mPipeFramesP2=%u " + "format=%#x isLinear=%d channelMask=%#x sampleRate=%u mSampleRate=%u " + "hasFastCapture=%d tid=%d mFastTrackAvail=%d", + frameCount, mFrameCount, mPipeFramesP2, + format, audio_is_linear_pcm(format), channelMask, sampleRate, mSampleRate, + hasFastCapture(), tid, mFastTrackAvail); *flags &= ~IAudioFlinger::TRACK_FAST; - // For compatibility with AudioRecord calculation, buffer depth is forced - // to be at least 2 x the record thread frame count and cover audio hardware latency. - // This is probably too conservative, but legacy application code may depend on it. - // If you change this calculation, also review the start threshold which is related. - uint32_t latencyMs = 50; // FIXME mInput->stream->get_latency(mInput->stream); - size_t mNormalFrameCount = 2048; // FIXME - uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate); - if (minBufCount < 2) { - minBufCount = 2; - } - size_t minFrameCount = mNormalFrameCount * minBufCount; - if (frameCount < minFrameCount) { - frameCount = minFrameCount; - } } } - // FIXME use flags and tid similar to createTrack_l() + // compute track buffer size in frames, and suggest the notification frame count + if (*flags & IAudioFlinger::TRACK_FAST) { + // fast track: frame count is exactly the pipe depth + frameCount = mPipeFramesP2; + // ignore requested notificationFrames, and always notify exactly once every HAL buffer + *notificationFrames = mFrameCount; + } else { + // not fast track: max notification period is resampled equivalent of one HAL buffer time + // or 20 ms if there is a fast capture + // TODO This could be a roundupRatio inline, and const + size_t maxNotificationFrames = ((int64_t) (hasFastCapture() ? mSampleRate/50 : mFrameCount) + * sampleRate + mSampleRate - 1) / mSampleRate; + // minimum number of notification periods is at least kMinNotifications, + // and at least kMinMs rounded up to a whole notification period (minNotificationsByMs) + static const size_t kMinNotifications = 3; + static const uint32_t kMinMs = 30; + // TODO This could be a roundupRatio inline + const size_t minFramesByMs = (sampleRate * kMinMs + 1000 - 1) / 1000; + // TODO This could be a roundupRatio inline + const size_t minNotificationsByMs = (minFramesByMs + maxNotificationFrames - 1) / + maxNotificationFrames; + const size_t minFrameCount = maxNotificationFrames * + max(kMinNotifications, minNotificationsByMs); + frameCount = max(frameCount, minFrameCount); + if (*notificationFrames == 0 || *notificationFrames > maxNotificationFrames) { + *notificationFrames = maxNotificationFrames; + } + } + *pFrameCount = frameCount; + + lStatus = initCheck(); + if (lStatus != NO_ERROR) { + ALOGE("createRecordTrack_l() audio driver not initialized"); + goto Exit; + } { // scope for mLock Mutex::Autolock _l(mLock); track = new RecordTrack(this, client, sampleRate, - format, channelMask, frameCount, sessionId, uid); + format, channelMask, frameCount, NULL, sessionId, uid, + *flags, TrackBase::TYPE_DEFAULT); - if (track->getCblk() == 0) { - ALOGE("createRecordTrack_l() no control block"); - lStatus = NO_MEMORY; + lStatus = track->initCheck(); + if (lStatus != NO_ERROR) { + ALOGE("createRecordTrack_l() initCheck failed %d; no control block?", lStatus); // track must be cleared from the caller as the caller has the AF lock goto Exit; } @@ -4764,12 +5626,11 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR sendPrioConfigEvent_l(callingPid, tid, kPriorityAudioApp); } } + lStatus = NO_ERROR; Exit: - if (status) { - *status = lStatus; - } + *status = lStatus; return track; } @@ -4782,82 +5643,86 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac status_t status = NO_ERROR; if (event == AudioSystem::SYNC_EVENT_NONE) { - clearSyncStartEvent(); + recordTrack->clearSyncStartEvent(); } else if (event != AudioSystem::SYNC_EVENT_SAME) { - mSyncStartEvent = mAudioFlinger->createSyncEvent(event, + recordTrack->mSyncStartEvent = mAudioFlinger->createSyncEvent(event, triggerSession, recordTrack->sessionId(), syncStartEventCallback, - this); + recordTrack); // Sync event can be cancelled by the trigger session if the track is not in a // compatible state in which case we start record immediately - if (mSyncStartEvent->isCancelled()) { - clearSyncStartEvent(); + if (recordTrack->mSyncStartEvent->isCancelled()) { + recordTrack->clearSyncStartEvent(); } else { // do not wait for the event for more than AudioSystem::kSyncRecordStartTimeOutMs - mFramestoDrop = - ((AudioSystem::kSyncRecordStartTimeOutMs * mReqSampleRate) / 1000); + recordTrack->mFramesToDrop = - + ((AudioSystem::kSyncRecordStartTimeOutMs * recordTrack->mSampleRate) / 1000); } } { + // This section is a rendezvous between binder thread executing start() and RecordThread AutoMutex lock(mLock); - if (mActiveTrack != 0) { - if (recordTrack != mActiveTrack.get()) { - status = -EBUSY; - } else if (mActiveTrack->mState == TrackBase::PAUSING) { - mActiveTrack->mState = TrackBase::ACTIVE; + if (mActiveTracks.indexOf(recordTrack) >= 0) { + if (recordTrack->mState == TrackBase::PAUSING) { + ALOGV("active record track PAUSING -> ACTIVE"); + recordTrack->mState = TrackBase::ACTIVE; + } else { + ALOGV("active record track state %d", recordTrack->mState); } return status; } - recordTrack->mState = TrackBase::IDLE; - mActiveTrack = recordTrack; - mLock.unlock(); - status_t status = AudioSystem::startInput(mId); - mLock.lock(); - if (status != NO_ERROR) { - mActiveTrack.clear(); - clearSyncStartEvent(); - return status; + // TODO consider other ways of handling this, such as changing the state to :STARTING and + // adding the track to mActiveTracks after returning from AudioSystem::startInput(), + // or using a separate command thread + recordTrack->mState = TrackBase::STARTING_1; + mActiveTracks.add(recordTrack); + mActiveTracksGen++; + status_t status = NO_ERROR; + if (recordTrack->isExternalTrack()) { + mLock.unlock(); + status = AudioSystem::startInput(mId, (audio_session_t)recordTrack->sessionId()); + mLock.lock(); + // FIXME should verify that recordTrack is still in mActiveTracks + if (status != NO_ERROR) { + mActiveTracks.remove(recordTrack); + mActiveTracksGen++; + recordTrack->clearSyncStartEvent(); + ALOGV("RecordThread::start error %d", status); + return status; + } } - mRsmpInIndex = mFrameCount; - mBytesRead = 0; - if (mResampler != NULL) { - mResampler->reset(); + // Catch up with current buffer indices if thread is already running. + // This is what makes a new client discard all buffered data. If the track's mRsmpInFront + // was initialized to some value closer to the thread's mRsmpInFront, then the track could + // see previously buffered data before it called start(), but with greater risk of overrun. + + recordTrack->mRsmpInFront = mRsmpInRear; + recordTrack->mRsmpInUnrel = 0; + // FIXME why reset? + if (recordTrack->mResampler != NULL) { + recordTrack->mResampler->reset(); } - mActiveTrack->mState = TrackBase::RESUMING; + recordTrack->mState = TrackBase::STARTING_2; // signal thread to start - ALOGV("Signal record thread"); mWaitWorkCV.broadcast(); - // do not wait for mStartStopCond if exiting - if (exitPending()) { - mActiveTrack.clear(); - status = INVALID_OPERATION; - goto startError; - } - mStartStopCond.wait(mLock); - if (mActiveTrack == 0) { + if (mActiveTracks.indexOf(recordTrack) < 0) { ALOGV("Record failed to start"); status = BAD_VALUE; goto startError; } - ALOGV("Record started OK"); return status; } startError: - AudioSystem::stopInput(mId); - clearSyncStartEvent(); - return status; -} - -void AudioFlinger::RecordThread::clearSyncStartEvent() -{ - if (mSyncStartEvent != 0) { - mSyncStartEvent->cancel(); + if (recordTrack->isExternalTrack()) { + AudioSystem::stopInput(mId, (audio_session_t)recordTrack->sessionId()); } - mSyncStartEvent.clear(); - mFramestoDrop = 0; + recordTrack->clearSyncStartEvent(); + // FIXME I wonder why we do not reset the state here? + return status; } void AudioFlinger::RecordThread::syncStartEventCallback(const wp<SyncEvent>& event) @@ -4865,46 +5730,42 @@ void AudioFlinger::RecordThread::syncStartEventCallback(const wp<SyncEvent>& eve sp<SyncEvent> strongEvent = event.promote(); if (strongEvent != 0) { - RecordThread *me = (RecordThread *)strongEvent->cookie(); - me->handleSyncStartEvent(strongEvent); - } -} - -void AudioFlinger::RecordThread::handleSyncStartEvent(const sp<SyncEvent>& event) -{ - if (event == mSyncStartEvent) { - // TODO: use actual buffer filling status instead of 2 buffers when info is available - // from audio HAL - mFramestoDrop = mFrameCount * 2; + sp<RefBase> ptr = strongEvent->cookie().promote(); + if (ptr != 0) { + RecordTrack *recordTrack = (RecordTrack *)ptr.get(); + recordTrack->handleSyncStartEvent(strongEvent); + } } } bool AudioFlinger::RecordThread::stop(RecordThread::RecordTrack* recordTrack) { ALOGV("RecordThread::stop"); AutoMutex _l(mLock); - if (recordTrack != mActiveTrack.get() || recordTrack->mState == TrackBase::PAUSING) { + if (mActiveTracks.indexOf(recordTrack) != 0 || recordTrack->mState == TrackBase::PAUSING) { return false; } + // note that threadLoop may still be processing the track at this point [without lock] recordTrack->mState = TrackBase::PAUSING; // do not wait for mStartStopCond if exiting if (exitPending()) { return true; } + // FIXME incorrect usage of wait: no explicit predicate or loop mStartStopCond.wait(mLock); - // if we have been restarted, recordTrack == mActiveTrack.get() here - if (exitPending() || recordTrack != mActiveTrack.get()) { + // if we have been restarted, recordTrack is in mActiveTracks here + if (exitPending() || mActiveTracks.indexOf(recordTrack) != 0) { ALOGV("Record stopped OK"); return true; } return false; } -bool AudioFlinger::RecordThread::isValidSyncEvent(const sp<SyncEvent>& event) const +bool AudioFlinger::RecordThread::isValidSyncEvent(const sp<SyncEvent>& event __unused) const { return false; } -status_t AudioFlinger::RecordThread::setSyncEvent(const sp<SyncEvent>& event) +status_t AudioFlinger::RecordThread::setSyncEvent(const sp<SyncEvent>& event __unused) { #if 0 // This branch is currently dead code, but is preserved in case it will be needed in future if (!isValidSyncEvent(event)) { @@ -4935,7 +5796,7 @@ void AudioFlinger::RecordThread::destroyTrack_l(const sp<RecordTrack>& track) track->terminate(); track->mState = TrackBase::STOPPED; // active tracks are removed by threadLoop() - if (mActiveTrack != track) { + if (mActiveTracks.indexOf(track) < 0) { removeTrack_l(track); } } @@ -4944,6 +5805,10 @@ void AudioFlinger::RecordThread::removeTrack_l(const sp<RecordTrack>& track) { mTracks.remove(track); // need anything related to effects here? + if (track->isFastTrack()) { + ALOG_ASSERT(!mFastTrackAvail); + mFastTrackAvail = true; + } } void AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args) @@ -4955,217 +5820,236 @@ void AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args) void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector<String16>& args) { - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; + dprintf(fd, "\nInput thread %p:\n", this); - snprintf(buffer, SIZE, "\nInput thread %p internals\n", this); - result.append(buffer); - - if (mActiveTrack != 0) { - snprintf(buffer, SIZE, "In index: %zu\n", mRsmpInIndex); - result.append(buffer); - snprintf(buffer, SIZE, "Buffer size: %zu bytes\n", mBufferSize); - result.append(buffer); - snprintf(buffer, SIZE, "Resampling: %d\n", (mResampler != NULL)); - result.append(buffer); - snprintf(buffer, SIZE, "Out channel count: %u\n", mReqChannelCount); - result.append(buffer); - snprintf(buffer, SIZE, "Out sample rate: %u\n", mReqSampleRate); - result.append(buffer); + if (mActiveTracks.size() > 0) { + dprintf(fd, " Buffer size: %zu bytes\n", mBufferSize); } else { - result.append("No active record client\n"); + dprintf(fd, " No active record clients\n"); } - - write(fd, result.string(), result.size()); + dprintf(fd, " Fast capture thread: %s\n", hasFastCapture() ? "yes" : "no"); + dprintf(fd, " Fast track available: %s\n", mFastTrackAvail ? "yes" : "no"); dumpBase(fd, args); } -void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args) +void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args __unused) { const size_t SIZE = 256; char buffer[SIZE]; String8 result; - snprintf(buffer, SIZE, "Input thread %p tracks\n", this); - result.append(buffer); - RecordTrack::appendDumpHeader(result); - for (size_t i = 0; i < mTracks.size(); ++i) { - sp<RecordTrack> track = mTracks[i]; - if (track != 0) { - track->dump(buffer, SIZE); - result.append(buffer); + size_t numtracks = mTracks.size(); + size_t numactive = mActiveTracks.size(); + size_t numactiveseen = 0; + dprintf(fd, " %d Tracks", numtracks); + if (numtracks) { + dprintf(fd, " of which %d are active\n", numactive); + RecordTrack::appendDumpHeader(result); + for (size_t i = 0; i < numtracks ; ++i) { + sp<RecordTrack> track = mTracks[i]; + if (track != 0) { + bool active = mActiveTracks.indexOf(track) >= 0; + if (active) { + numactiveseen++; + } + track->dump(buffer, SIZE, active); + result.append(buffer); + } } + } else { + dprintf(fd, "\n"); } - if (mActiveTrack != 0) { - snprintf(buffer, SIZE, "\nInput thread %p active tracks\n", this); + if (numactiveseen != numactive) { + snprintf(buffer, SIZE, " The following tracks are in the active list but" + " not in the track list\n"); result.append(buffer); RecordTrack::appendDumpHeader(result); - mActiveTrack->dump(buffer, SIZE); - result.append(buffer); + for (size_t i = 0; i < numactive; ++i) { + sp<RecordTrack> track = mActiveTracks[i]; + if (mTracks.indexOf(track) < 0) { + track->dump(buffer, SIZE, true); + result.append(buffer); + } + } } write(fd, result.string(), result.size()); } // AudioBufferProvider interface -status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts) -{ - size_t framesReq = buffer->frameCount; - size_t framesReady = mFrameCount - mRsmpInIndex; - int channelCount; - - if (framesReady == 0) { - mBytesRead = mInput->stream->read(mInput->stream, mRsmpInBuffer, mBufferSize); - if (mBytesRead <= 0) { - if ((mBytesRead < 0) && (mActiveTrack->mState == TrackBase::ACTIVE)) { - ALOGE("RecordThread::getNextBuffer() Error reading audio input"); - // Force input into standby so that it tries to - // recover at next read attempt - inputStandBy(); - usleep(kRecordThreadSleepUs); - } - buffer->raw = NULL; - buffer->frameCount = 0; - return NOT_ENOUGH_DATA; - } - mRsmpInIndex = 0; - framesReady = mFrameCount; - } - - if (framesReq > framesReady) { - framesReq = framesReady; - } - - if (mChannelCount == 1 && mReqChannelCount == 2) { - channelCount = 1; - } else { - channelCount = 2; - } - buffer->raw = mRsmpInBuffer + mRsmpInIndex * channelCount; - buffer->frameCount = framesReq; +status_t AudioFlinger::RecordThread::ResamplerBufferProvider::getNextBuffer( + AudioBufferProvider::Buffer* buffer, int64_t pts __unused) +{ + RecordTrack *activeTrack = mRecordTrack; + sp<ThreadBase> threadBase = activeTrack->mThread.promote(); + if (threadBase == 0) { + buffer->frameCount = 0; + buffer->raw = NULL; + return NOT_ENOUGH_DATA; + } + RecordThread *recordThread = (RecordThread *) threadBase.get(); + int32_t rear = recordThread->mRsmpInRear; + int32_t front = activeTrack->mRsmpInFront; + ssize_t filled = rear - front; + // FIXME should not be P2 (don't want to increase latency) + // FIXME if client not keeping up, discard + LOG_ALWAYS_FATAL_IF(!(0 <= filled && (size_t) filled <= recordThread->mRsmpInFrames)); + // 'filled' may be non-contiguous, so return only the first contiguous chunk + front &= recordThread->mRsmpInFramesP2 - 1; + size_t part1 = recordThread->mRsmpInFramesP2 - front; + if (part1 > (size_t) filled) { + part1 = filled; + } + size_t ask = buffer->frameCount; + ALOG_ASSERT(ask > 0); + if (part1 > ask) { + part1 = ask; + } + if (part1 == 0) { + // Higher-level should keep mRsmpInBuffer full, and not call resampler if empty + LOG_ALWAYS_FATAL("RecordThread::getNextBuffer() starved"); + buffer->raw = NULL; + buffer->frameCount = 0; + activeTrack->mRsmpInUnrel = 0; + return NOT_ENOUGH_DATA; + } + + buffer->raw = recordThread->mRsmpInBuffer + front * recordThread->mChannelCount; + buffer->frameCount = part1; + activeTrack->mRsmpInUnrel = part1; return NO_ERROR; } // AudioBufferProvider interface -void AudioFlinger::RecordThread::releaseBuffer(AudioBufferProvider::Buffer* buffer) +void AudioFlinger::RecordThread::ResamplerBufferProvider::releaseBuffer( + AudioBufferProvider::Buffer* buffer) { - mRsmpInIndex += buffer->frameCount; + RecordTrack *activeTrack = mRecordTrack; + size_t stepCount = buffer->frameCount; + if (stepCount == 0) { + return; + } + ALOG_ASSERT(stepCount <= activeTrack->mRsmpInUnrel); + activeTrack->mRsmpInUnrel -= stepCount; + activeTrack->mRsmpInFront += stepCount; + buffer->raw = NULL; buffer->frameCount = 0; } -bool AudioFlinger::RecordThread::checkForNewParameters_l() +bool AudioFlinger::RecordThread::checkForNewParameter_l(const String8& keyValuePair, + status_t& status) { bool reconfig = false; - while (!mNewParameters.isEmpty()) { - status_t status = NO_ERROR; - String8 keyValuePair = mNewParameters[0]; - AudioParameter param = AudioParameter(keyValuePair); - int value; - audio_format_t reqFormat = mFormat; - uint32_t reqSamplingRate = mReqSampleRate; - uint32_t reqChannelCount = mReqChannelCount; - - if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) { - reqSamplingRate = value; + status = NO_ERROR; + + audio_format_t reqFormat = mFormat; + uint32_t samplingRate = mSampleRate; + audio_channel_mask_t channelMask = audio_channel_in_mask_from_count(mChannelCount); + + AudioParameter param = AudioParameter(keyValuePair); + int value; + // TODO Investigate when this code runs. Check with audio policy when a sample rate and + // channel count change can be requested. Do we mandate the first client defines the + // HAL sampling rate and channel count or do we allow changes on the fly? + if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) { + samplingRate = value; + reconfig = true; + } + if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) { + if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) { + status = BAD_VALUE; + } else { + reqFormat = (audio_format_t) value; reconfig = true; } - if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) { - if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) { - status = BAD_VALUE; - } else { - reqFormat = (audio_format_t) value; - reconfig = true; - } + } + if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) { + audio_channel_mask_t mask = (audio_channel_mask_t) value; + if (mask != AUDIO_CHANNEL_IN_MONO && mask != AUDIO_CHANNEL_IN_STEREO) { + status = BAD_VALUE; + } else { + channelMask = mask; + reconfig = true; } - if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) { - reqChannelCount = popcount(value); + } + if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { + // do not accept frame count changes if tracks are open as the track buffer + // size depends on frame count and correct behavior would not be guaranteed + // if frame count is changed after track creation + if (mActiveTracks.size() > 0) { + status = INVALID_OPERATION; + } else { reconfig = true; } - if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { - // do not accept frame count changes if tracks are open as the track buffer - // size depends on frame count and correct behavior would not be guaranteed - // if frame count is changed after track creation - if (mActiveTrack != 0) { - status = INVALID_OPERATION; - } else { - reconfig = true; - } + } + if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) { + // forward device change to effects that have requested to be + // aware of attached audio device. + for (size_t i = 0; i < mEffectChains.size(); i++) { + mEffectChains[i]->setDevice_l(value); } - if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) { - // forward device change to effects that have requested to be - // aware of attached audio device. - for (size_t i = 0; i < mEffectChains.size(); i++) { - mEffectChains[i]->setDevice_l(value); - } - // store input device and output device but do not forward output device to audio HAL. - // Note that status is ignored by the caller for output device - // (see AudioFlinger::setParameters() - if (audio_is_output_devices(value)) { - mOutDevice = value; - status = BAD_VALUE; - } else { - mInDevice = value; - // disable AEC and NS if the device is a BT SCO headset supporting those - // pre processings - if (mTracks.size() > 0) { - bool suspend = audio_is_bluetooth_sco_device(mInDevice) && - mAudioFlinger->btNrecIsOff(); - for (size_t i = 0; i < mTracks.size(); i++) { - sp<RecordTrack> track = mTracks[i]; - setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId()); - setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId()); - } + // store input device and output device but do not forward output device to audio HAL. + // Note that status is ignored by the caller for output device + // (see AudioFlinger::setParameters() + if (audio_is_output_devices(value)) { + mOutDevice = value; + status = BAD_VALUE; + } else { + mInDevice = value; + // disable AEC and NS if the device is a BT SCO headset supporting those + // pre processings + if (mTracks.size() > 0) { + bool suspend = audio_is_bluetooth_sco_device(mInDevice) && + mAudioFlinger->btNrecIsOff(); + for (size_t i = 0; i < mTracks.size(); i++) { + sp<RecordTrack> track = mTracks[i]; + setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId()); + setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId()); } } } - if (param.getInt(String8(AudioParameter::keyInputSource), value) == NO_ERROR && - mAudioSource != (audio_source_t)value) { - // forward device change to effects that have requested to be - // aware of attached audio device. - for (size_t i = 0; i < mEffectChains.size(); i++) { - mEffectChains[i]->setAudioSource_l((audio_source_t)value); - } - mAudioSource = (audio_source_t)value; + } + if (param.getInt(String8(AudioParameter::keyInputSource), value) == NO_ERROR && + mAudioSource != (audio_source_t)value) { + // forward device change to effects that have requested to be + // aware of attached audio device. + for (size_t i = 0; i < mEffectChains.size(); i++) { + mEffectChains[i]->setAudioSource_l((audio_source_t)value); } - if (status == NO_ERROR) { + mAudioSource = (audio_source_t)value; + } + + if (status == NO_ERROR) { + status = mInput->stream->common.set_parameters(&mInput->stream->common, + keyValuePair.string()); + if (status == INVALID_OPERATION) { + inputStandBy(); status = mInput->stream->common.set_parameters(&mInput->stream->common, keyValuePair.string()); - if (status == INVALID_OPERATION) { - inputStandBy(); - status = mInput->stream->common.set_parameters(&mInput->stream->common, - keyValuePair.string()); + } + if (reconfig) { + if (status == BAD_VALUE && + reqFormat == mInput->stream->common.get_format(&mInput->stream->common) && + reqFormat == AUDIO_FORMAT_PCM_16_BIT && + (mInput->stream->common.get_sample_rate(&mInput->stream->common) + <= (2 * samplingRate)) && + audio_channel_count_from_in_mask( + mInput->stream->common.get_channels(&mInput->stream->common)) <= FCC_2 && + (channelMask == AUDIO_CHANNEL_IN_MONO || + channelMask == AUDIO_CHANNEL_IN_STEREO)) { + status = NO_ERROR; } - if (reconfig) { - if (status == BAD_VALUE && - reqFormat == mInput->stream->common.get_format(&mInput->stream->common) && - reqFormat == AUDIO_FORMAT_PCM_16_BIT && - (mInput->stream->common.get_sample_rate(&mInput->stream->common) - <= (2 * reqSamplingRate)) && - popcount(mInput->stream->common.get_channels(&mInput->stream->common)) - <= FCC_2 && - (reqChannelCount <= FCC_2)) { - status = NO_ERROR; - } - if (status == NO_ERROR) { - readInputParameters(); - sendIoConfigEvent_l(AudioSystem::INPUT_CONFIG_CHANGED); - } + if (status == NO_ERROR) { + readInputParameters_l(); + sendIoConfigEvent_l(AudioSystem::INPUT_CONFIG_CHANGED); } } - - mNewParameters.removeAt(0); - - mParamStatus = status; - mParamCond.signal(); - // wait for condition with time out in case the thread calling ThreadBase::setParameters() - // already timed out waiting for the status and will never signal the condition. - mWaitWorkCV.waitRelative(mLock, kSetParametersTimeoutNs); } + return reconfig; } @@ -5182,9 +6066,9 @@ String8 AudioFlinger::RecordThread::getParameters(const String8& keys) return out_s8; } -void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param) { +void AudioFlinger::RecordThread::audioConfigChanged(int event, int param __unused) { AudioSystem::OutputDescriptor desc; - void *param2 = NULL; + const void *param2 = NULL; switch (event) { case AudioSystem::INPUT_OPENED: @@ -5201,56 +6085,47 @@ void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param) { default: break; } - mAudioFlinger->audioConfigChanged_l(event, mId, param2); + mAudioFlinger->audioConfigChanged(event, mId, param2); } -void AudioFlinger::RecordThread::readInputParameters() +void AudioFlinger::RecordThread::readInputParameters_l() { - delete[] mRsmpInBuffer; - // mRsmpInBuffer is always assigned a new[] below - delete[] mRsmpOutBuffer; - mRsmpOutBuffer = NULL; - delete mResampler; - mResampler = NULL; - mSampleRate = mInput->stream->common.get_sample_rate(&mInput->stream->common); mChannelMask = mInput->stream->common.get_channels(&mInput->stream->common); - mChannelCount = popcount(mChannelMask); - mFormat = mInput->stream->common.get_format(&mInput->stream->common); + mChannelCount = audio_channel_count_from_in_mask(mChannelMask); + mHALFormat = mInput->stream->common.get_format(&mInput->stream->common); + mFormat = mHALFormat; if (mFormat != AUDIO_FORMAT_PCM_16_BIT) { - ALOGE("HAL format %d not supported; must be AUDIO_FORMAT_PCM_16_BIT", mFormat); + ALOGE("HAL format %#x not supported; must be AUDIO_FORMAT_PCM_16_BIT", mFormat); } - mFrameSize = audio_stream_frame_size(&mInput->stream->common); + mFrameSize = audio_stream_in_frame_size(mInput->stream); mBufferSize = mInput->stream->common.get_buffer_size(&mInput->stream->common); mFrameCount = mBufferSize / mFrameSize; - mRsmpInBuffer = new int16_t[mFrameCount * mChannelCount]; + // This is the formula for calculating the temporary buffer size. + // With 7 HAL buffers, we can guarantee ability to down-sample the input by ratio of 6:1 to + // 1 full output buffer, regardless of the alignment of the available input. + // The value is somewhat arbitrary, and could probably be even larger. + // A larger value should allow more old data to be read after a track calls start(), + // without increasing latency. + mRsmpInFrames = mFrameCount * 7; + mRsmpInFramesP2 = roundup(mRsmpInFrames); + delete[] mRsmpInBuffer; - if (mSampleRate != mReqSampleRate && mChannelCount <= FCC_2 && mReqChannelCount <= FCC_2) - { - int channelCount; - // optimization: if mono to mono, use the resampler in stereo to stereo mode to avoid - // stereo to mono post process as the resampler always outputs stereo. - if (mChannelCount == 1 && mReqChannelCount == 2) { - channelCount = 1; - } else { - channelCount = 2; - } - mResampler = AudioResampler::create(16, channelCount, mReqSampleRate); - mResampler->setSampleRate(mSampleRate); - mResampler->setVolume(AudioMixer::UNITY_GAIN, AudioMixer::UNITY_GAIN); - mRsmpOutBuffer = new int32_t[mFrameCount * FCC_2]; + // TODO optimize audio capture buffer sizes ... + // Here we calculate the size of the sliding buffer used as a source + // for resampling. mRsmpInFramesP2 is currently roundup(mFrameCount * 7). + // For current HAL frame counts, this is usually 2048 = 40 ms. It would + // be better to have it derived from the pipe depth in the long term. + // The current value is higher than necessary. However it should not add to latency. - // optmization: if mono to mono, alter input frame count as if we were inputing - // stereo samples - if (mChannelCount == 1 && mReqChannelCount == 1) { - mFrameCount >>= 1; - } + // Over-allocate beyond mRsmpInFramesP2 to permit a HAL read past end of buffer + mRsmpInBuffer = new int16_t[(mRsmpInFramesP2 + mFrameCount - 1) * mChannelCount]; - } - mRsmpInIndex = mFrameCount; + // AudioRecord mSampleRate and mChannelCount are constant due to AudioRecord API constraints. + // But if thread's mSampleRate or mChannelCount changes, how will that affect active tracks? } -unsigned int AudioFlinger::RecordThread::getInputFramesLost() +uint32_t AudioFlinger::RecordThread::getInputFramesLost() { Mutex::Autolock _l(mLock); if (initCheck() != NO_ERROR) { @@ -5339,4 +6214,80 @@ size_t AudioFlinger::RecordThread::removeEffectChain_l(const sp<EffectChain>& ch return 0; } +status_t AudioFlinger::RecordThread::createAudioPatch_l(const struct audio_patch *patch, + audio_patch_handle_t *handle) +{ + status_t status = NO_ERROR; + if (mInput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) { + // store new device and send to effects + mInDevice = patch->sources[0].ext.device.type; + for (size_t i = 0; i < mEffectChains.size(); i++) { + mEffectChains[i]->setDevice_l(mInDevice); + } + + // disable AEC and NS if the device is a BT SCO headset supporting those + // pre processings + if (mTracks.size() > 0) { + bool suspend = audio_is_bluetooth_sco_device(mInDevice) && + mAudioFlinger->btNrecIsOff(); + for (size_t i = 0; i < mTracks.size(); i++) { + sp<RecordTrack> track = mTracks[i]; + setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId()); + setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId()); + } + } + + // store new source and send to effects + if (mAudioSource != patch->sinks[0].ext.mix.usecase.source) { + mAudioSource = patch->sinks[0].ext.mix.usecase.source; + for (size_t i = 0; i < mEffectChains.size(); i++) { + mEffectChains[i]->setAudioSource_l(mAudioSource); + } + } + + audio_hw_device_t *hwDevice = mInput->audioHwDev->hwDevice(); + status = hwDevice->create_audio_patch(hwDevice, + patch->num_sources, + patch->sources, + patch->num_sinks, + patch->sinks, + handle); + } else { + ALOG_ASSERT(false, "createAudioPatch_l() called on a pre 3.0 HAL"); + } + return status; +} + +status_t AudioFlinger::RecordThread::releaseAudioPatch_l(const audio_patch_handle_t handle) +{ + status_t status = NO_ERROR; + if (mInput->audioHwDev->version() >= AUDIO_DEVICE_API_VERSION_3_0) { + audio_hw_device_t *hwDevice = mInput->audioHwDev->hwDevice(); + status = hwDevice->release_audio_patch(hwDevice, handle); + } else { + ALOG_ASSERT(false, "releaseAudioPatch_l() called on a pre 3.0 HAL"); + } + return status; +} + +void AudioFlinger::RecordThread::addPatchRecord(const sp<PatchRecord>& record) +{ + Mutex::Autolock _l(mLock); + mTracks.add(record); +} + +void AudioFlinger::RecordThread::deletePatchRecord(const sp<PatchRecord>& record) +{ + Mutex::Autolock _l(mLock); + destroyTrack_l(record); +} + +void AudioFlinger::RecordThread::getAudioPortConfig(struct audio_port_config *config) +{ + ThreadBase::getAudioPortConfig(config); + config->role = AUDIO_PORT_ROLE_SINK; + config->ext.mix.hw_module = mInput->audioHwDev->handle(); + config->ext.mix.usecase.source = mAudioSource; +} + }; // namespace android diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index a2fb874..648502b 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -36,6 +36,8 @@ public: audio_devices_t outDevice, audio_devices_t inDevice, type_t type); virtual ~ThreadBase(); + virtual status_t readyToRun(); + void dumpBase(int fd, const Vector<String16>& args); void dumpEffectChains(int fd, const Vector<String16>& args); @@ -44,60 +46,169 @@ public: // base for record and playback enum { CFG_EVENT_IO, - CFG_EVENT_PRIO + CFG_EVENT_PRIO, + CFG_EVENT_SET_PARAMETER, + CFG_EVENT_CREATE_AUDIO_PATCH, + CFG_EVENT_RELEASE_AUDIO_PATCH, + }; + + class ConfigEventData: public RefBase { + public: + virtual ~ConfigEventData() {} + + virtual void dump(char *buffer, size_t size) = 0; + protected: + ConfigEventData() {} }; - class ConfigEvent { + // Config event sequence by client if status needed (e.g binder thread calling setParameters()): + // 1. create SetParameterConfigEvent. This sets mWaitStatus in config event + // 2. Lock mLock + // 3. Call sendConfigEvent_l(): Append to mConfigEvents and mWaitWorkCV.signal + // 4. sendConfigEvent_l() reads status from event->mStatus; + // 5. sendConfigEvent_l() returns status + // 6. Unlock + // + // Parameter sequence by server: threadLoop calling processConfigEvents_l(): + // 1. Lock mLock + // 2. If there is an entry in mConfigEvents proceed ... + // 3. Read first entry in mConfigEvents + // 4. Remove first entry from mConfigEvents + // 5. Process + // 6. Set event->mStatus + // 7. event->mCond.signal + // 8. Unlock + + class ConfigEvent: public RefBase { public: - ConfigEvent(int type) : mType(type) {} virtual ~ConfigEvent() {} - int type() const { return mType; } + void dump(char *buffer, size_t size) { mData->dump(buffer, size); } - virtual void dump(char *buffer, size_t size) = 0; + const int mType; // event type e.g. CFG_EVENT_IO + Mutex mLock; // mutex associated with mCond + Condition mCond; // condition for status return + status_t mStatus; // status communicated to sender + bool mWaitStatus; // true if sender is waiting for status + sp<ConfigEventData> mData; // event specific parameter data - private: - const int mType; + protected: + ConfigEvent(int type) : mType(type), mStatus(NO_ERROR), mWaitStatus(false), mData(NULL) {} }; - class IoConfigEvent : public ConfigEvent { + class IoConfigEventData : public ConfigEventData { public: - IoConfigEvent(int event, int param) : - ConfigEvent(CFG_EVENT_IO), mEvent(event), mParam(event) {} - virtual ~IoConfigEvent() {} - - int event() const { return mEvent; } - int param() const { return mParam; } + IoConfigEventData(int event, int param) : + mEvent(event), mParam(param) {} virtual void dump(char *buffer, size_t size) { snprintf(buffer, size, "IO event: event %d, param %d\n", mEvent, mParam); } - private: const int mEvent; const int mParam; }; - class PrioConfigEvent : public ConfigEvent { + class IoConfigEvent : public ConfigEvent { public: - PrioConfigEvent(pid_t pid, pid_t tid, int32_t prio) : - ConfigEvent(CFG_EVENT_PRIO), mPid(pid), mTid(tid), mPrio(prio) {} - virtual ~PrioConfigEvent() {} + IoConfigEvent(int event, int param) : + ConfigEvent(CFG_EVENT_IO) { + mData = new IoConfigEventData(event, param); + } + virtual ~IoConfigEvent() {} + }; - pid_t pid() const { return mPid; } - pid_t tid() const { return mTid; } - int32_t prio() const { return mPrio; } + class PrioConfigEventData : public ConfigEventData { + public: + PrioConfigEventData(pid_t pid, pid_t tid, int32_t prio) : + mPid(pid), mTid(tid), mPrio(prio) {} virtual void dump(char *buffer, size_t size) { snprintf(buffer, size, "Prio event: pid %d, tid %d, prio %d\n", mPid, mTid, mPrio); } - private: const pid_t mPid; const pid_t mTid; const int32_t mPrio; }; + class PrioConfigEvent : public ConfigEvent { + public: + PrioConfigEvent(pid_t pid, pid_t tid, int32_t prio) : + ConfigEvent(CFG_EVENT_PRIO) { + mData = new PrioConfigEventData(pid, tid, prio); + } + virtual ~PrioConfigEvent() {} + }; + + class SetParameterConfigEventData : public ConfigEventData { + public: + SetParameterConfigEventData(String8 keyValuePairs) : + mKeyValuePairs(keyValuePairs) {} + + virtual void dump(char *buffer, size_t size) { + snprintf(buffer, size, "KeyValue: %s\n", mKeyValuePairs.string()); + } + + const String8 mKeyValuePairs; + }; + + class SetParameterConfigEvent : public ConfigEvent { + public: + SetParameterConfigEvent(String8 keyValuePairs) : + ConfigEvent(CFG_EVENT_SET_PARAMETER) { + mData = new SetParameterConfigEventData(keyValuePairs); + mWaitStatus = true; + } + virtual ~SetParameterConfigEvent() {} + }; + + class CreateAudioPatchConfigEventData : public ConfigEventData { + public: + CreateAudioPatchConfigEventData(const struct audio_patch patch, + audio_patch_handle_t handle) : + mPatch(patch), mHandle(handle) {} + + virtual void dump(char *buffer, size_t size) { + snprintf(buffer, size, "Patch handle: %u\n", mHandle); + } + + const struct audio_patch mPatch; + audio_patch_handle_t mHandle; + }; + + class CreateAudioPatchConfigEvent : public ConfigEvent { + public: + CreateAudioPatchConfigEvent(const struct audio_patch patch, + audio_patch_handle_t handle) : + ConfigEvent(CFG_EVENT_CREATE_AUDIO_PATCH) { + mData = new CreateAudioPatchConfigEventData(patch, handle); + mWaitStatus = true; + } + virtual ~CreateAudioPatchConfigEvent() {} + }; + + class ReleaseAudioPatchConfigEventData : public ConfigEventData { + public: + ReleaseAudioPatchConfigEventData(const audio_patch_handle_t handle) : + mHandle(handle) {} + + virtual void dump(char *buffer, size_t size) { + snprintf(buffer, size, "Patch handle: %u\n", mHandle); + } + + audio_patch_handle_t mHandle; + }; + + class ReleaseAudioPatchConfigEvent : public ConfigEvent { + public: + ReleaseAudioPatchConfigEvent(const audio_patch_handle_t handle) : + ConfigEvent(CFG_EVENT_RELEASE_AUDIO_PATCH) { + mData = new ReleaseAudioPatchConfigEventData(handle); + mWaitStatus = true; + } + virtual ~ReleaseAudioPatchConfigEvent() {} + }; class PMDeathRecipient : public IBinder::DeathRecipient { public: @@ -122,9 +233,9 @@ public: // dynamic externally-visible uint32_t sampleRate() const { return mSampleRate; } - uint32_t channelCount() const { return mChannelCount; } audio_channel_mask_t channelMask() const { return mChannelMask; } - audio_format_t format() const { return mFormat; } + audio_format_t format() const { return mHALFormat; } + uint32_t channelCount() const { return mChannelCount; } // Called by AudioFlinger::frameCount(audio_io_handle_t output) and effects, // and returns the [normal mix] buffer's frame count. virtual size_t frameCount() const = 0; @@ -133,14 +244,29 @@ public: // Should be "virtual status_t requestExitAndWait()" and override same // method in Thread, but Thread::requestExitAndWait() is not yet virtual. void exit(); - virtual bool checkForNewParameters_l() = 0; + virtual bool checkForNewParameter_l(const String8& keyValuePair, + status_t& status) = 0; virtual status_t setParameters(const String8& keyValuePairs); virtual String8 getParameters(const String8& keys) = 0; - virtual void audioConfigChanged_l(int event, int param = 0) = 0; + virtual void audioConfigChanged(int event, int param = 0) = 0; + // sendConfigEvent_l() must be called with ThreadBase::mLock held + // Can temporarily release the lock if waiting for a reply from + // processConfigEvents_l(). + status_t sendConfigEvent_l(sp<ConfigEvent>& event); void sendIoConfigEvent(int event, int param = 0); void sendIoConfigEvent_l(int event, int param = 0); void sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio); - void processConfigEvents(); + status_t sendSetParameterConfigEvent_l(const String8& keyValuePair); + status_t sendCreateAudioPatchConfigEvent(const struct audio_patch *patch, + audio_patch_handle_t *handle); + status_t sendReleaseAudioPatchConfigEvent(audio_patch_handle_t handle); + void processConfigEvents_l(); + virtual void cacheParameters_l() = 0; + virtual status_t createAudioPatch_l(const struct audio_patch *patch, + audio_patch_handle_t *handle) = 0; + virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle) = 0; + virtual void getAudioPortConfig(struct audio_port_config *config) = 0; + // see note at declaration of mStandby, mOutDevice and mInDevice bool standby() const { return mStandby; } @@ -156,7 +282,7 @@ public: int sessionId, effect_descriptor_t *desc, int *enabled, - status_t *status); + status_t *status /*non-NULL*/); void disconnectEffect(const sp< EffectModule>& effect, EffectHandle *handle, bool unpinIfLast); @@ -198,13 +324,13 @@ public: // effect void removeEffect_l(const sp< EffectModule>& effect); // detach all tracks connected to an auxiliary effect - virtual void detachAuxEffect_l(int effectId) {} + virtual void detachAuxEffect_l(int effectId __unused) {} // returns either EFFECT_SESSION if effects on this audio session exist in one // chain, or TRACK_SESSION if tracks on this audio session exist, or both virtual uint32_t hasAudioSession(int sessionId) const = 0; // the value returned by default implementation is not important as the // strategy is only meaningful for PlaybackThread which implements this method - virtual uint32_t getStrategyForSession_l(int sessionId) { return 0; } + virtual uint32_t getStrategyForSession_l(int sessionId __unused) { return 0; } // suspend or restore effect according to the type of effect passed. a NULL // type pointer means suspend all effects in the session @@ -223,6 +349,15 @@ public: virtual status_t setSyncEvent(const sp<SyncEvent>& event) = 0; virtual bool isValidSyncEvent(const sp<SyncEvent>& event) const = 0; + // Return a reference to a per-thread heap which can be used to allocate IMemory + // objects that will be read-only to client processes, read/write to mediaserver, + // and shared by all client processes of the thread. + // The heap is per-thread rather than common across all threads, because + // clients can't be trusted not to modify the offset of the IMemory they receive. + // If a thread does not have such a heap, this method returns 0. + virtual sp<MemoryDealer> readOnlyHeap() const { return 0; } + + virtual sp<IMemory> pipeMemory() const { return 0; } mutable Mutex mLock; @@ -267,48 +402,29 @@ protected: const sp<AudioFlinger> mAudioFlinger; - // updated by PlaybackThread::readOutputParameters() or - // RecordThread::readInputParameters() + // updated by PlaybackThread::readOutputParameters_l() or + // RecordThread::readInputParameters_l() uint32_t mSampleRate; size_t mFrameCount; // output HAL, direct output, record audio_channel_mask_t mChannelMask; uint32_t mChannelCount; size_t mFrameSize; - audio_format_t mFormat; - - // Parameter sequence by client: binder thread calling setParameters(): - // 1. Lock mLock - // 2. Append to mNewParameters - // 3. mWaitWorkCV.signal - // 4. mParamCond.waitRelative with timeout - // 5. read mParamStatus - // 6. mWaitWorkCV.signal - // 7. Unlock - // - // Parameter sequence by server: threadLoop calling checkForNewParameters_l(): - // 1. Lock mLock - // 2. If there is an entry in mNewParameters proceed ... - // 2. Read first entry in mNewParameters - // 3. Process - // 4. Remove first entry from mNewParameters - // 5. Set mParamStatus - // 6. mParamCond.signal - // 7. mWaitWorkCV.wait with timeout (this is to avoid overwriting mParamStatus) - // 8. Unlock - Condition mParamCond; - Vector<String8> mNewParameters; - status_t mParamStatus; - - // vector owns each ConfigEvent *, so must delete after removing - Vector<ConfigEvent *> mConfigEvents; + audio_format_t mFormat; // Source format for Recording and + // Sink format for Playback. + // Sink format may be different than + // HAL format if Fastmixer is used. + audio_format_t mHALFormat; + size_t mBufferSize; // HAL buffer size for read() or write() + + Vector< sp<ConfigEvent> > mConfigEvents; // These fields are written and read by thread itself without lock or barrier, - // and read by other threads without lock or barrier via standby() , outDevice() + // and read by other threads without lock or barrier via standby(), outDevice() // and inDevice(). // Because of the absence of a lock or barrier, any other thread that reads // these fields must use the information in isolation, or be prepared to deal // with possibility that it might be inconsistent with other information. - bool mStandby; // Whether thread is currently in standby. + bool mStandby; // Whether thread is currently in standby. audio_devices_t mOutDevice; // output device audio_devices_t mInDevice; // input device audio_source_t mAudioSource; // (see audio.h, audio_source_t) @@ -358,7 +474,6 @@ public: void dump(int fd, const Vector<String16>& args); // Thread virtuals - virtual status_t readyToRun(); virtual bool threadLoop(); // RefBase @@ -391,7 +506,7 @@ protected: virtual bool waitingAsyncCallback(); virtual bool waitingAsyncCallback_l(); virtual bool shouldStandby_l(); - + virtual void onAddNewTrack_l(); // ThreadBase virtuals virtual void preExit(); @@ -419,13 +534,13 @@ public: uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, - size_t frameCount, + size_t *pFrameCount, const sp<IMemory>& sharedBuffer, int sessionId, IAudioFlinger::track_flags_t *flags, pid_t tid, int uid, - status_t *status); + status_t *status /*non-NULL*/); AudioStreamOut* getOutput() const; AudioStreamOut* clearOutput(); @@ -445,9 +560,13 @@ public: { return android_atomic_acquire_load(&mSuspended) > 0; } virtual String8 getParameters(const String8& keys); - virtual void audioConfigChanged_l(int event, int param = 0); + virtual void audioConfigChanged(int event, int param = 0); status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames); - int16_t *mixBuffer() const { return mMixBuffer; }; + // FIXME rename mixBuffer() to sinkBuffer() and remove int16_t* dependency. + // Consider also removing and passing an explicit mMainBuffer initialization + // parameter to AF::PlaybackThread::Track::Track(). + int16_t *mixBuffer() const { + return reinterpret_cast<int16_t *>(mSinkBuffer); }; virtual void detachAuxEffect_l(int effectId); status_t attachAuxEffect(const sp<AudioFlinger::PlaybackThread::Track> track, @@ -472,14 +591,76 @@ public: // Return's the HAL's frame count i.e. fast mixer buffer size. size_t frameCountHAL() const { return mFrameCount; } - status_t getTimestamp_l(AudioTimestamp& timestamp); + status_t getTimestamp_l(AudioTimestamp& timestamp); + + void addPatchTrack(const sp<PatchTrack>& track); + void deletePatchTrack(const sp<PatchTrack>& track); + + virtual void getAudioPortConfig(struct audio_port_config *config); protected: - // updated by readOutputParameters() + // updated by readOutputParameters_l() size_t mNormalFrameCount; // normal mixer and effects - int16_t* mMixBuffer; // frame size aligned mix buffer - int8_t* mAllocMixBuffer; // mixer buffer allocation address + void* mSinkBuffer; // frame size aligned sink buffer + + // TODO: + // Rearrange the buffer info into a struct/class with + // clear, copy, construction, destruction methods. + // + // mSinkBuffer also has associated with it: + // + // mSinkBufferSize: Sink Buffer Size + // mFormat: Sink Buffer Format + + // Mixer Buffer (mMixerBuffer*) + // + // In the case of floating point or multichannel data, which is not in the + // sink format, it is required to accumulate in a higher precision or greater channel count + // buffer before downmixing or data conversion to the sink buffer. + + // Set to "true" to enable the Mixer Buffer otherwise mixer output goes to sink buffer. + bool mMixerBufferEnabled; + + // Storage, 32 byte aligned (may make this alignment a requirement later). + // Due to constraints on mNormalFrameCount, the buffer size is a multiple of 16 frames. + void* mMixerBuffer; + + // Size of mMixerBuffer in bytes: mNormalFrameCount * #channels * sampsize. + size_t mMixerBufferSize; + + // The audio format of mMixerBuffer. Set to AUDIO_FORMAT_PCM_(FLOAT|16_BIT) only. + audio_format_t mMixerBufferFormat; + + // An internal flag set to true by MixerThread::prepareTracks_l() + // when mMixerBuffer contains valid data after mixing. + bool mMixerBufferValid; + + // Effects Buffer (mEffectsBuffer*) + // + // In the case of effects data, which is not in the sink format, + // it is required to accumulate in a different buffer before data conversion + // to the sink buffer. + + // Set to "true" to enable the Effects Buffer otherwise effects output goes to sink buffer. + bool mEffectBufferEnabled; + + // Storage, 32 byte aligned (may make this alignment a requirement later). + // Due to constraints on mNormalFrameCount, the buffer size is a multiple of 16 frames. + void* mEffectBuffer; + + // Size of mEffectsBuffer in bytes: mNormalFrameCount * #channels * sampsize. + size_t mEffectBufferSize; + + // The audio format of mEffectsBuffer. Set to AUDIO_FORMAT_PCM_16_BIT only. + audio_format_t mEffectBufferFormat; + + // An internal flag set to true by MixerThread::prepareTracks_l() + // when mEffectsBuffer contains valid data after mixing. + // + // When this is set, all mixer data is routed into the effects buffer + // for any processing (including output processing). + bool mEffectBufferValid; // suspend count, > 0 means suspended. While suspended, the thread continues to pull from // tracks and mix, but doesn't write to HAL. A2DP and SCO HAL implementations can't handle @@ -505,7 +686,8 @@ protected: // Allocate a track name for a given channel mask. // Returns name >= 0 if successful, -1 on failure. - virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId) = 0; + virtual int getTrackName_l(audio_channel_mask_t channelMask, + audio_format_t format, int sessionId) = 0; virtual void deleteTrackName_l(int name) = 0; // Time to sleep between cycles when: @@ -527,11 +709,14 @@ protected: virtual uint32_t correctLatency_l(uint32_t latency) const; + virtual status_t createAudioPatch_l(const struct audio_patch *patch, + audio_patch_handle_t *handle); + virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle); + private: friend class AudioFlinger; // for numerous - PlaybackThread(const Client&); PlaybackThread& operator = (const PlaybackThread&); status_t addTrack_l(const sp<Track>& track); @@ -539,7 +724,7 @@ private: void removeTrack_l(const sp<Track>& track); void broadcast_l(); - void readOutputParameters(); + void readOutputParameters_l(); virtual void dumpInternals(int fd, const Vector<String16>& args); void dumpTracks(int fd, const Vector<String16>& args); @@ -558,7 +743,7 @@ private: // FIXME rename these former local variables of threadLoop to standard "m" names nsecs_t standbyTime; - size_t mixBufferSize; + size_t mSinkBufferSize; // cached copies of activeSleepTimeUs() and idleSleepTimeUs() made by cacheParameters_l() uint32_t activeSleepTime; @@ -623,13 +808,12 @@ private: sp<NBLog::Writer> mFastMixerNBLogWriter; public: virtual bool hasFastMixer() const = 0; - virtual FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex) const + virtual FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex __unused) const { FastTrackUnderruns dummy; return dummy; } protected: // accessed by both binder threads and within threadLoop(), lock on mutex needed unsigned mFastTrackAvailMask; // bit i set if fast track [i] is available - virtual void flushOutput_l(); private: // timestamp latch: @@ -654,12 +838,14 @@ public: // Thread virtuals - virtual bool checkForNewParameters_l(); + virtual bool checkForNewParameter_l(const String8& keyValuePair, + status_t& status); virtual void dumpInternals(int fd, const Vector<String16>& args); protected: virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove); - virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId); + virtual int getTrackName_l(audio_channel_mask_t channelMask, + audio_format_t format, int sessionId); virtual void deleteTrackName_l(int name); virtual uint32_t idleSleepTimeUs() const; virtual uint32_t suspendSleepTimeUs() const; @@ -676,7 +862,7 @@ protected: AudioMixer* mAudioMixer; // normal mixer private: // one-time initialization, no locks required - FastMixer* mFastMixer; // non-NULL if there is also a fast mixer + sp<FastMixer> mFastMixer; // non-0 if there is also a fast mixer sp<AudioWatchdog> mAudioWatchdog; // non-0 if there is an audio watchdog thread // contents are not guaranteed to be consistent, no locks required @@ -692,11 +878,12 @@ private: int32_t mFastMixerFutex; // for cold idle public: - virtual bool hasFastMixer() const { return mFastMixer != NULL; } + virtual bool hasFastMixer() const { return mFastMixer != 0; } virtual FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex) const { ALOG_ASSERT(fastIndex < FastMixerState::kMaxFastTracks); return mFastMixerDumpState.mTracks[fastIndex].mUnderruns; } + }; class DirectOutputThread : public PlaybackThread { @@ -708,10 +895,12 @@ public: // Thread virtuals - virtual bool checkForNewParameters_l(); + virtual bool checkForNewParameter_l(const String8& keyValuePair, + status_t& status); protected: - virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId); + virtual int getTrackName_l(audio_channel_mask_t channelMask, + audio_format_t format, int sessionId); virtual void deleteTrackName_l(int name); virtual uint32_t activeSleepTimeUs() const; virtual uint32_t idleSleepTimeUs() const; @@ -748,11 +937,11 @@ protected: // threadLoop snippets virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove); virtual void threadLoop_exit(); - virtual void flushOutput_l(); virtual bool waitingAsyncCallback(); virtual bool waitingAsyncCallback_l(); virtual bool shouldStandby_l(); + virtual void onAddNewTrack_l(); private: void flushHw_l(); @@ -838,17 +1027,28 @@ public: // record thread -class RecordThread : public ThreadBase, public AudioBufferProvider - // derives from AudioBufferProvider interface for use by resampler +class RecordThread : public ThreadBase { public: + class RecordTrack; + class ResamplerBufferProvider : public AudioBufferProvider + // derives from AudioBufferProvider interface for use by resampler + { + public: + ResamplerBufferProvider(RecordTrack* recordTrack) : mRecordTrack(recordTrack) { } + virtual ~ResamplerBufferProvider() { } + // AudioBufferProvider interface + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts); + virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer); + private: + RecordTrack * const mRecordTrack; + }; + #include "RecordTracks.h" RecordThread(const sp<AudioFlinger>& audioFlinger, AudioStreamIn *input, - uint32_t sampleRate, - audio_channel_mask_t channelMask, audio_io_handle_t id, audio_devices_t outDevice, audio_devices_t inDevice @@ -867,23 +1067,28 @@ public: // Thread virtuals virtual bool threadLoop(); - virtual status_t readyToRun(); // RefBase virtual void onFirstRef(); virtual status_t initCheck() const { return (mInput == NULL) ? NO_INIT : NO_ERROR; } + + virtual sp<MemoryDealer> readOnlyHeap() const { return mReadOnlyHeap; } + + virtual sp<IMemory> pipeMemory() const { return mPipeMemory; } + sp<AudioFlinger::RecordThread::RecordTrack> createRecordTrack_l( const sp<AudioFlinger::Client>& client, uint32_t sampleRate, audio_format_t format, audio_channel_mask_t channelMask, - size_t frameCount, + size_t *pFrameCount, int sessionId, + size_t *notificationFrames, int uid, IAudioFlinger::track_flags_t *flags, pid_t tid, - status_t *status); + status_t *status /*non-NULL*/); status_t start(RecordTrack* recordTrack, AudioSystem::sync_event_t event, @@ -897,15 +1102,21 @@ public: AudioStreamIn* clearInput(); virtual audio_stream_t* stream() const; - // AudioBufferProvider interface - virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts); - virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer); - virtual bool checkForNewParameters_l(); + virtual bool checkForNewParameter_l(const String8& keyValuePair, + status_t& status); + virtual void cacheParameters_l() {} virtual String8 getParameters(const String8& keys); - virtual void audioConfigChanged_l(int event, int param = 0); - void readInputParameters(); - virtual unsigned int getInputFramesLost(); + virtual void audioConfigChanged(int event, int param = 0); + virtual status_t createAudioPatch_l(const struct audio_patch *patch, + audio_patch_handle_t *handle); + virtual status_t releaseAudioPatch_l(const audio_patch_handle_t handle); + + void addPatchRecord(const sp<PatchRecord>& record); + void deletePatchRecord(const sp<PatchRecord>& record); + + void readInputParameters_l(); + virtual uint32_t getInputFramesLost(); virtual status_t addEffectChain_l(const sp<EffectChain>& chain); virtual size_t removeEffectChain_l(const sp<EffectChain>& chain); @@ -920,45 +1131,73 @@ public: virtual bool isValidSyncEvent(const sp<SyncEvent>& event) const; static void syncStartEventCallback(const wp<SyncEvent>& event); - void handleSyncStartEvent(const sp<SyncEvent>& event); virtual size_t frameCount() const { return mFrameCount; } - bool hasFastRecorder() const { return false; } + bool hasFastCapture() const { return mFastCapture != 0; } + virtual void getAudioPortConfig(struct audio_port_config *config); private: - void clearSyncStartEvent(); - // Enter standby if not already in standby, and set mStandby flag - void standby(); + void standbyIfNotAlreadyInStandby(); // Call the HAL standby method unconditionally, and don't change mStandby flag - void inputStandBy(); + void inputStandBy(); AudioStreamIn *mInput; SortedVector < sp<RecordTrack> > mTracks; - // mActiveTrack has dual roles: it indicates the current active track, and + // mActiveTracks has dual roles: it indicates the current active track(s), and // is used together with mStartStopCond to indicate start()/stop() progress - sp<RecordTrack> mActiveTrack; + SortedVector< sp<RecordTrack> > mActiveTracks; + // generation counter for mActiveTracks + int mActiveTracksGen; Condition mStartStopCond; - // updated by RecordThread::readInputParameters() - AudioResampler *mResampler; - // interleaved stereo pairs of fixed-point signed Q19.12 - int32_t *mRsmpOutBuffer; - int16_t *mRsmpInBuffer; // [mFrameCount * mChannelCount] - size_t mRsmpInIndex; - size_t mBufferSize; // stream buffer size for read() - const uint32_t mReqChannelCount; - const uint32_t mReqSampleRate; - ssize_t mBytesRead; - // sync event triggering actual audio capture. Frames read before this event will - // be dropped and therefore not read by the application. - sp<SyncEvent> mSyncStartEvent; - // number of captured frames to drop after the start sync event has been received. - // when < 0, maximum frames to drop before starting capture even if sync event is - // not received - ssize_t mFramestoDrop; + // resampler converts input at HAL Hz to output at AudioRecord client Hz + int16_t *mRsmpInBuffer; // see new[] for details on the size + size_t mRsmpInFrames; // size of resampler input in frames + size_t mRsmpInFramesP2;// size rounded up to a power-of-2 + + // rolling index that is never cleared + int32_t mRsmpInRear; // last filled frame + 1 // For dumpsys const sp<NBAIO_Sink> mTeeSink; + + const sp<MemoryDealer> mReadOnlyHeap; + + // one-time initialization, no locks required + sp<FastCapture> mFastCapture; // non-0 if there is also a fast capture + // FIXME audio watchdog thread + + // contents are not guaranteed to be consistent, no locks required + FastCaptureDumpState mFastCaptureDumpState; +#ifdef STATE_QUEUE_DUMP + // FIXME StateQueue observer and mutator dump fields +#endif + // FIXME audio watchdog dump + + // accessible only within the threadLoop(), no locks required + // mFastCapture->sq() // for mutating and pushing state + int32_t mFastCaptureFutex; // for cold idle + + // The HAL input source is treated as non-blocking, + // but current implementation is blocking + sp<NBAIO_Source> mInputSource; + // The source for the normal capture thread to read from: mInputSource or mPipeSource + sp<NBAIO_Source> mNormalSource; + // If a fast capture is present, the non-blocking pipe sink written to by fast capture, + // otherwise clear + sp<NBAIO_Sink> mPipeSink; + // If a fast capture is present, the non-blocking pipe source read by normal thread, + // otherwise clear + sp<NBAIO_Source> mPipeSource; + // Depth of pipe from fast capture to normal thread and fast clients, always power of 2 + size_t mPipeFramesP2; + // If a fast capture is present, the Pipe as IMemory, otherwise clear + sp<IMemory> mPipeMemory; + + static const size_t kFastCaptureLogSize = 4 * 1024; + sp<NBLog::Writer> mFastCaptureNBLogWriter; + + bool mFastTrackAvail; // true if fast track available }; diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h index cd201d9..864daa5 100644 --- a/services/audioflinger/TrackBase.h +++ b/services/audioflinger/TrackBase.h @@ -34,7 +34,25 @@ public: RESUMING, ACTIVE, PAUSING, - PAUSED + PAUSED, + STARTING_1, // for RecordTrack only + STARTING_2, // for RecordTrack only + }; + + // where to allocate the data buffer + enum alloc_type { + ALLOC_CBLK, // allocate immediately after control block + ALLOC_READONLY, // allocate from a separate read-only heap per thread + ALLOC_PIPE, // do not allocate; use the pipe buffer + ALLOC_LOCAL, // allocate a local buffer + ALLOC_NONE, // do not allocate:use the buffer passed to TrackBase constructor + }; + + enum track_type { + TYPE_DEFAULT, + TYPE_TIMED, + TYPE_OUTPUT, + TYPE_PATCH, }; TrackBase(ThreadBase *thread, @@ -43,11 +61,15 @@ public: audio_format_t format, audio_channel_mask_t channelMask, size_t frameCount, - const sp<IMemory>& sharedBuffer, + void *buffer, int sessionId, int uid, - bool isOut); + IAudioFlinger::track_flags_t flags, + bool isOut, + alloc_type alloc = ALLOC_CBLK, + track_type type = TYPE_DEFAULT); virtual ~TrackBase(); + virtual status_t initCheck() const; virtual status_t start(AudioSystem::sync_event_t event, int triggerSession) = 0; @@ -58,6 +80,14 @@ public: int uid() const { return mUid; } virtual status_t setSyncEvent(const sp<SyncEvent>& event); + sp<IMemory> getBuffers() const { return mBufferMemory; } + void* buffer() const { return mBuffer; } + bool isFastTrack() const { return (mFlags & IAudioFlinger::TRACK_FAST) != 0; } + bool isTimedTrack() const { return (mType == TYPE_TIMED); } + bool isOutputTrack() const { return (mType == TYPE_OUTPUT); } + bool isPatchTrack() const { return (mType == TYPE_PATCH); } + bool isExternalTrack() const { return !isOutputTrack() && !isPatchTrack(); } + protected: TrackBase(const TrackBase&); TrackBase& operator = (const TrackBase&); @@ -78,15 +108,6 @@ protected: virtual uint32_t sampleRate() const { return mSampleRate; } - // Return a pointer to the start of a contiguous slice of the track buffer. - // Parameter 'offset' is the requested start position, expressed in - // monotonically increasing frame units relative to the track epoch. - // Parameter 'frames' is the requested length, also in frame units. - // Always returns non-NULL. It is the caller's responsibility to - // verify that this will be successful; the result of calling this - // function with invalid 'offset' or 'frames' is undefined. - void* getBuffer(uint32_t offset, uint32_t frames) const; - bool isStopped() const { return (mState == STOPPED || mState == FLUSHED); } @@ -118,6 +139,7 @@ protected: /*const*/ sp<Client> mClient; // see explanation at ~TrackBase() why not const sp<IMemory> mCblkMemory; audio_track_cblk_t* mCblk; + sp<IMemory> mBufferMemory; // currently non-0 for fast RecordTrack only void* mBuffer; // start of track buffer, typically in shared memory // except for OutputTrack when it is in local memory // we don't really need a lock for these @@ -136,10 +158,25 @@ protected: const int mSessionId; int mUid; Vector < sp<SyncEvent> >mSyncEvents; + const IAudioFlinger::track_flags_t mFlags; const bool mIsOut; ServerProxy* mServerProxy; const int mId; sp<NBAIO_Sink> mTeeSink; sp<NBAIO_Source> mTeeSource; bool mTerminated; + track_type mType; // must be one of TYPE_DEFAULT, TYPE_OUTPUT, TYPE_PATCH ... +}; + +// PatchProxyBufferProvider interface is implemented by PatchTrack and PatchRecord. +// it provides buffer access methods that map those of a ClientProxy (see AudioTrackShared.h) +class PatchProxyBufferProvider +{ +public: + + virtual ~PatchProxyBufferProvider() {} + + virtual status_t obtainBuffer(Proxy::Buffer* buffer, + const struct timespec *requested = NULL) = 0; + virtual void releaseBuffer(Proxy::Buffer* buffer) = 0; }; diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index cbf56b5..6cbab04 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -35,6 +35,7 @@ #include <media/nbaio/Pipe.h> #include <media/nbaio/PipeReader.h> +#include <audio_utils/minifloat.h> // ---------------------------------------------------------------------------- @@ -67,10 +68,13 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( audio_format_t format, audio_channel_mask_t channelMask, size_t frameCount, - const sp<IMemory>& sharedBuffer, + void *buffer, int sessionId, int clientUid, - bool isOut) + IAudioFlinger::track_flags_t flags, + bool isOut, + alloc_type alloc, + track_type type) : RefBase(), mThread(thread), mClient(client), @@ -80,15 +84,19 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( mSampleRate(sampleRate), mFormat(format), mChannelMask(channelMask), - mChannelCount(popcount(channelMask)), + mChannelCount(isOut ? + audio_channel_count_from_out_mask(channelMask) : + audio_channel_count_from_in_mask(channelMask)), mFrameSize(audio_is_linear_pcm(format) ? mChannelCount * audio_bytes_per_sample(format) : sizeof(int8_t)), mFrameCount(frameCount), mSessionId(sessionId), + mFlags(flags), mIsOut(isOut), mServerProxy(NULL), mId(android_atomic_inc(&nextTrackId)), - mTerminated(false) + mTerminated(false), + mType(type) { // if the caller is us, trust the specified uid if (IPCThreadState::self()->getCallingPid() != getpid_cached || clientUid == -1) { @@ -102,27 +110,20 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( // battery usage on it. mUid = clientUid; - // client == 0 implies sharedBuffer == 0 - ALOG_ASSERT(!(client == 0 && sharedBuffer != 0)); - - ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), - sharedBuffer->size()); - // ALOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize); size_t size = sizeof(audio_track_cblk_t); - size_t bufferSize = (sharedBuffer == 0 ? roundup(frameCount) : frameCount) * mFrameSize; - if (sharedBuffer == 0) { + size_t bufferSize = (buffer == NULL ? roundup(frameCount) : frameCount) * mFrameSize; + if (buffer == NULL && alloc == ALLOC_CBLK) { size += bufferSize; } if (client != 0) { mCblkMemory = client->heap()->allocate(size); - if (mCblkMemory != 0) { - mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer()); - // can't assume mCblk != NULL - } else { + if (mCblkMemory == 0 || + (mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer())) == NULL) { ALOGE("not enough memory for AudioTrack size=%u", size); client->heap()->dump("AudioTrack"); + mCblkMemory.clear(); return; } } else { @@ -134,22 +135,55 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( // construct the shared structure in-place. if (mCblk != NULL) { new(mCblk) audio_track_cblk_t(); - // clear all buffers - mCblk->frameCount_ = frameCount; - if (sharedBuffer == 0) { - mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); + switch (alloc) { + case ALLOC_READONLY: { + const sp<MemoryDealer> roHeap(thread->readOnlyHeap()); + if (roHeap == 0 || + (mBufferMemory = roHeap->allocate(bufferSize)) == 0 || + (mBuffer = mBufferMemory->pointer()) == NULL) { + ALOGE("not enough memory for read-only buffer size=%zu", bufferSize); + if (roHeap != 0) { + roHeap->dump("buffer"); + } + mCblkMemory.clear(); + mBufferMemory.clear(); + return; + } memset(mBuffer, 0, bufferSize); - } else { - mBuffer = sharedBuffer->pointer(); + } break; + case ALLOC_PIPE: + mBufferMemory = thread->pipeMemory(); + // mBuffer is the virtual address as seen from current process (mediaserver), + // and should normally be coming from mBufferMemory->pointer(). + // However in this case the TrackBase does not reference the buffer directly. + // It should references the buffer via the pipe. + // Therefore, to detect incorrect usage of the buffer, we set mBuffer to NULL. + mBuffer = NULL; + break; + case ALLOC_CBLK: + // clear all buffers + if (buffer == NULL) { + mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); + memset(mBuffer, 0, bufferSize); + } else { + mBuffer = buffer; #if 0 - mCblk->mFlags = CBLK_FORCEREADY; // FIXME hack, need to fix the track ready logic + mCblk->mFlags = CBLK_FORCEREADY; // FIXME hack, need to fix the track ready logic #endif + } + break; + case ALLOC_LOCAL: + mBuffer = calloc(1, bufferSize); + break; + case ALLOC_NONE: + mBuffer = buffer; + break; } #ifdef TEE_SINK if (mTeeSinkTrackEnabled) { - NBAIO_Format pipeFormat = Format_from_SR_C(mSampleRate, mChannelCount); - if (pipeFormat != Format_Invalid) { + NBAIO_Format pipeFormat = Format_from_SR_C(mSampleRate, mChannelCount, mFormat); + if (Format_isValid(pipeFormat)) { Pipe *pipe = new Pipe(mTeeSinkTrackFrames, pipeFormat); size_t numCounterOffers = 0; const NBAIO_Format offers[1] = {pipeFormat}; @@ -168,6 +202,17 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( } } +status_t AudioFlinger::ThreadBase::TrackBase::initCheck() const +{ + status_t status; + if (mType == TYPE_OUTPUT || mType == TYPE_PATCH) { + status = cblk() != NULL ? NO_ERROR : NO_MEMORY; + } else { + status = getCblk() != 0 ? NO_ERROR : NO_MEMORY; + } + return status; +} + AudioFlinger::ThreadBase::TrackBase::~TrackBase() { #ifdef TEE_SINK @@ -184,13 +229,15 @@ AudioFlinger::ThreadBase::TrackBase::~TrackBase() } mCblkMemory.clear(); // free the shared memory before releasing the heap it belongs to if (mClient != 0) { - // Client destructor must run with AudioFlinger mutex locked - Mutex::Autolock _l(mClient->audioFlinger()->mLock); + // Client destructor must run with AudioFlinger client mutex locked + Mutex::Autolock _l(mClient->audioFlinger()->mClientLock); // If the client's reference count drops to zero, the associated destructor // must run with AudioFlinger lock held. Thus the explicit clear() rather than // relying on the automatic clear() at end of scope. mClient.clear(); } + // flush the binder command buffer + IPCThreadState::self()->flushCommands(); } // AudioBufferProvider interface @@ -276,6 +323,11 @@ status_t AudioFlinger::TrackHandle::queueTimedBuffer(const sp<IMemory>& buffer, if (!mTrack->isTimedTrack()) return INVALID_OPERATION; + if (buffer == 0 || buffer->pointer() == NULL) { + ALOGE("queueTimedBuffer() buffer is 0 or has NULL pointer()"); + return BAD_VALUE; + } + PlaybackThread::TimedTrack* tt = reinterpret_cast<PlaybackThread::TimedTrack*>(mTrack.get()); return tt->queueTimedBuffer(buffer, pts); @@ -325,12 +377,17 @@ AudioFlinger::PlaybackThread::Track::Track( audio_format_t format, audio_channel_mask_t channelMask, size_t frameCount, + void *buffer, const sp<IMemory>& sharedBuffer, int sessionId, int uid, - IAudioFlinger::track_flags_t flags) - : TrackBase(thread, client, sampleRate, format, channelMask, frameCount, sharedBuffer, - sessionId, uid, true /*isOut*/), + IAudioFlinger::track_flags_t flags, + track_type type) + : TrackBase(thread, client, sampleRate, format, channelMask, frameCount, + (sharedBuffer != 0) ? sharedBuffer->pointer() : buffer, + sessionId, uid, flags, true /*isOut*/, + (type == TYPE_PATCH) ? ( buffer == NULL ? ALLOC_LOCAL : ALLOC_NONE) : ALLOC_CBLK, + type), mFillingUpStatus(FS_INVALID), // mRetryCount initialized later when needed mSharedBuffer(sharedBuffer), @@ -340,46 +397,55 @@ AudioFlinger::PlaybackThread::Track::Track( mAuxBuffer(NULL), mAuxEffectId(0), mHasVolumeController(false), mPresentationCompleteFrames(0), - mFlags(flags), mFastIndex(-1), mCachedVolume(1.0), mIsInvalid(false), mAudioTrackServerProxy(NULL), - mResumeToStopping(false) + mResumeToStopping(false), + mFlushHwPending(false), + mPreviousValid(false), + mPreviousFramesWritten(0) + // mPreviousTimestamp { - if (mCblk != NULL) { - if (sharedBuffer == 0) { - mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount, - mFrameSize); - } else { - mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount, - mFrameSize); - } - mServerProxy = mAudioTrackServerProxy; - // to avoid leaking a track name, do not allocate one unless there is an mCblk - mName = thread->getTrackName_l(channelMask, sessionId); - if (mName < 0) { - ALOGE("no more track names available"); - return; - } - // only allocate a fast track index if we were able to allocate a normal track name - if (flags & IAudioFlinger::TRACK_FAST) { - mAudioTrackServerProxy->framesReadyIsCalledByMultipleThreads(); - ALOG_ASSERT(thread->mFastTrackAvailMask != 0); - int i = __builtin_ctz(thread->mFastTrackAvailMask); - ALOG_ASSERT(0 < i && i < (int)FastMixerState::kMaxFastTracks); - // FIXME This is too eager. We allocate a fast track index before the - // fast track becomes active. Since fast tracks are a scarce resource, - // this means we are potentially denying other more important fast tracks from - // being created. It would be better to allocate the index dynamically. - mFastIndex = i; - // Read the initial underruns because this field is never cleared by the fast mixer - mObservedUnderruns = thread->getFastTrackUnderruns(i); - thread->mFastTrackAvailMask &= ~(1 << i); - } + // client == 0 implies sharedBuffer == 0 + ALOG_ASSERT(!(client == 0 && sharedBuffer != 0)); + + ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), + sharedBuffer->size()); + + if (mCblk == NULL) { + return; + } + + if (sharedBuffer == 0) { + mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount, + mFrameSize, !isExternalTrack(), sampleRate); + } else { + mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount, + mFrameSize); + } + mServerProxy = mAudioTrackServerProxy; + + mName = thread->getTrackName_l(channelMask, format, sessionId); + if (mName < 0) { + ALOGE("no more track names available"); + return; + } + // only allocate a fast track index if we were able to allocate a normal track name + if (flags & IAudioFlinger::TRACK_FAST) { + mAudioTrackServerProxy->framesReadyIsCalledByMultipleThreads(); + ALOG_ASSERT(thread->mFastTrackAvailMask != 0); + int i = __builtin_ctz(thread->mFastTrackAvailMask); + ALOG_ASSERT(0 < i && i < (int)FastMixerState::kMaxFastTracks); + // FIXME This is too eager. We allocate a fast track index before the + // fast track becomes active. Since fast tracks are a scarce resource, + // this means we are potentially denying other more important fast tracks from + // being created. It would be better to allocate the index dynamically. + mFastIndex = i; + // Read the initial underruns because this field is never cleared by the fast mixer + mObservedUnderruns = thread->getFastTrackUnderruns(i); + thread->mFastTrackAvailMask &= ~(1 << i); } - ALOGV("Track constructor name %d, calling pid %d", mName, - IPCThreadState::self()->getCallingPid()); } AudioFlinger::PlaybackThread::Track::~Track() @@ -392,11 +458,18 @@ AudioFlinger::PlaybackThread::Track::~Track() // This prevents that leak. if (mSharedBuffer != 0) { mSharedBuffer.clear(); - // flush the binder command buffer - IPCThreadState::self()->flushCommands(); } } +status_t AudioFlinger::PlaybackThread::Track::initCheck() const +{ + status_t status = TrackBase::initCheck(); + if (status == NO_ERROR && mName < 0) { + status = NO_MEMORY; + } + return status; +} + void AudioFlinger::PlaybackThread::Track::destroy() { // NOTE: destroyTrack_l() can remove a strong reference to this Track @@ -414,7 +487,7 @@ void AudioFlinger::PlaybackThread::Track::destroy() Mutex::Autolock _l(thread->mLock); PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); bool wasActive = playbackThread->destroyTrack_l(this); - if (!isOutputTrack() && !wasActive) { + if (isExternalTrack() && !wasActive) { AudioSystem::releaseOutput(thread->id()); } } @@ -423,17 +496,19 @@ void AudioFlinger::PlaybackThread::Track::destroy() /*static*/ void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result) { - result.append(" Name Client Type Fmt Chn mask Session fCount S F SRate " + result.append(" Name Active Client Type Fmt Chn mask Session fCount S F SRate " "L dB R dB Server Main buf Aux Buf Flags UndFrmCnt\n"); } -void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size) +void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size, bool active) { - uint32_t vlr = mAudioTrackServerProxy->getVolumeLR(); + gain_minifloat_packed_t vlr = mAudioTrackServerProxy->getVolumeLR(); if (isFastTrack()) { - sprintf(buffer, " F %2d", mFastIndex); + sprintf(buffer, " F %2d", mFastIndex); + } else if (mName >= AudioMixer::TRACK0) { + sprintf(buffer, " %4d", mName - AudioMixer::TRACK0); } else { - sprintf(buffer, " %4d", mName - AudioMixer::TRACK0); + sprintf(buffer, " none"); } track_state state = mState; char stateChar; @@ -488,8 +563,9 @@ void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size) nowInUnderrun = '?'; break; } - snprintf(&buffer[7], size-7, " %6u %4u %08X %08X %7u %6zu %1c %1d %5u %5.2g %5.2g " + snprintf(&buffer[8], size-8, " %6s %6u %4u %08X %08X %7u %6zu %1c %1d %5u %5.2g %5.2g " "%08X %p %p 0x%03X %9u%c\n", + active ? "yes" : "no", (mClient == 0) ? getpid_cached : mClient->pid(), mStreamType, mFormat, @@ -499,8 +575,8 @@ void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size) stateChar, mFillingUpStatus, mAudioTrackServerProxy->getSampleRate(), - 20.0 * log10((vlr & 0xFFFF) / 4096.0), - 20.0 * log10((vlr >> 16) / 4096.0), + 20.0 * log10(float_from_gain(gain_minifloat_unpack_left(vlr))), + 20.0 * log10(float_from_gain(gain_minifloat_unpack_right(vlr))), mCblk->mServer, mMainBuffer, mAuxBuffer, @@ -515,7 +591,7 @@ uint32_t AudioFlinger::PlaybackThread::Track::sampleRate() const { // AudioBufferProvider interface status_t AudioFlinger::PlaybackThread::Track::getNextBuffer( - AudioBufferProvider::Buffer* buffer, int64_t pts) + AudioBufferProvider::Buffer* buffer, int64_t pts __unused) { ServerProxy::Buffer buf; size_t desiredFrames = buffer->frameCount; @@ -552,7 +628,14 @@ size_t AudioFlinger::PlaybackThread::Track::framesReleased() const // Don't call for fast tracks; the framesReady() could result in priority inversion bool AudioFlinger::PlaybackThread::Track::isReady() const { - if (mFillingUpStatus != FS_FILLING || isStopped() || isPausing() || isStopping()) { + if (mFillingUpStatus != FS_FILLING || isStopped() || isPausing()) { + return true; + } + + if (isStopping()) { + if (framesReady() > 0) { + mFillingUpStatus = FS_FILLED; + } return true; } @@ -565,8 +648,8 @@ bool AudioFlinger::PlaybackThread::Track::isReady() const { return false; } -status_t AudioFlinger::PlaybackThread::Track::start(AudioSystem::sync_event_t event, - int triggerSession) +status_t AudioFlinger::PlaybackThread::Track::start(AudioSystem::sync_event_t event __unused, + int triggerSession __unused) { status_t status = NO_ERROR; ALOGV("start(%d), calling pid %d session %d", @@ -589,7 +672,10 @@ status_t AudioFlinger::PlaybackThread::Track::start(AudioSystem::sync_event_t ev // here the track could be either new, or restarted // in both cases "unstop" the track - if (state == PAUSED) { + // initial state-stopping. next state-pausing. + // What if resume is called ? + + if (state == PAUSED || state == PAUSING) { if (mResumeToStopping) { // happened we need to resume to STOPPING_1 mState = TrackBase::STOPPING_1; @@ -644,7 +730,7 @@ void AudioFlinger::PlaybackThread::Track::stop() if (playbackThread->mActiveTracks.indexOf(this) < 0) { reset(); mState = STOPPED; - } else if (!isFastTrack() && !isOffloaded()) { + } else if (!isFastTrack() && !isOffloaded() && !isDirect()) { mState = STOPPED; } else { // For fast tracks prepareTracks_l() will set state to STOPPING_2 @@ -720,6 +806,7 @@ void AudioFlinger::PlaybackThread::Track::flush() mRetryCount = PlaybackThread::kMaxTrackRetriesOffload; } + mFlushHwPending = true; mResumeToStopping = false; } else { if (mState != STOPPING_1 && mState != STOPPING_2 && mState != STOPPED && @@ -740,11 +827,19 @@ void AudioFlinger::PlaybackThread::Track::flush() // Prevent flush being lost if the track is flushed and then resumed // before mixer thread can run. This is important when offloading // because the hardware buffer could hold a large amount of audio - playbackThread->flushOutput_l(); playbackThread->broadcast_l(); } } +// must be called with thread lock held +void AudioFlinger::PlaybackThread::Track::flushAck() +{ + if (!isOffloaded()) + return; + + mFlushHwPending = false; +} + void AudioFlinger::PlaybackThread::Track::reset() { // Do not reset twice to avoid discarding data written just after a flush and before @@ -779,27 +874,51 @@ status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& times { // Client should implement this using SSQ; the unpresented frame count in latch is irrelevant if (isFastTrack()) { + // FIXME no lock held to set mPreviousValid = false return INVALID_OPERATION; } sp<ThreadBase> thread = mThread.promote(); if (thread == 0) { + // FIXME no lock held to set mPreviousValid = false return INVALID_OPERATION; } Mutex::Autolock _l(thread->mLock); PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); - if (!isOffloaded()) { + if (!isOffloaded() && !isDirect()) { if (!playbackThread->mLatchQValid) { + mPreviousValid = false; return INVALID_OPERATION; } uint32_t unpresentedFrames = ((int64_t) playbackThread->mLatchQ.mUnpresentedFrames * mSampleRate) / playbackThread->mSampleRate; uint32_t framesWritten = mAudioTrackServerProxy->framesReleased(); + bool checkPreviousTimestamp = mPreviousValid && framesWritten >= mPreviousFramesWritten; if (framesWritten < unpresentedFrames) { + mPreviousValid = false; return INVALID_OPERATION; } - timestamp.mPosition = framesWritten - unpresentedFrames; - timestamp.mTime = playbackThread->mLatchQ.mTimestamp.mTime; + mPreviousFramesWritten = framesWritten; + uint32_t position = framesWritten - unpresentedFrames; + struct timespec time = playbackThread->mLatchQ.mTimestamp.mTime; + if (checkPreviousTimestamp) { + if (time.tv_sec < mPreviousTimestamp.mTime.tv_sec || + (time.tv_sec == mPreviousTimestamp.mTime.tv_sec && + time.tv_nsec < mPreviousTimestamp.mTime.tv_nsec)) { + ALOGW("Time is going backwards"); + } + // position can bobble slightly as an artifact; this hides the bobble + static const uint32_t MINIMUM_POSITION_DELTA = 8u; + if ((position <= mPreviousTimestamp.mPosition) || + (position - mPreviousTimestamp.mPosition) < MINIMUM_POSITION_DELTA) { + position = mPreviousTimestamp.mPosition; + time = mPreviousTimestamp.mTime; + } + } + timestamp.mPosition = position; + timestamp.mTime = time; + mPreviousTimestamp = timestamp; + mPreviousValid = true; return NO_ERROR; } @@ -885,8 +1004,6 @@ bool AudioFlinger::PlaybackThread::Track::presentationComplete(size_t framesWrit } if (framesWritten >= mPresentationCompleteFrames || isOffloaded()) { - ALOGV("presentationComplete() session %d complete: framesWritten %d", - mSessionId, framesWritten); triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE); mAudioTrackServerProxy->setStreamEndDone(); return true; @@ -907,27 +1024,27 @@ void AudioFlinger::PlaybackThread::Track::triggerEvents(AudioSystem::sync_event_ // implement VolumeBufferProvider interface -uint32_t AudioFlinger::PlaybackThread::Track::getVolumeLR() +gain_minifloat_packed_t AudioFlinger::PlaybackThread::Track::getVolumeLR() { // called by FastMixer, so not allowed to take any locks, block, or do I/O including logs ALOG_ASSERT(isFastTrack() && (mCblk != NULL)); - uint32_t vlr = mAudioTrackServerProxy->getVolumeLR(); - uint32_t vl = vlr & 0xFFFF; - uint32_t vr = vlr >> 16; + gain_minifloat_packed_t vlr = mAudioTrackServerProxy->getVolumeLR(); + float vl = float_from_gain(gain_minifloat_unpack_left(vlr)); + float vr = float_from_gain(gain_minifloat_unpack_right(vlr)); // track volumes come from shared memory, so can't be trusted and must be clamped - if (vl > MAX_GAIN_INT) { - vl = MAX_GAIN_INT; + if (vl > GAIN_FLOAT_UNITY) { + vl = GAIN_FLOAT_UNITY; } - if (vr > MAX_GAIN_INT) { - vr = MAX_GAIN_INT; + if (vr > GAIN_FLOAT_UNITY) { + vr = GAIN_FLOAT_UNITY; } // now apply the cached master volume and stream type volume; // this is trusted but lacks any synchronization or barrier so may be stale float v = mCachedVolume; vl *= v; vr *= v; - // re-combine into U4.16 - vlr = (vr << 16) | (vl & 0xFFFF); + // re-combine into packed minifloat + vlr = gain_minifloat_pack(gain_from_float(vl), gain_from_float(vr)); // FIXME look at mute, pause, and stop flags return vlr; } @@ -967,6 +1084,33 @@ void AudioFlinger::PlaybackThread::Track::signal() } } +//To be called with thread lock held +bool AudioFlinger::PlaybackThread::Track::isResumePending() { + + if (mState == RESUMING) + return true; + /* Resume is pending if track was stopping before pause was called */ + if (mState == STOPPING_1 && + mResumeToStopping) + return true; + + return false; +} + +//To be called with thread lock held +void AudioFlinger::PlaybackThread::Track::resumeAck() { + + + if (mState == RESUMING) + mState = ACTIVE; + + // Other possibility of pending resume is stopping_1 state + // Do not update the state from stopping as this prevents + // drain being called. + if (mState == STOPPING_1) { + mResumeToStopping = false; + } +} // ---------------------------------------------------------------------------- sp<AudioFlinger::PlaybackThread::TimedTrack> @@ -980,7 +1124,8 @@ AudioFlinger::PlaybackThread::TimedTrack::create( size_t frameCount, const sp<IMemory>& sharedBuffer, int sessionId, - int uid) { + int uid) +{ if (!client->reserveTimedTrack()) return 0; @@ -1001,7 +1146,8 @@ AudioFlinger::PlaybackThread::TimedTrack::TimedTrack( int sessionId, int uid) : Track(thread, client, streamType, sampleRate, format, channelMask, - frameCount, sharedBuffer, sessionId, uid, IAudioFlinger::TRACK_TIMED), + frameCount, (sharedBuffer != 0) ? sharedBuffer->pointer() : NULL, sharedBuffer, + sessionId, uid, IAudioFlinger::TRACK_TIMED, TYPE_TIMED), mQueueHeadInFlight(false), mTrimQueueHeadOnRelease(false), mFramesPendingInQueue(0), @@ -1046,15 +1192,14 @@ status_t AudioFlinger::PlaybackThread::TimedTrack::allocateTimedBuffer( mTimedMemoryDealer = new MemoryDealer(kTimedBufferHeapSize, "AudioFlingerTimed"); - if (mTimedMemoryDealer == NULL) + if (mTimedMemoryDealer == NULL) { return NO_MEMORY; + } } sp<IMemory> newBuffer = mTimedMemoryDealer->allocate(size); - if (newBuffer == NULL) { - newBuffer = mTimedMemoryDealer->allocate(size); - if (newBuffer == NULL) - return NO_MEMORY; + if (newBuffer == 0 || newBuffer->pointer() == NULL) { + return NO_MEMORY; } *buffer = newBuffer; @@ -1153,7 +1298,7 @@ void AudioFlinger::PlaybackThread::TimedTrack::trimTimedBufferQueueHead_l( void AudioFlinger::PlaybackThread::TimedTrack::updateFramesPendingAfterTrim_l( const TimedBuffer& buf, - const char* logTag) { + const char* logTag __unused) { uint32_t bufBytes = buf.buffer()->size(); uint32_t consumedAlready = buf.position(); @@ -1464,7 +1609,7 @@ void AudioFlinger::PlaybackThread::TimedTrack::releaseBuffer( mTrimQueueHeadOnRelease = false; } } else { - LOG_FATAL("TimedTrack::releaseBuffer of non-silence buffer with no" + LOG_ALWAYS_FATAL("TimedTrack::releaseBuffer of non-silence buffer with no" " buffers in the timed buffer queue"); } @@ -1497,7 +1642,7 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack( size_t frameCount, int uid) : Track(playbackThread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount, - NULL, 0, uid, IAudioFlinger::TRACK_DEFAULT), + NULL, 0, 0, uid, IAudioFlinger::TRACK_DEFAULT, TYPE_OUTPUT), mActive(false), mSourceThread(sourceThread), mClientProxy(NULL) { @@ -1505,17 +1650,16 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack( mOutBuffer.frameCount = 0; playbackThread->mTracks.add(this); ALOGV("OutputTrack constructor mCblk %p, mBuffer %p, " - "mCblk->frameCount_ %u, mChannelMask 0x%08x", + "frameCount %u, mChannelMask 0x%08x", mCblk, mBuffer, - mCblk->frameCount_, mChannelMask); + frameCount, mChannelMask); // since client and server are in the same process, // the buffer has the same virtual address on both sides - mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize); - mClientProxy->setVolumeLR((uint32_t(uint16_t(0x1000)) << 16) | uint16_t(0x1000)); - mClientProxy->setSendLevel(0.0); - mClientProxy->setSampleRate(sampleRate); mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize, true /*clientInServer*/); + mClientProxy->setVolumeLR(GAIN_MINIFLOAT_PACKED_UNITY); + mClientProxy->setSendLevel(0.0); + mClientProxy->setSampleRate(sampleRate); } else { ALOGW("Error creating output track on thread %p", playbackThread); } @@ -1706,6 +1850,75 @@ void AudioFlinger::PlaybackThread::OutputTrack::clearBufferQueue() } +AudioFlinger::PlaybackThread::PatchTrack::PatchTrack(PlaybackThread *playbackThread, + uint32_t sampleRate, + audio_channel_mask_t channelMask, + audio_format_t format, + size_t frameCount, + void *buffer, + IAudioFlinger::track_flags_t flags) + : Track(playbackThread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount, + buffer, 0, 0, getuid(), flags, TYPE_PATCH), + mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, true, true)) +{ + uint64_t mixBufferNs = ((uint64_t)2 * playbackThread->frameCount() * 1000000000) / + playbackThread->sampleRate(); + mPeerTimeout.tv_sec = mixBufferNs / 1000000000; + mPeerTimeout.tv_nsec = (int) (mixBufferNs % 1000000000); + + ALOGV("PatchTrack %p sampleRate %d mPeerTimeout %d.%03d sec", + this, sampleRate, + (int)mPeerTimeout.tv_sec, + (int)(mPeerTimeout.tv_nsec / 1000000)); +} + +AudioFlinger::PlaybackThread::PatchTrack::~PatchTrack() +{ +} + +// AudioBufferProvider interface +status_t AudioFlinger::PlaybackThread::PatchTrack::getNextBuffer( + AudioBufferProvider::Buffer* buffer, int64_t pts) +{ + ALOG_ASSERT(mPeerProxy != 0, "PatchTrack::getNextBuffer() called without peer proxy"); + Proxy::Buffer buf; + buf.mFrameCount = buffer->frameCount; + status_t status = mPeerProxy->obtainBuffer(&buf, &mPeerTimeout); + ALOGV_IF(status != NO_ERROR, "PatchTrack() %p getNextBuffer status %d", this, status); + buffer->frameCount = buf.mFrameCount; + if (buf.mFrameCount == 0) { + return WOULD_BLOCK; + } + status = Track::getNextBuffer(buffer, pts); + return status; +} + +void AudioFlinger::PlaybackThread::PatchTrack::releaseBuffer(AudioBufferProvider::Buffer* buffer) +{ + ALOG_ASSERT(mPeerProxy != 0, "PatchTrack::releaseBuffer() called without peer proxy"); + Proxy::Buffer buf; + buf.mFrameCount = buffer->frameCount; + buf.mRaw = buffer->raw; + mPeerProxy->releaseBuffer(&buf); + TrackBase::releaseBuffer(buffer); +} + +status_t AudioFlinger::PlaybackThread::PatchTrack::obtainBuffer(Proxy::Buffer* buffer, + const struct timespec *timeOut) +{ + return mProxy->obtainBuffer(buffer, timeOut); +} + +void AudioFlinger::PlaybackThread::PatchTrack::releaseBuffer(Proxy::Buffer* buffer) +{ + mProxy->releaseBuffer(buffer); + if (android_atomic_and(~CBLK_DISABLED, &mCblk->mFlags) & CBLK_DISABLED) { + ALOGW("PatchTrack::releaseBuffer() disabled due to previous underrun, restarting"); + start(); + } + android_atomic_or(CBLK_FORCEREADY, &mCblk->mFlags); +} + // ---------------------------------------------------------------------------- // Record // ---------------------------------------------------------------------------- @@ -1722,10 +1935,6 @@ AudioFlinger::RecordHandle::~RecordHandle() { mRecordTrack->destroy(); } -sp<IMemory> AudioFlinger::RecordHandle::getCblk() const { - return mRecordTrack->getCblk(); -} - status_t AudioFlinger::RecordHandle::start(int /*AudioSystem::sync_event_t*/ event, int triggerSession) { ALOGV("RecordHandle::start()"); @@ -1749,7 +1958,7 @@ status_t AudioFlinger::RecordHandle::onTransact( // ---------------------------------------------------------------------------- -// RecordTrack constructor must be called with AudioFlinger::mLock held +// RecordTrack constructor must be called with AudioFlinger::mLock and ThreadBase::mLock held AudioFlinger::RecordThread::RecordTrack::RecordTrack( RecordThread *thread, const sp<Client>& client, @@ -1757,28 +1966,59 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack( audio_format_t format, audio_channel_mask_t channelMask, size_t frameCount, + void *buffer, int sessionId, - int uid) + int uid, + IAudioFlinger::track_flags_t flags, + track_type type) : TrackBase(thread, client, sampleRate, format, - channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, uid, false /*isOut*/), - mOverflow(false) + channelMask, frameCount, buffer, sessionId, uid, + flags, false /*isOut*/, + (type == TYPE_DEFAULT) ? + ((flags & IAudioFlinger::TRACK_FAST) ? ALLOC_PIPE : ALLOC_CBLK) : + ((buffer == NULL) ? ALLOC_LOCAL : ALLOC_NONE), + type), + mOverflow(false), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpOutFrameCount(0), + // See real initialization of mRsmpInFront at RecordThread::start() + mRsmpInUnrel(0), mRsmpInFront(0), mFramesToDrop(0), mResamplerBufferProvider(NULL) { - ALOGV("RecordTrack constructor"); - if (mCblk != NULL) { - mAudioRecordServerProxy = new AudioRecordServerProxy(mCblk, mBuffer, frameCount, - mFrameSize); - mServerProxy = mAudioRecordServerProxy; + if (mCblk == NULL) { + return; + } + + mServerProxy = new AudioRecordServerProxy(mCblk, mBuffer, frameCount, + mFrameSize, !isExternalTrack()); + + uint32_t channelCount = audio_channel_count_from_in_mask(channelMask); + // FIXME I don't understand either of the channel count checks + if (thread->mSampleRate != sampleRate && thread->mChannelCount <= FCC_2 && + channelCount <= FCC_2) { + // sink SR + mResampler = AudioResampler::create(AUDIO_FORMAT_PCM_16_BIT, + thread->mChannelCount, sampleRate); + // source SR + mResampler->setSampleRate(thread->mSampleRate); + mResampler->setVolume(AudioMixer::UNITY_GAIN_FLOAT, AudioMixer::UNITY_GAIN_FLOAT); + mResamplerBufferProvider = new ResamplerBufferProvider(this); + } + + if (flags & IAudioFlinger::TRACK_FAST) { + ALOG_ASSERT(thread->mFastTrackAvail); + thread->mFastTrackAvail = false; } } AudioFlinger::RecordThread::RecordTrack::~RecordTrack() { ALOGV("%s", __func__); + delete mResampler; + delete[] mRsmpOutBuffer; + delete mResamplerBufferProvider; } // AudioBufferProvider interface status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer, - int64_t pts) + int64_t pts __unused) { ServerProxy::Buffer buf; buf.mFrameCount = buffer->frameCount; @@ -1809,8 +2049,8 @@ void AudioFlinger::RecordThread::RecordTrack::stop() sp<ThreadBase> thread = mThread.promote(); if (thread != 0) { RecordThread *recordThread = (RecordThread *)thread.get(); - if (recordThread->stop(this)) { - AudioSystem::stopInput(recordThread->id()); + if (recordThread->stop(this) && isExternalTrack()) { + AudioSystem::stopInput(recordThread->id(), (audio_session_t)mSessionId); } } } @@ -1822,10 +2062,12 @@ void AudioFlinger::RecordThread::RecordTrack::destroy() { sp<ThreadBase> thread = mThread.promote(); if (thread != 0) { - if (mState == ACTIVE || mState == RESUMING) { - AudioSystem::stopInput(thread->id()); + if (isExternalTrack()) { + if (mState == ACTIVE || mState == RESUMING) { + AudioSystem::stopInput(thread->id(), (audio_session_t)mSessionId); + } + AudioSystem::releaseInput(thread->id(), (audio_session_t)mSessionId); } - AudioSystem::releaseInput(thread->id()); Mutex::Autolock _l(thread->mLock); RecordThread *recordThread = (RecordThread *) thread.get(); recordThread->destroyTrack_l(this); @@ -1846,19 +2088,111 @@ void AudioFlinger::RecordThread::RecordTrack::invalidate() /*static*/ void AudioFlinger::RecordThread::RecordTrack::appendDumpHeader(String8& result) { - result.append("Client Fmt Chn mask Session S Server fCount\n"); + result.append(" Active Client Fmt Chn mask Session S Server fCount SRate\n"); } -void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size) +void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size, bool active) { - snprintf(buffer, size, "%6u %3u %08X %7u %1d %08X %6zu\n", + snprintf(buffer, size, " %6s %6u %3u %08X %7u %1d %08X %6zu %5u\n", + active ? "yes" : "no", (mClient == 0) ? getpid_cached : mClient->pid(), mFormat, mChannelMask, mSessionId, mState, mCblk->mServer, - mFrameCount); + mFrameCount, + mSampleRate); + +} + +void AudioFlinger::RecordThread::RecordTrack::handleSyncStartEvent(const sp<SyncEvent>& event) +{ + if (event == mSyncStartEvent) { + ssize_t framesToDrop = 0; + sp<ThreadBase> threadBase = mThread.promote(); + if (threadBase != 0) { + // TODO: use actual buffer filling status instead of 2 buffers when info is available + // from audio HAL + framesToDrop = threadBase->mFrameCount * 2; + } + mFramesToDrop = framesToDrop; + } +} + +void AudioFlinger::RecordThread::RecordTrack::clearSyncStartEvent() +{ + if (mSyncStartEvent != 0) { + mSyncStartEvent->cancel(); + mSyncStartEvent.clear(); + } + mFramesToDrop = 0; +} + + +AudioFlinger::RecordThread::PatchRecord::PatchRecord(RecordThread *recordThread, + uint32_t sampleRate, + audio_channel_mask_t channelMask, + audio_format_t format, + size_t frameCount, + void *buffer, + IAudioFlinger::track_flags_t flags) + : RecordTrack(recordThread, NULL, sampleRate, format, channelMask, frameCount, + buffer, 0, getuid(), flags, TYPE_PATCH), + mProxy(new ClientProxy(mCblk, mBuffer, frameCount, mFrameSize, false, true)) +{ + uint64_t mixBufferNs = ((uint64_t)2 * recordThread->frameCount() * 1000000000) / + recordThread->sampleRate(); + mPeerTimeout.tv_sec = mixBufferNs / 1000000000; + mPeerTimeout.tv_nsec = (int) (mixBufferNs % 1000000000); + + ALOGV("PatchRecord %p sampleRate %d mPeerTimeout %d.%03d sec", + this, sampleRate, + (int)mPeerTimeout.tv_sec, + (int)(mPeerTimeout.tv_nsec / 1000000)); +} + +AudioFlinger::RecordThread::PatchRecord::~PatchRecord() +{ +} + +// AudioBufferProvider interface +status_t AudioFlinger::RecordThread::PatchRecord::getNextBuffer( + AudioBufferProvider::Buffer* buffer, int64_t pts) +{ + ALOG_ASSERT(mPeerProxy != 0, "PatchRecord::getNextBuffer() called without peer proxy"); + Proxy::Buffer buf; + buf.mFrameCount = buffer->frameCount; + status_t status = mPeerProxy->obtainBuffer(&buf, &mPeerTimeout); + ALOGV_IF(status != NO_ERROR, + "PatchRecord() %p mPeerProxy->obtainBuffer status %d", this, status); + buffer->frameCount = buf.mFrameCount; + if (buf.mFrameCount == 0) { + return WOULD_BLOCK; + } + status = RecordTrack::getNextBuffer(buffer, pts); + return status; +} + +void AudioFlinger::RecordThread::PatchRecord::releaseBuffer(AudioBufferProvider::Buffer* buffer) +{ + ALOG_ASSERT(mPeerProxy != 0, "PatchRecord::releaseBuffer() called without peer proxy"); + Proxy::Buffer buf; + buf.mFrameCount = buffer->frameCount; + buf.mRaw = buffer->raw; + mPeerProxy->releaseBuffer(&buf); + TrackBase::releaseBuffer(buffer); +} + +status_t AudioFlinger::RecordThread::PatchRecord::obtainBuffer(Proxy::Buffer* buffer, + const struct timespec *timeOut) +{ + return mProxy->obtainBuffer(buffer, timeOut); +} + +void AudioFlinger::RecordThread::PatchRecord::releaseBuffer(Proxy::Buffer* buffer) +{ + mProxy->releaseBuffer(buffer); } }; // namespace android diff --git a/services/audioflinger/test-resample.cpp b/services/audioflinger/test-resample.cpp index 7a314cf..84a655a 100644 --- a/services/audioflinger/test-resample.cpp +++ b/services/audioflinger/test-resample.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#include "AudioResampler.h" -#include <media/AudioBufferProvider.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> @@ -24,81 +22,114 @@ #include <sys/mman.h> #include <sys/stat.h> #include <errno.h> +#include <inttypes.h> #include <time.h> #include <math.h> +#include <audio_utils/primitives.h> +#include <audio_utils/sndfile.h> +#include <utils/Vector.h> +#include <media/AudioBufferProvider.h> +#include "AudioResampler.h" using namespace android; -struct HeaderWav { - HeaderWav(size_t size, int nc, int sr, int bits) { - strncpy(RIFF, "RIFF", 4); - chunkSize = size + sizeof(HeaderWav); - strncpy(WAVE, "WAVE", 4); - strncpy(fmt, "fmt ", 4); - fmtSize = 16; - audioFormat = 1; - numChannels = nc; - samplesRate = sr; - byteRate = sr * numChannels * (bits/8); - align = nc*(bits/8); - bitsPerSample = bits; - strncpy(data, "data", 4); - dataSize = size; - } - - char RIFF[4]; // RIFF - uint32_t chunkSize; // File size - char WAVE[4]; // WAVE - char fmt[4]; // fmt\0 - uint32_t fmtSize; // fmt size - uint16_t audioFormat; // 1=PCM - uint16_t numChannels; // num channels - uint32_t samplesRate; // sample rate in hz - uint32_t byteRate; // Bps - uint16_t align; // 2=16-bit mono, 4=16-bit stereo - uint16_t bitsPerSample; // bits per sample - char data[4]; // "data" - uint32_t dataSize; // size -}; +static bool gVerbose = false; static int usage(const char* name) { - fprintf(stderr,"Usage: %s [-p] [-h] [-s] [-q {dq|lq|mq|hq|vhq}] [-i input-sample-rate] " - "[-o output-sample-rate] [<input-file>] <output-file>\n", name); + fprintf(stderr,"Usage: %s [-p] [-f] [-F] [-v] [-c channels]" + " [-q {dq|lq|mq|hq|vhq|dlq|dmq|dhq}]" + " [-i input-sample-rate] [-o output-sample-rate]" + " [-O csv] [-P csv] [<input-file>]" + " <output-file>\n", name); fprintf(stderr," -p enable profiling\n"); - fprintf(stderr," -h create wav file\n"); - fprintf(stderr," -s stereo\n"); + fprintf(stderr," -f enable filter profiling\n"); + fprintf(stderr," -F enable floating point -q {dlq|dmq|dhq} only"); + fprintf(stderr," -v verbose : log buffer provider calls\n"); + fprintf(stderr," -c # channels (1-2 for lq|mq|hq; 1-8 for dlq|dmq|dhq)\n"); fprintf(stderr," -q resampler quality\n"); fprintf(stderr," dq : default quality\n"); fprintf(stderr," lq : low quality\n"); fprintf(stderr," mq : medium quality\n"); fprintf(stderr," hq : high quality\n"); fprintf(stderr," vhq : very high quality\n"); - fprintf(stderr," -i input file sample rate\n"); + fprintf(stderr," dlq : dynamic low quality\n"); + fprintf(stderr," dmq : dynamic medium quality\n"); + fprintf(stderr," dhq : dynamic high quality\n"); + fprintf(stderr," -i input file sample rate (ignored if input file is specified)\n"); fprintf(stderr," -o output file sample rate\n"); + fprintf(stderr," -O # frames output per call to resample() in CSV format\n"); + fprintf(stderr," -P # frames provided per call to resample() in CSV format\n"); return -1; } -int main(int argc, char* argv[]) { +// Convert a list of integers in CSV format to a Vector of those values. +// Returns the number of elements in the list, or -1 on error. +int parseCSV(const char *string, Vector<int>& values) +{ + // pass 1: count the number of values and do syntax check + size_t numValues = 0; + bool hadDigit = false; + for (const char *p = string; ; ) { + switch (*p++) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + hadDigit = true; + break; + case '\0': + if (hadDigit) { + // pass 2: allocate and initialize vector of values + values.resize(++numValues); + values.editItemAt(0) = atoi(p = optarg); + for (size_t i = 1; i < numValues; ) { + if (*p++ == ',') { + values.editItemAt(i++) = atoi(p); + } + } + return numValues; + } + // fall through + case ',': + if (hadDigit) { + hadDigit = false; + numValues++; + break; + } + // fall through + default: + return -1; + } + } +} +int main(int argc, char* argv[]) { const char* const progname = argv[0]; - bool profiling = false; - bool writeHeader = false; + bool profileResample = false; + bool profileFilter = false; + bool useFloat = false; int channels = 1; int input_freq = 0; int output_freq = 0; AudioResampler::src_quality quality = AudioResampler::DEFAULT_QUALITY; + Vector<int> Ovalues; + Vector<int> Pvalues; int ch; - while ((ch = getopt(argc, argv, "phsq:i:o:")) != -1) { + while ((ch = getopt(argc, argv, "pfFvc:q:i:o:O:P:")) != -1) { switch (ch) { case 'p': - profiling = true; + profileResample = true; + break; + case 'f': + profileFilter = true; + break; + case 'F': + useFloat = true; break; - case 'h': - writeHeader = true; + case 'v': + gVerbose = true; break; - case 's': - channels = 2; + case 'c': + channels = atoi(optarg); break; case 'q': if (!strcmp(optarg, "dq")) @@ -111,6 +142,12 @@ int main(int argc, char* argv[]) { quality = AudioResampler::HIGH_QUALITY; else if (!strcmp(optarg, "vhq")) quality = AudioResampler::VERY_HIGH_QUALITY; + else if (!strcmp(optarg, "dlq")) + quality = AudioResampler::DYN_LOW_QUALITY; + else if (!strcmp(optarg, "dmq")) + quality = AudioResampler::DYN_MED_QUALITY; + else if (!strcmp(optarg, "dhq")) + quality = AudioResampler::DYN_HIGH_QUALITY; else { usage(progname); return -1; @@ -122,12 +159,35 @@ int main(int argc, char* argv[]) { case 'o': output_freq = atoi(optarg); break; + case 'O': + if (parseCSV(optarg, Ovalues) < 0) { + fprintf(stderr, "incorrect syntax for -O option\n"); + return -1; + } + break; + case 'P': + if (parseCSV(optarg, Pvalues) < 0) { + fprintf(stderr, "incorrect syntax for -P option\n"); + return -1; + } + break; case '?': default: usage(progname); return -1; } } + + if (channels < 1 + || channels > (quality < AudioResampler::DYN_LOW_QUALITY ? 2 : 8)) { + fprintf(stderr, "invalid number of audio channels %d\n", channels); + return -1; + } + if (useFloat && quality < AudioResampler::DYN_LOW_QUALITY) { + fprintf(stderr, "float processing is only possible for dynamic resamplers\n"); + return -1; + } + argc -= optind; argv += optind; @@ -148,25 +208,22 @@ int main(int argc, char* argv[]) { size_t input_size; void* input_vaddr; if (argc == 2) { - struct stat st; - if (stat(file_in, &st) < 0) { - fprintf(stderr, "stat: %s\n", strerror(errno)); - return -1; - } - - int input_fd = open(file_in, O_RDONLY); - if (input_fd < 0) { - fprintf(stderr, "open: %s\n", strerror(errno)); - return -1; - } - - input_size = st.st_size; - input_vaddr = mmap(0, input_size, PROT_READ, MAP_PRIVATE, input_fd, 0); - if (input_vaddr == MAP_FAILED ) { - fprintf(stderr, "mmap: %s\n", strerror(errno)); - return -1; + SF_INFO info; + info.format = 0; + SNDFILE *sf = sf_open(file_in, SFM_READ, &info); + if (sf == NULL) { + perror(file_in); + return EXIT_FAILURE; } + input_size = info.frames * info.channels * sizeof(short); + input_vaddr = malloc(input_size); + (void) sf_readf_short(sf, (short *) input_vaddr, info.frames); + sf_close(sf); + channels = info.channels; + input_freq = info.samplerate; } else { + // data for testing is exactly (input sampling rate/1000)/2 seconds + // so 44.1khz input is 22.05 seconds double k = 1000; // Hz / s double time = (input_freq / 2) / k; size_t input_frames = size_t(input_freq * time); @@ -177,98 +234,276 @@ int main(int argc, char* argv[]) { double t = double(i) / input_freq; double y = sin(M_PI * k * t * t); int16_t yi = floor(y * 32767.0 + 0.5); - for (size_t j=0 ; j<(size_t)channels ; j++) { - in[i*channels + j] = yi / (1+j); + for (int j = 0; j < channels; j++) { + in[i*channels + j] = yi / (1 + j); } } } + size_t input_framesize = channels * sizeof(int16_t); + size_t input_frames = input_size / input_framesize; + + // For float processing, convert input int16_t to float array + if (useFloat) { + void *new_vaddr; + + input_framesize = channels * sizeof(float); + input_size = input_frames * input_framesize; + new_vaddr = malloc(input_size); + memcpy_to_float_from_i16(reinterpret_cast<float*>(new_vaddr), + reinterpret_cast<int16_t*>(input_vaddr), input_frames * channels); + free(input_vaddr); + input_vaddr = new_vaddr; + } // ---------------------------------------------------------- class Provider: public AudioBufferProvider { - int16_t* mAddr; - size_t mNumFrames; + const void* mAddr; // base address + const size_t mNumFrames; // total frames + const size_t mFrameSize; // size of each frame in bytes + size_t mNextFrame; // index of next frame to provide + size_t mUnrel; // number of frames not yet released + const Vector<int> mPvalues; // number of frames provided per call + size_t mNextPidx; // index of next entry in mPvalues to use public: - Provider(const void* addr, size_t size, int channels) { - mAddr = (int16_t*) addr; - mNumFrames = size / (channels*sizeof(int16_t)); + Provider(const void* addr, size_t frames, size_t frameSize, const Vector<int>& Pvalues) + : mAddr(addr), + mNumFrames(frames), + mFrameSize(frameSize), + mNextFrame(0), mUnrel(0), mPvalues(Pvalues), mNextPidx(0) { } virtual status_t getNextBuffer(Buffer* buffer, int64_t pts = kInvalidPTS) { - buffer->frameCount = mNumFrames; - buffer->i16 = mAddr; - return NO_ERROR; + (void)pts; // suppress warning + size_t requestedFrames = buffer->frameCount; + if (requestedFrames > mNumFrames - mNextFrame) { + buffer->frameCount = mNumFrames - mNextFrame; + } + if (!mPvalues.isEmpty()) { + size_t provided = mPvalues[mNextPidx++]; + printf("mPvalue[%zu]=%zu not %zu\n", mNextPidx-1, provided, buffer->frameCount); + if (provided < buffer->frameCount) { + buffer->frameCount = provided; + } + if (mNextPidx >= mPvalues.size()) { + mNextPidx = 0; + } + } + if (gVerbose) { + printf("getNextBuffer() requested %zu frames out of %zu frames available," + " and returned %zu frames\n", + requestedFrames, (size_t) (mNumFrames - mNextFrame), buffer->frameCount); + } + mUnrel = buffer->frameCount; + if (buffer->frameCount > 0) { + buffer->raw = (char *)mAddr + mFrameSize * mNextFrame; + return NO_ERROR; + } else { + buffer->raw = NULL; + return NOT_ENOUGH_DATA; + } } virtual void releaseBuffer(Buffer* buffer) { + if (buffer->frameCount > mUnrel) { + fprintf(stderr, "ERROR releaseBuffer() released %zu frames but only %zu available " + "to release\n", buffer->frameCount, mUnrel); + mNextFrame += mUnrel; + mUnrel = 0; + } else { + if (gVerbose) { + printf("releaseBuffer() released %zu frames out of %zu frames available " + "to release\n", buffer->frameCount, mUnrel); + } + mNextFrame += buffer->frameCount; + mUnrel -= buffer->frameCount; + } + buffer->frameCount = 0; + buffer->raw = NULL; } - } provider(input_vaddr, input_size, channels); - - size_t input_frames = input_size / (channels * sizeof(int16_t)); - size_t output_size = 2 * 4 * ((int64_t) input_frames * output_freq) / input_freq; - output_size &= ~7; // always stereo, 32-bits - - void* output_vaddr = malloc(output_size); + void reset() { + mNextFrame = 0; + } + } provider(input_vaddr, input_frames, input_framesize, Pvalues); - if (profiling) { - AudioResampler* resampler = AudioResampler::create(16, channels, - output_freq, quality); + if (gVerbose) { + printf("%zu input frames\n", input_frames); + } - size_t out_frames = output_size/8; - resampler->setSampleRate(input_freq); - resampler->setVolume(0x1000, 0x1000); + audio_format_t format = useFloat ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; + int output_channels = channels > 2 ? channels : 2; // output is at least stereo samples + size_t output_framesize = output_channels * (useFloat ? sizeof(float) : sizeof(int32_t)); + size_t output_frames = ((int64_t) input_frames * output_freq) / input_freq; + size_t output_size = output_frames * output_framesize; - memset(output_vaddr, 0, output_size); + if (profileFilter) { + // Check how fast sample rate changes are that require filter changes. + // The delta sample rate changes must indicate a downsampling ratio, + // and must be larger than 10% changes. + // + // On fast devices, filters should be generated between 0.1ms - 1ms. + // (single threaded). + AudioResampler* resampler = AudioResampler::create(format, channels, + 8000, quality); + int looplimit = 100; timespec start, end; clock_gettime(CLOCK_MONOTONIC, &start); - resampler->resample((int*) output_vaddr, out_frames, &provider); - resampler->resample((int*) output_vaddr, out_frames, &provider); - resampler->resample((int*) output_vaddr, out_frames, &provider); - resampler->resample((int*) output_vaddr, out_frames, &provider); + for (int i = 0; i < looplimit; ++i) { + resampler->setSampleRate(9000); + resampler->setSampleRate(12000); + resampler->setSampleRate(20000); + resampler->setSampleRate(30000); + } clock_gettime(CLOCK_MONOTONIC, &end); int64_t start_ns = start.tv_sec * 1000000000LL + start.tv_nsec; int64_t end_ns = end.tv_sec * 1000000000LL + end.tv_nsec; - int64_t time = (end_ns - start_ns)/4; - printf("%f Mspl/s\n", out_frames/(time/1e9)/1e6); + int64_t time = end_ns - start_ns; + printf("%.2f sample rate changes with filter calculation/sec\n", + looplimit * 4 / (time / 1e9)); + // Check how fast sample rate changes are without filter changes. + // This should be very fast, probably 0.1us - 1us per sample rate + // change. + resampler->setSampleRate(1000); + looplimit = 1000; + clock_gettime(CLOCK_MONOTONIC, &start); + for (int i = 0; i < looplimit; ++i) { + resampler->setSampleRate(1000+i); + } + clock_gettime(CLOCK_MONOTONIC, &end); + start_ns = start.tv_sec * 1000000000LL + start.tv_nsec; + end_ns = end.tv_sec * 1000000000LL + end.tv_nsec; + time = end_ns - start_ns; + printf("%.2f sample rate changes without filter calculation/sec\n", + looplimit / (time / 1e9)); + resampler->reset(); delete resampler; } - AudioResampler* resampler = AudioResampler::create(16, channels, + void* output_vaddr = malloc(output_size); + AudioResampler* resampler = AudioResampler::create(format, channels, output_freq, quality); - size_t out_frames = output_size/8; + resampler->setSampleRate(input_freq); - resampler->setVolume(0x1000, 0x1000); + resampler->setVolume(AudioResampler::UNITY_GAIN_FLOAT, AudioResampler::UNITY_GAIN_FLOAT); + + if (profileResample) { + /* + * For profiling on mobile devices, upon experimentation + * it is better to run a few trials with a shorter loop limit, + * and take the minimum time. + * + * Long tests can cause CPU temperature to build up and thermal throttling + * to reduce CPU frequency. + * + * For frequency checks (index=0, or 1, etc.): + * "cat /sys/devices/system/cpu/cpu${index}/cpufreq/scaling_*_freq" + * + * For temperature checks (index=0, or 1, etc.): + * "cat /sys/class/thermal/thermal_zone${index}/temp" + * + * Another way to avoid thermal throttling is to fix the CPU frequency + * at a lower level which prevents excessive temperatures. + */ + const int trials = 4; + const int looplimit = 4; + timespec start, end; + int64_t time = 0; + + for (int n = 0; n < trials; ++n) { + clock_gettime(CLOCK_MONOTONIC, &start); + for (int i = 0; i < looplimit; ++i) { + resampler->resample((int*) output_vaddr, output_frames, &provider); + provider.reset(); // during benchmarking reset only the provider + } + clock_gettime(CLOCK_MONOTONIC, &end); + int64_t start_ns = start.tv_sec * 1000000000LL + start.tv_nsec; + int64_t end_ns = end.tv_sec * 1000000000LL + end.tv_nsec; + int64_t diff_ns = end_ns - start_ns; + if (n == 0 || diff_ns < time) { + time = diff_ns; // save the best out of our trials. + } + } + // Mfrms/s is "Millions of output frames per second". + printf("quality: %d channels: %d msec: %" PRId64 " Mfrms/s: %.2lf\n", + quality, channels, time/1000000, output_frames * looplimit / (time / 1e9) / 1e6); + resampler->reset(); + } memset(output_vaddr, 0, output_size); - resampler->resample((int*) output_vaddr, out_frames, &provider); + if (gVerbose) { + printf("resample() %zu output frames\n", output_frames); + } + if (Ovalues.isEmpty()) { + Ovalues.push(output_frames); + } + for (size_t i = 0, j = 0; i < output_frames; ) { + size_t thisFrames = Ovalues[j++]; + if (j >= Ovalues.size()) { + j = 0; + } + if (thisFrames == 0 || thisFrames > output_frames - i) { + thisFrames = output_frames - i; + } + resampler->resample((int*) output_vaddr + output_channels*i, thisFrames, &provider); + i += thisFrames; + } + if (gVerbose) { + printf("resample() complete\n"); + } + resampler->reset(); + if (gVerbose) { + printf("reset() complete\n"); + } + delete resampler; + resampler = NULL; + + // For float processing, convert output format from float to Q4.27, + // which is then converted to int16_t for final storage. + if (useFloat) { + memcpy_to_q4_27_from_float(reinterpret_cast<int32_t*>(output_vaddr), + reinterpret_cast<float*>(output_vaddr), output_frames * output_channels); + } - // down-mix (we just truncate and keep the left channel) + // mono takes left channel only (out of stereo output pair) + // stereo and multichannel preserve all channels. int32_t* out = (int32_t*) output_vaddr; - int16_t* convert = (int16_t*) malloc(out_frames * channels * sizeof(int16_t)); - for (size_t i = 0; i < out_frames; i++) { - for (int j=0 ; j<channels ; j++) { - int32_t s = out[i * 2 + j] >> 12; - if (s > 32767) s = 32767; - else if (s < -32768) s = -32768; + int16_t* convert = (int16_t*) malloc(output_frames * channels * sizeof(int16_t)); + + const int volumeShift = 12; // shift requirement for Q4.27 to Q.15 + // round to half towards zero and saturate at int16 (non-dithered) + const int roundVal = (1<<(volumeShift-1)) - 1; // volumePrecision > 0 + + for (size_t i = 0; i < output_frames; i++) { + for (int j = 0; j < channels; j++) { + int32_t s = out[i * output_channels + j] + roundVal; // add offset here + if (s < 0) { + s = (s + 1) >> volumeShift; // round to 0 + if (s < -32768) { + s = -32768; + } + } else { + s = s >> volumeShift; + if (s > 32767) { + s = 32767; + } + } convert[i * channels + j] = int16_t(s); } } // write output to disk - int output_fd = open(file_out, O_WRONLY | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if (output_fd < 0) { - fprintf(stderr, "open: %s\n", strerror(errno)); - return -1; + SF_INFO info; + info.frames = 0; + info.samplerate = output_freq; + info.channels = channels; + info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; + SNDFILE *sf = sf_open(file_out, SFM_WRITE, &info); + if (sf == NULL) { + perror(file_out); + return EXIT_FAILURE; } + (void) sf_writef_short(sf, convert, output_frames); + sf_close(sf); - if (writeHeader) { - HeaderWav wav(out_frames * channels * sizeof(int16_t), channels, output_freq, 16); - write(output_fd, &wav, sizeof(wav)); - } - - write(output_fd, convert, out_frames * channels * sizeof(int16_t)); - close(output_fd); - - return 0; + return EXIT_SUCCESS; } diff --git a/services/audioflinger/tests/Android.mk b/services/audioflinger/tests/Android.mk new file mode 100644 index 0000000..7bba05b --- /dev/null +++ b/services/audioflinger/tests/Android.mk @@ -0,0 +1,73 @@ +# Build the unit tests for audioflinger + +# +# resampler unit test +# +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SHARED_LIBRARIES := \ + liblog \ + libutils \ + libcutils \ + libstlport \ + libaudioutils \ + libaudioresampler + +LOCAL_STATIC_LIBRARIES := \ + libgtest \ + libgtest_main + +LOCAL_C_INCLUDES := \ + bionic \ + bionic/libstdc++/include \ + external/gtest/include \ + external/stlport/stlport \ + $(call include-path-for, audio-utils) \ + frameworks/av/services/audioflinger + +LOCAL_SRC_FILES := \ + resampler_tests.cpp + +LOCAL_MODULE := resampler_tests +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) + +# +# audio mixer test tool +# +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + test-mixer.cpp \ + ../AudioMixer.cpp.arm \ + +LOCAL_C_INCLUDES := \ + bionic \ + bionic/libstdc++/include \ + external/stlport/stlport \ + $(call include-path-for, audio-effects) \ + $(call include-path-for, audio-utils) \ + frameworks/av/services/audioflinger + +LOCAL_STATIC_LIBRARIES := \ + libsndfile + +LOCAL_SHARED_LIBRARIES := \ + libstlport \ + libeffects \ + libnbaio \ + libcommon_time_client \ + libaudioresampler \ + libaudioutils \ + libdl \ + libcutils \ + libutils \ + liblog + +LOCAL_MODULE:= test-mixer + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_EXECUTABLE) diff --git a/services/audioflinger/tests/build_and_run_all_unit_tests.sh b/services/audioflinger/tests/build_and_run_all_unit_tests.sh new file mode 100755 index 0000000..2c453b0 --- /dev/null +++ b/services/audioflinger/tests/build_and_run_all_unit_tests.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +if [ -z "$ANDROID_BUILD_TOP" ]; then + echo "Android build environment not set" + exit -1 +fi + +# ensure we have mm +. $ANDROID_BUILD_TOP/build/envsetup.sh + +pushd $ANDROID_BUILD_TOP/frameworks/av/services/audioflinger/ +pwd +mm + +echo "waiting for device" +adb root && adb wait-for-device remount +adb push $OUT/system/lib/libaudioresampler.so /system/lib +adb push $OUT/system/bin/resampler_tests /system/bin + +sh $ANDROID_BUILD_TOP/frameworks/av/services/audioflinger/tests/run_all_unit_tests.sh + +popd diff --git a/services/audioflinger/tests/mixer_to_wav_tests.sh b/services/audioflinger/tests/mixer_to_wav_tests.sh new file mode 100755 index 0000000..9b39e77 --- /dev/null +++ b/services/audioflinger/tests/mixer_to_wav_tests.sh @@ -0,0 +1,134 @@ +#!/bin/bash +# +# This script uses test-mixer to generate WAV files +# for evaluation of the AudioMixer component. +# +# Sine and chirp signals are used for input because they +# show up as clear lines, either horizontal or diagonal, +# on a spectrogram. This means easy verification of multiple +# track mixing. +# +# After execution, look for created subdirectories like +# mixer_i_i +# mixer_i_f +# mixer_f_f +# +# Recommend using a program such as audacity to evaluate +# the output WAV files, e.g. +# +# cd testdir +# audacity *.wav +# +# Using Audacity: +# +# Under "Waveform" view mode you can zoom into the +# start of the WAV file to verify proper ramping. +# +# Select "Spectrogram" to see verify the lines +# (sine = horizontal, chirp = diagonal) which should +# be clear (except for around the start as the volume +# ramping causes spectral distortion). + +if [ -z "$ANDROID_BUILD_TOP" ]; then + echo "Android build environment not set" + exit -1 +fi + +# ensure we have mm +. $ANDROID_BUILD_TOP/build/envsetup.sh + +pushd $ANDROID_BUILD_TOP/frameworks/av/services/audioflinger/ + +# build +pwd +mm + +# send to device +echo "waiting for device" +adb root && adb wait-for-device remount +adb push $OUT/system/lib/libaudioresampler.so /system/lib +adb push $OUT/system/bin/test-mixer /system/bin + +# createwav creates a series of WAV files testing various +# mixer settings +# $1 = flags +# $2 = directory +function createwav() { +# create directory if it doesn't exist + if [ ! -d $2 ]; then + mkdir $2 + fi + +# Test: +# process__genericResampling +# track__Resample / track__genericResample + adb shell test-mixer $1 -s 48000 \ + -o /sdcard/tm48000gr.wav \ + sine:2,4000,7520 chirp:2,9200 sine:1,3000,18000 + adb pull /sdcard/tm48000gr.wav $2 + +# Test: +# process__genericResample +# track__Resample / track__genericResample +# track__NoResample / track__16BitsStereo / track__16BitsMono +# Aux buffer + adb shell test-mixer $1 -c 5 -s 9307 \ + -a /sdcard/aux9307gra.wav -o /sdcard/tm9307gra.wav \ + sine:4,1000,3000 sine:1,2000,9307 chirp:3,9307 + adb pull /sdcard/tm9307gra.wav $2 + adb pull /sdcard/aux9307gra.wav $2 + +# Test: +# process__genericNoResampling +# track__NoResample / track__16BitsStereo / track__16BitsMono + adb shell test-mixer $1 -s 32000 \ + -o /sdcard/tm32000gnr.wav \ + sine:2,1000,32000 chirp:2,32000 sine:1,3000,32000 + adb pull /sdcard/tm32000gnr.wav $2 + +# Test: +# process__genericNoResampling +# track__NoResample / track__16BitsStereo / track__16BitsMono +# Aux buffer + adb shell test-mixer $1 -s 32000 \ + -a /sdcard/aux32000gnra.wav -o /sdcard/tm32000gnra.wav \ + sine:2,1000,32000 chirp:2,32000 sine:1,3000,32000 + adb pull /sdcard/tm32000gnra.wav $2 + adb pull /sdcard/aux32000gnra.wav $2 + +# Test: +# process__NoResampleOneTrack / process__OneTrack16BitsStereoNoResampling +# Downmixer + adb shell test-mixer $1 -s 32000 \ + -o /sdcard/tm32000nrot.wav \ + sine:6,1000,32000 + adb pull /sdcard/tm32000nrot.wav $2 + +# Test: +# process__NoResampleOneTrack / OneTrack16BitsStereoNoResampling +# Aux buffer + adb shell test-mixer $1 -s 44100 \ + -a /sdcard/aux44100nrota.wav -o /sdcard/tm44100nrota.wav \ + sine:2,2000,44100 + adb pull /sdcard/tm44100nrota.wav $2 + adb pull /sdcard/aux44100nrota.wav $2 +} + +# +# Call createwav to generate WAV files in various combinations +# +# i_i = integer input track, integer mixer output +# f_f = float input track, float mixer output +# i_f = integer input track, float_mixer output +# +# If the mixer output is float, then the output WAV file is pcm float. +# +# TODO: create a "snr" like "diff" to automatically +# compare files in these directories together. +# + +createwav "" "tests/mixer_i_i" +createwav "-f -m" "tests/mixer_f_f" +createwav "-m" "tests/mixer_i_f" + +popd diff --git a/services/audioflinger/tests/resampler_tests.cpp b/services/audioflinger/tests/resampler_tests.cpp new file mode 100644 index 0000000..d6217ba --- /dev/null +++ b/services/audioflinger/tests/resampler_tests.cpp @@ -0,0 +1,411 @@ +/* + * 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_NDEBUG 0 +#define LOG_TAG "audioflinger_resampler_tests" + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <errno.h> +#include <time.h> +#include <math.h> +#include <vector> +#include <utility> +#include <iostream> +#include <cutils/log.h> +#include <gtest/gtest.h> +#include <media/AudioBufferProvider.h> +#include "AudioResampler.h" +#include "test_utils.h" + +void resample(int channels, void *output, + size_t outputFrames, const std::vector<size_t> &outputIncr, + android::AudioBufferProvider *provider, android::AudioResampler *resampler) +{ + for (size_t i = 0, j = 0; i < outputFrames; ) { + size_t thisFrames = outputIncr[j++]; + if (j >= outputIncr.size()) { + j = 0; + } + if (thisFrames == 0 || thisFrames > outputFrames - i) { + thisFrames = outputFrames - i; + } + resampler->resample((int32_t*) output + channels*i, thisFrames, provider); + i += thisFrames; + } +} + +void buffercmp(const void *reference, const void *test, + size_t outputFrameSize, size_t outputFrames) +{ + for (size_t i = 0; i < outputFrames; ++i) { + int check = memcmp((const char*)reference + i * outputFrameSize, + (const char*)test + i * outputFrameSize, outputFrameSize); + if (check) { + ALOGE("Failure at frame %zu", i); + ASSERT_EQ(check, 0); /* fails */ + } + } +} + +void testBufferIncrement(size_t channels, bool useFloat, + unsigned inputFreq, unsigned outputFreq, + enum android::AudioResampler::src_quality quality) +{ + const audio_format_t format = useFloat ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; + // create the provider + std::vector<int> inputIncr; + SignalProvider provider; + if (useFloat) { + provider.setChirp<float>(channels, + 0., outputFreq/2., outputFreq, outputFreq/2000.); + } else { + provider.setChirp<int16_t>(channels, + 0., outputFreq/2., outputFreq, outputFreq/2000.); + } + provider.setIncr(inputIncr); + + // calculate the output size + size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq; + size_t outputFrameSize = channels * (useFloat ? sizeof(float) : sizeof(int32_t)); + size_t outputSize = outputFrameSize * outputFrames; + outputSize &= ~7; + + // create the resampler + android::AudioResampler* resampler; + + resampler = android::AudioResampler::create(format, channels, outputFreq, quality); + resampler->setSampleRate(inputFreq); + resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT, + android::AudioResampler::UNITY_GAIN_FLOAT); + + // set up the reference run + std::vector<size_t> refIncr; + refIncr.push_back(outputFrames); + void* reference = malloc(outputSize); + resample(channels, reference, outputFrames, refIncr, &provider, resampler); + + provider.reset(); + +#if 0 + /* this test will fail - API interface issue: reset() does not clear internal buffers */ + resampler->reset(); +#else + delete resampler; + resampler = android::AudioResampler::create(format, channels, outputFreq, quality); + resampler->setSampleRate(inputFreq); + resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT, + android::AudioResampler::UNITY_GAIN_FLOAT); +#endif + + // set up the test run + std::vector<size_t> outIncr; + outIncr.push_back(1); + outIncr.push_back(2); + outIncr.push_back(3); + void* test = malloc(outputSize); + inputIncr.push_back(1); + inputIncr.push_back(3); + provider.setIncr(inputIncr); + resample(channels, test, outputFrames, outIncr, &provider, resampler); + + // check + buffercmp(reference, test, outputFrameSize, outputFrames); + + free(reference); + free(test); + delete resampler; +} + +template <typename T> +inline double sqr(T v) +{ + double dv = static_cast<double>(v); + return dv * dv; +} + +template <typename T> +double signalEnergy(T *start, T *end, unsigned stride) +{ + double accum = 0; + + for (T *p = start; p < end; p += stride) { + accum += sqr(*p); + } + unsigned count = (end - start + stride - 1) / stride; + return accum / count; +} + +// TI = resampler input type, int16_t or float +// TO = resampler output type, int32_t or float +template <typename TI, typename TO> +void testStopbandDownconversion(size_t channels, + unsigned inputFreq, unsigned outputFreq, + unsigned passband, unsigned stopband, + enum android::AudioResampler::src_quality quality) +{ + // create the provider + std::vector<int> inputIncr; + SignalProvider provider; + provider.setChirp<TI>(channels, + 0., inputFreq/2., inputFreq, inputFreq/2000.); + provider.setIncr(inputIncr); + + // calculate the output size + size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq; + size_t outputFrameSize = channels * sizeof(TO); + size_t outputSize = outputFrameSize * outputFrames; + outputSize &= ~7; + + // create the resampler + android::AudioResampler* resampler; + + resampler = android::AudioResampler::create( + is_same<TI, int16_t>::value ? AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_FLOAT, + channels, outputFreq, quality); + resampler->setSampleRate(inputFreq); + resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT, + android::AudioResampler::UNITY_GAIN_FLOAT); + + // set up the reference run + std::vector<size_t> refIncr; + refIncr.push_back(outputFrames); + void* reference = malloc(outputSize); + resample(channels, reference, outputFrames, refIncr, &provider, resampler); + + TO *out = reinterpret_cast<TO *>(reference); + + // check signal energy in passband + const unsigned passbandFrame = passband * outputFreq / 1000.; + const unsigned stopbandFrame = stopband * outputFreq / 1000.; + + // check each channel separately + for (size_t i = 0; i < channels; ++i) { + double passbandEnergy = signalEnergy(out, out + passbandFrame * channels, channels); + double stopbandEnergy = signalEnergy(out + stopbandFrame * channels, + out + outputFrames * channels, channels); + double dbAtten = -10. * log10(stopbandEnergy / passbandEnergy); + ASSERT_GT(dbAtten, 60.); + +#if 0 + // internal verification + printf("if:%d of:%d pbf:%d sbf:%d sbe: %f pbe: %f db: %.2f\n", + provider.getNumFrames(), outputFrames, + passbandFrame, stopbandFrame, stopbandEnergy, passbandEnergy, dbAtten); + for (size_t i = 0; i < 10; ++i) { + std::cout << out[i+passbandFrame*channels] << std::endl; + } + for (size_t i = 0; i < 10; ++i) { + std::cout << out[i+stopbandFrame*channels] << std::endl; + } +#endif + } + + free(reference); + delete resampler; +} + +/* Buffer increment test + * + * We compare a reference output, where we consume and process the entire + * buffer at a time, and a test output, where we provide small chunks of input + * data and process small chunks of output (which may not be equivalent in size). + * + * Two subtests - fixed phase (3:2 down) and interpolated phase (147:320 up) + */ +TEST(audioflinger_resampler, bufferincrement_fixedphase) { + // all of these work + static const enum android::AudioResampler::src_quality kQualityArray[] = { + android::AudioResampler::LOW_QUALITY, + android::AudioResampler::MED_QUALITY, + android::AudioResampler::HIGH_QUALITY, + android::AudioResampler::VERY_HIGH_QUALITY, + android::AudioResampler::DYN_LOW_QUALITY, + android::AudioResampler::DYN_MED_QUALITY, + android::AudioResampler::DYN_HIGH_QUALITY, + }; + + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testBufferIncrement(2, false, 48000, 32000, kQualityArray[i]); + } +} + +TEST(audioflinger_resampler, bufferincrement_interpolatedphase) { + // all of these work except low quality + static const enum android::AudioResampler::src_quality kQualityArray[] = { +// android::AudioResampler::LOW_QUALITY, + android::AudioResampler::MED_QUALITY, + android::AudioResampler::HIGH_QUALITY, + android::AudioResampler::VERY_HIGH_QUALITY, + android::AudioResampler::DYN_LOW_QUALITY, + android::AudioResampler::DYN_MED_QUALITY, + android::AudioResampler::DYN_HIGH_QUALITY, + }; + + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testBufferIncrement(2, false, 22050, 48000, kQualityArray[i]); + } +} + +TEST(audioflinger_resampler, bufferincrement_fixedphase_multi) { + // only dynamic quality + static const enum android::AudioResampler::src_quality kQualityArray[] = { + android::AudioResampler::DYN_LOW_QUALITY, + android::AudioResampler::DYN_MED_QUALITY, + android::AudioResampler::DYN_HIGH_QUALITY, + }; + + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testBufferIncrement(4, false, 48000, 32000, kQualityArray[i]); + } +} + +TEST(audioflinger_resampler, bufferincrement_interpolatedphase_multi_float) { + // only dynamic quality + static const enum android::AudioResampler::src_quality kQualityArray[] = { + android::AudioResampler::DYN_LOW_QUALITY, + android::AudioResampler::DYN_MED_QUALITY, + android::AudioResampler::DYN_HIGH_QUALITY, + }; + + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testBufferIncrement(8, true, 22050, 48000, kQualityArray[i]); + } +} + +/* Simple aliasing test + * + * This checks stopband response of the chirp signal to make sure frequencies + * are properly suppressed. It uses downsampling because the stopband can be + * clearly isolated by input frequencies exceeding the output sample rate (nyquist). + */ +TEST(audioflinger_resampler, stopbandresponse_integer) { + // not all of these may work (old resamplers fail on downsampling) + static const enum android::AudioResampler::src_quality kQualityArray[] = { + //android::AudioResampler::LOW_QUALITY, + //android::AudioResampler::MED_QUALITY, + //android::AudioResampler::HIGH_QUALITY, + //android::AudioResampler::VERY_HIGH_QUALITY, + android::AudioResampler::DYN_LOW_QUALITY, + android::AudioResampler::DYN_MED_QUALITY, + android::AudioResampler::DYN_HIGH_QUALITY, + }; + + // in this test we assume a maximum transition band between 12kHz and 20kHz. + // there must be at least 60dB relative attenuation between stopband and passband. + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testStopbandDownconversion<int16_t, int32_t>( + 2, 48000, 32000, 12000, 20000, kQualityArray[i]); + } + + // in this test we assume a maximum transition band between 7kHz and 15kHz. + // there must be at least 60dB relative attenuation between stopband and passband. + // (the weird ratio triggers interpolative resampling) + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testStopbandDownconversion<int16_t, int32_t>( + 2, 48000, 22101, 7000, 15000, kQualityArray[i]); + } +} + +TEST(audioflinger_resampler, stopbandresponse_integer_multichannel) { + // not all of these may work (old resamplers fail on downsampling) + static const enum android::AudioResampler::src_quality kQualityArray[] = { + //android::AudioResampler::LOW_QUALITY, + //android::AudioResampler::MED_QUALITY, + //android::AudioResampler::HIGH_QUALITY, + //android::AudioResampler::VERY_HIGH_QUALITY, + android::AudioResampler::DYN_LOW_QUALITY, + android::AudioResampler::DYN_MED_QUALITY, + android::AudioResampler::DYN_HIGH_QUALITY, + }; + + // in this test we assume a maximum transition band between 12kHz and 20kHz. + // there must be at least 60dB relative attenuation between stopband and passband. + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testStopbandDownconversion<int16_t, int32_t>( + 8, 48000, 32000, 12000, 20000, kQualityArray[i]); + } + + // in this test we assume a maximum transition band between 7kHz and 15kHz. + // there must be at least 60dB relative attenuation between stopband and passband. + // (the weird ratio triggers interpolative resampling) + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testStopbandDownconversion<int16_t, int32_t>( + 8, 48000, 22101, 7000, 15000, kQualityArray[i]); + } +} + +TEST(audioflinger_resampler, stopbandresponse_float) { + // not all of these may work (old resamplers fail on downsampling) + static const enum android::AudioResampler::src_quality kQualityArray[] = { + //android::AudioResampler::LOW_QUALITY, + //android::AudioResampler::MED_QUALITY, + //android::AudioResampler::HIGH_QUALITY, + //android::AudioResampler::VERY_HIGH_QUALITY, + android::AudioResampler::DYN_LOW_QUALITY, + android::AudioResampler::DYN_MED_QUALITY, + android::AudioResampler::DYN_HIGH_QUALITY, + }; + + // in this test we assume a maximum transition band between 12kHz and 20kHz. + // there must be at least 60dB relative attenuation between stopband and passband. + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testStopbandDownconversion<float, float>( + 2, 48000, 32000, 12000, 20000, kQualityArray[i]); + } + + // in this test we assume a maximum transition band between 7kHz and 15kHz. + // there must be at least 60dB relative attenuation between stopband and passband. + // (the weird ratio triggers interpolative resampling) + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testStopbandDownconversion<float, float>( + 2, 48000, 22101, 7000, 15000, kQualityArray[i]); + } +} + +TEST(audioflinger_resampler, stopbandresponse_float_multichannel) { + // not all of these may work (old resamplers fail on downsampling) + static const enum android::AudioResampler::src_quality kQualityArray[] = { + //android::AudioResampler::LOW_QUALITY, + //android::AudioResampler::MED_QUALITY, + //android::AudioResampler::HIGH_QUALITY, + //android::AudioResampler::VERY_HIGH_QUALITY, + android::AudioResampler::DYN_LOW_QUALITY, + android::AudioResampler::DYN_MED_QUALITY, + android::AudioResampler::DYN_HIGH_QUALITY, + }; + + // in this test we assume a maximum transition band between 12kHz and 20kHz. + // there must be at least 60dB relative attenuation between stopband and passband. + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testStopbandDownconversion<float, float>( + 8, 48000, 32000, 12000, 20000, kQualityArray[i]); + } + + // in this test we assume a maximum transition band between 7kHz and 15kHz. + // there must be at least 60dB relative attenuation between stopband and passband. + // (the weird ratio triggers interpolative resampling) + for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) { + testStopbandDownconversion<float, float>( + 8, 48000, 22101, 7000, 15000, kQualityArray[i]); + } +} + diff --git a/services/audioflinger/tests/run_all_unit_tests.sh b/services/audioflinger/tests/run_all_unit_tests.sh new file mode 100755 index 0000000..ffae6ae --- /dev/null +++ b/services/audioflinger/tests/run_all_unit_tests.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +if [ -z "$ANDROID_BUILD_TOP" ]; then + echo "Android build environment not set" + exit -1 +fi + +echo "waiting for device" +adb root && adb wait-for-device remount + +adb shell /system/bin/resampler_tests diff --git a/services/audioflinger/tests/test-mixer.cpp b/services/audioflinger/tests/test-mixer.cpp new file mode 100644 index 0000000..9a4fad6 --- /dev/null +++ b/services/audioflinger/tests/test-mixer.cpp @@ -0,0 +1,306 @@ +/* + * 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 <stdio.h> +#include <inttypes.h> +#include <math.h> +#include <vector> +#include <audio_utils/primitives.h> +#include <audio_utils/sndfile.h> +#include <media/AudioBufferProvider.h> +#include "AudioMixer.h" +#include "test_utils.h" + +/* Testing is typically through creation of an output WAV file from several + * source inputs, to be later analyzed by an audio program such as Audacity. + * + * Sine or chirp functions are typically more useful as input to the mixer + * as they show up as straight lines on a spectrogram if successfully mixed. + * + * A sample shell script is provided: mixer_to_wave_tests.sh + */ + +using namespace android; + +static void usage(const char* name) { + fprintf(stderr, "Usage: %s [-f] [-m] [-c channels]" + " [-s sample-rate] [-o <output-file>] [-a <aux-buffer-file>] [-P csv]" + " (<input-file> | <command>)+\n", name); + fprintf(stderr, " -f enable floating point input track\n"); + fprintf(stderr, " -m enable floating point mixer output\n"); + fprintf(stderr, " -c number of mixer output channels\n"); + fprintf(stderr, " -s mixer sample-rate\n"); + fprintf(stderr, " -o <output-file> WAV file, pcm16 (or float if -m specified)\n"); + fprintf(stderr, " -a <aux-buffer-file>\n"); + fprintf(stderr, " -P # frames provided per call to resample() in CSV format\n"); + fprintf(stderr, " <input-file> is a WAV file\n"); + fprintf(stderr, " <command> can be 'sine:<channels>,<frequency>,<samplerate>'\n"); + fprintf(stderr, " 'chirp:<channels>,<samplerate>'\n"); +} + +static int writeFile(const char *filename, const void *buffer, + uint32_t sampleRate, uint32_t channels, size_t frames, bool isBufferFloat) { + if (filename == NULL) { + return 0; // ok to pass in NULL filename + } + // write output to file. + SF_INFO info; + info.frames = 0; + info.samplerate = sampleRate; + info.channels = channels; + info.format = SF_FORMAT_WAV | (isBufferFloat ? SF_FORMAT_FLOAT : SF_FORMAT_PCM_16); + printf("saving file:%s channels:%u samplerate:%u frames:%zu\n", + filename, info.channels, info.samplerate, frames); + SNDFILE *sf = sf_open(filename, SFM_WRITE, &info); + if (sf == NULL) { + perror(filename); + return EXIT_FAILURE; + } + if (isBufferFloat) { + (void) sf_writef_float(sf, (float*)buffer, frames); + } else { + (void) sf_writef_short(sf, (short*)buffer, frames); + } + sf_close(sf); + return EXIT_SUCCESS; +} + +int main(int argc, char* argv[]) { + const char* const progname = argv[0]; + bool useInputFloat = false; + bool useMixerFloat = false; + bool useRamp = true; + uint32_t outputSampleRate = 48000; + uint32_t outputChannels = 2; // stereo for now + std::vector<int> Pvalues; + const char* outputFilename = NULL; + const char* auxFilename = NULL; + std::vector<int32_t> Names; + std::vector<SignalProvider> Providers; + + for (int ch; (ch = getopt(argc, argv, "fmc:s:o:a:P:")) != -1;) { + switch (ch) { + case 'f': + useInputFloat = true; + break; + case 'm': + useMixerFloat = true; + break; + case 'c': + outputChannels = atoi(optarg); + break; + case 's': + outputSampleRate = atoi(optarg); + break; + case 'o': + outputFilename = optarg; + break; + case 'a': + auxFilename = optarg; + break; + case 'P': + if (parseCSV(optarg, Pvalues) < 0) { + fprintf(stderr, "incorrect syntax for -P option\n"); + return EXIT_FAILURE; + } + break; + case '?': + default: + usage(progname); + return EXIT_FAILURE; + } + } + argc -= optind; + argv += optind; + + if (argc == 0) { + usage(progname); + return EXIT_FAILURE; + } + if ((unsigned)argc > AudioMixer::MAX_NUM_TRACKS) { + fprintf(stderr, "too many tracks: %d > %u", argc, AudioMixer::MAX_NUM_TRACKS); + return EXIT_FAILURE; + } + + size_t outputFrames = 0; + + // create providers for each track + Providers.resize(argc); + for (int i = 0; i < argc; ++i) { + static const char chirp[] = "chirp:"; + static const char sine[] = "sine:"; + static const double kSeconds = 1; + + if (!strncmp(argv[i], chirp, strlen(chirp))) { + std::vector<int> v; + + parseCSV(argv[i] + strlen(chirp), v); + if (v.size() == 2) { + printf("creating chirp(%d %d)\n", v[0], v[1]); + if (useInputFloat) { + Providers[i].setChirp<float>(v[0], 0, v[1]/2, v[1], kSeconds); + } else { + Providers[i].setChirp<int16_t>(v[0], 0, v[1]/2, v[1], kSeconds); + } + Providers[i].setIncr(Pvalues); + } else { + fprintf(stderr, "malformed input '%s'\n", argv[i]); + } + } else if (!strncmp(argv[i], sine, strlen(sine))) { + std::vector<int> v; + + parseCSV(argv[i] + strlen(sine), v); + if (v.size() == 3) { + printf("creating sine(%d %d %d)\n", v[0], v[1], v[2]); + if (useInputFloat) { + Providers[i].setSine<float>(v[0], v[1], v[2], kSeconds); + } else { + Providers[i].setSine<int16_t>(v[0], v[1], v[2], kSeconds); + } + Providers[i].setIncr(Pvalues); + } else { + fprintf(stderr, "malformed input '%s'\n", argv[i]); + } + } else { + printf("creating filename(%s)\n", argv[i]); + if (useInputFloat) { + Providers[i].setFile<float>(argv[i]); + } else { + Providers[i].setFile<short>(argv[i]); + } + Providers[i].setIncr(Pvalues); + } + // calculate the number of output frames + size_t nframes = (int64_t) Providers[i].getNumFrames() * outputSampleRate + / Providers[i].getSampleRate(); + if (i == 0 || outputFrames > nframes) { // choose minimum for outputFrames + outputFrames = nframes; + } + } + + // create the output buffer. + const size_t outputFrameSize = outputChannels + * (useMixerFloat ? sizeof(float) : sizeof(int16_t)); + const size_t outputSize = outputFrames * outputFrameSize; + const audio_channel_mask_t outputChannelMask = + audio_channel_out_mask_from_count(outputChannels); + void *outputAddr = NULL; + (void) posix_memalign(&outputAddr, 32, outputSize); + memset(outputAddr, 0, outputSize); + + // create the aux buffer, if needed. + const size_t auxFrameSize = sizeof(int32_t); // Q4.27 always + const size_t auxSize = outputFrames * auxFrameSize; + void *auxAddr = NULL; + if (auxFilename) { + (void) posix_memalign(&auxAddr, 32, auxSize); + memset(auxAddr, 0, auxSize); + } + + // create the mixer. + const size_t mixerFrameCount = 320; // typical numbers may range from 240 or 960 + AudioMixer *mixer = new AudioMixer(mixerFrameCount, outputSampleRate); + audio_format_t inputFormat = useInputFloat + ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; + audio_format_t mixerFormat = useMixerFloat + ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; + float f = AudioMixer::UNITY_GAIN_FLOAT / Providers.size(); // normalize volume by # tracks + static float f0; // zero + + // set up the tracks. + for (size_t i = 0; i < Providers.size(); ++i) { + //printf("track %d out of %d\n", i, Providers.size()); + uint32_t channelMask = audio_channel_out_mask_from_count(Providers[i].getNumChannels()); + int32_t name = mixer->getTrackName(channelMask, + inputFormat, AUDIO_SESSION_OUTPUT_MIX); + ALOG_ASSERT(name >= 0); + Names.push_back(name); + mixer->setBufferProvider(name, &Providers[i]); + mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, + (void *)outputAddr); + mixer->setParameter( + name, + AudioMixer::TRACK, + AudioMixer::MIXER_FORMAT, + (void *)(uintptr_t)mixerFormat); + mixer->setParameter( + name, + AudioMixer::TRACK, + AudioMixer::FORMAT, + (void *)(uintptr_t)inputFormat); + mixer->setParameter( + name, + AudioMixer::TRACK, + AudioMixer::MIXER_CHANNEL_MASK, + (void *)(uintptr_t)outputChannelMask); + mixer->setParameter( + name, + AudioMixer::TRACK, + AudioMixer::CHANNEL_MASK, + (void *)(uintptr_t)channelMask); + mixer->setParameter( + name, + AudioMixer::RESAMPLE, + AudioMixer::SAMPLE_RATE, + (void *)(uintptr_t)Providers[i].getSampleRate()); + if (useRamp) { + mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f0); + mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f0); + mixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME0, &f); + mixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::VOLUME1, &f); + } else { + mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f); + mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f); + } + if (auxFilename) { + mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::AUX_BUFFER, + (void *) auxAddr); + mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::AUXLEVEL, &f0); + mixer->setParameter(name, AudioMixer::RAMP_VOLUME, AudioMixer::AUXLEVEL, &f); + } + mixer->enable(name); + } + + // pump the mixer to process data. + size_t i; + for (i = 0; i < outputFrames - mixerFrameCount; i += mixerFrameCount) { + for (size_t j = 0; j < Names.size(); ++j) { + mixer->setParameter(Names[j], AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, + (char *) outputAddr + i * outputFrameSize); + if (auxFilename) { + mixer->setParameter(Names[j], AudioMixer::TRACK, AudioMixer::AUX_BUFFER, + (char *) auxAddr + i * auxFrameSize); + } + } + mixer->process(AudioBufferProvider::kInvalidPTS); + } + outputFrames = i; // reset output frames to the data actually produced. + + // write to files + writeFile(outputFilename, outputAddr, + outputSampleRate, outputChannels, outputFrames, useMixerFloat); + if (auxFilename) { + // Aux buffer is always in q4_27 format for now. + // memcpy_to_i16_from_q4_27(), but with stereo frame count (not sample count) + ditherAndClamp((int32_t*)auxAddr, (int32_t*)auxAddr, outputFrames >> 1); + writeFile(auxFilename, auxAddr, outputSampleRate, 1, outputFrames, false); + } + + delete mixer; + free(outputAddr); + free(auxAddr); + return EXIT_SUCCESS; +} diff --git a/services/audioflinger/tests/test_utils.h b/services/audioflinger/tests/test_utils.h new file mode 100644 index 0000000..3d51cdc --- /dev/null +++ b/services/audioflinger/tests/test_utils.h @@ -0,0 +1,307 @@ +/* + * 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_TEST_UTILS_H +#define ANDROID_AUDIO_TEST_UTILS_H + +#include <audio_utils/sndfile.h> + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#endif + +template<typename T, typename U> +struct is_same +{ + static const bool value = false; +}; + +template<typename T> +struct is_same<T, T> // partial specialization +{ + static const bool value = true; +}; + +template<typename T> +static inline T convertValue(double val) +{ + if (is_same<T, int16_t>::value) { + return floor(val * 32767.0 + 0.5); + } else if (is_same<T, int32_t>::value) { + return floor(val * (1UL<<31) + 0.5); + } + return val; // assume float or double +} + +// Convert a list of integers in CSV format to a Vector of those values. +// Returns the number of elements in the list, or -1 on error. +static inline int parseCSV(const char *string, std::vector<int>& values) +{ + // pass 1: count the number of values and do syntax check + size_t numValues = 0; + bool hadDigit = false; + for (const char *p = string; ; ) { + switch (*p++) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + hadDigit = true; + break; + case '\0': + if (hadDigit) { + // pass 2: allocate and initialize vector of values + values.resize(++numValues); + values[0] = atoi(p = string); + for (size_t i = 1; i < numValues; ) { + if (*p++ == ',') { + values[i++] = atoi(p); + } + } + return numValues; + } + // fall through + case ',': + if (hadDigit) { + hadDigit = false; + numValues++; + break; + } + // fall through + default: + return -1; + } + } +} + +/* Creates a type-independent audio buffer provider from + * a buffer base address, size, framesize, and input increment array. + * + * No allocation or deallocation of the provided buffer is done. + */ +class TestProvider : public android::AudioBufferProvider { +public: + TestProvider(void* addr, size_t frames, size_t frameSize, + const std::vector<int>& inputIncr) + : mAddr(addr), + mNumFrames(frames), + mFrameSize(frameSize), + mNextFrame(0), mUnrel(0), mInputIncr(inputIncr), mNextIdx(0) + { + } + + TestProvider() + : mAddr(NULL), mNumFrames(0), mFrameSize(0), + mNextFrame(0), mUnrel(0), mNextIdx(0) + { + } + + void setIncr(const std::vector<int>& inputIncr) { + mInputIncr = inputIncr; + mNextIdx = 0; + } + + virtual android::status_t getNextBuffer(Buffer* buffer, int64_t pts __unused = kInvalidPTS) + { + size_t requestedFrames = buffer->frameCount; + if (requestedFrames > mNumFrames - mNextFrame) { + buffer->frameCount = mNumFrames - mNextFrame; + } + if (!mInputIncr.empty()) { + size_t provided = mInputIncr[mNextIdx++]; + ALOGV("getNextBuffer() mValue[%zu]=%zu not %zu", + mNextIdx-1, provided, buffer->frameCount); + if (provided < buffer->frameCount) { + buffer->frameCount = provided; + } + if (mNextIdx >= mInputIncr.size()) { + mNextIdx = 0; + } + } + ALOGV("getNextBuffer() requested %zu frames out of %zu frames available" + " and returned %zu frames", + requestedFrames, mNumFrames - mNextFrame, buffer->frameCount); + mUnrel = buffer->frameCount; + if (buffer->frameCount > 0) { + buffer->raw = (char *)mAddr + mFrameSize * mNextFrame; + return android::NO_ERROR; + } else { + buffer->raw = NULL; + return android::NOT_ENOUGH_DATA; + } + } + + virtual void releaseBuffer(Buffer* buffer) + { + if (buffer->frameCount > mUnrel) { + ALOGE("releaseBuffer() released %zu frames but only %zu available " + "to release", buffer->frameCount, mUnrel); + mNextFrame += mUnrel; + mUnrel = 0; + } else { + + ALOGV("releaseBuffer() released %zu frames out of %zu frames available " + "to release", buffer->frameCount, mUnrel); + mNextFrame += buffer->frameCount; + mUnrel -= buffer->frameCount; + } + buffer->frameCount = 0; + buffer->raw = NULL; + } + + void reset() + { + mNextFrame = 0; + } + + size_t getNumFrames() + { + return mNumFrames; + } + + +protected: + void* mAddr; // base address + size_t mNumFrames; // total frames + int mFrameSize; // frame size (# channels * bytes per sample) + size_t mNextFrame; // index of next frame to provide + size_t mUnrel; // number of frames not yet released + std::vector<int> mInputIncr; // number of frames provided per call + size_t mNextIdx; // index of next entry in mInputIncr to use +}; + +/* Creates a buffer filled with a sine wave. + */ +template<typename T> +static void createSine(void *vbuffer, size_t frames, + size_t channels, double sampleRate, double freq) +{ + double tscale = 1. / sampleRate; + T* buffer = reinterpret_cast<T*>(vbuffer); + for (size_t i = 0; i < frames; ++i) { + double t = i * tscale; + double y = sin(2. * M_PI * freq * t); + T yt = convertValue<T>(y); + + for (size_t j = 0; j < channels; ++j) { + buffer[i*channels + j] = yt / T(j + 1); + } + } +} + +/* Creates a buffer filled with a chirp signal (a sine wave sweep). + * + * When creating the Chirp, note that the frequency is the true sinusoidal + * frequency not the sampling rate. + * + * http://en.wikipedia.org/wiki/Chirp + */ +template<typename T> +static void createChirp(void *vbuffer, size_t frames, + size_t channels, double sampleRate, double minfreq, double maxfreq) +{ + double tscale = 1. / sampleRate; + T *buffer = reinterpret_cast<T*>(vbuffer); + // note the chirp constant k has a divide-by-two. + double k = (maxfreq - minfreq) / (2. * tscale * frames); + for (size_t i = 0; i < frames; ++i) { + double t = i * tscale; + double y = sin(2. * M_PI * (k * t + minfreq) * t); + T yt = convertValue<T>(y); + + for (size_t j = 0; j < channels; ++j) { + buffer[i*channels + j] = yt / T(j + 1); + } + } +} + +/* This derived class creates a buffer provider of datatype T, + * consisting of an input signal, e.g. from createChirp(). + * The number of frames can be obtained from the base class + * TestProvider::getNumFrames(). + */ + +class SignalProvider : public TestProvider { +public: + SignalProvider() + : mSampleRate(0), + mChannels(0) + { + } + + virtual ~SignalProvider() + { + free(mAddr); + mAddr = NULL; + } + + template <typename T> + void setChirp(size_t channels, double minfreq, double maxfreq, double sampleRate, double time) + { + createBufferByFrames<T>(channels, sampleRate, sampleRate*time); + createChirp<T>(mAddr, mNumFrames, mChannels, mSampleRate, minfreq, maxfreq); + } + + template <typename T> + void setSine(size_t channels, + double freq, double sampleRate, double time) + { + createBufferByFrames<T>(channels, sampleRate, sampleRate*time); + createSine<T>(mAddr, mNumFrames, mChannels, mSampleRate, freq); + } + + template <typename T> + void setFile(const char *file_in) + { + SF_INFO info; + info.format = 0; + SNDFILE *sf = sf_open(file_in, SFM_READ, &info); + if (sf == NULL) { + perror(file_in); + return; + } + createBufferByFrames<T>(info.channels, info.samplerate, info.frames); + if (is_same<T, float>::value) { + (void) sf_readf_float(sf, (float *) mAddr, mNumFrames); + } else if (is_same<T, short>::value) { + (void) sf_readf_short(sf, (short *) mAddr, mNumFrames); + } + sf_close(sf); + } + + template <typename T> + void createBufferByFrames(size_t channels, uint32_t sampleRate, size_t frames) + { + mNumFrames = frames; + mChannels = channels; + mFrameSize = mChannels * sizeof(T); + free(mAddr); + mAddr = malloc(mFrameSize * mNumFrames); + mSampleRate = sampleRate; + } + + uint32_t getSampleRate() const { + return mSampleRate; + } + + uint32_t getNumChannels() const { + return mChannels; + } + +protected: + uint32_t mSampleRate; + uint32_t mChannels; +}; + +#endif // ANDROID_AUDIO_TEST_UTILS_H diff --git a/services/audiopolicy/Android.mk b/services/audiopolicy/Android.mk new file mode 100644 index 0000000..6512c38 --- /dev/null +++ b/services/audiopolicy/Android.mk @@ -0,0 +1,86 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + AudioPolicyService.cpp \ + AudioPolicyEffects.cpp + +ifeq ($(USE_LEGACY_AUDIO_POLICY), 1) +LOCAL_SRC_FILES += \ + AudioPolicyInterfaceImplLegacy.cpp \ + AudioPolicyClientImplLegacy.cpp + + LOCAL_CFLAGS += -DUSE_LEGACY_AUDIO_POLICY +else +LOCAL_SRC_FILES += \ + AudioPolicyInterfaceImpl.cpp \ + AudioPolicyClientImpl.cpp +endif + +LOCAL_C_INCLUDES := \ + $(TOPDIR)frameworks/av/services/audioflinger \ + $(call include-path-for, audio-effects) \ + $(call include-path-for, audio-utils) + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + liblog \ + libbinder \ + libmedia \ + libhardware \ + libhardware_legacy + +ifneq ($(USE_LEGACY_AUDIO_POLICY), 1) +LOCAL_SHARED_LIBRARIES += \ + libaudiopolicymanager +endif + +LOCAL_STATIC_LIBRARIES := \ + libmedia_helper \ + libserviceutility + +LOCAL_MODULE:= libaudiopolicyservice + +LOCAL_CFLAGS += -fvisibility=hidden + +include $(BUILD_SHARED_LIBRARY) + + +ifneq ($(USE_LEGACY_AUDIO_POLICY), 1) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + AudioPolicyManager.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + liblog \ + libsoundtrigger + +LOCAL_STATIC_LIBRARIES := \ + libmedia_helper + +LOCAL_MODULE:= libaudiopolicymanagerdefault + +include $(BUILD_SHARED_LIBRARY) + +ifneq ($(USE_CUSTOM_AUDIO_POLICY), 1) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + AudioPolicyFactory.cpp + +LOCAL_SHARED_LIBRARIES := \ + libaudiopolicymanagerdefault + +LOCAL_MODULE:= libaudiopolicymanager + +include $(BUILD_SHARED_LIBRARY) + +endif +endif diff --git a/services/audiopolicy/AudioPolicyClientImpl.cpp b/services/audiopolicy/AudioPolicyClientImpl.cpp new file mode 100644 index 0000000..3e090e9 --- /dev/null +++ b/services/audiopolicy/AudioPolicyClientImpl.cpp @@ -0,0 +1,221 @@ +/* + * 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 "AudioPolicyClientImpl" +//#define LOG_NDEBUG 0 + +#include <soundtrigger/SoundTrigger.h> +#include <utils/Log.h> +#include "AudioPolicyService.h" + +namespace android { + +/* implementation of the client interface from the policy manager */ + +audio_module_handle_t AudioPolicyService::AudioPolicyClient::loadHwModule(const char *name) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return 0; + } + + return af->loadHwModule(name); +} + +status_t AudioPolicyService::AudioPolicyClient::openOutput(audio_module_handle_t module, + audio_io_handle_t *output, + audio_config_t *config, + audio_devices_t *devices, + const String8& address, + uint32_t *latencyMs, + audio_output_flags_t flags) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return PERMISSION_DENIED; + } + return af->openOutput(module, output, config, devices, address, latencyMs, flags); +} + +audio_io_handle_t AudioPolicyService::AudioPolicyClient::openDuplicateOutput( + audio_io_handle_t output1, + audio_io_handle_t output2) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return 0; + } + return af->openDuplicateOutput(output1, output2); +} + +status_t AudioPolicyService::AudioPolicyClient::closeOutput(audio_io_handle_t output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->closeOutput(output); +} + +status_t AudioPolicyService::AudioPolicyClient::suspendOutput(audio_io_handle_t output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return PERMISSION_DENIED; + } + + return af->suspendOutput(output); +} + +status_t AudioPolicyService::AudioPolicyClient::restoreOutput(audio_io_handle_t output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return PERMISSION_DENIED; + } + + return af->restoreOutput(output); +} + +status_t AudioPolicyService::AudioPolicyClient::openInput(audio_module_handle_t module, + audio_io_handle_t *input, + audio_config_t *config, + audio_devices_t *device, + const String8& address, + audio_source_t source, + audio_input_flags_t flags) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return PERMISSION_DENIED; + } + + return af->openInput(module, input, config, device, address, source, flags); +} + +status_t AudioPolicyService::AudioPolicyClient::closeInput(audio_io_handle_t input) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->closeInput(input); +} + +status_t AudioPolicyService::AudioPolicyClient::setStreamVolume(audio_stream_type_t stream, + float volume, audio_io_handle_t output, + int delay_ms) +{ + return mAudioPolicyService->setStreamVolume(stream, volume, output, + delay_ms); +} + +status_t AudioPolicyService::AudioPolicyClient::invalidateStream(audio_stream_type_t stream) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->invalidateStream(stream); +} + +void AudioPolicyService::AudioPolicyClient::setParameters(audio_io_handle_t io_handle, + const String8& keyValuePairs, + int delay_ms) +{ + mAudioPolicyService->setParameters(io_handle, keyValuePairs.string(), delay_ms); +} + +String8 AudioPolicyService::AudioPolicyClient::getParameters(audio_io_handle_t io_handle, + const String8& keys) +{ + String8 result = AudioSystem::getParameters(io_handle, keys); + return result; +} + +status_t AudioPolicyService::AudioPolicyClient::startTone(audio_policy_tone_t tone, + audio_stream_type_t stream) +{ + return mAudioPolicyService->startTone(tone, stream); +} + +status_t AudioPolicyService::AudioPolicyClient::stopTone() +{ + return mAudioPolicyService->stopTone(); +} + +status_t AudioPolicyService::AudioPolicyClient::setVoiceVolume(float volume, int delay_ms) +{ + return mAudioPolicyService->setVoiceVolume(volume, delay_ms); +} + +status_t AudioPolicyService::AudioPolicyClient::moveEffects(int session, + audio_io_handle_t src_output, + audio_io_handle_t dst_output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->moveEffects(session, src_output, dst_output); +} + +status_t AudioPolicyService::AudioPolicyClient::createAudioPatch(const struct audio_patch *patch, + audio_patch_handle_t *handle, + int delayMs) +{ + return mAudioPolicyService->clientCreateAudioPatch(patch, handle, delayMs); +} + +status_t AudioPolicyService::AudioPolicyClient::releaseAudioPatch(audio_patch_handle_t handle, + int delayMs) +{ + 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(); +} + +audio_unique_id_t AudioPolicyService::AudioPolicyClient::newAudioUniqueId() +{ + return AudioSystem::newAudioUniqueId(); +} + +}; // namespace android diff --git a/services/audiopolicy/AudioPolicyClientImplLegacy.cpp b/services/audiopolicy/AudioPolicyClientImplLegacy.cpp new file mode 100644 index 0000000..9639096 --- /dev/null +++ b/services/audiopolicy/AudioPolicyClientImplLegacy.cpp @@ -0,0 +1,309 @@ +/* + * 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 "AudioPolicyService" +//#define LOG_NDEBUG 0 + +#include "Configuration.h" +#undef __STRICT_ANSI__ +#define __STDINT_LIMITS +#define __STDC_LIMIT_MACROS +#include <stdint.h> + +#include <sys/time.h> +#include <binder/IServiceManager.h> +#include <utils/Log.h> +#include <cutils/properties.h> +#include <binder/IPCThreadState.h> +#include <utils/String16.h> +#include <utils/threads.h> +#include "AudioPolicyService.h" +#include "ServiceUtilities.h" +#include <hardware_legacy/power.h> +#include <media/AudioEffect.h> +#include <media/EffectsFactoryApi.h> +//#include <media/IAudioFlinger.h> + +#include <hardware/hardware.h> +#include <system/audio.h> +#include <system/audio_policy.h> +#include <hardware/audio_policy.h> +#include <audio_effects/audio_effects_conf.h> +#include <media/AudioParameter.h> + + +namespace android { + +/* implementation of the interface to the policy manager */ +extern "C" { + +audio_module_handle_t aps_load_hw_module(void *service __unused, + const char *name) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return 0; + } + + return af->loadHwModule(name); +} + +static audio_io_handle_t open_output(audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + uint32_t *pLatencyMs, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return AUDIO_IO_HANDLE_NONE; + } + + if (pSamplingRate == NULL || pFormat == NULL || pChannelMask == NULL || + pDevices == NULL || pLatencyMs == NULL) { + return AUDIO_IO_HANDLE_NONE; + } + audio_config_t config = AUDIO_CONFIG_INITIALIZER; + config.sample_rate = *pSamplingRate; + config.format = *pFormat; + config.channel_mask = *pChannelMask; + if (offloadInfo != NULL) { + config.offload_info = *offloadInfo; + } + audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; + status_t status = af->openOutput(module, &output, &config, pDevices, + String8(""), pLatencyMs, flags); + if (status == NO_ERROR) { + *pSamplingRate = config.sample_rate; + *pFormat = config.format; + *pChannelMask = config.channel_mask; + if (offloadInfo != NULL) { + *offloadInfo = config.offload_info; + } + } + return output; +} + +// deprecated: replaced by aps_open_output_on_module() +audio_io_handle_t aps_open_output(void *service __unused, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + uint32_t *pLatencyMs, + audio_output_flags_t flags) +{ + return open_output((audio_module_handle_t)0, pDevices, pSamplingRate, pFormat, pChannelMask, + pLatencyMs, flags, NULL); +} + +audio_io_handle_t aps_open_output_on_module(void *service __unused, + audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + uint32_t *pLatencyMs, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + return open_output(module, pDevices, pSamplingRate, pFormat, pChannelMask, + pLatencyMs, flags, offloadInfo); +} + +audio_io_handle_t aps_open_dup_output(void *service __unused, + audio_io_handle_t output1, + audio_io_handle_t output2) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return 0; + } + return af->openDuplicateOutput(output1, output2); +} + +int aps_close_output(void *service __unused, audio_io_handle_t output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->closeOutput(output); +} + +int aps_suspend_output(void *service __unused, audio_io_handle_t output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return PERMISSION_DENIED; + } + + return af->suspendOutput(output); +} + +int aps_restore_output(void *service __unused, audio_io_handle_t output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return PERMISSION_DENIED; + } + + return af->restoreOutput(output); +} + +static audio_io_handle_t open_input(audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + ALOGW("%s: could not get AudioFlinger", __func__); + return AUDIO_IO_HANDLE_NONE; + } + + if (pSamplingRate == NULL || pFormat == NULL || pChannelMask == NULL || pDevices == NULL) { + return AUDIO_IO_HANDLE_NONE; + } + audio_config_t config = AUDIO_CONFIG_INITIALIZER;; + config.sample_rate = *pSamplingRate; + config.format = *pFormat; + config.channel_mask = *pChannelMask; + audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; + status_t status = af->openInput(module, &input, &config, pDevices, + String8(""), AUDIO_SOURCE_MIC, AUDIO_INPUT_FLAG_FAST /*FIXME*/); + if (status == NO_ERROR) { + *pSamplingRate = config.sample_rate; + *pFormat = config.format; + *pChannelMask = config.channel_mask; + } + return input; +} + + +// deprecated: replaced by aps_open_input_on_module(), and acoustics parameter is ignored +audio_io_handle_t aps_open_input(void *service __unused, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + audio_in_acoustics_t acoustics __unused) +{ + return open_input((audio_module_handle_t)0, pDevices, pSamplingRate, pFormat, pChannelMask); +} + +audio_io_handle_t aps_open_input_on_module(void *service __unused, + audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask) +{ + return open_input(module, pDevices, pSamplingRate, pFormat, pChannelMask); +} + +int aps_close_input(void *service __unused, audio_io_handle_t input) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->closeInput(input); +} + +int aps_invalidate_stream(void *service __unused, audio_stream_type_t stream) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->invalidateStream(stream); +} + +int aps_move_effects(void *service __unused, int session, + audio_io_handle_t src_output, + audio_io_handle_t dst_output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + return PERMISSION_DENIED; + } + + return af->moveEffects(session, src_output, dst_output); +} + +char * aps_get_parameters(void *service __unused, audio_io_handle_t io_handle, + const char *keys) +{ + String8 result = AudioSystem::getParameters(io_handle, String8(keys)); + return strdup(result.string()); +} + +void aps_set_parameters(void *service, audio_io_handle_t io_handle, + const char *kv_pairs, int delay_ms) +{ + AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; + + audioPolicyService->setParameters(io_handle, kv_pairs, delay_ms); +} + +int aps_set_stream_volume(void *service, audio_stream_type_t stream, + float volume, audio_io_handle_t output, + int delay_ms) +{ + AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; + + return audioPolicyService->setStreamVolume(stream, volume, output, + delay_ms); +} + +int aps_start_tone(void *service, audio_policy_tone_t tone, + audio_stream_type_t stream) +{ + AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; + + return audioPolicyService->startTone(tone, stream); +} + +int aps_stop_tone(void *service) +{ + AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; + + return audioPolicyService->stopTone(); +} + +int aps_set_voice_volume(void *service, float volume, int delay_ms) +{ + AudioPolicyService *audioPolicyService = (AudioPolicyService *)service; + + return audioPolicyService->setVoiceVolume(volume, delay_ms); +} + +}; // extern "C" + +}; // namespace android diff --git a/services/audiopolicy/AudioPolicyEffects.cpp b/services/audiopolicy/AudioPolicyEffects.cpp new file mode 100644 index 0000000..c45acd0 --- /dev/null +++ b/services/audiopolicy/AudioPolicyEffects.cpp @@ -0,0 +1,654 @@ +/* + * 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 "AudioPolicyEffects" +//#define LOG_NDEBUG 0 + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <cutils/misc.h> +#include <media/AudioEffect.h> +#include <system/audio.h> +#include <hardware/audio_effect.h> +#include <audio_effects/audio_effects_conf.h> +#include <utils/Vector.h> +#include <utils/SortedVector.h> +#include <cutils/config_utils.h> +#include "AudioPolicyEffects.h" +#include "ServiceUtilities.h" + +namespace android { + +// ---------------------------------------------------------------------------- +// AudioPolicyEffects Implementation +// ---------------------------------------------------------------------------- + +AudioPolicyEffects::AudioPolicyEffects() +{ + // load automatic audio effect modules + if (access(AUDIO_EFFECT_VENDOR_CONFIG_FILE, R_OK) == 0) { + loadAudioEffectConfig(AUDIO_EFFECT_VENDOR_CONFIG_FILE); + } else if (access(AUDIO_EFFECT_DEFAULT_CONFIG_FILE, R_OK) == 0) { + loadAudioEffectConfig(AUDIO_EFFECT_DEFAULT_CONFIG_FILE); + } +} + + +AudioPolicyEffects::~AudioPolicyEffects() +{ + size_t i = 0; + // release audio input processing resources + for (i = 0; i < mInputSources.size(); i++) { + delete mInputSources.valueAt(i); + } + mInputSources.clear(); + + for (i = 0; i < mInputs.size(); i++) { + mInputs.valueAt(i)->mEffects.clear(); + delete mInputs.valueAt(i); + } + mInputs.clear(); + + // release audio output processing resources + for (i = 0; i < mOutputStreams.size(); i++) { + delete mOutputStreams.valueAt(i); + } + mOutputStreams.clear(); + + for (i = 0; i < mOutputSessions.size(); i++) { + mOutputSessions.valueAt(i)->mEffects.clear(); + delete mOutputSessions.valueAt(i); + } + mOutputSessions.clear(); +} + + +status_t AudioPolicyEffects::addInputEffects(audio_io_handle_t input, + audio_source_t inputSource, + int audioSession) +{ + status_t status = NO_ERROR; + + // create audio pre processors according to input source + audio_source_t aliasSource = (inputSource == AUDIO_SOURCE_HOTWORD) ? + AUDIO_SOURCE_VOICE_RECOGNITION : inputSource; + + ssize_t index = mInputSources.indexOfKey(aliasSource); + if (index < 0) { + ALOGV("addInputEffects(): no processing needs to be attached to this source"); + return status; + } + ssize_t idx = mInputs.indexOfKey(input); + EffectVector *inputDesc; + if (idx < 0) { + inputDesc = new EffectVector(audioSession); + mInputs.add(input, inputDesc); + } else { + // EffectVector is existing and we just need to increase ref count + inputDesc = mInputs.valueAt(idx); + } + inputDesc->mRefCount++; + + ALOGV("addInputEffects(): input: %d, refCount: %d", input, inputDesc->mRefCount); + + Vector <EffectDesc *> effects = mInputSources.valueAt(index)->mEffects; + for (size_t i = 0; i < effects.size(); i++) { + EffectDesc *effect = effects[i]; + sp<AudioEffect> fx = new AudioEffect(NULL, &effect->mUuid, -1, 0, 0, audioSession, input); + status_t status = fx->initCheck(); + if (status != NO_ERROR && status != ALREADY_EXISTS) { + ALOGW("addInputEffects(): failed to create Fx %s on source %d", + effect->mName, (int32_t)aliasSource); + // fx goes out of scope and strong ref on AudioEffect is released + continue; + } + for (size_t j = 0; j < effect->mParams.size(); j++) { + fx->setParameter(effect->mParams[j]); + } + ALOGV("addInputEffects(): added Fx %s on source: %d", effect->mName, (int32_t)aliasSource); + inputDesc->mEffects.add(fx); + } + setProcessorEnabled(inputDesc, true); + + return status; +} + + +status_t AudioPolicyEffects::releaseInputEffects(audio_io_handle_t input) +{ + status_t status = NO_ERROR; + + ssize_t index = mInputs.indexOfKey(input); + if (index < 0) { + return status; + } + EffectVector *inputDesc = mInputs.valueAt(index); + inputDesc->mRefCount--; + ALOGV("releaseInputEffects(): input: %d, refCount: %d", input, inputDesc->mRefCount); + if (inputDesc->mRefCount == 0) { + setProcessorEnabled(inputDesc, false); + delete inputDesc; + mInputs.removeItemsAt(index); + ALOGV("releaseInputEffects(): all effects released"); + } + return status; +} + +status_t AudioPolicyEffects::queryDefaultInputEffects(int audioSession, + effect_descriptor_t *descriptors, + uint32_t *count) +{ + status_t status = NO_ERROR; + + size_t index; + for (index = 0; index < mInputs.size(); index++) { + if (mInputs.valueAt(index)->mSessionId == audioSession) { + break; + } + } + if (index == mInputs.size()) { + *count = 0; + return BAD_VALUE; + } + Vector< sp<AudioEffect> > effects = mInputs.valueAt(index)->mEffects; + + for (size_t i = 0; i < effects.size(); i++) { + effect_descriptor_t desc = effects[i]->descriptor(); + if (i < *count) { + descriptors[i] = desc; + } + } + if (effects.size() > *count) { + status = NO_MEMORY; + } + *count = effects.size(); + return status; +} + + +status_t AudioPolicyEffects::queryDefaultOutputSessionEffects(int audioSession, + effect_descriptor_t *descriptors, + uint32_t *count) +{ + status_t status = NO_ERROR; + + size_t index; + for (index = 0; index < mOutputSessions.size(); index++) { + if (mOutputSessions.valueAt(index)->mSessionId == audioSession) { + break; + } + } + if (index == mOutputSessions.size()) { + *count = 0; + return BAD_VALUE; + } + Vector< sp<AudioEffect> > effects = mOutputSessions.valueAt(index)->mEffects; + + for (size_t i = 0; i < effects.size(); i++) { + effect_descriptor_t desc = effects[i]->descriptor(); + if (i < *count) { + descriptors[i] = desc; + } + } + if (effects.size() > *count) { + status = NO_MEMORY; + } + *count = effects.size(); + return status; +} + + +status_t AudioPolicyEffects::addOutputSessionEffects(audio_io_handle_t output, + audio_stream_type_t stream, + int audioSession) +{ + status_t status = NO_ERROR; + + // create audio processors according to stream + ssize_t index = mOutputStreams.indexOfKey(stream); + if (index < 0) { + ALOGV("addOutputSessionEffects(): no output processing needed for this stream"); + return NO_ERROR; + } + + ssize_t idx = mOutputSessions.indexOfKey(audioSession); + EffectVector *procDesc; + if (idx < 0) { + procDesc = new EffectVector(audioSession); + mOutputSessions.add(audioSession, procDesc); + } else { + // EffectVector is existing and we just need to increase ref count + procDesc = mOutputSessions.valueAt(idx); + } + procDesc->mRefCount++; + + ALOGV("addOutputSessionEffects(): session: %d, refCount: %d", audioSession, procDesc->mRefCount); + + Vector <EffectDesc *> effects = mOutputStreams.valueAt(index)->mEffects; + for (size_t i = 0; i < effects.size(); i++) { + EffectDesc *effect = effects[i]; + sp<AudioEffect> fx = new AudioEffect(NULL, &effect->mUuid, 0, 0, 0, audioSession, output); + status_t status = fx->initCheck(); + if (status != NO_ERROR && status != ALREADY_EXISTS) { + ALOGE("addOutputSessionEffects(): failed to create Fx %s on session %d", + effect->mName, audioSession); + // fx goes out of scope and strong ref on AudioEffect is released + continue; + } + ALOGV("addOutputSessionEffects(): added Fx %s on session: %d for stream: %d", + effect->mName, audioSession, (int32_t)stream); + procDesc->mEffects.add(fx); + } + + setProcessorEnabled(procDesc, true); + + return status; +} + +status_t AudioPolicyEffects::releaseOutputSessionEffects(audio_io_handle_t output, + audio_stream_type_t stream, + int audioSession) +{ + status_t status = NO_ERROR; + (void) output; // argument not used for now + (void) stream; // argument not used for now + + ssize_t index = mOutputSessions.indexOfKey(audioSession); + if (index < 0) { + ALOGV("releaseOutputSessionEffects: no output processing was attached to this stream"); + return NO_ERROR; + } + + EffectVector *procDesc = mOutputSessions.valueAt(index); + procDesc->mRefCount--; + ALOGV("releaseOutputSessionEffects(): session: %d, refCount: %d", audioSession, procDesc->mRefCount); + if (procDesc->mRefCount == 0) { + setProcessorEnabled(procDesc, false); + procDesc->mEffects.clear(); + delete procDesc; + mOutputSessions.removeItemsAt(index); + ALOGV("releaseOutputSessionEffects(): output processing released from session: %d", + audioSession); + } + return status; +} + + +void AudioPolicyEffects::setProcessorEnabled(const EffectVector *effectVector, bool enabled) +{ + const Vector<sp<AudioEffect> > &fxVector = effectVector->mEffects; + for (size_t i = 0; i < fxVector.size(); i++) { + fxVector.itemAt(i)->setEnabled(enabled); + } +} + + +// ---------------------------------------------------------------------------- +// Audio processing configuration +// ---------------------------------------------------------------------------- + +/*static*/ const char * const AudioPolicyEffects::kInputSourceNames[AUDIO_SOURCE_CNT -1] = { + MIC_SRC_TAG, + VOICE_UL_SRC_TAG, + VOICE_DL_SRC_TAG, + VOICE_CALL_SRC_TAG, + CAMCORDER_SRC_TAG, + VOICE_REC_SRC_TAG, + VOICE_COMM_SRC_TAG +}; + +// returns the audio_source_t enum corresponding to the input source name or +// AUDIO_SOURCE_CNT is no match found +audio_source_t AudioPolicyEffects::inputSourceNameToEnum(const char *name) +{ + int i; + for (i = AUDIO_SOURCE_MIC; i < AUDIO_SOURCE_CNT; i++) { + if (strcmp(name, kInputSourceNames[i - AUDIO_SOURCE_MIC]) == 0) { + ALOGV("inputSourceNameToEnum found source %s %d", name, i); + break; + } + } + return (audio_source_t)i; +} + +const char *AudioPolicyEffects::kStreamNames[AUDIO_STREAM_CNT+1] = { + AUDIO_STREAM_DEFAULT_TAG, + AUDIO_STREAM_VOICE_CALL_TAG, + AUDIO_STREAM_SYSTEM_TAG, + AUDIO_STREAM_RING_TAG, + AUDIO_STREAM_MUSIC_TAG, + AUDIO_STREAM_ALARM_TAG, + AUDIO_STREAM_NOTIFICATION_TAG, + AUDIO_STREAM_BLUETOOTH_SCO_TAG, + AUDIO_STREAM_ENFORCED_AUDIBLE_TAG, + AUDIO_STREAM_DTMF_TAG, + AUDIO_STREAM_TTS_TAG +}; + +// returns the audio_stream_t enum corresponding to the output stream name or +// AUDIO_STREAM_CNT is no match found +audio_stream_type_t AudioPolicyEffects::streamNameToEnum(const char *name) +{ + int i; + for (i = AUDIO_STREAM_DEFAULT; i < AUDIO_STREAM_CNT; i++) { + if (strcmp(name, kStreamNames[i - AUDIO_STREAM_DEFAULT]) == 0) { + ALOGV("streamNameToEnum found stream %s %d", name, i); + break; + } + } + return (audio_stream_type_t)i; +} + +// ---------------------------------------------------------------------------- +// Audio Effect Config parser +// ---------------------------------------------------------------------------- + +size_t AudioPolicyEffects::growParamSize(char *param, + size_t size, + size_t *curSize, + size_t *totSize) +{ + // *curSize is at least sizeof(effect_param_t) + 2 * sizeof(int) + size_t pos = ((*curSize - 1 ) / size + 1) * size; + + if (pos + size > *totSize) { + while (pos + size > *totSize) { + *totSize += ((*totSize + 7) / 8) * 4; + } + param = (char *)realloc(param, *totSize); + } + *curSize = pos + size; + return pos; +} + +size_t AudioPolicyEffects::readParamValue(cnode *node, + char *param, + size_t *curSize, + size_t *totSize) +{ + if (strncmp(node->name, SHORT_TAG, sizeof(SHORT_TAG) + 1) == 0) { + size_t pos = growParamSize(param, sizeof(short), curSize, totSize); + *(short *)((char *)param + pos) = (short)atoi(node->value); + ALOGV("readParamValue() reading short %d", *(short *)((char *)param + pos)); + return sizeof(short); + } else if (strncmp(node->name, INT_TAG, sizeof(INT_TAG) + 1) == 0) { + size_t pos = growParamSize(param, sizeof(int), curSize, totSize); + *(int *)((char *)param + pos) = atoi(node->value); + ALOGV("readParamValue() reading int %d", *(int *)((char *)param + pos)); + return sizeof(int); + } else if (strncmp(node->name, FLOAT_TAG, sizeof(FLOAT_TAG) + 1) == 0) { + size_t pos = growParamSize(param, sizeof(float), curSize, totSize); + *(float *)((char *)param + pos) = (float)atof(node->value); + ALOGV("readParamValue() reading float %f",*(float *)((char *)param + pos)); + return sizeof(float); + } else if (strncmp(node->name, BOOL_TAG, sizeof(BOOL_TAG) + 1) == 0) { + size_t pos = growParamSize(param, sizeof(bool), curSize, totSize); + if (strncmp(node->value, "false", strlen("false") + 1) == 0) { + *(bool *)((char *)param + pos) = false; + } else { + *(bool *)((char *)param + pos) = true; + } + ALOGV("readParamValue() reading bool %s",*(bool *)((char *)param + pos) ? "true" : "false"); + return sizeof(bool); + } else if (strncmp(node->name, STRING_TAG, sizeof(STRING_TAG) + 1) == 0) { + size_t len = strnlen(node->value, EFFECT_STRING_LEN_MAX); + if (*curSize + len + 1 > *totSize) { + *totSize = *curSize + len + 1; + param = (char *)realloc(param, *totSize); + } + strncpy(param + *curSize, node->value, len); + *curSize += len; + param[*curSize] = '\0'; + ALOGV("readParamValue() reading string %s", param + *curSize - len); + return len; + } + ALOGW("readParamValue() unknown param type %s", node->name); + return 0; +} + +effect_param_t *AudioPolicyEffects::loadEffectParameter(cnode *root) +{ + cnode *param; + cnode *value; + size_t curSize = sizeof(effect_param_t); + size_t totSize = sizeof(effect_param_t) + 2 * sizeof(int); + effect_param_t *fx_param = (effect_param_t *)malloc(totSize); + + param = config_find(root, PARAM_TAG); + value = config_find(root, VALUE_TAG); + if (param == NULL && value == NULL) { + // try to parse simple parameter form {int int} + param = root->first_child; + if (param != NULL) { + // Note: that a pair of random strings is read as 0 0 + int *ptr = (int *)fx_param->data; + int *ptr2 = (int *)((char *)param + sizeof(effect_param_t)); + ALOGW("loadEffectParameter() ptr %p ptr2 %p", ptr, ptr2); + *ptr++ = atoi(param->name); + *ptr = atoi(param->value); + fx_param->psize = sizeof(int); + fx_param->vsize = sizeof(int); + return fx_param; + } + } + if (param == NULL || value == NULL) { + ALOGW("loadEffectParameter() invalid parameter description %s", root->name); + goto error; + } + + fx_param->psize = 0; + param = param->first_child; + while (param) { + ALOGV("loadEffectParameter() reading param of type %s", param->name); + size_t size = readParamValue(param, (char *)fx_param, &curSize, &totSize); + if (size == 0) { + goto error; + } + fx_param->psize += size; + param = param->next; + } + + // align start of value field on 32 bit boundary + curSize = ((curSize - 1 ) / sizeof(int) + 1) * sizeof(int); + + fx_param->vsize = 0; + value = value->first_child; + while (value) { + ALOGV("loadEffectParameter() reading value of type %s", value->name); + size_t size = readParamValue(value, (char *)fx_param, &curSize, &totSize); + if (size == 0) { + goto error; + } + fx_param->vsize += size; + value = value->next; + } + + return fx_param; + +error: + delete fx_param; + return NULL; +} + +void AudioPolicyEffects::loadEffectParameters(cnode *root, Vector <effect_param_t *>& params) +{ + cnode *node = root->first_child; + while (node) { + ALOGV("loadEffectParameters() loading param %s", node->name); + effect_param_t *param = loadEffectParameter(node); + if (param == NULL) { + node = node->next; + continue; + } + params.add(param); + node = node->next; + } +} + + +AudioPolicyEffects::EffectDescVector *AudioPolicyEffects::loadEffectConfig( + cnode *root, + const Vector <EffectDesc *>& effects) +{ + cnode *node = root->first_child; + if (node == NULL) { + ALOGW("loadInputSource() empty element %s", root->name); + return NULL; + } + EffectDescVector *desc = new EffectDescVector(); + while (node) { + size_t i; + for (i = 0; i < effects.size(); i++) { + if (strncmp(effects[i]->mName, node->name, EFFECT_STRING_LEN_MAX) == 0) { + ALOGV("loadEffectConfig() found effect %s in list", node->name); + break; + } + } + if (i == effects.size()) { + ALOGV("loadEffectConfig() effect %s not in list", node->name); + node = node->next; + continue; + } + EffectDesc *effect = new EffectDesc(*effects[i]); // deep copy + loadEffectParameters(node, effect->mParams); + ALOGV("loadEffectConfig() adding effect %s uuid %08x", + effect->mName, effect->mUuid.timeLow); + desc->mEffects.add(effect); + node = node->next; + } + if (desc->mEffects.size() == 0) { + ALOGW("loadEffectConfig() no valid effects found in config %s", root->name); + delete desc; + return NULL; + } + return desc; +} + +status_t AudioPolicyEffects::loadInputEffectConfigurations(cnode *root, + const Vector <EffectDesc *>& effects) +{ + cnode *node = config_find(root, PREPROCESSING_TAG); + if (node == NULL) { + return -ENOENT; + } + node = node->first_child; + while (node) { + audio_source_t source = inputSourceNameToEnum(node->name); + if (source == AUDIO_SOURCE_CNT) { + ALOGW("loadInputSources() invalid input source %s", node->name); + node = node->next; + continue; + } + ALOGV("loadInputSources() loading input source %s", node->name); + EffectDescVector *desc = loadEffectConfig(node, effects); + if (desc == NULL) { + node = node->next; + continue; + } + mInputSources.add(source, desc); + node = node->next; + } + return NO_ERROR; +} + +status_t AudioPolicyEffects::loadStreamEffectConfigurations(cnode *root, + const Vector <EffectDesc *>& effects) +{ + cnode *node = config_find(root, OUTPUT_SESSION_PROCESSING_TAG); + if (node == NULL) { + return -ENOENT; + } + node = node->first_child; + while (node) { + audio_stream_type_t stream = streamNameToEnum(node->name); + if (stream == AUDIO_STREAM_CNT) { + ALOGW("loadStreamEffectConfigurations() invalid output stream %s", node->name); + node = node->next; + continue; + } + ALOGV("loadStreamEffectConfigurations() loading output stream %s", node->name); + EffectDescVector *desc = loadEffectConfig(node, effects); + if (desc == NULL) { + node = node->next; + continue; + } + mOutputStreams.add(stream, desc); + node = node->next; + } + return NO_ERROR; +} + +AudioPolicyEffects::EffectDesc *AudioPolicyEffects::loadEffect(cnode *root) +{ + cnode *node = config_find(root, UUID_TAG); + if (node == NULL) { + return NULL; + } + effect_uuid_t uuid; + if (AudioEffect::stringToGuid(node->value, &uuid) != NO_ERROR) { + ALOGW("loadEffect() invalid uuid %s", node->value); + return NULL; + } + return new EffectDesc(root->name, uuid); +} + +status_t AudioPolicyEffects::loadEffects(cnode *root, Vector <EffectDesc *>& effects) +{ + cnode *node = config_find(root, EFFECTS_TAG); + if (node == NULL) { + return -ENOENT; + } + node = node->first_child; + while (node) { + ALOGV("loadEffects() loading effect %s", node->name); + EffectDesc *effect = loadEffect(node); + if (effect == NULL) { + node = node->next; + continue; + } + effects.add(effect); + node = node->next; + } + return NO_ERROR; +} + +status_t AudioPolicyEffects::loadAudioEffectConfig(const char *path) +{ + cnode *root; + char *data; + + data = (char *)load_file(path, NULL); + if (data == NULL) { + return -ENODEV; + } + root = config_node("", ""); + config_load(root, data); + + Vector <EffectDesc *> effects; + loadEffects(root, effects); + loadInputEffectConfigurations(root, effects); + loadStreamEffectConfigurations(root, effects); + + config_free(root); + free(root); + free(data); + + return NO_ERROR; +} + + +}; // namespace android diff --git a/services/audiopolicy/AudioPolicyEffects.h b/services/audiopolicy/AudioPolicyEffects.h new file mode 100644 index 0000000..dbe0d0e --- /dev/null +++ b/services/audiopolicy/AudioPolicyEffects.h @@ -0,0 +1,189 @@ +/* + * 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_AUDIOPOLICYEFFECTS_H +#define ANDROID_AUDIOPOLICYEFFECTS_H + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <cutils/misc.h> +#include <media/AudioEffect.h> +#include <system/audio.h> +#include <hardware/audio_effect.h> +#include <utils/Vector.h> +#include <utils/SortedVector.h> + +namespace android { + +// ---------------------------------------------------------------------------- + +// AudioPolicyEffects class +// This class will manage all effects attached to input and output streams in +// AudioPolicyService as configured in audio_effects.conf. +class AudioPolicyEffects : public RefBase +{ + +public: + + // The constructor will parse audio_effects.conf + // First it will look whether vendor specific file exists, + // otherwise it will parse the system default file. + AudioPolicyEffects(); + virtual ~AudioPolicyEffects(); + + // Return a list of effect descriptors for default input effects + // associated with audioSession + status_t queryDefaultInputEffects(int audioSession, + effect_descriptor_t *descriptors, + uint32_t *count); + + // Add all input effects associated with this input + // Effects are attached depending on the audio_source_t + status_t addInputEffects(audio_io_handle_t input, + audio_source_t inputSource, + int audioSession); + + // Add all input effects associated to this input + status_t releaseInputEffects(audio_io_handle_t input); + + + // Return a list of effect descriptors for default output effects + // associated with audioSession + status_t queryDefaultOutputSessionEffects(int audioSession, + effect_descriptor_t *descriptors, + uint32_t *count); + + // Add all output effects associated to this output + // Effects are attached depending on the audio_stream_type_t + status_t addOutputSessionEffects(audio_io_handle_t output, + audio_stream_type_t stream, + int audioSession); + + // release all output effects associated with this output stream and audiosession + status_t releaseOutputSessionEffects(audio_io_handle_t output, + audio_stream_type_t stream, + int audioSession); + +private: + + // class to store the description of an effects and its parameters + // as defined in audio_effects.conf + class EffectDesc { + public: + EffectDesc(const char *name, const effect_uuid_t& uuid) : + mName(strdup(name)), + mUuid(uuid) { } + EffectDesc(const EffectDesc& orig) : + mName(strdup(orig.mName)), + mUuid(orig.mUuid) { + // deep copy mParams + for (size_t k = 0; k < orig.mParams.size(); k++) { + effect_param_t *origParam = orig.mParams[k]; + // psize and vsize are rounded up to an int boundary for allocation + size_t origSize = sizeof(effect_param_t) + + ((origParam->psize + 3) & ~3) + + ((origParam->vsize + 3) & ~3); + effect_param_t *dupParam = (effect_param_t *) malloc(origSize); + memcpy(dupParam, origParam, origSize); + // This works because the param buffer allocation is also done by + // multiples of 4 bytes originally. In theory we should memcpy only + // the actual param size, that is without rounding vsize. + mParams.add(dupParam); + } + } + /*virtual*/ ~EffectDesc() { + free(mName); + for (size_t k = 0; k < mParams.size(); k++) { + free(mParams[k]); + } + } + char *mName; + effect_uuid_t mUuid; + Vector <effect_param_t *> mParams; + }; + + // class to store voctor of EffectDesc + class EffectDescVector { + public: + EffectDescVector() {} + /*virtual*/ ~EffectDescVector() { + for (size_t j = 0; j < mEffects.size(); j++) { + delete mEffects[j]; + } + } + Vector <EffectDesc *> mEffects; + }; + + // class to store voctor of AudioEffects + class EffectVector { + public: + EffectVector(int session) : mSessionId(session), mRefCount(0) {} + /*virtual*/ ~EffectVector() {} + const int mSessionId; + // AudioPolicyManager keeps mLock, no need for lock on reference count here + int mRefCount; + Vector< sp<AudioEffect> >mEffects; + }; + + + static const char * const kInputSourceNames[AUDIO_SOURCE_CNT -1]; + audio_source_t inputSourceNameToEnum(const char *name); + + static const char *kStreamNames[AUDIO_STREAM_CNT+1]; //+1 required as streams start from -1 + audio_stream_type_t streamNameToEnum(const char *name); + + // Enable or disable all effects in effect vector + void setProcessorEnabled(const EffectVector *effectVector, bool enabled); + + // Parse audio_effects.conf + status_t loadAudioEffectConfig(const char *path); + + // Load all effects descriptors in configuration file + status_t loadEffects(cnode *root, Vector <EffectDesc *>& effects); + EffectDesc *loadEffect(cnode *root); + + // Load all automatic effect configurations + status_t loadInputEffectConfigurations(cnode *root, const Vector <EffectDesc *>& effects); + status_t loadStreamEffectConfigurations(cnode *root, const Vector <EffectDesc *>& effects); + EffectDescVector *loadEffectConfig(cnode *root, const Vector <EffectDesc *>& effects); + + // Load all automatic effect parameters + void loadEffectParameters(cnode *root, Vector <effect_param_t *>& params); + effect_param_t *loadEffectParameter(cnode *root); + size_t readParamValue(cnode *node, + char *param, + size_t *curSize, + size_t *totSize); + size_t growParamSize(char *param, + size_t size, + size_t *curSize, + size_t *totSize); + + // Automatic input effects are configured per audio_source_t + KeyedVector< audio_source_t, EffectDescVector* > mInputSources; + // Automatic input effects are unique for audio_io_handle_t + KeyedVector< audio_io_handle_t, EffectVector* > mInputs; + + // Automatic output effects are organized per audio_stream_type_t + KeyedVector< audio_stream_type_t, EffectDescVector* > mOutputStreams; + // Automatic output effects are unique for audiosession ID + KeyedVector< int32_t, EffectVector* > mOutputSessions; +}; + +}; // namespace android + +#endif // ANDROID_AUDIOPOLICYEFFECTS_H diff --git a/services/audiopolicy/AudioPolicyFactory.cpp b/services/audiopolicy/AudioPolicyFactory.cpp new file mode 100644 index 0000000..2ae7bc1 --- /dev/null +++ b/services/audiopolicy/AudioPolicyFactory.cpp @@ -0,0 +1,32 @@ +/* + * 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 "AudioPolicyManager.h" + +namespace android { + +extern "C" AudioPolicyInterface* createAudioPolicyManager( + AudioPolicyClientInterface *clientInterface) +{ + return new AudioPolicyManager(clientInterface); +} + +extern "C" void destroyAudioPolicyManager(AudioPolicyInterface *interface) +{ + delete interface; +} + +}; // namespace android diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h new file mode 100644 index 0000000..5524463 --- /dev/null +++ b/services/audiopolicy/AudioPolicyInterface.h @@ -0,0 +1,308 @@ +/* + * 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_AUDIOPOLICY_INTERFACE_H +#define ANDROID_AUDIOPOLICY_INTERFACE_H + +#include <media/AudioSystem.h> +#include <utils/String8.h> + +#include <hardware/audio_policy.h> + +namespace android { + +// ---------------------------------------------------------------------------- + +// The AudioPolicyInterface and AudioPolicyClientInterface classes define the communication interfaces +// between the platform specific audio policy manager and Android generic audio policy manager. +// The platform specific audio policy manager must implement methods of the AudioPolicyInterface class. +// This implementation makes use of the AudioPolicyClientInterface to control the activity and +// configuration of audio input and output streams. +// +// The platform specific audio policy manager is in charge of the audio routing and volume control +// policies for a given platform. +// The main roles of this module are: +// - keep track of current system state (removable device connections, phone state, user requests...). +// System state changes and user actions are notified to audio policy manager with methods of the AudioPolicyInterface. +// - process getOutput() queries received when AudioTrack objects are created: Those queries +// return a handler on an output that has been selected, configured and opened by the audio policy manager and that +// must be used by the AudioTrack when registering to the AudioFlinger with the createTrack() method. +// When the AudioTrack object is released, a putOutput() query is received and the audio policy manager can decide +// to close or reconfigure the output depending on other streams using this output and current system state. +// - similarly process getInput() and putInput() queries received from AudioRecord objects and configure audio inputs. +// - process volume control requests: the stream volume is converted from an index value (received from UI) to a float value +// applicable to each output as a function of platform specific settings and current output route (destination device). It +// also make sure that streams are not muted if not allowed (e.g. camera shutter sound in some countries). +// +// The platform specific audio policy manager is provided as a shared library by platform vendors (as for libaudio.so) +// and is linked with libaudioflinger.so + + +// Audio Policy Manager Interface +class AudioPolicyInterface +{ + +public: + virtual ~AudioPolicyInterface() {} + // + // configuration functions + // + + // indicate a change in device connection status + virtual status_t setDeviceConnectionState(audio_devices_t device, + audio_policy_dev_state_t state, + const char *device_address) = 0; + // retrieve a device connection status + virtual audio_policy_dev_state_t getDeviceConnectionState(audio_devices_t device, + const char *device_address) = 0; + // indicate a change in phone state. Valid phones states are defined by audio_mode_t + virtual void setPhoneState(audio_mode_t state) = 0; + // force using a specific device category for the specified usage + virtual void setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config) = 0; + // retrieve current device category forced for a given usage + virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage) = 0; + // set a system property (e.g. camera sound always audible) + virtual void setSystemProperty(const char* property, const char* value) = 0; + // check proper initialization + virtual status_t initCheck() = 0; + + // + // Audio routing query functions + // + + // request an output appropriate for playback of the supplied stream type and parameters + virtual audio_io_handle_t getOutput(audio_stream_type_t stream, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) = 0; + virtual audio_io_handle_t getOutputForAttr(const audio_attributes_t *attr, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) = 0; + // indicates to the audio policy manager that the output starts being used by corresponding stream. + virtual status_t startOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session = 0) = 0; + // indicates to the audio policy manager that the output stops being used by corresponding stream. + virtual status_t stopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session = 0) = 0; + // releases the output. + virtual void releaseOutput(audio_io_handle_t output) = 0; + + // request an input appropriate for record from the supplied device with supplied parameters. + virtual audio_io_handle_t getInput(audio_source_t inputSource, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_session_t session, + audio_input_flags_t flags) = 0; + // indicates to the audio policy manager that the input starts being used. + virtual status_t startInput(audio_io_handle_t input, + audio_session_t session) = 0; + // indicates to the audio policy manager that the input stops being used. + virtual status_t stopInput(audio_io_handle_t input, + audio_session_t session) = 0; + // releases the input. + virtual void releaseInput(audio_io_handle_t input, + audio_session_t session) = 0; + + // + // volume control functions + // + + // initialises stream volume conversion parameters by specifying volume index range. + virtual void initStreamVolume(audio_stream_type_t stream, + int indexMin, + int indexMax) = 0; + + // sets the new stream volume at a level corresponding to the supplied index for the + // supplied device. By convention, specifying AUDIO_DEVICE_OUT_DEFAULT means + // setting volume for all devices + virtual status_t setStreamVolumeIndex(audio_stream_type_t stream, + int index, + audio_devices_t device) = 0; + + // retrieve current volume index for the specified stream and the + // specified device. By convention, specifying AUDIO_DEVICE_OUT_DEFAULT means + // querying the volume of the active device. + virtual status_t getStreamVolumeIndex(audio_stream_type_t stream, + int *index, + audio_devices_t device) = 0; + + // return the strategy corresponding to a given stream type + virtual uint32_t getStrategyForStream(audio_stream_type_t stream) = 0; + + // return the enabled output devices for the given stream type + virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream) = 0; + + // Audio effect management + virtual audio_io_handle_t getOutputForEffect(const effect_descriptor_t *desc) = 0; + virtual status_t registerEffect(const effect_descriptor_t *desc, + audio_io_handle_t io, + uint32_t strategy, + int session, + int id) = 0; + virtual status_t unregisterEffect(int id) = 0; + virtual status_t setEffectEnabled(int id, bool enabled) = 0; + + virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const = 0; + virtual bool isStreamActiveRemotely(audio_stream_type_t stream, + uint32_t inPastMs = 0) const = 0; + virtual bool isSourceActive(audio_source_t source) const = 0; + + //dump state + 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; + + virtual status_t acquireSoundTriggerSession(audio_session_t *session, + audio_io_handle_t *ioHandle, + audio_devices_t *device) = 0; + + virtual status_t releaseSoundTriggerSession(audio_session_t session) = 0; +}; + + +// Audio Policy client Interface +class AudioPolicyClientInterface +{ +public: + virtual ~AudioPolicyClientInterface() {} + + // + // Audio HW module functions + // + + // loads a HW module. + virtual audio_module_handle_t loadHwModule(const char *name) = 0; + + // + // Audio output Control functions + // + + // opens an audio output with the requested parameters. The parameter values can indicate to use the default values + // in case the audio policy manager has no specific requirements for the output being opened. + // When the function returns, the parameter values reflect the actual values used by the audio hardware output stream. + // The audio policy manager can check if the proposed parameters are suitable or not and act accordingly. + virtual status_t openOutput(audio_module_handle_t module, + audio_io_handle_t *output, + audio_config_t *config, + audio_devices_t *devices, + const String8& address, + uint32_t *latencyMs, + audio_output_flags_t flags) = 0; + // creates a special output that is duplicated to the two outputs passed as arguments. The duplication is performed by + // a special mixer thread in the AudioFlinger. + virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1, audio_io_handle_t output2) = 0; + // closes the output stream + virtual status_t closeOutput(audio_io_handle_t output) = 0; + // suspends the output. When an output is suspended, the corresponding audio hardware output stream is placed in + // standby and the AudioTracks attached to the mixer thread are still processed but the output mix is discarded. + virtual status_t suspendOutput(audio_io_handle_t output) = 0; + // restores a suspended output. + virtual status_t restoreOutput(audio_io_handle_t output) = 0; + + // + // Audio input Control functions + // + + // opens an audio input + virtual status_t openInput(audio_module_handle_t module, + audio_io_handle_t *input, + audio_config_t *config, + audio_devices_t *device, + const String8& address, + audio_source_t source, + audio_input_flags_t flags) = 0; + // closes an audio input + virtual status_t closeInput(audio_io_handle_t input) = 0; + // + // misc control functions + // + + // set a stream volume for a particular output. For the same user setting, a given stream type can have different volumes + // for each output (destination device) it is attached to. + virtual status_t setStreamVolume(audio_stream_type_t stream, float volume, audio_io_handle_t output, int delayMs = 0) = 0; + + // invalidate a stream type, causing a reroute to an unspecified new output + virtual status_t invalidateStream(audio_stream_type_t stream) = 0; + + // function enabling to send proprietary informations directly from audio policy manager to audio hardware interface. + virtual void setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs, int delayMs = 0) = 0; + // function enabling to receive proprietary informations directly from audio hardware interface to audio policy manager. + virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys) = 0; + + // request the playback of a tone on the specified stream: used for instance to replace notification sounds when playing + // over a telephony device during a phone call. + virtual status_t startTone(audio_policy_tone_t tone, audio_stream_type_t stream) = 0; + virtual status_t stopTone() = 0; + + // set down link audio volume. + virtual status_t setVoiceVolume(float volume, int delayMs = 0) = 0; + + // move effect to the specified output + virtual status_t moveEffects(int session, + audio_io_handle_t srcOutput, + audio_io_handle_t dstOutput) = 0; + + /* Create a patch between several source and sink ports */ + virtual status_t createAudioPatch(const struct audio_patch *patch, + audio_patch_handle_t *handle, + int delayMs) = 0; + + /* Release a patch */ + 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; + + virtual audio_unique_id_t newAudioUniqueId() = 0; +}; + +extern "C" AudioPolicyInterface* createAudioPolicyManager(AudioPolicyClientInterface *clientInterface); +extern "C" void destroyAudioPolicyManager(AudioPolicyInterface *interface); + + +}; // namespace android + +#endif // ANDROID_AUDIOPOLICY_INTERFACE_H diff --git a/services/audiopolicy/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/AudioPolicyInterfaceImpl.cpp new file mode 100644 index 0000000..2c51e25 --- /dev/null +++ b/services/audiopolicy/AudioPolicyInterfaceImpl.cpp @@ -0,0 +1,554 @@ +/* + * 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 "AudioPolicyIntefaceImpl" +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include "AudioPolicyService.h" +#include "ServiceUtilities.h" + +namespace android { + + +// ---------------------------------------------------------------------------- + +status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device, + audio_policy_dev_state_t state, + const char *device_address) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (!audio_is_output_device(device) && !audio_is_input_device(device)) { + return BAD_VALUE; + } + if (state != AUDIO_POLICY_DEVICE_STATE_AVAILABLE && + state != AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) { + return BAD_VALUE; + } + + ALOGV("setDeviceConnectionState()"); + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->setDeviceConnectionState(device, + state, device_address); +} + +audio_policy_dev_state_t AudioPolicyService::getDeviceConnectionState( + audio_devices_t device, + const char *device_address) +{ + if (mAudioPolicyManager == NULL) { + return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; + } + return mAudioPolicyManager->getDeviceConnectionState(device, + device_address); +} + +status_t AudioPolicyService::setPhoneState(audio_mode_t state) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (uint32_t(state) >= AUDIO_MODE_CNT) { + return BAD_VALUE; + } + + ALOGV("setPhoneState()"); + + // TODO: check if it is more appropriate to do it in platform specific policy manager + AudioSystem::setMode(state); + + Mutex::Autolock _l(mLock); + mAudioPolicyManager->setPhoneState(state); + return NO_ERROR; +} + +status_t AudioPolicyService::setForceUse(audio_policy_force_use_t usage, + audio_policy_forced_cfg_t config) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) { + return BAD_VALUE; + } + if (config < 0 || config >= AUDIO_POLICY_FORCE_CFG_CNT) { + return BAD_VALUE; + } + ALOGV("setForceUse()"); + Mutex::Autolock _l(mLock); + mAudioPolicyManager->setForceUse(usage, config); + return NO_ERROR; +} + +audio_policy_forced_cfg_t AudioPolicyService::getForceUse(audio_policy_force_use_t usage) +{ + if (mAudioPolicyManager == NULL) { + return AUDIO_POLICY_FORCE_NONE; + } + if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) { + return AUDIO_POLICY_FORCE_NONE; + } + return mAudioPolicyManager->getForceUse(usage); +} + +audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + if (mAudioPolicyManager == NULL) { + return 0; + } + ALOGV("getOutput()"); + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->getOutput(stream, samplingRate, + format, channelMask, flags, offloadInfo); +} + +audio_io_handle_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + if (mAudioPolicyManager == NULL) { + return 0; + } + ALOGV("getOutput()"); + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->getOutputForAttr(attr, samplingRate, + format, channelMask, flags, offloadInfo); +} + +status_t AudioPolicyService::startOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + ALOGV("startOutput()"); + Mutex::Autolock _l(mLock); + + // create audio processors according to stream + status_t status = mAudioPolicyEffects->addOutputSessionEffects(output, stream, session); + if (status != NO_ERROR && status != ALREADY_EXISTS) { + ALOGW("Failed to add effects on session %d", session); + } + + return mAudioPolicyManager->startOutput(output, stream, session); +} + +status_t AudioPolicyService::stopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + ALOGV("stopOutput()"); + mOutputCommandThread->stopOutputCommand(output, stream, session); + return NO_ERROR; +} + +status_t AudioPolicyService::doStopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session) +{ + ALOGV("doStopOutput from tid %d", gettid()); + Mutex::Autolock _l(mLock); + + // release audio processors from the stream + status_t status = mAudioPolicyEffects->releaseOutputSessionEffects(output, stream, session); + if (status != NO_ERROR && status != ALREADY_EXISTS) { + ALOGW("Failed to release effects on session %d", session); + } + + return mAudioPolicyManager->stopOutput(output, stream, session); +} + +void AudioPolicyService::releaseOutput(audio_io_handle_t output) +{ + if (mAudioPolicyManager == NULL) { + return; + } + ALOGV("releaseOutput()"); + mOutputCommandThread->releaseOutputCommand(output); +} + +void AudioPolicyService::doReleaseOutput(audio_io_handle_t output) +{ + ALOGV("doReleaseOutput from tid %d", gettid()); + Mutex::Autolock _l(mLock); + mAudioPolicyManager->releaseOutput(output); +} + +audio_io_handle_t AudioPolicyService::getInput(audio_source_t inputSource, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + int audioSession, + audio_input_flags_t flags) +{ + if (mAudioPolicyManager == NULL) { + return 0; + } + // already checked by client, but double-check in case the client wrapper is bypassed + if (inputSource >= AUDIO_SOURCE_CNT && inputSource != AUDIO_SOURCE_HOTWORD) { + return 0; + } + + if ((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) { + return 0; + } + + Mutex::Autolock _l(mLock); + // the audio_in_acoustics_t parameter is ignored by get_input() + audio_io_handle_t input = mAudioPolicyManager->getInput(inputSource, samplingRate, + format, channelMask, + (audio_session_t)audioSession, flags); + + if (input == 0) { + return input; + } + + // create audio pre processors according to input source + status_t status = mAudioPolicyEffects->addInputEffects(input, inputSource, audioSession); + if (status != NO_ERROR && status != ALREADY_EXISTS) { + ALOGW("Failed to add effects on input %d", input); + } + + return input; +} + +status_t AudioPolicyService::startInput(audio_io_handle_t input, + audio_session_t session) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + + return mAudioPolicyManager->startInput(input, session); +} + +status_t AudioPolicyService::stopInput(audio_io_handle_t input, + audio_session_t session) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + + return mAudioPolicyManager->stopInput(input, session); +} + +void AudioPolicyService::releaseInput(audio_io_handle_t input, + audio_session_t session) +{ + if (mAudioPolicyManager == NULL) { + return; + } + Mutex::Autolock _l(mLock); + mAudioPolicyManager->releaseInput(input, session); + + // release audio processors from the input + status_t status = mAudioPolicyEffects->releaseInputEffects(input); + if(status != NO_ERROR) { + ALOGW("Failed to release effects on input %d", input); + } +} + +status_t AudioPolicyService::initStreamVolume(audio_stream_type_t stream, + int indexMin, + int indexMax) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); + mAudioPolicyManager->initStreamVolume(stream, indexMin, indexMax); + return NO_ERROR; +} + +status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream, + int index, + audio_devices_t device) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->setStreamVolumeIndex(stream, + index, + device); +} + +status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream, + int *index, + audio_devices_t device) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->getStreamVolumeIndex(stream, + index, + device); +} + +uint32_t AudioPolicyService::getStrategyForStream(audio_stream_type_t stream) +{ + if (mAudioPolicyManager == NULL) { + return 0; + } + return mAudioPolicyManager->getStrategyForStream(stream); +} + +//audio policy: use audio_device_t appropriately + +audio_devices_t AudioPolicyService::getDevicesForStream(audio_stream_type_t stream) +{ + if (mAudioPolicyManager == NULL) { + return (audio_devices_t)0; + } + return mAudioPolicyManager->getDevicesForStream(stream); +} + +audio_io_handle_t AudioPolicyService::getOutputForEffect(const effect_descriptor_t *desc) +{ + // FIXME change return type to status_t, and return NO_INIT here + if (mAudioPolicyManager == NULL) { + return 0; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->getOutputForEffect(desc); +} + +status_t AudioPolicyService::registerEffect(const effect_descriptor_t *desc, + audio_io_handle_t io, + uint32_t strategy, + int session, + int id) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + return mAudioPolicyManager->registerEffect(desc, io, strategy, session, id); +} + +status_t AudioPolicyService::unregisterEffect(int id) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + return mAudioPolicyManager->unregisterEffect(id); +} + +status_t AudioPolicyService::setEffectEnabled(int id, bool enabled) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + return mAudioPolicyManager->setEffectEnabled(id, enabled); +} + +bool AudioPolicyService::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const +{ + if (mAudioPolicyManager == NULL) { + return 0; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->isStreamActive(stream, inPastMs); +} + +bool AudioPolicyService::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const +{ + if (mAudioPolicyManager == NULL) { + return 0; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->isStreamActiveRemotely(stream, inPastMs); +} + +bool AudioPolicyService::isSourceActive(audio_source_t source) const +{ + if (mAudioPolicyManager == NULL) { + return false; + } + Mutex::Autolock _l(mLock); + return mAudioPolicyManager->isSourceActive(source); +} + +status_t AudioPolicyService::queryDefaultPreProcessing(int audioSession, + effect_descriptor_t *descriptors, + uint32_t *count) +{ + if (mAudioPolicyManager == NULL) { + *count = 0; + return NO_INIT; + } + Mutex::Autolock _l(mLock); + + return mAudioPolicyEffects->queryDefaultInputEffects(audioSession, descriptors, count); +} + +bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info) +{ + if (mAudioPolicyManager == NULL) { + ALOGV("mAudioPolicyManager == NULL"); + return false; + } + + return mAudioPolicyManager->isOffloadSupported(info); +} + +status_t AudioPolicyService::listAudioPorts(audio_port_role_t role, + audio_port_type_t type, + unsigned int *num_ports, + struct audio_port *ports, + unsigned int *generation) +{ + Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + + return mAudioPolicyManager->listAudioPorts(role, type, num_ports, ports, generation); +} + +status_t AudioPolicyService::getAudioPort(struct audio_port *port) +{ + Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + + return mAudioPolicyManager->getAudioPort(port); +} + +status_t AudioPolicyService::createAudioPatch(const struct audio_patch *patch, + audio_patch_handle_t *handle) +{ + Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + return mAudioPolicyManager->createAudioPatch(patch, handle, + IPCThreadState::self()->getCallingUid()); +} + +status_t AudioPolicyService::releaseAudioPatch(audio_patch_handle_t handle) +{ + Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } + 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, + unsigned int *generation) +{ + Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + + return mAudioPolicyManager->listAudioPatches(num_patches, patches, generation); +} + +status_t AudioPolicyService::setAudioPortConfig(const struct audio_port_config *config) +{ + Mutex::Autolock _l(mLock); + if(!modifyAudioRoutingAllowed()) { + return PERMISSION_DENIED; + } + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + + return mAudioPolicyManager->setAudioPortConfig(config); +} + +status_t AudioPolicyService::acquireSoundTriggerSession(audio_session_t *session, + audio_io_handle_t *ioHandle, + audio_devices_t *device) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + + return mAudioPolicyManager->acquireSoundTriggerSession(session, ioHandle, device); +} + +status_t AudioPolicyService::releaseSoundTriggerSession(audio_session_t session) +{ + if (mAudioPolicyManager == NULL) { + return NO_INIT; + } + + return mAudioPolicyManager->releaseSoundTriggerSession(session); +} + +}; // namespace android diff --git a/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp b/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp new file mode 100644 index 0000000..f20c070 --- /dev/null +++ b/services/audiopolicy/AudioPolicyInterfaceImplLegacy.cpp @@ -0,0 +1,516 @@ +/* + * 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 "AudioPolicyService" +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include "AudioPolicyService.h" +#include "ServiceUtilities.h" + +#include <system/audio.h> +#include <system/audio_policy.h> +#include <hardware/audio_policy.h> + +namespace android { + + +// ---------------------------------------------------------------------------- + +status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device, + audio_policy_dev_state_t state, + const char *device_address) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (!audio_is_output_device(device) && !audio_is_input_device(device)) { + return BAD_VALUE; + } + if (state != AUDIO_POLICY_DEVICE_STATE_AVAILABLE && + state != AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) { + return BAD_VALUE; + } + + ALOGV("setDeviceConnectionState()"); + Mutex::Autolock _l(mLock); + return mpAudioPolicy->set_device_connection_state(mpAudioPolicy, device, + state, device_address); +} + +audio_policy_dev_state_t AudioPolicyService::getDeviceConnectionState( + audio_devices_t device, + const char *device_address) +{ + if (mpAudioPolicy == NULL) { + return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; + } + return mpAudioPolicy->get_device_connection_state(mpAudioPolicy, device, + device_address); +} + +status_t AudioPolicyService::setPhoneState(audio_mode_t state) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (uint32_t(state) >= AUDIO_MODE_CNT) { + return BAD_VALUE; + } + + ALOGV("setPhoneState()"); + + // TODO: check if it is more appropriate to do it in platform specific policy manager + AudioSystem::setMode(state); + + Mutex::Autolock _l(mLock); + mpAudioPolicy->set_phone_state(mpAudioPolicy, state); + return NO_ERROR; +} + +status_t AudioPolicyService::setForceUse(audio_policy_force_use_t usage, + audio_policy_forced_cfg_t config) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) { + return BAD_VALUE; + } + if (config < 0 || config >= AUDIO_POLICY_FORCE_CFG_CNT) { + return BAD_VALUE; + } + ALOGV("setForceUse()"); + Mutex::Autolock _l(mLock); + mpAudioPolicy->set_force_use(mpAudioPolicy, usage, config); + return NO_ERROR; +} + +audio_policy_forced_cfg_t AudioPolicyService::getForceUse(audio_policy_force_use_t usage) +{ + if (mpAudioPolicy == NULL) { + return AUDIO_POLICY_FORCE_NONE; + } + if (usage < 0 || usage >= AUDIO_POLICY_FORCE_USE_CNT) { + return AUDIO_POLICY_FORCE_NONE; + } + return mpAudioPolicy->get_force_use(mpAudioPolicy, usage); +} + +audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + if (mpAudioPolicy == NULL) { + return 0; + } + ALOGV("getOutput()"); + Mutex::Autolock _l(mLock); + return mpAudioPolicy->get_output(mpAudioPolicy, stream, samplingRate, + format, channelMask, flags, offloadInfo); +} + +status_t AudioPolicyService::startOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + ALOGV("startOutput()"); + Mutex::Autolock _l(mLock); + + // create audio processors according to stream + status_t status = mAudioPolicyEffects->addOutputSessionEffects(output, stream, session); + if (status != NO_ERROR && status != ALREADY_EXISTS) { + ALOGW("Failed to add effects on session %d", session); + } + + return mpAudioPolicy->start_output(mpAudioPolicy, output, stream, session); +} + +status_t AudioPolicyService::stopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + ALOGV("stopOutput()"); + mOutputCommandThread->stopOutputCommand(output, stream, session); + return NO_ERROR; +} + +status_t AudioPolicyService::doStopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session) +{ + ALOGV("doStopOutput from tid %d", gettid()); + Mutex::Autolock _l(mLock); + + // release audio processors from the stream + status_t status = mAudioPolicyEffects->releaseOutputSessionEffects(output, stream, session); + if (status != NO_ERROR && status != ALREADY_EXISTS) { + ALOGW("Failed to release effects on session %d", session); + } + + return mpAudioPolicy->stop_output(mpAudioPolicy, output, stream, session); +} + +void AudioPolicyService::releaseOutput(audio_io_handle_t output) +{ + if (mpAudioPolicy == NULL) { + return; + } + ALOGV("releaseOutput()"); + mOutputCommandThread->releaseOutputCommand(output); +} + +void AudioPolicyService::doReleaseOutput(audio_io_handle_t output) +{ + ALOGV("doReleaseOutput from tid %d", gettid()); + Mutex::Autolock _l(mLock); + mpAudioPolicy->release_output(mpAudioPolicy, output); +} + +audio_io_handle_t AudioPolicyService::getInput(audio_source_t inputSource, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + int audioSession, + audio_input_flags_t flags __unused) +{ + if (mpAudioPolicy == NULL) { + return 0; + } + // already checked by client, but double-check in case the client wrapper is bypassed + if (inputSource >= AUDIO_SOURCE_CNT && inputSource != AUDIO_SOURCE_HOTWORD) { + return 0; + } + + if ((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) { + return 0; + } + + Mutex::Autolock _l(mLock); + // the audio_in_acoustics_t parameter is ignored by get_input() + audio_io_handle_t input = mpAudioPolicy->get_input(mpAudioPolicy, inputSource, samplingRate, + format, channelMask, (audio_in_acoustics_t) 0); + + if (input == 0) { + return input; + } + + // create audio pre processors according to input source + status_t status = mAudioPolicyEffects->addInputEffects(input, inputSource, audioSession); + if (status != NO_ERROR && status != ALREADY_EXISTS) { + ALOGW("Failed to add effects on input %d", input); + } + + return input; +} + +status_t AudioPolicyService::startInput(audio_io_handle_t input, + audio_session_t session __unused) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + + return mpAudioPolicy->start_input(mpAudioPolicy, input); +} + +status_t AudioPolicyService::stopInput(audio_io_handle_t input, + audio_session_t session __unused) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + + return mpAudioPolicy->stop_input(mpAudioPolicy, input); +} + +void AudioPolicyService::releaseInput(audio_io_handle_t input, + audio_session_t session __unused) +{ + if (mpAudioPolicy == NULL) { + return; + } + Mutex::Autolock _l(mLock); + mpAudioPolicy->release_input(mpAudioPolicy, input); + + // release audio processors from the input + status_t status = mAudioPolicyEffects->releaseInputEffects(input); + if(status != NO_ERROR) { + ALOGW("Failed to release effects on input %d", input); + } +} + +status_t AudioPolicyService::initStreamVolume(audio_stream_type_t stream, + int indexMin, + int indexMax) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); + mpAudioPolicy->init_stream_volume(mpAudioPolicy, stream, indexMin, indexMax); + return NO_ERROR; +} + +status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream, + int index, + audio_devices_t device) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); + if (mpAudioPolicy->set_stream_volume_index_for_device) { + return mpAudioPolicy->set_stream_volume_index_for_device(mpAudioPolicy, + stream, + index, + device); + } else { + return mpAudioPolicy->set_stream_volume_index(mpAudioPolicy, stream, index); + } +} + +status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream, + int *index, + audio_devices_t device) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + if (uint32_t(stream) >= AUDIO_STREAM_CNT) { + return BAD_VALUE; + } + Mutex::Autolock _l(mLock); + if (mpAudioPolicy->get_stream_volume_index_for_device) { + return mpAudioPolicy->get_stream_volume_index_for_device(mpAudioPolicy, + stream, + index, + device); + } else { + return mpAudioPolicy->get_stream_volume_index(mpAudioPolicy, stream, index); + } +} + +uint32_t AudioPolicyService::getStrategyForStream(audio_stream_type_t stream) +{ + if (mpAudioPolicy == NULL) { + return 0; + } + return mpAudioPolicy->get_strategy_for_stream(mpAudioPolicy, stream); +} + +//audio policy: use audio_device_t appropriately + +audio_devices_t AudioPolicyService::getDevicesForStream(audio_stream_type_t stream) +{ + if (mpAudioPolicy == NULL) { + return (audio_devices_t)0; + } + return mpAudioPolicy->get_devices_for_stream(mpAudioPolicy, stream); +} + +audio_io_handle_t AudioPolicyService::getOutputForEffect(const effect_descriptor_t *desc) +{ + // FIXME change return type to status_t, and return NO_INIT here + if (mpAudioPolicy == NULL) { + return 0; + } + Mutex::Autolock _l(mLock); + return mpAudioPolicy->get_output_for_effect(mpAudioPolicy, desc); +} + +status_t AudioPolicyService::registerEffect(const effect_descriptor_t *desc, + audio_io_handle_t io, + uint32_t strategy, + int session, + int id) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + return mpAudioPolicy->register_effect(mpAudioPolicy, desc, io, strategy, session, id); +} + +status_t AudioPolicyService::unregisterEffect(int id) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + return mpAudioPolicy->unregister_effect(mpAudioPolicy, id); +} + +status_t AudioPolicyService::setEffectEnabled(int id, bool enabled) +{ + if (mpAudioPolicy == NULL) { + return NO_INIT; + } + return mpAudioPolicy->set_effect_enabled(mpAudioPolicy, id, enabled); +} + +bool AudioPolicyService::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const +{ + if (mpAudioPolicy == NULL) { + return 0; + } + Mutex::Autolock _l(mLock); + return mpAudioPolicy->is_stream_active(mpAudioPolicy, stream, inPastMs); +} + +bool AudioPolicyService::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const +{ + if (mpAudioPolicy == NULL) { + return 0; + } + Mutex::Autolock _l(mLock); + return mpAudioPolicy->is_stream_active_remotely(mpAudioPolicy, stream, inPastMs); +} + +bool AudioPolicyService::isSourceActive(audio_source_t source) const +{ + if (mpAudioPolicy == NULL) { + return false; + } + if (mpAudioPolicy->is_source_active == 0) { + return false; + } + Mutex::Autolock _l(mLock); + return mpAudioPolicy->is_source_active(mpAudioPolicy, source); +} + +status_t AudioPolicyService::queryDefaultPreProcessing(int audioSession, + effect_descriptor_t *descriptors, + uint32_t *count) +{ + if (mpAudioPolicy == NULL) { + *count = 0; + return NO_INIT; + } + Mutex::Autolock _l(mLock); + + return mAudioPolicyEffects->queryDefaultInputEffects(audioSession, descriptors, count); +} + +bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info) +{ + if (mpAudioPolicy == NULL) { + ALOGV("mpAudioPolicy == NULL"); + return false; + } + + if (mpAudioPolicy->is_offload_supported == NULL) { + ALOGV("HAL does not implement is_offload_supported"); + return false; + } + + return mpAudioPolicy->is_offload_supported(mpAudioPolicy, &info); +} + +status_t AudioPolicyService::listAudioPorts(audio_port_role_t role __unused, + audio_port_type_t type __unused, + unsigned int *num_ports, + struct audio_port *ports __unused, + unsigned int *generation __unused) +{ + *num_ports = 0; + return INVALID_OPERATION; +} + +status_t AudioPolicyService::getAudioPort(struct audio_port *port __unused) +{ + return INVALID_OPERATION; +} + +status_t AudioPolicyService::createAudioPatch(const struct audio_patch *patch __unused, + audio_patch_handle_t *handle __unused) +{ + return INVALID_OPERATION; +} + +status_t AudioPolicyService::releaseAudioPatch(audio_patch_handle_t handle __unused) +{ + return INVALID_OPERATION; +} + +status_t AudioPolicyService::listAudioPatches(unsigned int *num_patches, + struct audio_patch *patches __unused, + unsigned int *generation __unused) +{ + *num_patches = 0; + return INVALID_OPERATION; +} + +status_t AudioPolicyService::setAudioPortConfig(const struct audio_port_config *config __unused) +{ + return INVALID_OPERATION; +} + +audio_io_handle_t AudioPolicyService::getOutputForAttr(const audio_attributes_t *attr __unused, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + audio_stream_type_t stream = audio_attributes_to_stream_type(attr); + + return getOutput(stream, samplingRate, format, channelMask, flags, offloadInfo); +} + +status_t AudioPolicyService::acquireSoundTriggerSession(audio_session_t *session, + audio_io_handle_t *ioHandle, + audio_devices_t *device) +{ + return INVALID_OPERATION; +} + +status_t AudioPolicyService::releaseSoundTriggerSession(audio_session_t session) +{ + return INVALID_OPERATION; +} + +}; // namespace android diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp new file mode 100644 index 0000000..22c4e04 --- /dev/null +++ b/services/audiopolicy/AudioPolicyManager.cpp @@ -0,0 +1,7182 @@ +/* + * 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 "AudioPolicyManager" +//#define LOG_NDEBUG 0 + +//#define VERY_VERBOSE_LOGGING +#ifdef VERY_VERBOSE_LOGGING +#define ALOGVV ALOGV +#else +#define ALOGVV(a...) do { } while(0) +#endif + +// A device mask for all audio input devices that are considered "virtual" when evaluating +// active inputs in getActiveInput() +#define APM_AUDIO_IN_DEVICE_VIRTUAL_ALL AUDIO_DEVICE_IN_REMOTE_SUBMIX +// A device mask for all audio output devices that are considered "remote" when evaluating +// active output devices in isStreamActiveRemotely() +#define APM_AUDIO_OUT_DEVICE_REMOTE_ALL AUDIO_DEVICE_OUT_REMOTE_SUBMIX +// A device mask for all audio input and output devices where matching inputs/outputs on device +// type alone is not enough: the address must match too +#define APM_AUDIO_DEVICE_MATCH_ADDRESS_ALL (AUDIO_DEVICE_IN_REMOTE_SUBMIX | \ + AUDIO_DEVICE_OUT_REMOTE_SUBMIX) + +#include <inttypes.h> +#include <math.h> + +#include <cutils/properties.h> +#include <utils/Log.h> +#include <hardware/audio.h> +#include <hardware/audio_effect.h> +#include <media/AudioParameter.h> +#include <soundtrigger/SoundTrigger.h> +#include "AudioPolicyManager.h" +#include "audio_policy_conf.h" + +namespace android { + +// ---------------------------------------------------------------------------- +// Definitions for audio_policy.conf file parsing +// ---------------------------------------------------------------------------- + +struct StringToEnum { + const char *name; + uint32_t value; +}; + +#define STRING_TO_ENUM(string) { #string, string } +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +const StringToEnum sDeviceNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_DEVICE_OUT_EARPIECE), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPEAKER), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPEAKER_SAFE), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_WIRED_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_WIRED_HEADPHONE), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_SCO), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_ALL_SCO), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_ALL_A2DP), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_AUX_DIGITAL), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_HDMI), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_USB_ACCESSORY), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_USB_DEVICE), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_ALL_USB), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_REMOTE_SUBMIX), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_TELEPHONY_TX), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_LINE), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_HDMI_ARC), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPDIF), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_FM), + STRING_TO_ENUM(AUDIO_DEVICE_OUT_AUX_LINE), + STRING_TO_ENUM(AUDIO_DEVICE_IN_AMBIENT), + STRING_TO_ENUM(AUDIO_DEVICE_IN_BUILTIN_MIC), + STRING_TO_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_IN_ALL_SCO), + STRING_TO_ENUM(AUDIO_DEVICE_IN_WIRED_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_IN_AUX_DIGITAL), + STRING_TO_ENUM(AUDIO_DEVICE_IN_HDMI), + STRING_TO_ENUM(AUDIO_DEVICE_IN_TELEPHONY_RX), + STRING_TO_ENUM(AUDIO_DEVICE_IN_VOICE_CALL), + STRING_TO_ENUM(AUDIO_DEVICE_IN_BACK_MIC), + STRING_TO_ENUM(AUDIO_DEVICE_IN_REMOTE_SUBMIX), + STRING_TO_ENUM(AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET), + STRING_TO_ENUM(AUDIO_DEVICE_IN_USB_ACCESSORY), + STRING_TO_ENUM(AUDIO_DEVICE_IN_USB_DEVICE), + STRING_TO_ENUM(AUDIO_DEVICE_IN_FM_TUNER), + STRING_TO_ENUM(AUDIO_DEVICE_IN_TV_TUNER), + STRING_TO_ENUM(AUDIO_DEVICE_IN_LINE), + STRING_TO_ENUM(AUDIO_DEVICE_IN_SPDIF), + STRING_TO_ENUM(AUDIO_DEVICE_IN_BLUETOOTH_A2DP), + STRING_TO_ENUM(AUDIO_DEVICE_IN_LOOPBACK), +}; + +const StringToEnum sFlagNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DIRECT), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_PRIMARY), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_FAST), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_DEEP_BUFFER), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_NON_BLOCKING), + STRING_TO_ENUM(AUDIO_OUTPUT_FLAG_HW_AV_SYNC), +}; + +const StringToEnum sFormatNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_FORMAT_PCM_16_BIT), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_8_BIT), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_32_BIT), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_8_24_BIT), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_FLOAT), + STRING_TO_ENUM(AUDIO_FORMAT_PCM_24_BIT_PACKED), + STRING_TO_ENUM(AUDIO_FORMAT_MP3), + STRING_TO_ENUM(AUDIO_FORMAT_AAC), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_MAIN), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_LC), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_SSR), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_LTP), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_HE_V1), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_SCALABLE), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_ERLC), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_LD), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_HE_V2), + STRING_TO_ENUM(AUDIO_FORMAT_AAC_ELD), + STRING_TO_ENUM(AUDIO_FORMAT_VORBIS), + STRING_TO_ENUM(AUDIO_FORMAT_HE_AAC_V1), + STRING_TO_ENUM(AUDIO_FORMAT_HE_AAC_V2), + STRING_TO_ENUM(AUDIO_FORMAT_OPUS), + STRING_TO_ENUM(AUDIO_FORMAT_AC3), + STRING_TO_ENUM(AUDIO_FORMAT_E_AC3), +}; + +const StringToEnum sOutChannelsNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_MONO), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_STEREO), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_QUAD), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_5POINT1), + STRING_TO_ENUM(AUDIO_CHANNEL_OUT_7POINT1), +}; + +const StringToEnum sInChannelsNameToEnumTable[] = { + STRING_TO_ENUM(AUDIO_CHANNEL_IN_MONO), + STRING_TO_ENUM(AUDIO_CHANNEL_IN_STEREO), + 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, + const char *name) +{ + for (size_t i = 0; i < size; i++) { + if (strcmp(table[i].name, name) == 0) { + ALOGV("stringToEnum() found %s", table[i].name); + return table[i].value; + } + } + return 0; +} + +const char *AudioPolicyManager::enumToString(const struct StringToEnum *table, + size_t size, + uint32_t value) +{ + for (size_t i = 0; i < size; i++) { + if (table[i].value == value) { + return table[i].name; + } + } + return ""; +} + +bool AudioPolicyManager::stringToBool(const char *value) +{ + return ((strcasecmp("true", value) == 0) || (strcmp("1", value) == 0)); +} + + +// ---------------------------------------------------------------------------- +// AudioPolicyInterface implementation +// ---------------------------------------------------------------------------- + + +status_t AudioPolicyManager::setDeviceConnectionState(audio_devices_t device, + audio_policy_dev_state_t state, + const char *device_address) +{ + String8 address = (device_address == NULL) ? String8("") : String8(device_address); + + ALOGV("setDeviceConnectionState() device: %x, state %d, address %s", + device, state, address.string()); + + // connect/disconnect only 1 device at a time + if (!audio_is_output_device(device) && !audio_is_input_device(device)) return BAD_VALUE; + + // handle output devices + if (audio_is_output_device(device)) { + SortedVector <audio_io_handle_t> outputs; + + 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 + // by checkOutputsForDevice(). This will be needed by checkOutputForAllStrategies() + mPreviousOutputs = mOutputs; + switch (state) + { + // handle output device connection + case AUDIO_POLICY_DEVICE_STATE_AVAILABLE: + if (index >= 0) { + ALOGW("setDeviceConnectionState() device already connected: %x", device); + return INVALID_OPERATION; + } + ALOGV("setDeviceConnectionState() connecting device %x", device); + + // register new device as available + index = mAvailableOutputDevices.add(devDesc); + if (index >= 0) { + sp<HwModule> module = getModuleForDevice(device); + if (module == 0) { + ALOGD("setDeviceConnectionState() could not find HW module for device %08x", + device); + mAvailableOutputDevices.remove(devDesc); + return INVALID_OPERATION; + } + mAvailableOutputDevices[index]->mId = nextUniqueId(); + mAvailableOutputDevices[index]->mModule = module; + } else { + return NO_MEMORY; + } + + if (checkOutputsForDevice(devDesc, state, outputs, address) != NO_ERROR) { + mAvailableOutputDevices.remove(devDesc); + 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()); + break; + // handle output device disconnection + case AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE: { + if (index < 0) { + ALOGW("setDeviceConnectionState() device not connected: %x", device); + return INVALID_OPERATION; + } + + ALOGV("setDeviceConnectionState() disconnecting output device %x", device); + + // Set Disconnect to HALs + AudioParameter param = AudioParameter(address); + param.addInt(String8(AUDIO_PARAMETER_DEVICE_DISCONNECT), device); + mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString()); + + // remove device from available output devices + mAvailableOutputDevices.remove(devDesc); + + checkOutputsForDevice(devDesc, state, outputs, address); + } break; + + default: + ALOGE("setDeviceConnectionState() invalid state: %x", state); + return BAD_VALUE; + } + + // checkA2dpSuspend must run before checkOutputForAllStrategies so that A2DP + // output is suspended before any tracks are moved to it + checkA2dpSuspend(); + checkOutputForAllStrategies(); + // outputs must be closed after checkOutputForAllStrategies() is executed + if (!outputs.isEmpty()) { + for (size_t i = 0; i < outputs.size(); i++) { + sp<AudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]); + // close unused outputs after device disconnection or direct outputs that have been + // opened by checkOutputsForDevice() to query dynamic parameters + if ((state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE) || + (((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0) && + (desc->mDirectOpenCount == 0))) { + closeOutput(outputs[i]); + } + } + // check again after closing A2DP output to reset mA2dpSuspended if needed + checkA2dpSuspend(); + } + + updateDevicesAndOutputs(); + if (mPhoneState == AUDIO_MODE_IN_CALL) { + audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/); + updateCallRouting(newDevice); + } + for (size_t i = 0; i < mOutputs.size(); i++) { + audio_io_handle_t output = mOutputs.keyAt(i); + if ((mPhoneState != AUDIO_MODE_IN_CALL) || (output != mPrimaryOutput)) { + audio_devices_t newDevice = getNewOutputDevice(mOutputs.keyAt(i), + true /*fromCache*/); + // do not force device change on duplicated output because if device is 0, it will + // also force a device 0 for the two outputs it is duplicated to which may override + // a valid device selection on those outputs. + bool force = !mOutputs.valueAt(i)->isDuplicated() + && (!deviceDistinguishesOnAddress(device) + // always force when disconnecting (a non-duplicated device) + || (state == AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE)); + setOutputDevice(output, newDevice, force, 0); + } + } + + 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(String8(""), device); + devDesc->mAddress = address; + ssize_t index = mAvailableInputDevices.indexOf(devDesc); + switch (state) + { + // handle input device connection + case AUDIO_POLICY_DEVICE_STATE_AVAILABLE: { + if (index >= 0) { + ALOGW("setDeviceConnectionState() device already connected: %d", device); + return INVALID_OPERATION; + } + sp<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; + } + + index = mAvailableInputDevices.add(devDesc); + if (index >= 0) { + mAvailableInputDevices[index]->mId = nextUniqueId(); + mAvailableInputDevices[index]->mModule = module; + } else { + return NO_MEMORY; + } + } break; + + // handle input device disconnection + case AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE: { + if (index < 0) { + ALOGW("setDeviceConnectionState() device not connected: %d", device); + return INVALID_OPERATION; + } + + ALOGV("setDeviceConnectionState() disconnecting input device %x", device); + + // Set Disconnect to HALs + AudioParameter param = AudioParameter(address); + param.addInt(String8(AUDIO_PARAMETER_DEVICE_DISCONNECT), device); + mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, param.toString()); + + checkInputsForDevice(device, state, inputs, address); + mAvailableInputDevices.remove(devDesc); + + } break; + + default: + ALOGE("setDeviceConnectionState() invalid state: %x", state); + return BAD_VALUE; + } + + closeAllInputs(); + + if (mPhoneState == AUDIO_MODE_IN_CALL) { + audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/); + updateCallRouting(newDevice); + } + + mpClientInterface->onAudioPortListUpdate(); + return NO_ERROR; + } // end if is input device + + ALOGW("setDeviceConnectionState() invalid device: %x", device); + return BAD_VALUE; +} + +audio_policy_dev_state_t AudioPolicyManager::getDeviceConnectionState(audio_devices_t device, + const char *device_address) +{ + audio_policy_dev_state_t state = AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; + sp<DeviceDescriptor> devDesc = new DeviceDescriptor(String8(""), device); + devDesc->mAddress = (device_address == NULL) ? String8("") : String8(device_address); + ssize_t index; + DeviceVector *deviceVector; + + if (audio_is_output_device(device)) { + deviceVector = &mAvailableOutputDevices; + } else if (audio_is_input_device(device)) { + deviceVector = &mAvailableInputDevices; + } else { + ALOGW("getDeviceConnectionState() invalid device type %08x", device); + return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; + } + + index = deviceVector->indexOf(devDesc); + if (index >= 0) { + return AUDIO_POLICY_DEVICE_STATE_AVAILABLE; + } else { + return AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE; + } +} + +void AudioPolicyManager::updateCallRouting(audio_devices_t rxDevice, int delayMs) +{ + bool createTxPatch = false; + struct audio_patch patch; + patch.num_sources = 1; + patch.num_sinks = 1; + status_t status; + audio_patch_handle_t afPatchHandle; + DeviceVector deviceList; + + audio_devices_t txDevice = getDeviceForInputSource(AUDIO_SOURCE_VOICE_COMMUNICATION); + ALOGV("updateCallRouting device rxDevice %08x txDevice %08x", rxDevice, txDevice); + + // release existing RX patch if any + if (mCallRxPatch != 0) { + mpClientInterface->releaseAudioPatch(mCallRxPatch->mAfPatchHandle, 0); + mCallRxPatch.clear(); + } + // release TX patch if any + if (mCallTxPatch != 0) { + mpClientInterface->releaseAudioPatch(mCallTxPatch->mAfPatchHandle, 0); + mCallTxPatch.clear(); + } + + // If the RX device is on the primary HW module, then use legacy routing method for voice calls + // via setOutputDevice() on primary output. + // Otherwise, create two audio patches for TX and RX path. + if (availablePrimaryOutputDevices() & rxDevice) { + setOutputDevice(mPrimaryOutput, rxDevice, true, delayMs); + // If the TX device is also on the primary HW module, setOutputDevice() will take care + // of it due to legacy implementation. If not, create a patch. + if ((availablePrimaryInputDevices() & txDevice & ~AUDIO_DEVICE_BIT_IN) + == AUDIO_DEVICE_NONE) { + createTxPatch = true; + } + } else { + // create RX path audio patch + deviceList = mAvailableOutputDevices.getDevicesFromType(rxDevice); + ALOG_ASSERT(!deviceList.isEmpty(), + "updateCallRouting() selected device not in output device list"); + sp<DeviceDescriptor> rxSinkDeviceDesc = deviceList.itemAt(0); + deviceList = mAvailableInputDevices.getDevicesFromType(AUDIO_DEVICE_IN_TELEPHONY_RX); + ALOG_ASSERT(!deviceList.isEmpty(), + "updateCallRouting() no telephony RX device"); + sp<DeviceDescriptor> rxSourceDeviceDesc = deviceList.itemAt(0); + + rxSourceDeviceDesc->toAudioPortConfig(&patch.sources[0]); + rxSinkDeviceDesc->toAudioPortConfig(&patch.sinks[0]); + + // request to reuse existing output stream if one is already opened to reach the RX device + SortedVector<audio_io_handle_t> outputs = + getOutputsForDevice(rxDevice, mOutputs); + audio_io_handle_t output = selectOutput(outputs, + AUDIO_OUTPUT_FLAG_NONE, + AUDIO_FORMAT_INVALID); + if (output != AUDIO_IO_HANDLE_NONE) { + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output); + ALOG_ASSERT(!outputDesc->isDuplicated(), + "updateCallRouting() RX device output is duplicated"); + outputDesc->toAudioPortConfig(&patch.sources[1]); + patch.num_sources = 2; + } + + afPatchHandle = AUDIO_PATCH_HANDLE_NONE; + status = mpClientInterface->createAudioPatch(&patch, &afPatchHandle, 0); + ALOGW_IF(status != NO_ERROR, "updateCallRouting() error %d creating RX audio patch", + status); + if (status == NO_ERROR) { + mCallRxPatch = new AudioPatch((audio_patch_handle_t)nextUniqueId(), + &patch, mUidCached); + mCallRxPatch->mAfPatchHandle = afPatchHandle; + mCallRxPatch->mUid = mUidCached; + } + createTxPatch = true; + } + if (createTxPatch) { + + struct audio_patch patch; + patch.num_sources = 1; + patch.num_sinks = 1; + deviceList = mAvailableInputDevices.getDevicesFromType(txDevice); + ALOG_ASSERT(!deviceList.isEmpty(), + "updateCallRouting() selected device not in input device list"); + sp<DeviceDescriptor> txSourceDeviceDesc = deviceList.itemAt(0); + txSourceDeviceDesc->toAudioPortConfig(&patch.sources[0]); + deviceList = mAvailableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_TELEPHONY_TX); + ALOG_ASSERT(!deviceList.isEmpty(), + "updateCallRouting() no telephony TX device"); + sp<DeviceDescriptor> txSinkDeviceDesc = deviceList.itemAt(0); + txSinkDeviceDesc->toAudioPortConfig(&patch.sinks[0]); + + SortedVector<audio_io_handle_t> outputs = + getOutputsForDevice(AUDIO_DEVICE_OUT_TELEPHONY_TX, mOutputs); + audio_io_handle_t output = selectOutput(outputs, + AUDIO_OUTPUT_FLAG_NONE, + AUDIO_FORMAT_INVALID); + // request to reuse existing output stream if one is already opened to reach the TX + // path output device + if (output != AUDIO_IO_HANDLE_NONE) { + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output); + ALOG_ASSERT(!outputDesc->isDuplicated(), + "updateCallRouting() RX device output is duplicated"); + outputDesc->toAudioPortConfig(&patch.sources[1]); + patch.num_sources = 2; + } + + afPatchHandle = AUDIO_PATCH_HANDLE_NONE; + status = mpClientInterface->createAudioPatch(&patch, &afPatchHandle, 0); + ALOGW_IF(status != NO_ERROR, "setPhoneState() error %d creating TX audio patch", + status); + if (status == NO_ERROR) { + mCallTxPatch = new AudioPatch((audio_patch_handle_t)nextUniqueId(), + &patch, mUidCached); + mCallTxPatch->mAfPatchHandle = afPatchHandle; + mCallTxPatch->mUid = mUidCached; + } + } +} + +void AudioPolicyManager::setPhoneState(audio_mode_t state) +{ + ALOGV("setPhoneState() state %d", state); + if (state < 0 || state >= AUDIO_MODE_CNT) { + ALOGW("setPhoneState() invalid state %d", state); + return; + } + + if (state == mPhoneState ) { + ALOGW("setPhoneState() setting same state %d", state); + return; + } + + // if leaving call state, handle special case of active streams + // pertaining to sonification strategy see handleIncallSonification() + if (isInCall()) { + ALOGV("setPhoneState() in call state management: new state is %d", state); + for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) { + handleIncallSonification((audio_stream_type_t)stream, false, true); + } + } + + // store previous phone state for management of sonification strategy below + int oldState = mPhoneState; + mPhoneState = state; + bool force = false; + + // are we entering or starting a call + if (!isStateInCall(oldState) && isStateInCall(state)) { + ALOGV(" Entering call in setPhoneState()"); + // force routing command to audio hardware when starting a call + // even if no device change is needed + force = true; + for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) { + mStreams[AUDIO_STREAM_DTMF].mVolumeCurve[j] = + sVolumeProfiles[AUDIO_STREAM_VOICE_CALL][j]; + } + } else if (isStateInCall(oldState) && !isStateInCall(state)) { + ALOGV(" Exiting call in setPhoneState()"); + // force routing command to audio hardware when exiting a call + // even if no device change is needed + force = true; + for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) { + mStreams[AUDIO_STREAM_DTMF].mVolumeCurve[j] = + sVolumeProfiles[AUDIO_STREAM_DTMF][j]; + } + } else if (isStateInCall(state) && (state != oldState)) { + ALOGV(" Switching between telephony and VoIP in setPhoneState()"); + // force routing command to audio hardware when switching between telephony and VoIP + // even if no device change is needed + force = true; + } + + // check for device and output changes triggered by new phone state + checkA2dpSuspend(); + checkOutputForAllStrategies(); + updateDevicesAndOutputs(); + + sp<AudioOutputDescriptor> hwOutputDesc = mOutputs.valueFor(mPrimaryOutput); + + int delayMs = 0; + if (isStateInCall(state)) { + nsecs_t sysTime = systemTime(); + for (size_t i = 0; i < mOutputs.size(); i++) { + sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i); + // mute media and sonification strategies and delay device switch by the largest + // latency of any output where either strategy is active. + // This avoid sending the ring tone or music tail into the earpiece or headset. + if ((desc->isStrategyActive(STRATEGY_MEDIA, + SONIFICATION_HEADSET_MUSIC_DELAY, + sysTime) || + desc->isStrategyActive(STRATEGY_SONIFICATION, + SONIFICATION_HEADSET_MUSIC_DELAY, + sysTime)) && + (delayMs < (int)desc->mLatency*2)) { + delayMs = desc->mLatency*2; + } + setStrategyMute(STRATEGY_MEDIA, true, mOutputs.keyAt(i)); + setStrategyMute(STRATEGY_MEDIA, false, mOutputs.keyAt(i), MUTE_TIME_MS, + getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/)); + setStrategyMute(STRATEGY_SONIFICATION, true, mOutputs.keyAt(i)); + setStrategyMute(STRATEGY_SONIFICATION, false, mOutputs.keyAt(i), MUTE_TIME_MS, + getDeviceForStrategy(STRATEGY_SONIFICATION, true /*fromCache*/)); + } + } + + // Note that despite the fact that getNewOutputDevice() is called on the primary output, + // the device returned is not necessarily reachable via this output + audio_devices_t rxDevice = getNewOutputDevice(mPrimaryOutput, false /*fromCache*/); + // force routing command to audio hardware when ending call + // even if no device change is needed + if (isStateInCall(oldState) && rxDevice == AUDIO_DEVICE_NONE) { + rxDevice = hwOutputDesc->device(); + } + + if (state == AUDIO_MODE_IN_CALL) { + updateCallRouting(rxDevice, delayMs); + } else if (oldState == AUDIO_MODE_IN_CALL) { + if (mCallRxPatch != 0) { + mpClientInterface->releaseAudioPatch(mCallRxPatch->mAfPatchHandle, 0); + mCallRxPatch.clear(); + } + if (mCallTxPatch != 0) { + mpClientInterface->releaseAudioPatch(mCallTxPatch->mAfPatchHandle, 0); + mCallTxPatch.clear(); + } + setOutputDevice(mPrimaryOutput, rxDevice, force, 0); + } else { + setOutputDevice(mPrimaryOutput, rxDevice, force, 0); + } + // if entering in call state, handle special case of active streams + // pertaining to sonification strategy see handleIncallSonification() + if (isStateInCall(state)) { + ALOGV("setPhoneState() in call state management: new state is %d", state); + for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) { + handleIncallSonification((audio_stream_type_t)stream, true, true); + } + } + + // Flag that ringtone volume must be limited to music volume until we exit MODE_RINGTONE + if (state == AUDIO_MODE_RINGTONE && + isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY)) { + mLimitRingtoneVolume = true; + } else { + mLimitRingtoneVolume = false; + } +} + +void AudioPolicyManager::setForceUse(audio_policy_force_use_t usage, + audio_policy_forced_cfg_t config) +{ + ALOGV("setForceUse() usage %d, config %d, mPhoneState %d", usage, config, mPhoneState); + + bool forceVolumeReeval = false; + switch(usage) { + case AUDIO_POLICY_FORCE_FOR_COMMUNICATION: + if (config != AUDIO_POLICY_FORCE_SPEAKER && config != AUDIO_POLICY_FORCE_BT_SCO && + config != AUDIO_POLICY_FORCE_NONE) { + ALOGW("setForceUse() invalid config %d for FOR_COMMUNICATION", config); + return; + } + forceVolumeReeval = true; + mForceUse[usage] = config; + break; + case AUDIO_POLICY_FORCE_FOR_MEDIA: + if (config != AUDIO_POLICY_FORCE_HEADPHONES && config != AUDIO_POLICY_FORCE_BT_A2DP && + config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY && + config != AUDIO_POLICY_FORCE_ANALOG_DOCK && + config != AUDIO_POLICY_FORCE_DIGITAL_DOCK && config != AUDIO_POLICY_FORCE_NONE && + config != AUDIO_POLICY_FORCE_NO_BT_A2DP) { + ALOGW("setForceUse() invalid config %d for FOR_MEDIA", config); + return; + } + mForceUse[usage] = config; + break; + case AUDIO_POLICY_FORCE_FOR_RECORD: + if (config != AUDIO_POLICY_FORCE_BT_SCO && config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY && + config != AUDIO_POLICY_FORCE_NONE) { + ALOGW("setForceUse() invalid config %d for FOR_RECORD", config); + return; + } + mForceUse[usage] = config; + break; + case AUDIO_POLICY_FORCE_FOR_DOCK: + if (config != AUDIO_POLICY_FORCE_NONE && config != AUDIO_POLICY_FORCE_BT_CAR_DOCK && + config != AUDIO_POLICY_FORCE_BT_DESK_DOCK && + config != AUDIO_POLICY_FORCE_WIRED_ACCESSORY && + config != AUDIO_POLICY_FORCE_ANALOG_DOCK && + config != AUDIO_POLICY_FORCE_DIGITAL_DOCK) { + ALOGW("setForceUse() invalid config %d for FOR_DOCK", config); + } + forceVolumeReeval = true; + mForceUse[usage] = config; + break; + case AUDIO_POLICY_FORCE_FOR_SYSTEM: + if (config != AUDIO_POLICY_FORCE_NONE && + config != AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) { + ALOGW("setForceUse() invalid config %d for FOR_SYSTEM", config); + } + forceVolumeReeval = true; + mForceUse[usage] = config; + break; + case AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO: + if (config != AUDIO_POLICY_FORCE_NONE && + config != AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED) { + ALOGW("setForceUse() invalid config %d forHDMI_SYSTEM_AUDIO", config); + } + mForceUse[usage] = config; + break; + default: + ALOGW("setForceUse() invalid usage %d", usage); + break; + } + + // check for device and output changes triggered by new force usage + checkA2dpSuspend(); + checkOutputForAllStrategies(); + updateDevicesAndOutputs(); + if (mPhoneState == AUDIO_MODE_IN_CALL) { + audio_devices_t newDevice = getNewOutputDevice(mPrimaryOutput, true /*fromCache*/); + updateCallRouting(newDevice); + } + for (size_t i = 0; i < mOutputs.size(); i++) { + audio_io_handle_t output = mOutputs.keyAt(i); + audio_devices_t newDevice = getNewOutputDevice(output, true /*fromCache*/); + if ((mPhoneState != AUDIO_MODE_IN_CALL) || (output != mPrimaryOutput)) { + setOutputDevice(output, newDevice, (newDevice != AUDIO_DEVICE_NONE)); + } + if (forceVolumeReeval && (newDevice != AUDIO_DEVICE_NONE)) { + applyStreamVolumes(output, newDevice, 0, true); + } + } + + audio_io_handle_t activeInput = getActiveInput(); + if (activeInput != 0) { + setInputDevice(activeInput, getNewInputDevice(activeInput)); + } + +} + +audio_policy_forced_cfg_t AudioPolicyManager::getForceUse(audio_policy_force_use_t usage) +{ + return mForceUse[usage]; +} + +void AudioPolicyManager::setSystemProperty(const char* property, const char* value) +{ + ALOGV("setSystemProperty() property %s, value %s", property, value); +} + +// Find a direct output profile compatible with the parameters passed, even if the input flags do +// not explicitly request a direct output +sp<AudioPolicyManager::IOProfile> AudioPolicyManager::getProfileForDirectOutput( + audio_devices_t device, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags) +{ + for (size_t i = 0; i < mHwModules.size(); i++) { + if (mHwModules[i]->mHandle == 0) { + continue; + } + for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) { + sp<IOProfile> profile = mHwModules[i]->mOutputProfiles[j]; + bool found = profile->isCompatibleProfile(device, samplingRate, + NULL /*updatedSamplingRate*/, format, channelMask, + flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD ? + AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD : AUDIO_OUTPUT_FLAG_DIRECT); + if (found && (mAvailableOutputDevices.types() & profile->mSupportedDevices.types())) { + return profile; + } + } + } + return 0; +} + +audio_io_handle_t AudioPolicyManager::getOutput(audio_stream_type_t stream, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + + routing_strategy strategy = getStrategy(stream); + audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/); + ALOGV("getOutput() device %d, stream %d, samplingRate %d, format %x, channelMask %x, flags %x", + device, stream, samplingRate, format, channelMask, flags); + + return getOutputForDevice(device, stream, samplingRate,format, channelMask, flags, + offloadInfo); +} + +audio_io_handle_t AudioPolicyManager::getOutputForAttr(const audio_attributes_t *attr, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + if (attr == NULL) { + ALOGE("getOutputForAttr() called with NULL audio attributes"); + return 0; + } + ALOGV("getOutputForAttr() usage=%d, content=%d, tag=%s flags=%08x", + attr->usage, attr->content_type, attr->tags, attr->flags); + + // TODO this is where filtering for custom policies (rerouting, dynamic sources) will go + routing_strategy strategy = (routing_strategy) getStrategyForAttr(attr); + audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/); + + if ((attr->flags & AUDIO_FLAG_HW_AV_SYNC) != 0) { + flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_HW_AV_SYNC); + } + + ALOGV("getOutputForAttr() device %d, samplingRate %d, format %x, channelMask %x, flags %x", + device, samplingRate, format, channelMask, flags); + + audio_stream_type_t stream = streamTypefromAttributesInt(attr); + return getOutputForDevice(device, stream, samplingRate, format, channelMask, flags, + offloadInfo); +} + +audio_io_handle_t AudioPolicyManager::getOutputForDevice( + audio_devices_t device, + audio_stream_type_t stream, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo) +{ + audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; + uint32_t latency = 0; + status_t status; + +#ifdef AUDIO_POLICY_TEST + if (mCurOutput != 0) { + ALOGV("getOutput() test output mCurOutput %d, samplingRate %d, format %d, channelMask %x, mDirectOutput %d", + mCurOutput, mTestSamplingRate, mTestFormat, mTestChannels, mDirectOutput); + + if (mTestOutputs[mCurOutput] == 0) { + ALOGV("getOutput() opening test output"); + sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(NULL); + outputDesc->mDevice = mTestDevice; + outputDesc->mLatency = mTestLatencyMs; + outputDesc->mFlags = + (audio_output_flags_t)(mDirectOutput ? AUDIO_OUTPUT_FLAG_DIRECT : 0); + outputDesc->mRefCount[stream] = 0; + audio_config_t config = AUDIO_CONFIG_INITIALIZER; + config.sample_rate = mTestSamplingRate; + config.channel_mask = mTestChannels; + config.format = mTestFormat; + if (offloadInfo != NULL) { + config.offload_info = *offloadInfo; + } + status = mpClientInterface->openOutput(0, + &mTestOutputs[mCurOutput], + &config, + &outputDesc->mDevice, + String8(""), + &outputDesc->mLatency, + outputDesc->mFlags); + if (status == NO_ERROR) { + outputDesc->mSamplingRate = config.sample_rate; + outputDesc->mFormat = config.format; + outputDesc->mChannelMask = config.channel_mask; + AudioParameter outputCmd = AudioParameter(); + outputCmd.addInt(String8("set_id"),mCurOutput); + mpClientInterface->setParameters(mTestOutputs[mCurOutput],outputCmd.toString()); + addOutput(mTestOutputs[mCurOutput], outputDesc); + } + } + return mTestOutputs[mCurOutput]; + } +#endif //AUDIO_POLICY_TEST + + // open a direct output if required by specified parameters + //force direct flag if offload flag is set: offloading implies a direct output stream + // and all common behaviors are driven by checking only the direct flag + // this should normally be set appropriately in the policy configuration file + if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) { + flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_DIRECT); + } + if ((flags & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) != 0) { + flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_DIRECT); + } + + // Do not allow offloading if one non offloadable effect is enabled. This prevents from + // creating an offloaded track and tearing it down immediately after start when audioflinger + // detects there is an active non offloadable effect. + // FIXME: We should check the audio session here but we do not have it in this context. + // This may prevent offloading in rare situations where effects are left active by apps + // in the background. + sp<IOProfile> profile; + if (((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0) || + !isNonOffloadableEffectEnabled()) { + profile = getProfileForDirectOutput(device, + samplingRate, + format, + channelMask, + (audio_output_flags_t)flags); + } + + if (profile != 0) { + sp<AudioOutputDescriptor> outputDesc = NULL; + + for (size_t i = 0; i < mOutputs.size(); i++) { + sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i); + if (!desc->isDuplicated() && (profile == desc->mProfile)) { + outputDesc = desc; + // reuse direct output if currently open and configured with same parameters + if ((samplingRate == outputDesc->mSamplingRate) && + (format == outputDesc->mFormat) && + (channelMask == outputDesc->mChannelMask)) { + outputDesc->mDirectOpenCount++; + ALOGV("getOutput() reusing direct output %d", mOutputs.keyAt(i)); + return mOutputs.keyAt(i); + } + } + } + // close direct output if currently open and configured with different parameters + if (outputDesc != NULL) { + closeOutput(outputDesc->mIoHandle); + } + outputDesc = new AudioOutputDescriptor(profile); + outputDesc->mDevice = device; + outputDesc->mLatency = 0; + outputDesc->mFlags =(audio_output_flags_t) (outputDesc->mFlags | flags); + audio_config_t config = AUDIO_CONFIG_INITIALIZER; + config.sample_rate = samplingRate; + config.channel_mask = channelMask; + config.format = format; + if (offloadInfo != NULL) { + config.offload_info = *offloadInfo; + } + status = mpClientInterface->openOutput(profile->mModule->mHandle, + &output, + &config, + &outputDesc->mDevice, + String8(""), + &outputDesc->mLatency, + outputDesc->mFlags); + + // only accept an output with the requested parameters + if (status != NO_ERROR || + (samplingRate != 0 && samplingRate != config.sample_rate) || + (format != AUDIO_FORMAT_DEFAULT && format != config.format) || + (channelMask != 0 && channelMask != config.channel_mask)) { + ALOGV("getOutput() failed opening direct output: output %d samplingRate %d %d," + "format %d %d, channelMask %04x %04x", output, samplingRate, + outputDesc->mSamplingRate, format, outputDesc->mFormat, channelMask, + outputDesc->mChannelMask); + if (output != AUDIO_IO_HANDLE_NONE) { + mpClientInterface->closeOutput(output); + } + return AUDIO_IO_HANDLE_NONE; + } + outputDesc->mSamplingRate = config.sample_rate; + outputDesc->mChannelMask = config.channel_mask; + outputDesc->mFormat = config.format; + outputDesc->mRefCount[stream] = 0; + outputDesc->mStopTime[stream] = 0; + outputDesc->mDirectOpenCount = 1; + + audio_io_handle_t srcOutput = getOutputForEffect(); + addOutput(output, outputDesc); + audio_io_handle_t dstOutput = getOutputForEffect(); + if (dstOutput == output) { + mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, srcOutput, dstOutput); + } + mPreviousOutputs = mOutputs; + ALOGV("getOutput() returns new direct output %d", output); + mpClientInterface->onAudioPortListUpdate(); + return output; + } + + // ignoring channel mask due to downmix capability in mixer + + // open a non direct output + + // for non direct outputs, only PCM is supported + if (audio_is_linear_pcm(format)) { + // get which output is suitable for the specified stream. The actual + // routing change will happen when startOutput() will be called + SortedVector<audio_io_handle_t> outputs = getOutputsForDevice(device, mOutputs); + + // at this stage we should ignore the DIRECT flag as no direct output could be found earlier + flags = (audio_output_flags_t)(flags & ~AUDIO_OUTPUT_FLAG_DIRECT); + output = selectOutput(outputs, flags, format); + } + ALOGW_IF((output == 0), "getOutput() could not find output for stream %d, samplingRate %d," + "format %d, channels %x, flags %x", stream, samplingRate, format, channelMask, flags); + + ALOGV("getOutput() returns output %d", output); + + return output; +} + +audio_io_handle_t AudioPolicyManager::selectOutput(const SortedVector<audio_io_handle_t>& outputs, + audio_output_flags_t flags, + audio_format_t format) +{ + // select one output among several that provide a path to a particular device or set of + // devices (the list was previously build by getOutputsForDevice()). + // The priority is as follows: + // 1: the output with the highest number of requested policy flags + // 2: the primary output + // 3: the first output in the list + + if (outputs.size() == 0) { + return 0; + } + if (outputs.size() == 1) { + return outputs[0]; + } + + int maxCommonFlags = 0; + audio_io_handle_t outputFlags = 0; + audio_io_handle_t outputPrimary = 0; + + for (size_t i = 0; i < outputs.size(); i++) { + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(outputs[i]); + if (!outputDesc->isDuplicated()) { + // if a valid format is specified, skip output if not compatible + if (format != AUDIO_FORMAT_INVALID) { + if (outputDesc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) { + if (format != outputDesc->mFormat) { + continue; + } + } else if (!audio_is_linear_pcm(format)) { + continue; + } + } + + int commonFlags = popcount(outputDesc->mProfile->mFlags & flags); + if (commonFlags > maxCommonFlags) { + outputFlags = outputs[i]; + maxCommonFlags = commonFlags; + ALOGV("selectOutput() commonFlags for output %d, %04x", outputs[i], commonFlags); + } + if (outputDesc->mProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) { + outputPrimary = outputs[i]; + } + } + } + + if (outputFlags != 0) { + return outputFlags; + } + if (outputPrimary != 0) { + return outputPrimary; + } + + return outputs[0]; +} + +status_t AudioPolicyManager::startOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session) +{ + ALOGV("startOutput() output %d, stream %d, session %d", output, stream, session); + ssize_t index = mOutputs.indexOfKey(output); + if (index < 0) { + ALOGW("startOutput() unknown output %d", output); + return BAD_VALUE; + } + + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index); + + // increment usage count for this stream on the requested output: + // NOTE that the usage count is the same for duplicated output and hardware output which is + // necessary for a correct control of hardware output routing by startOutput() and stopOutput() + outputDesc->changeRefCount(stream, 1); + + if (outputDesc->mRefCount[stream] == 1) { + audio_devices_t newDevice = getNewOutputDevice(output, false /*fromCache*/); + routing_strategy strategy = getStrategy(stream); + bool shouldWait = (strategy == STRATEGY_SONIFICATION) || + (strategy == STRATEGY_SONIFICATION_RESPECTFUL); + uint32_t waitMs = 0; + bool force = false; + for (size_t i = 0; i < mOutputs.size(); i++) { + sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i); + if (desc != outputDesc) { + // force a device change if any other output is managed by the same hw + // module and has a current device selection that differs from selected device. + // In this case, the audio HAL must receive the new device selection so that it can + // change the device currently selected by the other active output. + if (outputDesc->sharesHwModuleWith(desc) && + desc->device() != newDevice) { + force = true; + } + // wait for audio on other active outputs to be presented when starting + // a notification so that audio focus effect can propagate. + uint32_t latency = desc->latency(); + if (shouldWait && desc->isActive(latency * 2) && (waitMs < latency)) { + waitMs = latency; + } + } + } + uint32_t muteWaitMs = setOutputDevice(output, newDevice, force); + + // handle special case for sonification while in call + if (isInCall()) { + handleIncallSonification(stream, true, false); + } + + // apply volume rules for current stream and device if necessary + checkAndSetVolume(stream, + mStreams[stream].getVolumeIndex(newDevice), + output, + newDevice); + + // update the outputs if starting an output with a stream that can affect notification + // routing + handleNotificationRoutingForStream(stream); + if (waitMs > muteWaitMs) { + usleep((waitMs - muteWaitMs) * 2 * 1000); + } + } + return NO_ERROR; +} + + +status_t AudioPolicyManager::stopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session) +{ + ALOGV("stopOutput() output %d, stream %d, session %d", output, stream, session); + ssize_t index = mOutputs.indexOfKey(output); + if (index < 0) { + ALOGW("stopOutput() unknown output %d", output); + return BAD_VALUE; + } + + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index); + + // handle special case for sonification while in call + if (isInCall()) { + handleIncallSonification(stream, false, false); + } + + if (outputDesc->mRefCount[stream] > 0) { + // decrement usage count of this stream on the output + outputDesc->changeRefCount(stream, -1); + // store time at which the stream was stopped - see isStreamActive() + if (outputDesc->mRefCount[stream] == 0) { + outputDesc->mStopTime[stream] = systemTime(); + audio_devices_t newDevice = getNewOutputDevice(output, false /*fromCache*/); + // delay the device switch by twice the latency because stopOutput() is executed when + // the track stop() command is received and at that time the audio track buffer can + // still contain data that needs to be drained. The latency only covers the audio HAL + // and kernel buffers. Also the latency does not always include additional delay in the + // audio path (audio DSP, CODEC ...) + setOutputDevice(output, newDevice, false, outputDesc->mLatency*2); + + // force restoring the device selection on other active outputs if it differs from the + // one being selected for this output + for (size_t i = 0; i < mOutputs.size(); i++) { + audio_io_handle_t curOutput = mOutputs.keyAt(i); + sp<AudioOutputDescriptor> desc = mOutputs.valueAt(i); + if (curOutput != output && + desc->isActive() && + outputDesc->sharesHwModuleWith(desc) && + (newDevice != desc->device())) { + setOutputDevice(curOutput, + getNewOutputDevice(curOutput, false /*fromCache*/), + true, + outputDesc->mLatency*2); + } + } + // update the outputs if stopping one with a stream that can affect notification routing + handleNotificationRoutingForStream(stream); + } + return NO_ERROR; + } else { + ALOGW("stopOutput() refcount is already 0 for output %d", output); + return INVALID_OPERATION; + } +} + +void AudioPolicyManager::releaseOutput(audio_io_handle_t output) +{ + ALOGV("releaseOutput() %d", output); + ssize_t index = mOutputs.indexOfKey(output); + if (index < 0) { + ALOGW("releaseOutput() releasing unknown output %d", output); + return; + } + +#ifdef AUDIO_POLICY_TEST + int testIndex = testOutputIndex(output); + if (testIndex != 0) { + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(index); + if (outputDesc->isActive()) { + mpClientInterface->closeOutput(output); + mOutputs.removeItem(output); + mTestOutputs[testIndex] = 0; + } + return; + } +#endif //AUDIO_POLICY_TEST + + sp<AudioOutputDescriptor> desc = mOutputs.valueAt(index); + if (desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) { + if (desc->mDirectOpenCount <= 0) { + ALOGW("releaseOutput() invalid open count %d for output %d", + desc->mDirectOpenCount, output); + return; + } + if (--desc->mDirectOpenCount == 0) { + closeOutput(output); + // If effects where present on the output, audioflinger moved them to the primary + // output by default: move them back to the appropriate output. + audio_io_handle_t dstOutput = getOutputForEffect(); + if (dstOutput != mPrimaryOutput) { + mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, mPrimaryOutput, dstOutput); + } + mpClientInterface->onAudioPortListUpdate(); + } + } +} + + +audio_io_handle_t AudioPolicyManager::getInput(audio_source_t inputSource, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_session_t session, + audio_input_flags_t flags) +{ + ALOGV("getInput() inputSource %d, samplingRate %d, format %d, channelMask %x, session %d, " + "flags %#x", + inputSource, samplingRate, format, channelMask, session, flags); + + audio_devices_t device = getDeviceForInputSource(inputSource); + + if (device == AUDIO_DEVICE_NONE) { + ALOGW("getInput() could not find device for inputSource %d", inputSource); + return AUDIO_IO_HANDLE_NONE; + } + + // adapt channel selection to input source + switch (inputSource) { + case AUDIO_SOURCE_VOICE_UPLINK: + channelMask = AUDIO_CHANNEL_IN_VOICE_UPLINK; + break; + case AUDIO_SOURCE_VOICE_DOWNLINK: + channelMask = AUDIO_CHANNEL_IN_VOICE_DNLINK; + break; + case AUDIO_SOURCE_VOICE_CALL: + channelMask = AUDIO_CHANNEL_IN_VOICE_UPLINK | AUDIO_CHANNEL_IN_VOICE_DNLINK; + break; + default: + break; + } + + sp<IOProfile> profile = getInputProfile(device, + samplingRate, + format, + channelMask, + flags); + if (profile == 0) { + ALOGW("getInput() could not find profile for device 0x%X, samplingRate %u, format %#x, " + "channelMask 0x%X, flags %#x", + device, samplingRate, format, channelMask, flags); + return AUDIO_IO_HANDLE_NONE; + } + + if (profile->mModule->mHandle == 0) { + ALOGE("getInput(): HW module %s not opened", profile->mModule->mName); + return AUDIO_IO_HANDLE_NONE; + } + + audio_config_t config = AUDIO_CONFIG_INITIALIZER; + config.sample_rate = samplingRate; + config.channel_mask = channelMask; + config.format = format; + audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; + + bool isSoundTrigger = false; + audio_source_t halInputSource = inputSource; + if (inputSource == AUDIO_SOURCE_HOTWORD) { + ssize_t index = mSoundTriggerSessions.indexOfKey(session); + if (index >= 0) { + input = mSoundTriggerSessions.valueFor(session); + isSoundTrigger = true; + ALOGV("SoundTrigger capture on session %d input %d", session, input); + } else { + halInputSource = AUDIO_SOURCE_VOICE_RECOGNITION; + } + } + status_t status = mpClientInterface->openInput(profile->mModule->mHandle, + &input, + &config, + &device, + String8(""), + halInputSource, + flags); + + // only accept input with the exact requested set of parameters + if (status != NO_ERROR || + (samplingRate != config.sample_rate) || + (format != config.format) || + (channelMask != config.channel_mask)) { + ALOGW("getInput() failed opening input: samplingRate %d, format %d, channelMask %x", + samplingRate, format, channelMask); + if (input != AUDIO_IO_HANDLE_NONE) { + mpClientInterface->closeInput(input); + } + return AUDIO_IO_HANDLE_NONE; + } + + sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(profile); + inputDesc->mInputSource = inputSource; + inputDesc->mRefCount = 0; + inputDesc->mOpenRefCount = 1; + inputDesc->mSamplingRate = samplingRate; + inputDesc->mFormat = format; + inputDesc->mChannelMask = channelMask; + inputDesc->mDevice = device; + inputDesc->mSessions.add(session); + inputDesc->mIsSoundTrigger = isSoundTrigger; + + addInput(input, inputDesc); + mpClientInterface->onAudioPortListUpdate(); + return input; +} + +status_t AudioPolicyManager::startInput(audio_io_handle_t input, + audio_session_t session) +{ + ALOGV("startInput() input %d", input); + ssize_t index = mInputs.indexOfKey(input); + if (index < 0) { + ALOGW("startInput() unknown input %d", input); + return BAD_VALUE; + } + sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index); + + index = inputDesc->mSessions.indexOf(session); + if (index < 0) { + ALOGW("startInput() unknown session %d on input %d", session, input); + return BAD_VALUE; + } + + // virtual input devices are compatible with other input devices + if (!isVirtualInputDevice(inputDesc->mDevice)) { + + // for a non-virtual input device, check if there is another (non-virtual) active input + audio_io_handle_t activeInput = getActiveInput(); + if (activeInput != 0 && activeInput != input) { + + // If the already active input uses AUDIO_SOURCE_HOTWORD then it is closed, + // otherwise the active input continues and the new input cannot be started. + sp<AudioInputDescriptor> activeDesc = mInputs.valueFor(activeInput); + if (activeDesc->mInputSource == AUDIO_SOURCE_HOTWORD) { + ALOGW("startInput(%d) preempting low-priority input %d", input, activeInput); + stopInput(activeInput, activeDesc->mSessions.itemAt(0)); + releaseInput(activeInput, activeDesc->mSessions.itemAt(0)); + } else { + ALOGE("startInput(%d) failed: other input %d already started", input, activeInput); + return INVALID_OPERATION; + } + } + } + + if (inputDesc->mRefCount == 0) { + if (activeInputsCount() == 0) { + SoundTrigger::setCaptureState(true); + } + setInputDevice(input, getNewInputDevice(input), true /* force */); + + // Automatically enable the remote submix output when input is started. + // For remote submix (a virtual device), we open only one input per capture request. + if (audio_is_remote_submix_device(inputDesc->mDevice)) { + setDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, + AUDIO_POLICY_DEVICE_STATE_AVAILABLE, AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS); + } + } + + ALOGV("AudioPolicyManager::startInput() input source = %d", inputDesc->mInputSource); + + inputDesc->mRefCount++; + return NO_ERROR; +} + +status_t AudioPolicyManager::stopInput(audio_io_handle_t input, + audio_session_t session) +{ + ALOGV("stopInput() input %d", input); + ssize_t index = mInputs.indexOfKey(input); + if (index < 0) { + ALOGW("stopInput() unknown input %d", input); + return BAD_VALUE; + } + sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index); + + index = inputDesc->mSessions.indexOf(session); + if (index < 0) { + ALOGW("stopInput() unknown session %d on input %d", session, input); + return BAD_VALUE; + } + + if (inputDesc->mRefCount == 0) { + ALOGW("stopInput() input %d already stopped", input); + return INVALID_OPERATION; + } + + inputDesc->mRefCount--; + if (inputDesc->mRefCount == 0) { + + // automatically disable the remote submix output when input is stopped + if (audio_is_remote_submix_device(inputDesc->mDevice)) { + setDeviceConnectionState(AUDIO_DEVICE_OUT_REMOTE_SUBMIX, + AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE, AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS); + } + + resetInputDevice(input); + + if (activeInputsCount() == 0) { + SoundTrigger::setCaptureState(false); + } + } + return NO_ERROR; +} + +void AudioPolicyManager::releaseInput(audio_io_handle_t input, + audio_session_t session) +{ + ALOGV("releaseInput() %d", input); + ssize_t index = mInputs.indexOfKey(input); + if (index < 0) { + ALOGW("releaseInput() releasing unknown input %d", input); + return; + } + sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(index); + ALOG_ASSERT(inputDesc != 0); + + index = inputDesc->mSessions.indexOf(session); + if (index < 0) { + ALOGW("releaseInput() unknown session %d on input %d", session, input); + return; + } + inputDesc->mSessions.remove(session); + if (inputDesc->mOpenRefCount == 0) { + ALOGW("releaseInput() invalid open ref count %d", inputDesc->mOpenRefCount); + return; + } + inputDesc->mOpenRefCount--; + if (inputDesc->mOpenRefCount > 0) { + ALOGV("releaseInput() exit > 0"); + return; + } + + closeInput(input); + mpClientInterface->onAudioPortListUpdate(); + ALOGV("releaseInput() exit"); +} + +void AudioPolicyManager::closeAllInputs() { + bool patchRemoved = false; + + for(size_t input_index = 0; input_index < mInputs.size(); input_index++) { + sp<AudioInputDescriptor> inputDesc = mInputs.valueAt(input_index); + ssize_t patch_index = mAudioPatches.indexOfKey(inputDesc->mPatchHandle); + if (patch_index >= 0) { + sp<AudioPatch> patchDesc = mAudioPatches.valueAt(patch_index); + status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0); + mAudioPatches.removeItemsAt(patch_index); + patchRemoved = true; + } + mpClientInterface->closeInput(mInputs.keyAt(input_index)); + } + mInputs.clear(); + nextAudioPortGeneration(); + + if (patchRemoved) { + mpClientInterface->onAudioPatchListUpdate(); + } +} + +void AudioPolicyManager::initStreamVolume(audio_stream_type_t stream, + int indexMin, + int indexMax) +{ + ALOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax); + if (indexMin < 0 || indexMin >= indexMax) { + ALOGW("initStreamVolume() invalid index limits for stream %d, min %d, max %d", stream , indexMin, indexMax); + return; + } + mStreams[stream].mIndexMin = indexMin; + mStreams[stream].mIndexMax = indexMax; +} + +status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream, + int index, + audio_devices_t device) +{ + + if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) { + return BAD_VALUE; + } + if (!audio_is_output_device(device)) { + return BAD_VALUE; + } + + // Force max volume if stream cannot be muted + if (!mStreams[stream].mCanBeMuted) index = mStreams[stream].mIndexMax; + + ALOGV("setStreamVolumeIndex() stream %d, device %04x, index %d", + stream, device, index); + + // if device is AUDIO_DEVICE_OUT_DEFAULT set default value and + // clear all device specific values + if (device == AUDIO_DEVICE_OUT_DEFAULT) { + mStreams[stream].mIndexCur.clear(); + } + mStreams[stream].mIndexCur.add(device, index); + + // compute and apply stream volume on all outputs according to connected device + status_t status = NO_ERROR; + for (size_t i = 0; i < mOutputs.size(); i++) { + audio_devices_t curDevice = + getDeviceForVolume(mOutputs.valueAt(i)->device()); + if ((device == AUDIO_DEVICE_OUT_DEFAULT) || (device == curDevice)) { + status_t volStatus = checkAndSetVolume(stream, index, mOutputs.keyAt(i), curDevice); + if (volStatus != NO_ERROR) { + status = volStatus; + } + } + } + return status; +} + +status_t AudioPolicyManager::getStreamVolumeIndex(audio_stream_type_t stream, + int *index, + audio_devices_t device) +{ + if (index == NULL) { + return BAD_VALUE; + } + if (!audio_is_output_device(device)) { + return BAD_VALUE; + } + // if device is AUDIO_DEVICE_OUT_DEFAULT, return volume for device corresponding to + // the strategy the stream belongs to. + if (device == AUDIO_DEVICE_OUT_DEFAULT) { + device = getDeviceForStrategy(getStrategy(stream), true /*fromCache*/); + } + device = getDeviceForVolume(device); + + *index = mStreams[stream].getVolumeIndex(device); + ALOGV("getStreamVolumeIndex() stream %d device %08x index %d", stream, device, *index); + return NO_ERROR; +} + +audio_io_handle_t AudioPolicyManager::selectOutputForEffects( + const SortedVector<audio_io_handle_t>& outputs) +{ + // select one output among several suitable for global effects. + // The priority is as follows: + // 1: An offloaded output. If the effect ends up not being offloadable, + // AudioFlinger will invalidate the track and the offloaded output + // will be closed causing the effect to be moved to a PCM output. + // 2: A deep buffer output + // 3: the first output in the list + + if (outputs.size() == 0) { + return 0; + } + + audio_io_handle_t outputOffloaded = 0; + audio_io_handle_t outputDeepBuffer = 0; + + for (size_t i = 0; i < outputs.size(); i++) { + sp<AudioOutputDescriptor> desc = mOutputs.valueFor(outputs[i]); + ALOGV("selectOutputForEffects outputs[%zu] flags %x", i, desc->mFlags); + if ((desc->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) { + outputOffloaded = outputs[i]; + } + if ((desc->mFlags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) != 0) { + outputDeepBuffer = outputs[i]; + } + } + + ALOGV("selectOutputForEffects outputOffloaded %d outputDeepBuffer %d", + outputOffloaded, outputDeepBuffer); + if (outputOffloaded != 0) { + return outputOffloaded; + } + if (outputDeepBuffer != 0) { + return outputDeepBuffer; + } + + return outputs[0]; +} + +audio_io_handle_t AudioPolicyManager::getOutputForEffect(const effect_descriptor_t *desc) +{ + // apply simple rule where global effects are attached to the same output as MUSIC streams + + routing_strategy strategy = getStrategy(AUDIO_STREAM_MUSIC); + audio_devices_t device = getDeviceForStrategy(strategy, false /*fromCache*/); + SortedVector<audio_io_handle_t> dstOutputs = getOutputsForDevice(device, mOutputs); + + audio_io_handle_t output = selectOutputForEffects(dstOutputs); + ALOGV("getOutputForEffect() got output %d for fx %s flags %x", + output, (desc == NULL) ? "unspecified" : desc->name, (desc == NULL) ? 0 : desc->flags); + + return output; +} + +status_t AudioPolicyManager::registerEffect(const effect_descriptor_t *desc, + audio_io_handle_t io, + uint32_t strategy, + int session, + int id) +{ + ssize_t index = mOutputs.indexOfKey(io); + if (index < 0) { + index = mInputs.indexOfKey(io); + if (index < 0) { + ALOGW("registerEffect() unknown io %d", io); + return INVALID_OPERATION; + } + } + + if (mTotalEffectsMemory + desc->memoryUsage > getMaxEffectsMemory()) { + ALOGW("registerEffect() memory limit exceeded for Fx %s, Memory %d KB", + desc->name, desc->memoryUsage); + return INVALID_OPERATION; + } + mTotalEffectsMemory += desc->memoryUsage; + ALOGV("registerEffect() effect %s, io %d, strategy %d session %d id %d", + desc->name, io, strategy, session, id); + ALOGV("registerEffect() memory %d, total memory %d", desc->memoryUsage, mTotalEffectsMemory); + + sp<EffectDescriptor> effectDesc = new EffectDescriptor(); + memcpy (&effectDesc->mDesc, desc, sizeof(effect_descriptor_t)); + effectDesc->mIo = io; + effectDesc->mStrategy = (routing_strategy)strategy; + effectDesc->mSession = session; + effectDesc->mEnabled = false; + + mEffects.add(id, effectDesc); + + return NO_ERROR; +} + +status_t AudioPolicyManager::unregisterEffect(int id) +{ + ssize_t index = mEffects.indexOfKey(id); + if (index < 0) { + ALOGW("unregisterEffect() unknown effect ID %d", id); + return INVALID_OPERATION; + } + + sp<EffectDescriptor> effectDesc = mEffects.valueAt(index); + + setEffectEnabled(effectDesc, false); + + if (mTotalEffectsMemory < effectDesc->mDesc.memoryUsage) { + ALOGW("unregisterEffect() memory %d too big for total %d", + effectDesc->mDesc.memoryUsage, mTotalEffectsMemory); + effectDesc->mDesc.memoryUsage = mTotalEffectsMemory; + } + mTotalEffectsMemory -= effectDesc->mDesc.memoryUsage; + ALOGV("unregisterEffect() effect %s, ID %d, memory %d total memory %d", + effectDesc->mDesc.name, id, effectDesc->mDesc.memoryUsage, mTotalEffectsMemory); + + mEffects.removeItem(id); + + return NO_ERROR; +} + +status_t AudioPolicyManager::setEffectEnabled(int id, bool enabled) +{ + ssize_t index = mEffects.indexOfKey(id); + if (index < 0) { + ALOGW("unregisterEffect() unknown effect ID %d", id); + return INVALID_OPERATION; + } + + return setEffectEnabled(mEffects.valueAt(index), enabled); +} + +status_t AudioPolicyManager::setEffectEnabled(const sp<EffectDescriptor>& effectDesc, bool enabled) +{ + if (enabled == effectDesc->mEnabled) { + ALOGV("setEffectEnabled(%s) effect already %s", + enabled?"true":"false", enabled?"enabled":"disabled"); + return INVALID_OPERATION; + } + + if (enabled) { + if (mTotalEffectsCpuLoad + effectDesc->mDesc.cpuLoad > getMaxEffectsCpuLoad()) { + ALOGW("setEffectEnabled(true) CPU Load limit exceeded for Fx %s, CPU %f MIPS", + effectDesc->mDesc.name, (float)effectDesc->mDesc.cpuLoad/10); + return INVALID_OPERATION; + } + mTotalEffectsCpuLoad += effectDesc->mDesc.cpuLoad; + ALOGV("setEffectEnabled(true) total CPU %d", mTotalEffectsCpuLoad); + } else { + if (mTotalEffectsCpuLoad < effectDesc->mDesc.cpuLoad) { + ALOGW("setEffectEnabled(false) CPU load %d too high for total %d", + effectDesc->mDesc.cpuLoad, mTotalEffectsCpuLoad); + effectDesc->mDesc.cpuLoad = mTotalEffectsCpuLoad; + } + mTotalEffectsCpuLoad -= effectDesc->mDesc.cpuLoad; + ALOGV("setEffectEnabled(false) total CPU %d", mTotalEffectsCpuLoad); + } + effectDesc->mEnabled = enabled; + return NO_ERROR; +} + +bool AudioPolicyManager::isNonOffloadableEffectEnabled() +{ + for (size_t i = 0; i < mEffects.size(); i++) { + sp<EffectDescriptor> effectDesc = mEffects.valueAt(i); + if (effectDesc->mEnabled && (effectDesc->mStrategy == STRATEGY_MEDIA) && + ((effectDesc->mDesc.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) == 0)) { + ALOGV("isNonOffloadableEffectEnabled() non offloadable effect %s enabled on session %d", + effectDesc->mDesc.name, effectDesc->mSession); + return true; + } + } + return false; +} + +bool AudioPolicyManager::isStreamActive(audio_stream_type_t stream, uint32_t inPastMs) const +{ + nsecs_t sysTime = systemTime(); + for (size_t i = 0; i < mOutputs.size(); i++) { + const sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(i); + if (outputDesc->isStreamActive(stream, inPastMs, sysTime)) { + return true; + } + } + return false; +} + +bool AudioPolicyManager::isStreamActiveRemotely(audio_stream_type_t stream, + uint32_t inPastMs) const +{ + nsecs_t sysTime = systemTime(); + for (size_t i = 0; i < mOutputs.size(); i++) { + const sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(i); + if (((outputDesc->device() & APM_AUDIO_OUT_DEVICE_REMOTE_ALL) != 0) && + outputDesc->isStreamActive(stream, inPastMs, sysTime)) { + return true; + } + } + return false; +} + +bool AudioPolicyManager::isSourceActive(audio_source_t source) const +{ + for (size_t i = 0; i < mInputs.size(); i++) { + const sp<AudioInputDescriptor> inputDescriptor = mInputs.valueAt(i); + if ((inputDescriptor->mInputSource == (int)source || + (source == AUDIO_SOURCE_VOICE_RECOGNITION && + inputDescriptor->mInputSource == AUDIO_SOURCE_HOTWORD)) + && (inputDescriptor->mRefCount > 0)) { + return true; + } + } + return false; +} + + +status_t AudioPolicyManager::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "\nAudioPolicyManager Dump: %p\n", this); + result.append(buffer); + + snprintf(buffer, SIZE, " Primary Output: %d\n", mPrimaryOutput); + result.append(buffer); + snprintf(buffer, SIZE, " Phone state: %d\n", mPhoneState); + result.append(buffer); + snprintf(buffer, SIZE, " Force use for communications %d\n", + mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]); + result.append(buffer); + snprintf(buffer, SIZE, " Force use for media %d\n", mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA]); + result.append(buffer); + snprintf(buffer, SIZE, " Force use for record %d\n", mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD]); + result.append(buffer); + snprintf(buffer, SIZE, " Force use for dock %d\n", mForceUse[AUDIO_POLICY_FORCE_FOR_DOCK]); + result.append(buffer); + snprintf(buffer, SIZE, " Force use for system %d\n", mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM]); + result.append(buffer); + snprintf(buffer, SIZE, " Force use for hdmi system audio %d\n", + mForceUse[AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO]); + result.append(buffer); + + snprintf(buffer, SIZE, " Available output devices:\n"); + result.append(buffer); + write(fd, result.string(), result.size()); + for (size_t i = 0; i < mAvailableOutputDevices.size(); i++) { + mAvailableOutputDevices[i]->dump(fd, 2, i); + } + snprintf(buffer, SIZE, "\n Available input devices:\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < mAvailableInputDevices.size(); i++) { + mAvailableInputDevices[i]->dump(fd, 2, i); + } + + snprintf(buffer, SIZE, "\nHW Modules dump:\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < mHwModules.size(); i++) { + snprintf(buffer, SIZE, "- HW Module %zu:\n", i + 1); + write(fd, buffer, strlen(buffer)); + mHwModules[i]->dump(fd); + } + + snprintf(buffer, SIZE, "\nOutputs dump:\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < mOutputs.size(); i++) { + snprintf(buffer, SIZE, "- Output %d dump:\n", mOutputs.keyAt(i)); + write(fd, buffer, strlen(buffer)); + mOutputs.valueAt(i)->dump(fd); + } + + snprintf(buffer, SIZE, "\nInputs dump:\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < mInputs.size(); i++) { + snprintf(buffer, SIZE, "- Input %d dump:\n", mInputs.keyAt(i)); + write(fd, buffer, strlen(buffer)); + mInputs.valueAt(i)->dump(fd); + } + + snprintf(buffer, SIZE, "\nStreams dump:\n"); + write(fd, buffer, strlen(buffer)); + snprintf(buffer, SIZE, + " Stream Can be muted Index Min Index Max Index Cur [device : index]...\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < AUDIO_STREAM_CNT; i++) { + snprintf(buffer, SIZE, " %02zu ", i); + write(fd, buffer, strlen(buffer)); + mStreams[i].dump(fd); + } + + snprintf(buffer, SIZE, "\nTotal Effects CPU: %f MIPS, Total Effects memory: %d KB\n", + (float)mTotalEffectsCpuLoad/10, mTotalEffectsMemory); + write(fd, buffer, strlen(buffer)); + + snprintf(buffer, SIZE, "Registered effects:\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < mEffects.size(); i++) { + snprintf(buffer, SIZE, "- Effect %d dump:\n", mEffects.keyAt(i)); + write(fd, buffer, strlen(buffer)); + mEffects.valueAt(i)->dump(fd); + } + + snprintf(buffer, SIZE, "\nAudio Patches:\n"); + write(fd, buffer, strlen(buffer)); + for (size_t i = 0; i < mAudioPatches.size(); i++) { + mAudioPatches[i]->dump(fd, 2, i); + } + + return NO_ERROR; +} + +// This function checks for the parameters which can be offloaded. +// This can be enhanced depending on the capability of the DSP and policy +// of the system. +bool AudioPolicyManager::isOffloadSupported(const audio_offload_info_t& offloadInfo) +{ + ALOGV("isOffloadSupported: SR=%u, CM=0x%x, Format=0x%x, StreamType=%d," + " BitRate=%u, duration=%" PRId64 " us, has_video=%d", + offloadInfo.sample_rate, offloadInfo.channel_mask, + offloadInfo.format, + offloadInfo.stream_type, offloadInfo.bit_rate, offloadInfo.duration_us, + offloadInfo.has_video); + + // Check if offload has been disabled + char propValue[PROPERTY_VALUE_MAX]; + if (property_get("audio.offload.disable", propValue, "0")) { + if (atoi(propValue) != 0) { + ALOGV("offload disabled by audio.offload.disable=%s", propValue ); + return false; + } + } + + // Check if stream type is music, then only allow offload as of now. + if (offloadInfo.stream_type != AUDIO_STREAM_MUSIC) + { + ALOGV("isOffloadSupported: stream_type != MUSIC, returning false"); + return false; + } + + //TODO: enable audio offloading with video when ready + if (offloadInfo.has_video) + { + ALOGV("isOffloadSupported: has_video == true, returning false"); + return false; + } + + //If duration is less than minimum value defined in property, return false + if (property_get("audio.offload.min.duration.secs", propValue, NULL)) { + if (offloadInfo.duration_us < (atoi(propValue) * 1000000 )) { + ALOGV("Offload denied by duration < audio.offload.min.duration.secs(=%s)", propValue); + return false; + } + } else if (offloadInfo.duration_us < OFFLOAD_DEFAULT_MIN_DURATION_SECS * 1000000) { + ALOGV("Offload denied by duration < default min(=%u)", OFFLOAD_DEFAULT_MIN_DURATION_SECS); + return false; + } + + // Do not allow offloading if one non offloadable effect is enabled. This prevents from + // creating an offloaded track and tearing it down immediately after start when audioflinger + // detects there is an active non offloadable effect. + // FIXME: We should check the audio session here but we do not have it in this context. + // This may prevent offloading in rare situations where effects are left active by apps + // in the background. + if (isNonOffloadableEffectEnabled()) { + return false; + } + + // See if there is a profile to support this. + // AUDIO_DEVICE_NONE + sp<IOProfile> profile = getProfileForDirectOutput(AUDIO_DEVICE_NONE /*ignore device */, + offloadInfo.sample_rate, + offloadInfo.format, + offloadInfo.channel_mask, + AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD); + ALOGV("isOffloadSupported() profile %sfound", profile != 0 ? "" : "NOT "); + 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) { + size_t numOutputs = 0; + for (size_t i = 0; i < mOutputs.size(); i++) { + if (!mOutputs[i]->isDuplicated()) { + numOutputs++; + if (portsWritten < portsMax) { + mOutputs[i]->toAudioPort(&ports[portsWritten++]); + } + } + } + *num_ports += numOutputs; + } + } + *generation = curAudioPortGeneration(); + ALOGV("listAudioPorts() got %zu ports needed %d", portsWritten, *num_ports); + return NO_ERROR; +} + +status_t AudioPolicyManager::getAudioPort(struct audio_port *port __unused) +{ + return NO_ERROR; +} + +sp<AudioPolicyManager::AudioOutputDescriptor> AudioPolicyManager::getOutputFromId( + audio_port_handle_t id) const +{ + sp<AudioOutputDescriptor> outputDesc = NULL; + for (size_t i = 0; i < mOutputs.size(); i++) { + outputDesc = mOutputs.valueAt(i); + if (outputDesc->mId == id) { + break; + } + } + return outputDesc; +} + +sp<AudioPolicyManager::AudioInputDescriptor> AudioPolicyManager::getInputFromId( + audio_port_handle_t id) const +{ + sp<AudioInputDescriptor> inputDesc = NULL; + for (size_t i = 0; i < mInputs.size(); i++) { + inputDesc = mInputs.valueAt(i); + if (inputDesc->mId == id) { + break; + } + } + return inputDesc; +} + +sp <AudioPolicyManager::HwModule> AudioPolicyManager::getModuleForDevice( + audio_devices_t device) const +{ + sp <HwModule> module; + + 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 module; +} + +sp <AudioPolicyManager::HwModule> AudioPolicyManager::getModuleFromName(const char *name) const +{ + sp <HwModule> module; + + for (size_t i = 0; i < mHwModules.size(); i++) + { + if (strcmp(mHwModules[i]->mName, name) == 0) { + return mHwModules[i]; + } + } + return module; +} + +audio_devices_t AudioPolicyManager::availablePrimaryOutputDevices() +{ + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(mPrimaryOutput); + audio_devices_t devices = outputDesc->mProfile->mSupportedDevices.types(); + return devices & mAvailableOutputDevices.types(); +} + +audio_devices_t AudioPolicyManager::availablePrimaryInputDevices() +{ + audio_module_handle_t primaryHandle = + mOutputs.valueFor(mPrimaryOutput)->mProfile->mModule->mHandle; + audio_devices_t devices = AUDIO_DEVICE_NONE; + for (size_t i = 0; i < mAvailableInputDevices.size(); i++) { + if (mAvailableInputDevices[i]->mModule->mHandle == primaryHandle) { + devices |= mAvailableInputDevices[i]->mDeviceType; + } + } + return devices; +} + +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 == 0 || patch->num_sources > AUDIO_PATCH_PORTS_MAX || + patch->num_sinks == 0 || patch->num_sinks > AUDIO_PATCH_PORTS_MAX) { + return BAD_VALUE; + } + // only one source per audio patch supported for now + if (patch->num_sources > 1) { + return INVALID_OPERATION; + } + + if (patch->sources[0].role != AUDIO_PORT_ROLE_SOURCE) { + return INVALID_OPERATION; + } + for (size_t i = 0; i < patch->num_sinks; i++) { + if (patch->sinks[i].role != AUDIO_PORT_ROLE_SINK) { + return INVALID_OPERATION; + } + } + + sp<AudioPatch> patchDesc; + ssize_t index = mAudioPatches.indexOfKey(*handle); + + ALOGV("createAudioPatch source id %d role %d type %d", patch->sources[0].id, + patch->sources[0].role, + patch->sources[0].type); +#if LOG_NDEBUG == 0 + for (size_t i = 0; i < patch->num_sinks; i++) { + ALOGV("createAudioPatch sink %d: id %d role %d type %d", i, patch->sinks[i].id, + patch->sinks[i].role, + patch->sinks[i].type); + } +#endif + + 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) { + sp<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; + } + ALOG_ASSERT(!outputDesc->isDuplicated(),"duplicated output %d in source in ports", + outputDesc->mIoHandle); + 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; + } + } + DeviceVector devices; + for (size_t i = 0; i < patch->num_sinks; i++) { + // Only support mix to devices connection + // TODO add support for mix to mix connection + if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) { + ALOGV("createAudioPatch() source mix but sink is not a device"); + return INVALID_OPERATION; + } + sp<DeviceDescriptor> devDesc = + mAvailableOutputDevices.getDeviceFromId(patch->sinks[i].id); + if (devDesc == 0) { + ALOGV("createAudioPatch() out device not found for id %d", patch->sinks[i].id); + return BAD_VALUE; + } + + if (!outputDesc->mProfile->isCompatibleProfile(devDesc->mDeviceType, + patch->sources[0].sample_rate, + NULL, // updatedSamplingRate + patch->sources[0].format, + patch->sources[0].channel_mask, + AUDIO_OUTPUT_FLAG_NONE /*FIXME*/)) { + ALOGV("createAudioPatch() profile not supported for device %08x", + devDesc->mDeviceType); + return INVALID_OPERATION; + } + devices.add(devDesc); + } + if (devices.size() == 0) { + return INVALID_OPERATION; + } + + // TODO: reconfigure output format and channels here + ALOGV("createAudioPatch() setting device %08x on output %d", + devices.types(), outputDesc->mIoHandle); + setOutputDevice(outputDesc->mIoHandle, devices.types(), 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 + // only one sink supported when connecting an input device to a mix + if (patch->num_sinks > 1) { + return INVALID_OPERATION; + } + sp<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->mDeviceType, + patch->sinks[0].sample_rate, + NULL, /*updatedSampleRate*/ + patch->sinks[0].format, + patch->sinks[0].channel_mask, + // FIXME for the parameter type, + // and the NONE + (audio_output_flags_t) + AUDIO_INPUT_FLAG_NONE)) { + return INVALID_OPERATION; + } + // TODO: reconfigure output format and channels here + ALOGV("createAudioPatch() setting device %08x on output %d", + devDesc->mDeviceType, inputDesc->mIoHandle); + setInputDevice(inputDesc->mIoHandle, devDesc->mDeviceType, 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) { + return BAD_VALUE; + } + } + sp<DeviceDescriptor> srcDeviceDesc = + mAvailableInputDevices.getDeviceFromId(patch->sources[0].id); + + //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]); + if (srcDeviceDesc == 0) { + return BAD_VALUE; + } + + for (size_t i = 0; i < patch->num_sinks; i++) { + if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) { + ALOGV("createAudioPatch() source device but one sink is not a device"); + return INVALID_OPERATION; + } + + sp<DeviceDescriptor> sinkDeviceDesc = + mAvailableOutputDevices.getDeviceFromId(patch->sinks[i].id); + if (sinkDeviceDesc == 0) { + return BAD_VALUE; + } + sinkDeviceDesc->toAudioPortConfig(&newPatch.sinks[i], &patch->sinks[i]); + + if (srcDeviceDesc->mModule != sinkDeviceDesc->mModule) { + // only one sink supported when connected devices across HW modules + if (patch->num_sinks > 1) { + return INVALID_OPERATION; + } + SortedVector<audio_io_handle_t> outputs = + getOutputsForDevice(sinkDeviceDesc->mDeviceType, + mOutputs); + // if the sink device is reachable via an opened output stream, request to go via + // this output stream by adding a second source to the patch description + audio_io_handle_t output = selectOutput(outputs, + AUDIO_OUTPUT_FLAG_NONE, + AUDIO_FORMAT_INVALID); + if (output != AUDIO_IO_HANDLE_NONE) { + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output); + if (outputDesc->isDuplicated()) { + return INVALID_OPERATION; + } + outputDesc->toAudioPortConfig(&newPatch.sources[1], &patch->sources[0]); + newPatch.num_sources = 2; + } + } + } + // 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) { + sp<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) { + sp<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 %zu", + *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 %zu 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 %zu 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) { + return INVALID_OPERATION; + } + + sp<AudioPortConfig> audioPortConfig; + if (config->type == AUDIO_PORT_TYPE_MIX) { + if (config->role == AUDIO_PORT_ROLE_SOURCE) { + sp<AudioOutputDescriptor> outputDesc = getOutputFromId(config->id); + if (outputDesc == NULL) { + return BAD_VALUE; + } + ALOG_ASSERT(!outputDesc->isDuplicated(), + "setAudioPortConfig() called on duplicated output %d", + outputDesc->mIoHandle); + audioPortConfig = outputDesc; + } else if (config->role == AUDIO_PORT_ROLE_SINK) { + sp<AudioInputDescriptor> inputDesc = getInputFromId(config->id); + if (inputDesc == NULL) { + return BAD_VALUE; + } + audioPortConfig = inputDesc; + } 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; + } + audioPortConfig = deviceDesc; + } else { + return BAD_VALUE; + } + + struct audio_port_config backupConfig; + status_t status = audioPortConfig->applyAudioPortConfig(config, &backupConfig); + if (status == NO_ERROR) { + struct audio_port_config newConfig; + audioPortConfig->toAudioPortConfig(&newConfig, config); + status = mpClientInterface->setAudioPortConfig(&newConfig, 0); + } + if (status != NO_ERROR) { + audioPortConfig->applyAudioPortConfig(&backupConfig); + } + + 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::acquireSoundTriggerSession(audio_session_t *session, + audio_io_handle_t *ioHandle, + audio_devices_t *device) +{ + *session = (audio_session_t)mpClientInterface->newAudioUniqueId(); + *ioHandle = (audio_io_handle_t)mpClientInterface->newAudioUniqueId(); + *device = getDeviceForInputSource(AUDIO_SOURCE_HOTWORD); + + mSoundTriggerSessions.add(*session, *ioHandle); + + return NO_ERROR; +} + +status_t AudioPolicyManager::releaseSoundTriggerSession(audio_session_t session) +{ + ssize_t index = mSoundTriggerSessions.indexOfKey(session); + if (index < 0) { + ALOGW("acquireSoundTriggerSession() session %d not registered", session); + return BAD_VALUE; + } + + mSoundTriggerSessions.removeItem(session); + return NO_ERROR; +} + +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 +// ---------------------------------------------------------------------------- + +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 + Thread(false), +#endif //AUDIO_POLICY_TEST + mPrimaryOutput((audio_io_handle_t)0), + mPhoneState(AUDIO_MODE_NORMAL), + mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f), + mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0), + mA2dpSuspended(false), + 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(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"); + defaultAudioPolicyConfig(); + } + } + // mAvailableOutputDevices and mAvailableInputDevices now contain all attached devices + + // must be done after reading the policy + initializeVolumeCurves(); + + // open all output streams needed to access attached devices + audio_devices_t outputDeviceTypes = mAvailableOutputDevices.types(); + audio_devices_t inputDeviceTypes = mAvailableInputDevices.types() & ~AUDIO_DEVICE_BIT_IN; + for (size_t i = 0; i < mHwModules.size(); i++) { + mHwModules[i]->mHandle = mpClientInterface->loadHwModule(mHwModules[i]->mName); + if (mHwModules[i]->mHandle == 0) { + ALOGW("could not open HW module %s", mHwModules[i]->mName); + continue; + } + // open all output streams needed to access attached devices + // except for direct output streams that are only opened when they are actually + // required by an app. + // This also validates mAvailableOutputDevices list + for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) + { + const sp<IOProfile> outProfile = mHwModules[i]->mOutputProfiles[j]; + + if (outProfile->mSupportedDevices.isEmpty()) { + ALOGW("Output profile contains no device on module %s", mHwModules[i]->mName); + continue; + } + + audio_devices_t profileType = outProfile->mSupportedDevices.types(); + if ((profileType & mDefaultOutputDevice->mDeviceType) != AUDIO_DEVICE_NONE) { + profileType = mDefaultOutputDevice->mDeviceType; + } else { + profileType = outProfile->mSupportedDevices[0]->mDeviceType; + } + if ((profileType & outputDeviceTypes) && + ((outProfile->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0)) { + sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(outProfile); + + outputDesc->mDevice = profileType; + audio_config_t config = AUDIO_CONFIG_INITIALIZER; + config.sample_rate = outputDesc->mSamplingRate; + config.channel_mask = outputDesc->mChannelMask; + config.format = outputDesc->mFormat; + audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; + status_t status = mpClientInterface->openOutput(outProfile->mModule->mHandle, + &output, + &config, + &outputDesc->mDevice, + String8(""), + &outputDesc->mLatency, + outputDesc->mFlags); + + if (status != NO_ERROR) { + ALOGW("Cannot open output stream for device %08x on hw module %s", + outputDesc->mDevice, + mHwModules[i]->mName); + } else { + outputDesc->mSamplingRate = config.sample_rate; + outputDesc->mChannelMask = config.channel_mask; + outputDesc->mFormat = config.format; + + for (size_t k = 0; k < outProfile->mSupportedDevices.size(); k++) { + audio_devices_t type = outProfile->mSupportedDevices[k]->mDeviceType; + ssize_t index = + mAvailableOutputDevices.indexOf(outProfile->mSupportedDevices[k]); + // 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 && + outProfile->mFlags & AUDIO_OUTPUT_FLAG_PRIMARY) { + mPrimaryOutput = output; + } + addOutput(output, outputDesc); + setOutputDevice(output, + outputDesc->mDevice, + true); + } + } + } + // open input streams needed to access attached devices to validate + // mAvailableInputDevices list + for (size_t j = 0; j < mHwModules[i]->mInputProfiles.size(); j++) + { + const sp<IOProfile> inProfile = mHwModules[i]->mInputProfiles[j]; + + if (inProfile->mSupportedDevices.isEmpty()) { + ALOGW("Input profile contains no device on module %s", mHwModules[i]->mName); + continue; + } + + audio_devices_t profileType = inProfile->mSupportedDevices[0]->mDeviceType; + if (profileType & inputDeviceTypes) { + sp<AudioInputDescriptor> inputDesc = new AudioInputDescriptor(inProfile); + + inputDesc->mInputSource = AUDIO_SOURCE_MIC; + inputDesc->mDevice = profileType; + + audio_config_t config = AUDIO_CONFIG_INITIALIZER; + config.sample_rate = inputDesc->mSamplingRate; + config.channel_mask = inputDesc->mChannelMask; + config.format = inputDesc->mFormat; + audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; + status_t status = mpClientInterface->openInput(inProfile->mModule->mHandle, + &input, + &config, + &inputDesc->mDevice, + String8(""), + AUDIO_SOURCE_MIC, + AUDIO_INPUT_FLAG_NONE); + + if (status == NO_ERROR) { + for (size_t k = 0; k < inProfile->mSupportedDevices.size(); k++) { + audio_devices_t type = inProfile->mSupportedDevices[k]->mDeviceType; + ssize_t index = + mAvailableInputDevices.indexOf(inProfile->mSupportedDevices[k]); + // 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); + } else { + ALOGW("Cannot open input stream for device %08x on hw module %s", + inputDesc->mDevice, + mHwModules[i]->mName); + } + } + } + } + // make sure all attached devices have been allocated a unique ID + for (size_t i = 0; i < mAvailableOutputDevices.size();) { + if (mAvailableOutputDevices[i]->mId == 0) { + ALOGW("Input device %08x unreachable", mAvailableOutputDevices[i]->mDeviceType); + mAvailableOutputDevices.remove(mAvailableOutputDevices[i]); + continue; + } + i++; + } + for (size_t i = 0; i < mAvailableInputDevices.size();) { + if (mAvailableInputDevices[i]->mId == 0) { + ALOGW("Input device %08x unreachable", mAvailableInputDevices[i]->mDeviceType); + mAvailableInputDevices.remove(mAvailableInputDevices[i]); + continue; + } + i++; + } + // make sure default device is reachable + if (mAvailableOutputDevices.indexOf(mDefaultOutputDevice) < 0) { + ALOGE("Default device %08x is unreachable", mDefaultOutputDevice->mDeviceType); + } + + ALOGE_IF((mPrimaryOutput == 0), "Failed to open primary output"); + + updateDevicesAndOutputs(); + +#ifdef AUDIO_POLICY_TEST + if (mPrimaryOutput != 0) { + AudioParameter outputCmd = AudioParameter(); + outputCmd.addInt(String8("set_id"), 0); + mpClientInterface->setParameters(mPrimaryOutput, outputCmd.toString()); + + mTestDevice = AUDIO_DEVICE_OUT_SPEAKER; + mTestSamplingRate = 44100; + mTestFormat = AUDIO_FORMAT_PCM_16_BIT; + mTestChannels = AUDIO_CHANNEL_OUT_STEREO; + mTestLatencyMs = 0; + mCurOutput = 0; + mDirectOutput = false; + for (int i = 0; i < NUM_TEST_OUTPUTS; i++) { + mTestOutputs[i] = 0; + } + + const size_t SIZE = 256; + char buffer[SIZE]; + snprintf(buffer, SIZE, "AudioPolicyManagerTest"); + run(buffer, ANDROID_PRIORITY_AUDIO); + } +#endif //AUDIO_POLICY_TEST +} + +AudioPolicyManager::~AudioPolicyManager() +{ +#ifdef AUDIO_POLICY_TEST + exit(); +#endif //AUDIO_POLICY_TEST + for (size_t i = 0; i < mOutputs.size(); i++) { + mpClientInterface->closeOutput(mOutputs.keyAt(i)); + } + for (size_t i = 0; i < mInputs.size(); i++) { + mpClientInterface->closeInput(mInputs.keyAt(i)); + } + mAvailableOutputDevices.clear(); + mAvailableInputDevices.clear(); + mOutputs.clear(); + mInputs.clear(); + mHwModules.clear(); +} + +status_t AudioPolicyManager::initCheck() +{ + return (mPrimaryOutput == 0) ? NO_INIT : NO_ERROR; +} + +#ifdef AUDIO_POLICY_TEST +bool AudioPolicyManager::threadLoop() +{ + ALOGV("entering threadLoop()"); + while (!exitPending()) + { + String8 command; + int valueInt; + String8 value; + + Mutex::Autolock _l(mLock); + mWaitWorkCV.waitRelative(mLock, milliseconds(50)); + + command = mpClientInterface->getParameters(0, String8("test_cmd_policy")); + AudioParameter param = AudioParameter(command); + + if (param.getInt(String8("test_cmd_policy"), valueInt) == NO_ERROR && + valueInt != 0) { + ALOGV("Test command %s received", command.string()); + String8 target; + if (param.get(String8("target"), target) != NO_ERROR) { + target = "Manager"; + } + if (param.getInt(String8("test_cmd_policy_output"), valueInt) == NO_ERROR) { + param.remove(String8("test_cmd_policy_output")); + mCurOutput = valueInt; + } + if (param.get(String8("test_cmd_policy_direct"), value) == NO_ERROR) { + param.remove(String8("test_cmd_policy_direct")); + if (value == "false") { + mDirectOutput = false; + } else if (value == "true") { + mDirectOutput = true; + } + } + if (param.getInt(String8("test_cmd_policy_input"), valueInt) == NO_ERROR) { + param.remove(String8("test_cmd_policy_input")); + mTestInput = valueInt; + } + + if (param.get(String8("test_cmd_policy_format"), value) == NO_ERROR) { + param.remove(String8("test_cmd_policy_format")); + int format = AUDIO_FORMAT_INVALID; + if (value == "PCM 16 bits") { + format = AUDIO_FORMAT_PCM_16_BIT; + } else if (value == "PCM 8 bits") { + format = AUDIO_FORMAT_PCM_8_BIT; + } else if (value == "Compressed MP3") { + format = AUDIO_FORMAT_MP3; + } + if (format != AUDIO_FORMAT_INVALID) { + if (target == "Manager") { + mTestFormat = format; + } else if (mTestOutputs[mCurOutput] != 0) { + AudioParameter outputParam = AudioParameter(); + outputParam.addInt(String8("format"), format); + mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString()); + } + } + } + if (param.get(String8("test_cmd_policy_channels"), value) == NO_ERROR) { + param.remove(String8("test_cmd_policy_channels")); + int channels = 0; + + if (value == "Channels Stereo") { + channels = AUDIO_CHANNEL_OUT_STEREO; + } else if (value == "Channels Mono") { + channels = AUDIO_CHANNEL_OUT_MONO; + } + if (channels != 0) { + if (target == "Manager") { + mTestChannels = channels; + } else if (mTestOutputs[mCurOutput] != 0) { + AudioParameter outputParam = AudioParameter(); + outputParam.addInt(String8("channels"), channels); + mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString()); + } + } + } + if (param.getInt(String8("test_cmd_policy_sampleRate"), valueInt) == NO_ERROR) { + param.remove(String8("test_cmd_policy_sampleRate")); + if (valueInt >= 0 && valueInt <= 96000) { + int samplingRate = valueInt; + if (target == "Manager") { + mTestSamplingRate = samplingRate; + } else if (mTestOutputs[mCurOutput] != 0) { + AudioParameter outputParam = AudioParameter(); + outputParam.addInt(String8("sampling_rate"), samplingRate); + mpClientInterface->setParameters(mTestOutputs[mCurOutput], outputParam.toString()); + } + } + } + + if (param.get(String8("test_cmd_policy_reopen"), value) == NO_ERROR) { + param.remove(String8("test_cmd_policy_reopen")); + + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(mPrimaryOutput); + mpClientInterface->closeOutput(mPrimaryOutput); + + audio_module_handle_t moduleHandle = outputDesc->mModule->mHandle; + + mOutputs.removeItem(mPrimaryOutput); + + sp<AudioOutputDescriptor> outputDesc = new AudioOutputDescriptor(NULL); + outputDesc->mDevice = AUDIO_DEVICE_OUT_SPEAKER; + audio_config_t config = AUDIO_CONFIG_INITIALIZER; + config.sample_rate = outputDesc->mSamplingRate; + config.channel_mask = outputDesc->mChannelMask; + config.format = outputDesc->mFormat; + status_t status = mpClientInterface->openOutput(moduleHandle, + &mPrimaryOutput, + &config, + &outputDesc->mDevice, + String8(""), + &outputDesc->mLatency, + outputDesc->mFlags); + if (status != NO_ERROR) { + ALOGE("Failed to reopen hardware output stream, " + "samplingRate: %d, format %d, channels %d", + outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannelMask); + } else { + outputDesc->mSamplingRate = config.sample_rate; + outputDesc->mChannelMask = config.channel_mask; + outputDesc->mFormat = config.format; + AudioParameter outputCmd = AudioParameter(); + outputCmd.addInt(String8("set_id"), 0); + mpClientInterface->setParameters(mPrimaryOutput, outputCmd.toString()); + addOutput(mPrimaryOutput, outputDesc); + } + } + + + mpClientInterface->setParameters(0, String8("test_cmd_policy=")); + } + } + return false; +} + +void AudioPolicyManager::exit() +{ + { + AutoMutex _l(mLock); + requestExit(); + mWaitWorkCV.signal(); + } + requestExitAndWait(); +} + +int AudioPolicyManager::testOutputIndex(audio_io_handle_t output) +{ + for (int i = 0; i < NUM_TEST_OUTPUTS; i++) { + if (output == mTestOutputs[i]) return i; + } + return 0; +} +#endif //AUDIO_POLICY_TEST + +// --- + +void AudioPolicyManager::addOutput(audio_io_handle_t output, sp<AudioOutputDescriptor> outputDesc) +{ + outputDesc->mIoHandle = output; + outputDesc->mId = nextUniqueId(); + mOutputs.add(output, outputDesc); + nextAudioPortGeneration(); +} + +void AudioPolicyManager::addInput(audio_io_handle_t input, sp<AudioInputDescriptor> inputDesc) +{ + inputDesc->mIoHandle = input; + inputDesc->mId = nextUniqueId(); + mInputs.add(input, inputDesc); + nextAudioPortGeneration(); +} + +void AudioPolicyManager::findIoHandlesByAddress(sp<AudioOutputDescriptor> desc /*in*/, + const String8 address /*in*/, + SortedVector<audio_io_handle_t>& outputs /*out*/) { + // look for a match on the given address on the addresses of the outputs: + // find the address by finding the patch that maps to this output + ssize_t patchIdx = mAudioPatches.indexOfKey(desc->mPatchHandle); + //ALOGV(" inspecting output %d (patch %d) for supported device=0x%x", + // outputIdx, patchIdx, desc->mProfile->mSupportedDevices.types()); + if (patchIdx >= 0) { + const sp<AudioPatch> patchDesc = mAudioPatches.valueAt(patchIdx); + const int numSinks = patchDesc->mPatch.num_sinks; + for (ssize_t j=0; j < numSinks; j++) { + if (patchDesc->mPatch.sinks[j].type == AUDIO_PORT_TYPE_DEVICE) { + const char* patchAddr = + patchDesc->mPatch.sinks[j].ext.device.address; + if (strncmp(patchAddr, + address.string(), AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0) { + ALOGV("findIoHandlesByAddress(): adding opened output %d on same address %s", + desc->mIoHandle, patchDesc->mPatch.sinks[j].ext.device.address); + outputs.add(desc->mIoHandle); + break; + } + } + } + } +} + +status_t AudioPolicyManager::checkOutputsForDevice(const sp<DeviceDescriptor> devDesc, + audio_policy_dev_state_t state, + SortedVector<audio_io_handle_t>& outputs, + const String8 address) +{ + audio_devices_t device = devDesc->mDeviceType; + sp<AudioOutputDescriptor> desc; + // erase all current sample rates, formats and channel masks + devDesc->clearCapabilities(); + + if (state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) { + // first list already open outputs that can be routed to this device + for (size_t i = 0; i < mOutputs.size(); i++) { + desc = mOutputs.valueAt(i); + if (!desc->isDuplicated() && (desc->mProfile->mSupportedDevices.types() & device)) { + if (!deviceDistinguishesOnAddress(device)) { + ALOGV("checkOutputsForDevice(): adding opened output %d", mOutputs.keyAt(i)); + outputs.add(mOutputs.keyAt(i)); + } else { + ALOGV(" checking address match due to device 0x%x", device); + findIoHandlesByAddress(desc, address, outputs); + } + } + } + // then look for output profiles that can be routed to this device + SortedVector< sp<IOProfile> > profiles; + for (size_t i = 0; i < mHwModules.size(); i++) + { + if (mHwModules[i]->mHandle == 0) { + continue; + } + for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) + { + if (mHwModules[i]->mOutputProfiles[j]->mSupportedDevices.types() & device) { + ALOGV("checkOutputsForDevice(): adding profile %zu from module %zu", j, i); + profiles.add(mHwModules[i]->mOutputProfiles[j]); + } + } + } + + ALOGV(" found %d profiles, %d outputs", profiles.size(), outputs.size()); + + if (profiles.isEmpty() && outputs.isEmpty()) { + ALOGW("checkOutputsForDevice(): No output available for device %04x", device); + return BAD_VALUE; + } + + // open outputs for matching profiles if needed. Direct outputs are also opened to + // query for dynamic parameters and will be closed later by setDeviceConnectionState() + for (ssize_t profile_index = 0; profile_index < (ssize_t)profiles.size(); profile_index++) { + sp<IOProfile> profile = profiles[profile_index]; + + // nothing to do if one output is already opened for this profile + size_t j; + for (j = 0; j < outputs.size(); j++) { + desc = mOutputs.valueFor(outputs.itemAt(j)); + if (!desc->isDuplicated() && desc->mProfile == profile) { + // matching profile: save the sample rates, format and channel masks supported + // by the profile in our device descriptor + devDesc->importAudioPort(profile); + break; + } + } + if (j != outputs.size()) { + continue; + } + + ALOGV("opening output for device %08x with params %s profile %p", + device, address.string(), profile.get()); + desc = new AudioOutputDescriptor(profile); + desc->mDevice = device; + audio_config_t config = AUDIO_CONFIG_INITIALIZER; + config.sample_rate = desc->mSamplingRate; + config.channel_mask = desc->mChannelMask; + config.format = desc->mFormat; + config.offload_info.sample_rate = desc->mSamplingRate; + config.offload_info.channel_mask = desc->mChannelMask; + config.offload_info.format = desc->mFormat; + audio_io_handle_t output = AUDIO_IO_HANDLE_NONE; + status_t status = mpClientInterface->openOutput(profile->mModule->mHandle, + &output, + &config, + &desc->mDevice, + address, + &desc->mLatency, + desc->mFlags); + if (status == NO_ERROR) { + desc->mSamplingRate = config.sample_rate; + desc->mChannelMask = config.channel_mask; + desc->mFormat = config.format; + + // Here is where the out_set_parameters() for card & device gets called + if (!address.isEmpty()) { + char *param = audio_device_address_to_parameter(device, address); + mpClientInterface->setParameters(output, String8(param)); + free(param); + } + + // Here is where we step through and resolve any "dynamic" fields + String8 reply; + char *value; + if (profile->mSamplingRates[0] == 0) { + reply = mpClientInterface->getParameters(output, + String8(AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES)); + ALOGV("checkOutputsForDevice() supported sampling rates %s", + reply.string()); + value = strpbrk((char *)reply.string(), "="); + if (value != NULL) { + profile->loadSamplingRates(value + 1); + } + } + if (profile->mFormats[0] == AUDIO_FORMAT_DEFAULT) { + reply = mpClientInterface->getParameters(output, + String8(AUDIO_PARAMETER_STREAM_SUP_FORMATS)); + ALOGV("checkOutputsForDevice() supported formats %s", + reply.string()); + value = strpbrk((char *)reply.string(), "="); + if (value != NULL) { + profile->loadFormats(value + 1); + } + } + if (profile->mChannelMasks[0] == 0) { + reply = mpClientInterface->getParameters(output, + String8(AUDIO_PARAMETER_STREAM_SUP_CHANNELS)); + ALOGV("checkOutputsForDevice() supported channel masks %s", + reply.string()); + value = strpbrk((char *)reply.string(), "="); + if (value != NULL) { + profile->loadOutChannels(value + 1); + } + } + if (((profile->mSamplingRates[0] == 0) && + (profile->mSamplingRates.size() < 2)) || + ((profile->mFormats[0] == AUDIO_FORMAT_DEFAULT) && + (profile->mFormats.size() < 2)) || + ((profile->mChannelMasks[0] == 0) && + (profile->mChannelMasks.size() < 2))) { + ALOGW("checkOutputsForDevice() missing param"); + mpClientInterface->closeOutput(output); + output = AUDIO_IO_HANDLE_NONE; + } else if (profile->mSamplingRates[0] == 0 || profile->mFormats[0] == 0 || + profile->mChannelMasks[0] == 0) { + mpClientInterface->closeOutput(output); + config.sample_rate = profile->pickSamplingRate(); + config.channel_mask = profile->pickChannelMask(); + config.format = profile->pickFormat(); + config.offload_info.sample_rate = config.sample_rate; + config.offload_info.channel_mask = config.channel_mask; + config.offload_info.format = config.format; + status = mpClientInterface->openOutput(profile->mModule->mHandle, + &output, + &config, + &desc->mDevice, + address, + &desc->mLatency, + desc->mFlags); + if (status == NO_ERROR) { + desc->mSamplingRate = config.sample_rate; + desc->mChannelMask = config.channel_mask; + desc->mFormat = config.format; + } else { + output = AUDIO_IO_HANDLE_NONE; + } + } + + if (output != AUDIO_IO_HANDLE_NONE) { + addOutput(output, desc); + if ((desc->mFlags & AUDIO_OUTPUT_FLAG_DIRECT) == 0) { + audio_io_handle_t duplicatedOutput = AUDIO_IO_HANDLE_NONE; + + // set initial stream volume for device + applyStreamVolumes(output, device, 0, true); + + //TODO: configure audio effect output stage here + + // open a duplicating output thread for the new output and the primary output + duplicatedOutput = mpClientInterface->openDuplicateOutput(output, + mPrimaryOutput); + if (duplicatedOutput != AUDIO_IO_HANDLE_NONE) { + // add duplicated output descriptor + sp<AudioOutputDescriptor> dupOutputDesc = + new AudioOutputDescriptor(NULL); + dupOutputDesc->mOutput1 = mOutputs.valueFor(mPrimaryOutput); + dupOutputDesc->mOutput2 = mOutputs.valueFor(output); + dupOutputDesc->mSamplingRate = desc->mSamplingRate; + dupOutputDesc->mFormat = desc->mFormat; + dupOutputDesc->mChannelMask = desc->mChannelMask; + dupOutputDesc->mLatency = desc->mLatency; + addOutput(duplicatedOutput, dupOutputDesc); + applyStreamVolumes(duplicatedOutput, device, 0, true); + } else { + ALOGW("checkOutputsForDevice() could not open dup output for %d and %d", + mPrimaryOutput, output); + mpClientInterface->closeOutput(output); + mOutputs.removeItem(output); + nextAudioPortGeneration(); + output = AUDIO_IO_HANDLE_NONE; + } + } + } + } else { + output = AUDIO_IO_HANDLE_NONE; + } + if (output == AUDIO_IO_HANDLE_NONE) { + ALOGW("checkOutputsForDevice() could not open output for device %x", device); + profiles.removeAt(profile_index); + profile_index--; + } else { + outputs.add(output); + devDesc->importAudioPort(profile); + + if (deviceDistinguishesOnAddress(device)) { + ALOGV("checkOutputsForDevice(): setOutputDevice(dev=0x%x, addr=%s)", + device, address.string()); + setOutputDevice(output, device, true/*force*/, 0/*delay*/, + NULL/*patch handle*/, address.string()); + } + ALOGV("checkOutputsForDevice(): adding output %d", output); + } + } + + if (profiles.isEmpty()) { + ALOGW("checkOutputsForDevice(): No output available for device %04x", device); + return BAD_VALUE; + } + } else { // Disconnect + // check if one opened output is not needed any more after disconnecting one device + for (size_t i = 0; i < mOutputs.size(); i++) { + desc = mOutputs.valueAt(i); + if (!desc->isDuplicated()) { + if (!(desc->mProfile->mSupportedDevices.types() + & mAvailableOutputDevices.types())) { + ALOGV("checkOutputsForDevice(): disconnecting adding output %d", + mOutputs.keyAt(i)); + outputs.add(mOutputs.keyAt(i)); + } else if (deviceDistinguishesOnAddress(device) && + // exact match on device + (desc->mProfile->mSupportedDevices.types() == device)) { + findIoHandlesByAddress(desc, address, outputs); + } + } + } + // Clear any profiles associated with the disconnected device. + for (size_t i = 0; i < mHwModules.size(); i++) + { + if (mHwModules[i]->mHandle == 0) { + continue; + } + for (size_t j = 0; j < mHwModules[i]->mOutputProfiles.size(); j++) + { + sp<IOProfile> profile = mHwModules[i]->mOutputProfiles[j]; + if (profile->mSupportedDevices.types() & device) { + ALOGV("checkOutputsForDevice(): " + "clearing direct output profile %zu on module %zu", j, i); + if (profile->mSamplingRates[0] == 0) { + profile->mSamplingRates.clear(); + profile->mSamplingRates.add(0); + } + if (profile->mFormats[0] == AUDIO_FORMAT_DEFAULT) { + profile->mFormats.clear(); + profile->mFormats.add(AUDIO_FORMAT_DEFAULT); + } + if (profile->mChannelMasks[0] == 0) { + profile->mChannelMasks.clear(); + profile->mChannelMasks.add(0); + } + } + } + } + } + return NO_ERROR; +} + +status_t AudioPolicyManager::checkInputsForDevice(audio_devices_t device, + audio_policy_dev_state_t state, + SortedVector<audio_io_handle_t>& inputs, + const String8 address) +{ + sp<AudioInputDescriptor> desc; + if (state == AUDIO_POLICY_DEVICE_STATE_AVAILABLE) { + // first list already open inputs that can be routed to this device + for (size_t input_index = 0; input_index < mInputs.size(); input_index++) { + desc = mInputs.valueAt(input_index); + if (desc->mProfile->mSupportedDevices.types() & (device & ~AUDIO_DEVICE_BIT_IN)) { + ALOGV("checkInputsForDevice(): adding opened input %d", mInputs.keyAt(input_index)); + inputs.add(mInputs.keyAt(input_index)); + } + } + + // then look for input profiles that can be routed to this device + SortedVector< sp<IOProfile> > profiles; + for (size_t module_idx = 0; module_idx < mHwModules.size(); module_idx++) + { + if (mHwModules[module_idx]->mHandle == 0) { + continue; + } + for (size_t profile_index = 0; + profile_index < mHwModules[module_idx]->mInputProfiles.size(); + profile_index++) + { + if (mHwModules[module_idx]->mInputProfiles[profile_index]->mSupportedDevices.types() + & (device & ~AUDIO_DEVICE_BIT_IN)) { + ALOGV("checkInputsForDevice(): adding profile %zu from module %zu", + profile_index, module_idx); + profiles.add(mHwModules[module_idx]->mInputProfiles[profile_index]); + } + } + } + + if (profiles.isEmpty() && inputs.isEmpty()) { + ALOGW("checkInputsForDevice(): No input available for device 0x%X", device); + return BAD_VALUE; + } + + // open inputs for matching profiles if needed. Direct inputs are also opened to + // query for dynamic parameters and will be closed later by setDeviceConnectionState() + for (ssize_t profile_index = 0; profile_index < (ssize_t)profiles.size(); profile_index++) { + + sp<IOProfile> profile = profiles[profile_index]; + // nothing to do if one input is already opened for this profile + size_t input_index; + for (input_index = 0; input_index < mInputs.size(); input_index++) { + desc = mInputs.valueAt(input_index); + if (desc->mProfile == profile) { + break; + } + } + if (input_index != mInputs.size()) { + continue; + } + + ALOGV("opening input for device 0x%X with params %s", device, address.string()); + desc = new AudioInputDescriptor(profile); + desc->mDevice = device; + audio_config_t config = AUDIO_CONFIG_INITIALIZER; + config.sample_rate = desc->mSamplingRate; + config.channel_mask = desc->mChannelMask; + config.format = desc->mFormat; + audio_io_handle_t input = AUDIO_IO_HANDLE_NONE; + status_t status = mpClientInterface->openInput(profile->mModule->mHandle, + &input, + &config, + &desc->mDevice, + address, + AUDIO_SOURCE_MIC, + AUDIO_INPUT_FLAG_NONE /*FIXME*/); + + if (status == NO_ERROR) { + desc->mSamplingRate = config.sample_rate; + desc->mChannelMask = config.channel_mask; + desc->mFormat = config.format; + + if (!address.isEmpty()) { + char *param = audio_device_address_to_parameter(device, address); + mpClientInterface->setParameters(input, String8(param)); + free(param); + } + + // Here is where we step through and resolve any "dynamic" fields + String8 reply; + char *value; + if (profile->mSamplingRates[0] == 0) { + reply = mpClientInterface->getParameters(input, + String8(AUDIO_PARAMETER_STREAM_SUP_SAMPLING_RATES)); + ALOGV("checkInputsForDevice() direct input sup sampling rates %s", + reply.string()); + value = strpbrk((char *)reply.string(), "="); + if (value != NULL) { + profile->loadSamplingRates(value + 1); + } + } + if (profile->mFormats[0] == AUDIO_FORMAT_DEFAULT) { + reply = mpClientInterface->getParameters(input, + String8(AUDIO_PARAMETER_STREAM_SUP_FORMATS)); + ALOGV("checkInputsForDevice() direct input sup formats %s", reply.string()); + value = strpbrk((char *)reply.string(), "="); + if (value != NULL) { + profile->loadFormats(value + 1); + } + } + if (profile->mChannelMasks[0] == 0) { + reply = mpClientInterface->getParameters(input, + String8(AUDIO_PARAMETER_STREAM_SUP_CHANNELS)); + ALOGV("checkInputsForDevice() direct input sup channel masks %s", + reply.string()); + value = strpbrk((char *)reply.string(), "="); + if (value != NULL) { + profile->loadInChannels(value + 1); + } + } + if (((profile->mSamplingRates[0] == 0) && (profile->mSamplingRates.size() < 2)) || + ((profile->mFormats[0] == 0) && (profile->mFormats.size() < 2)) || + ((profile->mChannelMasks[0] == 0) && (profile->mChannelMasks.size() < 2))) { + ALOGW("checkInputsForDevice() direct input missing param"); + mpClientInterface->closeInput(input); + input = AUDIO_IO_HANDLE_NONE; + } + + if (input != 0) { + addInput(input, desc); + } + } // endif input != 0 + + if (input == AUDIO_IO_HANDLE_NONE) { + ALOGW("checkInputsForDevice() could not open input for device 0x%X", device); + profiles.removeAt(profile_index); + profile_index--; + } else { + inputs.add(input); + ALOGV("checkInputsForDevice(): adding input %d", input); + } + } // end scan profiles + + if (profiles.isEmpty()) { + ALOGW("checkInputsForDevice(): No input available for device 0x%X", device); + return BAD_VALUE; + } + } else { + // Disconnect + // check if one opened input is not needed any more after disconnecting one device + for (size_t input_index = 0; input_index < mInputs.size(); input_index++) { + desc = mInputs.valueAt(input_index); + if (!(desc->mProfile->mSupportedDevices.types() & mAvailableInputDevices.types())) { + ALOGV("checkInputsForDevice(): disconnecting adding input %d", + mInputs.keyAt(input_index)); + inputs.add(mInputs.keyAt(input_index)); + } + } + // Clear any profiles associated with the disconnected device. + for (size_t module_index = 0; module_index < mHwModules.size(); module_index++) { + if (mHwModules[module_index]->mHandle == 0) { + continue; + } + for (size_t profile_index = 0; + profile_index < mHwModules[module_index]->mInputProfiles.size(); + profile_index++) { + sp<IOProfile> profile = mHwModules[module_index]->mInputProfiles[profile_index]; + if (profile->mSupportedDevices.types() & device) { + ALOGV("checkInputsForDevice(): clearing direct input profile %zu on module %zu", + profile_index, module_index); + if (profile->mSamplingRates[0] == 0) { + profile->mSamplingRates.clear(); + profile->mSamplingRates.add(0); + } + if (profile->mFormats[0] == AUDIO_FORMAT_DEFAULT) { + profile->mFormats.clear(); + profile->mFormats.add(AUDIO_FORMAT_DEFAULT); + } + if (profile->mChannelMasks[0] == 0) { + profile->mChannelMasks.clear(); + profile->mChannelMasks.add(0); + } + } + } + } + } // end disconnect + + return NO_ERROR; +} + + +void AudioPolicyManager::closeOutput(audio_io_handle_t output) +{ + ALOGV("closeOutput(%d)", output); + + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output); + if (outputDesc == NULL) { + ALOGW("closeOutput() unknown output %d", output); + return; + } + + // look for duplicated outputs connected to the output being removed. + for (size_t i = 0; i < mOutputs.size(); i++) { + sp<AudioOutputDescriptor> dupOutputDesc = mOutputs.valueAt(i); + if (dupOutputDesc->isDuplicated() && + (dupOutputDesc->mOutput1 == outputDesc || + dupOutputDesc->mOutput2 == outputDesc)) { + sp<AudioOutputDescriptor> outputDesc2; + if (dupOutputDesc->mOutput1 == outputDesc) { + outputDesc2 = dupOutputDesc->mOutput2; + } else { + outputDesc2 = dupOutputDesc->mOutput1; + } + // As all active tracks on duplicated output will be deleted, + // and as they were also referenced on the other output, the reference + // count for their stream type must be adjusted accordingly on + // the other output. + for (int j = 0; j < AUDIO_STREAM_CNT; j++) { + int refCount = dupOutputDesc->mRefCount[j]; + outputDesc2->changeRefCount((audio_stream_type_t)j,-refCount); + } + audio_io_handle_t duplicatedOutput = mOutputs.keyAt(i); + ALOGV("closeOutput() closing also duplicated output %d", duplicatedOutput); + + mpClientInterface->closeOutput(duplicatedOutput); + mOutputs.removeItem(duplicatedOutput); + } + } + + nextAudioPortGeneration(); + + ssize_t index = mAudioPatches.indexOfKey(outputDesc->mPatchHandle); + if (index >= 0) { + sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index); + status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0); + mAudioPatches.removeItemsAt(index); + mpClientInterface->onAudioPatchListUpdate(); + } + + AudioParameter param; + param.add(String8("closing"), String8("true")); + mpClientInterface->setParameters(output, param.toString()); + + mpClientInterface->closeOutput(output); + mOutputs.removeItem(output); + mPreviousOutputs = mOutputs; +} + +void AudioPolicyManager::closeInput(audio_io_handle_t input) +{ + ALOGV("closeInput(%d)", input); + + sp<AudioInputDescriptor> inputDesc = mInputs.valueFor(input); + if (inputDesc == NULL) { + ALOGW("closeInput() unknown input %d", input); + return; + } + + nextAudioPortGeneration(); + + ssize_t index = mAudioPatches.indexOfKey(inputDesc->mPatchHandle); + if (index >= 0) { + sp<AudioPatch> patchDesc = mAudioPatches.valueAt(index); + status_t status = mpClientInterface->releaseAudioPatch(patchDesc->mAfPatchHandle, 0); + mAudioPatches.removeItemsAt(index); + mpClientInterface->onAudioPatchListUpdate(); + } + + mpClientInterface->closeInput(input); + mInputs.removeItem(input); +} + +SortedVector<audio_io_handle_t> AudioPolicyManager::getOutputsForDevice(audio_devices_t device, + DefaultKeyedVector<audio_io_handle_t, sp<AudioOutputDescriptor> > openOutputs) +{ + SortedVector<audio_io_handle_t> outputs; + + ALOGVV("getOutputsForDevice() device %04x", device); + for (size_t i = 0; i < openOutputs.size(); i++) { + ALOGVV("output %d isDuplicated=%d device=%04x", + i, openOutputs.valueAt(i)->isDuplicated(), openOutputs.valueAt(i)->supportedDevices()); + if ((device & openOutputs.valueAt(i)->supportedDevices()) == device) { + ALOGVV("getOutputsForDevice() found output %d", openOutputs.keyAt(i)); + outputs.add(openOutputs.keyAt(i)); + } + } + return outputs; +} + +bool AudioPolicyManager::vectorsEqual(SortedVector<audio_io_handle_t>& outputs1, + SortedVector<audio_io_handle_t>& outputs2) +{ + if (outputs1.size() != outputs2.size()) { + return false; + } + for (size_t i = 0; i < outputs1.size(); i++) { + if (outputs1[i] != outputs2[i]) { + return false; + } + } + return true; +} + +void AudioPolicyManager::checkOutputForStrategy(routing_strategy strategy) +{ + audio_devices_t oldDevice = getDeviceForStrategy(strategy, true /*fromCache*/); + audio_devices_t newDevice = getDeviceForStrategy(strategy, false /*fromCache*/); + SortedVector<audio_io_handle_t> srcOutputs = getOutputsForDevice(oldDevice, mPreviousOutputs); + SortedVector<audio_io_handle_t> dstOutputs = getOutputsForDevice(newDevice, mOutputs); + + if (!vectorsEqual(srcOutputs,dstOutputs)) { + ALOGV("checkOutputForStrategy() strategy %d, moving from output %d to output %d", + strategy, srcOutputs[0], dstOutputs[0]); + // mute strategy while moving tracks from one output to another + for (size_t i = 0; i < srcOutputs.size(); i++) { + sp<AudioOutputDescriptor> desc = mOutputs.valueFor(srcOutputs[i]); + if (desc->isStrategyActive(strategy)) { + setStrategyMute(strategy, true, srcOutputs[i]); + setStrategyMute(strategy, false, srcOutputs[i], MUTE_TIME_MS, newDevice); + } + } + + // Move effects associated to this strategy from previous output to new output + if (strategy == STRATEGY_MEDIA) { + audio_io_handle_t fxOutput = selectOutputForEffects(dstOutputs); + SortedVector<audio_io_handle_t> moved; + for (size_t i = 0; i < mEffects.size(); i++) { + sp<EffectDescriptor> effectDesc = mEffects.valueAt(i); + if (effectDesc->mSession == AUDIO_SESSION_OUTPUT_MIX && + effectDesc->mIo != fxOutput) { + if (moved.indexOf(effectDesc->mIo) < 0) { + ALOGV("checkOutputForStrategy() moving effect %d to output %d", + mEffects.keyAt(i), fxOutput); + mpClientInterface->moveEffects(AUDIO_SESSION_OUTPUT_MIX, effectDesc->mIo, + fxOutput); + moved.add(effectDesc->mIo); + } + effectDesc->mIo = fxOutput; + } + } + } + // Move tracks associated to this strategy from previous output to new output + for (int i = 0; i < AUDIO_STREAM_CNT; i++) { + if (getStrategy((audio_stream_type_t)i) == strategy) { + mpClientInterface->invalidateStream((audio_stream_type_t)i); + } + } + } +} + +void AudioPolicyManager::checkOutputForAllStrategies() +{ + checkOutputForStrategy(STRATEGY_ENFORCED_AUDIBLE); + checkOutputForStrategy(STRATEGY_PHONE); + checkOutputForStrategy(STRATEGY_SONIFICATION); + checkOutputForStrategy(STRATEGY_SONIFICATION_RESPECTFUL); + checkOutputForStrategy(STRATEGY_MEDIA); + checkOutputForStrategy(STRATEGY_DTMF); +} + +audio_io_handle_t AudioPolicyManager::getA2dpOutput() +{ + for (size_t i = 0; i < mOutputs.size(); i++) { + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueAt(i); + if (!outputDesc->isDuplicated() && outputDesc->device() & AUDIO_DEVICE_OUT_ALL_A2DP) { + return mOutputs.keyAt(i); + } + } + + return 0; +} + +void AudioPolicyManager::checkA2dpSuspend() +{ + audio_io_handle_t a2dpOutput = getA2dpOutput(); + if (a2dpOutput == 0) { + mA2dpSuspended = false; + return; + } + + bool isScoConnected = + (mAvailableInputDevices.types() & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) != 0; + // suspend A2DP output if: + // (NOT already suspended) && + // ((SCO device is connected && + // (forced usage for communication || for record is SCO))) || + // (phone state is ringing || in call) + // + // restore A2DP output if: + // (Already suspended) && + // ((SCO device is NOT connected || + // (forced usage NOT for communication && NOT for record is SCO))) && + // (phone state is NOT ringing && NOT in call) + // + if (mA2dpSuspended) { + if ((!isScoConnected || + ((mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION] != AUDIO_POLICY_FORCE_BT_SCO) && + (mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] != AUDIO_POLICY_FORCE_BT_SCO))) && + ((mPhoneState != AUDIO_MODE_IN_CALL) && + (mPhoneState != AUDIO_MODE_RINGTONE))) { + + mpClientInterface->restoreOutput(a2dpOutput); + mA2dpSuspended = false; + } + } else { + if ((isScoConnected && + ((mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION] == AUDIO_POLICY_FORCE_BT_SCO) || + (mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] == AUDIO_POLICY_FORCE_BT_SCO))) || + ((mPhoneState == AUDIO_MODE_IN_CALL) || + (mPhoneState == AUDIO_MODE_RINGTONE))) { + + mpClientInterface->suspendOutput(a2dpOutput); + mA2dpSuspended = true; + } + } +} + +audio_devices_t AudioPolicyManager::getNewOutputDevice(audio_io_handle_t output, bool fromCache) +{ + audio_devices_t device = AUDIO_DEVICE_NONE; + + sp<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 + // 2: we are in call or the strategy phone is active on the output: + // use device for strategy phone + // 3: the strategy sonification is active on the output: + // use device for strategy sonification + // 4: the strategy "respectful" sonification is active on the output: + // use device for strategy "respectful" sonification + // 5: the strategy media is active on the output: + // use device for strategy media + // 6: the strategy DTMF is active on the output: + // use device for strategy DTMF + if (outputDesc->isStrategyActive(STRATEGY_ENFORCED_AUDIBLE)) { + device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache); + } else if (isInCall() || + outputDesc->isStrategyActive(STRATEGY_PHONE)) { + device = getDeviceForStrategy(STRATEGY_PHONE, fromCache); + } else if (outputDesc->isStrategyActive(STRATEGY_SONIFICATION)) { + device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache); + } else if (outputDesc->isStrategyActive(STRATEGY_SONIFICATION_RESPECTFUL)) { + device = getDeviceForStrategy(STRATEGY_SONIFICATION_RESPECTFUL, fromCache); + } else if (outputDesc->isStrategyActive(STRATEGY_MEDIA)) { + device = getDeviceForStrategy(STRATEGY_MEDIA, fromCache); + } else if (outputDesc->isStrategyActive(STRATEGY_DTMF)) { + device = getDeviceForStrategy(STRATEGY_DTMF, fromCache); + } + + ALOGV("getNewOutputDevice() selected device %x", device); + return device; +} + +audio_devices_t AudioPolicyManager::getNewInputDevice(audio_io_handle_t input) +{ + sp<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); + return device; +} + +uint32_t AudioPolicyManager::getStrategyForStream(audio_stream_type_t stream) { + return (uint32_t)getStrategy(stream); +} + +audio_devices_t AudioPolicyManager::getDevicesForStream(audio_stream_type_t stream) { + // 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) { + 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++) { + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(outputs[i]); + if (outputDesc->isStrategyActive(strategy)) { + devices = outputDesc->device(); + break; + } + } + + /*Filter SPEAKER_SAFE out of results, as AudioService doesn't know about it + and doesn't really need to.*/ + if (devices & AUDIO_DEVICE_OUT_SPEAKER_SAFE) { + devices |= AUDIO_DEVICE_OUT_SPEAKER; + devices &= ~AUDIO_DEVICE_OUT_SPEAKER_SAFE; + } + + return devices; +} + +AudioPolicyManager::routing_strategy AudioPolicyManager::getStrategy( + audio_stream_type_t stream) { + // stream to strategy mapping + switch (stream) { + case AUDIO_STREAM_VOICE_CALL: + case AUDIO_STREAM_BLUETOOTH_SCO: + return STRATEGY_PHONE; + case AUDIO_STREAM_RING: + case AUDIO_STREAM_ALARM: + return STRATEGY_SONIFICATION; + case AUDIO_STREAM_NOTIFICATION: + return STRATEGY_SONIFICATION_RESPECTFUL; + case AUDIO_STREAM_DTMF: + return STRATEGY_DTMF; + default: + ALOGE("unknown stream type"); + case AUDIO_STREAM_SYSTEM: + // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs + // while key clicks are played produces a poor result + case AUDIO_STREAM_TTS: + case AUDIO_STREAM_MUSIC: + return STRATEGY_MEDIA; + case AUDIO_STREAM_ENFORCED_AUDIBLE: + return STRATEGY_ENFORCED_AUDIBLE; + } +} + +uint32_t AudioPolicyManager::getStrategyForAttr(const audio_attributes_t *attr) { + // flags to strategy mapping + if ((attr->flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) == AUDIO_FLAG_AUDIBILITY_ENFORCED) { + return (uint32_t) STRATEGY_ENFORCED_AUDIBLE; + } + + // usage to strategy mapping + switch (attr->usage) { + case AUDIO_USAGE_MEDIA: + case AUDIO_USAGE_GAME: + case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY: + case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: + case AUDIO_USAGE_ASSISTANCE_SONIFICATION: + return (uint32_t) STRATEGY_MEDIA; + + case AUDIO_USAGE_VOICE_COMMUNICATION: + return (uint32_t) STRATEGY_PHONE; + + case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING: + return (uint32_t) STRATEGY_DTMF; + + case AUDIO_USAGE_ALARM: + case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE: + return (uint32_t) STRATEGY_SONIFICATION; + + case AUDIO_USAGE_NOTIFICATION: + case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST: + case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT: + case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED: + case AUDIO_USAGE_NOTIFICATION_EVENT: + return (uint32_t) STRATEGY_SONIFICATION_RESPECTFUL; + + case AUDIO_USAGE_UNKNOWN: + default: + return (uint32_t) STRATEGY_MEDIA; + } +} + +void AudioPolicyManager::handleNotificationRoutingForStream(audio_stream_type_t stream) { + switch(stream) { + case AUDIO_STREAM_MUSIC: + checkOutputForStrategy(STRATEGY_SONIFICATION_RESPECTFUL); + updateDevicesAndOutputs(); + break; + default: + break; + } +} + +audio_devices_t AudioPolicyManager::getDeviceForStrategy(routing_strategy strategy, + bool fromCache) +{ + uint32_t device = AUDIO_DEVICE_NONE; + + if (fromCache) { + ALOGVV("getDeviceForStrategy() from cache strategy %d, device %x", + strategy, mDeviceForStrategy[strategy]); + return mDeviceForStrategy[strategy]; + } + audio_devices_t availableOutputDeviceTypes = mAvailableOutputDevices.types(); + switch (strategy) { + + case STRATEGY_SONIFICATION_RESPECTFUL: + if (isInCall()) { + device = getDeviceForStrategy(STRATEGY_SONIFICATION, false /*fromCache*/); + } else if (isStreamActiveRemotely(AUDIO_STREAM_MUSIC, + SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { + // while media is playing on a remote device, use the the sonification behavior. + // Note that we test this usecase before testing if media is playing because + // the isStreamActive() method only informs about the activity of a stream, not + // if it's for local playback. Note also that we use the same delay between both tests + device = getDeviceForStrategy(STRATEGY_SONIFICATION, false /*fromCache*/); + //user "safe" speaker if available instead of normal speaker to avoid triggering + //other acoustic safety mechanisms for notification + if (device == AUDIO_DEVICE_OUT_SPEAKER && (availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) + device = AUDIO_DEVICE_OUT_SPEAKER_SAFE; + } else if (isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) { + // while media is playing (or has recently played), use the same device + device = getDeviceForStrategy(STRATEGY_MEDIA, false /*fromCache*/); + } else { + // when media is not playing anymore, fall back on the sonification behavior + device = getDeviceForStrategy(STRATEGY_SONIFICATION, false /*fromCache*/); + //user "safe" speaker if available instead of normal speaker to avoid triggering + //other acoustic safety mechanisms for notification + if (device == AUDIO_DEVICE_OUT_SPEAKER && (availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER_SAFE)) + device = AUDIO_DEVICE_OUT_SPEAKER_SAFE; + } + + break; + + case STRATEGY_DTMF: + if (!isInCall()) { + // when off call, DTMF strategy follows the same rules as MEDIA strategy + device = getDeviceForStrategy(STRATEGY_MEDIA, false /*fromCache*/); + break; + } + // when in call, DTMF and PHONE strategies follow the same rules + // FALL THROUGH + + case STRATEGY_PHONE: + // Force use of only devices on primary output if: + // - in call AND + // - cannot route from voice call RX OR + // - audio HAL version is < 3.0 and TX device is on the primary HW module + if (mPhoneState == AUDIO_MODE_IN_CALL) { + audio_devices_t txDevice = getDeviceForInputSource(AUDIO_SOURCE_VOICE_COMMUNICATION); + sp<AudioOutputDescriptor> hwOutputDesc = mOutputs.valueFor(mPrimaryOutput); + if (((mAvailableInputDevices.types() & + AUDIO_DEVICE_IN_TELEPHONY_RX & ~AUDIO_DEVICE_BIT_IN) == 0) || + (((txDevice & availablePrimaryInputDevices() & ~AUDIO_DEVICE_BIT_IN) != 0) && + (hwOutputDesc->getAudioPort()->mModule->mHalVersion < + AUDIO_DEVICE_API_VERSION_3_0))) { + availableOutputDeviceTypes = availablePrimaryOutputDevices(); + } + } + // for phone strategy, we first consider the forced use and then the available devices by order + // of priority + switch (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]) { + case AUDIO_POLICY_FORCE_BT_SCO: + if (!isInCall() || strategy != STRATEGY_DTMF) { + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT; + if (device) break; + } + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_SCO; + if (device) break; + // if SCO device is requested but no SCO device is available, fall back to default case + // FALL THROUGH + + default: // FORCE_NONE + // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP + if (!isInCall() && + (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && + (getA2dpOutput() != 0) && !mA2dpSuspended) { + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; + if (device) break; + } + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_WIRED_HEADPHONE; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_WIRED_HEADSET; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_DEVICE; + if (device) break; + if (mPhoneState != AUDIO_MODE_IN_CALL) { + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_ACCESSORY; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_AUX_DIGITAL; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; + if (device) break; + } + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_EARPIECE; + if (device) break; + device = mDefaultOutputDevice->mDeviceType; + if (device == AUDIO_DEVICE_NONE) { + ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE"); + } + break; + + case AUDIO_POLICY_FORCE_SPEAKER: + // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to + // A2DP speaker when forcing to speaker output + if (!isInCall() && + (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && + (getA2dpOutput() != 0) && !mA2dpSuspended) { + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; + if (device) break; + } + if (mPhoneState != AUDIO_MODE_IN_CALL) { + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_ACCESSORY; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_DEVICE; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_AUX_DIGITAL; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; + if (device) break; + } + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_LINE; + if (device) break; + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER; + if (device) break; + device = mDefaultOutputDevice->mDeviceType; + if (device == AUDIO_DEVICE_NONE) { + ALOGE("getDeviceForStrategy() no device found for STRATEGY_PHONE, FORCE_SPEAKER"); + } + break; + } + break; + + case STRATEGY_SONIFICATION: + + // If incall, just select the STRATEGY_PHONE device: The rest of the behavior is handled by + // handleIncallSonification(). + if (isInCall()) { + device = getDeviceForStrategy(STRATEGY_PHONE, false /*fromCache*/); + break; + } + // FALL THROUGH + + case STRATEGY_ENFORCED_AUDIBLE: + // strategy STRATEGY_ENFORCED_AUDIBLE uses same routing policy as STRATEGY_SONIFICATION + // except: + // - when in call where it doesn't default to STRATEGY_PHONE behavior + // - in countries where not enforced in which case it follows STRATEGY_MEDIA + + if ((strategy == STRATEGY_SONIFICATION) || + (mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED)) { + device = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER; + if (device == AUDIO_DEVICE_NONE) { + ALOGE("getDeviceForStrategy() speaker device not found for STRATEGY_SONIFICATION"); + } + } + // The second device used for sonification is the same as the device used by media strategy + // FALL THROUGH + + case STRATEGY_MEDIA: { + uint32_t device2 = AUDIO_DEVICE_NONE; + if (strategy != STRATEGY_SONIFICATION) { + // no sonification on remote submix (e.g. WFD) + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_REMOTE_SUBMIX; + } + if ((device2 == AUDIO_DEVICE_NONE) && + (mForceUse[AUDIO_POLICY_FORCE_FOR_MEDIA] != AUDIO_POLICY_FORCE_NO_BT_A2DP) && + (getA2dpOutput() != 0) && !mA2dpSuspended) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP; + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER; + } + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_WIRED_HEADPHONE; + } + if ((device2 == AUDIO_DEVICE_NONE)) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_LINE; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_WIRED_HEADSET; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_ACCESSORY; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_USB_DEVICE; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET; + } + if ((device2 == AUDIO_DEVICE_NONE) && (strategy != STRATEGY_SONIFICATION)) { + // no sonification on aux digital (e.g. HDMI) + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_AUX_DIGITAL; + } + if ((device2 == AUDIO_DEVICE_NONE) && + (mForceUse[AUDIO_POLICY_FORCE_FOR_DOCK] == AUDIO_POLICY_FORCE_ANALOG_DOCK)) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET; + } + if (device2 == AUDIO_DEVICE_NONE) { + device2 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER; + } + int device3 = AUDIO_DEVICE_NONE; + if (strategy == STRATEGY_MEDIA) { + // ARC, SPDIF and AUX_LINE can co-exist with others. + device3 = availableOutputDeviceTypes & AUDIO_DEVICE_OUT_HDMI_ARC; + device3 |= (availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPDIF); + device3 |= (availableOutputDeviceTypes & AUDIO_DEVICE_OUT_AUX_LINE); + } + + device2 |= device3; + // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION or + // STRATEGY_ENFORCED_AUDIBLE, AUDIO_DEVICE_NONE otherwise + device |= device2; + + // If hdmi system audio mode is on, remove speaker out of output list. + if ((strategy == STRATEGY_MEDIA) && + (mForceUse[AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO] == + AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED)) { + device &= ~AUDIO_DEVICE_OUT_SPEAKER; + } + + if (device) break; + device = mDefaultOutputDevice->mDeviceType; + if (device == AUDIO_DEVICE_NONE) { + ALOGE("getDeviceForStrategy() no device found for STRATEGY_MEDIA"); + } + } break; + + default: + ALOGW("getDeviceForStrategy() unknown strategy: %d", strategy); + break; + } + + ALOGVV("getDeviceForStrategy() strategy %d, device %x", strategy, device); + return device; +} + +void AudioPolicyManager::updateDevicesAndOutputs() +{ + for (int i = 0; i < NUM_STRATEGIES; i++) { + mDeviceForStrategy[i] = getDeviceForStrategy((routing_strategy)i, false /*fromCache*/); + } + mPreviousOutputs = mOutputs; +} + +uint32_t AudioPolicyManager::checkDeviceMuteStrategies(sp<AudioOutputDescriptor> outputDesc, + audio_devices_t prevDevice, + uint32_t delayMs) +{ + // mute/unmute strategies using an incompatible device combination + // if muting, wait for the audio in pcm buffer to be drained before proceeding + // if unmuting, unmute only after the specified delay + if (outputDesc->isDuplicated()) { + return 0; + } + + uint32_t muteWaitMs = 0; + audio_devices_t device = outputDesc->device(); + bool shouldMute = outputDesc->isActive() && (popcount(device) >= 2); + + for (size_t i = 0; i < NUM_STRATEGIES; i++) { + audio_devices_t curDevice = getDeviceForStrategy((routing_strategy)i, false /*fromCache*/); + bool mute = shouldMute && (curDevice & device) && (curDevice != device); + bool doMute = false; + + if (mute && !outputDesc->mStrategyMutedByDevice[i]) { + doMute = true; + outputDesc->mStrategyMutedByDevice[i] = true; + } else if (!mute && outputDesc->mStrategyMutedByDevice[i]){ + doMute = true; + outputDesc->mStrategyMutedByDevice[i] = false; + } + if (doMute) { + for (size_t j = 0; j < mOutputs.size(); j++) { + sp<AudioOutputDescriptor> desc = mOutputs.valueAt(j); + // skip output if it does not share any device with current output + if ((desc->supportedDevices() & outputDesc->supportedDevices()) + == AUDIO_DEVICE_NONE) { + continue; + } + audio_io_handle_t curOutput = mOutputs.keyAt(j); + ALOGVV("checkDeviceMuteStrategies() %s strategy %d (curDevice %04x) on output %d", + mute ? "muting" : "unmuting", i, curDevice, curOutput); + setStrategyMute((routing_strategy)i, mute, curOutput, mute ? 0 : delayMs); + if (desc->isStrategyActive((routing_strategy)i)) { + if (mute) { + // FIXME: should not need to double latency if volume could be applied + // immediately by the audioflinger mixer. We must account for the delay + // between now and the next time the audioflinger thread for this output + // will process a buffer (which corresponds to one buffer size, + // usually 1/2 or 1/4 of the latency). + if (muteWaitMs < desc->latency() * 2) { + muteWaitMs = desc->latency() * 2; + } + } + } + } + } + } + + // temporary mute output if device selection changes to avoid volume bursts due to + // different per device volumes + if (outputDesc->isActive() && (device != prevDevice)) { + if (muteWaitMs < outputDesc->latency() * 2) { + muteWaitMs = outputDesc->latency() * 2; + } + for (size_t i = 0; i < NUM_STRATEGIES; i++) { + if (outputDesc->isStrategyActive((routing_strategy)i)) { + setStrategyMute((routing_strategy)i, true, outputDesc->mIoHandle); + // do tempMute unmute after twice the mute wait time + setStrategyMute((routing_strategy)i, false, outputDesc->mIoHandle, + muteWaitMs *2, device); + } + } + } + + // wait for the PCM output buffers to empty before proceeding with the rest of the command + if (muteWaitMs > delayMs) { + muteWaitMs -= delayMs; + usleep(muteWaitMs * 1000); + return muteWaitMs; + } + return 0; +} + +uint32_t AudioPolicyManager::setOutputDevice(audio_io_handle_t output, + audio_devices_t device, + bool force, + int delayMs, + audio_patch_handle_t *patchHandle, + const char* address) +{ + ALOGV("setOutputDevice() output %d device %04x delayMs %d", output, device, delayMs); + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output); + AudioParameter param; + uint32_t muteWaitMs; + + if (outputDesc->isDuplicated()) { + muteWaitMs = setOutputDevice(outputDesc->mOutput1->mIoHandle, device, force, delayMs); + muteWaitMs += setOutputDevice(outputDesc->mOutput2->mIoHandle, device, force, delayMs); + return muteWaitMs; + } + // no need to proceed if new device is not AUDIO_DEVICE_NONE and not supported by current + // output profile + if ((device != AUDIO_DEVICE_NONE) && + ((device & outputDesc->mProfile->mSupportedDevices.types()) == 0)) { + return 0; + } + + // filter devices according to output selected + device = (audio_devices_t)(device & outputDesc->mProfile->mSupportedDevices.types()); + + audio_devices_t prevDevice = outputDesc->mDevice; + + ALOGV("setOutputDevice() prevDevice %04x", prevDevice); + + if (device != AUDIO_DEVICE_NONE) { + outputDesc->mDevice = device; + } + muteWaitMs = checkDeviceMuteStrategies(outputDesc, prevDevice, delayMs); + + // Do not change the routing if: + // - the requested device is AUDIO_DEVICE_NONE + // - the requested device is the same as current device and force is not specified. + // Doing this check here allows the caller to call setOutputDevice() without conditions + if ((device == AUDIO_DEVICE_NONE || device == prevDevice) && !force) { + ALOGV("setOutputDevice() setting same device %04x or null device for output %d", device, output); + return muteWaitMs; + } + + ALOGV("setOutputDevice() changing device"); + + // do the routing + if (device == AUDIO_DEVICE_NONE) { + resetOutputDevice(output, delayMs, NULL); + } else { + DeviceVector deviceList = (address == NULL) ? + mAvailableOutputDevices.getDevicesFromType(device) + : mAvailableOutputDevices.getDevicesFromTypeAddr(device, String8(address)); + if (!deviceList.isEmpty()) { + struct audio_patch patch; + outputDesc->toAudioPortConfig(&patch.sources[0]); + patch.num_sources = 1; + 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.num_sinks++; + } + 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, + &afPatchHandle, + delayMs); + ALOGV("setOutputDevice() createAudioPatch returned %d patchHandle %d" + "num_sources %d num_sinks %d", + status, afPatchHandle, patch.num_sources, patch.num_sinks); + if (status == NO_ERROR) { + 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(); + } + } + + // inform all input as well + for (size_t i = 0; i < mInputs.size(); i++) { + const sp<AudioInputDescriptor> inputDescriptor = mInputs.valueAt(i); + if (!isVirtualInputDevice(inputDescriptor->mDevice)) { + AudioParameter inputCmd = AudioParameter(); + ALOGV("%s: inform input %d of device:%d", __func__, + inputDescriptor->mIoHandle, device); + inputCmd.addInt(String8(AudioParameter::keyRouting),device); + mpClientInterface->setParameters(inputDescriptor->mIoHandle, + inputCmd.toString(), + delayMs); + } + } + } + + // update stream volumes according to new device + applyStreamVolumes(output, device, delayMs); + + return muteWaitMs; +} + +status_t AudioPolicyManager::resetOutputDevice(audio_io_handle_t output, + int delayMs, + audio_patch_handle_t *patchHandle) +{ + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output); + ssize_t index; + if (patchHandle) { + index = mAudioPatches.indexOfKey(*patchHandle); + } else { + index = mAudioPatches.indexOfKey(outputDesc->mPatchHandle); + } + if (index < 0) { + return INVALID_OPERATION; + } + 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, + audio_patch_handle_t *patchHandle) +{ + status_t status = NO_ERROR; + + sp<AudioInputDescriptor> inputDesc = mInputs.valueFor(input); + if ((device != AUDIO_DEVICE_NONE) && ((device != inputDesc->mDevice) || force)) { + inputDesc->mDevice = device; + + DeviceVector deviceList = mAvailableInputDevices.getDevicesFromType(device); + if (!deviceList.isEmpty()) { + struct audio_patch patch; + inputDesc->toAudioPortConfig(&patch.sinks[0]); + // AUDIO_SOURCE_HOTWORD is for internal use only: + // handled as AUDIO_SOURCE_VOICE_RECOGNITION by the audio HAL + if (patch.sinks[0].ext.mix.usecase.source == AUDIO_SOURCE_HOTWORD && + !inputDesc->mIsSoundTrigger) { + patch.sinks[0].ext.mix.usecase.source = AUDIO_SOURCE_VOICE_RECOGNITION; + } + patch.num_sinks = 1; + //only one input device for now + deviceList.itemAt(0)->toAudioPortConfig(&patch.sources[0]); + patch.num_sources = 1; + 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, + &afPatchHandle, + 0); + ALOGV("setInputDevice() createAudioPatch returned %d patchHandle %d", + status, afPatchHandle); + if (status == NO_ERROR) { + 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, + audio_patch_handle_t *patchHandle) +{ + sp<AudioInputDescriptor> inputDesc = mInputs.valueFor(input); + ssize_t index; + if (patchHandle) { + index = mAudioPatches.indexOfKey(*patchHandle); + } else { + index = mAudioPatches.indexOfKey(inputDesc->mPatchHandle); + } + if (index < 0) { + return INVALID_OPERATION; + } + 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; +} + +sp<AudioPolicyManager::IOProfile> AudioPolicyManager::getInputProfile(audio_devices_t device, + uint32_t& samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_input_flags_t flags) +{ + // Choose an input profile based on the requested capture parameters: select the first available + // profile supporting all requested parameters. + + for (size_t i = 0; i < mHwModules.size(); i++) + { + if (mHwModules[i]->mHandle == 0) { + continue; + } + for (size_t j = 0; j < mHwModules[i]->mInputProfiles.size(); j++) + { + sp<IOProfile> profile = mHwModules[i]->mInputProfiles[j]; + // profile->log(); + if (profile->isCompatibleProfile(device, samplingRate, + &samplingRate /*updatedSamplingRate*/, + format, channelMask, (audio_output_flags_t) flags)) { + return profile; + } + } + } + return NULL; +} + +audio_devices_t AudioPolicyManager::getDeviceForInputSource(audio_source_t inputSource) +{ + uint32_t device = AUDIO_DEVICE_NONE; + audio_devices_t availableDeviceTypes = mAvailableInputDevices.types() & + ~AUDIO_DEVICE_BIT_IN; + switch (inputSource) { + case AUDIO_SOURCE_VOICE_UPLINK: + if (availableDeviceTypes & AUDIO_DEVICE_IN_VOICE_CALL) { + device = AUDIO_DEVICE_IN_VOICE_CALL; + break; + } + break; + + case AUDIO_SOURCE_DEFAULT: + case AUDIO_SOURCE_MIC: + if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_A2DP) { + device = AUDIO_DEVICE_IN_BLUETOOTH_A2DP; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) { + device = AUDIO_DEVICE_IN_WIRED_HEADSET; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) { + device = AUDIO_DEVICE_IN_USB_DEVICE; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { + device = AUDIO_DEVICE_IN_BUILTIN_MIC; + } + break; + + case AUDIO_SOURCE_VOICE_COMMUNICATION: + // Allow only use of devices on primary input if in call and HAL does not support routing + // to voice call path. + if ((mPhoneState == AUDIO_MODE_IN_CALL) && + (mAvailableOutputDevices.types() & AUDIO_DEVICE_OUT_TELEPHONY_TX) == 0) { + availableDeviceTypes = availablePrimaryInputDevices() & ~AUDIO_DEVICE_BIT_IN; + } + + switch (mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]) { + case AUDIO_POLICY_FORCE_BT_SCO: + // if SCO device is requested but no SCO device is available, fall back to default case + if (availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) { + device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; + break; + } + // FALL THROUGH + + default: // FORCE_NONE + if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) { + device = AUDIO_DEVICE_IN_WIRED_HEADSET; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) { + device = AUDIO_DEVICE_IN_USB_DEVICE; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { + device = AUDIO_DEVICE_IN_BUILTIN_MIC; + } + break; + + case AUDIO_POLICY_FORCE_SPEAKER: + if (availableDeviceTypes & AUDIO_DEVICE_IN_BACK_MIC) { + device = AUDIO_DEVICE_IN_BACK_MIC; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { + device = AUDIO_DEVICE_IN_BUILTIN_MIC; + } + break; + } + break; + + case AUDIO_SOURCE_VOICE_RECOGNITION: + case AUDIO_SOURCE_HOTWORD: + if (mForceUse[AUDIO_POLICY_FORCE_FOR_RECORD] == AUDIO_POLICY_FORCE_BT_SCO && + availableDeviceTypes & AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET) { + device = AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_WIRED_HEADSET) { + device = AUDIO_DEVICE_IN_WIRED_HEADSET; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_USB_DEVICE) { + device = AUDIO_DEVICE_IN_USB_DEVICE; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { + device = AUDIO_DEVICE_IN_BUILTIN_MIC; + } + break; + case AUDIO_SOURCE_CAMCORDER: + if (availableDeviceTypes & AUDIO_DEVICE_IN_BACK_MIC) { + device = AUDIO_DEVICE_IN_BACK_MIC; + } else if (availableDeviceTypes & AUDIO_DEVICE_IN_BUILTIN_MIC) { + device = AUDIO_DEVICE_IN_BUILTIN_MIC; + } + break; + case AUDIO_SOURCE_VOICE_DOWNLINK: + case AUDIO_SOURCE_VOICE_CALL: + if (availableDeviceTypes & AUDIO_DEVICE_IN_VOICE_CALL) { + device = AUDIO_DEVICE_IN_VOICE_CALL; + } + break; + case AUDIO_SOURCE_REMOTE_SUBMIX: + if (availableDeviceTypes & AUDIO_DEVICE_IN_REMOTE_SUBMIX) { + device = AUDIO_DEVICE_IN_REMOTE_SUBMIX; + } + break; + default: + ALOGW("getDeviceForInputSource() invalid input source %d", inputSource); + break; + } + ALOGV("getDeviceForInputSource()input source %d, device %08x", inputSource, device); + return device; +} + +bool AudioPolicyManager::isVirtualInputDevice(audio_devices_t device) +{ + if ((device & AUDIO_DEVICE_BIT_IN) != 0) { + device &= ~AUDIO_DEVICE_BIT_IN; + if ((popcount(device) == 1) && ((device & ~APM_AUDIO_IN_DEVICE_VIRTUAL_ALL) == 0)) + return true; + } + return false; +} + +bool AudioPolicyManager::deviceDistinguishesOnAddress(audio_devices_t device) { + return ((device & APM_AUDIO_DEVICE_MATCH_ADDRESS_ALL) != 0); +} + +audio_io_handle_t AudioPolicyManager::getActiveInput(bool ignoreVirtualInputs) +{ + for (size_t i = 0; i < mInputs.size(); i++) { + const sp<AudioInputDescriptor> input_descriptor = mInputs.valueAt(i); + if ((input_descriptor->mRefCount > 0) + && (!ignoreVirtualInputs || !isVirtualInputDevice(input_descriptor->mDevice))) { + return mInputs.keyAt(i); + } + } + return 0; +} + +uint32_t AudioPolicyManager::activeInputsCount() const +{ + uint32_t count = 0; + for (size_t i = 0; i < mInputs.size(); i++) { + const sp<AudioInputDescriptor> desc = mInputs.valueAt(i); + if (desc->mRefCount > 0) { + return count++; + } + } + return count; +} + + +audio_devices_t AudioPolicyManager::getDeviceForVolume(audio_devices_t device) +{ + if (device == AUDIO_DEVICE_NONE) { + // this happens when forcing a route update and no track is active on an output. + // In this case the returned category is not important. + device = AUDIO_DEVICE_OUT_SPEAKER; + } else if (popcount(device) > 1) { + // Multiple device selection is either: + // - speaker + one other device: give priority to speaker in this case. + // - one A2DP device + another device: happens with duplicated output. In this case + // retain the device on the A2DP output as the other must not correspond to an active + // selection if not the speaker. + // - HDMI-CEC system audio mode only output: give priority to available item in order. + if (device & AUDIO_DEVICE_OUT_SPEAKER) { + device = AUDIO_DEVICE_OUT_SPEAKER; + } else if (device & AUDIO_DEVICE_OUT_HDMI_ARC) { + device = AUDIO_DEVICE_OUT_HDMI_ARC; + } else if (device & AUDIO_DEVICE_OUT_AUX_LINE) { + device = AUDIO_DEVICE_OUT_AUX_LINE; + } else if (device & AUDIO_DEVICE_OUT_SPDIF) { + device = AUDIO_DEVICE_OUT_SPDIF; + } else { + device = (audio_devices_t)(device & AUDIO_DEVICE_OUT_ALL_A2DP); + } + } + + /*SPEAKER_SAFE is an alias of SPEAKER for purposes of volume control*/ + if (device == AUDIO_DEVICE_OUT_SPEAKER_SAFE) + device = AUDIO_DEVICE_OUT_SPEAKER; + + ALOGW_IF(popcount(device) != 1, + "getDeviceForVolume() invalid device combination: %08x", + device); + + return device; +} + +AudioPolicyManager::device_category AudioPolicyManager::getDeviceCategory(audio_devices_t device) +{ + switch(getDeviceForVolume(device)) { + case AUDIO_DEVICE_OUT_EARPIECE: + return DEVICE_CATEGORY_EARPIECE; + case AUDIO_DEVICE_OUT_WIRED_HEADSET: + case AUDIO_DEVICE_OUT_WIRED_HEADPHONE: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET: + case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP: + case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES: + return DEVICE_CATEGORY_HEADSET; + case AUDIO_DEVICE_OUT_LINE: + case AUDIO_DEVICE_OUT_AUX_DIGITAL: + /*USB? Remote submix?*/ + return DEVICE_CATEGORY_EXT_MEDIA; + case AUDIO_DEVICE_OUT_SPEAKER: + case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: + case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER: + case AUDIO_DEVICE_OUT_USB_ACCESSORY: + case AUDIO_DEVICE_OUT_USB_DEVICE: + case AUDIO_DEVICE_OUT_REMOTE_SUBMIX: + default: + return DEVICE_CATEGORY_SPEAKER; + } +} + +float AudioPolicyManager::volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc, + int indexInUi) +{ + device_category deviceCategory = getDeviceCategory(device); + const VolumeCurvePoint *curve = streamDesc.mVolumeCurve[deviceCategory]; + + // the volume index in the UI is relative to the min and max volume indices for this stream type + int nbSteps = 1 + curve[VOLMAX].mIndex - + curve[VOLMIN].mIndex; + int volIdx = (nbSteps * (indexInUi - streamDesc.mIndexMin)) / + (streamDesc.mIndexMax - streamDesc.mIndexMin); + + // find what part of the curve this index volume belongs to, or if it's out of bounds + int segment = 0; + if (volIdx < curve[VOLMIN].mIndex) { // out of bounds + return 0.0f; + } else if (volIdx < curve[VOLKNEE1].mIndex) { + segment = 0; + } else if (volIdx < curve[VOLKNEE2].mIndex) { + segment = 1; + } else if (volIdx <= curve[VOLMAX].mIndex) { + segment = 2; + } else { // out of bounds + return 1.0f; + } + + // linear interpolation in the attenuation table in dB + float decibels = curve[segment].mDBAttenuation + + ((float)(volIdx - curve[segment].mIndex)) * + ( (curve[segment+1].mDBAttenuation - + curve[segment].mDBAttenuation) / + ((float)(curve[segment+1].mIndex - + curve[segment].mIndex)) ); + + float amplification = exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 ) + + ALOGVV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f] ampl=%.5f", + curve[segment].mIndex, volIdx, + curve[segment+1].mIndex, + curve[segment].mDBAttenuation, + decibels, + curve[segment+1].mDBAttenuation, + amplification); + + return amplification; +} + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sDefaultVolumeCurve[AudioPolicyManager::VOLCNT] = { + {1, -49.5f}, {33, -33.5f}, {66, -17.0f}, {100, 0.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sDefaultMediaVolumeCurve[AudioPolicyManager::VOLCNT] = { + {1, -58.0f}, {20, -40.0f}, {60, -17.0f}, {100, 0.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sExtMediaSystemVolumeCurve[AudioPolicyManager::VOLCNT] = { + {1, -58.0f}, {20, -40.0f}, {60, -21.0f}, {100, -10.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sSpeakerMediaVolumeCurve[AudioPolicyManager::VOLCNT] = { + {1, -56.0f}, {20, -34.0f}, {60, -11.0f}, {100, 0.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sSpeakerMediaVolumeCurveDrc[AudioPolicyManager::VOLCNT] = { + {1, -55.0f}, {20, -43.0f}, {86, -12.0f}, {100, 0.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sSpeakerSonificationVolumeCurve[AudioPolicyManager::VOLCNT] = { + {1, -29.7f}, {33, -20.1f}, {66, -10.2f}, {100, 0.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sSpeakerSonificationVolumeCurveDrc[AudioPolicyManager::VOLCNT] = { + {1, -35.7f}, {33, -26.1f}, {66, -13.2f}, {100, 0.0f} +}; + +// AUDIO_STREAM_SYSTEM, AUDIO_STREAM_ENFORCED_AUDIBLE and AUDIO_STREAM_DTMF volume tracks +// AUDIO_STREAM_RING on phones and AUDIO_STREAM_MUSIC on tablets. +// AUDIO_STREAM_DTMF tracks AUDIO_STREAM_VOICE_CALL while in call (See AudioService.java). +// The range is constrained between -24dB and -6dB over speaker and -30dB and -18dB over headset. + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sDefaultSystemVolumeCurve[AudioPolicyManager::VOLCNT] = { + {1, -24.0f}, {33, -18.0f}, {66, -12.0f}, {100, -6.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sDefaultSystemVolumeCurveDrc[AudioPolicyManager::VOLCNT] = { + {1, -34.0f}, {33, -24.0f}, {66, -15.0f}, {100, -6.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sHeadsetSystemVolumeCurve[AudioPolicyManager::VOLCNT] = { + {1, -30.0f}, {33, -26.0f}, {66, -22.0f}, {100, -18.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sDefaultVoiceVolumeCurve[AudioPolicyManager::VOLCNT] = { + {0, -42.0f}, {33, -28.0f}, {66, -14.0f}, {100, 0.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint + AudioPolicyManager::sSpeakerVoiceVolumeCurve[AudioPolicyManager::VOLCNT] = { + {0, -24.0f}, {33, -16.0f}, {66, -8.0f}, {100, 0.0f} +}; + +const AudioPolicyManager::VolumeCurvePoint + *AudioPolicyManager::sVolumeProfiles[AUDIO_STREAM_CNT] + [AudioPolicyManager::DEVICE_CATEGORY_CNT] = { + { // AUDIO_STREAM_VOICE_CALL + sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_HEADSET + sSpeakerVoiceVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_EARPIECE + sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_SYSTEM + sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET + sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_EARPIECE + sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_RING + sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET + sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultVolumeCurve, // DEVICE_CATEGORY_EARPIECE + sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_MUSIC + sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_HEADSET + sSpeakerMediaVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_EARPIECE + sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_ALARM + sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET + sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultVolumeCurve, // DEVICE_CATEGORY_EARPIECE + sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_NOTIFICATION + sDefaultVolumeCurve, // DEVICE_CATEGORY_HEADSET + sSpeakerSonificationVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultVolumeCurve, // DEVICE_CATEGORY_EARPIECE + sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_BLUETOOTH_SCO + sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_HEADSET + sSpeakerVoiceVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_EARPIECE + sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_ENFORCED_AUDIBLE + sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET + sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_EARPIECE + sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_DTMF + sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET + sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_EARPIECE + sExtMediaSystemVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, + { // AUDIO_STREAM_TTS + sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_HEADSET + sSpeakerMediaVolumeCurve, // DEVICE_CATEGORY_SPEAKER + sDefaultMediaVolumeCurve, // DEVICE_CATEGORY_EARPIECE + sDefaultMediaVolumeCurve // DEVICE_CATEGORY_EXT_MEDIA + }, +}; + +void AudioPolicyManager::initializeVolumeCurves() +{ + for (int i = 0; i < AUDIO_STREAM_CNT; i++) { + for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) { + mStreams[i].mVolumeCurve[j] = + sVolumeProfiles[i][j]; + } + } + + // Check availability of DRC on speaker path: if available, override some of the speaker curves + if (mSpeakerDrcEnabled) { + mStreams[AUDIO_STREAM_SYSTEM].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = + sDefaultSystemVolumeCurveDrc; + mStreams[AUDIO_STREAM_RING].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = + sSpeakerSonificationVolumeCurveDrc; + mStreams[AUDIO_STREAM_ALARM].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = + sSpeakerSonificationVolumeCurveDrc; + mStreams[AUDIO_STREAM_NOTIFICATION].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = + sSpeakerSonificationVolumeCurveDrc; + mStreams[AUDIO_STREAM_MUSIC].mVolumeCurve[DEVICE_CATEGORY_SPEAKER] = + sSpeakerMediaVolumeCurveDrc; + } +} + +float AudioPolicyManager::computeVolume(audio_stream_type_t stream, + int index, + audio_io_handle_t output, + audio_devices_t device) +{ + float volume = 1.0; + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output); + StreamDescriptor &streamDesc = mStreams[stream]; + + if (device == AUDIO_DEVICE_NONE) { + device = outputDesc->device(); + } + + volume = volIndexToAmpl(device, streamDesc, index); + + // if a headset is connected, apply the following rules to ring tones and notifications + // to avoid sound level bursts in user's ears: + // - always attenuate ring tones and notifications volume by 6dB + // - if music is playing, always limit the volume to current music volume, + // with a minimum threshold at -36dB so that notification is always perceived. + const routing_strategy stream_strategy = getStrategy(stream); + if ((device & (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | + AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | + AUDIO_DEVICE_OUT_WIRED_HEADSET | + AUDIO_DEVICE_OUT_WIRED_HEADPHONE)) && + ((stream_strategy == STRATEGY_SONIFICATION) + || (stream_strategy == STRATEGY_SONIFICATION_RESPECTFUL) + || (stream == AUDIO_STREAM_SYSTEM) + || ((stream_strategy == STRATEGY_ENFORCED_AUDIBLE) && + (mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_NONE))) && + streamDesc.mCanBeMuted) { + volume *= SONIFICATION_HEADSET_VOLUME_FACTOR; + // when the phone is ringing we must consider that music could have been paused just before + // by the music application and behave as if music was active if the last music track was + // just stopped + if (isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY) || + mLimitRingtoneVolume) { + audio_devices_t musicDevice = getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/); + float musicVol = computeVolume(AUDIO_STREAM_MUSIC, + mStreams[AUDIO_STREAM_MUSIC].getVolumeIndex(musicDevice), + output, + musicDevice); + float minVol = (musicVol > SONIFICATION_HEADSET_VOLUME_MIN) ? + musicVol : SONIFICATION_HEADSET_VOLUME_MIN; + if (volume > minVol) { + volume = minVol; + ALOGV("computeVolume limiting volume to %f musicVol %f", minVol, musicVol); + } + } + } + + return volume; +} + +status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream, + int index, + audio_io_handle_t output, + audio_devices_t device, + int delayMs, + bool force) +{ + + // do not change actual stream volume if the stream is muted + if (mOutputs.valueFor(output)->mMuteCount[stream] != 0) { + ALOGVV("checkAndSetVolume() stream %d muted count %d", + stream, mOutputs.valueFor(output)->mMuteCount[stream]); + return NO_ERROR; + } + + // do not change in call volume if bluetooth is connected and vice versa + if ((stream == AUDIO_STREAM_VOICE_CALL && + mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION] == AUDIO_POLICY_FORCE_BT_SCO) || + (stream == AUDIO_STREAM_BLUETOOTH_SCO && + mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION] != AUDIO_POLICY_FORCE_BT_SCO)) { + ALOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm", + stream, mForceUse[AUDIO_POLICY_FORCE_FOR_COMMUNICATION]); + return INVALID_OPERATION; + } + + float volume = computeVolume(stream, index, output, device); + // We actually change the volume if: + // - the float value returned by computeVolume() changed + // - the force flag is set + if (volume != mOutputs.valueFor(output)->mCurVolume[stream] || + force) { + mOutputs.valueFor(output)->mCurVolume[stream] = volume; + ALOGVV("checkAndSetVolume() for output %d stream %d, volume %f, delay %d", output, stream, volume, delayMs); + // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is + // enabled + if (stream == AUDIO_STREAM_BLUETOOTH_SCO) { + mpClientInterface->setStreamVolume(AUDIO_STREAM_VOICE_CALL, volume, output, delayMs); + } + mpClientInterface->setStreamVolume(stream, volume, output, delayMs); + } + + if (stream == AUDIO_STREAM_VOICE_CALL || + stream == AUDIO_STREAM_BLUETOOTH_SCO) { + float voiceVolume; + // Force voice volume to max for bluetooth SCO as volume is managed by the headset + if (stream == AUDIO_STREAM_VOICE_CALL) { + voiceVolume = (float)index/(float)mStreams[stream].mIndexMax; + } else { + voiceVolume = 1.0; + } + + if (voiceVolume != mLastVoiceVolume && output == mPrimaryOutput) { + mpClientInterface->setVoiceVolume(voiceVolume, delayMs); + mLastVoiceVolume = voiceVolume; + } + } + + return NO_ERROR; +} + +void AudioPolicyManager::applyStreamVolumes(audio_io_handle_t output, + audio_devices_t device, + int delayMs, + bool force) +{ + ALOGVV("applyStreamVolumes() for output %d and device %x", output, device); + + for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) { + checkAndSetVolume((audio_stream_type_t)stream, + mStreams[stream].getVolumeIndex(device), + output, + device, + delayMs, + force); + } +} + +void AudioPolicyManager::setStrategyMute(routing_strategy strategy, + bool on, + audio_io_handle_t output, + int delayMs, + audio_devices_t device) +{ + ALOGVV("setStrategyMute() strategy %d, mute %d, output %d", strategy, on, output); + for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) { + if (getStrategy((audio_stream_type_t)stream) == strategy) { + setStreamMute((audio_stream_type_t)stream, on, output, delayMs, device); + } + } +} + +void AudioPolicyManager::setStreamMute(audio_stream_type_t stream, + bool on, + audio_io_handle_t output, + int delayMs, + audio_devices_t device) +{ + StreamDescriptor &streamDesc = mStreams[stream]; + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(output); + if (device == AUDIO_DEVICE_NONE) { + device = outputDesc->device(); + } + + ALOGVV("setStreamMute() stream %d, mute %d, output %d, mMuteCount %d device %04x", + stream, on, output, outputDesc->mMuteCount[stream], device); + + if (on) { + if (outputDesc->mMuteCount[stream] == 0) { + if (streamDesc.mCanBeMuted && + ((stream != AUDIO_STREAM_ENFORCED_AUDIBLE) || + (mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_NONE))) { + checkAndSetVolume(stream, 0, output, device, delayMs); + } + } + // increment mMuteCount after calling checkAndSetVolume() so that volume change is not ignored + outputDesc->mMuteCount[stream]++; + } else { + if (outputDesc->mMuteCount[stream] == 0) { + ALOGV("setStreamMute() unmuting non muted stream!"); + return; + } + if (--outputDesc->mMuteCount[stream] == 0) { + checkAndSetVolume(stream, + streamDesc.getVolumeIndex(device), + output, + device, + delayMs); + } + } +} + +void AudioPolicyManager::handleIncallSonification(audio_stream_type_t stream, + bool starting, bool stateChange) +{ + // if the stream pertains to sonification strategy and we are in call we must + // mute the stream if it is low visibility. If it is high visibility, we must play a tone + // in the device used for phone strategy and play the tone if the selected device does not + // interfere with the device used for phone strategy + // if stateChange is true, we are called from setPhoneState() and we must mute or unmute as + // many times as there are active tracks on the output + const routing_strategy stream_strategy = getStrategy(stream); + if ((stream_strategy == STRATEGY_SONIFICATION) || + ((stream_strategy == STRATEGY_SONIFICATION_RESPECTFUL))) { + sp<AudioOutputDescriptor> outputDesc = mOutputs.valueFor(mPrimaryOutput); + ALOGV("handleIncallSonification() stream %d starting %d device %x stateChange %d", + stream, starting, outputDesc->mDevice, stateChange); + if (outputDesc->mRefCount[stream]) { + int muteCount = 1; + if (stateChange) { + muteCount = outputDesc->mRefCount[stream]; + } + if (audio_is_low_visibility(stream)) { + ALOGV("handleIncallSonification() low visibility, muteCount %d", muteCount); + for (int i = 0; i < muteCount; i++) { + setStreamMute(stream, starting, mPrimaryOutput); + } + } else { + ALOGV("handleIncallSonification() high visibility"); + if (outputDesc->device() & + getDeviceForStrategy(STRATEGY_PHONE, true /*fromCache*/)) { + ALOGV("handleIncallSonification() high visibility muted, muteCount %d", muteCount); + for (int i = 0; i < muteCount; i++) { + setStreamMute(stream, starting, mPrimaryOutput); + } + } + if (starting) { + mpClientInterface->startTone(AUDIO_POLICY_TONE_IN_CALL_NOTIFICATION, + AUDIO_STREAM_VOICE_CALL); + } else { + mpClientInterface->stopTone(); + } + } + } + } +} + +bool AudioPolicyManager::isInCall() +{ + return isStateInCall(mPhoneState); +} + +bool AudioPolicyManager::isStateInCall(int state) { + return ((state == AUDIO_MODE_IN_CALL) || + (state == AUDIO_MODE_IN_COMMUNICATION)); +} + +uint32_t AudioPolicyManager::getMaxEffectsCpuLoad() +{ + return MAX_EFFECTS_CPU_LOAD; +} + +uint32_t AudioPolicyManager::getMaxEffectsMemory() +{ + return MAX_EFFECTS_MEMORY; +} + + +// --- AudioOutputDescriptor class implementation + +AudioPolicyManager::AudioOutputDescriptor::AudioOutputDescriptor( + const sp<IOProfile>& profile) + : mId(0), mIoHandle(0), mLatency(0), + mFlags((audio_output_flags_t)0), mDevice(AUDIO_DEVICE_NONE), mPatchHandle(0), + mOutput1(0), mOutput2(0), mProfile(profile), mDirectOpenCount(0) +{ + // clear usage count for all stream types + for (int i = 0; i < AUDIO_STREAM_CNT; i++) { + mRefCount[i] = 0; + mCurVolume[i] = -1.0; + mMuteCount[i] = 0; + mStopTime[i] = 0; + } + for (int i = 0; i < NUM_STRATEGIES; i++) { + mStrategyMutedByDevice[i] = false; + } + if (profile != NULL) { + mFlags = profile->mFlags; + mSamplingRate = profile->pickSamplingRate(); + mFormat = profile->pickFormat(); + mChannelMask = profile->pickChannelMask(); + if (profile->mGains.size() > 0) { + profile->mGains[0]->getDefaultConfig(&mGain); + } + } +} + +audio_devices_t AudioPolicyManager::AudioOutputDescriptor::device() const +{ + if (isDuplicated()) { + return (audio_devices_t)(mOutput1->mDevice | mOutput2->mDevice); + } else { + return mDevice; + } +} + +uint32_t AudioPolicyManager::AudioOutputDescriptor::latency() +{ + if (isDuplicated()) { + return (mOutput1->mLatency > mOutput2->mLatency) ? mOutput1->mLatency : mOutput2->mLatency; + } else { + return mLatency; + } +} + +bool AudioPolicyManager::AudioOutputDescriptor::sharesHwModuleWith( + const sp<AudioOutputDescriptor> outputDesc) +{ + if (isDuplicated()) { + return mOutput1->sharesHwModuleWith(outputDesc) || mOutput2->sharesHwModuleWith(outputDesc); + } else if (outputDesc->isDuplicated()){ + return sharesHwModuleWith(outputDesc->mOutput1) || sharesHwModuleWith(outputDesc->mOutput2); + } else { + return (mProfile->mModule == outputDesc->mProfile->mModule); + } +} + +void AudioPolicyManager::AudioOutputDescriptor::changeRefCount(audio_stream_type_t stream, + int delta) +{ + // forward usage count change to attached outputs + if (isDuplicated()) { + mOutput1->changeRefCount(stream, delta); + mOutput2->changeRefCount(stream, delta); + } + if ((delta + (int)mRefCount[stream]) < 0) { + ALOGW("changeRefCount() invalid delta %d for stream %d, refCount %d", + delta, stream, mRefCount[stream]); + mRefCount[stream] = 0; + return; + } + mRefCount[stream] += delta; + ALOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]); +} + +audio_devices_t AudioPolicyManager::AudioOutputDescriptor::supportedDevices() +{ + if (isDuplicated()) { + return (audio_devices_t)(mOutput1->supportedDevices() | mOutput2->supportedDevices()); + } else { + return mProfile->mSupportedDevices.types() ; + } +} + +bool AudioPolicyManager::AudioOutputDescriptor::isActive(uint32_t inPastMs) const +{ + return isStrategyActive(NUM_STRATEGIES, inPastMs); +} + +bool AudioPolicyManager::AudioOutputDescriptor::isStrategyActive(routing_strategy strategy, + uint32_t inPastMs, + nsecs_t sysTime) const +{ + if ((sysTime == 0) && (inPastMs != 0)) { + sysTime = systemTime(); + } + for (int i = 0; i < (int)AUDIO_STREAM_CNT; i++) { + if (((getStrategy((audio_stream_type_t)i) == strategy) || + (NUM_STRATEGIES == strategy)) && + isStreamActive((audio_stream_type_t)i, inPastMs, sysTime)) { + return true; + } + } + return false; +} + +bool AudioPolicyManager::AudioOutputDescriptor::isStreamActive(audio_stream_type_t stream, + uint32_t inPastMs, + nsecs_t sysTime) const +{ + if (mRefCount[stream] != 0) { + return true; + } + if (inPastMs == 0) { + return false; + } + if (sysTime == 0) { + sysTime = systemTime(); + } + if (ns2ms(sysTime - mStopTime[stream]) < inPastMs) { + return true; + } + return false; +} + +void AudioPolicyManager::AudioOutputDescriptor::toAudioPortConfig( + struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig) const +{ + ALOG_ASSERT(!isDuplicated(), "toAudioPortConfig() called on duplicated output %d", mIoHandle); + + dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK| + AUDIO_PORT_CONFIG_FORMAT|AUDIO_PORT_CONFIG_GAIN; + if (srcConfig != NULL) { + dstConfig->config_mask |= srcConfig->config_mask; + } + AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig); + + dstConfig->id = mId; + dstConfig->role = AUDIO_PORT_ROLE_SOURCE; + dstConfig->type = AUDIO_PORT_TYPE_MIX; + 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( + struct audio_port *port) const +{ + ALOG_ASSERT(!isDuplicated(), "toAudioPort() called on duplicated output %d", mIoHandle); + 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; +} + +status_t AudioPolicyManager::AudioOutputDescriptor::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " ID: %d\n", mId); + result.append(buffer); + snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate); + result.append(buffer); + snprintf(buffer, SIZE, " Format: %08x\n", mFormat); + result.append(buffer); + snprintf(buffer, SIZE, " Channels: %08x\n", mChannelMask); + result.append(buffer); + snprintf(buffer, SIZE, " Latency: %d\n", mLatency); + result.append(buffer); + snprintf(buffer, SIZE, " Flags %08x\n", mFlags); + result.append(buffer); + snprintf(buffer, SIZE, " Devices %08x\n", device()); + result.append(buffer); + snprintf(buffer, SIZE, " Stream volume refCount muteCount\n"); + result.append(buffer); + for (int i = 0; i < (int)AUDIO_STREAM_CNT; i++) { + snprintf(buffer, SIZE, " %02d %.03f %02d %02d\n", + i, mCurVolume[i], mRefCount[i], mMuteCount[i]); + result.append(buffer); + } + write(fd, result.string(), result.size()); + + return NO_ERROR; +} + +// --- AudioInputDescriptor class implementation + +AudioPolicyManager::AudioInputDescriptor::AudioInputDescriptor(const sp<IOProfile>& profile) + : mId(0), mIoHandle(0), + mDevice(AUDIO_DEVICE_NONE), mPatchHandle(0), mRefCount(0), + mInputSource(AUDIO_SOURCE_DEFAULT), mProfile(profile), mIsSoundTrigger(false) +{ + if (profile != NULL) { + mSamplingRate = profile->pickSamplingRate(); + mFormat = profile->pickFormat(); + mChannelMask = profile->pickChannelMask(); + if (profile->mGains.size() > 0) { + profile->mGains[0]->getDefaultConfig(&mGain); + } + } +} + +void AudioPolicyManager::AudioInputDescriptor::toAudioPortConfig( + struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig) const +{ + ALOG_ASSERT(mProfile != 0, + "toAudioPortConfig() called on input with null profile %d", mIoHandle); + dstConfig->config_mask = AUDIO_PORT_CONFIG_SAMPLE_RATE|AUDIO_PORT_CONFIG_CHANNEL_MASK| + AUDIO_PORT_CONFIG_FORMAT|AUDIO_PORT_CONFIG_GAIN; + if (srcConfig != NULL) { + dstConfig->config_mask |= srcConfig->config_mask; + } + + AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig); + + dstConfig->id = mId; + dstConfig->role = AUDIO_PORT_ROLE_SINK; + dstConfig->type = AUDIO_PORT_TYPE_MIX; + dstConfig->ext.mix.hw_module = mProfile->mModule->mHandle; + dstConfig->ext.mix.handle = mIoHandle; + dstConfig->ext.mix.usecase.source = mInputSource; +} + +void AudioPolicyManager::AudioInputDescriptor::toAudioPort( + struct audio_port *port) const +{ + ALOG_ASSERT(mProfile != 0, "toAudioPort() called on input with null profile %d", mIoHandle); + + 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; +} + +status_t AudioPolicyManager::AudioInputDescriptor::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " ID: %d\n", mId); + result.append(buffer); + snprintf(buffer, SIZE, " Sampling rate: %d\n", mSamplingRate); + result.append(buffer); + snprintf(buffer, SIZE, " Format: %d\n", mFormat); + result.append(buffer); + snprintf(buffer, SIZE, " Channels: %08x\n", mChannelMask); + result.append(buffer); + snprintf(buffer, SIZE, " Devices %08x\n", mDevice); + result.append(buffer); + snprintf(buffer, SIZE, " Ref Count %d\n", mRefCount); + result.append(buffer); + snprintf(buffer, SIZE, " Open Ref Count %d\n", mOpenRefCount); + result.append(buffer); + + write(fd, result.string(), result.size()); + + return NO_ERROR; +} + +// --- StreamDescriptor class implementation + +AudioPolicyManager::StreamDescriptor::StreamDescriptor() + : mIndexMin(0), mIndexMax(1), mCanBeMuted(true) +{ + mIndexCur.add(AUDIO_DEVICE_OUT_DEFAULT, 0); +} + +int AudioPolicyManager::StreamDescriptor::getVolumeIndex(audio_devices_t device) +{ + device = AudioPolicyManager::getDeviceForVolume(device); + // there is always a valid entry for AUDIO_DEVICE_OUT_DEFAULT + if (mIndexCur.indexOfKey(device) < 0) { + device = AUDIO_DEVICE_OUT_DEFAULT; + } + return mIndexCur.valueFor(device); +} + +void AudioPolicyManager::StreamDescriptor::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "%s %02d %02d ", + mCanBeMuted ? "true " : "false", mIndexMin, mIndexMax); + result.append(buffer); + for (size_t i = 0; i < mIndexCur.size(); i++) { + snprintf(buffer, SIZE, "%04x : %02d, ", + mIndexCur.keyAt(i), + mIndexCur.valueAt(i)); + result.append(buffer); + } + result.append("\n"); + + write(fd, result.string(), result.size()); +} + +// --- EffectDescriptor class implementation + +status_t AudioPolicyManager::EffectDescriptor::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " I/O: %d\n", mIo); + result.append(buffer); + snprintf(buffer, SIZE, " Strategy: %d\n", mStrategy); + result.append(buffer); + snprintf(buffer, SIZE, " Session: %d\n", mSession); + result.append(buffer); + snprintf(buffer, SIZE, " Name: %s\n", mDesc.name); + result.append(buffer); + snprintf(buffer, SIZE, " %s\n", mEnabled ? "Enabled" : "Disabled"); + result.append(buffer); + write(fd, result.string(), result.size()); + + return NO_ERROR; +} + +// --- HwModule class implementation + +AudioPolicyManager::HwModule::HwModule(const char *name) + : mName(strndup(name, AUDIO_HARDWARE_MODULE_ID_MAX_LEN)), + mHalVersion(AUDIO_DEVICE_API_VERSION_MIN), mHandle(0) +{ +} + +AudioPolicyManager::HwModule::~HwModule() +{ + for (size_t i = 0; i < mOutputProfiles.size(); i++) { + mOutputProfiles[i]->mSupportedDevices.clear(); + } + for (size_t i = 0; i < mInputProfiles.size(); i++) { + mInputProfiles[i]->mSupportedDevices.clear(); + } + 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; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, " - name: %s\n", mName); + result.append(buffer); + snprintf(buffer, SIZE, " - handle: %d\n", mHandle); + result.append(buffer); + snprintf(buffer, SIZE, " - version: %u.%u\n", mHalVersion >> 8, mHalVersion & 0xFF); + result.append(buffer); + write(fd, result.string(), result.size()); + if (mOutputProfiles.size()) { + write(fd, " - outputs:\n", strlen(" - outputs:\n")); + for (size_t i = 0; i < mOutputProfiles.size(); i++) { + snprintf(buffer, SIZE, " output %zu:\n", i); + write(fd, buffer, strlen(buffer)); + mOutputProfiles[i]->dump(fd); + } + } + if (mInputProfiles.size()) { + write(fd, " - inputs:\n", strlen(" - inputs:\n")); + for (size_t i = 0; i < mInputProfiles.size(); i++) { + snprintf(buffer, SIZE, " input %zu:\n", i); + write(fd, buffer, strlen(buffer)); + 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 + + +AudioPolicyManager::AudioPort::AudioPort(const String8& name, audio_port_type_t type, + audio_port_role_t role, const sp<HwModule>& module) : + mName(name), mType(type), mRole(role), mModule(module), mFlags((audio_output_flags_t)0) +{ + mUseInChannelMask = ((type == AUDIO_PORT_TYPE_DEVICE) && (role == AUDIO_PORT_ROLE_SOURCE)) || + ((type == AUDIO_PORT_TYPE_MIX) && (role == AUDIO_PORT_ROLE_SINK)); +} + +void AudioPolicyManager::AudioPort::toAudioPort(struct audio_port *port) const +{ + port->role = mRole; + port->type = mType; + unsigned int i; + for (i = 0; i < mSamplingRates.size() && i < AUDIO_PORT_MAX_SAMPLING_RATES; i++) { + if (mSamplingRates[i] != 0) { + port->sample_rates[i] = mSamplingRates[i]; + } + } + port->num_sample_rates = i; + for (i = 0; i < mChannelMasks.size() && i < AUDIO_PORT_MAX_CHANNEL_MASKS; i++) { + if (mChannelMasks[i] != 0) { + port->channel_masks[i] = mChannelMasks[i]; + } + } + port->num_channel_masks = i; + for (i = 0; i < mFormats.size() && i < AUDIO_PORT_MAX_FORMATS; i++) { + if (mFormats[i] != 0) { + port->formats[i] = mFormats[i]; + } + } + port->num_formats = i; + + ALOGV("AudioPort::toAudioPort() num gains %zu", mGains.size()); + + for (i = 0; i < mGains.size() && i < AUDIO_PORT_MAX_GAINS; i++) { + port->gains[i] = mGains[i]->mGain; + } + port->num_gains = i; +} + +void AudioPolicyManager::AudioPort::importAudioPort(const sp<AudioPort> port) { + for (size_t k = 0 ; k < port->mSamplingRates.size() ; k++) { + const uint32_t rate = port->mSamplingRates.itemAt(k); + if (rate != 0) { // skip "dynamic" rates + bool hasRate = false; + for (size_t l = 0 ; l < mSamplingRates.size() ; l++) { + if (rate == mSamplingRates.itemAt(l)) { + hasRate = true; + break; + } + } + if (!hasRate) { // never import a sampling rate twice + mSamplingRates.add(rate); + } + } + } + for (size_t k = 0 ; k < port->mChannelMasks.size() ; k++) { + const audio_channel_mask_t mask = port->mChannelMasks.itemAt(k); + if (mask != 0) { // skip "dynamic" masks + bool hasMask = false; + for (size_t l = 0 ; l < mChannelMasks.size() ; l++) { + if (mask == mChannelMasks.itemAt(l)) { + hasMask = true; + break; + } + } + if (!hasMask) { // never import a channel mask twice + mChannelMasks.add(mask); + } + } + } + for (size_t k = 0 ; k < port->mFormats.size() ; k++) { + const audio_format_t format = port->mFormats.itemAt(k); + if (format != 0) { // skip "dynamic" formats + bool hasFormat = false; + for (size_t l = 0 ; l < mFormats.size() ; l++) { + if (format == mFormats.itemAt(l)) { + hasFormat = true; + break; + } + } + if (!hasFormat) { // never import a channel mask twice + mFormats.add(format); + } + } + } +} + +void AudioPolicyManager::AudioPort::clearCapabilities() { + mChannelMasks.clear(); + mFormats.clear(); + mSamplingRates.clear(); +} + +void AudioPolicyManager::AudioPort::loadSamplingRates(char *name) +{ + char *str = strtok(name, "|"); + + // by convention, "0' in the first entry in mSamplingRates indicates the supported sampling + // rates should be read from the output stream after it is opened for the first time + if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { + mSamplingRates.add(0); + return; + } + + while (str != NULL) { + uint32_t rate = atoi(str); + if (rate != 0) { + ALOGV("loadSamplingRates() adding rate %d", rate); + mSamplingRates.add(rate); + } + str = strtok(NULL, "|"); + } +} + +void AudioPolicyManager::AudioPort::loadFormats(char *name) +{ + char *str = strtok(name, "|"); + + // by convention, "0' in the first entry in mFormats indicates the supported formats + // should be read from the output stream after it is opened for the first time + if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { + mFormats.add(AUDIO_FORMAT_DEFAULT); + return; + } + + while (str != NULL) { + audio_format_t format = (audio_format_t)stringToEnum(sFormatNameToEnumTable, + ARRAY_SIZE(sFormatNameToEnumTable), + str); + if (format != AUDIO_FORMAT_DEFAULT) { + mFormats.add(format); + } + str = strtok(NULL, "|"); + } +} + +void AudioPolicyManager::AudioPort::loadInChannels(char *name) +{ + const char *str = strtok(name, "|"); + + ALOGV("loadInChannels() %s", name); + + if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { + mChannelMasks.add(0); + return; + } + + while (str != NULL) { + audio_channel_mask_t channelMask = + (audio_channel_mask_t)stringToEnum(sInChannelsNameToEnumTable, + ARRAY_SIZE(sInChannelsNameToEnumTable), + str); + if (channelMask != 0) { + ALOGV("loadInChannels() adding channelMask %04x", channelMask); + mChannelMasks.add(channelMask); + } + str = strtok(NULL, "|"); + } +} + +void AudioPolicyManager::AudioPort::loadOutChannels(char *name) +{ + const char *str = strtok(name, "|"); + + ALOGV("loadOutChannels() %s", name); + + // by convention, "0' in the first entry in mChannelMasks indicates the supported channel + // masks should be read from the output stream after it is opened for the first time + if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { + mChannelMasks.add(0); + return; + } + + while (str != NULL) { + audio_channel_mask_t channelMask = + (audio_channel_mask_t)stringToEnum(sOutChannelsNameToEnumTable, + ARRAY_SIZE(sOutChannelsNameToEnumTable), + str); + if (channelMask != 0) { + mChannelMasks.add(channelMask); + } + str = strtok(NULL, "|"); + } + 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, int index) +{ + cnode *node = root->first_child; + + sp<AudioGain> gain = new AudioGain(index, mUseInChannelMask); + + 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 (mUseInChannelMask) { + 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; + int index = 0; + while (node) { + ALOGV("loadGains() loading gain %s", node->name); + loadGain(node, index++); + node = node->next; + } +} + +status_t AudioPolicyManager::AudioPort::checkExactSamplingRate(uint32_t samplingRate) const +{ + for (size_t i = 0; i < mSamplingRates.size(); i ++) { + if (mSamplingRates[i] == samplingRate) { + return NO_ERROR; + } + } + return BAD_VALUE; +} + +status_t AudioPolicyManager::AudioPort::checkCompatibleSamplingRate(uint32_t samplingRate, + uint32_t *updatedSamplingRate) const +{ + // Search for the closest supported sampling rate that is above (preferred) + // or below (acceptable) the desired sampling rate, within a permitted ratio. + // The sampling rates do not need to be sorted in ascending order. + ssize_t maxBelow = -1; + ssize_t minAbove = -1; + uint32_t candidate; + for (size_t i = 0; i < mSamplingRates.size(); i++) { + candidate = mSamplingRates[i]; + if (candidate == samplingRate) { + if (updatedSamplingRate != NULL) { + *updatedSamplingRate = candidate; + } + return NO_ERROR; + } + // candidate < desired + if (candidate < samplingRate) { + if (maxBelow < 0 || candidate > mSamplingRates[maxBelow]) { + maxBelow = i; + } + // candidate > desired + } else { + if (minAbove < 0 || candidate < mSamplingRates[minAbove]) { + minAbove = i; + } + } + } + // This uses hard-coded knowledge about AudioFlinger resampling ratios. + // TODO Move these assumptions out. + static const uint32_t kMaxDownSampleRatio = 6; // beyond this aliasing occurs + static const uint32_t kMaxUpSampleRatio = 256; // beyond this sample rate inaccuracies occur + // due to approximation by an int32_t of the + // phase increments + // Prefer to down-sample from a higher sampling rate, as we get the desired frequency spectrum. + if (minAbove >= 0) { + candidate = mSamplingRates[minAbove]; + if (candidate / kMaxDownSampleRatio <= samplingRate) { + if (updatedSamplingRate != NULL) { + *updatedSamplingRate = candidate; + } + return NO_ERROR; + } + } + // But if we have to up-sample from a lower sampling rate, that's OK. + if (maxBelow >= 0) { + candidate = mSamplingRates[maxBelow]; + if (candidate * kMaxUpSampleRatio >= samplingRate) { + if (updatedSamplingRate != NULL) { + *updatedSamplingRate = candidate; + } + return NO_ERROR; + } + } + // leave updatedSamplingRate unmodified + return BAD_VALUE; +} + +status_t AudioPolicyManager::AudioPort::checkExactChannelMask(audio_channel_mask_t channelMask) const +{ + for (size_t i = 0; i < mChannelMasks.size(); i++) { + if (mChannelMasks[i] == channelMask) { + return NO_ERROR; + } + } + return BAD_VALUE; +} + +status_t AudioPolicyManager::AudioPort::checkCompatibleChannelMask(audio_channel_mask_t channelMask) + const +{ + const bool isRecordThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK; + for (size_t i = 0; i < mChannelMasks.size(); i ++) { + // FIXME Does not handle multi-channel automatic conversions yet + audio_channel_mask_t supported = mChannelMasks[i]; + if (supported == channelMask) { + return NO_ERROR; + } + if (isRecordThread) { + // This uses hard-coded knowledge that AudioFlinger can silently down-mix and up-mix. + // FIXME Abstract this out to a table. + if (((supported == AUDIO_CHANNEL_IN_FRONT_BACK || supported == AUDIO_CHANNEL_IN_STEREO) + && channelMask == AUDIO_CHANNEL_IN_MONO) || + (supported == AUDIO_CHANNEL_IN_MONO && (channelMask == AUDIO_CHANNEL_IN_FRONT_BACK + || channelMask == AUDIO_CHANNEL_IN_STEREO))) { + return NO_ERROR; + } + } + } + return BAD_VALUE; +} + +status_t AudioPolicyManager::AudioPort::checkFormat(audio_format_t format) const +{ + for (size_t i = 0; i < mFormats.size(); i ++) { + if (mFormats[i] == format) { + return NO_ERROR; + } + } + return BAD_VALUE; +} + + +uint32_t AudioPolicyManager::AudioPort::pickSamplingRate() const +{ + // special case for uninitialized dynamic profile + if (mSamplingRates.size() == 1 && mSamplingRates[0] == 0) { + return 0; + } + + // For direct outputs, pick minimum sampling rate: this helps ensuring that the + // channel count / sampling rate combination chosen will be supported by the connected + // sink + if ((mType == AUDIO_PORT_TYPE_MIX) && (mRole == AUDIO_PORT_ROLE_SOURCE) && + (mFlags & (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD))) { + uint32_t samplingRate = UINT_MAX; + for (size_t i = 0; i < mSamplingRates.size(); i ++) { + if ((mSamplingRates[i] < samplingRate) && (mSamplingRates[i] > 0)) { + samplingRate = mSamplingRates[i]; + } + } + return (samplingRate == UINT_MAX) ? 0 : samplingRate; + } + + uint32_t samplingRate = 0; + uint32_t maxRate = MAX_MIXER_SAMPLING_RATE; + + // For mixed output and inputs, use max mixer sampling rates. Do not + // limit sampling rate otherwise + if (mType != AUDIO_PORT_TYPE_MIX) { + maxRate = UINT_MAX; + } + for (size_t i = 0; i < mSamplingRates.size(); i ++) { + if ((mSamplingRates[i] > samplingRate) && (mSamplingRates[i] <= maxRate)) { + samplingRate = mSamplingRates[i]; + } + } + return samplingRate; +} + +audio_channel_mask_t AudioPolicyManager::AudioPort::pickChannelMask() const +{ + // special case for uninitialized dynamic profile + if (mChannelMasks.size() == 1 && mChannelMasks[0] == 0) { + return AUDIO_CHANNEL_NONE; + } + audio_channel_mask_t channelMask = AUDIO_CHANNEL_NONE; + + // For direct outputs, pick minimum channel count: this helps ensuring that the + // channel count / sampling rate combination chosen will be supported by the connected + // sink + if ((mType == AUDIO_PORT_TYPE_MIX) && (mRole == AUDIO_PORT_ROLE_SOURCE) && + (mFlags & (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD))) { + uint32_t channelCount = UINT_MAX; + for (size_t i = 0; i < mChannelMasks.size(); i ++) { + uint32_t cnlCount; + if (mUseInChannelMask) { + cnlCount = audio_channel_count_from_in_mask(mChannelMasks[i]); + } else { + cnlCount = audio_channel_count_from_out_mask(mChannelMasks[i]); + } + if ((cnlCount < channelCount) && (cnlCount > 0)) { + channelMask = mChannelMasks[i]; + channelCount = cnlCount; + } + } + return channelMask; + } + + uint32_t channelCount = 0; + uint32_t maxCount = MAX_MIXER_CHANNEL_COUNT; + + // For mixed output and inputs, use max mixer channel count. Do not + // limit channel count otherwise + if (mType != AUDIO_PORT_TYPE_MIX) { + maxCount = UINT_MAX; + } + for (size_t i = 0; i < mChannelMasks.size(); i ++) { + uint32_t cnlCount; + if (mUseInChannelMask) { + cnlCount = audio_channel_count_from_in_mask(mChannelMasks[i]); + } else { + cnlCount = audio_channel_count_from_out_mask(mChannelMasks[i]); + } + if ((cnlCount > channelCount) && (cnlCount <= maxCount)) { + channelMask = mChannelMasks[i]; + channelCount = cnlCount; + } + } + return channelMask; +} + +/* format in order of increasing preference */ +const audio_format_t AudioPolicyManager::AudioPort::sPcmFormatCompareTable[] = { + AUDIO_FORMAT_DEFAULT, + AUDIO_FORMAT_PCM_16_BIT, + AUDIO_FORMAT_PCM_8_24_BIT, + AUDIO_FORMAT_PCM_24_BIT_PACKED, + AUDIO_FORMAT_PCM_32_BIT, + AUDIO_FORMAT_PCM_FLOAT, +}; + +int AudioPolicyManager::AudioPort::compareFormats(audio_format_t format1, + audio_format_t format2) +{ + // NOTE: AUDIO_FORMAT_INVALID is also considered not PCM and will be compared equal to any + // compressed format and better than any PCM format. This is by design of pickFormat() + if (!audio_is_linear_pcm(format1)) { + if (!audio_is_linear_pcm(format2)) { + return 0; + } + return 1; + } + if (!audio_is_linear_pcm(format2)) { + return -1; + } + + int index1 = -1, index2 = -1; + for (size_t i = 0; + (i < ARRAY_SIZE(sPcmFormatCompareTable)) && ((index1 == -1) || (index2 == -1)); + i ++) { + if (sPcmFormatCompareTable[i] == format1) { + index1 = i; + } + if (sPcmFormatCompareTable[i] == format2) { + index2 = i; + } + } + // format1 not found => index1 < 0 => format2 > format1 + // format2 not found => index2 < 0 => format2 < format1 + return index1 - index2; +} + +audio_format_t AudioPolicyManager::AudioPort::pickFormat() const +{ + // special case for uninitialized dynamic profile + if (mFormats.size() == 1 && mFormats[0] == 0) { + return AUDIO_FORMAT_DEFAULT; + } + + audio_format_t format = AUDIO_FORMAT_DEFAULT; + audio_format_t bestFormat = + AudioPolicyManager::AudioPort::sPcmFormatCompareTable[ + ARRAY_SIZE(AudioPolicyManager::AudioPort::sPcmFormatCompareTable) - 1]; + // For mixed output and inputs, use best mixer output format. Do not + // limit format otherwise + if ((mType != AUDIO_PORT_TYPE_MIX) || + ((mRole == AUDIO_PORT_ROLE_SOURCE) && + (((mFlags & (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)) != 0)))) { + bestFormat = AUDIO_FORMAT_INVALID; + } + + for (size_t i = 0; i < mFormats.size(); i ++) { + if ((compareFormats(mFormats[i], format) > 0) && + (compareFormats(mFormats[i], bestFormat) <= 0)) { + format = mFormats[i]; + } + } + return format; +} + +status_t AudioPolicyManager::AudioPort::checkGain(const struct audio_gain_config *gainConfig, + int index) const +{ + if (index < 0 || (size_t)index >= mGains.size()) { + return BAD_VALUE; + } + return mGains[index]->checkConfig(gainConfig); +} + +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++) { + if (i == 0 && mSamplingRates[i] == 0) { + snprintf(buffer, SIZE, "Dynamic"); + } else { + 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++) { + ALOGV("AudioPort::dump mChannelMasks %zu %08x", i, mChannelMasks[i]); + + if (i == 0 && mChannelMasks[i] == 0) { + snprintf(buffer, SIZE, "Dynamic"); + } else { + 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++) { + const char *formatStr = enumToString(sFormatNameToEnumTable, + ARRAY_SIZE(sFormatNameToEnumTable), + mFormats[i]); + if (i == 0 && strcmp(formatStr, "") == 0) { + snprintf(buffer, SIZE, "Dynamic"); + } else { + snprintf(buffer, SIZE, "%s", formatStr); + } + 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(int index, bool useInChannelMask) +{ + mIndex = index; + mUseInChannelMask = useInChannelMask; + memset(&mGain, 0, sizeof(struct audio_gain)); +} + +void AudioPolicyManager::AudioGain::getDefaultConfig(struct audio_gain_config *config) +{ + config->index = mIndex; + config->mode = mGain.mode; + config->channel_mask = mGain.channel_mask; + if ((mGain.mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) { + config->values[0] = mGain.default_value; + } else { + uint32_t numValues; + if (mUseInChannelMask) { + numValues = audio_channel_count_from_in_mask(mGain.channel_mask); + } else { + numValues = audio_channel_count_from_out_mask(mGain.channel_mask); + } + for (size_t i = 0; i < numValues; i++) { + config->values[i] = mGain.default_value; + } + } + if ((mGain.mode & AUDIO_GAIN_MODE_RAMP) == AUDIO_GAIN_MODE_RAMP) { + config->ramp_duration_ms = mGain.min_ramp_ms; + } +} + +status_t AudioPolicyManager::AudioGain::checkConfig(const struct audio_gain_config *config) +{ + if ((config->mode & ~mGain.mode) != 0) { + return BAD_VALUE; + } + if ((config->mode & AUDIO_GAIN_MODE_JOINT) == AUDIO_GAIN_MODE_JOINT) { + if ((config->values[0] < mGain.min_value) || + (config->values[0] > mGain.max_value)) { + return BAD_VALUE; + } + } else { + if ((config->channel_mask & ~mGain.channel_mask) != 0) { + return BAD_VALUE; + } + uint32_t numValues; + if (mUseInChannelMask) { + numValues = audio_channel_count_from_in_mask(config->channel_mask); + } else { + numValues = audio_channel_count_from_out_mask(config->channel_mask); + } + for (size_t i = 0; i < numValues; i++) { + if ((config->values[i] < mGain.min_value) || + (config->values[i] > mGain.max_value)) { + return BAD_VALUE; + } + } + } + if ((config->mode & AUDIO_GAIN_MODE_RAMP) == AUDIO_GAIN_MODE_RAMP) { + if ((config->ramp_duration_ms < mGain.min_ramp_ms) || + (config->ramp_duration_ms > mGain.max_ramp_ms)) { + return BAD_VALUE; + } + } + return NO_ERROR; +} + +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()); +} + +// --- AudioPortConfig class implementation + +AudioPolicyManager::AudioPortConfig::AudioPortConfig() +{ + mSamplingRate = 0; + mChannelMask = AUDIO_CHANNEL_NONE; + mFormat = AUDIO_FORMAT_INVALID; + mGain.index = -1; +} + +status_t AudioPolicyManager::AudioPortConfig::applyAudioPortConfig( + const struct audio_port_config *config, + struct audio_port_config *backupConfig) +{ + struct audio_port_config localBackupConfig; + status_t status = NO_ERROR; + + localBackupConfig.config_mask = config->config_mask; + toAudioPortConfig(&localBackupConfig); + + sp<AudioPort> audioport = getAudioPort(); + if (audioport == 0) { + status = NO_INIT; + goto exit; + } + if (config->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { + status = audioport->checkExactSamplingRate(config->sample_rate); + if (status != NO_ERROR) { + goto exit; + } + mSamplingRate = config->sample_rate; + } + if (config->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { + status = audioport->checkExactChannelMask(config->channel_mask); + if (status != NO_ERROR) { + goto exit; + } + mChannelMask = config->channel_mask; + } + if (config->config_mask & AUDIO_PORT_CONFIG_FORMAT) { + status = audioport->checkFormat(config->format); + if (status != NO_ERROR) { + goto exit; + } + mFormat = config->format; + } + if (config->config_mask & AUDIO_PORT_CONFIG_GAIN) { + status = audioport->checkGain(&config->gain, config->gain.index); + if (status != NO_ERROR) { + goto exit; + } + mGain = config->gain; + } + +exit: + if (status != NO_ERROR) { + applyAudioPortConfig(&localBackupConfig); + } + if (backupConfig != NULL) { + *backupConfig = localBackupConfig; + } + return status; +} + +void AudioPolicyManager::AudioPortConfig::toAudioPortConfig( + struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig) const +{ + if (dstConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { + dstConfig->sample_rate = mSamplingRate; + if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE)) { + dstConfig->sample_rate = srcConfig->sample_rate; + } + } else { + dstConfig->sample_rate = 0; + } + if (dstConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { + dstConfig->channel_mask = mChannelMask; + if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK)) { + dstConfig->channel_mask = srcConfig->channel_mask; + } + } else { + dstConfig->channel_mask = AUDIO_CHANNEL_NONE; + } + if (dstConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT) { + dstConfig->format = mFormat; + if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT)) { + dstConfig->format = srcConfig->format; + } + } else { + dstConfig->format = AUDIO_FORMAT_INVALID; + } + if (dstConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) { + dstConfig->gain = mGain; + if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_GAIN)) { + dstConfig->gain = srcConfig->gain; + } + } else { + dstConfig->gain.index = -1; + } + if (dstConfig->gain.index != -1) { + dstConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN; + } else { + dstConfig->config_mask &= ~AUDIO_PORT_CONFIG_GAIN; + } +} + +// --- IOProfile class implementation + +AudioPolicyManager::IOProfile::IOProfile(const String8& name, audio_port_role_t role, + const sp<HwModule>& module) + : AudioPort(name, AUDIO_PORT_TYPE_MIX, role, module) +{ +} + +AudioPolicyManager::IOProfile::~IOProfile() +{ +} + +// checks if the IO profile is compatible with specified parameters. +// Sampling rate, format and channel mask must be specified in order to +// get a valid a match +bool AudioPolicyManager::IOProfile::isCompatibleProfile(audio_devices_t device, + uint32_t samplingRate, + uint32_t *updatedSamplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags) const +{ + const bool isPlaybackThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SOURCE; + const bool isRecordThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK; + ALOG_ASSERT(isPlaybackThread != isRecordThread); + + if ((mSupportedDevices.types() & device) != device) { + return false; + } + + if (samplingRate == 0) { + return false; + } + uint32_t myUpdatedSamplingRate = samplingRate; + if (isPlaybackThread && checkExactSamplingRate(samplingRate) != NO_ERROR) { + return false; + } + if (isRecordThread && checkCompatibleSamplingRate(samplingRate, &myUpdatedSamplingRate) != + NO_ERROR) { + return false; + } + + if (!audio_is_valid_format(format) || checkFormat(format) != NO_ERROR) { + return false; + } + + if (isPlaybackThread && (!audio_is_output_channel(channelMask) || + checkExactChannelMask(channelMask) != NO_ERROR)) { + return false; + } + if (isRecordThread && (!audio_is_input_channel(channelMask) || + checkCompatibleChannelMask(channelMask) != NO_ERROR)) { + return false; + } + + if (isPlaybackThread && (mFlags & flags) != flags) { + return false; + } + // The only input flag that is allowed to be different is the fast flag. + // An existing fast stream is compatible with a normal track request. + // An existing normal stream is compatible with a fast track request, + // but the fast request will be denied by AudioFlinger and converted to normal track. + if (isRecordThread && (((audio_input_flags_t) mFlags ^ (audio_input_flags_t) flags) & + ~AUDIO_INPUT_FLAG_FAST)) { + return false; + } + + if (updatedSamplingRate != NULL) { + *updatedSamplingRate = myUpdatedSamplingRate; + } + return true; +} + +void AudioPolicyManager::IOProfile::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + AudioPort::dump(fd, 4); + + snprintf(buffer, SIZE, " - flags: 0x%04x\n", mFlags); + result.append(buffer); + snprintf(buffer, SIZE, " - devices:\n"); + result.append(buffer); + write(fd, result.string(), result.size()); + for (size_t i = 0; i < mSupportedDevices.size(); i++) { + mSupportedDevices[i]->dump(fd, 6, i); + } +} + +void AudioPolicyManager::IOProfile::log() +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + ALOGV(" - sampling rates: "); + for (size_t i = 0; i < mSamplingRates.size(); i++) { + ALOGV(" %d", mSamplingRates[i]); + } + + ALOGV(" - channel masks: "); + for (size_t i = 0; i < mChannelMasks.size(); i++) { + ALOGV(" 0x%04x", mChannelMasks[i]); + } + + ALOGV(" - formats: "); + for (size_t i = 0; i < mFormats.size(); i++) { + ALOGV(" 0x%08x", mFormats[i]); + } + + ALOGV(" - devices: 0x%04x\n", mSupportedDevices.types()); + ALOGV(" - flags: 0x%04x\n", mFlags); +} + + +// --- DeviceDescriptor implementation + + +AudioPolicyManager::DeviceDescriptor::DeviceDescriptor(const 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), + mDeviceType(type), mAddress(""), mId(0) +{ + if (mGains.size() > 0) { + mGains[0]->getDefaultConfig(&mGain); + } +} + +bool AudioPolicyManager::DeviceDescriptor::equals(const sp<DeviceDescriptor>& other) const +{ + // Devices are considered equal if they: + // - are of the same type (a device type cannot be AUDIO_DEVICE_NONE) + // - have the same address or one device does not specify the address + // - have the same channel mask or one device does not specify the channel mask + return (mDeviceType == other->mDeviceType) && + (mAddress == "" || other->mAddress == "" || mAddress == other->mAddress) && + (mChannelMask == 0 || other->mChannelMask == 0 || + mChannelMask == other->mChannelMask); +} + +void AudioPolicyManager::DeviceVector::refreshTypes() +{ + mDeviceTypes = AUDIO_DEVICE_NONE; + for(size_t i = 0; i < size(); i++) { + mDeviceTypes |= itemAt(i)->mDeviceType; + } + ALOGV("DeviceVector::refreshTypes() mDeviceTypes %08x", mDeviceTypes); +} + +ssize_t AudioPolicyManager::DeviceVector::indexOf(const sp<DeviceDescriptor>& item) const +{ + for(size_t i = 0; i < size(); i++) { + if (item->equals(itemAt(i))) { + return i; + } + } + return -1; +} + +ssize_t AudioPolicyManager::DeviceVector::add(const sp<DeviceDescriptor>& item) +{ + ssize_t ret = indexOf(item); + + if (ret < 0) { + ret = SortedVector::add(item); + if (ret >= 0) { + refreshTypes(); + } + } else { + ALOGW("DeviceVector::add device %08x already in", item->mDeviceType); + ret = -1; + } + return ret; +} + +ssize_t AudioPolicyManager::DeviceVector::remove(const sp<DeviceDescriptor>& item) +{ + size_t i; + ssize_t ret = indexOf(item); + + if (ret < 0) { + ALOGW("DeviceVector::remove device %08x not in", item->mDeviceType); + } else { + ret = SortedVector::removeAt(ret); + if (ret >= 0) { + refreshTypes(); + } + } + return ret; +} + +void AudioPolicyManager::DeviceVector::loadDevicesFromType(audio_devices_t types) +{ + DeviceVector deviceList; + + uint32_t role_bit = AUDIO_DEVICE_BIT_IN & types; + types &= ~role_bit; + + while (types) { + uint32_t i = 31 - __builtin_clz(types); + uint32_t type = 1 << i; + types &= ~type; + 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 +{ + sp<DeviceDescriptor> device; + for (size_t i = 0; i < size(); i++) { + if (itemAt(i)->mDeviceType == type) { + device = itemAt(i); + if (itemAt(i)->mAddress = address) { + break; + } + } + } + ALOGV("DeviceVector::getDevice() for type %d address %s found %p", + type, address.string(), device.get()); + 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(%zu)->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 +{ + DeviceVector devices; + for (size_t i = 0; (i < size()) && (type != AUDIO_DEVICE_NONE); i++) { + if (itemAt(i)->mDeviceType & type & ~AUDIO_DEVICE_BIT_IN) { + devices.add(itemAt(i)); + type &= ~itemAt(i)->mDeviceType; + ALOGV("DeviceVector::getDevicesFromType() for type %x found %p", + itemAt(i)->mDeviceType, itemAt(i).get()); + } + } + return devices; +} + +AudioPolicyManager::DeviceVector AudioPolicyManager::DeviceVector::getDevicesFromTypeAddr( + audio_devices_t type, String8 address) const +{ + DeviceVector devices; + //ALOGV(" looking for device=%x, addr=%s", type, address.string()); + for (size_t i = 0; i < size(); i++) { + //ALOGV(" at i=%d: device=%x, addr=%s", + // i, itemAt(i)->mDeviceType, itemAt(i)->mAddress.string()); + if (itemAt(i)->mDeviceType == type) { + if (itemAt(i)->mAddress == address) { + //ALOGV(" found matching address %s", address.string()); + devices.add(itemAt(i)); + } + } + } + return devices; +} + +sp<AudioPolicyManager::DeviceDescriptor> AudioPolicyManager::DeviceVector::getDeviceFromName( + const String8& name) const +{ + 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->config_mask = AUDIO_PORT_CONFIG_CHANNEL_MASK|AUDIO_PORT_CONFIG_GAIN; + if (srcConfig != NULL) { + dstConfig->config_mask |= srcConfig->config_mask; + } + + AudioPortConfig::toAudioPortConfig(dstConfig, srcConfig); + + dstConfig->id = mId; + dstConfig->role = audio_is_output_device(mDeviceType) ? + AUDIO_PORT_ROLE_SINK : AUDIO_PORT_ROLE_SOURCE; + dstConfig->type = AUDIO_PORT_TYPE_DEVICE; + 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("DeviceDescriptor::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); +} + +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, "%*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); + } + write(fd, result.string(), result.size()); + AudioPort::dump(fd, spaces); + + return NO_ERROR; +} + +status_t AudioPolicyManager::AudioPatch::dump(int fd, int spaces, int index) const +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + + snprintf(buffer, SIZE, "%*sAudio patch %d:\n", spaces, "", index+1); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- handle: %2d\n", spaces, "", mHandle); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- audio flinger handle: %2d\n", spaces, "", mAfPatchHandle); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- owner uid: %2d\n", spaces, "", mUid); + result.append(buffer); + snprintf(buffer, SIZE, "%*s- %d sources:\n", spaces, "", mPatch.num_sources); + result.append(buffer); + for (size_t i = 0; i < mPatch.num_sources; i++) { + if (mPatch.sources[i].type == AUDIO_PORT_TYPE_DEVICE) { + snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "", + mPatch.sources[i].id, enumToString(sDeviceNameToEnumTable, + ARRAY_SIZE(sDeviceNameToEnumTable), + mPatch.sources[i].ext.device.type)); + } else { + snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "", + mPatch.sources[i].id, mPatch.sources[i].ext.mix.handle); + } + result.append(buffer); + } + snprintf(buffer, SIZE, "%*s- %d sinks:\n", spaces, "", mPatch.num_sinks); + result.append(buffer); + for (size_t i = 0; i < mPatch.num_sinks; i++) { + if (mPatch.sinks[i].type == AUDIO_PORT_TYPE_DEVICE) { + snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "", + mPatch.sinks[i].id, enumToString(sDeviceNameToEnumTable, + ARRAY_SIZE(sDeviceNameToEnumTable), + mPatch.sinks[i].ext.device.type)); + } else { + snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "", + mPatch.sinks[i].id, mPatch.sinks[i].ext.mix.handle); + } + result.append(buffer); + } + + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +// --- audio_policy.conf file parsing + +audio_output_flags_t AudioPolicyManager::parseFlagNames(char *name) +{ + uint32_t flag = 0; + + // it is OK to cast name to non const here as we are not going to use it after + // strtok() modifies it + char *flagName = strtok(name, "|"); + while (flagName != NULL) { + if (strlen(flagName) != 0) { + flag |= stringToEnum(sFlagNameToEnumTable, + ARRAY_SIZE(sFlagNameToEnumTable), + flagName); + } + flagName = strtok(NULL, "|"); + } + //force direct flag if offload flag is set: offloading implies a direct output stream + // and all common behaviors are driven by checking only the direct flag + // this should normally be set appropriately in the policy configuration file + if ((flag & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0) { + flag |= AUDIO_OUTPUT_FLAG_DIRECT; + } + + return (audio_output_flags_t)flag; +} + +audio_devices_t AudioPolicyManager::parseDeviceNames(char *name) +{ + uint32_t device = 0; + + char *devName = strtok(name, "|"); + while (devName != NULL) { + if (strlen(devName) != 0) { + device |= stringToEnum(sDeviceNameToEnumTable, + ARRAY_SIZE(sDeviceNameToEnumTable), + devName); + } + devName = strtok(NULL, "|"); + } + return device; +} + +void AudioPolicyManager::loadHwModule(cnode *root) +{ + status_t status = NAME_NOT_FOUND; + cnode *node; + sp<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 = module->loadOutput(node); + if (status == NAME_NOT_FOUND || status == NO_ERROR) { + status = tmpStatus; + } + node = node->next; + } + } + node = config_find(root, INPUTS_TAG); + if (node != NULL) { + node = node->first_child; + while (node) { + ALOGV("loadHwModule() loading input %s", node->name); + 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); + } +} + +void AudioPolicyManager::loadHwModules(cnode *root) +{ + cnode *node = config_find(root, AUDIO_HW_MODULE_TAG); + if (node == NULL) { + return; + } + + node = node->first_child; + while (node) { + ALOGV("loadHwModules() loading module %s", node->name); + loadHwModule(node); + node = node->next; + } +} + +void AudioPolicyManager::loadGlobalConfig(cnode *root, const sp<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.loadDevicesFromName((char *)node->value, + declaredDevices); + ALOGV("loadGlobalConfig() Attached Output Devices %08x", + mAvailableOutputDevices.types()); + } else if (strcmp(DEFAULT_OUTPUT_DEVICE_TAG, node->name) == 0) { + audio_devices_t device = (audio_devices_t)stringToEnum(sDeviceNameToEnumTable, + ARRAY_SIZE(sDeviceNameToEnumTable), + (char *)node->value); + if (device != AUDIO_DEVICE_NONE) { + 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.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); + ALOGV("loadGlobalConfig() mSpeakerDrcEnabled = %d", mSpeakerDrcEnabled); + } else if (strcmp(AUDIO_HAL_VERSION_TAG, node->name) == 0) { + uint32_t major, minor; + sscanf((char *)node->value, "%u.%u", &major, &minor); + module->mHalVersion = HARDWARE_DEVICE_API_VERSION(major, minor); + ALOGV("loadGlobalConfig() mHalVersion = %04x major %u minor %u", + module->mHalVersion, major, minor); + } + node = node->next; + } +} + +status_t AudioPolicyManager::loadAudioPolicyConfig(const char *path) +{ + cnode *root; + char *data; + + data = (char *)load_file(path, NULL); + if (data == NULL) { + return -ENODEV; + } + root = config_node("", ""); + config_load(root, data); + + 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); + + ALOGI("loadAudioPolicyConfig() loaded %s\n", path); + + return NO_ERROR; +} + +void AudioPolicyManager::defaultAudioPolicyConfig(void) +{ + sp<HwModule> module; + sp<IOProfile> profile; + sp<DeviceDescriptor> defaultInputDevice = new DeviceDescriptor(String8(""), + AUDIO_DEVICE_IN_BUILTIN_MIC); + mAvailableOutputDevices.add(mDefaultOutputDevice); + mAvailableInputDevices.add(defaultInputDevice); + + module = new HwModule("primary"); + + 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); + profile->mSupportedDevices.add(mDefaultOutputDevice); + profile->mFlags = AUDIO_OUTPUT_FLAG_PRIMARY; + module->mOutputProfiles.add(profile); + + 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); + profile->mSupportedDevices.add(defaultInputDevice); + module->mInputProfiles.add(profile); + + mHwModules.add(module); +} + +audio_stream_type_t AudioPolicyManager::streamTypefromAttributesInt(const audio_attributes_t *attr) +{ + // flags to stream type mapping + if ((attr->flags & AUDIO_FLAG_AUDIBILITY_ENFORCED) == AUDIO_FLAG_AUDIBILITY_ENFORCED) { + return AUDIO_STREAM_ENFORCED_AUDIBLE; + } + if ((attr->flags & AUDIO_FLAG_SCO) == AUDIO_FLAG_SCO) { + return AUDIO_STREAM_BLUETOOTH_SCO; + } + + // usage to stream type mapping + switch (attr->usage) { + case AUDIO_USAGE_MEDIA: + case AUDIO_USAGE_GAME: + case AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY: + case AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: + return AUDIO_STREAM_MUSIC; + case AUDIO_USAGE_ASSISTANCE_SONIFICATION: + return AUDIO_STREAM_SYSTEM; + case AUDIO_USAGE_VOICE_COMMUNICATION: + return AUDIO_STREAM_VOICE_CALL; + + case AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING: + return AUDIO_STREAM_DTMF; + + case AUDIO_USAGE_ALARM: + return AUDIO_STREAM_ALARM; + case AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE: + return AUDIO_STREAM_RING; + + case AUDIO_USAGE_NOTIFICATION: + case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST: + case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT: + case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED: + case AUDIO_USAGE_NOTIFICATION_EVENT: + return AUDIO_STREAM_NOTIFICATION; + + case AUDIO_USAGE_UNKNOWN: + default: + return AUDIO_STREAM_MUSIC; + } +} +}; // namespace android diff --git a/services/audiopolicy/AudioPolicyManager.h b/services/audiopolicy/AudioPolicyManager.h new file mode 100644 index 0000000..da0d95d --- /dev/null +++ b/services/audiopolicy/AudioPolicyManager.h @@ -0,0 +1,855 @@ +/* + * 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. + */ + + +#include <stdint.h> +#include <sys/types.h> +#include <cutils/config_utils.h> +#include <cutils/misc.h> +#include <utils/Timers.h> +#include <utils/Errors.h> +#include <utils/KeyedVector.h> +#include <utils/SortedVector.h> +#include "AudioPolicyInterface.h" + + +namespace android { + +// ---------------------------------------------------------------------------- + +// Attenuation applied to STRATEGY_SONIFICATION streams when a headset is connected: 6dB +#define SONIFICATION_HEADSET_VOLUME_FACTOR 0.5 +// Min volume for STRATEGY_SONIFICATION streams when limited by music volume: -36dB +#define SONIFICATION_HEADSET_VOLUME_MIN 0.016 +// Time in milliseconds during which we consider that music is still active after a music +// track was stopped - see computeVolume() +#define SONIFICATION_HEADSET_MUSIC_DELAY 5000 +// Time in milliseconds after media stopped playing during which we consider that the +// sonification should be as unobtrusive as during the time media was playing. +#define SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY 5000 +// Time in milliseconds during witch some streams are muted while the audio path +// is switched +#define MUTE_TIME_MS 2000 + +#define NUM_TEST_OUTPUTS 5 + +#define NUM_VOL_CURVE_KNEES 2 + +// Default minimum length allowed for offloading a compressed track +// Can be overridden by the audio.offload.min.duration.secs property +#define OFFLOAD_DEFAULT_MIN_DURATION_SECS 60 + +#define MAX_MIXER_SAMPLING_RATE 48000 +#define MAX_MIXER_CHANNEL_COUNT 8 + +// ---------------------------------------------------------------------------- +// AudioPolicyManager implements audio policy manager behavior common to all platforms. +// ---------------------------------------------------------------------------- + +class AudioPolicyManager: public AudioPolicyInterface +#ifdef AUDIO_POLICY_TEST + , public Thread +#endif //AUDIO_POLICY_TEST +{ + +public: + AudioPolicyManager(AudioPolicyClientInterface *clientInterface); + virtual ~AudioPolicyManager(); + + // AudioPolicyInterface + virtual status_t setDeviceConnectionState(audio_devices_t device, + audio_policy_dev_state_t state, + const char *device_address); + virtual audio_policy_dev_state_t getDeviceConnectionState(audio_devices_t device, + const char *device_address); + virtual void setPhoneState(audio_mode_t state); + virtual void setForceUse(audio_policy_force_use_t usage, + audio_policy_forced_cfg_t config); + virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage); + virtual void setSystemProperty(const char* property, const char* value); + virtual status_t initCheck(); + virtual audio_io_handle_t getOutput(audio_stream_type_t stream, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo); + virtual audio_io_handle_t getOutputForAttr(const audio_attributes_t *attr, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo); + virtual status_t startOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session = 0); + virtual status_t stopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session = 0); + virtual void releaseOutput(audio_io_handle_t output); + virtual audio_io_handle_t getInput(audio_source_t inputSource, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_session_t session, + audio_input_flags_t flags); + + // indicates to the audio policy manager that the input starts being used. + virtual status_t startInput(audio_io_handle_t input, + audio_session_t session); + + // indicates to the audio policy manager that the input stops being used. + virtual status_t stopInput(audio_io_handle_t input, + audio_session_t session); + virtual void releaseInput(audio_io_handle_t input, + audio_session_t session); + virtual void closeAllInputs(); + virtual void initStreamVolume(audio_stream_type_t stream, + int indexMin, + int indexMax); + virtual status_t setStreamVolumeIndex(audio_stream_type_t stream, + int index, + audio_devices_t device); + virtual status_t getStreamVolumeIndex(audio_stream_type_t stream, + int *index, + audio_devices_t device); + + // return the strategy corresponding to a given stream type + virtual uint32_t getStrategyForStream(audio_stream_type_t stream); + // return the strategy corresponding to the given audio attributes + virtual uint32_t getStrategyForAttr(const audio_attributes_t *attr); + + // return the enabled output devices for the given stream type + virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream); + + virtual audio_io_handle_t getOutputForEffect(const effect_descriptor_t *desc = NULL); + virtual status_t registerEffect(const effect_descriptor_t *desc, + audio_io_handle_t io, + uint32_t strategy, + int session, + int id); + virtual status_t unregisterEffect(int id); + virtual status_t setEffectEnabled(int id, bool enabled); + + virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const; + // return whether a stream is playing remotely, override to change the definition of + // local/remote playback, used for instance by notification manager to not make + // media players lose audio focus when not playing locally + virtual bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs = 0) const; + virtual bool isSourceActive(audio_source_t source) const; + + virtual status_t dump(int fd); + + 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); + + virtual status_t acquireSoundTriggerSession(audio_session_t *session, + audio_io_handle_t *ioHandle, + audio_devices_t *device); + + virtual status_t releaseSoundTriggerSession(audio_session_t session); + +protected: + + enum routing_strategy { + STRATEGY_MEDIA, + STRATEGY_PHONE, + STRATEGY_SONIFICATION, + STRATEGY_SONIFICATION_RESPECTFUL, + STRATEGY_DTMF, + STRATEGY_ENFORCED_AUDIBLE, + NUM_STRATEGIES + }; + + // 4 points to define the volume attenuation curve, each characterized by the volume + // index (from 0 to 100) at which they apply, and the attenuation in dB at that index. + // we use 100 steps to avoid rounding errors when computing the volume in volIndexToAmpl() + + enum { VOLMIN = 0, VOLKNEE1 = 1, VOLKNEE2 = 2, VOLMAX = 3, VOLCNT = 4}; + + class VolumeCurvePoint + { + public: + int mIndex; + float mDBAttenuation; + }; + + // device categories used for volume curve management. + enum device_category { + DEVICE_CATEGORY_HEADSET, + DEVICE_CATEGORY_SPEAKER, + DEVICE_CATEGORY_EARPIECE, + DEVICE_CATEGORY_EXT_MEDIA, + DEVICE_CATEGORY_CNT + }; + + class HwModule; + + class AudioGain: public RefBase + { + public: + AudioGain(int index, bool useInChannelMask); + virtual ~AudioGain() {} + + void dump(int fd, int spaces, int index) const; + + void getDefaultConfig(struct audio_gain_config *config); + status_t checkConfig(const struct audio_gain_config *config); + int mIndex; + struct audio_gain mGain; + bool mUseInChannelMask; + }; + + class AudioPort: public virtual RefBase + { + public: + AudioPort(const String8& name, audio_port_type_t type, + audio_port_role_t role, const sp<HwModule>& module); + virtual ~AudioPort() {} + + virtual void toAudioPort(struct audio_port *port) const; + + void importAudioPort(const sp<AudioPort> port); + void clearCapabilities(); + + void loadSamplingRates(char *name); + void loadFormats(char *name); + void loadOutChannels(char *name); + void loadInChannels(char *name); + + audio_gain_mode_t loadGainMode(char *name); + void loadGain(cnode *root, int index); + void loadGains(cnode *root); + + // searches for an exact match + status_t checkExactSamplingRate(uint32_t samplingRate) const; + // searches for a compatible match, and returns the best match via updatedSamplingRate + status_t checkCompatibleSamplingRate(uint32_t samplingRate, + uint32_t *updatedSamplingRate) const; + // searches for an exact match + status_t checkExactChannelMask(audio_channel_mask_t channelMask) const; + // searches for a compatible match, currently implemented for input channel masks only + status_t checkCompatibleChannelMask(audio_channel_mask_t channelMask) const; + status_t checkFormat(audio_format_t format) const; + status_t checkGain(const struct audio_gain_config *gainConfig, int index) const; + + uint32_t pickSamplingRate() const; + audio_channel_mask_t pickChannelMask() const; + audio_format_t pickFormat() const; + + static const audio_format_t sPcmFormatCompareTable[]; + static int compareFormats(audio_format_t format1, audio_format_t format2); + + void dump(int fd, int spaces) const; + + String8 mName; + audio_port_type_t mType; + audio_port_role_t mRole; + bool mUseInChannelMask; + // 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 + Vector < sp<AudioGain> > mGains; // gain controllers + sp<HwModule> mModule; // audio HW module exposing this I/O stream + audio_output_flags_t mFlags; // attribute flags (e.g primary output, + // direct output...). For outputs only. + }; + + class AudioPortConfig: public virtual RefBase + { + public: + AudioPortConfig(); + virtual ~AudioPortConfig() {} + + status_t applyAudioPortConfig(const struct audio_port_config *config, + struct audio_port_config *backupConfig = NULL); + virtual void toAudioPortConfig(struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig = NULL) const = 0; + virtual sp<AudioPort> getAudioPort() const = 0; + uint32_t mSamplingRate; + audio_format_t mFormat; + audio_channel_mask_t mChannelMask; + struct audio_gain_config mGain; + }; + + + 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) {} + + status_t dump(int fd, int spaces, int index) const; + + audio_patch_handle_t mHandle; + struct audio_patch mPatch; + uid_t mUid; + audio_patch_handle_t mAfPatchHandle; + }; + + class DeviceDescriptor: public AudioPort, public AudioPortConfig + { + public: + DeviceDescriptor(const String8& name, audio_devices_t type); + + virtual ~DeviceDescriptor() {} + + bool equals(const sp<DeviceDescriptor>& other) const; + virtual void toAudioPortConfig(struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig = NULL) const; + virtual sp<AudioPort> getAudioPort() const { return (AudioPort*) this; } + + virtual void toAudioPort(struct audio_port *port) const; + + status_t dump(int fd, int spaces, int index) const; + + audio_devices_t mDeviceType; + String8 mAddress; + audio_port_handle_t mId; + }; + + class DeviceVector : public SortedVector< sp<DeviceDescriptor> > + { + public: + DeviceVector() : SortedVector(), mDeviceTypes(AUDIO_DEVICE_NONE) {} + + ssize_t add(const sp<DeviceDescriptor>& item); + ssize_t remove(const sp<DeviceDescriptor>& item); + ssize_t indexOf(const sp<DeviceDescriptor>& item) const; + + 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; + DeviceVector getDevicesFromTypeAddr(audio_devices_t type, String8 address) + const; + + private: + void refreshTypes(); + audio_devices_t mDeviceTypes; + }; + + // the IOProfile class describes the capabilities of an output or input stream. + // It is currently assumed that all combination of listed parameters are supported. + // It is used by the policy manager to determine if an output or input is suitable for + // a given use case, open/close it accordingly and connect/disconnect audio tracks + // to/from it. + class IOProfile : public AudioPort + { + public: + IOProfile(const String8& name, audio_port_role_t role, const sp<HwModule>& module); + virtual ~IOProfile(); + + // This method is used for both output and input. + // If parameter updatedSamplingRate is non-NULL, it is assigned the actual sample rate. + // For input, flags is interpreted as audio_input_flags_t. + // TODO: merge audio_output_flags_t and audio_input_flags_t. + bool isCompatibleProfile(audio_devices_t device, + uint32_t samplingRate, + uint32_t *updatedSamplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags) const; + + void dump(int fd); + void log(); + + DeviceVector mSupportedDevices; // supported devices + // (devices this output can be routed to) + }; + + class HwModule : public RefBase + { + 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 ...) + uint32_t mHalVersion; // audio HAL API version + 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 + static const VolumeCurvePoint sDefaultMediaVolumeCurve[AudioPolicyManager::VOLCNT]; + // volume curve for non-media audio on ext media outputs (HDMI, Line, etc) + static const VolumeCurvePoint sExtMediaSystemVolumeCurve[AudioPolicyManager::VOLCNT]; + // volume curve for media strategy on speakers + static const VolumeCurvePoint sSpeakerMediaVolumeCurve[AudioPolicyManager::VOLCNT]; + static const VolumeCurvePoint sSpeakerMediaVolumeCurveDrc[AudioPolicyManager::VOLCNT]; + // volume curve for sonification strategy on speakers + static const VolumeCurvePoint sSpeakerSonificationVolumeCurve[AudioPolicyManager::VOLCNT]; + static const VolumeCurvePoint sSpeakerSonificationVolumeCurveDrc[AudioPolicyManager::VOLCNT]; + static const VolumeCurvePoint sDefaultSystemVolumeCurve[AudioPolicyManager::VOLCNT]; + static const VolumeCurvePoint sDefaultSystemVolumeCurveDrc[AudioPolicyManager::VOLCNT]; + static const VolumeCurvePoint sHeadsetSystemVolumeCurve[AudioPolicyManager::VOLCNT]; + static const VolumeCurvePoint sDefaultVoiceVolumeCurve[AudioPolicyManager::VOLCNT]; + static const VolumeCurvePoint sSpeakerVoiceVolumeCurve[AudioPolicyManager::VOLCNT]; + // default volume curves per stream and device category. See initializeVolumeCurves() + static const VolumeCurvePoint *sVolumeProfiles[AUDIO_STREAM_CNT][DEVICE_CATEGORY_CNT]; + + // descriptor for audio outputs. Used to maintain current configuration of each opened audio output + // and keep track of the usage of this output by each audio stream type. + class AudioOutputDescriptor: public AudioPortConfig + { + public: + AudioOutputDescriptor(const sp<IOProfile>& profile); + + status_t dump(int fd); + + audio_devices_t device() const; + void changeRefCount(audio_stream_type_t stream, int delta); + + bool isDuplicated() const { return (mOutput1 != NULL && mOutput2 != NULL); } + audio_devices_t supportedDevices(); + uint32_t latency(); + bool sharesHwModuleWith(const sp<AudioOutputDescriptor> outputDesc); + bool isActive(uint32_t inPastMs = 0) const; + bool isStreamActive(audio_stream_type_t stream, + uint32_t inPastMs = 0, + nsecs_t sysTime = 0) const; + bool isStrategyActive(routing_strategy strategy, + uint32_t inPastMs = 0, + nsecs_t sysTime = 0) const; + + virtual void toAudioPortConfig(struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig = NULL) const; + virtual sp<AudioPort> getAudioPort() const { return mProfile; } + void toAudioPort(struct audio_port *port) const; + + audio_port_handle_t mId; + audio_io_handle_t mIoHandle; // output handle + uint32_t mLatency; // + audio_output_flags_t mFlags; // + audio_devices_t mDevice; // current device this output is routed to + audio_patch_handle_t mPatchHandle; + uint32_t mRefCount[AUDIO_STREAM_CNT]; // number of streams of each type using this output + nsecs_t mStopTime[AUDIO_STREAM_CNT]; + sp<AudioOutputDescriptor> mOutput1; // used by duplicated outputs: first output + sp<AudioOutputDescriptor> mOutput2; // used by duplicated outputs: second output + float mCurVolume[AUDIO_STREAM_CNT]; // current stream volume + int mMuteCount[AUDIO_STREAM_CNT]; // mute request counter + const sp<IOProfile> mProfile; // I/O profile this output derives from + bool mStrategyMutedByDevice[NUM_STRATEGIES]; // strategies muted because of incompatible + // device selection. See checkDeviceMuteStrategies() + uint32_t mDirectOpenCount; // number of clients using this output (direct outputs only) + }; + + // descriptor for audio inputs. Used to maintain current configuration of each opened audio input + // and keep track of the usage of this input. + class AudioInputDescriptor: public AudioPortConfig + { + public: + AudioInputDescriptor(const sp<IOProfile>& profile); + + status_t dump(int fd); + + audio_port_handle_t mId; + audio_io_handle_t mIoHandle; // input handle + audio_devices_t mDevice; // current device this input is routed to + audio_patch_handle_t mPatchHandle; + uint32_t mRefCount; // number of AudioRecord clients using + // this input + uint32_t mOpenRefCount; + audio_source_t mInputSource; // input source selected by application + //(mediarecorder.h) + const sp<IOProfile> mProfile; // I/O profile this output derives from + SortedVector<audio_session_t> mSessions; // audio sessions attached to this input + bool mIsSoundTrigger; // used by a soundtrigger capture + + virtual void toAudioPortConfig(struct audio_port_config *dstConfig, + const struct audio_port_config *srcConfig = NULL) const; + virtual sp<AudioPort> getAudioPort() const { return mProfile; } + void toAudioPort(struct audio_port *port) const; + }; + + // stream descriptor used for volume control + class StreamDescriptor + { + public: + StreamDescriptor(); + + int getVolumeIndex(audio_devices_t device); + void dump(int fd); + + int mIndexMin; // min volume index + int mIndexMax; // max volume index + KeyedVector<audio_devices_t, int> mIndexCur; // current volume index per device + bool mCanBeMuted; // true is the stream can be muted + + const VolumeCurvePoint *mVolumeCurve[DEVICE_CATEGORY_CNT]; + }; + + // stream descriptor used for volume control + class EffectDescriptor : public RefBase + { + public: + + status_t dump(int fd); + + int mIo; // io the effect is attached to + routing_strategy mStrategy; // routing strategy the effect is associated to + int mSession; // audio session the effect is on + effect_descriptor_t mDesc; // effect descriptor + bool mEnabled; // enabled state: CPU load being used or not + }; + + void addOutput(audio_io_handle_t output, sp<AudioOutputDescriptor> outputDesc); + void addInput(audio_io_handle_t input, sp<AudioInputDescriptor> inputDesc); + + // return the strategy corresponding to a given stream type + static routing_strategy getStrategy(audio_stream_type_t stream); + + // return appropriate device for streams handled by the specified strategy according to current + // phone state, connected devices... + // if fromCache is true, the device is returned from mDeviceForStrategy[], + // otherwise it is determine by current state + // (device connected,phone state, force use, a2dp output...) + // This allows to: + // 1 speed up process when the state is stable (when starting or stopping an output) + // 2 access to either current device selection (fromCache == true) or + // "future" device selection (fromCache == false) when called from a context + // where conditions are changing (setDeviceConnectionState(), setPhoneState()...) AND + // before updateDevicesAndOutputs() is called. + virtual audio_devices_t getDeviceForStrategy(routing_strategy strategy, + bool fromCache); + + // change the route of the specified output. Returns the number of ms we have slept to + // allow new routing to take effect in certain cases. + uint32_t setOutputDevice(audio_io_handle_t output, + audio_devices_t device, + bool force = false, + int delayMs = 0, + audio_patch_handle_t *patchHandle = NULL, + const char* address = NULL); + status_t resetOutputDevice(audio_io_handle_t output, + int delayMs = 0, + audio_patch_handle_t *patchHandle = NULL); + status_t setInputDevice(audio_io_handle_t input, + audio_devices_t device, + 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); + + // return io handle of active input or 0 if no input is active + // Only considers inputs from physical devices (e.g. main mic, headset mic) when + // ignoreVirtualInputs is true. + audio_io_handle_t getActiveInput(bool ignoreVirtualInputs = true); + + uint32_t activeInputsCount() const; + + // initialize volume curves for each strategy and device category + void initializeVolumeCurves(); + + // compute the actual volume for a given stream according to the requested index and a particular + // device + virtual float computeVolume(audio_stream_type_t stream, int index, + audio_io_handle_t output, audio_devices_t device); + + // check that volume change is permitted, compute and send new volume to audio hardware + status_t checkAndSetVolume(audio_stream_type_t stream, int index, audio_io_handle_t output, + audio_devices_t device, int delayMs = 0, bool force = false); + + // apply all stream volumes to the specified output and device + void applyStreamVolumes(audio_io_handle_t output, audio_devices_t device, int delayMs = 0, bool force = false); + + // Mute or unmute all streams handled by the specified strategy on the specified output + void setStrategyMute(routing_strategy strategy, + bool on, + audio_io_handle_t output, + int delayMs = 0, + audio_devices_t device = (audio_devices_t)0); + + // Mute or unmute the stream on the specified output + void setStreamMute(audio_stream_type_t stream, + bool on, + audio_io_handle_t output, + int delayMs = 0, + audio_devices_t device = (audio_devices_t)0); + + // handle special cases for sonification strategy while in call: mute streams or replace by + // a special tone in the device used for communication + void handleIncallSonification(audio_stream_type_t stream, bool starting, bool stateChange); + + // true if device is in a telephony or VoIP call + virtual bool isInCall(); + + // true if given state represents a device in a telephony or VoIP call + virtual bool isStateInCall(int state); + + // when a device is connected, checks if an open output can be routed + // to this device. If none is open, tries to open one of the available outputs. + // Returns an output suitable to this device or 0. + // when a device is disconnected, checks if an output is not used any more and + // returns its handle if any. + // transfers the audio tracks and effects from one output thread to another accordingly. + status_t checkOutputsForDevice(const sp<DeviceDescriptor> devDesc, + audio_policy_dev_state_t state, + SortedVector<audio_io_handle_t>& outputs, + const String8 address); + + status_t checkInputsForDevice(audio_devices_t device, + audio_policy_dev_state_t state, + SortedVector<audio_io_handle_t>& inputs, + const String8 address); + + // close an output and its companion duplicating output. + void closeOutput(audio_io_handle_t output); + + // close an input. + void closeInput(audio_io_handle_t input); + + // checks and if necessary changes outputs used for all strategies. + // must be called every time a condition that affects the output choice for a given strategy + // changes: connected device, phone state, force use... + // Must be called before updateDevicesAndOutputs() + void checkOutputForStrategy(routing_strategy strategy); + + // Same as checkOutputForStrategy() but for a all strategies in order of priority + void checkOutputForAllStrategies(); + + // manages A2DP output suspend/restore according to phone state and BT SCO usage + void checkA2dpSuspend(); + + // returns the A2DP output handle if it is open or 0 otherwise + audio_io_handle_t getA2dpOutput(); + + // selects the most appropriate device on output for current state + // must be called every time a condition that affects the device choice for a given output is + // changed: connected device, phone state, force use, output start, output stop.. + // see getDeviceForStrategy() for the use of fromCache parameter + audio_devices_t getNewOutputDevice(audio_io_handle_t output, bool fromCache); + + // updates cache of device used by all strategies (mDeviceForStrategy[]) + // must be called every time a condition that affects the device choice for a given strategy is + // changed: connected device, phone state, force use... + // cached values are used by getDeviceForStrategy() if parameter fromCache is true. + // Must be called after checkOutputForAllStrategies() + void updateDevicesAndOutputs(); + + // selects the most appropriate device on input for current state + audio_devices_t getNewInputDevice(audio_io_handle_t input); + + virtual uint32_t getMaxEffectsCpuLoad(); + virtual uint32_t getMaxEffectsMemory(); +#ifdef AUDIO_POLICY_TEST + virtual bool threadLoop(); + void exit(); + int testOutputIndex(audio_io_handle_t output); +#endif //AUDIO_POLICY_TEST + + status_t setEffectEnabled(const sp<EffectDescriptor>& effectDesc, bool enabled); + + // returns the category the device belongs to with regard to volume curve management + static device_category getDeviceCategory(audio_devices_t device); + + // extract one device relevant for volume control from multiple device selection + static audio_devices_t getDeviceForVolume(audio_devices_t device); + + SortedVector<audio_io_handle_t> getOutputsForDevice(audio_devices_t device, + DefaultKeyedVector<audio_io_handle_t, sp<AudioOutputDescriptor> > openOutputs); + bool vectorsEqual(SortedVector<audio_io_handle_t>& outputs1, + SortedVector<audio_io_handle_t>& outputs2); + + // mute/unmute strategies using an incompatible device combination + // if muting, wait for the audio in pcm buffer to be drained before proceeding + // if unmuting, unmute only after the specified delay + // Returns the number of ms waited + uint32_t checkDeviceMuteStrategies(sp<AudioOutputDescriptor> outputDesc, + audio_devices_t prevDevice, + uint32_t delayMs); + + audio_io_handle_t selectOutput(const SortedVector<audio_io_handle_t>& outputs, + audio_output_flags_t flags, + audio_format_t format); + // samplingRate parameter is an in/out and so may be modified + sp<IOProfile> getInputProfile(audio_devices_t device, + uint32_t& samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_input_flags_t flags); + sp<IOProfile> getProfileForDirectOutput(audio_devices_t device, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags); + + audio_io_handle_t selectOutputForEffects(const SortedVector<audio_io_handle_t>& outputs); + + bool isNonOffloadableEffectEnabled(); + + status_t addAudioPatch(audio_patch_handle_t handle, + const sp<AudioPatch>& patch); + status_t removeAudioPatch(audio_patch_handle_t handle); + + sp<AudioOutputDescriptor> getOutputFromId(audio_port_handle_t id) const; + sp<AudioInputDescriptor> getInputFromId(audio_port_handle_t id) const; + sp<HwModule> getModuleForDevice(audio_devices_t device) const; + sp<HwModule> getModuleFromName(const char *name) const; + audio_devices_t availablePrimaryOutputDevices(); + audio_devices_t availablePrimaryInputDevices(); + + void updateCallRouting(audio_devices_t rxDevice, int delayMs = 0); + + // + // Audio policy configuration file parsing (audio_policy.conf) + // + static uint32_t stringToEnum(const struct StringToEnum *table, + size_t size, + const char *name); + static const char *enumToString(const struct StringToEnum *table, + size_t size, + uint32_t value); + static bool stringToBool(const char *value); + static audio_output_flags_t parseFlagNames(char *name); + static audio_devices_t parseDeviceNames(char *name); + void loadHwModule(cnode *root); + void loadHwModules(cnode *root); + void loadGlobalConfig(cnode *root, const sp<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 + DefaultKeyedVector<audio_io_handle_t, sp<AudioOutputDescriptor> > mOutputs; + // copy of mOutputs before setDeviceConnectionState() opens new outputs + // reset to mOutputs when updateDevicesAndOutputs() is called. + DefaultKeyedVector<audio_io_handle_t, sp<AudioOutputDescriptor> > mPreviousOutputs; + DefaultKeyedVector<audio_io_handle_t, sp<AudioInputDescriptor> > mInputs; // list of input descriptors + 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 + + StreamDescriptor mStreams[AUDIO_STREAM_CNT]; // stream descriptors for volume control + bool mLimitRingtoneVolume; // limit ringtone volume to music volume if headset connected + audio_devices_t mDeviceForStrategy[NUM_STRATEGIES]; + float mLastVoiceVolume; // last voice volume value sent to audio HAL + + // Maximum CPU load allocated to audio effects in 0.1 MIPS (ARMv5TE, 0 WS memory) units + static const uint32_t MAX_EFFECTS_CPU_LOAD = 1000; + // Maximum memory allocated to audio effects in KB + static const uint32_t MAX_EFFECTS_MEMORY = 512; + uint32_t mTotalEffectsCpuLoad; // current CPU load used by effects + uint32_t mTotalEffectsMemory; // current memory used by effects + KeyedVector<int, sp<EffectDescriptor> > mEffects; // list of registered audio effects + bool mA2dpSuspended; // true if A2DP output is suspended + sp<DeviceDescriptor> mDefaultOutputDevice; // output device selected by default at boot time + bool mSpeakerDrcEnabled;// true on devices that use DRC on the DEVICE_CATEGORY_SPEAKER path + // to boost soft sounds, used to adjust volume curves accordingly + + Vector < sp<HwModule> > mHwModules; + volatile int32_t mNextUniqueId; + volatile int32_t mAudioPortGeneration; + + DefaultKeyedVector<audio_patch_handle_t, sp<AudioPatch> > mAudioPatches; + + DefaultKeyedVector<audio_session_t, audio_io_handle_t> mSoundTriggerSessions; + + sp<AudioPatch> mCallTxPatch; + sp<AudioPatch> mCallRxPatch; + +#ifdef AUDIO_POLICY_TEST + Mutex mLock; + Condition mWaitWorkCV; + + int mCurOutput; + bool mDirectOutput; + audio_io_handle_t mTestOutputs[NUM_TEST_OUTPUTS]; + int mTestInput; + uint32_t mTestDevice; + uint32_t mTestSamplingRate; + uint32_t mTestFormat; + uint32_t mTestChannels; + uint32_t mTestLatencyMs; +#endif //AUDIO_POLICY_TEST + +private: + static float volIndexToAmpl(audio_devices_t device, const StreamDescriptor& streamDesc, + int indexInUi); + // updates device caching and output for streams that can influence the + // routing of notifications + void handleNotificationRoutingForStream(audio_stream_type_t stream); + static bool isVirtualInputDevice(audio_devices_t device); + static bool deviceDistinguishesOnAddress(audio_devices_t device); + // find the outputs on a given output descriptor that have the given address. + // to be called on an AudioOutputDescriptor whose supported devices (as defined + // in mProfile->mSupportedDevices) matches the device whose address is to be matched. + // see deviceDistinguishesOnAddress(audio_devices_t) for whether the device type is one + // where addresses are used to distinguish between one connected device and another. + void findIoHandlesByAddress(sp<AudioOutputDescriptor> desc /*in*/, + const String8 address /*in*/, + SortedVector<audio_io_handle_t>& outputs /*out*/); + uint32_t nextUniqueId(); + uint32_t nextAudioPortGeneration(); + uint32_t curAudioPortGeneration() const { return mAudioPortGeneration; } + // internal method to return the output handle for the given device and format + audio_io_handle_t getOutputForDevice( + audio_devices_t device, + audio_stream_type_t stream, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo); + // internal function to derive a stream type value from audio attributes + audio_stream_type_t streamTypefromAttributesInt(const audio_attributes_t *attr); +}; + +}; diff --git a/services/audiopolicy/AudioPolicyService.cpp b/services/audiopolicy/AudioPolicyService.cpp new file mode 100644 index 0000000..50bb8c7 --- /dev/null +++ b/services/audiopolicy/AudioPolicyService.cpp @@ -0,0 +1,1029 @@ +/* + * 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 "AudioPolicyService" +//#define LOG_NDEBUG 0 + +#include "Configuration.h" +#undef __STRICT_ANSI__ +#define __STDINT_LIMITS +#define __STDC_LIMIT_MACROS +#include <stdint.h> + +#include <sys/time.h> +#include <binder/IServiceManager.h> +#include <utils/Log.h> +#include <cutils/properties.h> +#include <binder/IPCThreadState.h> +#include <utils/String16.h> +#include <utils/threads.h> +#include "AudioPolicyService.h" +#include "ServiceUtilities.h" +#include <hardware_legacy/power.h> +#include <media/AudioEffect.h> +#include <media/EffectsFactoryApi.h> + +#include <hardware/hardware.h> +#include <system/audio.h> +#include <system/audio_policy.h> +#include <hardware/audio_policy.h> + +namespace android { + +static const char kDeadlockedString[] = "AudioPolicyService may be deadlocked\n"; +static const char kCmdDeadlockedString[] = "AudioPolicyService command thread may be deadlocked\n"; + +static const int kDumpLockRetries = 50; +static const int kDumpLockSleepUs = 20000; + +static const nsecs_t kAudioCommandTimeoutNs = seconds(3); // 3 seconds + +namespace { + extern struct audio_policy_service_ops aps_ops; +}; + +// ---------------------------------------------------------------------------- + +AudioPolicyService::AudioPolicyService() + : BnAudioPolicyService(), mpAudioPolicyDev(NULL), mpAudioPolicy(NULL), + mAudioPolicyManager(NULL), mAudioPolicyClient(NULL) +{ + char value[PROPERTY_VALUE_MAX]; + const struct hw_module_t *module; + int forced_val; + int rc; + + Mutex::Autolock _l(mLock); + + // start tone playback thread + mTonePlaybackThread = new AudioCommandThread(String8("ApmTone"), this); + // start audio commands thread + mAudioCommandThread = new AudioCommandThread(String8("ApmAudio"), this); + // start output activity command thread + mOutputCommandThread = new AudioCommandThread(String8("ApmOutput"), this); + +#ifdef USE_LEGACY_AUDIO_POLICY + ALOGI("AudioPolicyService CSTOR in legacy mode"); + + /* instantiate the audio policy manager */ + rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module); + if (rc) { + return; + } + rc = audio_policy_dev_open(module, &mpAudioPolicyDev); + ALOGE_IF(rc, "couldn't open audio policy device (%s)", strerror(-rc)); + if (rc) { + return; + } + + rc = mpAudioPolicyDev->create_audio_policy(mpAudioPolicyDev, &aps_ops, this, + &mpAudioPolicy); + ALOGE_IF(rc, "couldn't create audio policy (%s)", strerror(-rc)); + if (rc) { + return; + } + + rc = mpAudioPolicy->init_check(mpAudioPolicy); + ALOGE_IF(rc, "couldn't init_check the audio policy (%s)", strerror(-rc)); + if (rc) { + return; + } + ALOGI("Loaded audio policy from %s (%s)", module->name, module->id); +#else + ALOGI("AudioPolicyService CSTOR in new mode"); + + mAudioPolicyClient = new AudioPolicyClient(this); + mAudioPolicyManager = createAudioPolicyManager(mAudioPolicyClient); +#endif + + // load audio processing modules + mAudioPolicyEffects = new AudioPolicyEffects(); +} + +AudioPolicyService::~AudioPolicyService() +{ + mTonePlaybackThread->exit(); + mAudioCommandThread->exit(); + mOutputCommandThread->exit(); + +#ifdef USE_LEGACY_AUDIO_POLICY + if (mpAudioPolicy != NULL && mpAudioPolicyDev != NULL) { + mpAudioPolicyDev->destroy_audio_policy(mpAudioPolicyDev, mpAudioPolicy); + } + if (mpAudioPolicyDev != NULL) { + audio_policy_dev_close(mpAudioPolicyDev); + } +#else + destroyAudioPolicyManager(mAudioPolicyManager); + delete mAudioPolicyClient; +#endif + + mNotificationClients.clear(); + mAudioPolicyEffects.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, + audio_patch_handle_t *handle, + int delayMs) +{ + return mAudioCommandThread->createAudioPatchCommand(patch, handle, delayMs); +} + +status_t AudioPolicyService::clientReleaseAudioPatch(audio_patch_handle_t handle, + int delayMs) +{ + 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(), + IPCThreadState::self()->getCallingPid()); +} + +static bool tryLock(Mutex& mutex) +{ + bool locked = false; + for (int i = 0; i < kDumpLockRetries; ++i) { + if (mutex.tryLock() == NO_ERROR) { + locked = true; + break; + } + usleep(kDumpLockSleepUs); + } + return locked; +} + +status_t AudioPolicyService::dumpInternals(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + +#ifdef USE_LEGACY_AUDIO_POLICY + snprintf(buffer, SIZE, "PolicyManager Interface: %p\n", mpAudioPolicy); +#else + snprintf(buffer, SIZE, "AudioPolicyManager: %p\n", mAudioPolicyManager); +#endif + result.append(buffer); + snprintf(buffer, SIZE, "Command Thread: %p\n", mAudioCommandThread.get()); + result.append(buffer); + snprintf(buffer, SIZE, "Tones Thread: %p\n", mTonePlaybackThread.get()); + result.append(buffer); + + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioPolicyService::dump(int fd, const Vector<String16>& args __unused) +{ + if (!dumpAllowed()) { + dumpPermissionDenial(fd); + } else { + bool locked = tryLock(mLock); + if (!locked) { + String8 result(kDeadlockedString); + write(fd, result.string(), result.size()); + } + + dumpInternals(fd); + if (mAudioCommandThread != 0) { + mAudioCommandThread->dump(fd); + } + if (mTonePlaybackThread != 0) { + mTonePlaybackThread->dump(fd); + } + +#ifdef USE_LEGACY_AUDIO_POLICY + if (mpAudioPolicy) { + mpAudioPolicy->dump(mpAudioPolicy, fd); + } +#else + if (mAudioPolicyManager) { + mAudioPolicyManager->dump(fd); + } +#endif + + if (locked) mLock.unlock(); + } + return NO_ERROR; +} + +status_t AudioPolicyService::dumpPermissionDenial(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "Permission Denial: " + "can't dump AudioPolicyService from pid=%d, uid=%d\n", + IPCThreadState::self()->getCallingPid(), + IPCThreadState::self()->getCallingUid()); + result.append(buffer); + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioPolicyService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + return BnAudioPolicyService::onTransact(code, data, reply, flags); +} + + +// ----------- AudioPolicyService::AudioCommandThread implementation ---------- + +AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name, + const wp<AudioPolicyService>& service) + : Thread(false), mName(name), mService(service) +{ + mpToneGenerator = NULL; +} + + +AudioPolicyService::AudioCommandThread::~AudioCommandThread() +{ + if (!mAudioCommands.isEmpty()) { + release_wake_lock(mName.string()); + } + mAudioCommands.clear(); + delete mpToneGenerator; +} + +void AudioPolicyService::AudioCommandThread::onFirstRef() +{ + run(mName.string(), ANDROID_PRIORITY_AUDIO); +} + +bool AudioPolicyService::AudioCommandThread::threadLoop() +{ + nsecs_t waitTime = INT64_MAX; + + mLock.lock(); + while (!exitPending()) + { + sp<AudioPolicyService> svc; + while (!mAudioCommands.isEmpty() && !exitPending()) { + nsecs_t curTime = systemTime(); + // commands are sorted by increasing time stamp: execute them from index 0 and up + if (mAudioCommands[0]->mTime <= curTime) { + sp<AudioCommand> command = mAudioCommands[0]; + mAudioCommands.removeAt(0); + mLastCommand = command; + + switch (command->mCommand) { + case START_TONE: { + mLock.unlock(); + ToneData *data = (ToneData *)command->mParam.get(); + ALOGV("AudioCommandThread() processing start tone %d on stream %d", + data->mType, data->mStream); + delete mpToneGenerator; + mpToneGenerator = new ToneGenerator(data->mStream, 1.0); + mpToneGenerator->startTone(data->mType); + mLock.lock(); + }break; + case STOP_TONE: { + mLock.unlock(); + ALOGV("AudioCommandThread() processing stop tone"); + if (mpToneGenerator != NULL) { + mpToneGenerator->stopTone(); + delete mpToneGenerator; + mpToneGenerator = NULL; + } + mLock.lock(); + }break; + case SET_VOLUME: { + VolumeData *data = (VolumeData *)command->mParam.get(); + ALOGV("AudioCommandThread() processing set volume stream %d, \ + volume %f, output %d", data->mStream, data->mVolume, data->mIO); + command->mStatus = AudioSystem::setStreamVolume(data->mStream, + data->mVolume, + data->mIO); + }break; + case SET_PARAMETERS: { + ParametersData *data = (ParametersData *)command->mParam.get(); + ALOGV("AudioCommandThread() processing set parameters string %s, io %d", + data->mKeyValuePairs.string(), data->mIO); + command->mStatus = AudioSystem::setParameters(data->mIO, data->mKeyValuePairs); + }break; + case SET_VOICE_VOLUME: { + VoiceVolumeData *data = (VoiceVolumeData *)command->mParam.get(); + ALOGV("AudioCommandThread() processing set voice volume volume %f", + data->mVolume); + command->mStatus = AudioSystem::setVoiceVolume(data->mVolume); + }break; + case STOP_OUTPUT: { + StopOutputData *data = (StopOutputData *)command->mParam.get(); + ALOGV("AudioCommandThread() processing stop output %d", + data->mIO); + svc = mService.promote(); + if (svc == 0) { + break; + } + mLock.unlock(); + svc->doStopOutput(data->mIO, data->mStream, data->mSession); + mLock.lock(); + }break; + case RELEASE_OUTPUT: { + ReleaseOutputData *data = (ReleaseOutputData *)command->mParam.get(); + ALOGV("AudioCommandThread() processing release output %d", + data->mIO); + svc = mService.promote(); + if (svc == 0) { + break; + } + mLock.unlock(); + svc->doReleaseOutput(data->mIO); + mLock.lock(); + }break; + case CREATE_AUDIO_PATCH: { + CreateAudioPatchData *data = (CreateAudioPatchData *)command->mParam.get(); + ALOGV("AudioCommandThread() processing create audio patch"); + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + command->mStatus = PERMISSION_DENIED; + } else { + command->mStatus = af->createAudioPatch(&data->mPatch, &data->mHandle); + } + } break; + case RELEASE_AUDIO_PATCH: { + ReleaseAudioPatchData *data = (ReleaseAudioPatchData *)command->mParam.get(); + ALOGV("AudioCommandThread() processing release audio patch"); + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + command->mStatus = PERMISSION_DENIED; + } else { + command->mStatus = af->releaseAudioPatch(data->mHandle); + } + } break; + case UPDATE_AUDIOPORT_LIST: { + ALOGV("AudioCommandThread() processing update audio port list"); + 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"); + 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); + } + { + Mutex::Autolock _l(command->mLock); + if (command->mWaitStatus) { + command->mWaitStatus = false; + command->mCond.signal(); + } + } + waitTime = INT64_MAX; + } else { + waitTime = mAudioCommands[0]->mTime - curTime; + break; + } + } + // release mLock before releasing strong reference on the service as + // AudioPolicyService destructor calls AudioCommandThread::exit() which acquires mLock. + mLock.unlock(); + svc.clear(); + mLock.lock(); + if (!exitPending() && mAudioCommands.isEmpty()) { + // release delayed commands wake lock + release_wake_lock(mName.string()); + ALOGV("AudioCommandThread() going to sleep"); + mWaitWorkCV.waitRelative(mLock, waitTime); + ALOGV("AudioCommandThread() waking up"); + } + } + // release delayed commands wake lock before quitting + if (!mAudioCommands.isEmpty()) { + release_wake_lock(mName.string()); + } + mLock.unlock(); + return false; +} + +status_t AudioPolicyService::AudioCommandThread::dump(int fd) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "AudioCommandThread %p Dump\n", this); + result.append(buffer); + write(fd, result.string(), result.size()); + + bool locked = tryLock(mLock); + if (!locked) { + String8 result2(kCmdDeadlockedString); + write(fd, result2.string(), result2.size()); + } + + snprintf(buffer, SIZE, "- Commands:\n"); + result = String8(buffer); + result.append(" Command Time Wait pParam\n"); + for (size_t i = 0; i < mAudioCommands.size(); i++) { + mAudioCommands[i]->dump(buffer, SIZE); + result.append(buffer); + } + result.append(" Last Command\n"); + if (mLastCommand != 0) { + mLastCommand->dump(buffer, SIZE); + result.append(buffer); + } else { + result.append(" none\n"); + } + + write(fd, result.string(), result.size()); + + if (locked) mLock.unlock(); + + return NO_ERROR; +} + +void AudioPolicyService::AudioCommandThread::startToneCommand(ToneGenerator::tone_type type, + audio_stream_type_t stream) +{ + sp<AudioCommand> command = new AudioCommand(); + command->mCommand = START_TONE; + sp<ToneData> data = new ToneData(); + data->mType = type; + data->mStream = stream; + command->mParam = data; + ALOGV("AudioCommandThread() adding tone start type %d, stream %d", type, stream); + sendCommand(command); +} + +void AudioPolicyService::AudioCommandThread::stopToneCommand() +{ + sp<AudioCommand> command = new AudioCommand(); + command->mCommand = STOP_TONE; + ALOGV("AudioCommandThread() adding tone stop"); + sendCommand(command); +} + +status_t AudioPolicyService::AudioCommandThread::volumeCommand(audio_stream_type_t stream, + float volume, + audio_io_handle_t output, + int delayMs) +{ + sp<AudioCommand> command = new AudioCommand(); + command->mCommand = SET_VOLUME; + sp<VolumeData> data = new VolumeData(); + data->mStream = stream; + data->mVolume = volume; + data->mIO = output; + command->mParam = data; + command->mWaitStatus = true; + ALOGV("AudioCommandThread() adding set volume stream %d, volume %f, output %d", + stream, volume, output); + return sendCommand(command, delayMs); +} + +status_t AudioPolicyService::AudioCommandThread::parametersCommand(audio_io_handle_t ioHandle, + const char *keyValuePairs, + int delayMs) +{ + sp<AudioCommand> command = new AudioCommand(); + command->mCommand = SET_PARAMETERS; + sp<ParametersData> data = new ParametersData(); + data->mIO = ioHandle; + data->mKeyValuePairs = String8(keyValuePairs); + command->mParam = data; + command->mWaitStatus = true; + ALOGV("AudioCommandThread() adding set parameter string %s, io %d ,delay %d", + keyValuePairs, ioHandle, delayMs); + return sendCommand(command, delayMs); +} + +status_t AudioPolicyService::AudioCommandThread::voiceVolumeCommand(float volume, int delayMs) +{ + sp<AudioCommand> command = new AudioCommand(); + command->mCommand = SET_VOICE_VOLUME; + sp<VoiceVolumeData> data = new VoiceVolumeData(); + data->mVolume = volume; + command->mParam = data; + command->mWaitStatus = true; + ALOGV("AudioCommandThread() adding set voice volume volume %f", volume); + return sendCommand(command, delayMs); +} + +void AudioPolicyService::AudioCommandThread::stopOutputCommand(audio_io_handle_t output, + audio_stream_type_t stream, + int session) +{ + sp<AudioCommand> command = new AudioCommand(); + command->mCommand = STOP_OUTPUT; + sp<StopOutputData> data = new StopOutputData(); + data->mIO = output; + data->mStream = stream; + data->mSession = session; + command->mParam = data; + ALOGV("AudioCommandThread() adding stop output %d", output); + sendCommand(command); +} + +void AudioPolicyService::AudioCommandThread::releaseOutputCommand(audio_io_handle_t output) +{ + sp<AudioCommand> command = new AudioCommand(); + command->mCommand = RELEASE_OUTPUT; + sp<ReleaseOutputData> data = new ReleaseOutputData(); + data->mIO = output; + command->mParam = data; + ALOGV("AudioCommandThread() adding release output %d", output); + sendCommand(command); +} + +status_t AudioPolicyService::AudioCommandThread::createAudioPatchCommand( + const struct audio_patch *patch, + audio_patch_handle_t *handle, + int delayMs) +{ + status_t status = NO_ERROR; + + sp<AudioCommand> command = new AudioCommand(); + command->mCommand = CREATE_AUDIO_PATCH; + CreateAudioPatchData *data = new CreateAudioPatchData(); + data->mPatch = *patch; + data->mHandle = *handle; + command->mParam = data; + command->mWaitStatus = true; + ALOGV("AudioCommandThread() adding create patch delay %d", delayMs); + status = sendCommand(command, delayMs); + if (status == NO_ERROR) { + *handle = data->mHandle; + } + return status; +} + +status_t AudioPolicyService::AudioCommandThread::releaseAudioPatchCommand(audio_patch_handle_t handle, + int delayMs) +{ + sp<AudioCommand> command = new AudioCommand(); + command->mCommand = RELEASE_AUDIO_PATCH; + ReleaseAudioPatchData *data = new ReleaseAudioPatchData(); + data->mHandle = handle; + command->mParam = data; + command->mWaitStatus = true; + ALOGV("AudioCommandThread() adding release patch delay %d", delayMs); + 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) +{ + { + Mutex::Autolock _l(mLock); + insertCommand_l(command, delayMs); + mWaitWorkCV.signal(); + } + Mutex::Autolock _l(command->mLock); + while (command->mWaitStatus) { + nsecs_t timeOutNs = kAudioCommandTimeoutNs + milliseconds(delayMs); + if (command->mCond.waitRelative(command->mLock, timeOutNs) != NO_ERROR) { + command->mStatus = TIMED_OUT; + command->mWaitStatus = false; + } + } + return command->mStatus; +} + +// insertCommand_l() must be called with mLock held +void AudioPolicyService::AudioCommandThread::insertCommand_l(sp<AudioCommand>& command, int delayMs) +{ + ssize_t i; // not size_t because i will count down to -1 + Vector < sp<AudioCommand> > removedCommands; + command->mTime = systemTime() + milliseconds(delayMs); + + // acquire wake lock to make sure delayed commands are processed + if (mAudioCommands.isEmpty()) { + acquire_wake_lock(PARTIAL_WAKE_LOCK, mName.string()); + } + + // check same pending commands with later time stamps and eliminate them + for (i = mAudioCommands.size()-1; i >= 0; i--) { + sp<AudioCommand> command2 = mAudioCommands[i]; + // commands are sorted by increasing time stamp: no need to scan the rest of mAudioCommands + if (command2->mTime <= command->mTime) break; + + // create audio patch or release audio patch commands are equivalent + // with regard to filtering + if ((command->mCommand == CREATE_AUDIO_PATCH) || + (command->mCommand == RELEASE_AUDIO_PATCH)) { + if ((command2->mCommand != CREATE_AUDIO_PATCH) && + (command2->mCommand != RELEASE_AUDIO_PATCH)) { + continue; + } + } else if (command2->mCommand != command->mCommand) continue; + + switch (command->mCommand) { + case SET_PARAMETERS: { + ParametersData *data = (ParametersData *)command->mParam.get(); + ParametersData *data2 = (ParametersData *)command2->mParam.get(); + if (data->mIO != data2->mIO) break; + ALOGV("Comparing parameter command %s to new command %s", + data2->mKeyValuePairs.string(), data->mKeyValuePairs.string()); + AudioParameter param = AudioParameter(data->mKeyValuePairs); + AudioParameter param2 = AudioParameter(data2->mKeyValuePairs); + for (size_t j = 0; j < param.size(); j++) { + String8 key; + String8 value; + param.getAt(j, key, value); + for (size_t k = 0; k < param2.size(); k++) { + String8 key2; + String8 value2; + param2.getAt(k, key2, value2); + if (key2 == key) { + param2.remove(key2); + ALOGV("Filtering out parameter %s", key2.string()); + break; + } + } + } + // if all keys have been filtered out, remove the command. + // otherwise, update the key value pairs + if (param2.size() == 0) { + removedCommands.add(command2); + } else { + data2->mKeyValuePairs = param2.toString(); + } + command->mTime = command2->mTime; + // force delayMs to non 0 so that code below does not request to wait for + // command status as the command is now delayed + delayMs = 1; + } break; + + case SET_VOLUME: { + VolumeData *data = (VolumeData *)command->mParam.get(); + VolumeData *data2 = (VolumeData *)command2->mParam.get(); + if (data->mIO != data2->mIO) break; + if (data->mStream != data2->mStream) break; + ALOGV("Filtering out volume command on output %d for stream %d", + data->mIO, data->mStream); + removedCommands.add(command2); + command->mTime = command2->mTime; + // force delayMs to non 0 so that code below does not request to wait for + // command status as the command is now delayed + delayMs = 1; + } break; + + case CREATE_AUDIO_PATCH: + case RELEASE_AUDIO_PATCH: { + audio_patch_handle_t handle; + if (command->mCommand == CREATE_AUDIO_PATCH) { + handle = ((CreateAudioPatchData *)command->mParam.get())->mHandle; + } else { + handle = ((ReleaseAudioPatchData *)command->mParam.get())->mHandle; + } + audio_patch_handle_t handle2; + if (command2->mCommand == CREATE_AUDIO_PATCH) { + handle2 = ((CreateAudioPatchData *)command2->mParam.get())->mHandle; + } else { + handle2 = ((ReleaseAudioPatchData *)command2->mParam.get())->mHandle; + } + if (handle != handle2) break; + ALOGV("Filtering out %s audio patch command for handle %d", + (command->mCommand == CREATE_AUDIO_PATCH) ? "create" : "release", handle); + removedCommands.add(command2); + command->mTime = command2->mTime; + // force delayMs to non 0 so that code below does not request to wait for + // command status as the command is now delayed + delayMs = 1; + } break; + + case START_TONE: + case STOP_TONE: + default: + break; + } + } + + // remove filtered commands + for (size_t j = 0; j < removedCommands.size(); j++) { + // removed commands always have time stamps greater than current command + for (size_t k = i + 1; k < mAudioCommands.size(); k++) { + if (mAudioCommands[k].get() == removedCommands[j].get()) { + ALOGV("suppressing command: %d", mAudioCommands[k]->mCommand); + mAudioCommands.removeAt(k); + break; + } + } + } + removedCommands.clear(); + + // Disable wait for status if delay is not 0 + if (delayMs != 0) { + command->mWaitStatus = false; + } + + // insert command at the right place according to its time stamp + ALOGV("inserting command: %d at index %zd, num commands %zu", + command->mCommand, i+1, mAudioCommands.size()); + mAudioCommands.insertAt(command, i + 1); +} + +void AudioPolicyService::AudioCommandThread::exit() +{ + ALOGV("AudioCommandThread::exit"); + { + AutoMutex _l(mLock); + requestExit(); + mWaitWorkCV.signal(); + } + requestExitAndWait(); +} + +void AudioPolicyService::AudioCommandThread::AudioCommand::dump(char* buffer, size_t size) +{ + snprintf(buffer, size, " %02d %06d.%03d %01u %p\n", + mCommand, + (int)ns2s(mTime), + (int)ns2ms(mTime)%1000, + mWaitStatus, + mParam.get()); +} + +/******* helpers for the service_ops callbacks defined below *********/ +void AudioPolicyService::setParameters(audio_io_handle_t ioHandle, + const char *keyValuePairs, + int delayMs) +{ + mAudioCommandThread->parametersCommand(ioHandle, keyValuePairs, + delayMs); +} + +int AudioPolicyService::setStreamVolume(audio_stream_type_t stream, + float volume, + audio_io_handle_t output, + int delayMs) +{ + return (int)mAudioCommandThread->volumeCommand(stream, volume, + output, delayMs); +} + +int AudioPolicyService::startTone(audio_policy_tone_t tone, + audio_stream_type_t stream) +{ + if (tone != AUDIO_POLICY_TONE_IN_CALL_NOTIFICATION) { + ALOGE("startTone: illegal tone requested (%d)", tone); + } + if (stream != AUDIO_STREAM_VOICE_CALL) { + ALOGE("startTone: illegal stream (%d) requested for tone %d", stream, + tone); + } + mTonePlaybackThread->startToneCommand(ToneGenerator::TONE_SUP_CALL_WAITING, + AUDIO_STREAM_VOICE_CALL); + return 0; +} + +int AudioPolicyService::stopTone() +{ + mTonePlaybackThread->stopToneCommand(); + return 0; +} + +int AudioPolicyService::setVoiceVolume(float volume, int delayMs) +{ + return (int)mAudioCommandThread->voiceVolumeCommand(volume, delayMs); +} + +extern "C" { +audio_module_handle_t aps_load_hw_module(void *service __unused, + const char *name); +audio_io_handle_t aps_open_output(void *service __unused, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + uint32_t *pLatencyMs, + audio_output_flags_t flags); + +audio_io_handle_t aps_open_output_on_module(void *service __unused, + audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + uint32_t *pLatencyMs, + audio_output_flags_t flags, + const audio_offload_info_t *offloadInfo); +audio_io_handle_t aps_open_dup_output(void *service __unused, + audio_io_handle_t output1, + audio_io_handle_t output2); +int aps_close_output(void *service __unused, audio_io_handle_t output); +int aps_suspend_output(void *service __unused, audio_io_handle_t output); +int aps_restore_output(void *service __unused, audio_io_handle_t output); +audio_io_handle_t aps_open_input(void *service __unused, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask, + audio_in_acoustics_t acoustics __unused); +audio_io_handle_t aps_open_input_on_module(void *service __unused, + audio_module_handle_t module, + audio_devices_t *pDevices, + uint32_t *pSamplingRate, + audio_format_t *pFormat, + audio_channel_mask_t *pChannelMask); +int aps_close_input(void *service __unused, audio_io_handle_t input); +int aps_invalidate_stream(void *service __unused, audio_stream_type_t stream); +int aps_move_effects(void *service __unused, int session, + audio_io_handle_t src_output, + audio_io_handle_t dst_output); +char * aps_get_parameters(void *service __unused, audio_io_handle_t io_handle, + const char *keys); +void aps_set_parameters(void *service, audio_io_handle_t io_handle, + const char *kv_pairs, int delay_ms); +int aps_set_stream_volume(void *service, audio_stream_type_t stream, + float volume, audio_io_handle_t output, + int delay_ms); +int aps_start_tone(void *service, audio_policy_tone_t tone, + audio_stream_type_t stream); +int aps_stop_tone(void *service); +int aps_set_voice_volume(void *service, float volume, int delay_ms); +}; + +namespace { + struct audio_policy_service_ops aps_ops = { + .open_output = aps_open_output, + .open_duplicate_output = aps_open_dup_output, + .close_output = aps_close_output, + .suspend_output = aps_suspend_output, + .restore_output = aps_restore_output, + .open_input = aps_open_input, + .close_input = aps_close_input, + .set_stream_volume = aps_set_stream_volume, + .invalidate_stream = aps_invalidate_stream, + .set_parameters = aps_set_parameters, + .get_parameters = aps_get_parameters, + .start_tone = aps_start_tone, + .stop_tone = aps_stop_tone, + .set_voice_volume = aps_set_voice_volume, + .move_effects = aps_move_effects, + .load_hw_module = aps_load_hw_module, + .open_output_on_module = aps_open_output_on_module, + .open_input_on_module = aps_open_input_on_module, + }; +}; // namespace <unnamed> + +}; // namespace android diff --git a/services/audiopolicy/AudioPolicyService.h b/services/audiopolicy/AudioPolicyService.h new file mode 100644 index 0000000..0044e7a --- /dev/null +++ b/services/audiopolicy/AudioPolicyService.h @@ -0,0 +1,500 @@ +/* + * 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_AUDIOPOLICYSERVICE_H +#define ANDROID_AUDIOPOLICYSERVICE_H + +#include <cutils/misc.h> +#include <cutils/config_utils.h> +#include <cutils/compiler.h> +#include <utils/String8.h> +#include <utils/Vector.h> +#include <utils/SortedVector.h> +#include <binder/BinderService.h> +#include <system/audio.h> +#include <system/audio_policy.h> +#include <hardware/audio_policy.h> +#include <media/IAudioPolicyService.h> +#include <media/ToneGenerator.h> +#include <media/AudioEffect.h> +#include <hardware_legacy/AudioPolicyInterface.h> +#include "AudioPolicyEffects.h" +#include "AudioPolicyManager.h" + + +namespace android { + +// ---------------------------------------------------------------------------- + +class AudioPolicyService : + public BinderService<AudioPolicyService>, + public BnAudioPolicyService, + public IBinder::DeathRecipient +{ + friend class BinderService<AudioPolicyService>; + +public: + // for BinderService + static const char *getServiceName() ANDROID_API { return "media.audio_policy"; } + + virtual status_t dump(int fd, const Vector<String16>& args); + + // + // BnAudioPolicyService (see AudioPolicyInterface for method descriptions) + // + + virtual status_t setDeviceConnectionState(audio_devices_t device, + audio_policy_dev_state_t state, + const char *device_address); + virtual audio_policy_dev_state_t getDeviceConnectionState( + audio_devices_t device, + const char *device_address); + virtual status_t setPhoneState(audio_mode_t state); + virtual status_t setForceUse(audio_policy_force_use_t usage, audio_policy_forced_cfg_t config); + virtual audio_policy_forced_cfg_t getForceUse(audio_policy_force_use_t usage); + virtual audio_io_handle_t getOutput(audio_stream_type_t stream, + uint32_t samplingRate = 0, + audio_format_t format = AUDIO_FORMAT_DEFAULT, + audio_channel_mask_t channelMask = 0, + audio_output_flags_t flags = + AUDIO_OUTPUT_FLAG_NONE, + const audio_offload_info_t *offloadInfo = NULL); + virtual audio_io_handle_t getOutputForAttr(const audio_attributes_t *attr, + uint32_t samplingRate = 0, + audio_format_t format = AUDIO_FORMAT_DEFAULT, + audio_channel_mask_t channelMask = 0, + audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE, + const audio_offload_info_t *offloadInfo = NULL); + virtual status_t startOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session = 0); + virtual status_t stopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session = 0); + virtual void releaseOutput(audio_io_handle_t output); + virtual audio_io_handle_t getInput(audio_source_t inputSource, + uint32_t samplingRate, + audio_format_t format, + audio_channel_mask_t channelMask, + int audioSession, + audio_input_flags_t flags); + virtual status_t startInput(audio_io_handle_t input, + audio_session_t session); + virtual status_t stopInput(audio_io_handle_t input, + audio_session_t session); + virtual void releaseInput(audio_io_handle_t input, + audio_session_t session); + virtual status_t initStreamVolume(audio_stream_type_t stream, + int indexMin, + int indexMax); + virtual status_t setStreamVolumeIndex(audio_stream_type_t stream, + int index, + audio_devices_t device); + virtual status_t getStreamVolumeIndex(audio_stream_type_t stream, + int *index, + audio_devices_t device); + + virtual uint32_t getStrategyForStream(audio_stream_type_t stream); + virtual audio_devices_t getDevicesForStream(audio_stream_type_t stream); + + virtual audio_io_handle_t getOutputForEffect(const effect_descriptor_t *desc); + virtual status_t registerEffect(const effect_descriptor_t *desc, + audio_io_handle_t io, + uint32_t strategy, + int session, + int id); + virtual status_t unregisterEffect(int id); + virtual status_t setEffectEnabled(int id, bool enabled); + virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const; + virtual bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs = 0) const; + virtual bool isSourceActive(audio_source_t source) const; + + virtual status_t queryDefaultPreProcessing(int audioSession, + effect_descriptor_t *descriptors, + uint32_t *count); + virtual status_t onTransact( + uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags); + + // IBinder::DeathRecipient + virtual void binderDied(const wp<IBinder>& who); + + // + // Helpers for the struct audio_policy_service_ops implementation. + // This is used by the audio policy manager for certain operations that + // are implemented by the policy service. + // + virtual void setParameters(audio_io_handle_t ioHandle, + const char *keyValuePairs, + int delayMs); + + virtual status_t setStreamVolume(audio_stream_type_t stream, + float volume, + audio_io_handle_t output, + int delayMs = 0); + virtual status_t startTone(audio_policy_tone_t tone, audio_stream_type_t stream); + virtual status_t stopTone(); + virtual status_t setVoiceVolume(float volume, int delayMs = 0); + virtual bool isOffloadSupported(const audio_offload_info_t &config); + + 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); + virtual status_t releaseAudioPatch(audio_patch_handle_t handle); + 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 registerClient(const sp<IAudioPolicyServiceClient>& client); + + virtual status_t acquireSoundTriggerSession(audio_session_t *session, + audio_io_handle_t *ioHandle, + audio_devices_t *device); + + virtual status_t releaseSoundTriggerSession(audio_session_t session); + + status_t doStopOutput(audio_io_handle_t output, + audio_stream_type_t stream, + int session = 0); + void doReleaseOutput(audio_io_handle_t output); + + status_t clientCreateAudioPatch(const struct audio_patch *patch, + audio_patch_handle_t *handle, + 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; + virtual ~AudioPolicyService(); + + status_t dumpInternals(int fd); + + // Thread used for tone playback and to send audio config commands to audio flinger + // For tone playback, using a separate thread is necessary to avoid deadlock with mLock because + // startTone() and stopTone() are normally called with mLock locked and requesting a tone start + // or stop will cause calls to AudioPolicyService and an attempt to lock mLock. + // For audio config commands, it is necessary because audio flinger requires that the calling + // process (user) has permission to modify audio settings. + class AudioCommandThread : public Thread { + class AudioCommand; + public: + + // commands for tone AudioCommand + enum { + START_TONE, + STOP_TONE, + SET_VOLUME, + SET_PARAMETERS, + SET_VOICE_VOLUME, + STOP_OUTPUT, + RELEASE_OUTPUT, + CREATE_AUDIO_PATCH, + RELEASE_AUDIO_PATCH, + UPDATE_AUDIOPORT_LIST, + UPDATE_AUDIOPATCH_LIST, + SET_AUDIOPORT_CONFIG, + }; + + AudioCommandThread (String8 name, const wp<AudioPolicyService>& service); + virtual ~AudioCommandThread(); + + status_t dump(int fd); + + // Thread virtuals + virtual void onFirstRef(); + virtual bool threadLoop(); + + void exit(); + void startToneCommand(ToneGenerator::tone_type type, + audio_stream_type_t stream); + void stopToneCommand(); + status_t volumeCommand(audio_stream_type_t stream, float volume, + audio_io_handle_t output, int delayMs = 0); + status_t parametersCommand(audio_io_handle_t ioHandle, + const char *keyValuePairs, int delayMs = 0); + status_t voiceVolumeCommand(float volume, int delayMs = 0); + void stopOutputCommand(audio_io_handle_t output, + audio_stream_type_t stream, + int session); + void releaseOutputCommand(audio_io_handle_t output); + status_t sendCommand(sp<AudioCommand>& command, int delayMs = 0); + void insertCommand_l(sp<AudioCommand>& command, int delayMs = 0); + status_t createAudioPatchCommand(const struct audio_patch *patch, + audio_patch_handle_t *handle, + 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: + class AudioCommandData; + + // descriptor for requested tone playback event + class AudioCommand: public RefBase { + + public: + AudioCommand() + : mCommand(-1), mStatus(NO_ERROR), mWaitStatus(false) {} + + void dump(char* buffer, size_t size); + + int mCommand; // START_TONE, STOP_TONE ... + nsecs_t mTime; // time stamp + Mutex mLock; // mutex associated to mCond + Condition mCond; // condition for status return + status_t mStatus; // command status + bool mWaitStatus; // true if caller is waiting for status + sp<AudioCommandData> mParam; // command specific parameter data + }; + + class AudioCommandData: public RefBase { + public: + virtual ~AudioCommandData() {} + protected: + AudioCommandData() {} + }; + + class ToneData : public AudioCommandData { + public: + ToneGenerator::tone_type mType; // tone type (START_TONE only) + audio_stream_type_t mStream; // stream type (START_TONE only) + }; + + class VolumeData : public AudioCommandData { + public: + audio_stream_type_t mStream; + float mVolume; + audio_io_handle_t mIO; + }; + + class ParametersData : public AudioCommandData { + public: + audio_io_handle_t mIO; + String8 mKeyValuePairs; + }; + + class VoiceVolumeData : public AudioCommandData { + public: + float mVolume; + }; + + class StopOutputData : public AudioCommandData { + public: + audio_io_handle_t mIO; + audio_stream_type_t mStream; + int mSession; + }; + + class ReleaseOutputData : public AudioCommandData { + public: + audio_io_handle_t mIO; + }; + + class CreateAudioPatchData : public AudioCommandData { + public: + struct audio_patch mPatch; + audio_patch_handle_t mHandle; + }; + + class ReleaseAudioPatchData : public AudioCommandData { + public: + 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 + ToneGenerator *mpToneGenerator; // the tone generator + sp<AudioCommand> mLastCommand; // last processed command (used by dump) + String8 mName; // string used by wake lock fo delayed commands + wp<AudioPolicyService> mService; + }; + + class AudioPolicyClient : public AudioPolicyClientInterface + { + public: + AudioPolicyClient(AudioPolicyService *service) : mAudioPolicyService(service) {} + virtual ~AudioPolicyClient() {} + + // + // Audio HW module functions + // + + // loads a HW module. + virtual audio_module_handle_t loadHwModule(const char *name); + + // + // Audio output Control functions + // + + // opens an audio output with the requested parameters. The parameter values can indicate to use the default values + // in case the audio policy manager has no specific requirements for the output being opened. + // When the function returns, the parameter values reflect the actual values used by the audio hardware output stream. + // The audio policy manager can check if the proposed parameters are suitable or not and act accordingly. + virtual status_t openOutput(audio_module_handle_t module, + audio_io_handle_t *output, + audio_config_t *config, + audio_devices_t *devices, + const String8& address, + uint32_t *latencyMs, + audio_output_flags_t flags); + // creates a special output that is duplicated to the two outputs passed as arguments. The duplication is performed by + // a special mixer thread in the AudioFlinger. + virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1, audio_io_handle_t output2); + // closes the output stream + virtual status_t closeOutput(audio_io_handle_t output); + // suspends the output. When an output is suspended, the corresponding audio hardware output stream is placed in + // standby and the AudioTracks attached to the mixer thread are still processed but the output mix is discarded. + virtual status_t suspendOutput(audio_io_handle_t output); + // restores a suspended output. + virtual status_t restoreOutput(audio_io_handle_t output); + + // + // Audio input Control functions + // + + // opens an audio input + virtual audio_io_handle_t openInput(audio_module_handle_t module, + audio_io_handle_t *input, + audio_config_t *config, + audio_devices_t *devices, + const String8& address, + audio_source_t source, + audio_input_flags_t flags); + // closes an audio input + virtual status_t closeInput(audio_io_handle_t input); + // + // misc control functions + // + + // set a stream volume for a particular output. For the same user setting, a given stream type can have different volumes + // for each output (destination device) it is attached to. + virtual status_t setStreamVolume(audio_stream_type_t stream, float volume, audio_io_handle_t output, int delayMs = 0); + + // invalidate a stream type, causing a reroute to an unspecified new output + virtual status_t invalidateStream(audio_stream_type_t stream); + + // function enabling to send proprietary informations directly from audio policy manager to audio hardware interface. + virtual void setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs, int delayMs = 0); + // function enabling to receive proprietary informations directly from audio hardware interface to audio policy manager. + virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys); + + // request the playback of a tone on the specified stream: used for instance to replace notification sounds when playing + // over a telephony device during a phone call. + virtual status_t startTone(audio_policy_tone_t tone, audio_stream_type_t stream); + virtual status_t stopTone(); + + // set down link audio volume. + virtual status_t setVoiceVolume(float volume, int delayMs = 0); + + // move effect to the specified output + virtual status_t moveEffects(int session, + audio_io_handle_t srcOutput, + audio_io_handle_t dstOutput); + + /* Create a patch between several source and sink ports */ + virtual status_t createAudioPatch(const struct audio_patch *patch, + audio_patch_handle_t *handle, + int delayMs); + + /* Release a patch */ + 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(); + + virtual audio_unique_id_t newAudioUniqueId(); + + 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; + }; + + // Internal dump utilities. + status_t dumpPermissionDenial(int fd); + + + mutable Mutex mLock; // prevents concurrent access to AudioPolicy manager functions changing + // device connection state or routing + sp<AudioCommandThread> mAudioCommandThread; // audio commands thread + sp<AudioCommandThread> mTonePlaybackThread; // tone playback thread + sp<AudioCommandThread> mOutputCommandThread; // process stop and release output + struct audio_policy_device *mpAudioPolicyDev; + struct audio_policy *mpAudioPolicy; + AudioPolicyInterface *mAudioPolicyManager; + AudioPolicyClient *mAudioPolicyClient; + + DefaultKeyedVector< uid_t, sp<NotificationClient> > mNotificationClients; + + // Manage all effects configured in audio_effects.conf + sp<AudioPolicyEffects> mAudioPolicyEffects; +}; + +}; // namespace android + +#endif // ANDROID_AUDIOPOLICYSERVICE_H diff --git a/services/audiopolicy/audio_policy.conf b/services/audiopolicy/audio_policy.conf new file mode 100644 index 0000000..9b83fef --- /dev/null +++ b/services/audiopolicy/audio_policy.conf @@ -0,0 +1,145 @@ +# +# Template audio policy configuration file +# + +# Global configuration section: +# - before audio HAL version 3.0: +# lists input and output devices always present on the device +# as well as the output device selected by default. +# Devices are designated by a string that corresponds to the enum in audio.h +# +# global_configuration { +# attached_output_devices AUDIO_DEVICE_OUT_SPEAKER +# default_output_device AUDIO_DEVICE_OUT_SPEAKER +# attached_input_devices AUDIO_DEVICE_IN_BUILTIN_MIC|AUDIO_DEVICE_IN_REMOTE_SUBMIX +# } +# +# - after and including audio HAL 3.0 the global_configuration section is included in each +# hardware module section. +# it also includes the audio HAL version of this hw module: +# global_configuration { +# ... +# audio_hal_version <major.minor> # audio HAL version in e.g. 3.0 +# } +# other attributes (attached devices, default device) have to be included in the +# global_configuration section of each hardware module + + +# audio hardware module section: contains descriptors for all audio hw modules present on the +# device. Each hw module node is named after the corresponding hw module library base name. +# For instance, "primary" corresponds to audio.primary.<device>.so. +# The "primary" module is mandatory and must include at least one output with +# AUDIO_OUTPUT_FLAG_PRIMARY flag. +# Each module descriptor contains one or more output profile descriptors and zero or more +# input profile descriptors. Each profile lists all the parameters supported by a given output +# or input stream category. +# The "channel_masks", "formats", "devices" and "flags" are specified using strings corresponding +# to enums in audio.h and audio_policy.h. They are concatenated by use of "|" without space or "\n". +# +# For audio HAL version posterior to 3.0 the following sections or sub sections can be present in +# a hw module section: +# - A "global_configuration" section: see above +# - Optionally a "devices" section: +# This section contains descriptors for audio devices with attributes like an address or a +# gain controller. The syntax for the devices section and device descriptor is as follows: +# devices { +# <device name> { # <device name>: any string without space +# type <device type> # <device type> e.g. AUDIO_DEVICE_OUT_SPEAKER +# address <address> # optional: device address, char string less than 64 in length +# } +# } +# - one or more "gains" sections can be present in a device descriptor section. +# If present, they describe the capabilities of gain controllers attached to this input or +# output device. e.g. : +# <device name> { # <device name>: any string without space +# type <device type> # <device type> e.g. AUDIO_DEVICE_OUT_SPEAKER +# address <address> # optional: device address, char string less than 64 in length +# gains { +# <gain name> { +# mode <gain modes supported> # e.g. AUDIO_GAIN_MODE_CHANNELS +# channel_mask <controlled channels> # needed if mode AUDIO_GAIN_MODE_CHANNELS +# min_value_mB <min value in millibel> +# max_value_mB <max value in millibel> +# default_value_mB <default value in millibel> +# step_value_mB <step value in millibel> +# min_ramp_ms <min duration in ms> # needed if mode AUDIO_GAIN_MODE_RAMP +# max_ramp_ms <max duration ms> # needed if mode AUDIO_GAIN_MODE_RAMP +# } +# } +# } +# - when a device descriptor is present, output and input profiles can refer to this device by +# its name in their "devices" section instead of specifying a device type. e.g. : +# outputs { +# primary { +# sampling_rates 44100 +# channel_masks AUDIO_CHANNEL_OUT_STEREO +# formats AUDIO_FORMAT_PCM_16_BIT +# devices <device name> +# flags AUDIO_OUTPUT_FLAG_PRIMARY +# } +# } +# sample audio_policy.conf file below + +audio_hw_modules { + primary { + global_configuration { + attached_output_devices AUDIO_DEVICE_OUT_SPEAKER + default_output_device AUDIO_DEVICE_OUT_SPEAKER + attached_input_devices AUDIO_DEVICE_IN_BUILTIN_MIC + audio_hal_version 3.0 + } + devices { + speaker { + type AUDIO_DEVICE_OUT_SPEAKER + gains { + gain_1 { + mode AUDIO_GAIN_MODE_JOINT + min_value_mB -8400 + max_value_mB 4000 + default_value_mB 0 + step_value_mB 100 + } + } + } + } + outputs { + primary { + sampling_rates 48000 + channel_masks AUDIO_CHANNEL_OUT_STEREO + formats AUDIO_FORMAT_PCM_16_BIT + devices speaker + flags AUDIO_OUTPUT_FLAG_PRIMARY + } + } + inputs { + primary { + sampling_rates 8000|16000 + channel_masks AUDIO_CHANNEL_IN_MONO + formats AUDIO_FORMAT_PCM_16_BIT + devices AUDIO_DEVICE_IN_BUILTIN_MIC + } + } + } + r_submix { + global_configuration { + attached_input_devices AUDIO_DEVICE_IN_REMOTE_SUBMIX + audio_hal_version 2.0 + } + outputs { + submix { + sampling_rates 48000 + channel_masks AUDIO_CHANNEL_OUT_STEREO + formats AUDIO_FORMAT_PCM_16_BIT + devices AUDIO_DEVICE_OUT_REMOTE_SUBMIX + } + } + inputs { + submix { + sampling_rates 48000 + channel_masks AUDIO_CHANNEL_IN_STEREO + formats AUDIO_FORMAT_PCM_16_BIT + devices AUDIO_DEVICE_IN_REMOTE_SUBMIX + } + } + } +} diff --git a/services/audiopolicy/audio_policy_conf.h b/services/audiopolicy/audio_policy_conf.h new file mode 100644 index 0000000..2535a67 --- /dev/null +++ b/services/audiopolicy/audio_policy_conf.h @@ -0,0 +1,77 @@ +/* + * 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" +#define AUDIO_HAL_VERSION_TAG "audio_hal_version" + +// 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 diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk index 51ba698..e184d97 100644 --- a/services/camera/libcameraservice/Android.mk +++ b/services/camera/libcameraservice/Android.mk @@ -1,3 +1,17 @@ +# Copyright 2010 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. + LOCAL_PATH:= $(call my-dir) # @@ -20,6 +34,7 @@ LOCAL_SRC_FILES:= \ api1/client2/JpegProcessor.cpp \ api1/client2/CallbackProcessor.cpp \ api1/client2/ZslProcessor.cpp \ + api1/client2/ZslProcessorInterface.cpp \ api1/client2/BurstCapture.cpp \ api1/client2/JpegCompressor.cpp \ api1/client2/CaptureSequencer.cpp \ @@ -33,6 +48,7 @@ LOCAL_SRC_FILES:= \ device3/Camera3InputStream.cpp \ device3/Camera3OutputStream.cpp \ device3/Camera3ZslStream.cpp \ + device3/Camera3DummyStream.cpp \ device3/StatusTracker.cpp \ gui/RingBufferConsumer.cpp \ utils/CameraTraces.cpp \ @@ -53,6 +69,7 @@ LOCAL_SHARED_LIBRARIES:= \ LOCAL_C_INCLUDES += \ system/media/camera/include \ + system/media/private/camera/include \ external/jpeg diff --git a/services/camera/libcameraservice/CameraDeviceFactory.cpp b/services/camera/libcameraservice/CameraDeviceFactory.cpp index 7fdf304..bfef50e 100644 --- a/services/camera/libcameraservice/CameraDeviceFactory.cpp +++ b/services/camera/libcameraservice/CameraDeviceFactory.cpp @@ -46,6 +46,8 @@ sp<CameraDeviceBase> CameraDeviceFactory::createDevice(int cameraId) { device = new Camera2Device(cameraId); break; case CAMERA_DEVICE_API_VERSION_3_0: + case CAMERA_DEVICE_API_VERSION_3_1: + case CAMERA_DEVICE_API_VERSION_3_2: device = new Camera3Device(cameraId); break; default: diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 9ce7daf..fd5a426 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -1,24 +1,24 @@ /* -** -** Copyright (C) 2008, 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. -*/ + * Copyright (C) 2008 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 "CameraService" //#define LOG_NDEBUG 0 #include <stdio.h> +#include <string.h> #include <sys/types.h> #include <pthread.h> @@ -32,10 +32,15 @@ #include <gui/Surface.h> #include <hardware/hardware.h> #include <media/AudioSystem.h> +#include <media/IMediaHTTPService.h> #include <media/mediaplayer.h> #include <utils/Errors.h> #include <utils/Log.h> #include <utils/String16.h> +#include <utils/Trace.h> +#include <system/camera_vendor_tags.h> +#include <system/camera_metadata.h> +#include <system/camera.h> #include "CameraService.h" #include "api1/CameraClient.h" @@ -130,6 +135,12 @@ void CameraService::onFirstRef() mModule->set_callbacks(this); } + VendorTagDescriptor::clearGlobalVendorTagDescriptor(); + + if (mModule->common.module_api_version >= CAMERA_MODULE_API_VERSION_2_2) { + setUpVendorTags(); + } + CameraDeviceFactory::registerService(this); } } @@ -141,6 +152,7 @@ CameraService::~CameraService() { } } + VendorTagDescriptor::clearGlobalVendorTagDescriptor(); gCameraService = NULL; } @@ -168,6 +180,9 @@ void CameraService::onDeviceStatusChanged(int cameraId, { Mutex::Autolock al(mServiceLock); + /* Remove cached parameters from shim cache */ + mShimParams.removeItem(cameraId); + /* Find all clients that we need to disconnect */ sp<BasicClient> client = mClient[cameraId].promote(); if (client.get() != NULL) { @@ -220,12 +235,99 @@ status_t CameraService::getCameraInfo(int cameraId, } struct camera_info info; - status_t rc = mModule->get_camera_info(cameraId, &info); + status_t rc = filterGetInfoErrorCode( + mModule->get_camera_info(cameraId, &info)); cameraInfo->facing = info.facing; cameraInfo->orientation = info.orientation; return rc; } + +status_t CameraService::generateShimMetadata(int cameraId, /*out*/CameraMetadata* cameraInfo) { + status_t ret = OK; + struct CameraInfo info; + if ((ret = getCameraInfo(cameraId, &info)) != OK) { + return ret; + } + + CameraMetadata shimInfo; + int32_t orientation = static_cast<int32_t>(info.orientation); + if ((ret = shimInfo.update(ANDROID_SENSOR_ORIENTATION, &orientation, 1)) != OK) { + return ret; + } + + uint8_t facing = (info.facing == CAMERA_FACING_FRONT) ? + ANDROID_LENS_FACING_FRONT : ANDROID_LENS_FACING_BACK; + if ((ret = shimInfo.update(ANDROID_LENS_FACING, &facing, 1)) != OK) { + return ret; + } + + CameraParameters shimParams; + if ((ret = getLegacyParametersLazy(cameraId, /*out*/&shimParams)) != OK) { + // Error logged by callee + return ret; + } + + Vector<Size> sizes; + Vector<Size> jpegSizes; + Vector<int32_t> formats; + const char* supportedPreviewFormats; + { + shimParams.getSupportedPreviewSizes(/*out*/sizes); + shimParams.getSupportedPreviewFormats(/*out*/formats); + shimParams.getSupportedPictureSizes(/*out*/jpegSizes); + } + + // Always include IMPLEMENTATION_DEFINED + formats.add(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED); + + const size_t INTS_PER_CONFIG = 4; + + // Build available stream configurations metadata + size_t streamConfigSize = (sizes.size() * formats.size() + jpegSizes.size()) * INTS_PER_CONFIG; + + Vector<int32_t> streamConfigs; + streamConfigs.setCapacity(streamConfigSize); + + for (size_t i = 0; i < formats.size(); ++i) { + for (size_t j = 0; j < sizes.size(); ++j) { + streamConfigs.add(formats[i]); + streamConfigs.add(sizes[j].width); + streamConfigs.add(sizes[j].height); + streamConfigs.add(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT); + } + } + + for (size_t i = 0; i < jpegSizes.size(); ++i) { + streamConfigs.add(HAL_PIXEL_FORMAT_BLOB); + streamConfigs.add(jpegSizes[i].width); + streamConfigs.add(jpegSizes[i].height); + streamConfigs.add(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT); + } + + if ((ret = shimInfo.update(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, + streamConfigs.array(), streamConfigSize)) != OK) { + return ret; + } + + int64_t fakeMinFrames[0]; + // TODO: Fixme, don't fake min frame durations. + if ((ret = shimInfo.update(ANDROID_SCALER_AVAILABLE_MIN_FRAME_DURATIONS, + fakeMinFrames, 0)) != OK) { + return ret; + } + + int64_t fakeStalls[0]; + // TODO: Fixme, don't fake stall durations. + if ((ret = shimInfo.update(ANDROID_SCALER_AVAILABLE_STALL_DURATIONS, + fakeStalls, 0)) != OK) { + return ret; + } + + *cameraInfo = shimInfo; + return OK; +} + status_t CameraService::getCameraCharacteristics(int cameraId, CameraMetadata* cameraInfo) { if (!cameraInfo) { @@ -238,37 +340,51 @@ status_t CameraService::getCameraCharacteristics(int cameraId, return -ENODEV; } - if (mModule->common.module_api_version < CAMERA_MODULE_API_VERSION_2_0) { - // TODO: Remove this check once HAL1 shim is in place. - ALOGE("%s: Only HAL module version V2 or higher supports static metadata", __FUNCTION__); - return BAD_VALUE; - } - if (cameraId < 0 || cameraId >= mNumberOfCameras) { ALOGE("%s: Invalid camera id: %d", __FUNCTION__, cameraId); return BAD_VALUE; } int facing; - if (getDeviceVersion(cameraId, &facing) == CAMERA_DEVICE_API_VERSION_1_0) { - // TODO: Remove this check once HAL1 shim is in place. - ALOGE("%s: HAL1 doesn't support static metadata yet", __FUNCTION__); - return BAD_VALUE; - } + status_t ret = OK; + if (mModule->common.module_api_version < CAMERA_MODULE_API_VERSION_2_0 || + getDeviceVersion(cameraId, &facing) <= CAMERA_DEVICE_API_VERSION_2_1 ) { + /** + * Backwards compatibility mode for old HALs: + * - Convert CameraInfo into static CameraMetadata properties. + * - Retrieve cached CameraParameters for this camera. If none exist, + * attempt to open CameraClient and retrieve the CameraParameters. + * - Convert cached CameraParameters into static CameraMetadata + * properties. + */ + ALOGI("%s: Switching to HAL1 shim implementation...", __FUNCTION__); + + if ((ret = generateShimMetadata(cameraId, cameraInfo)) != OK) { + return ret; + } - if (getDeviceVersion(cameraId, &facing) <= CAMERA_DEVICE_API_VERSION_2_1) { - // Disable HAL2.x support for camera2 API for now. - ALOGW("%s: HAL2.x doesn't support getCameraCharacteristics for now", __FUNCTION__); - return BAD_VALUE; + } else { + /** + * Normal HAL 2.1+ codepath. + */ + struct camera_info info; + ret = filterGetInfoErrorCode(mModule->get_camera_info(cameraId, &info)); + *cameraInfo = info.static_camera_characteristics; } - struct camera_info info; - status_t ret = mModule->get_camera_info(cameraId, &info); - *cameraInfo = info.static_camera_characteristics; - return ret; } +status_t CameraService::getCameraVendorTagDescriptor(/*out*/sp<VendorTagDescriptor>& desc) { + if (!mModule) { + ALOGE("%s: camera hardware module doesn't exist", __FUNCTION__); + return -ENODEV; + } + + desc = VendorTagDescriptor::getGlobalVendorTagDescriptor(); + return OK; +} + int CameraService::getDeviceVersion(int cameraId, int* facing) { struct camera_info info; if (mModule->get_camera_info(cameraId, &info) != OK) { @@ -289,21 +405,162 @@ int CameraService::getDeviceVersion(int cameraId, int* facing) { return deviceVersion; } -bool CameraService::isValidCameraId(int cameraId) { - int facing; - int deviceVersion = getDeviceVersion(cameraId, &facing); +status_t CameraService::filterOpenErrorCode(status_t err) { + switch(err) { + case NO_ERROR: + case -EBUSY: + case -EINVAL: + case -EUSERS: + return err; + default: + break; + } + return -ENODEV; +} - switch(deviceVersion) { - case CAMERA_DEVICE_API_VERSION_1_0: - case CAMERA_DEVICE_API_VERSION_2_0: - case CAMERA_DEVICE_API_VERSION_2_1: - case CAMERA_DEVICE_API_VERSION_3_0: - return true; - default: +status_t CameraService::filterGetInfoErrorCode(status_t err) { + switch(err) { + case NO_ERROR: + case -EINVAL: + return err; + default: + break; + } + return -ENODEV; +} + +bool CameraService::setUpVendorTags() { + vendor_tag_ops_t vOps = vendor_tag_ops_t(); + + // Check if vendor operations have been implemented + if (mModule->get_vendor_tag_ops == NULL) { + ALOGI("%s: No vendor tags defined for this device.", __FUNCTION__); return false; } - return false; + ATRACE_BEGIN("camera3->get_metadata_vendor_tag_ops"); + mModule->get_vendor_tag_ops(&vOps); + ATRACE_END(); + + // Ensure all vendor operations are present + if (vOps.get_tag_count == NULL || vOps.get_all_tags == NULL || + vOps.get_section_name == NULL || vOps.get_tag_name == NULL || + vOps.get_tag_type == NULL) { + ALOGE("%s: Vendor tag operations not fully defined. Ignoring definitions." + , __FUNCTION__); + return false; + } + + // Read all vendor tag definitions into a descriptor + sp<VendorTagDescriptor> desc; + status_t res; + if ((res = VendorTagDescriptor::createDescriptorFromOps(&vOps, /*out*/desc)) + != OK) { + ALOGE("%s: Could not generate descriptor from vendor tag operations," + "received error %s (%d). Camera clients will not be able to use" + "vendor tags", __FUNCTION__, strerror(res), res); + return false; + } + + // Set the global descriptor to use with camera metadata + VendorTagDescriptor::setAsGlobalVendorTagDescriptor(desc); + return true; +} + +status_t CameraService::initializeShimMetadata(int cameraId) { + int pid = getCallingPid(); + int uid = getCallingUid(); + status_t ret = validateConnect(cameraId, uid); + if (ret != OK) { + // Error already logged by callee + return ret; + } + + bool needsNewClient = false; + sp<Client> client; + + String16 internalPackageName("media"); + { // Scope for service lock + Mutex::Autolock lock(mServiceLock); + if (mClient[cameraId] != NULL) { + client = static_cast<Client*>(mClient[cameraId].promote().get()); + } + if (client == NULL) { + needsNewClient = true; + ret = connectHelperLocked(/*out*/client, + /*cameraClient*/NULL, // Empty binder callbacks + cameraId, + internalPackageName, + uid, + pid); + + if (ret != OK) { + // Error already logged by callee + return ret; + } + } + + if (client == NULL) { + ALOGE("%s: Could not connect to client camera device.", __FUNCTION__); + return BAD_VALUE; + } + + String8 rawParams = client->getParameters(); + CameraParameters params(rawParams); + mShimParams.add(cameraId, params); + } + + // Close client if one was opened solely for this call + if (needsNewClient) { + client->disconnect(); + } + return OK; +} + +status_t CameraService::getLegacyParametersLazy(int cameraId, + /*out*/ + CameraParameters* parameters) { + + ALOGV("%s: for cameraId: %d", __FUNCTION__, cameraId); + + status_t ret = 0; + + if (parameters == NULL) { + ALOGE("%s: parameters must not be null", __FUNCTION__); + return BAD_VALUE; + } + + ssize_t index = -1; + { // Scope for service lock + Mutex::Autolock lock(mServiceLock); + index = mShimParams.indexOfKey(cameraId); + // Release service lock so initializeShimMetadata can be called correctly. + + if (index >= 0) { + *parameters = mShimParams[index]; + } + } + + if (index < 0) { + int64_t token = IPCThreadState::self()->clearCallingIdentity(); + ret = initializeShimMetadata(cameraId); + IPCThreadState::self()->restoreCallingIdentity(token); + if (ret != OK) { + // Error already logged by callee + return ret; + } + + { // Scope for service lock + Mutex::Autolock lock(mServiceLock); + index = mShimParams.indexOfKey(cameraId); + + LOG_ALWAYS_FATAL_IF(index < 0, "index should have been initialized"); + + *parameters = mShimParams[index]; + } + } + + return OK; } status_t CameraService::validateConnect(int cameraId, @@ -402,6 +659,77 @@ bool CameraService::canConnectUnsafe(int cameraId, return true; } +status_t CameraService::connectHelperLocked( + /*out*/ + sp<Client>& client, + /*in*/ + const sp<ICameraClient>& cameraClient, + int cameraId, + const String16& clientPackageName, + int clientUid, + int callingPid, + int halVersion, + bool legacyMode) { + + int facing = -1; + int deviceVersion = getDeviceVersion(cameraId, &facing); + + if (halVersion < 0 || halVersion == deviceVersion) { + // Default path: HAL version is unspecified by caller, create CameraClient + // based on device version reported by the HAL. + switch(deviceVersion) { + case CAMERA_DEVICE_API_VERSION_1_0: + client = new CameraClient(this, cameraClient, + clientPackageName, cameraId, + facing, callingPid, clientUid, getpid(), legacyMode); + break; + case CAMERA_DEVICE_API_VERSION_2_0: + case CAMERA_DEVICE_API_VERSION_2_1: + case CAMERA_DEVICE_API_VERSION_3_0: + case CAMERA_DEVICE_API_VERSION_3_1: + case CAMERA_DEVICE_API_VERSION_3_2: + client = new Camera2Client(this, cameraClient, + clientPackageName, cameraId, + facing, callingPid, clientUid, getpid(), legacyMode); + break; + case -1: + ALOGE("Invalid camera id %d", cameraId); + return BAD_VALUE; + default: + ALOGE("Unknown camera device HAL version: %d", deviceVersion); + return INVALID_OPERATION; + } + } else { + // A particular HAL version is requested by caller. Create CameraClient + // based on the requested HAL version. + if (deviceVersion > CAMERA_DEVICE_API_VERSION_1_0 && + halVersion == CAMERA_DEVICE_API_VERSION_1_0) { + // Only support higher HAL version device opened as HAL1.0 device. + client = new CameraClient(this, cameraClient, + clientPackageName, cameraId, + facing, callingPid, clientUid, getpid(), legacyMode); + } else { + // Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet. + ALOGE("Invalid camera HAL version %x: HAL %x device can only be" + " opened as HAL %x device", halVersion, deviceVersion, + CAMERA_DEVICE_API_VERSION_1_0); + return INVALID_OPERATION; + } + } + + status_t status = connectFinishUnsafe(client, client->getRemote()); + if (status != OK) { + // this is probably not recoverable.. maybe the client can try again + return status; + } + + mClient[cameraId] = client; + LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId, + getpid()); + + return OK; +} + status_t CameraService::connect( const sp<ICameraClient>& cameraClient, int cameraId, @@ -435,50 +763,81 @@ status_t CameraService::connect( return OK; } - int facing = -1; - int deviceVersion = getDeviceVersion(cameraId, &facing); - - // If there are other non-exclusive users of the camera, - // this will tear them down before we can reuse the camera - if (isValidCameraId(cameraId)) { - // transition from PRESENT -> NOT_AVAILABLE - updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE, - cameraId); + status = connectHelperLocked(/*out*/client, + cameraClient, + cameraId, + clientPackageName, + clientUid, + callingPid); + if (status != OK) { + return status; } - switch(deviceVersion) { - case CAMERA_DEVICE_API_VERSION_1_0: - client = new CameraClient(this, cameraClient, - clientPackageName, cameraId, - facing, callingPid, clientUid, getpid()); - break; - case CAMERA_DEVICE_API_VERSION_2_0: - case CAMERA_DEVICE_API_VERSION_2_1: - case CAMERA_DEVICE_API_VERSION_3_0: - client = new Camera2Client(this, cameraClient, - clientPackageName, cameraId, - facing, callingPid, clientUid, getpid(), - deviceVersion); - break; - case -1: - ALOGE("Invalid camera id %d", cameraId); - return BAD_VALUE; - default: - ALOGE("Unknown camera device HAL version: %d", deviceVersion); - return INVALID_OPERATION; + } + // important: release the mutex here so the client can call back + // into the service from its destructor (can be at the end of the call) + + device = client; + return OK; +} + +status_t CameraService::connectLegacy( + const sp<ICameraClient>& cameraClient, + int cameraId, int halVersion, + const String16& clientPackageName, + int clientUid, + /*out*/ + sp<ICamera>& device) { + + if (halVersion != CAMERA_HAL_API_VERSION_UNSPECIFIED && + mModule->common.module_api_version < CAMERA_MODULE_API_VERSION_2_3) { + /* + * Either the HAL version is unspecified in which case this just creates + * a camera client selected by the latest device version, or + * it's a particular version in which case the HAL must supported + * the open_legacy call + */ + ALOGE("%s: camera HAL module version %x doesn't support connecting to legacy HAL devices!", + __FUNCTION__, mModule->common.module_api_version); + return INVALID_OPERATION; + } + + String8 clientName8(clientPackageName); + int callingPid = getCallingPid(); + + LOG1("CameraService::connect legacy E (pid %d \"%s\", id %d)", callingPid, + clientName8.string(), cameraId); + + status_t status = validateConnect(cameraId, /*inout*/clientUid); + if (status != OK) { + return status; + } + + sp<Client> client; + { + Mutex::Autolock lock(mServiceLock); + sp<BasicClient> clientTmp; + if (!canConnectUnsafe(cameraId, clientPackageName, + cameraClient->asBinder(), + /*out*/clientTmp)) { + return -EBUSY; + } else if (client.get() != NULL) { + device = static_cast<Client*>(clientTmp.get()); + return OK; } - status_t status = connectFinishUnsafe(client, client->getRemote()); + status = connectHelperLocked(/*out*/client, + cameraClient, + cameraId, + clientPackageName, + clientUid, + callingPid, + halVersion, + /*legacyMode*/true); if (status != OK) { - // this is probably not recoverable.. maybe the client can try again - // OK: we can only get here if we were originally in PRESENT state - updateStatus(ICameraServiceListener::STATUS_PRESENT, cameraId); return status; } - mClient[cameraId] = client; - LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId, - getpid()); } // important: release the mutex here so the client can call back // into the service from its destructor (can be at the end of the call) @@ -493,8 +852,9 @@ status_t CameraService::connectFinishUnsafe(const sp<BasicClient>& client, if (status != OK) { return status; } - - remoteCallback->linkToDeath(this); + if (remoteCallback != NULL) { + remoteCallback->linkToDeath(this); + } return OK; } @@ -507,6 +867,11 @@ status_t CameraService::connectPro( /*out*/ sp<IProCameraUser>& device) { + if (cameraCb == 0) { + ALOGE("%s: Callback must not be null", __FUNCTION__); + return BAD_VALUE; + } + String8 clientName8(clientPackageName); int callingPid = getCallingPid(); @@ -541,8 +906,10 @@ status_t CameraService::connectPro( case CAMERA_DEVICE_API_VERSION_2_0: case CAMERA_DEVICE_API_VERSION_2_1: case CAMERA_DEVICE_API_VERSION_3_0: - client = new ProCamera2Client(this, cameraCb, String16(), - cameraId, facing, callingPid, USE_CALLING_UID, getpid()); + case CAMERA_DEVICE_API_VERSION_3_1: + case CAMERA_DEVICE_API_VERSION_3_2: + client = new ProCamera2Client(this, cameraCb, clientPackageName, + cameraId, facing, callingPid, clientUid, getpid()); break; case -1: ALOGE("Invalid camera id %d", cameraId); @@ -603,14 +970,6 @@ status_t CameraService::connectDevice( int facing = -1; int deviceVersion = getDeviceVersion(cameraId, &facing); - // If there are other non-exclusive users of the camera, - // this will tear them down before we can reuse the camera - if (isValidCameraId(cameraId)) { - // transition from PRESENT -> NOT_AVAILABLE - updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE, - cameraId); - } - switch(deviceVersion) { case CAMERA_DEVICE_API_VERSION_1_0: ALOGW("Camera using old HAL version: %d", deviceVersion); @@ -619,8 +978,10 @@ status_t CameraService::connectDevice( case CAMERA_DEVICE_API_VERSION_2_0: case CAMERA_DEVICE_API_VERSION_2_1: case CAMERA_DEVICE_API_VERSION_3_0: - client = new CameraDeviceClient(this, cameraCb, String16(), - cameraId, facing, callingPid, USE_CALLING_UID, getpid()); + case CAMERA_DEVICE_API_VERSION_3_1: + case CAMERA_DEVICE_API_VERSION_3_2: + client = new CameraDeviceClient(this, cameraCb, clientPackageName, + cameraId, facing, callingPid, clientUid, getpid()); break; case -1: ALOGE("Invalid camera id %d", cameraId); @@ -633,8 +994,6 @@ status_t CameraService::connectDevice( status_t status = connectFinishUnsafe(client, client->getRemote()); if (status != OK) { // this is probably not recoverable.. maybe the client can try again - // OK: we can only get here if we were originally in PRESENT state - updateStatus(ICameraServiceListener::STATUS_PRESENT, cameraId); return status; } @@ -709,6 +1068,78 @@ status_t CameraService::removeListener( return BAD_VALUE; } +status_t CameraService::getLegacyParameters( + int cameraId, + /*out*/ + String16* parameters) { + ALOGV("%s: for camera ID = %d", __FUNCTION__, cameraId); + + if (parameters == NULL) { + ALOGE("%s: parameters must not be null", __FUNCTION__); + return BAD_VALUE; + } + + status_t ret = 0; + + CameraParameters shimParams; + if ((ret = getLegacyParametersLazy(cameraId, /*out*/&shimParams)) != OK) { + // Error logged by caller + return ret; + } + + String8 shimParamsString8 = shimParams.flatten(); + String16 shimParamsString16 = String16(shimParamsString8); + + *parameters = shimParamsString16; + + return OK; +} + +status_t CameraService::supportsCameraApi(int cameraId, int apiVersion) { + ALOGV("%s: for camera ID = %d", __FUNCTION__, cameraId); + + switch (apiVersion) { + case API_VERSION_1: + case API_VERSION_2: + break; + default: + ALOGE("%s: Bad API version %d", __FUNCTION__, apiVersion); + return BAD_VALUE; + } + + int facing = -1; + int deviceVersion = getDeviceVersion(cameraId, &facing); + + switch(deviceVersion) { + case CAMERA_DEVICE_API_VERSION_1_0: + case CAMERA_DEVICE_API_VERSION_2_0: + case CAMERA_DEVICE_API_VERSION_2_1: + case CAMERA_DEVICE_API_VERSION_3_0: + case CAMERA_DEVICE_API_VERSION_3_1: + if (apiVersion == API_VERSION_2) { + ALOGV("%s: Camera id %d uses HAL prior to HAL3.2, doesn't support api2 without shim", + __FUNCTION__, cameraId); + return -EOPNOTSUPP; + } else { // if (apiVersion == API_VERSION_1) { + ALOGV("%s: Camera id %d uses older HAL before 3.2, but api1 is always supported", + __FUNCTION__, cameraId); + return OK; + } + case CAMERA_DEVICE_API_VERSION_3_2: + ALOGV("%s: Camera id %d uses HAL3.2 or newer, supports api1/api2 directly", + __FUNCTION__, cameraId); + return OK; + case -1: + ALOGE("%s: Invalid camera id %d", __FUNCTION__, cameraId); + return BAD_VALUE; + default: + ALOGE("%s: Unknown camera device HAL version: %d", __FUNCTION__, deviceVersion); + return INVALID_OPERATION; + } + + return OK; +} + void CameraService::removeClientByRemote(const wp<IBinder>& remoteBinder) { int callingPid = getCallingPid(); LOG1("CameraService::removeClientByRemote E (pid %d)", callingPid); @@ -723,9 +1154,13 @@ void CameraService::removeClientByRemote(const wp<IBinder>& remoteBinder) { if (client != 0) { // Found our camera, clear and leave. LOG1("removeClient: clear camera %d", outIndex); - mClient[outIndex].clear(); - client->getRemote()->unlinkToDeath(this); + sp<IBinder> remote = client->getRemote(); + if (remote != NULL) { + remote->unlinkToDeath(this); + } + + mClient[outIndex].clear(); } else { sp<ProClient> clientPro = findProClientUnsafe(remoteBinder); @@ -834,6 +1269,8 @@ status_t CameraService::onTransact( switch (code) { case BnCameraService::CONNECT: case BnCameraService::CONNECT_PRO: + case BnCameraService::CONNECT_DEVICE: + case BnCameraService::CONNECT_LEGACY: const int pid = getCallingPid(); const int self_pid = getpid(); if (pid != self_pid) { @@ -876,7 +1313,7 @@ void CameraService::setCameraFree(int cameraId) { MediaPlayer* CameraService::newMediaPlayer(const char *file) { MediaPlayer* mp = new MediaPlayer(); - if (mp->setDataSource(file, NULL) == NO_ERROR) { + if (mp->setDataSource(NULL /* httpService */, file, NULL) == NO_ERROR) { mp->setAudioStreamType(AUDIO_STREAM_ENFORCED_AUDIBLE); mp->prepare(); } else { @@ -980,13 +1417,15 @@ CameraService::BasicClient::~BasicClient() { void CameraService::BasicClient::disconnect() { ALOGV("BasicClient::disconnect"); mCameraService->removeClientByRemote(mRemoteBinder); + + finishCameraOps(); // client shouldn't be able to call into us anymore mClientPid = 0; } status_t CameraService::BasicClient::startCameraOps() { int32_t res; - + // Notify app ops that the camera is not available mOpsCallback = new OpsCallback(this); { @@ -1004,16 +1443,39 @@ status_t CameraService::BasicClient::startCameraOps() { mCameraId, String8(mClientPackageName).string()); return PERMISSION_DENIED; } + mOpsActive = true; + + // Transition device availability listeners from PRESENT -> NOT_AVAILABLE + mCameraService->updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE, + mCameraId); + return OK; } status_t CameraService::BasicClient::finishCameraOps() { + // Check if startCameraOps succeeded, and if so, finish the camera op if (mOpsActive) { + // Notify app ops that the camera is available again mAppOpsManager.finishOp(AppOpsManager::OP_CAMERA, mClientUid, mClientPackageName); mOpsActive = false; + + // Notify device availability listeners that this camera is available + // again + + StatusVector rejectSourceStates; + rejectSourceStates.push_back(ICameraServiceListener::STATUS_NOT_PRESENT); + rejectSourceStates.push_back(ICameraServiceListener::STATUS_ENUMERATING); + + // Transition to PRESENT if the camera is not in either of above 2 + // states + mCameraService->updateStatus(ICameraServiceListener::STATUS_PRESENT, + mCameraId, + &rejectSourceStates); + } + // Always stop watching, even if no camera op is active mAppOpsManager.stopWatchingMode(mOpsCallback); mOpsCallback.clear(); @@ -1044,7 +1506,8 @@ void CameraService::BasicClient::opChanged(int32_t op, const String16& packageNa // Reset the client PID to allow server-initiated disconnect, // and to prevent further calls by client. mClientPid = getCallingPid(); - notifyError(); + CaptureResultExtras resultExtras; // a dummy result (invalid) + notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_SERVICE, resultExtras); disconnect(); } } @@ -1073,7 +1536,8 @@ CameraService::Client* CameraService::Client::getClientFromCookie(void* user) { return client; } -void CameraService::Client::notifyError() { +void CameraService::Client::notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, + const CaptureResultExtras& resultExtras) { mRemoteCallback->notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_RELEASED, 0); } @@ -1082,15 +1546,6 @@ void CameraService::Client::disconnect() { ALOGV("Client::disconnect"); BasicClient::disconnect(); mCameraService->setCameraFree(mCameraId); - - StatusVector rejectSourceStates; - rejectSourceStates.push_back(ICameraServiceListener::STATUS_NOT_PRESENT); - rejectSourceStates.push_back(ICameraServiceListener::STATUS_ENUMERATING); - - // Transition to PRESENT if the camera is not in either of above 2 states - mCameraService->updateStatus(ICameraServiceListener::STATUS_PRESENT, - mCameraId, - &rejectSourceStates); } CameraService::Client::OpsCallback::OpsCallback(wp<BasicClient> client): @@ -1127,7 +1582,8 @@ CameraService::ProClient::ProClient(const sp<CameraService>& cameraService, CameraService::ProClient::~ProClient() { } -void CameraService::ProClient::notifyError() { +void CameraService::ProClient::notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, + const CaptureResultExtras& resultExtras) { mRemoteCallback->notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_RELEASED, 0); } @@ -1182,7 +1638,20 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) { result.appendFormat("Camera module author: %s\n", mModule->common.author); result.appendFormat("Number of camera devices: %d\n\n", mNumberOfCameras); + + sp<VendorTagDescriptor> desc = VendorTagDescriptor::getGlobalVendorTagDescriptor(); + if (desc == NULL) { + result.appendFormat("Vendor tags left unimplemented.\n"); + } else { + result.appendFormat("Vendor tag definitions:\n"); + } + write(fd, result.string(), result.size()); + + if (desc != NULL) { + desc->dump(fd, /*verbosity*/2, /*indentation*/4); + } + for (int i = 0; i < mNumberOfCameras; i++) { result = String8::format("Camera %d static information:\n", i); camera_info info; @@ -1207,7 +1676,7 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) { result.appendFormat(" Device static metadata:\n"); write(fd, result.string(), result.size()); dump_indented_camera_metadata(info.static_camera_characteristics, - fd, 2, 4); + fd, /*verbosity*/2, /*indentation*/4); } else { write(fd, result.string(), result.size()); } diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index ad6a582..a7328cf 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -1,24 +1,24 @@ /* -** -** Copyright (C) 2008, 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. -*/ + * Copyright (C) 2008 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_SERVERS_CAMERA_CAMERASERVICE_H #define ANDROID_SERVERS_CAMERA_CAMERASERVICE_H #include <utils/Vector.h> +#include <utils/KeyedVector.h> #include <binder/AppOpsManager.h> #include <binder/BinderService.h> #include <binder/IAppOpsCallback.h> @@ -31,6 +31,9 @@ #include <camera/IProCameraCallbacks.h> #include <camera/camera2/ICameraDeviceUser.h> #include <camera/camera2/ICameraDeviceCallbacks.h> +#include <camera/VendorTagDescriptor.h> +#include <camera/CaptureResult.h> +#include <camera/CameraParameters.h> #include <camera/ICameraServiceListener.h> @@ -73,12 +76,18 @@ public: struct CameraInfo* cameraInfo); virtual status_t getCameraCharacteristics(int cameraId, CameraMetadata* cameraInfo); + virtual status_t getCameraVendorTagDescriptor(/*out*/ sp<VendorTagDescriptor>& desc); virtual status_t connect(const sp<ICameraClient>& cameraClient, int cameraId, const String16& clientPackageName, int clientUid, /*out*/ sp<ICamera>& device); + virtual status_t connectLegacy(const sp<ICameraClient>& cameraClient, int cameraId, + int halVersion, const String16& clientPackageName, int clientUid, + /*out*/ + sp<ICamera>& device); + virtual status_t connectPro(const sp<IProCameraCallbacks>& cameraCb, int cameraId, const String16& clientPackageName, int clientUid, /*out*/ @@ -96,6 +105,15 @@ public: virtual status_t removeListener( const sp<ICameraServiceListener>& listener); + virtual status_t getLegacyParameters( + int cameraId, + /*out*/ + String16* parameters); + + // OK = supports api of that version, -EOPNOTSUPP = does not support + virtual status_t supportsCameraApi( + int cameraId, int apiVersion); + // Extra permissions checks virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); @@ -120,6 +138,10 @@ public: // CameraDeviceFactory functionality int getDeviceVersion(int cameraId, int* facing = NULL); + ///////////////////////////////////////////////////////////////////// + // Shared utilities + static status_t filterOpenErrorCode(status_t err); + static status_t filterGetInfoErrorCode(status_t err); ///////////////////////////////////////////////////////////////////// // CameraClient functionality @@ -131,20 +153,19 @@ public: class BasicClient : public virtual RefBase { public: - virtual status_t initialize(camera_module_t *module) = 0; - - virtual void disconnect() = 0; + virtual status_t initialize(camera_module_t *module) = 0; + virtual void disconnect(); // because we can't virtually inherit IInterface, which breaks // virtual inheritance virtual sp<IBinder> asBinderWrapper() = 0; // Return the remote callback binder object (e.g. IProCameraCallbacks) - sp<IBinder> getRemote() { + sp<IBinder> getRemote() { return mRemoteBinder; } - virtual status_t dump(int fd, const Vector<String16>& args) = 0; + virtual status_t dump(int fd, const Vector<String16>& args) = 0; protected: BasicClient(const sp<CameraService>& cameraService, @@ -181,7 +202,9 @@ public: status_t finishCameraOps(); // Notify client about a fatal error - virtual void notifyError() = 0; + virtual void notifyError( + ICameraDeviceCallbacks::CameraErrorCode errorCode, + const CaptureResultExtras& resultExtras) = 0; private: AppOpsManager mAppOpsManager; @@ -258,7 +281,8 @@ public: // convert client from cookie. Client lock should be acquired before getting Client. static Client* getClientFromCookie(void* user); - virtual void notifyError(); + virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, + const CaptureResultExtras& resultExtras); // Initialized in constructor @@ -306,7 +330,8 @@ public: virtual void onExclusiveLockStolen() = 0; protected: - virtual void notifyError(); + virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, + const CaptureResultExtras& resultExtras); sp<IProCameraCallbacks> mRemoteCallback; }; // class ProClient @@ -387,6 +412,57 @@ private: // Helpers bool isValidCameraId(int cameraId); + + bool setUpVendorTags(); + + /** + * A mapping of camera ids to CameraParameters returned by that camera device. + * + * This cache is used to generate CameraCharacteristic metadata when using + * the HAL1 shim. + */ + KeyedVector<int, CameraParameters> mShimParams; + + /** + * Initialize and cache the metadata used by the HAL1 shim for a given cameraId. + * + * Returns OK on success, or a negative error code. + */ + status_t initializeShimMetadata(int cameraId); + + /** + * Get the cached CameraParameters for the camera. If they haven't been + * cached yet, then initialize them for the first time. + * + * Returns OK on success, or a negative error code. + */ + status_t getLegacyParametersLazy(int cameraId, /*out*/CameraParameters* parameters); + + /** + * Generate the CameraCharacteristics metadata required by the Camera2 API + * from the available HAL1 CameraParameters and CameraInfo. + * + * Returns OK on success, or a negative error code. + */ + status_t generateShimMetadata(int cameraId, /*out*/CameraMetadata* cameraInfo); + + /** + * Connect a new camera client. This should only be used while holding the + * mutex for mServiceLock. + * + * Returns OK on success, or a negative error code. + */ + status_t connectHelperLocked( + /*out*/ + sp<Client>& client, + /*in*/ + const sp<ICameraClient>& cameraClient, + int cameraId, + const String16& clientPackageName, + int clientUid, + int callingPid, + int halVersion = CAMERA_HAL_API_VERSION_UNSPECIFIED, + bool legacyMode = false); }; } // namespace android diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp index af23557..36a93b2 100644 --- a/services/camera/libcameraservice/api1/Camera2Client.cpp +++ b/services/camera/libcameraservice/api1/Camera2Client.cpp @@ -54,16 +54,17 @@ Camera2Client::Camera2Client(const sp<CameraService>& cameraService, int clientPid, uid_t clientUid, int servicePid, - int deviceVersion): + bool legacyMode): Camera2ClientBase(cameraService, cameraClient, clientPackageName, cameraId, cameraFacing, clientPid, clientUid, servicePid), - mParameters(cameraId, cameraFacing), - mDeviceVersion(deviceVersion) + mParameters(cameraId, cameraFacing) { ATRACE_CALL(); SharedParameters::Lock l(mParameters); l.mParameters.state = Parameters::DISCONNECTED; + + mLegacyMode = legacyMode; } status_t Camera2Client::initialize(camera_module_t *module) @@ -80,7 +81,7 @@ status_t Camera2Client::initialize(camera_module_t *module) { SharedParameters::Lock l(mParameters); - res = l.mParameters.initialize(&(mDevice->info())); + res = l.mParameters.initialize(&(mDevice->info()), mDeviceVersion); if (res != OK) { ALOGE("%s: Camera %d: unable to build defaults: %s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); @@ -118,7 +119,9 @@ status_t Camera2Client::initialize(camera_module_t *module) mZslProcessorThread = zslProc; break; } - case CAMERA_DEVICE_API_VERSION_3_0:{ + case CAMERA_DEVICE_API_VERSION_3_0: + case CAMERA_DEVICE_API_VERSION_3_1: + case CAMERA_DEVICE_API_VERSION_3_2: { sp<ZslProcessor3> zslProc = new ZslProcessor3(this, mCaptureSequencer); mZslProcessor = zslProc; @@ -238,7 +241,7 @@ status_t Camera2Client::dump(int fd, const Vector<String16>& args) { result.append(" Scene mode: "); switch (p.sceneMode) { - case ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED: + case ANDROID_CONTROL_SCENE_MODE_DISABLED: result.append("AUTO\n"); break; CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_ACTION) CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_PORTRAIT) @@ -431,6 +434,9 @@ void Camera2Client::disconnect() { mCallbackProcessor->deleteStream(); mZslProcessor->deleteStream(); + // Remove all ZSL stream state before disconnect; needed to work around b/15408128. + mZslProcessor->disconnect(); + ALOGV("Camera %d: Disconnecting device", mCameraId); mDevice->disconnect(); @@ -753,6 +759,7 @@ status_t Camera2Client::startPreviewL(Parameters ¶ms, bool restart) { // ever take a picture. // TODO: Find a better compromise, though this likely would involve HAL // changes. + int lastJpegStreamId = mJpegProcessor->getStreamId(); res = updateProcessorStream(mJpegProcessor, params); if (res != OK) { ALOGE("%s: Camera %d: Can't pre-configure still image " @@ -760,6 +767,7 @@ status_t Camera2Client::startPreviewL(Parameters ¶ms, bool restart) { __FUNCTION__, mCameraId, strerror(-res), res); return res; } + bool jpegStreamChanged = mJpegProcessor->getStreamId() != lastJpegStreamId; Vector<int32_t> outputStreams; bool callbacksEnabled = (params.previewCallbackFlags & @@ -808,14 +816,24 @@ status_t Camera2Client::startPreviewL(Parameters ¶ms, bool restart) { return res; } } - if (params.zslMode && !params.recordingHint) { + + if (params.zslMode && !params.recordingHint && + getRecordingStreamId() == NO_STREAM) { res = updateProcessorStream(mZslProcessor, params); if (res != OK) { ALOGE("%s: Camera %d: Unable to update ZSL stream: %s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); return res; } + + if (jpegStreamChanged) { + ALOGV("%s: Camera %d: Clear ZSL buffer queue when Jpeg size is changed", + __FUNCTION__, mCameraId); + mZslProcessor->clearZslQueue(); + } outputStreams.push(getZslStreamId()); + } else { + mZslProcessor->deleteStream(); } outputStreams.push(getPreviewStreamId()); @@ -896,6 +914,13 @@ void Camera2Client::stopPreviewL() { ALOGE("%s: Camera %d: Waiting to stop streaming failed: %s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); } + // Clean up recording stream + res = mStreamingProcessor->deleteRecordingStream(); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to delete recording stream before " + "stop preview: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + } // no break case Parameters::WAITING_FOR_PREVIEW_WINDOW: { SharedParameters::Lock l(mParameters); @@ -1016,6 +1041,36 @@ status_t Camera2Client::startRecordingL(Parameters ¶ms, bool restart) { return res; } } + + if (mZslProcessor->getStreamId() != NO_STREAM) { + ALOGV("%s: Camera %d: Clearing out zsl stream before " + "creating recording stream", __FUNCTION__, mCameraId); + res = mStreamingProcessor->stopStream(); + if (res != OK) { + ALOGE("%s: Camera %d: Can't stop streaming to delete callback stream", + __FUNCTION__, mCameraId); + return res; + } + res = mDevice->waitUntilDrained(); + if (res != OK) { + ALOGE("%s: Camera %d: Waiting to stop streaming failed: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + } + res = mZslProcessor->clearZslQueue(); + if (res != OK) { + ALOGE("%s: Camera %d: Can't clear zsl queue", + __FUNCTION__, mCameraId); + return res; + } + res = mZslProcessor->deleteStream(); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to delete zsl stream before " + "record: %s (%d)", __FUNCTION__, mCameraId, + strerror(-res), res); + return res; + } + } + // Disable callbacks if they're enabled; can't record and use callbacks, // and we can't fail record start without stagefright asserting. params.previewCallbackFlags = 0; @@ -1036,6 +1091,22 @@ status_t Camera2Client::startRecordingL(Parameters ¶ms, bool restart) { res = mStreamingProcessor->startStream(StreamingProcessor::RECORD, outputStreams); + // try to reconfigure jpeg to video size if configureStreams failed + if (res == BAD_VALUE) { + + ALOGV("%s: Camera %d: configure still size to video size before recording" + , __FUNCTION__, mCameraId); + params.overrideJpegSizeByVideoSize(); + res = updateProcessorStream(mJpegProcessor, params); + if (res != OK) { + ALOGE("%s: Camera %d: Can't configure still image size to video size: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + res = mStreamingProcessor->startStream(StreamingProcessor::RECORD, + outputStreams); + } + if (res != OK) { ALOGE("%s: Camera %d: Unable to start recording stream: %s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); @@ -1075,6 +1146,7 @@ void Camera2Client::stopRecording() { mCameraService->playSound(CameraService::SOUND_RECORDING); + l.mParameters.recoverOverriddenJpegSize(); res = startPreviewL(l.mParameters, true); if (res != OK) { ALOGE("%s: Camera %d: Unable to return to preview", @@ -1162,7 +1234,7 @@ status_t Camera2Client::autoFocus() { * Handle quirk mode for AF in scene modes */ if (l.mParameters.quirks.triggerAfWithAuto && - l.mParameters.sceneMode != ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED && + l.mParameters.sceneMode != ANDROID_CONTROL_SCENE_MODE_DISABLED && l.mParameters.focusMode != Parameters::FOCUS_MODE_AUTO && !l.mParameters.focusingAreas[0].isEmpty()) { ALOGV("%s: Quirk: Switching from focusMode %d to AUTO", @@ -1266,6 +1338,7 @@ status_t Camera2Client::takePicture(int msgType) { ALOGV("%s: Camera %d: Starting picture capture", __FUNCTION__, mCameraId); + int lastJpegStreamId = mJpegProcessor->getStreamId(); res = updateProcessorStream(mJpegProcessor, l.mParameters); if (res != OK) { ALOGE("%s: Camera %d: Can't set up still image stream: %s (%d)", @@ -1273,6 +1346,14 @@ status_t Camera2Client::takePicture(int msgType) { return res; } takePictureCounter = ++l.mParameters.takePictureCounter; + + // Clear ZSL buffer queue when Jpeg size is changed. + bool jpegStreamChanged = mJpegProcessor->getStreamId() != lastJpegStreamId; + if (l.mParameters.zslMode && jpegStreamChanged) { + ALOGV("%s: Camera %d: Clear ZSL buffer queue when Jpeg size is changed", + __FUNCTION__, mCameraId); + mZslProcessor->clearZslQueue(); + } } ATRACE_ASYNC_BEGIN(kTakepictureLabel, takePictureCounter); @@ -1310,7 +1391,8 @@ String8 Camera2Client::getParameters() const { ATRACE_CALL(); ALOGV("%s: Camera %d", __FUNCTION__, mCameraId); Mutex::Autolock icl(mBinderSerializationLock); - if ( checkPid(__FUNCTION__) != OK) return String8(); + // The camera service can unconditionally get the parameters at all times + if (getCallingPid() != mServicePid && checkPid(__FUNCTION__) != OK) return String8(); SharedParameters::ReadLock l(mParameters); @@ -1390,6 +1472,13 @@ status_t Camera2Client::commandEnableShutterSoundL(bool enable) { return OK; } + // the camera2 api legacy mode can unconditionally disable the shutter sound + if (mLegacyMode) { + ALOGV("%s: Disable shutter sound in legacy mode", __FUNCTION__); + l.mParameters.playShutterSound = false; + return OK; + } + // Disabling shutter sound may not be allowed. In that case only // allow the mediaserver process to disable the sound. char value[PROPERTY_VALUE_MAX]; @@ -1655,8 +1744,8 @@ int Camera2Client::getZslStreamId() const { } status_t Camera2Client::registerFrameListener(int32_t minId, int32_t maxId, - wp<camera2::FrameProcessor::FilteredListener> listener) { - return mFrameProcessor->registerListener(minId, maxId, listener); + wp<camera2::FrameProcessor::FilteredListener> listener, bool sendPartials) { + return mFrameProcessor->registerListener(minId, maxId, listener, sendPartials); } status_t Camera2Client::removeFrameListener(int32_t minId, int32_t maxId, diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h index fe0bf74..f5c3a30 100644 --- a/services/camera/libcameraservice/api1/Camera2Client.h +++ b/services/camera/libcameraservice/api1/Camera2Client.h @@ -90,7 +90,7 @@ public: int clientPid, uid_t clientUid, int servicePid, - int deviceVersion); + bool legacyMode); virtual ~Camera2Client(); @@ -118,7 +118,8 @@ public: int getZslStreamId() const; status_t registerFrameListener(int32_t minId, int32_t maxId, - wp<camera2::FrameProcessor::FilteredListener> listener); + wp<camera2::FrameProcessor::FilteredListener> listener, + bool sendPartials = true); status_t removeFrameListener(int32_t minId, int32_t maxId, wp<camera2::FrameProcessor::FilteredListener> listener); @@ -170,7 +171,6 @@ private: void setPreviewCallbackFlagL(Parameters ¶ms, int flag); status_t updateRequests(Parameters ¶ms); - int mDeviceVersion; // Used with stream IDs static const int NO_STREAM = -1; @@ -204,6 +204,7 @@ private: bool mAfInMotion; /** Utility members */ + bool mLegacyMode; // Wait until the camera device has received the latest control settings status_t syncWithDevice(); diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp index 30b7bb8..33bdaa3 100644 --- a/services/camera/libcameraservice/api1/CameraClient.cpp +++ b/services/camera/libcameraservice/api1/CameraClient.cpp @@ -38,7 +38,7 @@ CameraClient::CameraClient(const sp<CameraService>& cameraService, const String16& clientPackageName, int cameraId, int cameraFacing, int clientPid, int clientUid, - int servicePid): + int servicePid, bool legacyMode): Client(cameraService, cameraClient, clientPackageName, cameraId, cameraFacing, clientPid, clientUid, servicePid) { @@ -54,6 +54,7 @@ CameraClient::CameraClient(const sp<CameraService>& cameraService, // Callback is disabled by default mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP; mOrientation = getOrientation(0, mCameraFacing == CAMERA_FACING_FRONT); + mLegacyMode = legacyMode; mPlayShutterSound = true; LOG1("CameraClient::CameraClient X (pid %d, id %d)", callingPid, cameraId); } @@ -79,7 +80,7 @@ status_t CameraClient::initialize(camera_module_t *module) { ALOGE("%s: Camera %d: unable to initialize device: %s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); mHardware.clear(); - return NO_INIT; + return res; } mHardware->setCallbacks(notifyCallback, @@ -556,7 +557,8 @@ status_t CameraClient::setParameters(const String8& params) { // get preview/capture parameters - key/value pairs String8 CameraClient::getParameters() const { Mutex::Autolock lock(mLock); - if (checkPidAndHardware() != NO_ERROR) return String8(); + // The camera service can unconditionally get the parameters at all times + if (getCallingPid() != mServicePid && checkPidAndHardware() != NO_ERROR) return String8(); String8 params(mHardware->getParameters().flatten()); LOG1("getParameters (pid %d) (%s)", getCallingPid(), params.string()); @@ -575,6 +577,13 @@ status_t CameraClient::enableShutterSound(bool enable) { return OK; } + // the camera2 api legacy mode can unconditionally disable the shutter sound + if (mLegacyMode) { + ALOGV("%s: Disable shutter sound in legacy mode", __FUNCTION__); + mPlayShutterSound = false; + return OK; + } + // Disabling shutter sound may not be allowed. In that case only // allow the mediaserver process to disable the sound. char value[PROPERTY_VALUE_MAX]; @@ -929,7 +938,20 @@ void CameraClient::copyFrameAndPostCopiedFrame( } previewBuffer = mPreviewBuffer; - memcpy(previewBuffer->base(), (uint8_t *)heap->base() + offset, size); + void* previewBufferBase = previewBuffer->base(); + void* heapBase = heap->base(); + + if (heapBase == MAP_FAILED) { + ALOGE("%s: Failed to mmap heap for preview frame.", __FUNCTION__); + mLock.unlock(); + return; + } else if (previewBufferBase == MAP_FAILED) { + ALOGE("%s: Failed to mmap preview buffer for preview frame.", __FUNCTION__); + mLock.unlock(); + return; + } + + memcpy(previewBufferBase, (uint8_t *) heapBase + offset, size); sp<MemoryBase> frame = new MemoryBase(previewBuffer, 0, size); if (frame == 0) { diff --git a/services/camera/libcameraservice/api1/CameraClient.h b/services/camera/libcameraservice/api1/CameraClient.h index 4b89564..6779f5e 100644 --- a/services/camera/libcameraservice/api1/CameraClient.h +++ b/services/camera/libcameraservice/api1/CameraClient.h @@ -64,7 +64,8 @@ public: int cameraFacing, int clientPid, int clientUid, - int servicePid); + int servicePid, + bool legacyMode = false); ~CameraClient(); status_t initialize(camera_module_t *module); @@ -129,6 +130,7 @@ private: int mPreviewCallbackFlag; int mOrientation; // Current display orientation bool mPlayShutterSound; + bool mLegacyMode; // camera2 api legacy mode? // Ensures atomicity among the public methods mutable Mutex mLock; diff --git a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp index d2ac79c..bf3318e 100644 --- a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp @@ -110,11 +110,13 @@ status_t CallbackProcessor::updateStream(const Parameters ¶ms) { if (!mCallbackToApp && mCallbackConsumer == 0) { // Create CPU buffer queue endpoint, since app hasn't given us one // Make it async to avoid disconnect deadlocks - sp<BufferQueue> bq = new BufferQueue(); - mCallbackConsumer = new CpuConsumer(bq, kCallbackHeapCount); + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + mCallbackConsumer = new CpuConsumer(consumer, kCallbackHeapCount); mCallbackConsumer->setFrameAvailableListener(this); mCallbackConsumer->setName(String8("Camera2Client::CallbackConsumer")); - mCallbackWindow = new Surface(bq); + mCallbackWindow = new Surface(producer); } if (mCallbackStreamId != NO_STREAM) { @@ -153,7 +155,7 @@ status_t CallbackProcessor::updateStream(const Parameters ¶ms) { callbackFormat, params.previewFormat); res = device->createStream(mCallbackWindow, params.previewWidth, params.previewHeight, - callbackFormat, 0, &mCallbackStreamId); + callbackFormat, &mCallbackStreamId); if (res != OK) { ALOGE("%s: Camera %d: Can't create output stream for callbacks: " "%s (%d)", __FUNCTION__, mId, diff --git a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp index f5c28ed..9849f4d 100644 --- a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp +++ b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp @@ -106,13 +106,12 @@ void CaptureSequencer::notifyAutoExposure(uint8_t newState, int triggerId) { } } -void CaptureSequencer::onFrameAvailable(int32_t requestId, - const CameraMetadata &frame) { - ALOGV("%s: Listener found new frame", __FUNCTION__); +void CaptureSequencer::onResultAvailable(const CaptureResult &result) { ATRACE_CALL(); + ALOGV("%s: New result available.", __FUNCTION__); Mutex::Autolock l(mInputMutex); - mNewFrameId = requestId; - mNewFrame = frame; + mNewFrameId = result.mResultExtras.requestId; + mNewFrame = result.mMetadata; if (!mNewFrameReceived) { mNewFrameReceived = true; mNewFrameSignal.signal(); @@ -351,8 +350,10 @@ CaptureSequencer::CaptureState CaptureSequencer::manageZslStart( return DONE; } + // We don't want to get partial results for ZSL capture. client->registerFrameListener(mCaptureId, mCaptureId + 1, - this); + this, + /*sendPartials*/false); // TODO: Actually select the right thing here. res = processor->pushToReprocess(mCaptureId); @@ -394,8 +395,14 @@ CaptureSequencer::CaptureState CaptureSequencer::manageStandardStart( bool isAeConverged = false; // Get the onFrameAvailable callback when the requestID == mCaptureId + // We don't want to get partial results for normal capture, as we need + // Get ANDROID_SENSOR_TIMESTAMP from the capture result, but partial + // result doesn't have to have this metadata available. + // TODO: Update to use the HALv3 shutter notification for remove the + // need for this listener and make it faster. see bug 12530628. client->registerFrameListener(mCaptureId, mCaptureId + 1, - this); + this, + /*sendPartials*/false); { Mutex::Autolock l(mInputMutex); @@ -438,11 +445,18 @@ CaptureSequencer::CaptureState CaptureSequencer::manageStandardPrecaptureWait( if (mNewAEState) { if (!mAeInPrecapture) { // Waiting to see PRECAPTURE state - if (mAETriggerId == mTriggerId && - mAEState == ANDROID_CONTROL_AE_STATE_PRECAPTURE) { - ALOGV("%s: Got precapture start", __FUNCTION__); - mAeInPrecapture = true; - mTimeoutCount = kMaxTimeoutsForPrecaptureEnd; + if (mAETriggerId == mTriggerId) { + if (mAEState == ANDROID_CONTROL_AE_STATE_PRECAPTURE) { + ALOGV("%s: Got precapture start", __FUNCTION__); + mAeInPrecapture = true; + mTimeoutCount = kMaxTimeoutsForPrecaptureEnd; + } else if (mAEState == ANDROID_CONTROL_AE_STATE_CONVERGED || + mAEState == ANDROID_CONTROL_AE_STATE_FLASH_REQUIRED) { + // It is legal to transit to CONVERGED or FLASH_REQUIRED + // directly after a trigger. + ALOGV("%s: AE is already in good state, start capture", __FUNCTION__); + return STANDARD_CAPTURE; + } } } else { // Waiting to see PRECAPTURE state end @@ -585,12 +599,15 @@ CaptureSequencer::CaptureState CaptureSequencer::manageStandardCaptureWait( entry = mNewFrame.find(ANDROID_SENSOR_TIMESTAMP); if (entry.count == 0) { ALOGE("No timestamp field in capture frame!"); - } - if (entry.data.i64[0] != mCaptureTimestamp) { - ALOGW("Mismatched capture timestamps: Metadata frame %" PRId64 "," - " captured buffer %" PRId64, - entry.data.i64[0], - mCaptureTimestamp); + } else if (entry.count == 1) { + if (entry.data.i64[0] != mCaptureTimestamp) { + ALOGW("Mismatched capture timestamps: Metadata frame %" PRId64 "," + " captured buffer %" PRId64, + entry.data.i64[0], + mCaptureTimestamp); + } + } else { + ALOGE("Timestamp metadata is malformed!"); } client->removeFrameListener(mCaptureId, mCaptureId + 1, this); diff --git a/services/camera/libcameraservice/api1/client2/CaptureSequencer.h b/services/camera/libcameraservice/api1/client2/CaptureSequencer.h index 9fb4ee7..d42ab13 100644 --- a/services/camera/libcameraservice/api1/client2/CaptureSequencer.h +++ b/services/camera/libcameraservice/api1/client2/CaptureSequencer.h @@ -24,6 +24,7 @@ #include <utils/Mutex.h> #include <utils/Condition.h> #include "camera/CameraMetadata.h" +#include "camera/CaptureResult.h" #include "Parameters.h" #include "FrameProcessor.h" @@ -61,8 +62,8 @@ class CaptureSequencer: // Notifications about AE state changes void notifyAutoExposure(uint8_t newState, int triggerId); - // Notifications from the frame processor - virtual void onFrameAvailable(int32_t requestId, const CameraMetadata &frame); + // Notification from the frame processor + virtual void onResultAvailable(const CaptureResult &result); // Notifications from the JPEG processor void onCaptureAvailable(nsecs_t timestamp, sp<MemoryBase> captureBuffer); diff --git a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp index dd5b27c..312a78c 100644 --- a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp @@ -40,7 +40,12 @@ FrameProcessor::FrameProcessor(wp<CameraDeviceBase> device, { SharedParameters::Lock l(client->getParameters()); - mUsePartialQuirk = l.mParameters.quirks.partialResults; + + if (client->getCameraDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_2) { + mUsePartialResult = (mNumPartialResults > 1); + } else { + mUsePartialResult = l.mParameters.quirks.partialResults; + } // Initialize starting 3A state m3aState.afTriggerId = l.mParameters.afTriggerCounter; @@ -55,7 +60,7 @@ FrameProcessor::FrameProcessor(wp<CameraDeviceBase> device, FrameProcessor::~FrameProcessor() { } -bool FrameProcessor::processSingleFrame(CameraMetadata &frame, +bool FrameProcessor::processSingleFrame(CaptureResult &frame, const sp<CameraDeviceBase> &device) { sp<Camera2Client> client = mClient.promote(); @@ -63,17 +68,21 @@ bool FrameProcessor::processSingleFrame(CameraMetadata &frame, return false; } - bool partialResult = false; - if (mUsePartialQuirk) { - camera_metadata_entry_t entry; - entry = frame.find(ANDROID_QUIRKS_PARTIAL_RESULT); - if (entry.count > 0 && - entry.data.u8[0] == ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL) { - partialResult = true; + bool isPartialResult = false; + if (mUsePartialResult) { + if (client->getCameraDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_2) { + isPartialResult = frame.mResultExtras.partialResultCount < mNumPartialResults; + } else { + camera_metadata_entry_t entry; + entry = frame.mMetadata.find(ANDROID_QUIRKS_PARTIAL_RESULT); + if (entry.count > 0 && + entry.data.u8[0] == ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL) { + isPartialResult = true; + } } } - if (!partialResult && processFaceDetect(frame, client) != OK) { + if (!isPartialResult && processFaceDetect(frame.mMetadata, client) != OK) { return false; } @@ -212,14 +221,15 @@ status_t FrameProcessor::processFaceDetect(const CameraMetadata &frame, return OK; } -status_t FrameProcessor::process3aState(const CameraMetadata &frame, +status_t FrameProcessor::process3aState(const CaptureResult &frame, const sp<Camera2Client> &client) { ATRACE_CALL(); + const CameraMetadata &metadata = frame.mMetadata; camera_metadata_ro_entry_t entry; int cameraId = client->getCameraId(); - entry = frame.find(ANDROID_REQUEST_FRAME_COUNT); + entry = metadata.find(ANDROID_REQUEST_FRAME_COUNT); int32_t frameNumber = entry.data.i32[0]; // Don't send 3A notifications for the same frame number twice @@ -238,26 +248,31 @@ status_t FrameProcessor::process3aState(const CameraMetadata &frame, // TODO: Also use AE mode, AE trigger ID - gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AF_MODE, + gotAllStates &= get3aResult<uint8_t>(metadata, ANDROID_CONTROL_AF_MODE, &new3aState.afMode, frameNumber, cameraId); - gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AWB_MODE, + gotAllStates &= get3aResult<uint8_t>(metadata, ANDROID_CONTROL_AWB_MODE, &new3aState.awbMode, frameNumber, cameraId); - gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AE_STATE, + gotAllStates &= get3aResult<uint8_t>(metadata, ANDROID_CONTROL_AE_STATE, &new3aState.aeState, frameNumber, cameraId); - gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AF_STATE, + gotAllStates &= get3aResult<uint8_t>(metadata, ANDROID_CONTROL_AF_STATE, &new3aState.afState, frameNumber, cameraId); - gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AWB_STATE, + gotAllStates &= get3aResult<uint8_t>(metadata, ANDROID_CONTROL_AWB_STATE, &new3aState.awbState, frameNumber, cameraId); - gotAllStates &= get3aResult<int32_t>(frame, ANDROID_CONTROL_AF_TRIGGER_ID, - &new3aState.afTriggerId, frameNumber, cameraId); + if (client->getCameraDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_2) { + new3aState.afTriggerId = frame.mResultExtras.afTriggerId; + new3aState.aeTriggerId = frame.mResultExtras.precaptureTriggerId; + } else { + gotAllStates &= get3aResult<int32_t>(metadata, ANDROID_CONTROL_AF_TRIGGER_ID, + &new3aState.afTriggerId, frameNumber, cameraId); - gotAllStates &= get3aResult<int32_t>(frame, ANDROID_CONTROL_AE_PRECAPTURE_ID, - &new3aState.aeTriggerId, frameNumber, cameraId); + gotAllStates &= get3aResult<int32_t>(metadata, ANDROID_CONTROL_AE_PRECAPTURE_ID, + &new3aState.aeTriggerId, frameNumber, cameraId); + } if (!gotAllStates) return BAD_VALUE; diff --git a/services/camera/libcameraservice/api1/client2/FrameProcessor.h b/services/camera/libcameraservice/api1/client2/FrameProcessor.h index 856ad32..68cf55b 100644 --- a/services/camera/libcameraservice/api1/client2/FrameProcessor.h +++ b/services/camera/libcameraservice/api1/client2/FrameProcessor.h @@ -51,14 +51,14 @@ class FrameProcessor : public FrameProcessorBase { void processNewFrames(const sp<Camera2Client> &client); - virtual bool processSingleFrame(CameraMetadata &frame, + virtual bool processSingleFrame(CaptureResult &frame, const sp<CameraDeviceBase> &device); status_t processFaceDetect(const CameraMetadata &frame, const sp<Camera2Client> &client); // Send 3A state change notifications to client based on frame metadata - status_t process3aState(const CameraMetadata &frame, + status_t process3aState(const CaptureResult &frame, const sp<Camera2Client> &client); // Helper for process3aState @@ -91,8 +91,8 @@ class FrameProcessor : public FrameProcessorBase { } } m3aState; - // Whether the partial result quirk is enabled for this device - bool mUsePartialQuirk; + // Whether the partial result is enabled for this device + bool mUsePartialResult; // Track most recent frame number for which 3A notifications were sent for. // Used to filter against sending 3A notifications for the same frame diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp index 2de7a2b..cda98be 100644 --- a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp @@ -73,24 +73,24 @@ status_t JpegProcessor::updateStream(const Parameters ¶ms) { } // Find out buffer size for JPEG - camera_metadata_ro_entry_t maxJpegSize = - params.staticInfo(ANDROID_JPEG_MAX_SIZE); - if (maxJpegSize.count == 0) { - ALOGE("%s: Camera %d: Can't find ANDROID_JPEG_MAX_SIZE!", - __FUNCTION__, mId); + ssize_t maxJpegSize = device->getJpegBufferSize(params.pictureWidth, params.pictureHeight); + if (maxJpegSize <= 0) { + ALOGE("%s: Camera %d: Jpeg buffer size (%zu) is invalid ", + __FUNCTION__, mId, maxJpegSize); return INVALID_OPERATION; } if (mCaptureConsumer == 0) { // Create CPU buffer queue endpoint - sp<BufferQueue> bq = new BufferQueue(); - mCaptureConsumer = new CpuConsumer(bq, 1); + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + mCaptureConsumer = new CpuConsumer(consumer, 1); mCaptureConsumer->setFrameAvailableListener(this); mCaptureConsumer->setName(String8("Camera2Client::CaptureConsumer")); - mCaptureWindow = new Surface(bq); + mCaptureWindow = new Surface(producer); // Create memory for API consumption - mCaptureHeap = new MemoryHeapBase(maxJpegSize.data.i32[0], 0, - "Camera2Client::CaptureHeap"); + mCaptureHeap = new MemoryHeapBase(maxJpegSize, 0, "Camera2Client::CaptureHeap"); if (mCaptureHeap->getSize() == 0) { ALOGE("%s: Camera %d: Unable to allocate memory for capture", __FUNCTION__, mId); @@ -132,8 +132,7 @@ status_t JpegProcessor::updateStream(const Parameters ¶ms) { // Create stream for HAL production res = device->createStream(mCaptureWindow, params.pictureWidth, params.pictureHeight, - HAL_PIXEL_FORMAT_BLOB, maxJpegSize.data.i32[0], - &mCaptureStreamId); + HAL_PIXEL_FORMAT_BLOB, &mCaptureStreamId); if (res != OK) { ALOGE("%s: Camera %d: Can't create output stream for capture: " "%s (%d)", __FUNCTION__, mId, diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp index 081a6e6..8d00590 100644 --- a/services/camera/libcameraservice/api1/client2/Parameters.cpp +++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp @@ -29,6 +29,9 @@ #include "Parameters.h" #include "system/camera.h" +#include "hardware/camera_common.h" +#include <media/MediaProfiles.h> +#include <media/mediarecorder.h> namespace android { namespace camera2 { @@ -43,7 +46,7 @@ Parameters::Parameters(int cameraId, Parameters::~Parameters() { } -status_t Parameters::initialize(const CameraMetadata *info) { +status_t Parameters::initialize(const CameraMetadata *info, int deviceVersion) { status_t res; if (info->entryCount() == 0) { @@ -51,6 +54,7 @@ status_t Parameters::initialize(const CameraMetadata *info) { return BAD_VALUE; } Parameters::info = info; + mDeviceVersion = deviceVersion; res = buildFastInfo(); if (res != OK) return res; @@ -59,7 +63,17 @@ status_t Parameters::initialize(const CameraMetadata *info) { if (res != OK) return res; const Size MAX_PREVIEW_SIZE = { MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT }; - res = getFilteredPreviewSizes(MAX_PREVIEW_SIZE, &availablePreviewSizes); + // Treat the H.264 max size as the max supported video size. + MediaProfiles *videoEncoderProfiles = MediaProfiles::getInstance(); + int32_t maxVideoWidth = videoEncoderProfiles->getVideoEncoderParamByName( + "enc.vid.width.max", VIDEO_ENCODER_H264); + int32_t maxVideoHeight = videoEncoderProfiles->getVideoEncoderParamByName( + "enc.vid.height.max", VIDEO_ENCODER_H264); + const Size MAX_VIDEO_SIZE = {maxVideoWidth, maxVideoHeight}; + + res = getFilteredSizes(MAX_PREVIEW_SIZE, &availablePreviewSizes); + if (res != OK) return res; + res = getFilteredSizes(MAX_VIDEO_SIZE, &availableVideoSizes); if (res != OK) return res; // TODO: Pick more intelligently @@ -84,8 +98,17 @@ status_t Parameters::initialize(const CameraMetadata *info) { ALOGV("Supported preview sizes are: %s", supportedPreviewSizes.string()); params.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, supportedPreviewSizes); + + String8 supportedVideoSizes; + for (size_t i = 0; i < availableVideoSizes.size(); i++) { + if (i != 0) supportedVideoSizes += ","; + supportedVideoSizes += String8::format("%dx%d", + availableVideoSizes[i].width, + availableVideoSizes[i].height); + } + ALOGV("Supported video sizes are: %s", supportedVideoSizes.string()); params.set(CameraParameters::KEY_SUPPORTED_VIDEO_SIZES, - supportedPreviewSizes); + supportedVideoSizes); } camera_metadata_ro_entry_t availableFpsRanges = @@ -99,16 +122,14 @@ status_t Parameters::initialize(const CameraMetadata *info) { previewTransform = degToTransform(0, cameraFacing == CAMERA_FACING_FRONT); - camera_metadata_ro_entry_t availableFormats = - staticInfo(ANDROID_SCALER_AVAILABLE_FORMATS); - { String8 supportedPreviewFormats; + SortedVector<int32_t> outputFormats = getAvailableOutputFormats(); bool addComma = false; - for (size_t i=0; i < availableFormats.count; i++) { + for (size_t i=0; i < outputFormats.size(); i++) { if (addComma) supportedPreviewFormats += ","; addComma = true; - switch (availableFormats.data.i32[i]) { + switch (outputFormats[i]) { case HAL_PIXEL_FORMAT_YCbCr_422_SP: supportedPreviewFormats += CameraParameters::PIXEL_FORMAT_YUV422SP; @@ -150,7 +171,7 @@ status_t Parameters::initialize(const CameraMetadata *info) { default: ALOGW("%s: Camera %d: Unknown preview format: %x", - __FUNCTION__, cameraId, availableFormats.data.i32[i]); + __FUNCTION__, cameraId, outputFormats[i]); addComma = false; break; } @@ -222,24 +243,26 @@ status_t Parameters::initialize(const CameraMetadata *info) { supportedPreviewFrameRates); } - camera_metadata_ro_entry_t availableJpegSizes = - staticInfo(ANDROID_SCALER_AVAILABLE_JPEG_SIZES, 2); - if (!availableJpegSizes.count) return NO_INIT; + Vector<Size> availableJpegSizes = getAvailableJpegSizes(); + if (!availableJpegSizes.size()) return NO_INIT; // TODO: Pick maximum - pictureWidth = availableJpegSizes.data.i32[0]; - pictureHeight = availableJpegSizes.data.i32[1]; + pictureWidth = availableJpegSizes[0].width; + pictureHeight = availableJpegSizes[0].height; + pictureWidthLastSet = pictureWidth; + pictureHeightLastSet = pictureHeight; + pictureSizeOverriden = false; params.setPictureSize(pictureWidth, pictureHeight); { String8 supportedPictureSizes; - for (size_t i=0; i < availableJpegSizes.count; i += 2) { + for (size_t i=0; i < availableJpegSizes.size(); i++) { if (i != 0) supportedPictureSizes += ","; supportedPictureSizes += String8::format("%dx%d", - availableJpegSizes.data.i32[i], - availableJpegSizes.data.i32[i+1]); + availableJpegSizes[i].width, + availableJpegSizes[i].height); } params.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, supportedPictureSizes); @@ -470,7 +493,7 @@ status_t Parameters::initialize(const CameraMetadata *info) { supportedAntibanding); } - sceneMode = ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED; + sceneMode = ANDROID_CONTROL_SCENE_MODE_DISABLED; params.set(CameraParameters::KEY_SCENE_MODE, CameraParameters::SCENE_MODE_AUTO); @@ -486,7 +509,7 @@ status_t Parameters::initialize(const CameraMetadata *info) { if (addComma) supportedSceneModes += ","; addComma = true; switch (availableSceneModes.data.u8[i]) { - case ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED: + case ANDROID_CONTROL_SCENE_MODE_DISABLED: noSceneModes = true; break; case ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY: @@ -624,8 +647,17 @@ status_t Parameters::initialize(const CameraMetadata *info) { focusMode = Parameters::FOCUS_MODE_AUTO; params.set(CameraParameters::KEY_FOCUS_MODE, CameraParameters::FOCUS_MODE_AUTO); - String8 supportedFocusModes(CameraParameters::FOCUS_MODE_INFINITY); - bool addComma = true; + String8 supportedFocusModes; + bool addComma = false; + camera_metadata_ro_entry_t focusDistanceCalibration = + staticInfo(ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION, 0, 0, false); + + if (focusDistanceCalibration.count && + focusDistanceCalibration.data.u8[0] != + ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED) { + supportedFocusModes += CameraParameters::FOCUS_MODE_INFINITY; + addComma = true; + } for (size_t i=0; i < availableAfModes.count; i++) { if (addComma) supportedFocusModes += ","; @@ -668,13 +700,13 @@ status_t Parameters::initialize(const CameraMetadata *info) { focusState = ANDROID_CONTROL_AF_STATE_INACTIVE; shadowFocusMode = FOCUS_MODE_INVALID; - camera_metadata_ro_entry_t max3aRegions = - staticInfo(ANDROID_CONTROL_MAX_REGIONS, 1, 1); - if (!max3aRegions.count) return NO_INIT; + camera_metadata_ro_entry_t max3aRegions = staticInfo(ANDROID_CONTROL_MAX_REGIONS, + Parameters::NUM_REGION, Parameters::NUM_REGION); + if (max3aRegions.count != Parameters::NUM_REGION) return NO_INIT; int32_t maxNumFocusAreas = 0; if (focusMode != Parameters::FOCUS_MODE_FIXED) { - maxNumFocusAreas = max3aRegions.data.i32[0]; + maxNumFocusAreas = max3aRegions.data.i32[Parameters::REGION_AF]; } params.set(CameraParameters::KEY_MAX_NUM_FOCUS_AREAS, maxNumFocusAreas); params.set(CameraParameters::KEY_FOCUS_AREAS, @@ -734,7 +766,7 @@ status_t Parameters::initialize(const CameraMetadata *info) { meteringAreas.add(Parameters::Area(0, 0, 0, 0, 0)); params.set(CameraParameters::KEY_MAX_NUM_METERING_AREAS, - max3aRegions.data.i32[0]); + max3aRegions.data.i32[Parameters::REGION_AE]); params.set(CameraParameters::KEY_METERING_AREAS, "(0,0,0,0,0)"); @@ -935,9 +967,8 @@ status_t Parameters::buildFastInfo() { staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS); if (!availableFocalLengths.count) return NO_INIT; - camera_metadata_ro_entry_t availableFormats = - staticInfo(ANDROID_SCALER_AVAILABLE_FORMATS); - if (!availableFormats.count) return NO_INIT; + SortedVector<int32_t> availableFormats = getAvailableOutputFormats(); + if (!availableFormats.size()) return NO_INIT; if (sceneModeOverrides.count > 0) { @@ -1021,8 +1052,8 @@ status_t Parameters::buildFastInfo() { // Check if the HAL supports HAL_PIXEL_FORMAT_YCbCr_420_888 fastInfo.useFlexibleYuv = false; - for (size_t i = 0; i < availableFormats.count; i++) { - if (availableFormats.data.i32[i] == HAL_PIXEL_FORMAT_YCbCr_420_888) { + for (size_t i = 0; i < availableFormats.size(); i++) { + if (availableFormats[i] == HAL_PIXEL_FORMAT_YCbCr_420_888) { fastInfo.useFlexibleYuv = true; break; } @@ -1225,8 +1256,7 @@ status_t Parameters::set(const String8& paramString) { "is active!", __FUNCTION__); return BAD_VALUE; } - camera_metadata_ro_entry_t availableFormats = - staticInfo(ANDROID_SCALER_AVAILABLE_FORMATS); + SortedVector<int32_t> availableFormats = getAvailableOutputFormats(); // If using flexible YUV, always support NV21/YV12. Otherwise, check // HAL's list. if (! (fastInfo.useFlexibleYuv && @@ -1235,11 +1265,10 @@ status_t Parameters::set(const String8& paramString) { validatedParams.previewFormat == HAL_PIXEL_FORMAT_YV12) ) ) { // Not using flexible YUV format, so check explicitly - for (i = 0; i < availableFormats.count; i++) { - if (availableFormats.data.i32[i] == - validatedParams.previewFormat) break; + for (i = 0; i < availableFormats.size(); i++) { + if (availableFormats[i] == validatedParams.previewFormat) break; } - if (i == availableFormats.count) { + if (i == availableFormats.size()) { ALOGE("%s: Requested preview format %s (0x%x) is not supported", __FUNCTION__, newParams.getPreviewFormat(), validatedParams.previewFormat); @@ -1355,17 +1384,16 @@ status_t Parameters::set(const String8& paramString) { // PICTURE_SIZE newParams.getPictureSize(&validatedParams.pictureWidth, &validatedParams.pictureHeight); - if (validatedParams.pictureWidth == pictureWidth || - validatedParams.pictureHeight == pictureHeight) { - camera_metadata_ro_entry_t availablePictureSizes = - staticInfo(ANDROID_SCALER_AVAILABLE_JPEG_SIZES); - for (i = 0; i < availablePictureSizes.count; i+=2) { - if ((availablePictureSizes.data.i32[i] == + if (validatedParams.pictureWidth != pictureWidth || + validatedParams.pictureHeight != pictureHeight) { + Vector<Size> availablePictureSizes = getAvailableJpegSizes(); + for (i = 0; i < availablePictureSizes.size(); i++) { + if ((availablePictureSizes[i].width == validatedParams.pictureWidth) && - (availablePictureSizes.data.i32[i+1] == + (availablePictureSizes[i].height == validatedParams.pictureHeight)) break; } - if (i == availablePictureSizes.count) { + if (i == availablePictureSizes.size()) { ALOGE("%s: Requested picture size %d x %d is not supported", __FUNCTION__, validatedParams.pictureWidth, validatedParams.pictureHeight); @@ -1522,7 +1550,7 @@ status_t Parameters::set(const String8& paramString) { newParams.get(CameraParameters::KEY_SCENE_MODE) ); if (validatedParams.sceneMode != sceneMode && validatedParams.sceneMode != - ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED) { + ANDROID_CONTROL_SCENE_MODE_DISABLED) { camera_metadata_ro_entry_t availableSceneModes = staticInfo(ANDROID_CONTROL_AVAILABLE_SCENE_MODES); for (i = 0; i < availableSceneModes.count; i++) { @@ -1537,7 +1565,7 @@ status_t Parameters::set(const String8& paramString) { } } bool sceneModeSet = - validatedParams.sceneMode != ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED; + validatedParams.sceneMode != ANDROID_CONTROL_SCENE_MODE_DISABLED; // FLASH_MODE if (sceneModeSet) { @@ -1667,10 +1695,11 @@ status_t Parameters::set(const String8& paramString) { // FOCUS_AREAS res = parseAreas(newParams.get(CameraParameters::KEY_FOCUS_AREAS), &validatedParams.focusingAreas); - size_t max3aRegions = - (size_t)staticInfo(ANDROID_CONTROL_MAX_REGIONS, 1, 1).data.i32[0]; + size_t maxAfRegions = (size_t)staticInfo(ANDROID_CONTROL_MAX_REGIONS, + Parameters::NUM_REGION, Parameters::NUM_REGION). + data.i32[Parameters::REGION_AF]; if (res == OK) res = validateAreas(validatedParams.focusingAreas, - max3aRegions, AREA_KIND_FOCUS); + maxAfRegions, AREA_KIND_FOCUS); if (res != OK) { ALOGE("%s: Requested focus areas are malformed: %s", __FUNCTION__, newParams.get(CameraParameters::KEY_FOCUS_AREAS)); @@ -1700,10 +1729,13 @@ status_t Parameters::set(const String8& paramString) { newParams.get(CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK)); // METERING_AREAS + size_t maxAeRegions = (size_t)staticInfo(ANDROID_CONTROL_MAX_REGIONS, + Parameters::NUM_REGION, Parameters::NUM_REGION). + data.i32[Parameters::REGION_AE]; res = parseAreas(newParams.get(CameraParameters::KEY_METERING_AREAS), &validatedParams.meteringAreas); if (res == OK) { - res = validateAreas(validatedParams.meteringAreas, max3aRegions, + res = validateAreas(validatedParams.meteringAreas, maxAeRegions, AREA_KIND_METERING); } if (res != OK) { @@ -1728,21 +1760,26 @@ status_t Parameters::set(const String8& paramString) { if (validatedParams.videoWidth != videoWidth || validatedParams.videoHeight != videoHeight) { if (state == RECORD) { - ALOGE("%s: Video size cannot be updated when recording is active!", - __FUNCTION__); - return BAD_VALUE; - } - for (i = 0; i < availablePreviewSizes.size(); i++) { - if ((availablePreviewSizes[i].width == - validatedParams.videoWidth) && - (availablePreviewSizes[i].height == - validatedParams.videoHeight)) break; - } - if (i == availablePreviewSizes.size()) { - ALOGE("%s: Requested video size %d x %d is not supported", - __FUNCTION__, validatedParams.videoWidth, + ALOGW("%s: Video size cannot be updated (from %d x %d to %d x %d)" + " when recording is active! Ignore the size update!", + __FUNCTION__, videoWidth, videoHeight, validatedParams.videoWidth, validatedParams.videoHeight); - return BAD_VALUE; + validatedParams.videoWidth = videoWidth; + validatedParams.videoHeight = videoHeight; + newParams.setVideoSize(videoWidth, videoHeight); + } else { + for (i = 0; i < availableVideoSizes.size(); i++) { + if ((availableVideoSizes[i].width == + validatedParams.videoWidth) && + (availableVideoSizes[i].height == + validatedParams.videoHeight)) break; + } + if (i == availableVideoSizes.size()) { + ALOGE("%s: Requested video size %d x %d is not supported", + __FUNCTION__, validatedParams.videoWidth, + validatedParams.videoHeight); + return BAD_VALUE; + } } } @@ -1764,6 +1801,7 @@ status_t Parameters::set(const String8& paramString) { /** Update internal parameters */ *this = validatedParams; + updateOverriddenJpegSize(); /** Update external parameters calculated from the internal ones */ @@ -1855,7 +1893,7 @@ status_t Parameters::updateRequest(CameraMetadata *request) const { // (face detection statistics and face priority scene mode). Map from other // to the other. bool sceneModeActive = - sceneMode != (uint8_t)ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED; + sceneMode != (uint8_t)ANDROID_CONTROL_SCENE_MODE_DISABLED; uint8_t reqControlMode = ANDROID_CONTROL_MODE_AUTO; if (enableFaceDetect || sceneModeActive) { reqControlMode = ANDROID_CONTROL_MODE_USE_SCENE_MODE; @@ -1867,7 +1905,7 @@ status_t Parameters::updateRequest(CameraMetadata *request) const { uint8_t reqSceneMode = sceneModeActive ? sceneMode : enableFaceDetect ? (uint8_t)ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY : - (uint8_t)ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED; + (uint8_t)ANDROID_CONTROL_SCENE_MODE_DISABLED; res = request->update(ANDROID_CONTROL_SCENE_MODE, &reqSceneMode, 1); if (res != OK) return res; @@ -1988,6 +2026,23 @@ status_t Parameters::updateRequest(CameraMetadata *request) const { reqMeteringAreas, reqMeteringAreasSize); if (res != OK) return res; + // Set awb regions to be the same as the metering regions if allowed + size_t maxAwbRegions = (size_t)staticInfo(ANDROID_CONTROL_MAX_REGIONS, + Parameters::NUM_REGION, Parameters::NUM_REGION). + data.i32[Parameters::REGION_AWB]; + if (maxAwbRegions > 0) { + if (maxAwbRegions >= meteringAreas.size()) { + res = request->update(ANDROID_CONTROL_AWB_REGIONS, + reqMeteringAreas, reqMeteringAreasSize); + } else { + // Ensure the awb regions are zeroed if the region count is too high. + int32_t zeroedAwbAreas[5] = {0, 0, 0, 0, 0}; + res = request->update(ANDROID_CONTROL_AWB_REGIONS, + zeroedAwbAreas, sizeof(zeroedAwbAreas)/sizeof(int32_t)); + } + if (res != OK) return res; + } + delete[] reqMeteringAreas; /* don't include jpeg thumbnail size - it's valid for @@ -2064,6 +2119,52 @@ status_t Parameters::updateRequestJpeg(CameraMetadata *request) const { return OK; } +status_t Parameters::overrideJpegSizeByVideoSize() { + if (pictureSizeOverriden) { + ALOGV("Picture size has been overridden. Skip overriding"); + return OK; + } + + pictureSizeOverriden = true; + pictureWidthLastSet = pictureWidth; + pictureHeightLastSet = pictureHeight; + pictureWidth = videoWidth; + pictureHeight = videoHeight; + // This change of picture size is invisible to app layer. + // Do not update app visible params + return OK; +} + +status_t Parameters::updateOverriddenJpegSize() { + if (!pictureSizeOverriden) { + ALOGV("Picture size has not been overridden. Skip checking"); + return OK; + } + + pictureWidthLastSet = pictureWidth; + pictureHeightLastSet = pictureHeight; + + if (pictureWidth <= videoWidth && pictureHeight <= videoHeight) { + // Picture size is now smaller than video size. No need to override anymore + return recoverOverriddenJpegSize(); + } + + pictureWidth = videoWidth; + pictureHeight = videoHeight; + + return OK; +} + +status_t Parameters::recoverOverriddenJpegSize() { + if (!pictureSizeOverriden) { + ALOGV("Picture size has not been overridden. Skip recovering"); + return OK; + } + pictureSizeOverriden = false; + pictureWidth = pictureWidthLastSet; + pictureHeight = pictureHeightLastSet; + return OK; +} const char* Parameters::getStateName(State state) { #define CASE_ENUM_TO_CHAR(x) case x: return(#x); break; @@ -2083,24 +2184,7 @@ const char* Parameters::getStateName(State state) { } int Parameters::formatStringToEnum(const char *format) { - return - !format ? - HAL_PIXEL_FORMAT_YCrCb_420_SP : - !strcmp(format, CameraParameters::PIXEL_FORMAT_YUV422SP) ? - HAL_PIXEL_FORMAT_YCbCr_422_SP : // NV16 - !strcmp(format, CameraParameters::PIXEL_FORMAT_YUV420SP) ? - HAL_PIXEL_FORMAT_YCrCb_420_SP : // NV21 - !strcmp(format, CameraParameters::PIXEL_FORMAT_YUV422I) ? - HAL_PIXEL_FORMAT_YCbCr_422_I : // YUY2 - !strcmp(format, CameraParameters::PIXEL_FORMAT_YUV420P) ? - HAL_PIXEL_FORMAT_YV12 : // YV12 - !strcmp(format, CameraParameters::PIXEL_FORMAT_RGB565) ? - HAL_PIXEL_FORMAT_RGB_565 : // RGB565 - !strcmp(format, CameraParameters::PIXEL_FORMAT_RGBA8888) ? - HAL_PIXEL_FORMAT_RGBA_8888 : // RGB8888 - !strcmp(format, CameraParameters::PIXEL_FORMAT_BAYER_RGGB) ? - HAL_PIXEL_FORMAT_RAW_SENSOR : // Raw sensor data - -1; + return CameraParameters::previewFormatToEnum(format); } const char* Parameters::formatEnumToString(int format) { @@ -2228,9 +2312,9 @@ int Parameters::abModeStringToEnum(const char *abMode) { int Parameters::sceneModeStringToEnum(const char *sceneMode) { return !sceneMode ? - ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED : + ANDROID_CONTROL_SCENE_MODE_DISABLED : !strcmp(sceneMode, CameraParameters::SCENE_MODE_AUTO) ? - ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED : + ANDROID_CONTROL_SCENE_MODE_DISABLED : !strcmp(sceneMode, CameraParameters::SCENE_MODE_ACTION) ? ANDROID_CONTROL_SCENE_MODE_ACTION : !strcmp(sceneMode, CameraParameters::SCENE_MODE_PORTRAIT) ? @@ -2569,7 +2653,7 @@ int Parameters::normalizedYToArray(int y) const { return cropYToArray(normalizedYToCrop(y)); } -status_t Parameters::getFilteredPreviewSizes(Size limit, Vector<Size> *sizes) { +status_t Parameters::getFilteredSizes(Size limit, Vector<Size> *sizes) { if (info == NULL) { ALOGE("%s: Static metadata is not initialized", __FUNCTION__); return NO_INIT; @@ -2578,22 +2662,37 @@ status_t Parameters::getFilteredPreviewSizes(Size limit, Vector<Size> *sizes) { ALOGE("%s: Input size is null", __FUNCTION__); return BAD_VALUE; } - - const size_t SIZE_COUNT = sizeof(Size) / sizeof(int); - camera_metadata_ro_entry_t availableProcessedSizes = - staticInfo(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES, SIZE_COUNT); - if (availableProcessedSizes.count < SIZE_COUNT) return BAD_VALUE; - - Size previewSize; - for (size_t i = 0; i < availableProcessedSizes.count; i += SIZE_COUNT) { - previewSize.width = availableProcessedSizes.data.i32[i]; - previewSize.height = availableProcessedSizes.data.i32[i+1]; - // Need skip the preview sizes that are too large. - if (previewSize.width <= limit.width && - previewSize.height <= limit.height) { - sizes->push(previewSize); + sizes->clear(); + + if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) { + Vector<StreamConfiguration> scs = getStreamConfigurations(); + for (size_t i=0; i < scs.size(); i++) { + const StreamConfiguration &sc = scs[i]; + if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT && + sc.format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED && + sc.width <= limit.width && sc.height <= limit.height) { + Size sz = {sc.width, sc.height}; + sizes->push(sz); } + } + } else { + const size_t SIZE_COUNT = sizeof(Size) / sizeof(int); + camera_metadata_ro_entry_t availableProcessedSizes = + staticInfo(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES, SIZE_COUNT); + if (availableProcessedSizes.count < SIZE_COUNT) return BAD_VALUE; + + Size filteredSize; + for (size_t i = 0; i < availableProcessedSizes.count; i += SIZE_COUNT) { + filteredSize.width = availableProcessedSizes.data.i32[i]; + filteredSize.height = availableProcessedSizes.data.i32[i+1]; + // Need skip the preview sizes that are too large. + if (filteredSize.width <= limit.width && + filteredSize.height <= limit.height) { + sizes->push(filteredSize); + } + } } + if (sizes->isEmpty()) { ALOGE("generated preview size list is empty!!"); return BAD_VALUE; @@ -2627,6 +2726,78 @@ Parameters::Size Parameters::getMaxSizeForRatio( return maxSize; } +Vector<Parameters::StreamConfiguration> Parameters::getStreamConfigurations() { + const int STREAM_CONFIGURATION_SIZE = 4; + const int STREAM_FORMAT_OFFSET = 0; + const int STREAM_WIDTH_OFFSET = 1; + const int STREAM_HEIGHT_OFFSET = 2; + const int STREAM_IS_INPUT_OFFSET = 3; + Vector<StreamConfiguration> scs; + if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) { + ALOGE("StreamConfiguration is only valid after device HAL 3.2!"); + return scs; + } + + camera_metadata_ro_entry_t availableStreamConfigs = + staticInfo(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS); + for (size_t i=0; i < availableStreamConfigs.count; i+= STREAM_CONFIGURATION_SIZE) { + int32_t format = availableStreamConfigs.data.i32[i + STREAM_FORMAT_OFFSET]; + int32_t width = availableStreamConfigs.data.i32[i + STREAM_WIDTH_OFFSET]; + int32_t height = availableStreamConfigs.data.i32[i + STREAM_HEIGHT_OFFSET]; + int32_t isInput = availableStreamConfigs.data.i32[i + STREAM_IS_INPUT_OFFSET]; + StreamConfiguration sc = {format, width, height, isInput}; + scs.add(sc); + } + return scs; +} + +SortedVector<int32_t> Parameters::getAvailableOutputFormats() { + SortedVector<int32_t> outputFormats; // Non-duplicated output formats + if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) { + Vector<StreamConfiguration> scs = getStreamConfigurations(); + for (size_t i=0; i < scs.size(); i++) { + const StreamConfiguration &sc = scs[i]; + if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) { + outputFormats.add(sc.format); + } + } + } else { + camera_metadata_ro_entry_t availableFormats = staticInfo(ANDROID_SCALER_AVAILABLE_FORMATS); + for (size_t i=0; i < availableFormats.count; i++) { + outputFormats.add(availableFormats.data.i32[i]); + } + } + return outputFormats; +} + +Vector<Parameters::Size> Parameters::getAvailableJpegSizes() { + Vector<Parameters::Size> jpegSizes; + if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) { + Vector<StreamConfiguration> scs = getStreamConfigurations(); + for (size_t i=0; i < scs.size(); i++) { + const StreamConfiguration &sc = scs[i]; + if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT && + sc.format == HAL_PIXEL_FORMAT_BLOB) { + Size sz = {sc.width, sc.height}; + jpegSizes.add(sz); + } + } + } else { + const int JPEG_SIZE_ENTRY_COUNT = 2; + const int WIDTH_OFFSET = 0; + const int HEIGHT_OFFSET = 1; + camera_metadata_ro_entry_t availableJpegSizes = + staticInfo(ANDROID_SCALER_AVAILABLE_JPEG_SIZES); + for (size_t i=0; i < availableJpegSizes.count; i+= JPEG_SIZE_ENTRY_COUNT) { + int width = availableJpegSizes.data.i32[i + WIDTH_OFFSET]; + int height = availableJpegSizes.data.i32[i + HEIGHT_OFFSET]; + Size sz = {width, height}; + jpegSizes.add(sz); + } + } + return jpegSizes; +} + Parameters::CropRegion Parameters::calculateCropRegion( Parameters::CropRegion::Outputs outputs) const { diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h index da07ccf..5e6e6ab 100644 --- a/services/camera/libcameraservice/api1/client2/Parameters.h +++ b/services/camera/libcameraservice/api1/client2/Parameters.h @@ -52,6 +52,9 @@ struct Parameters { int previewTransform; // set by CAMERA_CMD_SET_DISPLAY_ORIENTATION int pictureWidth, pictureHeight; + // Store the picture size before they are overriden by video snapshot + int pictureWidthLastSet, pictureHeightLastSet; + bool pictureSizeOverriden; int32_t jpegThumbSize[2]; uint8_t jpegQuality, jpegThumbQuality; @@ -114,6 +117,14 @@ struct Parameters { bool autoExposureLock; bool autoWhiteBalanceLock; + // 3A region types, for use with ANDROID_CONTROL_MAX_REGIONS + enum region_t { + REGION_AE = 0, + REGION_AWB, + REGION_AF, + NUM_REGION // Number of region types + } region; + Vector<Area> meteringAreas; int zoom; @@ -219,7 +230,7 @@ struct Parameters { ~Parameters(); // Sets up default parameters - status_t initialize(const CameraMetadata *info); + status_t initialize(const CameraMetadata *info, int deviceVersion); // Build fast-access device static info from static info status_t buildFastInfo(); @@ -245,6 +256,12 @@ struct Parameters { // Add/update JPEG entries in metadata status_t updateRequestJpeg(CameraMetadata *request) const; + /* Helper functions to override jpeg size for video snapshot */ + // Override jpeg size by video size. Called during startRecording. + status_t overrideJpegSizeByVideoSize(); + // Recover overridden jpeg size. Called during stopRecording. + status_t recoverOverriddenJpegSize(); + // Calculate the crop region rectangle based on current stream sizes struct CropRegion { float left; @@ -334,10 +351,35 @@ private: int normalizedYToCrop(int y) const; Vector<Size> availablePreviewSizes; + Vector<Size> availableVideoSizes; // Get size list (that are no larger than limit) from static metadata. - status_t getFilteredPreviewSizes(Size limit, Vector<Size> *sizes); + status_t getFilteredSizes(Size limit, Vector<Size> *sizes); // Get max size (from the size array) that matches the given aspect ratio. Size getMaxSizeForRatio(float ratio, const int32_t* sizeArray, size_t count); + + // Helper function for overriding jpeg size for video snapshot + // Check if overridden jpeg size needs to be updated after Parameters::set. + // The behavior of this function is tailored to the implementation of Parameters::set. + // Do not use this function for other purpose. + status_t updateOverriddenJpegSize(); + + struct StreamConfiguration { + int32_t format; + int32_t width; + int32_t height; + int32_t isInput; + }; + // Helper function extract available stream configuration + // Only valid since device HAL version 3.2 + // returns an empty Vector if device HAL version does support it + Vector<StreamConfiguration> getStreamConfigurations(); + + // Helper function to get non-duplicated available output formats + SortedVector<int32_t> getAvailableOutputFormats(); + // Helper function to get available output jpeg sizes + Vector<Size> getAvailableJpegSizes(); + + int mDeviceVersion; }; // This class encapsulates the Parameters class so that it can only be accessed diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp index 77ae7ec..ab0af0d 100644 --- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp @@ -89,8 +89,26 @@ status_t StreamingProcessor::updatePreviewRequest(const Parameters ¶ms) { Mutex::Autolock m(mMutex); if (mPreviewRequest.entryCount() == 0) { - res = device->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW, - &mPreviewRequest); + sp<Camera2Client> client = mClient.promote(); + if (client == 0) { + ALOGE("%s: Camera %d: Client does not exist", __FUNCTION__, mId); + return INVALID_OPERATION; + } + + // Use CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG for ZSL streaming case. + if (client->getCameraDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_0) { + if (params.zslMode && !params.recordingHint) { + res = device->createDefaultRequest(CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG, + &mPreviewRequest); + } else { + res = device->createDefaultRequest(CAMERA3_TEMPLATE_PREVIEW, + &mPreviewRequest); + } + } else { + res = device->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW, + &mPreviewRequest); + } + if (res != OK) { ALOGE("%s: Camera %d: Unable to create default preview request: " "%s (%d)", __FUNCTION__, mId, strerror(-res), res); @@ -163,8 +181,7 @@ status_t StreamingProcessor::updatePreviewStream(const Parameters ¶ms) { if (mPreviewStreamId == NO_STREAM) { res = device->createStream(mPreviewWindow, params.previewWidth, params.previewHeight, - CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, 0, - &mPreviewStreamId); + CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, &mPreviewStreamId); if (res != OK) { ALOGE("%s: Camera %d: Unable to create preview stream: %s (%d)", __FUNCTION__, mId, strerror(-res), res); @@ -319,13 +336,15 @@ status_t StreamingProcessor::updateRecordingStream(const Parameters ¶ms) { // Create CPU buffer queue endpoint. We need one more buffer here so that we can // always acquire and free a buffer when the heap is full; otherwise the consumer // will have buffers in flight we'll never clear out. - sp<BufferQueue> bq = new BufferQueue(); - mRecordingConsumer = new BufferItemConsumer(bq, + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + mRecordingConsumer = new BufferItemConsumer(consumer, GRALLOC_USAGE_HW_VIDEO_ENCODER, mRecordingHeapCount + 1); mRecordingConsumer->setFrameAvailableListener(this); mRecordingConsumer->setName(String8("Camera2-RecordingConsumer")); - mRecordingWindow = new Surface(bq); + mRecordingWindow = new Surface(producer); newConsumer = true; // Allocate memory later, since we don't know buffer size until receipt } @@ -365,7 +384,7 @@ status_t StreamingProcessor::updateRecordingStream(const Parameters ¶ms) { mRecordingFrameCount = 0; res = device->createStream(mRecordingWindow, params.videoWidth, params.videoHeight, - CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, 0, &mRecordingStreamId); + CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, &mRecordingStreamId); if (res != OK) { ALOGE("%s: Camera %d: Can't create output stream for recording: " "%s (%d)", __FUNCTION__, mId, @@ -428,10 +447,13 @@ status_t StreamingProcessor::startStream(StreamType type, Mutex::Autolock m(mMutex); - // If a recording stream is being started up, free up any - // outstanding buffers left from the previous recording session. - // There should never be any, so if there are, warn about it. - if (isStreamActive(outputStreams, mRecordingStreamId)) { + // If a recording stream is being started up and no recording + // stream is active yet, free up any outstanding buffers left + // from the previous recording session. There should never be + // any, so if there are, warn about it. + bool isRecordingStreamIdle = !isStreamActive(mActiveStreamIds, mRecordingStreamId); + bool startRecordingStream = isStreamActive(outputStreams, mRecordingStreamId); + if (startRecordingStream && isRecordingStreamIdle) { releaseAllRecordingFramesLocked(); } diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp index 130f81a..bb72206 100644 --- a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp +++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp @@ -48,6 +48,7 @@ ZslProcessor::ZslProcessor( mDevice(client->getCameraDevice()), mSequencer(sequencer), mId(client->getCameraId()), + mDeleted(false), mZslBufferAvailable(false), mZslStreamId(NO_STREAM), mZslReprocessStreamId(NO_STREAM), @@ -62,7 +63,7 @@ ZslProcessor::ZslProcessor( ZslProcessor::~ZslProcessor() { ALOGV("%s: Exit", __FUNCTION__); - deleteStream(); + disconnect(); } void ZslProcessor::onFrameAvailable() { @@ -73,18 +74,19 @@ void ZslProcessor::onFrameAvailable() { } } -void ZslProcessor::onFrameAvailable(int32_t /*requestId*/, - const CameraMetadata &frame) { +void ZslProcessor::onResultAvailable(const CaptureResult &result) { + ATRACE_CALL(); + ALOGV("%s:", __FUNCTION__); Mutex::Autolock l(mInputMutex); camera_metadata_ro_entry_t entry; - entry = frame.find(ANDROID_SENSOR_TIMESTAMP); + entry = result.mMetadata.find(ANDROID_SENSOR_TIMESTAMP); nsecs_t timestamp = entry.data.i64[0]; (void)timestamp; ALOGVV("Got preview frame for timestamp %" PRId64, timestamp); if (mState != RUNNING) return; - mFrameList.editItemAt(mFrameListHead) = frame; + mFrameList.editItemAt(mFrameListHead) = result.mMetadata; mFrameListHead = (mFrameListHead + 1) % kFrameListDepth; findMatchesLocked(); @@ -130,13 +132,15 @@ status_t ZslProcessor::updateStream(const Parameters ¶ms) { if (mZslConsumer == 0) { // Create CPU buffer queue endpoint - sp<BufferQueue> bq = new BufferQueue(); - mZslConsumer = new BufferItemConsumer(bq, + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + mZslConsumer = new BufferItemConsumer(consumer, GRALLOC_USAGE_HW_CAMERA_ZSL, kZslBufferDepth); mZslConsumer->setFrameAvailableListener(this); mZslConsumer->setName(String8("Camera2Client::ZslConsumer")); - mZslWindow = new Surface(bq); + mZslWindow = new Surface(producer); } if (mZslStreamId != NO_STREAM) { @@ -150,7 +154,7 @@ status_t ZslProcessor::updateStream(const Parameters ¶ms) { mId, strerror(-res), res); return res; } - if (currentWidth != (uint32_t)params.fastInfo.arrayWidth || + if (mDeleted || currentWidth != (uint32_t)params.fastInfo.arrayWidth || currentHeight != (uint32_t)params.fastInfo.arrayHeight) { res = device->deleteReprocessStream(mZslReprocessStreamId); if (res != OK) { @@ -172,6 +176,8 @@ status_t ZslProcessor::updateStream(const Parameters ¶ms) { } } + mDeleted = false; + if (mZslStreamId == NO_STREAM) { // Create stream for HAL production // TODO: Sort out better way to select resolution for ZSL @@ -180,8 +186,7 @@ status_t ZslProcessor::updateStream(const Parameters ¶ms) { (int)HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; res = device->createStream(mZslWindow, params.fastInfo.arrayWidth, params.fastInfo.arrayHeight, - streamType, 0, - &mZslStreamId); + streamType, &mZslStreamId); if (res != OK) { ALOGE("%s: Camera %d: Can't create output stream for ZSL: " "%s (%d)", __FUNCTION__, mId, @@ -199,13 +204,22 @@ status_t ZslProcessor::updateStream(const Parameters ¶ms) { } client->registerFrameListener(Camera2Client::kPreviewRequestIdStart, Camera2Client::kPreviewRequestIdEnd, - this); + this, + /*sendPartials*/false); return OK; } status_t ZslProcessor::deleteStream() { ATRACE_CALL(); + Mutex::Autolock l(mInputMutex); + // WAR(b/15408128): do not delete stream unless client is being disconnected. + mDeleted = true; + return OK; +} + +status_t ZslProcessor::disconnect() { + ATRACE_CALL(); status_t res; Mutex::Autolock l(mInputMutex); diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.h b/services/camera/libcameraservice/api1/client2/ZslProcessor.h index 6d3cb85..b6533cf 100644 --- a/services/camera/libcameraservice/api1/client2/ZslProcessor.h +++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.h @@ -24,6 +24,7 @@ #include <utils/Condition.h> #include <gui/BufferItemConsumer.h> #include <camera/CameraMetadata.h> +#include <camera/CaptureResult.h> #include "common/CameraDeviceBase.h" #include "api1/client2/ZslProcessorInterface.h" @@ -54,7 +55,7 @@ class ZslProcessor: // From mZslConsumer virtual void onFrameAvailable(); // From FrameProcessor - virtual void onFrameAvailable(int32_t requestId, const CameraMetadata &frame); + virtual void onResultAvailable(const CaptureResult &result); virtual void onBufferReleased(buffer_handle_t *handle); @@ -66,6 +67,7 @@ class ZslProcessor: status_t updateStream(const Parameters ¶ms); status_t deleteStream(); + status_t disconnect(); int getStreamId() const; status_t pushToReprocess(int32_t requestId); @@ -85,6 +87,8 @@ class ZslProcessor: wp<CaptureSequencer> mSequencer; int mId; + bool mDeleted; + mutable Mutex mInputMutex; bool mZslBufferAvailable; Condition mZslBufferAvailableSignal; diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp index 2fce2b6..2d31275 100644 --- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp +++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp @@ -51,9 +51,42 @@ ZslProcessor3::ZslProcessor3( mZslStreamId(NO_STREAM), mFrameListHead(0), mZslQueueHead(0), - mZslQueueTail(0) { - mZslQueue.insertAt(0, kZslBufferDepth); - mFrameList.insertAt(0, kFrameListDepth); + mZslQueueTail(0), + mHasFocuser(false) { + // Initialize buffer queue and frame list based on pipeline max depth. + size_t pipelineMaxDepth = kDefaultMaxPipelineDepth; + if (client != 0) { + sp<Camera3Device> device = + static_cast<Camera3Device*>(client->getCameraDevice().get()); + if (device != 0) { + camera_metadata_ro_entry_t entry = + device->info().find(ANDROID_REQUEST_PIPELINE_MAX_DEPTH); + if (entry.count == 1) { + pipelineMaxDepth = entry.data.u8[0]; + } else { + ALOGW("%s: Unable to find the android.request.pipelineMaxDepth," + " use default pipeline max depth %zu", __FUNCTION__, + kDefaultMaxPipelineDepth); + } + + entry = device->info().find(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE); + if (entry.count > 0 && entry.data.f[0] != 0.) { + mHasFocuser = true; + } + } + } + + ALOGV("%s: Initialize buffer queue and frame list depth based on max pipeline depth (%d)", + __FUNCTION__, pipelineMaxDepth); + // Need to keep buffer queue longer than metadata queue because sometimes buffer arrives + // earlier than metadata which causes the buffer corresponding to oldest metadata being + // removed. + mFrameListDepth = pipelineMaxDepth; + mBufferQueueDepth = mFrameListDepth + 1; + + + mZslQueue.insertAt(0, mBufferQueueDepth); + mFrameList.insertAt(0, mFrameListDepth); sp<CaptureSequencer> captureSequencer = mSequencer.promote(); if (captureSequencer != 0) captureSequencer->setZslProcessor(this); } @@ -63,19 +96,32 @@ ZslProcessor3::~ZslProcessor3() { deleteStream(); } -void ZslProcessor3::onFrameAvailable(int32_t /*requestId*/, - const CameraMetadata &frame) { +void ZslProcessor3::onResultAvailable(const CaptureResult &result) { + ATRACE_CALL(); + ALOGV("%s:", __FUNCTION__); Mutex::Autolock l(mInputMutex); camera_metadata_ro_entry_t entry; - entry = frame.find(ANDROID_SENSOR_TIMESTAMP); + entry = result.mMetadata.find(ANDROID_SENSOR_TIMESTAMP); nsecs_t timestamp = entry.data.i64[0]; + if (entry.count == 0) { + ALOGE("%s: metadata doesn't have timestamp, skip this result", __FUNCTION__); + return; + } (void)timestamp; - ALOGVV("Got preview metadata for timestamp %" PRId64, timestamp); + + entry = result.mMetadata.find(ANDROID_REQUEST_FRAME_COUNT); + if (entry.count == 0) { + ALOGE("%s: metadata doesn't have frame number, skip this result", __FUNCTION__); + return; + } + int32_t frameNumber = entry.data.i32[0]; + + ALOGVV("Got preview metadata for frame %d with timestamp %" PRId64, frameNumber, timestamp); if (mState != RUNNING) return; - mFrameList.editItemAt(mFrameListHead) = frame; - mFrameListHead = (mFrameListHead + 1) % kFrameListDepth; + mFrameList.editItemAt(mFrameListHead) = result.mMetadata; + mFrameListHead = (mFrameListHead + 1) % mFrameListDepth; } status_t ZslProcessor3::updateStream(const Parameters ¶ms) { @@ -135,7 +181,7 @@ status_t ZslProcessor3::updateStream(const Parameters ¶ms) { // Note that format specified internally in Camera3ZslStream res = device->createZslStream( params.fastInfo.arrayWidth, params.fastInfo.arrayHeight, - kZslBufferDepth, + mBufferQueueDepth, &mZslStreamId, &mZslStream); if (res != OK) { @@ -144,10 +190,15 @@ status_t ZslProcessor3::updateStream(const Parameters ¶ms) { strerror(-res), res); return res; } + + // Only add the camera3 buffer listener when the stream is created. + mZslStream->addBufferListener(this); } + client->registerFrameListener(Camera2Client::kPreviewRequestIdStart, Camera2Client::kPreviewRequestIdEnd, - this); + this, + /*sendPartials*/false); return OK; } @@ -249,18 +300,45 @@ status_t ZslProcessor3::pushToReprocess(int32_t requestId) { uint8_t requestType = ANDROID_REQUEST_TYPE_REPROCESS; res = request.update(ANDROID_REQUEST_TYPE, &requestType, 1); + if (res != OK) { + ALOGE("%s: Unable to update request type", + __FUNCTION__); + return INVALID_OPERATION; + } + int32_t inputStreams[1] = { mZslStreamId }; - if (res == OK) request.update(ANDROID_REQUEST_INPUT_STREAMS, + res = request.update(ANDROID_REQUEST_INPUT_STREAMS, inputStreams, 1); + if (res != OK) { + ALOGE("%s: Unable to update request input streams", + __FUNCTION__); + return INVALID_OPERATION; + } + + uint8_t captureIntent = + static_cast<uint8_t>(ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE); + res = request.update(ANDROID_CONTROL_CAPTURE_INTENT, + &captureIntent, 1); + if (res != OK ) { + ALOGE("%s: Unable to update request capture intent", + __FUNCTION__); + return INVALID_OPERATION; + } + // TODO: Shouldn't we also update the latest preview frame? int32_t outputStreams[1] = { client->getCaptureStreamId() }; - if (res == OK) request.update(ANDROID_REQUEST_OUTPUT_STREAMS, + res = request.update(ANDROID_REQUEST_OUTPUT_STREAMS, outputStreams, 1); + if (res != OK) { + ALOGE("%s: Unable to update request output streams", + __FUNCTION__); + return INVALID_OPERATION; + } + res = request.update(ANDROID_REQUEST_ID, &requestId, 1); - if (res != OK ) { ALOGE("%s: Unable to update frame to a reprocess request", __FUNCTION__); @@ -312,11 +390,19 @@ status_t ZslProcessor3::clearZslQueue() { status_t ZslProcessor3::clearZslQueueLocked() { if (mZslStream != 0) { + // clear result metadata list first. + clearZslResultQueueLocked(); return mZslStream->clearInputRingBuffer(); } return OK; } +void ZslProcessor3::clearZslResultQueueLocked() { + mFrameList.clear(); + mFrameListHead = 0; + mFrameList.insertAt(0, mFrameListDepth); +} + void ZslProcessor3::dump(int fd, const Vector<String16>& /*args*/) const { Mutex::Autolock l(mInputMutex); if (!mLatestCapturedRequest.isEmpty()) { @@ -413,6 +499,25 @@ nsecs_t ZslProcessor3::getCandidateTimestampLocked(size_t* metadataIdx) const { continue; } + // Check AF state if device has focuser + if (mHasFocuser) { + // Make sure the candidate frame has good focus. + entry = frame.find(ANDROID_CONTROL_AF_STATE); + if (entry.count == 0) { + ALOGW("%s: ZSL queue frame has no AF state field!", + __FUNCTION__); + continue; + } + uint8_t afState = entry.data.u8[0]; + if (afState != ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED && + afState != ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED && + afState != ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) { + ALOGW("%s: ZSL queue frame AF state is %d is not good for capture, skip it", + __FUNCTION__, afState); + continue; + } + } + minTimestamp = frameTimestamp; idx = j; } @@ -453,13 +558,15 @@ void ZslProcessor3::onBufferAcquired(const BufferInfo& /*bufferInfo*/) { } void ZslProcessor3::onBufferReleased(const BufferInfo& bufferInfo) { - Mutex::Autolock l(mInputMutex); // ignore output buffers if (bufferInfo.mOutput) { return; } + // Lock mutex only once we know this is an input buffer returned to avoid + // potential deadlock + Mutex::Autolock l(mInputMutex); // TODO: Verify that the buffer is in our queue by looking at timestamp // theoretically unnecessary unless we change the following assumptions: // -- only 1 buffer reprocessed at a time (which is the case now) @@ -470,11 +577,17 @@ void ZslProcessor3::onBufferReleased(const BufferInfo& bufferInfo) { // We need to guarantee that if we do two back-to-back captures, // the second won't use a buffer that's older/the same as the first, which // is theoretically possible if we don't clear out the queue and the - // selection criteria is something like 'newest'. Clearing out the queue - // on a completed capture ensures we'll only use new data. + // selection criteria is something like 'newest'. Clearing out the result + // metadata queue on a completed capture ensures we'll only use new timestamp. + // Calling clearZslQueueLocked is a guaranteed deadlock because this callback + // holds the Camera3Stream internal lock (mLock), and clearZslQueueLocked requires + // to hold the same lock. + // TODO: need figure out a way to clear the Zsl buffer queue properly. Right now + // it is safe not to do so, as back to back ZSL capture requires stop and start + // preview, which will flush ZSL queue automatically. ALOGV("%s: Memory optimization, clearing ZSL queue", __FUNCTION__); - clearZslQueueLocked(); + clearZslResultQueueLocked(); // Required so we accept more ZSL requests mState = RUNNING; diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h index d2f8322..daa352b 100644 --- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h +++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h @@ -50,8 +50,8 @@ class ZslProcessor3 : ZslProcessor3(sp<Camera2Client> client, wp<CaptureSequencer> sequencer); ~ZslProcessor3(); - // From FrameProcessor - virtual void onFrameAvailable(int32_t requestId, const CameraMetadata &frame); + // From FrameProcessor::FilteredListener + virtual void onResultAvailable(const CaptureResult &result); /** **************************************** @@ -107,8 +107,9 @@ class ZslProcessor3 : CameraMetadata frame; }; - static const size_t kZslBufferDepth = 4; - static const size_t kFrameListDepth = kZslBufferDepth * 2; + static const int32_t kDefaultMaxPipelineDepth = 4; + size_t mBufferQueueDepth; + size_t mFrameListDepth; Vector<CameraMetadata> mFrameList; size_t mFrameListHead; @@ -120,10 +121,14 @@ class ZslProcessor3 : CameraMetadata mLatestCapturedRequest; + bool mHasFocuser; + virtual bool threadLoop(); status_t clearZslQueueLocked(); + void clearZslResultQueueLocked(); + void dumpZslQueue(int id) const; nsecs_t getCandidateTimestampLocked(size_t* metadataIdx) const; diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessorInterface.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessorInterface.cpp new file mode 100644 index 0000000..9efeaba --- /dev/null +++ b/services/camera/libcameraservice/api1/client2/ZslProcessorInterface.cpp @@ -0,0 +1,28 @@ +/* + * 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 "ZslProcessorInterface.h" + +namespace android { +namespace camera2 { + +status_t ZslProcessorInterface::disconnect() { + return OK; +} + +}; //namespace camera2 +}; //namespace android + diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessorInterface.h b/services/camera/libcameraservice/api1/client2/ZslProcessorInterface.h index 183c0c2..9e266e7 100644 --- a/services/camera/libcameraservice/api1/client2/ZslProcessorInterface.h +++ b/services/camera/libcameraservice/api1/client2/ZslProcessorInterface.h @@ -19,6 +19,8 @@ #include <utils/Errors.h> #include <utils/RefBase.h> +#include <utils/String16.h> +#include <utils/Vector.h> namespace android { namespace camera2 { @@ -37,6 +39,9 @@ public: // Delete the underlying CameraDevice streams virtual status_t deleteStream() = 0; + // Clear any additional state necessary before the CameraDevice is disconnected + virtual status_t disconnect(); + /** * Submits a ZSL capture request (id = requestId) * diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp index 142da9e..80c797a 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp @@ -16,13 +16,14 @@ #define LOG_TAG "CameraDeviceClient" #define ATRACE_TAG ATRACE_TAG_CAMERA -// #define LOG_NDEBUG 0 +//#define LOG_NDEBUG 0 #include <cutils/properties.h> #include <utils/Log.h> #include <utils/Trace.h> #include <gui/Surface.h> #include <camera/camera2/CaptureRequest.h> +#include <camera/CameraUtils.h> #include "common/CameraDeviceBase.h" #include "api2/CameraDeviceClient.h" @@ -82,7 +83,7 @@ status_t CameraDeviceClient::initialize(camera_module_t *module) mFrameProcessor->registerListener(FRAME_PROCESSOR_LISTENER_MIN_ID, FRAME_PROCESSOR_LISTENER_MAX_ID, /*listener*/this, - /*quirkSendPartials*/true); + /*sendPartials*/true); return OK; } @@ -91,79 +92,101 @@ CameraDeviceClient::~CameraDeviceClient() { } status_t CameraDeviceClient::submitRequest(sp<CaptureRequest> request, - bool streaming) { + bool streaming, + /*out*/ + int64_t* lastFrameNumber) { + List<sp<CaptureRequest> > requestList; + requestList.push_back(request); + return submitRequestList(requestList, streaming, lastFrameNumber); +} + +status_t CameraDeviceClient::submitRequestList(List<sp<CaptureRequest> > requests, + bool streaming, int64_t* lastFrameNumber) { ATRACE_CALL(); - ALOGV("%s", __FUNCTION__); + ALOGV("%s-start of function. Request list size %zu", __FUNCTION__, requests.size()); status_t res; - if ( (res = checkPid(__FUNCTION__) ) != OK) return res; Mutex::Autolock icl(mBinderSerializationLock); if (!mDevice.get()) return DEAD_OBJECT; - if (request == 0) { + if (requests.empty()) { ALOGE("%s: Camera %d: Sent null request. Rejecting request.", __FUNCTION__, mCameraId); return BAD_VALUE; } - CameraMetadata metadata(request->mMetadata); - - if (metadata.isEmpty()) { - ALOGE("%s: Camera %d: Sent empty metadata packet. Rejecting request.", - __FUNCTION__, mCameraId); - return BAD_VALUE; - } else if (request->mSurfaceList.size() == 0) { - ALOGE("%s: Camera %d: Requests must have at least one surface target. " - "Rejecting request.", __FUNCTION__, mCameraId); - return BAD_VALUE; - } + List<const CameraMetadata> metadataRequestList; + int32_t requestId = mRequestIdCounter; + uint32_t loopCounter = 0; - if (!enforceRequestPermissions(metadata)) { - // Callee logs - return PERMISSION_DENIED; - } + for (List<sp<CaptureRequest> >::iterator it = requests.begin(); it != requests.end(); ++it) { + sp<CaptureRequest> request = *it; + if (request == 0) { + ALOGE("%s: Camera %d: Sent null request.", + __FUNCTION__, mCameraId); + return BAD_VALUE; + } - /** - * Write in the output stream IDs which we calculate from - * the capture request's list of surface targets - */ - Vector<int32_t> outputStreamIds; - outputStreamIds.setCapacity(request->mSurfaceList.size()); - for (size_t i = 0; i < request->mSurfaceList.size(); ++i) { - sp<Surface> surface = request->mSurfaceList[i]; + CameraMetadata metadata(request->mMetadata); + if (metadata.isEmpty()) { + ALOGE("%s: Camera %d: Sent empty metadata packet. Rejecting request.", + __FUNCTION__, mCameraId); + return BAD_VALUE; + } else if (request->mSurfaceList.isEmpty()) { + ALOGE("%s: Camera %d: Requests must have at least one surface target. " + "Rejecting request.", __FUNCTION__, mCameraId); + return BAD_VALUE; + } - if (surface == 0) continue; + if (!enforceRequestPermissions(metadata)) { + // Callee logs + return PERMISSION_DENIED; + } - sp<IGraphicBufferProducer> gbp = surface->getIGraphicBufferProducer(); - int idx = mStreamMap.indexOfKey(gbp->asBinder()); + /** + * Write in the output stream IDs which we calculate from + * the capture request's list of surface targets + */ + Vector<int32_t> outputStreamIds; + outputStreamIds.setCapacity(request->mSurfaceList.size()); + for (size_t i = 0; i < request->mSurfaceList.size(); ++i) { + sp<Surface> surface = request->mSurfaceList[i]; + if (surface == 0) continue; + + sp<IGraphicBufferProducer> gbp = surface->getIGraphicBufferProducer(); + int idx = mStreamMap.indexOfKey(gbp->asBinder()); + + // Trying to submit request with surface that wasn't created + if (idx == NAME_NOT_FOUND) { + ALOGE("%s: Camera %d: Tried to submit a request with a surface that" + " we have not called createStream on", + __FUNCTION__, mCameraId); + return BAD_VALUE; + } - // Trying to submit request with surface that wasn't created - if (idx == NAME_NOT_FOUND) { - ALOGE("%s: Camera %d: Tried to submit a request with a surface that" - " we have not called createStream on", - __FUNCTION__, mCameraId); - return BAD_VALUE; + int streamId = mStreamMap.valueAt(idx); + outputStreamIds.push_back(streamId); + ALOGV("%s: Camera %d: Appending output stream %d to request", + __FUNCTION__, mCameraId, streamId); } - int streamId = mStreamMap.valueAt(idx); - outputStreamIds.push_back(streamId); - ALOGV("%s: Camera %d: Appending output stream %d to request", - __FUNCTION__, mCameraId, streamId); - } + metadata.update(ANDROID_REQUEST_OUTPUT_STREAMS, &outputStreamIds[0], + outputStreamIds.size()); - metadata.update(ANDROID_REQUEST_OUTPUT_STREAMS, &outputStreamIds[0], - outputStreamIds.size()); + metadata.update(ANDROID_REQUEST_ID, &requestId, /*size*/1); + loopCounter++; // loopCounter starts from 1 + ALOGV("%s: Camera %d: Creating request with ID %d (%d of %zu)", + __FUNCTION__, mCameraId, requestId, loopCounter, requests.size()); - int32_t requestId = mRequestIdCounter++; - metadata.update(ANDROID_REQUEST_ID, &requestId, /*size*/1); - ALOGV("%s: Camera %d: Submitting request with ID %d", - __FUNCTION__, mCameraId, requestId); + metadataRequestList.push_back(metadata); + } + mRequestIdCounter++; if (streaming) { - res = mDevice->setStreamingRequest(metadata); + res = mDevice->setStreamingRequestList(metadataRequestList, lastFrameNumber); if (res != OK) { ALOGE("%s: Camera %d: Got error %d after trying to set streaming " "request", __FUNCTION__, mCameraId, res); @@ -171,11 +194,12 @@ status_t CameraDeviceClient::submitRequest(sp<CaptureRequest> request, mStreamingRequestList.push_back(requestId); } } else { - res = mDevice->capture(metadata); + res = mDevice->captureList(metadataRequestList, lastFrameNumber); if (res != OK) { ALOGE("%s: Camera %d: Got error %d after trying to set capture", - __FUNCTION__, mCameraId, res); + __FUNCTION__, mCameraId, res); } + ALOGV("%s: requestId = %d ", __FUNCTION__, requestId); } ALOGV("%s: Camera %d: End of function", __FUNCTION__, mCameraId); @@ -186,7 +210,7 @@ status_t CameraDeviceClient::submitRequest(sp<CaptureRequest> request, return res; } -status_t CameraDeviceClient::cancelRequest(int requestId) { +status_t CameraDeviceClient::cancelRequest(int requestId, int64_t* lastFrameNumber) { ATRACE_CALL(); ALOGV("%s, requestId = %d", __FUNCTION__, requestId); @@ -212,7 +236,7 @@ status_t CameraDeviceClient::cancelRequest(int requestId) { return BAD_VALUE; } - res = mDevice->clearStreamingRequest(); + res = mDevice->clearStreamingRequest(lastFrameNumber); if (res == OK) { ALOGV("%s: Camera %d: Successfully cleared streaming request", @@ -223,6 +247,26 @@ status_t CameraDeviceClient::cancelRequest(int requestId) { return res; } +status_t CameraDeviceClient::beginConfigure() { + // TODO: Implement this. + ALOGE("%s: Not implemented yet.", __FUNCTION__); + return OK; +} + +status_t CameraDeviceClient::endConfigure() { + ALOGV("%s: ending configure (%zu streams)", + __FUNCTION__, mStreamMap.size()); + + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return res; + + Mutex::Autolock icl(mBinderSerializationLock); + + if (!mDevice.get()) return DEAD_OBJECT; + + return mDevice->configureStreams(); +} + status_t CameraDeviceClient::deleteStream(int streamId) { ATRACE_CALL(); ALOGV("%s (streamId = 0x%x)", __FUNCTION__, streamId); @@ -259,8 +303,6 @@ status_t CameraDeviceClient::deleteStream(int streamId) { } else if (res == OK) { mStreamMap.removeItemsAt(index); - ALOGV("%s: Camera %d: Successfully deleted stream ID (%d)", - __FUNCTION__, mCameraId, streamId); } return res; @@ -277,6 +319,10 @@ status_t CameraDeviceClient::createStream(int width, int height, int format, Mutex::Autolock icl(mBinderSerializationLock); + if (bufferProducer == NULL) { + ALOGE("%s: bufferProducer must not be null", __FUNCTION__); + return BAD_VALUE; + } if (!mDevice.get()) return DEAD_OBJECT; // Don't create multiple streams for the same target surface @@ -346,23 +392,7 @@ status_t CameraDeviceClient::createStream(int width, int height, int format, // after each call, but only once we are done with all. int streamId = -1; - if (format == HAL_PIXEL_FORMAT_BLOB) { - // JPEG buffers need to be sized for maximum possible compressed size - CameraMetadata staticInfo = mDevice->info(); - camera_metadata_entry_t entry = staticInfo.find(ANDROID_JPEG_MAX_SIZE); - if (entry.count == 0) { - ALOGE("%s: Camera %d: Can't find maximum JPEG size in " - "static metadata!", __FUNCTION__, mCameraId); - return INVALID_OPERATION; - } - int32_t maxJpegSize = entry.data.i32[0]; - res = mDevice->createStream(anw, width, height, format, maxJpegSize, - &streamId); - } else { - // All other streams are a known size - res = mDevice->createStream(anw, width, height, format, /*size*/0, - &streamId); - } + res = mDevice->createStream(anw, width, height, format, &streamId); if (res == OK) { mStreamMap.add(bufferProducer->asBinder(), streamId); @@ -465,7 +495,7 @@ status_t CameraDeviceClient::waitUntilIdle() return res; } -status_t CameraDeviceClient::flush() { +status_t CameraDeviceClient::flush(int64_t* lastFrameNumber) { ATRACE_CALL(); ALOGV("%s", __FUNCTION__); @@ -476,7 +506,8 @@ status_t CameraDeviceClient::flush() { if (!mDevice.get()) return DEAD_OBJECT; - return mDevice->flush(); + mStreamingRequestList.clear(); + return mDevice->flush(lastFrameNumber); } status_t CameraDeviceClient::dump(int fd, const Vector<String16>& args) { @@ -493,13 +524,13 @@ status_t CameraDeviceClient::dump(int fd, const Vector<String16>& args) { return dumpDevice(fd, args); } - -void CameraDeviceClient::notifyError() { +void CameraDeviceClient::notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, + const CaptureResultExtras& resultExtras) { // Thread safe. Don't bother locking. sp<ICameraDeviceCallbacks> remoteCb = getRemoteCallback(); if (remoteCb != 0) { - remoteCb->onDeviceError(ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE); + remoteCb->onDeviceError(errorCode, resultExtras); } } @@ -512,12 +543,12 @@ void CameraDeviceClient::notifyIdle() { } } -void CameraDeviceClient::notifyShutter(int requestId, +void CameraDeviceClient::notifyShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp) { // Thread safe. Don't bother locking. sp<ICameraDeviceCallbacks> remoteCb = getRemoteCallback(); if (remoteCb != 0) { - remoteCb->onCaptureStarted(requestId, timestamp); + remoteCb->onCaptureStarted(resultExtras, timestamp); } } @@ -552,16 +583,14 @@ void CameraDeviceClient::detachDevice() { } /** Device-related methods */ -void CameraDeviceClient::onFrameAvailable(int32_t requestId, - const CameraMetadata& frame) { +void CameraDeviceClient::onResultAvailable(const CaptureResult& result) { ATRACE_CALL(); ALOGV("%s", __FUNCTION__); // Thread-safe. No lock necessary. sp<ICameraDeviceCallbacks> remoteCb = mRemoteCallback; if (remoteCb != NULL) { - ALOGV("%s: frame = %p ", __FUNCTION__, &frame); - remoteCb->onResultReceived(requestId, frame); + remoteCb->onResultReceived(result.mMetadata, result.mResultExtras); } } @@ -620,61 +649,8 @@ bool CameraDeviceClient::enforceRequestPermissions(CameraMetadata& metadata) { status_t CameraDeviceClient::getRotationTransformLocked(int32_t* transform) { ALOGV("%s: begin", __FUNCTION__); - if (transform == NULL) { - ALOGW("%s: null transform", __FUNCTION__); - return BAD_VALUE; - } - - *transform = 0; - const CameraMetadata& staticInfo = mDevice->info(); - camera_metadata_ro_entry_t entry = staticInfo.find(ANDROID_SENSOR_ORIENTATION); - if (entry.count == 0) { - ALOGE("%s: Camera %d: Can't find android.sensor.orientation in " - "static metadata!", __FUNCTION__, mCameraId); - return INVALID_OPERATION; - } - - int32_t& flags = *transform; - - int orientation = entry.data.i32[0]; - switch (orientation) { - case 0: - flags = 0; - break; - case 90: - flags = NATIVE_WINDOW_TRANSFORM_ROT_90; - break; - case 180: - flags = NATIVE_WINDOW_TRANSFORM_ROT_180; - break; - case 270: - flags = NATIVE_WINDOW_TRANSFORM_ROT_270; - break; - default: - ALOGE("%s: Invalid HAL android.sensor.orientation value: %d", - __FUNCTION__, orientation); - return INVALID_OPERATION; - } - - /** - * This magic flag makes surfaceflinger un-rotate the buffers - * to counter the extra global device UI rotation whenever the user - * physically rotates the device. - * - * By doing this, the camera buffer always ends up aligned - * with the physical camera for a "see through" effect. - * - * In essence, the buffer only gets rotated during preview use-cases. - * The user is still responsible to re-create streams of the proper - * aspect ratio, or the preview will end up looking non-uniformly - * stretched. - */ - flags |= NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY; - - ALOGV("%s: final transform = 0x%x", __FUNCTION__, flags); - - return OK; + return CameraUtils::getRotationTransform(staticInfo, transform); } } // namespace android diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h index b9c16aa..9981dfe 100644 --- a/services/camera/libcameraservice/api2/CameraDeviceClient.h +++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h @@ -63,9 +63,22 @@ public: */ // Note that the callee gets a copy of the metadata. - virtual int submitRequest(sp<CaptureRequest> request, - bool streaming = false); - virtual status_t cancelRequest(int requestId); + virtual status_t submitRequest(sp<CaptureRequest> request, + bool streaming = false, + /*out*/ + int64_t* lastFrameNumber = NULL); + // List of requests are copied. + virtual status_t submitRequestList(List<sp<CaptureRequest> > requests, + bool streaming = false, + /*out*/ + int64_t* lastFrameNumber = NULL); + virtual status_t cancelRequest(int requestId, + /*out*/ + int64_t* lastFrameNumber = NULL); + + virtual status_t beginConfigure(); + + virtual status_t endConfigure(); // Returns -EBUSY if device is not idle virtual status_t deleteStream(int streamId); @@ -89,7 +102,8 @@ public: virtual status_t waitUntilIdle(); // Flush all active and pending requests as fast as possible - virtual status_t flush(); + virtual status_t flush(/*out*/ + int64_t* lastFrameNumber = NULL); /** * Interface used by CameraService @@ -114,16 +128,16 @@ public: */ virtual void notifyIdle(); - virtual void notifyError(); - virtual void notifyShutter(int requestId, nsecs_t timestamp); + virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, + const CaptureResultExtras& resultExtras); + virtual void notifyShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp); /** * Interface used by independent components of CameraDeviceClient. */ protected: /** FilteredListener implementation **/ - virtual void onFrameAvailable(int32_t requestId, - const CameraMetadata& frame); + virtual void onResultAvailable(const CaptureResult& result); virtual void detachDevice(); // Calculate the ANativeWindow transform from android.sensor.orientation diff --git a/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp b/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp index 1a7a7a7..f8823a3 100644 --- a/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp +++ b/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp @@ -280,7 +280,7 @@ status_t ProCamera2Client::createStream(int width, int height, int format, window = new Surface(bufferProducer); } - return mDevice->createStream(window, width, height, format, /*size*/1, + return mDevice->createStream(window, width, height, format, streamId); } @@ -373,9 +373,7 @@ void ProCamera2Client::detachDevice() { Camera2ClientBase::detachDevice(); } -/** Device-related methods */ -void ProCamera2Client::onFrameAvailable(int32_t requestId, - const CameraMetadata& frame) { +void ProCamera2Client::onResultAvailable(const CaptureResult& result) { ATRACE_CALL(); ALOGV("%s", __FUNCTION__); @@ -383,13 +381,12 @@ void ProCamera2Client::onFrameAvailable(int32_t requestId, SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); if (mRemoteCallback != NULL) { - CameraMetadata tmp(frame); + CameraMetadata tmp(result.mMetadata); camera_metadata_t* meta = tmp.release(); ALOGV("%s: meta = %p ", __FUNCTION__, meta); - mRemoteCallback->onResultReceived(requestId, meta); + mRemoteCallback->onResultReceived(result.mResultExtras.requestId, meta); tmp.acquire(meta); } - } bool ProCamera2Client::enforceRequestPermissions(CameraMetadata& metadata) { diff --git a/services/camera/libcameraservice/api_pro/ProCamera2Client.h b/services/camera/libcameraservice/api_pro/ProCamera2Client.h index 8a0f547..9d83122 100644 --- a/services/camera/libcameraservice/api_pro/ProCamera2Client.h +++ b/services/camera/libcameraservice/api_pro/ProCamera2Client.h @@ -21,6 +21,7 @@ #include "common/FrameProcessorBase.h" #include "common/Camera2ClientBase.h" #include "device2/Camera2Device.h" +#include "camera/CaptureResult.h" namespace android { @@ -97,8 +98,8 @@ public: protected: /** FilteredListener implementation **/ - virtual void onFrameAvailable(int32_t requestId, - const CameraMetadata& frame); + virtual void onResultAvailable(const CaptureResult& result); + virtual void detachDevice(); private: diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp index 6a88c87..24d173c 100644 --- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp +++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp @@ -54,7 +54,8 @@ Camera2ClientBase<TClientBase>::Camera2ClientBase( int servicePid): TClientBase(cameraService, remoteCallback, clientPackageName, cameraId, cameraFacing, clientPid, clientUid, servicePid), - mSharedCameraCallbacks(remoteCallback) + mSharedCameraCallbacks(remoteCallback), + mDeviceVersion(cameraService->getDeviceVersion(cameraId)) { ALOGI("Camera %d: Opened", cameraId); @@ -111,8 +112,6 @@ Camera2ClientBase<TClientBase>::~Camera2ClientBase() { TClientBase::mDestructionStarted = true; - TClientBase::finishCameraOps(); - disconnect(); ALOGI("Closed Camera %d", TClientBase::mCameraId); @@ -221,10 +220,11 @@ status_t Camera2ClientBase<TClientBase>::connect( /** Device-related methods */ template <typename TClientBase> -void Camera2ClientBase<TClientBase>::notifyError(int errorCode, int arg1, - int arg2) { - ALOGE("Error condition %d reported by HAL, arguments %d, %d", errorCode, - arg1, arg2); +void Camera2ClientBase<TClientBase>::notifyError( + ICameraDeviceCallbacks::CameraErrorCode errorCode, + const CaptureResultExtras& resultExtras) { + ALOGE("Error condition %d reported by HAL, requestId %" PRId32, errorCode, + resultExtras.requestId); } template <typename TClientBase> @@ -233,13 +233,13 @@ void Camera2ClientBase<TClientBase>::notifyIdle() { } template <typename TClientBase> -void Camera2ClientBase<TClientBase>::notifyShutter(int requestId, +void Camera2ClientBase<TClientBase>::notifyShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp) { - (void)requestId; + (void)resultExtras; (void)timestamp; - ALOGV("%s: Shutter notification for request id %d at time %" PRId64, - __FUNCTION__, requestId, timestamp); + ALOGV("%s: Shutter notification for request id %" PRId32 " at time %" PRId64, + __FUNCTION__, resultExtras.requestId, timestamp); } template <typename TClientBase> @@ -279,6 +279,11 @@ int Camera2ClientBase<TClientBase>::getCameraId() const { } template <typename TClientBase> +int Camera2ClientBase<TClientBase>::getCameraDeviceVersion() const { + return mDeviceVersion; +} + +template <typename TClientBase> const sp<CameraDeviceBase>& Camera2ClientBase<TClientBase>::getCameraDevice() { return mDevice; } diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h index 61e44f0..f57d204 100644 --- a/services/camera/libcameraservice/common/Camera2ClientBase.h +++ b/services/camera/libcameraservice/common/Camera2ClientBase.h @@ -18,6 +18,7 @@ #define ANDROID_SERVERS_CAMERA_CAMERA2CLIENT_BASE_H #include "common/CameraDeviceBase.h" +#include "camera/CaptureResult.h" namespace android { @@ -61,9 +62,11 @@ public: * CameraDeviceBase::NotificationListener implementation */ - virtual void notifyError(int errorCode, int arg1, int arg2); + virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, + const CaptureResultExtras& resultExtras); virtual void notifyIdle(); - virtual void notifyShutter(int requestId, nsecs_t timestamp); + virtual void notifyShutter(const CaptureResultExtras& resultExtras, + nsecs_t timestamp); virtual void notifyAutoFocus(uint8_t newState, int triggerId); virtual void notifyAutoExposure(uint8_t newState, int triggerId); virtual void notifyAutoWhitebalance(uint8_t newState, @@ -73,6 +76,7 @@ public: int getCameraId() const; const sp<CameraDeviceBase>& getCameraDevice(); + int getCameraDeviceVersion() const; const sp<CameraService>& getCameraService(); @@ -119,6 +123,7 @@ protected: /** CameraDeviceBase instance wrapping HAL2+ entry */ + const int mDeviceVersion; sp<CameraDeviceBase> mDevice; /** Utility members */ diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h index e80abf1..d26e20c 100644 --- a/services/camera/libcameraservice/common/CameraDeviceBase.h +++ b/services/camera/libcameraservice/common/CameraDeviceBase.h @@ -22,9 +22,13 @@ #include <utils/String16.h> #include <utils/Vector.h> #include <utils/Timers.h> +#include <utils/List.h> +#include <camera/camera2/ICameraDeviceCallbacks.h> #include "hardware/camera2.h" +#include "hardware/camera3.h" #include "camera/CameraMetadata.h" +#include "camera/CaptureResult.h" namespace android { @@ -44,7 +48,7 @@ class CameraDeviceBase : public virtual RefBase { virtual status_t initialize(camera_module_t *module) = 0; virtual status_t disconnect() = 0; - virtual status_t dump(int fd, const Vector<String16>& args) = 0; + virtual status_t dump(int fd, const Vector<String16> &args) = 0; /** * The device's static characteristics metadata buffer @@ -54,19 +58,37 @@ class CameraDeviceBase : public virtual RefBase { /** * Submit request for capture. The CameraDevice takes ownership of the * passed-in buffer. + * Output lastFrameNumber is the expected frame number of this request. */ - virtual status_t capture(CameraMetadata &request) = 0; + virtual status_t capture(CameraMetadata &request, int64_t *lastFrameNumber = NULL) = 0; + + /** + * Submit a list of requests. + * Output lastFrameNumber is the expected last frame number of the list of requests. + */ + virtual status_t captureList(const List<const CameraMetadata> &requests, + int64_t *lastFrameNumber = NULL) = 0; /** * Submit request for streaming. The CameraDevice makes a copy of the * passed-in buffer and the caller retains ownership. + * Output lastFrameNumber is the last frame number of the previous streaming request. + */ + virtual status_t setStreamingRequest(const CameraMetadata &request, + int64_t *lastFrameNumber = NULL) = 0; + + /** + * Submit a list of requests for streaming. + * Output lastFrameNumber is the last frame number of the previous streaming request. */ - virtual status_t setStreamingRequest(const CameraMetadata &request) = 0; + virtual status_t setStreamingRequestList(const List<const CameraMetadata> &requests, + int64_t *lastFrameNumber = NULL) = 0; /** * Clear the streaming request slot. + * Output lastFrameNumber is the last frame number of the previous streaming request. */ - virtual status_t clearStreamingRequest() = 0; + virtual status_t clearStreamingRequest(int64_t *lastFrameNumber = NULL) = 0; /** * Wait until a request with the given ID has been dequeued by the @@ -87,8 +109,7 @@ class CameraDeviceBase : public virtual RefBase { * other formats, the size parameter is ignored. */ virtual status_t createStream(sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, int format, size_t size, - int *id) = 0; + uint32_t width, uint32_t height, int format, int *id) = 0; /** * Create an input reprocess stream that uses buffers from an existing @@ -120,6 +141,18 @@ class CameraDeviceBase : public virtual RefBase { virtual status_t deleteReprocessStream(int id) = 0; /** + * Take the currently-defined set of streams and configure the HAL to use + * them. This is a long-running operation (may be several hundered ms). + * + * The device must be idle (see waitUntilDrained) before calling this. + * + * Returns OK on success; otherwise on error: + * - BAD_VALUE if the set of streams was invalid (e.g. fmts or sizes) + * - INVALID_OPERATION if the device was in the wrong state + */ + virtual status_t configureStreams() = 0; + + /** * Create a metadata buffer with fields that the HAL device believes are * best for the given use case */ @@ -134,6 +167,12 @@ class CameraDeviceBase : public virtual RefBase { virtual status_t waitUntilDrained() = 0; /** + * Get Jpeg buffer size for a given jpeg resolution. + * Negative values are error codes. + */ + virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const = 0; + + /** * Abstract class for HAL notification listeners */ class NotificationListener { @@ -142,11 +181,12 @@ class CameraDeviceBase : public virtual RefBase { // API1 and API2. // Required for API 1 and 2 - virtual void notifyError(int errorCode, int arg1, int arg2) = 0; + virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode, + const CaptureResultExtras &resultExtras) = 0; // Required only for API2 virtual void notifyIdle() = 0; - virtual void notifyShutter(int requestId, + virtual void notifyShutter(const CaptureResultExtras &resultExtras, nsecs_t timestamp) = 0; // Required only for API1 @@ -179,11 +219,12 @@ class CameraDeviceBase : public virtual RefBase { virtual status_t waitForNextFrame(nsecs_t timeout) = 0; /** - * Get next metadata frame from the frame queue. Returns NULL if the queue - * is empty; caller takes ownership of the metadata buffer. - * May be called concurrently to most methods, except for waitForNextFrame + * Get next capture result frame from the result queue. Returns NOT_ENOUGH_DATA + * if the queue is empty; caller takes ownership of the metadata buffer inside + * the capture result object's metadata field. + * May be called concurrently to most methods, except for waitForNextFrame. */ - virtual status_t getNextFrame(CameraMetadata *frame) = 0; + virtual status_t getNextResult(CaptureResult *frame) = 0; /** * Trigger auto-focus. The latest ID used in a trigger autofocus or cancel @@ -224,9 +265,14 @@ class CameraDeviceBase : public virtual RefBase { /** * Flush all pending and in-flight requests. Blocks until flush is * complete. + * Output lastFrameNumber is the last frame number of the previous streaming request. */ - virtual status_t flush() = 0; + virtual status_t flush(int64_t *lastFrameNumber = NULL) = 0; + /** + * Get the HAL device version. + */ + virtual uint32_t getDeviceVersion() = 0; }; }; // namespace android diff --git a/services/camera/libcameraservice/common/FrameProcessorBase.cpp b/services/camera/libcameraservice/common/FrameProcessorBase.cpp index 4d31667..29eb78f 100644 --- a/services/camera/libcameraservice/common/FrameProcessorBase.cpp +++ b/services/camera/libcameraservice/common/FrameProcessorBase.cpp @@ -29,7 +29,17 @@ namespace camera2 { FrameProcessorBase::FrameProcessorBase(wp<CameraDeviceBase> device) : Thread(/*canCallJava*/false), - mDevice(device) { + mDevice(device), + mNumPartialResults(1) { + sp<CameraDeviceBase> cameraDevice = device.promote(); + if (cameraDevice != 0 && + cameraDevice->getDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_2) { + CameraMetadata staticInfo = cameraDevice->info(); + camera_metadata_entry_t entry = staticInfo.find(ANDROID_REQUEST_PARTIAL_RESULT_COUNT); + if (entry.count > 0) { + mNumPartialResults = entry.data.i32[0]; + } + } } FrameProcessorBase::~FrameProcessorBase() { @@ -37,11 +47,23 @@ FrameProcessorBase::~FrameProcessorBase() { } status_t FrameProcessorBase::registerListener(int32_t minId, - int32_t maxId, wp<FilteredListener> listener, bool quirkSendPartials) { + int32_t maxId, wp<FilteredListener> listener, bool sendPartials) { Mutex::Autolock l(mInputMutex); + List<RangeListener>::iterator item = mRangeListeners.begin(); + while (item != mRangeListeners.end()) { + if (item->minId == minId && + item->maxId == maxId && + item->listener == listener) { + // already registered, just return + ALOGV("%s: Attempt to register the same client twice, ignoring", + __FUNCTION__); + return OK; + } + item++; + } ALOGV("%s: Registering listener for frame id range %d - %d", __FUNCTION__, minId, maxId); - RangeListener rListener = { minId, maxId, listener, quirkSendPartials }; + RangeListener rListener = { minId, maxId, listener, sendPartials }; mRangeListeners.push_back(rListener); return OK; } @@ -99,15 +121,17 @@ bool FrameProcessorBase::threadLoop() { void FrameProcessorBase::processNewFrames(const sp<CameraDeviceBase> &device) { status_t res; ATRACE_CALL(); - CameraMetadata frame; + CaptureResult result; ALOGV("%s: Camera %d: Process new frames", __FUNCTION__, device->getId()); - while ( (res = device->getNextFrame(&frame)) == OK) { + while ( (res = device->getNextResult(&result)) == OK) { + // TODO: instead of getting frame number from metadata, we should read + // this from result.mResultExtras when CameraDeviceBase interface is fixed. camera_metadata_entry_t entry; - entry = frame.find(ANDROID_REQUEST_FRAME_COUNT); + entry = result.mMetadata.find(ANDROID_REQUEST_FRAME_COUNT); if (entry.count == 0) { ALOGE("%s: Camera %d: Error reading frame number", __FUNCTION__, device->getId()); @@ -115,13 +139,13 @@ void FrameProcessorBase::processNewFrames(const sp<CameraDeviceBase> &device) { } ATRACE_INT("cam2_frame", entry.data.i32[0]); - if (!processSingleFrame(frame, device)) { + if (!processSingleFrame(result, device)) { break; } - if (!frame.isEmpty()) { + if (!result.mMetadata.isEmpty()) { Mutex::Autolock al(mLastFrameMutex); - mLastFrame.acquire(frame); + mLastFrame.acquire(result.mMetadata); } } if (res != NOT_ENOUGH_DATA) { @@ -133,32 +157,40 @@ void FrameProcessorBase::processNewFrames(const sp<CameraDeviceBase> &device) { return; } -bool FrameProcessorBase::processSingleFrame(CameraMetadata &frame, - const sp<CameraDeviceBase> &device) { +bool FrameProcessorBase::processSingleFrame(CaptureResult &result, + const sp<CameraDeviceBase> &device) { ALOGV("%s: Camera %d: Process single frame (is empty? %d)", - __FUNCTION__, device->getId(), frame.isEmpty()); - return processListeners(frame, device) == OK; + __FUNCTION__, device->getId(), result.mMetadata.isEmpty()); + return processListeners(result, device) == OK; } -status_t FrameProcessorBase::processListeners(const CameraMetadata &frame, +status_t FrameProcessorBase::processListeners(const CaptureResult &result, const sp<CameraDeviceBase> &device) { ATRACE_CALL(); + camera_metadata_ro_entry_t entry; - // Quirks: Don't deliver partial results to listeners that don't want them - bool quirkIsPartial = false; - entry = frame.find(ANDROID_QUIRKS_PARTIAL_RESULT); - if (entry.count != 0 && - entry.data.u8[0] == ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL) { - ALOGV("%s: Camera %d: Not forwarding partial result to listeners", - __FUNCTION__, device->getId()); - quirkIsPartial = true; + // Check if this result is partial. + bool isPartialResult = false; + if (device->getDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_2) { + isPartialResult = result.mResultExtras.partialResultCount < mNumPartialResults; + } else { + entry = result.mMetadata.find(ANDROID_QUIRKS_PARTIAL_RESULT); + if (entry.count != 0 && + entry.data.u8[0] == ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL) { + ALOGV("%s: Camera %d: This is a partial result", + __FUNCTION__, device->getId()); + isPartialResult = true; + } } - entry = frame.find(ANDROID_REQUEST_ID); + // TODO: instead of getting requestID from CameraMetadata, we should get it + // from CaptureResultExtras. This will require changing Camera2Device. + // Currently Camera2Device uses MetadataQueue to store results, which does not + // include CaptureResultExtras. + entry = result.mMetadata.find(ANDROID_REQUEST_ID); if (entry.count == 0) { - ALOGE("%s: Camera %d: Error reading frame id", - __FUNCTION__, device->getId()); + ALOGE("%s: Camera %d: Error reading frame id", __FUNCTION__, device->getId()); return BAD_VALUE; } int32_t requestId = entry.data.i32[0]; @@ -168,10 +200,10 @@ status_t FrameProcessorBase::processListeners(const CameraMetadata &frame, Mutex::Autolock l(mInputMutex); List<RangeListener>::iterator item = mRangeListeners.begin(); + // Don't deliver partial results to listeners that don't want them while (item != mRangeListeners.end()) { - if (requestId >= item->minId && - requestId < item->maxId && - (!quirkIsPartial || item->quirkSendPartials) ) { + if (requestId >= item->minId && requestId < item->maxId && + (!isPartialResult || item->sendPartials)) { sp<FilteredListener> listener = item->listener.promote(); if (listener == 0) { item = mRangeListeners.erase(item); @@ -183,10 +215,12 @@ status_t FrameProcessorBase::processListeners(const CameraMetadata &frame, item++; } } - ALOGV("Got %zu range listeners out of %zu", listeners.size(), mRangeListeners.size()); + ALOGV("%s: Camera %d: Got %zu range listeners out of %zu", __FUNCTION__, + device->getId(), listeners.size(), mRangeListeners.size()); + List<sp<FilteredListener> >::iterator item = listeners.begin(); for (; item != listeners.end(); item++) { - (*item)->onFrameAvailable(requestId, frame); + (*item)->onResultAvailable(result); } return OK; } diff --git a/services/camera/libcameraservice/common/FrameProcessorBase.h b/services/camera/libcameraservice/common/FrameProcessorBase.h index 89b608a..a618d84 100644 --- a/services/camera/libcameraservice/common/FrameProcessorBase.h +++ b/services/camera/libcameraservice/common/FrameProcessorBase.h @@ -23,6 +23,7 @@ #include <utils/KeyedVector.h> #include <utils/List.h> #include <camera/CameraMetadata.h> +#include <camera/CaptureResult.h> namespace android { @@ -39,16 +40,16 @@ class FrameProcessorBase: public Thread { virtual ~FrameProcessorBase(); struct FilteredListener: virtual public RefBase { - virtual void onFrameAvailable(int32_t requestId, - const CameraMetadata &frame) = 0; + virtual void onResultAvailable(const CaptureResult &result) = 0; }; // Register a listener for a range of IDs [minId, maxId). Multiple listeners - // can be listening to the same range. - // QUIRK: sendPartials controls whether partial results will be sent. + // can be listening to the same range. Registering the same listener with + // the same range of IDs has no effect. + // sendPartials controls whether partial results will be sent. status_t registerListener(int32_t minId, int32_t maxId, wp<FilteredListener> listener, - bool quirkSendPartials = true); + bool sendPartials = true); status_t removeListener(int32_t minId, int32_t maxId, wp<FilteredListener> listener); @@ -66,16 +67,19 @@ class FrameProcessorBase: public Thread { int32_t minId; int32_t maxId; wp<FilteredListener> listener; - bool quirkSendPartials; + bool sendPartials; }; List<RangeListener> mRangeListeners; + // Number of partial result the HAL will potentially send. + int32_t mNumPartialResults; + void processNewFrames(const sp<CameraDeviceBase> &device); - virtual bool processSingleFrame(CameraMetadata &frame, + virtual bool processSingleFrame(CaptureResult &result, const sp<CameraDeviceBase> &device); - status_t processListeners(const CameraMetadata &frame, + status_t processListeners(const CaptureResult &result, const sp<CameraDeviceBase> &device); CameraMetadata mLastFrame; diff --git a/services/camera/libcameraservice/device1/CameraHardwareInterface.h b/services/camera/libcameraservice/device1/CameraHardwareInterface.h index 87b2807..6386838 100644 --- a/services/camera/libcameraservice/device1/CameraHardwareInterface.h +++ b/services/camera/libcameraservice/device1/CameraHardwareInterface.h @@ -92,8 +92,22 @@ public: status_t initialize(hw_module_t *module) { ALOGI("Opening camera %s", mName.string()); - int rc = module->methods->open(module, mName.string(), - (hw_device_t **)&mDevice); + camera_module_t *cameraModule = reinterpret_cast<camera_module_t *>(module); + camera_info info; + status_t res = cameraModule->get_camera_info(atoi(mName.string()), &info); + if (res != OK) return res; + + int rc = OK; + if (module->module_api_version >= CAMERA_MODULE_API_VERSION_2_3 && + info.device_version > CAMERA_DEVICE_API_VERSION_1_0) { + // Open higher version camera device as HAL1.0 device. + rc = cameraModule->open_legacy(module, mName.string(), + CAMERA_DEVICE_API_VERSION_1_0, + (hw_device_t **)&mDevice); + } else { + rc = CameraService::filterOpenErrorCode(module->methods->open( + module, mName.string(), (hw_device_t **)&mDevice)); + } if (rc != OK) { ALOGE("Could not open camera %s: %d", mName.string(), rc); return rc; @@ -611,9 +625,14 @@ private: static int __set_buffers_geometry(struct preview_stream_ops* w, int width, int height, int format) { + int rc; ANativeWindow *a = anw(w); - return native_window_set_buffers_geometry(a, - width, height, format); + + rc = native_window_set_buffers_dimensions(a, width, height); + if (!rc) { + rc = native_window_set_buffers_format(a, format); + } + return rc; } static int __set_crop(struct preview_stream_ops *w, diff --git a/services/camera/libcameraservice/device2/Camera2Device.cpp b/services/camera/libcameraservice/device2/Camera2Device.cpp index 2966d82..8caadd6 100644 --- a/services/camera/libcameraservice/device2/Camera2Device.cpp +++ b/services/camera/libcameraservice/device2/Camera2Device.cpp @@ -30,6 +30,7 @@ #include <utils/Trace.h> #include <utils/Timers.h> #include "Camera2Device.h" +#include "CameraService.h" namespace android { @@ -67,8 +68,8 @@ status_t Camera2Device::initialize(camera_module_t *module) camera2_device_t *device; - res = module->common.methods->open(&module->common, name, - reinterpret_cast<hw_device_t**>(&device)); + res = CameraService::filterOpenErrorCode(module->common.methods->open( + &module->common, name, reinterpret_cast<hw_device_t**>(&device))); if (res != OK) { ALOGE("%s: Could not open camera %d: %s (%d)", __FUNCTION__, @@ -112,20 +113,6 @@ status_t Camera2Device::initialize(camera_module_t *module) return res; } - res = device->ops->get_metadata_vendor_tag_ops(device, &mVendorTagOps); - if (res != OK ) { - ALOGE("%s: Camera %d: Unable to retrieve tag ops from device: %s (%d)", - __FUNCTION__, mId, strerror(-res), res); - device->common.close(&device->common); - return res; - } - res = set_camera_metadata_vendor_tag_ops(mVendorTagOps); - if (res != OK) { - ALOGE("%s: Camera %d: Unable to set tag ops: %s (%d)", - __FUNCTION__, mId, strerror(-res), res); - device->common.close(&device->common); - return res; - } res = device->ops->set_notify_callback(device, notificationCallback, NULL); if (res != OK) { @@ -137,6 +124,7 @@ status_t Camera2Device::initialize(camera_module_t *module) mDeviceInfo = info.static_camera_characteristics; mHal2Device = device; + mDeviceVersion = device->common.version; return OK; } @@ -213,7 +201,7 @@ const CameraMetadata& Camera2Device::info() const { return mDeviceInfo; } -status_t Camera2Device::capture(CameraMetadata &request) { +status_t Camera2Device::capture(CameraMetadata &request, int64_t* /*lastFrameNumber*/) { ATRACE_CALL(); ALOGV("%s: E", __FUNCTION__); @@ -221,15 +209,29 @@ status_t Camera2Device::capture(CameraMetadata &request) { return OK; } +status_t Camera2Device::captureList(const List<const CameraMetadata> &requests, + int64_t* /*lastFrameNumber*/) { + ATRACE_CALL(); + ALOGE("%s: Camera2Device burst capture not implemented", __FUNCTION__); + return INVALID_OPERATION; +} -status_t Camera2Device::setStreamingRequest(const CameraMetadata &request) { +status_t Camera2Device::setStreamingRequest(const CameraMetadata &request, + int64_t* /*lastFrameNumber*/) { ATRACE_CALL(); ALOGV("%s: E", __FUNCTION__); CameraMetadata streamRequest(request); return mRequestQueue.setStreamSlot(streamRequest.release()); } -status_t Camera2Device::clearStreamingRequest() { +status_t Camera2Device::setStreamingRequestList(const List<const CameraMetadata> &requests, + int64_t* /*lastFrameNumber*/) { + ATRACE_CALL(); + ALOGE("%s, Camera2Device streaming burst not implemented", __FUNCTION__); + return INVALID_OPERATION; +} + +status_t Camera2Device::clearStreamingRequest(int64_t* /*lastFrameNumber*/) { ATRACE_CALL(); return mRequestQueue.setStreamSlot(NULL); } @@ -240,13 +242,16 @@ status_t Camera2Device::waitUntilRequestReceived(int32_t requestId, nsecs_t time } status_t Camera2Device::createStream(sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, int format, size_t size, int *id) { + uint32_t width, uint32_t height, int format, int *id) { ATRACE_CALL(); status_t res; ALOGV("%s: E", __FUNCTION__); sp<StreamAdapter> stream = new StreamAdapter(mHal2Device); - + size_t size = 0; + if (format == HAL_PIXEL_FORMAT_BLOB) { + size = getJpegBufferSize(width, height); + } res = stream->connectToDevice(consumer, width, height, format, size); if (res != OK) { ALOGE("%s: Camera %d: Unable to create stream (%d x %d, format %x):" @@ -261,6 +266,17 @@ status_t Camera2Device::createStream(sp<ANativeWindow> consumer, return OK; } +ssize_t Camera2Device::getJpegBufferSize(uint32_t width, uint32_t height) const { + // Always give the max jpeg buffer size regardless of the actual jpeg resolution. + camera_metadata_ro_entry jpegBufMaxSize = mDeviceInfo.find(ANDROID_JPEG_MAX_SIZE); + if (jpegBufMaxSize.count == 0) { + ALOGE("%s: Camera %d: Can't find maximum JPEG size in static metadata!", __FUNCTION__, mId); + return BAD_VALUE; + } + + return jpegBufMaxSize.data.i32[0]; +} + status_t Camera2Device::createReprocessStreamFromStream(int outputId, int *id) { ATRACE_CALL(); status_t res; @@ -399,6 +415,19 @@ status_t Camera2Device::deleteReprocessStream(int id) { return OK; } +status_t Camera2Device::configureStreams() { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + + /** + * HAL2 devices do not need to configure streams; + * streams are created on the fly. + */ + ALOGW("%s: No-op for HAL2 devices", __FUNCTION__); + + return OK; +} + status_t Camera2Device::createDefaultRequest(int templateId, CameraMetadata *request) { @@ -462,7 +491,13 @@ void Camera2Device::notificationCallback(int32_t msg_type, if (listener != NULL) { switch (msg_type) { case CAMERA2_MSG_ERROR: - listener->notifyError(ext1, ext2, ext3); + // TODO: This needs to be fixed. ext2 and ext3 need to be considered. + listener->notifyError( + ((ext1 == CAMERA2_MSG_ERROR_DEVICE) + || (ext1 == CAMERA2_MSG_ERROR_HARDWARE)) ? + ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE : + ICameraDeviceCallbacks::ERROR_CAMERA_SERVICE, + CaptureResultExtras()); break; case CAMERA2_MSG_SHUTTER: { // TODO: Only needed for camera2 API, which is unsupported @@ -491,16 +526,22 @@ status_t Camera2Device::waitForNextFrame(nsecs_t timeout) { return mFrameQueue.waitForBuffer(timeout); } -status_t Camera2Device::getNextFrame(CameraMetadata *frame) { +status_t Camera2Device::getNextResult(CaptureResult *result) { ATRACE_CALL(); + ALOGV("%s: get CaptureResult", __FUNCTION__); + if (result == NULL) { + ALOGE("%s: result pointer is NULL", __FUNCTION__); + return BAD_VALUE; + } status_t res; camera_metadata_t *rawFrame; res = mFrameQueue.dequeue(&rawFrame); - if (rawFrame == NULL) { + if (rawFrame == NULL) { return NOT_ENOUGH_DATA; } else if (res == OK) { - frame->acquire(rawFrame); + result->mMetadata.acquire(rawFrame); } + return res; } @@ -570,13 +611,18 @@ status_t Camera2Device::pushReprocessBuffer(int reprocessStreamId, return res; } -status_t Camera2Device::flush() { +status_t Camera2Device::flush(int64_t* /*lastFrameNumber*/) { ATRACE_CALL(); mRequestQueue.clear(); return waitUntilDrained(); } +uint32_t Camera2Device::getDeviceVersion() { + ATRACE_CALL(); + return mDeviceVersion; +} + /** * Camera2Device::MetadataQueue */ @@ -1069,25 +1115,33 @@ status_t Camera2Device::StreamAdapter::connectToDevice( } if (mFormat == HAL_PIXEL_FORMAT_BLOB) { - res = native_window_set_buffers_geometry(mConsumerInterface.get(), - mSize, 1, mFormat); + res = native_window_set_buffers_dimensions(mConsumerInterface.get(), + mSize, 1); if (res != OK) { - ALOGE("%s: Unable to configure compressed stream buffer geometry" + ALOGE("%s: Unable to configure compressed stream buffer dimensions" " %d x %d, size %zu for stream %d", __FUNCTION__, mWidth, mHeight, mSize, mId); return res; } } else { - res = native_window_set_buffers_geometry(mConsumerInterface.get(), - mWidth, mHeight, mFormat); + res = native_window_set_buffers_dimensions(mConsumerInterface.get(), + mWidth, mHeight); if (res != OK) { - ALOGE("%s: Unable to configure stream buffer geometry" - " %d x %d, format 0x%x for stream %d", - __FUNCTION__, mWidth, mHeight, mFormat, mId); + ALOGE("%s: Unable to configure stream buffer dimensions" + " %d x %d for stream %d", + __FUNCTION__, mWidth, mHeight, mId); return res; } } + res = native_window_set_buffers_format(mConsumerInterface.get(), mFormat); + if (res != OK) { + ALOGE("%s: Unable to configure stream buffer format" + " %#x for stream %d", + __FUNCTION__, mFormat, mId); + return res; + } + int maxConsumerBuffers; res = mConsumerInterface->query(mConsumerInterface.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &maxConsumerBuffers); diff --git a/services/camera/libcameraservice/device2/Camera2Device.h b/services/camera/libcameraservice/device2/Camera2Device.h index 1f53c56..2a3f1d9 100644 --- a/services/camera/libcameraservice/device2/Camera2Device.h +++ b/services/camera/libcameraservice/device2/Camera2Device.h @@ -47,38 +47,48 @@ class Camera2Device: public CameraDeviceBase { virtual status_t disconnect(); virtual status_t dump(int fd, const Vector<String16>& args); virtual const CameraMetadata& info() const; - virtual status_t capture(CameraMetadata &request); - virtual status_t setStreamingRequest(const CameraMetadata &request); - virtual status_t clearStreamingRequest(); + virtual status_t capture(CameraMetadata &request, int64_t *lastFrameNumber = NULL); + virtual status_t captureList(const List<const CameraMetadata> &requests, + int64_t *lastFrameNumber = NULL); + virtual status_t setStreamingRequest(const CameraMetadata &request, + int64_t *lastFrameNumber = NULL); + virtual status_t setStreamingRequestList(const List<const CameraMetadata> &requests, + int64_t *lastFrameNumber = NULL); + virtual status_t clearStreamingRequest(int64_t *lastFrameNumber = NULL); virtual status_t waitUntilRequestReceived(int32_t requestId, nsecs_t timeout); virtual status_t createStream(sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, int format, size_t size, - int *id); + uint32_t width, uint32_t height, int format, int *id); virtual status_t createReprocessStreamFromStream(int outputId, int *id); virtual status_t getStreamInfo(int id, uint32_t *width, uint32_t *height, uint32_t *format); virtual status_t setStreamTransform(int id, int transform); virtual status_t deleteStream(int id); virtual status_t deleteReprocessStream(int id); + // No-op on HAL2 devices + virtual status_t configureStreams(); virtual status_t createDefaultRequest(int templateId, CameraMetadata *request); virtual status_t waitUntilDrained(); virtual status_t setNotifyCallback(NotificationListener *listener); virtual bool willNotify3A(); virtual status_t waitForNextFrame(nsecs_t timeout); - virtual status_t getNextFrame(CameraMetadata *frame); + virtual status_t getNextResult(CaptureResult *frame); virtual status_t triggerAutofocus(uint32_t id); virtual status_t triggerCancelAutofocus(uint32_t id); virtual status_t triggerPrecaptureMetering(uint32_t id); virtual status_t pushReprocessBuffer(int reprocessStreamId, buffer_handle_t *buffer, wp<BufferReleasedListener> listener); // Flush implemented as just a wait - virtual status_t flush(); + virtual status_t flush(int64_t *lastFrameNumber = NULL); + virtual uint32_t getDeviceVersion(); + virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const; + private: const int mId; camera2_device_t *mHal2Device; CameraMetadata mDeviceInfo; - vendor_tag_query_ops_t *mVendorTagOps; + + uint32_t mDeviceVersion; /** * Queue class for both sending requests to a camera2 device, and for diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp index 1d4768c..fafe349 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.cpp +++ b/services/camera/libcameraservice/device3/Camera3Device.cpp @@ -48,6 +48,8 @@ #include "device3/Camera3OutputStream.h" #include "device3/Camera3InputStream.h" #include "device3/Camera3ZslStream.h" +#include "device3/Camera3DummyStream.h" +#include "CameraService.h" using namespace android::camera3; @@ -57,7 +59,8 @@ Camera3Device::Camera3Device(int id): mId(id), mHal3Device(NULL), mStatus(STATUS_UNINITIALIZED), - mUsePartialResultQuirk(false), + mUsePartialResult(false), + mNumPartialResults(1), mNextResultFrameNumber(0), mNextShutterFrameNumber(0), mListener(NULL) @@ -102,8 +105,11 @@ status_t Camera3Device::initialize(camera_module_t *module) camera3_device_t *device; - res = module->common.methods->open(&module->common, deviceName.string(), - reinterpret_cast<hw_device_t**>(&device)); + ATRACE_BEGIN("camera3->open"); + res = CameraService::filterOpenErrorCode(module->common.methods->open( + &module->common, deviceName.string(), + reinterpret_cast<hw_device_t**>(&device))); + ATRACE_END(); if (res != OK) { SET_ERR_L("Could not open camera: %s (%d)", strerror(-res), res); @@ -111,10 +117,9 @@ status_t Camera3Device::initialize(camera_module_t *module) } /** Cross-check device version */ - - if (device->common.version != CAMERA_DEVICE_API_VERSION_3_0) { + if (device->common.version < CAMERA_DEVICE_API_VERSION_3_0) { SET_ERR_L("Could not open camera: " - "Camera device is not version %x, reports %x instead", + "Camera device should be at least %x, reports %x instead", CAMERA_DEVICE_API_VERSION_3_0, device->common.version); device->common.close(&device->common); @@ -122,13 +127,14 @@ status_t Camera3Device::initialize(camera_module_t *module) } camera_info info; - res = module->get_camera_info(mId, &info); + res = CameraService::filterGetInfoErrorCode(module->get_camera_info( + mId, &info)); if (res != OK) return res; if (info.device_version != device->common.version) { SET_ERR_L("HAL reporting mismatched camera_info version (%x)" " and device version (%x).", - device->common.version, info.device_version); + info.device_version, device->common.version); device->common.close(&device->common); return BAD_VALUE; } @@ -146,24 +152,6 @@ status_t Camera3Device::initialize(camera_module_t *module) return BAD_VALUE; } - /** Get vendor metadata tags */ - - mVendorTagOps.get_camera_vendor_section_name = NULL; - - ATRACE_BEGIN("camera3->get_metadata_vendor_tag_ops"); - device->ops->get_metadata_vendor_tag_ops(device, &mVendorTagOps); - ATRACE_END(); - - if (mVendorTagOps.get_camera_vendor_section_name != NULL) { - res = set_camera_metadata_vendor_tag_ops(&mVendorTagOps); - if (res != OK) { - SET_ERR_L("Unable to set tag ops: %s (%d)", - strerror(-res), res); - device->common.close(&device->common); - return res; - } - } - /** Start up status tracker thread */ mStatusTracker = new StatusTracker(this); res = mStatusTracker->run(String8::format("C3Dev-%d-Status", mId).string()); @@ -189,20 +177,29 @@ status_t Camera3Device::initialize(camera_module_t *module) /** Everything is good to go */ + mDeviceVersion = device->common.version; mDeviceInfo = info.static_camera_characteristics; mHal3Device = device; mStatus = STATUS_UNCONFIGURED; mNextStreamId = 0; + mDummyStreamId = NO_STREAM; mNeedConfig = true; mPauseStateNotify = false; - /** Check for quirks */ - // Will the HAL be sending in early partial result metadata? - camera_metadata_entry partialResultsQuirk = - mDeviceInfo.find(ANDROID_QUIRKS_USE_PARTIAL_RESULT); - if (partialResultsQuirk.count > 0 && partialResultsQuirk.data.u8[0] == 1) { - mUsePartialResultQuirk = true; + if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) { + camera_metadata_entry partialResultsCount = + mDeviceInfo.find(ANDROID_REQUEST_PARTIAL_RESULT_COUNT); + if (partialResultsCount.count > 0) { + mNumPartialResults = partialResultsCount.data.i32[0]; + mUsePartialResult = (mNumPartialResults > 1); + } + } else { + camera_metadata_entry partialResultsQuirk = + mDeviceInfo.find(ANDROID_QUIRKS_USE_PARTIAL_RESULT); + if (partialResultsQuirk.count > 0 && partialResultsQuirk.data.u8[0] == 1) { + mUsePartialResult = true; + } } return OK; @@ -271,7 +268,9 @@ status_t Camera3Device::disconnect() { mStatusTracker.clear(); if (mHal3Device != NULL) { + ATRACE_BEGIN("camera3->close"); mHal3Device->common.close(&mHal3Device->common); + ATRACE_END(); mHal3Device = NULL; } @@ -298,6 +297,85 @@ bool Camera3Device::tryLockSpinRightRound(Mutex& lock) { return gotLock; } +Camera3Device::Size Camera3Device::getMaxJpegResolution() const { + int32_t maxJpegWidth = 0, maxJpegHeight = 0; + if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) { + const int STREAM_CONFIGURATION_SIZE = 4; + const int STREAM_FORMAT_OFFSET = 0; + const int STREAM_WIDTH_OFFSET = 1; + const int STREAM_HEIGHT_OFFSET = 2; + const int STREAM_IS_INPUT_OFFSET = 3; + camera_metadata_ro_entry_t availableStreamConfigs = + mDeviceInfo.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS); + if (availableStreamConfigs.count == 0 || + availableStreamConfigs.count % STREAM_CONFIGURATION_SIZE != 0) { + return Size(0, 0); + } + + // Get max jpeg size (area-wise). + for (size_t i=0; i < availableStreamConfigs.count; i+= STREAM_CONFIGURATION_SIZE) { + int32_t format = availableStreamConfigs.data.i32[i + STREAM_FORMAT_OFFSET]; + int32_t width = availableStreamConfigs.data.i32[i + STREAM_WIDTH_OFFSET]; + int32_t height = availableStreamConfigs.data.i32[i + STREAM_HEIGHT_OFFSET]; + int32_t isInput = availableStreamConfigs.data.i32[i + STREAM_IS_INPUT_OFFSET]; + if (isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT + && format == HAL_PIXEL_FORMAT_BLOB && + (width * height > maxJpegWidth * maxJpegHeight)) { + maxJpegWidth = width; + maxJpegHeight = height; + } + } + } else { + camera_metadata_ro_entry availableJpegSizes = + mDeviceInfo.find(ANDROID_SCALER_AVAILABLE_JPEG_SIZES); + if (availableJpegSizes.count == 0 || availableJpegSizes.count % 2 != 0) { + return Size(0, 0); + } + + // Get max jpeg size (area-wise). + for (size_t i = 0; i < availableJpegSizes.count; i += 2) { + if ((availableJpegSizes.data.i32[i] * availableJpegSizes.data.i32[i + 1]) + > (maxJpegWidth * maxJpegHeight)) { + maxJpegWidth = availableJpegSizes.data.i32[i]; + maxJpegHeight = availableJpegSizes.data.i32[i + 1]; + } + } + } + return Size(maxJpegWidth, maxJpegHeight); +} + +ssize_t Camera3Device::getJpegBufferSize(uint32_t width, uint32_t height) const { + // Get max jpeg size (area-wise). + Size maxJpegResolution = getMaxJpegResolution(); + if (maxJpegResolution.width == 0) { + ALOGE("%s: Camera %d: Can't find find valid available jpeg sizes in static metadata!", + __FUNCTION__, mId); + return BAD_VALUE; + } + + // Get max jpeg buffer size + ssize_t maxJpegBufferSize = 0; + camera_metadata_ro_entry jpegBufMaxSize = mDeviceInfo.find(ANDROID_JPEG_MAX_SIZE); + if (jpegBufMaxSize.count == 0) { + ALOGE("%s: Camera %d: Can't find maximum JPEG size in static metadata!", __FUNCTION__, mId); + return BAD_VALUE; + } + maxJpegBufferSize = jpegBufMaxSize.data.i32[0]; + + // Calculate final jpeg buffer size for the given resolution. + float scaleFactor = ((float) (width * height)) / + (maxJpegResolution.width * maxJpegResolution.height); + ssize_t jpegBufferSize = scaleFactor * maxJpegBufferSize; + // Bound the buffer size to [MIN_JPEG_BUFFER_SIZE, maxJpegBufferSize]. + if (jpegBufferSize > maxJpegBufferSize) { + jpegBufferSize = maxJpegBufferSize; + } else if (jpegBufferSize < kMinJpegBufferSize) { + jpegBufferSize = kMinJpegBufferSize; + } + + return jpegBufferSize; +} + status_t Camera3Device::dump(int fd, const Vector<String16> &args) { ATRACE_CALL(); (void)args; @@ -386,14 +464,7 @@ const CameraMetadata& Camera3Device::info() const { return mDeviceInfo; } -status_t Camera3Device::capture(CameraMetadata &request) { - ATRACE_CALL(); - status_t res; - Mutex::Autolock il(mInterfaceLock); - Mutex::Autolock l(mLock); - - // TODO: take ownership of the request - +status_t Camera3Device::checkStatusOkToCaptureLocked() { switch (mStatus) { case STATUS_ERROR: CLOGE("Device has encountered a serious error"); @@ -402,7 +473,6 @@ status_t Camera3Device::capture(CameraMetadata &request) { CLOGE("Device not initialized"); return INVALID_OPERATION; case STATUS_UNCONFIGURED: - // May be lazily configuring streams, will check during setup case STATUS_CONFIGURED: case STATUS_ACTIVE: // OK @@ -411,71 +481,119 @@ status_t Camera3Device::capture(CameraMetadata &request) { SET_ERR_L("Unexpected status: %d", mStatus); return INVALID_OPERATION; } + return OK; +} - sp<CaptureRequest> newRequest = setUpRequestLocked(request); - if (newRequest == NULL) { - CLOGE("Can't create capture request"); +status_t Camera3Device::convertMetadataListToRequestListLocked( + const List<const CameraMetadata> &metadataList, RequestList *requestList) { + if (requestList == NULL) { + CLOGE("requestList cannot be NULL."); return BAD_VALUE; } - res = mRequestThread->queueRequest(newRequest); - if (res == OK) { - waitUntilStateThenRelock(/*active*/ true, kActiveTimeout); - if (res != OK) { - SET_ERR_L("Can't transition to active in %f seconds!", - kActiveTimeout/1e9); + int32_t burstId = 0; + for (List<const CameraMetadata>::const_iterator it = metadataList.begin(); + it != metadataList.end(); ++it) { + sp<CaptureRequest> newRequest = setUpRequestLocked(*it); + if (newRequest == 0) { + CLOGE("Can't create capture request"); + return BAD_VALUE; + } + + // Setup burst Id and request Id + newRequest->mResultExtras.burstId = burstId++; + if (it->exists(ANDROID_REQUEST_ID)) { + if (it->find(ANDROID_REQUEST_ID).count == 0) { + CLOGE("RequestID entry exists; but must not be empty in metadata"); + return BAD_VALUE; + } + newRequest->mResultExtras.requestId = it->find(ANDROID_REQUEST_ID).data.i32[0]; + } else { + CLOGE("RequestID does not exist in metadata"); + return BAD_VALUE; } - ALOGV("Camera %d: Capture request enqueued", mId); + + requestList->push_back(newRequest); + + ALOGV("%s: requestId = %" PRId32, __FUNCTION__, newRequest->mResultExtras.requestId); } - return res; + return OK; } +status_t Camera3Device::capture(CameraMetadata &request, int64_t* /*lastFrameNumber*/) { + ATRACE_CALL(); -status_t Camera3Device::setStreamingRequest(const CameraMetadata &request) { + List<const CameraMetadata> requests; + requests.push_back(request); + return captureList(requests, /*lastFrameNumber*/NULL); +} + +status_t Camera3Device::submitRequestsHelper( + const List<const CameraMetadata> &requests, bool repeating, + /*out*/ + int64_t *lastFrameNumber) { ATRACE_CALL(); - status_t res; Mutex::Autolock il(mInterfaceLock); Mutex::Autolock l(mLock); - switch (mStatus) { - case STATUS_ERROR: - CLOGE("Device has encountered a serious error"); - return INVALID_OPERATION; - case STATUS_UNINITIALIZED: - CLOGE("Device not initialized"); - return INVALID_OPERATION; - case STATUS_UNCONFIGURED: - // May be lazily configuring streams, will check during setup - case STATUS_CONFIGURED: - case STATUS_ACTIVE: - // OK - break; - default: - SET_ERR_L("Unexpected status: %d", mStatus); - return INVALID_OPERATION; + status_t res = checkStatusOkToCaptureLocked(); + if (res != OK) { + // error logged by previous call + return res; } - sp<CaptureRequest> newRepeatingRequest = setUpRequestLocked(request); - if (newRepeatingRequest == NULL) { - CLOGE("Can't create repeating request"); - return BAD_VALUE; + RequestList requestList; + + res = convertMetadataListToRequestListLocked(requests, /*out*/&requestList); + if (res != OK) { + // error logged by previous call + return res; } - RequestList newRepeatingRequests; - newRepeatingRequests.push_back(newRepeatingRequest); + if (repeating) { + res = mRequestThread->setRepeatingRequests(requestList, lastFrameNumber); + } else { + res = mRequestThread->queueRequestList(requestList, lastFrameNumber); + } - res = mRequestThread->setRepeatingRequests(newRepeatingRequests); if (res == OK) { - waitUntilStateThenRelock(/*active*/ true, kActiveTimeout); + waitUntilStateThenRelock(/*active*/true, kActiveTimeout); if (res != OK) { SET_ERR_L("Can't transition to active in %f seconds!", kActiveTimeout/1e9); } - ALOGV("Camera %d: Repeating request set", mId); + ALOGV("Camera %d: Capture request %" PRId32 " enqueued", mId, + (*(requestList.begin()))->mResultExtras.requestId); + } else { + CLOGE("Cannot queue request. Impossible."); + return BAD_VALUE; } + return res; } +status_t Camera3Device::captureList(const List<const CameraMetadata> &requests, + int64_t *lastFrameNumber) { + ATRACE_CALL(); + + return submitRequestsHelper(requests, /*repeating*/false, lastFrameNumber); +} + +status_t Camera3Device::setStreamingRequest(const CameraMetadata &request, + int64_t* /*lastFrameNumber*/) { + ATRACE_CALL(); + + List<const CameraMetadata> requests; + requests.push_back(request); + return setStreamingRequestList(requests, /*lastFrameNumber*/NULL); +} + +status_t Camera3Device::setStreamingRequestList(const List<const CameraMetadata> &requests, + int64_t *lastFrameNumber) { + ATRACE_CALL(); + + return submitRequestsHelper(requests, /*repeating*/true, lastFrameNumber); +} sp<Camera3Device::CaptureRequest> Camera3Device::setUpRequestLocked( const CameraMetadata &request) { @@ -483,10 +601,18 @@ sp<Camera3Device::CaptureRequest> Camera3Device::setUpRequestLocked( if (mStatus == STATUS_UNCONFIGURED || mNeedConfig) { res = configureStreamsLocked(); + // Stream configuration failed due to unsupported configuration. + // Device back to unconfigured state. Client might try other configuraitons + if (res == BAD_VALUE && mStatus == STATUS_UNCONFIGURED) { + CLOGE("No streams configured"); + return NULL; + } + // Stream configuration failed for other reason. Fatal. if (res != OK) { SET_ERR_L("Can't set up streams: %s (%d)", strerror(-res), res); return NULL; } + // Stream configuration successfully configure to empty stream configuration. if (mStatus == STATUS_UNCONFIGURED) { CLOGE("No streams configured"); return NULL; @@ -497,7 +623,7 @@ sp<Camera3Device::CaptureRequest> Camera3Device::setUpRequestLocked( return newRequest; } -status_t Camera3Device::clearStreamingRequest() { +status_t Camera3Device::clearStreamingRequest(int64_t *lastFrameNumber) { ATRACE_CALL(); Mutex::Autolock il(mInterfaceLock); Mutex::Autolock l(mLock); @@ -519,7 +645,8 @@ status_t Camera3Device::clearStreamingRequest() { return INVALID_OPERATION; } ALOGV("Camera %d: Clearing repeating request", mId); - return mRequestThread->clearRepeatingRequests(); + + return mRequestThread->clearRepeatingRequests(lastFrameNumber); } status_t Camera3Device::waitUntilRequestReceived(int32_t requestId, nsecs_t timeout) { @@ -676,12 +803,12 @@ status_t Camera3Device::createZslStream( } status_t Camera3Device::createStream(sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, int format, size_t size, int *id) { + uint32_t width, uint32_t height, int format, int *id) { ATRACE_CALL(); Mutex::Autolock il(mInterfaceLock); Mutex::Autolock l(mLock); - ALOGV("Camera %d: Creating new stream %d: %d x %d, format %d, size %zu", - mId, mNextStreamId, width, height, format, size); + ALOGV("Camera %d: Creating new stream %d: %d x %d, format %d", + mId, mNextStreamId, width, height, format); status_t res; bool wasActive = false; @@ -714,8 +841,14 @@ status_t Camera3Device::createStream(sp<ANativeWindow> consumer, sp<Camera3OutputStream> newStream; if (format == HAL_PIXEL_FORMAT_BLOB) { + ssize_t jpegBufferSize = getJpegBufferSize(width, height); + if (jpegBufferSize <= 0) { + SET_ERR_L("Invalid jpeg buffer size %zd", jpegBufferSize); + return BAD_VALUE; + } + newStream = new Camera3OutputStream(mNextStreamId, consumer, - width, height, size, format); + width, height, jpegBufferSize, format); } else { newStream = new Camera3OutputStream(mNextStreamId, consumer, width, height, format); @@ -840,16 +973,20 @@ status_t Camera3Device::deleteStream(int id) { } sp<Camera3StreamInterface> deletedStream; + ssize_t outputStreamIdx = mOutputStreams.indexOfKey(id); if (mInputStream != NULL && id == mInputStream->getId()) { deletedStream = mInputStream; mInputStream.clear(); } else { - ssize_t idx = mOutputStreams.indexOfKey(id); - if (idx == NAME_NOT_FOUND) { + if (outputStreamIdx == NAME_NOT_FOUND) { CLOGE("Stream %d does not exist", id); return BAD_VALUE; } - deletedStream = mOutputStreams.editValueAt(idx); + } + + // Delete output stream or the output part of a bi-directional stream. + if (outputStreamIdx != NAME_NOT_FOUND) { + deletedStream = mOutputStreams.editValueAt(outputStreamIdx); mOutputStreams.removeItem(id); } @@ -873,6 +1010,15 @@ status_t Camera3Device::deleteReprocessStream(int id) { return INVALID_OPERATION; } +status_t Camera3Device::configureStreams() { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + + Mutex::Autolock il(mInterfaceLock); + Mutex::Autolock l(mLock); + + return configureStreamsLocked(); +} status_t Camera3Device::createDefaultRequest(int templateId, CameraMetadata *request) { @@ -918,6 +1064,10 @@ status_t Camera3Device::waitUntilDrained() { Mutex::Autolock il(mInterfaceLock); Mutex::Autolock l(mLock); + return waitUntilDrainedLocked(); +} + +status_t Camera3Device::waitUntilDrainedLocked() { switch (mStatus) { case STATUS_UNINITIALIZED: case STATUS_UNCONFIGURED: @@ -1005,6 +1155,7 @@ status_t Camera3Device::setNotifyCallback(NotificationListener *listener) { ALOGW("%s: Replacing old callback listener", __FUNCTION__); } mListener = listener; + mRequestThread->setNotifyCallback(listener); return OK; } @@ -1030,7 +1181,7 @@ status_t Camera3Device::waitForNextFrame(nsecs_t timeout) { return OK; } -status_t Camera3Device::getNextFrame(CameraMetadata *frame) { +status_t Camera3Device::getNextResult(CaptureResult *frame) { ATRACE_CALL(); Mutex::Autolock l(mOutputLock); @@ -1038,8 +1189,14 @@ status_t Camera3Device::getNextFrame(CameraMetadata *frame) { return NOT_ENOUGH_DATA; } - CameraMetadata &result = *(mResultQueue.begin()); - frame->acquire(result); + if (frame == NULL) { + ALOGE("%s: argument cannot be NULL", __FUNCTION__); + return BAD_VALUE; + } + + CaptureResult &result = *(mResultQueue.begin()); + frame->mResultExtras = result.mResultExtras; + frame->mMetadata.acquire(result.mMetadata); mResultQueue.erase(mResultQueue.begin()); return OK; @@ -1059,7 +1216,7 @@ status_t Camera3Device::triggerAutofocus(uint32_t id) { { ANDROID_CONTROL_AF_TRIGGER_ID, static_cast<int32_t>(id) - }, + } }; return mRequestThread->queueTrigger(trigger, @@ -1080,7 +1237,7 @@ status_t Camera3Device::triggerCancelAutofocus(uint32_t id) { { ANDROID_CONTROL_AF_TRIGGER_ID, static_cast<int32_t>(id) - }, + } }; return mRequestThread->queueTrigger(trigger, @@ -1101,7 +1258,7 @@ status_t Camera3Device::triggerPrecaptureMetering(uint32_t id) { { ANDROID_CONTROL_AE_PRECAPTURE_ID, static_cast<int32_t>(id) - }, + } }; return mRequestThread->queueTrigger(trigger, @@ -1117,14 +1274,37 @@ status_t Camera3Device::pushReprocessBuffer(int reprocessStreamId, return INVALID_OPERATION; } -status_t Camera3Device::flush() { +status_t Camera3Device::flush(int64_t *frameNumber) { ATRACE_CALL(); ALOGV("%s: Camera %d: Flushing all requests", __FUNCTION__, mId); Mutex::Autolock il(mInterfaceLock); - Mutex::Autolock l(mLock); - mRequestThread->clear(); - return mHal3Device->ops->flush(mHal3Device); + NotificationListener* listener; + { + Mutex::Autolock l(mOutputLock); + listener = mListener; + } + + { + Mutex::Autolock l(mLock); + mRequestThread->clear(listener, /*out*/frameNumber); + } + + status_t res; + if (mHal3Device->common.version >= CAMERA_DEVICE_API_VERSION_3_1) { + res = mHal3Device->ops->flush(mHal3Device); + } else { + Mutex::Autolock l(mLock); + res = waitUntilDrainedLocked(); + } + + return res; +} + +uint32_t Camera3Device::getDeviceVersion() { + ATRACE_CALL(); + Mutex::Autolock il(mInterfaceLock); + return mDeviceVersion; } /** @@ -1248,6 +1428,15 @@ status_t Camera3Device::configureStreamsLocked() { return OK; } + // Workaround for device HALv3.2 or older spec bug - zero streams requires + // adding a dummy stream instead. + // TODO: Bug: 17321404 for fixing the HAL spec and removing this workaround. + if (mOutputStreams.size() == 0) { + addDummyStreamLocked(); + } else { + tryRemoveDummyStreamLocked(); + } + // Start configuring the streams ALOGV("%s: Camera %d: Starting stream configuration", __FUNCTION__, mId); @@ -1295,7 +1484,42 @@ status_t Camera3Device::configureStreamsLocked() { res = mHal3Device->ops->configure_streams(mHal3Device, &config); ATRACE_END(); - if (res != OK) { + if (res == BAD_VALUE) { + // HAL rejected this set of streams as unsupported, clean up config + // attempt and return to unconfigured state + if (mInputStream != NULL && mInputStream->isConfiguring()) { + res = mInputStream->cancelConfiguration(); + if (res != OK) { + SET_ERR_L("Can't cancel configuring input stream %d: %s (%d)", + mInputStream->getId(), strerror(-res), res); + return res; + } + } + + for (size_t i = 0; i < mOutputStreams.size(); i++) { + sp<Camera3OutputStreamInterface> outputStream = + mOutputStreams.editValueAt(i); + if (outputStream->isConfiguring()) { + res = outputStream->cancelConfiguration(); + if (res != OK) { + SET_ERR_L( + "Can't cancel configuring output stream %d: %s (%d)", + outputStream->getId(), strerror(-res), res); + return res; + } + } + } + + // Return state to that at start of call, so that future configures + // properly clean things up + mStatus = STATUS_UNCONFIGURED; + mNeedConfig = true; + + ALOGV("%s: Camera %d: Stream configuration failed", __FUNCTION__, mId); + return BAD_VALUE; + } else if (res != OK) { + // Some other kind of error from configure_streams - this is not + // expected SET_ERR_L("Unable to configure streams with HAL: %s (%d)", strerror(-res), res); return res; @@ -1335,7 +1559,7 @@ status_t Camera3Device::configureStreamsLocked() { mNeedConfig = false; - if (config.num_streams > 0) { + if (mDummyStreamId == NO_STREAM) { mStatus = STATUS_CONFIGURED; } else { mStatus = STATUS_UNCONFIGURED; @@ -1343,9 +1567,75 @@ status_t Camera3Device::configureStreamsLocked() { ALOGV("%s: Camera %d: Stream configuration complete", __FUNCTION__, mId); + // tear down the deleted streams after configure streams. + mDeletedStreams.clear(); + + return OK; +} + +status_t Camera3Device::addDummyStreamLocked() { + ATRACE_CALL(); + status_t res; + + if (mDummyStreamId != NO_STREAM) { + // Should never be adding a second dummy stream when one is already + // active + SET_ERR_L("%s: Camera %d: A dummy stream already exists!", + __FUNCTION__, mId); + return INVALID_OPERATION; + } + + ALOGV("%s: Camera %d: Adding a dummy stream", __FUNCTION__, mId); + + sp<Camera3OutputStreamInterface> dummyStream = + new Camera3DummyStream(mNextStreamId); + + res = mOutputStreams.add(mNextStreamId, dummyStream); + if (res < 0) { + SET_ERR_L("Can't add dummy stream to set: %s (%d)", strerror(-res), res); + return res; + } + + mDummyStreamId = mNextStreamId; + mNextStreamId++; + return OK; } +status_t Camera3Device::tryRemoveDummyStreamLocked() { + ATRACE_CALL(); + status_t res; + + if (mDummyStreamId == NO_STREAM) return OK; + if (mOutputStreams.size() == 1) return OK; + + ALOGV("%s: Camera %d: Removing the dummy stream", __FUNCTION__, mId); + + // Ok, have a dummy stream and there's at least one other output stream, + // so remove the dummy + + sp<Camera3StreamInterface> deletedStream; + ssize_t outputStreamIdx = mOutputStreams.indexOfKey(mDummyStreamId); + if (outputStreamIdx == NAME_NOT_FOUND) { + SET_ERR_L("Dummy stream %d does not appear to exist", mDummyStreamId); + return INVALID_OPERATION; + } + + deletedStream = mOutputStreams.editValueAt(outputStreamIdx); + mOutputStreams.removeItemsAt(outputStreamIdx); + + // Free up the stream endpoint so that it can be used by some other stream + res = deletedStream->disconnect(); + if (res != OK) { + SET_ERR_L("Can't disconnect deleted dummy stream %d", mDummyStreamId); + // fall through since we want to still list the stream as deleted. + } + mDeletedStreams.add(deletedStream); + mDummyStreamId = NO_STREAM; + + return res; +} + void Camera3Device::setErrorState(const char *fmt, ...) { Mutex::Autolock l(mLock); va_list args; @@ -1378,40 +1668,45 @@ void Camera3Device::setErrorStateLockedV(const char *fmt, va_list args) { // But only do error state transition steps for the first error if (mStatus == STATUS_ERROR || mStatus == STATUS_UNINITIALIZED) return; - // Save stack trace. View by dumping it later. - CameraTraces::saveTrace(); - // TODO: consider adding errorCause and client pid/procname - mErrorCause = errorCause; mRequestThread->setPaused(true); mStatus = STATUS_ERROR; + + // Notify upstream about a device error + if (mListener != NULL) { + mListener->notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE, + CaptureResultExtras()); + } + + // Save stack trace. View by dumping it later. + CameraTraces::saveTrace(); + // TODO: consider adding errorCause and client pid/procname } /** * In-flight request management */ -status_t Camera3Device::registerInFlight(int32_t frameNumber, - int32_t requestId, int32_t numBuffers) { +status_t Camera3Device::registerInFlight(uint32_t frameNumber, + int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput) { ATRACE_CALL(); Mutex::Autolock l(mInFlightLock); ssize_t res; - res = mInFlightMap.add(frameNumber, InFlightRequest(requestId, numBuffers)); + res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers, resultExtras, hasInput)); if (res < 0) return res; return OK; } /** - * QUIRK(partial results) * Check if all 3A fields are ready, and send off a partial 3A-only result * to the output frame queue */ -bool Camera3Device::processPartial3AQuirk( - int32_t frameNumber, int32_t requestId, - const CameraMetadata& partial) { +bool Camera3Device::processPartial3AResult( + uint32_t frameNumber, + const CameraMetadata& partial, const CaptureResultExtras& resultExtras) { // Check if all 3A states are present // The full list of fields is @@ -1431,8 +1726,6 @@ bool Camera3Device::processPartial3AQuirk( uint8_t aeState; uint8_t afState; uint8_t awbState; - int32_t afTriggerId; - int32_t aeTriggerId; gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AF_MODE, &afMode, frameNumber); @@ -1449,88 +1742,92 @@ bool Camera3Device::processPartial3AQuirk( gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AWB_STATE, &awbState, frameNumber); - gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AF_TRIGGER_ID, - &afTriggerId, frameNumber); - - gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AE_PRECAPTURE_ID, - &aeTriggerId, frameNumber); - if (!gotAllStates) return false; ALOGVV("%s: Camera %d: Frame %d, Request ID %d: AF mode %d, AWB mode %d, " "AF state %d, AE state %d, AWB state %d, " "AF trigger %d, AE precapture trigger %d", - __FUNCTION__, mId, frameNumber, requestId, + __FUNCTION__, mId, frameNumber, resultExtras.requestId, afMode, awbMode, afState, aeState, awbState, - afTriggerId, aeTriggerId); + resultExtras.afTriggerId, resultExtras.precaptureTriggerId); // Got all states, so construct a minimal result to send // In addition to the above fields, this means adding in // android.request.frameCount // android.request.requestId - // android.quirks.partialResult + // android.quirks.partialResult (for HAL version below HAL3.2) const size_t kMinimal3AResultEntries = 10; Mutex::Autolock l(mOutputLock); - CameraMetadata& min3AResult = - *mResultQueue.insert( - mResultQueue.end(), - CameraMetadata(kMinimal3AResultEntries, /*dataCapacity*/ 0)); - - if (!insert3AResult(min3AResult, ANDROID_REQUEST_FRAME_COUNT, - &frameNumber, frameNumber)) { + CaptureResult captureResult; + captureResult.mResultExtras = resultExtras; + captureResult.mMetadata = CameraMetadata(kMinimal3AResultEntries, /*dataCapacity*/ 0); + // TODO: change this to sp<CaptureResult>. This will need other changes, including, + // but not limited to CameraDeviceBase::getNextResult + CaptureResult& min3AResult = + *mResultQueue.insert(mResultQueue.end(), captureResult); + + if (!insert3AResult(min3AResult.mMetadata, ANDROID_REQUEST_FRAME_COUNT, + // TODO: This is problematic casting. Need to fix CameraMetadata. + reinterpret_cast<int32_t*>(&frameNumber), frameNumber)) { return false; } - if (!insert3AResult(min3AResult, ANDROID_REQUEST_ID, + int32_t requestId = resultExtras.requestId; + if (!insert3AResult(min3AResult.mMetadata, ANDROID_REQUEST_ID, &requestId, frameNumber)) { return false; } - static const uint8_t partialResult = ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL; - if (!insert3AResult(min3AResult, ANDROID_QUIRKS_PARTIAL_RESULT, - &partialResult, frameNumber)) { - return false; + if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) { + static const uint8_t partialResult = ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL; + if (!insert3AResult(min3AResult.mMetadata, ANDROID_QUIRKS_PARTIAL_RESULT, + &partialResult, frameNumber)) { + return false; + } } - if (!insert3AResult(min3AResult, ANDROID_CONTROL_AF_MODE, + if (!insert3AResult(min3AResult.mMetadata, ANDROID_CONTROL_AF_MODE, &afMode, frameNumber)) { return false; } - if (!insert3AResult(min3AResult, ANDROID_CONTROL_AWB_MODE, + if (!insert3AResult(min3AResult.mMetadata, ANDROID_CONTROL_AWB_MODE, &awbMode, frameNumber)) { return false; } - if (!insert3AResult(min3AResult, ANDROID_CONTROL_AE_STATE, + if (!insert3AResult(min3AResult.mMetadata, ANDROID_CONTROL_AE_STATE, &aeState, frameNumber)) { return false; } - if (!insert3AResult(min3AResult, ANDROID_CONTROL_AF_STATE, + if (!insert3AResult(min3AResult.mMetadata, ANDROID_CONTROL_AF_STATE, &afState, frameNumber)) { return false; } - if (!insert3AResult(min3AResult, ANDROID_CONTROL_AWB_STATE, + if (!insert3AResult(min3AResult.mMetadata, ANDROID_CONTROL_AWB_STATE, &awbState, frameNumber)) { return false; } - if (!insert3AResult(min3AResult, ANDROID_CONTROL_AF_TRIGGER_ID, - &afTriggerId, frameNumber)) { + if (!insert3AResult(min3AResult.mMetadata, ANDROID_CONTROL_AF_TRIGGER_ID, + &resultExtras.afTriggerId, frameNumber)) { return false; } - if (!insert3AResult(min3AResult, ANDROID_CONTROL_AE_PRECAPTURE_ID, - &aeTriggerId, frameNumber)) { + if (!insert3AResult(min3AResult.mMetadata, ANDROID_CONTROL_AE_PRECAPTURE_ID, + &resultExtras.precaptureTriggerId, frameNumber)) { return false; } + // We only send the aggregated partial when all 3A related metadata are available + // For both API1 and API2. + // TODO: we probably should pass through all partials to API2 unconditionally. mResultSignal.signal(); return true; @@ -1538,7 +1835,7 @@ bool Camera3Device::processPartial3AQuirk( template<typename T> bool Camera3Device::get3AResult(const CameraMetadata& result, int32_t tag, - T* value, int32_t frameNumber) { + T* value, uint32_t frameNumber) { (void) frameNumber; camera_metadata_ro_entry_t entry; @@ -1563,7 +1860,7 @@ bool Camera3Device::get3AResult(const CameraMetadata& result, int32_t tag, template<typename T> bool Camera3Device::insert3AResult(CameraMetadata& result, int32_t tag, - const T* value, int32_t frameNumber) { + const T* value, uint32_t frameNumber) { if (result.update(tag, value, 1) != NO_ERROR) { mResultQueue.erase(--mResultQueue.end(), mResultQueue.end()); SET_ERR("Frame %d: Failed to set %s in partial metadata", @@ -1583,18 +1880,34 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) { status_t res; uint32_t frameNumber = result->frame_number; - if (result->result == NULL && result->num_output_buffers == 0) { + if (result->result == NULL && result->num_output_buffers == 0 && + result->input_buffer == NULL) { SET_ERR("No result data provided by HAL for frame %d", frameNumber); return; } - bool partialResultQuirk = false; - CameraMetadata collectedQuirkResult; - // Get capture timestamp from list of in-flight requests, where it was added - // by the shutter notification for this frame. Then update the in-flight - // status and remove the in-flight entry if all result data has been - // received. + // For HAL3.2 or above, If HAL doesn't support partial, it must always set + // partial_result to 1 when metadata is included in this result. + if (!mUsePartialResult && + mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2 && + result->result != NULL && + result->partial_result != 1) { + SET_ERR("Result is malformed for frame %d: partial_result %u must be 1" + " if partial result is not supported", + frameNumber, result->partial_result); + return; + } + + bool isPartialResult = false; + CameraMetadata collectedPartialResult; + CaptureResultExtras resultExtras; + bool hasInputBufferInRequest = false; + + // Get capture timestamp and resultExtras from list of in-flight requests, + // where it was added by the shutter notification for this frame. + // Then update the in-flight status and remove the in-flight entry if + // all result data has been received. nsecs_t timestamp = 0; { Mutex::Autolock l(mInFlightLock); @@ -1605,71 +1918,108 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) { return; } InFlightRequest &request = mInFlightMap.editValueAt(idx); + ALOGVV("%s: got InFlightRequest requestId = %" PRId32 ", frameNumber = %" PRId64 + ", burstId = %" PRId32, + __FUNCTION__, request.resultExtras.requestId, request.resultExtras.frameNumber, + request.resultExtras.burstId); + // Always update the partial count to the latest one. When framework aggregates adjacent + // partial results into one, the latest partial count will be used. + request.resultExtras.partialResultCount = result->partial_result; // Check if this result carries only partial metadata - if (mUsePartialResultQuirk && result->result != NULL) { - camera_metadata_ro_entry_t partialResultEntry; - res = find_camera_metadata_ro_entry(result->result, - ANDROID_QUIRKS_PARTIAL_RESULT, &partialResultEntry); - if (res != NAME_NOT_FOUND && - partialResultEntry.count > 0 && - partialResultEntry.data.u8[0] == - ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL) { - // A partial result. Flag this as such, and collect this - // set of metadata into the in-flight entry. - partialResultQuirk = true; - request.partialResultQuirk.collectedResult.append( - result->result); - request.partialResultQuirk.collectedResult.erase( - ANDROID_QUIRKS_PARTIAL_RESULT); + if (mUsePartialResult && result->result != NULL) { + if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) { + if (result->partial_result > mNumPartialResults || result->partial_result < 1) { + SET_ERR("Result is malformed for frame %d: partial_result %u must be in" + " the range of [1, %d] when metadata is included in the result", + frameNumber, result->partial_result, mNumPartialResults); + return; + } + isPartialResult = (result->partial_result < mNumPartialResults); + if (isPartialResult) { + request.partialResult.collectedResult.append(result->result); + } + } else { + camera_metadata_ro_entry_t partialResultEntry; + res = find_camera_metadata_ro_entry(result->result, + ANDROID_QUIRKS_PARTIAL_RESULT, &partialResultEntry); + if (res != NAME_NOT_FOUND && + partialResultEntry.count > 0 && + partialResultEntry.data.u8[0] == + ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL) { + // A partial result. Flag this as such, and collect this + // set of metadata into the in-flight entry. + isPartialResult = true; + request.partialResult.collectedResult.append( + result->result); + request.partialResult.collectedResult.erase( + ANDROID_QUIRKS_PARTIAL_RESULT); + } + } + + if (isPartialResult) { // Fire off a 3A-only result if possible - if (!request.partialResultQuirk.haveSent3A) { - request.partialResultQuirk.haveSent3A = - processPartial3AQuirk(frameNumber, - request.requestId, - request.partialResultQuirk.collectedResult); + if (!request.partialResult.haveSent3A) { + request.partialResult.haveSent3A = + processPartial3AResult(frameNumber, + request.partialResult.collectedResult, + request.resultExtras); } } } timestamp = request.captureTimestamp; + resultExtras = request.resultExtras; + hasInputBufferInRequest = request.hasInputBuffer; + /** * One of the following must happen before it's legal to call process_capture_result, * unless partial metadata is being provided: * - CAMERA3_MSG_SHUTTER (expected during normal operation) * - CAMERA3_MSG_ERROR (expected during flush) */ - if (request.requestStatus == OK && timestamp == 0 && !partialResultQuirk) { + if (request.requestStatus == OK && timestamp == 0 && !isPartialResult) { SET_ERR("Called before shutter notify for frame %d", frameNumber); return; } // Did we get the (final) result metadata for this capture? - if (result->result != NULL && !partialResultQuirk) { + if (result->result != NULL && !isPartialResult) { if (request.haveResultMetadata) { SET_ERR("Called multiple times with metadata for frame %d", frameNumber); return; } - if (mUsePartialResultQuirk && - !request.partialResultQuirk.collectedResult.isEmpty()) { - collectedQuirkResult.acquire( - request.partialResultQuirk.collectedResult); + if (mUsePartialResult && + !request.partialResult.collectedResult.isEmpty()) { + collectedPartialResult.acquire( + request.partialResult.collectedResult); } request.haveResultMetadata = true; } - request.numBuffersLeft -= result->num_output_buffers; - + uint32_t numBuffersReturned = result->num_output_buffers; + if (result->input_buffer != NULL) { + if (hasInputBufferInRequest) { + numBuffersReturned += 1; + } else { + ALOGW("%s: Input buffer should be NULL if there is no input" + " buffer sent in the request", + __FUNCTION__); + } + } + request.numBuffersLeft -= numBuffersReturned; if (request.numBuffersLeft < 0) { SET_ERR("Too many buffers returned for frame %d", frameNumber); return; } - // Check if everything has arrived for this result (buffers and metadata) - if (request.haveResultMetadata && request.numBuffersLeft == 0) { + // Check if everything has arrived for this result (buffers and metadata), remove it from + // InFlightMap if both arrived or HAL reports error for this request (i.e. during flush). + if ((request.requestStatus != OK) || + (request.haveResultMetadata && request.numBuffersLeft == 0)) { ATRACE_ASYNC_END("frame capture", frameNumber); mInFlightMap.removeItemsAt(idx, 1); } @@ -1684,24 +2034,26 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) { // Process the result metadata, if provided bool gotResult = false; - if (result->result != NULL && !partialResultQuirk) { + if (result->result != NULL && !isPartialResult) { Mutex::Autolock l(mOutputLock); gotResult = true; - if (frameNumber != mNextResultFrameNumber) { + // TODO: need to track errors for tighter bounds on expected frame number + if (frameNumber < mNextResultFrameNumber) { SET_ERR("Out-of-order capture result metadata submitted! " "(got frame number %d, expecting %d)", frameNumber, mNextResultFrameNumber); return; } - mNextResultFrameNumber++; + mNextResultFrameNumber = frameNumber + 1; - CameraMetadata captureResult; - captureResult = result->result; + CaptureResult captureResult; + captureResult.mResultExtras = resultExtras; + captureResult.mMetadata = result->result; - if (captureResult.update(ANDROID_REQUEST_FRAME_COUNT, - (int32_t*)&frameNumber, 1) != OK) { + if (captureResult.mMetadata.update(ANDROID_REQUEST_FRAME_COUNT, + (int32_t*)&frameNumber, 1) != OK) { SET_ERR("Failed to set frame# in metadata (%d)", frameNumber); gotResult = false; @@ -1711,16 +2063,16 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) { } // Append any previous partials to form a complete result - if (mUsePartialResultQuirk && !collectedQuirkResult.isEmpty()) { - captureResult.append(collectedQuirkResult); + if (mUsePartialResult && !collectedPartialResult.isEmpty()) { + captureResult.mMetadata.append(collectedPartialResult); } - captureResult.sort(); + captureResult.mMetadata.sort(); // Check that there's a timestamp in the result metadata camera_metadata_entry entry = - captureResult.find(ANDROID_SENSOR_TIMESTAMP); + captureResult.mMetadata.find(ANDROID_SENSOR_TIMESTAMP); if (entry.count == 0) { SET_ERR("No timestamp provided by HAL for frame %d!", frameNumber); @@ -1734,9 +2086,13 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) { if (gotResult) { // Valid result, insert into queue - CameraMetadata& queuedResult = - *mResultQueue.insert(mResultQueue.end(), CameraMetadata()); - queuedResult.swap(captureResult); + List<CaptureResult>::iterator queuedResult = + mResultQueue.insert(mResultQueue.end(), CaptureResult(captureResult)); + ALOGVV("%s: result requestId = %" PRId32 ", frameNumber = %" PRId64 + ", burstId = %" PRId32, __FUNCTION__, + queuedResult->mResultExtras.requestId, + queuedResult->mResultExtras.frameNumber, + queuedResult->mResultExtras.burstId); } } // scope for mOutputLock @@ -1754,6 +2110,25 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) { } } + if (result->input_buffer != NULL) { + if (hasInputBufferInRequest) { + Camera3Stream *stream = + Camera3Stream::cast(result->input_buffer->stream); + res = stream->returnInputBuffer(*(result->input_buffer)); + // Note: stream may be deallocated at this point, if this buffer was the + // last reference to it. + if (res != OK) { + ALOGE("%s: RequestThread: Can't return input buffer for frame %d to" + " its stream:%s (%d)", __FUNCTION__, + frameNumber, strerror(-res), res); + } + } else { + ALOGW("%s: Input buffer should be NULL if there is no input" + " buffer sent in the request, skipping input buffer return.", + __FUNCTION__); + } + } + // Finally, signal any waiters for new frames if (gotResult) { @@ -1762,8 +2137,6 @@ void Camera3Device::processCaptureResult(const camera3_capture_result *result) { } - - void Camera3Device::notify(const camera3_notify_msg *msg) { ATRACE_CALL(); NotificationListener *listener; @@ -1779,80 +2152,134 @@ void Camera3Device::notify(const camera3_notify_msg *msg) { switch (msg->type) { case CAMERA3_MSG_ERROR: { - int streamId = 0; - if (msg->message.error.error_stream != NULL) { - Camera3Stream *stream = - Camera3Stream::cast( - msg->message.error.error_stream); - streamId = stream->getId(); - } - ALOGV("Camera %d: %s: HAL error, frame %d, stream %d: %d", - mId, __FUNCTION__, msg->message.error.frame_number, - streamId, msg->message.error.error_code); - - // Set request error status for the request in the in-flight tracking - { - Mutex::Autolock l(mInFlightLock); - ssize_t idx = mInFlightMap.indexOfKey(msg->message.error.frame_number); - if (idx >= 0) { - mInFlightMap.editValueAt(idx).requestStatus = msg->message.error.error_code; - } - } - - if (listener != NULL) { - listener->notifyError(msg->message.error.error_code, - msg->message.error.frame_number, streamId); - } + notifyError(msg->message.error, listener); break; } case CAMERA3_MSG_SHUTTER: { - ssize_t idx; - uint32_t frameNumber = msg->message.shutter.frame_number; - nsecs_t timestamp = msg->message.shutter.timestamp; - // Verify ordering of shutter notifications - { - Mutex::Autolock l(mOutputLock); - if (frameNumber != mNextShutterFrameNumber) { - SET_ERR("Shutter notification out-of-order. Expected " - "notification for frame %d, got frame %d", - mNextShutterFrameNumber, frameNumber); - break; - } - mNextShutterFrameNumber++; - } + notifyShutter(msg->message.shutter, listener); + break; + } + default: + SET_ERR("Unknown notify message from HAL: %d", + msg->type); + } +} - int32_t requestId = -1; +void Camera3Device::notifyError(const camera3_error_msg_t &msg, + NotificationListener *listener) { + + // Map camera HAL error codes to ICameraDeviceCallback error codes + // Index into this with the HAL error code + static const ICameraDeviceCallbacks::CameraErrorCode + halErrorMap[CAMERA3_MSG_NUM_ERRORS] = { + // 0 = Unused error code + ICameraDeviceCallbacks::ERROR_CAMERA_INVALID_ERROR, + // 1 = CAMERA3_MSG_ERROR_DEVICE + ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE, + // 2 = CAMERA3_MSG_ERROR_REQUEST + ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST, + // 3 = CAMERA3_MSG_ERROR_RESULT + ICameraDeviceCallbacks::ERROR_CAMERA_RESULT, + // 4 = CAMERA3_MSG_ERROR_BUFFER + ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER + }; - // Set timestamp for the request in the in-flight tracking - // and get the request ID to send upstream + ICameraDeviceCallbacks::CameraErrorCode errorCode = + ((msg.error_code >= 0) && + (msg.error_code < CAMERA3_MSG_NUM_ERRORS)) ? + halErrorMap[msg.error_code] : + ICameraDeviceCallbacks::ERROR_CAMERA_INVALID_ERROR; + + int streamId = 0; + if (msg.error_stream != NULL) { + Camera3Stream *stream = + Camera3Stream::cast(msg.error_stream); + streamId = stream->getId(); + } + ALOGV("Camera %d: %s: HAL error, frame %d, stream %d: %d", + mId, __FUNCTION__, msg.frame_number, + streamId, msg.error_code); + + CaptureResultExtras resultExtras; + switch (errorCode) { + case ICameraDeviceCallbacks::ERROR_CAMERA_DEVICE: + // SET_ERR calls notifyError + SET_ERR("Camera HAL reported serious device error"); + break; + case ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST: + case ICameraDeviceCallbacks::ERROR_CAMERA_RESULT: + case ICameraDeviceCallbacks::ERROR_CAMERA_BUFFER: { Mutex::Autolock l(mInFlightLock); - idx = mInFlightMap.indexOfKey(frameNumber); + ssize_t idx = mInFlightMap.indexOfKey(msg.frame_number); if (idx >= 0) { InFlightRequest &r = mInFlightMap.editValueAt(idx); - r.captureTimestamp = timestamp; - requestId = r.requestId; + r.requestStatus = msg.error_code; + resultExtras = r.resultExtras; + } else { + resultExtras.frameNumber = msg.frame_number; + ALOGE("Camera %d: %s: cannot find in-flight request on " + "frame %" PRId64 " error", mId, __FUNCTION__, + resultExtras.frameNumber); } } - if (idx < 0) { - SET_ERR("Shutter notification for non-existent frame number %d", - frameNumber); - break; - } - ALOGVV("Camera %d: %s: Shutter fired for frame %d (id %d) at %" PRId64, - mId, __FUNCTION__, frameNumber, requestId, timestamp); - // Call listener, if any if (listener != NULL) { - listener->notifyShutter(requestId, timestamp); + listener->notifyError(errorCode, resultExtras); + } else { + ALOGE("Camera %d: %s: no listener available", mId, __FUNCTION__); } break; - } default: - SET_ERR("Unknown notify message from HAL: %d", - msg->type); + // SET_ERR calls notifyError + SET_ERR("Unknown error message from HAL: %d", msg.error_code); + break; } } +void Camera3Device::notifyShutter(const camera3_shutter_msg_t &msg, + NotificationListener *listener) { + ssize_t idx; + // Verify ordering of shutter notifications + { + Mutex::Autolock l(mOutputLock); + // TODO: need to track errors for tighter bounds on expected frame number. + if (msg.frame_number < mNextShutterFrameNumber) { + SET_ERR("Shutter notification out-of-order. Expected " + "notification for frame %d, got frame %d", + mNextShutterFrameNumber, msg.frame_number); + return; + } + mNextShutterFrameNumber = msg.frame_number + 1; + } + + CaptureResultExtras resultExtras; + + // Set timestamp for the request in the in-flight tracking + // and get the request ID to send upstream + { + Mutex::Autolock l(mInFlightLock); + idx = mInFlightMap.indexOfKey(msg.frame_number); + if (idx >= 0) { + InFlightRequest &r = mInFlightMap.editValueAt(idx); + r.captureTimestamp = msg.timestamp; + resultExtras = r.resultExtras; + } + } + if (idx < 0) { + SET_ERR("Shutter notification for non-existent frame number %d", + msg.frame_number); + return; + } + ALOGVV("Camera %d: %s: Shutter fired for frame %d (id %d) at %" PRId64, + mId, __FUNCTION__, + msg.frame_number, resultExtras.requestId, msg.timestamp); + // Call listener, if any + if (listener != NULL) { + listener->notifyShutter(resultExtras, msg.timestamp); + } +} + + CameraMetadata Camera3Device::getLatestRequestLocked() { ALOGV("%s", __FUNCTION__); @@ -1865,6 +2292,7 @@ CameraMetadata Camera3Device::getLatestRequestLocked() { return retVal; } + /** * RequestThread inner class methods */ @@ -1881,19 +2309,40 @@ Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent, mDoPause(false), mPaused(true), mFrameNumber(0), - mLatestRequestId(NAME_NOT_FOUND) { + mLatestRequestId(NAME_NOT_FOUND), + mCurrentAfTriggerId(0), + mCurrentPreCaptureTriggerId(0), + mRepeatingLastFrameNumber(NO_IN_FLIGHT_REPEATING_FRAMES) { mStatusId = statusTracker->addComponent(); } +void Camera3Device::RequestThread::setNotifyCallback( + NotificationListener *listener) { + Mutex::Autolock l(mRequestLock); + mListener = listener; +} + void Camera3Device::RequestThread::configurationComplete() { Mutex::Autolock l(mRequestLock); mReconfigured = true; } -status_t Camera3Device::RequestThread::queueRequest( - sp<CaptureRequest> request) { +status_t Camera3Device::RequestThread::queueRequestList( + List<sp<CaptureRequest> > &requests, + /*out*/ + int64_t *lastFrameNumber) { Mutex::Autolock l(mRequestLock); - mRequestQueue.push_back(request); + for (List<sp<CaptureRequest> >::iterator it = requests.begin(); it != requests.end(); + ++it) { + mRequestQueue.push_back(*it); + } + + if (lastFrameNumber != NULL) { + *lastFrameNumber = mFrameNumber + mRequestQueue.size() - 1; + ALOGV("%s: requestId %d, mFrameNumber %" PRId32 ", lastFrameNumber %" PRId64 ".", + __FUNCTION__, (*(requests.begin()))->mResultExtras.requestId, mFrameNumber, + *lastFrameNumber); + } unpauseForNewRequests(); @@ -1957,28 +2406,72 @@ status_t Camera3Device::RequestThread::queueTriggerLocked( } status_t Camera3Device::RequestThread::setRepeatingRequests( - const RequestList &requests) { + const RequestList &requests, + /*out*/ + int64_t *lastFrameNumber) { Mutex::Autolock l(mRequestLock); + if (lastFrameNumber != NULL) { + *lastFrameNumber = mRepeatingLastFrameNumber; + } mRepeatingRequests.clear(); mRepeatingRequests.insert(mRepeatingRequests.begin(), requests.begin(), requests.end()); unpauseForNewRequests(); + mRepeatingLastFrameNumber = NO_IN_FLIGHT_REPEATING_FRAMES; return OK; } -status_t Camera3Device::RequestThread::clearRepeatingRequests() { +bool Camera3Device::RequestThread::isRepeatingRequestLocked(const sp<CaptureRequest> requestIn) { + if (mRepeatingRequests.empty()) { + return false; + } + int32_t requestId = requestIn->mResultExtras.requestId; + const RequestList &repeatRequests = mRepeatingRequests; + // All repeating requests are guaranteed to have same id so only check first quest + const sp<CaptureRequest> firstRequest = *repeatRequests.begin(); + return (firstRequest->mResultExtras.requestId == requestId); +} + +status_t Camera3Device::RequestThread::clearRepeatingRequests(/*out*/int64_t *lastFrameNumber) { Mutex::Autolock l(mRequestLock); mRepeatingRequests.clear(); + if (lastFrameNumber != NULL) { + *lastFrameNumber = mRepeatingLastFrameNumber; + } + mRepeatingLastFrameNumber = NO_IN_FLIGHT_REPEATING_FRAMES; return OK; } -status_t Camera3Device::RequestThread::clear() { +status_t Camera3Device::RequestThread::clear( + NotificationListener *listener, + /*out*/int64_t *lastFrameNumber) { Mutex::Autolock l(mRequestLock); + ALOGV("RequestThread::%s:", __FUNCTION__); + mRepeatingRequests.clear(); + + // Send errors for all requests pending in the request queue, including + // pending repeating requests + if (listener != NULL) { + for (RequestList::iterator it = mRequestQueue.begin(); + it != mRequestQueue.end(); ++it) { + // Set the frame number this request would have had, if it + // had been submitted; this frame number will not be reused. + // The requestId and burstId fields were set when the request was + // submitted originally (in convertMetadataListToRequestListLocked) + (*it)->mResultExtras.frameNumber = mFrameNumber++; + listener->notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST, + (*it)->mResultExtras); + } + } mRequestQueue.clear(); mTriggerMap.clear(); + if (lastFrameNumber != NULL) { + *lastFrameNumber = mRepeatingLastFrameNumber; + } + mRepeatingLastFrameNumber = NO_IN_FLIGHT_REPEATING_FRAMES; return OK; } @@ -2030,6 +2523,7 @@ bool Camera3Device::RequestThread::threadLoop() { // Create request to HAL camera3_capture_request_t request = camera3_capture_request_t(); + request.frame_number = nextRequest->mResultExtras.frameNumber; Vector<camera3_stream_buffer_t> outputBuffers; // Get the request ID, if any @@ -2050,7 +2544,7 @@ bool Camera3Device::RequestThread::threadLoop() { if (res < 0) { SET_ERR("RequestThread: Unable to insert triggers " "(capture request %d, HAL device: %s (%d)", - (mFrameNumber+1), strerror(-res), res); + request.frame_number, strerror(-res), res); cleanUpFailedRequest(request, nextRequest, outputBuffers); return false; } @@ -2068,7 +2562,7 @@ bool Camera3Device::RequestThread::threadLoop() { if (res != OK) { SET_ERR("RequestThread: Unable to insert dummy trigger IDs " "(capture request %d, HAL device: %s (%d)", - (mFrameNumber+1), strerror(-res), res); + request.frame_number, strerror(-res), res); cleanUpFailedRequest(request, nextRequest, outputBuffers); return false; } @@ -2092,7 +2586,7 @@ bool Camera3Device::RequestThread::threadLoop() { if (e.count > 0) { ALOGV("%s: Request (frame num %d) had AF trigger 0x%x", __FUNCTION__, - mFrameNumber+1, + request.frame_number, e.data.u8[0]); } } @@ -2103,6 +2597,7 @@ bool Camera3Device::RequestThread::threadLoop() { } camera3_stream_buffer_t inputBuffer; + uint32_t totalNumBuffers = 0; // Fill in buffers @@ -2110,11 +2605,21 @@ bool Camera3Device::RequestThread::threadLoop() { request.input_buffer = &inputBuffer; res = nextRequest->mInputStream->getInputBuffer(&inputBuffer); if (res != OK) { + // Can't get input buffer from gralloc queue - this could be due to + // disconnected queue or other producer misbehavior, so not a fatal + // error ALOGE("RequestThread: Can't get input buffer, skipping request:" " %s (%d)", strerror(-res), res); + Mutex::Autolock l(mRequestLock); + if (mListener != NULL) { + mListener->notifyError( + ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST, + nextRequest->mResultExtras); + } cleanUpFailedRequest(request, nextRequest, outputBuffers); return true; } + totalNumBuffers += 1; } else { request.input_buffer = NULL; } @@ -2126,26 +2631,41 @@ bool Camera3Device::RequestThread::threadLoop() { res = nextRequest->mOutputStreams.editItemAt(i)-> getBuffer(&outputBuffers.editItemAt(i)); if (res != OK) { + // Can't get output buffer from gralloc queue - this could be due to + // abandoned queue or other consumer misbehavior, so not a fatal + // error ALOGE("RequestThread: Can't get output buffer, skipping request:" " %s (%d)", strerror(-res), res); + Mutex::Autolock l(mRequestLock); + if (mListener != NULL) { + mListener->notifyError( + ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST, + nextRequest->mResultExtras); + } cleanUpFailedRequest(request, nextRequest, outputBuffers); return true; } request.num_output_buffers++; } - - request.frame_number = mFrameNumber++; + totalNumBuffers += request.num_output_buffers; // Log request in the in-flight queue sp<Camera3Device> parent = mParent.promote(); if (parent == NULL) { + // Should not happen, and nowhere to send errors to, so just log it CLOGE("RequestThread: Parent is gone"); cleanUpFailedRequest(request, nextRequest, outputBuffers); return false; } - res = parent->registerInFlight(request.frame_number, requestId, - request.num_output_buffers); + res = parent->registerInFlight(request.frame_number, + totalNumBuffers, nextRequest->mResultExtras, + /*hasInput*/request.input_buffer != NULL); + ALOGVV("%s: registered in flight requestId = %" PRId32 ", frameNumber = %" PRId64 + ", burstId = %" PRId32 ".", + __FUNCTION__, + nextRequest->mResultExtras.requestId, nextRequest->mResultExtras.frameNumber, + nextRequest->mResultExtras.burstId); if (res != OK) { SET_ERR("RequestThread: Unable to register new in-flight request:" " %s (%d)", strerror(-res), res); @@ -2168,6 +2688,9 @@ bool Camera3Device::RequestThread::threadLoop() { ATRACE_END(); if (res != OK) { + // Should only get a failure here for malformed requests or device-level + // errors, so consider all errors fatal. Bad metadata failures should + // come through notify. SET_ERR("RequestThread: Unable to submit capture request %d to HAL" " device: %s (%d)", request.frame_number, strerror(-res), res); cleanUpFailedRequest(request, nextRequest, outputBuffers); @@ -2196,21 +2719,6 @@ bool Camera3Device::RequestThread::threadLoop() { } mPrevTriggers = triggerCount; - // Return input buffer back to framework - if (request.input_buffer != NULL) { - Camera3Stream *stream = - Camera3Stream::cast(request.input_buffer->stream); - res = stream->returnInputBuffer(*(request.input_buffer)); - // Note: stream may be deallocated at this point, if this buffer was the - // last reference to it. - if (res != OK) { - ALOGE("%s: RequestThread: Can't return input buffer for frame %d to" - " its stream:%s (%d)", __FUNCTION__, - request.frame_number, strerror(-res), res); - // TODO: Report error upstream - } - } - return true; } @@ -2222,6 +2730,7 @@ CameraMetadata Camera3Device::RequestThread::getLatestRequest() const { return mLatestRequest; } + void Camera3Device::RequestThread::cleanUpFailedRequest( camera3_capture_request_t &request, sp<CaptureRequest> &nextRequest, @@ -2263,6 +2772,9 @@ sp<Camera3Device::CaptureRequest> ++firstRequest, requests.end()); // No need to wait any longer + + mRepeatingLastFrameNumber = mFrameNumber + requests.size() - 1; + break; } @@ -2314,6 +2826,11 @@ sp<Camera3Device::CaptureRequest> mReconfigured = false; } + if (nextRequest != NULL) { + nextRequest->mResultExtras.frameNumber = mFrameNumber++; + nextRequest->mResultExtras.afTriggerId = mCurrentAfTriggerId; + nextRequest->mResultExtras.precaptureTriggerId = mCurrentPreCaptureTriggerId; + } return nextRequest; } @@ -2376,13 +2893,34 @@ status_t Camera3Device::RequestThread::insertTriggers( Mutex::Autolock al(mTriggerMutex); + sp<Camera3Device> parent = mParent.promote(); + if (parent == NULL) { + CLOGE("RequestThread: Parent is gone"); + return DEAD_OBJECT; + } + CameraMetadata &metadata = request->mSettings; size_t count = mTriggerMap.size(); for (size_t i = 0; i < count; ++i) { RequestTrigger trigger = mTriggerMap.valueAt(i); - uint32_t tag = trigger.metadataTag; + + if (tag == ANDROID_CONTROL_AF_TRIGGER_ID || tag == ANDROID_CONTROL_AE_PRECAPTURE_ID) { + bool isAeTrigger = (trigger.metadataTag == ANDROID_CONTROL_AE_PRECAPTURE_ID); + uint32_t triggerId = static_cast<uint32_t>(trigger.entryValue); + if (isAeTrigger) { + request->mResultExtras.precaptureTriggerId = triggerId; + mCurrentPreCaptureTriggerId = triggerId; + } else { + request->mResultExtras.afTriggerId = triggerId; + mCurrentAfTriggerId = triggerId; + } + if (parent->mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) { + continue; // Trigger ID tag is deprecated since device HAL 3.2 + } + } + camera_metadata_entry entry = metadata.find(tag); if (entry.count > 0) { diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h index 468f641..b99ed7e 100644 --- a/services/camera/libcameraservice/device3/Camera3Device.h +++ b/services/camera/libcameraservice/device3/Camera3Device.h @@ -24,6 +24,8 @@ #include <utils/Thread.h> #include <utils/KeyedVector.h> #include <hardware/camera3.h> +#include <camera/CaptureResult.h> +#include <camera/camera2/ICameraDeviceUser.h> #include "common/CameraDeviceBase.h" #include "device3/StatusTracker.h" @@ -54,7 +56,7 @@ class Camera3StreamInterface; } /** - * CameraDevice for HAL devices with version CAMERA_DEVICE_API_VERSION_3_0 + * CameraDevice for HAL devices with version CAMERA_DEVICE_API_VERSION_3_0 or higher. */ class Camera3Device : public CameraDeviceBase, @@ -78,9 +80,14 @@ class Camera3Device : // Capture and setStreamingRequest will configure streams if currently in // idle state - virtual status_t capture(CameraMetadata &request); - virtual status_t setStreamingRequest(const CameraMetadata &request); - virtual status_t clearStreamingRequest(); + virtual status_t capture(CameraMetadata &request, int64_t *lastFrameNumber = NULL); + virtual status_t captureList(const List<const CameraMetadata> &requests, + int64_t *lastFrameNumber = NULL); + virtual status_t setStreamingRequest(const CameraMetadata &request, + int64_t *lastFrameNumber = NULL); + virtual status_t setStreamingRequestList(const List<const CameraMetadata> &requests, + int64_t *lastFrameNumber = NULL); + virtual status_t clearStreamingRequest(int64_t *lastFrameNumber = NULL); virtual status_t waitUntilRequestReceived(int32_t requestId, nsecs_t timeout); @@ -88,8 +95,7 @@ class Camera3Device : // If adding streams while actively capturing, will pause device before adding // stream, reconfiguring device, and unpausing. virtual status_t createStream(sp<ANativeWindow> consumer, - uint32_t width, uint32_t height, int format, size_t size, - int *id); + uint32_t width, uint32_t height, int format, int *id); virtual status_t createInputStream( uint32_t width, uint32_t height, int format, int *id); @@ -108,6 +114,8 @@ class Camera3Device : virtual status_t deleteStream(int id); virtual status_t deleteReprocessStream(int id); + virtual status_t configureStreams(); + virtual status_t createDefaultRequest(int templateId, CameraMetadata *request); // Transitions to the idle state on success @@ -116,7 +124,7 @@ class Camera3Device : virtual status_t setNotifyCallback(NotificationListener *listener); virtual bool willNotify3A(); virtual status_t waitForNextFrame(nsecs_t timeout); - virtual status_t getNextFrame(CameraMetadata *frame); + virtual status_t getNextResult(CaptureResult *frame); virtual status_t triggerAutofocus(uint32_t id); virtual status_t triggerCancelAutofocus(uint32_t id); @@ -125,7 +133,11 @@ class Camera3Device : virtual status_t pushReprocessBuffer(int reprocessStreamId, buffer_handle_t *buffer, wp<BufferReleasedListener> listener); - virtual status_t flush(); + virtual status_t flush(int64_t *lastFrameNumber = NULL); + + virtual uint32_t getDeviceVersion(); + + virtual ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const; // Methods called by subclasses void notifyStatus(bool idle); // updates from StatusTracker @@ -137,6 +149,10 @@ class Camera3Device : static const nsecs_t kShutdownTimeout = 5000000000; // 5 sec static const nsecs_t kActiveTimeout = 500000000; // 500 ms struct RequestTrigger; + // minimal jpeg buffer size: 256KB + blob header + static const ssize_t kMinJpegBufferSize = 256 * 1024 + sizeof(camera3_jpeg_blob); + // Constant to use for stream ID when one doesn't exist + static const int NO_STREAM = -1; // A lock to enforce serialization on the input/configure side // of the public interface. @@ -157,7 +173,8 @@ class Camera3Device : camera3_device_t *mHal3Device; CameraMetadata mDeviceInfo; - vendor_tag_query_ops_t mVendorTagOps; + + uint32_t mDeviceVersion; enum Status { STATUS_ERROR, @@ -181,6 +198,8 @@ class Camera3Device : int mNextStreamId; bool mNeedConfig; + int mDummyStreamId; + // Whether to send state updates upstream // Pause when doing transparent reconfiguration bool mPauseStateNotify; @@ -188,8 +207,11 @@ class Camera3Device : // Need to hold on to stream references until configure completes. Vector<sp<camera3::Camera3StreamInterface> > mDeletedStreams; - // Whether quirk ANDROID_QUIRKS_USE_PARTIAL_RESULT is enabled - bool mUsePartialResultQuirk; + // Whether the HAL will send partial result + bool mUsePartialResult; + + // Number of partial results that will be delivered by the HAL. + uint32_t mNumPartialResults; /**** End scope for mLock ****/ @@ -199,9 +221,20 @@ class Camera3Device : sp<camera3::Camera3Stream> mInputStream; Vector<sp<camera3::Camera3OutputStreamInterface> > mOutputStreams; + CaptureResultExtras mResultExtras; }; typedef List<sp<CaptureRequest> > RequestList; + status_t checkStatusOkToCaptureLocked(); + + status_t convertMetadataListToRequestListLocked( + const List<const CameraMetadata> &metadataList, + /*out*/ + RequestList *requestList); + + status_t submitRequestsHelper(const List<const CameraMetadata> &requests, bool repeating, + int64_t *lastFrameNumber = NULL); + /** * Get the last request submitted to the hal by the request thread. * @@ -237,6 +270,13 @@ class Camera3Device : status_t waitUntilStateThenRelock(bool active, nsecs_t timeout); /** + * Implementation of waitUntilDrained. On success, will transition to IDLE state. + * + * Need to be called with mLock and mInterfaceLock held. + */ + status_t waitUntilDrainedLocked(); + + /** * Do common work for setting up a streaming or single capture request. * On success, will transition to ACTIVE if in IDLE. */ @@ -255,6 +295,17 @@ class Camera3Device : status_t configureStreamsLocked(); /** + * Add a dummy stream to the current stream set as a workaround for + * not allowing 0 streams in the camera HAL spec. + */ + status_t addDummyStreamLocked(); + + /** + * Remove a dummy stream if the current config includes real streams. + */ + status_t tryRemoveDummyStreamLocked(); + + /** * Set device into an error state due to some fatal failure, and set an * error message to indicate why. Only the first call's message will be * used. The message is also sent to the log. @@ -270,6 +321,18 @@ class Camera3Device : */ bool tryLockSpinRightRound(Mutex& lock); + struct Size { + int width; + int height; + Size(int w, int h) : width(w), height(h){} + }; + + /** + * Helper function to get the largest Jpeg resolution (in area) + * Return Size(0, 0) if static metatdata is invalid + */ + Size getMaxJpegResolution() const; + struct RequestTrigger { // Metadata tag number, e.g. android.control.aePrecaptureTrigger uint32_t metadataTag; @@ -298,6 +361,8 @@ class Camera3Device : sp<camera3::StatusTracker> statusTracker, camera3_device_t *hal3Device); + void setNotifyCallback(NotificationListener *listener); + /** * Call after stream (re)-configuration is completed. */ @@ -308,15 +373,22 @@ class Camera3Device : * on either. Use waitUntilPaused to wait until request queue * has emptied out. */ - status_t setRepeatingRequests(const RequestList& requests); - status_t clearRepeatingRequests(); + status_t setRepeatingRequests(const RequestList& requests, + /*out*/ + int64_t *lastFrameNumber = NULL); + status_t clearRepeatingRequests(/*out*/ + int64_t *lastFrameNumber = NULL); - status_t queueRequest(sp<CaptureRequest> request); + status_t queueRequestList(List<sp<CaptureRequest> > &requests, + /*out*/ + int64_t *lastFrameNumber = NULL); /** * Remove all queued and repeating requests, and pending triggers */ - status_t clear(); + status_t clear(NotificationListener *listener, + /*out*/ + int64_t *lastFrameNumber = NULL); /** * Queue a trigger to be dispatched with the next outgoing @@ -391,10 +463,15 @@ class Camera3Device : // Relay error to parent device object setErrorState void setErrorState(const char *fmt, ...); + // If the input request is in mRepeatingRequests. Must be called with mRequestLock hold + bool isRepeatingRequestLocked(const sp<CaptureRequest>); + wp<Camera3Device> mParent; wp<camera3::StatusTracker> mStatusTracker; camera3_device_t *mHal3Device; + NotificationListener *mListener; + const int mId; // The camera ID int mStatusId; // The RequestThread's component ID for // status tracking @@ -429,6 +506,10 @@ class Camera3Device : TriggerMap mTriggerMap; TriggerMap mTriggerRemovedMap; TriggerMap mTriggerReplacedMap; + uint32_t mCurrentAfTriggerId; + uint32_t mCurrentPreCaptureTriggerId; + + int64_t mRepeatingLastFrameNumber; }; sp<RequestThread> mRequestThread; @@ -437,71 +518,90 @@ class Camera3Device : */ struct InFlightRequest { - // android.request.id for the request - int requestId; // Set by notify() SHUTTER call. nsecs_t captureTimestamp; int requestStatus; // Set by process_capture_result call with valid metadata bool haveResultMetadata; // Decremented by calls to process_capture_result with valid output - // buffers + // and input buffers int numBuffersLeft; + CaptureResultExtras resultExtras; + // If this request has any input buffer + bool hasInputBuffer; - // Fields used by the partial result quirk only - struct PartialResultQuirkInFlight { + // Fields used by the partial result only + struct PartialResultInFlight { // Set by process_capture_result once 3A has been sent to clients bool haveSent3A; // Result metadata collected so far, when partial results are in use CameraMetadata collectedResult; - PartialResultQuirkInFlight(): + PartialResultInFlight(): haveSent3A(false) { } - } partialResultQuirk; + } partialResult; // Default constructor needed by KeyedVector InFlightRequest() : - requestId(0), captureTimestamp(0), requestStatus(OK), haveResultMetadata(false), - numBuffersLeft(0) { + numBuffersLeft(0), + hasInputBuffer(false){ } - InFlightRequest(int id, int numBuffers) : - requestId(id), + InFlightRequest(int numBuffers) : captureTimestamp(0), requestStatus(OK), haveResultMetadata(false), - numBuffersLeft(numBuffers) { + numBuffersLeft(numBuffers), + hasInputBuffer(false){ } - }; + + InFlightRequest(int numBuffers, CaptureResultExtras extras) : + captureTimestamp(0), + requestStatus(OK), + haveResultMetadata(false), + numBuffersLeft(numBuffers), + resultExtras(extras), + hasInputBuffer(false){ + } + + InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput) : + captureTimestamp(0), + requestStatus(OK), + haveResultMetadata(false), + numBuffersLeft(numBuffers), + resultExtras(extras), + hasInputBuffer(hasInput){ + } +}; // Map from frame number to the in-flight request state typedef KeyedVector<uint32_t, InFlightRequest> InFlightMap; Mutex mInFlightLock; // Protects mInFlightMap InFlightMap mInFlightMap; - status_t registerInFlight(int32_t frameNumber, int32_t requestId, - int32_t numBuffers); + status_t registerInFlight(uint32_t frameNumber, + int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput); /** - * For the partial result quirk, check if all 3A state fields are available + * For the partial result, check if all 3A state fields are available * and if so, queue up 3A-only result to the client. Returns true if 3A * is sent. */ - bool processPartial3AQuirk(int32_t frameNumber, int32_t requestId, - const CameraMetadata& partial); + bool processPartial3AResult(uint32_t frameNumber, + const CameraMetadata& partial, const CaptureResultExtras& resultExtras); // Helpers for reading and writing 3A metadata into to/from partial results template<typename T> bool get3AResult(const CameraMetadata& result, int32_t tag, - T* value, int32_t frameNumber); + T* value, uint32_t frameNumber); template<typename T> bool insert3AResult(CameraMetadata &result, int32_t tag, const T* value, - int32_t frameNumber); + uint32_t frameNumber); /** * Tracking for idle detection */ @@ -518,7 +618,7 @@ class Camera3Device : uint32_t mNextResultFrameNumber; uint32_t mNextShutterFrameNumber; - List<CameraMetadata> mResultQueue; + List<CaptureResult> mResultQueue; Condition mResultSignal; NotificationListener *mListener; @@ -531,6 +631,12 @@ class Camera3Device : void notify(const camera3_notify_msg *msg); + // Specific notify handlers + void notifyError(const camera3_error_msg_t &msg, + NotificationListener *listener); + void notifyShutter(const camera3_shutter_msg_t &msg, + NotificationListener *listener); + /** * Static callback forwarding methods from HAL to instance */ diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp new file mode 100644 index 0000000..6656b09 --- /dev/null +++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp @@ -0,0 +1,97 @@ +/* + * 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 "Camera3-DummyStream" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/Trace.h> +#include "Camera3DummyStream.h" + +namespace android { + +namespace camera3 { + +Camera3DummyStream::Camera3DummyStream(int id) : + Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, DUMMY_WIDTH, DUMMY_HEIGHT, + /*maxSize*/0, DUMMY_FORMAT) { + +} + +Camera3DummyStream::~Camera3DummyStream() { + +} + +status_t Camera3DummyStream::getBufferLocked(camera3_stream_buffer *buffer) { + ATRACE_CALL(); + ALOGE("%s: Stream %d: Dummy stream cannot produce buffers!", mId); + return INVALID_OPERATION; +} + +status_t Camera3DummyStream::returnBufferLocked( + const camera3_stream_buffer &buffer, + nsecs_t timestamp) { + ATRACE_CALL(); + ALOGE("%s: Stream %d: Dummy stream cannot return buffers!", mId); + return INVALID_OPERATION; +} + +status_t Camera3DummyStream::returnBufferCheckedLocked( + const camera3_stream_buffer &buffer, + nsecs_t timestamp, + bool output, + /*out*/ + sp<Fence> *releaseFenceOut) { + ATRACE_CALL(); + ALOGE("%s: Stream %d: Dummy stream cannot return buffers!", mId); + return INVALID_OPERATION; +} + +void Camera3DummyStream::dump(int fd, const Vector<String16> &args) const { + (void) args; + String8 lines; + lines.appendFormat(" Stream[%d]: Dummy\n", mId); + write(fd, lines.string(), lines.size()); + + Camera3IOStreamBase::dump(fd, args); +} + +status_t Camera3DummyStream::setTransform(int transform) { + ATRACE_CALL(); + // Do nothing + return OK; +} + +status_t Camera3DummyStream::configureQueueLocked() { + // Do nothing + return OK; +} + +status_t Camera3DummyStream::disconnectLocked() { + mState = (mState == STATE_IN_RECONFIG) ? STATE_IN_CONFIG + : STATE_CONSTRUCTED; + return OK; +} + +status_t Camera3DummyStream::getEndpointUsage(uint32_t *usage) { + *usage = DUMMY_USAGE; + return OK; +} + +}; // namespace camera3 + +}; // namespace android diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h new file mode 100644 index 0000000..3e42623 --- /dev/null +++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h @@ -0,0 +1,98 @@ +/* + * 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_SERVERS_CAMERA3_DUMMY_STREAM_H +#define ANDROID_SERVERS_CAMERA3_DUMMY_STREAM_H + +#include <utils/RefBase.h> +#include <gui/Surface.h> + +#include "Camera3Stream.h" +#include "Camera3IOStreamBase.h" +#include "Camera3OutputStreamInterface.h" + +namespace android { +namespace camera3 { + +/** + * A dummy output stream class, to be used as a placeholder when no valid + * streams are configured by the client. + * This is necessary because camera HAL v3.2 or older disallow configuring + * 0 output streams, while the public camera2 API allows for it. + */ +class Camera3DummyStream : + public Camera3IOStreamBase, + public Camera3OutputStreamInterface { + + public: + /** + * Set up a dummy stream; doesn't actually connect to anything, and uses + * a default dummy format and size. + */ + Camera3DummyStream(int id); + + virtual ~Camera3DummyStream(); + + /** + * Camera3Stream interface + */ + + virtual void dump(int fd, const Vector<String16> &args) const; + + status_t setTransform(int transform); + + protected: + + /** + * Note that we release the lock briefly in this function + */ + virtual status_t returnBufferCheckedLocked( + const camera3_stream_buffer &buffer, + nsecs_t timestamp, + bool output, + /*out*/ + sp<Fence> *releaseFenceOut); + + virtual status_t disconnectLocked(); + + private: + + // Default dummy parameters; 320x240 is a required size for all devices, + // otherwise act like a SurfaceView would. + static const int DUMMY_WIDTH = 320; + static const int DUMMY_HEIGHT = 240; + static const int DUMMY_FORMAT = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; + static const uint32_t DUMMY_USAGE = GRALLOC_USAGE_HW_COMPOSER; + + /** + * Internal Camera3Stream interface + */ + virtual status_t getBufferLocked(camera3_stream_buffer *buffer); + virtual status_t returnBufferLocked( + const camera3_stream_buffer &buffer, + nsecs_t timestamp); + + virtual status_t configureQueueLocked(); + + virtual status_t getEndpointUsage(uint32_t *usage); + +}; // class Camera3DummyStream + +} // namespace camera3 + +} // namespace android + +#endif diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp index d662cc2..cc66459 100644 --- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp +++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp @@ -34,7 +34,8 @@ Camera3IOStreamBase::Camera3IOStreamBase(int id, camera3_stream_type_t type, Camera3Stream(id, type, width, height, maxSize, format), mTotalBufferCount(0), - mDequeuedBufferCount(0), + mHandoutTotalBufferCount(0), + mHandoutOutputBufferCount(0), mFrameCount(0), mLastTimestamp(0) { @@ -55,8 +56,8 @@ bool Camera3IOStreamBase::hasOutstandingBuffersLocked() const { nsecs_t signalTime = mCombinedFence->getSignalTime(); ALOGV("%s: Stream %d: Has %zu outstanding buffers," " buffer signal time is %" PRId64, - __FUNCTION__, mId, mDequeuedBufferCount, signalTime); - if (mDequeuedBufferCount > 0 || signalTime == INT64_MAX) { + __FUNCTION__, mId, mHandoutTotalBufferCount, signalTime); + if (mHandoutTotalBufferCount > 0 || signalTime == INT64_MAX) { return true; } return false; @@ -75,7 +76,7 @@ void Camera3IOStreamBase::dump(int fd, const Vector<String16> &args) const { lines.appendFormat(" Frames produced: %d, last timestamp: %" PRId64 " ns\n", mFrameCount, mLastTimestamp); lines.appendFormat(" Total buffers: %zu, currently dequeued: %zu\n", - mTotalBufferCount, mDequeuedBufferCount); + mTotalBufferCount, mHandoutTotalBufferCount); write(fd, lines.string(), lines.size()); } @@ -104,6 +105,14 @@ size_t Camera3IOStreamBase::getBufferCountLocked() { return mTotalBufferCount; } +size_t Camera3IOStreamBase::getHandoutOutputBufferCountLocked() { + return mHandoutOutputBufferCount; +} + +size_t Camera3IOStreamBase::getHandoutInputBufferCountLocked() { + return (mHandoutTotalBufferCount - mHandoutOutputBufferCount); +} + status_t Camera3IOStreamBase::disconnectLocked() { switch (mState) { case STATE_IN_RECONFIG: @@ -117,9 +126,9 @@ status_t Camera3IOStreamBase::disconnectLocked() { return -ENOTCONN; } - if (mDequeuedBufferCount > 0) { + if (mHandoutTotalBufferCount > 0) { ALOGE("%s: Can't disconnect with %zu buffers still dequeued!", - __FUNCTION__, mDequeuedBufferCount); + __FUNCTION__, mHandoutTotalBufferCount); return INVALID_OPERATION; } @@ -130,7 +139,8 @@ void Camera3IOStreamBase::handoutBufferLocked(camera3_stream_buffer &buffer, buffer_handle_t *handle, int acquireFence, int releaseFence, - camera3_buffer_status_t status) { + camera3_buffer_status_t status, + bool output) { /** * Note that all fences are now owned by HAL. */ @@ -144,14 +154,25 @@ void Camera3IOStreamBase::handoutBufferLocked(camera3_stream_buffer &buffer, buffer.status = status; // Inform tracker about becoming busy - if (mDequeuedBufferCount == 0 && mState != STATE_IN_CONFIG && + if (mHandoutTotalBufferCount == 0 && mState != STATE_IN_CONFIG && mState != STATE_IN_RECONFIG) { + /** + * Avoid a spurious IDLE->ACTIVE->IDLE transition when using buffers + * before/after register_stream_buffers during initial configuration + * or re-configuration. + * + * TODO: IN_CONFIG and IN_RECONFIG checks only make sense for <HAL3.2 + */ sp<StatusTracker> statusTracker = mStatusTracker.promote(); if (statusTracker != 0) { statusTracker->markComponentActive(mStatusId); } } - mDequeuedBufferCount++; + mHandoutTotalBufferCount++; + + if (output) { + mHandoutOutputBufferCount++; + } } status_t Camera3IOStreamBase::getBufferPreconditionCheckLocked() const { @@ -163,15 +184,6 @@ status_t Camera3IOStreamBase::getBufferPreconditionCheckLocked() const { return INVALID_OPERATION; } - // Only limit dequeue amount when fully configured - if (mState == STATE_CONFIGURED && - mDequeuedBufferCount == camera3_stream::max_buffers) { - ALOGE("%s: Stream %d: Already dequeued maximum number of simultaneous" - " buffers (%d)", __FUNCTION__, mId, - camera3_stream::max_buffers); - return INVALID_OPERATION; - } - return OK; } @@ -183,7 +195,7 @@ status_t Camera3IOStreamBase::returnBufferPreconditionCheckLocked() const { __FUNCTION__, mId, mState); return INVALID_OPERATION; } - if (mDequeuedBufferCount == 0) { + if (mHandoutTotalBufferCount == 0) { ALOGE("%s: Stream %d: No buffers outstanding to return", __FUNCTION__, mId); return INVALID_OPERATION; @@ -221,9 +233,20 @@ status_t Camera3IOStreamBase::returnAnyBufferLocked( mCombinedFence = Fence::merge(mName, mCombinedFence, releaseFence); } - mDequeuedBufferCount--; - if (mDequeuedBufferCount == 0 && mState != STATE_IN_CONFIG && + if (output) { + mHandoutOutputBufferCount--; + } + + mHandoutTotalBufferCount--; + if (mHandoutTotalBufferCount == 0 && mState != STATE_IN_CONFIG && mState != STATE_IN_RECONFIG) { + /** + * Avoid a spurious IDLE->ACTIVE->IDLE transition when using buffers + * before/after register_stream_buffers during initial configuration + * or re-configuration. + * + * TODO: IN_CONFIG and IN_RECONFIG checks only make sense for <HAL3.2 + */ ALOGV("%s: Stream %d: All buffers returned; now idle", __FUNCTION__, mId); sp<StatusTracker> statusTracker = mStatusTracker.promote(); diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h index fcb9d04..a35c290 100644 --- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h +++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h @@ -48,7 +48,10 @@ class Camera3IOStreamBase : protected: size_t mTotalBufferCount; // sum of input and output buffers that are currently acquired by HAL - size_t mDequeuedBufferCount; + size_t mHandoutTotalBufferCount; + // number of output buffers that are currently acquired by HAL. This will be + // Redundant when camera3 streams are no longer bidirectional streams. + size_t mHandoutOutputBufferCount; Condition mBufferReturnedSignal; uint32_t mFrameCount; // Last received output buffer's timestamp @@ -76,6 +79,10 @@ class Camera3IOStreamBase : virtual size_t getBufferCountLocked(); + virtual size_t getHandoutOutputBufferCountLocked(); + + virtual size_t getHandoutInputBufferCountLocked(); + virtual status_t getEndpointUsage(uint32_t *usage) = 0; status_t getBufferPreconditionCheckLocked() const; @@ -92,7 +99,8 @@ class Camera3IOStreamBase : buffer_handle_t *handle, int acquire_fence, int release_fence, - camera3_buffer_status_t status); + camera3_buffer_status_t status, + bool output); }; // class Camera3IOStreamBase diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp index 5aa9a3e..319be1d 100644 --- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp @@ -81,7 +81,7 @@ status_t Camera3InputStream::getInputBufferLocked( * in which case we reassign it to acquire_fence */ handoutBufferLocked(*buffer, &(anb->handle), /*acquireFence*/fenceFd, - /*releaseFence*/-1, CAMERA3_BUFFER_STATUS_OK); + /*releaseFence*/-1, CAMERA3_BUFFER_STATUS_OK, /*output*/false); mBuffersInFlight.push_back(bufferItem); return OK; @@ -199,14 +199,36 @@ status_t Camera3InputStream::configureQueueLocked() { assert(mMaxSize == 0); assert(camera3_stream::format != HAL_PIXEL_FORMAT_BLOB); - mTotalBufferCount = BufferQueue::MIN_UNDEQUEUED_BUFFERS + - camera3_stream::max_buffers; - mDequeuedBufferCount = 0; + mHandoutTotalBufferCount = 0; mFrameCount = 0; if (mConsumer.get() == 0) { - sp<BufferQueue> bq = new BufferQueue(); - mConsumer = new BufferItemConsumer(bq, camera3_stream::usage, + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + int minUndequeuedBuffers = 0; + res = producer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffers); + if (res != OK || minUndequeuedBuffers < 0) { + ALOGE("%s: Stream %d: Could not query min undequeued buffers (error %d, bufCount %d)", + __FUNCTION__, mId, res, minUndequeuedBuffers); + return res; + } + size_t minBufs = static_cast<size_t>(minUndequeuedBuffers); + /* + * We promise never to 'acquire' more than camera3_stream::max_buffers + * at any one time. + * + * Boost the number up to meet the minimum required buffer count. + * + * (Note that this sets consumer-side buffer count only, + * and not the sum of producer+consumer side as in other camera streams). + */ + mTotalBufferCount = camera3_stream::max_buffers > minBufs ? + camera3_stream::max_buffers : minBufs; + // TODO: somehow set the total buffer count when producer connects? + + mConsumer = new BufferItemConsumer(consumer, camera3_stream::usage, mTotalBufferCount); mConsumer->setName(String8::format("Camera3-InputStream-%d", mId)); } diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.h b/services/camera/libcameraservice/device3/Camera3InputStream.h index 681d684..ae49467 100644 --- a/services/camera/libcameraservice/device3/Camera3InputStream.h +++ b/services/camera/libcameraservice/device3/Camera3InputStream.h @@ -44,6 +44,8 @@ class Camera3InputStream : public Camera3IOStreamBase { virtual void dump(int fd, const Vector<String16> &args) const; + // TODO: expose an interface to get the IGraphicBufferProducer + private: typedef BufferItemConsumer::BufferItem BufferItem; diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp index 682755d..169eb82 100644 --- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp @@ -119,7 +119,7 @@ status_t Camera3OutputStream::getBufferLocked(camera3_stream_buffer *buffer) { * in which case we reassign it to acquire_fence */ handoutBufferLocked(*buffer, &(anb->handle), /*acquireFence*/fenceFd, - /*releaseFence*/-1, CAMERA3_BUFFER_STATUS_OK); + /*releaseFence*/-1, CAMERA3_BUFFER_STATUS_OK, /*output*/true); return OK; } @@ -289,20 +289,25 @@ status_t Camera3OutputStream::configureQueueLocked() { if (mMaxSize == 0) { // For buffers of known size - res = native_window_set_buffers_geometry(mConsumer.get(), - camera3_stream::width, camera3_stream::height, - camera3_stream::format); + res = native_window_set_buffers_dimensions(mConsumer.get(), + camera3_stream::width, camera3_stream::height); } else { // For buffers with bounded size - res = native_window_set_buffers_geometry(mConsumer.get(), - mMaxSize, 1, - camera3_stream::format); + res = native_window_set_buffers_dimensions(mConsumer.get(), + mMaxSize, 1); } if (res != OK) { - ALOGE("%s: Unable to configure stream buffer geometry" - " %d x %d, format %x for stream %d", + ALOGE("%s: Unable to configure stream buffer dimensions" + " %d x %d (maxSize %zu) for stream %d", __FUNCTION__, camera3_stream::width, camera3_stream::height, - camera3_stream::format, mId); + mMaxSize, mId); + return res; + } + res = native_window_set_buffers_format(mConsumer.get(), + camera3_stream::format); + if (res != OK) { + ALOGE("%s: Unable to configure stream buffer format %#x for stream %d", + __FUNCTION__, camera3_stream::format, mId); return res; } @@ -324,7 +329,7 @@ status_t Camera3OutputStream::configureQueueLocked() { } mTotalBufferCount = maxConsumerBuffers + camera3_stream::max_buffers; - mDequeuedBufferCount = 0; + mHandoutTotalBufferCount = 0; mFrameCount = 0; mLastTimestamp = 0; diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h index 6cbb9f4..f963326 100644 --- a/services/camera/libcameraservice/device3/Camera3OutputStream.h +++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h @@ -76,6 +76,8 @@ class Camera3OutputStream : /*out*/ sp<Fence> *releaseFenceOut); + virtual status_t disconnectLocked(); + sp<ANativeWindow> mConsumer; private: int mTransform; @@ -91,7 +93,6 @@ class Camera3OutputStream : nsecs_t timestamp); virtual status_t configureQueueLocked(); - virtual status_t disconnectLocked(); virtual status_t getEndpointUsage(uint32_t *usage); diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp index 70406f1..3c0e908 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.cpp +++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp @@ -23,6 +23,8 @@ #include "device3/Camera3Stream.h" #include "device3/StatusTracker.h" +#include <cutils/properties.h> + namespace android { namespace camera3 { @@ -137,6 +139,7 @@ camera3_stream* Camera3Stream::startConfiguration() { if (mState == STATE_CONSTRUCTED) { mState = STATE_IN_CONFIG; } else { // mState == STATE_CONFIGURED + LOG_ALWAYS_FATAL_IF(mState != STATE_CONFIGURED, "Invalid state: 0x%x", mState); mState = STATE_IN_RECONFIG; } @@ -206,11 +209,61 @@ status_t Camera3Stream::finishConfiguration(camera3_device *hal3Device) { return res; } +status_t Camera3Stream::cancelConfiguration() { + ATRACE_CALL(); + Mutex::Autolock l(mLock); + switch (mState) { + case STATE_ERROR: + ALOGE("%s: In error state", __FUNCTION__); + return INVALID_OPERATION; + case STATE_IN_CONFIG: + case STATE_IN_RECONFIG: + // OK + break; + case STATE_CONSTRUCTED: + case STATE_CONFIGURED: + ALOGE("%s: Cannot cancel configuration that hasn't been started", + __FUNCTION__); + return INVALID_OPERATION; + default: + ALOGE("%s: Unknown state", __FUNCTION__); + return INVALID_OPERATION; + } + + camera3_stream::usage = oldUsage; + camera3_stream::max_buffers = oldMaxBuffers; + + mState = (mState == STATE_IN_RECONFIG) ? STATE_CONFIGURED : STATE_CONSTRUCTED; + return OK; +} + status_t Camera3Stream::getBuffer(camera3_stream_buffer *buffer) { ATRACE_CALL(); Mutex::Autolock l(mLock); + status_t res = OK; + + // This function should be only called when the stream is configured already. + if (mState != STATE_CONFIGURED) { + ALOGE("%s: Stream %d: Can't get buffers if stream is not in CONFIGURED state %d", + __FUNCTION__, mId, mState); + return INVALID_OPERATION; + } + + // Wait for new buffer returned back if we are running into the limit. + if (getHandoutOutputBufferCountLocked() == camera3_stream::max_buffers) { + ALOGV("%s: Already dequeued max output buffers (%d), wait for next returned one.", + __FUNCTION__, camera3_stream::max_buffers); + res = mOutputBufferReturnedSignal.waitRelative(mLock, kWaitForBufferDuration); + if (res != OK) { + if (res == TIMED_OUT) { + ALOGE("%s: wait for output buffer return timed out after %lldms", __FUNCTION__, + kWaitForBufferDuration / 1000000LL); + } + return res; + } + } - status_t res = getBufferLocked(buffer); + res = getBufferLocked(buffer); if (res == OK) { fireBufferListenersLocked(*buffer, /*acquired*/true, /*output*/true); } @@ -223,9 +276,18 @@ status_t Camera3Stream::returnBuffer(const camera3_stream_buffer &buffer, ATRACE_CALL(); Mutex::Autolock l(mLock); + /** + * TODO: Check that the state is valid first. + * + * <HAL3.2 IN_CONFIG and IN_RECONFIG in addition to CONFIGURED. + * >= HAL3.2 CONFIGURED only + * + * Do this for getBuffer as well. + */ status_t res = returnBufferLocked(buffer, timestamp); if (res == OK) { fireBufferListenersLocked(buffer, /*acquired*/false, /*output*/true); + mOutputBufferReturnedSignal.signal(); } return res; @@ -234,8 +296,30 @@ status_t Camera3Stream::returnBuffer(const camera3_stream_buffer &buffer, status_t Camera3Stream::getInputBuffer(camera3_stream_buffer *buffer) { ATRACE_CALL(); Mutex::Autolock l(mLock); + status_t res = OK; + + // This function should be only called when the stream is configured already. + if (mState != STATE_CONFIGURED) { + ALOGE("%s: Stream %d: Can't get input buffers if stream is not in CONFIGURED state %d", + __FUNCTION__, mId, mState); + return INVALID_OPERATION; + } + + // Wait for new buffer returned back if we are running into the limit. + if (getHandoutInputBufferCountLocked() == camera3_stream::max_buffers) { + ALOGV("%s: Already dequeued max input buffers (%d), wait for next returned one.", + __FUNCTION__, camera3_stream::max_buffers); + res = mInputBufferReturnedSignal.waitRelative(mLock, kWaitForBufferDuration); + if (res != OK) { + if (res == TIMED_OUT) { + ALOGE("%s: wait for input buffer return timed out after %lldms", __FUNCTION__, + kWaitForBufferDuration / 1000000LL); + } + return res; + } + } - status_t res = getInputBufferLocked(buffer); + res = getInputBufferLocked(buffer); if (res == OK) { fireBufferListenersLocked(*buffer, /*acquired*/true, /*output*/false); } @@ -250,6 +334,7 @@ status_t Camera3Stream::returnInputBuffer(const camera3_stream_buffer &buffer) { status_t res = returnInputBufferLocked(buffer); if (res == OK) { fireBufferListenersLocked(buffer, /*acquired*/false, /*output*/false); + mInputBufferReturnedSignal.signal(); } return res; } @@ -314,12 +399,35 @@ status_t Camera3Stream::disconnect() { status_t Camera3Stream::registerBuffersLocked(camera3_device *hal3Device) { ATRACE_CALL(); + + /** + * >= CAMERA_DEVICE_API_VERSION_3_2: + * + * camera3_device_t->ops->register_stream_buffers() is not called and must + * be NULL. + */ + if (hal3Device->common.version >= CAMERA_DEVICE_API_VERSION_3_2) { + ALOGV("%s: register_stream_buffers unused as of HAL3.2", __FUNCTION__); + + if (hal3Device->ops->register_stream_buffers != NULL) { + ALOGE("%s: register_stream_buffers is deprecated in HAL3.2; " + "must be set to NULL in camera3_device::ops", __FUNCTION__); + return INVALID_OPERATION; + } else { + ALOGD("%s: Skipping NULL check for deprecated register_stream_buffers", __FUNCTION__); + } + + return OK; + } else { + ALOGV("%s: register_stream_buffers using deprecated code path", __FUNCTION__); + } + status_t res; size_t bufferCount = getBufferCountLocked(); Vector<buffer_handle_t*> buffers; - buffers.insertAt(NULL, 0, bufferCount); + buffers.insertAt(/*prototype_item*/NULL, /*index*/0, bufferCount); camera3_stream_buffer_set bufferSet = camera3_stream_buffer_set(); bufferSet.stream = this; @@ -327,7 +435,7 @@ status_t Camera3Stream::registerBuffersLocked(camera3_device *hal3Device) { bufferSet.buffers = buffers.editArray(); Vector<camera3_stream_buffer_t> streamBuffers; - streamBuffers.insertAt(camera3_stream_buffer_t(), 0, bufferCount); + streamBuffers.insertAt(camera3_stream_buffer_t(), /*index*/0, bufferCount); // Register all buffers with the HAL. This means getting all the buffers // from the stream, providing them to the HAL with the @@ -394,6 +502,18 @@ status_t Camera3Stream::returnInputBufferLocked( void Camera3Stream::addBufferListener( wp<Camera3StreamBufferListener> listener) { Mutex::Autolock l(mLock); + + List<wp<Camera3StreamBufferListener> >::iterator it, end; + for (it = mBufferListenerList.begin(), end = mBufferListenerList.end(); + it != end; + ) { + if (*it == listener) { + ALOGE("%s: Try to add the same listener twice, ignoring...", __FUNCTION__); + return; + } + it++; + } + mBufferListenerList.push_back(listener); } diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h index 6eeb721..d0e1337 100644 --- a/services/camera/libcameraservice/device3/Camera3Stream.h +++ b/services/camera/libcameraservice/device3/Camera3Stream.h @@ -82,6 +82,23 @@ namespace camera3 { * STATE_CONFIGURED => STATE_CONSTRUCTED: * When disconnect() is called after making sure stream is idle with * waitUntilIdle(). + * + * Status Tracking: + * Each stream is tracked by StatusTracker as a separate component, + * depending on the handed out buffer count. The state must be STATE_CONFIGURED + * in order for the component to be marked. + * + * It's marked in one of two ways: + * + * - ACTIVE: One or more buffers have been handed out (with #getBuffer). + * - IDLE: All buffers have been returned (with #returnBuffer), and their + * respective release_fence(s) have been signaled. + * + * A typical use case is output streams. When the HAL has any buffers + * dequeued, the stream is marked ACTIVE. When the HAL returns all buffers + * (e.g. if no capture requests are active), the stream is marked IDLE. + * In this use case, the app consumer does not affect the component status. + * */ class Camera3Stream : protected camera3_stream, @@ -142,6 +159,13 @@ class Camera3Stream : status_t finishConfiguration(camera3_device *hal3Device); /** + * Cancels the stream configuration process. This returns the stream to the + * initial state, allowing it to be configured again later. + * This is done if the HAL rejects the proposed combined stream configuration + */ + status_t cancelConfiguration(); + + /** * Fill in the camera3_stream_buffer with the next valid buffer for this * stream, to hand over to the HAL. * @@ -209,8 +233,17 @@ class Camera3Stream : */ virtual void dump(int fd, const Vector<String16> &args) const = 0; + /** + * Add a camera3 buffer listener. Adding the same listener twice has + * no effect. + */ void addBufferListener( wp<Camera3StreamBufferListener> listener); + + /** + * Remove a camera3 buffer listener. Removing the same listener twice + * or the listener that was never added has no effect. + */ void removeBufferListener( const sp<Camera3StreamBufferListener>& listener); @@ -262,6 +295,12 @@ class Camera3Stream : // Get the total number of buffers in the queue virtual size_t getBufferCountLocked() = 0; + // Get handout output buffer count. + virtual size_t getHandoutOutputBufferCountLocked() = 0; + + // Get handout input buffer count. + virtual size_t getHandoutInputBufferCountLocked() = 0; + // Get the usage flags for the other endpoint, or return // INVALID_OPERATION if they cannot be obtained. virtual status_t getEndpointUsage(uint32_t *usage) = 0; @@ -274,6 +313,9 @@ class Camera3Stream : private: uint32_t oldUsage; uint32_t oldMaxBuffers; + Condition mOutputBufferReturnedSignal; + Condition mInputBufferReturnedSignal; + static const nsecs_t kWaitForBufferDuration = 3000000000LL; // 3000 ms // Gets all buffers from endpoint and registers them with the HAL. status_t registerBuffersLocked(camera3_device *hal3Device); diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h index c93ae15..da989cd 100644 --- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h +++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h @@ -82,6 +82,13 @@ class Camera3StreamInterface : public virtual RefBase { virtual status_t finishConfiguration(camera3_device *hal3Device) = 0; /** + * Cancels the stream configuration process. This returns the stream to the + * initial state, allowing it to be configured again later. + * This is done if the HAL rejects the proposed combined stream configuration + */ + virtual status_t cancelConfiguration() = 0; + + /** * Fill in the camera3_stream_buffer with the next valid buffer for this * stream, to hand over to the HAL. * diff --git a/services/camera/libcameraservice/device3/Camera3ZslStream.cpp b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp index 44d8188..92bf81b 100644 --- a/services/camera/libcameraservice/device3/Camera3ZslStream.cpp +++ b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp @@ -111,15 +111,17 @@ struct TimestampFinder : public RingBufferConsumer::RingBufferComparator { } // namespace anonymous Camera3ZslStream::Camera3ZslStream(int id, uint32_t width, uint32_t height, - int depth) : + int bufferCount) : Camera3OutputStream(id, CAMERA3_STREAM_BIDIRECTIONAL, width, height, HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED), - mDepth(depth) { + mDepth(bufferCount) { - sp<BufferQueue> bq = new BufferQueue(); - mProducer = new RingBufferConsumer(bq, GRALLOC_USAGE_HW_CAMERA_ZSL, depth); - mConsumer = new Surface(bq); + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + mProducer = new RingBufferConsumer(consumer, GRALLOC_USAGE_HW_CAMERA_ZSL, bufferCount); + mConsumer = new Surface(producer); } Camera3ZslStream::~Camera3ZslStream() { @@ -174,7 +176,7 @@ status_t Camera3ZslStream::getInputBufferLocked(camera3_stream_buffer *buffer) { * in which case we reassign it to acquire_fence */ handoutBufferLocked(*buffer, &(anb->handle), /*acquireFence*/fenceFd, - /*releaseFence*/-1, CAMERA3_BUFFER_STATUS_OK); + /*releaseFence*/-1, CAMERA3_BUFFER_STATUS_OK, /*output*/false); mBuffersInFlight.push_back(bufferItem); @@ -298,6 +300,7 @@ status_t Camera3ZslStream::enqueueInputBufferByTimestamp( nsecs_t actual = pinnedBuffer->getBufferItem().mTimestamp; if (actual != timestamp) { + // TODO: this is problematic, we'll end up with using wrong result for this pinned buffer. ALOGW("%s: ZSL buffer candidate search didn't find an exact match --" " requested timestamp = %" PRId64 ", actual timestamp = %" PRId64, __FUNCTION__, timestamp, actual); @@ -315,11 +318,21 @@ status_t Camera3ZslStream::enqueueInputBufferByTimestamp( status_t Camera3ZslStream::clearInputRingBuffer() { Mutex::Autolock l(mLock); + return clearInputRingBufferLocked(); +} + +status_t Camera3ZslStream::clearInputRingBufferLocked() { mInputBufferQueue.clear(); return mProducer->clear(); } +status_t Camera3ZslStream::disconnectLocked() { + clearInputRingBufferLocked(); + + return Camera3OutputStream::disconnectLocked(); +} + status_t Camera3ZslStream::setTransform(int /*transform*/) { ALOGV("%s: Not implemented", __FUNCTION__); return INVALID_OPERATION; diff --git a/services/camera/libcameraservice/device3/Camera3ZslStream.h b/services/camera/libcameraservice/device3/Camera3ZslStream.h index c7f4490..d89c38d 100644 --- a/services/camera/libcameraservice/device3/Camera3ZslStream.h +++ b/services/camera/libcameraservice/device3/Camera3ZslStream.h @@ -37,10 +37,10 @@ class Camera3ZslStream : public Camera3OutputStream { public: /** - * Set up a ZSL stream of a given resolution. Depth is the number of buffers + * Set up a ZSL stream of a given resolution. bufferCount is the number of buffers * cached within the stream that can be retrieved for input. */ - Camera3ZslStream(int id, uint32_t width, uint32_t height, int depth); + Camera3ZslStream(int id, uint32_t width, uint32_t height, int bufferCount); ~Camera3ZslStream(); virtual void dump(int fd, const Vector<String16> &args) const; @@ -96,6 +96,12 @@ class Camera3ZslStream : bool output, /*out*/ sp<Fence> *releaseFenceOut); + + // Disconnet the Camera3ZslStream specific bufferQueues. + virtual status_t disconnectLocked(); + + status_t clearInputRingBufferLocked(); + }; // class Camera3ZslStream }; // namespace camera3 diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.h b/services/camera/libcameraservice/gui/RingBufferConsumer.h index b4ad824..a03736d 100644 --- a/services/camera/libcameraservice/gui/RingBufferConsumer.h +++ b/services/camera/libcameraservice/gui/RingBufferConsumer.h @@ -64,7 +64,7 @@ class RingBufferConsumer : public ConsumerBase, // bufferCount parameter specifies how many buffers can be pinned for user // access at the same time. RingBufferConsumer(const sp<IGraphicBufferConsumer>& consumer, uint32_t consumerUsage, - int bufferCount = BufferQueue::MIN_UNDEQUEUED_BUFFERS); + int bufferCount); virtual ~RingBufferConsumer(); diff --git a/services/medialog/MediaLogService.cpp b/services/medialog/MediaLogService.cpp index e53b3a6..41dab1f 100644 --- a/services/medialog/MediaLogService.cpp +++ b/services/medialog/MediaLogService.cpp @@ -54,7 +54,7 @@ void MediaLogService::unregisterWriter(const sp<IMemory>& shared) } } -status_t MediaLogService::dump(int fd, const Vector<String16>& args) +status_t MediaLogService::dump(int fd, const Vector<String16>& args __unused) { // FIXME merge with similar but not identical code at services/audioflinger/ServiceUtilities.cpp static const String16 sDump("android.permission.DUMP"); diff --git a/services/soundtrigger/Android.mk b/services/soundtrigger/Android.mk new file mode 100644 index 0000000..572ae56 --- /dev/null +++ b/services/soundtrigger/Android.mk @@ -0,0 +1,45 @@ +# Copyright 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + + +ifeq ($(SOUND_TRIGGER_USE_STUB_MODULE), 1) + LOCAL_CFLAGS += -DSOUND_TRIGGER_USE_STUB_MODULE +endif + +LOCAL_SRC_FILES:= \ + SoundTriggerHwService.cpp + +LOCAL_SHARED_LIBRARIES:= \ + libui \ + liblog \ + libutils \ + libbinder \ + libcutils \ + libhardware \ + libsoundtrigger \ + libmedia + +LOCAL_STATIC_LIBRARIES := \ + libserviceutility + +LOCAL_C_INCLUDES += \ + $(TOPDIR)frameworks/av/services/audioflinger + +LOCAL_MODULE:= libsoundtriggerservice + +include $(BUILD_SHARED_LIBRARY) diff --git a/services/soundtrigger/SoundTriggerHwService.cpp b/services/soundtrigger/SoundTriggerHwService.cpp new file mode 100644 index 0000000..b5aaee3 --- /dev/null +++ b/services/soundtrigger/SoundTriggerHwService.cpp @@ -0,0 +1,822 @@ +/* + * 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 "SoundTriggerHwService" +//#define LOG_NDEBUG 0 + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <pthread.h> + +#include <system/sound_trigger.h> +#include <cutils/atomic.h> +#include <cutils/properties.h> +#include <hardware/hardware.h> +#include <media/AudioSystem.h> +#include <utils/Errors.h> +#include <utils/Log.h> +#include <binder/IServiceManager.h> +#include <binder/MemoryBase.h> +#include <binder/MemoryHeapBase.h> +#include <hardware/sound_trigger.h> +#include <ServiceUtilities.h> +#include "SoundTriggerHwService.h" + +namespace android { + +#ifdef SOUND_TRIGGER_USE_STUB_MODULE +#define HW_MODULE_PREFIX "stub" +#else +#define HW_MODULE_PREFIX "primary" +#endif + +SoundTriggerHwService::SoundTriggerHwService() + : BnSoundTriggerHwService(), + mNextUniqueId(1), + mMemoryDealer(new MemoryDealer(1024 * 1024, "SoundTriggerHwService")), + mCaptureState(false) +{ +} + +void SoundTriggerHwService::onFirstRef() +{ + const hw_module_t *mod; + int rc; + sound_trigger_hw_device *dev; + + rc = hw_get_module_by_class(SOUND_TRIGGER_HARDWARE_MODULE_ID, HW_MODULE_PREFIX, &mod); + if (rc != 0) { + ALOGE("couldn't load sound trigger module %s.%s (%s)", + SOUND_TRIGGER_HARDWARE_MODULE_ID, "primary", strerror(-rc)); + return; + } + rc = sound_trigger_hw_device_open(mod, &dev); + if (rc != 0) { + ALOGE("couldn't open sound trigger hw device in %s.%s (%s)", + SOUND_TRIGGER_HARDWARE_MODULE_ID, "primary", strerror(-rc)); + return; + } + if (dev->common.version != SOUND_TRIGGER_DEVICE_API_VERSION_CURRENT) { + ALOGE("wrong sound trigger hw device version %04x", dev->common.version); + return; + } + + sound_trigger_module_descriptor descriptor; + rc = dev->get_properties(dev, &descriptor.properties); + if (rc != 0) { + ALOGE("could not read implementation properties"); + return; + } + descriptor.handle = + (sound_trigger_module_handle_t)android_atomic_inc(&mNextUniqueId); + ALOGI("loaded default module %s, handle %d", descriptor.properties.description, + descriptor.handle); + + sp<ISoundTriggerClient> client; + sp<Module> module = new Module(this, dev, descriptor, client); + mModules.add(descriptor.handle, module); + mCallbackThread = new CallbackThread(this); +} + +SoundTriggerHwService::~SoundTriggerHwService() +{ + if (mCallbackThread != 0) { + mCallbackThread->exit(); + } + for (size_t i = 0; i < mModules.size(); i++) { + sound_trigger_hw_device_close(mModules.valueAt(i)->hwDevice()); + } +} + +status_t SoundTriggerHwService::listModules(struct sound_trigger_module_descriptor *modules, + uint32_t *numModules) +{ + ALOGV("listModules"); + if (!captureHotwordAllowed()) { + return PERMISSION_DENIED; + } + + AutoMutex lock(mServiceLock); + if (numModules == NULL || (*numModules != 0 && modules == NULL)) { + return BAD_VALUE; + } + size_t maxModules = *numModules; + *numModules = mModules.size(); + for (size_t i = 0; i < mModules.size() && i < maxModules; i++) { + modules[i] = mModules.valueAt(i)->descriptor(); + } + return NO_ERROR; +} + +status_t SoundTriggerHwService::attach(const sound_trigger_module_handle_t handle, + const sp<ISoundTriggerClient>& client, + sp<ISoundTrigger>& moduleInterface) +{ + ALOGV("attach module %d", handle); + if (!captureHotwordAllowed()) { + return PERMISSION_DENIED; + } + + AutoMutex lock(mServiceLock); + moduleInterface.clear(); + if (client == 0) { + return BAD_VALUE; + } + ssize_t index = mModules.indexOfKey(handle); + if (index < 0) { + return BAD_VALUE; + } + sp<Module> module = mModules.valueAt(index); + + module->setClient(client); + client->asBinder()->linkToDeath(module); + moduleInterface = module; + + module->setCaptureState_l(mCaptureState); + + return NO_ERROR; +} + +status_t SoundTriggerHwService::setCaptureState(bool active) +{ + ALOGV("setCaptureState %d", active); + AutoMutex lock(mServiceLock); + mCaptureState = active; + for (size_t i = 0; i < mModules.size(); i++) { + mModules.valueAt(i)->setCaptureState_l(active); + } + return NO_ERROR; +} + + +void SoundTriggerHwService::detachModule(sp<Module> module) +{ + ALOGV("detachModule"); + AutoMutex lock(mServiceLock); + module->clearClient(); +} + + +static const int kDumpLockRetries = 50; +static const int kDumpLockSleep = 60000; + +static bool tryLock(Mutex& mutex) +{ + bool locked = false; + for (int i = 0; i < kDumpLockRetries; ++i) { + if (mutex.tryLock() == NO_ERROR) { + locked = true; + break; + } + usleep(kDumpLockSleep); + } + return locked; +} + +status_t SoundTriggerHwService::dump(int fd, const Vector<String16>& args __unused) { + String8 result; + if (checkCallingPermission(String16("android.permission.DUMP")) == false) { + result.appendFormat("Permission Denial: can't dump SoundTriggerHwService"); + write(fd, result.string(), result.size()); + } else { + bool locked = tryLock(mServiceLock); + // failed to lock - SoundTriggerHwService is probably deadlocked + if (!locked) { + result.append("SoundTriggerHwService may be deadlocked\n"); + write(fd, result.string(), result.size()); + } + + if (locked) mServiceLock.unlock(); + } + return NO_ERROR; +} + +status_t SoundTriggerHwService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + return BnSoundTriggerHwService::onTransact(code, data, reply, flags); +} + + +// static +void SoundTriggerHwService::recognitionCallback(struct sound_trigger_recognition_event *event, + void *cookie) +{ + Module *module = (Module *)cookie; + if (module == NULL) { + return; + } + sp<SoundTriggerHwService> service = module->service().promote(); + if (service == 0) { + return; + } + + service->sendRecognitionEvent(event, module); +} + +sp<IMemory> SoundTriggerHwService::prepareRecognitionEvent_l( + struct sound_trigger_recognition_event *event) +{ + sp<IMemory> eventMemory; + + //sanitize event + switch (event->type) { + case SOUND_MODEL_TYPE_KEYPHRASE: + ALOGW_IF(event->data_size != 0 && event->data_offset != + sizeof(struct sound_trigger_phrase_recognition_event), + "prepareRecognitionEvent_l(): invalid data offset %u for keyphrase event type", + event->data_offset); + event->data_offset = sizeof(struct sound_trigger_phrase_recognition_event); + break; + case SOUND_MODEL_TYPE_UNKNOWN: + ALOGW_IF(event->data_size != 0 && event->data_offset != + sizeof(struct sound_trigger_recognition_event), + "prepareRecognitionEvent_l(): invalid data offset %u for unknown event type", + event->data_offset); + event->data_offset = sizeof(struct sound_trigger_recognition_event); + break; + default: + return eventMemory; + } + + size_t size = event->data_offset + event->data_size; + eventMemory = mMemoryDealer->allocate(size); + if (eventMemory == 0 || eventMemory->pointer() == NULL) { + eventMemory.clear(); + return eventMemory; + } + memcpy(eventMemory->pointer(), event, size); + + return eventMemory; +} + +void SoundTriggerHwService::sendRecognitionEvent(struct sound_trigger_recognition_event *event, + Module *module) + { + AutoMutex lock(mServiceLock); + if (module == NULL) { + return; + } + sp<IMemory> eventMemory = prepareRecognitionEvent_l(event); + if (eventMemory == 0) { + return; + } + sp<Module> strongModule; + for (size_t i = 0; i < mModules.size(); i++) { + if (mModules.valueAt(i).get() == module) { + strongModule = mModules.valueAt(i); + break; + } + } + if (strongModule == 0) { + return; + } + + sendCallbackEvent_l(new CallbackEvent(CallbackEvent::TYPE_RECOGNITION, + eventMemory, strongModule)); +} + +// static +void SoundTriggerHwService::soundModelCallback(struct sound_trigger_model_event *event, + void *cookie) +{ + Module *module = (Module *)cookie; + if (module == NULL) { + return; + } + sp<SoundTriggerHwService> service = module->service().promote(); + if (service == 0) { + return; + } + + service->sendSoundModelEvent(event, module); +} + +sp<IMemory> SoundTriggerHwService::prepareSoundModelEvent_l(struct sound_trigger_model_event *event) +{ + sp<IMemory> eventMemory; + + size_t size = event->data_offset + event->data_size; + eventMemory = mMemoryDealer->allocate(size); + if (eventMemory == 0 || eventMemory->pointer() == NULL) { + eventMemory.clear(); + return eventMemory; + } + memcpy(eventMemory->pointer(), event, size); + + return eventMemory; +} + +void SoundTriggerHwService::sendSoundModelEvent(struct sound_trigger_model_event *event, + Module *module) +{ + AutoMutex lock(mServiceLock); + sp<IMemory> eventMemory = prepareSoundModelEvent_l(event); + if (eventMemory == 0) { + return; + } + sp<Module> strongModule; + for (size_t i = 0; i < mModules.size(); i++) { + if (mModules.valueAt(i).get() == module) { + strongModule = mModules.valueAt(i); + break; + } + } + if (strongModule == 0) { + return; + } + sendCallbackEvent_l(new CallbackEvent(CallbackEvent::TYPE_SOUNDMODEL, + eventMemory, strongModule)); +} + + +sp<IMemory> SoundTriggerHwService::prepareServiceStateEvent_l(sound_trigger_service_state_t state) +{ + sp<IMemory> eventMemory; + + size_t size = sizeof(sound_trigger_service_state_t); + eventMemory = mMemoryDealer->allocate(size); + if (eventMemory == 0 || eventMemory->pointer() == NULL) { + eventMemory.clear(); + return eventMemory; + } + *((sound_trigger_service_state_t *)eventMemory->pointer()) = state; + return eventMemory; +} + +// call with mServiceLock held +void SoundTriggerHwService::sendServiceStateEvent_l(sound_trigger_service_state_t state, + Module *module) +{ + sp<IMemory> eventMemory = prepareServiceStateEvent_l(state); + if (eventMemory == 0) { + return; + } + sp<Module> strongModule; + for (size_t i = 0; i < mModules.size(); i++) { + if (mModules.valueAt(i).get() == module) { + strongModule = mModules.valueAt(i); + break; + } + } + if (strongModule == 0) { + return; + } + sendCallbackEvent_l(new CallbackEvent(CallbackEvent::TYPE_SERVICE_STATE, + eventMemory, strongModule)); +} + +// call with mServiceLock held +void SoundTriggerHwService::sendCallbackEvent_l(const sp<CallbackEvent>& event) +{ + mCallbackThread->sendCallbackEvent(event); +} + +void SoundTriggerHwService::onCallbackEvent(const sp<CallbackEvent>& event) +{ + ALOGV("onCallbackEvent"); + sp<Module> module; + { + AutoMutex lock(mServiceLock); + module = event->mModule.promote(); + if (module == 0) { + return; + } + } + module->onCallbackEvent(event); + { + AutoMutex lock(mServiceLock); + // clear now to execute with mServiceLock locked + event->mMemory.clear(); + } +} + +#undef LOG_TAG +#define LOG_TAG "SoundTriggerHwService::CallbackThread" + +SoundTriggerHwService::CallbackThread::CallbackThread(const wp<SoundTriggerHwService>& service) + : mService(service) +{ +} + +SoundTriggerHwService::CallbackThread::~CallbackThread() +{ + while (!mEventQueue.isEmpty()) { + mEventQueue[0]->mMemory.clear(); + mEventQueue.removeAt(0); + } +} + +void SoundTriggerHwService::CallbackThread::onFirstRef() +{ + run("soundTrigger cbk", ANDROID_PRIORITY_URGENT_AUDIO); +} + +bool SoundTriggerHwService::CallbackThread::threadLoop() +{ + while (!exitPending()) { + sp<CallbackEvent> event; + sp<SoundTriggerHwService> service; + { + Mutex::Autolock _l(mCallbackLock); + while (mEventQueue.isEmpty() && !exitPending()) { + ALOGV("CallbackThread::threadLoop() sleep"); + mCallbackCond.wait(mCallbackLock); + ALOGV("CallbackThread::threadLoop() wake up"); + } + if (exitPending()) { + break; + } + event = mEventQueue[0]; + mEventQueue.removeAt(0); + service = mService.promote(); + } + if (service != 0) { + service->onCallbackEvent(event); + } + } + return false; +} + +void SoundTriggerHwService::CallbackThread::exit() +{ + Mutex::Autolock _l(mCallbackLock); + requestExit(); + mCallbackCond.broadcast(); +} + +void SoundTriggerHwService::CallbackThread::sendCallbackEvent( + const sp<SoundTriggerHwService::CallbackEvent>& event) +{ + AutoMutex lock(mCallbackLock); + mEventQueue.add(event); + mCallbackCond.signal(); +} + +SoundTriggerHwService::CallbackEvent::CallbackEvent(event_type type, sp<IMemory> memory, + wp<Module> module) + : mType(type), mMemory(memory), mModule(module) +{ +} + +SoundTriggerHwService::CallbackEvent::~CallbackEvent() +{ +} + + +#undef LOG_TAG +#define LOG_TAG "SoundTriggerHwService::Module" + +SoundTriggerHwService::Module::Module(const sp<SoundTriggerHwService>& service, + sound_trigger_hw_device* hwDevice, + sound_trigger_module_descriptor descriptor, + const sp<ISoundTriggerClient>& client) + : mService(service), mHwDevice(hwDevice), mDescriptor(descriptor), + mClient(client), mServiceState(SOUND_TRIGGER_STATE_NO_INIT) +{ +} + +SoundTriggerHwService::Module::~Module() { +} + +void SoundTriggerHwService::Module::detach() { + ALOGV("detach()"); + if (!captureHotwordAllowed()) { + return; + } + { + AutoMutex lock(mLock); + for (size_t i = 0; i < mModels.size(); i++) { + sp<Model> model = mModels.valueAt(i); + ALOGV("detach() unloading model %d", model->mHandle); + if (model->mState == Model::STATE_ACTIVE) { + mHwDevice->stop_recognition(mHwDevice, model->mHandle); + } + mHwDevice->unload_sound_model(mHwDevice, model->mHandle); + } + mModels.clear(); + } + if (mClient != 0) { + mClient->asBinder()->unlinkToDeath(this); + } + sp<SoundTriggerHwService> service = mService.promote(); + if (service == 0) { + return; + } + service->detachModule(this); +} + +status_t SoundTriggerHwService::Module::loadSoundModel(const sp<IMemory>& modelMemory, + sound_model_handle_t *handle) +{ + ALOGV("loadSoundModel() handle"); + if (!captureHotwordAllowed()) { + return PERMISSION_DENIED; + } + + if (modelMemory == 0 || modelMemory->pointer() == NULL) { + ALOGE("loadSoundModel() modelMemory is 0 or has NULL pointer()"); + return BAD_VALUE; + } + struct sound_trigger_sound_model *sound_model = + (struct sound_trigger_sound_model *)modelMemory->pointer(); + + AutoMutex lock(mLock); + status_t status = mHwDevice->load_sound_model(mHwDevice, + sound_model, + SoundTriggerHwService::soundModelCallback, + this, + handle); + if (status != NO_ERROR) { + return status; + } + audio_session_t session; + audio_io_handle_t ioHandle; + audio_devices_t device; + + status = AudioSystem::acquireSoundTriggerSession(&session, &ioHandle, &device); + if (status != NO_ERROR) { + return status; + } + + sp<Model> model = new Model(*handle, session, ioHandle, device, sound_model->type); + mModels.replaceValueFor(*handle, model); + + return status; +} + +status_t SoundTriggerHwService::Module::unloadSoundModel(sound_model_handle_t handle) +{ + ALOGV("unloadSoundModel() model handle %d", handle); + if (!captureHotwordAllowed()) { + return PERMISSION_DENIED; + } + + AutoMutex lock(mLock); + ssize_t index = mModels.indexOfKey(handle); + if (index < 0) { + return BAD_VALUE; + } + sp<Model> model = mModels.valueAt(index); + mModels.removeItem(handle); + if (model->mState == Model::STATE_ACTIVE) { + mHwDevice->stop_recognition(mHwDevice, model->mHandle); + } + AudioSystem::releaseSoundTriggerSession(model->mCaptureSession); + return mHwDevice->unload_sound_model(mHwDevice, handle); +} + +status_t SoundTriggerHwService::Module::startRecognition(sound_model_handle_t handle, + const sp<IMemory>& dataMemory) +{ + ALOGV("startRecognition() model handle %d", handle); + if (!captureHotwordAllowed()) { + return PERMISSION_DENIED; + } + + if (dataMemory != 0 && dataMemory->pointer() == NULL) { + ALOGE("startRecognition() dataMemory is non-0 but has NULL pointer()"); + return BAD_VALUE; + + } + AutoMutex lock(mLock); + if (mServiceState == SOUND_TRIGGER_STATE_DISABLED) { + return INVALID_OPERATION; + } + sp<Model> model = getModel(handle); + if (model == 0) { + return BAD_VALUE; + } + if ((dataMemory == 0) || + (dataMemory->size() < sizeof(struct sound_trigger_recognition_config))) { + return BAD_VALUE; + } + + if (model->mState == Model::STATE_ACTIVE) { + return INVALID_OPERATION; + } + + struct sound_trigger_recognition_config *config = + (struct sound_trigger_recognition_config *)dataMemory->pointer(); + + //TODO: get capture handle and device from audio policy service + config->capture_handle = model->mCaptureIOHandle; + config->capture_device = model->mCaptureDevice; + status_t status = mHwDevice->start_recognition(mHwDevice, handle, config, + SoundTriggerHwService::recognitionCallback, + this); + + if (status == NO_ERROR) { + model->mState = Model::STATE_ACTIVE; + model->mConfig = *config; + } + + return status; +} + +status_t SoundTriggerHwService::Module::stopRecognition(sound_model_handle_t handle) +{ + ALOGV("stopRecognition() model handle %d", handle); + if (!captureHotwordAllowed()) { + return PERMISSION_DENIED; + } + + AutoMutex lock(mLock); + sp<Model> model = getModel(handle); + if (model == 0) { + return BAD_VALUE; + } + + if (model->mState != Model::STATE_ACTIVE) { + return INVALID_OPERATION; + } + mHwDevice->stop_recognition(mHwDevice, handle); + model->mState = Model::STATE_IDLE; + return NO_ERROR; +} + + +void SoundTriggerHwService::Module::onCallbackEvent(const sp<CallbackEvent>& event) +{ + ALOGV("onCallbackEvent type %d", event->mType); + + sp<IMemory> eventMemory = event->mMemory; + + if (eventMemory == 0 || eventMemory->pointer() == NULL) { + return; + } + if (mClient == 0) { + ALOGI("%s mClient == 0", __func__); + return; + } + + switch (event->mType) { + case CallbackEvent::TYPE_RECOGNITION: { + struct sound_trigger_recognition_event *recognitionEvent = + (struct sound_trigger_recognition_event *)eventMemory->pointer(); + sp<ISoundTriggerClient> client; + { + AutoMutex lock(mLock); + sp<Model> model = getModel(recognitionEvent->model); + if (model == 0) { + ALOGW("%s model == 0", __func__); + return; + } + if (model->mState != Model::STATE_ACTIVE) { + ALOGV("onCallbackEvent model->mState %d != Model::STATE_ACTIVE", model->mState); + return; + } + + recognitionEvent->capture_session = model->mCaptureSession; + model->mState = Model::STATE_IDLE; + client = mClient; + } + if (client != 0) { + client->onRecognitionEvent(eventMemory); + } + } break; + case CallbackEvent::TYPE_SOUNDMODEL: { + struct sound_trigger_model_event *soundmodelEvent = + (struct sound_trigger_model_event *)eventMemory->pointer(); + sp<ISoundTriggerClient> client; + { + AutoMutex lock(mLock); + sp<Model> model = getModel(soundmodelEvent->model); + if (model == 0) { + ALOGW("%s model == 0", __func__); + return; + } + client = mClient; + } + if (client != 0) { + client->onSoundModelEvent(eventMemory); + } + } break; + case CallbackEvent::TYPE_SERVICE_STATE: { + sp<ISoundTriggerClient> client; + { + AutoMutex lock(mLock); + client = mClient; + } + if (client != 0) { + client->onServiceStateChange(eventMemory); + } + } break; + default: + LOG_ALWAYS_FATAL("onCallbackEvent unknown event type %d", event->mType); + } +} + +sp<SoundTriggerHwService::Model> SoundTriggerHwService::Module::getModel( + sound_model_handle_t handle) +{ + sp<Model> model; + ssize_t index = mModels.indexOfKey(handle); + if (index >= 0) { + model = mModels.valueAt(index); + } + return model; +} + +void SoundTriggerHwService::Module::binderDied( + const wp<IBinder> &who __unused) { + ALOGW("client binder died for module %d", mDescriptor.handle); + detach(); +} + +// Called with mServiceLock held +void SoundTriggerHwService::Module::setCaptureState_l(bool active) +{ + ALOGV("Module::setCaptureState_l %d", active); + sp<SoundTriggerHwService> service; + sound_trigger_service_state_t state; + + Vector< sp<IMemory> > events; + { + AutoMutex lock(mLock); + state = (active && !mDescriptor.properties.concurrent_capture) ? + SOUND_TRIGGER_STATE_DISABLED : SOUND_TRIGGER_STATE_ENABLED; + + if (state == mServiceState) { + return; + } + + mServiceState = state; + + service = mService.promote(); + if (service == 0) { + return; + } + + if (state == SOUND_TRIGGER_STATE_ENABLED) { + goto exit; + } + + for (size_t i = 0; i < mModels.size(); i++) { + sp<Model> model = mModels.valueAt(i); + if (model->mState == Model::STATE_ACTIVE) { + mHwDevice->stop_recognition(mHwDevice, model->mHandle); + // keep model in ACTIVE state so that event is processed by onCallbackEvent() + struct sound_trigger_phrase_recognition_event phraseEvent; + switch (model->mType) { + case SOUND_MODEL_TYPE_KEYPHRASE: + phraseEvent.num_phrases = model->mConfig.num_phrases; + for (size_t i = 0; i < phraseEvent.num_phrases; i++) { + phraseEvent.phrase_extras[i] = model->mConfig.phrases[i]; + } + break; + case SOUND_MODEL_TYPE_UNKNOWN: + default: + break; + } + phraseEvent.common.status = RECOGNITION_STATUS_ABORT; + phraseEvent.common.type = model->mType; + phraseEvent.common.model = model->mHandle; + phraseEvent.common.data_size = 0; + sp<IMemory> eventMemory = service->prepareRecognitionEvent_l(&phraseEvent.common); + if (eventMemory != 0) { + events.add(eventMemory); + } + } + } + } + + for (size_t i = 0; i < events.size(); i++) { + service->sendCallbackEvent_l(new CallbackEvent(CallbackEvent::TYPE_RECOGNITION, events[i], + this)); + } + +exit: + service->sendServiceStateEvent_l(state, this); +} + + +SoundTriggerHwService::Model::Model(sound_model_handle_t handle, audio_session_t session, + audio_io_handle_t ioHandle, audio_devices_t device, + sound_trigger_sound_model_type_t type) : + mHandle(handle), mState(STATE_IDLE), mCaptureSession(session), + mCaptureIOHandle(ioHandle), mCaptureDevice(device), mType(type) +{ + +} + +status_t SoundTriggerHwService::Module::dump(int fd __unused, + const Vector<String16>& args __unused) { + String8 result; + return NO_ERROR; +} + +}; // namespace android diff --git a/services/soundtrigger/SoundTriggerHwService.h b/services/soundtrigger/SoundTriggerHwService.h new file mode 100644 index 0000000..d05dacd --- /dev/null +++ b/services/soundtrigger/SoundTriggerHwService.h @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2008 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_HARDWARE_SOUNDTRIGGER_HAL_SERVICE_H +#define ANDROID_HARDWARE_SOUNDTRIGGER_HAL_SERVICE_H + +#include <utils/Vector.h> +//#include <binder/AppOpsManager.h> +#include <binder/MemoryDealer.h> +#include <binder/BinderService.h> +#include <binder/IAppOpsCallback.h> +#include <soundtrigger/ISoundTriggerHwService.h> +#include <soundtrigger/ISoundTrigger.h> +#include <soundtrigger/ISoundTriggerClient.h> +#include <system/sound_trigger.h> +#include <hardware/sound_trigger.h> + +namespace android { + +class MemoryHeapBase; + +class SoundTriggerHwService : + public BinderService<SoundTriggerHwService>, + public BnSoundTriggerHwService +{ + friend class BinderService<SoundTriggerHwService>; +public: + class Module; + + static char const* getServiceName() { return "media.sound_trigger_hw"; } + + SoundTriggerHwService(); + virtual ~SoundTriggerHwService(); + + // ISoundTriggerHwService + virtual status_t listModules(struct sound_trigger_module_descriptor *modules, + uint32_t *numModules); + + virtual status_t attach(const sound_trigger_module_handle_t handle, + const sp<ISoundTriggerClient>& client, + sp<ISoundTrigger>& module); + + virtual status_t setCaptureState(bool active); + + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); + + virtual status_t dump(int fd, const Vector<String16>& args); + + class Model : public RefBase { + public: + + enum { + STATE_IDLE, + STATE_ACTIVE + }; + + Model(sound_model_handle_t handle, audio_session_t session, audio_io_handle_t ioHandle, + audio_devices_t device, sound_trigger_sound_model_type_t type); + ~Model() {} + + sound_model_handle_t mHandle; + int mState; + audio_session_t mCaptureSession; + audio_io_handle_t mCaptureIOHandle; + audio_devices_t mCaptureDevice; + sound_trigger_sound_model_type_t mType; + struct sound_trigger_recognition_config mConfig; + }; + + class CallbackEvent : public RefBase { + public: + typedef enum { + TYPE_RECOGNITION, + TYPE_SOUNDMODEL, + TYPE_SERVICE_STATE, + } event_type; + CallbackEvent(event_type type, sp<IMemory> memory, wp<Module> module); + + virtual ~CallbackEvent(); + + event_type mType; + sp<IMemory> mMemory; + wp<Module> mModule; + }; + + class Module : public virtual RefBase, + public BnSoundTrigger, + public IBinder::DeathRecipient { + public: + + Module(const sp<SoundTriggerHwService>& service, + sound_trigger_hw_device* hwDevice, + sound_trigger_module_descriptor descriptor, + const sp<ISoundTriggerClient>& client); + + virtual ~Module(); + + virtual void detach(); + + virtual status_t loadSoundModel(const sp<IMemory>& modelMemory, + sound_model_handle_t *handle); + + virtual status_t unloadSoundModel(sound_model_handle_t handle); + + virtual status_t startRecognition(sound_model_handle_t handle, + const sp<IMemory>& dataMemory); + virtual status_t stopRecognition(sound_model_handle_t handle); + + virtual status_t dump(int fd, const Vector<String16>& args); + + + sound_trigger_hw_device *hwDevice() const { return mHwDevice; } + struct sound_trigger_module_descriptor descriptor() { return mDescriptor; } + void setClient(sp<ISoundTriggerClient> client) { mClient = client; } + void clearClient() { mClient.clear(); } + sp<ISoundTriggerClient> client() const { return mClient; } + wp<SoundTriggerHwService> service() const { return mService; } + + void onCallbackEvent(const sp<CallbackEvent>& event); + + sp<Model> getModel(sound_model_handle_t handle); + + void setCaptureState_l(bool active); + + // IBinder::DeathRecipient implementation + virtual void binderDied(const wp<IBinder> &who); + + private: + + Mutex mLock; + wp<SoundTriggerHwService> mService; + struct sound_trigger_hw_device* mHwDevice; + struct sound_trigger_module_descriptor mDescriptor; + sp<ISoundTriggerClient> mClient; + DefaultKeyedVector< sound_model_handle_t, sp<Model> > mModels; + sound_trigger_service_state_t mServiceState; + }; // class Module + + class CallbackThread : public Thread { + public: + + CallbackThread(const wp<SoundTriggerHwService>& service); + + virtual ~CallbackThread(); + + // Thread virtuals + virtual bool threadLoop(); + + // RefBase + virtual void onFirstRef(); + + void exit(); + void sendCallbackEvent(const sp<CallbackEvent>& event); + + private: + wp<SoundTriggerHwService> mService; + Condition mCallbackCond; + Mutex mCallbackLock; + Vector< sp<CallbackEvent> > mEventQueue; + }; + + void detachModule(sp<Module> module); + + static void recognitionCallback(struct sound_trigger_recognition_event *event, void *cookie); + sp<IMemory> prepareRecognitionEvent_l(struct sound_trigger_recognition_event *event); + void sendRecognitionEvent(struct sound_trigger_recognition_event *event, Module *module); + + static void soundModelCallback(struct sound_trigger_model_event *event, void *cookie); + sp<IMemory> prepareSoundModelEvent_l(struct sound_trigger_model_event *event); + void sendSoundModelEvent(struct sound_trigger_model_event *event, Module *module); + + sp<IMemory> prepareServiceStateEvent_l(sound_trigger_service_state_t state); + void sendServiceStateEvent_l(sound_trigger_service_state_t state, Module *module); + + void sendCallbackEvent_l(const sp<CallbackEvent>& event); + void onCallbackEvent(const sp<CallbackEvent>& event); + +private: + + virtual void onFirstRef(); + + Mutex mServiceLock; + volatile int32_t mNextUniqueId; + DefaultKeyedVector< sound_trigger_module_handle_t, sp<Module> > mModules; + sp<CallbackThread> mCallbackThread; + sp<MemoryDealer> mMemoryDealer; + bool mCaptureState; +}; + +} // namespace android + +#endif // ANDROID_HARDWARE_SOUNDTRIGGER_HAL_SERVICE_H |