diff options
Diffstat (limited to 'services')
80 files changed, 15237 insertions, 6088 deletions
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index 8473fabf..28999533 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -13,40 +13,14 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) -LOCAL_SRC_FILES := \ - AudioBufferProviderSource.cpp \ - AudioStreamOutSink.cpp \ - AudioStreamInSource.cpp \ - NBAIO.cpp \ - MonoPipe.cpp \ - MonoPipeReader.cpp \ - Pipe.cpp \ - PipeReader.cpp \ - roundup.c \ - SourceAudioBufferProvider.cpp - -# libsndfile license is incompatible; uncomment to use for local debug only -#LOCAL_SRC_FILES += LibsndfileSink.cpp LibsndfileSource.cpp -#LOCAL_C_INCLUDES += path/to/libsndfile/src -#LOCAL_STATIC_LIBRARIES += libsndfile - -# uncomment for systrace -# LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_AUDIO - -LOCAL_MODULE := libnbaio - -include $(BUILD_STATIC_LIBRARY) - -include $(CLEAR_VARS) - LOCAL_SRC_FILES:= \ AudioFlinger.cpp \ AudioMixer.cpp.arm \ AudioResampler.cpp.arm \ AudioPolicyService.cpp \ - ServiceUtilities.cpp -# AudioResamplerSinc.cpp.arm -# AudioResamplerCubic.cpp.arm + ServiceUtilities.cpp \ + AudioResamplerCubic.cpp.arm \ + AudioResamplerSinc.cpp.arm LOCAL_SRC_FILES += StateQueue.cpp @@ -66,6 +40,7 @@ LOCAL_SHARED_LIBRARIES := \ libbinder \ libmedia \ libmedia_native \ + libnbaio \ libhardware \ libhardware_legacy \ libeffects \ @@ -74,7 +49,6 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_STATIC_LIBRARIES := \ libscheduling_policy \ - libnbaio \ libcpustats \ libmedia_helper @@ -89,7 +63,7 @@ LOCAL_CFLAGS += -DFAST_MIXER_STATISTICS LOCAL_CFLAGS += -DSTATE_QUEUE_INSTANTIATIONS='"StateQueueInstantiations.cpp"' -LOCAL_CFLAGS += -DHAVE_REQUEST_PRIORITY -UFAST_TRACKS_AT_NON_NATIVE_SAMPLE_RATE -USOAKER +LOCAL_CFLAGS += -UFAST_TRACKS_AT_NON_NATIVE_SAMPLE_RATE # uncomment for systrace # LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_AUDIO @@ -99,7 +73,32 @@ LOCAL_CFLAGS += -DHAVE_REQUEST_PRIORITY -UFAST_TRACKS_AT_NON_NATIVE_SAMPLE_RATE # LOCAL_CFLAGS += -DTEE_SINK_FRAMES=0x200000 # uncomment to enable the audio watchdog -LOCAL_SRC_FILES += AudioWatchdog.cpp -LOCAL_CFLAGS += -DAUDIO_WATCHDOG +# LOCAL_SRC_FILES += AudioWatchdog.cpp +# LOCAL_CFLAGS += -DAUDIO_WATCHDOG include $(BUILD_SHARED_LIBRARY) + +# +# build audio resampler test tool +# +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + test-resample.cpp \ + AudioResampler.cpp.arm \ + AudioResamplerCubic.cpp.arm \ + AudioResamplerSinc.cpp.arm + +LOCAL_SHARED_LIBRARIES := \ + libdl \ + libcutils \ + libutils + +LOCAL_MODULE:= test-resample + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_EXECUTABLE) + + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/services/audioflinger/AudioBufferProvider.h b/services/audioflinger/AudioBufferProvider.h deleted file mode 100644 index 43e4de75..00000000 --- a/services/audioflinger/AudioBufferProvider.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2007 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_BUFFER_PROVIDER_H -#define ANDROID_AUDIO_BUFFER_PROVIDER_H - -#include <utils/Errors.h> - -namespace android { -// ---------------------------------------------------------------------------- - -class AudioBufferProvider -{ -public: - - struct Buffer { - Buffer() : raw(NULL), frameCount(0) { } - union { - void* raw; - short* i16; - int8_t* i8; - }; - size_t frameCount; - }; - - virtual ~AudioBufferProvider() {} - - // value representing an invalid presentation timestamp - static const int64_t kInvalidPTS = 0x7FFFFFFFFFFFFFFFLL; // <stdint.h> is too painful - - // pts is the local time when the next sample yielded by getNextBuffer - // will be rendered. - // Pass kInvalidPTS if the PTS is unknown or not applicable. - virtual status_t getNextBuffer(Buffer* buffer, int64_t pts = kInvalidPTS) = 0; - - virtual void releaseBuffer(Buffer* buffer) = 0; -}; - -// ---------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_AUDIO_BUFFER_PROVIDER_H diff --git a/services/audioflinger/AudioBufferProviderSource.cpp b/services/audioflinger/AudioBufferProviderSource.cpp deleted file mode 100644 index 43421719..00000000 --- a/services/audioflinger/AudioBufferProviderSource.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "AudioBufferProviderSource" -//#define LOG_NDEBUG 0 - -#include <cutils/compiler.h> -#include <utils/Log.h> -#include "AudioBufferProviderSource.h" - -namespace android { - -AudioBufferProviderSource::AudioBufferProviderSource(AudioBufferProvider *provider, - NBAIO_Format format) : - NBAIO_Source(format), mProvider(provider), mConsumed(0) -{ - ALOG_ASSERT(provider != NULL); - ALOG_ASSERT(format != Format_Invalid); -} - -AudioBufferProviderSource::~AudioBufferProviderSource() -{ - if (mBuffer.raw != NULL) { - mProvider->releaseBuffer(&mBuffer); - } -} - -ssize_t AudioBufferProviderSource::availableToRead() -{ - if (CC_UNLIKELY(!mNegotiated)) { - return NEGOTIATE; - } - return mBuffer.raw != NULL ? mBuffer.frameCount - mConsumed : 0; -} - -ssize_t AudioBufferProviderSource::read(void *buffer, size_t count) -{ - if (CC_UNLIKELY(!mNegotiated)) { - return NEGOTIATE; - } - if (CC_UNLIKELY(mBuffer.raw == NULL)) { - mBuffer.frameCount = count; - status_t status = mProvider->getNextBuffer(&mBuffer, AudioBufferProvider::kInvalidPTS); - if (status != OK) { - return status == NOT_ENOUGH_DATA ? (ssize_t) WOULD_BLOCK : (ssize_t) status; - } - ALOG_ASSERT(mBuffer.raw != NULL); - // mConsumed is 0 either from constructor or after releaseBuffer() - } - size_t available = mBuffer.frameCount - mConsumed; - if (CC_UNLIKELY(count > available)) { - count = available; - } - // count could be zero, either because count was zero on entry or - // available is zero, but both are unlikely so don't check for that - memcpy(buffer, (char *) mBuffer.raw + (mConsumed << mBitShift), count << mBitShift); - if (CC_UNLIKELY((mConsumed += count) >= mBuffer.frameCount)) { - mProvider->releaseBuffer(&mBuffer); - mBuffer.raw = NULL; - mConsumed = 0; - } - mFramesRead += count; - // For better responsiveness with large values of count, - // return a short count rather than continuing with next buffer. - // This gives the caller a chance to interpolate other actions. - return count; -} - -ssize_t AudioBufferProviderSource::readVia(readVia_t via, size_t total, void *user, size_t block) -{ - if (CC_UNLIKELY(!mNegotiated)) { - return NEGOTIATE; - } - if (CC_UNLIKELY(block == 0)) { - block = ~0; - } - for (size_t accumulator = 0; ; ) { - ALOG_ASSERT(accumulator <= total); - size_t count = total - accumulator; - if (CC_UNLIKELY(count == 0)) { - return accumulator; - } - if (CC_LIKELY(count > block)) { - count = block; - } - // 1 <= count <= block - if (CC_UNLIKELY(mBuffer.raw == NULL)) { - mBuffer.frameCount = count; - status_t status = mProvider->getNextBuffer(&mBuffer, AudioBufferProvider::kInvalidPTS); - if (CC_LIKELY(status == OK)) { - ALOG_ASSERT(mBuffer.raw != NULL && mBuffer.frameCount <= count); - // mConsumed is 0 either from constructor or after releaseBuffer() - continue; - } - // FIXME simplify logic - does the initial count and block checks again for no reason; - // don't you just want to fall through to the size_t available line? - if (CC_LIKELY(status == NOT_ENOUGH_DATA)) { - status = WOULD_BLOCK; - } - return accumulator > 0 ? accumulator : (ssize_t) status; - } - size_t available = mBuffer.frameCount - mConsumed; - if (CC_UNLIKELY(count > available)) { - count = available; - } - if (CC_LIKELY(count > 0)) { - ssize_t ret = via(user, (char *) mBuffer.raw + (mConsumed << mBitShift), count); - if (CC_UNLIKELY(ret <= 0)) { - if (CC_LIKELY(accumulator > 0)) { - return accumulator; - } - return ret; - } - ALOG_ASSERT((size_t) ret <= count); - mFramesRead += ret; - accumulator += ret; - if (CC_LIKELY((mConsumed += ret) < mBuffer.frameCount)) { - continue; - } - } - mProvider->releaseBuffer(&mBuffer); - mBuffer.raw = NULL; - mConsumed = 0; - // don't get next buffer until we really need it - } -} - -} // namespace android diff --git a/services/audioflinger/AudioBufferProviderSource.h b/services/audioflinger/AudioBufferProviderSource.h deleted file mode 100644 index 2b399372..00000000 --- a/services/audioflinger/AudioBufferProviderSource.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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. - */ - -// Implementation of NBAIO_Source that wraps an AudioBufferProvider - -#ifndef ANDROID_AUDIO_BUFFER_PROVIDER_SOURCE_H -#define ANDROID_AUDIO_BUFFER_PROVIDER_SOURCE_H - -#include "NBAIO.h" -#include "AudioBufferProvider.h" - -namespace android { - -class AudioBufferProviderSource : public NBAIO_Source { - -public: - AudioBufferProviderSource(AudioBufferProvider *provider, NBAIO_Format format); - virtual ~AudioBufferProviderSource(); - - // NBAIO_Port interface - - //virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers, - // NBAIO_Format counterOffers[], size_t& numCounterOffers); - //virtual NBAIO_Format format(); - - // NBAIO_Source interface - - //virtual size_t framesRead() const; - //virtual size_t framesOverrun(); - //virtual size_t overruns(); - virtual ssize_t availableToRead(); - virtual ssize_t read(void *buffer, size_t count); - virtual ssize_t readVia(readVia_t via, size_t total, void *user, size_t block); - -private: - AudioBufferProvider * const mProvider; - AudioBufferProvider::Buffer mBuffer; // current buffer - size_t mConsumed; // number of frames consumed so far from current buffer -}; - -} // namespace android - -#endif // ANDROID_AUDIO_BUFFER_PROVIDER_SOURCE_H diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index aab99849..1913b6f8 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -76,20 +76,14 @@ #include "FastMixer.h" // NBAIO implementations -#include "AudioStreamOutSink.h" -#include "MonoPipe.h" -#include "MonoPipeReader.h" -#include "Pipe.h" -#include "PipeReader.h" -#include "SourceAudioBufferProvider.h" - -#ifdef HAVE_REQUEST_PRIORITY -#include "SchedulingPolicyService.h" -#endif +#include <media/nbaio/AudioStreamOutSink.h> +#include <media/nbaio/MonoPipe.h> +#include <media/nbaio/MonoPipeReader.h> +#include <media/nbaio/Pipe.h> +#include <media/nbaio/PipeReader.h> +#include <media/nbaio/SourceAudioBufferProvider.h> -#ifdef SOAKER -#include "Soaker.h" -#endif +#include "SchedulingPolicyService.h" // ---------------------------------------------------------------------------- @@ -167,6 +161,19 @@ static const enum { static uint32_t gScreenState; // incremented by 2 when screen state changes, bit 0 == 1 means "off" // AudioFlinger::setParameters() updates, other threads read w/o lock +// Priorities for requestPriority +static const int kPriorityAudioApp = 2; +static const int kPriorityFastMixer = 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. +// Currently the client uses double-buffering by default, but doesn't tell us about that. +// So for now we just assume that client is double-buffered. +// FIXME It would be better for client to tell AudioFlinger whether it wants double-buffering or +// N-buffering, so AudioFlinger could allocate the right amount of memory. +// See the client's minBufCount and mNotificationFramesAct calculations for details. +static const int kFastTrackMultiplier = 2; + // ---------------------------------------------------------------------------- #ifdef ADD_BATTERY_DATA @@ -216,9 +223,8 @@ out: AudioFlinger::AudioFlinger() : BnAudioFlinger(), mPrimaryHardwareDev(NULL), - mHardwareStatus(AUDIO_HW_IDLE), // see also onFirstRef() + mHardwareStatus(AUDIO_HW_IDLE), mMasterVolume(1.0f), - mMasterVolumeSupportLvl(MVS_NONE), mMasterMute(false), mNextUniqueId(1), mMode(AUDIO_MODE_INVALID), @@ -247,21 +253,17 @@ void AudioFlinger::onFirstRef() } mMode = AUDIO_MODE_NORMAL; - mMasterVolumeSW = 1.0; - mMasterVolume = 1.0; - mHardwareStatus = AUDIO_HW_IDLE; } AudioFlinger::~AudioFlinger() { - while (!mRecordThreads.isEmpty()) { - // closeInput() will remove first entry from mRecordThreads - closeInput(mRecordThreads.keyAt(0)); + // closeInput_nonvirtual() will remove specified entry from mRecordThreads + closeInput_nonvirtual(mRecordThreads.keyAt(0)); } while (!mPlaybackThreads.isEmpty()) { - // closeOutput() will remove first entry from mPlaybackThreads - closeOutput(mPlaybackThreads.keyAt(0)); + // closeOutput_nonvirtual() will remove specified entry from mPlaybackThreads + closeOutput_nonvirtual(mPlaybackThreads.keyAt(0)); } for (size_t i = 0; i < mAudioHwDevs.size(); i++) { @@ -278,7 +280,9 @@ static const char * const audio_interfaces[] = { }; #define ARRAY_SIZE(x) (sizeof((x))/sizeof(((x)[0]))) -audio_hw_device_t* AudioFlinger::findSuitableHwDev_l(audio_module_handle_t module, uint32_t devices) +AudioFlinger::AudioHwDevice* AudioFlinger::findSuitableHwDev_l( + audio_module_handle_t module, + audio_devices_t devices) { // if module is 0, the request comes from an old policy manager and we should load // well known modules @@ -287,24 +291,26 @@ audio_hw_device_t* AudioFlinger::findSuitableHwDev_l(audio_module_handle_t modul for (size_t i = 0; i < ARRAY_SIZE(audio_interfaces); i++) { loadHwModule_l(audio_interfaces[i]); } + // then try to find a module supporting the requested device. + for (size_t i = 0; i < mAudioHwDevs.size(); i++) { + AudioHwDevice *audioHwDevice = mAudioHwDevs.valueAt(i); + audio_hw_device_t *dev = audioHwDevice->hwDevice(); + if ((dev->get_supported_devices != NULL) && + (dev->get_supported_devices(dev) & devices) == devices) + return audioHwDevice; + } } else { // check a match for the requested module handle - AudioHwDevice *audioHwdevice = mAudioHwDevs.valueFor(module); - if (audioHwdevice != NULL) { - return audioHwdevice->hwDevice(); + AudioHwDevice *audioHwDevice = mAudioHwDevs.valueFor(module); + if (audioHwDevice != NULL) { + return audioHwDevice; } } - // then try to find a module supporting the requested device. - for (size_t i = 0; i < mAudioHwDevs.size(); i++) { - audio_hw_device_t *dev = mAudioHwDevs.valueAt(i)->hwDevice(); - if ((dev->get_supported_devices(dev) & devices) == devices) - return dev; - } return NULL; } -status_t AudioFlinger::dumpClients(int fd, const Vector<String16>& args) +void AudioFlinger::dumpClients(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; @@ -327,11 +333,10 @@ status_t AudioFlinger::dumpClients(int fd, const Vector<String16>& args) result.append(buffer); } write(fd, result.string(), result.size()); - return NO_ERROR; } -status_t AudioFlinger::dumpInternals(int fd, const Vector<String16>& args) +void AudioFlinger::dumpInternals(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; @@ -344,10 +349,9 @@ status_t AudioFlinger::dumpInternals(int fd, const Vector<String16>& args) (uint32_t)(mStandbyTimeInNsecs / 1000000)); result.append(buffer); write(fd, result.string(), result.size()); - return NO_ERROR; } -status_t AudioFlinger::dumpPermissionDenial(int fd, const Vector<String16>& args) +void AudioFlinger::dumpPermissionDenial(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; @@ -358,7 +362,6 @@ status_t AudioFlinger::dumpPermissionDenial(int fd, const Vector<String16>& args IPCThreadState::self()->getCallingUid()); result.append(buffer); write(fd, result.string(), result.size()); - return NO_ERROR; } static bool tryLock(Mutex& mutex) @@ -440,7 +443,7 @@ sp<IAudioTrack> AudioFlinger::createTrack( audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, IAudioFlinger::track_flags_t flags, const sp<IMemory>& sharedBuffer, @@ -515,7 +518,7 @@ sp<IAudioTrack> AudioFlinger::createTrack( if (mPendingSyncEvents[i]->triggerSession() == lSessionId) { if (thread->isValidSyncEvent(mPendingSyncEvents[i])) { if (lStatus == NO_ERROR) { - track->setSyncEvent(mPendingSyncEvents[i]); + (void) track->setSyncEvent(mPendingSyncEvents[i]); } else { mPendingSyncEvents[i]->cancel(); } @@ -610,30 +613,27 @@ status_t AudioFlinger::setMasterVolume(float value) return PERMISSION_DENIED; } - float swmv = value; - Mutex::Autolock _l(mLock); + mMasterVolume = value; - // when hw supports master volume, don't scale in sw mixer - if (MVS_NONE != mMasterVolumeSupportLvl) { - for (size_t i = 0; i < mAudioHwDevs.size(); i++) { - AutoMutex lock(mHardwareLock); - audio_hw_device_t *dev = mAudioHwDevs.valueAt(i)->hwDevice(); + // Set master volume in the HALs which support it. + for (size_t i = 0; i < mAudioHwDevs.size(); i++) { + AutoMutex lock(mHardwareLock); + AudioHwDevice *dev = mAudioHwDevs.valueAt(i); - mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; - if (NULL != dev->set_master_volume) { - dev->set_master_volume(dev, value); - } - mHardwareStatus = AUDIO_HW_IDLE; + mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; + if (dev->canSetMasterVolume()) { + dev->hwDevice()->set_master_volume(dev->hwDevice(), value); } - - swmv = 1.0; + mHardwareStatus = AUDIO_HW_IDLE; } - mMasterVolume = value; - mMasterVolumeSW = swmv; + // Now set the master volume in each playback thread. Playback threads + // assigned to HALs which do not have master volume support will apply + // master volume during the mix operation. Threads with HALs which do + // support master volume will simply ignore the setting. for (size_t i = 0; i < mPlaybackThreads.size(); i++) - mPlaybackThreads.valueAt(i)->setMasterVolume(swmv); + mPlaybackThreads.valueAt(i)->setMasterVolume(value); return NO_ERROR; } @@ -656,8 +656,9 @@ status_t AudioFlinger::setMode(audio_mode_t mode) { // scope for the lock AutoMutex lock(mHardwareLock); + audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice(); mHardwareStatus = AUDIO_HW_SET_MODE; - ret = mPrimaryHardwareDev->set_mode(mPrimaryHardwareDev, mode); + ret = dev->set_mode(dev, mode); mHardwareStatus = AUDIO_HW_IDLE; } @@ -684,8 +685,9 @@ status_t AudioFlinger::setMicMute(bool state) } AutoMutex lock(mHardwareLock); + audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice(); mHardwareStatus = AUDIO_HW_SET_MIC_MUTE; - ret = mPrimaryHardwareDev->set_mic_mute(mPrimaryHardwareDev, state); + ret = dev->set_mic_mute(dev, state); mHardwareStatus = AUDIO_HW_IDLE; return ret; } @@ -699,22 +701,44 @@ bool AudioFlinger::getMicMute() const bool state = AUDIO_MODE_INVALID; AutoMutex lock(mHardwareLock); + audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice(); mHardwareStatus = AUDIO_HW_GET_MIC_MUTE; - mPrimaryHardwareDev->get_mic_mute(mPrimaryHardwareDev, &state); + dev->get_mic_mute(dev, &state); mHardwareStatus = AUDIO_HW_IDLE; return state; } status_t AudioFlinger::setMasterMute(bool muted) { + status_t ret = initCheck(); + if (ret != NO_ERROR) { + return ret; + } + // check calling permissions if (!settingsAllowed()) { return PERMISSION_DENIED; } Mutex::Autolock _l(mLock); - // This is an optimization, so PlaybackThread doesn't have to look at the one from AudioFlinger mMasterMute = muted; + + // Set master mute in the HALs which support it. + for (size_t i = 0; i < mAudioHwDevs.size(); i++) { + AutoMutex lock(mHardwareLock); + AudioHwDevice *dev = mAudioHwDevs.valueAt(i); + + mHardwareStatus = AUDIO_HW_SET_MASTER_MUTE; + if (dev->canSetMasterMute()) { + dev->hwDevice()->set_master_mute(dev->hwDevice(), muted); + } + mHardwareStatus = AUDIO_HW_IDLE; + } + + // Now set the master mute in each playback thread. Playback threads + // assigned to HALs which do not have master mute support will apply master + // mute during the mix operation. Threads with HALs which do support master + // mute will simply ignore the setting. for (size_t i = 0; i < mPlaybackThreads.size(); i++) mPlaybackThreads.valueAt(i)->setMasterMute(muted); @@ -727,12 +751,6 @@ float AudioFlinger::masterVolume() const return masterVolume_l(); } -float AudioFlinger::masterVolumeSW() const -{ - Mutex::Autolock _l(mLock); - return masterVolumeSW_l(); -} - bool AudioFlinger::masterMute() const { Mutex::Autolock _l(mLock); @@ -741,23 +759,14 @@ bool AudioFlinger::masterMute() const float AudioFlinger::masterVolume_l() const { - if (MVS_FULL == mMasterVolumeSupportLvl) { - float ret_val; - AutoMutex lock(mHardwareLock); - - mHardwareStatus = AUDIO_HW_GET_MASTER_VOLUME; - ALOG_ASSERT((NULL != mPrimaryHardwareDev) && - (NULL != mPrimaryHardwareDev->get_master_volume), - "can't get master volume"); - - mPrimaryHardwareDev->get_master_volume(mPrimaryHardwareDev, &ret_val); - mHardwareStatus = AUDIO_HW_IDLE; - return ret_val; - } - return mMasterVolume; } +bool AudioFlinger::masterMute_l() const +{ + return mMasterMute; +} + status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value, audio_io_handle_t output) { @@ -876,17 +885,19 @@ status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8& if (mBtNrecIsOff != btNrecIsOff) { for (size_t i = 0; i < mRecordThreads.size(); i++) { sp<RecordThread> thread = mRecordThreads.valueAt(i); - RecordThread::RecordTrack *track = thread->track(); - if (track != NULL) { - audio_devices_t device = (audio_devices_t)( - thread->device() & AUDIO_DEVICE_IN_ALL); - bool suspend = audio_is_bluetooth_sco_device(device) && btNrecIsOff; + audio_devices_t device = thread->inDevice(); + bool suspend = audio_is_bluetooth_sco_device(device) && btNrecIsOff; + // collect all of the thread's session IDs + KeyedVector<int, bool> ids = thread->sessionIds(); + // suspend effects associated with those session IDs + for (size_t j = 0; j < ids.size(); ++j) { + int sessionId = ids.keyAt(j); thread->setEffectSuspended(FX_IID_AEC, suspend, - track->sessionId()); + sessionId); thread->setEffectSuspended(FX_IID_NS, suspend, - track->sessionId()); + sessionId); } } mBtNrecIsOff = btNrecIsOff; @@ -908,7 +919,7 @@ status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8& { Mutex::Autolock _l(mLock); thread = checkPlaybackThread_l(ioHandle); - if (thread == NULL) { + if (thread == 0) { thread = checkRecordThread_l(ioHandle); } else if (thread == primaryPlaybackThread_l()) { // indicate output device change to all input threads for pre processing @@ -964,7 +975,8 @@ String8 AudioFlinger::getParameters(audio_io_handle_t ioHandle, const String8& k return String8(""); } -size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t format, int channelCount) const +size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t format, + audio_channel_mask_t channelMask) const { status_t ret = initCheck(); if (ret != NO_ERROR) { @@ -975,20 +987,17 @@ size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t form mHardwareStatus = AUDIO_HW_GET_INPUT_BUFFER_SIZE; struct audio_config config = { sample_rate: sampleRate, - channel_mask: audio_channel_in_mask_from_count(channelCount), + channel_mask: channelMask, format: format, }; - size_t size = mPrimaryHardwareDev->get_input_buffer_size(mPrimaryHardwareDev, &config); + audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice(); + size_t size = dev->get_input_buffer_size(dev, &config); mHardwareStatus = AUDIO_HW_IDLE; return size; } unsigned int AudioFlinger::getInputFramesLost(audio_io_handle_t ioHandle) const { - if (ioHandle == 0) { - return 0; - } - Mutex::Autolock _l(mLock); RecordThread *recordThread = checkRecordThread_l(ioHandle); @@ -1011,8 +1020,9 @@ status_t AudioFlinger::setVoiceVolume(float value) } AutoMutex lock(mHardwareLock); + audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice(); mHardwareStatus = AUDIO_HW_SET_VOICE_VOLUME; - ret = mPrimaryHardwareDev->set_voice_volume(mPrimaryHardwareDev, value); + ret = dev->set_voice_volume(dev, value); mHardwareStatus = AUDIO_HW_IDLE; return ret; @@ -1053,11 +1063,11 @@ void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client) // 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++) { - mPlaybackThreads.valueAt(i)->sendConfigEvent(AudioSystem::OUTPUT_OPENED); + mPlaybackThreads.valueAt(i)->sendIoConfigEvent(AudioSystem::OUTPUT_OPENED); } for (size_t i = 0; i < mRecordThreads.size(); i++) { - mRecordThreads.valueAt(i)->sendConfigEvent(AudioSystem::INPUT_OPENED); + mRecordThreads.valueAt(i)->sendIoConfigEvent(AudioSystem::INPUT_OPENED); } } } @@ -1124,16 +1134,17 @@ sp<AudioFlinger::PlaybackThread> AudioFlinger::getEffectThread_l(int sessionId, // ---------------------------------------------------------------------------- AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id, - uint32_t device, type_t type) - : Thread(false), + audio_devices_t outDevice, audio_devices_t inDevice, type_t type) + : Thread(false /*canCallJava*/), mType(type), mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mNormalFrameCount(0), // mChannelMask mChannelCount(0), mFrameSize(1), mFormat(AUDIO_FORMAT_INVALID), mParamStatus(NO_ERROR), - mStandby(false), mId(id), - mDevice(device), + mStandby(false), mOutDevice(outDevice), mInDevice(inDevice), + mAudioSource(AUDIO_SOURCE_DEFAULT), mId(id), + // mName will be set by concrete (non-virtual) subclass mDeathRecipient(new PMDeathRecipient(this)) { } @@ -1152,6 +1163,8 @@ AudioFlinger::ThreadBase::~ThreadBase() void AudioFlinger::ThreadBase::exit() { ALOGV("ThreadBase::exit"); + // do any cleanup required for exit to succeed + preExit(); { // This lock prevents the following race in thread (uniprocessor for illustration): // if (!exitPending()) { @@ -1164,7 +1177,7 @@ void AudioFlinger::ThreadBase::exit() // } AutoMutex lock(mLock); requestExit(); - mWaitWorkCV.signal(); + mWaitWorkCV.broadcast(); } // When Thread::requestExitAndWait is made virtual and this method is renamed to // "virtual status_t requestExitAndWait()", replace by "return Thread::requestExitAndWait();" @@ -1191,20 +1204,28 @@ status_t AudioFlinger::ThreadBase::setParameters(const String8& keyValuePairs) return status; } -void AudioFlinger::ThreadBase::sendConfigEvent(int event, int param) +void AudioFlinger::ThreadBase::sendIoConfigEvent(int event, int param) { Mutex::Autolock _l(mLock); - sendConfigEvent_l(event, param); + sendIoConfigEvent_l(event, 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(); } -// sendConfigEvent_l() must be called with ThreadBase::mLock held -void AudioFlinger::ThreadBase::sendConfigEvent_l(int event, int param) +// sendPrioConfigEvent_l() must be called with ThreadBase::mLock held +void AudioFlinger::ThreadBase::sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio) { - ConfigEvent configEvent; - configEvent.mEvent = event; - configEvent.mParam = param; - mConfigEvents.add(configEvent); - ALOGV("sendConfigEvent() num events %d event %d, param %d", mConfigEvents.size(), event, param); + 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(); } @@ -1213,20 +1234,37 @@ void AudioFlinger::ThreadBase::processConfigEvents() mLock.lock(); while (!mConfigEvents.isEmpty()) { ALOGV("processConfigEvents() remaining events %d", mConfigEvents.size()); - ConfigEvent configEvent = mConfigEvents[0]; + 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(); - mAudioFlinger->mLock.lock(); - audioConfigChanged_l(configEvent.mEvent, configEvent.mParam); - mAudioFlinger->mLock.unlock(); + switch(event->type()) { + case CFG_EVENT_PRIO: { + PrioConfigEvent *prioEvent = static_cast<PrioConfigEvent *>(event); + int err = requestPriority(prioEvent->pid(), prioEvent->tid(), prioEvent->prio()); + 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(); } mLock.unlock(); } -status_t AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args) +void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; @@ -1270,10 +1308,8 @@ status_t AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args snprintf(buffer, SIZE, "\n\nPending config events: \n"); result.append(buffer); - snprintf(buffer, SIZE, " Index event param\n"); - result.append(buffer); for (size_t i = 0; i < mConfigEvents.size(); i++) { - snprintf(buffer, SIZE, " %02d %02d %d\n", i, mConfigEvents[i].mEvent, mConfigEvents[i].mParam); + mConfigEvents[i]->dump(buffer, SIZE); result.append(buffer); } result.append("\n"); @@ -1283,10 +1319,9 @@ status_t AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args if (locked) { mLock.unlock(); } - return NO_ERROR; } -status_t AudioFlinger::ThreadBase::dumpEffectChains(int fd, const Vector<String16>& args) +void AudioFlinger::ThreadBase::dumpEffectChains(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; @@ -1301,7 +1336,6 @@ status_t AudioFlinger::ThreadBase::dumpEffectChains(int fd, const Vector<String1 chain->dump(fd, args); } } - return NO_ERROR; } void AudioFlinger::ThreadBase::acquireWakeLock() @@ -1397,8 +1431,8 @@ void AudioFlinger::ThreadBase::checkSuspendOnAddEffectChain_l(const sp<EffectCha return; } - KeyedVector <int, sp<SuspendedSessionDesc> > sessionEffects = - mSuspendedSessions.editValueAt(index); + const KeyedVector <int, sp<SuspendedSessionDesc> >& sessionEffects = + mSuspendedSessions.valueAt(index); for (size_t i = 0; i < sessionEffects.size(); i++) { sp<SuspendedSessionDesc> desc = sessionEffects.valueAt(i); @@ -1424,7 +1458,7 @@ void AudioFlinger::ThreadBase::updateSuspendedSessions_l(const effect_uuid_t *ty if (suspend) { if (index >= 0) { - sessionEffects = mSuspendedSessions.editValueAt(index); + sessionEffects = mSuspendedSessions.valueAt(index); } else { mSuspendedSessions.add(sessionId, sessionEffects); } @@ -1432,7 +1466,7 @@ void AudioFlinger::ThreadBase::updateSuspendedSessions_l(const effect_uuid_t *ty if (index < 0) { return; } - sessionEffects = mSuspendedSessions.editValueAt(index); + sessionEffects = mSuspendedSessions.valueAt(index); } @@ -1449,7 +1483,7 @@ void AudioFlinger::ThreadBase::updateSuspendedSessions_l(const effect_uuid_t *ty } else { desc = new SuspendedSessionDesc(); if (type != NULL) { - memcpy(&desc->mType, type, sizeof(effect_uuid_t)); + desc->mType = *type; } sessionEffects.add(key, desc); ALOGV("updateSuspendedSessions_l() suspend adding effect %08x", key); @@ -1509,18 +1543,12 @@ void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled_l(const sp<EffectModu AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, audio_io_handle_t id, - uint32_t device, + audio_devices_t device, type_t type) - : ThreadBase(audioFlinger, id, device, type), + : ThreadBase(audioFlinger, id, device, AUDIO_DEVICE_NONE, type), mMixBuffer(NULL), mSuspended(0), mBytesWritten(0), - // Assumes constructor is called by AudioFlinger with it's mLock held, - // but it would be safer to explicitly pass initial masterMute as parameter - mMasterMute(audioFlinger->masterMute_l()), // mStreamTypes[] initialized in constructor body mOutput(output), - // Assumes constructor is called by AudioFlinger with it's mLock held, - // but it would be safer to explicitly pass initial masterVolume as parameter - mMasterVolume(audioFlinger->masterVolumeSW_l()), mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false), mMixerStatus(MIXER_IDLE), mMixerStatusIgnoringFastTracks(MIXER_IDLE), @@ -1531,6 +1559,25 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge { snprintf(mName, kNameLength, "AudioOut_%X", id); + // Assumes constructor is called by AudioFlinger with it's mLock held, but + // it would be safer to explicitly pass initial masterVolume/masterMute as + // parameter. + // + // If the HAL we are using has support for master volume or master mute, + // then do not attenuate or mute during mixing (just leave the volume at 1.0 + // and the mute set to false). + mMasterVolume = audioFlinger->masterVolume_l(); + mMasterMute = audioFlinger->masterMute_l(); + if (mOutput && mOutput->audioHwDev) { + if (mOutput->audioHwDev->canSetMasterVolume()) { + mMasterVolume = 1.0; + } + + if (mOutput->audioHwDev->canSetMasterMute()) { + mMasterMute = false; + } + } + readOutputParameters(); // mStreamTypes[AUDIO_STREAM_CNT] is initialized by stream_type_t default constructor @@ -1549,15 +1596,14 @@ AudioFlinger::PlaybackThread::~PlaybackThread() delete [] mMixBuffer; } -status_t AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args) +void AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args) { dumpInternals(fd, args); dumpTracks(fd, args); dumpEffectChains(fd, args); - return NO_ERROR; } -status_t AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& args) +void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; @@ -1605,11 +1651,9 @@ status_t AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16> FastTrackUnderruns underruns = getFastTrackUnderruns(0); fdprintf(fd, "Normal mixer raw underrun counters: partial=%u empty=%u\n", underruns.mBitFields.mPartial, underruns.mBitFields.mEmpty); - - return NO_ERROR; } -status_t AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args) +void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; @@ -1633,8 +1677,6 @@ status_t AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String fdprintf(fd, "Fast track availMask=%#x\n", mFastTrackAvailMask); dumpBase(fd, args); - - return NO_ERROR; } // Thread virtuals @@ -1654,13 +1696,22 @@ void AudioFlinger::PlaybackThread::onFirstRef() run(mName, ANDROID_PRIORITY_URGENT_AUDIO); } +// ThreadBase virtuals +void AudioFlinger::PlaybackThread::preExit() +{ + ALOGV(" preExit()"); + // FIXME this is using hard-coded strings but in the future, this functionality will be + // converted to use audio HAL extensions required to support tunneling + mOutput->stream->common.set_parameters(&mOutput->stream->common, "exiting=1"); +} + // PlaybackThread::createTrack_l() must be called with AudioFlinger::mLock held sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l( const sp<AudioFlinger::Client>& client, audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, const sp<IMemory>& sharedBuffer, int sessionId, @@ -1688,7 +1739,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac ( (tid != -1) && ((frameCount == 0) || - (frameCount >= (int) (mFrameCount * 2))) // * 2 is due to SRC jitter, see below + (frameCount >= (int) (mFrameCount * kFastTrackMultiplier))) ) ) && // PCM data @@ -1709,13 +1760,13 @@ 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 * 2; // FIXME * 2 is due to SRC jitter, should be computed + frameCount = mFrameCount * kFastTrackMultiplier; } 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=%d sampleRate=%d mSampleRate=%d " + "mFrameCount=%d format=%d isLinear=%d channelMask=%#x sampleRate=%d mSampleRate=%d " "hasFastMixer=%d tid=%d fastTrackAvailMask=%#x", isTimed, sharedBuffer.get(), frameCount, mFrameCount, format, audio_is_linear_pcm(format), @@ -1789,7 +1840,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac track = TimedTrack::create(this, client, streamType, sampleRate, format, channelMask, frameCount, sharedBuffer, sessionId); } - if (track == NULL || track->getCblk() == NULL || track->name() < 0) { + if (track == 0 || track->getCblk() == NULL || track->name() < 0) { lStatus = NO_MEMORY; goto Exit; } @@ -1802,20 +1853,14 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac chain->setStrategy(AudioSystem::getStrategyForStream(track->streamType())); chain->incTrackCnt(); } - } -#ifdef HAVE_REQUEST_PRIORITY - if ((flags & IAudioFlinger::TRACK_FAST) && (tid != -1)) { - pid_t callingPid = IPCThreadState::self()->getCallingPid(); - // we don't have CAP_SYS_NICE, nor do we want to have it as it's too powerful, - // so ask activity manager to do this on our behalf - int err = requestPriority(callingPid, tid, 1); - if (err != 0) { - ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d", - 1, callingPid, tid, err); + if ((flags & IAudioFlinger::TRACK_FAST) && (tid != -1)) { + pid_t callingPid = IPCThreadState::self()->getCallingPid(); + // we don't have CAP_SYS_NICE, nor do we want to have it as it's too powerful, + // so ask activity manager to do this on our behalf + sendPrioConfigEvent_l(callingPid, tid, kPriorityAudioApp); } } -#endif lStatus = NO_ERROR; @@ -1857,13 +1902,25 @@ uint32_t AudioFlinger::PlaybackThread::latency_l() const void AudioFlinger::PlaybackThread::setMasterVolume(float value) { Mutex::Autolock _l(mLock); - mMasterVolume = value; + // Don't apply master volume in SW if our HAL can do it for us. + if (mOutput && mOutput->audioHwDev && + mOutput->audioHwDev->canSetMasterVolume()) { + mMasterVolume = 1.0; + } else { + mMasterVolume = value; + } } void AudioFlinger::PlaybackThread::setMasterMute(bool muted) { Mutex::Autolock _l(mLock); - setMasterMute_l(muted); + // Don't apply master mute in SW if our HAL can do it for us. + if (mOutput && mOutput->audioHwDev && + mOutput->audioHwDev->canSetMasterMute()) { + mMasterMute = false; + } else { + mMasterMute = muted; + } } void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value) @@ -2070,10 +2127,20 @@ status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, ui } *halFrames = mBytesWritten / audio_stream_frame_size(&mOutput->stream->common); - return mOutput->stream->get_render_position(mOutput->stream, dspFrames); + if (isSuspended()) { + // return an estimation of rendered frames when the output is suspended + int32_t frames = mBytesWritten - latency_l(); + if (frames < 0) { + frames = 0; + } + *dspFrames = (uint32_t)frames; + return NO_ERROR; + } else { + return mOutput->stream->get_render_position(mOutput->stream, dspFrames); + } } -uint32_t AudioFlinger::PlaybackThread::hasAudioSession(int sessionId) +uint32_t AudioFlinger::PlaybackThread::hasAudioSession(int sessionId) const { Mutex::Autolock _l(mLock); uint32_t result = 0; @@ -2155,7 +2222,7 @@ status_t AudioFlinger::PlaybackThread::setSyncEvent(const sp<SyncEvent>& event) for (size_t i = 0; i < mTracks.size(); ++i) { sp<Track> track = mTracks[i]; if (event->triggerSession() == track->sessionId()) { - track->setSyncEvent(event); + (void) track->setSyncEvent(event); return NO_ERROR; } } @@ -2163,15 +2230,9 @@ status_t AudioFlinger::PlaybackThread::setSyncEvent(const sp<SyncEvent>& event) return NAME_NOT_FOUND; } -bool AudioFlinger::PlaybackThread::isValidSyncEvent(const sp<SyncEvent>& event) +bool AudioFlinger::PlaybackThread::isValidSyncEvent(const sp<SyncEvent>& event) const { - switch (event->type()) { - case AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE: - return true; - default: - break; - } - return false; + return event->type() == AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE; } void AudioFlinger::PlaybackThread::threadLoop_removeTracks(const Vector< sp<Track> >& tracksToRemove) @@ -2192,28 +2253,25 @@ void AudioFlinger::PlaybackThread::threadLoop_removeTracks(const Vector< sp<Trac // ---------------------------------------------------------------------------- AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, - audio_io_handle_t id, uint32_t device, type_t type) + audio_io_handle_t id, audio_devices_t device, type_t type) : PlaybackThread(audioFlinger, output, id, device, type), // mAudioMixer below -#ifdef SOAKER - mSoaker(NULL), -#endif // mFastMixer below mFastMixerFutex(0) // mOutputSink below // mPipeSink below // mNormalSink below { - ALOGV("MixerThread() id=%d device=%d type=%d", id, device, type); - ALOGV("mSampleRate=%d, mChannelMask=%d, mChannelCount=%d, mFormat=%d, mFrameSize=%d, " + ALOGV("MixerThread() id=%d device=%#x type=%d", id, device, type); + ALOGV("mSampleRate=%d, mChannelMask=%#x, mChannelCount=%d, mFormat=%d, mFrameSize=%d, " "mFrameCount=%d, mNormalFrameCount=%d", mSampleRate, mChannelMask, mChannelCount, mFormat, mFrameSize, mFrameCount, mNormalFrameCount); mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate); // FIXME - Current mixer implementation only supports stereo output - if (mChannelCount == 1) { - ALOGE("Invalid audio hardware channel count"); + if (mChannelCount != FCC_2) { + ALOGE("Invalid audio hardware channel count %d", mChannelCount); } // create an NBAIO sink for the HAL output stream, and negotiate @@ -2267,13 +2325,6 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud mTeeSource = teeSource; #endif -#ifdef SOAKER - // create a soaker as workaround for governor issues - mSoaker = new Soaker(); - // FIXME Soaker should only run when needed, i.e. when FastMixer is not in COLD_IDLE - mSoaker->run("Soaker", PRIORITY_LOWEST); -#endif - // create fast mixer and configure it initially with just one fast track for our submix mFastMixer = new FastMixer(); FastMixerStateQueue *sq = mFastMixer->sq(); @@ -2305,14 +2356,12 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud // start the fast mixer mFastMixer->run("FastMixer", PRIORITY_URGENT_AUDIO); -#ifdef HAVE_REQUEST_PRIORITY pid_t tid = mFastMixer->getTid(); - int err = requestPriority(getpid_cached, tid, 2); + 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", - 2, getpid_cached, tid, err); + kPriorityFastMixer, getpid_cached, tid, err); } -#endif #ifdef AUDIO_WATCHDOG // create and start the watchdog @@ -2320,10 +2369,10 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud mAudioWatchdog->setDump(&mAudioWatchdogDump); mAudioWatchdog->run("AudioWatchdog", PRIORITY_URGENT_AUDIO); tid = mAudioWatchdog->getTid(); - err = requestPriority(getpid_cached, tid, 1); + err = requestPriority(getpid_cached, tid, kPriorityFastMixer); if (err != 0) { ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d", - 1, getpid_cached, tid, err); + kPriorityFastMixer, getpid_cached, tid, err); } #endif @@ -2370,17 +2419,13 @@ AudioFlinger::MixerThread::~MixerThread() delete fastTrack->mBufferProvider; sq->end(false /*didModify*/); delete mFastMixer; -#ifdef SOAKER - if (mSoaker != NULL) { - mSoaker->requestExitAndWait(); - } - delete mSoaker; -#endif +#ifdef AUDIO_WATCHDOG if (mAudioWatchdog != 0) { mAudioWatchdog->requestExit(); mAudioWatchdog->requestExitAndWait(); mAudioWatchdog.clear(); } +#endif } delete mAudioMixer; } @@ -2508,9 +2553,6 @@ bool AudioFlinger::PlaybackThread::threadLoop() // MIXER nsecs_t lastWarning = 0; -if (mType == MIXER) { - longStandbyExit = false; -} // DUPLICATING // FIXME could this be made local to while loop? @@ -2519,9 +2561,9 @@ if (mType == MIXER) { cacheParameters_l(); sleepTime = idleSleepTime; -if (mType == MIXER) { - sleepTimeShift = 0; -} + if (mType == MIXER) { + sleepTimeShift = 0; + } CpuStats cpuStats; const String8 myName(String8::format("thread %p type %d TID %d", this, mType, gettid())); @@ -2548,13 +2590,12 @@ if (mType == MIXER) { // put audio hardware into standby after short delay if (CC_UNLIKELY((!mActiveTracks.size() && systemTime() > standbyTime) || - mSuspended > 0)) { + isSuspended())) { if (!mStandby) { threadLoop_standby(); mStandby = true; - mBytesWritten = 0; } if (!mActiveTracks.size() && mConfigEvents.isEmpty()) { @@ -2574,6 +2615,7 @@ if (mType == MIXER) { mMixerStatus = MIXER_IDLE; mMixerStatusIgnoringFastTracks = MIXER_IDLE; + mBytesWritten = 0; checkSilentMode_l(); @@ -2602,8 +2644,9 @@ if (mType == MIXER) { threadLoop_sleepTime(); } - if (mSuspended > 0) { + if (isSuspended()) { sleepTime = suspendSleepTimeUs(); + mBytesWritten += mixBufferSize; } // only process effects if we're going to write @@ -2635,11 +2678,6 @@ if (mType == MIXER) { ns2ms(delta), mNumDelayedWrites, this); lastWarning = now; } - // FIXME this is broken: longStandbyExit should be handled out of the if() and with - // a different threshold. Or completely removed for what it is worth anyway... - if (mStandby) { - longStandbyExit = true; - } } } @@ -2667,15 +2705,13 @@ if (mType == MIXER) { // is now local to this block, but will keep it for now (at least until merge done). } -if (mType == MIXER || mType == DIRECT) { - // put output stream into standby mode - if (!mStandby) { - mOutput->stream->common.standby(&mOutput->stream->common); + // for DuplicatingThread, standby mode is handled by the outputTracks, otherwise ... + if (mType == MIXER || mType == DIRECT) { + // put output stream into standby mode + if (!mStandby) { + mOutput->stream->common.standby(&mOutput->stream->common); + } } -} -if (mType == DUPLICATING) { - // for DuplicatingThread, standby mode is handled by the outputTracks -} releaseWakeLock(); @@ -2702,9 +2738,11 @@ void AudioFlinger::MixerThread::threadLoop_write() if (old == -1) { __futex_syscall3(&mFastMixerFutex, FUTEX_WAKE_PRIVATE, 1); } +#ifdef AUDIO_WATCHDOG if (mAudioWatchdog != 0) { mAudioWatchdog->resume(); } +#endif } state->mCommand = FastMixerState::MIX_WRITE; sq->end(); @@ -2781,9 +2819,11 @@ void AudioFlinger::MixerThread::threadLoop_standby() if (kUseFastMixer == FastMixer_Dynamic) { mNormalSink = mOutputSink; } +#ifdef AUDIO_WATCHDOG if (mAudioWatchdog != 0) { mAudioWatchdog->pause(); } +#endif } else { sq->end(false /*didModify*/); } @@ -2794,7 +2834,7 @@ void AudioFlinger::MixerThread::threadLoop_standby() // shared by MIXER and DIRECT, overridden by DUPLICATING void AudioFlinger::PlaybackThread::threadLoop_standby() { - ALOGV("Audio hardware entering standby, mixer %p, suspend count %u", this, mSuspended); + ALOGV("Audio hardware entering standby, mixer %p, suspend count %d", this, mSuspended); mOutput->stream->common.standby(&mOutput->stream->common); } @@ -2804,9 +2844,10 @@ void AudioFlinger::MixerThread::threadLoop_mix() int64_t pts; status_t status = INVALID_OPERATION; - if (NULL != mOutput->stream->get_next_write_timestamp) { - status = mOutput->stream->get_next_write_timestamp( - mOutput->stream, &pts); + if (mNormalSink != 0) { + status = mNormalSink->getNextWriteTimestamp(&pts); + } else { + status = mOutputSink->getNextWriteTimestamp(&pts); } if (status != NO_ERROR) { @@ -2847,11 +2888,10 @@ void AudioFlinger::MixerThread::threadLoop_sleepTime() } else { sleepTime = idleSleepTime; } - } else if (mBytesWritten != 0 || - (mMixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)) { + } else if (mBytesWritten != 0 || (mMixerStatus == MIXER_TRACKS_ENABLED)) { memset (mMixBuffer, 0, mixBufferSize); sleepTime = 0; - ALOGV_IF((mBytesWritten == 0 && (mMixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)), "anticipated start"); + ALOGV_IF((mBytesWritten == 0 && (mMixerStatus == MIXER_TRACKS_ENABLED)), "anticipated start"); } // TODO add standby time extension fct of effect tail } @@ -2993,7 +3033,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000; size_t framesWritten = mBytesWritten / audio_stream_frame_size(&mOutput->stream->common); - if (!track->presentationComplete(framesWritten, audioHALFrames)) { + if (!(mStandby || track->presentationComplete(framesWritten, audioHALFrames))) { // track stays in active list until presentation is complete break; } @@ -3235,11 +3275,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac // Remove it from the list of active tracks. // TODO: use actual buffer filling status instead of latency when available from // audio HAL - size_t audioHALFrames = - (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000; + size_t audioHALFrames = (latency_l() * mSampleRate) / 1000; size_t framesWritten = mBytesWritten / audio_stream_frame_size(&mOutput->stream->common); - if (track->presentationComplete(framesWritten, audioHALFrames)) { + if (mStandby || track->presentationComplete(framesWritten, audioHALFrames)) { if (track->isStopped()) { track->reset(); } @@ -3296,9 +3335,11 @@ track_is_ready: ; sq->end(didModify); sq->push(block); } +#ifdef AUDIO_WATCHDOG if (pauseAudioWatchdog && mAudioWatchdog != 0) { mAudioWatchdog->pause(); } +#endif // Now perform the deferred reset on fast tracks that have stopped while (resetMask != 0) { @@ -3373,7 +3414,7 @@ void AudioFlinger::PlaybackThread::cacheParameters_l() idleSleepTime = idleSleepTimeUs(); } -void AudioFlinger::MixerThread::invalidateTracks(audio_stream_type_t streamType) +void AudioFlinger::PlaybackThread::invalidateTracks(audio_stream_type_t streamType) { ALOGV ("MixerThread::invalidateTracks() mixer %p, streamType %d, mTracks.size %d", this, streamType, mTracks.size()); @@ -3390,9 +3431,9 @@ void AudioFlinger::MixerThread::invalidateTracks(audio_stream_type_t streamType) } // getTrackName_l() must be called with ThreadBase::mLock held -int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask) +int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask, int sessionId) { - return mAudioMixer->getTrackName(channelMask); + return mAudioMixer->getTrackName(channelMask, sessionId); } // deleteTrackName_l() must be called with ThreadBase::mLock held @@ -3460,14 +3501,14 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l() #ifdef ADD_BATTERY_DATA // when changing the audio output device, call addBatteryData to notify // the change - if ((int)mDevice != value) { + if (mOutDevice != value) { uint32_t params = 0; // check whether speaker is on if (value & AUDIO_DEVICE_OUT_SPEAKER) { params |= IMediaPlayerService::kBatteryDataSpeakerOn; } - int deviceWithoutSpeaker + audio_devices_t deviceWithoutSpeaker = AUDIO_DEVICE_OUT_ALL & ~AUDIO_DEVICE_OUT_SPEAKER; // check if any other device (except speaker) is on if (value & deviceWithoutSpeaker ) { @@ -3482,9 +3523,9 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l() // forward device change to effects that have requested to be // aware of attached audio device. - mDevice = (uint32_t)value; + mOutDevice = value; for (size_t i = 0; i < mEffectChains.size(); i++) { - mEffectChains[i]->setDevice_l(mDevice); + mEffectChains[i]->setDevice_l(mOutDevice); } } @@ -3505,7 +3546,7 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l() readOutputParameters(); mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate); for (size_t i = 0; i < mTracks.size() ; i++) { - int name = getTrackName_l((audio_channel_mask_t)mTracks[i]->mChannelMask); + int name = getTrackName_l(mTracks[i]->mChannelMask, mTracks[i]->mSessionId); if (name < 0) break; mTracks[i]->mName = name; // limit track sample rate to 2 x new output sample rate @@ -3513,7 +3554,7 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l() mTracks[i]->mCblk->sampleRate = 2 * sampleRate(); } } - sendConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED); + sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED); } } @@ -3539,7 +3580,7 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l() return reconfig; } -status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& args) +void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; @@ -3593,7 +3634,8 @@ status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16> #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); + ssize_t actual = teeSource->read(buffer, count, + AudioBufferProvider::kInvalidPTS); bool wasFirstRead = firstRead; firstRead = false; if (actual <= 0) { @@ -3602,7 +3644,7 @@ status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16> } break; } - ALOG_ASSERT(actual <= count); + ALOG_ASSERT(actual <= (ssize_t)count); write(teeFd, buffer, actual * channelCount * sizeof(short)); total += actual; } @@ -3619,13 +3661,13 @@ status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16> } } +#ifdef AUDIO_WATCHDOG if (mAudioWatchdog != 0) { // Make a non-atomic copy of audio watchdog dump so it won't change underneath us AudioWatchdogDump wdCopy = mAudioWatchdogDump; wdCopy.dump(fd); } - - return NO_ERROR; +#endif } uint32_t AudioFlinger::MixerThread::idleSleepTimeUs() const @@ -3651,7 +3693,7 @@ void AudioFlinger::MixerThread::cacheParameters_l() // ---------------------------------------------------------------------------- AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, - AudioStreamOut* output, audio_io_handle_t id, uint32_t device) + AudioStreamOut* output, audio_io_handle_t id, audio_devices_t device) : PlaybackThread(audioFlinger, output, id, device, DIRECT) // mLeftVolFloat, mRightVolFloat { @@ -3756,11 +3798,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep // 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 = - (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000; + size_t audioHALFrames = (latency_l() * mSampleRate) / 1000; size_t framesWritten = mBytesWritten / audio_stream_frame_size(&mOutput->stream->common); - if (track->presentationComplete(framesWritten, audioHALFrames)) { + if (mStandby || track->presentationComplete(framesWritten, audioHALFrames)) { if (track->isStopped()) { track->reset(); } @@ -3836,7 +3877,8 @@ void AudioFlinger::DirectOutputThread::threadLoop_sleepTime() } // getTrackName_l() must be called with ThreadBase::mLock held -int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask) +int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask, + int sessionId) { return 0; } @@ -3879,7 +3921,7 @@ bool AudioFlinger::DirectOutputThread::checkForNewParameters_l() } if (status == NO_ERROR && reconfig) { readOutputParameters(); - sendConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED); + sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED); } } @@ -3940,7 +3982,7 @@ void AudioFlinger::DirectOutputThread::cacheParameters_l() AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread, audio_io_handle_t id) - : MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->device(), DUPLICATING), + : MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->outDevice(), DUPLICATING), mWaitTimeMs(UINT_MAX) { addOutputTrack(mainThread); @@ -4070,6 +4112,7 @@ bool AudioFlinger::DuplicatingThread::outputsReady(const SortedVector< sp<Output return false; } PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); + // see note at standby() declaration if (playbackThread->standby() && !playbackThread->isSuspended()) { ALOGV("DuplicatingThread output track %p on thread %p Not Ready", outputTracks[i].get(), thread.get()); return false; @@ -4099,7 +4142,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( const sp<Client>& client, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, const sp<IMemory>& sharedBuffer, int sessionId) @@ -4274,7 +4317,7 @@ AudioFlinger::PlaybackThread::Track::Track( audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, const sp<IMemory>& sharedBuffer, int sessionId, @@ -4300,7 +4343,7 @@ AudioFlinger::PlaybackThread::Track::Track( // 16 bit because data is converted to 16 bit before being stored in buffer by AudioTrack mCblk->frameSize = audio_is_linear_pcm(format) ? mChannelCount * sizeof(int16_t) : sizeof(uint8_t); // to avoid leaking a track name, do not allocate one unless there is an mCblk - mName = thread->getTrackName_l((audio_channel_mask_t)channelMask); + mName = thread->getTrackName_l(channelMask, sessionId); mCblk->mName = mName; if (mName < 0) { ALOGE("no more track names available"); @@ -4329,11 +4372,6 @@ AudioFlinger::PlaybackThread::Track::Track( AudioFlinger::PlaybackThread::Track::~Track() { ALOGV("PlaybackThread::Track destructor"); - sp<ThreadBase> thread = mThread.promote(); - if (thread != 0) { - Mutex::Autolock _l(thread->mLock); - mState = TERMINATED; - } } void AudioFlinger::PlaybackThread::Track::destroy() @@ -4496,8 +4534,6 @@ status_t AudioFlinger::PlaybackThread::Track::getNextBuffer( } buffer->raw = getBuffer(s, framesReq); - if (buffer->raw == NULL) goto getNextBuffer_exit; - buffer->frameCount = framesReq; return NO_ERROR; } @@ -4645,7 +4681,7 @@ void AudioFlinger::PlaybackThread::Track::flush() if (thread != 0) { Mutex::Autolock _l(thread->mLock); if (mState != STOPPING_1 && mState != STOPPING_2 && mState != STOPPED && mState != PAUSED && - mState != PAUSING) { + mState != PAUSING && mState != IDLE && mState != FLUSHED) { return; } // No point remaining in PAUSED state after a flush => go to @@ -4808,7 +4844,7 @@ status_t AudioFlinger::PlaybackThread::Track::setSyncEvent(const sp<SyncEvent>& event->cancel(); return INVALID_OPERATION; } - TrackBase::setSyncEvent(event); + (void) TrackBase::setSyncEvent(event); return NO_ERROR; } @@ -4821,12 +4857,12 @@ AudioFlinger::PlaybackThread::TimedTrack::create( audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, const sp<IMemory>& sharedBuffer, int sessionId) { if (!client->reserveTimedTrack()) - return NULL; + return 0; return new TimedTrack( thread, client, streamType, sampleRate, format, channelMask, frameCount, @@ -4839,7 +4875,7 @@ AudioFlinger::PlaybackThread::TimedTrack::TimedTrack( audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, const sp<IMemory>& sharedBuffer, int sessionId) @@ -5061,7 +5097,7 @@ status_t AudioFlinger::PlaybackThread::TimedTrack::getNextBuffer( AudioBufferProvider::Buffer* buffer, int64_t pts) { if (pts == AudioBufferProvider::kInvalidPTS) { - buffer->raw = 0; + buffer->raw = NULL; buffer->frameCount = 0; mTimedAudioOutputOnTime = false; return INVALID_OPERATION; @@ -5076,7 +5112,7 @@ status_t AudioFlinger::PlaybackThread::TimedTrack::getNextBuffer( // if we have no timed buffers, then fail if (mTimedBufferQueue.isEmpty()) { - buffer->raw = 0; + buffer->raw = NULL; buffer->frameCount = 0; return NOT_ENOUGH_DATA; } @@ -5103,7 +5139,7 @@ status_t AudioFlinger::PlaybackThread::TimedTrack::getNextBuffer( // the transform failed. this shouldn't happen, but if it does // then just drop this buffer ALOGW("timedGetNextBuffer transform failed"); - buffer->raw = 0; + buffer->raw = NULL; buffer->frameCount = 0; trimTimedBufferQueueHead_l("getNextBuffer; no transform"); return NO_ERROR; @@ -5112,7 +5148,7 @@ status_t AudioFlinger::PlaybackThread::TimedTrack::getNextBuffer( if (mMediaTimeTransformTarget == TimedAudioTrack::COMMON_TIME) { if (OK != mCCHelper.commonTimeToLocalTime(transformedPTS, &headLocalPTS)) { - buffer->raw = 0; + buffer->raw = NULL; buffer->frameCount = 0; return INVALID_OPERATION; } @@ -5334,7 +5370,7 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack( const sp<Client>& client, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, int sessionId) : TrackBase(thread, client, sampleRate, format, @@ -5355,10 +5391,7 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack( AudioFlinger::RecordThread::RecordTrack::~RecordTrack() { - sp<ThreadBase> thread = mThread.promote(); - if (thread != 0) { - AudioSystem::releaseInput(thread->id()); - } + ALOGV("%s", __func__); } // AudioBufferProvider interface @@ -5389,8 +5422,6 @@ status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvi } buffer->raw = getBuffer(s, framesReq); - if (buffer->raw == NULL) goto getNextBuffer_exit; - buffer->frameCount = framesReq; return NO_ERROR; } @@ -5418,17 +5449,29 @@ void AudioFlinger::RecordThread::RecordTrack::stop() sp<ThreadBase> thread = mThread.promote(); if (thread != 0) { RecordThread *recordThread = (RecordThread *)thread.get(); - recordThread->stop(this); - TrackBase::reset(); - // Force overrun condition to avoid false overrun callback until first data is - // read from buffer - android_atomic_or(CBLK_UNDERRUN_ON, &mCblk->flags); + recordThread->mLock.lock(); + bool doStop = recordThread->stop_l(this); + if (doStop) { + TrackBase::reset(); + // Force overrun condition to avoid false overrun callback until first data is + // read from buffer + android_atomic_or(CBLK_UNDERRUN_ON, &mCblk->flags); + } + recordThread->mLock.unlock(); + if (doStop) { + AudioSystem::stopInput(recordThread->id()); + } } } +/*static*/ void AudioFlinger::RecordThread::RecordTrack::appendDumpHeader(String8& result) +{ + result.append(" Clien Fmt Chn mask Session Buf S SRate Serv User FrameCount\n"); +} + void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size) { - snprintf(buffer, size, " %05d %03u 0x%08x %05d %04u %01d %05u %08x %08x\n", + snprintf(buffer, size, " %05d %03u 0x%08x %05d %04u %01d %05u %08x %08x %05d\n", (mClient == 0) ? getpid_cached : mClient->pid(), mFormat, mChannelMask, @@ -5437,7 +5480,8 @@ void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size) mState, mCblk->sampleRate, mCblk->server, - mCblk->user); + mCblk->user, + mCblk->frameCount); } @@ -5448,7 +5492,7 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack( DuplicatingThread *sourceThread, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount) : Track(playbackThread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount, NULL, 0, IAudioFlinger::TRACK_DEFAULT), @@ -5836,9 +5880,10 @@ sp<IAudioRecord> AudioFlinger::openRecord( audio_io_handle_t input, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, IAudioFlinger::track_flags_t flags, + pid_t tid, int *sessionId, status_t *status) { @@ -5877,13 +5922,8 @@ sp<IAudioRecord> AudioFlinger::openRecord( } } // create new record track. The record track uses one track in mHardwareMixerThread by convention. - recordTrack = thread->createRecordTrack_l(client, - sampleRate, - format, - channelMask, - frameCount, - lSessionId, - &lStatus); + recordTrack = thread->createRecordTrack_l(client, sampleRate, format, channelMask, + frameCount, lSessionId, flags, tid, &lStatus); } if (lStatus != NO_ERROR) { // remove local strong reference to Client before deleting the RecordTrack so that the Client @@ -5913,19 +5953,24 @@ AudioFlinger::RecordHandle::RecordHandle(const sp<AudioFlinger::RecordThread::Re } AudioFlinger::RecordHandle::~RecordHandle() { - stop(); + stop_nonvirtual(); + mRecordTrack->destroy(); } sp<IMemory> AudioFlinger::RecordHandle::getCblk() const { return mRecordTrack->getCblk(); } -status_t AudioFlinger::RecordHandle::start(int event, int triggerSession) { +status_t AudioFlinger::RecordHandle::start(int /*AudioSystem::sync_event_t*/ event, int triggerSession) { ALOGV("RecordHandle::start()"); return mRecordTrack->start((AudioSystem::sync_event_t)event, triggerSession); } void AudioFlinger::RecordHandle::stop() { + stop_nonvirtual(); +} + +void AudioFlinger::RecordHandle::stop_nonvirtual() { ALOGV("RecordHandle::stop()"); mRecordTrack->stop(); } @@ -5941,13 +5986,13 @@ status_t AudioFlinger::RecordHandle::onTransact( AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, AudioStreamIn *input, uint32_t sampleRate, - uint32_t channels, + audio_channel_mask_t channelMask, audio_io_handle_t id, - uint32_t device) : - ThreadBase(audioFlinger, id, device, RECORD), - mInput(input), mTrack(NULL), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL), + audio_devices_t device) : + ThreadBase(audioFlinger, id, AUDIO_DEVICE_NONE, device, RECORD), + mInput(input), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL), // mRsmpInIndex and mInputBytes set by readInputParameters() - mReqChannelCount(popcount(channels)), + 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?) @@ -5985,8 +6030,12 @@ bool AudioFlinger::RecordThread::threadLoop() nsecs_t lastWarning = 0; + inputStandBy(); acquireWakeLock(); + // used to verify we've read at least once before evaluating how many bytes were read + bool readOnce = false; + // start recording while (!exitPending()) { @@ -5996,10 +6045,7 @@ bool AudioFlinger::RecordThread::threadLoop() Mutex::Autolock _l(mLock); checkForNewParameters_l(); if (mActiveTrack == 0 && mConfigEvents.isEmpty()) { - if (!mStandby) { - mInput->stream->common.standby(&mInput->stream->common); - mStandby = true; - } + standby(); if (exitPending()) break; @@ -6013,20 +6059,17 @@ bool AudioFlinger::RecordThread::threadLoop() } if (mActiveTrack != 0) { if (mActiveTrack->mState == TrackBase::PAUSING) { - if (!mStandby) { - mInput->stream->common.standby(&mInput->stream->common); - mStandby = true; - } + standby(); mActiveTrack.clear(); mStartStopCond.broadcast(); } else if (mActiveTrack->mState == TrackBase::RESUMING) { if (mReqChannelCount != mActiveTrack->channelCount()) { mActiveTrack.clear(); mStartStopCond.broadcast(); - } else if (mBytesRead != 0) { + } else if (readOnce) { // record start succeeds only if first read from audio input // succeeds - if (mBytesRead > 0) { + if (mBytesRead >= 0) { mActiveTrack->mState = TrackBase::ACTIVE; } else { mActiveTrack.clear(); @@ -6034,6 +6077,9 @@ bool AudioFlinger::RecordThread::threadLoop() mStartStopCond.broadcast(); } mStandby = false; + } else if (mActiveTrack->mState == TrackBase::TERMINATED) { + removeTrack_l(mActiveTrack); + mActiveTrack.clear(); } } lockEffectChains_l(effectChains); @@ -6052,6 +6098,7 @@ bool AudioFlinger::RecordThread::threadLoop() buffer.frameCount = mFrameCount; if (CC_LIKELY(mActiveTrack->getNextBuffer(&buffer) == NO_ERROR)) { + readOnce = true; size_t framesOut = buffer.frameCount; if (mResampler == NULL) { // no resampling @@ -6068,18 +6115,12 @@ bool AudioFlinger::RecordThread::threadLoop() mFormat != AUDIO_FORMAT_PCM_16_BIT) { memcpy(dst, src, framesIn * mFrameSize); } else { - int16_t *src16 = (int16_t *)src; - int16_t *dst16 = (int16_t *)dst; if (mChannelCount == 1) { - while (framesIn--) { - *dst16++ = *src16; - *dst16++ = *src16++; - } + upmix_to_stereo_i16_from_mono_i16((int16_t *)dst, + (int16_t *)src, framesIn); } else { - while (framesIn--) { - *dst16++ = (int16_t)(((int32_t)*src16 + (int32_t)*(src16 + 1)) >> 1); - src16 += 2; - } + downmix_to_mono_i16_from_stereo_i16((int16_t *)dst, + (int16_t *)src, framesIn); } } } @@ -6092,12 +6133,13 @@ bool AudioFlinger::RecordThread::threadLoop() mBytesRead = mInput->stream->read(mInput->stream, mRsmpInBuffer, mInputBytes); mRsmpInIndex = 0; } - if (mBytesRead < 0) { - ALOGE("Error reading audio input"); - if (mActiveTrack->mState == TrackBase::ACTIVE) { + 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 - mInput->stream->common.standby(&mInput->stream->common); + inputStandBy(); usleep(kRecordThreadSleepUs); } mRsmpInIndex = mFrameCount; @@ -6114,18 +6156,14 @@ bool AudioFlinger::RecordThread::threadLoop() if (mChannelCount == 1 && mReqChannelCount == 1) { framesOut >>= 1; } - mResampler->resample(mRsmpOutBuffer, framesOut, this); + mResampler->resample(mRsmpOutBuffer, framesOut, 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) { ditherAndClamp(mRsmpOutBuffer, mRsmpOutBuffer, framesOut); // the resampler always outputs stereo samples: do post stereo to mono conversion - int16_t *src = (int16_t *)mRsmpOutBuffer; - int16_t *dst = buffer.i16; - while (framesOut--) { - *dst++ = (int16_t)(((int32_t)*src + (int32_t)*(src + 1)) >> 1); - src += 2; - } + downmix_to_mono_i16_from_stereo_i16(buffer.i16, (int16_t *)mRsmpOutBuffer, + framesOut); } else { ditherAndClamp((int32_t *)buffer.raw, mRsmpOutBuffer, framesOut); } @@ -6151,7 +6189,7 @@ bool AudioFlinger::RecordThread::threadLoop() } } } - mActiveTrack->overflow(); + mActiveTrack->clearOverflow(); } // client isn't retrieving buffers fast enough else { @@ -6173,12 +6211,13 @@ bool AudioFlinger::RecordThread::threadLoop() effectChains.clear(); } - if (!mStandby) { - mInput->stream->common.standby(&mInput->stream->common); - } - mActiveTrack.clear(); + standby(); - mStartStopCond.broadcast(); + { + Mutex::Autolock _l(mLock); + mActiveTrack.clear(); + mStartStopCond.broadcast(); + } releaseWakeLock(); @@ -6186,14 +6225,28 @@ bool AudioFlinger::RecordThread::threadLoop() return false; } +void AudioFlinger::RecordThread::standby() +{ + if (!mStandby) { + inputStandBy(); + mStandby = true; + } +} + +void AudioFlinger::RecordThread::inputStandBy() +{ + mInput->stream->common.standby(&mInput->stream->common); +} sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createRecordTrack_l( const sp<AudioFlinger::Client>& client, uint32_t sampleRate, audio_format_t format, - int channelMask, + audio_channel_mask_t channelMask, int frameCount, int sessionId, + IAudioFlinger::track_flags_t flags, + pid_t tid, status_t *status) { sp<RecordTrack> track; @@ -6205,6 +6258,8 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR goto Exit; } + // FIXME use flags and tid similar to createTrack_l() + { // scope for mLock Mutex::Autolock _l(mLock); @@ -6215,11 +6270,11 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR lStatus = NO_MEMORY; goto Exit; } + mTracks.add(track); - mTrack = track.get(); // disable AEC and NS if the device is a BT SCO headset supporting those pre processings - bool suspend = audio_is_bluetooth_sco_device( - (audio_devices_t)(mDevice & AUDIO_DEVICE_IN_ALL)) && mAudioFlinger->btNrecIsOff(); + bool suspend = audio_is_bluetooth_sco_device(mInDevice) && + mAudioFlinger->btNrecIsOff(); setEffectSuspended_l(FX_IID_AEC, suspend, sessionId); setEffectSuspended_l(FX_IID_NS, suspend, sessionId); } @@ -6287,7 +6342,7 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac mActiveTrack->mState = TrackBase::RESUMING; // signal thread to start ALOGV("Signal record thread"); - mWaitWorkCV.signal(); + mWaitWorkCV.broadcast(); // do not wait for mStartStopCond if exiting if (exitPending()) { mActiveTrack.clear(); @@ -6337,50 +6392,97 @@ void AudioFlinger::RecordThread::handleSyncStartEvent(const sp<SyncEvent>& event } } -void AudioFlinger::RecordThread::stop(RecordThread::RecordTrack* recordTrack) { +bool AudioFlinger::RecordThread::stop_l(RecordThread::RecordTrack* recordTrack) { ALOGV("RecordThread::stop"); - sp<ThreadBase> strongMe = this; - { - AutoMutex lock(mLock); - if (mActiveTrack != 0 && recordTrack == mActiveTrack.get()) { - mActiveTrack->mState = TrackBase::PAUSING; - // do not wait for mStartStopCond if exiting - if (exitPending()) { - return; - } - mStartStopCond.wait(mLock); - // if we have been restarted, recordTrack == mActiveTrack.get() here - if (mActiveTrack == 0 || recordTrack != mActiveTrack.get()) { - mLock.unlock(); - AudioSystem::stopInput(mId); - mLock.lock(); - ALOGV("Record stopped OK"); - } - } + if (recordTrack != mActiveTrack.get() || recordTrack->mState == TrackBase::PAUSING) { + return false; } + recordTrack->mState = TrackBase::PAUSING; + // do not wait for mStartStopCond if exiting + if (exitPending()) { + return true; + } + mStartStopCond.wait(mLock); + // if we have been restarted, recordTrack == mActiveTrack.get() here + if (exitPending() || recordTrack != mActiveTrack.get()) { + ALOGV("Record stopped OK"); + return true; + } + return false; } -bool AudioFlinger::RecordThread::isValidSyncEvent(const sp<SyncEvent>& event) +bool AudioFlinger::RecordThread::isValidSyncEvent(const sp<SyncEvent>& event) const { return false; } status_t AudioFlinger::RecordThread::setSyncEvent(const sp<SyncEvent>& event) { +#if 0 // This branch is currently dead code, but is preserved in case it will be needed in future if (!isValidSyncEvent(event)) { return BAD_VALUE; } + int eventSession = event->triggerSession(); + status_t ret = NAME_NOT_FOUND; + Mutex::Autolock _l(mLock); - if (mTrack != NULL && event->triggerSession() == mTrack->sessionId()) { - mTrack->setSyncEvent(event); - return NO_ERROR; + for (size_t i = 0; i < mTracks.size(); i++) { + sp<RecordTrack> track = mTracks[i]; + if (eventSession == track->sessionId()) { + (void) track->setSyncEvent(event); + ret = NO_ERROR; + } + } + return ret; +#else + return BAD_VALUE; +#endif +} + +void AudioFlinger::RecordThread::RecordTrack::destroy() +{ + // see comments at AudioFlinger::PlaybackThread::Track::destroy() + sp<RecordTrack> keep(this); + { + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + if (mState == ACTIVE || mState == RESUMING) { + AudioSystem::stopInput(thread->id()); + } + AudioSystem::releaseInput(thread->id()); + Mutex::Autolock _l(thread->mLock); + RecordThread *recordThread = (RecordThread *) thread.get(); + recordThread->destroyTrack_l(this); + } + } +} + +// destroyTrack_l() must be called with ThreadBase::mLock held +void AudioFlinger::RecordThread::destroyTrack_l(const sp<RecordTrack>& track) +{ + track->mState = TrackBase::TERMINATED; + // active tracks are removed by threadLoop() + if (mActiveTrack != track) { + removeTrack_l(track); } - return NAME_NOT_FOUND; } -status_t AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args) +void AudioFlinger::RecordThread::removeTrack_l(const sp<RecordTrack>& track) +{ + mTracks.remove(track); + // need anything related to effects here? +} + +void AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args) +{ + dumpInternals(fd, args); + dumpTracks(fd, args); + dumpEffectChains(fd, args); +} + +void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; @@ -6390,11 +6492,6 @@ status_t AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args) result.append(buffer); if (mActiveTrack != 0) { - result.append("Active Track:\n"); - result.append(" Clien Fmt Chn mask Session Buf S SRate Serv User\n"); - mActiveTrack->dump(buffer, SIZE); - result.append(buffer); - snprintf(buffer, SIZE, "In index: %d\n", mRsmpInIndex); result.append(buffer); snprintf(buffer, SIZE, "In size: %d\n", mInputBytes); @@ -6405,17 +6502,41 @@ status_t AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args) result.append(buffer); snprintf(buffer, SIZE, "Out sample rate: %d\n", mReqSampleRate); result.append(buffer); - - } else { - result.append("No record client\n"); + result.append("No active record client\n"); } + write(fd, result.string(), result.size()); dumpBase(fd, args); - dumpEffectChains(fd, args); +} - return NO_ERROR; +void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args) +{ + 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); + } + } + + if (mActiveTrack != 0) { + snprintf(buffer, SIZE, "\nInput thread %p active tracks\n", this); + result.append(buffer); + RecordTrack::appendDumpHeader(result); + mActiveTrack->dump(buffer, SIZE); + result.append(buffer); + + } + write(fd, result.string(), result.size()); } // AudioBufferProvider interface @@ -6427,12 +6548,12 @@ status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer* if (framesReady == 0) { mBytesRead = mInput->stream->read(mInput->stream, mRsmpInBuffer, mInputBytes); - if (mBytesRead < 0) { - ALOGE("RecordThread::getNextBuffer() Error reading audio input"); - if (mActiveTrack->mState == TrackBase::ACTIVE) { + 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 - mInput->stream->common.standby(&mInput->stream->common); + inputStandBy(); usleep(kRecordThreadSleepUs); } buffer->raw = NULL; @@ -6505,28 +6626,40 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l() 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 (value & AUDIO_DEVICE_OUT_ALL) { - mDevice &= (uint32_t)~(value & AUDIO_DEVICE_OUT_ALL); + if (audio_is_output_devices(value)) { + mOutDevice = value; status = BAD_VALUE; } else { - mDevice &= (uint32_t)~(value & AUDIO_DEVICE_IN_ALL); + mInDevice = value; // disable AEC and NS if the device is a BT SCO headset supporting those pre processings - if (mTrack != NULL) { - bool suspend = audio_is_bluetooth_sco_device( - (audio_devices_t)value) && mAudioFlinger->btNrecIsOff(); - setEffectSuspended_l(FX_IID_AEC, suspend, mTrack->sessionId()); - setEffectSuspended_l(FX_IID_NS, suspend, mTrack->sessionId()); + 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()); + } } } - mDevice |= (uint32_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); + } + mAudioSource = (audio_source_t)value; } if (status == NO_ERROR) { status = mInput->stream->common.set_parameters(&mInput->stream->common, keyValuePair.string()); if (status == INVALID_OPERATION) { - mInput->stream->common.standby(&mInput->stream->common); + inputStandBy(); status = mInput->stream->common.set_parameters(&mInput->stream->common, keyValuePair.string()); } @@ -6541,7 +6674,7 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l() } if (status == NO_ERROR) { readInputParameters(); - sendConfigEvent_l(AudioSystem::INPUT_CONFIG_CHANGED); + sendIoConfigEvent_l(AudioSystem::INPUT_CONFIG_CHANGED); } } } @@ -6648,7 +6781,7 @@ unsigned int AudioFlinger::RecordThread::getInputFramesLost() return mInput->stream->get_input_frames_lost(mInput->stream); } -uint32_t AudioFlinger::RecordThread::hasAudioSession(int sessionId) +uint32_t AudioFlinger::RecordThread::hasAudioSession(int sessionId) const { Mutex::Autolock _l(mLock); uint32_t result = 0; @@ -6656,23 +6789,28 @@ uint32_t AudioFlinger::RecordThread::hasAudioSession(int sessionId) result = EFFECT_SESSION; } - if (mTrack != NULL && sessionId == mTrack->sessionId()) { - result |= TRACK_SESSION; + for (size_t i = 0; i < mTracks.size(); ++i) { + if (sessionId == mTracks[i]->sessionId()) { + result |= TRACK_SESSION; + break; + } } return result; } -AudioFlinger::RecordThread::RecordTrack* AudioFlinger::RecordThread::track() +KeyedVector<int, bool> AudioFlinger::RecordThread::sessionIds() const { + KeyedVector<int, bool> ids; Mutex::Autolock _l(mLock); - return mTrack; -} - -AudioFlinger::AudioStreamIn* AudioFlinger::RecordThread::getInput() const -{ - Mutex::Autolock _l(mLock); - return mInput; + for (size_t j = 0; j < mTracks.size(); ++j) { + sp<RecordThread::RecordTrack> track = mTracks[j]; + int sessionId = track->sessionId(); + if (ids.indexOfKey(sessionId) < 0) { + ids.add(sessionId, true); + } + } + return ids; } AudioFlinger::AudioStreamIn* AudioFlinger::RecordThread::clearInput() @@ -6730,16 +6868,52 @@ audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name) return 0; } - if ((mMasterVolumeSupportLvl != MVS_NONE) && - (NULL != dev->set_master_volume)) { + // Check and cache this HAL's level of support for master mute and master + // volume. If this is the first HAL opened, and it supports the get + // methods, use the initial values provided by the HAL as the current + // master mute and volume settings. + + AudioHwDevice::Flags flags = static_cast<AudioHwDevice::Flags>(0); + { // scope for auto-lock pattern AutoMutex lock(mHardwareLock); + + if (0 == mAudioHwDevs.size()) { + mHardwareStatus = AUDIO_HW_GET_MASTER_VOLUME; + if (NULL != dev->get_master_volume) { + float mv; + if (OK == dev->get_master_volume(dev, &mv)) { + mMasterVolume = mv; + } + } + + mHardwareStatus = AUDIO_HW_GET_MASTER_MUTE; + if (NULL != dev->get_master_mute) { + bool mm; + if (OK == dev->get_master_mute(dev, &mm)) { + mMasterMute = mm; + } + } + } + mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; - dev->set_master_volume(dev, mMasterVolume); + if ((NULL != dev->set_master_volume) && + (OK == dev->set_master_volume(dev, mMasterVolume))) { + flags = static_cast<AudioHwDevice::Flags>(flags | + AudioHwDevice::AHWD_CAN_SET_MASTER_VOLUME); + } + + mHardwareStatus = AUDIO_HW_SET_MASTER_MUTE; + if ((NULL != dev->set_master_mute) && + (OK == dev->set_master_mute(dev, mMasterMute))) { + flags = static_cast<AudioHwDevice::Flags>(flags | + AudioHwDevice::AHWD_CAN_SET_MASTER_MUTE); + } + mHardwareStatus = AUDIO_HW_IDLE; } audio_module_handle_t handle = nextUniqueId(); - mAudioHwDevs.add(handle, new AudioHwDevice(name, dev)); + mAudioHwDevs.add(handle, new AudioHwDevice(name, dev, flags)); ALOGI("loadHwModule() Loaded %s audio interface from %s (%s) handle %d", name, dev->common.module->name, dev->common.module->id, handle); @@ -6748,6 +6922,24 @@ audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name) } +// ---------------------------------------------------------------------------- + +int32_t AudioFlinger::getPrimaryOutputSamplingRate() +{ + Mutex::Autolock _l(mLock); + PlaybackThread *thread = primaryPlaybackThread_l(); + return thread != NULL ? thread->sampleRate() : 0; +} + +int32_t AudioFlinger::getPrimaryOutputFrameCount() +{ + Mutex::Autolock _l(mLock); + PlaybackThread *thread = primaryPlaybackThread_l(); + return thread != NULL ? thread->frameCountHAL() : 0; +} + +// ---------------------------------------------------------------------------- + audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module, audio_devices_t *pDevices, uint32_t *pSamplingRate, @@ -6764,11 +6956,11 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module, format: pFormat ? *pFormat : AUDIO_FORMAT_DEFAULT, }; audio_stream_out_t *outStream = NULL; - audio_hw_device_t *outHwDev; + AudioHwDevice *outHwDev; ALOGV("openOutput(), module %d Device %x, SamplingRate %d, Format %d, Channels %x, flags %x", module, - (pDevices != NULL) ? (int)*pDevices : 0, + (pDevices != NULL) ? *pDevices : 0, config.sample_rate, config.format, config.channel_mask, @@ -6784,11 +6976,12 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module, if (outHwDev == NULL) return 0; + audio_hw_device_t *hwDevHal = outHwDev->hwDevice(); audio_io_handle_t id = nextUniqueId(); mHardwareStatus = AUDIO_HW_OUTPUT_OPEN; - status = outHwDev->open_output_stream(outHwDev, + status = hwDevHal->open_output_stream(hwDevHal, id, *pDevices, (audio_output_flags_t)flags, @@ -6832,41 +7025,8 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module, AutoMutex lock(mHardwareLock); mHardwareStatus = AUDIO_HW_SET_MODE; - outHwDev->set_mode(outHwDev, mMode); - - // Determine the level of master volume support the primary audio HAL has, - // and set the initial master volume at the same time. - float initialVolume = 1.0; - mMasterVolumeSupportLvl = MVS_NONE; - - mHardwareStatus = AUDIO_HW_GET_MASTER_VOLUME; - if ((NULL != outHwDev->get_master_volume) && - (NO_ERROR == outHwDev->get_master_volume(outHwDev, &initialVolume))) { - mMasterVolumeSupportLvl = MVS_FULL; - } else { - mMasterVolumeSupportLvl = MVS_SETONLY; - initialVolume = 1.0; - } - - mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; - if ((NULL == outHwDev->set_master_volume) || - (NO_ERROR != outHwDev->set_master_volume(outHwDev, initialVolume))) { - mMasterVolumeSupportLvl = MVS_NONE; - } - // now that we have a primary device, initialize master volume on other devices - for (size_t i = 0; i < mAudioHwDevs.size(); i++) { - audio_hw_device_t *dev = mAudioHwDevs.valueAt(i)->hwDevice(); - - if ((dev != mPrimaryHardwareDev) && - (NULL != dev->set_master_volume)) { - dev->set_master_volume(dev, initialVolume); - } - } + hwDevHal->set_mode(hwDevHal, mMode); mHardwareStatus = AUDIO_HW_IDLE; - mMasterVolumeSW = (MVS_NONE == mMasterVolumeSupportLvl) - ? initialVolume - : 1.0; - mMasterVolume = initialVolume; } return id; } @@ -6897,6 +7057,11 @@ audio_io_handle_t AudioFlinger::openDuplicateOutput(audio_io_handle_t output1, status_t AudioFlinger::closeOutput(audio_io_handle_t output) { + return closeOutput_nonvirtual(output); +} + +status_t AudioFlinger::closeOutput_nonvirtual(audio_io_handle_t output) +{ // keep strong reference on the playback thread so that // it is not destroyed while exit() is executed sp<PlaybackThread> thread; @@ -6928,7 +7093,7 @@ status_t AudioFlinger::closeOutput(audio_io_handle_t output) 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); + out->hwDev()->close_output_stream(out->hwDev(), out->stream); delete out; } return NO_ERROR; @@ -6969,7 +7134,7 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, audio_devices_t *pDevices, uint32_t *pSamplingRate, audio_format_t *pFormat, - uint32_t *pChannelMask) + audio_channel_mask_t *pChannelMask) { status_t status; RecordThread *thread = NULL; @@ -6982,7 +7147,7 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, audio_format_t reqFormat = config.format; audio_channel_mask_t reqChannels = config.channel_mask; audio_stream_in_t *inStream = NULL; - audio_hw_device_t *inHwDev; + AudioHwDevice *inHwDev; if (pDevices == NULL || *pDevices == 0) { return 0; @@ -6994,9 +7159,10 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, if (inHwDev == NULL) return 0; + audio_hw_device_t *inHwHal = inHwDev->hwDevice(); audio_io_handle_t id = nextUniqueId(); - status = inHwDev->open_input_stream(inHwDev, id, *pDevices, &config, + status = inHwHal->open_input_stream(inHwHal, id, *pDevices, &config, &inStream); ALOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %d, Channels %x, status %d", inStream, @@ -7012,9 +7178,9 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, 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 channels"); + ALOGV("openInput() reopening with proposed sampling rate and channel mask"); inStream = NULL; - status = inHwDev->open_input_stream(inHwDev, id, *pDevices, &config, &inStream); + status = inHwHal->open_input_stream(inHwHal, id, *pDevices, &config, &inStream); } if (status == NO_ERROR && inStream != NULL) { @@ -7023,7 +7189,7 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, // Start record thread // RecorThread require both input and output device indication to forward to audio // pre processing modules - uint32_t device = (*pDevices) | primaryOutputDevice_l(); + audio_devices_t device = (*pDevices) | primaryOutputDevice_l(); thread = new RecordThread(this, input, reqSamplingRate, @@ -7036,8 +7202,6 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, if (pFormat != NULL) *pFormat = config.format; if (pChannelMask != NULL) *pChannelMask = reqChannels; - input->stream->common.standby(&input->stream->common); - // notify client processes of the new input creation thread->audioConfigChanged_l(AudioSystem::INPUT_OPENED); return id; @@ -7048,13 +7212,18 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, status_t AudioFlinger::closeInput(audio_io_handle_t input) { + return closeInput_nonvirtual(input); +} + +status_t AudioFlinger::closeInput_nonvirtual(audio_io_handle_t input) +{ // keep strong reference on the record thread so that // it is not destroyed while exit() is executed sp<RecordThread> thread; { Mutex::Autolock _l(mLock); thread = checkRecordThread_l(input); - if (thread == NULL) { + if (thread == 0) { return BAD_VALUE; } @@ -7069,7 +7238,7 @@ status_t AudioFlinger::closeInput(audio_io_handle_t input) 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); + in->hwDev()->close_input_stream(in->hwDev(), in->stream); delete in; return NO_ERROR; @@ -7078,21 +7247,11 @@ status_t AudioFlinger::closeInput(audio_io_handle_t input) status_t AudioFlinger::setStreamOutput(audio_stream_type_t stream, audio_io_handle_t output) { Mutex::Autolock _l(mLock); - MixerThread *dstThread = checkMixerThread_l(output); - if (dstThread == NULL) { - ALOGW("setStreamOutput() bad output id %d", output); - return BAD_VALUE; - } - ALOGV("setStreamOutput() stream %d to output %d", stream, output); - audioConfigChanged_l(AudioSystem::STREAM_CONFIG_CHANGED, output, &stream); for (size_t i = 0; i < mPlaybackThreads.size(); i++) { PlaybackThread *thread = mPlaybackThreads.valueAt(i).get(); - if (thread != dstThread && thread->type() != ThreadBase::DIRECT) { - MixerThread *srcThread = (MixerThread *)thread; - srcThread->invalidateTracks(stream); - } + thread->invalidateTracks(stream); } return NO_ERROR; @@ -7186,20 +7345,14 @@ void AudioFlinger::purgeStaleEffects_l() { } } if (!found) { + Mutex::Autolock _l (t->mLock); // remove all effects from the chain while (ec->mEffects.size()) { sp<EffectModule> effect = ec->mEffects[0]; effect->unPin(); - Mutex::Autolock _l (t->mLock); t->removeEffect_l(effect); - for (size_t j = 0; j < effect->mHandles.size(); j++) { - sp<EffectHandle> handle = effect->mHandles[j].promote(); - if (handle != 0) { - handle->mEffect.clear(); - if (handle->mHasControl && handle->mEnabled) { - t->checkSuspendOnEffectEnabled_l(effect, false, effect->sessionId()); - } - } + if (effect->purgeHandles()) { + t->checkSuspendOnEffectEnabled_l(effect, false, effect->sessionId()); } AudioSystem::unregisterEffect(effect->id()); } @@ -7237,14 +7390,14 @@ AudioFlinger::PlaybackThread *AudioFlinger::primaryPlaybackThread_l() const for (size_t i = 0; i < mPlaybackThreads.size(); i++) { PlaybackThread *thread = mPlaybackThreads.valueAt(i).get(); AudioStreamOut *output = thread->getOutput(); - if (output != NULL && output->hwDev == mPrimaryHardwareDev) { + if (output != NULL && output->audioHwDev == mPrimaryHardwareDev) { return thread; } } return NULL; } -uint32_t AudioFlinger::primaryOutputDevice_l() const +audio_devices_t AudioFlinger::primaryOutputDevice_l() const { PlaybackThread *thread = primaryPlaybackThread_l(); @@ -7252,7 +7405,7 @@ uint32_t AudioFlinger::primaryOutputDevice_l() const return 0; } - return thread->device(); + return thread->outDevice(); } sp<AudioFlinger::SyncEvent> AudioFlinger::createSyncEvent(AudioSystem::sync_event_t type, @@ -7401,7 +7554,7 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid, // 0 and the effect is not auxiliary, continue enumeration in case // an auxiliary version of this effect type is available found = true; - memcpy(&d, &desc, sizeof(effect_descriptor_t)); + d = desc; if (sessionId != AUDIO_SESSION_OUTPUT_MIX || (desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { break; @@ -7417,7 +7570,7 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid, // connect to output mix (Compliance to OpenSL ES) if (sessionId == AUDIO_SESSION_OUTPUT_MIX && (d.flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_AUXILIARY) { - memcpy(&desc, &d, sizeof(effect_descriptor_t)); + desc = d; } } @@ -7436,7 +7589,7 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid, } // return effect descriptor - memcpy(pDesc, &desc, sizeof(effect_descriptor_t)); + *pDesc = desc; // If output is not specified try to find a matching audio session ID in one of the // output threads. @@ -7665,12 +7818,14 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l( } effectCreated = true; - effect->setDevice(mDevice); + effect->setDevice(mOutDevice); + effect->setDevice(mInDevice); effect->setMode(mAudioFlinger->getMode()); + effect->setAudioSource(mAudioSource); } // create effect handle and connect it to effect module handle = new EffectHandle(effect, client, effectClient, priority); - lStatus = effect->addHandle(handle); + lStatus = effect->addHandle(handle.get()); if (enabled != NULL) { *enabled = (int)effect->isEnabled(); } @@ -7742,8 +7897,10 @@ status_t AudioFlinger::ThreadBase::addEffect_l(const sp<EffectModule>& effect) return status; } - effect->setDevice(mDevice); + effect->setDevice(mOutDevice); + effect->setDevice(mInDevice); effect->setMode(mAudioFlinger->getMode()); + effect->setAudioSource(mAudioSource); return NO_ERROR; } @@ -7789,7 +7946,7 @@ sp<AudioFlinger::EffectChain> AudioFlinger::ThreadBase::getEffectChain(int sessi return getEffectChain_l(sessionId); } -sp<AudioFlinger::EffectChain> AudioFlinger::ThreadBase::getEffectChain_l(int sessionId) +sp<AudioFlinger::EffectChain> AudioFlinger::ThreadBase::getEffectChain_l(int sessionId) const { size_t size = mEffectChains.size(); for (size_t i = 0; i < size; i++) { @@ -7810,7 +7967,7 @@ void AudioFlinger::ThreadBase::setMode(audio_mode_t mode) } void AudioFlinger::ThreadBase::disconnectEffect(const sp<EffectModule>& effect, - const wp<EffectHandle>& handle, + EffectHandle *handle, bool unpinIfLast) { Mutex::Autolock _l(mLock); @@ -8002,16 +8159,18 @@ AudioFlinger::EffectModule::EffectModule(ThreadBase *thread, effect_descriptor_t *desc, int id, int sessionId) - : mThread(thread), mChain(chain), mId(id), mSessionId(sessionId), mEffectInterface(NULL), - mStatus(NO_INIT), mState(IDLE), mSuspended(false) + : mPinned(sessionId > AUDIO_SESSION_OUTPUT_MIX), + mThread(thread), mChain(chain), mId(id), mSessionId(sessionId), + mDescriptor(*desc), + // mConfig is set by configure() and not used before then + mEffectInterface(NULL), + mStatus(NO_INIT), mState(IDLE), + // mMaxDisableWaitCnt is set by configure() and not used before then + // mDisableWaitCnt is set by process() and updateState() and not used before then + mSuspended(false) { ALOGV("Constructor %p", this); int lStatus; - if (thread == NULL) { - return; - } - - memcpy(&mDescriptor, desc, sizeof(effect_descriptor_t)); // create effect engine from effect factory mStatus = EffectCreate(&desc->uuid, sessionId, thread->id(), &mEffectInterface); @@ -8025,9 +8184,6 @@ AudioFlinger::EffectModule::EffectModule(ThreadBase *thread, goto Error; } - if (mSessionId > AUDIO_SESSION_OUTPUT_MIX) { - mPinned = true; - } ALOGV("Constructor success name %s, Interface %p", mDescriptor.name, mEffectInterface); return; Error: @@ -8055,38 +8211,41 @@ AudioFlinger::EffectModule::~EffectModule() } } -status_t AudioFlinger::EffectModule::addHandle(const sp<EffectHandle>& handle) +status_t AudioFlinger::EffectModule::addHandle(EffectHandle *handle) { status_t status; Mutex::Autolock _l(mLock); int priority = handle->priority(); size_t size = mHandles.size(); - sp<EffectHandle> h; + EffectHandle *controlHandle = NULL; size_t i; for (i = 0; i < size; i++) { - h = mHandles[i].promote(); - if (h == 0) continue; + EffectHandle *h = mHandles[i]; + if (h == NULL || h->destroyed_l()) continue; + // first non destroyed handle is considered in control + if (controlHandle == NULL) + controlHandle = h; if (h->priority() <= priority) break; } // if inserted in first place, move effect control from previous owner to this handle if (i == 0) { bool enabled = false; - if (h != 0) { - enabled = h->enabled(); - h->setControl(false/*hasControl*/, true /*signal*/, enabled /*enabled*/); + if (controlHandle != NULL) { + enabled = controlHandle->enabled(); + controlHandle->setControl(false/*hasControl*/, true /*signal*/, enabled /*enabled*/); } handle->setControl(true /*hasControl*/, false /*signal*/, enabled /*enabled*/); status = NO_ERROR; } else { status = ALREADY_EXISTS; } - ALOGV("addHandle() %p added handle %p in position %d", this, handle.get(), i); + ALOGV("addHandle() %p added handle %p in position %d", this, handle, i); mHandles.insertAt(handle, i); return status; } -size_t AudioFlinger::EffectModule::removeHandle(const wp<EffectHandle>& handle) +size_t AudioFlinger::EffectModule::removeHandle(EffectHandle *handle) { Mutex::Autolock _l(mLock); size_t size = mHandles.size(); @@ -8097,43 +8256,44 @@ size_t AudioFlinger::EffectModule::removeHandle(const wp<EffectHandle>& handle) if (i == size) { return size; } - ALOGV("removeHandle() %p removed handle %p in position %d", this, handle.unsafe_get(), i); + ALOGV("removeHandle() %p removed handle %p in position %d", this, handle, i); - bool enabled = false; - EffectHandle *hdl = handle.unsafe_get(); - if (hdl != NULL) { - ALOGV("removeHandle() unsafe_get OK"); - enabled = hdl->enabled(); - } mHandles.removeAt(i); - size = mHandles.size(); // if removed from first place, move effect control from this handle to next in line - if (i == 0 && size != 0) { - sp<EffectHandle> h = mHandles[0].promote(); - if (h != 0) { - h->setControl(true /*hasControl*/, true /*signal*/ , enabled /*enabled*/); + if (i == 0) { + EffectHandle *h = controlHandle_l(); + if (h != NULL) { + h->setControl(true /*hasControl*/, true /*signal*/ , handle->enabled() /*enabled*/); } } // Prevent calls to process() and other functions on effect interface from now on. // The effect engine will be released by the destructor when the last strong reference on // this object is released which can happen after next process is called. - if (size == 0 && !mPinned) { + if (mHandles.size() == 0 && !mPinned) { mState = DESTROYED; } - return size; + return mHandles.size(); } -sp<AudioFlinger::EffectHandle> AudioFlinger::EffectModule::controlHandle() +// must be called with EffectModule::mLock held +AudioFlinger::EffectHandle *AudioFlinger::EffectModule::controlHandle_l() { - Mutex::Autolock _l(mLock); - return mHandles.size() != 0 ? mHandles[0].promote() : 0; + // the first valid handle in the list has control over the module + for (size_t i = 0; i < mHandles.size(); i++) { + EffectHandle *h = mHandles[i]; + if (h != NULL && !h->destroyed_l()) { + return h; + } + } + + return NULL; } -void AudioFlinger::EffectModule::disconnect(const wp<EffectHandle>& handle, bool unpinIfLast) +size_t AudioFlinger::EffectModule::disconnect(EffectHandle *handle, bool unpinIfLast) { - ALOGV("disconnect() %p handle %p", this, handle.unsafe_get()); + ALOGV("disconnect() %p handle %p", this, handle); // keep a strong reference on this EffectModule to avoid calling the // destructor before we exit sp<EffectModule> keep(this); @@ -8143,6 +8303,7 @@ void AudioFlinger::EffectModule::disconnect(const wp<EffectHandle>& handle, bool thread->disconnectEffect(keep, handle, unpinIfLast); } } + return mHandles.size(); } void AudioFlinger::EffectModule::updateState() { @@ -8240,7 +8401,6 @@ void AudioFlinger::EffectModule::reset_l() status_t AudioFlinger::EffectModule::configure() { - uint32_t channels; if (mEffectInterface == NULL) { return NO_INIT; } @@ -8251,18 +8411,14 @@ status_t AudioFlinger::EffectModule::configure() } // TODO: handle configuration of effects replacing track process - if (thread->channelCount() == 1) { - channels = AUDIO_CHANNEL_OUT_MONO; - } else { - channels = AUDIO_CHANNEL_OUT_STEREO; - } + audio_channel_mask_t channelMask = thread->channelMask(); if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_MONO; } else { - mConfig.inputCfg.channels = channels; + mConfig.inputCfg.channels = channelMask; } - mConfig.outputCfg.channels = channels; + mConfig.outputCfg.channels = channelMask; mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT; mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT; mConfig.inputCfg.samplingRate = thread->sampleRate(); @@ -8452,8 +8608,8 @@ status_t AudioFlinger::EffectModule::command(uint32_t cmdCode, if (cmdCode != EFFECT_CMD_GET_PARAM && status == NO_ERROR) { uint32_t size = (replySize == NULL) ? 0 : *replySize; for (size_t i = 1; i < mHandles.size(); i++) { - sp<EffectHandle> h = mHandles[i].promote(); - if (h != 0) { + EffectHandle *h = mHandles[i]; + if (h != NULL && !h->destroyed_l()) { h->commandExecuted(cmdCode, cmdSize, pCmdData, size, pReplyData); } } @@ -8463,8 +8619,14 @@ status_t AudioFlinger::EffectModule::command(uint32_t cmdCode, status_t AudioFlinger::EffectModule::setEnabled(bool enabled) { - Mutex::Autolock _l(mLock); + return setEnabled_l(enabled); +} + +// must be called with EffectModule::mLock held +status_t AudioFlinger::EffectModule::setEnabled_l(bool enabled) +{ + ALOGV("setEnabled %p enabled %d", this, enabled); if (enabled != isEnabled()) { @@ -8499,8 +8661,8 @@ status_t AudioFlinger::EffectModule::setEnabled(bool enabled) return NO_ERROR; // simply ignore as we are being destroyed } for (size_t i = 1; i < mHandles.size(); i++) { - sp<EffectHandle> h = mHandles[i].promote(); - if (h != 0) { + EffectHandle *h = mHandles[i]; + if (h != NULL && !h->destroyed_l()) { h->setEnabled(enabled); } } @@ -8573,46 +8735,25 @@ status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right, return status; } -status_t AudioFlinger::EffectModule::setDevice(uint32_t device) +status_t AudioFlinger::EffectModule::setDevice(audio_devices_t device) { + if (device == AUDIO_DEVICE_NONE) { + return NO_ERROR; + } + Mutex::Autolock _l(mLock); status_t status = NO_ERROR; if (device && (mDescriptor.flags & EFFECT_FLAG_DEVICE_MASK) == EFFECT_FLAG_DEVICE_IND) { - // audio pre processing modules on RecordThread can receive both output and - // input device indication in the same call - uint32_t dev = device & AUDIO_DEVICE_OUT_ALL; - if (dev) { - status_t cmdStatus; - uint32_t size = sizeof(status_t); - - status = (*mEffectInterface)->command(mEffectInterface, - EFFECT_CMD_SET_DEVICE, - sizeof(uint32_t), - &dev, - &size, - &cmdStatus); - if (status == NO_ERROR) { - status = cmdStatus; - } - } - dev = device & AUDIO_DEVICE_IN_ALL; - if (dev) { - status_t cmdStatus; - uint32_t size = sizeof(status_t); - - status_t status2 = (*mEffectInterface)->command(mEffectInterface, - EFFECT_CMD_SET_INPUT_DEVICE, - sizeof(uint32_t), - &dev, - &size, - &cmdStatus); - if (status2 == NO_ERROR) { - status2 = cmdStatus; - } - if (status == NO_ERROR) { - status = status2; - } - } + status_t cmdStatus; + uint32_t size = sizeof(status_t); + uint32_t cmd = audio_is_output_devices(device) ? EFFECT_CMD_SET_DEVICE : + EFFECT_CMD_SET_INPUT_DEVICE; + status = (*mEffectInterface)->command(mEffectInterface, + cmd, + sizeof(uint32_t), + &device, + &size, + &cmdStatus); } return status; } @@ -8637,6 +8778,22 @@ status_t AudioFlinger::EffectModule::setMode(audio_mode_t mode) return status; } +status_t AudioFlinger::EffectModule::setAudioSource(audio_source_t source) +{ + Mutex::Autolock _l(mLock); + status_t status = NO_ERROR; + if ((mDescriptor.flags & EFFECT_FLAG_AUDIO_SOURCE_MASK) == EFFECT_FLAG_AUDIO_SOURCE_IND) { + uint32_t size = 0; + status = (*mEffectInterface)->command(mEffectInterface, + EFFECT_CMD_SET_AUDIO_SOURCE, + sizeof(audio_source_t), + &source, + &size, + NULL); + } + return status; +} + void AudioFlinger::EffectModule::setSuspended(bool suspended) { Mutex::Autolock _l(mLock); @@ -8649,7 +8806,23 @@ bool AudioFlinger::EffectModule::suspended() const return mSuspended; } -status_t AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args) +bool AudioFlinger::EffectModule::purgeHandles() +{ + bool enabled = false; + Mutex::Autolock _l(mLock); + for (size_t i = 0; i < mHandles.size(); i++) { + EffectHandle *handle = mHandles[i]; + if (handle != NULL && !handle->destroyed_l()) { + handle->effect().clear(); + if (handle->hasControl()) { + enabled = handle->enabled(); + } + } + } + return enabled; +} + +void AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; @@ -8715,8 +8888,8 @@ status_t AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args) result.append(buffer); result.append("\t\t\tPid Priority Ctrl Locked client server\n"); for (size_t i = 0; i < mHandles.size(); ++i) { - sp<EffectHandle> handle = mHandles[i].promote(); - if (handle != 0) { + EffectHandle *handle = mHandles[i]; + if (handle != NULL && !handle->destroyed_l()) { handle->dump(buffer, SIZE); result.append(buffer); } @@ -8729,8 +8902,6 @@ status_t AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args) if (locked) { mLock.unlock(); } - - return NO_ERROR; } // ---------------------------------------------------------------------------- @@ -8746,7 +8917,7 @@ AudioFlinger::EffectHandle::EffectHandle(const sp<EffectModule>& effect, int32_t priority) : BnEffect(), mEffect(effect), mEffectClient(effectClient), mClient(client), mCblk(NULL), - mPriority(priority), mHasControl(false), mEnabled(false) + mPriority(priority), mHasControl(false), mEnabled(false), mDestroyed(false) { ALOGV("constructor %p", this); @@ -8771,8 +8942,15 @@ AudioFlinger::EffectHandle::EffectHandle(const sp<EffectModule>& effect, AudioFlinger::EffectHandle::~EffectHandle() { ALOGV("Destructor %p", this); + + if (mEffect == 0) { + mDestroyed = true; + return; + } + mEffect->lock(); + mDestroyed = true; + mEffect->unlock(); disconnect(false); - ALOGV("Destructor DONE %p", this); } status_t AudioFlinger::EffectHandle::enable() @@ -8843,9 +9021,8 @@ void AudioFlinger::EffectHandle::disconnect(bool unpinIfLast) if (mEffect == 0) { return; } - mEffect->disconnect(this, unpinIfLast); - - if (mHasControl && mEnabled) { + // restore suspended effects if the disconnected handle was enabled and the last one. + if ((mEffect->disconnect(this, unpinIfLast) == 0) && mEnabled) { sp<ThreadBase> thread = mEffect->thread().promote(); if (thread != 0) { thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId()); @@ -9275,7 +9452,7 @@ size_t AudioFlinger::EffectChain::removeEffect_l(const sp<EffectModule>& effect) } // setDevice_l() must be called with PlaybackThread::mLock held -void AudioFlinger::EffectChain::setDevice_l(uint32_t device) +void AudioFlinger::EffectChain::setDevice_l(audio_devices_t device) { size_t size = mEffects.size(); for (size_t i = 0; i < size; i++) { @@ -9292,6 +9469,15 @@ void AudioFlinger::EffectChain::setMode_l(audio_mode_t mode) } } +// setAudioSource_l() must be called with PlaybackThread::mLock held +void AudioFlinger::EffectChain::setAudioSource_l(audio_source_t source) +{ + size_t size = mEffects.size(); + for (size_t i = 0; i < size; i++) { + mEffects[i]->setAudioSource(source); + } +} + // setVolume_l() must be called with PlaybackThread::mLock held bool AudioFlinger::EffectChain::setVolume_l(uint32_t *left, uint32_t *right) { @@ -9350,7 +9536,7 @@ bool AudioFlinger::EffectChain::setVolume_l(uint32_t *left, uint32_t *right) return hasControl; } -status_t AudioFlinger::EffectChain::dump(int fd, const Vector<String16>& args) +void AudioFlinger::EffectChain::dump(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; @@ -9384,8 +9570,6 @@ status_t AudioFlinger::EffectChain::dump(int fd, const Vector<String16>& args) if (locked) { mLock.unlock(); } - - return NO_ERROR; } // must be called with ThreadBase::mLock held @@ -9401,7 +9585,7 @@ void AudioFlinger::EffectChain::setEffectSuspended_l( desc = mSuspendedEffects.valueAt(index); } else { desc = new SuspendedEffectDesc(); - memcpy(&desc->mType, type, sizeof(effect_uuid_t)); + desc->mType = *type; mSuspendedEffects.add(type->timeLow, desc); ALOGV("setEffectSuspended_l() add entry for %08x", type->timeLow); } @@ -9428,10 +9612,12 @@ void AudioFlinger::EffectChain::setEffectSuspended_l( sp<EffectModule> effect = desc->mEffect.promote(); if (effect != 0) { effect->setSuspended(false); - sp<EffectHandle> handle = effect->controlHandle(); - if (handle != 0) { - effect->setEnabled(handle->enabled()); + effect->lock(); + EffectHandle *handle = effect->controlHandle_l(); + if (handle != NULL && !handle->destroyed_l()) { + effect->setEnabled_l(handle->enabled()); } + effect->unlock(); } desc->mEffect.clear(); } diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index cfd718f4..49e2b2cb 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -45,10 +45,10 @@ #include <hardware/audio.h> #include <hardware/audio_policy.h> -#include "AudioBufferProvider.h" -#include "ExtendedAudioBufferProvider.h" +#include <media/AudioBufferProvider.h> +#include <media/ExtendedAudioBufferProvider.h> #include "FastMixer.h" -#include "NBAIO.h" +#include <media/nbaio/NBAIO.h> #include "AudioWatchdog.h" #include <powermanager/IPowerManager.h> @@ -91,7 +91,7 @@ public: audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, IAudioFlinger::track_flags_t flags, const sp<IMemory>& sharedBuffer, @@ -105,9 +105,10 @@ public: audio_io_handle_t input, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, IAudioFlinger::track_flags_t flags, + pid_t tid, int *sessionId, status_t *status); @@ -121,7 +122,6 @@ public: virtual status_t setMasterMute(bool muted); virtual float masterVolume() const; - virtual float masterVolumeSW() const; virtual bool masterMute() const; virtual status_t setStreamVolume(audio_stream_type_t stream, float value, @@ -142,7 +142,8 @@ public: virtual void registerClient(const sp<IAudioFlingerClient>& client); - virtual size_t getInputBufferSize(uint32_t sampleRate, audio_format_t format, int channelCount) const; + 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, @@ -206,6 +207,9 @@ public: virtual audio_module_handle_t loadHwModule(const char *name); + virtual int32_t getPrimaryOutputSamplingRate(); + virtual int32_t getPrimaryOutputFrameCount(); + virtual status_t onTransact( uint32_t code, const Parcel& data, @@ -232,8 +236,8 @@ public: virtual ~SyncEvent() {} void trigger() { Mutex::Autolock _l(mLock); if (mCallback) mCallback(this); } - bool isCancelled() { Mutex::Autolock _l(mLock); return (mCallback == NULL); } - void cancel() {Mutex::Autolock _l(mLock); mCallback = NULL; } + bool isCancelled() const { Mutex::Autolock _l(mLock); return (mCallback == NULL); } + void cancel() { Mutex::Autolock _l(mLock); mCallback = NULL; } AudioSystem::sync_event_t type() const { return mType; } int triggerSession() const { return mTriggerSession; } int listenerSession() const { return mListenerSession; } @@ -245,7 +249,7 @@ public: const int mListenerSession; sync_event_callback_t mCallback; void * const mCookie; - Mutex mLock; + mutable Mutex mLock; }; sp<SyncEvent> createSyncEvent(AudioSystem::sync_event_t type, @@ -255,6 +259,8 @@ public: void *cookie); private: + class AudioHwDevice; // fwd declaration for findSuitableHwDev_l + audio_mode_t getMode() const { return mMode; } bool btNrecIsOff() const { return mBtNrecIsOff; } @@ -268,17 +274,17 @@ private: // RefBase virtual void onFirstRef(); - audio_hw_device_t* findSuitableHwDev_l(audio_module_handle_t module, uint32_t devices); + AudioHwDevice* findSuitableHwDev_l(audio_module_handle_t module, audio_devices_t devices); void purgeStaleEffects_l(); // 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; - // Internal dump utilites. - status_t dumpPermissionDenial(int fd, const Vector<String16>& args); - status_t dumpClients(int fd, const Vector<String16>& args); - status_t dumpInternals(int fd, const Vector<String16>& args); + // Internal dump utilities. + void dumpPermissionDenial(int fd, const Vector<String16>& args); + void dumpClients(int fd, const Vector<String16>& args); + void dumpInternals(int fd, const Vector<String16>& args); // --- Client --- class Client : public RefBase { @@ -350,11 +356,12 @@ private: RECORD // Thread class is RecordThread }; - ThreadBase (const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id, uint32_t device, type_t type); + ThreadBase (const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id, + audio_devices_t outDevice, audio_devices_t inDevice, type_t type); virtual ~ThreadBase(); - status_t dumpBase(int fd, const Vector<String16>& args); - status_t dumpEffectChains(int fd, const Vector<String16>& args); + void dumpBase(int fd, const Vector<String16>& args); + void dumpEffectChains(int fd, const Vector<String16>& args); void clearPowerManager(); @@ -380,14 +387,14 @@ private: const sp<Client>& client, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, const sp<IMemory>& sharedBuffer, int sessionId); virtual ~TrackBase(); - virtual status_t start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE, - int triggerSession = 0) = 0; + virtual status_t start(AudioSystem::sync_event_t event, + int triggerSession) = 0; virtual void stop() = 0; sp<IMemory> getCblk() const { return mCblkMemory; } audio_track_cblk_t* cblk() const { return mCblk; } @@ -412,10 +419,17 @@ private: int channelCount() const { return mChannelCount; } - uint32_t channelMask() const { return mChannelMask; } + audio_channel_mask_t channelMask() const { return mChannelMask; } int sampleRate() const; // FIXME inline after cblk sr moved + // 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 { @@ -444,8 +458,9 @@ private: /*const*/ sp<Client> mClient; // see explanation at ~TrackBase() why not const sp<IMemory> mCblkMemory; audio_track_cblk_t* mCblk; - void* mBuffer; - void* mBufferEnd; + void* mBuffer; // start of track buffer, typically in shared memory + void* mBufferEnd; // &mBuffer[mFrameCount * frameSize], where frameSize + // is based on mChannelCount and 16-bit samples uint32_t mFrameCount; // we don't really need a lock for these track_state mState; @@ -455,18 +470,67 @@ private: bool mStepServerFailed; const int mSessionId; uint8_t mChannelCount; - uint32_t mChannelMask; + audio_channel_mask_t mChannelMask; Vector < sp<SyncEvent> >mSyncEvents; }; + enum { + CFG_EVENT_IO, + CFG_EVENT_PRIO + }; + class ConfigEvent { public: - ConfigEvent() : mEvent(0), mParam(0) {} + ConfigEvent(int type) : mType(type) {} + virtual ~ConfigEvent() {} + + int type() const { return mType; } + + virtual void dump(char *buffer, size_t size) = 0; + + private: + const int mType; + }; + + class IoConfigEvent : public ConfigEvent { + 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; } + + 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 { + public: + PrioConfigEvent(pid_t pid, pid_t tid, int32_t prio) : + ConfigEvent(CFG_EVENT_PRIO), mPid(pid), mTid(tid), mPrio(prio) {} + virtual ~PrioConfigEvent() {} + + pid_t pid() const { return mPid; } + pid_t tid() const { return mTid; } + int32_t prio() const { return mPrio; } + + virtual void dump(char *buffer, size_t size) { + snprintf(buffer, size, "Prio event: pid %d, tid %d, prio %d\n", mPid, mTid, mPrio); + } - int mEvent; - int mParam; + private: + const pid_t mPid; + const pid_t mTid; + const int32_t mPrio; }; + class PMDeathRecipient : public IBinder::DeathRecipient { public: PMDeathRecipient(const wp<ThreadBase>& thread) : mThread(thread) {} @@ -483,14 +547,22 @@ private: }; virtual status_t initCheck() const = 0; + + // static externally-visible type_t type() const { return mType; } + audio_io_handle_t id() const { return mId;} + + // dynamic externally-visible uint32_t sampleRate() const { return mSampleRate; } int channelCount() const { return mChannelCount; } + audio_channel_mask_t channelMask() const { return mChannelMask; } audio_format_t format() const { return mFormat; } // Called by AudioFlinger::frameCount(audio_io_handle_t output) and effects, - // and returns the normal mix buffer's frame count. No API for HAL frame count. + // and returns the normal mix buffer's frame count. size_t frameCount() const { return mNormalFrameCount; } - void wakeUp() { mWaitWorkCV.broadcast(); } + // Return's the HAL's frame count i.e. fast mixer buffer size. + size_t frameCountHAL() const { return mFrameCount; } + // Should be "virtual status_t requestExitAndWait()" and override same // method in Thread, but Thread::requestExitAndWait() is not yet virtual. void exit(); @@ -498,12 +570,16 @@ private: virtual status_t setParameters(const String8& keyValuePairs); virtual String8 getParameters(const String8& keys) = 0; virtual void audioConfigChanged_l(int event, int param = 0) = 0; - void sendConfigEvent(int event, int param = 0); - void sendConfigEvent_l(int event, int param = 0); + 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(); - audio_io_handle_t id() const { return mId;} + + // see note at declaration of mStandby, mOutDevice and mInDevice bool standby() const { return mStandby; } - uint32_t device() const { return mDevice; } + audio_devices_t outDevice() const { return mOutDevice; } + audio_devices_t inDevice() const { return mInDevice; } + virtual audio_stream_t* stream() const = 0; sp<EffectHandle> createEffect_l( @@ -515,7 +591,7 @@ private: int *enabled, status_t *status); void disconnectEffect(const sp< EffectModule>& effect, - const wp<EffectHandle>& handle, + EffectHandle *handle, bool unpinIfLast); // return values for hasAudioSession (bit field) @@ -529,7 +605,7 @@ private: // get effect chain corresponding to session Id. sp<EffectChain> getEffectChain(int sessionId); // same as getEffectChain() but must be called with ThreadBase mutex locked - sp<EffectChain> getEffectChain_l(int sessionId); + sp<EffectChain> getEffectChain_l(int sessionId) const; // add an effect chain to the chain list (mEffectChains) virtual status_t addEffectChain_l(const sp<EffectChain>& chain) = 0; // remove an effect chain from the chain list (mEffectChains) @@ -556,7 +632,7 @@ private: virtual void detachAuxEffect_l(int effectId) {} // 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) = 0; + 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; } @@ -576,7 +652,7 @@ private: int sessionId = AUDIO_SESSION_OUTPUT_MIX); virtual status_t setSyncEvent(const sp<SyncEvent>& event) = 0; - virtual bool isValidSyncEvent(const sp<SyncEvent>& event) = 0; + virtual bool isValidSyncEvent(const sp<SyncEvent>& event) const = 0; mutable Mutex mLock; @@ -598,7 +674,7 @@ private: void releaseWakeLock_l(); void setEffectSuspended_l(const effect_uuid_t *type, bool suspend, - int sessionId = AUDIO_SESSION_OUTPUT_MIX); + int sessionId); // updated mSuspendedSessions when an effect suspended or restored void updateSuspendedSessions_l(const effect_uuid_t *type, bool suspend, @@ -606,6 +682,8 @@ private: // check if some effects must be suspended when an effect chain is added void checkSuspendOnAddEffectChain_l(const sp<EffectChain>& chain); + virtual void preExit() { } + friend class AudioFlinger; // for mEffectChains const type_t mType; @@ -617,7 +695,7 @@ private: uint32_t mSampleRate; size_t mFrameCount; // output HAL, direct output, record size_t mNormalFrameCount; // normal mixer and effects - uint32_t mChannelMask; + audio_channel_mask_t mChannelMask; uint16_t mChannelCount; size_t mFrameSize; audio_format_t mFormat; @@ -645,12 +723,22 @@ private: Vector<String8> mNewParameters; status_t mParamStatus; - Vector<ConfigEvent> mConfigEvents; - bool mStandby; + Vector<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 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. + audio_devices_t mOutDevice; // output device + audio_devices_t mInDevice; // input device + audio_source_t mAudioSource; // (see audio.h, audio_source_t) + const audio_io_handle_t mId; Vector< sp<EffectChain> > mEffectChains; - uint32_t mDevice; // output device for PlaybackThread - // input + output devices for RecordThread + static const int kNameLength = 16; // prctl(PR_SET_NAME) limit char mName[kNameLength]; sp<IPowerManager> mPowerManager; @@ -691,7 +779,7 @@ private: audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, const sp<IMemory>& sharedBuffer, int sessionId, @@ -708,9 +796,7 @@ private: void flush(); void destroy(); void mute(bool); - int name() const { - return mName; - } + int name() const { return mName; } audio_stream_type_t streamType() const { return mStreamType; @@ -767,10 +853,14 @@ private: void triggerEvents(AudioSystem::sync_event_t type); virtual bool isTimedTrack() const { return false; } bool isFastTrack() const { return (mFlags & IAudioFlinger::TRACK_FAST) != 0; } + protected: - // we don't really need a lock for these - volatile bool mMute; + // written by Track::mute() called by binder thread(s), without a mutex or barrier. + // read by Track::isMuted() called by playback thread, also without a mutex or barrier. + // The lack of mutex or barrier is safe because the mute status is only used by itself. + bool mMute; + // FILLED state is used for suppressing volume ramp at begin of playing enum {FS_INVALID, FS_FILLING, FS_FILLED, FS_ACTIVE}; mutable uint8_t mFillingUpStatus; @@ -813,11 +903,11 @@ private: audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, const sp<IMemory>& sharedBuffer, int sessionId); - ~TimedTrack(); + virtual ~TimedTrack(); class TimedBuffer { public: @@ -856,7 +946,7 @@ private: audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, const sp<IMemory>& sharedBuffer, int sessionId); @@ -905,7 +995,7 @@ private: DuplicatingThread *sourceThread, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount); virtual ~OutputTrack(); @@ -936,10 +1026,10 @@ private: }; // end of OutputTrack PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, - audio_io_handle_t id, uint32_t device, type_t type); + audio_io_handle_t id, audio_devices_t device, type_t type); virtual ~PlaybackThread(); - status_t dump(int fd, const Vector<String16>& args); + void dump(int fd, const Vector<String16>& args); // Thread virtuals virtual status_t readyToRun(); @@ -962,6 +1052,9 @@ protected: // is safe to do so. That will drop the final ref count and destroy the tracks. virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove) = 0; + // ThreadBase virtuals + virtual void preExit(); + public: virtual status_t initCheck() const { return (mOutput == NULL) ? NO_INIT : NO_ERROR; } @@ -984,7 +1077,7 @@ public: audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, const sp<IMemory>& sharedBuffer, int sessionId, @@ -996,9 +1089,19 @@ public: AudioStreamOut* clearOutput(); virtual audio_stream_t* stream() const; - void suspend() { mSuspended++; } - void restore() { if (mSuspended > 0) mSuspended--; } - bool isSuspended() const { return (mSuspended > 0); } + // a very large number of suspend() will eventually wraparound, but unlikely + void suspend() { (void) android_atomic_inc(&mSuspended); } + void restore() + { + // if restore() is done without suspend(), get back into + // range so that the next suspend() will operate correctly + if (android_atomic_dec(&mSuspended) <= 0) { + android_atomic_release_store(0, &mSuspended); + } + } + bool isSuspended() const + { return android_atomic_acquire_load(&mSuspended) > 0; } + virtual String8 getParameters(const String8& keys); virtual void audioConfigChanged_l(int event, int param = 0); status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames); @@ -1012,16 +1115,25 @@ public: virtual status_t addEffectChain_l(const sp<EffectChain>& chain); virtual size_t removeEffectChain_l(const sp<EffectChain>& chain); - virtual uint32_t hasAudioSession(int sessionId); + virtual uint32_t hasAudioSession(int sessionId) const; virtual uint32_t getStrategyForSession_l(int sessionId); virtual status_t setSyncEvent(const sp<SyncEvent>& event); - virtual bool isValidSyncEvent(const sp<SyncEvent>& event); + virtual bool isValidSyncEvent(const sp<SyncEvent>& event) const; + void invalidateTracks(audio_stream_type_t streamType); + protected: int16_t* mMixBuffer; - uint32_t mSuspended; // suspend count, > 0 means suspended + + // 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 + // concurrent use of both of them, so Audio Policy Service suspends one of the threads to + // workaround that restriction. + // 'volatile' means accessed via atomic operations and no lock. + volatile int32_t mSuspended; + int mBytesWritten; private: // mMasterMute is in both PlaybackThread and in AudioFlinger. When a @@ -1034,7 +1146,7 @@ public: // 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) = 0; + virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId) = 0; virtual void deleteTrackName_l(int name) = 0; // Time to sleep between cycles when: @@ -1069,13 +1181,14 @@ public: void readOutputParameters(); - virtual status_t dumpInternals(int fd, const Vector<String16>& args); - status_t dumpTracks(int fd, const Vector<String16>& args); + virtual void dumpInternals(int fd, const Vector<String16>& args); + void dumpTracks(int fd, const Vector<String16>& args); SortedVector< sp<Track> > mTracks; // mStreamTypes[] uses 1 additional stream type internally for the OutputTrack used by DuplicatingThread stream_type_t mStreamTypes[AUDIO_STREAM_CNT + 1]; AudioStreamOut *mOutput; + float mMasterVolume; nsecs_t mLastWriteTime; int mNumWrites; @@ -1100,7 +1213,6 @@ public: // FIXME move these declarations into the specific sub-class that needs them // MIXER only - bool longStandbyExit; uint32_t sleepTimeShift; // same as AudioFlinger::mStandbyTimeInNsecs except for DIRECT which uses a shorter value @@ -1139,19 +1251,18 @@ public: MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, audio_io_handle_t id, - uint32_t device, + audio_devices_t device, type_t type = MIXER); virtual ~MixerThread(); // Thread virtuals - void invalidateTracks(audio_stream_type_t streamType); virtual bool checkForNewParameters_l(); - virtual status_t dumpInternals(int fd, const Vector<String16>& args); + 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); + virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId); virtual void deleteTrackName_l(int name); virtual uint32_t idleSleepTimeUs() const; virtual uint32_t suspendSleepTimeUs() const; @@ -1167,9 +1278,6 @@ public: AudioMixer* mAudioMixer; // normal mixer private: -#ifdef SOAKER - Thread* mSoaker; -#endif // one-time initialization, no locks required FastMixer* mFastMixer; // non-NULL if there is also a fast mixer sp<AudioWatchdog> mAudioWatchdog; // non-0 if there is an audio watchdog thread @@ -1198,7 +1306,7 @@ public: public: DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, - audio_io_handle_t id, uint32_t device); + audio_io_handle_t id, audio_devices_t device); virtual ~DirectOutputThread(); // Thread virtuals @@ -1206,7 +1314,7 @@ public: virtual bool checkForNewParameters_l(); protected: - virtual int getTrackName_l(audio_channel_mask_t channelMask); + virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId); virtual void deleteTrackName_l(int name); virtual uint32_t activeSleepTimeUs() const; virtual uint32_t idleSleepTimeUs() const; @@ -1287,7 +1395,7 @@ private: bool reRegister); // return thread associated with primary hardware device, or NULL PlaybackThread *primaryPlaybackThread_l() const; - uint32_t primaryOutputDevice_l() const; + audio_devices_t primaryOutputDevice_l() const; sp<PlaybackThread> getEffectThread_l(int sessionId, int EffectId); @@ -1321,6 +1429,7 @@ private: // record thread class RecordThread : public ThreadBase, public AudioBufferProvider + // derives from AudioBufferProvider interface for use by resampler { public: @@ -1331,18 +1440,22 @@ private: const sp<Client>& client, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, int sessionId); virtual ~RecordTrack(); - virtual status_t start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE, - int triggerSession = 0); + virtual status_t start(AudioSystem::sync_event_t event, int triggerSession); virtual void stop(); - bool overflow() { bool tmp = mOverflow; mOverflow = false; return tmp; } + void destroy(); + + // clear the buffer overflow flag + void clearOverflow() { mOverflow = false; } + // set the buffer overflow flag and return previous value bool setOverflow() { bool tmp = mOverflow; mOverflow = true; return tmp; } + static void appendDumpHeader(String8& result); void dump(char* buffer, size_t size); private: @@ -1355,19 +1468,25 @@ private: virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts = kInvalidPTS); // releaseBuffer() not overridden - bool mOverflow; + bool mOverflow; // overflow on most recent attempt to fill client buffer }; - RecordThread(const sp<AudioFlinger>& audioFlinger, AudioStreamIn *input, uint32_t sampleRate, - uint32_t channels, + audio_channel_mask_t channelMask, audio_io_handle_t id, - uint32_t device); + audio_devices_t device); virtual ~RecordThread(); - // Thread + // no addTrack_l ? + void destroyTrack_l(const sp<RecordTrack>& track); + void removeTrack_l(const sp<RecordTrack>& track); + + void dumpInternals(int fd, const Vector<String16>& args); + void dumpTracks(int fd, const Vector<String16>& args); + + // Thread virtuals virtual bool threadLoop(); virtual status_t readyToRun(); @@ -1379,17 +1498,22 @@ private: const sp<AudioFlinger::Client>& client, uint32_t sampleRate, audio_format_t format, - int channelMask, + audio_channel_mask_t channelMask, int frameCount, int sessionId, + IAudioFlinger::track_flags_t flags, + pid_t tid, status_t *status); status_t start(RecordTrack* recordTrack, AudioSystem::sync_event_t event, int triggerSession); - void stop(RecordTrack* recordTrack); - status_t dump(int fd, const Vector<String16>& args); - AudioStreamIn* getInput() const; + + // ask the thread to stop the specified track, and + // return true if the caller should then do it's part of the stopping process + bool stop_l(RecordTrack* recordTrack); + + void dump(int fd, const Vector<String16>& args); AudioStreamIn* clearInput(); virtual audio_stream_t* stream() const; @@ -1405,11 +1529,15 @@ private: virtual status_t addEffectChain_l(const sp<EffectChain>& chain); virtual size_t removeEffectChain_l(const sp<EffectChain>& chain); - virtual uint32_t hasAudioSession(int sessionId); - RecordTrack* track(); + virtual uint32_t hasAudioSession(int sessionId) const; + + // Return the set of unique session IDs across all tracks. + // The keys are the session IDs, and the associated values are meaningless. + // FIXME replace by Set [and implement Bag/Multiset for other uses]. + KeyedVector<int, bool> sessionIds() const; virtual status_t setSyncEvent(const sp<SyncEvent>& event); - virtual bool isValidSyncEvent(const sp<SyncEvent>& event); + virtual bool isValidSyncEvent(const sp<SyncEvent>& event) const; static void syncStartEventCallback(const wp<SyncEvent>& event); void handleSyncStartEvent(const sp<SyncEvent>& event); @@ -1417,9 +1545,16 @@ private: private: void clearSyncStartEvent(); - RecordThread(); + // Enter standby if not already in standby, and set mStandby flag + void standby(); + + // Call the HAL standby method unconditionally, and don't change mStandby flag + void inputStandBy(); + AudioStreamIn *mInput; - RecordTrack* mTrack; + SortedVector < sp<RecordTrack> > mTracks; + // mActiveTrack has dual roles: it indicates the current active track, and + // is used together with mStartStopCond to indicate start()/stop() progress sp<RecordTrack> mActiveTrack; Condition mStartStopCond; AudioResampler *mResampler; @@ -1445,12 +1580,15 @@ private: RecordHandle(const sp<RecordThread::RecordTrack>& recordTrack); virtual ~RecordHandle(); virtual sp<IMemory> getCblk() const; - virtual status_t start(int event, int triggerSession); + virtual status_t start(int /*AudioSystem::sync_event_t*/ event, int triggerSession); virtual void stop(); virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); private: const sp<RecordThread::RecordTrack> mRecordTrack; + + // for use from destructor + void stop_nonvirtual(); }; //--- Audio Effect Management @@ -1510,6 +1648,7 @@ private: return mSessionId; } status_t setEnabled(bool enabled); + status_t setEnabled_l(bool enabled); bool isEnabled() const; bool isProcessEnabled() const; @@ -1521,27 +1660,31 @@ private: void setThread(const wp<ThreadBase>& thread) { mThread = thread; } const wp<ThreadBase>& thread() { return mThread; } - status_t addHandle(const sp<EffectHandle>& handle); - void disconnect(const wp<EffectHandle>& handle, bool unpinIfLast); - size_t removeHandle (const wp<EffectHandle>& handle); + status_t addHandle(EffectHandle *handle); + size_t disconnect(EffectHandle *handle, bool unpinIfLast); + size_t removeHandle(EffectHandle *handle); - effect_descriptor_t& desc() { return mDescriptor; } + const effect_descriptor_t& desc() const { return mDescriptor; } wp<EffectChain>& chain() { return mChain; } - status_t setDevice(uint32_t device); + status_t setDevice(audio_devices_t device); status_t setVolume(uint32_t *left, uint32_t *right, bool controller); status_t setMode(audio_mode_t mode); + status_t setAudioSource(audio_source_t source); status_t start(); status_t stop(); void setSuspended(bool suspended); bool suspended() const; - sp<EffectHandle> controlHandle(); + EffectHandle* controlHandle_l(); bool isPinned() const { return mPinned; } void unPin() { mPinned = false; } + bool purgeHandles(); + void lock() { mLock.lock(); } + void unlock() { mLock.unlock(); } - status_t dump(int fd, const Vector<String16>& args); + void dump(int fd, const Vector<String16>& args); protected: friend class AudioFlinger; // for mHandles @@ -1559,14 +1702,14 @@ private: mutable Mutex mLock; // mutex for process, commands and handles list protection wp<ThreadBase> mThread; // parent thread wp<EffectChain> mChain; // parent effect chain - int mId; // this instance unique ID - int mSessionId; // audio session ID - effect_descriptor_t mDescriptor;// effect descriptor received from effect engine + const int mId; // this instance unique ID + const int mSessionId; // audio session ID + const effect_descriptor_t mDescriptor;// effect descriptor received from effect engine effect_config_t mConfig; // input and output audio configuration effect_handle_t mEffectInterface; // Effect module C API status_t mStatus; // initialization status effect_state mState; // current activation state - Vector< wp<EffectHandle> > mHandles; // list of client handles + Vector<EffectHandle *> mHandles; // list of client handles // First handle in mHandles has highest priority and controls the effect module uint32_t mMaxDisableWaitCnt; // maximum grace period before forcing an effect off after // sending disable command. @@ -1624,6 +1767,8 @@ mutable Mutex mLock; // mutex for process, commands and handl int priority() const { return mPriority; } bool hasControl() const { return mHasControl; } sp<EffectModule> effect() const { return mEffect; } + // destroyed_l() must be called with the associated EffectModule mLock held + bool destroyed_l() const { return mDestroyed; } void dump(char* buffer, size_t size); @@ -1642,6 +1787,8 @@ mutable Mutex mLock; // mutex for process, commands and handl bool mHasControl; // true if this handle is controlling the effect bool mEnabled; // cached enable state: needed when the effect is // restored after being suspended + bool mDestroyed; // Set to true by destructor. Access with EffectModule + // mLock held }; // the EffectChain class represents a group of effects associated to one audio session. @@ -1684,8 +1831,9 @@ mutable Mutex mLock; // mutex for process, commands and handl sp<EffectModule> getEffectFromId_l(int id); sp<EffectModule> getEffectFromType_l(const effect_uuid_t *type); bool setVolume_l(uint32_t *left, uint32_t *right); - void setDevice_l(uint32_t device); + void setDevice_l(audio_devices_t device); void setMode_l(audio_mode_t mode); + void setAudioSource_l(audio_source_t source); void setInBuffer(int16_t *buffer, bool ownsBuffer = false) { mInBuffer = buffer; @@ -1703,12 +1851,12 @@ mutable Mutex mLock; // mutex for process, commands and handl void incTrackCnt() { android_atomic_inc(&mTrackCnt); } void decTrackCnt() { android_atomic_dec(&mTrackCnt); } - int32_t trackCnt() const { return mTrackCnt;} + int32_t trackCnt() const { return android_atomic_acquire_load(&mTrackCnt); } void incActiveTrackCnt() { android_atomic_inc(&mActiveTrackCnt); mTailBufferCount = mMaxTailBuffers; } void decActiveTrackCnt() { android_atomic_dec(&mActiveTrackCnt); } - int32_t activeTrackCnt() const { return mActiveTrackCnt;} + int32_t activeTrackCnt() const { return android_atomic_acquire_load(&mActiveTrackCnt); } uint32_t strategy() const { return mStrategy; } void setStrategy(uint32_t strategy) @@ -1725,7 +1873,7 @@ mutable Mutex mLock; // mutex for process, commands and handl void clearInputBuffer(); - status_t dump(int fd, const Vector<String16>& args); + void dump(int fd, const Vector<String16>& args); protected: friend class AudioFlinger; // for mThread, mEffects @@ -1760,8 +1908,11 @@ mutable Mutex mLock; // mutex for process, commands and handl int mSessionId; // audio session ID int16_t *mInBuffer; // chain input buffer int16_t *mOutBuffer; // chain output buffer - volatile int32_t mActiveTrackCnt; // number of active tracks connected - volatile int32_t mTrackCnt; // number of tracks connected + + // 'volatile' here means these are accessed with atomic operations instead of mutex + volatile int32_t mActiveTrackCnt; // number of active tracks connected + volatile int32_t mTrackCnt; // number of tracks connected + int32_t mTailBufferCount; // current effect tail buffer count int32_t mMaxTailBuffers; // maximum effect tail buffers bool mOwnInBuffer; // true if the chain owns its input buffer @@ -1778,24 +1929,59 @@ mutable Mutex mLock; // mutex for process, commands and handl KeyedVector< int, sp<SuspendedEffectDesc> > mSuspendedEffects; }; + class AudioHwDevice { + public: + enum Flags { + AHWD_CAN_SET_MASTER_VOLUME = 0x1, + AHWD_CAN_SET_MASTER_MUTE = 0x2, + }; + + AudioHwDevice(const char *moduleName, + audio_hw_device_t *hwDevice, + Flags flags) + : mModuleName(strdup(moduleName)) + , mHwDevice(hwDevice) + , mFlags(flags) { } + /*virtual*/ ~AudioHwDevice() { free((void *)mModuleName); } + + bool canSetMasterVolume() const { + return (0 != (mFlags & AHWD_CAN_SET_MASTER_VOLUME)); + } + + bool canSetMasterMute() const { + return (0 != (mFlags & AHWD_CAN_SET_MASTER_MUTE)); + } + + const char *moduleName() const { return mModuleName; } + audio_hw_device_t *hwDevice() const { return mHwDevice; } + private: + const char * const mModuleName; + audio_hw_device_t * const mHwDevice; + Flags mFlags; + }; + // AudioStreamOut and AudioStreamIn are immutable, so their fields are const. // For emphasis, we could also make all pointers to them be "const *", // but that would clutter the code unnecessarily. struct AudioStreamOut { - audio_hw_device_t* const hwDev; + AudioHwDevice* const audioHwDev; audio_stream_out_t* const stream; - AudioStreamOut(audio_hw_device_t *dev, audio_stream_out_t *out) : - hwDev(dev), stream(out) {} + audio_hw_device_t* hwDev() const { return audioHwDev->hwDevice(); } + + AudioStreamOut(AudioHwDevice *dev, audio_stream_out_t *out) : + audioHwDev(dev), stream(out) {} }; struct AudioStreamIn { - audio_hw_device_t* const hwDev; + AudioHwDevice* const audioHwDev; audio_stream_in_t* const stream; - AudioStreamIn(audio_hw_device_t *dev, audio_stream_in_t *in) : - hwDev(dev), stream(in) {} + audio_hw_device_t* hwDev() const { return audioHwDev->hwDevice(); } + + AudioStreamIn(AudioHwDevice *dev, audio_stream_in_t *in) : + audioHwDev(dev), stream(in) {} }; // for mAudioSessionRefs only @@ -1807,41 +1993,6 @@ mutable Mutex mLock; // mutex for process, commands and handl int mCnt; }; - enum master_volume_support { - // MVS_NONE: - // Audio HAL has no support for master volume, either setting or - // getting. All master volume control must be implemented in SW by the - // AudioFlinger mixing core. - MVS_NONE, - - // MVS_SETONLY: - // Audio HAL has support for setting master volume, but not for getting - // master volume (original HAL design did not include a getter). - // AudioFlinger needs to keep track of the last set master volume in - // addition to needing to set an initial, default, master volume at HAL - // load time. - MVS_SETONLY, - - // MVS_FULL: - // Audio HAL has support both for setting and getting master volume. - // AudioFlinger should send all set and get master volume requests - // directly to the HAL. - MVS_FULL, - }; - - class AudioHwDevice { - public: - AudioHwDevice(const char *moduleName, audio_hw_device_t *hwDevice) : - mModuleName(strdup(moduleName)), mHwDevice(hwDevice){} - ~AudioHwDevice() { free((void *)mModuleName); } - - const char *moduleName() const { return mModuleName; } - audio_hw_device_t *hwDevice() const { return mHwDevice; } - private: - const char * const mModuleName; - audio_hw_device_t * const mHwDevice; - }; - mutable Mutex mLock; DefaultKeyedVector< pid_t, wp<Client> > mClients; // see ~Client() @@ -1851,7 +2002,7 @@ mutable Mutex mLock; // mutex for process, commands and handl // always take mLock before mHardwareLock // These two fields are immutable after onFirstRef(), so no lock needed to access - audio_hw_device_t* mPrimaryHardwareDev; // mAudioHwDevs[0] or NULL + AudioHwDevice* mPrimaryHardwareDev; // mAudioHwDevs[0] or NULL DefaultKeyedVector<audio_module_handle_t, AudioHwDevice*> mAudioHwDevs; // for dump, indicates which hardware operation is currently in progress (but not stream ops) @@ -1875,6 +2026,8 @@ mutable Mutex mLock; // mutex for process, commands and handl AUDIO_HW_GET_INPUT_BUFFER_SIZE, // get_input_buffer_size AUDIO_HW_GET_MASTER_VOLUME, // get_master_volume AUDIO_HW_GET_PARAMETER, // get_parameters + AUDIO_HW_SET_MASTER_MUTE, // set_master_mute + AUDIO_HW_GET_MASTER_MUTE, // get_master_mute }; mutable hardware_call_state mHardwareStatus; // for dump only @@ -1883,11 +2036,10 @@ mutable Mutex mLock; // mutex for process, commands and handl DefaultKeyedVector< audio_io_handle_t, sp<PlaybackThread> > mPlaybackThreads; stream_type_t mStreamTypes[AUDIO_STREAM_CNT]; - // both are protected by mLock + // member variables below are protected by mLock float mMasterVolume; - float mMasterVolumeSW; - master_volume_support mMasterVolumeSupportLvl; bool mMasterMute; + // end of variables protected by mLock DefaultKeyedVector< audio_io_handle_t, sp<RecordThread> > mRecordThreads; @@ -1900,8 +2052,7 @@ mutable Mutex mLock; // mutex for process, commands and handl Vector<AudioSessionRef*> mAudioSessionRefs; float masterVolume_l() const; - float masterVolumeSW_l() const { return mMasterVolumeSW; } - bool masterMute_l() const { return mMasterMute; } + bool masterMute_l() const; audio_module_handle_t loadHwModule_l(const char *name); Vector < sp<SyncEvent> > mPendingSyncEvents; // sync events awaiting for a session @@ -1910,6 +2061,9 @@ mutable Mutex mLock; // mutex for process, commands and handl private: sp<Client> registerPid_l(pid_t pid); // always returns non-0 + // for use from destructor + status_t closeOutput_nonvirtual(audio_io_handle_t output); + status_t closeInput_nonvirtual(audio_io_handle_t input); }; diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp index 3e4c55e0..1e4049ab 100644 --- a/services/audioflinger/AudioMixer.cpp +++ b/services/audioflinger/AudioMixer.cpp @@ -163,7 +163,7 @@ AudioMixer::~AudioMixer() delete [] mState.resampleTemp; } -int AudioMixer::getTrackName(audio_channel_mask_t channelMask) +int AudioMixer::getTrackName(audio_channel_mask_t channelMask, int sessionId) { uint32_t names = (~mTrackNames) & mConfiguredNames; if (names != 0) { @@ -189,6 +189,7 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask) t->enabled = false; t->format = 16; t->channelMask = AUDIO_CHANNEL_OUT_STEREO; + t->sessionId = sessionId; // setBufferProvider(name, AudioBufferProvider *) is required before enable(name) t->bufferProvider = NULL; t->downmixerBufferProvider = NULL; @@ -270,7 +271,7 @@ status_t AudioMixer::prepareTrackForDownmix(track_t* pTrack, int trackName) } if (EffectCreate(&dwnmFxDesc.uuid, - -2 /*sessionId*/, -2 /*ioId*/,// both not relevant here, using random value + 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; @@ -416,7 +417,7 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) case TRACK: switch (param) { case CHANNEL_MASK: { - uint32_t mask = (uint32_t)value; + audio_channel_mask_t mask = (audio_channel_mask_t) value; if (track.channelMask != mask) { uint32_t channelCount = popcount(mask); ALOG_ASSERT((channelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX) && channelCount); @@ -538,11 +539,23 @@ bool AudioMixer::track_t::setResampler(uint32_t value, uint32_t devSampleRate) if (sampleRate != value) { sampleRate = value; if (resampler == NULL) { + ALOGV("creating resampler from track %d Hz to device %d Hz", value, 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; + } else { + quality = AudioResampler::DEFAULT_QUALITY; + } resampler = AudioResampler::create( format, // the resampler sees the number of channels after the downmixer, if any downmixerBufferProvider != NULL ? MAX_NUM_CHANNELS : channelCount, - devSampleRate); + devSampleRate, quality); resampler->setLocalTimeFreq(localTimeFreq); } return true; @@ -1085,6 +1098,12 @@ void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts) e0 &= ~(1<<i); track_t& t = state->tracks[i]; t.buffer.frameCount = state->frameCount; + int valid = t.bufferProvider->getValid(); + if (valid != AudioBufferProvider::kValid) { + ALOGE("invalid bufferProvider=%p name=%d frameCount=%d valid=%#x enabledTracks=%#x", + t.bufferProvider, i, t.buffer.frameCount, valid, enabledTracks); + // expect to crash + } t.bufferProvider->getNextBuffer(&t.buffer, pts); t.frameCount = t.buffer.frameCount; t.in = t.buffer.raw; diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h index 46deae75..63333578 100644 --- a/services/audioflinger/AudioMixer.h +++ b/services/audioflinger/AudioMixer.h @@ -23,7 +23,7 @@ #include <utils/threads.h> -#include "AudioBufferProvider.h" +#include <media/AudioBufferProvider.h> #include "AudioResampler.h" #include <audio_effects/effect_downmix.h> @@ -91,7 +91,7 @@ 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 getTrackName(audio_channel_mask_t channelMask, int sessionId); // Free an allocated track by name void deleteTrackName(int name); @@ -192,7 +192,7 @@ private: DownmixerBufferProvider* downmixerBufferProvider; // 4 bytes - int32_t padding; + int32_t sessionId; // 16-byte boundary diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp index 0d139703..55e08269 100644 --- a/services/audioflinger/AudioPolicyService.cpp +++ b/services/audioflinger/AudioPolicyService.cpp @@ -49,6 +49,8 @@ static const char kCmdDeadlockedString[] = "AudioPolicyService command thread ma static const int kDumpLockRetries = 50; static const int kDumpLockSleepUs = 20000; +static const nsecs_t kAudioCommandTimeout = 3000000000; // 3 seconds + namespace { extern struct audio_policy_service_ops aps_ops; }; @@ -91,10 +93,6 @@ AudioPolicyService::AudioPolicyService() if (rc) return; - property_get("ro.camera.sound.forced", value, "0"); - forced_val = strtol(value, NULL, 0); - mpAudioPolicy->set_can_mute_enforced_audible(mpAudioPolicy, !forced_val); - ALOGI("Loaded audio policy from %s (%s)", module->name, module->id); // load audio pre processing modules @@ -223,7 +221,7 @@ audio_policy_forced_cfg_t AudioPolicyService::getForceUse(audio_policy_force_use audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream, uint32_t samplingRate, audio_format_t format, - uint32_t channels, + audio_channel_mask_t channelMask, audio_output_flags_t flags) { if (mpAudioPolicy == NULL) { @@ -231,7 +229,7 @@ audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream, } ALOGV("getOutput() tid %d", gettid()); Mutex::Autolock _l(mLock); - return mpAudioPolicy->get_output(mpAudioPolicy, stream, samplingRate, format, channels, flags); + return mpAudioPolicy->get_output(mpAudioPolicy, stream, samplingRate, format, channelMask, flags); } status_t AudioPolicyService::startOutput(audio_io_handle_t output, @@ -271,8 +269,7 @@ void AudioPolicyService::releaseOutput(audio_io_handle_t output) audio_io_handle_t AudioPolicyService::getInput(audio_source_t inputSource, uint32_t samplingRate, audio_format_t format, - uint32_t channels, - audio_in_acoustics_t acoustics, + audio_channel_mask_t channelMask, int audioSession) { if (mpAudioPolicy == NULL) { @@ -283,8 +280,9 @@ audio_io_handle_t AudioPolicyService::getInput(audio_source_t inputSource, 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, channels, acoustics); + format, channelMask, (audio_in_acoustics_t) 0); if (input == 0) { return input; @@ -373,6 +371,7 @@ status_t AudioPolicyService::initStreamVolume(audio_stream_type_t stream, 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; } @@ -390,7 +389,7 @@ status_t AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream, 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, @@ -411,6 +410,7 @@ status_t AudioPolicyService::getStreamVolumeIndex(audio_stream_type_t stream, 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, @@ -439,7 +439,7 @@ audio_devices_t AudioPolicyService::getDevicesForStream(audio_stream_type_t stre return mpAudioPolicy->get_devices_for_stream(mpAudioPolicy, stream); } -audio_io_handle_t AudioPolicyService::getOutputForEffect(effect_descriptor_t *desc) +audio_io_handle_t AudioPolicyService::getOutputForEffect(const effect_descriptor_t *desc) { if (mpAudioPolicy == NULL) { return NO_INIT; @@ -448,7 +448,7 @@ audio_io_handle_t AudioPolicyService::getOutputForEffect(effect_descriptor_t *de return mpAudioPolicy->get_output_for_effect(mpAudioPolicy, desc); } -status_t AudioPolicyService::registerEffect(effect_descriptor_t *desc, +status_t AudioPolicyService::registerEffect(const effect_descriptor_t *desc, audio_io_handle_t io, uint32_t strategy, int session, @@ -485,6 +485,18 @@ bool AudioPolicyService::isStreamActive(audio_stream_type_t stream, uint32_t inP return mpAudioPolicy->is_stream_active(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) @@ -512,7 +524,7 @@ status_t AudioPolicyService::queryDefaultPreProcessing(int audioSession, for (size_t i = 0; i < effects.size(); i++) { effect_descriptor_t desc = effects[i]->descriptor(); if (i < *count) { - memcpy(descriptors + i, &desc, sizeof(effect_descriptor_t)); + descriptors[i] = desc; } } if (effects.size() > *count) { @@ -687,7 +699,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() data->mIO); if (command->mWaitStatus) { command->mCond.signal(); - mWaitWorkCV.wait(mLock); + command->mCond.waitRelative(mLock, kAudioCommandTimeout); } delete data; }break; @@ -698,7 +710,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() command->mStatus = AudioSystem::setParameters(data->mIO, data->mKeyValuePairs); if (command->mWaitStatus) { command->mCond.signal(); - mWaitWorkCV.wait(mLock); + command->mCond.waitRelative(mLock, kAudioCommandTimeout); } delete data; }break; @@ -709,7 +721,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop() command->mStatus = AudioSystem::setVoiceVolume(data->mVolume); if (command->mWaitStatus) { command->mCond.signal(); - mWaitWorkCV.wait(mLock); + command->mCond.waitRelative(mLock, kAudioCommandTimeout); } delete data; }break; @@ -778,7 +790,6 @@ void AudioPolicyService::AudioCommandThread::startToneCommand(ToneGenerator::ton data->mType = type; data->mStream = stream; command->mParam = (void *)data; - command->mWaitStatus = false; Mutex::Autolock _l(mLock); insertCommand_l(command); ALOGV("AudioCommandThread() adding tone start type %d, stream %d", type, stream); @@ -790,7 +801,6 @@ void AudioPolicyService::AudioCommandThread::stopToneCommand() AudioCommand *command = new AudioCommand(); command->mCommand = STOP_TONE; command->mParam = NULL; - command->mWaitStatus = false; Mutex::Autolock _l(mLock); insertCommand_l(command); ALOGV("AudioCommandThread() adding tone stop"); @@ -811,11 +821,6 @@ status_t AudioPolicyService::AudioCommandThread::volumeCommand(audio_stream_type data->mVolume = volume; data->mIO = output; command->mParam = data; - if (delayMs == 0) { - command->mWaitStatus = true; - } else { - command->mWaitStatus = false; - } Mutex::Autolock _l(mLock); insertCommand_l(command, delayMs); ALOGV("AudioCommandThread() adding set volume stream %d, volume %f, output %d", @@ -824,7 +829,7 @@ status_t AudioPolicyService::AudioCommandThread::volumeCommand(audio_stream_type if (command->mWaitStatus) { command->mCond.wait(mLock); status = command->mStatus; - mWaitWorkCV.signal(); + command->mCond.signal(); } return status; } @@ -841,11 +846,6 @@ status_t AudioPolicyService::AudioCommandThread::parametersCommand(audio_io_hand data->mIO = ioHandle; data->mKeyValuePairs = String8(keyValuePairs); command->mParam = data; - if (delayMs == 0) { - command->mWaitStatus = true; - } else { - command->mWaitStatus = false; - } Mutex::Autolock _l(mLock); insertCommand_l(command, delayMs); ALOGV("AudioCommandThread() adding set parameter string %s, io %d ,delay %d", @@ -854,7 +854,7 @@ status_t AudioPolicyService::AudioCommandThread::parametersCommand(audio_io_hand if (command->mWaitStatus) { command->mCond.wait(mLock); status = command->mStatus; - mWaitWorkCV.signal(); + command->mCond.signal(); } return status; } @@ -868,11 +868,6 @@ status_t AudioPolicyService::AudioCommandThread::voiceVolumeCommand(float volume VoiceVolumeData *data = new VoiceVolumeData(); data->mVolume = volume; command->mParam = data; - if (delayMs == 0) { - command->mWaitStatus = true; - } else { - command->mWaitStatus = false; - } Mutex::Autolock _l(mLock); insertCommand_l(command, delayMs); ALOGV("AudioCommandThread() adding set voice volume volume %f", volume); @@ -880,7 +875,7 @@ status_t AudioPolicyService::AudioCommandThread::voiceVolumeCommand(float volume if (command->mWaitStatus) { command->mCond.wait(mLock); status = command->mStatus; - mWaitWorkCV.signal(); + command->mCond.signal(); } return status; } @@ -891,6 +886,7 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma ssize_t i; // not size_t because i will count down to -1 Vector <AudioCommand *> removedCommands; + nsecs_t time = 0; command->mTime = systemTime() + milliseconds(delayMs); // acquire wake lock to make sure delayed commands are processed @@ -936,6 +932,7 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma } else { data2->mKeyValuePairs = param2.toString(); } + time = command2->mTime; } break; case SET_VOLUME: { @@ -946,6 +943,7 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma ALOGV("Filtering out volume command on output %d for stream %d", data->mIO, data->mStream); removedCommands.add(command2); + time = command2->mTime; } break; case START_TONE: case STOP_TONE: @@ -967,6 +965,17 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma } removedCommands.clear(); + // wait for status only if delay is 0 and command time was not modified above + if (delayMs == 0 && time == 0) { + command->mWaitStatus = true; + } else { + command->mWaitStatus = false; + } + // update command time if modified above + if (time != 0) { + command->mTime = time; + } + // 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()); @@ -1422,7 +1431,7 @@ static int aps_restore_output(void *service, audio_io_handle_t output) return af->restoreOutput(output); } -// deprecated: replaced by aps_open_input_on_module() +// 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, diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h index fbca0008..63f95494 100644 --- a/services/audioflinger/AudioPolicyService.h +++ b/services/audioflinger/AudioPolicyService.h @@ -64,7 +64,7 @@ public: virtual audio_io_handle_t getOutput(audio_stream_type_t stream, uint32_t samplingRate = 0, audio_format_t format = AUDIO_FORMAT_DEFAULT, - uint32_t channels = 0, + audio_channel_mask_t channelMask = 0, audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE); virtual status_t startOutput(audio_io_handle_t output, @@ -77,9 +77,7 @@ public: virtual audio_io_handle_t getInput(audio_source_t inputSource, uint32_t samplingRate = 0, audio_format_t format = AUDIO_FORMAT_DEFAULT, - uint32_t channels = 0, - audio_in_acoustics_t acoustics = - (audio_in_acoustics_t)0 /*AUDIO_IN_ACOUSTICS_NONE*/, + 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); @@ -97,8 +95,8 @@ public: 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(effect_descriptor_t *desc); - virtual status_t registerEffect(effect_descriptor_t *desc, + 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, @@ -106,6 +104,7 @@ public: 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 isSourceActive(audio_source_t source) const; virtual status_t queryDefaultPreProcessing(int audioSession, effect_descriptor_t *descriptors, diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp index fbb54cf9..2c3c719b 100644 --- a/services/audioflinger/AudioResampler.cpp +++ b/services/audioflinger/AudioResampler.cpp @@ -23,10 +23,8 @@ #include <cutils/log.h> #include <cutils/properties.h> #include "AudioResampler.h" -#if 0 #include "AudioResamplerSinc.h" #include "AudioResamplerCubic.h" -#endif #ifdef __arm__ #include <machine/cpu-features.h> @@ -42,7 +40,7 @@ namespace android { class AudioResamplerOrder1 : public AudioResampler { public: AudioResamplerOrder1(int bitDepth, int inChannelCount, int32_t sampleRate) : - AudioResampler(bitDepth, inChannelCount, sampleRate), mX0L(0), mX0R(0) { + AudioResampler(bitDepth, inChannelCount, sampleRate, LOW_QUALITY), mX0L(0), mX0R(0) { } virtual void resample(int32_t* out, size_t outFrameCount, AudioBufferProvider* provider); @@ -79,38 +77,129 @@ private: int mX0R; }; +bool AudioResampler::qualityIsSupported(src_quality quality) +{ + switch (quality) { + case DEFAULT_QUALITY: + case LOW_QUALITY: + case MED_QUALITY: + case HIGH_QUALITY: + case VERY_HIGH_QUALITY: + return true; + default: + return false; + } +} + // ---------------------------------------------------------------------------- -AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount, - int32_t sampleRate, int quality) { - // can only create low quality resample now - AudioResampler* resampler; +static pthread_once_t once_control = PTHREAD_ONCE_INIT; +static AudioResampler::src_quality defaultQuality = AudioResampler::DEFAULT_QUALITY; +void AudioResampler::init_routine() +{ char value[PROPERTY_VALUE_MAX]; - if (property_get("af.resampler.quality", value, 0)) { - quality = atoi(value); - ALOGD("forcing AudioResampler quality to %d", quality); + if (property_get("af.resampler.quality", value, NULL) > 0) { + char *endptr; + unsigned long l = strtoul(value, &endptr, 0); + if (*endptr == '\0') { + defaultQuality = (src_quality) l; + ALOGD("forcing AudioResampler quality to %d", defaultQuality); + if (defaultQuality < DEFAULT_QUALITY || defaultQuality > VERY_HIGH_QUALITY) { + defaultQuality = DEFAULT_QUALITY; + } + } } +} - if (quality == DEFAULT) - quality = LOW_QUALITY; +uint32_t AudioResampler::qualityMHz(src_quality quality) +{ + switch (quality) { + default: + case DEFAULT_QUALITY: + case LOW_QUALITY: + return 3; + case MED_QUALITY: + return 6; + case HIGH_QUALITY: + return 20; + case VERY_HIGH_QUALITY: + return 34; + } +} + +static const uint32_t maxMHz = 130; // an arbitrary number that permits 3 VHQ, should be tunable +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +static uint32_t currentMHz = 0; + +AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount, + int32_t sampleRate, src_quality quality) { + + bool atFinalQuality; + if (quality == DEFAULT_QUALITY) { + // read the resampler default quality property the first time it is needed + int ok = pthread_once(&once_control, init_routine); + if (ok != 0) { + ALOGE("%s pthread_once failed: %d", __func__, ok); + } + quality = defaultQuality; + atFinalQuality = false; + } else { + atFinalQuality = true; + } + + // naive implementation of CPU load throttling doesn't account for whether resampler is active + pthread_mutex_lock(&mutex); + for (;;) { + uint32_t deltaMHz = qualityMHz(quality); + uint32_t newMHz = currentMHz + deltaMHz; + if ((qualityIsSupported(quality) && newMHz <= maxMHz) || atFinalQuality) { + ALOGV("resampler load %u -> %u MHz due to delta +%u MHz from quality %d", + currentMHz, newMHz, deltaMHz, quality); + currentMHz = newMHz; + break; + } + // 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; + case MED_QUALITY: + quality = LOW_QUALITY; + break; + case HIGH_QUALITY: + quality = MED_QUALITY; + break; + case VERY_HIGH_QUALITY: + quality = HIGH_QUALITY; + break; + } + } + pthread_mutex_unlock(&mutex); + + AudioResampler* resampler; switch (quality) { default: + case DEFAULT_QUALITY: case LOW_QUALITY: ALOGV("Create linear Resampler"); resampler = new AudioResamplerOrder1(bitDepth, inChannelCount, sampleRate); break; -#if 0 case MED_QUALITY: ALOGV("Create cubic Resampler"); resampler = new AudioResamplerCubic(bitDepth, inChannelCount, sampleRate); break; case HIGH_QUALITY: - ALOGV("Create sinc Resampler"); + ALOGV("Create HIGH_QUALITY sinc Resampler"); resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate); break; -#endif + case VERY_HIGH_QUALITY: + ALOGV("Create VERY_HIGH_QUALITY sinc Resampler = %d", quality); + resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate, quality); + break; } // initialize resampler @@ -119,17 +208,20 @@ AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount, } AudioResampler::AudioResampler(int bitDepth, int inChannelCount, - int32_t sampleRate) : + int32_t sampleRate, src_quality quality) : mBitDepth(bitDepth), mChannelCount(inChannelCount), mSampleRate(sampleRate), mInSampleRate(sampleRate), mInputIndex(0), mPhaseFraction(0), mLocalTimeFreq(0), - mPTS(AudioBufferProvider::kInvalidPTS) { + 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); } + if (sampleRate <= 0) { + ALOGE("Unsupported sample rate %d Hz", sampleRate); + } // initialize common members mVolume[0] = mVolume[1] = 0; @@ -138,6 +230,15 @@ AudioResampler::AudioResampler(int bitDepth, int inChannelCount, } AudioResampler::~AudioResampler() { + pthread_mutex_lock(&mutex); + src_quality quality = getQuality(); + uint32_t deltaMHz = qualityMHz(quality); + int32_t newMHz = currentMHz - deltaMHz; + ALOGV("resampler load %u -> %d MHz due to delta -%u MHz from quality %d", + currentMHz, newMHz, deltaMHz, quality); + LOG_ALWAYS_FATAL_IF(newMHz < 0, "negative resampler load %d MHz", newMHz); + currentMHz = newMHz; + pthread_mutex_unlock(&mutex); } void AudioResampler::setSampleRate(int32_t inSampleRate) { diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h index 1610e003..2b8694f1 100644 --- a/services/audioflinger/AudioResampler.h +++ b/services/audioflinger/AudioResampler.h @@ -20,7 +20,7 @@ #include <stdint.h> #include <sys/types.h> -#include "AudioBufferProvider.h" +#include <media/AudioBufferProvider.h> namespace android { // ---------------------------------------------------------------------------- @@ -35,14 +35,15 @@ public: // certain fixed rate conversions. Sample rate cannot be // changed dynamically. enum src_quality { - DEFAULT=0, + DEFAULT_QUALITY=0, LOW_QUALITY=1, MED_QUALITY=2, - HIGH_QUALITY=3 + HIGH_QUALITY=3, + VERY_HIGH_QUALITY=4, }; static AudioResampler* create(int bitDepth, int inChannelCount, - int32_t sampleRate, int quality=DEFAULT); + int32_t sampleRate, src_quality quality=DEFAULT_QUALITY); virtual ~AudioResampler(); @@ -60,6 +61,9 @@ public: virtual void reset(); virtual size_t getUnreleasedFrames() const { return mInputIndex; } + // called from destructor, so must not be virtual + src_quality getQuality() const { return mQuality; } + protected: // number of bits for phase fraction - 30 bits allows nearly 2x downsampling static const int kNumPhaseBits = 30; @@ -70,7 +74,7 @@ protected: // multiplier to calculate fixed point phase increment static const double kPhaseMultiplier = 1L << kNumPhaseBits; - AudioResampler(int bitDepth, int inChannelCount, int32_t sampleRate); + AudioResampler(int bitDepth, int inChannelCount, int32_t sampleRate, src_quality quality); // prevent copying AudioResampler(const AudioResampler&); @@ -93,6 +97,19 @@ protected: uint32_t mPhaseFraction; uint64_t mLocalTimeFreq; int64_t mPTS; + +private: + const src_quality mQuality; + + // Return 'true' if the quality level is supported without explicit request + static bool qualityIsSupported(src_quality quality); + + // For pthread_once() + static void init_routine(); + + // Return the estimated CPU load for specific resampler in MHz. + // The absolute number is irrelevant, it's the relative values that matter. + static uint32_t qualityMHz(src_quality quality); }; // ---------------------------------------------------------------------------- diff --git a/services/audioflinger/AudioResamplerCubic.h b/services/audioflinger/AudioResamplerCubic.h index 892785aa..203b9335 100644 --- a/services/audioflinger/AudioResamplerCubic.h +++ b/services/audioflinger/AudioResamplerCubic.h @@ -29,7 +29,7 @@ namespace android { class AudioResamplerCubic : public AudioResampler { public: AudioResamplerCubic(int bitDepth, int inChannelCount, int32_t sampleRate) : - AudioResampler(bitDepth, inChannelCount, sampleRate) { + AudioResampler(bitDepth, inChannelCount, sampleRate, MED_QUALITY) { } virtual void resample(int32_t* out, size_t outFrameCount, AudioBufferProvider* provider); diff --git a/services/audioflinger/AudioResamplerSinc.cpp b/services/audioflinger/AudioResamplerSinc.cpp index 76662d80..3f22ca64 100644 --- a/services/audioflinger/AudioResamplerSinc.cpp +++ b/services/audioflinger/AudioResamplerSinc.cpp @@ -14,9 +14,36 @@ * limitations under the License. */ +#define LOG_TAG "AudioResamplerSinc" +//#define LOG_NDEBUG 0 + +#include <malloc.h> #include <string.h> +#include <stdlib.h> +#include <dlfcn.h> + +#include <cutils/compiler.h> +#include <cutils/properties.h> + +#include <utils/Log.h> + #include "AudioResamplerSinc.h" + + +#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) +#else +#define USE_NEON (false) +#endif + + namespace android { // ---------------------------------------------------------------------------- @@ -24,45 +51,345 @@ namespace android { /* * These coeficients are computed with the "fir" utility found in * tools/resampler_tools - * TODO: A good optimization would be to transpose this matrix, to take - * better advantage of the data-cache. + * cmd-line: fir -l 7 -s 48000 -c 20478 */ -const int32_t AudioResamplerSinc::mFirCoefsUp[] = { - 0x7fffffff, 0x7f15d078, 0x7c5e0da6, 0x77ecd867, 0x71e2e251, 0x6a6c304a, 0x61be7269, 0x58170412, 0x4db8ab05, 0x42e92ea6, 0x37eee214, 0x2d0e3bb1, 0x22879366, 0x18951e95, 0x0f693d0d, 0x072d2621, - 0x00000000, 0xf9f66655, 0xf51a5fd7, 0xf16bbd84, 0xeee0d9ac, 0xed67a922, 0xece70de6, 0xed405897, 0xee50e505, 0xeff3be30, 0xf203370f, 0xf45a6741, 0xf6d67d53, 0xf957db66, 0xfbc2f647, 0xfe00f2b9, - 0x00000000, 0x01b37218, 0x0313a0c6, 0x041d930d, 0x04d28057, 0x053731b0, 0x05534dff, 0x05309bfd, 0x04da440d, 0x045c1aee, 0x03c1fcdd, 0x03173ef5, 0x02663ae8, 0x01b7f736, 0x0113ec79, 0x007fe6a9, - 0x00000000, 0xff96b229, 0xff44f99f, 0xff0a86be, 0xfee5f803, 0xfed518fd, 0xfed521fd, 0xfee2f4fd, 0xfefb54f8, 0xff1b159b, 0xff3f4203, 0xff6539e0, 0xff8ac502, 0xffae1ddd, 0xffcdf3f9, 0xffe96798, - 0x00000000, 0x00119de6, 0x001e6b7e, 0x0026cb7a, 0x002b4830, 0x002c83d6, 0x002b2a82, 0x0027e67a, 0x002356f9, 0x001e098e, 0x001875e4, 0x0012fbbe, 0x000de2d1, 0x00095c10, 0x00058414, 0x00026636, - 0x00000000, 0xfffe44a9, 0xfffd206d, 0xfffc7b7f, 0xfffc3c8f, 0xfffc4ac2, 0xfffc8f2b, 0xfffcf5c4, 0xfffd6df3, 0xfffdeab2, 0xfffe6275, 0xfffececf, 0xffff2c07, 0xffff788c, 0xffffb471, 0xffffe0f2, - 0x00000000, 0x000013e6, 0x00001f03, 0x00002396, 0x00002399, 0x000020b6, 0x00001c3c, 0x00001722, 0x00001216, 0x00000d81, 0x0000099c, 0x0000067c, 0x00000419, 0x0000025f, 0x00000131, 0x00000070, - 0x00000000, 0xffffffc7, 0xffffffb3, 0xffffffb3, 0xffffffbe, 0xffffffcd, 0xffffffdb, 0xffffffe7, 0xfffffff0, 0xfffffff7, 0xfffffffb, 0xfffffffe, 0xffffffff, 0x00000000, 0x00000000, 0x00000000, - 0x00000000 // this one is needed for lerping the last coefficient +const int32_t AudioResamplerSinc::mFirCoefsUp[] __attribute__ ((aligned (32))) = { + 0x6d374bc7, 0x111c6ba0, 0xf3240e61, 0x07d14a38, 0xfc509e64, 0x0139cee9, 0xffc8c866, 0xfffcc300, + 0x6d35278a, 0x103e8192, 0xf36b9dfd, 0x07bdfaa5, 0xfc5102d0, 0x013d618d, 0xffc663b9, 0xfffd9592, + 0x6d2ebafe, 0x0f62811a, 0xf3b3d8ac, 0x07a9f399, 0xfc51d9a6, 0x0140bea5, 0xffc41212, 0xfffe631e, + 0x6d24069d, 0x0e8875ad, 0xf3fcb43e, 0x07953976, 0xfc53216f, 0x0143e67c, 0xffc1d373, 0xffff2b9f, + 0x6d150b35, 0x0db06a89, 0xf4462690, 0x077fd0ac, 0xfc54d8ae, 0x0146d965, 0xffbfa7d9, 0xffffef10, + 0x6d01c9e3, 0x0cda6ab5, 0xf4902587, 0x0769bdaf, 0xfc56fdda, 0x014997bb, 0xffbd8f40, 0x0000ad6e, + 0x6cea4418, 0x0c0680fe, 0xf4daa718, 0x07530501, 0xfc598f60, 0x014c21db, 0xffbb89a1, 0x000166b6, + 0x6cce7b97, 0x0b34b7f5, 0xf525a143, 0x073bab28, 0xfc5c8ba5, 0x014e782a, 0xffb996f3, 0x00021ae5, + 0x6cae7272, 0x0a6519f4, 0xf5710a17, 0x0723b4b4, 0xfc5ff105, 0x01509b14, 0xffb7b728, 0x0002c9fd, + 0x6c8a2b0f, 0x0997b116, 0xf5bcd7b1, 0x070b2639, 0xfc63bdd3, 0x01528b08, 0xffb5ea31, 0x000373fb, + 0x6c61a823, 0x08cc873c, 0xf609003f, 0x06f20453, 0xfc67f05a, 0x0154487b, 0xffb42ffc, 0x000418e2, + 0x6c34ecb5, 0x0803a60a, 0xf6557a00, 0x06d853a2, 0xfc6c86dd, 0x0155d3e8, 0xffb28876, 0x0004b8b3, + 0x6c03fc1c, 0x073d16e7, 0xf6a23b44, 0x06be18cd, 0xfc717f97, 0x01572dcf, 0xffb0f388, 0x00055371, + 0x6bced9ff, 0x0678e2fc, 0xf6ef3a6e, 0x06a3587e, 0xfc76d8bc, 0x015856b6, 0xffaf7118, 0x0005e921, + 0x6b958a54, 0x05b71332, 0xf73c6df4, 0x06881761, 0xfc7c9079, 0x01594f25, 0xffae010b, 0x000679c5, + 0x6b581163, 0x04f7b037, 0xf789cc61, 0x066c5a27, 0xfc82a4f4, 0x015a17ab, 0xffaca344, 0x00070564, + 0x6b1673c1, 0x043ac276, 0xf7d74c53, 0x06502583, 0xfc89144d, 0x015ab0db, 0xffab57a1, 0x00078c04, + 0x6ad0b652, 0x0380521c, 0xf824e480, 0x06337e2a, 0xfc8fdc9f, 0x015b1b4e, 0xffaa1e02, 0x00080dab, + 0x6a86de48, 0x02c86715, 0xf8728bb3, 0x061668d2, 0xfc96fbfc, 0x015b579e, 0xffa8f641, 0x00088a62, + 0x6a38f123, 0x0213090c, 0xf8c038d0, 0x05f8ea30, 0xfc9e7074, 0x015b666c, 0xffa7e039, 0x00090230, + 0x69e6f4b1, 0x01603f6e, 0xf90de2d1, 0x05db06fc, 0xfca63810, 0x015b485b, 0xffa6dbc0, 0x0009751e, + 0x6990ef0b, 0x00b01162, 0xf95b80cb, 0x05bcc3ed, 0xfcae50d6, 0x015afe14, 0xffa5e8ad, 0x0009e337, + 0x6936e697, 0x000285d0, 0xf9a909ea, 0x059e25b5, 0xfcb6b8c4, 0x015a8843, 0xffa506d2, 0x000a4c85, + 0x68d8e206, 0xff57a35e, 0xf9f67577, 0x057f310a, 0xfcbf6dd8, 0x0159e796, 0xffa43603, 0x000ab112, + 0x6876e855, 0xfeaf706f, 0xfa43bad2, 0x055fea9d, 0xfcc86e09, 0x01591cc0, 0xffa3760e, 0x000b10ec, + 0x681100c9, 0xfe09f323, 0xfa90d17b, 0x0540571a, 0xfcd1b74c, 0x01582878, 0xffa2c6c2, 0x000b6c1d, + 0x67a732f4, 0xfd673159, 0xfaddb10c, 0x05207b2f, 0xfcdb4793, 0x01570b77, 0xffa227ec, 0x000bc2b3, + 0x673986ac, 0xfcc730aa, 0xfb2a513b, 0x05005b82, 0xfce51ccb, 0x0155c678, 0xffa19957, 0x000c14bb, + 0x66c80413, 0xfc29f670, 0xfb76a9dd, 0x04dffcb6, 0xfcef34e1, 0x01545a3c, 0xffa11acb, 0x000c6244, + 0x6652b392, 0xfb8f87bd, 0xfbc2b2e4, 0x04bf6369, 0xfcf98dbe, 0x0152c783, 0xffa0ac11, 0x000cab5c, + 0x65d99dd5, 0xfaf7e963, 0xfc0e6461, 0x049e9433, 0xfd04254a, 0x01510f13, 0xffa04cf0, 0x000cf012, + 0x655ccbd3, 0xfa631fef, 0xfc59b685, 0x047d93a8, 0xfd0ef969, 0x014f31b2, 0xff9ffd2c, 0x000d3075, + 0x64dc46c3, 0xf9d12fab, 0xfca4a19f, 0x045c6654, 0xfd1a0801, 0x014d3029, 0xff9fbc89, 0x000d6c97, + 0x64581823, 0xf9421c9d, 0xfcef1e20, 0x043b10bd, 0xfd254ef4, 0x014b0b45, 0xff9f8ac9, 0x000da486, + 0x63d049b4, 0xf8b5ea87, 0xfd392498, 0x04199760, 0xfd30cc24, 0x0148c3d2, 0xff9f67ae, 0x000dd854, + 0x6344e578, 0xf82c9ce7, 0xfd82adba, 0x03f7feb4, 0xfd3c7d73, 0x01465a9f, 0xff9f52f7, 0x000e0812, + 0x62b5f5b2, 0xf7a636fa, 0xfdcbb25a, 0x03d64b27, 0xfd4860c2, 0x0143d07f, 0xff9f4c65, 0x000e33d3, + 0x622384e8, 0xf722bbb5, 0xfe142b6e, 0x03b4811d, 0xfd5473f3, 0x01412643, 0xff9f53b4, 0x000e5ba7, + 0x618d9ddc, 0xf6a22dcf, 0xfe5c120f, 0x0392a4f4, 0xfd60b4e7, 0x013e5cc0, 0xff9f68a1, 0x000e7fa1, + 0x60f44b91, 0xf6248fb6, 0xfea35f79, 0x0370bafc, 0xfd6d2180, 0x013b74ca, 0xff9f8ae9, 0x000e9fd5, + 0x60579947, 0xf5a9e398, 0xfeea0d0c, 0x034ec77f, 0xfd79b7a1, 0x01386f3a, 0xff9fba47, 0x000ebc54, + 0x5fb79278, 0xf5322b61, 0xff30144a, 0x032ccebb, 0xfd86752e, 0x01354ce7, 0xff9ff674, 0x000ed533, + 0x5f1442dc, 0xf4bd68b6, 0xff756edc, 0x030ad4e1, 0xfd93580d, 0x01320ea9, 0xffa03f2b, 0x000eea84, + 0x5e6db665, 0xf44b9cfe, 0xffba168d, 0x02e8de19, 0xfda05e23, 0x012eb55a, 0xffa09425, 0x000efc5c, + 0x5dc3f93c, 0xf3dcc959, 0xfffe054e, 0x02c6ee7f, 0xfdad855b, 0x012b41d3, 0xffa0f519, 0x000f0ace, + 0x5d1717c4, 0xf370eea9, 0x00413536, 0x02a50a22, 0xfdbacb9e, 0x0127b4f1, 0xffa161bf, 0x000f15ef, + 0x5c671e96, 0xf3080d8c, 0x0083a081, 0x02833506, 0xfdc82edb, 0x01240f8e, 0xffa1d9cf, 0x000f1dd2, + 0x5bb41a80, 0xf2a2265e, 0x00c54190, 0x02617321, 0xfdd5ad01, 0x01205285, 0xffa25cfe, 0x000f228d, + 0x5afe1886, 0xf23f393b, 0x010612eb, 0x023fc85c, 0xfde34403, 0x011c7eb2, 0xffa2eb04, 0x000f2434, + 0x5a4525df, 0xf1df45fd, 0x01460f41, 0x021e3891, 0xfdf0f1d6, 0x011894f0, 0xffa38395, 0x000f22dc, + 0x59894ff3, 0xf1824c3e, 0x01853165, 0x01fcc78f, 0xfdfeb475, 0x0114961b, 0xffa42668, 0x000f1e99, + 0x58caa45b, 0xf1284b58, 0x01c37452, 0x01db7914, 0xfe0c89db, 0x0110830f, 0xffa4d332, 0x000f1781, + 0x580930e1, 0xf0d14267, 0x0200d32c, 0x01ba50d2, 0xfe1a7009, 0x010c5ca6, 0xffa589a6, 0x000f0da8, + 0x5745037c, 0xf07d3043, 0x023d493c, 0x0199526b, 0xfe286505, 0x010823ba, 0xffa6497c, 0x000f0125, + 0x567e2a51, 0xf02c138a, 0x0278d1f2, 0x01788170, 0xfe3666d5, 0x0103d927, 0xffa71266, 0x000ef20b, + 0x55b4b3af, 0xefddea9a, 0x02b368e6, 0x0157e166, 0xfe447389, 0x00ff7dc4, 0xffa7e41a, 0x000ee070, + 0x54e8ae13, 0xef92b393, 0x02ed09d7, 0x013775bf, 0xfe528931, 0x00fb126b, 0xffa8be4c, 0x000ecc69, + 0x541a281e, 0xef4a6c58, 0x0325b0ad, 0x011741df, 0xfe60a5e5, 0x00f697f3, 0xffa9a0b1, 0x000eb60b, + 0x5349309e, 0xef051290, 0x035d5977, 0x00f7491a, 0xfe6ec7c0, 0x00f20f32, 0xffaa8afe, 0x000e9d6b, + 0x5275d684, 0xeec2a3a3, 0x0394006a, 0x00d78eb3, 0xfe7cece2, 0x00ed78ff, 0xffab7ce7, 0x000e829e, + 0x51a028e8, 0xee831cc3, 0x03c9a1e5, 0x00b815da, 0xfe8b1373, 0x00e8d62d, 0xffac7621, 0x000e65ba, + 0x50c83704, 0xee467ae1, 0x03fe3a6f, 0x0098e1b3, 0xfe99399f, 0x00e4278f, 0xffad7662, 0x000e46d3, + 0x4fee1037, 0xee0cbab9, 0x0431c6b5, 0x0079f54c, 0xfea75d97, 0x00df6df7, 0xffae7d5f, 0x000e25fd, + 0x4f11c3fe, 0xedd5d8ca, 0x0464438c, 0x005b53a4, 0xfeb57d92, 0x00daaa34, 0xffaf8acd, 0x000e034f, + 0x4e3361f7, 0xeda1d15c, 0x0495adf2, 0x003cffa9, 0xfec397cf, 0x00d5dd16, 0xffb09e63, 0x000ddedb, + 0x4d52f9df, 0xed70a07d, 0x04c6030d, 0x001efc35, 0xfed1aa92, 0x00d10769, 0xffb1b7d8, 0x000db8b7, + 0x4c709b8e, 0xed424205, 0x04f54029, 0x00014c12, 0xfedfb425, 0x00cc29f7, 0xffb2d6e1, 0x000d90f6, + 0x4b8c56f8, 0xed16b196, 0x052362ba, 0xffe3f1f7, 0xfeedb2da, 0x00c7458a, 0xffb3fb37, 0x000d67ae, + 0x4aa63c2c, 0xecedea99, 0x0550685d, 0xffc6f08a, 0xfefba508, 0x00c25ae8, 0xffb52490, 0x000d3cf1, + 0x49be5b50, 0xecc7e845, 0x057c4ed4, 0xffaa4a5d, 0xff09890f, 0x00bd6ad7, 0xffb652a7, 0x000d10d5, + 0x48d4c4a2, 0xeca4a59b, 0x05a7140b, 0xff8e01f1, 0xff175d53, 0x00b87619, 0xffb78533, 0x000ce36b, + 0x47e98874, 0xec841d68, 0x05d0b612, 0xff7219b3, 0xff252042, 0x00b37d70, 0xffb8bbed, 0x000cb4c8, + 0x46fcb72d, 0xec664a48, 0x05f93324, 0xff5693fe, 0xff32d04f, 0x00ae8198, 0xffb9f691, 0x000c84ff, + 0x460e6148, 0xec4b26a2, 0x0620899e, 0xff3b731b, 0xff406bf8, 0x00a9834e, 0xffbb34d8, 0x000c5422, + 0x451e9750, 0xec32acb0, 0x0646b808, 0xff20b93e, 0xff4df1be, 0x00a4834c, 0xffbc767f, 0x000c2245, + 0x442d69de, 0xec1cd677, 0x066bbd0d, 0xff066889, 0xff5b602c, 0x009f8249, 0xffbdbb42, 0x000bef79, + 0x433ae99c, 0xec099dcf, 0x068f9781, 0xfeec830d, 0xff68b5d5, 0x009a80f8, 0xffbf02dd, 0x000bbbd2, + 0x4247273f, 0xebf8fc64, 0x06b2465b, 0xfed30ac5, 0xff75f153, 0x0095800c, 0xffc04d0f, 0x000b8760, + 0x41523389, 0xebeaebaf, 0x06d3c8bb, 0xfeba0199, 0xff831148, 0x00908034, 0xffc19996, 0x000b5235, + 0x405c1f43, 0xebdf6500, 0x06f41de3, 0xfea16960, 0xff90145e, 0x008b821b, 0xffc2e832, 0x000b1c64, + 0x3f64fb40, 0xebd6617b, 0x0713453d, 0xfe8943dc, 0xff9cf947, 0x0086866b, 0xffc438a3, 0x000ae5fc, + 0x3e6cd85b, 0xebcfda19, 0x07313e56, 0xfe7192bd, 0xffa9bebe, 0x00818dcb, 0xffc58aaa, 0x000aaf0f, + 0x3d73c772, 0xebcbc7a7, 0x074e08e0, 0xfe5a579d, 0xffb66386, 0x007c98de, 0xffc6de09, 0x000a77ac, + 0x3c79d968, 0xebca22cc, 0x0769a4b2, 0xfe439407, 0xffc2e669, 0x0077a845, 0xffc83285, 0x000a3fe5, + 0x3b7f1f23, 0xebcae405, 0x078411c7, 0xfe2d496f, 0xffcf463a, 0x0072bc9d, 0xffc987e0, 0x000a07c9, + 0x3a83a989, 0xebce03aa, 0x079d503b, 0xfe177937, 0xffdb81d6, 0x006dd680, 0xffcadde1, 0x0009cf67, + 0x3987897f, 0xebd379eb, 0x07b56051, 0xfe0224b0, 0xffe79820, 0x0068f687, 0xffcc344c, 0x000996ce, + 0x388acfe9, 0xebdb3ed5, 0x07cc426c, 0xfded4d13, 0xfff38806, 0x00641d44, 0xffcd8aeb, 0x00095e0e, + 0x378d8da8, 0xebe54a4f, 0x07e1f712, 0xfdd8f38b, 0xffff507b, 0x005f4b4a, 0xffcee183, 0x00092535, + 0x368fd397, 0xebf1941f, 0x07f67eec, 0xfdc5192d, 0x000af07f, 0x005a8125, 0xffd037e0, 0x0008ec50, + 0x3591b28b, 0xec0013e8, 0x0809dac3, 0xfdb1befc, 0x00166718, 0x0055bf60, 0xffd18dcc, 0x0008b36e, + 0x34933b50, 0xec10c12c, 0x081c0b84, 0xfd9ee5e7, 0x0021b355, 0x00510682, 0xffd2e311, 0x00087a9c, + 0x33947eab, 0xec23934f, 0x082d1239, 0xfd8c8ecc, 0x002cd44d, 0x004c570f, 0xffd4377d, 0x000841e8, + 0x32958d55, 0xec388194, 0x083cf010, 0xfd7aba74, 0x0037c922, 0x0047b186, 0xffd58ade, 0x0008095d, + 0x319677fa, 0xec4f8322, 0x084ba654, 0xfd696998, 0x004290fc, 0x00431666, 0xffd6dd02, 0x0007d108, + 0x30974f3b, 0xec688f02, 0x08593671, 0xfd589cdc, 0x004d2b0e, 0x003e8628, 0xffd82dba, 0x000798f5, + 0x2f9823a8, 0xec839c22, 0x0865a1f1, 0xfd4854d3, 0x00579691, 0x003a0141, 0xffd97cd6, 0x00076130, + 0x2e9905c1, 0xeca0a156, 0x0870ea7e, 0xfd3891fd, 0x0061d2ca, 0x00358824, 0xffdaca2a, 0x000729c4, + 0x2d9a05f4, 0xecbf9558, 0x087b11de, 0xfd2954c8, 0x006bdf05, 0x00311b41, 0xffdc1588, 0x0006f2bb, + 0x2c9b349e, 0xece06ecb, 0x088419f6, 0xfd1a9d91, 0x0075ba95, 0x002cbb03, 0xffdd5ec6, 0x0006bc21, + 0x2b9ca203, 0xed032439, 0x088c04c8, 0xfd0c6ca2, 0x007f64da, 0x002867d2, 0xffdea5bb, 0x000685ff, + 0x2a9e5e57, 0xed27ac16, 0x0892d470, 0xfcfec233, 0x0088dd38, 0x00242213, 0xffdfea3c, 0x0006505f, + 0x29a079b2, 0xed4dfcc2, 0x08988b2a, 0xfcf19e6b, 0x0092231e, 0x001fea27, 0xffe12c22, 0x00061b4b, + 0x28a30416, 0xed760c88, 0x089d2b4a, 0xfce50161, 0x009b3605, 0x001bc06b, 0xffe26b48, 0x0005e6cb, + 0x27a60d6a, 0xed9fd1a2, 0x08a0b740, 0xfcd8eb17, 0x00a4156b, 0x0017a53b, 0xffe3a788, 0x0005b2e8, + 0x26a9a57b, 0xedcb4237, 0x08a33196, 0xfccd5b82, 0x00acc0da, 0x001398ec, 0xffe4e0bf, 0x00057faa, + 0x25addbf9, 0xedf8545b, 0x08a49cf0, 0xfcc25285, 0x00b537e1, 0x000f9bd2, 0xffe616c8, 0x00054d1a, + 0x24b2c075, 0xee26fe17, 0x08a4fc0d, 0xfcb7cff0, 0x00bd7a1c, 0x000bae3c, 0xffe74984, 0x00051b3e, + 0x23b86263, 0xee573562, 0x08a451c0, 0xfcadd386, 0x00c5872a, 0x0007d075, 0xffe878d3, 0x0004ea1d, + 0x22bed116, 0xee88f026, 0x08a2a0f8, 0xfca45cf7, 0x00cd5eb7, 0x000402c8, 0xffe9a494, 0x0004b9c0, + 0x21c61bc0, 0xeebc2444, 0x089fecbb, 0xfc9b6be5, 0x00d50075, 0x00004579, 0xffeaccaa, 0x00048a2b, + 0x20ce516f, 0xeef0c78d, 0x089c3824, 0xfc92ffe1, 0x00dc6c1e, 0xfffc98c9, 0xffebf0fa, 0x00045b65, + 0x1fd7810f, 0xef26cfca, 0x08978666, 0xfc8b186d, 0x00e3a175, 0xfff8fcf7, 0xffed1166, 0x00042d74, + 0x1ee1b965, 0xef5e32bd, 0x0891dac8, 0xfc83b4fc, 0x00eaa045, 0xfff5723d, 0xffee2dd7, 0x0004005e, + 0x1ded0911, 0xef96e61c, 0x088b38a9, 0xfc7cd4f0, 0x00f16861, 0xfff1f8d2, 0xffef4632, 0x0003d426, + 0x1cf97e8b, 0xefd0df9a, 0x0883a378, 0xfc76779e, 0x00f7f9a3, 0xffee90eb, 0xfff05a60, 0x0003a8d2, + 0x1c072823, 0xf00c14e1, 0x087b1ebc, 0xfc709c4d, 0x00fe53ef, 0xffeb3ab8, 0xfff16a4a, 0x00037e65, + 0x1b1613ff, 0xf0487b98, 0x0871ae0d, 0xfc6b4233, 0x0104772e, 0xffe7f666, 0xfff275db, 0x000354e5, + 0x1a26501b, 0xf0860962, 0x08675516, 0xfc66687a, 0x010a6353, 0xffe4c41e, 0xfff37d00, 0x00032c54, + 0x1937ea47, 0xf0c4b3e0, 0x085c1794, 0xfc620e3d, 0x01101858, 0xffe1a408, 0xfff47fa5, 0x000304b7, + 0x184af025, 0xf10470b0, 0x084ff957, 0xfc5e328c, 0x0115963d, 0xffde9646, 0xfff57db8, 0x0002de0e, + 0x175f6f2b, 0xf1453571, 0x0842fe3d, 0xfc5ad465, 0x011add0b, 0xffdb9af8, 0xfff67729, 0x0002b85f, + 0x1675749e, 0xf186f7c0, 0x08352a35, 0xfc57f2be, 0x011fecd3, 0xffd8b23b, 0xfff76be9, 0x000293aa, + 0x158d0d95, 0xf1c9ad40, 0x0826813e, 0xfc558c7c, 0x0124c5ab, 0xffd5dc28, 0xfff85be8, 0x00026ff2, + 0x14a646f6, 0xf20d4b92, 0x08170767, 0xfc53a07b, 0x012967b1, 0xffd318d6, 0xfff9471b, 0x00024d39, + 0x13c12d73, 0xf251c85d, 0x0806c0cb, 0xfc522d88, 0x012dd30a, 0xffd06858, 0xfffa2d74, 0x00022b7f, + 0x12ddcd8f, 0xf297194d, 0x07f5b193, 0xfc513266, 0x013207e4, 0xffcdcabe, 0xfffb0ee9, 0x00020ac7, + 0x11fc3395, 0xf2dd3411, 0x07e3ddf7, 0xfc50adcc, 0x01360670, 0xffcb4014, 0xfffbeb70, 0x0001eb10, + 0x111c6ba0, 0xf3240e61, 0x07d14a38, 0xfc509e64, 0x0139cee9, 0xffc8c866, 0xfffcc300, 0x0001cc5c, }; /* - * These coefficients are optimized for 48KHz -> 44.1KHz (stop-band at 22.050KHz) - * It's possible to use the above coefficient for any down-sampling - * at the expense of a slower processing loop (we can interpolate - * these coefficient from the above by "Stretching" them in time). + * These coefficients are optimized for 48KHz -> 44.1KHz + * cmd-line: fir -l 7 -s 48000 -c 17189 */ -const int32_t AudioResamplerSinc::mFirCoefsDown[] = { - 0x7fffffff, 0x7f55e46d, 0x7d5b4c60, 0x7a1b4b98, 0x75a7fb14, 0x7019f0bd, 0x698f875a, 0x622bfd59, 0x5a167256, 0x5178cc54, 0x487e8e6c, 0x3f53aae8, 0x36235ad4, 0x2d17047b, 0x245539ab, 0x1c00d540, - 0x14383e57, 0x0d14d5ca, 0x06aa910b, 0x0107c38b, 0xfc351654, 0xf835abae, 0xf5076b45, 0xf2a37202, 0xf0fe9faa, 0xf00a3bbd, 0xefb4aa81, 0xefea2b05, 0xf0959716, 0xf1a11e83, 0xf2f6f7a0, 0xf481fff4, - 0xf62e48ce, 0xf7e98ca5, 0xf9a38b4c, 0xfb4e4bfa, 0xfcde456f, 0xfe4a6d30, 0xff8c2fdf, 0x009f5555, 0x0181d393, 0x0233940f, 0x02b62f06, 0x030ca07d, 0x033afa62, 0x03461725, 0x03334f83, 0x030835fa, - 0x02ca59cc, 0x027f12d1, 0x022b570d, 0x01d39a49, 0x017bb78f, 0x0126e414, 0x00d7aaaf, 0x008feec7, 0x0050f584, 0x001b73e3, 0xffefa063, 0xffcd46ed, 0xffb3ddcd, 0xffa29aaa, 0xff988691, 0xff949066, - 0xff959d24, 0xff9a959e, 0xffa27195, 0xffac4011, 0xffb72d2b, 0xffc28569, 0xffcdb706, 0xffd85171, 0xffe20364, 0xffea97e9, 0xfff1f2b2, 0xfff80c06, 0xfffcec92, 0x0000a955, 0x00035fd8, 0x000532cf, - 0x00064735, 0x0006c1f9, 0x0006c62d, 0x000673ba, 0x0005e68f, 0x00053630, 0x000475a3, 0x0003b397, 0x0002fac1, 0x00025257, 0x0001be9e, 0x0001417a, 0x0000dafd, 0x000089eb, 0x00004c28, 0x00001f1d, - 0x00000000, 0xffffec10, 0xffffe0be, 0xffffdbc5, 0xffffdb39, 0xffffdd8b, 0xffffe182, 0xffffe638, 0xffffeb0a, 0xffffef8f, 0xfffff38b, 0xfffff6e3, 0xfffff993, 0xfffffba6, 0xfffffd30, 0xfffffe4a, - 0xffffff09, 0xffffff85, 0xffffffd1, 0xfffffffb, 0x0000000f, 0x00000016, 0x00000015, 0x00000012, 0x0000000d, 0x00000009, 0x00000006, 0x00000003, 0x00000002, 0x00000001, 0x00000000, 0x00000000, - 0x00000000 // this one is needed for lerping the last coefficient +const int32_t AudioResamplerSinc::mFirCoefsDown[] __attribute__ ((aligned (32))) = { + 0x5bacb6f4, 0x1ded1a1d, 0xf0398d56, 0x0394f674, 0x0193a5f9, 0xfe66dbeb, 0x00791043, 0xfffe6631, + 0x5bab6c81, 0x1d3ddccd, 0xf0421d2c, 0x03af9995, 0x01818dc9, 0xfe6bb63e, 0x0079812a, 0xfffdc37d, + 0x5ba78d37, 0x1c8f2cf9, 0xf04beb1d, 0x03c9a04a, 0x016f8aca, 0xfe70a511, 0x0079e34d, 0xfffd2545, + 0x5ba1194f, 0x1be11231, 0xf056f2c7, 0x03e309fe, 0x015d9e64, 0xfe75a79f, 0x007a36e2, 0xfffc8b86, + 0x5b981122, 0x1b3393f8, 0xf0632fb7, 0x03fbd625, 0x014bc9fa, 0xfe7abd23, 0x007a7c20, 0xfffbf639, + 0x5b8c7530, 0x1a86b9bf, 0xf0709d74, 0x04140449, 0x013a0ee9, 0xfe7fe4db, 0x007ab33d, 0xfffb655b, + 0x5b7e461a, 0x19da8ae5, 0xf07f3776, 0x042b93fd, 0x01286e86, 0xfe851e05, 0x007adc72, 0xfffad8e4, + 0x5b6d84a8, 0x192f0eb7, 0xf08ef92d, 0x044284e6, 0x0116ea22, 0xfe8a67dd, 0x007af7f6, 0xfffa50ce, + 0x5b5a31c6, 0x18844c70, 0xf09fddfe, 0x0458d6b7, 0x01058306, 0xfe8fc1a5, 0x007b0603, 0xfff9cd12, + 0x5b444e81, 0x17da4b37, 0xf0b1e143, 0x046e8933, 0x00f43a74, 0xfe952a9b, 0x007b06d4, 0xfff94da9, + 0x5b2bdc0e, 0x17311222, 0xf0c4fe50, 0x04839c29, 0x00e311a9, 0xfe9aa201, 0x007afaa1, 0xfff8d28c, + 0x5b10dbc2, 0x1688a832, 0xf0d9306d, 0x04980f79, 0x00d209db, 0xfea02719, 0x007ae1a7, 0xfff85bb1, + 0x5af34f18, 0x15e11453, 0xf0ee72db, 0x04abe310, 0x00c12439, 0xfea5b926, 0x007abc20, 0xfff7e910, + 0x5ad337af, 0x153a5d5e, 0xf104c0d2, 0x04bf16e9, 0x00b061eb, 0xfeab576d, 0x007a8a49, 0xfff77a9f, + 0x5ab09748, 0x14948a16, 0xf11c1583, 0x04d1ab0d, 0x009fc413, 0xfeb10134, 0x007a4c5d, 0xfff71057, + 0x5a8b6fc7, 0x13efa12c, 0xf1346c17, 0x04e39f93, 0x008f4bcb, 0xfeb6b5c0, 0x007a029a, 0xfff6aa2b, + 0x5a63c336, 0x134ba937, 0xf14dbfb1, 0x04f4f4a2, 0x007efa29, 0xfebc745c, 0x0079ad3d, 0xfff64812, + 0x5a3993c0, 0x12a8a8bb, 0xf1680b6e, 0x0505aa6a, 0x006ed038, 0xfec23c50, 0x00794c82, 0xfff5ea02, + 0x5a0ce3b2, 0x1206a625, 0xf1834a63, 0x0515c12d, 0x005ecf01, 0xfec80ce8, 0x0078e0a9, 0xfff58ff0, + 0x59ddb57f, 0x1165a7cc, 0xf19f77a0, 0x05253938, 0x004ef782, 0xfecde571, 0x007869ee, 0xfff539cf, + 0x59ac0bba, 0x10c5b3ef, 0xf1bc8e31, 0x053412e4, 0x003f4ab4, 0xfed3c538, 0x0077e891, 0xfff4e794, + 0x5977e919, 0x1026d0b8, 0xf1da891b, 0x05424e9b, 0x002fc98a, 0xfed9ab8f, 0x00775ccf, 0xfff49934, + 0x59415075, 0x0f890437, 0xf1f96360, 0x054feccf, 0x002074ed, 0xfedf97c6, 0x0076c6e8, 0xfff44ea3, + 0x590844c9, 0x0eec5465, 0xf21917ff, 0x055cee03, 0x00114dc3, 0xfee58932, 0x00762719, 0xfff407d2, + 0x58ccc930, 0x0e50c723, 0xf239a1ef, 0x056952c3, 0x000254e8, 0xfeeb7f27, 0x00757da3, 0xfff3c4b7, + 0x588ee0ea, 0x0db6623b, 0xf25afc29, 0x05751baa, 0xfff38b32, 0xfef178fc, 0x0074cac4, 0xfff38542, + 0x584e8f56, 0x0d1d2b5d, 0xf27d219f, 0x0580495c, 0xffe4f171, 0xfef7760c, 0x00740ebb, 0xfff34968, + 0x580bd7f4, 0x0c85281f, 0xf2a00d43, 0x058adc8d, 0xffd6886d, 0xfefd75af, 0x007349c7, 0xfff3111b, + 0x57c6be67, 0x0bee5dff, 0xf2c3ba04, 0x0594d5fa, 0xffc850e6, 0xff037744, 0x00727c27, 0xfff2dc4c, + 0x577f4670, 0x0b58d262, 0xf2e822ce, 0x059e366c, 0xffba4b98, 0xff097a29, 0x0071a61b, 0xfff2aaef, + 0x573573f2, 0x0ac48a92, 0xf30d428e, 0x05a6feb9, 0xffac7936, 0xff0f7dbf, 0x0070c7e1, 0xfff27cf3, + 0x56e94af1, 0x0a318bc1, 0xf333142f, 0x05af2fbf, 0xff9eda6d, 0xff15816a, 0x006fe1b8, 0xfff2524c, + 0x569acf90, 0x099fdb04, 0xf359929a, 0x05b6ca6b, 0xff916fe1, 0xff1b848e, 0x006ef3df, 0xfff22aea, + 0x564a0610, 0x090f7d57, 0xf380b8ba, 0x05bdcfb2, 0xff843a32, 0xff218692, 0x006dfe94, 0xfff206bf, + 0x55f6f2d3, 0x0880779d, 0xf3a88179, 0x05c44095, 0xff7739f7, 0xff2786e1, 0x006d0217, 0xfff1e5bb, + 0x55a19a5c, 0x07f2ce9b, 0xf3d0e7c2, 0x05ca1e1f, 0xff6a6fc1, 0xff2d84e5, 0x006bfea4, 0xfff1c7d0, + 0x554a0148, 0x076686fc, 0xf3f9e680, 0x05cf6965, 0xff5ddc1a, 0xff33800e, 0x006af47b, 0xfff1acef, + 0x54f02c56, 0x06dba551, 0xf42378a0, 0x05d42387, 0xff517f86, 0xff3977cb, 0x0069e3d9, 0xfff19508, + 0x54942061, 0x06522e0f, 0xf44d9912, 0x05d84daf, 0xff455a80, 0xff3f6b8f, 0x0068ccfa, 0xfff1800b, + 0x5435e263, 0x05ca258f, 0xf47842c5, 0x05dbe90f, 0xff396d7f, 0xff455acf, 0x0067b01e, 0xfff16de9, + 0x53d57774, 0x0543900d, 0xf4a370ad, 0x05def6e4, 0xff2db8f2, 0xff4b4503, 0x00668d80, 0xfff15e93, + 0x5372e4c6, 0x04be71ab, 0xf4cf1dbf, 0x05e17873, 0xff223d40, 0xff5129a3, 0x0065655d, 0xfff151f9, + 0x530e2fac, 0x043ace6e, 0xf4fb44f4, 0x05e36f0d, 0xff16faca, 0xff57082e, 0x006437f1, 0xfff1480b, + 0x52a75d90, 0x03b8aa40, 0xf527e149, 0x05e4dc08, 0xff0bf1ed, 0xff5ce021, 0x00630577, 0xfff140b9, + 0x523e73fd, 0x033808eb, 0xf554edbd, 0x05e5c0c6, 0xff0122fc, 0xff62b0fd, 0x0061ce2c, 0xfff13bf3, + 0x51d37897, 0x02b8ee22, 0xf5826555, 0x05e61eae, 0xfef68e45, 0xff687a47, 0x00609249, 0xfff139aa, + 0x5166711c, 0x023b5d76, 0xf5b0431a, 0x05e5f733, 0xfeec340f, 0xff6e3b84, 0x005f520a, 0xfff139cd, + 0x50f76368, 0x01bf5a5e, 0xf5de8218, 0x05e54bcd, 0xfee2149b, 0xff73f43d, 0x005e0da8, 0xfff13c4c, + 0x5086556f, 0x0144e834, 0xf60d1d63, 0x05e41dfe, 0xfed83023, 0xff79a3fe, 0x005cc55c, 0xfff14119, + 0x50134d3e, 0x00cc0a36, 0xf63c1012, 0x05e26f4e, 0xfece86db, 0xff7f4a54, 0x005b7961, 0xfff14821, + 0x4f9e50ff, 0x0054c382, 0xf66b5544, 0x05e0414d, 0xfec518f1, 0xff84e6d0, 0x005a29ed, 0xfff15156, + 0x4f2766f2, 0xffdf171b, 0xf69ae81d, 0x05dd9593, 0xfebbe68c, 0xff8a7905, 0x0058d738, 0xfff15ca8, + 0x4eae9571, 0xff6b07e7, 0xf6cac3c7, 0x05da6dbe, 0xfeb2efcd, 0xff900089, 0x0057817b, 0xfff16a07, + 0x4e33e2ee, 0xfef898ae, 0xf6fae373, 0x05d6cb72, 0xfeaa34d0, 0xff957cf4, 0x005628ec, 0xfff17962, + 0x4db755f3, 0xfe87cc1b, 0xf72b425b, 0x05d2b05c, 0xfea1b5a9, 0xff9aede0, 0x0054cdc0, 0xfff18aab, + 0x4d38f520, 0xfe18a4bc, 0xf75bdbbd, 0x05ce1e2d, 0xfe997268, 0xffa052ec, 0x0053702d, 0xfff19dd1, + 0x4cb8c72e, 0xfdab2501, 0xf78caae0, 0x05c9169d, 0xfe916b15, 0xffa5abb8, 0x00521068, 0xfff1b2c5, + 0x4c36d2eb, 0xfd3f4f3d, 0xf7bdab16, 0x05c39b6a, 0xfe899fb2, 0xffaaf7e6, 0x0050aea5, 0xfff1c976, + 0x4bb31f3c, 0xfcd525a5, 0xf7eed7b4, 0x05bdae57, 0xfe82103f, 0xffb0371c, 0x004f4b17, 0xfff1e1d6, + 0x4b2db31a, 0xfc6caa53, 0xf8202c1c, 0x05b7512e, 0xfe7abcb1, 0xffb56902, 0x004de5f1, 0xfff1fbd5, + 0x4aa69594, 0xfc05df40, 0xf851a3b6, 0x05b085bc, 0xfe73a4fb, 0xffba8d44, 0x004c7f66, 0xfff21764, + 0x4a1dcdce, 0xfba0c64b, 0xf88339f5, 0x05a94dd5, 0xfe6cc909, 0xffbfa38d, 0x004b17a6, 0xfff23473, + 0x499362ff, 0xfb3d6133, 0xf8b4ea55, 0x05a1ab52, 0xfe6628c1, 0xffc4ab8f, 0x0049aee3, 0xfff252f3, + 0x49075c72, 0xfadbb19a, 0xf8e6b059, 0x0599a00e, 0xfe5fc405, 0xffc9a4fc, 0x0048454b, 0xfff272d6, + 0x4879c185, 0xfa7bb908, 0xf9188793, 0x05912dea, 0xfe599aaf, 0xffce8f8a, 0x0046db0f, 0xfff2940b, + 0x47ea99a9, 0xfa1d78e3, 0xf94a6b9b, 0x058856cd, 0xfe53ac97, 0xffd36af1, 0x0045705c, 0xfff2b686, + 0x4759ec60, 0xf9c0f276, 0xf97c5815, 0x057f1c9e, 0xfe4df98e, 0xffd836eb, 0x00440561, 0xfff2da36, + 0x46c7c140, 0xf96626f0, 0xf9ae48af, 0x0575814c, 0xfe48815e, 0xffdcf336, 0x00429a4a, 0xfff2ff0d, + 0x46341fed, 0xf90d1761, 0xf9e03924, 0x056b86c6, 0xfe4343d0, 0xffe19f91, 0x00412f43, 0xfff324fd, + 0x459f101d, 0xf8b5c4be, 0xfa122537, 0x05612f00, 0xfe3e40a6, 0xffe63bc0, 0x003fc478, 0xfff34bf9, + 0x45089996, 0xf8602fdc, 0xfa4408ba, 0x05567bf1, 0xfe39779a, 0xffeac787, 0x003e5a12, 0xfff373f0, + 0x4470c42d, 0xf80c5977, 0xfa75df87, 0x054b6f92, 0xfe34e867, 0xffef42af, 0x003cf03d, 0xfff39cd7, + 0x43d797c7, 0xf7ba422b, 0xfaa7a586, 0x05400be1, 0xfe3092bf, 0xfff3ad01, 0x003b871f, 0xfff3c69f, + 0x433d1c56, 0xf769ea78, 0xfad956ab, 0x053452dc, 0xfe2c7650, 0xfff8064b, 0x003a1ee3, 0xfff3f13a, + 0x42a159dc, 0xf71b52c4, 0xfb0aeef6, 0x05284685, 0xfe2892c5, 0xfffc4e5c, 0x0038b7ae, 0xfff41c9c, + 0x42045865, 0xf6ce7b57, 0xfb3c6a73, 0x051be8dd, 0xfe24e7c3, 0x00008507, 0x003751a7, 0xfff448b7, + 0x4166200e, 0xf683645a, 0xfb6dc53c, 0x050f3bec, 0xfe2174ec, 0x0004aa1f, 0x0035ecf4, 0xfff4757e, + 0x40c6b8fd, 0xf63a0ddf, 0xfb9efb77, 0x050241b6, 0xfe1e39da, 0x0008bd7c, 0x003489b9, 0xfff4a2e5, + 0x40262b65, 0xf5f277d9, 0xfbd00956, 0x04f4fc46, 0xfe1b3628, 0x000cbef7, 0x0033281a, 0xfff4d0de, + 0x3f847f83, 0xf5aca21f, 0xfc00eb1b, 0x04e76da3, 0xfe18696a, 0x0010ae6e, 0x0031c83a, 0xfff4ff5d, + 0x3ee1bda2, 0xf5688c6d, 0xfc319d13, 0x04d997d8, 0xfe15d32f, 0x00148bbd, 0x00306a3b, 0xfff52e57, + 0x3e3dee13, 0xf5263665, 0xfc621b9a, 0x04cb7cf2, 0xfe137304, 0x001856c7, 0x002f0e3f, 0xfff55dbf, + 0x3d991932, 0xf4e59f8a, 0xfc926319, 0x04bd1efb, 0xfe114872, 0x001c0f6e, 0x002db466, 0xfff58d89, + 0x3cf34766, 0xf4a6c748, 0xfcc27008, 0x04ae8000, 0xfe0f52fc, 0x001fb599, 0x002c5cd0, 0xfff5bdaa, + 0x3c4c811c, 0xf469aced, 0xfcf23eec, 0x049fa20f, 0xfe0d9224, 0x0023492f, 0x002b079a, 0xfff5ee17, + 0x3ba4cec9, 0xf42e4faf, 0xfd21cc59, 0x04908733, 0xfe0c0567, 0x0026ca1c, 0x0029b4e4, 0xfff61ec5, + 0x3afc38eb, 0xf3f4aea6, 0xfd5114f0, 0x0481317a, 0xfe0aac3f, 0x002a384c, 0x002864c9, 0xfff64fa8, + 0x3a52c805, 0xf3bcc8d3, 0xfd801564, 0x0471a2ef, 0xfe098622, 0x002d93ae, 0x00271766, 0xfff680b5, + 0x39a884a1, 0xf3869d1a, 0xfdaeca73, 0x0461dda0, 0xfe089283, 0x0030dc34, 0x0025ccd7, 0xfff6b1e4, + 0x38fd774e, 0xf3522a49, 0xfddd30eb, 0x0451e396, 0xfe07d0d3, 0x003411d2, 0x00248535, 0xfff6e329, + 0x3851a8a2, 0xf31f6f0f, 0xfe0b45aa, 0x0441b6dd, 0xfe07407d, 0x0037347d, 0x0023409a, 0xfff7147a, + 0x37a52135, 0xf2ee6a07, 0xfe39059b, 0x0431597d, 0xfe06e0eb, 0x003a442e, 0x0021ff1f, 0xfff745cd, + 0x36f7e9a4, 0xf2bf19ae, 0xfe666dbc, 0x0420cd80, 0xfe06b184, 0x003d40e0, 0x0020c0dc, 0xfff7771a, + 0x364a0a90, 0xf2917c6d, 0xfe937b15, 0x041014eb, 0xfe06b1ac, 0x00402a8e, 0x001f85e6, 0xfff7a857, + 0x359b8c9d, 0xf265908f, 0xfec02ac2, 0x03ff31c3, 0xfe06e0c4, 0x00430137, 0x001e4e56, 0xfff7d97a, + 0x34ec786f, 0xf23b544b, 0xfeec79ec, 0x03ee260d, 0xfe073e2a, 0x0045c4dd, 0x001d1a3f, 0xfff80a7c, + 0x343cd6af, 0xf212c5be, 0xff1865cd, 0x03dcf3ca, 0xfe07c93a, 0x00487582, 0x001be9b7, 0xfff83b52, + 0x338cb004, 0xf1ebe2ec, 0xff43ebac, 0x03cb9cf9, 0xfe08814e, 0x004b132b, 0x001abcd0, 0xfff86bf6, + 0x32dc0d17, 0xf1c6a9c3, 0xff6f08e4, 0x03ba2398, 0xfe0965bc, 0x004d9dde, 0x0019939d, 0xfff89c60, + 0x322af693, 0xf1a3181a, 0xff99badb, 0x03a889a1, 0xfe0a75da, 0x005015a5, 0x00186e31, 0xfff8cc86, + 0x3179751f, 0xf1812bb0, 0xffc3ff0c, 0x0396d10c, 0xfe0bb0f9, 0x00527a8a, 0x00174c9c, 0xfff8fc62, + 0x30c79163, 0xf160e22d, 0xffedd2fd, 0x0384fbd1, 0xfe0d166b, 0x0054cc9a, 0x00162eef, 0xfff92bec, + 0x30155404, 0xf1423924, 0x00173447, 0x03730be0, 0xfe0ea57e, 0x00570be4, 0x00151538, 0xfff95b1e, + 0x2f62c5a7, 0xf1252e0f, 0x00402092, 0x0361032a, 0xfe105d7e, 0x00593877, 0x0013ff88, 0xfff989ef, + 0x2eafeeed, 0xf109be56, 0x00689598, 0x034ee39b, 0xfe123db6, 0x005b5267, 0x0012edea, 0xfff9b85b, + 0x2dfcd873, 0xf0efe748, 0x0090911f, 0x033caf1d, 0xfe144570, 0x005d59c6, 0x0011e06d, 0xfff9e65a, + 0x2d498ad3, 0xf0d7a622, 0x00b81102, 0x032a6796, 0xfe1673f2, 0x005f4eac, 0x0010d71d, 0xfffa13e5, + 0x2c960ea3, 0xf0c0f808, 0x00df1328, 0x03180ee7, 0xfe18c884, 0x0061312e, 0x000fd205, 0xfffa40f8, + 0x2be26c73, 0xf0abda0e, 0x0105958c, 0x0305a6f0, 0xfe1b4268, 0x00630167, 0x000ed130, 0xfffa6d8d, + 0x2b2eaccf, 0xf0984931, 0x012b9635, 0x02f3318a, 0xfe1de0e2, 0x0064bf71, 0x000dd4a7, 0xfffa999d, + 0x2a7ad83c, 0xf086425a, 0x0151133e, 0x02e0b08d, 0xfe20a335, 0x00666b68, 0x000cdc74, 0xfffac525, + 0x29c6f738, 0xf075c260, 0x01760ad1, 0x02ce25ca, 0xfe2388a1, 0x0068056b, 0x000be89f, 0xfffaf01e, + 0x2913123c, 0xf066c606, 0x019a7b27, 0x02bb9310, 0xfe269065, 0x00698d98, 0x000af931, 0xfffb1a84, + 0x285f31b7, 0xf05949fb, 0x01be628c, 0x02a8fa2a, 0xfe29b9c1, 0x006b0411, 0x000a0e2f, 0xfffb4453, + 0x27ab5e12, 0xf04d4ade, 0x01e1bf58, 0x02965cdb, 0xfe2d03f2, 0x006c68f8, 0x000927a0, 0xfffb6d86, + 0x26f79fab, 0xf042c539, 0x02048ff8, 0x0283bce6, 0xfe306e35, 0x006dbc71, 0x00084589, 0xfffb961a, + 0x2643feda, 0xf039b587, 0x0226d2e6, 0x02711c05, 0xfe33f7c7, 0x006efea0, 0x000767f0, 0xfffbbe09, + 0x259083eb, 0xf032182f, 0x024886ad, 0x025e7bf0, 0xfe379fe3, 0x00702fae, 0x00068ed8, 0xfffbe552, + 0x24dd3721, 0xf02be98a, 0x0269a9e9, 0x024bde5a, 0xfe3b65c4, 0x00714fc0, 0x0005ba46, 0xfffc0bef, + 0x242a20b3, 0xf02725dc, 0x028a3b44, 0x023944ee, 0xfe3f48a5, 0x00725f02, 0x0004ea3a, 0xfffc31df, + 0x237748cf, 0xf023c95d, 0x02aa397b, 0x0226b156, 0xfe4347c0, 0x00735d9c, 0x00041eb9, 0xfffc571e, + 0x22c4b795, 0xf021d031, 0x02c9a359, 0x02142533, 0xfe476250, 0x00744bba, 0x000357c2, 0xfffc7ba9, + 0x2212751a, 0xf0213671, 0x02e877b9, 0x0201a223, 0xfe4b978e, 0x0075298a, 0x00029558, 0xfffc9f7e, + 0x21608968, 0xf021f823, 0x0306b586, 0x01ef29be, 0xfe4fe6b3, 0x0075f739, 0x0001d779, 0xfffcc29a, + 0x20aefc79, 0xf0241140, 0x03245bbc, 0x01dcbd96, 0xfe544efb, 0x0076b4f5, 0x00011e26, 0xfffce4fc, + 0x1ffdd63b, 0xf0277db1, 0x03416966, 0x01ca5f37, 0xfe58cf9d, 0x007762f0, 0x0000695e, 0xfffd06a1, + 0x1f4d1e8e, 0xf02c3953, 0x035ddd9e, 0x01b81028, 0xfe5d67d4, 0x0078015a, 0xffffb91f, 0xfffd2787, + 0x1e9cdd43, 0xf0323ff5, 0x0379b790, 0x01a5d1ea, 0xfe6216db, 0x00789065, 0xffff0d66, 0xfffd47ae, + 0x1ded1a1d, 0xf0398d56, 0x0394f674, 0x0193a5f9, 0xfe66dbeb, 0x00791043, 0xfffe6631, 0xfffd6713, }; +// we use 15 bits to interpolate between these samples +// this cannot change because the mul below rely on it. +static const int pLerpBits = 15; + +static pthread_once_t once_control = PTHREAD_ONCE_INIT; +static readCoefficientsFn readResampleCoefficients = NULL; + +/*static*/ AudioResamplerSinc::Constants AudioResamplerSinc::highQualityConstants; +/*static*/ AudioResamplerSinc::Constants AudioResamplerSinc::veryHighQualityConstants; + +void AudioResamplerSinc::init_routine() +{ + // for high quality resampler, the parameters for coefficients are compile-time constants + Constants *c = &highQualityConstants; + c->coefsBits = RESAMPLE_FIR_LERP_INT_BITS; + c->cShift = kNumPhaseBits - c->coefsBits; + c->cMask = ((1<< c->coefsBits)-1) << c->cShift; + c->pShift = kNumPhaseBits - c->coefsBits - pLerpBits; + c->pMask = ((1<< pLerpBits)-1) << c->pShift; + c->halfNumCoefs = RESAMPLE_FIR_NUM_COEF; + + // for very high quality resampler, the parameters are load-time constants + veryHighQualityConstants = highQualityConstants; + + // Open the dll to get the coefficients for VERY_HIGH_QUALITY + void *resampleCoeffLib = dlopen("libaudio-resampler.so", RTLD_NOW); + ALOGV("Open libaudio-resampler library = %p", resampleCoeffLib); + if (resampleCoeffLib == NULL) { + ALOGE("Could not open audio-resampler library: %s", dlerror()); + return; + } + + readResampleFirNumCoeffFn readResampleFirNumCoeff; + readResampleFirLerpIntBitsFn readResampleFirLerpIntBits; + + readResampleCoefficients = (readCoefficientsFn) + dlsym(resampleCoeffLib, "readResamplerCoefficients"); + readResampleFirNumCoeff = (readResampleFirNumCoeffFn) + dlsym(resampleCoeffLib, "readResampleFirNumCoeff"); + readResampleFirLerpIntBits = (readResampleFirLerpIntBitsFn) + dlsym(resampleCoeffLib, "readResampleFirLerpIntBits"); + + if (!readResampleCoefficients || !readResampleFirNumCoeff || !readResampleFirLerpIntBits) { + readResampleCoefficients = NULL; + dlclose(resampleCoeffLib); + resampleCoeffLib = NULL; + ALOGE("Could not find symbol: %s", dlerror()); + return; + } + + c = &veryHighQualityConstants; + c->coefsBits = readResampleFirLerpIntBits(); + c->cShift = kNumPhaseBits - c->coefsBits; + c->cMask = ((1<<c->coefsBits)-1) << c->cShift; + c->pShift = kNumPhaseBits - c->coefsBits - pLerpBits; + c->pMask = ((1<<pLerpBits)-1) << c->pShift; + // number of zero-crossing on each side + c->halfNumCoefs = readResampleFirNumCoeff(); + ALOGV("coefsBits = %d", c->coefsBits); + ALOGV("halfNumCoefs = %d", c->halfNumCoefs); + // note that we "leak" resampleCoeffLib until the process exits +} + // ---------------------------------------------------------------------------- static inline int32_t mulRL(int left, int32_t in, uint32_t vRL) { -#if defined(__arm__) && !defined(__thumb__) +#if USE_INLINE_ASSEMBLY int32_t out; if (left) { asm( "smultb %[out], %[in], %[vRL] \n" @@ -77,18 +404,15 @@ int32_t mulRL(int left, int32_t in, uint32_t vRL) } return out; #else - if (left) { - return int16_t(in>>16) * int16_t(vRL&0xFFFF); - } else { - return int16_t(in>>16) * int16_t(vRL>>16); - } + int16_t v = left ? int16_t(vRL) : int16_t(vRL>>16); + return int32_t((int64_t(in) * v) >> 16); #endif } static inline int32_t mulAdd(int16_t in, int32_t v, int32_t a) { -#if defined(__arm__) && !defined(__thumb__) +#if USE_INLINE_ASSEMBLY int32_t out; asm( "smlawb %[out], %[v], %[in], %[a] \n" : [out]"=r"(out) @@ -96,16 +420,14 @@ int32_t mulAdd(int16_t in, int32_t v, int32_t a) : ); return out; #else - return a + in * (v>>16); - // improved precision - // return a + in * (v>>16) + ((in * (v & 0xffff)) >> 16); + return a + int32_t((int64_t(v) * in) >> 16); #endif } static inline int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a) { -#if defined(__arm__) && !defined(__thumb__) +#if USE_INLINE_ASSEMBLY int32_t out; if (left) { asm( "smlawb %[out], %[v], %[inRL], %[a] \n" @@ -120,22 +442,17 @@ int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a) } return out; #else - if (left) { - return a + (int16_t(inRL&0xFFFF) * (v>>16)); - //improved precision - // return a + (int16_t(inRL&0xFFFF) * (v>>16)) + ((int16_t(inRL&0xFFFF) * (v & 0xffff)) >> 16); - } else { - return a + (int16_t(inRL>>16) * (v>>16)); - } + int16_t s = left ? int16_t(inRL) : int16_t(inRL>>16); + return a + int32_t((int64_t(v) * s) >> 16); #endif } // ---------------------------------------------------------------------------- AudioResamplerSinc::AudioResamplerSinc(int bitDepth, - int inChannelCount, int32_t sampleRate) - : AudioResampler(bitDepth, inChannelCount, sampleRate), - mState(0) + int inChannelCount, int32_t sampleRate, src_quality quality) + : AudioResampler(bitDepth, inChannelCount, sampleRate, quality), + mState(0), mImpulse(0), mRingFull(0), mFirCoefs(0) { /* * Layout of the state buffer for 32 tap: @@ -153,26 +470,50 @@ AudioResamplerSinc::AudioResamplerSinc(int bitDepth, * */ - const size_t numCoefs = 2*halfNumCoefs; - const size_t stateSize = numCoefs * inChannelCount * 2; - mState = new int16_t[stateSize]; - memset(mState, 0, sizeof(int16_t)*stateSize); - mImpulse = mState + (halfNumCoefs-1)*inChannelCount; - mRingFull = mImpulse + (numCoefs+1)*inChannelCount; + mVolumeSIMD[0] = 0; + mVolumeSIMD[1] = 0; + + // Load the constants for coefficients + int ok = pthread_once(&once_control, init_routine); + if (ok != 0) { + ALOGE("%s pthread_once failed: %d", __func__, ok); + } + mConstants = (quality == VERY_HIGH_QUALITY) ? + &veryHighQualityConstants : &highQualityConstants; } -AudioResamplerSinc::~AudioResamplerSinc() -{ - delete [] mState; + +AudioResamplerSinc::~AudioResamplerSinc() { + free(mState); } void AudioResamplerSinc::init() { + const Constants& c(*mConstants); + const size_t numCoefs = 2 * c.halfNumCoefs; + const size_t stateSize = numCoefs * mChannelCount * 2; + mState = (int16_t*)memalign(32, stateSize*sizeof(int16_t)); + memset(mState, 0, sizeof(int16_t)*stateSize); + mImpulse = mState + (c.halfNumCoefs-1)*mChannelCount; + mRingFull = mImpulse + (numCoefs+1)*mChannelCount; +} + +void AudioResamplerSinc::setVolume(int16_t left, int16_t right) { + AudioResampler::setVolume(left, right); + mVolumeSIMD[0] = int32_t(left)<<16; + mVolumeSIMD[1] = int32_t(right)<<16; } void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount, AudioBufferProvider* provider) { - mFirCoefs = (mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown; + // FIXME store current state (up or down sample) and only load the coefs when the state + // changes. Or load two pointers one for up and one for down in the init function. + // Not critical now since the read functions are fast, but would be important if read was slow. + if (mConstants == &veryHighQualityConstants && readResampleCoefficients) { + mFirCoefs = readResampleCoefficients( mInSampleRate <= mSampleRate ); + } else { + mFirCoefs = (mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown; + } // select the appropriate resampler switch (mChannelCount) { @@ -190,6 +531,8 @@ template<int CHANNELS> void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount, AudioBufferProvider* provider) { + const Constants& c(*mConstants); + const size_t headOffset = c.halfNumCoefs*CHANNELS; int16_t* impulse = mImpulse; uint32_t vRL = mVolumeRL; size_t inputIndex = mInputIndex; @@ -224,43 +567,31 @@ void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount, } } } - int16_t *in = mBuffer.i16; + int16_t const * const in = mBuffer.i16; const size_t frameCount = mBuffer.frameCount; // Always read-in the first samples from the input buffer - int16_t* head = impulse + halfNumCoefs*CHANNELS; - head[0] = in[inputIndex*CHANNELS + 0]; - if (CHANNELS == 2) - head[1] = in[inputIndex*CHANNELS + 1]; + int16_t* head = impulse + headOffset; + for (size_t i=0 ; i<CHANNELS ; i++) { + head[i] = in[inputIndex*CHANNELS + i]; + } // handle boundary case - int32_t l, r; - while (outputIndex < outputSampleCount) { - filterCoefficient<CHANNELS>(l, r, phaseFraction, impulse); - out[outputIndex++] += 2 * mulRL(1, l, vRL); - out[outputIndex++] += 2 * mulRL(0, r, vRL); + while (CC_LIKELY(outputIndex < outputSampleCount)) { + filterCoefficient<CHANNELS>(&out[outputIndex], phaseFraction, impulse, vRL); + outputIndex += 2; phaseFraction += phaseIncrement; - const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; - if (phaseIndex == 1) { - inputIndex++; - if (inputIndex >= frameCount) - break; // need a new buffer - read<CHANNELS>(impulse, phaseFraction, in, inputIndex); - } else if (phaseIndex == 2) { // maximum value - inputIndex++; - if (inputIndex >= frameCount) - break; // 0 frame available, 2 frames needed - // read first frame - read<CHANNELS>(impulse, phaseFraction, in, inputIndex); + const size_t phaseIndex = phaseFraction >> kNumPhaseBits; + for (size_t i=0 ; i<phaseIndex ; i++) { inputIndex++; - if (inputIndex >= frameCount) - break; // 0 frame available, 1 frame needed - // read second frame + if (inputIndex >= frameCount) { + goto done; // need a new buffer + } read<CHANNELS>(impulse, phaseFraction, in, inputIndex); } } - +done: // if done with buffer, save samples if (inputIndex >= frameCount) { inputIndex -= frameCount; @@ -286,63 +617,215 @@ void AudioResamplerSinc::read( int16_t*& impulse, uint32_t& phaseFraction, const int16_t* in, size_t inputIndex) { - const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits; impulse += CHANNELS; phaseFraction -= 1LU<<kNumPhaseBits; - if (impulse >= mRingFull) { - const size_t stateSize = (halfNumCoefs*2)*CHANNELS; + + const Constants& c(*mConstants); + if (CC_UNLIKELY(impulse >= mRingFull)) { + const size_t stateSize = (c.halfNumCoefs*2)*CHANNELS; memcpy(mState, mState+stateSize, sizeof(int16_t)*stateSize); impulse -= stateSize; } - int16_t* head = impulse + halfNumCoefs*CHANNELS; - head[0] = in[inputIndex*CHANNELS + 0]; - if (CHANNELS == 2) - head[1] = in[inputIndex*CHANNELS + 1]; + + int16_t* head = impulse + c.halfNumCoefs*CHANNELS; + for (size_t i=0 ; i<CHANNELS ; i++) { + head[i] = in[inputIndex*CHANNELS + i]; + } } template<int CHANNELS> void AudioResamplerSinc::filterCoefficient( - int32_t& l, int32_t& r, uint32_t phase, const int16_t *samples) + int32_t* out, uint32_t phase, const int16_t *samples, uint32_t vRL) { + // 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. + // compute the index of the coefficient on the positive side and // negative side - uint32_t indexP = (phase & cMask) >> cShift; - uint16_t lerpP = (phase & pMask) >> pShift; - uint32_t indexN = (-phase & cMask) >> cShift; - uint16_t lerpN = (-phase & pMask) >> pShift; - if ((indexP == 0) && (lerpP == 0)) { - indexN = cMask >> cShift; - lerpN = pMask >> pShift; - } + const Constants& c(*mConstants); + const int32_t ONE = c.cMask | c.pMask; + uint32_t indexP = ( phase & c.cMask) >> c.cShift; + uint32_t lerpP = ( phase & c.pMask) >> c.pShift; + uint32_t indexN = ((ONE-phase) & c.cMask) >> c.cShift; + uint32_t lerpN = ((ONE-phase) & c.pMask) >> c.pShift; + + const size_t offset = c.halfNumCoefs; + indexP *= offset; + indexN *= offset; + + int32_t const* coefsP = mFirCoefs + indexP; + int32_t const* coefsN = mFirCoefs + indexN; + int16_t const* sP = samples; + int16_t const* sN = samples + CHANNELS; - l = 0; - r = 0; - const int32_t* coefs = mFirCoefs; - const int16_t *sP = samples; - const int16_t *sN = samples+CHANNELS; - for (unsigned int i=0 ; i<halfNumCoefs/4 ; i++) { - interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP); - interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN); - sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits; - interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP); - interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN); - sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits; - interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP); - interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN); - sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits; - interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP); - interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN); - sP -= CHANNELS; sN += CHANNELS; coefs += 1<<coefsBits; + size_t count = offset; + + if (!USE_NEON) { + int32_t l = 0; + int32_t r = 0; + for (size_t i=0 ; i<count ; i++) { + interpolate<CHANNELS>(l, r, coefsP++, offset, lerpP, sP); + sP -= CHANNELS; + interpolate<CHANNELS>(l, r, coefsN++, offset, lerpN, sN); + sN += CHANNELS; + } + out[0] += 2 * mulRL(1, l, vRL); + out[1] += 2 * mulRL(0, r, vRL); + } else if (CHANNELS == 1) { + int32_t const* coefsP1 = coefsP + offset; + int32_t const* coefsN1 = coefsN + offset; + sP -= CHANNELS*3; + asm ( + "vmov.32 d2[0], %[lerpP] \n" // load the positive phase + "vmov.32 d2[1], %[lerpN] \n" // load the negative phase + "veor q0, q0, q0 \n" // result, initialize to 0 + "vshl.s32 d2, d2, #16 \n" // convert to 32 bits + + "1: \n" + "vld1.16 { d4}, [%[sP]] \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.16 { d6}, [%[sN]]! \n" // load 4 16-bits stereo samples + "vld1.32 {q10}, [%[coefsN0]:128]! \n" // load 4 32-bits coefs + "vld1.32 {q11}, [%[coefsN1]: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[1] \n" // interpolate (step3) 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 + "subs %[count], %[count], #4 \n" // update loop counter + + "vqrdmulh.s32 q12, q12, q8 \n" // multiply samples by interpolated coef + "vqrdmulh.s32 q14, q14, q10 \n" // multiply samples by interpolated coef + "sub %[sP], %[sP], #8 \n" // move pointer to next set of samples + + "vadd.s32 q0, q0, q12 \n" // accumulate result + "vadd.s32 q0, q0, q14 \n" // accumulate result + + "bne 1b \n" // loop + + "vld1.s32 {d2}, [%[vLR]] \n" // load volumes + "vld1.s32 {d3}, %[out] \n" // load the output + "vpadd.s32 d0, d0, d1 \n" // add all 4 partial sums + "vpadd.s32 d0, d0, d0 \n" // together + "vdup.i32 d0, d0[0] \n" // interleave L,R channels + "vqrdmulh.s32 d0, d0, d2 \n" // apply volume + "vadd.s32 d3, d3, d0 \n" // accumulate result + "vst1.s32 {d3}, %[out] \n" // store result + + : [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), + [lerpN] "r" (lerpN), + [vLR] "r" (mVolumeSIMD) + : "cc", "memory", + "q0", "q1", "q2", "q3", + "q8", "q9", "q10", "q11", + "q12", "q14" + ); + } else if (CHANNELS == 2) { + int32_t const* coefsP1 = coefsP + offset; + int32_t const* coefsN1 = coefsN + offset; + sP -= CHANNELS*3; + asm ( + "vmov.32 d2[0], %[lerpP] \n" // load the positive phase + "vmov.32 d2[1], %[lerpN] \n" // load the negative phase + "veor q0, q0, q0 \n" // result, initialize to 0 + "veor q4, q4, q4 \n" // result, initialize to 0 + "vshl.s32 d2, d2, #16 \n" // convert to 32 bits + + "1: \n" + "vld2.16 {d4,d5}, [%[sP]] \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 + "vld2.16 {d6,d7}, [%[sN]]! \n" // load 4 16-bits stereo samples + "vld1.32 {q10}, [%[coefsN0]:128]! \n" // load 4 32-bits coefs + "vld1.32 {q11}, [%[coefsN1]:128]! \n" // load 4 32-bits coefs for interpolation + + "vrev64.16 d4, d4 \n" // reverse 2 frames of the positive side + "vrev64.16 d5, d5 \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 + "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 + "subs %[count], %[count], #4 \n" // update loop counter + + "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 + "sub %[sP], %[sP], #16 \n" // move pointer to next set of samples + + "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 + + "bne 1b \n" // loop + + "vld1.s32 {d2}, [%[vLR]] \n" // load volumes + "vld1.s32 {d3}, %[out] \n" // load the output + "vpadd.s32 d0, d0, d1 \n" // add all 4 partial sums from q0 + "vpadd.s32 d8, d8, d9 \n" // add all 4 partial sums from q4 + "vpadd.s32 d0, d0, d0 \n" // together + "vpadd.s32 d8, d8, d8 \n" // together + "vtrn.s32 d0, d8 \n" // interlace L,R channels + "vqrdmulh.s32 d0, d0, d2 \n" // apply volume + "vadd.s32 d3, d3, d0 \n" // accumulate result + "vst1.s32 {d3}, %[out] \n" // store result + + : [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), + [lerpN] "r" (lerpN), + [vLR] "r" (mVolumeSIMD) + : "cc", "memory", + "q0", "q1", "q2", "q3", "q4", + "q8", "q9", "q10", "q11", + "q12", "q13", "q14", "q15" + ); } } template<int CHANNELS> void AudioResamplerSinc::interpolate( int32_t& l, int32_t& r, - const int32_t* coefs, int16_t lerp, const int16_t* samples) + const int32_t* coefs, size_t offset, + int32_t lerp, const int16_t* samples) { int32_t c0 = coefs[0]; - int32_t c1 = coefs[1]; + int32_t c1 = coefs[offset]; int32_t sinc = mulAdd(lerp, (c1-c0)<<1, c0); if (CHANNELS == 2) { uint32_t rl = *reinterpret_cast<const uint32_t*>(samples); @@ -352,6 +835,5 @@ void AudioResamplerSinc::interpolate( r = l = mulAdd(samples[0], sinc, l); } } - // ---------------------------------------------------------------------------- }; // namespace android diff --git a/services/audioflinger/AudioResamplerSinc.h b/services/audioflinger/AudioResamplerSinc.h index f0a07b8d..09c68660 100644 --- a/services/audioflinger/AudioResamplerSinc.h +++ b/services/audioflinger/AudioResamplerSinc.h @@ -25,11 +25,17 @@ namespace android { + +typedef const int32_t * (*readCoefficientsFn)(bool upDownSample); +typedef int32_t (*readResampleFirNumCoeffFn)(); +typedef int32_t (*readResampleFirLerpIntBitsFn)(); + // ---------------------------------------------------------------------------- class AudioResamplerSinc : public AudioResampler { public: - AudioResamplerSinc(int bitDepth, int inChannelCount, int32_t sampleRate); + AudioResamplerSinc(int bitDepth, int inChannelCount, int32_t sampleRate, + src_quality quality = HIGH_QUALITY); virtual ~AudioResamplerSinc(); @@ -38,18 +44,21 @@ public: private: void init(); + virtual void setVolume(int16_t left, int16_t right); + template<int CHANNELS> void resample(int32_t* out, size_t outFrameCount, AudioBufferProvider* provider); template<int CHANNELS> inline void filterCoefficient( - int32_t& l, int32_t& r, uint32_t phase, const int16_t *samples); + int32_t* out, uint32_t phase, const int16_t *samples, uint32_t vRL); template<int CHANNELS> inline void interpolate( int32_t& l, int32_t& r, - const int32_t* coefs, int16_t lerp, const int16_t* samples); + const int32_t* coefs, size_t offset, + int32_t lerp, const int16_t* samples); template<int CHANNELS> inline void read(int16_t*& impulse, uint32_t& phaseFraction, @@ -58,6 +67,7 @@ private: int16_t *mState; int16_t *mImpulse; int16_t *mRingFull; + int32_t mVolumeSIMD[2]; const int32_t * mFirCoefs; static const int32_t mFirCoefsDown[]; @@ -65,21 +75,23 @@ private: // ---------------------------------------------------------------------------- static const int32_t RESAMPLE_FIR_NUM_COEF = 8; - static const int32_t RESAMPLE_FIR_LERP_INT_BITS = 4; - - // we have 16 coefs samples per zero-crossing - static const int coefsBits = RESAMPLE_FIR_LERP_INT_BITS; // 4 - static const int cShift = kNumPhaseBits - coefsBits; // 26 - static const uint32_t cMask = ((1<<coefsBits)-1) << cShift; // 0xf<<26 = 3c00 0000 - - // and we use 15 bits to interpolate between these samples - // this cannot change because the mul below rely on it. - static const int pLerpBits = 15; - static const int pShift = kNumPhaseBits - coefsBits - pLerpBits; // 11 - static const uint32_t pMask = ((1<<pLerpBits)-1) << pShift; // 0x7fff << 11 - - // number of zero-crossing on each side - static const unsigned int halfNumCoefs = RESAMPLE_FIR_NUM_COEF; + static const int32_t RESAMPLE_FIR_LERP_INT_BITS = 7; + + struct Constants { + int coefsBits; + int cShift; + uint32_t cMask; + int pShift; + uint32_t pMask; + // number of zero-crossing on each side + unsigned int halfNumCoefs; + }; + + static Constants highQualityConstants; + static Constants veryHighQualityConstants; + const Constants *mConstants; // points to appropriate set of coefficient parameters + + static void init_routine(); }; // ---------------------------------------------------------------------------- diff --git a/services/audioflinger/AudioStreamInSource.cpp b/services/audioflinger/AudioStreamInSource.cpp deleted file mode 100644 index 8b4bebf0..00000000 --- a/services/audioflinger/AudioStreamInSource.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "AudioStreamInSource" -//#define LOG_NDEBUG 0 - -#include <cutils/compiler.h> -#include <utils/Log.h> -#include "AudioStreamInSource.h" - -namespace android { - -AudioStreamInSource::AudioStreamInSource(audio_stream_in *stream) : - NBAIO_Source(), - mStream(stream), - mStreamBufferSizeBytes(0), - mFramesOverrun(0), - mOverruns(0) -{ - ALOG_ASSERT(stream != NULL); -} - -AudioStreamInSource::~AudioStreamInSource() -{ -} - -ssize_t AudioStreamInSource::negotiate(const NBAIO_Format offers[], size_t numOffers, - NBAIO_Format counterOffers[], size_t& numCounterOffers) -{ - if (mFormat == Format_Invalid) { - mStreamBufferSizeBytes = mStream->common.get_buffer_size(&mStream->common); - audio_format_t streamFormat = mStream->common.get_format(&mStream->common); - if (streamFormat == AUDIO_FORMAT_PCM_16_BIT) { - uint32_t sampleRate = mStream->common.get_sample_rate(&mStream->common); - audio_channel_mask_t channelMask = - (audio_channel_mask_t) mStream->common.get_channels(&mStream->common); - mFormat = Format_from_SR_C(sampleRate, popcount(channelMask)); - mBitShift = Format_frameBitShift(mFormat); - } - } - return NBAIO_Source::negotiate(offers, numOffers, counterOffers, numCounterOffers); -} - -size_t AudioStreamInSource::framesOverrun() -{ - uint32_t framesOverrun = mStream->get_input_frames_lost(mStream); - if (framesOverrun > 0) { - mFramesOverrun += framesOverrun; - // FIXME only increment for contiguous ranges - ++mOverruns; - } - return mFramesOverrun; -} - -ssize_t AudioStreamInSource::read(void *buffer, size_t count) -{ - if (CC_UNLIKELY(mFormat == Format_Invalid)) { - return NEGOTIATE; - } - ssize_t bytesRead = mStream->read(mStream, buffer, count << mBitShift); - if (bytesRead > 0) { - size_t framesRead = bytesRead >> mBitShift; - mFramesRead += framesRead; - return framesRead; - } else { - return bytesRead; - } -} - -} // namespace android diff --git a/services/audioflinger/AudioStreamInSource.h b/services/audioflinger/AudioStreamInSource.h deleted file mode 100644 index 07d8c89e..00000000 --- a/services/audioflinger/AudioStreamInSource.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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_STREAM_IN_SOURCE_H -#define ANDROID_AUDIO_STREAM_IN_SOURCE_H - -#include <hardware/audio.h> -#include "NBAIO.h" - -namespace android { - -// not multi-thread safe -class AudioStreamInSource : public NBAIO_Source { - -public: - AudioStreamInSource(audio_stream_in *stream); - virtual ~AudioStreamInSource(); - - // NBAIO_Port interface - - virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers, - NBAIO_Format counterOffers[], size_t& numCounterOffers); - //virtual NBAIO_Format format() const; - - // NBAIO_Sink interface - - //virtual size_t framesRead() const; - virtual size_t framesOverrun(); - virtual size_t overruns() { (void) framesOverrun(); return mOverruns; } - - // This is an over-estimate, and could dupe the caller into making a blocking read() - // FIXME Use an audio HAL API to query the buffer filling status when it's available. - virtual ssize_t availableToRead() { return mStreamBufferSizeBytes >> mBitShift; } - - virtual ssize_t read(void *buffer, size_t count); - - // NBAIO_Sink end - -#if 0 // until necessary - audio_stream_in *stream() const { return mStream; } -#endif - -private: - audio_stream_in * const mStream; - size_t mStreamBufferSizeBytes; // as reported by get_buffer_size() - size_t mFramesOverrun; - size_t mOverruns; -}; - -} // namespace android - -#endif // ANDROID_AUDIO_STREAM_IN_SOURCE_H diff --git a/services/audioflinger/AudioStreamOutSink.cpp b/services/audioflinger/AudioStreamOutSink.cpp deleted file mode 100644 index 8a5aa0c3..00000000 --- a/services/audioflinger/AudioStreamOutSink.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "AudioStreamOutSink" -//#define LOG_NDEBUG 0 - -#include <utils/Log.h> -#include "AudioStreamOutSink.h" - -namespace android { - -AudioStreamOutSink::AudioStreamOutSink(audio_stream_out *stream) : - NBAIO_Sink(), - mStream(stream), - mStreamBufferSizeBytes(0) -{ - ALOG_ASSERT(stream != NULL); -} - -AudioStreamOutSink::~AudioStreamOutSink() -{ -} - -ssize_t AudioStreamOutSink::negotiate(const NBAIO_Format offers[], size_t numOffers, - NBAIO_Format counterOffers[], size_t& numCounterOffers) -{ - if (mFormat == Format_Invalid) { - mStreamBufferSizeBytes = mStream->common.get_buffer_size(&mStream->common); - audio_format_t streamFormat = mStream->common.get_format(&mStream->common); - if (streamFormat == AUDIO_FORMAT_PCM_16_BIT) { - uint32_t sampleRate = mStream->common.get_sample_rate(&mStream->common); - audio_channel_mask_t channelMask = - (audio_channel_mask_t) mStream->common.get_channels(&mStream->common); - mFormat = Format_from_SR_C(sampleRate, popcount(channelMask)); - mBitShift = Format_frameBitShift(mFormat); - } - } - return NBAIO_Sink::negotiate(offers, numOffers, counterOffers, numCounterOffers); -} - -ssize_t AudioStreamOutSink::write(const void *buffer, size_t count) -{ - if (!mNegotiated) { - return NEGOTIATE; - } - ALOG_ASSERT(mFormat != Format_Invalid); - ssize_t ret = mStream->write(mStream, buffer, count << mBitShift); - if (ret > 0) { - ret >>= mBitShift; - mFramesWritten += ret; - } else { - // FIXME verify HAL implementations are returning the correct error codes e.g. WOULD_BLOCK - } - return ret; -} - -} // namespace android diff --git a/services/audioflinger/AudioStreamOutSink.h b/services/audioflinger/AudioStreamOutSink.h deleted file mode 100644 index 1eff3f64..00000000 --- a/services/audioflinger/AudioStreamOutSink.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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_STREAM_OUT_SINK_H -#define ANDROID_AUDIO_STREAM_OUT_SINK_H - -#include <hardware/audio.h> -#include "NBAIO.h" - -namespace android { - -// not multi-thread safe -class AudioStreamOutSink : public NBAIO_Sink { - -public: - AudioStreamOutSink(audio_stream_out *stream); - virtual ~AudioStreamOutSink(); - - // NBAIO_Port interface - - virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers, - NBAIO_Format counterOffers[], size_t& numCounterOffers); - //virtual NBAIO_Format format(); - - // NBAIO_Sink interface - - //virtual size_t framesWritten() const; - //virtual size_t framesUnderrun() const; - //virtual size_t underruns() const; - - // This is an over-estimate, and could dupe the caller into making a blocking write() - // FIXME Use an audio HAL API to query the buffer emptying status when it's available. - virtual ssize_t availableToWrite() const { return mStreamBufferSizeBytes >> mBitShift; } - - virtual ssize_t write(const void *buffer, size_t count); - - // NBAIO_Sink end - -#if 0 // until necessary - audio_stream_out *stream() const { return mStream; } -#endif - -private: - audio_stream_out * const mStream; - size_t mStreamBufferSizeBytes; // as reported by get_buffer_size() -}; - -} // namespace android - -#endif // ANDROID_AUDIO_STREAM_OUT_SINK_H diff --git a/services/audioflinger/ExtendedAudioBufferProvider.h b/services/audioflinger/ExtendedAudioBufferProvider.h deleted file mode 100644 index 88279b48..00000000 --- a/services/audioflinger/ExtendedAudioBufferProvider.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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_EXTENDED_AUDIO_BUFFER_PROVIDER_H -#define ANDROID_EXTENDED_AUDIO_BUFFER_PROVIDER_H - -#include "AudioBufferProvider.h" - -namespace android { - -class ExtendedAudioBufferProvider : public AudioBufferProvider { -public: - virtual size_t framesReady() const = 0; // see description at AudioFlinger.h -}; - -} // namespace android - -#endif // ANDROID_EXTENDED_AUDIO_BUFFER_PROVIDER_H diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index 76521323..3c8a256a 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -222,8 +222,8 @@ bool FastMixer::threadLoop() mixBuffer = new short[frameCount * 2]; periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00 underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75 - overrunNs = (frameCount * 250000000LL) / sampleRate; // 0.25 - forceNs = (frameCount * 750000000LL) / sampleRate; // 0.75 + overrunNs = (frameCount * 500000000LL) / sampleRate; // 0.50 + forceNs = (frameCount * 950000000LL) / sampleRate; // 0.95 warmupNs = (frameCount * 500000000LL) / sampleRate; // 0.50 } else { periodNs = 0; @@ -281,8 +281,9 @@ bool FastMixer::threadLoop() AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider; ALOG_ASSERT(bufferProvider != NULL && fastTrackNames[i] == -1); if (mixer != NULL) { - // calling getTrackName with default channel mask - name = mixer->getTrackName(AUDIO_CHANNEL_OUT_STEREO); + // 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); @@ -399,8 +400,13 @@ bool FastMixer::threadLoop() ftDump->mUnderruns = underruns; ftDump->mFramesReady = framesReady; } + + int64_t pts; + if (outputSink == NULL || (OK != outputSink->getNextWriteTimestamp(&pts))) + pts = AudioBufferProvider::kInvalidPTS; + // process() is CPU-bound - mixer->process(AudioBufferProvider::kInvalidPTS); + mixer->process(pts); mixBufferState = MIXED; } else if (mixBufferState == MIXED) { mixBufferState = UNDEFINED; @@ -448,6 +454,9 @@ bool FastMixer::threadLoop() 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; @@ -607,6 +616,20 @@ FastMixerDumpState::~FastMixerDumpState() { } +// helper function called by qsort() +static int compare_uint32_t(const void *pa, const void *pb) +{ + uint32_t a = *(const uint32_t *)pa; + uint32_t b = *(const uint32_t *)pb; + if (a < b) { + return -1; + } else if (a > b) { + return 1; + } else { + return 0; + } +} + void FastMixerDumpState::dump(int fd) { if (mCommand == FastMixerState::INITIAL) { @@ -669,10 +692,18 @@ void FastMixerDumpState::dump(int fd) CentralTendencyStatistics kHz, loadMHz; uint32_t previousCpukHz = 0; #endif + // Assuming a normal distribution for cycle times, three standard deviations on either side of + // the mean account for 99.73% of the population. So if we take each tail to be 1/1000 of the + // sample set, we get 99.8% combined, or close to three standard deviations. + static const uint32_t kTailDenominator = 1000; + uint32_t *tail = n >= kTailDenominator ? new uint32_t[n] : NULL; // loop over all the samples - for (; n > 0; --n) { + for (uint32_t j = 0; j < n; ++j) { size_t i = oldestClosed++ & (kSamplingN - 1); uint32_t wallNs = mMonotonicNs[i]; + if (tail != NULL) { + tail[j] = wallNs; + } wall.sample(wallNs); uint32_t sampleLoadNs = mLoadNs[i]; loadNs.sample(sampleLoadNs); @@ -706,6 +737,23 @@ void FastMixerDumpState::dump(int fd) " mean=%.1f min=%.1f max=%.1f stddev=%.1f\n", loadMHz.mean(), loadMHz.minimum(), loadMHz.maximum(), loadMHz.stddev()); #endif + if (tail != NULL) { + qsort(tail, n, sizeof(uint32_t), compare_uint32_t); + // assume same number of tail samples on each side, left and right + uint32_t count = n / kTailDenominator; + CentralTendencyStatistics left, right; + for (uint32_t i = 0; i < count; ++i) { + left.sample(tail[i]); + right.sample(tail[n - (i + 1)]); + } + fdprintf(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); + delete[] tail; + } #endif // The active track mask and track states are updated non-atomically. // So if we relied on isActive to decide whether to display, diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h index bc69c9c4..6e53f217 100644 --- a/services/audioflinger/FastMixerState.h +++ b/services/audioflinger/FastMixerState.h @@ -18,8 +18,8 @@ #define ANDROID_AUDIO_FAST_MIXER_STATE_H #include <system/audio.h> -#include "ExtendedAudioBufferProvider.h" -#include "NBAIO.h" +#include <media/ExtendedAudioBufferProvider.h> +#include <media/nbaio/NBAIO.h> namespace android { diff --git a/services/audioflinger/LibsndfileSink.cpp b/services/audioflinger/LibsndfileSink.cpp deleted file mode 100644 index efc1c8f7..00000000 --- a/services/audioflinger/LibsndfileSink.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "LibsndfileSink" -//#define LOG_NDEBUG 0 - -#include <utils/Errors.h> -#include <utils/Log.h> -#include "LibsndfileSink.h" - -namespace android { - -LibsndfileSink::LibsndfileSink(SNDFILE *sndfile, const SF_INFO &sfinfo) : - NBAIO_Sink(Format_from_SR_C(sfinfo.samplerate, sfinfo.channels)), - mSndfile(sndfile) -{ -} - -LibsndfileSink::~LibsndfileSink() -{ - // do not close mSndfile; we don't own it -} - -ssize_t LibsndfileSink::write(const void *buffer, size_t count) -{ - if (!mNegotiated) { - return (ssize_t) NEGOTIATE; - } - if (mSndfile == NULL) { - return (ssize_t) NO_INIT; - } - sf_count_t actual = sf_writef_short(mSndfile, (short *) buffer, (sf_count_t) count); - mFramesWritten += actual; - return actual; -} - -} // namespace android diff --git a/services/audioflinger/LibsndfileSink.h b/services/audioflinger/LibsndfileSink.h deleted file mode 100644 index f5d53d51..00000000 --- a/services/audioflinger/LibsndfileSink.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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_LIBSNDFILE_SINK_H -#define ANDROID_AUDIO_LIBSNDFILE_SINK_H - -#include "NBAIO.h" -#include "sndfile.h" - -// Implementation of NBAIO_Sink that wraps a libsndfile opened in SFM_WRITE mode - -namespace android { - -class LibsndfileSink : public NBAIO_Sink { - -public: - LibsndfileSink(SNDFILE *sndfile, const SF_INFO &sfinfo); - virtual ~LibsndfileSink(); - - // NBAIO_Port interface - - //virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers, - // NBAIO_Format counterOffers[], size_t& numCounterOffers); - //virtual NBAIO_Format format() const; - - // NBAIO_Sink interface - - //virtual size_t framesWritten() const; - //virtual size_t framesUnderrun() const; - //virtual size_t underruns() const; - //virtual ssize_t availableToWrite() const; - virtual ssize_t write(const void *buffer, size_t count); - //virtual ssize_t writeVia(writeVia_t via, size_t total, void *user, size_t block); - -private: - SNDFILE * mSndfile; -}; - -} // namespace android - -#endif // ANDROID_AUDIO_LIBSNDFILE_SINK_H diff --git a/services/audioflinger/LibsndfileSource.cpp b/services/audioflinger/LibsndfileSource.cpp deleted file mode 100644 index 28317d65..00000000 --- a/services/audioflinger/LibsndfileSource.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "LibsndfileSource" -//#define LOG_NDEBUG 0 - -#include <utils/Errors.h> -#include <utils/Log.h> -#include "LibsndfileSource.h" - -namespace android { - -LibsndfileSource::LibsndfileSource(SNDFILE *sndfile, const SF_INFO &sfinfo, bool loop) : - NBAIO_Source(Format_from_SR_C(sfinfo.samplerate, sfinfo.channels)), - mSndfile(sndfile), - mEstimatedFramesUntilEOF(sfinfo.frames), - mLooping(loop && sfinfo.seekable), - mReadAnyFramesThisLoopCycle(false) -{ -} - -LibsndfileSource::~LibsndfileSource() -{ - // do not close mSndfile; we don't own it -} - -ssize_t LibsndfileSource::availableToRead() -{ - // after we reach the presumed EOF, report infinity just in case there's actually more - return !mLooping && mEstimatedFramesUntilEOF > 0 ? mEstimatedFramesUntilEOF : SSIZE_MAX; -} - -ssize_t LibsndfileSource::read(void *buffer, size_t count) -{ - if (!mNegotiated) { - return (ssize_t) NEGOTIATE; - } - if (mSndfile == NULL) { - return (ssize_t) NO_INIT; - } - sf_count_t actual = sf_readf_short(mSndfile, (short *) buffer, (sf_count_t) count); - // Detect EOF by zero frames read, not by mFramesUntilEOF as it could be inaccurate - if (actual == 0) { - if (mLooping) { - if (mReadAnyFramesThisLoopCycle) { - (void) sf_seek(mSndfile, (sf_count_t) 0, SEEK_SET); - mReadAnyFramesThisLoopCycle = false; - } else { - // We didn't read any frames during the current loop cycle, so disable - // further looping to prevent the caller from busy waiting at read(). - // This is especially important when looping an empty file. - mLooping = false; - } - } - } else { - mFramesRead += actual; - if (actual >= mEstimatedFramesUntilEOF) { - mEstimatedFramesUntilEOF = 0; - } else { - mEstimatedFramesUntilEOF -= actual; - } - mReadAnyFramesThisLoopCycle = true; - } - return actual; -} - -} // namespace android diff --git a/services/audioflinger/LibsndfileSource.h b/services/audioflinger/LibsndfileSource.h deleted file mode 100644 index 4fbdb4b7..00000000 --- a/services/audioflinger/LibsndfileSource.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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_LIBSNDFILE_SOURCE_H -#define ANDROID_AUDIO_LIBSNDFILE_SOURCE_H - -#include "NBAIO.h" -#include "sndfile.h" - -// Implementation of NBAIO_Source that wraps a libsndfile opened in SFM_READ mode - -namespace android { - -class LibsndfileSource : public NBAIO_Source { - -public: - // If 'loop' is true and it permits seeking, then we'll act as an infinite source - LibsndfileSource(SNDFILE *sndfile, const SF_INFO &sfinfo, bool loop = false); - virtual ~LibsndfileSource(); - - // NBAIO_Port interface - - //virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers, - // NBAIO_Format counterOffers[], size_t& numCounterOffers); - //virtual NBAIO_Format format() const; - - // NBAIO_Source interface - - //virtual size_t framesRead() const; - //virtual size_t framesOverrun(); - //virtual size_t overruns(); - virtual ssize_t availableToRead(); - virtual ssize_t read(void *buffer, size_t count); - //virtual ssize_t readVia(readVia_t via, size_t total, void *user, size_t block); - -private: - SNDFILE * mSndfile; - sf_count_t mEstimatedFramesUntilEOF; - bool mLooping; - bool mReadAnyFramesThisLoopCycle; -}; - -} // namespace android - -#endif // ANDROID_AUDIO_LIBSNDFILE_SOURCE_H diff --git a/services/audioflinger/MonoPipe.cpp b/services/audioflinger/MonoPipe.cpp deleted file mode 100644 index f3fc19a7..00000000 --- a/services/audioflinger/MonoPipe.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "MonoPipe" -//#define LOG_NDEBUG 0 - -#include <cutils/atomic.h> -#include <cutils/compiler.h> -#include <utils/Log.h> -#include <utils/Trace.h> -#include "MonoPipe.h" -#include "roundup.h" - -namespace android { - -MonoPipe::MonoPipe(size_t reqFrames, NBAIO_Format format, bool writeCanBlock) : - NBAIO_Sink(format), - mReqFrames(reqFrames), - mMaxFrames(roundup(reqFrames)), - mBuffer(malloc(mMaxFrames * Format_frameSize(format))), - mFront(0), - mRear(0), - mWriteTsValid(false), - // mWriteTs - mSetpoint((reqFrames * 11) / 16), - mWriteCanBlock(writeCanBlock) -{ -} - -MonoPipe::~MonoPipe() -{ - free(mBuffer); -} - -ssize_t MonoPipe::availableToWrite() const -{ - if (CC_UNLIKELY(!mNegotiated)) { - return NEGOTIATE; - } - // uses mMaxFrames not mReqFrames, so allows "over-filling" the pipe beyond requested limit - ssize_t ret = mMaxFrames - (mRear - android_atomic_acquire_load(&mFront)); - ALOG_ASSERT((0 <= ret) && (ret <= mMaxFrames)); - return ret; -} - -ssize_t MonoPipe::write(const void *buffer, size_t count) -{ - if (CC_UNLIKELY(!mNegotiated)) { - return NEGOTIATE; - } - size_t totalFramesWritten = 0; - while (count > 0) { - // can't return a negative value, as we already checked for !mNegotiated - size_t avail = availableToWrite(); - size_t written = avail; - if (CC_LIKELY(written > count)) { - written = count; - } - size_t rear = mRear & (mMaxFrames - 1); - size_t part1 = mMaxFrames - rear; - if (part1 > written) { - part1 = written; - } - if (CC_LIKELY(part1 > 0)) { - memcpy((char *) mBuffer + (rear << mBitShift), buffer, part1 << mBitShift); - if (CC_UNLIKELY(rear + part1 == mMaxFrames)) { - size_t part2 = written - part1; - if (CC_LIKELY(part2 > 0)) { - memcpy(mBuffer, (char *) buffer + (part1 << mBitShift), part2 << mBitShift); - } - } - android_atomic_release_store(written + mRear, &mRear); - totalFramesWritten += written; - } - if (!mWriteCanBlock) { - break; - } - count -= written; - buffer = (char *) buffer + (written << mBitShift); - // Simulate blocking I/O by sleeping at different rates, depending on a throttle. - // The throttle tries to keep the mean pipe depth near the setpoint, with a slight jitter. - uint32_t ns; - if (written > 0) { - size_t filled = (mMaxFrames - avail) + written; - // FIXME cache these values to avoid re-computation - if (filled <= mSetpoint / 2) { - // pipe is (nearly) empty, fill quickly - ns = written * ( 500000000 / Format_sampleRate(mFormat)); - } else if (filled <= (mSetpoint * 3) / 4) { - // pipe is below setpoint, fill at slightly faster rate - ns = written * ( 750000000 / Format_sampleRate(mFormat)); - } else if (filled <= (mSetpoint * 5) / 4) { - // pipe is at setpoint, fill at nominal rate - ns = written * (1000000000 / Format_sampleRate(mFormat)); - } else if (filled <= (mSetpoint * 3) / 2) { - // pipe is above setpoint, fill at slightly slower rate - ns = written * (1150000000 / Format_sampleRate(mFormat)); - } else if (filled <= (mSetpoint * 7) / 4) { - // pipe is overflowing, fill slowly - ns = written * (1350000000 / Format_sampleRate(mFormat)); - } else { - // pipe is severely overflowing - ns = written * (1750000000 / Format_sampleRate(mFormat)); - } - } else { - ns = count * (1350000000 / Format_sampleRate(mFormat)); - } - if (ns > 999999999) { - ns = 999999999; - } - struct timespec nowTs; - bool nowTsValid = !clock_gettime(CLOCK_MONOTONIC, &nowTs); - // deduct the elapsed time since previous write() completed - if (nowTsValid && mWriteTsValid) { - time_t sec = nowTs.tv_sec - mWriteTs.tv_sec; - long nsec = nowTs.tv_nsec - mWriteTs.tv_nsec; - if (nsec < 0) { - --sec; - nsec += 1000000000; - } - if (sec == 0) { - if ((long) ns > nsec) { - ns -= nsec; - } else { - ns = 0; - } - } - } - if (ns > 0) { - const struct timespec req = {0, ns}; - nanosleep(&req, NULL); - } - // record the time that this write() completed - if (nowTsValid) { - mWriteTs = nowTs; - if ((mWriteTs.tv_nsec += ns) >= 1000000000) { - mWriteTs.tv_nsec -= 1000000000; - ++mWriteTs.tv_sec; - } - } - mWriteTsValid = nowTsValid; - } - mFramesWritten += totalFramesWritten; - return totalFramesWritten; -} - -void MonoPipe::setAvgFrames(size_t setpoint) -{ - mSetpoint = setpoint; -} - -} // namespace android diff --git a/services/audioflinger/MonoPipe.h b/services/audioflinger/MonoPipe.h deleted file mode 100644 index f6e2cb35..00000000 --- a/services/audioflinger/MonoPipe.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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_MONO_PIPE_H -#define ANDROID_AUDIO_MONO_PIPE_H - -#include <time.h> -#include "NBAIO.h" - -namespace android { - -// MonoPipe is similar to Pipe except: -// - supports only a single reader, called MonoPipeReader -// - write() cannot overrun; instead it will return a short actual count if insufficient space -// - write() can optionally block if the pipe is full -// Like Pipe, it is not multi-thread safe for either writer or reader -// but writer and reader can be different threads. -class MonoPipe : public NBAIO_Sink { - - friend class MonoPipeReader; - -public: - // reqFrames will be rounded up to a power of 2, and all slots are available. Must be >= 2. - // Note: whatever shares this object with another thread needs to do so in an SMP-safe way (like - // creating it the object before creating the other thread, or storing the object with a - // release_store). Otherwise the other thread could see a partially-constructed object. - MonoPipe(size_t reqFrames, NBAIO_Format format, bool writeCanBlock = false); - virtual ~MonoPipe(); - - // NBAIO_Port interface - - //virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers, - // NBAIO_Format counterOffers[], size_t& numCounterOffers); - //virtual NBAIO_Format format() const; - - // NBAIO_Sink interface - - //virtual size_t framesWritten() const; - //virtual size_t framesUnderrun() const; - //virtual size_t underruns() const; - - virtual ssize_t availableToWrite() const; - virtual ssize_t write(const void *buffer, size_t count); - //virtual ssize_t writeVia(writeVia_t via, size_t total, void *user, size_t block); - - // average number of frames present in the pipe under normal conditions. - // See throttling mechanism in MonoPipe::write() - size_t getAvgFrames() const { return mSetpoint; } - void setAvgFrames(size_t setpoint); - size_t maxFrames() const { return mMaxFrames; } - -private: - const size_t mReqFrames; // as requested in constructor, unrounded - const size_t mMaxFrames; // always a power of 2 - void * const mBuffer; - // mFront and mRear will never be separated by more than mMaxFrames. - // 32-bit overflow is possible if the pipe is active for a long time, but if that happens it's - // safe because we "&" with (mMaxFrames-1) at end of computations to calculate a buffer index. - volatile int32_t mFront; // written by reader with android_atomic_release_store, - // read by writer with android_atomic_acquire_load - volatile int32_t mRear; // written by writer with android_atomic_release_store, - // read by reader with android_atomic_acquire_load - bool mWriteTsValid; // whether mWriteTs is valid - struct timespec mWriteTs; // time that the previous write() completed - size_t mSetpoint; // target value for pipe fill depth - const bool mWriteCanBlock; // whether write() should block if the pipe is full -}; - -} // namespace android - -#endif // ANDROID_AUDIO_MONO_PIPE_H diff --git a/services/audioflinger/MonoPipeReader.cpp b/services/audioflinger/MonoPipeReader.cpp deleted file mode 100644 index b80d0c08..00000000 --- a/services/audioflinger/MonoPipeReader.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "MonoPipeReader" -//#define LOG_NDEBUG 0 - -#include <cutils/compiler.h> -#include <utils/Log.h> -#include "MonoPipeReader.h" - -namespace android { - -MonoPipeReader::MonoPipeReader(MonoPipe* pipe) : - NBAIO_Source(pipe->mFormat), - mPipe(pipe) -{ -} - -MonoPipeReader::~MonoPipeReader() -{ -} - -ssize_t MonoPipeReader::availableToRead() -{ - if (CC_UNLIKELY(!mNegotiated)) { - return NEGOTIATE; - } - ssize_t ret = android_atomic_acquire_load(&mPipe->mRear) - mPipe->mFront; - ALOG_ASSERT((0 <= ret) && (ret <= mMaxFrames)); - return ret; -} - -ssize_t MonoPipeReader::read(void *buffer, size_t count) -{ - // count == 0 is unlikely and not worth checking for explicitly; will be handled automatically - ssize_t red = availableToRead(); - if (CC_UNLIKELY(red <= 0)) { - return red; - } - if (CC_LIKELY((size_t) red > count)) { - red = count; - } - size_t front = mPipe->mFront & (mPipe->mMaxFrames - 1); - size_t part1 = mPipe->mMaxFrames - front; - if (part1 > (size_t) red) { - part1 = red; - } - if (CC_LIKELY(part1 > 0)) { - memcpy(buffer, (char *) mPipe->mBuffer + (front << mBitShift), part1 << mBitShift); - if (CC_UNLIKELY(front + part1 == mPipe->mMaxFrames)) { - size_t part2 = red - part1; - if (CC_LIKELY(part2 > 0)) { - memcpy((char *) buffer + (part1 << mBitShift), mPipe->mBuffer, part2 << mBitShift); - } - } - android_atomic_release_store(red + mPipe->mFront, &mPipe->mFront); - mFramesRead += red; - } - return red; -} - -} // namespace android diff --git a/services/audioflinger/MonoPipeReader.h b/services/audioflinger/MonoPipeReader.h deleted file mode 100644 index 9bb0a94f..00000000 --- a/services/audioflinger/MonoPipeReader.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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_MONO_PIPE_READER_H -#define ANDROID_AUDIO_MONO_PIPE_READER_H - -#include "MonoPipe.h" - -namespace android { - -// MonoPipeReader is safe for only a single reader thread -class MonoPipeReader : public NBAIO_Source { - -public: - - // Construct a MonoPipeReader and associate it with a MonoPipe; - // any data already in the pipe is visible to this PipeReader. - // There can be only a single MonoPipeReader per MonoPipe. - // FIXME make this constructor a factory method of MonoPipe. - MonoPipeReader(MonoPipe* pipe); - virtual ~MonoPipeReader(); - - // NBAIO_Port interface - - //virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers, - // NBAIO_Format counterOffers[], size_t& numCounterOffers); - //virtual NBAIO_Format format() const; - - // NBAIO_Source interface - - //virtual size_t framesRead() const; - //virtual size_t framesOverrun(); - //virtual size_t overruns(); - - virtual ssize_t availableToRead(); - - virtual ssize_t read(void *buffer, size_t count); - - // NBAIO_Source end - -#if 0 // until necessary - MonoPipe* pipe() const { return mPipe; } -#endif - -private: - MonoPipe * const mPipe; -}; - -} // namespace android - -#endif // ANDROID_AUDIO_MONO_PIPE_READER_H diff --git a/services/audioflinger/NBAIO.cpp b/services/audioflinger/NBAIO.cpp deleted file mode 100644 index 9d71eae5..00000000 --- a/services/audioflinger/NBAIO.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "NBAIO" -//#define LOG_NDEBUG 0 - -#include <utils/Log.h> -#include "NBAIO.h" - -namespace android { - -size_t Format_frameSize(NBAIO_Format format) -{ - switch (format) { - case Format_SR44_1_C2_I16: - case Format_SR48_C2_I16: - return 2 * sizeof(short); - case Format_SR44_1_C1_I16: - case Format_SR48_C1_I16: - return 1 * sizeof(short); - case Format_Invalid: - default: - return 0; - } -} - -size_t Format_frameBitShift(NBAIO_Format format) -{ - switch (format) { - case Format_SR44_1_C2_I16: - case Format_SR48_C2_I16: - return 2; // 1 << 2 == 2 * sizeof(short) - case Format_SR44_1_C1_I16: - case Format_SR48_C1_I16: - return 1; // 1 << 1 == 1 * sizeof(short) - case Format_Invalid: - default: - return 0; - } -} - -unsigned Format_sampleRate(NBAIO_Format format) -{ - switch (format) { - case Format_SR44_1_C1_I16: - case Format_SR44_1_C2_I16: - return 44100; - case Format_SR48_C1_I16: - case Format_SR48_C2_I16: - return 48000; - case Format_Invalid: - default: - return 0; - } -} - -unsigned Format_channelCount(NBAIO_Format format) -{ - switch (format) { - case Format_SR44_1_C1_I16: - case Format_SR48_C1_I16: - return 1; - case Format_SR44_1_C2_I16: - case Format_SR48_C2_I16: - return 2; - case Format_Invalid: - default: - return 0; - } -} - -NBAIO_Format Format_from_SR_C(unsigned sampleRate, unsigned channelCount) -{ - if (sampleRate == 44100 && channelCount == 2) return Format_SR44_1_C2_I16; - if (sampleRate == 48000 && channelCount == 2) return Format_SR48_C2_I16; - if (sampleRate == 44100 && channelCount == 1) return Format_SR44_1_C1_I16; - if (sampleRate == 48000 && channelCount == 1) return Format_SR48_C1_I16; - return Format_Invalid; -} - -// This is a default implementation; it is expected that subclasses will optimize this. -ssize_t NBAIO_Sink::writeVia(writeVia_t via, size_t total, void *user, size_t block) -{ - if (!mNegotiated) { - return (ssize_t) NEGOTIATE; - } - static const size_t maxBlock = 32; - size_t frameSize = Format_frameSize(mFormat); - ALOG_ASSERT(frameSize > 0 && frameSize <= 8); - // double guarantees alignment for stack similar to what malloc() gives for heap - if (block == 0 || block > maxBlock) { - block = maxBlock; - } - double buffer[((frameSize * block) + sizeof(double) - 1) / sizeof(double)]; - size_t accumulator = 0; - while (accumulator < total) { - size_t count = total - accumulator; - if (count > block) { - count = block; - } - ssize_t ret = via(user, buffer, count); - if (ret > 0) { - ALOG_ASSERT((size_t) ret <= count); - size_t maxRet = ret; - ret = write(buffer, maxRet); - if (ret > 0) { - ALOG_ASSERT((size_t) ret <= maxRet); - accumulator += ret; - continue; - } - } - return accumulator > 0 ? accumulator : ret; - } - return accumulator; -} - -// This is a default implementation; it is expected that subclasses will optimize this. -ssize_t NBAIO_Source::readVia(readVia_t via, size_t total, void *user, size_t block) -{ - if (!mNegotiated) { - return (ssize_t) NEGOTIATE; - } - static const size_t maxBlock = 32; - size_t frameSize = Format_frameSize(mFormat); - ALOG_ASSERT(frameSize > 0 && frameSize <= 8); - // double guarantees alignment for stack similar to what malloc() gives for heap - if (block == 0 || block > maxBlock) { - block = maxBlock; - } - double buffer[((frameSize * block) + sizeof(double) - 1) / sizeof(double)]; - size_t accumulator = 0; - while (accumulator < total) { - size_t count = total - accumulator; - if (count > block) { - count = block; - } - ssize_t ret = read(buffer, count); - if (ret > 0) { - ALOG_ASSERT((size_t) ret <= count); - size_t maxRet = ret; - ret = via(user, buffer, maxRet); - if (ret > 0) { - ALOG_ASSERT((size_t) ret <= maxRet); - accumulator += ret; - continue; - } - } - return accumulator > 0 ? accumulator : ret; - } - return accumulator; -} - -// Default implementation that only accepts my mFormat -ssize_t NBAIO_Port::negotiate(const NBAIO_Format offers[], size_t numOffers, - NBAIO_Format counterOffers[], size_t& numCounterOffers) -{ - ALOGV("negotiate offers=%p numOffers=%u countersOffers=%p numCounterOffers=%u", - offers, numOffers, counterOffers, numCounterOffers); - if (mFormat != Format_Invalid) { - for (size_t i = 0; i < numOffers; ++i) { - if (offers[i] == mFormat) { - mNegotiated = true; - return i; - } - } - if (numCounterOffers > 0) { - counterOffers[0] = mFormat; - } - numCounterOffers = 1; - } else { - numCounterOffers = 0; - } - return (ssize_t) NEGOTIATE; -} - -} // namespace android diff --git a/services/audioflinger/NBAIO.h b/services/audioflinger/NBAIO.h deleted file mode 100644 index b5ae0f10..00000000 --- a/services/audioflinger/NBAIO.h +++ /dev/null @@ -1,293 +0,0 @@ -/* - * 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_NBAIO_H -#define ANDROID_AUDIO_NBAIO_H - -// Non-blocking audio I/O interface -// -// This header file has the abstract interfaces only. Concrete implementation classes are declared -// elsewhere. Implementations _should_ be non-blocking for all methods, especially read() and -// write(), but this is not enforced. In general, implementations do not need to be multi-thread -// safe, and any exceptions are noted in the particular implementation. - -#include <limits.h> -#include <stdlib.h> -#include <utils/RefBase.h> - -namespace android { - -// In addition to the usual status_t -enum { - NEGOTIATE = 0x80000010, // Must (re-)negotiate format. For negotiate() only, the offeree - // doesn't accept offers, and proposes counter-offers - OVERRUN = 0x80000011, // availableToRead(), read(), or readVia() detected lost input due - // to overrun; an event is counted and the caller should re-try - UNDERRUN = 0x80000012, // availableToWrite(), write(), or writeVia() detected a gap in - // output due to underrun (not being called often enough, or with - // enough data); an event is counted and the caller should re-try -}; - -// Negotiation of format is based on the data provider and data sink, or the data consumer and -// data source, exchanging prioritized arrays of offers and counter-offers until a single offer is -// mutually agreed upon. Each offer is an NBAIO_Format. For simplicity and performance, -// NBAIO_Format is an enum that ties together the most important combinations of the various -// attributes, rather than a struct with separate fields for format, sample rate, channel count, -// interleave, packing, alignment, etc. The reason is that NBAIO_Format tries to abstract out only -// the combinations that are actually needed within AudioFligner. If the list of combinations grows -// too large, then this decision should be re-visited. -enum NBAIO_Format { - Format_Invalid, - Format_SR44_1_C2_I16, // 44.1 kHz PCM stereo interleaved 16-bit signed - Format_SR48_C2_I16, // 48 kHz PCM stereo interleaved 16-bit signed - Format_SR44_1_C1_I16, // 44.1 kHz PCM mono interleaved 16-bit signed - Format_SR48_C1_I16, // 48 kHz PCM mono interleaved 16-bit signed -}; - -// Return the frame size of an NBAIO_Format in bytes -size_t Format_frameSize(NBAIO_Format format); - -// Return the frame size of an NBAIO_Format as a bit shift -size_t Format_frameBitShift(NBAIO_Format format); - -// Convert a sample rate in Hz and channel count to an NBAIO_Format -NBAIO_Format Format_from_SR_C(unsigned sampleRate, unsigned channelCount); - -// Return the sample rate in Hz of an NBAIO_Format -unsigned Format_sampleRate(NBAIO_Format format); - -// Return the channel count of an NBAIO_Format -unsigned Format_channelCount(NBAIO_Format format); - -// Callbacks used by NBAIO_Sink::writeVia() and NBAIO_Source::readVia() below. -typedef ssize_t (*writeVia_t)(void *user, void *buffer, size_t count); -typedef ssize_t (*readVia_t)(void *user, const void *buffer, size_t count); - -// Abstract class (interface) representing a data port. -class NBAIO_Port : public RefBase { - -public: - - // negotiate() must called first. The purpose of negotiate() is to check compatibility of - // formats, not to automatically adapt if they are incompatible. It's the responsibility of - // whoever sets up the graph connections to make sure formats are compatible, and this method - // just verifies that. The edges are "dumb" and don't attempt to adapt to bad connections. - // How it works: offerer proposes an array of formats, in descending order of preference from - // offers[0] to offers[numOffers - 1]. If offeree accepts one of these formats, it returns - // the index of that offer. Otherwise, offeree sets numCounterOffers to the number of - // counter-offers (up to a maximumum of the entry value of numCounterOffers), fills in the - // provided array counterOffers[] with its counter-offers, in descending order of preference - // from counterOffers[0] to counterOffers[numCounterOffers - 1], and returns NEGOTIATE. - // Note that since the offerer allocates space for counter-offers, but only the offeree knows - // how many counter-offers it has, there may be insufficient space for all counter-offers. - // In that case, the offeree sets numCounterOffers to the requested number of counter-offers - // (which is greater than the entry value of numCounterOffers), fills in as many of the most - // important counterOffers as will fit, and returns NEGOTIATE. As this implies a re-allocation, - // it should be used as a last resort. It is preferable for the offerer to simply allocate a - // larger space to begin with, and/or for the offeree to tolerate a smaller space than desired. - // Alternatively, the offerer can pass NULL for offers and counterOffers, and zero for - // numOffers. This indicates that it has not allocated space for any counter-offers yet. - // In this case, the offerree should set numCounterOffers appropriately and return NEGOTIATE. - // Then the offerer will allocate the correct amount of memory and retry. - // Format_Invalid is not allowed as either an offer or counter-offer. - // Returns: - // >= 0 Offer accepted. - // NEGOTIATE No offer accepted, and counter-offer(s) optionally made. See above for details. - virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers, - NBAIO_Format counterOffers[], size_t& numCounterOffers); - - // Return the current negotiated format, or Format_Invalid if negotiation has not been done, - // or if re-negotiation is required. - virtual NBAIO_Format format() const { return mNegotiated ? mFormat : Format_Invalid; } - -protected: - NBAIO_Port(NBAIO_Format format) : mNegotiated(false), mFormat(format), - mBitShift(Format_frameBitShift(format)) { } - virtual ~NBAIO_Port() { } - - // Implementations are free to ignore these if they don't need them - - bool mNegotiated; // mNegotiated implies (mFormat != Format_Invalid) - NBAIO_Format mFormat; // (mFormat != Format_Invalid) does not imply mNegotiated - size_t mBitShift; // assign in parallel with any assignment to mFormat -}; - -// Abstract class (interface) representing a non-blocking data sink, for use by a data provider. -class NBAIO_Sink : public NBAIO_Port { - -public: - - // For the next two APIs: - // 32 bits rolls over after 27 hours at 44.1 kHz; if that concerns you then poll periodically. - - // Return the number of frames written successfully since construction. - virtual size_t framesWritten() const { return mFramesWritten; } - - // Number of frames lost due to underrun since construction. - virtual size_t framesUnderrun() const { return 0; } - - // Number of underruns since construction, where a set of contiguous lost frames is one event. - virtual size_t underruns() const { return 0; } - - // Estimate of number of frames that could be written successfully now without blocking. - // When a write() is actually attempted, the implementation is permitted to return a smaller or - // larger transfer count, however it will make a good faith effort to give an accurate estimate. - // Errors: - // NEGOTIATE (Re-)negotiation is needed. - // UNDERRUN write() has not been called frequently enough, or with enough frames to keep up. - // An underrun event is counted, and the caller should re-try this operation. - // WOULD_BLOCK Determining how many frames can be written without blocking would itself block. - virtual ssize_t availableToWrite() const { return SSIZE_MAX; } - - // Transfer data to sink from single input buffer. Implies a copy. - // Inputs: - // buffer Non-NULL buffer owned by provider. - // count Maximum number of frames to transfer. - // Return value: - // > 0 Number of frames successfully transferred prior to first error. - // = 0 Count was zero. - // < 0 status_t error occurred prior to the first frame transfer. - // Errors: - // NEGOTIATE (Re-)negotiation is needed. - // WOULD_BLOCK No frames can be transferred without blocking. - // UNDERRUN write() has not been called frequently enough, or with enough frames to keep up. - // An underrun event is counted, and the caller should re-try this operation. - virtual ssize_t write(const void *buffer, size_t count) = 0; - - // Transfer data to sink using a series of callbacks. More suitable for zero-fill, synthesis, - // and non-contiguous transfers (e.g. circular buffer or writev). - // Inputs: - // via Callback function that the sink will call as many times as needed to consume data. - // total Estimate of the number of frames the provider has available. This is an estimate, - // and it can provide a different number of frames during the series of callbacks. - // user Arbitrary void * reserved for data provider. - // block Number of frames per block, that is a suggested value for 'count' in each callback. - // Zero means no preference. This parameter is a hint only, and may be ignored. - // Return value: - // > 0 Total number of frames successfully transferred prior to first error. - // = 0 Count was zero. - // < 0 status_t error occurred prior to the first frame transfer. - // Errors: - // NEGOTIATE (Re-)negotiation is needed. - // WOULD_BLOCK No frames can be transferred without blocking. - // UNDERRUN write() has not been called frequently enough, or with enough frames to keep up. - // An underrun event is counted, and the caller should re-try this operation. - // - // The 'via' callback is called by the data sink as follows: - // Inputs: - // user Arbitrary void * reserved for data provider. - // buffer Non-NULL buffer owned by sink that callback should fill in with data, - // up to a maximum of 'count' frames. - // count Maximum number of frames to transfer during this callback. - // Return value: - // > 0 Number of frames successfully transferred during this callback prior to first error. - // = 0 Count was zero. - // < 0 status_t error occurred prior to the first frame transfer during this callback. - virtual ssize_t writeVia(writeVia_t via, size_t total, void *user, size_t block = 0); - -protected: - NBAIO_Sink(NBAIO_Format format = Format_Invalid) : NBAIO_Port(format), mFramesWritten(0) { } - virtual ~NBAIO_Sink() { } - - // Implementations are free to ignore these if they don't need them - size_t mFramesWritten; -}; - -// Abstract class (interface) representing a non-blocking data source, for use by a data consumer. -class NBAIO_Source : public NBAIO_Port { - -public: - - // For the next two APIs: - // 32 bits rolls over after 27 hours at 44.1 kHz; if that concerns you then poll periodically. - - // Number of frames read successfully since construction. - virtual size_t framesRead() const { return mFramesRead; } - - // Number of frames lost due to overrun since construction. - // Not const because implementations may need to do I/O. - virtual size_t framesOverrun() /*const*/ { return 0; } - - // Number of overruns since construction, where a set of contiguous lost frames is one event. - // Not const because implementations may need to do I/O. - virtual size_t overruns() /*const*/ { return 0; } - - // Estimate of number of frames that could be read successfully now. - // When a read() is actually attempted, the implementation is permitted to return a smaller or - // larger transfer count, however it will make a good faith effort to give an accurate estimate. - // Errors: - // NEGOTIATE (Re-)negotiation is needed. - // OVERRUN One or more frames were lost due to overrun, try again to read more recent data. - // WOULD_BLOCK Determining how many frames can be read without blocking would itself block. - virtual ssize_t availableToRead() { return SSIZE_MAX; } - - // Transfer data from source into single destination buffer. Implies a copy. - // Inputs: - // buffer Non-NULL destination buffer owned by consumer. - // count Maximum number of frames to transfer. - // Return value: - // > 0 Number of frames successfully transferred prior to first error. - // = 0 Count was zero. - // < 0 status_t error occurred prior to the first frame transfer. - // Errors: - // NEGOTIATE (Re-)negotiation is needed. - // WOULD_BLOCK No frames can be transferred without blocking. - // OVERRUN read() has not been called frequently enough, or with enough frames to keep up. - // One or more frames were lost due to overrun, try again to read more recent data. - virtual ssize_t read(void *buffer, size_t count) = 0; - - // Transfer data from source using a series of callbacks. More suitable for zero-fill, - // synthesis, and non-contiguous transfers (e.g. circular buffer or readv). - // Inputs: - // via Callback function that the source will call as many times as needed to provide data. - // total Estimate of the number of frames the consumer desires. This is an estimate, - // and it can consume a different number of frames during the series of callbacks. - // user Arbitrary void * reserved for data consumer. - // block Number of frames per block, that is a suggested value for 'count' in each callback. - // Zero means no preference. This parameter is a hint only, and may be ignored. - // Return value: - // > 0 Total number of frames successfully transferred prior to first error. - // = 0 Count was zero. - // < 0 status_t error occurred prior to the first frame transfer. - // Errors: - // NEGOTIATE (Re-)negotiation is needed. - // WOULD_BLOCK No frames can be transferred without blocking. - // OVERRUN read() has not been called frequently enough, or with enough frames to keep up. - // One or more frames were lost due to overrun, try again to read more recent data. - // - // The 'via' callback is called by the data source as follows: - // Inputs: - // user Arbitrary void * reserved for data consumer. - // dest Non-NULL buffer owned by source that callback should consume data from, - // up to a maximum of 'count' frames. - // count Maximum number of frames to transfer during this callback. - // Return value: - // > 0 Number of frames successfully transferred during this callback prior to first error. - // = 0 Count was zero. - // < 0 status_t error occurred prior to the first frame transfer during this callback. - virtual ssize_t readVia(readVia_t via, size_t total, void *user, size_t block = 0); - -protected: - NBAIO_Source(NBAIO_Format format = Format_Invalid) : NBAIO_Port(format), mFramesRead(0) { } - virtual ~NBAIO_Source() { } - - // Implementations are free to ignore these if they don't need them - size_t mFramesRead; -}; - -} // namespace android - -#endif // ANDROID_AUDIO_NBAIO_H diff --git a/services/audioflinger/Pipe.cpp b/services/audioflinger/Pipe.cpp deleted file mode 100644 index e5b35610..00000000 --- a/services/audioflinger/Pipe.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "Pipe" -//#define LOG_NDEBUG 0 - -#include <cutils/atomic.h> -#include <cutils/compiler.h> -#include <utils/Log.h> -#include "Pipe.h" -#include "roundup.h" - -namespace android { - -Pipe::Pipe(size_t maxFrames, NBAIO_Format format) : - NBAIO_Sink(format), - mMaxFrames(roundup(maxFrames)), - mBuffer(malloc(mMaxFrames * Format_frameSize(format))), - mRear(0), - mReaders(0) -{ -} - -Pipe::~Pipe() -{ - ALOG_ASSERT(android_atomic_acquire_load(&mReaders) == 0); - free(mBuffer); -} - -ssize_t Pipe::write(const void *buffer, size_t count) -{ - // count == 0 is unlikely and not worth checking for - if (CC_UNLIKELY(!mNegotiated)) { - return NEGOTIATE; - } - // write() is not multi-thread safe w.r.t. itself, so no mutex or atomic op needed to read mRear - size_t rear = mRear & (mMaxFrames - 1); - size_t written = mMaxFrames - rear; - if (CC_LIKELY(written > count)) { - written = count; - } - memcpy((char *) mBuffer + (rear << mBitShift), buffer, written << mBitShift); - if (CC_UNLIKELY(rear + written == mMaxFrames)) { - if (CC_UNLIKELY((count -= written) > rear)) { - count = rear; - } - if (CC_LIKELY(count > 0)) { - memcpy(mBuffer, (char *) buffer + (written << mBitShift), count << mBitShift); - written += count; - } - } - android_atomic_release_store(written + mRear, &mRear); - mFramesWritten += written; - return written; -} - -} // namespace android diff --git a/services/audioflinger/Pipe.h b/services/audioflinger/Pipe.h deleted file mode 100644 index 79a4eee3..00000000 --- a/services/audioflinger/Pipe.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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_PIPE_H -#define ANDROID_AUDIO_PIPE_H - -#include "NBAIO.h" - -namespace android { - -// Pipe is multi-thread safe for readers (see PipeReader), but safe for only a single writer thread. -// It cannot UNDERRUN on write, unless we allow designation of a master reader that provides the -// time-base. Readers can be added and removed dynamically, and it's OK to have no readers. -class Pipe : public NBAIO_Sink { - - friend class PipeReader; - -public: - // maxFrames will be rounded up to a power of 2, and all slots are available. Must be >= 2. - Pipe(size_t maxFrames, NBAIO_Format format); - virtual ~Pipe(); - - // NBAIO_Port interface - - //virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers, - // NBAIO_Format counterOffers[], size_t& numCounterOffers); - //virtual NBAIO_Format format() const; - - // NBAIO_Sink interface - - //virtual size_t framesWritten() const; - //virtual size_t framesUnderrun() const; - //virtual size_t underruns() const; - - // The write side of a pipe permits overruns; flow control is the caller's responsibility. - // It doesn't return +infinity because that would guarantee an overrun. - virtual ssize_t availableToWrite() const { return mMaxFrames; } - - virtual ssize_t write(const void *buffer, size_t count); - //virtual ssize_t writeVia(writeVia_t via, size_t total, void *user, size_t block); - -private: - const size_t mMaxFrames; // always a power of 2 - void * const mBuffer; - volatile int32_t mRear; // written by android_atomic_release_store - volatile int32_t mReaders; // number of PipeReader clients currently attached to this Pipe -}; - -} // namespace android - -#endif // ANDROID_AUDIO_PIPE_H diff --git a/services/audioflinger/PipeReader.cpp b/services/audioflinger/PipeReader.cpp deleted file mode 100644 index df3ee04f..00000000 --- a/services/audioflinger/PipeReader.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "PipeReader" -//#define LOG_NDEBUG 0 - -#include <cutils/compiler.h> -#include <utils/Log.h> -#include "PipeReader.h" - -namespace android { - -PipeReader::PipeReader(Pipe& pipe) : - NBAIO_Source(pipe.mFormat), - mPipe(pipe), - // any data already in the pipe is not visible to this PipeReader - mFront(android_atomic_acquire_load(&pipe.mRear)), - mFramesOverrun(0), - mOverruns(0) -{ - android_atomic_inc(&pipe.mReaders); -} - -PipeReader::~PipeReader() -{ - int32_t readers = android_atomic_dec(&mPipe.mReaders); - ALOG_ASSERT(readers > 0); -} - -ssize_t PipeReader::availableToRead() -{ - if (CC_UNLIKELY(!mNegotiated)) { - return NEGOTIATE; - } - int32_t rear = android_atomic_acquire_load(&mPipe.mRear); - // read() is not multi-thread safe w.r.t. itself, so no mutex or atomic op needed to read mFront - size_t avail = rear - mFront; - if (CC_UNLIKELY(avail > mPipe.mMaxFrames)) { - // Discard 1/16 of the most recent data in pipe to avoid another overrun immediately - int32_t oldFront = mFront; - mFront = rear - mPipe.mMaxFrames + (mPipe.mMaxFrames >> 4); - mFramesOverrun += (size_t) (mFront - oldFront); - ++mOverruns; - return OVERRUN; - } - return avail; -} - -ssize_t PipeReader::read(void *buffer, size_t count) -{ - ssize_t avail = availableToRead(); - if (CC_UNLIKELY(avail <= 0)) { - return avail; - } - // An overrun can occur from here on and be silently ignored, - // but it will be caught at next read() - if (CC_LIKELY(count > (size_t) avail)) { - count = avail; - } - size_t front = mFront & (mPipe.mMaxFrames - 1); - size_t red = mPipe.mMaxFrames - front; - if (CC_LIKELY(red > count)) { - red = count; - } - // In particular, an overrun during the memcpy will result in reading corrupt data - memcpy(buffer, (char *) mPipe.mBuffer + (front << mBitShift), red << mBitShift); - // We could re-read the rear pointer here to detect the corruption, but why bother? - if (CC_UNLIKELY(front + red == mPipe.mMaxFrames)) { - if (CC_UNLIKELY((count -= red) > front)) { - count = front; - } - if (CC_LIKELY(count > 0)) { - memcpy((char *) buffer + (red << mBitShift), mPipe.mBuffer, count << mBitShift); - red += count; - } - } - mFront += red; - mFramesRead += red; - return red; -} - -} // namespace android diff --git a/services/audioflinger/PipeReader.h b/services/audioflinger/PipeReader.h deleted file mode 100644 index 398353b0..00000000 --- a/services/audioflinger/PipeReader.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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_PIPE_READER_H -#define ANDROID_AUDIO_PIPE_READER_H - -#include "Pipe.h" - -namespace android { - -// PipeReader is safe for only a single thread -class PipeReader : public NBAIO_Source { - -public: - - // Construct a PipeReader and associate it with a Pipe - // FIXME make this constructor a factory method of Pipe. - PipeReader(Pipe& pipe); - virtual ~PipeReader(); - - // NBAIO_Port interface - - //virtual ssize_t negotiate(const NBAIO_Format offers[], size_t numOffers, - // NBAIO_Format counterOffers[], size_t& numCounterOffers); - //virtual NBAIO_Format format() const; - - // NBAIO_Source interface - - //virtual size_t framesRead() const; - virtual size_t framesOverrun() { return mFramesOverrun; } - virtual size_t overruns() { return mOverruns; } - - virtual ssize_t availableToRead(); - - virtual ssize_t read(void *buffer, size_t count); - - // NBAIO_Source end - -#if 0 // until necessary - Pipe& pipe() const { return mPipe; } -#endif - -private: - Pipe& mPipe; - int32_t mFront; // follows behind mPipe.mRear - size_t mFramesOverrun; - size_t mOverruns; -}; - -} // namespace android - -#endif // ANDROID_AUDIO_PIPE_READER_H diff --git a/services/audioflinger/Soaker.h b/services/audioflinger/Soaker.h deleted file mode 100644 index 43d9d2f3..00000000 --- a/services/audioflinger/Soaker.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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_SOAKER_H -#define _ANDROID_AUDIO_SOAKER_H - -#include <utils/Thread.h> - -namespace android { - -class Soaker : public Thread { -public: - Soaker() : Thread() { } - virtual ~Soaker() { } -protected: - virtual bool threadLoop() { - int j = 0; - for (;;) { - for (int i = 0; i < 10000; ++i) { - j += i * i; - } - if (exitPending()) { - return false; - } - } - return j < 555555; - } -}; - -} // namespace android - -#endif // _ANDROID_AUDIO_SOAKER_H diff --git a/services/audioflinger/SourceAudioBufferProvider.cpp b/services/audioflinger/SourceAudioBufferProvider.cpp deleted file mode 100644 index e9d6d2c7..00000000 --- a/services/audioflinger/SourceAudioBufferProvider.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "SourceAudioBufferProvider" -//#define LOG_NDEBUG 0 - -#include <utils/Log.h> -#include "SourceAudioBufferProvider.h" - -namespace android { - -SourceAudioBufferProvider::SourceAudioBufferProvider(const sp<NBAIO_Source>& source) : - mSource(source), - // mFrameBitShiftFormat below - mAllocated(NULL), mSize(0), mOffset(0), mRemaining(0), mGetCount(0) -{ - ALOG_ASSERT(source != 0); - - // negotiate with source - NBAIO_Format counterOffers[1]; - size_t numCounterOffers = 1; - ssize_t index = source->negotiate(NULL, 0, counterOffers, numCounterOffers); - ALOG_ASSERT(index == (ssize_t) NEGOTIATE && numCounterOffers > 0); - numCounterOffers = 0; - index = source->negotiate(counterOffers, 1, NULL, numCounterOffers); - ALOG_ASSERT(index == 0); - mFrameBitShift = Format_frameBitShift(source->format()); -} - -SourceAudioBufferProvider::~SourceAudioBufferProvider() -{ - free(mAllocated); -} - -status_t SourceAudioBufferProvider::getNextBuffer(Buffer *buffer, int64_t pts) -{ - ALOG_ASSERT(buffer != NULL && buffer->frameCount > 0 && mGetCount == 0); - // any leftover data available? - if (mRemaining > 0) { - ALOG_ASSERT(mOffset + mRemaining <= mSize); - if (mRemaining < buffer->frameCount) { - buffer->frameCount = mRemaining; - } - buffer->raw = (char *) mAllocated + (mOffset << mFrameBitShift); - mGetCount = buffer->frameCount; - return OK; - } - // do we need to reallocate? - if (buffer->frameCount > mSize) { - free(mAllocated); - mAllocated = malloc(buffer->frameCount << mFrameBitShift); - mSize = buffer->frameCount; - } - // read from source - ssize_t actual = mSource->read(mAllocated, buffer->frameCount); - if (actual > 0) { - ALOG_ASSERT((size_t) actual <= buffer->frameCount); - mOffset = 0; - mRemaining = actual; - buffer->raw = mAllocated; - buffer->frameCount = actual; - mGetCount = actual; - return OK; - } - buffer->raw = NULL; - buffer->frameCount = 0; - mGetCount = 0; - return NOT_ENOUGH_DATA; -} - -void SourceAudioBufferProvider::releaseBuffer(Buffer *buffer) -{ - ALOG_ASSERT((buffer != NULL) && - (buffer->raw == (char *) mAllocated + (mOffset << mFrameBitShift)) && - (buffer->frameCount <= mGetCount) && - (mGetCount <= mRemaining) && - (mOffset + mRemaining <= mSize)); - mOffset += buffer->frameCount; - mRemaining -= buffer->frameCount; - buffer->raw = NULL; - buffer->frameCount = 0; - mGetCount = 0; -} - -size_t SourceAudioBufferProvider::framesReady() const -{ - ssize_t avail = mSource->availableToRead(); - return avail < 0 ? 0 : (size_t) avail; -} - -} // namespace android diff --git a/services/audioflinger/SourceAudioBufferProvider.h b/services/audioflinger/SourceAudioBufferProvider.h deleted file mode 100644 index 85ccbb2e..00000000 --- a/services/audioflinger/SourceAudioBufferProvider.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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. - */ - -// Implementation of AudioBufferProvider that wraps an NBAIO_Source - -#ifndef ANDROID_SOURCE_AUDIO_BUFFER_PROVIDER_H -#define ANDROID_SOURCE_AUDIO_BUFFER_PROVIDER_H - -#include "NBAIO.h" -#include "ExtendedAudioBufferProvider.h" - -namespace android { - -class SourceAudioBufferProvider : public ExtendedAudioBufferProvider { - -public: - SourceAudioBufferProvider(const sp<NBAIO_Source>& source); - virtual ~SourceAudioBufferProvider(); - - // AudioBufferProvider interface - virtual status_t getNextBuffer(Buffer *buffer, int64_t pts); - virtual void releaseBuffer(Buffer *buffer); - - // ExtendedAudioBufferProvider interface - virtual size_t framesReady() const; - -private: - const sp<NBAIO_Source> mSource; // the wrapped source - /*const*/ size_t mFrameBitShift; // log2(frame size in bytes) - void* mAllocated; // pointer to base of allocated memory - size_t mSize; // size of mAllocated in frames - size_t mOffset; // frame offset within mAllocated of valid data - size_t mRemaining; // frame count within mAllocated of valid data - size_t mGetCount; // buffer.frameCount of the most recent getNextBuffer -}; - -} // namespace android - -#endif // ANDROID_SOURCE_AUDIO_BUFFER_PROVIDER_H diff --git a/services/audioflinger/audio-resampler/Android.mk b/services/audioflinger/audio-resampler/Android.mk new file mode 100644 index 00000000..ba37b198 --- /dev/null +++ b/services/audioflinger/audio-resampler/Android.mk @@ -0,0 +1,14 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + AudioResamplerCoefficients.cpp + +LOCAL_MODULE := libaudio-resampler + +LOCAL_MODULE_TAGS := optional + +LOCAL_SHARED_LIBRARIES := libutils liblog + +include $(BUILD_SHARED_LIBRARY) diff --git a/services/audioflinger/roundup.h b/services/audioflinger/audio-resampler/AudioResamplerCoefficients.cpp index 4c3cc257..d45d6972 100644 --- a/services/audioflinger/roundup.h +++ b/services/audioflinger/audio-resampler/AudioResamplerCoefficients.cpp @@ -14,18 +14,41 @@ * limitations under the License. */ -#ifndef ROUNDUP_H -#define ROUNDUP_H +#define LOG_TAG "ResamplerCoefficients" +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> + +#include "filter_coefficients.h" + +const int32_t RESAMPLE_FIR_NUM_COEF = 16; +const int32_t RESAMPLE_FIR_LERP_INT_BITS = 7; + +using namespace android; #ifdef __cplusplus extern "C" { #endif -// Round up to the next highest power of 2 -unsigned roundup(unsigned v); +const int32_t* readResamplerCoefficients(bool upSample) { + + ALOGV("readResamplerCoefficients"); + if (upSample) { + return up_sampler_filter_coefficients; + } else { + return dn_sampler_filter_coefficients; + } + +} + +int32_t readResampleFirNumCoeff() { + return RESAMPLE_FIR_NUM_COEF; +} + +int32_t readResampleFirLerpIntBits() { + return RESAMPLE_FIR_LERP_INT_BITS; +} #ifdef __cplusplus } #endif - -#endif // ROUNDUP_H diff --git a/services/audioflinger/audio-resampler/filter_coefficients.h b/services/audioflinger/audio-resampler/filter_coefficients.h new file mode 100644 index 00000000..bf70c632 --- /dev/null +++ b/services/audioflinger/audio-resampler/filter_coefficients.h @@ -0,0 +1,285 @@ +/* + * 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. + */ +#include <stdlib.h> + +namespace android { + +// cmd-line: fir -l 7 -s 48000 -c 23400 -n 16 -b 9.62 +const int32_t up_sampler_filter_coefficients[] __attribute__ ((aligned (32))) = { + 0x7ccccccd, 0x0323eb7f, 0xfd086246, 0x02b2aa5c, 0xfda45e2c, 0x01fa5183, 0xfe694e12, 0x0137e672, 0xff1c87d3, 0x009ce6d8, 0xff9a68b0, 0x003d150d, 0xffde727a, 0x00106595, 0xfff93679, 0x00021fc5, + 0x7cc9b757, 0x022ac835, 0xfd7e3a71, 0x026b7da1, 0xfdd2b905, 0x01db7c90, 0xfe7db77c, 0x012aa7bf, 0xff24dc32, 0x0097dfc9, 0xff9d4ae9, 0x003b8742, 0xffdf38e5, 0x00100be5, 0xfff959f5, 0x0002144b, + 0x7cc0773c, 0x01354bc1, 0xfdf365e8, 0x0224726d, 0xfe011d2e, 0x01bc908b, 0xfe923a2b, 0x011d528d, 0xff2d426f, 0x0092cbc0, 0xffa035cc, 0x0039f42e, 0xffe00236, 0x000fb0d2, 0xfff97dfa, 0x000208b0, + 0x7cb10d52, 0x0043843f, 0xfe67d5a8, 0x01dd92df, 0xfe2f83c1, 0x019d9230, 0xfea6d2e5, 0x010fe901, 0xff35b924, 0x008dab9d, 0xffa328d4, 0x00385c1d, 0xffe0ce46, 0x000f5471, 0xfff9a27f, 0x0001fcf5, + 0x7c9b7afd, 0xff557f58, 0xfedb7ae9, 0x0196e8fe, 0xfe5de5e3, 0x017e8635, 0xfebb7e75, 0x01026d40, 0xff3e3eed, 0x0088803e, 0xffa6237a, 0x0036bf58, 0xffe19cec, 0x000ef6d4, 0xfff9c77d, 0x0001f11e, + 0x7c7fc22f, 0xfe6b4a44, 0xff4e471d, 0x01507eb8, 0xfe8c3cc3, 0x015f714d, 0xfed039a8, 0x00f4e16f, 0xff46d266, 0x00834a83, 0xffa9253b, 0x00351e2d, 0xffe26e01, 0x000e980f, 0xfff9eceb, 0x0001e52e, + 0x7c5de56a, 0xfd84f1c8, 0xffc02bf2, 0x010a5de2, 0xfeba819d, 0x01405821, 0xfee5014c, 0x00e747b0, 0xff4f722b, 0x007e0b4b, 0xffac2d8f, 0x003378e7, 0xffe3415d, 0x000e3834, 0xfffa12c0, 0x0001d927, + 0x7c35e7bb, 0xfca28234, 0x00311b54, 0x00c49034, 0xfee8adba, 0x01213f58, 0xfef9d232, 0x00d9a226, 0xff581cd8, 0x0078c375, 0xffaf3bf2, 0x0031cfd1, 0xffe416d8, 0x000dd758, 0xfffa38f5, 0x0001cd0d, + 0x7c07ccbe, 0xfbc40766, 0x00a1076e, 0x007f1f4b, 0xff16ba71, 0x01022b90, 0xff0ea931, 0x00cbf2f0, 0xff60d10b, 0x007373de, 0xffb24fde, 0x00302337, 0xffe4ee4b, 0x000d758d, 0xfffa5f81, 0x0001c0e1, + 0x7bd3989d, 0xfae98cc5, 0x010fe2ab, 0x003a14a6, 0xff44a128, 0x00e3215e, 0xff238322, 0x00be3c2d, 0xff698d62, 0x006e1d66, 0xffb568ce, 0x002e7363, 0xffe5c78d, 0x000d12e6, 0xfffa865d, 0x0001b4a8, + 0x7b99500c, 0xfa131d41, 0x017d9fb8, 0xfff579a3, 0xff725b54, 0x00c42551, 0xff385ce3, 0x00b07ff8, 0xff72507e, 0x0068c0e9, 0xffb8863e, 0x002cc0a2, 0xffe6a277, 0x000caf76, 0xfffaad81, 0x0001a863, + 0x7b58f84d, 0xf940c355, 0x01ea3184, 0xffb15783, 0xff9fe27d, 0x00a53bed, 0xff4d3358, 0x00a2c06b, 0xff7b18fe, 0x00635f45, 0xffbba7aa, 0x002b0b3d, 0xffe77ee2, 0x000c4b50, 0xfffad4e4, 0x00019c15, + 0x7b12972d, 0xf8728902, 0x02558b43, 0xff6db764, 0xffcd303b, 0x008669ae, 0xff620368, 0x0094ff9b, 0xff83e586, 0x005df954, 0xffbecc8d, 0x00295380, 0xffe85ca7, 0x000be687, 0xfffafc7f, 0x00018fc1, + 0x7ac63304, 0xf7a877d4, 0x02bfa06d, 0xff2aa243, 0xfffa3e37, 0x0067b303, 0xff76ca02, 0x00873f9b, 0xff8cb4bb, 0x00588ff1, 0xffc1f465, 0x002799b3, 0xffe93b9e, 0x000b812d, 0xfffb244a, 0x0001836a, + 0x7a73d2b5, 0xf6e298db, 0x032864c1, 0xfee820f8, 0x00270631, 0x00491c54, 0xff8b841a, 0x0079827a, 0xff958542, 0x005323f7, 0xffc51eaf, 0x0025de22, 0xffea1ba2, 0x000b1b55, 0xfffb4c3e, 0x00017712, + 0x7a1b7daa, 0xf620f4b2, 0x038fcc44, 0xfea63c38, 0x005381fa, 0x002aa9fa, 0xffa02eac, 0x006bca44, 0xff9e55c6, 0x004db63c, 0xffc84ae9, 0x00242115, 0xffeafc8b, 0x000ab510, 0xfffb7452, 0x00016abb, + 0x79bd3bd8, 0xf5639376, 0x03f5cb46, 0xfe64fc93, 0x007fab77, 0x000c6043, 0xffb4c6b9, 0x005e1900, 0xffa724f0, 0x00484799, 0xffcb7893, 0x002262d6, 0xffebde33, 0x000a4e72, 0xfffb9c80, 0x00015e68, + 0x795915bc, 0xf4aa7cce, 0x045a565c, 0xfe246a72, 0x00ab7ca6, 0xffee4372, 0xffc9494b, 0x005070b0, 0xffaff16f, 0x0042d8e1, 0xffcea72c, 0x0020a3ad, 0xffecc075, 0x0009e78c, 0xfffbc4bf, 0x0001521b, + 0x78ef1457, 0xf3f5b7e4, 0x04bd6269, 0xfde48e17, 0x00d6ef99, 0xffd057bb, 0xffddb374, 0x0042d353, 0xffb8b9f3, 0x003d6aea, 0xffd1d635, 0x001ee3e1, 0xffeda32a, 0x00098070, 0xfffbed0a, 0x000145d7, + 0x787f4134, 0xf3454b6a, 0x051ee498, 0xfda56f9c, 0x0101fe7a, 0xffb2a145, 0xfff2024e, 0x003542e2, 0xffc17d30, 0x0037fe85, 0xffd50530, 0x001d23b9, 0xffee862e, 0x0009192f, 0xfffc1558, 0x0001399e, + 0x7809a65e, 0xf2993d95, 0x057ed264, 0xfd6716f2, 0x012ca389, 0xff952429, 0x000632fa, 0x0027c151, 0xffca39dd, 0x00329483, 0xffd833a0, 0x001b637e, 0xffef695c, 0x0008b1db, 0xfffc3da2, 0x00012d72, + 0x778e4e68, 0xf1f19421, 0x05dd218f, 0xfd298be0, 0x0156d920, 0xff77e470, 0x001a42a4, 0x001a508e, 0xffd2eeb3, 0x002d2db0, 0xffdb6109, 0x0019a373, 0xfff04c8f, 0x00084a86, 0xfffc65e2, 0x00012155, + 0x770d4466, 0xf14e544f, 0x0639c82d, 0xfcecd602, 0x018099b2, 0xff5ae614, 0x002e2e82, 0x000cf281, 0xffdb9a70, 0x0027cada, 0xffde8cf1, 0x0017e3df, 0xfff12fa3, 0x0007e33f, 0xfffc8e11, 0x0001154a, + 0x768693ec, 0xf0af82e4, 0x0694bca0, 0xfcb0fcca, 0x01a9dfcc, 0xff3e2d01, 0x0041f3d2, 0xffffa90e, 0xffe43bd5, 0x00226ccb, 0xffe1b6dd, 0x00162507, 0xfff21275, 0x00077c17, 0xfffcb628, 0x00010952, + 0x75fa4911, 0xf015242b, 0x06edf595, 0xfc76077b, 0x01d2a615, 0xff21bd11, 0x00558fdc, 0xfff27611, 0xffecd1a6, 0x001d144a, 0xffe4de56, 0x0014672d, 0xfff2f4e0, 0x00071520, 0xfffcde20, 0x0000fd6f, + 0x75687068, 0xef7f3bf5, 0x07456a0e, 0xfc3bfd2e, 0x01fae74e, 0xff059a0e, 0x0068fff3, 0xffe55b60, 0xfff55aae, 0x0017c21c, 0xffe802e6, 0x0012aa95, 0xfff3d6c3, 0x0006ae6a, 0xfffd05f3, 0x0000f1a4, + 0x74d11703, 0xeeedcd98, 0x079b1158, 0xfc02e4cc, 0x02229e57, 0xfee9c7af, 0x007c4177, 0xffd85ac9, 0xfffdd5b8, 0x00127704, 0xffeb2416, 0x0010ef82, 0xfff4b7fb, 0x00064804, 0xfffd2d9b, 0x0000e5f3, + 0x74344a70, 0xee60dbee, 0x07eee314, 0xfbcac510, 0x0249c629, 0xfece499d, 0x008f51cf, 0xffcb7615, 0x00064197, 0x000d33c3, 0xffee4174, 0x000f3633, 0xfff59866, 0x0005e1fe, 0xfffd5511, 0x0000da5c, + 0x739218b8, 0xedd86958, 0x0840d732, 0xfb93a486, 0x027059da, 0xfeb3236b, 0x00a22e71, 0xffbeaf06, 0x000e9d1f, 0x0007f915, 0xfff15a8d, 0x000d7eea, 0xfff677e2, 0x00057c68, 0xfffd7c4f, 0x0000cee3, + 0x72ea905a, 0xed5477be, 0x0890e5f7, 0xfb5d898c, 0x029654a0, 0xfe98589b, 0x00b4d4dd, 0xffb20754, 0x0016e72c, 0x0002c7b6, 0xfff46ef1, 0x000bc9e6, 0xfff75650, 0x00051750, 0xfffda350, 0x0000c388, + 0x723dc051, 0xecd5088e, 0x08df07f6, 0xfb287a4d, 0x02bbb1cc, 0xfe7dec9c, 0x00c7429f, 0xffa580b1, 0x001f1e9b, 0xfffda05c, 0xfff77e31, 0x000a1765, 0xfff8338e, 0x0004b2c7, 0xfffdca0d, 0x0000b84d, + 0x718bb80b, 0xec5a1cbc, 0x092b3617, 0xfaf47cc4, 0x02e06ccf, 0xfe63e2cc, 0x00d97550, 0xff991cc9, 0x00274253, 0xfff883be, 0xfffa87df, 0x000867a5, 0xfff90f7c, 0x00044eda, 0xfffdf080, 0x0000ad34, + 0x70d4876b, 0xebe3b4c5, 0x09756994, 0xfac196bb, 0x03048139, 0xfe4a3e70, 0x00eb6a95, 0xff8cdd3c, 0x002f513a, 0xfff3728d, 0xfffd8b92, 0x0006bae1, 0xfff9e9fd, 0x0003eb98, 0xfffe16a6, 0x0000a23f, + 0x70183ec5, 0xeb71d0ab, 0x09bd9bfb, 0xfa8fcdca, 0x0327eab8, 0xfe3102bd, 0x00fd2022, 0xff80c3a4, 0x00374a40, 0xffee6d78, 0x000088df, 0x00051157, 0xfffac2f0, 0x0003890e, 0xfffe3c76, 0x0000976e, + 0x6f56eee1, 0xeb046ffc, 0x0a03c72b, 0xfa5f2755, 0x034aa51b, 0xfe1832d4, 0x010e93b5, 0xff74d194, 0x003f2c57, 0xffe97529, 0x00037f60, 0x00036b3f, 0xfffb9a38, 0x0003274c, 0xfffe61ee, 0x00008cc4, + 0x6e90a8f2, 0xea9b91cc, 0x0a47e559, 0xfa2fa890, 0x036cac52, 0xfdffd1bd, 0x011fc31c, 0xff690894, 0x0046f679, 0xffe48a4a, 0x00066eae, 0x0001c8d2, 0xfffc6fb8, 0x0002c65d, 0xfffe8707, 0x00008241, + 0x6dc57e9b, 0xea3734bb, 0x0a89f10c, 0xfa015679, 0x038dfc6c, 0xfde7e26f, 0x0130ac31, 0xff5d6a24, 0x004ea7a3, 0xffdfad7f, 0x00095666, 0x00002a4a, 0xfffd4352, 0x00026650, 0xfffeabbd, 0x000077e8, + 0x6cf581e8, 0xe9d756f3, 0x0ac9e521, 0xf9d435dc, 0x03ae919a, 0xfdd067ca, 0x01414cdd, 0xff51f7bb, 0x00563edb, 0xffdadf69, 0x000c3627, 0xfffe8fdc, 0xfffe14eb, 0x00020730, 0xfffed00a, 0x00006db9, + 0x6c20c550, 0xe97bf627, 0x0b07bcc6, 0xf9a84b50, 0x03ce682d, 0xfdb96498, 0x0151a317, 0xff46b2c7, 0x005dbb29, 0xffd620a6, 0x000f0d91, 0xfffcf9be, 0xfffee466, 0x0001a90b, 0xfffef3ea, 0x000063b5, + 0x6b475bb0, 0xe9250f99, 0x0b437380, 0xf97d9b37, 0x03ed7c9a, 0xfda2db8c, 0x0161ace5, 0xff3b9cad, 0x00651b9c, 0xffd171d1, 0x0011dc47, 0xfffb6825, 0xffffb1aa, 0x00014bed, 0xffff1759, 0x000059dd, + 0x6a69584a, 0xe8d2a017, 0x0b7d0525, 0xf95429c0, 0x040bcb77, 0xfd8ccf46, 0x01716859, 0xff30b6c8, 0x006c5f4b, 0xffccd380, 0x0014a1ee, 0xfff9db44, 0x00007c9c, 0x0000efe1, 0xffff3a53, 0x00005033, + 0x6986cec4, 0xe884a3fb, 0x0bb46de2, 0xf92bfae4, 0x0429517b, 0xfd77424c, 0x0180d397, 0xff260269, 0x00738551, 0xffc84645, 0x00175e2d, 0xfff8534d, 0x00014521, 0x000094f3, 0xffff5cd2, 0x000046b8, + 0x689fd324, 0xe83b1731, 0x0be9aa34, 0xf9051266, 0x04460b81, 0xfd62370e, 0x018fecd1, 0xff1b80da, 0x007a8cd0, 0xffc3cab1, 0x001a10ad, 0xfff6d070, 0x00020b23, 0x00003b2e, 0xffff7ed3, 0x00003d6c, + 0x67b479cf, 0xe7f5f531, 0x0c1cb6ef, 0xf8df73d6, 0x0461f688, 0xfd4dafe6, 0x019eb246, 0xff113358, 0x008174ef, 0xffbf614e, 0x001cb91a, 0xfff552de, 0x0002ce87, 0xffffe29d, 0xffffa052, 0x00003450, + 0x66c4d787, 0xe7b53908, 0x0c4d913a, 0xf8bb228c, 0x047d0fb1, 0xfd39af17, 0x01ad2249, 0xff071b16, 0x00883cdc, 0xffbb0aa3, 0x001f5723, 0xfff3dac3, 0x00038f37, 0xffff8b4b, 0xffffc14b, 0x00002b66, + 0x65d10168, 0xe778dd50, 0x0c7c368d, 0xf89821ac, 0x0497543f, 0xfd2636ca, 0x01bb3b37, 0xfefd3941, 0x008ee3cd, 0xffb6c735, 0x0021ea76, 0xfff2684e, 0x00044d1b, 0xffff3540, 0xffffe1bc, 0x000022ad, + 0x64d90ce7, 0xe740dc3c, 0x0ca8a4b7, 0xf8767422, 0x04b0c19a, 0xfd134913, 0x01c8fb81, 0xfef38ef6, 0x009568fc, 0xffb29782, 0x002472c8, 0xfff0fba9, 0x0005081f, 0xfffee088, 0x0000019f, 0x00001a28, + 0x63dd0fcd, 0xe70d2f8d, 0x0cd2d9d5, 0xf8561ca7, 0x04c9554e, 0xfd00e7ec, 0x01d661a6, 0xfeea1d4c, 0x009bcbab, 0xffae7c06, 0x0026efcc, 0xffef94fe, 0x0005c02c, 0xfffe8d2c, 0x000020f3, 0x000011d5, + 0x62dd2039, 0xe6ddd09f, 0x0cfad45a, 0xf8371dbb, 0x04e10d0a, 0xfcef153a, 0x01e36c34, 0xfee0e54e, 0x00a20b23, 0xffaa7538, 0x0029613a, 0xffee3477, 0x0006752d, 0xfffe3b35, 0x00003fb3, 0x000009b6, + 0x61d95497, 0xe6b2b862, 0x0d209309, 0xf81979ab, 0x04f7e6a2, 0xfcddd2c7, 0x01f019cb, 0xfed7e7fd, 0x00a826b2, 0xffa6838c, 0x002bc6cd, 0xffecda3b, 0x0007270f, 0xfffdeaaa, 0x00005ddd, 0x000001cc, + 0x60d1c3a6, 0xe68bdf5e, 0x0d4414f9, 0xf7fd328c, 0x050de00d, 0xfccd2246, 0x01fc691b, 0xfecf2650, 0x00ae1dae, 0xffa2a770, 0x002e2040, 0xffeb866f, 0x0007d5bf, 0xfffd9b96, 0x00007b6f, 0xfffffa17, + 0x5fc68470, 0xe6693db5, 0x0d65598f, 0xf7e24a3c, 0x0522f766, 0xfcbd0551, 0x020858e2, 0xfec6a130, 0x00b3ef73, 0xff9ee150, 0x00306d52, 0xffea3939, 0x0008812a, 0xfffd4dff, 0x00009865, 0xfffff297, + 0x5eb7ae46, 0xe64acb24, 0x0d846084, 0xf7c8c267, 0x05372aee, 0xfcad7d6b, 0x0213e7f0, 0xfebe5980, 0x00b99b65, 0xff9b3192, 0x0032adc4, 0xffe8f2bb, 0x0009293e, 0xfffd01ee, 0x0000b4bd, 0xffffeb4c, + 0x5da558c5, 0xe6307f05, 0x0da129df, 0xf7b09c7f, 0x054a7909, 0xfc9e8bfd, 0x021f1526, 0xfeb65015, 0x00bf20ee, 0xff979898, 0x0034e15b, 0xffe7b317, 0x0009cdeb, 0xfffcb769, 0x0000d074, 0xffffe438, + 0x5c8f9bcb, 0xe61a504f, 0x0dbbb5f6, 0xf799d9c4, 0x055ce03f, 0xfc903258, 0x0229df75, 0xfeae85bb, 0x00c47f7f, 0xff9416c1, 0x003707dc, 0xffe67a6f, 0x000a6f20, 0xfffc6e78, 0x0000eb89, 0xffffdd5a, + 0x5b768f7a, 0xe6083599, 0x0dd40571, 0xf7847b3d, 0x056e5f3d, 0xfc8271b4, 0x023445dd, 0xfea6fb32, 0x00c9b691, 0xff90ac66, 0x00392111, 0xffe548e0, 0x000b0cce, 0xfffc2720, 0x000105f9, 0xffffd6b2, + 0x5a5a4c32, 0xe5fa2519, 0x0dea1943, 0xf77081be, 0x057ef4d3, 0xfc754b32, 0x023e4772, 0xfe9fb12e, 0x00cec5a1, 0xff8d59dd, 0x003b2cc5, 0xffe41e88, 0x000ba6e5, 0xfffbe169, 0x00011fc3, 0xffffd041, + 0x593aea93, 0xe5f014aa, 0x0dfdf2ae, 0xf75dede5, 0x058e9ff8, 0xfc68bfd7, 0x0247e354, 0xfe98a85b, 0x00d3ac38, 0xff8a1f77, 0x003d2ac6, 0xffe2fb83, 0x000c3d59, 0xfffb9d59, 0x000138e4, 0xffffca06, + 0x58188376, 0xe5e9f9ca, 0x0e0f9342, 0xf74cc01c, 0x059d5fc5, 0xfc5cd092, 0x025118b8, 0xfe91e159, 0x00d869e1, 0xff86fd81, 0x003f1ae4, 0xffe1dfec, 0x000cd01b, 0xfffb5af3, 0x0001515c, 0xffffc402, + 0x56f32fea, 0xe5e7c99e, 0x0e1efcdb, 0xf73cf898, 0x05ab3377, 0xfc517e38, 0x0259e6e1, 0xfe8b5cba, 0x00dcfe32, 0xff83f443, 0x0040fcf3, 0xffe0cbdc, 0x000d5f1f, 0xfffb1a3f, 0x00016928, 0xffffbe35, + 0x55cb0935, 0xe5e978f0, 0x0e2c319d, 0xf72e9758, 0x05b81a70, 0xfc46c987, 0x02624d23, 0xfe851b09, 0x00e168c5, 0xff810401, 0x0042d0c9, 0xffdfbf6b, 0x000dea5a, 0xfffadb40, 0x00018048, 0xffffb89f, + 0x54a028d0, 0xe5eefc35, 0x0e3733fc, 0xf7219c2a, 0x05c41435, 0xfc3cb323, 0x026a4ae5, 0xfe7f1cc4, 0x00e5a93c, 0xff7e2cfb, 0x0044963d, 0xffdebaaf, 0x000e71c1, 0xfffa9dfa, 0x000196ba, 0xffffb340, + 0x5372a862, 0xe5f8478d, 0x0e4006b2, 0xf71606a6, 0x05cf2070, 0xfc333b97, 0x0271df9c, 0xfe79625e, 0x00e9bf43, 0xff7b6f6c, 0x00464d2b, 0xffddbdbd, 0x000ef549, 0xfffa6273, 0x0001ac7d, 0xffffae17, + 0x5242a1c1, 0xe6054ec6, 0x0e46acc4, 0xf70bd632, 0x05d93eee, 0xfc2a6356, 0x02790ace, 0xfe73ec40, 0x00edaa88, 0xff78cb8c, 0x0047f571, 0xffdcc8a9, 0x000f74e9, 0xfffa28ad, 0x0001c191, 0xffffa924, + 0x51102eec, 0xe616055a, 0x0e4b297c, 0xf7030a01, 0x05e26f9f, 0xfc222abb, 0x027fcc12, 0xfe6ebac6, 0x00f16ac4, 0xff76418b, 0x00498eed, 0xffdbdb84, 0x000ff098, 0xfff9f0ac, 0x0001d5f4, 0xffffa467, + 0x4fdb6a09, 0xe62a5e76, 0x0e4d806f, 0xf6fba113, 0x05eab296, 0xfc1a9208, 0x02862311, 0xfe69ce43, 0x00f4ffb6, 0xff73d199, 0x004b1984, 0xffdaf65e, 0x0010684e, 0xfff9ba73, 0x0001e9a7, 0xffff9fe0, + 0x4ea46d66, 0xe6424cf8, 0x0e4db575, 0xf6f59a36, 0x05f20809, 0xfc139968, 0x028c0f83, 0xfe6526fe, 0x00f86924, 0xff717bdf, 0x004c951b, 0xffda1948, 0x0010dc05, 0xfff98604, 0x0001fca8, 0xffff9b8f, + 0x4d6b536f, 0xe65dc373, 0x0e4bccac, 0xf6f0f407, 0x05f87053, 0xfc0d40ec, 0x0291912f, 0xfe60c533, 0x00fba6da, 0xff6f4083, 0x004e0199, 0xffd9444e, 0x00114bb4, 0xfff95363, 0x00020ef7, 0xffff9773, + 0x4c3036b2, 0xe67cb42f, 0x0e47ca78, 0xf6edacf2, 0x05fdebee, 0xfc07888e, 0x0296a7f0, 0xfe5ca913, 0x00feb8ad, 0xff6d1fa5, 0x004f5ee9, 0xffd8777d, 0x0011b757, 0xfff92290, 0x00022095, 0xffff938c, + 0x4af331d9, 0xe69f112f, 0x0e41b37c, 0xf6ebc332, 0x06027b78, 0xfc027031, 0x029b53af, 0xfe58d2c5, 0x01019e78, 0xff6b1961, 0x0050acf7, 0xffd7b2e0, 0x00121ee9, 0xfff8f38e, 0x00023181, 0xffff8fd9, + 0x49b45fa8, 0xe6c4cc2e, 0x0e398c9f, 0xf6eb34d4, 0x06061fb2, 0xfbfdf79e, 0x029f9466, 0xfe554265, 0x0104581c, 0xff692dd2, 0x0051ebb4, 0xffd6f67f, 0x00128265, 0xfff8c65d, 0x000241bb, 0xffff8c5a, + 0x4873daf7, 0xe6edd6a4, 0x0e2f5b0b, 0xf6ebffb2, 0x0608d97c, 0xfbfa1e88, 0x02a36a1e, 0xfe51f802, 0x0106e583, 0xff675d09, 0x00531b12, 0xffd64264, 0x0012e1c8, 0xfff89b00, 0x00025143, 0xffff890e, + 0x4731beb7, 0xe71a21c7, 0x0e232425, 0xf6ee217b, 0x060aa9da, 0xfbf6e48c, 0x02a6d4f0, 0xfe4ef3a4, 0x0109469d, 0xff65a718, 0x00543b04, 0xffd59695, 0x00133d0e, 0xfff87176, 0x0002601b, 0xffff85f5, + 0x45ee25e7, 0xe7499e8f, 0x0e14ed93, 0xf6f197ad, 0x060b91ee, 0xfbf4492d, 0x02a9d508, 0xfe4c3546, 0x010b7b61, 0xff640c08, 0x00554b83, 0xffd4f316, 0x00139436, 0xfff849c0, 0x00026e41, 0xffff830e, + 0x44a92b96, 0xe77c3db4, 0x0e04bd39, 0xf6f65f9b, 0x060b92ff, 0xfbf24bd9, 0x02ac6a9e, 0xfe49bcd9, 0x010d83cb, 0xff628be3, 0x00564c88, 0xffd457ec, 0x0013e73e, 0xfff823dd, 0x00027bb8, 0xffff805a, + 0x4362eadc, 0xe7b1efb4, 0x0df29936, 0xf6fc766a, 0x060aae6e, 0xfbf0ebe7, 0x02ae95fb, 0xfe478a42, 0x010f5fe2, 0xff6126a9, 0x00573e0f, 0xffd3c519, 0x00143626, 0xfff7ffce, 0x0002887f, 0xffff7dd6, + 0x421b7edf, 0xe7eaa4d4, 0x0dde87e2, 0xf703d912, 0x0608e5c2, 0xfbf02896, 0x02b05779, 0xfe459d5e, 0x01110faf, 0xff5fdc5b, 0x00582016, 0xffd33a9e, 0x001480ec, 0xfff7dd92, 0x00029497, 0xffff7b82, + 0x40d302c5, 0xe8264d21, 0x0dc88fd2, 0xf70c8461, 0x06063a9d, 0xfbf00112, 0x02b1af7f, 0xfe43f5ff, 0x01129344, 0xff5eacf3, 0x0058f29f, 0xffd2b87c, 0x0014c792, 0xfff7bd28, 0x0002a002, 0xffff795f, + 0x3f8991bd, 0xe864d874, 0x0db0b7d1, 0xf71674fa, 0x0602aec3, 0xfbf0746e, 0x02b29e84, 0xfe4293ec, 0x0113eabb, 0xff5d9867, 0x0059b5ad, 0xffd23eaf, 0x00150a19, 0xfff79e8f, 0x0002aac0, 0xffff776a, + 0x3e3f46f2, 0xe8a63671, 0x0d9706e1, 0xf721a756, 0x05fe4414, 0xfbf181a9, 0x02b3250f, 0xfe4176e2, 0x01151632, 0xff5c9eaa, 0x005a6946, 0xffd1cd37, 0x00154883, 0xfff781c5, 0x0002b4d2, 0xffff75a3, + 0x3cf43d8f, 0xe8ea568f, 0x0d7b843b, 0xf72e17c4, 0x05f8fc8f, 0xfbf327ab, 0x02b343b5, 0xfe409e95, 0x011615ce, 0xff5bbfaa, 0x005b0d72, 0xffd1640e, 0x001582d3, 0xfff766c8, 0x0002be3b, 0xffff740a, + 0x3ba890b9, 0xe9312813, 0x0d5e3749, 0xf73bc26b, 0x05f2da52, 0xfbf56549, 0x02b2fb1a, 0xfe400aae, 0x0116e9bc, 0xff5afb53, 0x005ba23b, 0xffd1032f, 0x0015b90b, 0xfff74d97, 0x0002c6fa, 0xffff729e, + 0x3a5c5b8e, 0xe97a9a17, 0x0d3f27ab, 0xf74aa34c, 0x05ebdf97, 0xfbf83941, 0x02b24bf1, 0xfe3fbacd, 0x0117922f, 0xff5a5189, 0x005c27af, 0xffd0aa93, 0x0015eb2f, 0xfff7362f, 0x0002cf12, 0xffff715d, + 0x390fb920, 0xe9c69b8c, 0x0d1e5d32, 0xf75ab63f, 0x05e40eb3, 0xfbfba23f, 0x02b136f9, 0xfe3fae87, 0x01180f5d, 0xff59c230, 0x005c9ddc, 0xffd05a33, 0x00161944, 0xfff7208d, 0x0002d684, 0xffff7047, + 0x37c2c474, 0xea151b3a, 0x0cfbdfdd, 0xf76bf6f7, 0x05db6a19, 0xfbff9ed7, 0x02afbd02, 0xfe3fe569, 0x01186187, 0xff594d27, 0x005d04d4, 0xffd01205, 0x0016434f, 0xfff70caf, 0x0002dd53, 0xffff6f5c, + 0x36759880, 0xea6607c4, 0x0cd7b7dd, 0xf77e6103, 0x05d1f459, 0xfc042d8e, 0x02addee8, 0xfe405ef6, 0x011888f2, 0xff58f249, 0x005d5cab, 0xffcfd1ff, 0x00166956, 0xfff6fa92, 0x0002e37e, 0xffff6e99, + 0x35285026, 0xeab94fa9, 0x0cb1ed8c, 0xf791efcb, 0x05c7b01a, 0xfc094cd2, 0x02ab9d96, 0xfe411aa8, 0x011885e7, 0xff58b16c, 0x005da575, 0xffcf9a15, 0x00168b5e, 0xfff6ea31, 0x0002e90a, 0xffff6dff, + 0x33db0631, 0xeb0ee148, 0x0c8a8973, 0xf7a69e96, 0x05bca021, 0xfc0efafe, 0x02a8fa03, 0xfe4217ef, 0x011858b9, 0xff588a65, 0x005ddf4c, 0xffcf6a3b, 0x0016a96f, 0xfff6db89, 0x0002edf6, 0xffff6d8d, + 0x328dd556, 0xeb66aae0, 0x0c619444, 0xf7bc6889, 0x05b0c74b, 0xfc15365c, 0x02a5f535, 0xfe435633, 0x011801be, 0xff587d03, 0x005e0a48, 0xffcf4262, 0x0016c390, 0xfff6ce97, 0x0002f246, 0xffff6d40, + 0x3140d82e, 0xebc09a94, 0x0c3716da, 0xf7d348a4, 0x05a42890, 0xfc1bfd22, 0x02a2903e, 0xfe44d4d3, 0x01178152, 0xff588913, 0x005e2687, 0xffcf227b, 0x0016d9c9, 0xfff6c356, 0x0002f5fc, 0xffff6d1a, + 0x2ff42933, 0xec1c9e6d, 0x0c0b1a37, 0xf7eb39cc, 0x0596c6ff, 0xfc234d75, 0x029ecc3c, 0xfe469325, 0x0116d7d7, 0xff58ae5d, 0x005e3427, 0xffcf0a77, 0x0016ec22, 0xfff6b9c1, 0x0002f919, 0xffff6d17, + 0x2ea7e2c0, 0xec7aa45b, 0x0bdda783, 0xf80436c0, 0x0588a5bf, 0xfc2b2567, 0x029aaa5a, 0xfe489077, 0x011605b5, 0xff58eca8, 0x005e3347, 0xffcefa44, 0x0016faa5, 0xfff6b1d5, 0x0002fba0, 0xffff6d38, + 0x2d5c1f0e, 0xecda9a39, 0x0baec80a, 0xf81e3a25, 0x0579c812, 0xfc3382fb, 0x02962bd1, 0xfe4acc0e, 0x01150b5a, 0xff5943b4, 0x005e240a, 0xffcef1cf, 0x0017055b, 0xfff6ab8c, 0x0002fd94, 0xffff6d7c, + 0x2c10f82d, 0xed3c6dce, 0x0b7e853c, 0xf8393e81, 0x056a314b, 0xfc3c6420, 0x029151e3, 0xfe4d4526, 0x0113e937, 0xff59b340, 0x005e0694, 0xffcef106, 0x00170c4f, 0xfff6a6e2, 0x0002fef6, 0xffff6de2, + 0x2ac68807, 0xeda00cd1, 0x0b4ce8a8, 0xf8553e3c, 0x0559e4da, 0xfc45c6b6, 0x028c1de0, 0xfe4ffaf6, 0x01129fc5, 0xff5a3b09, 0x005ddb0b, 0xffcef7d4, 0x00170f8a, 0xfff6a3d0, 0x0002ffc9, 0xffff6e67, + 0x297ce85a, 0xee0564e8, 0x0b19fbfe, 0xf87233a4, 0x0548e63f, 0xfc4fa88f, 0x02869122, 0xfe52ecab, 0x01112f81, 0xff5adac6, 0x005da198, 0xffcf0623, 0x00170f18, 0xfff6a252, 0x00030010, 0xffff6f0d, + 0x283432b9, 0xee6c63ad, 0x0ae5c90b, 0xf89018eb, 0x05373912, 0xfc5a076a, 0x0280ad0f, 0xfe561969, 0x010f98eb, 0xff5b922d, 0x005d5a62, 0xffcf1bde, 0x00170b04, 0xfff6a262, 0x0002ffcd, 0xffff6fd1, + 0x26ec8083, 0xeed4f6b0, 0x0ab059bc, 0xf8aee828, 0x0524e100, 0xfc64e0f9, 0x027a7318, 0xfe598050, 0x010ddc8c, 0xff5c60ee, 0x005d0597, 0xffcf38ec, 0x0017035a, 0xfff6a3f9, 0x0002ff03, 0xffff70b2, + 0x25a5eae8, 0xef3f0b78, 0x0a79b814, 0xf8ce9b5d, 0x0511e1c6, 0xfc7032de, 0x0273e4b8, 0xfe5d2075, 0x010bfaee, 0xff5d46bb, 0x005ca363, 0xffcf5d36, 0x0016f828, 0xfff6a713, 0x0002fdb4, 0xffff71b0, + 0x24608ae2, 0xefaa8f87, 0x0a41ee32, 0xf8ef2c71, 0x04fe3f39, 0xfc7bfaad, 0x026d0374, 0xfe60f8ea, 0x0109f4a2, 0xff5e433e, 0x005c33f6, 0xffcf88a2, 0x0016e979, 0xfff6aba9, 0x0002fbe4, 0xffff72c9, + 0x231c7932, 0xf017705a, 0x0a09064e, 0xf9109535, 0x04e9fd3c, 0xfc8835ed, 0x0265d0dd, 0xfe6508b6, 0x0107ca3c, 0xff5f5621, 0x005bb77f, 0xffcfbb17, 0x0016d75b, 0xfff6b1b4, 0x0002f995, 0xffff73fc, + 0x21d9ce63, 0xf0859b6e, 0x09cf0ab4, 0xf932cf65, 0x04d51fc6, 0xfc94e216, 0x025e4e8b, 0xfe694edd, 0x01057c57, 0xff607f0b, 0x005b2e31, 0xffcff478, 0x0016c1dc, 0xfff6b92d, 0x0002f6c9, 0xffff7549, + 0x2098a2bf, 0xf0f4fe3d, 0x099405c6, 0xf955d4a7, 0x04bfaadf, 0xfca1fc96, 0x02567e22, 0xfe6dca58, 0x01030b8e, 0xff61bd9f, 0x005a9840, 0xffd034ac, 0x0016a90a, 0xfff6c20f, 0x0002f385, 0xffff76ae, + 0x1f590e55, 0xf1658649, 0x095801f8, 0xf9799e8f, 0x04a9a29e, 0xfcaf82ca, 0x024e614c, 0xfe727a1f, 0x01007885, 0xff631180, 0x0059f5e1, 0xffd07b95, 0x00168cf2, 0xfff6cc52, 0x0002efca, 0xffff782a, + 0x1e1b28f2, 0xf1d72114, 0x091b09d1, 0xf99e269e, 0x04930b2b, 0xfcbd7206, 0x0245f9bf, 0xfe775d1f, 0x00fdc3e0, 0xff647a4b, 0x0059474a, 0xffd0c915, 0x00166da5, 0xfff6d7f0, 0x0002eb9c, 0xffff79bc, + 0x1cdf0a20, 0xf249bc2c, 0x08dd27e6, 0xf9c36642, 0x047be8bc, 0xfccbc793, 0x023d4937, 0xfe7c7243, 0x00faee49, 0xff65f79e, 0x00588cb4, 0xffd11d0f, 0x00164b32, 0xfff6e4e1, 0x0002e6fe, 0xffff7b63, + 0x1ba4c923, 0xf2bd4523, 0x089e66dd, 0xf9e956da, 0x04643f95, 0xfcda80ad, 0x0234517a, 0xfe81b86d, 0x00f7f86e, 0xff678912, 0x0057c658, 0xffd17764, 0x001625a7, 0xfff6f31d, 0x0002e1f3, 0xffff7d1f, + 0x1a6c7cf9, 0xf331a99b, 0x085ed167, 0xfa0ff1b6, 0x044c1409, 0xfce99a86, 0x022b1455, 0xfe872e7c, 0x00f4e2ff, 0xff692e3f, 0x0056f471, 0xffd1d7f5, 0x0015fd15, 0xfff7029f, 0x0002dc7d, 0xffff7eed, + 0x19363c54, 0xf3a6d741, 0x081e7241, 0xfa373017, 0x04336a75, 0xfcf91246, 0x0221939d, 0xfe8cd349, 0x00f1aeb2, 0xff6ae6ba, 0x0056173b, 0xffd23ea1, 0x0015d18b, 0xfff7135d, 0x0002d6a0, 0xffff80cd, + 0x18021d9d, 0xf41cbbd3, 0x07dd5430, 0xfa5f0b30, 0x041a4744, 0xfd08e50c, 0x0217d12d, 0xfe92a5a7, 0x00ee5c3e, 0xff6cb218, 0x00552ef3, 0xffd2ab47, 0x0015a31b, 0xfff72551, 0x0002d060, 0xffff82bf, + 0x16d036eb, 0xf493451f, 0x079b8203, 0xfa877c29, 0x0400aeec, 0xfd190fed, 0x020dcee8, 0xfe98a466, 0x00eaec5e, 0xff6e8fe9, 0x00543bd8, 0xffd31dc7, 0x001571d5, 0xfff73873, 0x0002c9be, 0xffff84c0, + 0x15a09e09, 0xf50a610a, 0x0759068f, 0xfab07c1d, 0x03e6a5ee, 0xfd298ff6, 0x02038eb7, 0xfe9ece4f, 0x00e75fd1, 0xff707fbd, 0x00533e29, 0xffd395fd, 0x00153dca, 0xfff74cba, 0x0002c2be, 0xffff86d0, + 0x1473686d, 0xf581fd8b, 0x0715ecae, 0xfada0420, 0x03cc30d4, 0xfd3a622b, 0x01f9128a, 0xfea52227, 0x00e3b758, 0xff728121, 0x00523626, 0xffd413c9, 0x0015070b, 0xfff76220, 0x0002bb64, 0xffff88ee, + 0x1348ab3a, 0xf5fa08b5, 0x06d23f3d, 0xfb040d3b, 0x03b15431, 0xfd4b8389, 0x01ee5c55, 0xfeab9eb2, 0x00dff3b7, 0xff7493a2, 0x00512412, 0xffd49705, 0x0014cdab, 0xfff7789c, 0x0002b3b3, 0xffff8b19, + 0x12207b3e, 0xf67270b1, 0x068e091c, 0xfb2e906f, 0x039614a1, 0xfd5cf105, 0x01e36e14, 0xfeb242ac, 0x00dc15b4, 0xff76b6ca, 0x0050082f, 0xffd51f90, 0x001491b9, 0xfff79026, 0x0002abad, 0xffff8d50, + 0x10faecee, 0xf6eb23c6, 0x0649552a, 0xfb5986b6, 0x037a76c7, 0xfd6ea790, 0x01d849c7, 0xfeb90cce, 0x00d81e1a, 0xff78ea20, 0x004ee2c1, 0xffd5ad44, 0x00145349, 0xfff7a8b6, 0x0002a357, 0xffff8f92, + 0x0fd81464, 0xf7641059, 0x06042e45, 0xfb84e906, 0x035e7f4e, 0xfd80a411, 0x01ccf173, 0xfebffbd0, 0x00d40db3, 0xff7b2d2d, 0x004db40c, 0xffd63ffe, 0x0014126c, 0xfff7c245, 0x00029ab2, 0xffff91de, + 0x0eb80562, 0xf7dd24ef, 0x05be9f49, 0xfbb0b04e, 0x034232e6, 0xfd92e36c, 0x01c16720, 0xfec70e64, 0x00cfe54f, 0xff7d7f76, 0x004c7c55, 0xffd6d798, 0x0013cf36, 0xfff7dcc8, 0x000291c3, 0xffff9434, + 0x0d9ad348, 0xf856502d, 0x0578b30e, 0xfbdcd57a, 0x03259644, 0xfda5627e, 0x01b5acdd, 0xfece433a, 0x00cba5bc, 0xff7fe07f, 0x004b3be3, 0xffd773ed, 0x001389b7, 0xfff7f83a, 0x0002888c, 0xffff9691, + 0x0c80911b, 0xf8cf80de, 0x05327467, 0xfc095174, 0x0308ae24, 0xfdb81e22, 0x01a9c4bc, 0xfed598fe, 0x00c74fce, 0xff824fca, 0x0049f2fc, 0xffd814d7, 0x00134204, 0xfff81490, 0x00027f11, 0xffff98f5, + 0x0b69517e, 0xf948a5f0, 0x04ebee1c, 0xfc361d25, 0x02eb7f44, 0xfdcb132d, 0x019db0d0, 0xfedd0e5c, 0x00c2e457, 0xff84ccdb, 0x0048a1e7, 0xffd8ba31, 0x0012f82e, 0xfff831c3, 0x00027555, 0xffff9b60, + 0x0a5526b0, 0xf9c1ae7b, 0x04a52af2, 0xfc633173, 0x02ce0e67, 0xfdde3e6f, 0x01917334, 0xfee4a1fa, 0x00be642f, 0xff875731, 0x004748ed, 0xffd963d4, 0x0012ac48, 0xfff84fcb, 0x00026b5b, 0xffff9dd0, + 0x0944228e, 0xfa3a89be, 0x045e359f, 0xfc908746, 0x02b0604f, 0xfdf19cb9, 0x01850e00, 0xfeec527e, 0x00b9d02b, 0xff89ee4d, 0x0045e856, 0xffda1199, 0x00125e66, 0xfff86e9e, 0x00026126, 0xffffa045, + 0x08365690, 0xfab32723, 0x041718d2, 0xfcbe1789, 0x029279c4, 0xfe052ad4, 0x01788354, 0xfef41e8c, 0x00b52925, 0xff8c91ad, 0x0044806c, 0xffdac35a, 0x00120e9b, 0xfff88e35, 0x000256b9, 0xffffa2be, + 0x072bd3c5, 0xfb2b7641, 0x03cfdf29, 0xfcebdb26, 0x02745f8c, 0xfe18e58c, 0x016bd54f, 0xfefc04c6, 0x00b06ff7, 0xff8f40d0, 0x00431177, 0xffdb78ef, 0x0011bcf9, 0xfff8ae88, 0x00024c18, 0xffffa539, + 0x0624aad6, 0xfba366df, 0x03889336, 0xfd19cb0e, 0x02561670, 0xfe2cc9a7, 0x015f0612, 0xff0403cc, 0x00aba57c, 0xff91fb31, 0x00419bc2, 0xffdc3231, 0x00116994, 0xfff8cf8d, 0x00024146, 0xffffa7b7, + 0x0520ec00, 0xfc1ae8f2, 0x03413f7b, 0xfd47e035, 0x0237a337, 0xfe40d3ed, 0x015217c0, 0xff0c1a3c, 0x00a6ca90, 0xff94c04f, 0x00401f98, 0xffdceef9, 0x00111480, 0xfff8f13c, 0x00023645, 0xffffaa35, + 0x0420a716, 0xfc91eca1, 0x02f9ee68, 0xfd761395, 0x02190aa6, 0xfe550124, 0x01450c7f, 0xff1446b5, 0x00a1e00f, 0xff978fa6, 0x003e9d42, 0xffddaf1e, 0x0010bdcf, 0xfff9138e, 0x00022b19, 0xffffacb4, + 0x0323eb7f, 0xfd086246, 0x02b2aa5c, 0xfda45e2c, 0x01fa5183, 0xfe694e12, 0x0137e672, 0xff1c87d3, 0x009ce6d8, 0xff9a68b0, 0x003d150d, 0xffde727a, 0x00106595, 0xfff93679, 0x00021fc5, 0xffffaf33, +}; + +// cmd-line: fir -l 7 -s 44100 -c 19876 -n 16 -b 9.62 +const int32_t dn_sampler_filter_coefficients[] __attribute__ ((aligned (32))) = { + 0x736144b5, 0x0c333a22, 0xf4fca390, 0x09424904, 0xf8c92a41, 0x052ac04c, 0xfca4fc64, 0x01ed8cc7, 0xff119cc0, 0x0053ba6e, 0xfff9a80d, 0xffeaeaab, 0x001690d9, 0xfff11dcd, 0x000715d9, 0xfffdb4b9, + 0x735ed3aa, 0x0b433de8, 0xf560f0f3, 0x091282c4, 0xf8dd5ccf, 0x0525cb66, 0xfca23e3d, 0x01f33960, 0xff0bc9c2, 0x00586127, 0xfff68603, 0xffecbad5, 0x0015ab8b, 0xfff17c10, 0x0006f71a, 0xfffdbc2f, + 0x735780bb, 0x0a55a98f, 0xf5c5b2a1, 0x08e1ea27, 0xf8f25767, 0x0520366d, 0xfc9ff262, 0x01f89c98, 0xff0620a4, 0x005cf349, 0xfff36c0d, 0xffee8913, 0x0014c5dc, 0xfff1db1a, 0x0006d7d7, 0xfffdc3db, + 0x734b4c77, 0x096a8a51, 0xf62adb7c, 0x08b086aa, 0xf9081629, 0x051a030f, 0xfc9e186a, 0x01fdb637, 0xff00a1d8, 0x00617065, 0xfff05a84, 0xfff0552d, 0x0013dfed, 0xfff23ada, 0x0006b817, 0xfffdcbba, + 0x733a37d2, 0x0881ed1f, 0xf6905e79, 0x087e5fd7, 0xf91e9521, 0x05133308, 0xfc9cafe0, 0x0202860e, 0xfefb4dc7, 0x0065d80c, 0xffed51bc, 0xfff21ee8, 0x0012f9de, 0xfff29b40, 0x000697e0, 0xfffdd3ca, + 0x7324441e, 0x079bdea7, 0xf6f62e9d, 0x084b7d43, 0xf935d048, 0x050bc828, 0xfc9bb83e, 0x02070bf9, 0xfef624d8, 0x006a29d6, 0xffea520a, 0xfff3e60f, 0x001213d0, 0xfff2fc3d, 0x00067739, 0xfffddc07, + 0x7309730f, 0x06b86b52, 0xf75c3eff, 0x0817e68c, 0xf94dc388, 0x0503c44d, 0xfc9b30f3, 0x020b47dd, 0xfef12766, 0x006e655c, 0xffe75bbe, 0xfff5aa69, 0x00112de1, 0xfff35dc1, 0x00065629, 0xfffde470, + 0x72e9c6b8, 0x05d79f40, 0xf7c282cb, 0x07e3a35a, 0xf9666ab7, 0x04fb2969, 0xfc9b195f, 0x020f39ab, 0xfeec55cc, 0x00728a3d, 0xffe46f2a, 0xfff76bc2, 0x00104831, 0xfff3bfbc, 0x000634b6, 0xfffded03, + 0x72c5418e, 0x04f98649, 0xf828ed43, 0x07aebb5d, 0xf97fc19e, 0x04f1f97c, 0xfc9b70d6, 0x0212e15c, 0xfee7b059, 0x0076981a, 0xffe18c9a, 0xfff929e3, 0x000f62de, 0xfff4221f, 0x000612e8, 0xfffdf5bc, + 0x729be665, 0x041e2bfe, 0xf88f71bf, 0x0779364a, 0xf999c3f4, 0x04e83697, 0xfc9c369c, 0x02163ef1, 0xfee33759, 0x007a8e98, 0xffdeb45b, 0xfffae49b, 0x000e7e08, 0xfff484db, 0x0005f0c4, 0xfffdfe9b, + 0x726db871, 0x03459ba4, 0xf8f603ae, 0x07431bdf, 0xf9b46d64, 0x04dde2da, 0xfc9d69eb, 0x02195278, 0xfedeeb11, 0x007e6d61, 0xffdbe6b6, 0xfffc9bb4, 0x000d99cc, 0xfff4e7e1, 0x0005ce51, 0xfffe079b, + 0x723abb44, 0x026fe039, 0xf95c9699, 0x070c73dd, 0xf9cfb988, 0x04d30074, 0xfc9f09ee, 0x021c1c06, 0xfedacbbf, 0x00823422, 0xffd923f4, 0xfffe4efd, 0x000cb647, 0xfff54b20, 0x0005ab95, 0xfffe10bc, + 0x7202f2d3, 0x019d046d, 0xf9c31e22, 0x06d5460b, 0xf9eba3ef, 0x04c791a4, 0xfca115c5, 0x021e9bbb, 0xfed6d99c, 0x0085e28b, 0xffd66c59, 0xfffffe46, 0x000bd397, 0xfff5ae8c, 0x00058898, 0xfffe19fa, + 0x71c6636d, 0x00cd12a4, 0xfa298e07, 0x069d9a31, 0xfa082817, 0x04bb98b5, 0xfca38c83, 0x0220d1bf, 0xfed314da, 0x00897851, 0xffd3c02a, 0x0001a95d, 0x000af1d9, 0xfff61214, 0x0005655e, 0xfffe2354, + 0x718511c2, 0x000014f8, 0xfa8fda21, 0x0665781b, 0xfa254176, 0x04af1804, 0xfca66d2e, 0x0222be45, 0xfecf7da3, 0x008cf52d, 0xffd11fa9, 0x00035015, 0x000a1129, 0xfff675ab, 0x000541f0, 0xfffe2cc8, + 0x713f02e0, 0xff361534, 0xfaf5f669, 0x062ce795, 0xfa42eb75, 0x04a211f8, 0xfca9b6bf, 0x02246187, 0xfecc141d, 0x009058da, 0xffce8b13, 0x0004f23e, 0x000931a3, 0xfff6d942, 0x00051e52, 0xfffe3652, + 0x70f43c32, 0xfe6f1cd7, 0xfb5bd6f4, 0x05f3f06b, 0xfa61216f, 0x04948906, 0xfcad6827, 0x0225bbca, 0xfec8d867, 0x0093a31a, 0xffcc02a8, 0x00068fad, 0x00085362, 0xfff73ccb, 0x0004fa8b, 0xfffe3ff2, + 0x70a4c37f, 0xfdab350f, 0xfbc16ff6, 0x05ba9a6b, 0xfa7fdeba, 0x04867fb3, 0xfcb18047, 0x0226cd5b, 0xfec5ca9a, 0x0096d3af, 0xffc986a1, 0x00082835, 0x00077681, 0xfff7a037, 0x0004d6a1, 0xfffe49a4, + 0x70509eec, 0xfcea66be, 0xfc26b5c5, 0x0580ed5f, 0xfa9f1e9e, 0x0477f88d, 0xfcb5fdf7, 0x02279691, 0xfec2eaca, 0x0099ea62, 0xffc71738, 0x0009bbab, 0x00069b1b, 0xfff8037a, 0x0004b29a, 0xfffe5367, + 0x6ff7d4f8, 0xfc2cba75, 0xfc8b9cda, 0x0546f10f, 0xfabedc5a, 0x0468f62e, 0xfcbae002, 0x022817ca, 0xfec03901, 0x009ce6fe, 0xffc4b4a4, 0x000b49e6, 0x0005c149, 0xfff86686, 0x00048e7c, 0xfffe5d38, + 0x6f9a6c7f, 0xfb723876, 0xfcf019cd, 0x050cad3f, 0xfadf1328, 0x04597b40, 0xfcc0252b, 0x0228516f, 0xfebdb547, 0x009fc954, 0xffc25f1a, 0x000cd2bd, 0x0004e926, 0xfff8c94c, 0x00046a4c, 0xfffe6716, + 0x6f386cb6, 0xfabae8b2, 0xfd54215c, 0x04d229b1, 0xfaffbe36, 0x04498a72, 0xfcc5cc26, 0x022843f0, 0xfebb5f9b, 0x00a29136, 0xffc016cb, 0x000e5609, 0x000412c9, 0xfff92bc0, 0x00044612, 0xfffe70ff, + 0x6ed1dd2e, 0xfa06d2ca, 0xfdb7a869, 0x04976e20, 0xfb20d8ad, 0x04392684, 0xfccbd3a0, 0x0227efc6, 0xfeb937f9, 0x00a53e7b, 0xffbddbe8, 0x000fd3a3, 0x00033e4c, 0xfff98dd6, 0x000421d2, 0xfffe7aef, + 0x6e66c5ce, 0xf955fe0c, 0xfe1aa3fc, 0x045c8240, 0xfb425db0, 0x0428523d, 0xfcd23a3a, 0x02275572, 0xfeb73e54, 0x00a7d0ff, 0xffbbae9f, 0x00114b67, 0x00026bc6, 0xfff9ef80, 0x0003fd92, 0xfffe84e7, + 0x6df72ed9, 0xf8a87178, 0xfe7d0942, 0x04216dc0, 0xfb64485b, 0x0417106e, 0xfcd8fe8b, 0x0226757e, 0xfeb5729b, 0x00aa48a0, 0xffb98f1c, 0x0012bd30, 0x00019b4e, 0xfffa50b1, 0x0003d957, 0xfffe8ee3, + 0x6d8320e6, 0xf7fe33ba, 0xfedecd90, 0x03e63846, 0xfb8693c6, 0x040563f4, 0xfce01f21, 0x0225507c, 0xfeb3d4b7, 0x00aca542, 0xffb77d88, 0x001428db, 0x0000ccfc, 0xfffab15e, 0x0003b527, 0xfffe98e2, + 0x6d0aa4e6, 0xf7574b2b, 0xff3fe663, 0x03aae970, 0xfba93b01, 0x03f34fb2, 0xfce79a7f, 0x0223e706, 0xfeb26489, 0x00aee6ca, 0xffb57a0b, 0x00158e47, 0x000000e6, 0xfffb117a, 0x00039108, 0xfffea2e1, + 0x6c8dc41f, 0xf6b3bdd3, 0xffa04963, 0x036f88d2, 0xfbcc391d, 0x03e0d697, 0xfcef6f20, 0x022239bc, 0xfeb121ee, 0x00b10d23, 0xffb384ca, 0x0016ed53, 0xffff3721, 0xfffb70fa, 0x00036cfe, 0xfffeacdf, + 0x6c0c882a, 0xf6139169, 0xffffec5f, 0x03341df4, 0xfbef8924, 0x03cdfb99, 0xfcf79b75, 0x02204949, 0xfeb00cbf, 0x00b3183c, 0xffb19de7, 0x001845e0, 0xfffe6fc3, 0xfffbcfd2, 0x00034910, 0xfffeb6db, + 0x6b86faf8, 0xf576cb4e, 0x005ec552, 0x02f8b055, 0xfc13261f, 0x03bac1b4, 0xfd001de8, 0x021e165d, 0xfeaf24cc, 0x00b50805, 0xffafc584, 0x001997d0, 0xfffdaadf, 0xfffc2df6, 0x00032541, 0xfffec0d2, + 0x6afd26cb, 0xf4dd7092, 0x00bcca63, 0x02bd4768, 0xfc370b14, 0x03a72bf0, 0xfd08f4d6, 0x021ba1b2, 0xfeae69e1, 0x00b6dc75, 0xffadfbbe, 0x001ae306, 0xfffce88b, 0xfffc8b5c, 0x00030196, 0xfffecac3, + 0x6a6f1638, 0xf44785f1, 0x0119f1e4, 0x0281ea90, 0xfc5b3309, 0x03933d58, 0xfd121e99, 0x0218ec06, 0xfeaddbc4, 0x00b89584, 0xffac40b3, 0x001c2765, 0xfffc28d9, 0xfffce7f8, 0x0002de16, 0xfffed4ab, + 0x69dcd425, 0xf3b50fd6, 0x01763256, 0x0246a125, 0xfc7f9902, 0x037ef900, 0xfd1b9980, 0x0215f621, 0xfead7a37, 0x00ba3330, 0xffaa947c, 0x001d64d5, 0xfffb6bdd, 0xfffd43c1, 0x0002bac4, 0xfffede8a, + 0x69466bc8, 0xf3261255, 0x01d18265, 0x020b726f, 0xfca43803, 0x036a6201, 0xfd2563d3, 0x0212c0d2, 0xfead44f4, 0x00bbb579, 0xffa8f730, 0x001e9b3a, 0xfffab1a8, 0xfffd9eab, 0x000297a5, 0xfffee85e, + 0x68abe8a8, 0xf29a9133, 0x022bd8ee, 0x01d065a8, 0xfcc90b12, 0x03557b7a, 0xfd2f7bd1, 0x020f4cec, 0xfead3bb2, 0x00bd1c63, 0xffa768e6, 0x001fca7d, 0xfff9fa4d, 0xfffdf8ae, 0x000274be, 0xfffef225, + 0x680d5698, 0xf2128fde, 0x02852cfc, 0x019581f9, 0xfcee0d33, 0x03404890, 0xfd39dfb4, 0x020b9b4c, 0xfead5e22, 0x00be67f6, 0xffa5e9b1, 0x0020f288, 0xfff945dc, 0xfffe51be, 0x00025214, 0xfffefbde, + 0x676ac1bb, 0xf18e1174, 0x02dd75ca, 0x015ace79, 0xfd133970, 0x032acc6d, 0xfd448dae, 0x0207acd4, 0xfeadabef, 0x00bf983d, 0xffa479a2, 0x00221344, 0xfff89465, 0xfffea9d2, 0x00022fa9, 0xffff0587, + 0x66c4367d, 0xf10d18bd, 0x0334aac4, 0x0120522f, 0xfd388ad1, 0x03150a3f, 0xfd4f83eb, 0x0203826c, 0xfeae24c1, 0x00c0ad48, 0xffa318c7, 0x00232c9d, 0xfff7e5f9, 0xffff00e1, 0x00020d84, 0xffff0f1f, + 0x6619c197, 0xf08fa82f, 0x038ac385, 0x00e6140f, 0xfd5dfc63, 0x02ff0538, 0xfd5ac08e, 0x01ff1d04, 0xfeaec838, 0x00c1a728, 0xffa1c72f, 0x00243e7f, 0xfff73aa7, 0xffff56e3, 0x0001eba8, 0xffff18a4, + 0x656b700a, 0xf015c1ee, 0x03dfb7dd, 0x00ac1af9, 0xfd838938, 0x02e8c08e, 0xfd6641b8, 0x01fa7d91, 0xfeaf95f2, 0x00c285f4, 0xffa084e3, 0x002548d9, 0xfff6927e, 0xffffabcd, 0x0001ca18, 0xffff2215, + 0x64b94f22, 0xef9f67cb, 0x04337fcb, 0x00726dbb, 0xfda92c63, 0x02d23f7a, 0xfd720581, 0x01f5a50d, 0xfeb08d86, 0x00c349c4, 0xff9f51eb, 0x00264b9a, 0xfff5ed8b, 0xffffff99, 0x0001a8da, 0xffff2b70, + 0x64036c6f, 0xef2c9b43, 0x04861383, 0x0039130c, 0xfdcee0ff, 0x02bb8537, 0xfd7e09fc, 0x01f0947a, 0xfeb1ae87, 0x00c3f2b6, 0xff9e2e50, 0x002746b2, 0xfff54bdc, 0x0000523d, 0x000187f0, 0xffff34b6, + 0x6349d5c9, 0xeebd5d81, 0x04d76b6b, 0x00001191, 0xfdf4a22a, 0x02a49505, 0xfd8a4d37, 0x01eb4cde, 0xfeb2f884, 0x00c480e9, 0xff9d1a14, 0x00283a12, 0xfff4ad7e, 0x0000a3b3, 0x0001675f, 0xffff3de3, + 0x628c994c, 0xee51af5f, 0x0527801d, 0xffc76fd5, 0xfe1a6b08, 0x028d7223, 0xfd96cd3d, 0x01e5cf44, 0xfeb46b07, 0x00c4f480, 0xff9c1539, 0x002925ae, 0xfff4127d, 0x0000f3f1, 0x00014729, 0xffff46f7, + 0x61cbc559, 0xede99165, 0x05764a68, 0xff8f344f, 0xfe4036c5, 0x02761fd3, 0xfda3880f, 0x01e01cbe, 0xfeb60596, 0x00c54da2, 0xff9b1fc1, 0x002a0979, 0xfff37ae4, 0x000142f1, 0x00012754, 0xffff4ff1, + 0x61076890, 0xed8503c7, 0x05c3c34e, 0xff576560, 0xfe660094, 0x025ea157, 0xfdb07bb0, 0x01da3661, 0xfeb7c7b0, 0x00c58c79, 0xff9a39a9, 0x002ae568, 0xfff2e6bf, 0x000190ac, 0x000107e1, 0xffff58d0, + 0x603f91d5, 0xed24066b, 0x060fe408, 0xff20094d, 0xfe8bc3ad, 0x0246f9f3, 0xfdbda61a, 0x01d41d4a, 0xfeb9b0d3, 0x00c5b132, 0xff9962ec, 0x002bb971, 0xfff25619, 0x0001dd1b, 0x0000e8d4, 0xffff6192, + 0x5f745049, 0xecc698e6, 0x065aa604, 0xfee92646, 0xfeb17b53, 0x022f2cea, 0xfdcb0546, 0x01cdd297, 0xfebbc078, 0x00c5bbfc, 0xff989b85, 0x002c858d, 0xfff1c8fa, 0x00022837, 0x0000ca30, 0xffff6a38, + 0x5ea5b34c, 0xec6cba79, 0x06a402e4, 0xfeb2c261, 0xfed722d0, 0x02173d81, 0xfdd89727, 0x01c7576d, 0xfebdf613, 0x00c5ad0a, 0xff97e36c, 0x002d49b4, 0xfff13f6c, 0x000271fa, 0x0000abf8, 0xffff72be, + 0x5dd3ca7a, 0xec166a19, 0x06ebf483, 0xfe7ce399, 0xfefcb57a, 0x01ff2ef9, 0xfde659af, 0x01c0acf5, 0xfec05114, 0x00c58494, 0xff973a96, 0x002e05df, 0xfff0b977, 0x0002ba5f, 0x00008e30, 0xffff7b26, + 0x5cfea5aa, 0xebc3a669, 0x073274f1, 0xfe478fd2, 0xff222eac, 0x01e70494, 0xfdf44acc, 0x01b9d45b, 0xfec2d0e8, 0x00c542d1, 0xff96a0f8, 0x002eba0a, 0xfff03724, 0x0003015f, 0x000070d9, 0xffff836d, + 0x5c2654ed, 0xeb746dbe, 0x07777e74, 0xfe12ccd1, 0xff4789d1, 0x01cec194, 0xfe026869, 0x01b2ced1, 0xfec574f9, 0x00c4e7fe, 0xff961684, 0x002f6630, 0xffefb87a, 0x000346f6, 0x000053f7, 0xffff8b93, + 0x5b4ae88d, 0xeb28be1f, 0x07bb0b8b, 0xfddea042, 0xff6cc25a, 0x01b66936, 0xfe10b06f, 0x01ab9d8b, 0xfec83caa, 0x00c47459, 0xff959b29, 0x00300a4f, 0xffef3d7f, 0x00038b1d, 0x0000378c, 0xffff9398, + 0x5a6c7108, 0xeae09544, 0x07fd16eb, 0xfdab0fb6, 0xff91d3c6, 0x019dfeb6, 0xfe1f20c5, 0x01a441c2, 0xfecb275e, 0x00c3e824, 0xff952ed7, 0x0030a665, 0xffeec63a, 0x0003cdd1, 0x00001b9a, 0xffff9b7a, + 0x598aff13, 0xea9bf097, 0x083d9b81, 0xfd7820a0, 0xffb6b99f, 0x0185854f, 0xfe2db74f, 0x019cbcb1, 0xfece3472, 0x00c343a4, 0xff94d178, 0x00313a72, 0xffee52b1, 0x00040f0d, 0x00000024, 0xffffa339, + 0x58a6a397, 0xea5acd38, 0x087c9471, 0xfd45d856, 0xffdb6f7c, 0x016d0037, 0xfe3c71f1, 0x01950f98, 0xfed16342, 0x00c2871f, 0xff9482f8, 0x0031c677, 0xffede2e7, 0x00044ecb, 0xffffe52d, 0xffffaad3, + 0x57bf6fae, 0xea1d27f7, 0x08b9fd18, 0xfd143c12, 0xfffff100, 0x015472a1, 0xfe4b4e8c, 0x018d3bb8, 0xfed4b325, 0x00c1b2e0, 0xff944340, 0x00324a74, 0xffed76e3, 0x00048d0a, 0xffffcab5, 0xffffb249, + 0x56d574a2, 0xe9e2fd5b, 0x08f5d10a, 0xfce350f0, 0x002439db, 0x013bdfbc, 0xfe5a4b03, 0x01854258, 0xfed82370, 0x00c0c731, 0xff941236, 0x0032c66e, 0xffed0ea7, 0x0004c9c4, 0xffffb0bf, 0xffffb99a, + 0x55e8c3ee, 0xe9ac49a0, 0x09300c14, 0xfcb31bec, 0x004845cc, 0x01234ab4, 0xfe696534, 0x017d24bf, 0xfedbb373, 0x00bfc463, 0xff93efbf, 0x00333a67, 0xffecaa36, 0x000504f6, 0xffff974d, 0xffffc0c5, + 0x54f96f37, 0xe97908b8, 0x0968aa3b, 0xfc83a1e5, 0x006c10a0, 0x010ab6b0, 0xfe789b01, 0x0174e437, 0xfedf627d, 0x00beaac6, 0xff93dbc0, 0x0033a665, 0xffec4994, 0x00053e9e, 0xffff7e61, 0xffffc7ca, + 0x54078851, 0xe9493649, 0x099fa7bb, 0xfc54e79a, 0x008f9631, 0x00f226d0, 0xfe87ea47, 0x016c820d, 0xfee32fdb, 0x00bd7aae, 0xff93d618, 0x00340a6d, 0xffebecc2, 0x000576b8, 0xffff65fc, 0xffffcea8, + 0x53132138, 0xe91ccdb5, 0x09d5010b, 0xfc26f1ad, 0x00b2d26b, 0x00d99e31, 0xfe9750e8, 0x0163ff90, 0xfee71ad4, 0x00bc3470, 0xff93deaa, 0x00346687, 0xffeb93c3, 0x0005ad41, 0xffff4e20, 0xffffd55f, + 0x521c4c10, 0xe8f3ca12, 0x0a08b2d9, 0xfbf9c49d, 0x00d5c147, 0x00c11feb, 0xfea6ccc3, 0x015b5e11, 0xfeeb22af, 0x00bad866, 0xff93f552, 0x0034babb, 0xffeb3e96, 0x0005e238, 0xffff36ce, 0xffffdbee, + 0x51231b26, 0xe8ce2631, 0x0a3aba09, 0xfbcd64ca, 0x00f85ecf, 0x00a8af0c, 0xfeb65bb9, 0x01529ee3, 0xfeef46b0, 0x00b966e9, 0xff9419ef, 0x00350711, 0xffeaed3c, 0x00061599, 0xffff2007, 0xffffe255, + 0x5027a0e9, 0xe8abdc9d, 0x0a6b13bc, 0xfba1d673, 0x011aa71d, 0x00904ea0, 0xfec5fbac, 0x0149c35a, 0xfef3861a, 0x00b7e055, 0xff944c5a, 0x00354b94, 0xffea9fb6, 0x00064764, 0xffff09ce, 0xffffe894, + 0x4f29efed, 0xe88ce79a, 0x0a99bd47, 0xfb771db9, 0x013c965b, 0x007801aa, 0xfed5aa7e, 0x0140cccb, 0xfef7e02a, 0x00b6450a, 0xff948c6e, 0x0035884f, 0xffea5602, 0x00067797, 0xfffef421, 0xffffeeaa, + 0x4e2a1ae8, 0xe871412a, 0x0ac6b43a, 0xfb4d3e97, 0x015e28c7, 0x005fcb26, 0xfee56614, 0x0137bc8f, 0xfefc541e, 0x00b49568, 0xff94da03, 0x0035bd4e, 0xffea1020, 0x0006a630, 0xfffedf04, 0xfffff498, + 0x4d2834b0, 0xe858e30a, 0x0af1f65d, 0xfb243cea, 0x017f5aad, 0x0047ae09, 0xfef52c54, 0x012e93fc, 0xff00e133, 0x00b2d1d1, 0xff9534f0, 0x0035ea9d, 0xffe9ce0d, 0x0006d32f, 0xfffeca76, 0xfffffa5d, + 0x4c245038, 0xe843c6b5, 0x0b1b81ad, 0xfafc1c6e, 0x01a0286c, 0x002fad3f, 0xff04fb25, 0x0125546c, 0xff0586a0, 0x00b0faaa, 0xff959d0a, 0x0036104b, 0xffe98fc8, 0x0006fe92, 0xfffeb678, 0xfffffff8, + 0x4b1e8091, 0xe831e563, 0x0b435462, 0xfad4e0b9, 0x01c08e78, 0x0017cbae, 0xff14d073, 0x011bff38, 0xff0a439e, 0x00af1059, 0xff961224, 0x00362e66, 0xffe9554c, 0x00072859, 0xfffea30b, 0x0000056a, + 0x4a16d8e5, 0xe823380d, 0x0b696ceb, 0xfaae8d43, 0x01e08952, 0x00000c33, 0xff24aa2a, 0x011295bb, 0xff0f1762, 0x00ad1346, 0xff969412, 0x003644fd, 0xffe91e99, 0x00075084, 0xfffe9030, 0x00000ab3, + 0x490d6c79, 0xe817b76c, 0x0b8dc9ed, 0xfa89255f, 0x02001593, 0xffe871a0, 0xff348639, 0x0109194f, 0xff140121, 0x00ab03da, 0xff9722a5, 0x00365422, 0xffe8eba8, 0x00077712, 0xfffe7de7, 0x00000fd2, + 0x48024ea7, 0xe80f5bfb, 0x0bb06a47, 0xfa64ac3f, 0x021f2fe5, 0xffd0fec1, 0xff446293, 0x00ff8b4f, 0xff19000e, 0x00a8e282, 0xff97bdac, 0x00365be6, 0xffe8bc77, 0x00079c04, 0xfffe6c2f, 0x000014c8, + 0x46f592e2, 0xe80a1df5, 0x0bd14d0b, 0xfa4124f2, 0x023dd505, 0xffb9b656, 0xff543d2e, 0x00f5ed15, 0xff1e135b, 0x00a6afa8, 0xff9864f6, 0x00365c5b, 0xffe89101, 0x0007bf5b, 0xfffe5b0b, 0x00001994, + 0x45e74cad, 0xe807f55b, 0x0bf07186, 0xfa1e9262, 0x025c01c5, 0xffa29b18, 0xff641402, 0x00ec3ffc, 0xff233a39, 0x00a46bbc, 0xff991851, 0x00365594, 0xffe8693f, 0x0007e116, 0xfffe4a79, 0x00001e37, + 0x44d78fa0, 0xe808d9f1, 0x0c0dd738, 0xf9fcf758, 0x0279b30b, 0xff8bafb3, 0xff73e50e, 0x00e2855d, 0xff2873d6, 0x00a2172d, 0xff99d789, 0x003647a5, 0xffe8452d, 0x00080137, 0xfffe3a79, 0x000022b1, + 0x43c66f62, 0xe80cc342, 0x0c297dd9, 0xf9dc567b, 0x0296e5d0, 0xff74f6cc, 0xff83ae52, 0x00d8be92, 0xff2dbf61, 0x009fb26c, 0xff9aa268, 0x003632a2, 0xffe824c5, 0x00081fbf, 0xfffe2b0d, 0x00002701, + 0x42b3ffa9, 0xe813a89f, 0x0c436557, 0xf9bcb24a, 0x02b39724, 0xff5e72fb, 0xff936dd2, 0x00ceecf5, 0xff331c08, 0x009d3deb, 0xff9b78ba, 0x003616a2, 0xffe807ff, 0x00083cb0, 0xfffe1c32, 0x00002b28, + 0x41a05437, 0xe81d8122, 0x0c5b8dd4, 0xf99e0d26, 0x02cfc429, 0xff4826cf, 0xffa3219a, 0x00c511dc, 0xff3888f8, 0x009aba1d, 0xff9c5a47, 0x0035f3b9, 0xffe7eed5, 0x0008580a, 0xfffe0dea, 0x00002f26, + 0x408b80d9, 0xe82a43ac, 0x0c71f7a9, 0xf980694a, 0x02eb6a18, 0xff3214c9, 0xffb2c7b6, 0x00bb2e9f, 0xff3e055d, 0x00982778, 0xff9d46d6, 0x0035ca00, 0xffe7d93f, 0x000871cf, 0xfffe0034, 0x000032fb, + 0x3f759967, 0xe839e6e9, 0x0c86a361, 0xf963c8cc, 0x03068640, 0xff1c3f63, 0xffc25e3b, 0x00b14493, 0xff439064, 0x0095866f, 0xff9e3e30, 0x0035998d, 0xffe7c735, 0x00088a02, 0xfffdf310, 0x000036a8, + 0x3e5eb1bd, 0xe84c6152, 0x0c9991be, 0xf9482da0, 0x03211603, 0xff06a907, 0xffd1e340, 0x00a7550c, 0xff492937, 0x0092d77b, 0xff9f4019, 0x00356279, 0xffe7b8af, 0x0008a0a5, 0xfffde67c, 0x00003a2d, + 0x3d46ddc1, 0xe861a92b, 0x0caac3b5, 0xf92d9997, 0x033b16dc, 0xfef15417, 0xffe154e3, 0x009d615d, 0xff4ecf02, 0x00901b11, 0xffa04c57, 0x003524dd, 0xffe7ada5, 0x0008b5ba, 0xfffdda79, 0x00003d89, + 0x3c2e315a, 0xe879b487, 0x0cba3a6d, 0xf9140e5e, 0x03548659, 0xfedc42e7, 0xfff0b148, 0x00936ad6, 0xff5480f0, 0x008d51ab, 0xffa162ae, 0x0034e0d3, 0xffe7a60d, 0x0008c944, 0xfffdcf05, 0x000040be, + 0x3b14c072, 0xe8947947, 0x0cc7f742, 0xf8fb8d7d, 0x036d621f, 0xfec777be, 0xfffff697, 0x008972c7, 0xff5a3e2c, 0x008a7bc1, 0xffa282e1, 0x00349674, 0xffe7a1de, 0x0008db46, 0xfffdc421, 0x000043cc, + 0x39fa9ef3, 0xe8b1ed1c, 0x0cd3fbc0, 0xf8e4185a, 0x0385a7eb, 0xfeb2f4d9, 0x000f22fe, 0x007f7a7c, 0xff6005e1, 0x008799cd, 0xffa3acb4, 0x003445dc, 0xffe7a10d, 0x0008ebc1, 0xfffdb9cb, 0x000046b2, + 0x38dfe0c6, 0xe8d2058b, 0x0cde49a8, 0xf8cdb036, 0x039d558e, 0xfe9ebc66, 0x001e34b4, 0x00758341, 0xff65d73a, 0x0084ac48, 0xffa4dfe8, 0x0033ef25, 0xffe7a391, 0x0008fabb, 0xfffdb002, 0x00004972, + 0x37c499d0, 0xe8f4b7e9, 0x0ce6e2ea, 0xf8b85631, 0x03b468f1, 0xfe8ad087, 0x002d29f3, 0x006b8e5c, 0xff6bb163, 0x0081b3af, 0xffa61c3e, 0x0033926d, 0xffe7a95f, 0x00090836, 0xfffda6c5, 0x00004c0b, + 0x36a8ddf3, 0xe919f961, 0x0cedc9a7, 0xf8a40b44, 0x03cae014, 0xfe773351, 0x003c00fd, 0x00619d15, 0xff719388, 0x007eb07b, 0xffa76176, 0x00332fcf, 0xffe7b26c, 0x00091435, 0xfffd9e13, 0x00004e7f, + 0x358cc109, 0xe941bef3, 0x0cf30031, 0xf890d048, 0x03e0b90d, 0xfe63e6cb, 0x004ab81b, 0x0057b0ae, 0xff777cd6, 0x007ba32a, 0xffa8af51, 0x0032c769, 0xffe7bead, 0x00091ebd, 0xfffd95eb, 0x000050cd, + 0x347056e3, 0xe96bfd76, 0x0cf6890a, 0xf87ea5f1, 0x03f5f20a, 0xfe50ecf0, 0x00594d9d, 0x004dca68, 0xff7d6c79, 0x00788c36, 0xffaa058d, 0x00325958, 0xffe7ce16, 0x000927d1, 0xfffd8e4d, 0x000052f7, + 0x3353b349, 0xe998a999, 0x0cf866e1, 0xf86d8cd1, 0x040a894e, 0xfe3e47ac, 0x0067bfd8, 0x0043eb7f, 0xff83619f, 0x00756c1d, 0xffab63ea, 0x0031e5ba, 0xffe7e09c, 0x00092f75, 0xfffd8735, 0x000054fc, + 0x3236e9f7, 0xe9c7b7e3, 0x0cf89c96, 0xf85d8555, 0x041e7d34, 0xfe2bf8de, 0x00760d2a, 0x003a152f, 0xff895b77, 0x0072435b, 0xffacca25, 0x00316cae, 0xffe7f631, 0x000935ad, 0xfffd80a4, 0x000056dd, + 0x311a0e9b, 0xe9f91cb9, 0x0cf72d34, 0xf84e8fc9, 0x0431cc31, 0xfe1a0256, 0x008433f9, 0x003048ae, 0xff8f5930, 0x006f126b, 0xffae37fd, 0x0030ee53, 0xffe80eca, 0x00093a7f, 0xfffd7a98, 0x0000589b, + 0x2ffd34d4, 0xea2ccc59, 0x0cf41bf7, 0xf840ac57, 0x044474ce, 0xfe0865d7, 0x009232b2, 0x0026872f, 0xff9559fb, 0x006bd9cd, 0xffafad2e, 0x00306ac8, 0xffe82a59, 0x00093ded, 0xfffd750f, 0x00005a36, + 0x2ee07030, 0xea62bae0, 0x0cef6c43, 0xf833db04, 0x045675ab, 0xfdf72515, 0x00a007c9, 0x001cd1e4, 0xff9b5d0a, 0x006899fb, 0xffb12976, 0x002fe22c, 0xffe848d3, 0x00093ffe, 0xfffd7008, 0x00005baf, + 0x2dc3d429, 0xea9adc49, 0x0ce921ab, 0xf8281bb6, 0x0467cd83, 0xfde641b7, 0x00adb1bb, 0x001329f7, 0xffa16190, 0x00655372, 0xffb2ac90, 0x002f54a1, 0xffe86a29, 0x000940b6, 0xfffd6b81, 0x00005d06, + 0x2ca77428, 0xead52471, 0x0ce13feb, 0xf81d6e2e, 0x04787b24, 0xfdd5bd53, 0x00bb2f0b, 0x00099093, 0xffa766c0, 0x006206b1, 0xffb4363a, 0x002ec246, 0xffe88e4d, 0x00094019, 0xfffd6779, 0x00005e3d, + 0x2b8b637b, 0xeb118714, 0x0cd7caec, 0xf813d20d, 0x04887d76, 0xfdc59972, 0x00c87e47, 0x000006db, 0xffad6bd0, 0x005eb431, 0xffb5c630, 0x002e2b3c, 0xffe8b532, 0x00093e2e, 0xfffd63ed, 0x00005f52, + 0x2a6fb55e, 0xeb4ff7d4, 0x0cccc6bc, 0xf80b46d3, 0x0497d378, 0xfdb5d78f, 0x00d59e03, 0xfff68df1, 0xffb36ff9, 0x005b5c71, 0xffb75c2c, 0x002d8fa4, 0xffe8decb, 0x00093af8, 0xfffd60dd, 0x00006048, + 0x29547ced, 0xeb906a35, 0x0cc03797, 0xf803cbdc, 0x04a67c41, 0xfda67913, 0x00e28cdd, 0xffed26f0, 0xffb97271, 0x0057ffec, 0xffb8f7ea, 0x002cefa1, 0xffe90b08, 0x0009367e, 0xfffd5e46, 0x0000611f, + 0x2839cd30, 0xebd2d1a1, 0x0cb221de, 0xf7fd6065, 0x04b476fe, 0xfd977f5d, 0x00ef497a, 0xffe3d2f2, 0xffbf7274, 0x00549f1c, 0xffba9927, 0x002c4b53, 0xffe939db, 0x000930c4, 0xfffd5c26, 0x000061d8, + 0x271fb90d, 0xec17216b, 0x0ca28a1a, 0xf7f8038c, 0x04c1c2f3, 0xfd88ebb9, 0x00fbd28a, 0xffda930a, 0xffc56f3e, 0x00513a7e, 0xffbc3f9d, 0x002ba2dc, 0xffe96b35, 0x000929d1, 0xfffd5a7c, 0x00006272, + 0x2606534e, 0xec5d4ccd, 0x0c9174fa, 0xf7f3b44b, 0x04ce5f7d, 0xfd7abf64, 0x010826c4, 0xffd16848, 0xffcb680e, 0x004dd28c, 0xffbdeb07, 0x002af65f, 0xffe99f08, 0x000921aa, 0xfffd5945, 0x000062f0, + 0x24edae9c, 0xeca546eb, 0x0c7ee754, 0xf7f0717e, 0x04da4c10, 0xfd6cfb8e, 0x011444e7, 0xffc853b6, 0xffd15c22, 0x004a67c0, 0xffbf9b21, 0x002a45fe, 0xffe9d545, 0x00091854, 0xfffd5880, 0x00006351, + 0x23d5dd81, 0xecef02d5, 0x0c6ae622, 0xf7ee39e2, 0x04e58836, 0xfd5fa157, 0x01202bbe, 0xffbf565a, 0xffd74abe, 0x0046fa93, 0xffc14fa5, 0x002991db, 0xffea0ddc, 0x00090dd6, 0xfffd582a, 0x00006396, + 0x22bef262, 0xed3a7388, 0x0c557681, 0xf7ed0c12, 0x04f01392, 0xfd52b1cf, 0x012bda1b, 0xffb67137, 0xffdd3325, 0x00438b7e, 0xffc3084f, 0x0028da1a, 0xffea48be, 0x00090236, 0xfffd5842, 0x000063c0, + 0x21a8ff7e, 0xed878bf0, 0x0c3e9db5, 0xf7ece68c, 0x04f9edda, 0xfd462df6, 0x01374eda, 0xffada547, 0xffe3149e, 0x00401af9, 0xffc4c4da, 0x00281edd, 0xffea85dc, 0x0008f57a, 0xfffd58c5, 0x000063d0, + 0x209416f2, 0xedd63ee5, 0x0c26611f, 0xf7edc7af, 0x050316e0, 0xfd3a16c0, 0x014288e0, 0xffa4f383, 0xffe8ee72, 0x003ca97b, 0xffc68502, 0x00276046, 0xffeac525, 0x0008e7a7, 0xfffd59b2, 0x000063c6, + 0x1f804ab0, 0xee267f35, 0x0c0cc646, 0xf7efadbd, 0x050b8e8a, 0xfd2e6d0d, 0x014d871b, 0xff9c5cdc, 0xffeebfec, 0x0039377a, 0xffc84881, 0x00269e7a, 0xffeb068a, 0x0008d8c4, 0xfffd5b05, 0x000063a3, + 0x1e6dac83, 0xee783f9e, 0x0bf1d2d0, 0xf7f296d7, 0x051354d5, 0xfd2331b0, 0x01584883, 0xff93e241, 0xfff48859, 0x0035c56c, 0xffca0f14, 0x0025d99b, 0xffeb49fc, 0x0008c8d7, 0xfffd5cbe, 0x00006368, + 0x1d5c4e09, 0xeecb72d1, 0x0bd58c81, 0xf7f68103, 0x051a69d4, 0xfd18656f, 0x0162cc19, 0xff8b8498, 0xfffa470a, 0x003253c6, 0xffcbd876, 0x002511cd, 0xffeb8f6a, 0x0008b7e7, 0xfffd5ed8, 0x00006316, + 0x1c4c40b6, 0xef200b76, 0x0bb7f940, 0xf7fb6a29, 0x0520cdb1, 0xfd0e08fb, 0x016d10e9, 0xff8344c4, 0xfffffb51, 0x002ee2fa, 0xffcda463, 0x00244733, 0xffebd6c4, 0x0008a5fa, 0xfffd6154, 0x000062ad, + 0x1b3d95d1, 0xef75fc2b, 0x0b991f0f, 0xf8015015, 0x052680ae, 0xfd041cfa, 0x01771608, 0xff7b23a1, 0x0005a483, 0x002b737b, 0xffcf7299, 0x002379ef, 0xffec1ffa, 0x00089316, 0xfffd642d, 0x0000622e, + 0x1a305e70, 0xefcd3787, 0x0b79040c, 0xf8083077, 0x052b8320, 0xfcfaa200, 0x0180da94, 0xff732209, 0x000b41fa, 0x002805ba, 0xffd142d3, 0x0022aa26, 0xffec6afc, 0x00087f43, 0xfffd6762, 0x0000619a, + 0x1924ab7b, 0xf025b01a, 0x0b57ae75, 0xf81008e2, 0x052fd573, 0xfcf19894, 0x018a5db5, 0xff6b40cb, 0x0010d30e, 0x00249a28, 0xffd314cf, 0x0021d7fa, 0xffecb7b9, 0x00086a86, 0xfffd6af1, 0x000060f1, + 0x181a8da5, 0xf07f586e, 0x0b3524a0, 0xf818d6cf, 0x0533782a, 0xfce9012c, 0x01939e9e, 0xff6380b5, 0x00165720, 0x00213134, 0xffd4e84a, 0x00210390, 0xffed0621, 0x000854e6, 0xfffd6ed6, 0x00006035, + 0x17121573, 0xf0da230b, 0x0b116cff, 0xf822979b, 0x05366bdc, 0xfce0dc2f, 0x019c9c8b, 0xff5be28d, 0x001bcd8e, 0x001dcb4a, 0xffd6bd01, 0x00202d09, 0xffed5624, 0x00083e6a, 0xfffd7310, 0x00005f66, + 0x160b5331, 0xf1360276, 0x0aec8e1c, 0xf82d488c, 0x0538b136, 0xfcd929f4, 0x01a556c1, 0xff546713, 0x002135bd, 0x001a68d8, 0xffd892b4, 0x001f5489, 0xffeda7b1, 0x00082718, 0xfffd779d, 0x00005e84, + 0x150656f8, 0xf192e932, 0x0ac68e9b, 0xf838e6c9, 0x053a48fa, 0xfcd1eac3, 0x01adcc91, 0xff4d0f02, 0x00268f13, 0x00170a47, 0xffda6921, 0x001e7a33, 0xffedfab8, 0x00080ef7, 0xfffd7c7a, 0x00005d92, + 0x140330a9, 0xf1f0c9c5, 0x0a9f7537, 0xf8456f65, 0x053b3400, 0xfccb1ed7, 0x01b5fd54, 0xff45db10, 0x002bd8fa, 0x0013b003, 0xffdc4007, 0x001d9e2a, 0xffee4f29, 0x0007f60f, 0xfffd81a4, 0x00005c8e, + 0x1301efed, 0xf24f96b5, 0x0a7748c0, 0xf852df56, 0x053b7332, 0xfcc4c658, 0x01bde86f, 0xff3ecbea, 0x003112e0, 0x00105a72, 0xffde1726, 0x001cc091, 0xffeea4f2, 0x0007dc65, 0xfffd8719, 0x00005b7b, + 0x1202a434, 0xf2af428c, 0x0a4e101f, 0xf861337c, 0x053b0791, 0xfcbee162, 0x01c58d50, 0xff37e23b, 0x00363c35, 0x000d09fc, 0xffdfee3f, 0x001be18a, 0xffeefc04, 0x0007c201, 0xfffd8cd7, 0x00005a58, + 0x11055cb4, 0xf30fbfd7, 0x0a23d24e, 0xf870689f, 0x0539f231, 0xfcb97001, 0x01cceb6e, 0xff311ea4, 0x003b546b, 0x0009bf05, 0xffe1c511, 0x001b0138, 0xffef544e, 0x0007a6e9, 0xfffd92db, 0x00005927, + 0x100a2864, 0xf371012c, 0x09f8965d, 0xf8807b70, 0x0538343a, 0xfcb47232, 0x01d4024c, 0xff2a81c4, 0x00405afa, 0x000679f2, 0xffe39b60, 0x001a1fbc, 0xffefadc0, 0x00078b24, 0xfffd9923, 0x000057e9, + 0x0f111603, 0xf3d2f926, 0x09cc636e, 0xf8916889, 0x0535cee9, 0xfcafe7e2, 0x01dad175, 0xff240c2f, 0x00454f5d, 0x00033b23, 0xffe570ed, 0x00193d3a, 0xfff00849, 0x00076eba, 0xfffd9fac, 0x0000569d, + 0x0e1a340d, 0xf4359a6a, 0x099f40b5, 0xf8a32c6e, 0x0532c38c, 0xfcabd0f2, 0x01e15880, 0xff1dbe77, 0x004a310f, 0x000002f9, 0xffe7457c, 0x001859d2, 0xfff063d9, 0x000751b0, 0xfffda675, 0x00005545, + 0x0d2590c3, 0xf498d7a5, 0x09713575, 0xf8b5c38d, 0x052f1386, 0xfca82d32, 0x01e7970e, 0xff179926, 0x004eff94, 0xfffcd1d3, 0xffe918ce, 0x001775a7, 0xfff0c060, 0x0007340d, 0xfffdad79, 0x000053e2, + 0x0c333a22, 0xf4fca390, 0x09424904, 0xf8c92a41, 0x052ac04c, 0xfca4fc64, 0x01ed8cc7, 0xff119cc0, 0x0053ba6e, 0xfff9a80d, 0xffeaeaab, 0x001690d9, 0xfff11dcd, 0x000715d9, 0xfffdb4b9, 0x00005274, +}; +} diff --git a/services/audioflinger/roundup.c b/services/audioflinger/roundup.c deleted file mode 100644 index 4f9af6aa..00000000 --- a/services/audioflinger/roundup.c +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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. - */ - -#include "roundup.h" - -unsigned roundup(unsigned v) -{ - // __builtin_clz is undefined for zero input - if (v == 0) { - v = 1; - } - int lz = __builtin_clz((int) v); - unsigned rounded = ((unsigned) 0x80000000) >> lz; - // 0x800000001 and higher are actually rounded _down_ to prevent overflow - if (v > rounded && lz > 0) { - rounded <<= 1; - } - return rounded; -} diff --git a/services/audioflinger/test-resample.cpp b/services/audioflinger/test-resample.cpp new file mode 100644 index 00000000..7a314cfa --- /dev/null +++ b/services/audioflinger/test-resample.cpp @@ -0,0 +1,274 @@ +/* + * 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. + */ + +#include "AudioResampler.h" +#include <media/AudioBufferProvider.h> +#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> + +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 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," -p enable profiling\n"); + fprintf(stderr," -h create wav file\n"); + fprintf(stderr," -s stereo\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," -o output file sample rate\n"); + return -1; +} + +int main(int argc, char* argv[]) { + + const char* const progname = argv[0]; + bool profiling = false; + bool writeHeader = false; + int channels = 1; + int input_freq = 0; + int output_freq = 0; + AudioResampler::src_quality quality = AudioResampler::DEFAULT_QUALITY; + + int ch; + while ((ch = getopt(argc, argv, "phsq:i:o:")) != -1) { + switch (ch) { + case 'p': + profiling = true; + break; + case 'h': + writeHeader = true; + break; + case 's': + channels = 2; + break; + case 'q': + if (!strcmp(optarg, "dq")) + quality = AudioResampler::DEFAULT_QUALITY; + else if (!strcmp(optarg, "lq")) + quality = AudioResampler::LOW_QUALITY; + else if (!strcmp(optarg, "mq")) + quality = AudioResampler::MED_QUALITY; + else if (!strcmp(optarg, "hq")) + quality = AudioResampler::HIGH_QUALITY; + else if (!strcmp(optarg, "vhq")) + quality = AudioResampler::VERY_HIGH_QUALITY; + else { + usage(progname); + return -1; + } + break; + case 'i': + input_freq = atoi(optarg); + break; + case 'o': + output_freq = atoi(optarg); + break; + case '?': + default: + usage(progname); + return -1; + } + } + argc -= optind; + argv += optind; + + const char* file_in = NULL; + const char* file_out = NULL; + if (argc == 1) { + file_out = argv[0]; + } else if (argc == 2) { + file_in = argv[0]; + file_out = argv[1]; + } else { + usage(progname); + return -1; + } + + // ---------------------------------------------------------- + + 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; + } + } else { + double k = 1000; // Hz / s + double time = (input_freq / 2) / k; + size_t input_frames = size_t(input_freq * time); + input_size = channels * sizeof(int16_t) * input_frames; + input_vaddr = malloc(input_size); + int16_t* in = (int16_t*)input_vaddr; + for (size_t i=0 ; i<input_frames ; i++) { + 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); + } + } + } + + // ---------------------------------------------------------- + + class Provider: public AudioBufferProvider { + int16_t* mAddr; + size_t mNumFrames; + public: + Provider(const void* addr, size_t size, int channels) { + mAddr = (int16_t*) addr; + mNumFrames = size / (channels*sizeof(int16_t)); + } + virtual status_t getNextBuffer(Buffer* buffer, + int64_t pts = kInvalidPTS) { + buffer->frameCount = mNumFrames; + buffer->i16 = mAddr; + return NO_ERROR; + } + virtual void releaseBuffer(Buffer* buffer) { + } + } 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); + + if (profiling) { + AudioResampler* resampler = AudioResampler::create(16, channels, + output_freq, quality); + + size_t out_frames = output_size/8; + resampler->setSampleRate(input_freq); + resampler->setVolume(0x1000, 0x1000); + + memset(output_vaddr, 0, output_size); + 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); + 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); + + delete resampler; + } + + AudioResampler* resampler = AudioResampler::create(16, channels, + output_freq, quality); + size_t out_frames = output_size/8; + resampler->setSampleRate(input_freq); + resampler->setVolume(0x1000, 0x1000); + + memset(output_vaddr, 0, output_size); + resampler->resample((int*) output_vaddr, out_frames, &provider); + + // down-mix (we just truncate and keep the left channel) + 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; + 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; + } + + 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; +} diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk index 3cae1f52..eff47c84 100644 --- a/services/camera/libcameraservice/Android.mk +++ b/services/camera/libcameraservice/Android.mk @@ -7,7 +7,20 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - CameraService.cpp + CameraService.cpp \ + CameraClient.cpp \ + Camera2Client.cpp \ + Camera2Device.cpp \ + camera2/CameraMetadata.cpp \ + camera2/Parameters.cpp \ + camera2/FrameProcessor.cpp \ + camera2/StreamingProcessor.cpp \ + camera2/JpegProcessor.cpp \ + camera2/CallbackProcessor.cpp \ + camera2/ZslProcessor.cpp \ + camera2/BurstCapture.cpp \ + camera2/JpegCompressor.cpp \ + camera2/CaptureSequencer.cpp LOCAL_SHARED_LIBRARIES:= \ libui \ @@ -18,7 +31,14 @@ LOCAL_SHARED_LIBRARIES:= \ libmedia_native \ libcamera_client \ libgui \ - libhardware + libhardware \ + libsync \ + libcamera_metadata \ + libjpeg + +LOCAL_C_INCLUDES += \ + system/media/camera/include \ + external/jpeg LOCAL_MODULE:= libcameraservice diff --git a/services/camera/libcameraservice/Camera2Client.cpp b/services/camera/libcameraservice/Camera2Client.cpp new file mode 100644 index 00000000..30335773 --- /dev/null +++ b/services/camera/libcameraservice/Camera2Client.cpp @@ -0,0 +1,1636 @@ +/* + * 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. + */ + +#define LOG_TAG "Camera2" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/Trace.h> + +#include <cutils/properties.h> +#include <gui/SurfaceTextureClient.h> +#include <gui/Surface.h> +#include "camera2/Parameters.h" +#include "Camera2Client.h" + +#define ALOG1(...) ALOGD_IF(gLogLevel >= 1, __VA_ARGS__); +#define ALOG2(...) ALOGD_IF(gLogLevel >= 2, __VA_ARGS__); + +namespace android { +using namespace camera2; + +static int getCallingPid() { + return IPCThreadState::self()->getCallingPid(); +} + +static int getCallingUid() { + return IPCThreadState::self()->getCallingUid(); +} + +// Interface used by CameraService + +Camera2Client::Camera2Client(const sp<CameraService>& cameraService, + const sp<ICameraClient>& cameraClient, + int cameraId, + int cameraFacing, + int clientPid, + int servicePid): + Client(cameraService, cameraClient, + cameraId, cameraFacing, clientPid, servicePid), + mSharedCameraClient(cameraClient), + mParameters(cameraId, cameraFacing) +{ + ATRACE_CALL(); + ALOGI("Camera %d: Opened", cameraId); + + mDevice = new Camera2Device(cameraId); + + SharedParameters::Lock l(mParameters); + l.mParameters.state = Parameters::DISCONNECTED; +} + +status_t Camera2Client::checkPid(const char* checkLocation) const { + int callingPid = getCallingPid(); + if (callingPid == mClientPid) return NO_ERROR; + + ALOGE("%s: attempt to use a locked camera from a different process" + " (old pid %d, new pid %d)", checkLocation, mClientPid, callingPid); + return PERMISSION_DENIED; +} + +status_t Camera2Client::initialize(camera_module_t *module) +{ + ATRACE_CALL(); + ALOGV("%s: Initializing client for camera %d", __FUNCTION__, mCameraId); + status_t res; + + res = mDevice->initialize(module); + if (res != OK) { + ALOGE("%s: Camera %d: unable to initialize device: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return NO_INIT; + } + + res = mDevice->setNotifyCallback(this); + + SharedParameters::Lock l(mParameters); + + res = l.mParameters.initialize(&(mDevice->info())); + if (res != OK) { + ALOGE("%s: Camera %d: unable to build defaults: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return NO_INIT; + } + + String8 threadName; + + mStreamingProcessor = new StreamingProcessor(this); + + mFrameProcessor = new FrameProcessor(this); + threadName = String8::format("C2-%d-FrameProc", + mCameraId); + mFrameProcessor->run(threadName.string()); + + mCaptureSequencer = new CaptureSequencer(this); + threadName = String8::format("C2-%d-CaptureSeq", + mCameraId); + mCaptureSequencer->run(threadName.string()); + + mJpegProcessor = new JpegProcessor(this, mCaptureSequencer); + threadName = String8::format("C2-%d-JpegProc", + mCameraId); + mJpegProcessor->run(threadName.string()); + + mZslProcessor = new ZslProcessor(this, mCaptureSequencer); + threadName = String8::format("C2-%d-ZslProc", + mCameraId); + mZslProcessor->run(threadName.string()); + + mCallbackProcessor = new CallbackProcessor(this); + threadName = String8::format("C2-%d-CallbkProc", + mCameraId); + mCallbackProcessor->run(threadName.string()); + + if (gLogLevel >= 1) { + ALOGD("%s: Default parameters converted from camera %d:", __FUNCTION__, + mCameraId); + ALOGD("%s", l.mParameters.paramsFlattened.string()); + } + + return OK; +} + +Camera2Client::~Camera2Client() { + ATRACE_CALL(); + + mDestructionStarted = true; + + disconnect(); + + ALOGI("Camera %d: Closed", mCameraId); +} + +status_t Camera2Client::dump(int fd, const Vector<String16>& args) { + String8 result; + result.appendFormat("Client2[%d] (%p) PID: %d, dump:\n", + mCameraId, + getCameraClient()->asBinder().get(), + mClientPid); + result.append(" State: "); +#define CASE_APPEND_ENUM(x) case x: result.append(#x "\n"); break; + + const Parameters& p = mParameters.unsafeAccess(); + + result.append(Parameters::getStateName(p.state)); + + result.append("\n Current parameters:\n"); + result.appendFormat(" Preview size: %d x %d\n", + p.previewWidth, p.previewHeight); + result.appendFormat(" Preview FPS range: %d - %d\n", + p.previewFpsRange[0], p.previewFpsRange[1]); + result.appendFormat(" Preview HAL pixel format: 0x%x\n", + p.previewFormat); + result.appendFormat(" Preview transform: %x\n", + p.previewTransform); + result.appendFormat(" Picture size: %d x %d\n", + p.pictureWidth, p.pictureHeight); + result.appendFormat(" Jpeg thumbnail size: %d x %d\n", + p.jpegThumbSize[0], p.jpegThumbSize[1]); + result.appendFormat(" Jpeg quality: %d, thumbnail quality: %d\n", + p.jpegQuality, p.jpegThumbQuality); + result.appendFormat(" Jpeg rotation: %d\n", p.jpegRotation); + result.appendFormat(" GPS tags %s\n", + p.gpsEnabled ? "enabled" : "disabled"); + if (p.gpsEnabled) { + result.appendFormat(" GPS lat x long x alt: %f x %f x %f\n", + p.gpsCoordinates[0], p.gpsCoordinates[1], + p.gpsCoordinates[2]); + result.appendFormat(" GPS timestamp: %lld\n", + p.gpsTimestamp); + result.appendFormat(" GPS processing method: %s\n", + p.gpsProcessingMethod.string()); + } + + result.append(" White balance mode: "); + switch (p.wbMode) { + CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_MODE_AUTO) + CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_MODE_INCANDESCENT) + CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_MODE_FLUORESCENT) + CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_MODE_WARM_FLUORESCENT) + CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_MODE_DAYLIGHT) + CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_MODE_CLOUDY_DAYLIGHT) + CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_MODE_TWILIGHT) + CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_MODE_SHADE) + default: result.append("UNKNOWN\n"); + } + + result.append(" Effect mode: "); + switch (p.effectMode) { + CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_MODE_OFF) + CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_MODE_MONO) + CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_MODE_NEGATIVE) + CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_MODE_SOLARIZE) + CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_MODE_SEPIA) + CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_MODE_POSTERIZE) + CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_MODE_WHITEBOARD) + CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_MODE_BLACKBOARD) + CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_MODE_AQUA) + default: result.append("UNKNOWN\n"); + } + + result.append(" Antibanding mode: "); + switch (p.antibandingMode) { + CASE_APPEND_ENUM(ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO) + CASE_APPEND_ENUM(ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF) + CASE_APPEND_ENUM(ANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ) + CASE_APPEND_ENUM(ANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ) + default: result.append("UNKNOWN\n"); + } + + result.append(" Scene mode: "); + switch (p.sceneMode) { + case ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED: + result.append("AUTO\n"); break; + CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_ACTION) + CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_PORTRAIT) + CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_LANDSCAPE) + CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_NIGHT) + CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_NIGHT_PORTRAIT) + CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_THEATRE) + CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_BEACH) + CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_SNOW) + CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_SUNSET) + CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_STEADYPHOTO) + CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_FIREWORKS) + CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_SPORTS) + CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_PARTY) + CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_CANDLELIGHT) + CASE_APPEND_ENUM(ANDROID_CONTROL_SCENE_MODE_BARCODE) + default: result.append("UNKNOWN\n"); + } + + result.append(" Flash mode: "); + switch (p.flashMode) { + CASE_APPEND_ENUM(Parameters::FLASH_MODE_OFF) + CASE_APPEND_ENUM(Parameters::FLASH_MODE_AUTO) + CASE_APPEND_ENUM(Parameters::FLASH_MODE_ON) + CASE_APPEND_ENUM(Parameters::FLASH_MODE_TORCH) + CASE_APPEND_ENUM(Parameters::FLASH_MODE_RED_EYE) + CASE_APPEND_ENUM(Parameters::FLASH_MODE_INVALID) + default: result.append("UNKNOWN\n"); + } + + result.append(" Focus mode: "); + switch (p.focusMode) { + CASE_APPEND_ENUM(Parameters::FOCUS_MODE_AUTO) + CASE_APPEND_ENUM(Parameters::FOCUS_MODE_MACRO) + CASE_APPEND_ENUM(Parameters::FOCUS_MODE_CONTINUOUS_VIDEO) + CASE_APPEND_ENUM(Parameters::FOCUS_MODE_CONTINUOUS_PICTURE) + CASE_APPEND_ENUM(Parameters::FOCUS_MODE_EDOF) + CASE_APPEND_ENUM(Parameters::FOCUS_MODE_INFINITY) + CASE_APPEND_ENUM(Parameters::FOCUS_MODE_FIXED) + CASE_APPEND_ENUM(Parameters::FOCUS_MODE_INVALID) + default: result.append("UNKNOWN\n"); + } + + result.append(" Focus state: "); + switch (p.focusState) { + CASE_APPEND_ENUM(ANDROID_CONTROL_AF_STATE_INACTIVE) + CASE_APPEND_ENUM(ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN) + CASE_APPEND_ENUM(ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED) + CASE_APPEND_ENUM(ANDROID_CONTROL_AF_STATE_ACTIVE_SCAN) + CASE_APPEND_ENUM(ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED) + CASE_APPEND_ENUM(ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) + default: result.append("UNKNOWN\n"); + } + + result.append(" Focusing areas:\n"); + for (size_t i = 0; i < p.focusingAreas.size(); i++) { + result.appendFormat(" [ (%d, %d, %d, %d), weight %d ]\n", + p.focusingAreas[i].left, + p.focusingAreas[i].top, + p.focusingAreas[i].right, + p.focusingAreas[i].bottom, + p.focusingAreas[i].weight); + } + + result.appendFormat(" Exposure compensation index: %d\n", + p.exposureCompensation); + + result.appendFormat(" AE lock %s, AWB lock %s\n", + p.autoExposureLock ? "enabled" : "disabled", + p.autoWhiteBalanceLock ? "enabled" : "disabled" ); + + result.appendFormat(" Metering areas:\n"); + for (size_t i = 0; i < p.meteringAreas.size(); i++) { + result.appendFormat(" [ (%d, %d, %d, %d), weight %d ]\n", + p.meteringAreas[i].left, + p.meteringAreas[i].top, + p.meteringAreas[i].right, + p.meteringAreas[i].bottom, + p.meteringAreas[i].weight); + } + + result.appendFormat(" Zoom index: %d\n", p.zoom); + result.appendFormat(" Video size: %d x %d\n", p.videoWidth, + p.videoHeight); + + result.appendFormat(" Recording hint is %s\n", + p.recordingHint ? "set" : "not set"); + + result.appendFormat(" Video stabilization is %s\n", + p.videoStabilization ? "enabled" : "disabled"); + + result.append(" Current streams:\n"); + result.appendFormat(" Preview stream ID: %d\n", + getPreviewStreamId()); + result.appendFormat(" Capture stream ID: %d\n", + getCaptureStreamId()); + result.appendFormat(" Recording stream ID: %d\n", + getRecordingStreamId()); + + result.append(" Quirks for this camera:\n"); + bool haveQuirk = false; + if (p.quirks.triggerAfWithAuto) { + result.appendFormat(" triggerAfWithAuto\n"); + haveQuirk = true; + } + if (p.quirks.useZslFormat) { + result.appendFormat(" useZslFormat\n"); + haveQuirk = true; + } + if (p.quirks.meteringCropRegion) { + result.appendFormat(" meteringCropRegion\n"); + haveQuirk = true; + } + if (!haveQuirk) { + result.appendFormat(" none\n"); + } + + write(fd, result.string(), result.size()); + + mStreamingProcessor->dump(fd, args); + + mCaptureSequencer->dump(fd, args); + + mFrameProcessor->dump(fd, args); + + mZslProcessor->dump(fd, args); + + result = " Device dump:\n"; + write(fd, result.string(), result.size()); + + status_t res = mDevice->dump(fd, args); + if (res != OK) { + result = String8::format(" Error dumping device: %s (%d)", + strerror(-res), res); + write(fd, result.string(), result.size()); + } + +#undef CASE_APPEND_ENUM + return NO_ERROR; +} + +// ICamera interface + +void Camera2Client::disconnect() { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + status_t res; + + // Allow both client and the media server to disconnect at all times + int callingPid = getCallingPid(); + if (callingPid != mClientPid && callingPid != mServicePid) return; + + if (mDevice == 0) return; + + ALOGV("Camera %d: Shutting down", mCameraId); + + stopPreviewL(); + + { + SharedParameters::Lock l(mParameters); + if (l.mParameters.state == Parameters::DISCONNECTED) return; + l.mParameters.state = Parameters::DISCONNECTED; + } + + mStreamingProcessor->deletePreviewStream(); + mStreamingProcessor->deleteRecordingStream(); + mJpegProcessor->deleteStream(); + mCallbackProcessor->deleteStream(); + mZslProcessor->deleteStream(); + + mFrameProcessor->requestExit(); + mCaptureSequencer->requestExit(); + mJpegProcessor->requestExit(); + mZslProcessor->requestExit(); + mCallbackProcessor->requestExit(); + + ALOGV("Camera %d: Waiting for threads", mCameraId); + + mFrameProcessor->join(); + mCaptureSequencer->join(); + mJpegProcessor->join(); + mZslProcessor->join(); + mCallbackProcessor->join(); + + ALOGV("Camera %d: Disconnecting device", mCameraId); + + mDevice->disconnect(); + + mDevice.clear(); + + CameraService::Client::disconnect(); +} + +status_t Camera2Client::connect(const sp<ICameraClient>& client) { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + Mutex::Autolock icl(mICameraLock); + + if (mClientPid != 0 && getCallingPid() != mClientPid) { + ALOGE("%s: Camera %d: Connection attempt from pid %d; " + "current locked to pid %d", __FUNCTION__, + mCameraId, getCallingPid(), mClientPid); + return BAD_VALUE; + } + + mClientPid = getCallingPid(); + + mCameraClient = client; + mSharedCameraClient = client; + + return OK; +} + +status_t Camera2Client::lock() { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + Mutex::Autolock icl(mICameraLock); + ALOGV("%s: Camera %d: Lock call from pid %d; current client pid %d", + __FUNCTION__, mCameraId, getCallingPid(), mClientPid); + + if (mClientPid == 0) { + mClientPid = getCallingPid(); + return OK; + } + + if (mClientPid != getCallingPid()) { + ALOGE("%s: Camera %d: Lock call from pid %d; currently locked to pid %d", + __FUNCTION__, mCameraId, getCallingPid(), mClientPid); + return EBUSY; + } + + return OK; +} + +status_t Camera2Client::unlock() { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + Mutex::Autolock icl(mICameraLock); + ALOGV("%s: Camera %d: Unlock call from pid %d; current client pid %d", + __FUNCTION__, mCameraId, getCallingPid(), mClientPid); + + if (mClientPid == getCallingPid()) { + SharedParameters::Lock l(mParameters); + if (l.mParameters.state == Parameters::RECORD || + l.mParameters.state == Parameters::VIDEO_SNAPSHOT) { + ALOGD("Not allowed to unlock camera during recording."); + return INVALID_OPERATION; + } + mClientPid = 0; + mCameraClient.clear(); + mSharedCameraClient.clear(); + return OK; + } + + ALOGE("%s: Camera %d: Unlock call from pid %d; currently locked to pid %d", + __FUNCTION__, mCameraId, getCallingPid(), mClientPid); + return EBUSY; +} + +status_t Camera2Client::setPreviewDisplay( + const sp<Surface>& surface) { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + Mutex::Autolock icl(mICameraLock); + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return res; + + sp<IBinder> binder; + sp<ANativeWindow> window; + if (surface != 0) { + binder = surface->asBinder(); + window = surface; + } + + return setPreviewWindowL(binder,window); +} + +status_t Camera2Client::setPreviewTexture( + const sp<ISurfaceTexture>& surfaceTexture) { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + Mutex::Autolock icl(mICameraLock); + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return res; + + sp<IBinder> binder; + sp<ANativeWindow> window; + if (surfaceTexture != 0) { + binder = surfaceTexture->asBinder(); + window = new SurfaceTextureClient(surfaceTexture); + } + return setPreviewWindowL(binder, window); +} + +status_t Camera2Client::setPreviewWindowL(const sp<IBinder>& binder, + sp<ANativeWindow> window) { + ATRACE_CALL(); + status_t res; + + if (binder == mPreviewSurface) { + ALOGV("%s: Camera %d: New window is same as old window", + __FUNCTION__, mCameraId); + return NO_ERROR; + } + + Parameters::State state; + { + SharedParameters::Lock l(mParameters); + state = l.mParameters.state; + } + switch (state) { + case Parameters::DISCONNECTED: + case Parameters::RECORD: + case Parameters::STILL_CAPTURE: + case Parameters::VIDEO_SNAPSHOT: + ALOGE("%s: Camera %d: Cannot set preview display while in state %s", + __FUNCTION__, mCameraId, + Parameters::getStateName(state)); + return INVALID_OPERATION; + case Parameters::STOPPED: + case Parameters::WAITING_FOR_PREVIEW_WINDOW: + // OK + break; + case Parameters::PREVIEW: + // Already running preview - need to stop and create a new stream + mStreamingProcessor->stopStream(); + state = Parameters::WAITING_FOR_PREVIEW_WINDOW; + break; + } + + mPreviewSurface = binder; + res = mStreamingProcessor->setPreviewWindow(window); + if (res != OK) { + ALOGE("%s: Unable to set new preview window: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + + if (state == Parameters::WAITING_FOR_PREVIEW_WINDOW) { + SharedParameters::Lock l(mParameters); + l.mParameters.state = state; + return startPreviewL(l.mParameters, false); + } + + return OK; +} + +void Camera2Client::setPreviewCallbackFlag(int flag) { + ATRACE_CALL(); + ALOGV("%s: Camera %d: Flag 0x%x", __FUNCTION__, mCameraId, flag); + Mutex::Autolock icl(mICameraLock); + status_t res; + if ( checkPid(__FUNCTION__) != OK) return; + + SharedParameters::Lock l(mParameters); + setPreviewCallbackFlagL(l.mParameters, flag); +} + +void Camera2Client::setPreviewCallbackFlagL(Parameters ¶ms, int flag) { + status_t res = OK; + if (flag & CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) { + ALOGV("%s: setting oneshot", __FUNCTION__); + params.previewCallbackOneShot = true; + } + if (params.previewCallbackFlags != (uint32_t)flag) { + params.previewCallbackFlags = flag; + switch(params.state) { + case Parameters::PREVIEW: + res = startPreviewL(params, true); + break; + case Parameters::RECORD: + case Parameters::VIDEO_SNAPSHOT: + res = startRecordingL(params, true); + break; + default: + break; + } + if (res != OK) { + ALOGE("%s: Camera %d: Unable to refresh request in state %s", + __FUNCTION__, mCameraId, + Parameters::getStateName(params.state)); + } + } + +} + +status_t Camera2Client::startPreview() { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + Mutex::Autolock icl(mICameraLock); + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return res; + SharedParameters::Lock l(mParameters); + return startPreviewL(l.mParameters, false); +} + +status_t Camera2Client::startPreviewL(Parameters ¶ms, bool restart) { + ATRACE_CALL(); + status_t res; + + ALOGV("%s: state == %d, restart = %d", __FUNCTION__, params.state, restart); + + if ( (params.state == Parameters::PREVIEW || + params.state == Parameters::RECORD || + params.state == Parameters::VIDEO_SNAPSHOT) + && !restart) { + // Succeed attempt to re-enter a streaming state + ALOGI("%s: Camera %d: Preview already active, ignoring restart", + __FUNCTION__, mCameraId); + return OK; + } + if (params.state > Parameters::PREVIEW && !restart) { + ALOGE("%s: Can't start preview in state %s", + __FUNCTION__, + Parameters::getStateName(params.state)); + return INVALID_OPERATION; + } + + if (!mStreamingProcessor->haveValidPreviewWindow()) { + params.state = Parameters::WAITING_FOR_PREVIEW_WINDOW; + return OK; + } + params.state = Parameters::STOPPED; + + res = mStreamingProcessor->updatePreviewStream(params); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update preview stream: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + + Vector<uint8_t> outputStreams; + bool callbacksEnabled = params.previewCallbackFlags & + CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK; + if (callbacksEnabled) { + res = mCallbackProcessor->updateStream(params); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update callback stream: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + outputStreams.push(getCallbackStreamId()); + } + if (params.zslMode && !params.recordingHint) { + res = mZslProcessor->updateStream(params); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update ZSL stream: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + outputStreams.push(getZslStreamId()); + } + + outputStreams.push(getPreviewStreamId()); + + if (!params.recordingHint) { + if (!restart) { + res = mStreamingProcessor->updatePreviewRequest(params); + if (res != OK) { + ALOGE("%s: Camera %d: Can't set up preview request: " + "%s (%d)", __FUNCTION__, mCameraId, + strerror(-res), res); + return res; + } + } + res = mStreamingProcessor->startStream(StreamingProcessor::PREVIEW, + outputStreams); + } else { + // With recording hint set, we're going to be operating under the + // assumption that the user will record video. To optimize recording + // startup time, create the necessary output streams for recording and + // video snapshot now if they don't already exist. + res = mJpegProcessor->updateStream(params); + if (res != OK) { + ALOGE("%s: Camera %d: Can't pre-configure still image " + "stream: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + + if (!restart) { + res = mStreamingProcessor->updateRecordingRequest(params); + if (res != OK) { + ALOGE("%s: Camera %d: Can't set up preview request with " + "record hint: %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 streaming preview: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + + params.state = Parameters::PREVIEW; + return OK; +} + +void Camera2Client::stopPreview() { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + Mutex::Autolock icl(mICameraLock); + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return; + stopPreviewL(); +} + +void Camera2Client::stopPreviewL() { + ATRACE_CALL(); + status_t res; + const nsecs_t kStopCaptureTimeout = 3000000000LL; // 3 seconds + Parameters::State state; + { + SharedParameters::Lock l(mParameters); + state = l.mParameters.state; + } + + switch (state) { + case Parameters::DISCONNECTED: + ALOGE("%s: Camera %d: Call before initialized", + __FUNCTION__, mCameraId); + break; + case Parameters::STOPPED: + case Parameters::VIDEO_SNAPSHOT: + case Parameters::STILL_CAPTURE: + mCaptureSequencer->waitUntilIdle(kStopCaptureTimeout); + // no break + case Parameters::RECORD: + case Parameters::PREVIEW: + mStreamingProcessor->stopStream(); + res = mDevice->waitUntilDrained(); + if (res != OK) { + ALOGE("%s: Camera %d: Waiting to stop streaming failed: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + } + // no break + case Parameters::WAITING_FOR_PREVIEW_WINDOW: { + SharedParameters::Lock l(mParameters); + l.mParameters.state = Parameters::STOPPED; + commandStopFaceDetectionL(l.mParameters); + break; + } + default: + ALOGE("%s: Camera %d: Unknown state %d", __FUNCTION__, mCameraId, + state); + } +} + +bool Camera2Client::previewEnabled() { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return false; + + SharedParameters::Lock l(mParameters); + return l.mParameters.state == Parameters::PREVIEW; +} + +status_t Camera2Client::storeMetaDataInBuffers(bool enabled) { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return res; + + SharedParameters::Lock l(mParameters); + switch (l.mParameters.state) { + case Parameters::RECORD: + case Parameters::VIDEO_SNAPSHOT: + ALOGE("%s: Camera %d: Can't be called in state %s", + __FUNCTION__, mCameraId, + Parameters::getStateName(l.mParameters.state)); + return INVALID_OPERATION; + default: + // OK + break; + } + + l.mParameters.storeMetadataInBuffers = enabled; + + return OK; +} + +status_t Camera2Client::startRecording() { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + Mutex::Autolock icl(mICameraLock); + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return res; + SharedParameters::Lock l(mParameters); + + return startRecordingL(l.mParameters, false); +} + +status_t Camera2Client::startRecordingL(Parameters ¶ms, bool restart) { + status_t res; + + ALOGV("%s: state == %d, restart = %d", __FUNCTION__, params.state, restart); + + switch (params.state) { + case Parameters::STOPPED: + res = startPreviewL(params, false); + if (res != OK) return res; + break; + case Parameters::PREVIEW: + // Ready to go + break; + case Parameters::RECORD: + case Parameters::VIDEO_SNAPSHOT: + // OK to call this when recording is already on, just skip unless + // we're looking to restart + if (!restart) return OK; + break; + default: + ALOGE("%s: Camera %d: Can't start recording in state %s", + __FUNCTION__, mCameraId, + Parameters::getStateName(params.state)); + return INVALID_OPERATION; + }; + + if (!params.storeMetadataInBuffers) { + ALOGE("%s: Camera %d: Recording only supported in metadata mode, but " + "non-metadata recording mode requested!", __FUNCTION__, + mCameraId); + return INVALID_OPERATION; + } + + if (!restart) { + mCameraService->playSound(CameraService::SOUND_RECORDING); + mStreamingProcessor->updateRecordingRequest(params); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update recording request: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + } + + res = mStreamingProcessor->updateRecordingStream(params); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update recording stream: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + + Vector<uint8_t> outputStreams; + bool callbacksEnabled = params.previewCallbackFlags & + CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK; + if (callbacksEnabled) { + res = mCallbackProcessor->updateStream(params); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update callback stream: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + outputStreams.push(getCallbackStreamId()); + } + outputStreams.push(getPreviewStreamId()); + outputStreams.push(getRecordingStreamId()); + + 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); + return res; + } + + if (params.state < Parameters::RECORD) { + params.state = Parameters::RECORD; + } + + return OK; +} + +void Camera2Client::stopRecording() { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + Mutex::Autolock icl(mICameraLock); + SharedParameters::Lock l(mParameters); + + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return; + + switch (l.mParameters.state) { + case Parameters::RECORD: + // OK to stop + break; + case Parameters::STOPPED: + case Parameters::PREVIEW: + case Parameters::STILL_CAPTURE: + case Parameters::VIDEO_SNAPSHOT: + default: + ALOGE("%s: Camera %d: Can't stop recording in state %s", + __FUNCTION__, mCameraId, + Parameters::getStateName(l.mParameters.state)); + return; + }; + + mCameraService->playSound(CameraService::SOUND_RECORDING); + + res = startPreviewL(l.mParameters, true); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to return to preview", + __FUNCTION__, mCameraId); + } +} + +bool Camera2Client::recordingEnabled() { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + + if ( checkPid(__FUNCTION__) != OK) return false; + + return recordingEnabledL(); +} + +bool Camera2Client::recordingEnabledL() { + ATRACE_CALL(); + SharedParameters::Lock l(mParameters); + + return (l.mParameters.state == Parameters::RECORD + || l.mParameters.state == Parameters::VIDEO_SNAPSHOT); +} + +void Camera2Client::releaseRecordingFrame(const sp<IMemory>& mem) { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + if ( checkPid(__FUNCTION__) != OK) return; + + mStreamingProcessor->releaseRecordingFrame(mem); +} + +status_t Camera2Client::autoFocus() { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + ALOGV("%s: Camera %d", __FUNCTION__, mCameraId); + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return res; + + int triggerId; + bool notifyImmediately = false; + bool notifySuccess = false; + { + SharedParameters::Lock l(mParameters); + if (l.mParameters.state < Parameters::PREVIEW) { + return INVALID_OPERATION; + } + + /** + * If the camera does not support auto-focus, it is a no-op and + * onAutoFocus(boolean, Camera) callback will be called immediately + * with a fake value of success set to true. + */ + if (l.mParameters.focusMode == Parameters::FOCUS_MODE_FIXED) { + notifyImmediately = true; + notifySuccess = true; + } + /** + * If we're in CAF mode, and AF has already been locked, just fire back + * the callback right away; the HAL would not send a notification since + * no state change would happen on a AF trigger. + */ + if ( (l.mParameters.focusMode == Parameters::FOCUS_MODE_CONTINUOUS_PICTURE || + l.mParameters.focusMode == Parameters::FOCUS_MODE_CONTINUOUS_VIDEO) && + l.mParameters.focusState == ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED ) { + notifyImmediately = true; + notifySuccess = true; + } + /** + * Send immediate notification back to client + */ + if (notifyImmediately) { + SharedCameraClient::Lock l(mSharedCameraClient); + if (l.mCameraClient != 0) { + l.mCameraClient->notifyCallback(CAMERA_MSG_FOCUS, + notifySuccess ? 1 : 0, 0); + } + return OK; + } + /** + * Handle quirk mode for AF in scene modes + */ + if (l.mParameters.quirks.triggerAfWithAuto && + l.mParameters.sceneMode != ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED && + l.mParameters.focusMode != Parameters::FOCUS_MODE_AUTO && + !l.mParameters.focusingAreas[0].isEmpty()) { + ALOGV("%s: Quirk: Switching from focusMode %d to AUTO", + __FUNCTION__, l.mParameters.focusMode); + l.mParameters.shadowFocusMode = l.mParameters.focusMode; + l.mParameters.focusMode = Parameters::FOCUS_MODE_AUTO; + updateRequests(l.mParameters); + } + + l.mParameters.currentAfTriggerId = ++l.mParameters.afTriggerCounter; + triggerId = l.mParameters.currentAfTriggerId; + } + syncWithDevice(); + + mDevice->triggerAutofocus(triggerId); + + return OK; +} + +status_t Camera2Client::cancelAutoFocus() { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + ALOGV("%s: Camera %d", __FUNCTION__, mCameraId); + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return res; + + int triggerId; + { + SharedParameters::Lock l(mParameters); + triggerId = ++l.mParameters.afTriggerCounter; + + // When using triggerAfWithAuto quirk, may need to reset focus mode to + // the real state at this point. No need to cancel explicitly if + // changing the AF mode. + if (l.mParameters.shadowFocusMode != Parameters::FOCUS_MODE_INVALID) { + ALOGV("%s: Quirk: Restoring focus mode to %d", __FUNCTION__, + l.mParameters.shadowFocusMode); + l.mParameters.focusMode = l.mParameters.shadowFocusMode; + l.mParameters.shadowFocusMode = Parameters::FOCUS_MODE_INVALID; + updateRequests(l.mParameters); + + return OK; + } + } + syncWithDevice(); + + mDevice->triggerCancelAutofocus(triggerId); + + return OK; +} + +status_t Camera2Client::takePicture(int msgType) { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return res; + + { + SharedParameters::Lock l(mParameters); + switch (l.mParameters.state) { + case Parameters::DISCONNECTED: + case Parameters::STOPPED: + case Parameters::WAITING_FOR_PREVIEW_WINDOW: + ALOGE("%s: Camera %d: Cannot take picture without preview enabled", + __FUNCTION__, mCameraId); + return INVALID_OPERATION; + case Parameters::PREVIEW: + // Good to go for takePicture + res = commandStopFaceDetectionL(l.mParameters); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to stop face detection for still capture", + __FUNCTION__, mCameraId); + return res; + } + l.mParameters.state = Parameters::STILL_CAPTURE; + break; + case Parameters::RECORD: + // Good to go for video snapshot + l.mParameters.state = Parameters::VIDEO_SNAPSHOT; + break; + case Parameters::STILL_CAPTURE: + case Parameters::VIDEO_SNAPSHOT: + ALOGE("%s: Camera %d: Already taking a picture", + __FUNCTION__, mCameraId); + return INVALID_OPERATION; + } + + ALOGV("%s: Camera %d: Starting picture capture", __FUNCTION__, mCameraId); + + res = mJpegProcessor->updateStream(l.mParameters); + if (res != OK) { + ALOGE("%s: Camera %d: Can't set up still image stream: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + } + + // Need HAL to have correct settings before (possibly) triggering precapture + syncWithDevice(); + + res = mCaptureSequencer->startCapture(msgType); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to start capture: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + } + + return res; +} + +status_t Camera2Client::setParameters(const String8& params) { + ATRACE_CALL(); + ALOGV("%s: Camera %d", __FUNCTION__, mCameraId); + Mutex::Autolock icl(mICameraLock); + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return res; + + SharedParameters::Lock l(mParameters); + + res = l.mParameters.set(params); + if (res != OK) return res; + + res = updateRequests(l.mParameters); + + return res; +} + +String8 Camera2Client::getParameters() const { + ATRACE_CALL(); + ALOGV("%s: Camera %d", __FUNCTION__, mCameraId); + Mutex::Autolock icl(mICameraLock); + if ( checkPid(__FUNCTION__) != OK) return String8(); + + SharedParameters::ReadLock l(mParameters); + + return l.mParameters.get(); +} + +status_t Camera2Client::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return res; + + ALOGV("%s: Camera %d: Command %d (%d, %d)", __FUNCTION__, mCameraId, + cmd, arg1, arg2); + + switch (cmd) { + case CAMERA_CMD_START_SMOOTH_ZOOM: + return commandStartSmoothZoomL(); + case CAMERA_CMD_STOP_SMOOTH_ZOOM: + return commandStopSmoothZoomL(); + case CAMERA_CMD_SET_DISPLAY_ORIENTATION: + return commandSetDisplayOrientationL(arg1); + case CAMERA_CMD_ENABLE_SHUTTER_SOUND: + return commandEnableShutterSoundL(arg1 == 1); + case CAMERA_CMD_PLAY_RECORDING_SOUND: + return commandPlayRecordingSoundL(); + case CAMERA_CMD_START_FACE_DETECTION: + return commandStartFaceDetectionL(arg1); + case CAMERA_CMD_STOP_FACE_DETECTION: { + SharedParameters::Lock l(mParameters); + return commandStopFaceDetectionL(l.mParameters); + } + case CAMERA_CMD_ENABLE_FOCUS_MOVE_MSG: + return commandEnableFocusMoveMsgL(arg1 == 1); + case CAMERA_CMD_PING: + return commandPingL(); + case CAMERA_CMD_SET_VIDEO_BUFFER_COUNT: + return commandSetVideoBufferCountL(arg1); + default: + ALOGE("%s: Unknown command %d (arguments %d, %d)", + __FUNCTION__, cmd, arg1, arg2); + return BAD_VALUE; + } +} + +status_t Camera2Client::commandStartSmoothZoomL() { + ALOGE("%s: Unimplemented!", __FUNCTION__); + return OK; +} + +status_t Camera2Client::commandStopSmoothZoomL() { + ALOGE("%s: Unimplemented!", __FUNCTION__); + return OK; +} + +status_t Camera2Client::commandSetDisplayOrientationL(int degrees) { + int transform = Parameters::degToTransform(degrees, + mCameraFacing == CAMERA_FACING_FRONT); + if (transform == -1) { + ALOGE("%s: Camera %d: Error setting %d as display orientation value", + __FUNCTION__, mCameraId, degrees); + return BAD_VALUE; + } + SharedParameters::Lock l(mParameters); + if (transform != l.mParameters.previewTransform && + getPreviewStreamId() != NO_STREAM) { + mDevice->setStreamTransform(getPreviewStreamId(), transform); + } + l.mParameters.previewTransform = transform; + return OK; +} + +status_t Camera2Client::commandEnableShutterSoundL(bool enable) { + SharedParameters::Lock l(mParameters); + if (enable) { + l.mParameters.playShutterSound = true; + 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]; + property_get("ro.camera.sound.forced", value, "0"); + if (strncmp(value, "0", 2) != 0) { + // Disabling shutter sound is not allowed. Deny if the current + // process is not mediaserver. + if (getCallingPid() != getpid()) { + ALOGE("Failed to disable shutter sound. Permission denied (pid %d)", + getCallingPid()); + return PERMISSION_DENIED; + } + } + + l.mParameters.playShutterSound = false; + return OK; +} + +status_t Camera2Client::commandPlayRecordingSoundL() { + mCameraService->playSound(CameraService::SOUND_RECORDING); + return OK; +} + +status_t Camera2Client::commandStartFaceDetectionL(int type) { + ALOGV("%s: Camera %d: Starting face detection", + __FUNCTION__, mCameraId); + status_t res; + SharedParameters::Lock l(mParameters); + switch (l.mParameters.state) { + case Parameters::DISCONNECTED: + case Parameters::STOPPED: + case Parameters::WAITING_FOR_PREVIEW_WINDOW: + case Parameters::STILL_CAPTURE: + ALOGE("%s: Camera %d: Cannot start face detection without preview active", + __FUNCTION__, mCameraId); + return INVALID_OPERATION; + case Parameters::PREVIEW: + case Parameters::RECORD: + case Parameters::VIDEO_SNAPSHOT: + // Good to go for starting face detect + break; + } + // Ignoring type + if (l.mParameters.fastInfo.bestFaceDetectMode == + ANDROID_STATISTICS_FACE_DETECT_MODE_OFF) { + ALOGE("%s: Camera %d: Face detection not supported", + __FUNCTION__, mCameraId); + return INVALID_OPERATION; + } + if (l.mParameters.enableFaceDetect) return OK; + + l.mParameters.enableFaceDetect = true; + + res = updateRequests(l.mParameters); + + return res; +} + +status_t Camera2Client::commandStopFaceDetectionL(Parameters ¶ms) { + status_t res = OK; + ALOGV("%s: Camera %d: Stopping face detection", + __FUNCTION__, mCameraId); + + if (!params.enableFaceDetect) return OK; + + params.enableFaceDetect = false; + + if (params.state == Parameters::PREVIEW + || params.state == Parameters::RECORD + || params.state == Parameters::VIDEO_SNAPSHOT) { + res = updateRequests(params); + } + + return res; +} + +status_t Camera2Client::commandEnableFocusMoveMsgL(bool enable) { + SharedParameters::Lock l(mParameters); + l.mParameters.enableFocusMoveMessages = enable; + + return OK; +} + +status_t Camera2Client::commandPingL() { + // Always ping back if access is proper and device is alive + SharedParameters::Lock l(mParameters); + if (l.mParameters.state != Parameters::DISCONNECTED) { + return OK; + } else { + return NO_INIT; + } +} + +status_t Camera2Client::commandSetVideoBufferCountL(size_t count) { + if (recordingEnabledL()) { + ALOGE("%s: Camera %d: Error setting video buffer count after " + "recording was started", __FUNCTION__, mCameraId); + return INVALID_OPERATION; + } + + return mStreamingProcessor->setRecordingBufferCount(count); +} + +/** Device-related methods */ + +void Camera2Client::notifyError(int errorCode, int arg1, int arg2) { + ALOGE("Error condition %d reported by HAL, arguments %d, %d", errorCode, arg1, arg2); +} + +void Camera2Client::notifyShutter(int frameNumber, nsecs_t timestamp) { + ALOGV("%s: Shutter notification for frame %d at time %lld", __FUNCTION__, + frameNumber, timestamp); +} + +void Camera2Client::notifyAutoFocus(uint8_t newState, int triggerId) { + ALOGV("%s: Autofocus state now %d, last trigger %d", + __FUNCTION__, newState, triggerId); + bool sendCompletedMessage = false; + bool sendMovingMessage = false; + + bool success = false; + bool afInMotion = false; + { + SharedParameters::Lock l(mParameters); + l.mParameters.focusState = newState; + switch (l.mParameters.focusMode) { + case Parameters::FOCUS_MODE_AUTO: + case Parameters::FOCUS_MODE_MACRO: + // Don't send notifications upstream if they're not for the current AF + // trigger. For example, if cancel was called in between, or if we + // already sent a notification about this AF call. + if (triggerId != l.mParameters.currentAfTriggerId) break; + switch (newState) { + case ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED: + success = true; + // no break + case ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED: + sendCompletedMessage = true; + l.mParameters.currentAfTriggerId = -1; + break; + case ANDROID_CONTROL_AF_STATE_ACTIVE_SCAN: + // Just starting focusing, ignore + break; + case ANDROID_CONTROL_AF_STATE_INACTIVE: + case ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN: + case ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED: + default: + // Unexpected in AUTO/MACRO mode + ALOGE("%s: Unexpected AF state transition in AUTO/MACRO mode: %d", + __FUNCTION__, newState); + break; + } + break; + case Parameters::FOCUS_MODE_CONTINUOUS_VIDEO: + case Parameters::FOCUS_MODE_CONTINUOUS_PICTURE: + switch (newState) { + case ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED: + success = true; + // no break + case ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED: + // Don't send notifications upstream if they're not for + // the current AF trigger. For example, if cancel was + // called in between, or if we already sent a + // notification about this AF call. + // Send both a 'AF done' callback and a 'AF move' callback + if (triggerId != l.mParameters.currentAfTriggerId) break; + sendCompletedMessage = true; + afInMotion = false; + if (l.mParameters.enableFocusMoveMessages && + l.mParameters.afInMotion) { + sendMovingMessage = true; + } + l.mParameters.currentAfTriggerId = -1; + break; + case ANDROID_CONTROL_AF_STATE_INACTIVE: + // Cancel was called, or we switched state; care if + // currently moving + afInMotion = false; + if (l.mParameters.enableFocusMoveMessages && + l.mParameters.afInMotion) { + sendMovingMessage = true; + } + break; + case ANDROID_CONTROL_AF_STATE_PASSIVE_SCAN: + // Start passive scan, inform upstream + afInMotion = true; + // no break + case ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED: + // Stop passive scan, inform upstream + if (l.mParameters.enableFocusMoveMessages) { + sendMovingMessage = true; + } + break; + } + l.mParameters.afInMotion = afInMotion; + break; + case Parameters::FOCUS_MODE_EDOF: + case Parameters::FOCUS_MODE_INFINITY: + case Parameters::FOCUS_MODE_FIXED: + default: + if (newState != ANDROID_CONTROL_AF_STATE_INACTIVE) { + ALOGE("%s: Unexpected AF state change %d " + "(ID %d) in focus mode %d", + __FUNCTION__, newState, triggerId, + l.mParameters.focusMode); + } + } + } + if (sendMovingMessage) { + SharedCameraClient::Lock l(mSharedCameraClient); + if (l.mCameraClient != 0) { + l.mCameraClient->notifyCallback(CAMERA_MSG_FOCUS_MOVE, + afInMotion ? 1 : 0, 0); + } + } + if (sendCompletedMessage) { + SharedCameraClient::Lock l(mSharedCameraClient); + if (l.mCameraClient != 0) { + l.mCameraClient->notifyCallback(CAMERA_MSG_FOCUS, + success ? 1 : 0, 0); + } + } +} + +void Camera2Client::notifyAutoExposure(uint8_t newState, int triggerId) { + ALOGV("%s: Autoexposure state now %d, last trigger %d", + __FUNCTION__, newState, triggerId); + mCaptureSequencer->notifyAutoExposure(newState, triggerId); +} + +void Camera2Client::notifyAutoWhitebalance(uint8_t newState, int triggerId) { + ALOGV("%s: Auto-whitebalance state now %d, last trigger %d", + __FUNCTION__, newState, triggerId); +} + +int Camera2Client::getCameraId() const { + return mCameraId; +} + +const sp<Camera2Device>& Camera2Client::getCameraDevice() { + return mDevice; +} + +const sp<CameraService>& Camera2Client::getCameraService() { + return mCameraService; +} + +camera2::SharedParameters& Camera2Client::getParameters() { + return mParameters; +} + +int Camera2Client::getPreviewStreamId() const { + return mStreamingProcessor->getPreviewStreamId(); +} + +int Camera2Client::getCaptureStreamId() const { + return mJpegProcessor->getStreamId(); +} + +int Camera2Client::getCallbackStreamId() const { + return mCallbackProcessor->getStreamId(); +} + +int Camera2Client::getRecordingStreamId() const { + return mStreamingProcessor->getRecordingStreamId(); +} + +int Camera2Client::getZslStreamId() const { + return mZslProcessor->getStreamId(); +} + +status_t Camera2Client::registerFrameListener(int32_t minId, int32_t maxId, + wp<camera2::FrameProcessor::FilteredListener> listener) { + return mFrameProcessor->registerListener(minId, maxId, listener); +} + +status_t Camera2Client::removeFrameListener(int32_t minId, int32_t maxId, + wp<camera2::FrameProcessor::FilteredListener> listener) { + return mFrameProcessor->removeListener(minId, maxId, listener); +} + +status_t Camera2Client::stopStream() { + return mStreamingProcessor->stopStream(); +} + +Camera2Client::SharedCameraClient::Lock::Lock(SharedCameraClient &client): + mCameraClient(client.mCameraClient), + mSharedClient(client) { + mSharedClient.mCameraClientLock.lock(); +} + +Camera2Client::SharedCameraClient::Lock::~Lock() { + mSharedClient.mCameraClientLock.unlock(); +} + +Camera2Client::SharedCameraClient::SharedCameraClient(const sp<ICameraClient>&client): + mCameraClient(client) { +} + +Camera2Client::SharedCameraClient& Camera2Client::SharedCameraClient::operator=( + const sp<ICameraClient>&client) { + Mutex::Autolock l(mCameraClientLock); + mCameraClient = client; + return *this; +} + +void Camera2Client::SharedCameraClient::clear() { + Mutex::Autolock l(mCameraClientLock); + mCameraClient.clear(); +} + +const int32_t Camera2Client::kPreviewRequestIdStart; +const int32_t Camera2Client::kPreviewRequestIdEnd; +const int32_t Camera2Client::kRecordingRequestIdStart; +const int32_t Camera2Client::kRecordingRequestIdEnd; +const int32_t Camera2Client::kCaptureRequestIdStart; +const int32_t Camera2Client::kCaptureRequestIdEnd; + +/** Utility methods */ + +status_t Camera2Client::updateRequests(Parameters ¶ms) { + status_t res; + + ALOGV("%s: Camera %d: state = %d", __FUNCTION__, getCameraId(), params.state); + + res = mStreamingProcessor->incrementStreamingIds(); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to increment request IDs: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + + res = mStreamingProcessor->updatePreviewRequest(params); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update preview request: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + res = mStreamingProcessor->updateRecordingRequest(params); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update recording request: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + + if (params.state == Parameters::PREVIEW) { + res = startPreviewL(params, true); + if (res != OK) { + ALOGE("%s: Camera %d: Error streaming new preview request: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + } else if (params.state == Parameters::RECORD || + params.state == Parameters::VIDEO_SNAPSHOT) { + res = startRecordingL(params, true); + if (res != OK) { + ALOGE("%s: Camera %d: Error streaming new record request: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + } + return res; +} + + +size_t Camera2Client::calculateBufferSize(int width, int height, + int format, int stride) { + switch (format) { + case HAL_PIXEL_FORMAT_YCbCr_422_SP: // NV16 + return width * height * 2; + case HAL_PIXEL_FORMAT_YCrCb_420_SP: // NV21 + return width * height * 3 / 2; + case HAL_PIXEL_FORMAT_YCbCr_422_I: // YUY2 + return width * height * 2; + case HAL_PIXEL_FORMAT_YV12: { // YV12 + size_t ySize = stride * height; + size_t uvStride = (stride / 2 + 0xF) & ~0xF; + size_t uvSize = uvStride * height / 2; + return ySize + uvSize * 2; + } + case HAL_PIXEL_FORMAT_RGB_565: + return width * height * 2; + case HAL_PIXEL_FORMAT_RGBA_8888: + return width * height * 4; + case HAL_PIXEL_FORMAT_RAW_SENSOR: + return width * height * 2; + default: + ALOGE("%s: Unknown preview format: %x", + __FUNCTION__, format); + return 0; + } +} + +status_t Camera2Client::syncWithDevice() { + ATRACE_CALL(); + const nsecs_t kMaxSyncTimeout = 500000000; // 500 ms + status_t res; + + int32_t activeRequestId = mStreamingProcessor->getActiveRequestId(); + if (activeRequestId == 0) return OK; + + res = mDevice->waitUntilRequestReceived(activeRequestId, kMaxSyncTimeout); + if (res == TIMED_OUT) { + ALOGE("%s: Camera %d: Timed out waiting sync with HAL", + __FUNCTION__, mCameraId); + } else if (res != OK) { + ALOGE("%s: Camera %d: Error while waiting to sync with HAL", + __FUNCTION__, mCameraId); + } + return res; +} + +} // namespace android diff --git a/services/camera/libcameraservice/Camera2Client.h b/services/camera/libcameraservice/Camera2Client.h new file mode 100644 index 00000000..55ead024 --- /dev/null +++ b/services/camera/libcameraservice/Camera2Client.h @@ -0,0 +1,228 @@ +/* + * 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_SERVERS_CAMERA_CAMERA2CLIENT_H +#define ANDROID_SERVERS_CAMERA_CAMERA2CLIENT_H + +#include "Camera2Device.h" +#include "CameraService.h" +#include "camera2/Parameters.h" +#include "camera2/FrameProcessor.h" +#include "camera2/StreamingProcessor.h" +#include "camera2/JpegProcessor.h" +#include "camera2/ZslProcessor.h" +#include "camera2/CaptureSequencer.h" +#include "camera2/CallbackProcessor.h" + +namespace android { + +class IMemory; +/** + * Implements the android.hardware.camera API on top of + * camera device HAL version 2. + */ +class Camera2Client : + public CameraService::Client, + public Camera2Device::NotificationListener +{ +public: + /** + * ICamera interface (see ICamera for details) + */ + + virtual void disconnect(); + virtual status_t connect(const sp<ICameraClient>& client); + virtual status_t lock(); + virtual status_t unlock(); + virtual status_t setPreviewDisplay(const sp<Surface>& surface); + virtual status_t setPreviewTexture( + const sp<ISurfaceTexture>& surfaceTexture); + virtual void setPreviewCallbackFlag(int flag); + virtual status_t startPreview(); + virtual void stopPreview(); + virtual bool previewEnabled(); + virtual status_t storeMetaDataInBuffers(bool enabled); + virtual status_t startRecording(); + virtual void stopRecording(); + virtual bool recordingEnabled(); + virtual void releaseRecordingFrame(const sp<IMemory>& mem); + virtual status_t autoFocus(); + virtual status_t cancelAutoFocus(); + virtual status_t takePicture(int msgType); + virtual status_t setParameters(const String8& params); + virtual String8 getParameters() const; + virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2); + + /** + * Interface used by CameraService + */ + + Camera2Client(const sp<CameraService>& cameraService, + const sp<ICameraClient>& cameraClient, + int cameraId, + int cameraFacing, + int clientPid, + int servicePid); + virtual ~Camera2Client(); + + status_t initialize(camera_module_t *module); + + virtual status_t dump(int fd, const Vector<String16>& args); + + /** + * Interface used by Camera2Device + */ + + virtual void notifyError(int errorCode, int arg1, int arg2); + virtual void notifyShutter(int frameNumber, 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, int triggerId); + + /** + * Interface used by independent components of Camera2Client. + */ + + int getCameraId() const; + const sp<Camera2Device>& getCameraDevice(); + const sp<CameraService>& getCameraService(); + camera2::SharedParameters& getParameters(); + + int getPreviewStreamId() const; + int getCaptureStreamId() const; + int getCallbackStreamId() const; + int getRecordingStreamId() const; + int getZslStreamId() const; + + status_t registerFrameListener(int32_t minId, int32_t maxId, + wp<camera2::FrameProcessor::FilteredListener> listener); + status_t removeFrameListener(int32_t minId, int32_t maxId, + wp<camera2::FrameProcessor::FilteredListener> listener); + + status_t stopStream(); + + // Simple class to ensure that access to ICameraClient is serialized by + // requiring mCameraClientLock to be locked before access to mCameraClient + // is possible. + class SharedCameraClient { + public: + class Lock { + public: + Lock(SharedCameraClient &client); + ~Lock(); + sp<ICameraClient> &mCameraClient; + private: + SharedCameraClient &mSharedClient; + }; + SharedCameraClient(const sp<ICameraClient>& client); + SharedCameraClient& operator=(const sp<ICameraClient>& client); + void clear(); + private: + sp<ICameraClient> mCameraClient; + mutable Mutex mCameraClientLock; + } mSharedCameraClient; + + static size_t calculateBufferSize(int width, int height, + int format, int stride); + + static const int32_t kPreviewRequestIdStart = 10000000; + static const int32_t kPreviewRequestIdEnd = 20000000; + + static const int32_t kRecordingRequestIdStart = 20000000; + static const int32_t kRecordingRequestIdEnd = 30000000; + + static const int32_t kCaptureRequestIdStart = 30000000; + static const int32_t kCaptureRequestIdEnd = 40000000; + +private: + /** ICamera interface-related private members */ + + // Mutex that must be locked by methods implementing the ICamera interface. + // Ensures serialization between incoming ICamera calls. All methods below + // that append 'L' to the name assume that mICameraLock is locked when + // they're called + mutable Mutex mICameraLock; + + typedef camera2::Parameters Parameters; + typedef camera2::CameraMetadata CameraMetadata; + + status_t setPreviewWindowL(const sp<IBinder>& binder, + sp<ANativeWindow> window); + status_t startPreviewL(Parameters ¶ms, bool restart); + void stopPreviewL(); + status_t startRecordingL(Parameters ¶ms, bool restart); + bool recordingEnabledL(); + + // Individual commands for sendCommand() + status_t commandStartSmoothZoomL(); + status_t commandStopSmoothZoomL(); + status_t commandSetDisplayOrientationL(int degrees); + status_t commandEnableShutterSoundL(bool enable); + status_t commandPlayRecordingSoundL(); + status_t commandStartFaceDetectionL(int type); + status_t commandStopFaceDetectionL(Parameters ¶ms); + status_t commandEnableFocusMoveMsgL(bool enable); + status_t commandPingL(); + status_t commandSetVideoBufferCountL(size_t count); + + // Current camera device configuration + camera2::SharedParameters mParameters; + + /** Camera device-related private members */ + + void setPreviewCallbackFlagL(Parameters ¶ms, int flag); + status_t updateRequests(Parameters ¶ms); + + // Used with stream IDs + static const int NO_STREAM = -1; + + sp<camera2::FrameProcessor> mFrameProcessor; + + /* Preview/Recording related members */ + + sp<IBinder> mPreviewSurface; + sp<camera2::StreamingProcessor> mStreamingProcessor; + + /** Preview callback related members */ + + sp<camera2::CallbackProcessor> mCallbackProcessor; + + /* Still image capture related members */ + + sp<camera2::CaptureSequencer> mCaptureSequencer; + sp<camera2::JpegProcessor> mJpegProcessor; + sp<camera2::ZslProcessor> mZslProcessor; + + /** Notification-related members */ + + bool mAfInMotion; + + /** Camera2Device instance wrapping HAL2 entry */ + + sp<Camera2Device> mDevice; + + /** Utility members */ + + // Wait until the camera device has received the latest control settings + status_t syncWithDevice(); + + // Verify that caller is the owner of the camera + status_t checkPid(const char *checkLocation) const; +}; + +}; // namespace android + +#endif diff --git a/services/camera/libcameraservice/Camera2Device.cpp b/services/camera/libcameraservice/Camera2Device.cpp new file mode 100644 index 00000000..d6445c1b --- /dev/null +++ b/services/camera/libcameraservice/Camera2Device.cpp @@ -0,0 +1,1513 @@ +/* + * 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. + */ + +#define LOG_TAG "Camera2-Device" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 +//#define LOG_NNDEBUG 0 // Per-frame verbose logging + +#ifdef LOG_NNDEBUG +#define ALOGVV(...) ALOGV(__VA_ARGS__) +#else +#define ALOGVV(...) ((void)0) +#endif + +#include <utils/Log.h> +#include <utils/Trace.h> +#include <utils/Timers.h> +#include "Camera2Device.h" + +namespace android { + +Camera2Device::Camera2Device(int id): + mId(id), + mDevice(NULL) +{ + ATRACE_CALL(); + ALOGV("%s: Created device for camera %d", __FUNCTION__, id); +} + +Camera2Device::~Camera2Device() +{ + ATRACE_CALL(); + ALOGV("%s: Tearing down for camera id %d", __FUNCTION__, mId); + disconnect(); +} + +status_t Camera2Device::initialize(camera_module_t *module) +{ + ATRACE_CALL(); + ALOGV("%s: Initializing device for camera %d", __FUNCTION__, mId); + if (mDevice != NULL) { + ALOGE("%s: Already initialized!", __FUNCTION__); + return INVALID_OPERATION; + } + + status_t res; + char name[10]; + snprintf(name, sizeof(name), "%d", mId); + + camera2_device_t *device; + + res = 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__, + mId, strerror(-res), res); + return res; + } + + if (device->common.version != CAMERA_DEVICE_API_VERSION_2_0) { + ALOGE("%s: Could not open camera %d: " + "Camera device is not version %x, reports %x instead", + __FUNCTION__, mId, CAMERA_DEVICE_API_VERSION_2_0, + device->common.version); + device->common.close(&device->common); + return BAD_VALUE; + } + + camera_info info; + res = module->get_camera_info(mId, &info); + if (res != OK ) return res; + + if (info.device_version != device->common.version) { + ALOGE("%s: HAL reporting mismatched camera_info version (%x)" + " and device version (%x).", __FUNCTION__, + device->common.version, info.device_version); + device->common.close(&device->common); + return BAD_VALUE; + } + + res = mRequestQueue.setConsumerDevice(device); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to connect request queue to device: %s (%d)", + __FUNCTION__, mId, strerror(-res), res); + device->common.close(&device->common); + return res; + } + res = mFrameQueue.setProducerDevice(device); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to connect frame queue to device: %s (%d)", + __FUNCTION__, mId, strerror(-res), res); + device->common.close(&device->common); + 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) { + ALOGE("%s: Camera %d: Unable to initialize notification callback!", + __FUNCTION__, mId); + device->common.close(&device->common); + return res; + } + + mDeviceInfo = info.static_camera_characteristics; + mDevice = device; + + return OK; +} + +status_t Camera2Device::disconnect() { + ATRACE_CALL(); + status_t res = OK; + if (mDevice) { + ALOGV("%s: Closing device for camera %d", __FUNCTION__, mId); + + int inProgressCount = mDevice->ops->get_in_progress_count(mDevice); + if (inProgressCount > 0) { + ALOGW("%s: Closing camera device %d with %d requests in flight!", + __FUNCTION__, mId, inProgressCount); + } + mReprocessStreams.clear(); + mStreams.clear(); + res = mDevice->common.close(&mDevice->common); + if (res != OK) { + ALOGE("%s: Could not close camera %d: %s (%d)", + __FUNCTION__, + mId, strerror(-res), res); + } + mDevice = NULL; + ALOGV("%s: Shutdown complete", __FUNCTION__); + } + return res; +} + +status_t Camera2Device::dump(int fd, const Vector<String16>& args) { + ATRACE_CALL(); + String8 result; + int detailLevel = 0; + int n = args.size(); + String16 detailOption("-d"); + for (int i = 0; i + 1 < n; i++) { + if (args[i] == detailOption) { + String8 levelStr(args[i+1]); + detailLevel = atoi(levelStr.string()); + } + } + + result.appendFormat(" Camera2Device[%d] dump (detail level %d):\n", + mId, detailLevel); + + if (detailLevel > 0) { + result = " Request queue contents:\n"; + write(fd, result.string(), result.size()); + mRequestQueue.dump(fd, args); + + result = " Frame queue contents:\n"; + write(fd, result.string(), result.size()); + mFrameQueue.dump(fd, args); + } + + result = " Active streams:\n"; + write(fd, result.string(), result.size()); + for (StreamList::iterator s = mStreams.begin(); s != mStreams.end(); s++) { + (*s)->dump(fd, args); + } + + result = " HAL device dump:\n"; + write(fd, result.string(), result.size()); + + status_t res; + res = mDevice->ops->dump(mDevice, fd); + + return res; +} + +const camera2::CameraMetadata& Camera2Device::info() const { + ALOGVV("%s: E", __FUNCTION__); + + return mDeviceInfo; +} + +status_t Camera2Device::capture(CameraMetadata &request) { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + + mRequestQueue.enqueue(request.release()); + return OK; +} + + +status_t Camera2Device::setStreamingRequest(const CameraMetadata &request) { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + CameraMetadata streamRequest(request); + return mRequestQueue.setStreamSlot(streamRequest.release()); +} + +status_t Camera2Device::clearStreamingRequest() { + ATRACE_CALL(); + return mRequestQueue.setStreamSlot(NULL); +} + +status_t Camera2Device::waitUntilRequestReceived(int32_t requestId, nsecs_t timeout) { + ATRACE_CALL(); + return mRequestQueue.waitForDequeue(requestId, timeout); +} + +status_t Camera2Device::createStream(sp<ANativeWindow> consumer, + uint32_t width, uint32_t height, int format, size_t size, int *id) { + ATRACE_CALL(); + status_t res; + ALOGV("%s: E", __FUNCTION__); + + sp<StreamAdapter> stream = new StreamAdapter(mDevice); + + res = stream->connectToDevice(consumer, width, height, format, size); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to create stream (%d x %d, format %x):" + "%s (%d)", + __FUNCTION__, mId, width, height, format, strerror(-res), res); + return res; + } + + *id = stream->getId(); + + mStreams.push_back(stream); + return OK; +} + +status_t Camera2Device::createReprocessStreamFromStream(int outputId, int *id) { + ATRACE_CALL(); + status_t res; + ALOGV("%s: E", __FUNCTION__); + + bool found = false; + StreamList::iterator streamI; + for (streamI = mStreams.begin(); + streamI != mStreams.end(); streamI++) { + if ((*streamI)->getId() == outputId) { + found = true; + break; + } + } + if (!found) { + ALOGE("%s: Camera %d: Output stream %d doesn't exist; can't create " + "reprocess stream from it!", __FUNCTION__, mId, outputId); + return BAD_VALUE; + } + + sp<ReprocessStreamAdapter> stream = new ReprocessStreamAdapter(mDevice); + + res = stream->connectToDevice((*streamI)); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to create reprocessing stream from "\ + "stream %d: %s (%d)", __FUNCTION__, mId, outputId, + strerror(-res), res); + return res; + } + + *id = stream->getId(); + + mReprocessStreams.push_back(stream); + return OK; +} + + +status_t Camera2Device::getStreamInfo(int id, + uint32_t *width, uint32_t *height, uint32_t *format) { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + bool found = false; + StreamList::iterator streamI; + for (streamI = mStreams.begin(); + streamI != mStreams.end(); streamI++) { + if ((*streamI)->getId() == id) { + found = true; + break; + } + } + if (!found) { + ALOGE("%s: Camera %d: Stream %d does not exist", + __FUNCTION__, mId, id); + return BAD_VALUE; + } + + if (width) *width = (*streamI)->getWidth(); + if (height) *height = (*streamI)->getHeight(); + if (format) *format = (*streamI)->getFormat(); + + return OK; +} + +status_t Camera2Device::setStreamTransform(int id, + int transform) { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + bool found = false; + StreamList::iterator streamI; + for (streamI = mStreams.begin(); + streamI != mStreams.end(); streamI++) { + if ((*streamI)->getId() == id) { + found = true; + break; + } + } + if (!found) { + ALOGE("%s: Camera %d: Stream %d does not exist", + __FUNCTION__, mId, id); + return BAD_VALUE; + } + + return (*streamI)->setTransform(transform); +} + +status_t Camera2Device::deleteStream(int id) { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + bool found = false; + for (StreamList::iterator streamI = mStreams.begin(); + streamI != mStreams.end(); streamI++) { + if ((*streamI)->getId() == id) { + status_t res = (*streamI)->release(); + if (res != OK) { + ALOGE("%s: Unable to release stream %d from HAL device: " + "%s (%d)", __FUNCTION__, id, strerror(-res), res); + return res; + } + mStreams.erase(streamI); + found = true; + break; + } + } + if (!found) { + ALOGE("%s: Camera %d: Unable to find stream %d to delete", + __FUNCTION__, mId, id); + return BAD_VALUE; + } + return OK; +} + +status_t Camera2Device::deleteReprocessStream(int id) { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + bool found = false; + for (ReprocessStreamList::iterator streamI = mReprocessStreams.begin(); + streamI != mReprocessStreams.end(); streamI++) { + if ((*streamI)->getId() == id) { + status_t res = (*streamI)->release(); + if (res != OK) { + ALOGE("%s: Unable to release reprocess stream %d from " + "HAL device: %s (%d)", __FUNCTION__, id, + strerror(-res), res); + return res; + } + mReprocessStreams.erase(streamI); + found = true; + break; + } + } + if (!found) { + ALOGE("%s: Camera %d: Unable to find stream %d to delete", + __FUNCTION__, mId, id); + return BAD_VALUE; + } + return OK; +} + + +status_t Camera2Device::createDefaultRequest(int templateId, + CameraMetadata *request) { + ATRACE_CALL(); + status_t err; + ALOGV("%s: E", __FUNCTION__); + camera_metadata_t *rawRequest; + err = mDevice->ops->construct_default_request( + mDevice, templateId, &rawRequest); + request->acquire(rawRequest); + return err; +} + +status_t Camera2Device::waitUntilDrained() { + ATRACE_CALL(); + static const uint32_t kSleepTime = 50000; // 50 ms + static const uint32_t kMaxSleepTime = 10000000; // 10 s + ALOGV("%s: Camera %d: Starting wait", __FUNCTION__, mId); + if (mRequestQueue.getBufferCount() == + CAMERA2_REQUEST_QUEUE_IS_BOTTOMLESS) return INVALID_OPERATION; + + // TODO: Set up notifications from HAL, instead of sleeping here + uint32_t totalTime = 0; + while (mDevice->ops->get_in_progress_count(mDevice) > 0) { + usleep(kSleepTime); + totalTime += kSleepTime; + if (totalTime > kMaxSleepTime) { + ALOGE("%s: Waited %d us, %d requests still in flight", __FUNCTION__, + mDevice->ops->get_in_progress_count(mDevice), totalTime); + return TIMED_OUT; + } + } + ALOGV("%s: Camera %d: HAL is idle", __FUNCTION__, mId); + return OK; +} + +status_t Camera2Device::setNotifyCallback(NotificationListener *listener) { + ATRACE_CALL(); + status_t res; + res = mDevice->ops->set_notify_callback(mDevice, notificationCallback, + reinterpret_cast<void*>(listener) ); + if (res != OK) { + ALOGE("%s: Unable to set notification callback!", __FUNCTION__); + } + return res; +} + +void Camera2Device::notificationCallback(int32_t msg_type, + int32_t ext1, + int32_t ext2, + int32_t ext3, + void *user) { + ATRACE_CALL(); + NotificationListener *listener = reinterpret_cast<NotificationListener*>(user); + ALOGV("%s: Notification %d, arguments %d, %d, %d", __FUNCTION__, msg_type, + ext1, ext2, ext3); + if (listener != NULL) { + switch (msg_type) { + case CAMERA2_MSG_ERROR: + listener->notifyError(ext1, ext2, ext3); + break; + case CAMERA2_MSG_SHUTTER: { + nsecs_t timestamp = (nsecs_t)ext2 | ((nsecs_t)(ext3) << 32 ); + listener->notifyShutter(ext1, timestamp); + break; + } + case CAMERA2_MSG_AUTOFOCUS: + listener->notifyAutoFocus(ext1, ext2); + break; + case CAMERA2_MSG_AUTOEXPOSURE: + listener->notifyAutoExposure(ext1, ext2); + break; + case CAMERA2_MSG_AUTOWB: + listener->notifyAutoWhitebalance(ext1, ext2); + break; + default: + ALOGE("%s: Unknown notification %d (arguments %d, %d, %d)!", + __FUNCTION__, msg_type, ext1, ext2, ext3); + } + } +} + +status_t Camera2Device::waitForNextFrame(nsecs_t timeout) { + return mFrameQueue.waitForBuffer(timeout); +} + +status_t Camera2Device::getNextFrame(CameraMetadata *frame) { + ATRACE_CALL(); + status_t res; + camera_metadata_t *rawFrame; + res = mFrameQueue.dequeue(&rawFrame); + if (rawFrame == NULL) { + return NOT_ENOUGH_DATA; + } else if (res == OK) { + frame->acquire(rawFrame); + } + return res; +} + +status_t Camera2Device::triggerAutofocus(uint32_t id) { + ATRACE_CALL(); + status_t res; + ALOGV("%s: Triggering autofocus, id %d", __FUNCTION__, id); + res = mDevice->ops->trigger_action(mDevice, + CAMERA2_TRIGGER_AUTOFOCUS, id, 0); + if (res != OK) { + ALOGE("%s: Error triggering autofocus (id %d)", + __FUNCTION__, id); + } + return res; +} + +status_t Camera2Device::triggerCancelAutofocus(uint32_t id) { + ATRACE_CALL(); + status_t res; + ALOGV("%s: Canceling autofocus, id %d", __FUNCTION__, id); + res = mDevice->ops->trigger_action(mDevice, + CAMERA2_TRIGGER_CANCEL_AUTOFOCUS, id, 0); + if (res != OK) { + ALOGE("%s: Error canceling autofocus (id %d)", + __FUNCTION__, id); + } + return res; +} + +status_t Camera2Device::triggerPrecaptureMetering(uint32_t id) { + ATRACE_CALL(); + status_t res; + ALOGV("%s: Triggering precapture metering, id %d", __FUNCTION__, id); + res = mDevice->ops->trigger_action(mDevice, + CAMERA2_TRIGGER_PRECAPTURE_METERING, id, 0); + if (res != OK) { + ALOGE("%s: Error triggering precapture metering (id %d)", + __FUNCTION__, id); + } + return res; +} + +status_t Camera2Device::pushReprocessBuffer(int reprocessStreamId, + buffer_handle_t *buffer, wp<BufferReleasedListener> listener) { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + bool found = false; + status_t res = OK; + for (ReprocessStreamList::iterator streamI = mReprocessStreams.begin(); + streamI != mReprocessStreams.end(); streamI++) { + if ((*streamI)->getId() == reprocessStreamId) { + res = (*streamI)->pushIntoStream(buffer, listener); + if (res != OK) { + ALOGE("%s: Unable to push buffer to reprocess stream %d: %s (%d)", + __FUNCTION__, reprocessStreamId, strerror(-res), res); + return res; + } + found = true; + break; + } + } + if (!found) { + ALOGE("%s: Camera %d: Unable to find reprocess stream %d", + __FUNCTION__, mId, reprocessStreamId); + res = BAD_VALUE; + } + return res; +} + +/** + * Camera2Device::NotificationListener + */ + +Camera2Device::NotificationListener::~NotificationListener() { +} + +/** + * Camera2Device::MetadataQueue + */ + +Camera2Device::MetadataQueue::MetadataQueue(): + mDevice(NULL), + mFrameCount(0), + mLatestRequestId(0), + mCount(0), + mStreamSlotCount(0), + mSignalConsumer(true) +{ + ATRACE_CALL(); + camera2_request_queue_src_ops::dequeue_request = consumer_dequeue; + camera2_request_queue_src_ops::request_count = consumer_buffer_count; + camera2_request_queue_src_ops::free_request = consumer_free; + + camera2_frame_queue_dst_ops::dequeue_frame = producer_dequeue; + camera2_frame_queue_dst_ops::cancel_frame = producer_cancel; + camera2_frame_queue_dst_ops::enqueue_frame = producer_enqueue; +} + +Camera2Device::MetadataQueue::~MetadataQueue() { + ATRACE_CALL(); + Mutex::Autolock l(mMutex); + freeBuffers(mEntries.begin(), mEntries.end()); + freeBuffers(mStreamSlot.begin(), mStreamSlot.end()); +} + +// Connect to camera2 HAL as consumer (input requests/reprocessing) +status_t Camera2Device::MetadataQueue::setConsumerDevice(camera2_device_t *d) { + ATRACE_CALL(); + status_t res; + res = d->ops->set_request_queue_src_ops(d, + this); + if (res != OK) return res; + mDevice = d; + return OK; +} + +status_t Camera2Device::MetadataQueue::setProducerDevice(camera2_device_t *d) { + ATRACE_CALL(); + status_t res; + res = d->ops->set_frame_queue_dst_ops(d, + this); + return res; +} + +// Real interfaces +status_t Camera2Device::MetadataQueue::enqueue(camera_metadata_t *buf) { + ATRACE_CALL(); + ALOGVV("%s: E", __FUNCTION__); + Mutex::Autolock l(mMutex); + + mCount++; + mEntries.push_back(buf); + + return signalConsumerLocked(); +} + +int Camera2Device::MetadataQueue::getBufferCount() { + ATRACE_CALL(); + Mutex::Autolock l(mMutex); + if (mStreamSlotCount > 0) { + return CAMERA2_REQUEST_QUEUE_IS_BOTTOMLESS; + } + return mCount; +} + +status_t Camera2Device::MetadataQueue::dequeue(camera_metadata_t **buf, + bool incrementCount) +{ + ATRACE_CALL(); + ALOGVV("%s: E", __FUNCTION__); + status_t res; + Mutex::Autolock l(mMutex); + + if (mCount == 0) { + if (mStreamSlotCount == 0) { + ALOGVV("%s: Empty", __FUNCTION__); + *buf = NULL; + mSignalConsumer = true; + return OK; + } + ALOGVV("%s: Streaming %d frames to queue", __FUNCTION__, + mStreamSlotCount); + + for (List<camera_metadata_t*>::iterator slotEntry = mStreamSlot.begin(); + slotEntry != mStreamSlot.end(); + slotEntry++ ) { + size_t entries = get_camera_metadata_entry_count(*slotEntry); + size_t dataBytes = get_camera_metadata_data_count(*slotEntry); + + camera_metadata_t *copy = + allocate_camera_metadata(entries, dataBytes); + append_camera_metadata(copy, *slotEntry); + mEntries.push_back(copy); + } + mCount = mStreamSlotCount; + } + ALOGVV("MetadataQueue: deque (%d buffers)", mCount); + camera_metadata_t *b = *(mEntries.begin()); + mEntries.erase(mEntries.begin()); + + if (incrementCount) { + ATRACE_INT("cam2_request", mFrameCount); + camera_metadata_entry_t frameCount; + res = find_camera_metadata_entry(b, + ANDROID_REQUEST_FRAME_COUNT, + &frameCount); + if (res != OK) { + ALOGE("%s: Unable to add frame count: %s (%d)", + __FUNCTION__, strerror(-res), res); + } else { + *frameCount.data.i32 = mFrameCount; + } + mFrameCount++; + } + + // Check for request ID, and if present, signal waiters. + camera_metadata_entry_t requestId; + res = find_camera_metadata_entry(b, + ANDROID_REQUEST_ID, + &requestId); + if (res == OK) { + mLatestRequestId = requestId.data.i32[0]; + mNewRequestId.signal(); + } + + *buf = b; + mCount--; + + return OK; +} + +status_t Camera2Device::MetadataQueue::waitForBuffer(nsecs_t timeout) +{ + Mutex::Autolock l(mMutex); + status_t res; + while (mCount == 0) { + res = notEmpty.waitRelative(mMutex,timeout); + if (res != OK) return res; + } + return OK; +} + +status_t Camera2Device::MetadataQueue::waitForDequeue(int32_t id, + nsecs_t timeout) { + Mutex::Autolock l(mMutex); + status_t res; + while (mLatestRequestId != id) { + nsecs_t startTime = systemTime(); + + res = mNewRequestId.waitRelative(mMutex, timeout); + if (res != OK) return res; + + timeout -= (systemTime() - startTime); + } + + return OK; +} + +status_t Camera2Device::MetadataQueue::setStreamSlot(camera_metadata_t *buf) +{ + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + Mutex::Autolock l(mMutex); + if (buf == NULL) { + freeBuffers(mStreamSlot.begin(), mStreamSlot.end()); + mStreamSlotCount = 0; + return OK; + } + camera_metadata_t *buf2 = clone_camera_metadata(buf); + if (!buf2) { + ALOGE("%s: Unable to clone metadata buffer!", __FUNCTION__); + return NO_MEMORY; + } + + if (mStreamSlotCount > 1) { + List<camera_metadata_t*>::iterator deleter = ++mStreamSlot.begin(); + freeBuffers(++mStreamSlot.begin(), mStreamSlot.end()); + mStreamSlotCount = 1; + } + if (mStreamSlotCount == 1) { + free_camera_metadata( *(mStreamSlot.begin()) ); + *(mStreamSlot.begin()) = buf2; + } else { + mStreamSlot.push_front(buf2); + mStreamSlotCount = 1; + } + return signalConsumerLocked(); +} + +status_t Camera2Device::MetadataQueue::setStreamSlot( + const List<camera_metadata_t*> &bufs) +{ + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + Mutex::Autolock l(mMutex); + status_t res; + + if (mStreamSlotCount > 0) { + freeBuffers(mStreamSlot.begin(), mStreamSlot.end()); + } + mStreamSlotCount = 0; + for (List<camera_metadata_t*>::const_iterator r = bufs.begin(); + r != bufs.end(); r++) { + camera_metadata_t *r2 = clone_camera_metadata(*r); + if (!r2) { + ALOGE("%s: Unable to clone metadata buffer!", __FUNCTION__); + return NO_MEMORY; + } + mStreamSlot.push_back(r2); + mStreamSlotCount++; + } + return signalConsumerLocked(); +} + +status_t Camera2Device::MetadataQueue::dump(int fd, + const Vector<String16>& args) { + ATRACE_CALL(); + String8 result; + status_t notLocked; + notLocked = mMutex.tryLock(); + if (notLocked) { + result.append(" (Unable to lock queue mutex)\n"); + } + result.appendFormat(" Current frame number: %d\n", mFrameCount); + if (mStreamSlotCount == 0) { + result.append(" Stream slot: Empty\n"); + write(fd, result.string(), result.size()); + } else { + result.appendFormat(" Stream slot: %d entries\n", + mStreamSlot.size()); + int i = 0; + for (List<camera_metadata_t*>::iterator r = mStreamSlot.begin(); + r != mStreamSlot.end(); r++) { + result = String8::format(" Stream slot buffer %d:\n", i); + write(fd, result.string(), result.size()); + dump_indented_camera_metadata(*r, fd, 2, 10); + i++; + } + } + if (mEntries.size() == 0) { + result = " Main queue is empty\n"; + write(fd, result.string(), result.size()); + } else { + result = String8::format(" Main queue has %d entries:\n", + mEntries.size()); + int i = 0; + for (List<camera_metadata_t*>::iterator r = mEntries.begin(); + r != mEntries.end(); r++) { + result = String8::format(" Queue entry %d:\n", i); + write(fd, result.string(), result.size()); + dump_indented_camera_metadata(*r, fd, 2, 10); + i++; + } + } + + if (notLocked == 0) { + mMutex.unlock(); + } + + return OK; +} + +status_t Camera2Device::MetadataQueue::signalConsumerLocked() { + ATRACE_CALL(); + status_t res = OK; + notEmpty.signal(); + if (mSignalConsumer && mDevice != NULL) { + mSignalConsumer = false; + + mMutex.unlock(); + ALOGV("%s: Signaling consumer", __FUNCTION__); + res = mDevice->ops->notify_request_queue_not_empty(mDevice); + mMutex.lock(); + } + return res; +} + +status_t Camera2Device::MetadataQueue::freeBuffers( + List<camera_metadata_t*>::iterator start, + List<camera_metadata_t*>::iterator end) +{ + ATRACE_CALL(); + while (start != end) { + free_camera_metadata(*start); + start = mStreamSlot.erase(start); + } + return OK; +} + +Camera2Device::MetadataQueue* Camera2Device::MetadataQueue::getInstance( + const camera2_request_queue_src_ops_t *q) +{ + const MetadataQueue* cmq = static_cast<const MetadataQueue*>(q); + return const_cast<MetadataQueue*>(cmq); +} + +Camera2Device::MetadataQueue* Camera2Device::MetadataQueue::getInstance( + const camera2_frame_queue_dst_ops_t *q) +{ + const MetadataQueue* cmq = static_cast<const MetadataQueue*>(q); + return const_cast<MetadataQueue*>(cmq); +} + +int Camera2Device::MetadataQueue::consumer_buffer_count( + const camera2_request_queue_src_ops_t *q) +{ + MetadataQueue *queue = getInstance(q); + return queue->getBufferCount(); +} + +int Camera2Device::MetadataQueue::consumer_dequeue( + const camera2_request_queue_src_ops_t *q, + camera_metadata_t **buffer) +{ + MetadataQueue *queue = getInstance(q); + return queue->dequeue(buffer, true); +} + +int Camera2Device::MetadataQueue::consumer_free( + const camera2_request_queue_src_ops_t *q, + camera_metadata_t *old_buffer) +{ + ATRACE_CALL(); + MetadataQueue *queue = getInstance(q); + free_camera_metadata(old_buffer); + return OK; +} + +int Camera2Device::MetadataQueue::producer_dequeue( + const camera2_frame_queue_dst_ops_t *q, + size_t entries, size_t bytes, + camera_metadata_t **buffer) +{ + ATRACE_CALL(); + camera_metadata_t *new_buffer = + allocate_camera_metadata(entries, bytes); + if (new_buffer == NULL) return NO_MEMORY; + *buffer = new_buffer; + return OK; +} + +int Camera2Device::MetadataQueue::producer_cancel( + const camera2_frame_queue_dst_ops_t *q, + camera_metadata_t *old_buffer) +{ + ATRACE_CALL(); + free_camera_metadata(old_buffer); + return OK; +} + +int Camera2Device::MetadataQueue::producer_enqueue( + const camera2_frame_queue_dst_ops_t *q, + camera_metadata_t *filled_buffer) +{ + MetadataQueue *queue = getInstance(q); + return queue->enqueue(filled_buffer); +} + +/** + * Camera2Device::StreamAdapter + */ + +#ifndef container_of +#define container_of(ptr, type, member) \ + (type *)((char*)(ptr) - offsetof(type, member)) +#endif + +Camera2Device::StreamAdapter::StreamAdapter(camera2_device_t *d): + mState(RELEASED), + mDevice(d), + mId(-1), + mWidth(0), mHeight(0), mFormat(0), mSize(0), mUsage(0), + mMaxProducerBuffers(0), mMaxConsumerBuffers(0), + mTotalBuffers(0), + mFormatRequested(0), + mActiveBuffers(0), + mFrameCount(0), + mLastTimestamp(0) +{ + camera2_stream_ops::dequeue_buffer = dequeue_buffer; + camera2_stream_ops::enqueue_buffer = enqueue_buffer; + camera2_stream_ops::cancel_buffer = cancel_buffer; + camera2_stream_ops::set_crop = set_crop; +} + +Camera2Device::StreamAdapter::~StreamAdapter() { + ATRACE_CALL(); + if (mState != RELEASED) { + release(); + } +} + +status_t Camera2Device::StreamAdapter::connectToDevice( + sp<ANativeWindow> consumer, + uint32_t width, uint32_t height, int format, size_t size) { + ATRACE_CALL(); + status_t res; + ALOGV("%s: E", __FUNCTION__); + + if (mState != RELEASED) return INVALID_OPERATION; + if (consumer == NULL) { + ALOGE("%s: Null consumer passed to stream adapter", __FUNCTION__); + return BAD_VALUE; + } + + ALOGV("%s: New stream parameters %d x %d, format 0x%x, size %d", + __FUNCTION__, width, height, format, size); + + mConsumerInterface = consumer; + mWidth = width; + mHeight = height; + mSize = (format == HAL_PIXEL_FORMAT_BLOB) ? size : 0; + mFormatRequested = format; + + // Allocate device-side stream interface + + uint32_t id; + uint32_t formatActual; + uint32_t usage; + uint32_t maxBuffers = 2; + res = mDevice->ops->allocate_stream(mDevice, + mWidth, mHeight, mFormatRequested, getStreamOps(), + &id, &formatActual, &usage, &maxBuffers); + if (res != OK) { + ALOGE("%s: Device stream allocation failed: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + + ALOGV("%s: Allocated stream id %d, actual format 0x%x, " + "usage 0x%x, producer wants %d buffers", __FUNCTION__, + id, formatActual, usage, maxBuffers); + + mId = id; + mFormat = formatActual; + mUsage = usage; + mMaxProducerBuffers = maxBuffers; + + mState = ALLOCATED; + + // Configure consumer-side ANativeWindow interface + res = native_window_api_connect(mConsumerInterface.get(), + NATIVE_WINDOW_API_CAMERA); + if (res != OK) { + ALOGE("%s: Unable to connect to native window for stream %d", + __FUNCTION__, mId); + + return res; + } + + mState = CONNECTED; + + res = native_window_set_usage(mConsumerInterface.get(), mUsage); + if (res != OK) { + ALOGE("%s: Unable to configure usage %08x for stream %d", + __FUNCTION__, mUsage, mId); + return res; + } + + res = native_window_set_scaling_mode(mConsumerInterface.get(), + NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + if (res != OK) { + ALOGE("%s: Unable to configure stream scaling: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + + res = setTransform(0); + if (res != OK) { + return res; + } + + if (mFormat == HAL_PIXEL_FORMAT_BLOB) { + res = native_window_set_buffers_geometry(mConsumerInterface.get(), + mSize, 1, mFormat); + if (res != OK) { + ALOGE("%s: Unable to configure compressed stream buffer geometry" + " %d x %d, size %d for stream %d", + __FUNCTION__, mWidth, mHeight, mSize, mId); + return res; + } + } else { + res = native_window_set_buffers_geometry(mConsumerInterface.get(), + mWidth, mHeight, mFormat); + 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); + return res; + } + } + + int maxConsumerBuffers; + res = mConsumerInterface->query(mConsumerInterface.get(), + NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &maxConsumerBuffers); + if (res != OK) { + ALOGE("%s: Unable to query consumer undequeued" + " buffer count for stream %d", __FUNCTION__, mId); + return res; + } + mMaxConsumerBuffers = maxConsumerBuffers; + + ALOGV("%s: Consumer wants %d buffers", __FUNCTION__, + mMaxConsumerBuffers); + + mTotalBuffers = mMaxConsumerBuffers + mMaxProducerBuffers; + mActiveBuffers = 0; + mFrameCount = 0; + mLastTimestamp = 0; + + res = native_window_set_buffer_count(mConsumerInterface.get(), + mTotalBuffers); + if (res != OK) { + ALOGE("%s: Unable to set buffer count for stream %d", + __FUNCTION__, mId); + return res; + } + + // Register allocated buffers with HAL device + buffer_handle_t *buffers = new buffer_handle_t[mTotalBuffers]; + ANativeWindowBuffer **anwBuffers = new ANativeWindowBuffer*[mTotalBuffers]; + uint32_t bufferIdx = 0; + for (; bufferIdx < mTotalBuffers; bufferIdx++) { + res = native_window_dequeue_buffer_and_wait(mConsumerInterface.get(), + &anwBuffers[bufferIdx]); + if (res != OK) { + ALOGE("%s: Unable to dequeue buffer %d for initial registration for " + "stream %d", __FUNCTION__, bufferIdx, mId); + goto cleanUpBuffers; + } + + buffers[bufferIdx] = anwBuffers[bufferIdx]->handle; + ALOGV("%s: Buffer %p allocated", __FUNCTION__, (void*)buffers[bufferIdx]); + } + + ALOGV("%s: Registering %d buffers with camera HAL", __FUNCTION__, mTotalBuffers); + res = mDevice->ops->register_stream_buffers(mDevice, + mId, + mTotalBuffers, + buffers); + if (res != OK) { + ALOGE("%s: Unable to register buffers with HAL device for stream %d", + __FUNCTION__, mId); + } else { + mState = ACTIVE; + } + +cleanUpBuffers: + ALOGV("%s: Cleaning up %d buffers", __FUNCTION__, bufferIdx); + for (uint32_t i = 0; i < bufferIdx; i++) { + res = mConsumerInterface->cancelBuffer(mConsumerInterface.get(), + anwBuffers[i], -1); + if (res != OK) { + ALOGE("%s: Unable to cancel buffer %d after registration", + __FUNCTION__, i); + } + } + delete[] anwBuffers; + delete[] buffers; + + return res; +} + +status_t Camera2Device::StreamAdapter::release() { + ATRACE_CALL(); + status_t res; + ALOGV("%s: Releasing stream %d", __FUNCTION__, mId); + if (mState >= ALLOCATED) { + res = mDevice->ops->release_stream(mDevice, mId); + if (res != OK) { + ALOGE("%s: Unable to release stream %d", + __FUNCTION__, mId); + return res; + } + } + if (mState >= CONNECTED) { + res = native_window_api_disconnect(mConsumerInterface.get(), + NATIVE_WINDOW_API_CAMERA); + + /* this is not an error. if client calling process dies, + the window will also die and all calls to it will return + DEAD_OBJECT, thus it's already "disconnected" */ + if (res == DEAD_OBJECT) { + ALOGW("%s: While disconnecting stream %d from native window, the" + " native window died from under us", __FUNCTION__, mId); + } + else if (res != OK) { + ALOGE("%s: Unable to disconnect stream %d from native window (error %d %s)", + __FUNCTION__, mId, res, strerror(-res)); + return res; + } + } + mId = -1; + mState = RELEASED; + return OK; +} + +status_t Camera2Device::StreamAdapter::setTransform(int transform) { + ATRACE_CALL(); + status_t res; + if (mState < CONNECTED) { + ALOGE("%s: Cannot set transform on unconnected stream", __FUNCTION__); + return INVALID_OPERATION; + } + res = native_window_set_buffers_transform(mConsumerInterface.get(), + transform); + if (res != OK) { + ALOGE("%s: Unable to configure stream transform to %x: %s (%d)", + __FUNCTION__, transform, strerror(-res), res); + } + return res; +} + +status_t Camera2Device::StreamAdapter::dump(int fd, + const Vector<String16>& args) { + ATRACE_CALL(); + String8 result = String8::format(" Stream %d: %d x %d, format 0x%x\n", + mId, mWidth, mHeight, mFormat); + result.appendFormat(" size %d, usage 0x%x, requested format 0x%x\n", + mSize, mUsage, mFormatRequested); + result.appendFormat(" total buffers: %d, dequeued buffers: %d\n", + mTotalBuffers, mActiveBuffers); + result.appendFormat(" frame count: %d, last timestamp %lld\n", + mFrameCount, mLastTimestamp); + write(fd, result.string(), result.size()); + return OK; +} + +const camera2_stream_ops *Camera2Device::StreamAdapter::getStreamOps() { + return static_cast<camera2_stream_ops *>(this); +} + +ANativeWindow* Camera2Device::StreamAdapter::toANW( + const camera2_stream_ops_t *w) { + return static_cast<const StreamAdapter*>(w)->mConsumerInterface.get(); +} + +int Camera2Device::StreamAdapter::dequeue_buffer(const camera2_stream_ops_t *w, + buffer_handle_t** buffer) { + ATRACE_CALL(); + int res; + StreamAdapter* stream = + const_cast<StreamAdapter*>(static_cast<const StreamAdapter*>(w)); + if (stream->mState != ACTIVE) { + ALOGE("%s: Called when in bad state: %d", __FUNCTION__, stream->mState); + return INVALID_OPERATION; + } + + ANativeWindow *a = toANW(w); + ANativeWindowBuffer* anb; + res = native_window_dequeue_buffer_and_wait(a, &anb); + if (res != OK) { + ALOGE("Stream %d dequeue: Error from native_window: %s (%d)", stream->mId, + strerror(-res), res); + return res; + } + + *buffer = &(anb->handle); + stream->mActiveBuffers++; + + ALOGVV("Stream %d dequeue: Buffer %p dequeued", stream->mId, (void*)(**buffer)); + return res; +} + +int Camera2Device::StreamAdapter::enqueue_buffer(const camera2_stream_ops_t* w, + int64_t timestamp, + buffer_handle_t* buffer) { + ATRACE_CALL(); + StreamAdapter *stream = + const_cast<StreamAdapter*>(static_cast<const StreamAdapter*>(w)); + stream->mFrameCount++; + ALOGVV("Stream %d enqueue: Frame %d (%p) captured at %lld ns", + stream->mId, stream->mFrameCount, (void*)(*buffer), timestamp); + int state = stream->mState; + if (state != ACTIVE) { + ALOGE("%s: Called when in bad state: %d", __FUNCTION__, state); + return INVALID_OPERATION; + } + ANativeWindow *a = toANW(w); + status_t err; + + err = native_window_set_buffers_timestamp(a, timestamp); + if (err != OK) { + ALOGE("%s: Error setting timestamp on native window: %s (%d)", + __FUNCTION__, strerror(-err), err); + return err; + } + err = a->queueBuffer(a, + container_of(buffer, ANativeWindowBuffer, handle), -1); + if (err != OK) { + ALOGE("%s: Error queueing buffer to native window: %s (%d)", + __FUNCTION__, strerror(-err), err); + return err; + } + + stream->mActiveBuffers--; + stream->mLastTimestamp = timestamp; + return OK; +} + +int Camera2Device::StreamAdapter::cancel_buffer(const camera2_stream_ops_t* w, + buffer_handle_t* buffer) { + ATRACE_CALL(); + StreamAdapter *stream = + const_cast<StreamAdapter*>(static_cast<const StreamAdapter*>(w)); + ALOGVV("Stream %d cancel: Buffer %p", + stream->mId, (void*)(*buffer)); + if (stream->mState != ACTIVE) { + ALOGE("%s: Called when in bad state: %d", __FUNCTION__, stream->mState); + return INVALID_OPERATION; + } + + ANativeWindow *a = toANW(w); + int err = a->cancelBuffer(a, + container_of(buffer, ANativeWindowBuffer, handle), -1); + if (err != OK) { + ALOGE("%s: Error canceling buffer to native window: %s (%d)", + __FUNCTION__, strerror(-err), err); + return err; + } + + stream->mActiveBuffers--; + return OK; +} + +int Camera2Device::StreamAdapter::set_crop(const camera2_stream_ops_t* w, + int left, int top, int right, int bottom) { + ATRACE_CALL(); + int state = static_cast<const StreamAdapter*>(w)->mState; + if (state != ACTIVE) { + ALOGE("%s: Called when in bad state: %d", __FUNCTION__, state); + return INVALID_OPERATION; + } + ANativeWindow *a = toANW(w); + android_native_rect_t crop = { left, top, right, bottom }; + return native_window_set_crop(a, &crop); +} + +/** + * Camera2Device::ReprocessStreamAdapter + */ + +#ifndef container_of +#define container_of(ptr, type, member) \ + (type *)((char*)(ptr) - offsetof(type, member)) +#endif + +Camera2Device::ReprocessStreamAdapter::ReprocessStreamAdapter(camera2_device_t *d): + mState(RELEASED), + mDevice(d), + mId(-1), + mWidth(0), mHeight(0), mFormat(0), + mActiveBuffers(0), + mFrameCount(0) +{ + ATRACE_CALL(); + camera2_stream_in_ops::acquire_buffer = acquire_buffer; + camera2_stream_in_ops::release_buffer = release_buffer; +} + +Camera2Device::ReprocessStreamAdapter::~ReprocessStreamAdapter() { + ATRACE_CALL(); + if (mState != RELEASED) { + release(); + } +} + +status_t Camera2Device::ReprocessStreamAdapter::connectToDevice( + const sp<StreamAdapter> &outputStream) { + ATRACE_CALL(); + status_t res; + ALOGV("%s: E", __FUNCTION__); + + if (mState != RELEASED) return INVALID_OPERATION; + if (outputStream == NULL) { + ALOGE("%s: Null base stream passed to reprocess stream adapter", + __FUNCTION__); + return BAD_VALUE; + } + + mBaseStream = outputStream; + mWidth = outputStream->getWidth(); + mHeight = outputStream->getHeight(); + mFormat = outputStream->getFormat(); + + ALOGV("%s: New reprocess stream parameters %d x %d, format 0x%x", + __FUNCTION__, mWidth, mHeight, mFormat); + + // Allocate device-side stream interface + + uint32_t id; + res = mDevice->ops->allocate_reprocess_stream_from_stream(mDevice, + outputStream->getId(), getStreamOps(), + &id); + if (res != OK) { + ALOGE("%s: Device reprocess stream allocation failed: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + + ALOGV("%s: Allocated reprocess stream id %d based on stream %d", + __FUNCTION__, id, outputStream->getId()); + + mId = id; + + mState = ACTIVE; + + return OK; +} + +status_t Camera2Device::ReprocessStreamAdapter::release() { + ATRACE_CALL(); + status_t res; + ALOGV("%s: Releasing stream %d", __FUNCTION__, mId); + if (mState >= ACTIVE) { + res = mDevice->ops->release_reprocess_stream(mDevice, mId); + if (res != OK) { + ALOGE("%s: Unable to release stream %d", + __FUNCTION__, mId); + return res; + } + } + + List<QueueEntry>::iterator s; + for (s = mQueue.begin(); s != mQueue.end(); s++) { + sp<BufferReleasedListener> listener = s->releaseListener.promote(); + if (listener != 0) listener->onBufferReleased(s->handle); + } + for (s = mInFlightQueue.begin(); s != mInFlightQueue.end(); s++) { + sp<BufferReleasedListener> listener = s->releaseListener.promote(); + if (listener != 0) listener->onBufferReleased(s->handle); + } + mQueue.clear(); + mInFlightQueue.clear(); + + mState = RELEASED; + return OK; +} + +status_t Camera2Device::ReprocessStreamAdapter::pushIntoStream( + buffer_handle_t *handle, const wp<BufferReleasedListener> &releaseListener) { + ATRACE_CALL(); + // TODO: Some error checking here would be nice + ALOGV("%s: Pushing buffer %p to stream", __FUNCTION__, (void*)(*handle)); + + QueueEntry entry; + entry.handle = handle; + entry.releaseListener = releaseListener; + mQueue.push_back(entry); + return OK; +} + +status_t Camera2Device::ReprocessStreamAdapter::dump(int fd, + const Vector<String16>& args) { + ATRACE_CALL(); + String8 result = + String8::format(" Reprocess stream %d: %d x %d, fmt 0x%x\n", + mId, mWidth, mHeight, mFormat); + result.appendFormat(" acquired buffers: %d\n", + mActiveBuffers); + result.appendFormat(" frame count: %d\n", + mFrameCount); + write(fd, result.string(), result.size()); + return OK; +} + +const camera2_stream_in_ops *Camera2Device::ReprocessStreamAdapter::getStreamOps() { + return static_cast<camera2_stream_in_ops *>(this); +} + +int Camera2Device::ReprocessStreamAdapter::acquire_buffer( + const camera2_stream_in_ops_t *w, + buffer_handle_t** buffer) { + ATRACE_CALL(); + int res; + ReprocessStreamAdapter* stream = + const_cast<ReprocessStreamAdapter*>( + static_cast<const ReprocessStreamAdapter*>(w)); + if (stream->mState != ACTIVE) { + ALOGE("%s: Called when in bad state: %d", __FUNCTION__, stream->mState); + return INVALID_OPERATION; + } + + if (stream->mQueue.empty()) { + *buffer = NULL; + return OK; + } + + QueueEntry &entry = *(stream->mQueue.begin()); + + *buffer = entry.handle; + + stream->mInFlightQueue.push_back(entry); + stream->mQueue.erase(stream->mQueue.begin()); + + stream->mActiveBuffers++; + + ALOGV("Stream %d acquire: Buffer %p acquired", stream->mId, + (void*)(**buffer)); + return OK; +} + +int Camera2Device::ReprocessStreamAdapter::release_buffer( + const camera2_stream_in_ops_t* w, + buffer_handle_t* buffer) { + ATRACE_CALL(); + ReprocessStreamAdapter *stream = + const_cast<ReprocessStreamAdapter*>( + static_cast<const ReprocessStreamAdapter*>(w) ); + stream->mFrameCount++; + ALOGV("Reprocess stream %d release: Frame %d (%p)", + stream->mId, stream->mFrameCount, (void*)*buffer); + int state = stream->mState; + if (state != ACTIVE) { + ALOGE("%s: Called when in bad state: %d", __FUNCTION__, state); + return INVALID_OPERATION; + } + stream->mActiveBuffers--; + + List<QueueEntry>::iterator s; + for (s = stream->mInFlightQueue.begin(); s != stream->mInFlightQueue.end(); s++) { + if ( s->handle == buffer ) break; + } + if (s == stream->mInFlightQueue.end()) { + ALOGE("%s: Can't find buffer %p in in-flight list!", __FUNCTION__, + buffer); + return INVALID_OPERATION; + } + + sp<BufferReleasedListener> listener = s->releaseListener.promote(); + if (listener != 0) { + listener->onBufferReleased(s->handle); + } else { + ALOGE("%s: Can't free buffer - missing listener", __FUNCTION__); + } + stream->mInFlightQueue.erase(s); + + return OK; +} + +}; // namespace android diff --git a/services/camera/libcameraservice/Camera2Device.h b/services/camera/libcameraservice/Camera2Device.h new file mode 100644 index 00000000..41df2e4b --- /dev/null +++ b/services/camera/libcameraservice/Camera2Device.h @@ -0,0 +1,476 @@ +/* + * 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_SERVERS_CAMERA_CAMERA2DEVICE_H +#define ANDROID_SERVERS_CAMERA_CAMERA2DEVICE_H + +#include <utils/Condition.h> +#include <utils/Errors.h> +#include <utils/List.h> +#include <utils/Mutex.h> +#include <utils/RefBase.h> +#include <utils/String8.h> +#include <utils/String16.h> +#include <utils/Vector.h> + +#include "hardware/camera2.h" +#include "camera2/CameraMetadata.h" + +namespace android { + +class Camera2Device : public virtual RefBase { + public: + typedef camera2::CameraMetadata CameraMetadata; + + Camera2Device(int id); + + ~Camera2Device(); + + status_t initialize(camera_module_t *module); + status_t disconnect(); + + status_t dump(int fd, const Vector<String16>& args); + + /** + * The device's static characteristics metadata buffer + */ + const CameraMetadata& info() const; + + /** + * Submit request for capture. The Camera2Device takes ownership of the + * passed-in buffer. + */ + status_t capture(CameraMetadata &request); + + /** + * Submit request for streaming. The Camera2Device makes a copy of the + * passed-in buffer and the caller retains ownership. + */ + status_t setStreamingRequest(const CameraMetadata &request); + + /** + * Clear the streaming request slot. + */ + status_t clearStreamingRequest(); + + /** + * Wait until a request with the given ID has been dequeued by the + * HAL. Returns TIMED_OUT if the timeout duration is reached. Returns + * immediately if the latest request received by the HAL has this id. + */ + status_t waitUntilRequestReceived(int32_t requestId, nsecs_t timeout); + + /** + * Create an output stream of the requested size and format. + * + * If format is CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, then the HAL device selects + * an appropriate format; it can be queried with getStreamInfo. + * + * If format is HAL_PIXEL_FORMAT_COMPRESSED, the size parameter must be + * equal to the size in bytes of the buffers to allocate for the stream. For + * other formats, the size parameter is ignored. + */ + status_t createStream(sp<ANativeWindow> consumer, + uint32_t width, uint32_t height, int format, size_t size, + int *id); + + /** + * Create an input reprocess stream that uses buffers from an existing + * output stream. + */ + status_t createReprocessStreamFromStream(int outputId, int *id); + + /** + * Get information about a given stream. + */ + status_t getStreamInfo(int id, + uint32_t *width, uint32_t *height, uint32_t *format); + + /** + * Set stream gralloc buffer transform + */ + status_t setStreamTransform(int id, int transform); + + /** + * Delete stream. Must not be called if there are requests in flight which + * reference that stream. + */ + status_t deleteStream(int id); + + /** + * Delete reprocess stream. Must not be called if there are requests in + * flight which reference that stream. + */ + status_t deleteReprocessStream(int id); + + /** + * Create a metadata buffer with fields that the HAL device believes are + * best for the given use case + */ + status_t createDefaultRequest(int templateId, CameraMetadata *request); + + /** + * Wait until all requests have been processed. Returns INVALID_OPERATION if + * the streaming slot is not empty, or TIMED_OUT if the requests haven't + * finished processing in 10 seconds. + */ + status_t waitUntilDrained(); + + /** + * Abstract class for HAL notification listeners + */ + class NotificationListener { + public: + // Refer to the Camera2 HAL definition for notification definitions + virtual void notifyError(int errorCode, int arg1, int arg2) = 0; + virtual void notifyShutter(int frameNumber, nsecs_t timestamp) = 0; + virtual void notifyAutoFocus(uint8_t newState, int triggerId) = 0; + virtual void notifyAutoExposure(uint8_t newState, int triggerId) = 0; + virtual void notifyAutoWhitebalance(uint8_t newState, int triggerId) = 0; + protected: + virtual ~NotificationListener(); + }; + + /** + * Connect HAL notifications to a listener. Overwrites previous + * listener. Set to NULL to stop receiving notifications. + */ + status_t setNotifyCallback(NotificationListener *listener); + + /** + * Wait for a new frame to be produced, with timeout in nanoseconds. + * Returns TIMED_OUT when no frame produced within the specified duration + */ + status_t waitForNextFrame(nsecs_t timeout); + + /** + * Get next metadata frame from the frame queue. Returns NULL if the queue + * is empty; caller takes ownership of the metadata buffer. + */ + status_t getNextFrame(CameraMetadata *frame); + + /** + * Trigger auto-focus. The latest ID used in a trigger autofocus or cancel + * autofocus call will be returned by the HAL in all subsequent AF + * notifications. + */ + status_t triggerAutofocus(uint32_t id); + + /** + * Cancel auto-focus. The latest ID used in a trigger autofocus/cancel + * autofocus call will be returned by the HAL in all subsequent AF + * notifications. + */ + status_t triggerCancelAutofocus(uint32_t id); + + /** + * Trigger pre-capture metering. The latest ID used in a trigger pre-capture + * call will be returned by the HAL in all subsequent AE and AWB + * notifications. + */ + status_t triggerPrecaptureMetering(uint32_t id); + + /** + * Abstract interface for clients that want to listen to reprocess buffer + * release events + */ + struct BufferReleasedListener: public virtual RefBase { + virtual void onBufferReleased(buffer_handle_t *handle) = 0; + }; + + /** + * Push a buffer to be reprocessed into a reprocessing stream, and + * provide a listener to call once the buffer is returned by the HAL + */ + status_t pushReprocessBuffer(int reprocessStreamId, + buffer_handle_t *buffer, wp<BufferReleasedListener> listener); + + private: + const int mId; + camera2_device_t *mDevice; + + CameraMetadata mDeviceInfo; + vendor_tag_query_ops_t *mVendorTagOps; + + /** + * Queue class for both sending requests to a camera2 device, and for + * receiving frames from a camera2 device. + */ + class MetadataQueue: public camera2_request_queue_src_ops_t, + public camera2_frame_queue_dst_ops_t { + public: + MetadataQueue(); + ~MetadataQueue(); + + // Interface to camera2 HAL device, either for requests (device is + // consumer) or for frames (device is producer) + const camera2_request_queue_src_ops_t* getToConsumerInterface(); + void setFromConsumerInterface(camera2_device_t *d); + + // Connect queue consumer endpoint to a camera2 device + status_t setConsumerDevice(camera2_device_t *d); + // Connect queue producer endpoint to a camera2 device + status_t setProducerDevice(camera2_device_t *d); + + const camera2_frame_queue_dst_ops_t* getToProducerInterface(); + + // Real interfaces. On enqueue, queue takes ownership of buffer pointer + // On dequeue, user takes ownership of buffer pointer. + status_t enqueue(camera_metadata_t *buf); + status_t dequeue(camera_metadata_t **buf, bool incrementCount = false); + int getBufferCount(); + status_t waitForBuffer(nsecs_t timeout); + // Wait until a buffer with the given ID is dequeued. Will return + // immediately if the latest buffer dequeued has that ID. + status_t waitForDequeue(int32_t id, nsecs_t timeout); + + // Set repeating buffer(s); if the queue is empty on a dequeue call, the + // queue copies the contents of the stream slot into the queue, and then + // dequeues the first new entry. The metadata buffers passed in are + // copied. + status_t setStreamSlot(camera_metadata_t *buf); + status_t setStreamSlot(const List<camera_metadata_t*> &bufs); + + status_t dump(int fd, const Vector<String16>& args); + + private: + status_t signalConsumerLocked(); + status_t freeBuffers(List<camera_metadata_t*>::iterator start, + List<camera_metadata_t*>::iterator end); + + camera2_device_t *mDevice; + + Mutex mMutex; + Condition notEmpty; + + int mFrameCount; + int32_t mLatestRequestId; + Condition mNewRequestId; + + int mCount; + List<camera_metadata_t*> mEntries; + int mStreamSlotCount; + List<camera_metadata_t*> mStreamSlot; + + bool mSignalConsumer; + + static MetadataQueue* getInstance( + const camera2_frame_queue_dst_ops_t *q); + static MetadataQueue* getInstance( + const camera2_request_queue_src_ops_t *q); + + static int consumer_buffer_count( + const camera2_request_queue_src_ops_t *q); + + static int consumer_dequeue(const camera2_request_queue_src_ops_t *q, + camera_metadata_t **buffer); + + static int consumer_free(const camera2_request_queue_src_ops_t *q, + camera_metadata_t *old_buffer); + + static int producer_dequeue(const camera2_frame_queue_dst_ops_t *q, + size_t entries, size_t bytes, + camera_metadata_t **buffer); + + static int producer_cancel(const camera2_frame_queue_dst_ops_t *q, + camera_metadata_t *old_buffer); + + static int producer_enqueue(const camera2_frame_queue_dst_ops_t *q, + camera_metadata_t *filled_buffer); + + }; // class MetadataQueue + + MetadataQueue mRequestQueue; + MetadataQueue mFrameQueue; + + /** + * Adapter from an ANativeWindow interface to camera2 device stream ops. + * Also takes care of allocating/deallocating stream in device interface + */ + class StreamAdapter: public camera2_stream_ops, public virtual RefBase { + public: + StreamAdapter(camera2_device_t *d); + + ~StreamAdapter(); + + /** + * Create a HAL device stream of the requested size and format. + * + * If format is CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, then the HAL device + * selects an appropriate format; it can be queried with getFormat. + * + * If format is HAL_PIXEL_FORMAT_COMPRESSED, the size parameter must + * be equal to the size in bytes of the buffers to allocate for the + * stream. For other formats, the size parameter is ignored. + */ + status_t connectToDevice(sp<ANativeWindow> consumer, + uint32_t width, uint32_t height, int format, size_t size); + + status_t release(); + + status_t setTransform(int transform); + + // Get stream parameters. + // Only valid after a successful connectToDevice call. + int getId() const { return mId; } + uint32_t getWidth() const { return mWidth; } + uint32_t getHeight() const { return mHeight; } + uint32_t getFormat() const { return mFormat; } + + // Dump stream information + status_t dump(int fd, const Vector<String16>& args); + + private: + enum { + ERROR = -1, + RELEASED = 0, + ALLOCATED, + CONNECTED, + ACTIVE + } mState; + + sp<ANativeWindow> mConsumerInterface; + camera2_device_t *mDevice; + + uint32_t mId; + uint32_t mWidth; + uint32_t mHeight; + uint32_t mFormat; + size_t mSize; + uint32_t mUsage; + uint32_t mMaxProducerBuffers; + uint32_t mMaxConsumerBuffers; + uint32_t mTotalBuffers; + int mFormatRequested; + + /** Debugging information */ + uint32_t mActiveBuffers; + uint32_t mFrameCount; + int64_t mLastTimestamp; + + const camera2_stream_ops *getStreamOps(); + + static ANativeWindow* toANW(const camera2_stream_ops_t *w); + + static int dequeue_buffer(const camera2_stream_ops_t *w, + buffer_handle_t** buffer); + + static int enqueue_buffer(const camera2_stream_ops_t* w, + int64_t timestamp, + buffer_handle_t* buffer); + + static int cancel_buffer(const camera2_stream_ops_t* w, + buffer_handle_t* buffer); + + static int set_crop(const camera2_stream_ops_t* w, + int left, int top, int right, int bottom); + }; // class StreamAdapter + + typedef List<sp<StreamAdapter> > StreamList; + StreamList mStreams; + + /** + * Adapter from an ANativeWindow interface to camera2 device stream ops. + * Also takes care of allocating/deallocating stream in device interface + */ + class ReprocessStreamAdapter: public camera2_stream_in_ops, public virtual RefBase { + public: + ReprocessStreamAdapter(camera2_device_t *d); + + ~ReprocessStreamAdapter(); + + /** + * Create a HAL device reprocess stream based on an existing output stream. + */ + status_t connectToDevice(const sp<StreamAdapter> &outputStream); + + status_t release(); + + /** + * Push buffer into stream for reprocessing. Takes ownership until it notifies + * that the buffer has been released + */ + status_t pushIntoStream(buffer_handle_t *handle, + const wp<BufferReleasedListener> &releaseListener); + + /** + * Get stream parameters. + * Only valid after a successful connectToDevice call. + */ + int getId() const { return mId; } + uint32_t getWidth() const { return mWidth; } + uint32_t getHeight() const { return mHeight; } + uint32_t getFormat() const { return mFormat; } + + // Dump stream information + status_t dump(int fd, const Vector<String16>& args); + + private: + enum { + ERROR = -1, + RELEASED = 0, + ACTIVE + } mState; + + sp<ANativeWindow> mConsumerInterface; + wp<StreamAdapter> mBaseStream; + + struct QueueEntry { + buffer_handle_t *handle; + wp<BufferReleasedListener> releaseListener; + }; + + List<QueueEntry> mQueue; + + List<QueueEntry> mInFlightQueue; + + camera2_device_t *mDevice; + + uint32_t mId; + uint32_t mWidth; + uint32_t mHeight; + uint32_t mFormat; + + /** Debugging information */ + uint32_t mActiveBuffers; + uint32_t mFrameCount; + int64_t mLastTimestamp; + + const camera2_stream_in_ops *getStreamOps(); + + static int acquire_buffer(const camera2_stream_in_ops_t *w, + buffer_handle_t** buffer); + + static int release_buffer(const camera2_stream_in_ops_t* w, + buffer_handle_t* buffer); + + }; // class ReprocessStreamAdapter + + typedef List<sp<ReprocessStreamAdapter> > ReprocessStreamList; + ReprocessStreamList mReprocessStreams; + + // Receives HAL notifications and routes them to the NotificationListener + static void notificationCallback(int32_t msg_type, + int32_t ext1, + int32_t ext2, + int32_t ext3, + void *user); + +}; // class Camera2Device + +}; // namespace android + +#endif diff --git a/services/camera/libcameraservice/CameraClient.cpp b/services/camera/libcameraservice/CameraClient.cpp new file mode 100644 index 00000000..b930c02f --- /dev/null +++ b/services/camera/libcameraservice/CameraClient.cpp @@ -0,0 +1,959 @@ +/* + * 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. + */ + +#define LOG_TAG "CameraClient" +//#define LOG_NDEBUG 0 + +#include <cutils/properties.h> +#include <gui/SurfaceTextureClient.h> +#include <gui/Surface.h> + +#include "CameraClient.h" +#include "CameraHardwareInterface.h" +#include "CameraService.h" + +namespace android { + +#define LOG1(...) ALOGD_IF(gLogLevel >= 1, __VA_ARGS__); +#define LOG2(...) ALOGD_IF(gLogLevel >= 2, __VA_ARGS__); + +static int getCallingPid() { + return IPCThreadState::self()->getCallingPid(); +} + +static int getCallingUid() { + return IPCThreadState::self()->getCallingUid(); +} + +CameraClient::CameraClient(const sp<CameraService>& cameraService, + const sp<ICameraClient>& cameraClient, + int cameraId, int cameraFacing, int clientPid, int servicePid): + Client(cameraService, cameraClient, + cameraId, cameraFacing, clientPid, servicePid) +{ + int callingPid = getCallingPid(); + LOG1("CameraClient::CameraClient E (pid %d, id %d)", callingPid, cameraId); + + mHardware = NULL; + mMsgEnabled = 0; + mSurface = 0; + mPreviewWindow = 0; + mDestructionStarted = false; + + // Callback is disabled by default + mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP; + mOrientation = getOrientation(0, mCameraFacing == CAMERA_FACING_FRONT); + mPlayShutterSound = true; + LOG1("CameraClient::CameraClient X (pid %d, id %d)", callingPid, cameraId); +} + +status_t CameraClient::initialize(camera_module_t *module) { + int callingPid = getCallingPid(); + LOG1("CameraClient::initialize E (pid %d, id %d)", callingPid, mCameraId); + + char camera_device_name[10]; + status_t res; + snprintf(camera_device_name, sizeof(camera_device_name), "%d", mCameraId); + + mHardware = new CameraHardwareInterface(camera_device_name); + res = mHardware->initialize(&module->common); + if (res != OK) { + ALOGE("%s: Camera %d: unable to initialize device: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + mHardware.clear(); + return NO_INIT; + } + + mHardware->setCallbacks(notifyCallback, + dataCallback, + dataCallbackTimestamp, + (void *)mCameraId); + + // Enable zoom, error, focus, and metadata messages by default + enableMsgType(CAMERA_MSG_ERROR | CAMERA_MSG_ZOOM | CAMERA_MSG_FOCUS | + CAMERA_MSG_PREVIEW_METADATA | CAMERA_MSG_FOCUS_MOVE); + + LOG1("CameraClient::initialize X (pid %d, id %d)", callingPid, mCameraId); + return OK; +} + + +// tear down the client +CameraClient::~CameraClient() { + // this lock should never be NULL + Mutex* lock = mCameraService->getClientLockById(mCameraId); + lock->lock(); + mDestructionStarted = true; + // client will not be accessed from callback. should unlock to prevent dead-lock in disconnect + lock->unlock(); + int callingPid = getCallingPid(); + LOG1("CameraClient::~CameraClient E (pid %d, this %p)", callingPid, this); + + disconnect(); + LOG1("CameraClient::~CameraClient X (pid %d, this %p)", callingPid, this); +} + +status_t CameraClient::dump(int fd, const Vector<String16>& args) { + const size_t SIZE = 256; + char buffer[SIZE]; + + size_t len = snprintf(buffer, SIZE, "Client[%d] (%p) PID: %d\n", + mCameraId, + getCameraClient()->asBinder().get(), + mClientPid); + len = (len > SIZE - 1) ? SIZE - 1 : len; + write(fd, buffer, len); + return mHardware->dump(fd, args); +} + +// ---------------------------------------------------------------------------- + +status_t CameraClient::checkPid() const { + int callingPid = getCallingPid(); + if (callingPid == mClientPid) return NO_ERROR; + + ALOGW("attempt to use a locked camera from a different process" + " (old pid %d, new pid %d)", mClientPid, callingPid); + return EBUSY; +} + +status_t CameraClient::checkPidAndHardware() const { + status_t result = checkPid(); + if (result != NO_ERROR) return result; + if (mHardware == 0) { + ALOGE("attempt to use a camera after disconnect() (pid %d)", getCallingPid()); + return INVALID_OPERATION; + } + return NO_ERROR; +} + +status_t CameraClient::lock() { + int callingPid = getCallingPid(); + LOG1("lock (pid %d)", callingPid); + Mutex::Autolock lock(mLock); + + // lock camera to this client if the the camera is unlocked + if (mClientPid == 0) { + mClientPid = callingPid; + return NO_ERROR; + } + + // returns NO_ERROR if the client already owns the camera, EBUSY otherwise + return checkPid(); +} + +status_t CameraClient::unlock() { + int callingPid = getCallingPid(); + LOG1("unlock (pid %d)", callingPid); + Mutex::Autolock lock(mLock); + + // allow anyone to use camera (after they lock the camera) + status_t result = checkPid(); + if (result == NO_ERROR) { + if (mHardware->recordingEnabled()) { + ALOGE("Not allowed to unlock camera during recording."); + return INVALID_OPERATION; + } + mClientPid = 0; + LOG1("clear mCameraClient (pid %d)", callingPid); + // we need to remove the reference to ICameraClient so that when the app + // goes away, the reference count goes to 0. + mCameraClient.clear(); + } + return result; +} + +// connect a new client to the camera +status_t CameraClient::connect(const sp<ICameraClient>& client) { + int callingPid = getCallingPid(); + LOG1("connect E (pid %d)", callingPid); + Mutex::Autolock lock(mLock); + + if (mClientPid != 0 && checkPid() != NO_ERROR) { + ALOGW("Tried to connect to a locked camera (old pid %d, new pid %d)", + mClientPid, callingPid); + return EBUSY; + } + + if (mCameraClient != 0 && (client->asBinder() == mCameraClient->asBinder())) { + LOG1("Connect to the same client"); + return NO_ERROR; + } + + mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP; + mClientPid = callingPid; + mCameraClient = client; + + LOG1("connect X (pid %d)", callingPid); + return NO_ERROR; +} + +static void disconnectWindow(const sp<ANativeWindow>& window) { + if (window != 0) { + status_t result = native_window_api_disconnect(window.get(), + NATIVE_WINDOW_API_CAMERA); + if (result != NO_ERROR) { + ALOGW("native_window_api_disconnect failed: %s (%d)", strerror(-result), + result); + } + } +} + +void CameraClient::disconnect() { + int callingPid = getCallingPid(); + LOG1("disconnect E (pid %d)", callingPid); + Mutex::Autolock lock(mLock); + + // Allow both client and the media server to disconnect at all times + if (callingPid != mClientPid && callingPid != mServicePid) { + ALOGW("different client - don't disconnect"); + return; + } + + if (mClientPid <= 0) { + LOG1("camera is unlocked (mClientPid = %d), don't tear down hardware", mClientPid); + return; + } + + // Make sure disconnect() is done once and once only, whether it is called + // from the user directly, or called by the destructor. + if (mHardware == 0) return; + + LOG1("hardware teardown"); + // Before destroying mHardware, we must make sure it's in the + // idle state. + // Turn off all messages. + disableMsgType(CAMERA_MSG_ALL_MSGS); + mHardware->stopPreview(); + mHardware->cancelPicture(); + // Release the hardware resources. + mHardware->release(); + + // Release the held ANativeWindow resources. + if (mPreviewWindow != 0) { + disconnectWindow(mPreviewWindow); + mPreviewWindow = 0; + mHardware->setPreviewWindow(mPreviewWindow); + } + mHardware.clear(); + + CameraService::Client::disconnect(); + + LOG1("disconnect X (pid %d)", callingPid); +} + +// ---------------------------------------------------------------------------- + +status_t CameraClient::setPreviewWindow(const sp<IBinder>& binder, + const sp<ANativeWindow>& window) { + Mutex::Autolock lock(mLock); + status_t result = checkPidAndHardware(); + if (result != NO_ERROR) return result; + + // return if no change in surface. + if (binder == mSurface) { + return NO_ERROR; + } + + if (window != 0) { + result = native_window_api_connect(window.get(), NATIVE_WINDOW_API_CAMERA); + if (result != NO_ERROR) { + ALOGE("native_window_api_connect failed: %s (%d)", strerror(-result), + result); + return result; + } + } + + // If preview has been already started, register preview buffers now. + if (mHardware->previewEnabled()) { + if (window != 0) { + native_window_set_scaling_mode(window.get(), + NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + native_window_set_buffers_transform(window.get(), mOrientation); + result = mHardware->setPreviewWindow(window); + } + } + + if (result == NO_ERROR) { + // Everything has succeeded. Disconnect the old window and remember the + // new window. + disconnectWindow(mPreviewWindow); + mSurface = binder; + mPreviewWindow = window; + } else { + // Something went wrong after we connected to the new window, so + // disconnect here. + disconnectWindow(window); + } + + return result; +} + +// set the Surface that the preview will use +status_t CameraClient::setPreviewDisplay(const sp<Surface>& surface) { + LOG1("setPreviewDisplay(%p) (pid %d)", surface.get(), getCallingPid()); + + sp<IBinder> binder(surface != 0 ? surface->asBinder() : 0); + sp<ANativeWindow> window(surface); + return setPreviewWindow(binder, window); +} + +// set the SurfaceTexture that the preview will use +status_t CameraClient::setPreviewTexture( + const sp<ISurfaceTexture>& surfaceTexture) { + LOG1("setPreviewTexture(%p) (pid %d)", surfaceTexture.get(), + getCallingPid()); + + sp<IBinder> binder; + sp<ANativeWindow> window; + if (surfaceTexture != 0) { + binder = surfaceTexture->asBinder(); + window = new SurfaceTextureClient(surfaceTexture); + } + return setPreviewWindow(binder, window); +} + +// set the preview callback flag to affect how the received frames from +// preview are handled. +void CameraClient::setPreviewCallbackFlag(int callback_flag) { + LOG1("setPreviewCallbackFlag(%d) (pid %d)", callback_flag, getCallingPid()); + Mutex::Autolock lock(mLock); + if (checkPidAndHardware() != NO_ERROR) return; + + mPreviewCallbackFlag = callback_flag; + if (mPreviewCallbackFlag & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK) { + enableMsgType(CAMERA_MSG_PREVIEW_FRAME); + } else { + disableMsgType(CAMERA_MSG_PREVIEW_FRAME); + } +} + +// start preview mode +status_t CameraClient::startPreview() { + LOG1("startPreview (pid %d)", getCallingPid()); + return startCameraMode(CAMERA_PREVIEW_MODE); +} + +// start recording mode +status_t CameraClient::startRecording() { + LOG1("startRecording (pid %d)", getCallingPid()); + return startCameraMode(CAMERA_RECORDING_MODE); +} + +// start preview or recording +status_t CameraClient::startCameraMode(camera_mode mode) { + LOG1("startCameraMode(%d)", mode); + Mutex::Autolock lock(mLock); + status_t result = checkPidAndHardware(); + if (result != NO_ERROR) return result; + + switch(mode) { + case CAMERA_PREVIEW_MODE: + if (mSurface == 0 && mPreviewWindow == 0) { + LOG1("mSurface is not set yet."); + // still able to start preview in this case. + } + return startPreviewMode(); + case CAMERA_RECORDING_MODE: + if (mSurface == 0 && mPreviewWindow == 0) { + ALOGE("mSurface or mPreviewWindow must be set before startRecordingMode."); + return INVALID_OPERATION; + } + return startRecordingMode(); + default: + return UNKNOWN_ERROR; + } +} + +status_t CameraClient::startPreviewMode() { + LOG1("startPreviewMode"); + status_t result = NO_ERROR; + + // if preview has been enabled, nothing needs to be done + if (mHardware->previewEnabled()) { + return NO_ERROR; + } + + if (mPreviewWindow != 0) { + native_window_set_scaling_mode(mPreviewWindow.get(), + NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + native_window_set_buffers_transform(mPreviewWindow.get(), + mOrientation); + } + mHardware->setPreviewWindow(mPreviewWindow); + result = mHardware->startPreview(); + + return result; +} + +status_t CameraClient::startRecordingMode() { + LOG1("startRecordingMode"); + status_t result = NO_ERROR; + + // if recording has been enabled, nothing needs to be done + if (mHardware->recordingEnabled()) { + return NO_ERROR; + } + + // if preview has not been started, start preview first + if (!mHardware->previewEnabled()) { + result = startPreviewMode(); + if (result != NO_ERROR) { + return result; + } + } + + // start recording mode + enableMsgType(CAMERA_MSG_VIDEO_FRAME); + mCameraService->playSound(CameraService::SOUND_RECORDING); + result = mHardware->startRecording(); + if (result != NO_ERROR) { + ALOGE("mHardware->startRecording() failed with status %d", result); + } + return result; +} + +// stop preview mode +void CameraClient::stopPreview() { + LOG1("stopPreview (pid %d)", getCallingPid()); + Mutex::Autolock lock(mLock); + if (checkPidAndHardware() != NO_ERROR) return; + + + disableMsgType(CAMERA_MSG_PREVIEW_FRAME); + mHardware->stopPreview(); + + mPreviewBuffer.clear(); +} + +// stop recording mode +void CameraClient::stopRecording() { + LOG1("stopRecording (pid %d)", getCallingPid()); + Mutex::Autolock lock(mLock); + if (checkPidAndHardware() != NO_ERROR) return; + + disableMsgType(CAMERA_MSG_VIDEO_FRAME); + mHardware->stopRecording(); + mCameraService->playSound(CameraService::SOUND_RECORDING); + + mPreviewBuffer.clear(); +} + +// release a recording frame +void CameraClient::releaseRecordingFrame(const sp<IMemory>& mem) { + Mutex::Autolock lock(mLock); + if (checkPidAndHardware() != NO_ERROR) return; + mHardware->releaseRecordingFrame(mem); +} + +status_t CameraClient::storeMetaDataInBuffers(bool enabled) +{ + LOG1("storeMetaDataInBuffers: %s", enabled? "true": "false"); + Mutex::Autolock lock(mLock); + if (checkPidAndHardware() != NO_ERROR) { + return UNKNOWN_ERROR; + } + return mHardware->storeMetaDataInBuffers(enabled); +} + +bool CameraClient::previewEnabled() { + LOG1("previewEnabled (pid %d)", getCallingPid()); + + Mutex::Autolock lock(mLock); + if (checkPidAndHardware() != NO_ERROR) return false; + return mHardware->previewEnabled(); +} + +bool CameraClient::recordingEnabled() { + LOG1("recordingEnabled (pid %d)", getCallingPid()); + + Mutex::Autolock lock(mLock); + if (checkPidAndHardware() != NO_ERROR) return false; + return mHardware->recordingEnabled(); +} + +status_t CameraClient::autoFocus() { + LOG1("autoFocus (pid %d)", getCallingPid()); + + Mutex::Autolock lock(mLock); + status_t result = checkPidAndHardware(); + if (result != NO_ERROR) return result; + + return mHardware->autoFocus(); +} + +status_t CameraClient::cancelAutoFocus() { + LOG1("cancelAutoFocus (pid %d)", getCallingPid()); + + Mutex::Autolock lock(mLock); + status_t result = checkPidAndHardware(); + if (result != NO_ERROR) return result; + + return mHardware->cancelAutoFocus(); +} + +// take a picture - image is returned in callback +status_t CameraClient::takePicture(int msgType) { + LOG1("takePicture (pid %d): 0x%x", getCallingPid(), msgType); + + Mutex::Autolock lock(mLock); + status_t result = checkPidAndHardware(); + if (result != NO_ERROR) return result; + + if ((msgType & CAMERA_MSG_RAW_IMAGE) && + (msgType & CAMERA_MSG_RAW_IMAGE_NOTIFY)) { + ALOGE("CAMERA_MSG_RAW_IMAGE and CAMERA_MSG_RAW_IMAGE_NOTIFY" + " cannot be both enabled"); + return BAD_VALUE; + } + + // We only accept picture related message types + // and ignore other types of messages for takePicture(). + int picMsgType = msgType + & (CAMERA_MSG_SHUTTER | + CAMERA_MSG_POSTVIEW_FRAME | + CAMERA_MSG_RAW_IMAGE | + CAMERA_MSG_RAW_IMAGE_NOTIFY | + CAMERA_MSG_COMPRESSED_IMAGE); + + enableMsgType(picMsgType); + + return mHardware->takePicture(); +} + +// set preview/capture parameters - key/value pairs +status_t CameraClient::setParameters(const String8& params) { + LOG1("setParameters (pid %d) (%s)", getCallingPid(), params.string()); + + Mutex::Autolock lock(mLock); + status_t result = checkPidAndHardware(); + if (result != NO_ERROR) return result; + + CameraParameters p(params); + return mHardware->setParameters(p); +} + +// get preview/capture parameters - key/value pairs +String8 CameraClient::getParameters() const { + Mutex::Autolock lock(mLock); + if (checkPidAndHardware() != NO_ERROR) return String8(); + + String8 params(mHardware->getParameters().flatten()); + LOG1("getParameters (pid %d) (%s)", getCallingPid(), params.string()); + return params; +} + +// enable shutter sound +status_t CameraClient::enableShutterSound(bool enable) { + LOG1("enableShutterSound (pid %d)", getCallingPid()); + + status_t result = checkPidAndHardware(); + if (result != NO_ERROR) return result; + + if (enable) { + mPlayShutterSound = true; + 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]; + property_get("ro.camera.sound.forced", value, "0"); + if (strcmp(value, "0") != 0) { + // Disabling shutter sound is not allowed. Deny if the current + // process is not mediaserver. + if (getCallingPid() != getpid()) { + ALOGE("Failed to disable shutter sound. Permission denied (pid %d)", getCallingPid()); + return PERMISSION_DENIED; + } + } + + mPlayShutterSound = false; + return OK; +} + +status_t CameraClient::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) { + LOG1("sendCommand (pid %d)", getCallingPid()); + int orientation; + Mutex::Autolock lock(mLock); + status_t result = checkPidAndHardware(); + if (result != NO_ERROR) return result; + + if (cmd == CAMERA_CMD_SET_DISPLAY_ORIENTATION) { + // Mirror the preview if the camera is front-facing. + orientation = getOrientation(arg1, mCameraFacing == CAMERA_FACING_FRONT); + if (orientation == -1) return BAD_VALUE; + + if (mOrientation != orientation) { + mOrientation = orientation; + if (mPreviewWindow != 0) { + native_window_set_buffers_transform(mPreviewWindow.get(), + mOrientation); + } + } + return OK; + } else if (cmd == CAMERA_CMD_ENABLE_SHUTTER_SOUND) { + switch (arg1) { + case 0: + return enableShutterSound(false); + case 1: + return enableShutterSound(true); + default: + return BAD_VALUE; + } + return OK; + } else if (cmd == CAMERA_CMD_PLAY_RECORDING_SOUND) { + mCameraService->playSound(CameraService::SOUND_RECORDING); + } else if (cmd == CAMERA_CMD_SET_VIDEO_BUFFER_COUNT) { + // Silently ignore this command + return INVALID_OPERATION; + } else if (cmd == CAMERA_CMD_PING) { + // If mHardware is 0, checkPidAndHardware will return error. + return OK; + } + + return mHardware->sendCommand(cmd, arg1, arg2); +} + +// ---------------------------------------------------------------------------- + +void CameraClient::enableMsgType(int32_t msgType) { + android_atomic_or(msgType, &mMsgEnabled); + mHardware->enableMsgType(msgType); +} + +void CameraClient::disableMsgType(int32_t msgType) { + android_atomic_and(~msgType, &mMsgEnabled); + mHardware->disableMsgType(msgType); +} + +#define CHECK_MESSAGE_INTERVAL 10 // 10ms +bool CameraClient::lockIfMessageWanted(int32_t msgType) { + int sleepCount = 0; + while (mMsgEnabled & msgType) { + if (mLock.tryLock() == NO_ERROR) { + if (sleepCount > 0) { + LOG1("lockIfMessageWanted(%d): waited for %d ms", + msgType, sleepCount * CHECK_MESSAGE_INTERVAL); + } + return true; + } + if (sleepCount++ == 0) { + LOG1("lockIfMessageWanted(%d): enter sleep", msgType); + } + usleep(CHECK_MESSAGE_INTERVAL * 1000); + } + ALOGW("lockIfMessageWanted(%d): dropped unwanted message", msgType); + return false; +} + +// Callback messages can be dispatched to internal handlers or pass to our +// client's callback functions, depending on the message type. +// +// notifyCallback: +// CAMERA_MSG_SHUTTER handleShutter +// (others) c->notifyCallback +// dataCallback: +// CAMERA_MSG_PREVIEW_FRAME handlePreviewData +// CAMERA_MSG_POSTVIEW_FRAME handlePostview +// CAMERA_MSG_RAW_IMAGE handleRawPicture +// CAMERA_MSG_COMPRESSED_IMAGE handleCompressedPicture +// (others) c->dataCallback +// dataCallbackTimestamp +// (others) c->dataCallbackTimestamp +// +// NOTE: the *Callback functions grab mLock of the client before passing +// control to handle* functions. So the handle* functions must release the +// lock before calling the ICameraClient's callbacks, so those callbacks can +// invoke methods in the Client class again (For example, the preview frame +// callback may want to releaseRecordingFrame). The handle* functions must +// release the lock after all accesses to member variables, so it must be +// handled very carefully. + +void CameraClient::notifyCallback(int32_t msgType, int32_t ext1, + int32_t ext2, void* user) { + LOG2("notifyCallback(%d)", msgType); + + Mutex* lock = getClientLockFromCookie(user); + if (lock == NULL) return; + Mutex::Autolock alock(*lock); + + CameraClient* client = + static_cast<CameraClient*>(getClientFromCookie(user)); + if (client == NULL) return; + + if (!client->lockIfMessageWanted(msgType)) return; + + switch (msgType) { + case CAMERA_MSG_SHUTTER: + // ext1 is the dimension of the yuv picture. + client->handleShutter(); + break; + default: + client->handleGenericNotify(msgType, ext1, ext2); + break; + } +} + +void CameraClient::dataCallback(int32_t msgType, + const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata, void* user) { + LOG2("dataCallback(%d)", msgType); + + Mutex* lock = getClientLockFromCookie(user); + if (lock == NULL) return; + Mutex::Autolock alock(*lock); + + CameraClient* client = + static_cast<CameraClient*>(getClientFromCookie(user)); + if (client == NULL) return; + + if (!client->lockIfMessageWanted(msgType)) return; + if (dataPtr == 0 && metadata == NULL) { + ALOGE("Null data returned in data callback"); + client->handleGenericNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0); + return; + } + + switch (msgType & ~CAMERA_MSG_PREVIEW_METADATA) { + case CAMERA_MSG_PREVIEW_FRAME: + client->handlePreviewData(msgType, dataPtr, metadata); + break; + case CAMERA_MSG_POSTVIEW_FRAME: + client->handlePostview(dataPtr); + break; + case CAMERA_MSG_RAW_IMAGE: + client->handleRawPicture(dataPtr); + break; + case CAMERA_MSG_COMPRESSED_IMAGE: + client->handleCompressedPicture(dataPtr); + break; + default: + client->handleGenericData(msgType, dataPtr, metadata); + break; + } +} + +void CameraClient::dataCallbackTimestamp(nsecs_t timestamp, + int32_t msgType, const sp<IMemory>& dataPtr, void* user) { + LOG2("dataCallbackTimestamp(%d)", msgType); + + Mutex* lock = getClientLockFromCookie(user); + if (lock == NULL) return; + Mutex::Autolock alock(*lock); + + CameraClient* client = + static_cast<CameraClient*>(getClientFromCookie(user)); + if (client == NULL) return; + + if (!client->lockIfMessageWanted(msgType)) return; + + if (dataPtr == 0) { + ALOGE("Null data returned in data with timestamp callback"); + client->handleGenericNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0); + return; + } + + client->handleGenericDataTimestamp(timestamp, msgType, dataPtr); +} + +// snapshot taken callback +void CameraClient::handleShutter(void) { + if (mPlayShutterSound) { + mCameraService->playSound(CameraService::SOUND_SHUTTER); + } + + sp<ICameraClient> c = mCameraClient; + if (c != 0) { + mLock.unlock(); + c->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0); + if (!lockIfMessageWanted(CAMERA_MSG_SHUTTER)) return; + } + disableMsgType(CAMERA_MSG_SHUTTER); + + mLock.unlock(); +} + +// preview callback - frame buffer update +void CameraClient::handlePreviewData(int32_t msgType, + const sp<IMemory>& mem, + camera_frame_metadata_t *metadata) { + ssize_t offset; + size_t size; + sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); + + // local copy of the callback flags + int flags = mPreviewCallbackFlag; + + // is callback enabled? + if (!(flags & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK)) { + // If the enable bit is off, the copy-out and one-shot bits are ignored + LOG2("frame callback is disabled"); + mLock.unlock(); + return; + } + + // hold a strong pointer to the client + sp<ICameraClient> c = mCameraClient; + + // clear callback flags if no client or one-shot mode + if (c == 0 || (mPreviewCallbackFlag & CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK)) { + LOG2("Disable preview callback"); + mPreviewCallbackFlag &= ~(CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK | + CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK | + CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK); + disableMsgType(CAMERA_MSG_PREVIEW_FRAME); + } + + if (c != 0) { + // Is the received frame copied out or not? + if (flags & CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK) { + LOG2("frame is copied"); + copyFrameAndPostCopiedFrame(msgType, c, heap, offset, size, metadata); + } else { + LOG2("frame is forwarded"); + mLock.unlock(); + c->dataCallback(msgType, mem, metadata); + } + } else { + mLock.unlock(); + } +} + +// picture callback - postview image ready +void CameraClient::handlePostview(const sp<IMemory>& mem) { + disableMsgType(CAMERA_MSG_POSTVIEW_FRAME); + + sp<ICameraClient> c = mCameraClient; + mLock.unlock(); + if (c != 0) { + c->dataCallback(CAMERA_MSG_POSTVIEW_FRAME, mem, NULL); + } +} + +// picture callback - raw image ready +void CameraClient::handleRawPicture(const sp<IMemory>& mem) { + disableMsgType(CAMERA_MSG_RAW_IMAGE); + + ssize_t offset; + size_t size; + sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); + + sp<ICameraClient> c = mCameraClient; + mLock.unlock(); + if (c != 0) { + c->dataCallback(CAMERA_MSG_RAW_IMAGE, mem, NULL); + } +} + +// picture callback - compressed picture ready +void CameraClient::handleCompressedPicture(const sp<IMemory>& mem) { + disableMsgType(CAMERA_MSG_COMPRESSED_IMAGE); + + sp<ICameraClient> c = mCameraClient; + mLock.unlock(); + if (c != 0) { + c->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, mem, NULL); + } +} + + +void CameraClient::handleGenericNotify(int32_t msgType, + int32_t ext1, int32_t ext2) { + sp<ICameraClient> c = mCameraClient; + mLock.unlock(); + if (c != 0) { + c->notifyCallback(msgType, ext1, ext2); + } +} + +void CameraClient::handleGenericData(int32_t msgType, + const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata) { + sp<ICameraClient> c = mCameraClient; + mLock.unlock(); + if (c != 0) { + c->dataCallback(msgType, dataPtr, metadata); + } +} + +void CameraClient::handleGenericDataTimestamp(nsecs_t timestamp, + int32_t msgType, const sp<IMemory>& dataPtr) { + sp<ICameraClient> c = mCameraClient; + mLock.unlock(); + if (c != 0) { + c->dataCallbackTimestamp(timestamp, msgType, dataPtr); + } +} + +void CameraClient::copyFrameAndPostCopiedFrame( + int32_t msgType, const sp<ICameraClient>& client, + const sp<IMemoryHeap>& heap, size_t offset, size_t size, + camera_frame_metadata_t *metadata) { + LOG2("copyFrameAndPostCopiedFrame"); + // It is necessary to copy out of pmem before sending this to + // the callback. For efficiency, reuse the same MemoryHeapBase + // provided it's big enough. Don't allocate the memory or + // perform the copy if there's no callback. + // hold the preview lock while we grab a reference to the preview buffer + sp<MemoryHeapBase> previewBuffer; + + if (mPreviewBuffer == 0) { + mPreviewBuffer = new MemoryHeapBase(size, 0, NULL); + } else if (size > mPreviewBuffer->virtualSize()) { + mPreviewBuffer.clear(); + mPreviewBuffer = new MemoryHeapBase(size, 0, NULL); + } + if (mPreviewBuffer == 0) { + ALOGE("failed to allocate space for preview buffer"); + mLock.unlock(); + return; + } + previewBuffer = mPreviewBuffer; + + memcpy(previewBuffer->base(), (uint8_t *)heap->base() + offset, size); + + sp<MemoryBase> frame = new MemoryBase(previewBuffer, 0, size); + if (frame == 0) { + ALOGE("failed to allocate space for frame callback"); + mLock.unlock(); + return; + } + + mLock.unlock(); + client->dataCallback(msgType, frame, metadata); +} + +int CameraClient::getOrientation(int degrees, bool mirror) { + if (!mirror) { + if (degrees == 0) return 0; + else if (degrees == 90) return HAL_TRANSFORM_ROT_90; + else if (degrees == 180) return HAL_TRANSFORM_ROT_180; + else if (degrees == 270) return HAL_TRANSFORM_ROT_270; + } else { // Do mirror (horizontal flip) + if (degrees == 0) { // FLIP_H and ROT_0 + return HAL_TRANSFORM_FLIP_H; + } else if (degrees == 90) { // FLIP_H and ROT_90 + return HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_ROT_90; + } else if (degrees == 180) { // FLIP_H and ROT_180 + return HAL_TRANSFORM_FLIP_V; + } else if (degrees == 270) { // FLIP_H and ROT_270 + return HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90; + } + } + ALOGE("Invalid setDisplayOrientation degrees=%d", degrees); + return -1; +} + +}; // namespace android diff --git a/services/camera/libcameraservice/CameraClient.h b/services/camera/libcameraservice/CameraClient.h new file mode 100644 index 00000000..2f31c4e7 --- /dev/null +++ b/services/camera/libcameraservice/CameraClient.h @@ -0,0 +1,156 @@ +/* + * 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_SERVERS_CAMERA_CAMERACLIENT_H +#define ANDROID_SERVERS_CAMERA_CAMERACLIENT_H + +#include "CameraService.h" + +namespace android { + +class MemoryHeapBase; +class CameraHardwareInterface; + +class CameraClient : public CameraService::Client +{ +public: + // ICamera interface (see ICamera for details) + virtual void disconnect(); + virtual status_t connect(const sp<ICameraClient>& client); + virtual status_t lock(); + virtual status_t unlock(); + virtual status_t setPreviewDisplay(const sp<Surface>& surface); + virtual status_t setPreviewTexture(const sp<ISurfaceTexture>& surfaceTexture); + virtual void setPreviewCallbackFlag(int flag); + virtual status_t startPreview(); + virtual void stopPreview(); + virtual bool previewEnabled(); + virtual status_t storeMetaDataInBuffers(bool enabled); + virtual status_t startRecording(); + virtual void stopRecording(); + virtual bool recordingEnabled(); + virtual void releaseRecordingFrame(const sp<IMemory>& mem); + virtual status_t autoFocus(); + virtual status_t cancelAutoFocus(); + virtual status_t takePicture(int msgType); + virtual status_t setParameters(const String8& params); + virtual String8 getParameters() const; + virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2); + + // Interface used by CameraService + CameraClient(const sp<CameraService>& cameraService, + const sp<ICameraClient>& cameraClient, + int cameraId, + int cameraFacing, + int clientPid, + int servicePid); + ~CameraClient(); + + status_t initialize(camera_module_t *module); + + status_t dump(int fd, const Vector<String16>& args); + +private: + + // check whether the calling process matches mClientPid. + status_t checkPid() const; + status_t checkPidAndHardware() const; // also check mHardware != 0 + + // these are internal functions used to set up preview buffers + status_t registerPreviewBuffers(); + + // camera operation mode + enum camera_mode { + CAMERA_PREVIEW_MODE = 0, // frame automatically released + CAMERA_RECORDING_MODE = 1, // frame has to be explicitly released by releaseRecordingFrame() + }; + // these are internal functions used for preview/recording + status_t startCameraMode(camera_mode mode); + status_t startPreviewMode(); + status_t startRecordingMode(); + + // internal function used by sendCommand to enable/disable shutter sound. + status_t enableShutterSound(bool enable); + + // these are static callback functions + static void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user); + static void dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, + camera_frame_metadata_t *metadata, void* user); + static void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr, void* user); + // handlers for messages + void handleShutter(void); + void handlePreviewData(int32_t msgType, const sp<IMemory>& mem, + camera_frame_metadata_t *metadata); + void handlePostview(const sp<IMemory>& mem); + void handleRawPicture(const sp<IMemory>& mem); + void handleCompressedPicture(const sp<IMemory>& mem); + void handleGenericNotify(int32_t msgType, int32_t ext1, int32_t ext2); + void handleGenericData(int32_t msgType, const sp<IMemory>& dataPtr, + camera_frame_metadata_t *metadata); + void handleGenericDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr); + + void copyFrameAndPostCopiedFrame( + int32_t msgType, + const sp<ICameraClient>& client, + const sp<IMemoryHeap>& heap, + size_t offset, size_t size, + camera_frame_metadata_t *metadata); + + int getOrientation(int orientation, bool mirror); + + status_t setPreviewWindow( + const sp<IBinder>& binder, + const sp<ANativeWindow>& window); + + + // these are initialized in the constructor. + sp<CameraHardwareInterface> mHardware; // cleared after disconnect() + int mPreviewCallbackFlag; + int mOrientation; // Current display orientation + bool mPlayShutterSound; + + // Ensures atomicity among the public methods + mutable Mutex mLock; + // This is a binder of Surface or SurfaceTexture. + sp<IBinder> mSurface; + sp<ANativeWindow> mPreviewWindow; + + // If the user want us to return a copy of the preview frame (instead + // of the original one), we allocate mPreviewBuffer and reuse it if possible. + sp<MemoryHeapBase> mPreviewBuffer; + + // We need to avoid the deadlock when the incoming command thread and + // the CameraHardwareInterface callback thread both want to grab mLock. + // An extra flag is used to tell the callback thread that it should stop + // trying to deliver the callback messages if the client is not + // interested in it anymore. For example, if the client is calling + // stopPreview(), the preview frame messages do not need to be delivered + // anymore. + + // This function takes the same parameter as the enableMsgType() and + // disableMsgType() functions in CameraHardwareInterface. + void enableMsgType(int32_t msgType); + void disableMsgType(int32_t msgType); + volatile int32_t mMsgEnabled; + + // This function keeps trying to grab mLock, or give up if the message + // is found to be disabled. It returns true if mLock is grabbed. + bool lockIfMessageWanted(int32_t msgType); +}; + +} + +#endif diff --git a/services/camera/libcameraservice/CameraHardwareInterface.h b/services/camera/libcameraservice/CameraHardwareInterface.h index 87a08020..05ac9fa1 100644 --- a/services/camera/libcameraservice/CameraHardwareInterface.h +++ b/services/camera/libcameraservice/CameraHardwareInterface.h @@ -569,7 +569,7 @@ private: int rc; ANativeWindow *a = anw(w); ANativeWindowBuffer* anb; - rc = a->dequeueBuffer(a, &anb); + rc = native_window_dequeue_buffer_and_wait(a, &anb); if (!rc) { *buffer = &anb->handle; *stride = anb->stride; @@ -587,8 +587,7 @@ private: buffer_handle_t* buffer) { ANativeWindow *a = anw(w); - return a->lockBuffer(a, - container_of(buffer, ANativeWindowBuffer, handle)); + return 0; } static int __enqueue_buffer(struct preview_stream_ops* w, @@ -596,7 +595,7 @@ private: { ANativeWindow *a = anw(w); return a->queueBuffer(a, - container_of(buffer, ANativeWindowBuffer, handle)); + container_of(buffer, ANativeWindowBuffer, handle), -1); } static int __cancel_buffer(struct preview_stream_ops* w, @@ -604,7 +603,7 @@ private: { ANativeWindow *a = anw(w); return a->cancelBuffer(a, - container_of(buffer, ANativeWindowBuffer, handle)); + container_of(buffer, ANativeWindowBuffer, handle), -1); } static int __set_buffer_count(struct preview_stream_ops* w, int count) diff --git a/services/camera/libcameraservice/CameraHardwareStub.cpp b/services/camera/libcameraservice/CameraHardwareStub.cpp deleted file mode 100644 index cdfb2f5e..00000000 --- a/services/camera/libcameraservice/CameraHardwareStub.cpp +++ /dev/null @@ -1,410 +0,0 @@ -/* -** -** Copyright 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 "CameraHardwareStub" -#include <utils/Log.h> - -#include "CameraHardwareStub.h" -#include <utils/threads.h> -#include <fcntl.h> -#include <sys/mman.h> - -#include "CannedJpeg.h" - -namespace android { - -CameraHardwareStub::CameraHardwareStub() - : mParameters(), - mPreviewHeap(0), - mRawHeap(0), - mFakeCamera(0), - mPreviewFrameSize(0), - mNotifyCb(0), - mDataCb(0), - mDataCbTimestamp(0), - mCallbackCookie(0), - mMsgEnabled(0), - mCurrentPreviewFrame(0) -{ - initDefaultParameters(); -} - -void CameraHardwareStub::initDefaultParameters() -{ - CameraParameters p; - - p.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, "320x240"); - p.setPreviewSize(320, 240); - p.setPreviewFrameRate(15); - p.setPreviewFormat(CameraParameters::PIXEL_FORMAT_YUV420SP); - - p.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, "320x240"); - p.setPictureSize(320, 240); - p.setPictureFormat(CameraParameters::PIXEL_FORMAT_JPEG); - - if (setParameters(p) != NO_ERROR) { - ALOGE("Failed to set default parameters?!"); - } -} - -void CameraHardwareStub::initHeapLocked() -{ - // Create raw heap. - int picture_width, picture_height; - mParameters.getPictureSize(&picture_width, &picture_height); - mRawHeap = new MemoryHeapBase(picture_width * picture_height * 3 / 2); - - int preview_width, preview_height; - mParameters.getPreviewSize(&preview_width, &preview_height); - ALOGD("initHeapLocked: preview size=%dx%d", preview_width, preview_height); - - // Note that we enforce yuv420sp in setParameters(). - int how_big = preview_width * preview_height * 3 / 2; - - // If we are being reinitialized to the same size as before, no - // work needs to be done. - if (how_big == mPreviewFrameSize) - return; - - mPreviewFrameSize = how_big; - - // Make a new mmap'ed heap that can be shared across processes. - // use code below to test with pmem - mPreviewHeap = new MemoryHeapBase(mPreviewFrameSize * kBufferCount); - // Make an IMemory for each frame so that we can reuse them in callbacks. - for (int i = 0; i < kBufferCount; i++) { - mBuffers[i] = new MemoryBase(mPreviewHeap, i * mPreviewFrameSize, mPreviewFrameSize); - } - - // Recreate the fake camera to reflect the current size. - delete mFakeCamera; - mFakeCamera = new FakeCamera(preview_width, preview_height); -} - -CameraHardwareStub::~CameraHardwareStub() -{ - delete mFakeCamera; - mFakeCamera = 0; // paranoia -} - -status_t CameraHardwareStub::setPreviewWindow(const sp<ANativeWindow>& buf) -{ - return NO_ERROR; -} - -sp<IMemoryHeap> CameraHardwareStub::getRawHeap() const -{ - return mRawHeap; -} - -void CameraHardwareStub::setCallbacks(notify_callback notify_cb, - data_callback data_cb, - data_callback_timestamp data_cb_timestamp, - void* user) -{ - Mutex::Autolock lock(mLock); - mNotifyCb = notify_cb; - mDataCb = data_cb; - mDataCbTimestamp = data_cb_timestamp; - mCallbackCookie = user; -} - -void CameraHardwareStub::enableMsgType(int32_t msgType) -{ - Mutex::Autolock lock(mLock); - mMsgEnabled |= msgType; -} - -void CameraHardwareStub::disableMsgType(int32_t msgType) -{ - Mutex::Autolock lock(mLock); - mMsgEnabled &= ~msgType; -} - -bool CameraHardwareStub::msgTypeEnabled(int32_t msgType) -{ - Mutex::Autolock lock(mLock); - return (mMsgEnabled & msgType); -} - -// --------------------------------------------------------------------------- - -int CameraHardwareStub::previewThread() -{ - mLock.lock(); - // the attributes below can change under our feet... - - int previewFrameRate = mParameters.getPreviewFrameRate(); - - // Find the offset within the heap of the current buffer. - ssize_t offset = mCurrentPreviewFrame * mPreviewFrameSize; - - sp<MemoryHeapBase> heap = mPreviewHeap; - - // this assumes the internal state of fake camera doesn't change - // (or is thread safe) - FakeCamera* fakeCamera = mFakeCamera; - - sp<MemoryBase> buffer = mBuffers[mCurrentPreviewFrame]; - - mLock.unlock(); - - // TODO: here check all the conditions that could go wrong - if (buffer != 0) { - // Calculate how long to wait between frames. - int delay = (int)(1000000.0f / float(previewFrameRate)); - - // This is always valid, even if the client died -- the memory - // is still mapped in our process. - void *base = heap->base(); - - // Fill the current frame with the fake camera. - uint8_t *frame = ((uint8_t *)base) + offset; - fakeCamera->getNextFrameAsYuv420(frame); - - //ALOGV("previewThread: generated frame to buffer %d", mCurrentPreviewFrame); - - // Notify the client of a new frame. - if (mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME) - mDataCb(CAMERA_MSG_PREVIEW_FRAME, buffer, NULL, mCallbackCookie); - - // Advance the buffer pointer. - mCurrentPreviewFrame = (mCurrentPreviewFrame + 1) % kBufferCount; - - // Wait for it... - usleep(delay); - } - - return NO_ERROR; -} - -status_t CameraHardwareStub::startPreview() -{ - Mutex::Autolock lock(mLock); - if (mPreviewThread != 0) { - // already running - return INVALID_OPERATION; - } - mPreviewThread = new PreviewThread(this); - return NO_ERROR; -} - -void CameraHardwareStub::stopPreview() -{ - sp<PreviewThread> previewThread; - - { // scope for the lock - Mutex::Autolock lock(mLock); - previewThread = mPreviewThread; - } - - // don't hold the lock while waiting for the thread to quit - if (previewThread != 0) { - previewThread->requestExitAndWait(); - } - - Mutex::Autolock lock(mLock); - mPreviewThread.clear(); -} - -bool CameraHardwareStub::previewEnabled() { - return mPreviewThread != 0; -} - -status_t CameraHardwareStub::startRecording() -{ - return UNKNOWN_ERROR; -} - -void CameraHardwareStub::stopRecording() -{ -} - -bool CameraHardwareStub::recordingEnabled() -{ - return false; -} - -void CameraHardwareStub::releaseRecordingFrame(const sp<IMemory>& mem) -{ -} - -// --------------------------------------------------------------------------- - -int CameraHardwareStub::beginAutoFocusThread(void *cookie) -{ - CameraHardwareStub *c = (CameraHardwareStub *)cookie; - return c->autoFocusThread(); -} - -int CameraHardwareStub::autoFocusThread() -{ - if (mMsgEnabled & CAMERA_MSG_FOCUS) - mNotifyCb(CAMERA_MSG_FOCUS, true, 0, mCallbackCookie); - return NO_ERROR; -} - -status_t CameraHardwareStub::autoFocus() -{ - Mutex::Autolock lock(mLock); - if (createThread(beginAutoFocusThread, this) == false) - return UNKNOWN_ERROR; - return NO_ERROR; -} - -status_t CameraHardwareStub::cancelAutoFocus() -{ - return NO_ERROR; -} - -/*static*/ int CameraHardwareStub::beginPictureThread(void *cookie) -{ - CameraHardwareStub *c = (CameraHardwareStub *)cookie; - return c->pictureThread(); -} - -int CameraHardwareStub::pictureThread() -{ - if (mMsgEnabled & CAMERA_MSG_SHUTTER) - mNotifyCb(CAMERA_MSG_SHUTTER, 0, 0, mCallbackCookie); - - if (mMsgEnabled & CAMERA_MSG_RAW_IMAGE) { - //FIXME: use a canned YUV image! - // In the meantime just make another fake camera picture. - int w, h; - mParameters.getPictureSize(&w, &h); - sp<MemoryBase> mem = new MemoryBase(mRawHeap, 0, w * h * 3 / 2); - FakeCamera cam(w, h); - cam.getNextFrameAsYuv420((uint8_t *)mRawHeap->base()); - mDataCb(CAMERA_MSG_RAW_IMAGE, mem, NULL, mCallbackCookie); - } - - if (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE) { - sp<MemoryHeapBase> heap = new MemoryHeapBase(kCannedJpegSize); - sp<MemoryBase> mem = new MemoryBase(heap, 0, kCannedJpegSize); - memcpy(heap->base(), kCannedJpeg, kCannedJpegSize); - mDataCb(CAMERA_MSG_COMPRESSED_IMAGE, mem, NULL, mCallbackCookie); - } - return NO_ERROR; -} - -status_t CameraHardwareStub::takePicture() -{ - stopPreview(); - if (createThread(beginPictureThread, this) == false) - return UNKNOWN_ERROR; - return NO_ERROR; -} - -status_t CameraHardwareStub::cancelPicture() -{ - return NO_ERROR; -} - -status_t CameraHardwareStub::dump(int fd, const Vector<String16>& args) const -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - AutoMutex lock(&mLock); - if (mFakeCamera != 0) { - mFakeCamera->dump(fd); - mParameters.dump(fd, args); - snprintf(buffer, 255, " preview frame(%d), size (%d), running(%s)\n", mCurrentPreviewFrame, mPreviewFrameSize, mPreviewRunning?"true": "false"); - result.append(buffer); - } else { - result.append("No camera client yet.\n"); - } - write(fd, result.string(), result.size()); - return NO_ERROR; -} - -status_t CameraHardwareStub::setParameters(const CameraParameters& params) -{ - Mutex::Autolock lock(mLock); - // XXX verify params - - if (strcmp(params.getPreviewFormat(), - CameraParameters::PIXEL_FORMAT_YUV420SP) != 0) { - ALOGE("Only yuv420sp preview is supported"); - return -1; - } - - if (strcmp(params.getPictureFormat(), - CameraParameters::PIXEL_FORMAT_JPEG) != 0) { - ALOGE("Only jpeg still pictures are supported"); - return -1; - } - - int w, h; - params.getPictureSize(&w, &h); - if (w != kCannedJpegWidth && h != kCannedJpegHeight) { - ALOGE("Still picture size must be size of canned JPEG (%dx%d)", - kCannedJpegWidth, kCannedJpegHeight); - return -1; - } - - mParameters = params; - initHeapLocked(); - - return NO_ERROR; -} - -CameraParameters CameraHardwareStub::getParameters() const -{ - Mutex::Autolock lock(mLock); - return mParameters; -} - -status_t CameraHardwareStub::sendCommand(int32_t command, int32_t arg1, - int32_t arg2) -{ - return BAD_VALUE; -} - -void CameraHardwareStub::release() -{ -} - -sp<CameraHardwareInterface> CameraHardwareStub::createInstance() -{ - return new CameraHardwareStub(); -} - -static CameraInfo sCameraInfo[] = { - { - CAMERA_FACING_BACK, - 90, /* orientation */ - } -}; - -extern "C" int HAL_getNumberOfCameras() -{ - return sizeof(sCameraInfo) / sizeof(sCameraInfo[0]); -} - -extern "C" void HAL_getCameraInfo(int cameraId, struct CameraInfo* cameraInfo) -{ - memcpy(cameraInfo, &sCameraInfo[cameraId], sizeof(CameraInfo)); -} - -extern "C" sp<CameraHardwareInterface> HAL_openCameraHardware(int cameraId) -{ - return CameraHardwareStub::createInstance(); -} - -}; // namespace android diff --git a/services/camera/libcameraservice/CameraHardwareStub.h b/services/camera/libcameraservice/CameraHardwareStub.h deleted file mode 100644 index c6d87567..00000000 --- a/services/camera/libcameraservice/CameraHardwareStub.h +++ /dev/null @@ -1,126 +0,0 @@ -/* -** -** Copyright 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_CAMERA_HARDWARE_STUB_H -#define ANDROID_HARDWARE_CAMERA_HARDWARE_STUB_H - -#include "FakeCamera.h" -#include <utils/threads.h> -#include <camera/CameraHardwareInterface.h> -#include <binder/MemoryBase.h> -#include <binder/MemoryHeapBase.h> -#include <utils/threads.h> - -namespace android { - -class CameraHardwareStub : public CameraHardwareInterface { -public: - virtual status_t setPreviewWindow(const sp<ANativeWindow>& buf); - virtual sp<IMemoryHeap> getRawHeap() const; - - virtual void setCallbacks(notify_callback notify_cb, - data_callback data_cb, - data_callback_timestamp data_cb_timestamp, - void* user); - - virtual void enableMsgType(int32_t msgType); - virtual void disableMsgType(int32_t msgType); - virtual bool msgTypeEnabled(int32_t msgType); - - virtual status_t startPreview(); - virtual void stopPreview(); - virtual bool previewEnabled(); - - virtual status_t startRecording(); - virtual void stopRecording(); - virtual bool recordingEnabled(); - virtual void releaseRecordingFrame(const sp<IMemory>& mem); - - virtual status_t autoFocus(); - virtual status_t cancelAutoFocus(); - virtual status_t takePicture(); - virtual status_t cancelPicture(); - virtual status_t dump(int fd, const Vector<String16>& args) const; - virtual status_t setParameters(const CameraParameters& params); - virtual CameraParameters getParameters() const; - virtual status_t sendCommand(int32_t command, int32_t arg1, - int32_t arg2); - virtual void release(); - - static sp<CameraHardwareInterface> createInstance(); - -private: - CameraHardwareStub(); - virtual ~CameraHardwareStub(); - - static const int kBufferCount = 4; - - class PreviewThread : public Thread { - CameraHardwareStub* mHardware; - public: - PreviewThread(CameraHardwareStub* hw) : - Thread(false), mHardware(hw) { } - virtual void onFirstRef() { - run("CameraPreviewThread", PRIORITY_URGENT_DISPLAY); - } - virtual bool threadLoop() { - mHardware->previewThread(); - // loop until we need to quit - return true; - } - }; - - void initDefaultParameters(); - void initHeapLocked(); - - int previewThread(); - - static int beginAutoFocusThread(void *cookie); - int autoFocusThread(); - - static int beginPictureThread(void *cookie); - int pictureThread(); - - mutable Mutex mLock; - - CameraParameters mParameters; - - sp<MemoryHeapBase> mPreviewHeap; - sp<MemoryHeapBase> mRawHeap; - sp<MemoryBase> mBuffers[kBufferCount]; - - FakeCamera *mFakeCamera; - bool mPreviewRunning; - int mPreviewFrameSize; - - // protected by mLock - sp<PreviewThread> mPreviewThread; - - notify_callback mNotifyCb; - data_callback mDataCb; - data_callback_timestamp mDataCbTimestamp; - void *mCallbackCookie; - - int32_t mMsgEnabled; - - // only used from PreviewThread - int mCurrentPreviewFrame; -}; - -}; // namespace android - -#endif diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index 385be505..124d24d4 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -38,14 +38,15 @@ #include <utils/String16.h> #include "CameraService.h" -#include "CameraHardwareInterface.h" +#include "CameraClient.h" +#include "Camera2Client.h" namespace android { // ---------------------------------------------------------------------------- // Logging support -- this is for debugging only // Use "adb shell dumpsys media.camera -v 1" to change it. -static volatile int32_t gLogLevel = 0; +volatile int32_t gLogLevel = 0; #define LOG1(...) ALOGD_IF(gLogLevel >= 1, __VA_ARGS__); #define LOG2(...) ALOGD_IF(gLogLevel >= 2, __VA_ARGS__); @@ -133,7 +134,6 @@ status_t CameraService::getCameraInfo(int cameraId, sp<ICamera> CameraService::connect( const sp<ICameraClient>& cameraClient, int cameraId) { int callingPid = getCallingPid(); - sp<CameraHardwareInterface> hardware = NULL; LOG1("CameraService::connect E (pid %d, id %d)", callingPid, cameraId); @@ -186,18 +186,35 @@ sp<ICamera> CameraService::connect( return NULL; } - char camera_device_name[10]; - snprintf(camera_device_name, sizeof(camera_device_name), "%d", cameraId); + int deviceVersion; + if (mModule->common.module_api_version == CAMERA_MODULE_API_VERSION_2_0) { + deviceVersion = info.device_version; + } else { + deviceVersion = CAMERA_DEVICE_API_VERSION_1_0; + } + + switch(deviceVersion) { + case CAMERA_DEVICE_API_VERSION_1_0: + client = new CameraClient(this, cameraClient, cameraId, + info.facing, callingPid, getpid()); + break; + case CAMERA_DEVICE_API_VERSION_2_0: + client = new Camera2Client(this, cameraClient, cameraId, + info.facing, callingPid, getpid()); + break; + default: + ALOGE("Unknown camera device HAL version: %d", deviceVersion); + return NULL; + } - hardware = new CameraHardwareInterface(camera_device_name); - if (hardware->initialize(&mModule->common) != OK) { - hardware.clear(); + if (client->initialize(mModule) != OK) { return NULL; } - client = new Client(this, cameraClient, hardware, cameraId, info.facing, callingPid); + cameraClient->asBinder()->linkToDeath(this); + mClient[cameraId] = client; - LOG1("CameraService::connect X (id %d)", cameraId); + LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId, getpid()); return client; } @@ -205,12 +222,29 @@ void CameraService::removeClient(const sp<ICameraClient>& cameraClient) { int callingPid = getCallingPid(); LOG1("CameraService::removeClient E (pid %d)", callingPid); - for (int i = 0; i < mNumberOfCameras; i++) { - // Declare this before the lock to make absolutely sure the - // destructor won't be called with the lock held. - sp<Client> client; + // Declare this before the lock to make absolutely sure the + // destructor won't be called with the lock held. + Mutex::Autolock lock(mServiceLock); + + int outIndex; + sp<Client> client = findClientUnsafe(cameraClient->asBinder(), outIndex); + + if (client != 0) { + // Found our camera, clear and leave. + LOG1("removeClient: clear camera %d", outIndex); + mClient[outIndex].clear(); + + client->unlinkToDeath(this); + } - Mutex::Autolock lock(mServiceLock); + LOG1("CameraService::removeClient X (pid %d)", callingPid); +} + +sp<CameraService::Client> CameraService::findClientUnsafe( + const wp<IBinder>& cameraClient, int& outIndex) { + sp<Client> client; + + for (int i = 0; i < mNumberOfCameras; i++) { // This happens when we have already disconnected (or this is // just another unused camera). @@ -220,20 +254,21 @@ void CameraService::removeClient(const sp<ICameraClient>& cameraClient) { // Client::~Client() -> disconnect() -> removeClient(). client = mClient[i].promote(); - if (client == 0) { + // Clean up stale client entry + if (client == NULL) { mClient[i].clear(); continue; } - if (cameraClient->asBinder() == client->getCameraClient()->asBinder()) { - // Found our camera, clear and leave. - LOG1("removeClient: clear camera %d", i); - mClient[i].clear(); - break; + if (cameraClient == client->getCameraClient()->asBinder()) { + // Found our camera + outIndex = i; + return client; } } - LOG1("CameraService::removeClient X (pid %d)", callingPid); + outIndex = -1; + return NULL; } CameraService::Client* CameraService::getClientByIdUnsafe(int cameraId) { @@ -246,6 +281,21 @@ Mutex* CameraService::getClientLockById(int cameraId) { return &mClientLock[cameraId]; } +sp<CameraService::Client> CameraService::getClientByRemote( + const wp<IBinder>& cameraClient) { + + // Declare this before the lock to make absolutely sure the + // destructor won't be called with the lock held. + sp<Client> client; + + Mutex::Autolock lock(mServiceLock); + + int outIndex; + client = findClientUnsafe(cameraClient, outIndex); + + return client; +} + status_t CameraService::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { // Permission checks @@ -277,10 +327,14 @@ status_t CameraService::onTransact( // the hardware first. void CameraService::setCameraBusy(int cameraId) { android_atomic_write(1, &mBusy[cameraId]); + + ALOGV("setCameraBusy cameraId=%d", cameraId); } void CameraService::setCameraFree(int cameraId) { android_atomic_write(0, &mBusy[cameraId]); + + ALOGV("setCameraFree cameraId=%d", cameraId); } // We share the media players for shutter and recording sound for all clients. @@ -335,34 +389,18 @@ void CameraService::playSound(sound_kind kind) { CameraService::Client::Client(const sp<CameraService>& cameraService, const sp<ICameraClient>& cameraClient, - const sp<CameraHardwareInterface>& hardware, - int cameraId, int cameraFacing, int clientPid) { + int cameraId, int cameraFacing, int clientPid, int servicePid) { int callingPid = getCallingPid(); LOG1("Client::Client E (pid %d, id %d)", callingPid, cameraId); mCameraService = cameraService; mCameraClient = cameraClient; - mHardware = hardware; mCameraId = cameraId; mCameraFacing = cameraFacing; mClientPid = clientPid; - mMsgEnabled = 0; - mSurface = 0; - mPreviewWindow = 0; + mServicePid = servicePid; mDestructionStarted = false; - mHardware->setCallbacks(notifyCallback, - dataCallback, - dataCallbackTimestamp, - (void *)cameraId); - - // Enable zoom, error, focus, and metadata messages by default - enableMsgType(CAMERA_MSG_ERROR | CAMERA_MSG_ZOOM | CAMERA_MSG_FOCUS | - CAMERA_MSG_PREVIEW_METADATA | CAMERA_MSG_FOCUS_MOVE); - - // Callback is disabled by default - mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP; - mOrientation = getOrientation(0, mCameraFacing == CAMERA_FACING_FRONT); - mPlayShutterSound = true; + cameraService->setCameraBusy(cameraId); cameraService->loadSound(); LOG1("Client::Client X (pid %d, id %d)", callingPid, cameraId); @@ -370,582 +408,10 @@ CameraService::Client::Client(const sp<CameraService>& cameraService, // tear down the client CameraService::Client::~Client() { - // this lock should never be NULL - Mutex* lock = mCameraService->getClientLockById(mCameraId); - lock->lock(); - mDestructionStarted = true; - // client will not be accessed from callback. should unlock to prevent dead-lock in disconnect - lock->unlock(); - int callingPid = getCallingPid(); - LOG1("Client::~Client E (pid %d, this %p)", callingPid, this); - - // set mClientPid to let disconnet() tear down the hardware - mClientPid = callingPid; - disconnect(); mCameraService->releaseSound(); - LOG1("Client::~Client X (pid %d, this %p)", callingPid, this); -} - -// ---------------------------------------------------------------------------- - -status_t CameraService::Client::checkPid() const { - int callingPid = getCallingPid(); - if (callingPid == mClientPid) return NO_ERROR; - - ALOGW("attempt to use a locked camera from a different process" - " (old pid %d, new pid %d)", mClientPid, callingPid); - return EBUSY; -} -status_t CameraService::Client::checkPidAndHardware() const { - status_t result = checkPid(); - if (result != NO_ERROR) return result; - if (mHardware == 0) { - ALOGE("attempt to use a camera after disconnect() (pid %d)", getCallingPid()); - return INVALID_OPERATION; - } - return NO_ERROR; -} - -status_t CameraService::Client::lock() { - int callingPid = getCallingPid(); - LOG1("lock (pid %d)", callingPid); - Mutex::Autolock ilock(mICameraLock); - Mutex::Autolock lock(mLock); - - // lock camera to this client if the the camera is unlocked - if (mClientPid == 0) { - mClientPid = callingPid; - return NO_ERROR; - } - - // returns NO_ERROR if the client already owns the camera, EBUSY otherwise - return checkPid(); -} - -status_t CameraService::Client::unlock() { - int callingPid = getCallingPid(); - LOG1("unlock (pid %d)", callingPid); - Mutex::Autolock iLock(mICameraLock); - Mutex::Autolock lock(mLock); - - // allow anyone to use camera (after they lock the camera) - status_t result = checkPid(); - if (result == NO_ERROR) { - if (mHardware->recordingEnabled()) { - ALOGE("Not allowed to unlock camera during recording."); - return INVALID_OPERATION; - } - mClientPid = 0; - LOG1("clear mCameraClient (pid %d)", callingPid); - // we need to remove the reference to ICameraClient so that when the app - // goes away, the reference count goes to 0. - mCameraClient.clear(); - } - return result; -} - -// connect a new client to the camera -status_t CameraService::Client::connect(const sp<ICameraClient>& client) { - int callingPid = getCallingPid(); - LOG1("connect E (pid %d)", callingPid); - Mutex::Autolock iLock(mICameraLock); - Mutex::Autolock lock(mLock); - - if (mClientPid != 0 && checkPid() != NO_ERROR) { - ALOGW("Tried to connect to a locked camera (old pid %d, new pid %d)", - mClientPid, callingPid); - return EBUSY; - } - - if (mCameraClient != 0 && (client->asBinder() == mCameraClient->asBinder())) { - LOG1("Connect to the same client"); - return NO_ERROR; - } - - mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP; - mClientPid = callingPid; - mCameraClient = client; - - LOG1("connect X (pid %d)", callingPid); - return NO_ERROR; -} - -static void disconnectWindow(const sp<ANativeWindow>& window) { - if (window != 0) { - status_t result = native_window_api_disconnect(window.get(), - NATIVE_WINDOW_API_CAMERA); - if (result != NO_ERROR) { - ALOGW("native_window_api_disconnect failed: %s (%d)", strerror(-result), - result); - } - } -} - -void CameraService::Client::disconnect() { - int callingPid = getCallingPid(); - LOG1("disconnect E (pid %d)", callingPid); - Mutex::Autolock iLock(mICameraLock); - Mutex::Autolock lock(mLock); - - if (checkPid() != NO_ERROR) { - ALOGW("different client - don't disconnect"); - return; - } - - if (mClientPid <= 0) { - LOG1("camera is unlocked (mClientPid = %d), don't tear down hardware", mClientPid); - return; - } - - // Make sure disconnect() is done once and once only, whether it is called - // from the user directly, or called by the destructor. - if (mHardware == 0) return; - - LOG1("hardware teardown"); - // Before destroying mHardware, we must make sure it's in the - // idle state. - // Turn off all messages. - disableMsgType(CAMERA_MSG_ALL_MSGS); - mHardware->stopPreview(); - mHardware->cancelPicture(); - // Release the hardware resources. - mHardware->release(); - - // Release the held ANativeWindow resources. - if (mPreviewWindow != 0) { - disconnectWindow(mPreviewWindow); - mPreviewWindow = 0; - mHardware->setPreviewWindow(mPreviewWindow); - } - mHardware.clear(); - - mCameraService->removeClient(mCameraClient); - mCameraService->setCameraFree(mCameraId); - - LOG1("disconnect X (pid %d)", callingPid); -} - -// ---------------------------------------------------------------------------- - -status_t CameraService::Client::setPreviewWindow(const sp<IBinder>& binder, - const sp<ANativeWindow>& window) { - Mutex::Autolock iLock(mICameraLock); - Mutex::Autolock lock(mLock); - status_t result = checkPidAndHardware(); - if (result != NO_ERROR) return result; - - // return if no change in surface. - if (binder == mSurface) { - return NO_ERROR; - } - - if (window != 0) { - result = native_window_api_connect(window.get(), NATIVE_WINDOW_API_CAMERA); - if (result != NO_ERROR) { - ALOGE("native_window_api_connect failed: %s (%d)", strerror(-result), - result); - return result; - } - } - - // If preview has been already started, register preview buffers now. - if (mHardware->previewEnabled()) { - if (window != 0) { - native_window_set_scaling_mode(window.get(), - NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); - native_window_set_buffers_transform(window.get(), mOrientation); - result = mHardware->setPreviewWindow(window); - } - } - - if (result == NO_ERROR) { - // Everything has succeeded. Disconnect the old window and remember the - // new window. - disconnectWindow(mPreviewWindow); - mSurface = binder; - mPreviewWindow = window; - } else { - // Something went wrong after we connected to the new window, so - // disconnect here. - disconnectWindow(window); - } - - return result; -} - -// set the Surface that the preview will use -status_t CameraService::Client::setPreviewDisplay(const sp<Surface>& surface) { - LOG1("setPreviewDisplay(%p) (pid %d)", surface.get(), getCallingPid()); - - sp<IBinder> binder(surface != 0 ? surface->asBinder() : 0); - sp<ANativeWindow> window(surface); - return setPreviewWindow(binder, window); -} - -// set the SurfaceTexture that the preview will use -status_t CameraService::Client::setPreviewTexture( - const sp<ISurfaceTexture>& surfaceTexture) { - LOG1("setPreviewTexture(%p) (pid %d)", surfaceTexture.get(), - getCallingPid()); - - sp<IBinder> binder; - sp<ANativeWindow> window; - if (surfaceTexture != 0) { - binder = surfaceTexture->asBinder(); - window = new SurfaceTextureClient(surfaceTexture); - } - return setPreviewWindow(binder, window); -} - -// set the preview callback flag to affect how the received frames from -// preview are handled. -void CameraService::Client::setPreviewCallbackFlag(int callback_flag) { - LOG1("setPreviewCallbackFlag(%d) (pid %d)", callback_flag, getCallingPid()); - Mutex::Autolock iLock(mICameraLock); - Mutex::Autolock lock(mLock); - if (checkPidAndHardware() != NO_ERROR) return; - - mPreviewCallbackFlag = callback_flag; - if (mPreviewCallbackFlag & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK) { - enableMsgType(CAMERA_MSG_PREVIEW_FRAME); - } else { - disableMsgType(CAMERA_MSG_PREVIEW_FRAME); - } -} - -// start preview mode -status_t CameraService::Client::startPreview() { - LOG1("startPreview (pid %d)", getCallingPid()); - return startCameraMode(CAMERA_PREVIEW_MODE); -} - -// start recording mode -status_t CameraService::Client::startRecording() { - LOG1("startRecording (pid %d)", getCallingPid()); - return startCameraMode(CAMERA_RECORDING_MODE); -} - -// start preview or recording -status_t CameraService::Client::startCameraMode(camera_mode mode) { - LOG1("startCameraMode(%d)", mode); - Mutex::Autolock iLock(mICameraLock); - Mutex::Autolock lock(mLock); - status_t result = checkPidAndHardware(); - if (result != NO_ERROR) return result; - - switch(mode) { - case CAMERA_PREVIEW_MODE: - if (mSurface == 0 && mPreviewWindow == 0) { - LOG1("mSurface is not set yet."); - // still able to start preview in this case. - } - return startPreviewMode(); - case CAMERA_RECORDING_MODE: - if (mSurface == 0 && mPreviewWindow == 0) { - ALOGE("mSurface or mPreviewWindow must be set before startRecordingMode."); - return INVALID_OPERATION; - } - return startRecordingMode(); - default: - return UNKNOWN_ERROR; - } -} - -status_t CameraService::Client::startPreviewMode() { - LOG1("startPreviewMode"); - status_t result = NO_ERROR; - - // if preview has been enabled, nothing needs to be done - if (mHardware->previewEnabled()) { - return NO_ERROR; - } - - if (mPreviewWindow != 0) { - native_window_set_scaling_mode(mPreviewWindow.get(), - NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); - native_window_set_buffers_transform(mPreviewWindow.get(), - mOrientation); - } - mHardware->setPreviewWindow(mPreviewWindow); - result = mHardware->startPreview(); - - return result; -} - -status_t CameraService::Client::startRecordingMode() { - LOG1("startRecordingMode"); - status_t result = NO_ERROR; - - // if recording has been enabled, nothing needs to be done - if (mHardware->recordingEnabled()) { - return NO_ERROR; - } - - // if preview has not been started, start preview first - if (!mHardware->previewEnabled()) { - result = startPreviewMode(); - if (result != NO_ERROR) { - return result; - } - } - - // start recording mode - enableMsgType(CAMERA_MSG_VIDEO_FRAME); - mCameraService->playSound(SOUND_RECORDING); - result = mHardware->startRecording(); - if (result != NO_ERROR) { - ALOGE("mHardware->startRecording() failed with status %d", result); - } - return result; -} - -// stop preview mode -void CameraService::Client::stopPreview() { - LOG1("stopPreview (pid %d)", getCallingPid()); - Mutex::Autolock iLock(mICameraLock); - Mutex::Autolock lock(mLock); - if (checkPidAndHardware() != NO_ERROR) return; - - - disableMsgType(CAMERA_MSG_PREVIEW_FRAME); - mHardware->stopPreview(); - - mPreviewBuffer.clear(); -} - -// stop recording mode -void CameraService::Client::stopRecording() { - LOG1("stopRecording (pid %d)", getCallingPid()); - Mutex::Autolock iLock(mICameraLock); - Mutex::Autolock lock(mLock); - if (checkPidAndHardware() != NO_ERROR) return; - - disableMsgType(CAMERA_MSG_VIDEO_FRAME); - mHardware->stopRecording(); - mCameraService->playSound(SOUND_RECORDING); - - mPreviewBuffer.clear(); -} - -// release a recording frame -void CameraService::Client::releaseRecordingFrame(const sp<IMemory>& mem) { - Mutex::Autolock iLock(mICameraLock); - Mutex::Autolock lock(mLock); - if (checkPidAndHardware() != NO_ERROR) return; - mHardware->releaseRecordingFrame(mem); -} - -status_t CameraService::Client::storeMetaDataInBuffers(bool enabled) -{ - LOG1("storeMetaDataInBuffers: %s", enabled? "true": "false"); - Mutex::Autolock iLock(mICameraLock); - Mutex::Autolock lock(mLock); - if (checkPidAndHardware() != NO_ERROR) { - return UNKNOWN_ERROR; - } - return mHardware->storeMetaDataInBuffers(enabled); -} - -bool CameraService::Client::previewEnabled() { - LOG1("previewEnabled (pid %d)", getCallingPid()); - - Mutex::Autolock iLock(mICameraLock); - Mutex::Autolock lock(mLock); - if (checkPidAndHardware() != NO_ERROR) return false; - return mHardware->previewEnabled(); -} - -bool CameraService::Client::recordingEnabled() { - LOG1("recordingEnabled (pid %d)", getCallingPid()); - - Mutex::Autolock iLock(mICameraLock); - Mutex::Autolock lock(mLock); - if (checkPidAndHardware() != NO_ERROR) return false; - return mHardware->recordingEnabled(); -} - -status_t CameraService::Client::autoFocus() { - LOG1("autoFocus (pid %d)", getCallingPid()); - - Mutex::Autolock iLock(mICameraLock); - Mutex::Autolock lock(mLock); - status_t result = checkPidAndHardware(); - if (result != NO_ERROR) return result; - - return mHardware->autoFocus(); -} - -status_t CameraService::Client::cancelAutoFocus() { - LOG1("cancelAutoFocus (pid %d)", getCallingPid()); - - Mutex::Autolock iLock(mICameraLock); - Mutex::Autolock lock(mLock); - status_t result = checkPidAndHardware(); - if (result != NO_ERROR) return result; - - return mHardware->cancelAutoFocus(); -} - -// take a picture - image is returned in callback -status_t CameraService::Client::takePicture(int msgType) { - LOG1("takePicture (pid %d): 0x%x", getCallingPid(), msgType); - - Mutex::Autolock iLock(mICameraLock); - int picMsgType = 0; - { // scope for lock - Mutex::Autolock lock(mLock); - status_t result = checkPidAndHardware(); - if (result != NO_ERROR) return result; - - if ((msgType & CAMERA_MSG_RAW_IMAGE) && - (msgType & CAMERA_MSG_RAW_IMAGE_NOTIFY)) { - ALOGE("CAMERA_MSG_RAW_IMAGE and CAMERA_MSG_RAW_IMAGE_NOTIFY" - " cannot be both enabled"); - return BAD_VALUE; - } - - // We only accept picture related message types - // and ignore other types of messages for takePicture(). - picMsgType = msgType - & (CAMERA_MSG_SHUTTER | - CAMERA_MSG_POSTVIEW_FRAME | - CAMERA_MSG_RAW_IMAGE | - CAMERA_MSG_RAW_IMAGE_NOTIFY | - CAMERA_MSG_COMPRESSED_IMAGE); - - } - enableMsgType(picMsgType); - - return mHardware->takePicture(); -} - -// set preview/capture parameters - key/value pairs -status_t CameraService::Client::setParameters(const String8& params) { - LOG1("setParameters (pid %d) (%s)", getCallingPid(), params.string()); - - Mutex::Autolock iLock(mICameraLock); - Mutex::Autolock lock(mLock); - status_t result = checkPidAndHardware(); - if (result != NO_ERROR) return result; - - CameraParameters p(params); - return mHardware->setParameters(p); -} - -// get preview/capture parameters - key/value pairs -String8 CameraService::Client::getParameters() const { - Mutex::Autolock iLock(mICameraLock); - Mutex::Autolock lock(mLock); - if (checkPidAndHardware() != NO_ERROR) return String8(); - - String8 params(mHardware->getParameters().flatten()); - LOG1("getParameters (pid %d) (%s)", getCallingPid(), params.string()); - return params; -} - -// enable shutter sound -status_t CameraService::Client::enableShutterSound(bool enable) { - LOG1("enableShutterSound (pid %d)", getCallingPid()); - - status_t result = checkPidAndHardware(); - if (result != NO_ERROR) return result; - - if (enable) { - mPlayShutterSound = true; - 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]; - property_get("ro.camera.sound.forced", value, "0"); - if (strcmp(value, "0") != 0) { - // Disabling shutter sound is not allowed. Deny if the current - // process is not mediaserver. - if (getCallingPid() != getpid()) { - ALOGE("Failed to disable shutter sound. Permission denied (pid %d)", getCallingPid()); - return PERMISSION_DENIED; - } - } - - mPlayShutterSound = false; - return OK; -} - -status_t CameraService::Client::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) { - LOG1("sendCommand (pid %d)", getCallingPid()); - int orientation; - Mutex::Autolock iLock(mICameraLock); - Mutex::Autolock lock(mLock); - status_t result = checkPidAndHardware(); - if (result != NO_ERROR) return result; - - if (cmd == CAMERA_CMD_SET_DISPLAY_ORIENTATION) { - // Mirror the preview if the camera is front-facing. - orientation = getOrientation(arg1, mCameraFacing == CAMERA_FACING_FRONT); - if (orientation == -1) return BAD_VALUE; - - if (mOrientation != orientation) { - mOrientation = orientation; - if (mPreviewWindow != 0) { - native_window_set_buffers_transform(mPreviewWindow.get(), - mOrientation); - } - } - return OK; - } else if (cmd == CAMERA_CMD_ENABLE_SHUTTER_SOUND) { - switch (arg1) { - case 0: - enableShutterSound(false); - break; - case 1: - enableShutterSound(true); - break; - default: - return BAD_VALUE; - } - return OK; - } else if (cmd == CAMERA_CMD_PLAY_RECORDING_SOUND) { - mCameraService->playSound(SOUND_RECORDING); - } else if (cmd == CAMERA_CMD_PING) { - // If mHardware is 0, checkPidAndHardware will return error. - return OK; - } - - return mHardware->sendCommand(cmd, arg1, arg2); -} - -// ---------------------------------------------------------------------------- - -void CameraService::Client::enableMsgType(int32_t msgType) { - android_atomic_or(msgType, &mMsgEnabled); - mHardware->enableMsgType(msgType); -} - -void CameraService::Client::disableMsgType(int32_t msgType) { - android_atomic_and(~msgType, &mMsgEnabled); - mHardware->disableMsgType(msgType); -} - -#define CHECK_MESSAGE_INTERVAL 10 // 10ms -bool CameraService::Client::lockIfMessageWanted(int32_t msgType) { - int sleepCount = 0; - while (mMsgEnabled & msgType) { - if (mLock.tryLock() == NO_ERROR) { - if (sleepCount > 0) { - LOG1("lockIfMessageWanted(%d): waited for %d ms", - msgType, sleepCount * CHECK_MESSAGE_INTERVAL); - } - return true; - } - if (sleepCount++ == 0) { - LOG1("lockIfMessageWanted(%d): enter sleep", msgType); - } - usleep(CHECK_MESSAGE_INTERVAL * 1000); - } - ALOGW("lockIfMessageWanted(%d): dropped unwanted message", msgType); - return false; + // unconditionally disconnect. function is idempotent + Client::disconnect(); } // ---------------------------------------------------------------------------- @@ -967,311 +433,13 @@ CameraService::Client* CameraService::Client::getClientFromCookie(void* user) { // destruction already started, so should not be accessed if (client->mDestructionStarted) return NULL; - // The checks below are not necessary and are for debugging only. - if (client->mCameraService.get() != gCameraService) { - ALOGE("mismatch service!"); - return NULL; - } - - if (client->mHardware == 0) { - ALOGE("mHardware == 0: callback after disconnect()?"); - return NULL; - } - return client; } -// Callback messages can be dispatched to internal handlers or pass to our -// client's callback functions, depending on the message type. -// -// notifyCallback: -// CAMERA_MSG_SHUTTER handleShutter -// (others) c->notifyCallback -// dataCallback: -// CAMERA_MSG_PREVIEW_FRAME handlePreviewData -// CAMERA_MSG_POSTVIEW_FRAME handlePostview -// CAMERA_MSG_RAW_IMAGE handleRawPicture -// CAMERA_MSG_COMPRESSED_IMAGE handleCompressedPicture -// (others) c->dataCallback -// dataCallbackTimestamp -// (others) c->dataCallbackTimestamp -// -// NOTE: the *Callback functions grab mLock of the client before passing -// control to handle* functions. So the handle* functions must release the -// lock before calling the ICameraClient's callbacks, so those callbacks can -// invoke methods in the Client class again (For example, the preview frame -// callback may want to releaseRecordingFrame). The handle* functions must -// release the lock after all accesses to member variables, so it must be -// handled very carefully. - -void CameraService::Client::notifyCallback(int32_t msgType, int32_t ext1, - int32_t ext2, void* user) { - LOG2("notifyCallback(%d)", msgType); - - Mutex* lock = getClientLockFromCookie(user); - if (lock == NULL) return; - Mutex::Autolock alock(*lock); - - Client* client = getClientFromCookie(user); - if (client == NULL) return; - - if (!client->lockIfMessageWanted(msgType)) return; - - switch (msgType) { - case CAMERA_MSG_SHUTTER: - // ext1 is the dimension of the yuv picture. - client->handleShutter(); - break; - default: - client->handleGenericNotify(msgType, ext1, ext2); - break; - } -} - -void CameraService::Client::dataCallback(int32_t msgType, - const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata, void* user) { - LOG2("dataCallback(%d)", msgType); - - Mutex* lock = getClientLockFromCookie(user); - if (lock == NULL) return; - Mutex::Autolock alock(*lock); - - Client* client = getClientFromCookie(user); - if (client == NULL) return; - - if (!client->lockIfMessageWanted(msgType)) return; - if (dataPtr == 0 && metadata == NULL) { - ALOGE("Null data returned in data callback"); - client->handleGenericNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0); - return; - } - - switch (msgType & ~CAMERA_MSG_PREVIEW_METADATA) { - case CAMERA_MSG_PREVIEW_FRAME: - client->handlePreviewData(msgType, dataPtr, metadata); - break; - case CAMERA_MSG_POSTVIEW_FRAME: - client->handlePostview(dataPtr); - break; - case CAMERA_MSG_RAW_IMAGE: - client->handleRawPicture(dataPtr); - break; - case CAMERA_MSG_COMPRESSED_IMAGE: - client->handleCompressedPicture(dataPtr); - break; - default: - client->handleGenericData(msgType, dataPtr, metadata); - break; - } -} - -void CameraService::Client::dataCallbackTimestamp(nsecs_t timestamp, - int32_t msgType, const sp<IMemory>& dataPtr, void* user) { - LOG2("dataCallbackTimestamp(%d)", msgType); - - Mutex* lock = getClientLockFromCookie(user); - if (lock == NULL) return; - Mutex::Autolock alock(*lock); - - Client* client = getClientFromCookie(user); - if (client == NULL) return; - - if (!client->lockIfMessageWanted(msgType)) return; - - if (dataPtr == 0) { - ALOGE("Null data returned in data with timestamp callback"); - client->handleGenericNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0); - return; - } - - client->handleGenericDataTimestamp(timestamp, msgType, dataPtr); -} - -// snapshot taken callback -void CameraService::Client::handleShutter(void) { - if (mPlayShutterSound) { - mCameraService->playSound(SOUND_SHUTTER); - } - - sp<ICameraClient> c = mCameraClient; - if (c != 0) { - mLock.unlock(); - c->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0); - if (!lockIfMessageWanted(CAMERA_MSG_SHUTTER)) return; - } - disableMsgType(CAMERA_MSG_SHUTTER); - - mLock.unlock(); -} - -// preview callback - frame buffer update -void CameraService::Client::handlePreviewData(int32_t msgType, - const sp<IMemory>& mem, - camera_frame_metadata_t *metadata) { - ssize_t offset; - size_t size; - sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); - - // local copy of the callback flags - int flags = mPreviewCallbackFlag; - - // is callback enabled? - if (!(flags & CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK)) { - // If the enable bit is off, the copy-out and one-shot bits are ignored - LOG2("frame callback is disabled"); - mLock.unlock(); - return; - } - - // hold a strong pointer to the client - sp<ICameraClient> c = mCameraClient; - - // clear callback flags if no client or one-shot mode - if (c == 0 || (mPreviewCallbackFlag & CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK)) { - LOG2("Disable preview callback"); - mPreviewCallbackFlag &= ~(CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK | - CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK | - CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK); - disableMsgType(CAMERA_MSG_PREVIEW_FRAME); - } - - if (c != 0) { - // Is the received frame copied out or not? - if (flags & CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK) { - LOG2("frame is copied"); - copyFrameAndPostCopiedFrame(msgType, c, heap, offset, size, metadata); - } else { - LOG2("frame is forwarded"); - mLock.unlock(); - c->dataCallback(msgType, mem, metadata); - } - } else { - mLock.unlock(); - } -} - -// picture callback - postview image ready -void CameraService::Client::handlePostview(const sp<IMemory>& mem) { - disableMsgType(CAMERA_MSG_POSTVIEW_FRAME); - - sp<ICameraClient> c = mCameraClient; - mLock.unlock(); - if (c != 0) { - c->dataCallback(CAMERA_MSG_POSTVIEW_FRAME, mem, NULL); - } -} - -// picture callback - raw image ready -void CameraService::Client::handleRawPicture(const sp<IMemory>& mem) { - disableMsgType(CAMERA_MSG_RAW_IMAGE); - - ssize_t offset; - size_t size; - sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); - - sp<ICameraClient> c = mCameraClient; - mLock.unlock(); - if (c != 0) { - c->dataCallback(CAMERA_MSG_RAW_IMAGE, mem, NULL); - } -} - -// picture callback - compressed picture ready -void CameraService::Client::handleCompressedPicture(const sp<IMemory>& mem) { - disableMsgType(CAMERA_MSG_COMPRESSED_IMAGE); - - sp<ICameraClient> c = mCameraClient; - mLock.unlock(); - if (c != 0) { - c->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, mem, NULL); - } -} - - -void CameraService::Client::handleGenericNotify(int32_t msgType, - int32_t ext1, int32_t ext2) { - sp<ICameraClient> c = mCameraClient; - mLock.unlock(); - if (c != 0) { - c->notifyCallback(msgType, ext1, ext2); - } -} - -void CameraService::Client::handleGenericData(int32_t msgType, - const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata) { - sp<ICameraClient> c = mCameraClient; - mLock.unlock(); - if (c != 0) { - c->dataCallback(msgType, dataPtr, metadata); - } -} - -void CameraService::Client::handleGenericDataTimestamp(nsecs_t timestamp, - int32_t msgType, const sp<IMemory>& dataPtr) { - sp<ICameraClient> c = mCameraClient; - mLock.unlock(); - if (c != 0) { - c->dataCallbackTimestamp(timestamp, msgType, dataPtr); - } -} - -void CameraService::Client::copyFrameAndPostCopiedFrame( - int32_t msgType, const sp<ICameraClient>& client, - const sp<IMemoryHeap>& heap, size_t offset, size_t size, - camera_frame_metadata_t *metadata) { - LOG2("copyFrameAndPostCopiedFrame"); - // It is necessary to copy out of pmem before sending this to - // the callback. For efficiency, reuse the same MemoryHeapBase - // provided it's big enough. Don't allocate the memory or - // perform the copy if there's no callback. - // hold the preview lock while we grab a reference to the preview buffer - sp<MemoryHeapBase> previewBuffer; - - if (mPreviewBuffer == 0) { - mPreviewBuffer = new MemoryHeapBase(size, 0, NULL); - } else if (size > mPreviewBuffer->virtualSize()) { - mPreviewBuffer.clear(); - mPreviewBuffer = new MemoryHeapBase(size, 0, NULL); - } - if (mPreviewBuffer == 0) { - ALOGE("failed to allocate space for preview buffer"); - mLock.unlock(); - return; - } - previewBuffer = mPreviewBuffer; - - memcpy(previewBuffer->base(), (uint8_t *)heap->base() + offset, size); - - sp<MemoryBase> frame = new MemoryBase(previewBuffer, 0, size); - if (frame == 0) { - ALOGE("failed to allocate space for frame callback"); - mLock.unlock(); - return; - } - - mLock.unlock(); - client->dataCallback(msgType, frame, metadata); -} - -int CameraService::Client::getOrientation(int degrees, bool mirror) { - if (!mirror) { - if (degrees == 0) return 0; - else if (degrees == 90) return HAL_TRANSFORM_ROT_90; - else if (degrees == 180) return HAL_TRANSFORM_ROT_180; - else if (degrees == 270) return HAL_TRANSFORM_ROT_270; - } else { // Do mirror (horizontal flip) - if (degrees == 0) { // FLIP_H and ROT_0 - return HAL_TRANSFORM_FLIP_H; - } else if (degrees == 90) { // FLIP_H and ROT_90 - return HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_ROT_90; - } else if (degrees == 180) { // FLIP_H and ROT_180 - return HAL_TRANSFORM_FLIP_V; - } else if (degrees == 270) { // FLIP_H and ROT_270 - return HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90; - } - } - ALOGE("Invalid setDisplayOrientation degrees=%d", degrees); - return -1; +// NOTE: function is idempotent +void CameraService::Client::disconnect() { + mCameraService->removeClient(mCameraClient); + mCameraService->setCameraFree(mCameraId); } // ---------------------------------------------------------------------------- @@ -1293,41 +461,81 @@ static bool tryLock(Mutex& mutex) } status_t CameraService::dump(int fd, const Vector<String16>& args) { - static const char* kDeadlockedString = "CameraService may be deadlocked\n"; - - const size_t SIZE = 256; - char buffer[SIZE]; String8 result; if (checkCallingPermission(String16("android.permission.DUMP")) == false) { - snprintf(buffer, SIZE, "Permission Denial: " + result.appendFormat("Permission Denial: " "can't dump CameraService from pid=%d, uid=%d\n", getCallingPid(), getCallingUid()); - result.append(buffer); write(fd, result.string(), result.size()); } else { bool locked = tryLock(mServiceLock); // failed to lock - CameraService is probably deadlocked if (!locked) { - String8 result(kDeadlockedString); + result.append("CameraService may be deadlocked\n"); write(fd, result.string(), result.size()); } bool hasClient = false; + if (!mModule) { + result = String8::format("No camera module available!\n"); + write(fd, result.string(), result.size()); + return NO_ERROR; + } + + result = String8::format("Camera module HAL API version: 0x%x\n", + mModule->common.hal_api_version); + result.appendFormat("Camera module API version: 0x%x\n", + mModule->common.module_api_version); + result.appendFormat("Camera module name: %s\n", + mModule->common.name); + result.appendFormat("Camera module author: %s\n", + mModule->common.author); + result.appendFormat("Number of camera devices: %d\n\n", mNumberOfCameras); + write(fd, result.string(), result.size()); for (int i = 0; i < mNumberOfCameras; i++) { + result = String8::format("Camera %d static information:\n", i); + camera_info info; + + status_t rc = mModule->get_camera_info(i, &info); + if (rc != OK) { + result.appendFormat(" Error reading static information!\n"); + write(fd, result.string(), result.size()); + } else { + result.appendFormat(" Facing: %s\n", + info.facing == CAMERA_FACING_BACK ? "BACK" : "FRONT"); + result.appendFormat(" Orientation: %d\n", info.orientation); + int deviceVersion; + if (mModule->common.module_api_version < + CAMERA_MODULE_API_VERSION_2_0) { + deviceVersion = CAMERA_DEVICE_API_VERSION_1_0; + } else { + deviceVersion = info.device_version; + } + result.appendFormat(" Device version: 0x%x\n", deviceVersion); + if (deviceVersion >= CAMERA_DEVICE_API_VERSION_2_0) { + result.appendFormat(" Device static metadata:\n"); + write(fd, result.string(), result.size()); + dump_indented_camera_metadata(info.static_camera_characteristics, + fd, 2, 4); + } else { + write(fd, result.string(), result.size()); + } + } + sp<Client> client = mClient[i].promote(); - if (client == 0) continue; + if (client == 0) { + result = String8::format(" Device is closed, no client instance\n"); + write(fd, result.string(), result.size()); + continue; + } hasClient = true; - sprintf(buffer, "Client[%d] (%p) PID: %d\n", - i, - client->getCameraClient()->asBinder().get(), - client->mClientPid); - result.append(buffer); + result = String8::format(" Device is open. Client instance dump:\n"); write(fd, result.string(), result.size()); - client->mHardware->dump(fd, args); + client->dump(fd, args); } if (!hasClient) { - result.append("No camera client yet.\n"); + result = String8::format("\nNo active camera clients yet.\n"); write(fd, result.string(), result.size()); } @@ -1336,16 +544,42 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) { // change logging level int n = args.size(); for (int i = 0; i + 1 < n; i++) { - if (args[i] == String16("-v")) { + String16 verboseOption("-v"); + if (args[i] == verboseOption) { String8 levelStr(args[i+1]); int level = atoi(levelStr.string()); - sprintf(buffer, "Set Log Level to %d", level); - result.append(buffer); + result = String8::format("\nSetting log level to %d.\n", level); setLogLevel(level); + write(fd, result.string(), result.size()); } } + } return NO_ERROR; } +/*virtual*/void CameraService::binderDied( + const wp<IBinder> &who) { + + /** + * While tempting to promote the wp<IBinder> into a sp, + * it's actually not supported by the binder driver + */ + + ALOGV("java clients' binder died"); + + sp<Client> cameraClient = getClientByRemote(who); + + if (cameraClient == 0) { + ALOGV("java clients' binder death already cleaned up (normal case)"); + return; + } + + ALOGW("Disconnecting camera client %p since the binder for it " + "died (this pid %d)", cameraClient.get(), getCallingPid()); + + cameraClient->disconnect(); + +} + }; // namespace android diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index 95ac1970..4dab340d 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -27,17 +27,19 @@ namespace android { +extern volatile int32_t gLogLevel; + class MemoryHeapBase; class MediaPlayer; -class CameraHardwareInterface; class CameraService : public BinderService<CameraService>, - public BnCameraService + public BnCameraService, + public IBinder::DeathRecipient { - class Client; friend class BinderService<CameraService>; public: + class Client; static char const* getServiceName() { return "media.camera"; } CameraService(); @@ -53,6 +55,8 @@ public: virtual Client* getClientByIdUnsafe(int cameraId); virtual Mutex* getClientLockById(int cameraId); + virtual sp<Client> getClientByRemote(const wp<IBinder>& cameraClient); + virtual status_t dump(int fd, const Vector<String16>& args); virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); @@ -68,114 +72,59 @@ public: void playSound(sound_kind kind); void releaseSound(); -private: - Mutex mServiceLock; - wp<Client> mClient[MAX_CAMERAS]; // protected by mServiceLock - Mutex mClientLock[MAX_CAMERAS]; // prevent Client destruction inside callbacks - int mNumberOfCameras; - - // atomics to record whether the hardware is allocated to some client. - volatile int32_t mBusy[MAX_CAMERAS]; - void setCameraBusy(int cameraId); - void setCameraFree(int cameraId); - - // sounds - MediaPlayer* newMediaPlayer(const char *file); - - Mutex mSoundLock; - sp<MediaPlayer> mSoundPlayer[NUM_SOUNDS]; - int mSoundRef; // reference count (release all MediaPlayer when 0) - class Client : public BnCamera { public: // ICamera interface (see ICamera for details) - virtual void disconnect(); - virtual status_t connect(const sp<ICameraClient>& client); - virtual status_t lock(); - virtual status_t unlock(); - virtual status_t setPreviewDisplay(const sp<Surface>& surface); - virtual status_t setPreviewTexture(const sp<ISurfaceTexture>& surfaceTexture); - virtual void setPreviewCallbackFlag(int flag); - virtual status_t startPreview(); - virtual void stopPreview(); - virtual bool previewEnabled(); - virtual status_t storeMetaDataInBuffers(bool enabled); - virtual status_t startRecording(); - virtual void stopRecording(); - virtual bool recordingEnabled(); - virtual void releaseRecordingFrame(const sp<IMemory>& mem); - virtual status_t autoFocus(); - virtual status_t cancelAutoFocus(); - virtual status_t takePicture(int msgType); - virtual status_t setParameters(const String8& params); - virtual String8 getParameters() const; - virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2); - private: - friend class CameraService; - Client(const sp<CameraService>& cameraService, - const sp<ICameraClient>& cameraClient, - const sp<CameraHardwareInterface>& hardware, - int cameraId, - int cameraFacing, - int clientPid); - ~Client(); + virtual void disconnect(); + virtual status_t connect(const sp<ICameraClient>& client) = 0; + virtual status_t lock() = 0; + virtual status_t unlock() = 0; + virtual status_t setPreviewDisplay(const sp<Surface>& surface) = 0; + virtual status_t setPreviewTexture(const sp<ISurfaceTexture>& surfaceTexture) = 0; + virtual void setPreviewCallbackFlag(int flag) = 0; + virtual status_t startPreview() = 0; + virtual void stopPreview() = 0; + virtual bool previewEnabled() = 0; + virtual status_t storeMetaDataInBuffers(bool enabled) = 0; + virtual status_t startRecording() = 0; + virtual void stopRecording() = 0; + virtual bool recordingEnabled() = 0; + virtual void releaseRecordingFrame(const sp<IMemory>& mem) = 0; + virtual status_t autoFocus() = 0; + virtual status_t cancelAutoFocus() = 0; + virtual status_t takePicture(int msgType) = 0; + virtual status_t setParameters(const String8& params) = 0; + virtual String8 getParameters() const = 0; + virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) = 0; + + // Interface used by CameraService + Client(const sp<CameraService>& cameraService, + const sp<ICameraClient>& cameraClient, + int cameraId, + int cameraFacing, + int clientPid, + int servicePid); + ~Client(); // return our camera client - const sp<ICameraClient>& getCameraClient() { return mCameraClient; } - - // check whether the calling process matches mClientPid. - status_t checkPid() const; - status_t checkPidAndHardware() const; // also check mHardware != 0 - - // these are internal functions used to set up preview buffers - status_t registerPreviewBuffers(); - - // camera operation mode - enum camera_mode { - CAMERA_PREVIEW_MODE = 0, // frame automatically released - CAMERA_RECORDING_MODE = 1, // frame has to be explicitly released by releaseRecordingFrame() - }; - // these are internal functions used for preview/recording - status_t startCameraMode(camera_mode mode); - status_t startPreviewMode(); - status_t startRecordingMode(); - - // internal function used by sendCommand to enable/disable shutter sound. - status_t enableShutterSound(bool enable); - - // these are static callback functions - static void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user); - static void dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, - camera_frame_metadata_t *metadata, void* user); - static void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr, void* user); + const sp<ICameraClient>& getCameraClient() { + return mCameraClient; + } + + virtual status_t initialize(camera_module_t *module) = 0; + + virtual status_t dump(int fd, const Vector<String16>& args) = 0; + + protected: static Mutex* getClientLockFromCookie(void* user); // convert client from cookie. Client lock should be acquired before getting Client. static Client* getClientFromCookie(void* user); - // handlers for messages - void handleShutter(void); - void handlePreviewData(int32_t msgType, const sp<IMemory>& mem, - camera_frame_metadata_t *metadata); - void handlePostview(const sp<IMemory>& mem); - void handleRawPicture(const sp<IMemory>& mem); - void handleCompressedPicture(const sp<IMemory>& mem); - void handleGenericNotify(int32_t msgType, int32_t ext1, int32_t ext2); - void handleGenericData(int32_t msgType, const sp<IMemory>& dataPtr, - camera_frame_metadata_t *metadata); - void handleGenericDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr); - - void copyFrameAndPostCopiedFrame( - int32_t msgType, - const sp<ICameraClient>& client, - const sp<IMemoryHeap>& heap, - size_t offset, size_t size, - camera_frame_metadata_t *metadata); - - int getOrientation(int orientation, bool mirror); - - status_t setPreviewWindow( - const sp<IBinder>& binder, - const sp<ANativeWindow>& window); + + // the instance is in the middle of destruction. When this is set, + // the instance should not be accessed from callback. + // CameraService's mClientLock should be acquired to access this. + bool mDestructionStarted; // these are initialized in the constructor. sp<CameraService> mCameraService; // immutable after constructor @@ -183,51 +132,35 @@ private: int mCameraId; // immutable after constructor int mCameraFacing; // immutable after constructor pid_t mClientPid; - sp<CameraHardwareInterface> mHardware; // cleared after disconnect() - int mPreviewCallbackFlag; - int mOrientation; // Current display orientation - bool mPlayShutterSound; - - // Ensures atomicity among the public methods - mutable Mutex mLock; - // A lock to synchronize access through the ICamera binder - // interface. The entire binder call should be done with mICameraLock - // locked, to serialize ICamera access, even when mLock is disabled for - // calls to the HAL. - mutable Mutex mICameraLock; - // This is a binder of Surface or SurfaceTexture. - sp<IBinder> mSurface; - sp<ANativeWindow> mPreviewWindow; - - // If the user want us to return a copy of the preview frame (instead - // of the original one), we allocate mPreviewBuffer and reuse it if possible. - sp<MemoryHeapBase> mPreviewBuffer; - - // the instance is in the middle of destruction. When this is set, - // the instance should not be accessed from callback. - // CameraService's mClientLock should be acquired to access this. - bool mDestructionStarted; + pid_t mServicePid; // immutable after constructor - // We need to avoid the deadlock when the incoming command thread and - // the CameraHardwareInterface callback thread both want to grab mLock. - // An extra flag is used to tell the callback thread that it should stop - // trying to deliver the callback messages if the client is not - // interested in it anymore. For example, if the client is calling - // stopPreview(), the preview frame messages do not need to be delivered - // anymore. - - // This function takes the same parameter as the enableMsgType() and - // disableMsgType() functions in CameraHardwareInterface. - void enableMsgType(int32_t msgType); - void disableMsgType(int32_t msgType); - volatile int32_t mMsgEnabled; - - // This function keeps trying to grab mLock, or give up if the message - // is found to be disabled. It returns true if mLock is grabbed. - bool lockIfMessageWanted(int32_t msgType); }; +private: + Mutex mServiceLock; + wp<Client> mClient[MAX_CAMERAS]; // protected by mServiceLock + Mutex mClientLock[MAX_CAMERAS]; // prevent Client destruction inside callbacks + int mNumberOfCameras; + + // needs to be called with mServiceLock held + sp<Client> findClientUnsafe(const wp<IBinder>& cameraClient, int& outIndex); + + // atomics to record whether the hardware is allocated to some client. + volatile int32_t mBusy[MAX_CAMERAS]; + void setCameraBusy(int cameraId); + void setCameraFree(int cameraId); + + // sounds + MediaPlayer* newMediaPlayer(const char *file); + + Mutex mSoundLock; + sp<MediaPlayer> mSoundPlayer[NUM_SOUNDS]; + int mSoundRef; // reference count (release all MediaPlayer when 0) + camera_module_t *mModule; + + // IBinder::DeathRecipient implementation + virtual void binderDied(const wp<IBinder> &who); }; } // namespace android diff --git a/services/camera/libcameraservice/CannedJpeg.h b/services/camera/libcameraservice/CannedJpeg.h deleted file mode 100644 index 6dd99c1e..00000000 --- a/services/camera/libcameraservice/CannedJpeg.h +++ /dev/null @@ -1,750 +0,0 @@ -/* - * Copyright (C) 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. - */ - -const int kCannedJpegWidth = 320; -const int kCannedJpegHeight = 240; -const int kCannedJpegSize = 8733; - -const char kCannedJpeg[] = { - 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01, - 0x01, 0x01, 0x00, 0x60, 0x00, 0x60, 0x00, 0x00, 0xff, 0xe1, 0x00, 0x66, - 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x49, 0x49, 0x2a, 0x00, 0x08, 0x00, - 0x00, 0x00, 0x04, 0x00, 0x1a, 0x01, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x00, 0x00, 0x1b, 0x01, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x28, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x31, 0x01, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x50, 0x61, 0x69, 0x6e, 0x74, 0x2e, 0x4e, 0x45, 0x54, 0x20, 0x76, 0x33, - 0x2e, 0x33, 0x36, 0x00, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, - 0x03, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x05, - 0x08, 0x05, 0x05, 0x04, 0x04, 0x05, 0x0a, 0x07, 0x07, 0x06, 0x08, 0x0c, - 0x0a, 0x0c, 0x0c, 0x0b, 0x0a, 0x0b, 0x0b, 0x0d, 0x0e, 0x12, 0x10, 0x0d, - 0x0e, 0x11, 0x0e, 0x0b, 0x0b, 0x10, 0x16, 0x10, 0x11, 0x13, 0x14, 0x15, - 0x15, 0x15, 0x0c, 0x0f, 0x17, 0x18, 0x16, 0x14, 0x18, 0x12, 0x14, 0x15, - 0x14, 0xff, 0xdb, 0x00, 0x43, 0x01, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, - 0x09, 0x05, 0x05, 0x09, 0x14, 0x0d, 0x0b, 0x0d, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, - 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xff, 0xc0, - 0x00, 0x11, 0x08, 0x00, 0xf0, 0x01, 0x40, 0x03, 0x01, 0x22, 0x00, 0x02, - 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x00, 0x1f, 0x00, 0x00, 0x01, - 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, - 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, - 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, - 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, - 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, - 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, - 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, - 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, - 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, - 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, - 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, - 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, - 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, - 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, - 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, - 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc4, 0x00, 0x1f, 0x01, 0x00, 0x03, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, - 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, - 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, - 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, - 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, - 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, - 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, - 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, - 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, - 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, - 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, - 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, - 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, - 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, - 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, - 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, - 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, - 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0xf9, 0xd2, 0xa3, 0x95, 0xbb, - 0x54, 0x84, 0xe0, 0x66, 0xa0, 0x27, 0x27, 0x35, 0xed, 0x9e, 0x50, 0x95, - 0x2c, 0x4b, 0xc6, 0x6a, 0x35, 0x1b, 0x8e, 0x2a, 0x70, 0x30, 0x28, 0x00, - 0xa8, 0xe5, 0x6e, 0x71, 0x52, 0x31, 0xda, 0x33, 0x50, 0x13, 0x93, 0x40, - 0x09, 0x52, 0xc6, 0xb8, 0x19, 0xf5, 0xa6, 0x2a, 0xee, 0x6c, 0x54, 0xd4, - 0x00, 0x54, 0x52, 0x36, 0x5b, 0x1e, 0x95, 0x23, 0xb6, 0xd5, 0xcd, 0x41, - 0x40, 0x05, 0x4c, 0x8b, 0xb5, 0x7d, 0xea, 0x34, 0x5d, 0xcd, 0xed, 0x53, - 0x50, 0x01, 0x50, 0xbb, 0x6e, 0x6f, 0x6a, 0x91, 0xdb, 0x6a, 0xfb, 0xd4, - 0x34, 0x00, 0x54, 0xe8, 0xbb, 0x57, 0x15, 0x1c, 0x6b, 0x96, 0xcf, 0xa5, - 0x4b, 0x40, 0x05, 0x42, 0xcd, 0xb9, 0xb3, 0x4f, 0x91, 0xb0, 0x31, 0xeb, - 0x51, 0x50, 0x02, 0x81, 0x93, 0x53, 0xa8, 0xda, 0x31, 0x51, 0xc4, 0xbc, - 0xe6, 0xa4, 0xa0, 0x00, 0x9c, 0x0a, 0x81, 0x8e, 0xe3, 0x9a, 0x92, 0x56, - 0xe3, 0x15, 0x15, 0x00, 0x28, 0x19, 0x38, 0xa9, 0xc0, 0xc0, 0xc5, 0x47, - 0x12, 0xf7, 0xa9, 0x28, 0x00, 0x27, 0x00, 0x9a, 0x80, 0x9c, 0x9c, 0xd3, - 0xe5, 0x6e, 0xd5, 0x1d, 0x00, 0x2a, 0x8d, 0xc7, 0x15, 0x3d, 0x32, 0x35, - 0xc0, 0xcf, 0xad, 0x3e, 0x80, 0x11, 0x8e, 0xd1, 0x9a, 0x82, 0x9f, 0x23, - 0x64, 0xe3, 0xd2, 0x99, 0x40, 0x0e, 0x45, 0xdc, 0xde, 0xd5, 0x35, 0x36, - 0x35, 0xc2, 0xfb, 0x9a, 0x75, 0x00, 0x35, 0xdb, 0x6a, 0xfb, 0xd4, 0x34, - 0xe9, 0x1b, 0x73, 0x7b, 0x0a, 0x6d, 0x00, 0x3e, 0x35, 0xcb, 0x7b, 0x0a, - 0x96, 0x91, 0x17, 0x6a, 0xd2, 0xd0, 0x03, 0x64, 0x6c, 0x2f, 0xb9, 0xa8, - 0x69, 0xce, 0xdb, 0x9a, 0x9b, 0xd6, 0x80, 0x1f, 0x12, 0xe4, 0xe7, 0xd2, - 0xa5, 0xa4, 0x51, 0xb4, 0x62, 0x97, 0xa5, 0x00, 0x67, 0xc9, 0xad, 0xd8, - 0x91, 0x81, 0x72, 0x9f, 0x9d, 0x47, 0xfd, 0xb3, 0x65, 0xff, 0x00, 0x3f, - 0x29, 0x5f, 0xa0, 0x1f, 0xf0, 0xe9, 0x6f, 0x09, 0x7f, 0xd0, 0xfb, 0xad, - 0x7f, 0xe0, 0x24, 0x34, 0x7f, 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, - 0xee, 0xb5, 0xff, 0x00, 0x80, 0x90, 0xd7, 0x3f, 0xb7, 0x87, 0x73, 0x6f, - 0x63, 0x33, 0xe0, 0x28, 0xf5, 0x9b, 0x11, 0xc9, 0xb9, 0x4c, 0xfd, 0x69, - 0xff, 0x00, 0xdb, 0x96, 0x1f, 0xf3, 0xf5, 0x1f, 0xe7, 0x5f, 0x7d, 0x7f, - 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, 0xff, 0x00, 0x80, - 0x90, 0xd1, 0xff, 0x00, 0x0e, 0x96, 0xf0, 0x97, 0xfd, 0x0f, 0xba, 0xd7, - 0xfe, 0x02, 0x43, 0x47, 0xb7, 0x87, 0x70, 0xf6, 0x33, 0x3e, 0x02, 0x93, - 0x5b, 0xb1, 0x3c, 0x0b, 0x94, 0xc7, 0xd6, 0x99, 0xfd, 0xb3, 0x65, 0xff, - 0x00, 0x3f, 0x29, 0xf9, 0xd7, 0xe8, 0x07, 0xfc, 0x3a, 0x5b, 0xc2, 0x5f, - 0xf4, 0x3e, 0xeb, 0x5f, 0xf8, 0x09, 0x0d, 0x1f, 0xf0, 0xe9, 0x6f, 0x09, - 0x7f, 0xd0, 0xfb, 0xad, 0x7f, 0xe0, 0x24, 0x34, 0xbd, 0xbc, 0x03, 0xd8, - 0xcc, 0xf8, 0x0e, 0x3d, 0x6a, 0xc1, 0x47, 0x37, 0x29, 0x9f, 0xad, 0x3b, - 0xfb, 0x72, 0xc3, 0xfe, 0x7e, 0xa3, 0xfc, 0xeb, 0xef, 0xaf, 0xf8, 0x74, - 0xb7, 0x84, 0xbf, 0xe8, 0x7d, 0xd6, 0xbf, 0xf0, 0x12, 0x1a, 0x3f, 0xe1, - 0xd2, 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, 0x00, 0xc0, 0x48, - 0x69, 0xfb, 0x78, 0x77, 0x0f, 0x63, 0x33, 0xe0, 0x19, 0x35, 0xbb, 0x26, - 0x3c, 0x5c, 0xa6, 0x3e, 0xb4, 0xdf, 0xed, 0x9b, 0x2f, 0xf9, 0xf9, 0x4a, - 0xfd, 0x00, 0xff, 0x00, 0x87, 0x4b, 0x78, 0x4b, 0xfe, 0x87, 0xdd, 0x6b, - 0xff, 0x00, 0x01, 0x21, 0xa3, 0xfe, 0x1d, 0x2d, 0xe1, 0x2f, 0xfa, 0x1f, - 0x75, 0xaf, 0xfc, 0x04, 0x86, 0x97, 0xb7, 0x80, 0x7b, 0x19, 0x9f, 0x01, - 0xa6, 0xb5, 0x60, 0xab, 0xff, 0x00, 0x1f, 0x51, 0xe7, 0xeb, 0x4e, 0xfe, - 0xdc, 0xb0, 0xff, 0x00, 0x9f, 0xa8, 0xff, 0x00, 0x3a, 0xfb, 0xeb, 0xfe, - 0x1d, 0x2d, 0xe1, 0x2f, 0xfa, 0x1f, 0x75, 0xaf, 0xfc, 0x04, 0x86, 0x8f, - 0xf8, 0x74, 0xb7, 0x84, 0xbf, 0xe8, 0x7d, 0xd6, 0xbf, 0xf0, 0x12, 0x1a, - 0x3d, 0xbc, 0x03, 0xd8, 0xcc, 0xf8, 0x05, 0xf5, 0xab, 0x26, 0x6f, 0xf8, - 0xf9, 0x4c, 0x7d, 0x69, 0xbf, 0xdb, 0x36, 0x5f, 0xf3, 0xf2, 0x9f, 0x9d, - 0x7e, 0x80, 0x7f, 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, - 0xff, 0x00, 0x80, 0x90, 0xd1, 0xff, 0x00, 0x0e, 0x96, 0xf0, 0x97, 0xfd, - 0x0f, 0xba, 0xd7, 0xfe, 0x02, 0x43, 0x47, 0xb7, 0x80, 0x7b, 0x19, 0x9f, - 0x02, 0x26, 0xb5, 0x60, 0xab, 0x8f, 0xb5, 0x47, 0xf9, 0xd2, 0xff, 0x00, - 0x6e, 0x58, 0x7f, 0xcf, 0xd4, 0x7f, 0x9d, 0x7d, 0xf5, 0xff, 0x00, 0x0e, - 0x96, 0xf0, 0x97, 0xfd, 0x0f, 0xba, 0xd7, 0xfe, 0x02, 0x43, 0x47, 0xfc, - 0x3a, 0x5b, 0xc2, 0x5f, 0xf4, 0x3e, 0xeb, 0x5f, 0xf8, 0x09, 0x0d, 0x1e, - 0xde, 0x01, 0xec, 0x66, 0x7c, 0x00, 0xda, 0xd5, 0x93, 0x1c, 0xfd, 0xa5, - 0x3f, 0x3a, 0x4f, 0xed, 0x8b, 0x2f, 0xf9, 0xf9, 0x4f, 0xce, 0xbf, 0x40, - 0x3f, 0xe1, 0xd2, 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, 0x00, - 0xc0, 0x48, 0x68, 0xff, 0x00, 0x87, 0x4b, 0x78, 0x4b, 0xfe, 0x87, 0xdd, - 0x6b, 0xff, 0x00, 0x01, 0x21, 0xa7, 0xed, 0xe1, 0xdc, 0x3d, 0x8c, 0xcf, - 0x81, 0x57, 0x5a, 0xb0, 0x51, 0x8f, 0xb5, 0x47, 0xf9, 0xd1, 0xfd, 0xb9, - 0x61, 0xff, 0x00, 0x3f, 0x49, 0xf9, 0xd7, 0xdf, 0x5f, 0xf0, 0xe9, 0x6f, - 0x09, 0x7f, 0xd0, 0xfb, 0xad, 0x7f, 0xe0, 0x24, 0x34, 0x7f, 0xc3, 0xa5, - 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, 0xff, 0x00, 0x80, 0x90, 0xd2, - 0xf6, 0xf0, 0x0f, 0x63, 0x33, 0xe0, 0x06, 0xd6, 0xac, 0x98, 0xe7, 0xed, - 0x29, 0xf9, 0xd2, 0x0d, 0x62, 0xcb, 0xfe, 0x7e, 0x53, 0xf3, 0xaf, 0xd0, - 0x0f, 0xf8, 0x74, 0xb7, 0x84, 0xbf, 0xe8, 0x7d, 0xd6, 0xbf, 0xf0, 0x12, - 0x1a, 0x3f, 0xe1, 0xd2, 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, - 0x00, 0xc0, 0x48, 0x69, 0xfb, 0x78, 0x77, 0x0f, 0x63, 0x33, 0xe0, 0x51, - 0xad, 0xd8, 0x01, 0x8f, 0xb5, 0x47, 0xf9, 0xd0, 0x75, 0xcb, 0x0c, 0x7f, - 0xc7, 0xca, 0x7e, 0x75, 0xf7, 0xd7, 0xfc, 0x3a, 0x5b, 0xc2, 0x5f, 0xf4, - 0x3e, 0xeb, 0x5f, 0xf8, 0x09, 0x0d, 0x1f, 0xf0, 0xe9, 0x6f, 0x09, 0x7f, - 0xd0, 0xfb, 0xad, 0x7f, 0xe0, 0x24, 0x34, 0x7b, 0x78, 0x77, 0x0f, 0x63, - 0x33, 0xf3, 0xfc, 0xeb, 0x36, 0x44, 0xff, 0x00, 0xc7, 0xca, 0x7e, 0x74, - 0xa3, 0x58, 0xb1, 0x24, 0x66, 0xe5, 0x31, 0xf5, 0xaf, 0xbf, 0xff, 0x00, - 0xe1, 0xd2, 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, 0x00, 0xc0, - 0x48, 0x68, 0xff, 0x00, 0x87, 0x4b, 0x78, 0x4b, 0xfe, 0x87, 0xdd, 0x6b, - 0xff, 0x00, 0x01, 0x21, 0xa3, 0xdb, 0xc3, 0xb8, 0x7b, 0x19, 0x9f, 0x02, - 0xff, 0x00, 0x6d, 0xd8, 0x7f, 0xcf, 0xd4, 0x7f, 0x9d, 0x07, 0x5c, 0xb1, - 0x03, 0x8b, 0x94, 0xcf, 0xd6, 0xbe, 0xfa, 0xff, 0x00, 0x87, 0x4b, 0x78, - 0x4b, 0xfe, 0x87, 0xdd, 0x6b, 0xff, 0x00, 0x01, 0x21, 0xa3, 0xfe, 0x1d, - 0x2d, 0xe1, 0x2f, 0xfa, 0x1f, 0x75, 0xaf, 0xfc, 0x04, 0x86, 0x8f, 0x6f, - 0x0e, 0xe1, 0xec, 0x66, 0x7e, 0x7f, 0xff, 0x00, 0x6c, 0xd9, 0x7f, 0xcf, - 0xca, 0x7e, 0x74, 0xab, 0xac, 0x58, 0xe7, 0x9b, 0x94, 0xc7, 0xd6, 0xbe, - 0xff, 0x00, 0xff, 0x00, 0x87, 0x4b, 0x78, 0x4b, 0xfe, 0x87, 0xdd, 0x6b, - 0xff, 0x00, 0x01, 0x21, 0xa3, 0xfe, 0x1d, 0x2d, 0xe1, 0x2f, 0xfa, 0x1f, - 0x75, 0xaf, 0xfc, 0x04, 0x86, 0x8f, 0x6f, 0x0e, 0xe1, 0xec, 0x66, 0x7c, - 0x0b, 0xfd, 0xb9, 0x61, 0xff, 0x00, 0x3f, 0x51, 0xfe, 0x74, 0x8d, 0xae, - 0x58, 0xed, 0x38, 0xb9, 0x4c, 0xfd, 0x6b, 0xef, 0xbf, 0xf8, 0x74, 0xb7, - 0x84, 0xbf, 0xe8, 0x7d, 0xd6, 0xbf, 0xf0, 0x12, 0x1a, 0x3f, 0xe1, 0xd2, - 0xde, 0x12, 0xff, 0x00, 0xa1, 0xf7, 0x5a, 0xff, 0x00, 0xc0, 0x48, 0x68, - 0xf6, 0xf0, 0xee, 0x1e, 0xc6, 0x67, 0xe7, 0xff, 0x00, 0xf6, 0xc5, 0x97, - 0xfc, 0xfc, 0xa7, 0xe7, 0x4e, 0x4d, 0x62, 0xc7, 0x77, 0x37, 0x29, 0xf9, - 0xd7, 0xdf, 0xdf, 0xf0, 0xe9, 0x6f, 0x09, 0x7f, 0xd0, 0xfb, 0xad, 0x7f, - 0xe0, 0x24, 0x34, 0x7f, 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, - 0xb5, 0xff, 0x00, 0x80, 0x90, 0xd1, 0xed, 0xe1, 0xdc, 0x3d, 0x8c, 0xcf, - 0x81, 0x7f, 0xb7, 0x2c, 0x3f, 0xe7, 0xea, 0x3f, 0xce, 0x91, 0xf5, 0xcb, - 0x1c, 0x71, 0x72, 0x9f, 0x9d, 0x7d, 0xf7, 0xff, 0x00, 0x0e, 0x96, 0xf0, - 0x97, 0xfd, 0x0f, 0xba, 0xd7, 0xfe, 0x02, 0x43, 0x47, 0xfc, 0x3a, 0x5b, - 0xc2, 0x5f, 0xf4, 0x3e, 0xeb, 0x5f, 0xf8, 0x09, 0x0d, 0x1e, 0xde, 0x1d, - 0xc3, 0xd8, 0xcc, 0xfc, 0xff, 0x00, 0xfe, 0xd9, 0xb2, 0xff, 0x00, 0x9f, - 0x94, 0xfc, 0xe9, 0xd1, 0xeb, 0x36, 0x20, 0xe4, 0xdc, 0xa7, 0xe7, 0x5f, - 0x7f, 0x7f, 0xc3, 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, 0xff, - 0x00, 0x80, 0x90, 0xd1, 0xff, 0x00, 0x0e, 0x96, 0xf0, 0x97, 0xfd, 0x0f, - 0xba, 0xd7, 0xfe, 0x02, 0x43, 0x47, 0xb7, 0x87, 0x70, 0xf6, 0x33, 0x3e, - 0x05, 0xfe, 0xdc, 0xb0, 0xff, 0x00, 0x9f, 0xa8, 0xff, 0x00, 0x3a, 0x6c, - 0x9a, 0xdd, 0x89, 0x18, 0x17, 0x29, 0xf9, 0xd7, 0xdf, 0x9f, 0xf0, 0xe9, - 0x6f, 0x09, 0x7f, 0xd0, 0xfb, 0xad, 0x7f, 0xe0, 0x24, 0x34, 0x7f, 0xc3, - 0xa5, 0xbc, 0x25, 0xff, 0x00, 0x43, 0xee, 0xb5, 0xff, 0x00, 0x80, 0x90, - 0xd1, 0xed, 0xe1, 0xdc, 0x3d, 0x8c, 0xcf, 0xbc, 0xa8, 0xa2, 0x8a, 0xf3, - 0x0e, 0xf0, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, - 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, - 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0xa0, 0xbb, 0xbd, 0xb7, 0xb0, - 0x88, 0x49, 0x73, 0x3c, 0x56, 0xf1, 0x96, 0x0a, 0x1e, 0x57, 0x0a, 0x09, - 0x3d, 0x06, 0x4f, 0x7a, 0x9e, 0x95, 0xd3, 0x76, 0xea, 0x01, 0x45, 0x14, - 0x53, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, 0x82, 0xda, 0xf6, 0xde, - 0xf0, 0xca, 0x2d, 0xe7, 0x8a, 0x73, 0x13, 0x98, 0xe4, 0xf2, 0xdc, 0x36, - 0xc6, 0x1d, 0x54, 0xe3, 0xa1, 0xf6, 0xa4, 0xda, 0x4e, 0xcc, 0x09, 0xe8, - 0xa2, 0x8a, 0x60, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, - 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, - 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x02, 0xb8, - 0x51, 0x45, 0x14, 0x05, 0xc2, 0x8a, 0x28, 0xa0, 0x2e, 0x14, 0x51, 0x45, - 0x01, 0x70, 0xa2, 0x8a, 0x28, 0x18, 0x51, 0x45, 0x14, 0x0a, 0xe1, 0x45, - 0x14, 0x50, 0x17, 0x0a, 0x28, 0xa2, 0x80, 0xb9, 0xca, 0xfc, 0x4a, 0xf0, - 0x52, 0x78, 0xef, 0xc2, 0xb7, 0x1a, 0x76, 0xef, 0x2e, 0xe5, 0x4f, 0x9d, - 0x6c, 0xe4, 0xe0, 0x09, 0x00, 0x38, 0xcf, 0xb1, 0xc9, 0x1f, 0x8e, 0x7b, - 0x57, 0x3d, 0xf0, 0x5b, 0xc7, 0x53, 0x6b, 0xba, 0x6c, 0xda, 0x16, 0xaa, - 0x5a, 0x3d, 0x73, 0x4a, 0xfd, 0xd4, 0x8b, 0x2f, 0xdf, 0x91, 0x01, 0xc0, - 0x27, 0xdc, 0x1e, 0x0f, 0xe0, 0x7b, 0xd7, 0xa3, 0x5c, 0xdc, 0xc5, 0x67, - 0x04, 0x93, 0xcf, 0x2a, 0x43, 0x0c, 0x60, 0xb3, 0xc9, 0x23, 0x05, 0x55, - 0x1e, 0xa4, 0x9e, 0x95, 0xf3, 0x47, 0xc4, 0x8f, 0x1f, 0xe9, 0x36, 0xdf, - 0x10, 0xed, 0x3c, 0x41, 0xe1, 0x39, 0x99, 0xaf, 0xa1, 0xe2, 0xea, 0x42, - 0x98, 0x82, 0x72, 0x38, 0xe3, 0x90, 0x4e, 0x46, 0x41, 0xe9, 0x9c, 0x0c, - 0x7a, 0xd7, 0xc4, 0x67, 0x98, 0x9a, 0x59, 0x3e, 0x26, 0x9e, 0x64, 0xa6, - 0x93, 0x7e, 0xec, 0xe3, 0x7d, 0x65, 0x1e, 0xe9, 0x77, 0x8b, 0xd7, 0xd3, - 0x4b, 0x99, 0x4d, 0xa8, 0xbe, 0x63, 0xe9, 0xca, 0x2b, 0xe4, 0x3d, 0x73, - 0xe3, 0x3f, 0x8b, 0xb5, 0xc6, 0x6d, 0xfa, 0xb4, 0x96, 0x71, 0x9e, 0x91, - 0x59, 0x0f, 0x28, 0x0f, 0xc4, 0x7c, 0xdf, 0x99, 0xae, 0x56, 0xe7, 0x5a, - 0xd4, 0x6f, 0x18, 0xb5, 0xc5, 0xfd, 0xd4, 0xec, 0x7b, 0xc9, 0x33, 0x31, - 0xfd, 0x4d, 0x78, 0x75, 0xf8, 0xfb, 0x0b, 0x07, 0x6a, 0x14, 0x65, 0x25, - 0xe6, 0xd2, 0xff, 0x00, 0x32, 0x1d, 0x75, 0xd1, 0x1f, 0x73, 0x51, 0x5f, - 0x0b, 0xdb, 0xea, 0xf7, 0xf6, 0xad, 0xba, 0x0b, 0xdb, 0x88, 0x58, 0x77, - 0x8e, 0x56, 0x53, 0xfa, 0x1a, 0xe9, 0xf4, 0x5f, 0x8b, 0xfe, 0x2e, 0xd0, - 0xd9, 0x7c, 0xad, 0x66, 0x7b, 0x84, 0x1f, 0xf2, 0xce, 0xec, 0xf9, 0xc0, - 0xff, 0x00, 0xdf, 0x59, 0x23, 0xf0, 0x34, 0xa8, 0x71, 0xf6, 0x1a, 0x4e, - 0xd5, 0xa8, 0x4a, 0x2b, 0xc9, 0xa7, 0xfe, 0x40, 0xab, 0xae, 0xa8, 0xfa, - 0x13, 0xe2, 0xff, 0x00, 0x8f, 0xcf, 0x82, 0xfc, 0x3e, 0x21, 0xb3, 0x6d, - 0xda, 0xcd, 0xfe, 0x62, 0xb5, 0x45, 0xe5, 0x97, 0xb1, 0x7c, 0x7b, 0x67, - 0x8f, 0x72, 0x3d, 0xea, 0x5f, 0x84, 0x7e, 0x05, 0x6f, 0x04, 0x78, 0x60, - 0x2d, 0xd1, 0x2d, 0xa9, 0xde, 0xb0, 0x9e, 0xe8, 0x93, 0x9d, 0xad, 0x8e, - 0x17, 0xf0, 0x1d, 0x4f, 0xa9, 0x35, 0xe2, 0x5e, 0x13, 0xf8, 0x89, 0x61, - 0xac, 0xfc, 0x49, 0x8f, 0xc4, 0x3e, 0x30, 0x76, 0xcc, 0x68, 0x16, 0xd8, - 0x43, 0x19, 0x68, 0x61, 0x61, 0xd0, 0x91, 0x92, 0x40, 0x1c, 0x9e, 0x33, - 0xc9, 0xcd, 0x7d, 0x3b, 0x63, 0x7f, 0x6d, 0xaa, 0x5a, 0x45, 0x75, 0x69, - 0x3c, 0x77, 0x36, 0xd2, 0x8d, 0xc9, 0x2c, 0x4c, 0x19, 0x58, 0x7b, 0x11, - 0x5e, 0xde, 0x4d, 0x8b, 0xa3, 0x9d, 0xe3, 0x2a, 0x66, 0x1c, 0xe9, 0xf2, - 0x5e, 0x30, 0x8f, 0x58, 0xae, 0xb2, 0x6b, 0xbc, 0xbf, 0x05, 0xa1, 0x50, - 0x6a, 0x6f, 0x98, 0xb1, 0x45, 0x14, 0x57, 0xdc, 0x9b, 0x5c, 0x28, 0xa2, - 0x8a, 0x02, 0xe1, 0x45, 0x14, 0x50, 0x17, 0x0a, 0x28, 0xa2, 0x80, 0xb8, - 0x51, 0x45, 0x14, 0x05, 0xc2, 0x8a, 0x28, 0xa0, 0x2e, 0x14, 0x51, 0x45, - 0x01, 0x70, 0xa2, 0x8a, 0x28, 0x0b, 0x8d, 0xcd, 0x19, 0xa6, 0xe4, 0x51, - 0x91, 0x55, 0x62, 0x47, 0x66, 0x8c, 0xd3, 0x72, 0x28, 0xc8, 0xa2, 0xc0, - 0x3b, 0x34, 0x66, 0x9b, 0x91, 0x46, 0x45, 0x16, 0x01, 0xd9, 0xa3, 0x34, - 0xdc, 0x8a, 0x32, 0x28, 0xb0, 0x0e, 0xcd, 0x19, 0xa6, 0xe4, 0x52, 0xe4, - 0x51, 0x60, 0xb8, 0xb9, 0xa3, 0x34, 0xdc, 0x8a, 0x32, 0x28, 0xb0, 0x0e, - 0xdd, 0x46, 0x69, 0xb9, 0x14, 0x64, 0x51, 0x60, 0x1d, 0x9a, 0xa7, 0xac, - 0x6b, 0x16, 0x9a, 0x0e, 0x9b, 0x71, 0xa8, 0x5f, 0x4c, 0x20, 0xb5, 0x81, - 0x37, 0xbb, 0x9e, 0xc3, 0xd0, 0x7a, 0x93, 0xd0, 0x0a, 0xb5, 0x91, 0x5f, - 0x39, 0xfe, 0xd1, 0x1e, 0x37, 0x7d, 0x4b, 0x5a, 0x4f, 0x0f, 0x5b, 0x48, - 0x45, 0xa5, 0x96, 0x1e, 0x70, 0xa7, 0xef, 0xca, 0x46, 0x40, 0x3f, 0xee, - 0x83, 0xf9, 0x93, 0xe9, 0x5e, 0x06, 0x79, 0x9a, 0xc3, 0x27, 0xc1, 0x4b, - 0x12, 0xd5, 0xe5, 0xb4, 0x57, 0x76, 0xff, 0x00, 0xab, 0xbf, 0x24, 0x44, - 0xe5, 0xca, 0xae, 0x72, 0xbf, 0x12, 0xbe, 0x2a, 0xea, 0x3e, 0x3e, 0xbd, - 0x78, 0xd5, 0x9e, 0xd3, 0x48, 0x46, 0xfd, 0xd5, 0xa2, 0x9f, 0xbd, 0xe8, - 0xcf, 0xea, 0x7f, 0x41, 0xdb, 0xd4, 0xc3, 0xe0, 0x5f, 0x85, 0x1a, 0xd7, - 0x8f, 0xed, 0xe6, 0xb9, 0xb1, 0xf2, 0x2d, 0xed, 0x22, 0x6d, 0x86, 0x7b, - 0x96, 0x21, 0x59, 0xb1, 0x9c, 0x0c, 0x02, 0x4f, 0x51, 0xf9, 0xd7, 0x19, - 0x5e, 0xcd, 0xf0, 0x73, 0xe3, 0x16, 0x97, 0xe1, 0x0d, 0x06, 0x4d, 0x23, - 0x57, 0x49, 0x63, 0x44, 0x95, 0xa5, 0x86, 0x78, 0x53, 0x78, 0x21, 0xba, - 0xab, 0x0e, 0xb9, 0xcf, 0x7f, 0x7f, 0x6a, 0xfc, 0x1b, 0x2e, 0xa9, 0x87, - 0xcd, 0xb3, 0x2f, 0x69, 0x9c, 0xd5, 0x6a, 0x2e, 0xfa, 0xde, 0xda, 0xf4, - 0x57, 0xe8, 0xbf, 0xe1, 0x8e, 0x48, 0xda, 0x52, 0xf7, 0x8f, 0x30, 0xf1, - 0x57, 0x85, 0x75, 0x0f, 0x06, 0xeb, 0x12, 0x69, 0xba, 0x94, 0x42, 0x3b, - 0x84, 0x01, 0x83, 0x21, 0xca, 0xba, 0x9e, 0x8c, 0xa7, 0xb8, 0xac, 0x8a, - 0xed, 0x3e, 0x2c, 0xf8, 0xee, 0x1f, 0x1f, 0xf8, 0x9c, 0x5e, 0xda, 0xc2, - 0xf0, 0xda, 0x41, 0x08, 0x82, 0x2f, 0x33, 0x01, 0xd8, 0x02, 0x49, 0x63, - 0xe9, 0xc9, 0x3c, 0x57, 0x17, 0x5e, 0x26, 0x3e, 0x9e, 0x1e, 0x96, 0x2a, - 0xa4, 0x30, 0xb2, 0xe6, 0xa6, 0x9b, 0xb3, 0xee, 0x88, 0x76, 0xbe, 0x81, - 0x5a, 0x1a, 0x06, 0x83, 0x7b, 0xe2, 0x7d, 0x5e, 0xdf, 0x4d, 0xd3, 0xe2, - 0xf3, 0xae, 0xa7, 0x38, 0x55, 0xce, 0x00, 0x00, 0x64, 0x92, 0x7b, 0x00, - 0x39, 0xac, 0xfa, 0xea, 0x3e, 0x1b, 0x78, 0xc1, 0x7c, 0x0d, 0xe2, 0xcb, - 0x5d, 0x52, 0x58, 0x4c, 0xf6, 0xe1, 0x5a, 0x39, 0x51, 0x3e, 0xf6, 0xd6, - 0x18, 0x24, 0x7b, 0x8e, 0x0d, 0x67, 0x83, 0x85, 0x1a, 0x98, 0x8a, 0x70, - 0xc4, 0x4b, 0x96, 0x0d, 0xae, 0x67, 0xd9, 0x5f, 0x50, 0x56, 0xbe, 0xa6, - 0x97, 0x8d, 0x7e, 0x0e, 0xeb, 0xde, 0x06, 0xd3, 0x17, 0x50, 0xbb, 0x36, - 0xf7, 0x56, 0x99, 0x0b, 0x24, 0x96, 0xae, 0x4f, 0x96, 0x4f, 0x4d, 0xc0, - 0x81, 0xc1, 0x3c, 0x66, 0xa9, 0xfc, 0x3e, 0xf8, 0x93, 0xaa, 0x78, 0x03, - 0x50, 0x0f, 0x6c, 0xe6, 0x7b, 0x07, 0x6f, 0xdf, 0xd9, 0x3b, 0x7c, 0x8e, - 0x3d, 0x47, 0xa3, 0x7b, 0xfe, 0x79, 0xaf, 0x45, 0xf8, 0xad, 0xf1, 0xb3, - 0x47, 0xf1, 0x27, 0x85, 0x26, 0xd2, 0x34, 0x84, 0x9a, 0x67, 0xbb, 0x2b, - 0xe6, 0xcb, 0x34, 0x7b, 0x04, 0x6a, 0x18, 0x36, 0x07, 0xa9, 0xc8, 0x1e, - 0xd5, 0xe1, 0x95, 0xf4, 0x39, 0xab, 0xc2, 0x65, 0x79, 0x84, 0x67, 0x93, - 0x55, 0x6d, 0x24, 0x9d, 0xd3, 0xbd, 0x9f, 0x55, 0x7e, 0xaa, 0xd6, 0xbe, - 0xfb, 0xd8, 0xb9, 0x5a, 0x32, 0xf7, 0x59, 0xf6, 0xef, 0x86, 0xbc, 0x49, - 0x63, 0xe2, 0xbd, 0x1a, 0xdf, 0x53, 0xd3, 0xe5, 0xf3, 0x2d, 0xe6, 0x1d, - 0xfe, 0xf2, 0x1e, 0xea, 0xc3, 0xb1, 0x15, 0xa9, 0x9a, 0xf9, 0x7b, 0xe0, - 0x27, 0x8d, 0xe4, 0xf0, 0xef, 0x8a, 0x53, 0x4a, 0x9e, 0x43, 0xfd, 0x9f, - 0xa9, 0x30, 0x8f, 0x69, 0x3c, 0x24, 0xdf, 0xc0, 0xc3, 0xeb, 0xf7, 0x7f, - 0x11, 0xe9, 0x5f, 0x4f, 0xe4, 0x57, 0xee, 0x3c, 0x3f, 0x9b, 0xc7, 0x39, - 0xc1, 0x2a, 0xed, 0x5a, 0x6b, 0x49, 0x2f, 0x3f, 0xf2, 0x7b, 0xfe, 0x1d, - 0x0e, 0xb8, 0x4f, 0x99, 0x5c, 0x76, 0x4d, 0x19, 0xa6, 0xe4, 0x51, 0x91, - 0x5f, 0x4b, 0x62, 0xc7, 0x64, 0xd1, 0x9a, 0x6e, 0x45, 0x19, 0x14, 0x58, - 0x2e, 0x3b, 0x34, 0x66, 0x9b, 0x91, 0x46, 0x45, 0x16, 0x0b, 0x8e, 0xcd, - 0x19, 0xa6, 0xe4, 0x51, 0x91, 0x45, 0x80, 0x76, 0x68, 0xcd, 0x37, 0x34, - 0x64, 0x51, 0x60, 0xb8, 0xec, 0xd1, 0x9a, 0x6e, 0x45, 0x19, 0x14, 0x58, - 0x07, 0x64, 0xd1, 0x9a, 0x6e, 0x45, 0x19, 0x14, 0x58, 0x06, 0x6e, 0xa3, - 0x75, 0x37, 0x34, 0x66, 0xae, 0xc4, 0x5c, 0x76, 0xea, 0x37, 0x53, 0x73, - 0x46, 0x68, 0xb0, 0x5c, 0x76, 0xea, 0x37, 0x53, 0x73, 0x46, 0x68, 0xb0, - 0x5c, 0x76, 0xea, 0x37, 0x53, 0x72, 0x28, 0xcd, 0x16, 0x0b, 0x8e, 0xdd, - 0x46, 0xea, 0x6e, 0x68, 0xcd, 0x16, 0x0b, 0x8e, 0xdd, 0x46, 0xea, 0x6e, - 0x68, 0xcd, 0x16, 0x0b, 0x8e, 0xdd, 0x46, 0xea, 0xc4, 0xf1, 0x57, 0x8c, - 0x34, 0xaf, 0x06, 0x69, 0xff, 0x00, 0x6b, 0xd5, 0x2e, 0x44, 0x28, 0xc7, - 0x08, 0x8a, 0x37, 0x3c, 0x87, 0xd1, 0x47, 0x7f, 0xe5, 0x5c, 0x0d, 0x9f, - 0xed, 0x1f, 0xe1, 0xcb, 0x8b, 0xc1, 0x14, 0xd6, 0x97, 0xf6, 0xb0, 0x93, - 0x81, 0x3b, 0xa2, 0xb0, 0x1e, 0xe4, 0x06, 0x27, 0xf2, 0xcd, 0x78, 0xf8, - 0xac, 0xdf, 0x2f, 0xc0, 0xd4, 0x54, 0x71, 0x35, 0xa3, 0x19, 0x3e, 0x8d, - 0xfe, 0x7d, 0xbe, 0x64, 0xb9, 0x25, 0xb9, 0xeb, 0x05, 0xf6, 0x82, 0x4f, - 0x41, 0x5f, 0x10, 0xeb, 0x7a, 0x93, 0xeb, 0x3a, 0xcd, 0xf5, 0xfc, 0x84, - 0x97, 0xb9, 0x9d, 0xe6, 0x39, 0xff, 0x00, 0x69, 0x89, 0xfe, 0xb5, 0xf6, - 0xad, 0x8e, 0xa1, 0x6b, 0xab, 0x58, 0xc5, 0x75, 0x69, 0x34, 0x77, 0x36, - 0xb3, 0x2e, 0xe4, 0x91, 0x0e, 0x55, 0x85, 0x78, 0x5f, 0xfc, 0x2d, 0x5f, - 0x87, 0x3f, 0xf4, 0x25, 0x27, 0xfe, 0x00, 0xdb, 0xff, 0x00, 0x8d, 0x7c, - 0x67, 0x18, 0xe1, 0xa8, 0x63, 0x61, 0x87, 0x55, 0x31, 0x31, 0xa7, 0x1f, - 0x79, 0xab, 0xdd, 0xf3, 0x7c, 0x3a, 0xab, 0x76, 0xfd, 0x4c, 0xea, 0x59, - 0xdb, 0x53, 0xc4, 0x68, 0xaf, 0x6e, 0xff, 0x00, 0x85, 0xab, 0xf0, 0xe7, - 0xfe, 0x84, 0xa4, 0xff, 0x00, 0xc0, 0x1b, 0x7f, 0xf1, 0xa3, 0xfe, 0x16, - 0xaf, 0xc3, 0x9f, 0xfa, 0x12, 0x93, 0xff, 0x00, 0x00, 0x6d, 0xff, 0x00, - 0xc6, 0xbf, 0x33, 0xfe, 0xc5, 0xc0, 0xff, 0x00, 0xd0, 0x7c, 0x3e, 0xe9, - 0x7f, 0x91, 0x8f, 0x2a, 0xee, 0x78, 0x8d, 0x15, 0xed, 0xdf, 0xf0, 0xb5, - 0x7e, 0x1c, 0xff, 0x00, 0xd0, 0x94, 0x9f, 0xf8, 0x03, 0x6f, 0xfe, 0x34, - 0x7f, 0xc2, 0xd5, 0xf8, 0x73, 0xff, 0x00, 0x42, 0x52, 0x7f, 0xe0, 0x0d, - 0xbf, 0xf8, 0xd1, 0xfd, 0x8b, 0x81, 0xff, 0x00, 0xa0, 0xf8, 0x7d, 0xd2, - 0xff, 0x00, 0x20, 0xe5, 0x5d, 0xcf, 0x11, 0xa2, 0xbd, 0xbb, 0xfe, 0x16, - 0xaf, 0xc3, 0x9f, 0xfa, 0x12, 0x93, 0xff, 0x00, 0x00, 0x6d, 0xff, 0x00, - 0xc6, 0x8f, 0xf8, 0x5a, 0xbf, 0x0e, 0x7f, 0xe8, 0x4a, 0x4f, 0xfc, 0x01, - 0xb7, 0xff, 0x00, 0x1a, 0x3f, 0xb1, 0x70, 0x3f, 0xf4, 0x1f, 0x0f, 0xba, - 0x5f, 0xe4, 0x1c, 0xab, 0xb9, 0xe2, 0x34, 0x57, 0xb7, 0x7f, 0xc2, 0xd5, - 0xf8, 0x73, 0xff, 0x00, 0x42, 0x52, 0x7f, 0xe0, 0x0d, 0xbf, 0xf8, 0xd1, - 0xff, 0x00, 0x0b, 0x57, 0xe1, 0xcf, 0xfd, 0x09, 0x49, 0xff, 0x00, 0x80, - 0x36, 0xff, 0x00, 0xe3, 0x47, 0xf6, 0x2e, 0x07, 0xfe, 0x83, 0xe1, 0xf7, - 0x4b, 0xfc, 0x83, 0x95, 0x77, 0x3c, 0x52, 0x09, 0xde, 0xda, 0x78, 0xe6, - 0x89, 0x8a, 0x49, 0x1b, 0x07, 0x56, 0x1d, 0x41, 0x07, 0x20, 0xd7, 0xdb, - 0xfa, 0x5d, 0xf0, 0xd4, 0x74, 0xdb, 0x4b, 0xb0, 0x30, 0x27, 0x85, 0x25, - 0x03, 0xfd, 0xe5, 0x07, 0xfa, 0xd7, 0x85, 0xff, 0x00, 0xc2, 0xd5, 0xf8, - 0x73, 0xff, 0x00, 0x42, 0x52, 0x7f, 0xe0, 0x0d, 0xbf, 0xf8, 0xd7, 0xb6, - 0x69, 0x1a, 0x95, 0xa5, 0xc7, 0x87, 0xec, 0xaf, 0xe1, 0x55, 0xb3, 0xb1, - 0x7b, 0x54, 0x99, 0x11, 0xf0, 0x82, 0x28, 0xca, 0x02, 0x01, 0xec, 0x30, - 0x3f, 0x0e, 0x2b, 0xf4, 0x7e, 0x0e, 0xc2, 0xd0, 0xc1, 0xce, 0xbc, 0x69, - 0x62, 0x63, 0x51, 0x34, 0x9b, 0x4a, 0xfa, 0x5a, 0xfa, 0xeb, 0xea, 0x6d, - 0x4e, 0xca, 0xfa, 0x9a, 0x5b, 0xa8, 0xdd, 0x5e, 0x57, 0xab, 0xfe, 0xd1, - 0x3e, 0x1b, 0xd3, 0xaf, 0x1a, 0x0b, 0x68, 0x6e, 0xf5, 0x15, 0x53, 0x83, - 0x34, 0x28, 0xaa, 0x87, 0xe9, 0xb8, 0x82, 0x7f, 0x2a, 0xeb, 0xbc, 0x1b, - 0xf1, 0x07, 0x45, 0xf1, 0xcd, 0xbb, 0xbe, 0x99, 0x70, 0x7c, 0xe8, 0xc6, - 0x64, 0xb6, 0x98, 0x6d, 0x91, 0x07, 0xa9, 0x1d, 0xc7, 0xb8, 0xc8, 0xaf, - 0xb7, 0xc3, 0xe7, 0x19, 0x76, 0x2a, 0xb7, 0xd5, 0xe8, 0x56, 0x8c, 0xa7, - 0xd9, 0x3f, 0xcb, 0xbf, 0xc8, 0xd1, 0x49, 0x3d, 0x2e, 0x74, 0xdb, 0xa8, - 0xcd, 0x37, 0x34, 0x64, 0x57, 0xb2, 0x55, 0xc7, 0x6e, 0xa3, 0x75, 0x37, - 0x34, 0x66, 0x8b, 0x05, 0xc7, 0x6e, 0xa3, 0x75, 0x37, 0x34, 0x66, 0x8b, - 0x05, 0xc7, 0x6e, 0xa3, 0x75, 0x37, 0x34, 0x66, 0x8b, 0x05, 0xc7, 0x6e, - 0xa3, 0x75, 0x37, 0x34, 0x64, 0x51, 0x60, 0xb8, 0xed, 0xd4, 0x6e, 0xa6, - 0xe4, 0x51, 0x9a, 0x2c, 0x17, 0x1b, 0x9a, 0x33, 0x51, 0xee, 0xa3, 0x75, - 0x55, 0x88, 0xb9, 0x26, 0x68, 0xcd, 0x47, 0xba, 0x8d, 0xd4, 0x58, 0x2e, - 0x49, 0x9a, 0x33, 0x51, 0xee, 0xa3, 0x75, 0x16, 0x15, 0xc9, 0x32, 0x28, - 0xa8, 0xf7, 0x51, 0xba, 0x8b, 0x0e, 0xe4, 0x99, 0xa3, 0x35, 0x1e, 0xea, - 0x37, 0x51, 0x60, 0xb9, 0x26, 0x68, 0xcd, 0x47, 0xba, 0x8c, 0xd3, 0xb0, - 0x5c, 0xf9, 0x7b, 0xe3, 0xb6, 0xaf, 0x3e, 0xa5, 0xf1, 0x0e, 0xf2, 0xde, - 0x47, 0x26, 0x1b, 0x24, 0x48, 0x62, 0x4e, 0xc0, 0x15, 0x0c, 0x4f, 0xe2, - 0x58, 0xfe, 0x95, 0xe7, 0x95, 0xda, 0x7c, 0x64, 0xff, 0x00, 0x92, 0x97, - 0xad, 0xff, 0x00, 0xbf, 0x1f, 0xfe, 0x8a, 0x4a, 0xe2, 0xeb, 0xf9, 0x47, - 0x3a, 0x9c, 0xaa, 0x66, 0x78, 0x99, 0x49, 0xdd, 0xf3, 0xcb, 0xf0, 0x6d, - 0x23, 0x92, 0x5b, 0x9e, 0xf7, 0xfb, 0x34, 0xeb, 0x13, 0xcd, 0x67, 0xac, - 0xe9, 0xb2, 0x39, 0x6b, 0x78, 0x1a, 0x39, 0xa2, 0x04, 0xfd, 0xd2, 0xdb, - 0x83, 0x0f, 0xc7, 0x68, 0xfd, 0x6b, 0xc1, 0x2b, 0xda, 0xff, 0x00, 0x66, - 0x73, 0x8b, 0xed, 0x7f, 0xfe, 0xb9, 0xc3, 0xfc, 0xde, 0xbc, 0x52, 0xbd, - 0x8c, 0xd2, 0x72, 0x9e, 0x4b, 0x97, 0x39, 0x3b, 0xdb, 0xda, 0xaf, 0x92, - 0x92, 0xb1, 0x4f, 0xe1, 0x41, 0x45, 0x14, 0x57, 0xc6, 0x90, 0x14, 0x51, - 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x14, 0x51, 0x45, 0x00, 0x15, 0xef, - 0x7f, 0x13, 0xf5, 0x89, 0xf4, 0xef, 0x82, 0xbe, 0x19, 0xb6, 0x81, 0xca, - 0x0b, 0xc8, 0x2d, 0x62, 0x94, 0x83, 0xd5, 0x04, 0x3b, 0x88, 0xfc, 0x48, - 0x15, 0xe0, 0x95, 0xed, 0x7f, 0x17, 0x0f, 0xfc, 0x5a, 0x5f, 0x05, 0xff, - 0x00, 0xd7, 0x38, 0x3f, 0xf4, 0x45, 0x7d, 0x96, 0x47, 0x39, 0x43, 0x03, - 0x98, 0x4a, 0x2e, 0xcf, 0x91, 0x7e, 0x32, 0xb3, 0xfc, 0x0a, 0x8e, 0xcc, - 0xf1, 0x4a, 0xe9, 0xbe, 0x1a, 0x6a, 0xf3, 0xe8, 0xbe, 0x3a, 0xd1, 0x67, - 0x81, 0xca, 0x99, 0x2e, 0x52, 0x07, 0x00, 0xfd, 0xe4, 0x76, 0x0a, 0xc0, - 0xfe, 0x07, 0xf4, 0xae, 0x66, 0xb6, 0x3c, 0x1b, 0xff, 0x00, 0x23, 0x7e, - 0x87, 0xff, 0x00, 0x5f, 0xd0, 0x7f, 0xe8, 0xc5, 0xaf, 0x9b, 0xc0, 0xce, - 0x54, 0xf1, 0x54, 0xa7, 0x07, 0x66, 0xa4, 0xbf, 0x31, 0x2d, 0xcf, 0xb4, - 0x33, 0x46, 0x6a, 0x3d, 0xd4, 0x6e, 0xaf, 0xeb, 0x9b, 0x1d, 0x57, 0x24, - 0xcd, 0x19, 0xa8, 0xf7, 0x51, 0xba, 0x8b, 0x0e, 0xe4, 0x99, 0xa3, 0x35, - 0x1e, 0xea, 0x37, 0x51, 0x60, 0xb9, 0x26, 0x68, 0xcd, 0x47, 0xba, 0x8d, - 0xd4, 0x58, 0x2e, 0x49, 0x9a, 0x33, 0x51, 0xee, 0xa3, 0x75, 0x16, 0x15, - 0xc9, 0x33, 0x46, 0x6a, 0x3d, 0xd4, 0x6e, 0xa2, 0xc3, 0xb8, 0xdd, 0xd4, - 0x9b, 0xa9, 0xbb, 0xa8, 0xdd, 0x5a, 0x19, 0xdc, 0x7e, 0xea, 0x4d, 0xd4, - 0xdd, 0xd4, 0x6e, 0xa0, 0x57, 0x1f, 0xba, 0x8d, 0xd4, 0xcd, 0xd4, 0x6e, - 0xa0, 0x77, 0x1f, 0xba, 0x8d, 0xd4, 0xcd, 0xd4, 0x6e, 0xa4, 0x2b, 0x8f, - 0xdd, 0x49, 0xba, 0x9b, 0xba, 0x8d, 0xd4, 0xc2, 0xe3, 0xb7, 0x52, 0xee, - 0xa6, 0x6e, 0xa3, 0x75, 0x20, 0xb9, 0xf2, 0x9f, 0xc6, 0x3f, 0xf9, 0x29, - 0x5a, 0xdf, 0xfb, 0xf1, 0xff, 0x00, 0xe8, 0xb4, 0xae, 0x32, 0xbb, 0x2f, - 0x8c, 0x5c, 0xfc, 0x49, 0xd6, 0xff, 0x00, 0xdf, 0x8f, 0xff, 0x00, 0x45, - 0xa5, 0x71, 0xb5, 0xfc, 0x99, 0x9c, 0x7f, 0xc8, 0xcb, 0x13, 0xfe, 0x39, - 0xff, 0x00, 0xe9, 0x4c, 0xc1, 0xee, 0x7b, 0x57, 0xec, 0xd2, 0x71, 0x7d, - 0xaf, 0x7f, 0xd7, 0x38, 0x7f, 0x9b, 0xd7, 0x8a, 0xd7, 0xb4, 0x7e, 0xcd, - 0x67, 0x17, 0xda, 0xf7, 0xfd, 0x73, 0x87, 0xf9, 0xbd, 0x78, 0xbd, 0x7b, - 0x19, 0x97, 0xfc, 0x89, 0x32, 0xef, 0xfb, 0x8b, 0xff, 0x00, 0xa5, 0x21, - 0xbd, 0x90, 0x51, 0x45, 0x15, 0xf2, 0x02, 0x0a, 0x28, 0xa2, 0x80, 0x0a, - 0x28, 0xa2, 0x80, 0x0a, 0x28, 0xa2, 0x80, 0x0a, 0xf6, 0xaf, 0x8b, 0x47, - 0x3f, 0x09, 0x7c, 0x17, 0xff, 0x00, 0x5c, 0xe0, 0xff, 0x00, 0xd1, 0x15, - 0xe2, 0xb5, 0xed, 0x1f, 0x16, 0x4f, 0xfc, 0x5a, 0x6f, 0x06, 0x7f, 0xd7, - 0x38, 0x3f, 0xf4, 0x45, 0x7d, 0x7e, 0x4d, 0xff, 0x00, 0x22, 0xfc, 0xc3, - 0xfc, 0x11, 0xff, 0x00, 0xd2, 0x90, 0xd6, 0xcc, 0xf1, 0x7a, 0xd8, 0xf0, - 0x67, 0xfc, 0x8e, 0x1a, 0x17, 0xfd, 0x7f, 0xc1, 0xff, 0x00, 0xa3, 0x16, - 0xb1, 0xeb, 0x63, 0xc1, 0xdf, 0xf2, 0x37, 0x68, 0x7f, 0xf5, 0xfd, 0x07, - 0xfe, 0x8c, 0x5a, 0xf9, 0xbc, 0x27, 0xfb, 0xcd, 0x3f, 0xf1, 0x2f, 0xcc, - 0x48, 0xfb, 0x2b, 0x75, 0x1b, 0xa9, 0x9b, 0xa8, 0xdd, 0x5f, 0xd8, 0x16, - 0x37, 0xb8, 0xfd, 0xd4, 0x6e, 0xa6, 0x6e, 0xa3, 0x75, 0x20, 0xb8, 0xed, - 0xd4, 0xbb, 0xa9, 0x9b, 0xa8, 0xdd, 0x4c, 0x2e, 0x3f, 0x75, 0x26, 0xea, - 0x6e, 0xea, 0x37, 0x52, 0x0b, 0x8f, 0xdd, 0x49, 0xba, 0x9b, 0xba, 0x8d, - 0xd4, 0xec, 0x3b, 0x8f, 0xdd, 0x49, 0xba, 0x9b, 0xba, 0x8d, 0xd4, 0x0a, - 0xe4, 0x74, 0x53, 0x33, 0x4b, 0x93, 0x57, 0x63, 0x3b, 0x8e, 0xa2, 0x9b, - 0x9a, 0x4c, 0xd1, 0x60, 0xb8, 0xfa, 0x29, 0x99, 0xa3, 0x34, 0x58, 0x2e, - 0x3f, 0x34, 0x53, 0x33, 0x4b, 0x93, 0x45, 0x82, 0xe3, 0xa8, 0xcd, 0x33, - 0x34, 0x66, 0x8b, 0x05, 0xc7, 0xd1, 0x4d, 0xc9, 0xa3, 0x26, 0x8b, 0x05, - 0xcf, 0x96, 0x3e, 0x30, 0x7f, 0xc9, 0x48, 0xd6, 0xbf, 0xdf, 0x8f, 0xff, - 0x00, 0x45, 0xa5, 0x71, 0xb5, 0xdd, 0xfc, 0x6c, 0xd3, 0xa6, 0xb1, 0xf8, - 0x85, 0x7f, 0x2c, 0x8a, 0x44, 0x77, 0x4b, 0x1c, 0xd1, 0xb7, 0x66, 0x1b, - 0x02, 0x9f, 0xd5, 0x4d, 0x70, 0x95, 0xfc, 0x97, 0x9d, 0x42, 0x50, 0xcc, - 0xf1, 0x31, 0x92, 0xb7, 0xbf, 0x2f, 0xfd, 0x29, 0x90, 0x7b, 0x3f, 0xec, - 0xdb, 0xff, 0x00, 0x1f, 0xba, 0xef, 0xfd, 0x73, 0x87, 0xf9, 0xbd, 0x78, - 0xc5, 0x7b, 0x87, 0xec, 0xe3, 0xa7, 0x4d, 0x1c, 0x3a, 0xd5, 0xf3, 0x29, - 0x58, 0x24, 0x31, 0xc2, 0x8d, 0xfd, 0xe2, 0x37, 0x16, 0xfc, 0xb2, 0x3f, - 0x3a, 0xf0, 0xfa, 0xf6, 0x73, 0x58, 0x4a, 0x19, 0x26, 0x5b, 0xcc, 0xad, - 0x7f, 0x6a, 0xff, 0x00, 0xf2, 0x64, 0x01, 0x45, 0x14, 0x57, 0xc6, 0x00, - 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, 0x51, 0x45, 0x14, 0x00, - 0x57, 0xb3, 0xfc, 0x57, 0xff, 0x00, 0x92, 0x51, 0xe0, 0xdf, 0xfa, 0xe7, - 0x07, 0xfe, 0x88, 0xaf, 0x18, 0xaf, 0x70, 0xf8, 0x9b, 0xa7, 0x4d, 0x79, - 0xf0, 0x77, 0xc3, 0x37, 0x11, 0x29, 0x74, 0xb5, 0x8a, 0xd9, 0xe4, 0xc7, - 0x65, 0x30, 0xed, 0xcf, 0xe6, 0x40, 0xfc, 0x6b, 0xec, 0xf2, 0x38, 0x4a, - 0x78, 0x0c, 0xc1, 0x45, 0x5f, 0xdc, 0x5f, 0x84, 0xae, 0xc0, 0xf0, 0xfa, - 0xd8, 0xf0, 0x6f, 0xfc, 0x8d, 0xfa, 0x1f, 0xfd, 0x7f, 0x41, 0xff, 0x00, - 0xa3, 0x16, 0xb1, 0xeb, 0xa2, 0xf8, 0x79, 0xa7, 0x4d, 0xaa, 0x78, 0xdf, - 0x45, 0x86, 0x15, 0x2c, 0xcb, 0x75, 0x1c, 0xad, 0x8e, 0xca, 0x8c, 0x18, - 0x9f, 0xc8, 0x57, 0xcd, 0x60, 0x61, 0x29, 0xe2, 0xe9, 0x46, 0x2a, 0xed, - 0xca, 0x3f, 0x9a, 0x03, 0xeb, 0x9c, 0xd1, 0x4c, 0xcd, 0x2e, 0x4d, 0x7f, - 0x60, 0x58, 0xbb, 0x8e, 0xa2, 0x99, 0x9a, 0x33, 0x45, 0x82, 0xe3, 0xe8, - 0xa6, 0x66, 0x8c, 0xd1, 0x60, 0xb8, 0xfa, 0x29, 0x99, 0xa5, 0xc9, 0xa2, - 0xc1, 0x71, 0xd9, 0xa2, 0x9b, 0x93, 0x49, 0x9a, 0x2c, 0x17, 0x1f, 0x45, - 0x33, 0x34, 0x66, 0x8b, 0x05, 0xc6, 0x6e, 0xa3, 0x75, 0x30, 0x90, 0x3a, - 0x9c, 0x51, 0x9a, 0xd2, 0xc6, 0x57, 0x1f, 0xba, 0x8d, 0xd4, 0xda, 0x4c, - 0xd1, 0x60, 0xb8, 0xfd, 0xd4, 0x6e, 0xa6, 0x02, 0x0f, 0x43, 0x9a, 0x37, - 0x01, 0xdf, 0x14, 0x58, 0x2e, 0x3f, 0x75, 0x1b, 0xa9, 0xb4, 0x94, 0x58, - 0x2e, 0x3f, 0x75, 0x1b, 0xa9, 0xb4, 0x80, 0x83, 0xd0, 0xe6, 0x8b, 0x05, - 0xc7, 0xee, 0xa3, 0x75, 0x30, 0x90, 0x3a, 0x9c, 0x51, 0x9a, 0x2c, 0x17, - 0x31, 0x3c, 0x5d, 0xe0, 0xcd, 0x2f, 0xc6, 0xb6, 0x2b, 0x6f, 0xa8, 0xc4, - 0x4b, 0x26, 0x4c, 0x53, 0xc6, 0x71, 0x24, 0x64, 0xf5, 0xc1, 0xfe, 0x87, - 0x8a, 0xe0, 0x6d, 0x3f, 0x67, 0x7d, 0x32, 0x2b, 0xb0, 0xf7, 0x1a, 0xad, - 0xcc, 0xf6, 0xe0, 0xe7, 0xca, 0x58, 0xd5, 0x09, 0x1e, 0x85, 0xb2, 0x7f, - 0x95, 0x7a, 0xd5, 0x25, 0x78, 0x38, 0xcc, 0x87, 0x2c, 0xcc, 0x2b, 0x2a, - 0xf8, 0x9a, 0x2a, 0x52, 0xef, 0xaa, 0xfb, 0xec, 0xd5, 0xfe, 0x77, 0x0b, - 0x95, 0xf4, 0xad, 0x32, 0xd3, 0x44, 0xb0, 0x86, 0xca, 0xc6, 0x05, 0xb7, - 0xb6, 0x88, 0x61, 0x23, 0x4e, 0x83, 0xfc, 0x4f, 0xbd, 0x7c, 0x6b, 0x5f, - 0x69, 0x57, 0xc5, 0xb5, 0xf9, 0x8f, 0x88, 0xb0, 0x8d, 0x38, 0xe0, 0xe1, - 0x05, 0x64, 0xb9, 0xec, 0x97, 0xfd, 0xb8, 0x34, 0x14, 0x51, 0x45, 0x7e, - 0x32, 0x30, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, 0x28, 0x00, 0xa2, 0x8a, - 0x28, 0x00, 0xaf, 0xad, 0xfc, 0x2b, 0x04, 0x57, 0x9e, 0x06, 0xd1, 0xad, - 0xe7, 0x8d, 0x66, 0x86, 0x4d, 0x3a, 0x04, 0x78, 0xdc, 0x65, 0x58, 0x18, - 0xd7, 0x20, 0x8a, 0xf9, 0x22, 0xbe, 0xba, 0xf0, 0x67, 0xfc, 0x89, 0xfa, - 0x17, 0xfd, 0x78, 0x41, 0xff, 0x00, 0xa2, 0xd6, 0xbf, 0x5c, 0xf0, 0xee, - 0x2a, 0x58, 0x9c, 0x42, 0x7b, 0x72, 0xaf, 0xcc, 0x47, 0x05, 0xab, 0x7e, - 0xcf, 0x7a, 0x4d, 0xdd, 0xdb, 0x4b, 0x65, 0xa8, 0x4f, 0x63, 0x13, 0x1c, - 0xf9, 0x25, 0x04, 0x80, 0x7b, 0x02, 0x48, 0x3f, 0x9e, 0x6b, 0xaf, 0xf0, - 0x57, 0xc3, 0xcd, 0x27, 0xc0, 0xd1, 0xb9, 0xb3, 0x47, 0x9a, 0xee, 0x41, - 0xb6, 0x4b, 0xa9, 0xb0, 0x5c, 0x8f, 0x41, 0xe8, 0x3d, 0x87, 0xe3, 0x5d, - 0x36, 0x69, 0x6b, 0xf5, 0x7c, 0x37, 0x0f, 0xe5, 0x78, 0x3a, 0xff, 0x00, - 0x59, 0xa1, 0x41, 0x46, 0x7d, 0xf5, 0xd3, 0xd1, 0x6c, 0xbe, 0x49, 0x0a, - 0xe3, 0xb7, 0x51, 0xba, 0x99, 0x9a, 0x03, 0x03, 0xde, 0xbd, 0xfb, 0x05, - 0xc7, 0xee, 0xa3, 0x75, 0x30, 0x90, 0x06, 0x4f, 0x14, 0x51, 0x60, 0xb8, - 0xfd, 0xd4, 0x6e, 0xa6, 0xd1, 0x45, 0x82, 0xe3, 0xb7, 0x51, 0xba, 0x98, - 0x08, 0x3d, 0x0e, 0x68, 0x24, 0x0e, 0xf4, 0x58, 0x2e, 0x3f, 0x75, 0x1b, - 0xa9, 0x94, 0xb4, 0x58, 0x2e, 0x3b, 0x75, 0x1b, 0xa9, 0x99, 0xa0, 0x10, - 0x7a, 0x1a, 0x2c, 0x17, 0x31, 0x62, 0xbd, 0x91, 0x46, 0xf7, 0x90, 0x3e, - 0xd3, 0xf7, 0x1b, 0xa9, 0xad, 0x58, 0xa5, 0x13, 0x46, 0xae, 0xbd, 0x08, - 0xac, 0x3f, 0x38, 0xff, 0x00, 0x71, 0x3f, 0xef, 0x91, 0x56, 0x52, 0xf1, - 0xe3, 0x48, 0x55, 0x42, 0x80, 0x7a, 0x80, 0x3d, 0xeb, 0xb2, 0x70, 0xbe, - 0xc7, 0x9d, 0x4e, 0xaf, 0x2e, 0xec, 0xd6, 0xcd, 0x36, 0x59, 0x44, 0x31, - 0xb3, 0xb7, 0x41, 0x49, 0xba, 0xb3, 0x1e, 0xf2, 0x49, 0x12, 0x65, 0x60, - 0x08, 0x1d, 0x01, 0x1e, 0xf5, 0x84, 0x61, 0xcc, 0x75, 0x4e, 0xa7, 0x22, - 0x1b, 0x2d, 0xec, 0x8c, 0x0b, 0xa4, 0x81, 0x37, 0x1f, 0xb8, 0xbd, 0x7e, - 0xb4, 0xb1, 0x5e, 0xc8, 0xa0, 0x3b, 0xb8, 0x7d, 0xa7, 0xee, 0x37, 0x5f, - 0xad, 0x57, 0xf3, 0x8f, 0xf7, 0x13, 0xfe, 0xf9, 0x14, 0x79, 0xc7, 0xfb, - 0x89, 0xff, 0x00, 0x7c, 0x8a, 0xeb, 0xe5, 0x56, 0xb5, 0x8e, 0x0e, 0x77, - 0x7b, 0xdc, 0xdd, 0x8e, 0x41, 0x2a, 0x2b, 0xaf, 0x42, 0x33, 0x4e, 0xac, - 0xa8, 0xef, 0x1d, 0x3c, 0x85, 0x00, 0x05, 0x3d, 0x40, 0x1e, 0xe6, 0xb4, - 0x77, 0x57, 0x24, 0xa3, 0xca, 0x77, 0xc2, 0x7c, 0xe8, 0x74, 0x92, 0x08, - 0xd1, 0x99, 0x8f, 0x00, 0x66, 0xb2, 0xa5, 0xbd, 0x91, 0x81, 0x74, 0x70, - 0x99, 0x38, 0xd8, 0x3a, 0xfd, 0x69, 0xd2, 0x5e, 0x3b, 0xf9, 0xea, 0x40, - 0x2a, 0x3a, 0x02, 0x3d, 0xc5, 0x55, 0xf3, 0x8f, 0xf7, 0x13, 0xfe, 0xf9, - 0x15, 0xbc, 0x21, 0x6d, 0xce, 0x6a, 0x95, 0x6f, 0xa2, 0x64, 0xf1, 0x5e, - 0x48, 0xa3, 0x7b, 0x48, 0x1f, 0x69, 0xfb, 0x8d, 0xd4, 0xd6, 0xac, 0x52, - 0x89, 0xa3, 0x57, 0x5e, 0x86, 0xb0, 0xfc, 0xe3, 0xfd, 0xc4, 0xff, 0x00, - 0xbe, 0x45, 0x59, 0x4b, 0xc7, 0x8d, 0x61, 0x0a, 0x14, 0x03, 0xd4, 0x01, - 0xef, 0x44, 0xe1, 0x7d, 0x85, 0x4e, 0xaf, 0x2e, 0xec, 0xd6, 0xcd, 0x15, - 0x1e, 0xea, 0x5d, 0xd5, 0xcd, 0x63, 0xba, 0xe3, 0xeb, 0xc0, 0xbc, 0x71, - 0xf0, 0x57, 0x55, 0x83, 0x57, 0x9e, 0xe7, 0x44, 0x85, 0x6f, 0x6c, 0xa6, - 0x72, 0xe2, 0x25, 0x70, 0xaf, 0x16, 0x4e, 0x76, 0xe0, 0x91, 0x91, 0xe9, - 0x8a, 0xf7, 0xad, 0xd4, 0x66, 0xbe, 0x7f, 0x39, 0xc8, 0xf0, 0x99, 0xe5, - 0x18, 0xd2, 0xc4, 0xdd, 0x72, 0xbb, 0xa6, 0xb7, 0x5d, 0xfb, 0xef, 0xe8, - 0x17, 0xb1, 0xf2, 0xc5, 0xff, 0x00, 0xc3, 0x5f, 0x12, 0xe9, 0x96, 0x53, - 0x5d, 0xdd, 0x69, 0x52, 0x43, 0x6f, 0x0a, 0x97, 0x92, 0x42, 0xe8, 0x42, - 0x81, 0xd4, 0xf0, 0x6b, 0x99, 0xaf, 0xaa, 0xfe, 0x21, 0x9c, 0xf8, 0x1f, - 0x5c, 0xff, 0x00, 0xaf, 0x47, 0xfe, 0x55, 0xf2, 0xa5, 0x7e, 0x01, 0xc5, - 0x59, 0x1e, 0x1f, 0x22, 0xc4, 0x53, 0xa3, 0x87, 0x93, 0x92, 0x94, 0x6f, - 0xef, 0x5b, 0xbd, 0xba, 0x24, 0x5a, 0x77, 0x0a, 0xe9, 0xec, 0xbe, 0x19, - 0xf8, 0x9b, 0x51, 0xb3, 0x86, 0xea, 0xdb, 0x49, 0x92, 0x5b, 0x79, 0x90, - 0x49, 0x1b, 0x87, 0x40, 0x19, 0x48, 0xc8, 0x3d, 0x6b, 0x98, 0xaf, 0xac, - 0x3c, 0x08, 0x71, 0xe0, 0xad, 0x0b, 0xfe, 0xbc, 0xa1, 0xff, 0x00, 0xd0, - 0x05, 0x57, 0x0a, 0xe4, 0x58, 0x7c, 0xf6, 0xbd, 0x5a, 0x58, 0x89, 0x4a, - 0x2a, 0x2a, 0xfe, 0xed, 0xbb, 0xdb, 0xaa, 0x60, 0xdd, 0x8f, 0x9e, 0xdb, - 0xe1, 0x4f, 0x8a, 0xd1, 0x4b, 0x1d, 0x1a, 0x50, 0x00, 0xc9, 0x3e, 0x62, - 0x7f, 0xf1, 0x55, 0xc9, 0xd7, 0xd9, 0x17, 0x4d, 0xfe, 0x8d, 0x37, 0xfb, - 0x87, 0xf9, 0x57, 0xc6, 0xf5, 0xd1, 0xc5, 0x9c, 0x3d, 0x86, 0xc8, 0x5d, - 0x05, 0x87, 0x9c, 0xa5, 0xcf, 0xcd, 0x7e, 0x6b, 0x74, 0xb6, 0xd6, 0x4b, - 0xb8, 0x27, 0x70, 0xae, 0x87, 0x47, 0xf0, 0x07, 0x88, 0x35, 0xfb, 0x04, - 0xbd, 0xb0, 0xd3, 0x64, 0xb9, 0xb5, 0x72, 0x42, 0xc8, 0xae, 0xa0, 0x12, - 0x0e, 0x0f, 0x53, 0xeb, 0x5c, 0xf5, 0x7d, 0x27, 0xf0, 0x54, 0xe3, 0xe1, - 0xed, 0x8f, 0xfd, 0x74, 0x97, 0xff, 0x00, 0x43, 0x35, 0xe7, 0x70, 0xbe, - 0x4f, 0x43, 0x3c, 0xc6, 0xcb, 0x0d, 0x5e, 0x4d, 0x25, 0x16, 0xf4, 0xb5, - 0xee, 0x9a, 0x5d, 0x53, 0xee, 0x0d, 0xd8, 0xf3, 0x1f, 0x0d, 0x7c, 0x12, - 0xd7, 0xb5, 0x3d, 0x42, 0x31, 0xa9, 0x40, 0x34, 0xdb, 0x20, 0xc0, 0xc8, - 0xee, 0xea, 0xce, 0x47, 0x70, 0xa0, 0x13, 0xcf, 0xb9, 0xe2, 0xbe, 0x87, - 0xb7, 0x82, 0x3b, 0x4b, 0x78, 0xa0, 0x89, 0x42, 0x45, 0x12, 0x84, 0x45, - 0x1d, 0x80, 0x18, 0x02, 0x97, 0x34, 0x9b, 0xab, 0xfa, 0x07, 0x25, 0xe1, - 0xfc, 0x1e, 0x45, 0x09, 0x47, 0x0d, 0x76, 0xe5, 0xbb, 0x7a, 0xbd, 0x36, - 0x5a, 0x24, 0xad, 0xf2, 0x22, 0xf7, 0x24, 0xcd, 0x19, 0xa8, 0xf7, 0x51, - 0xba, 0xbe, 0x96, 0xc1, 0x71, 0xd2, 0x48, 0x22, 0x46, 0x76, 0xe0, 0x0e, - 0x6b, 0x2a, 0x5b, 0xd9, 0x18, 0x17, 0x47, 0x11, 0xe4, 0xe3, 0x60, 0xeb, - 0xf5, 0xa7, 0x49, 0x78, 0xef, 0xe7, 0xa9, 0x00, 0xa8, 0xe8, 0x08, 0xf7, - 0x02, 0xaa, 0xf9, 0xc7, 0xfb, 0x89, 0xff, 0x00, 0x7c, 0x8a, 0xe9, 0x84, - 0x2d, 0xb9, 0xc5, 0x56, 0xaf, 0x36, 0x89, 0x93, 0xc5, 0x7b, 0x22, 0x8d, - 0xef, 0x20, 0x7d, 0xa7, 0xee, 0x37, 0x53, 0x5a, 0xd1, 0x4a, 0x26, 0x8d, - 0x5d, 0x7a, 0x1a, 0xc2, 0xf3, 0x8f, 0xf7, 0x13, 0xfe, 0xf9, 0x15, 0x65, - 0x2f, 0x1e, 0x34, 0x84, 0x28, 0x50, 0x09, 0xe4, 0x01, 0xef, 0x44, 0xe1, - 0x7d, 0x85, 0x4e, 0xaf, 0x2e, 0xec, 0xd6, 0xa6, 0xcb, 0x28, 0x86, 0x36, - 0x76, 0xe8, 0x29, 0x37, 0x56, 0x63, 0xde, 0x3c, 0x89, 0x30, 0x60, 0xa4, - 0x0e, 0x80, 0x8f, 0x7a, 0xc2, 0x30, 0xe6, 0x3a, 0xa7, 0x53, 0x91, 0x0d, - 0x96, 0xf6, 0x46, 0x05, 0xd2, 0x40, 0x9b, 0x8f, 0xdc, 0x5e, 0xa3, 0xde, - 0x96, 0x2b, 0xd9, 0x14, 0x07, 0x77, 0x0f, 0xb4, 0xfd, 0xc6, 0xeb, 0xf5, - 0xaa, 0xfe, 0x71, 0xfe, 0xe2, 0x7f, 0xdf, 0x22, 0x8f, 0x38, 0xff, 0x00, - 0x71, 0x3f, 0xef, 0x91, 0x5d, 0x7c, 0xaa, 0xd6, 0xb1, 0xc1, 0xce, 0xef, - 0x7b, 0x9b, 0xb1, 0xc8, 0x25, 0x45, 0x75, 0xe8, 0x46, 0x69, 0xd5, 0x95, - 0x1d, 0xe3, 0xa0, 0x81, 0x46, 0x02, 0x9e, 0xa0, 0x0f, 0x72, 0x2b, 0x4b, - 0x35, 0xc9, 0x28, 0xf2, 0x9d, 0xf0, 0xa9, 0xce, 0x85, 0x92, 0x41, 0x1a, - 0x33, 0xb7, 0x40, 0x32, 0x6b, 0x2a, 0x5b, 0xd9, 0x1c, 0x17, 0x47, 0x09, - 0x93, 0x8d, 0x83, 0xaf, 0xd6, 0x9d, 0x25, 0xe3, 0xbf, 0x9e, 0xa4, 0x02, - 0xa3, 0xa0, 0x23, 0xdc, 0x55, 0x5f, 0x38, 0xff, 0x00, 0x71, 0x3f, 0xef, - 0x91, 0x5b, 0xc2, 0x16, 0xdc, 0xe6, 0xa9, 0x57, 0x9b, 0x44, 0xc8, 0xea, - 0x70, 0xa4, 0x88, 0x30, 0x09, 0xff, 0x00, 0xf5, 0xd3, 0x4d, 0xbb, 0x09, - 0x84, 0x59, 0x1b, 0xbd, 0x7b, 0x56, 0x95, 0xb4, 0x66, 0x18, 0x42, 0x13, - 0x92, 0x3d, 0x2a, 0xe5, 0x2b, 0x23, 0x2a, 0x70, 0x72, 0x6d, 0x32, 0x7a, - 0xc8, 0x2a, 0x40, 0x9f, 0x20, 0x8f, 0xff, 0x00, 0x5d, 0x6a, 0xe6, 0xa3, - 0xb9, 0x8c, 0xcf, 0x09, 0x40, 0x40, 0x27, 0xd6, 0xb1, 0x83, 0xe5, 0x3a, - 0x6a, 0x47, 0x99, 0x5c, 0xc7, 0xa2, 0xa5, 0x5b, 0x76, 0x69, 0x8c, 0x59, - 0x1b, 0xbf, 0x4a, 0x3e, 0xce, 0xde, 0x7f, 0x95, 0x91, 0xbb, 0xd7, 0xb5, - 0x74, 0xdd, 0x1c, 0x3c, 0xac, 0x7a, 0xa9, 0x2d, 0x6f, 0x80, 0x7f, 0xcb, - 0x1a, 0xd6, 0xa8, 0x6d, 0xe3, 0x30, 0xc2, 0xa8, 0x48, 0x24, 0x77, 0x15, - 0x26, 0x6b, 0x9a, 0x6f, 0x99, 0x9d, 0xf4, 0xe3, 0xca, 0x8c, 0xb6, 0x52, - 0x1a, 0xe3, 0x20, 0xff, 0x00, 0x96, 0x15, 0x5e, 0xb6, 0x2e, 0x23, 0x33, - 0x44, 0xc8, 0x08, 0x04, 0xd6, 0x67, 0xd9, 0xdb, 0xcf, 0xf2, 0xb2, 0x37, - 0x7a, 0xf6, 0xad, 0xa1, 0x24, 0xd1, 0xcb, 0x52, 0x0e, 0x2d, 0x58, 0x8a, - 0xa7, 0x0a, 0x48, 0x83, 0x00, 0x9f, 0xff, 0x00, 0x5d, 0x34, 0xdb, 0xb2, - 0xcc, 0x22, 0xc8, 0xdd, 0xeb, 0xda, 0xb4, 0xad, 0xa3, 0x30, 0xc2, 0xaa, - 0x4e, 0x48, 0xf4, 0xa2, 0x52, 0xb2, 0x0a, 0x70, 0x72, 0x6d, 0x32, 0x7a, - 0x29, 0xa4, 0xd1, 0x9a, 0xe5, 0xb1, 0xde, 0x3a, 0x8a, 0x6e, 0x73, 0x41, - 0x34, 0x58, 0x0e, 0x7f, 0xe2, 0x1f, 0xfc, 0x88, 0xfa, 0xe7, 0xfd, 0x7a, - 0xbf, 0xf2, 0xaf, 0x95, 0xab, 0xeb, 0x2f, 0x18, 0x58, 0x4b, 0xaa, 0xf8, - 0x57, 0x56, 0xb4, 0x80, 0x6e, 0x9a, 0x6b, 0x69, 0x15, 0x17, 0xd5, 0xb6, - 0x9c, 0x0f, 0xce, 0xbe, 0x4e, 0x65, 0x2a, 0x48, 0x20, 0x82, 0x38, 0x20, - 0xf6, 0xaf, 0xc1, 0x7c, 0x46, 0x84, 0x96, 0x32, 0x84, 0xed, 0xa3, 0x8b, - 0x5f, 0x73, 0xff, 0x00, 0x82, 0x8d, 0x20, 0x25, 0x7d, 0x5d, 0xe0, 0x5f, - 0xf9, 0x12, 0xf4, 0x2f, 0xfa, 0xf2, 0x87, 0xff, 0x00, 0x40, 0x15, 0xf2, - 0x9a, 0x23, 0x48, 0xea, 0x88, 0xa5, 0x9d, 0x8e, 0x02, 0x81, 0x92, 0x4d, - 0x7d, 0x69, 0xe1, 0x8b, 0x19, 0x34, 0xbf, 0x0d, 0xe9, 0x76, 0x73, 0x71, - 0x2c, 0x16, 0xd1, 0xc6, 0xe3, 0xd0, 0x85, 0x00, 0xd5, 0x78, 0x73, 0x09, - 0x3c, 0x56, 0x22, 0x76, 0xd1, 0x45, 0x2f, 0xc7, 0xfe, 0x00, 0x4c, 0xd0, - 0xba, 0xff, 0x00, 0x8f, 0x69, 0xbf, 0xdc, 0x3f, 0xca, 0xbe, 0x39, 0xaf, - 0xb1, 0xe5, 0x5f, 0x32, 0x27, 0x4c, 0xe3, 0x72, 0x91, 0x9a, 0xf9, 0x03, - 0x50, 0xb1, 0x9b, 0x4c, 0xbe, 0xb8, 0xb4, 0x9d, 0x0a, 0x4d, 0x04, 0x86, - 0x37, 0x53, 0xd8, 0x83, 0x8a, 0xed, 0xf1, 0x22, 0x12, 0xff, 0x00, 0x65, - 0x9d, 0xb4, 0xf7, 0xd7, 0xfe, 0x92, 0x10, 0x2b, 0xd7, 0xd2, 0x5f, 0x05, - 0xbf, 0xe4, 0x9f, 0x58, 0xff, 0x00, 0xd7, 0x49, 0x7f, 0xf4, 0x33, 0x5f, - 0x36, 0xd7, 0xd3, 0x9f, 0x0a, 0x74, 0xe9, 0xb4, 0xbf, 0x01, 0xe9, 0x91, - 0x4e, 0xa5, 0x24, 0x75, 0x69, 0x76, 0x9e, 0xa0, 0x33, 0x12, 0x3f, 0x42, - 0x2b, 0xc3, 0xf0, 0xf6, 0x12, 0x96, 0x69, 0x52, 0x49, 0x68, 0xa0, 0xff, - 0x00, 0x19, 0x44, 0x72, 0xd8, 0xeb, 0xe8, 0xa6, 0xe6, 0x8c, 0xd7, 0xf4, - 0x3d, 0x8c, 0x87, 0x51, 0x4d, 0xcd, 0x19, 0xa2, 0xc0, 0x65, 0xb2, 0x90, - 0xd7, 0x19, 0x04, 0x7f, 0xfb, 0x42, 0xab, 0xd6, 0xc5, 0xc4, 0x66, 0x68, - 0x59, 0x07, 0x04, 0xfa, 0xd6, 0x67, 0xd9, 0xdb, 0xcf, 0xf2, 0xb2, 0x37, - 0x7a, 0xf6, 0xae, 0xa8, 0x4a, 0xe8, 0xe0, 0xa9, 0x07, 0x16, 0xac, 0x45, - 0x53, 0x85, 0x24, 0x41, 0x80, 0x4f, 0xff, 0x00, 0xae, 0x9a, 0x6d, 0xd9, - 0x66, 0x11, 0x64, 0x6e, 0xf5, 0xed, 0x5a, 0x76, 0xd1, 0x98, 0x21, 0x0a, - 0x48, 0x27, 0xda, 0x89, 0x4a, 0xc8, 0x29, 0xc1, 0xc9, 0xb4, 0xc9, 0xab, - 0x20, 0xa9, 0x02, 0x7c, 0x82, 0x3f, 0xfd, 0x75, 0xab, 0x9a, 0x8a, 0xe6, - 0x33, 0x34, 0x45, 0x41, 0xc1, 0xf7, 0xac, 0x60, 0xf9, 0x4e, 0x9a, 0x91, - 0xe6, 0x46, 0x45, 0x15, 0x28, 0xb7, 0x66, 0x98, 0xc5, 0x91, 0xb8, 0x77, - 0xed, 0x47, 0xd9, 0xdb, 0xcf, 0xf2, 0xb2, 0x37, 0x7a, 0xf6, 0xae, 0x9b, - 0xa3, 0x87, 0x95, 0x8f, 0x55, 0x25, 0xad, 0xf0, 0x0f, 0xf9, 0x63, 0x5a, - 0xd5, 0x0d, 0xba, 0x18, 0x61, 0x54, 0x24, 0x12, 0x3d, 0x2a, 0x4c, 0xd7, - 0x34, 0xdf, 0x33, 0x3b, 0xe9, 0xc7, 0x95, 0x19, 0x6c, 0xa4, 0x35, 0xc6, - 0x41, 0xff, 0x00, 0x2c, 0x2a, 0xbd, 0x6c, 0x5c, 0x46, 0x66, 0x85, 0x90, - 0x1c, 0x13, 0xeb, 0x59, 0x9f, 0x67, 0x6f, 0x3f, 0xca, 0xc8, 0xdd, 0xfa, - 0x56, 0xd0, 0x92, 0x68, 0xe5, 0xa9, 0x06, 0x9a, 0xb1, 0xa0, 0xd6, 0xc1, - 0xae, 0x04, 0xb9, 0x39, 0x1d, 0xaa, 0x6a, 0x28, 0xae, 0x76, 0xdb, 0x3a, - 0xd2, 0xb6, 0xc1, 0x45, 0x14, 0x52, 0x19, 0x0a, 0xdb, 0x05, 0xb8, 0x32, - 0xe4, 0xe4, 0xf6, 0xa3, 0xec, 0xc3, 0xed, 0x1e, 0x6e, 0x4e, 0x7d, 0x2a, - 0x6a, 0x2a, 0xb9, 0x99, 0x3c, 0xa8, 0x28, 0xa2, 0x8a, 0x92, 0x82, 0xa1, - 0xfb, 0x30, 0xfb, 0x47, 0x9b, 0x93, 0x9f, 0x4a, 0x9a, 0x8a, 0x69, 0xd8, - 0x4d, 0x5f, 0x72, 0x16, 0xb6, 0x0d, 0x70, 0x25, 0xc9, 0xc8, 0xed, 0x53, - 0x51, 0x45, 0x0d, 0xb6, 0x09, 0x5b, 0x60, 0xa2, 0x8a, 0x29, 0x0c, 0x28, - 0xa2, 0x8a, 0x00, 0x2b, 0x83, 0xf1, 0x57, 0xc1, 0xdd, 0x1b, 0xc4, 0xb7, - 0xaf, 0x79, 0x1b, 0xcb, 0xa7, 0x5d, 0x48, 0x73, 0x21, 0x80, 0x02, 0x8e, - 0x7d, 0x4a, 0x9e, 0xff, 0x00, 0x42, 0x2b, 0xbc, 0xa2, 0xbc, 0xfc, 0x76, - 0x5f, 0x85, 0xcc, 0xa9, 0xfb, 0x1c, 0x5d, 0x35, 0x38, 0xf9, 0xfe, 0x8f, - 0x75, 0xf2, 0x1a, 0x6d, 0x1c, 0x3f, 0x84, 0xbe, 0x11, 0xe8, 0xde, 0x16, - 0xbb, 0x4b, 0xc2, 0xd2, 0x5f, 0xde, 0x27, 0x29, 0x24, 0xf8, 0xda, 0x87, - 0xd5, 0x54, 0x77, 0xf7, 0x39, 0xae, 0xe2, 0x8a, 0x29, 0xe0, 0xb0, 0x18, - 0x5c, 0xba, 0x97, 0xb1, 0xc2, 0x53, 0x50, 0x8f, 0x97, 0xeb, 0xd5, 0xfc, - 0xc2, 0xed, 0x85, 0x71, 0xfe, 0x31, 0xf8, 0x5f, 0xa4, 0x78, 0xc6, 0x6f, - 0xb4, 0xcd, 0xe6, 0x5a, 0x5e, 0xe3, 0x06, 0xe2, 0x0c, 0x65, 0xc7, 0x6d, - 0xc0, 0xf5, 0xfe, 0x7e, 0xf5, 0xd8, 0x51, 0x55, 0x8c, 0xc1, 0x61, 0xf1, - 0xf4, 0x9d, 0x0c, 0x54, 0x14, 0xe2, 0xfa, 0x3f, 0xeb, 0x46, 0x17, 0x68, - 0xf3, 0xbf, 0x0f, 0x7c, 0x11, 0xd1, 0x74, 0x6b, 0xc4, 0xb9, 0xb9, 0x96, - 0x5d, 0x49, 0xd0, 0xe5, 0x63, 0x94, 0x05, 0x8f, 0x3e, 0xa5, 0x47, 0x5f, - 0xc4, 0xe3, 0xda, 0xbd, 0x13, 0xa5, 0x14, 0x56, 0x38, 0x1c, 0xb7, 0x07, - 0x96, 0x41, 0xd3, 0xc1, 0xd3, 0x50, 0x4f, 0x7b, 0x75, 0xf5, 0x7b, 0xb0, - 0x6d, 0xb0, 0xa2, 0x8a, 0x2b, 0xd3, 0x10, 0x51, 0x45, 0x14, 0x00, 0x54, - 0x3f, 0x66, 0x1f, 0x68, 0xf3, 0x72, 0x73, 0xe9, 0x53, 0x51, 0x4d, 0x3b, - 0x09, 0xab, 0xee, 0x42, 0xd6, 0xc1, 0xae, 0x04, 0xb9, 0x39, 0x1d, 0xaa, - 0x6a, 0x28, 0xa1, 0xb6, 0xc1, 0x2b, 0x6c, 0x14, 0x51, 0x45, 0x21, 0x90, - 0xad, 0xb0, 0x5b, 0x83, 0x2e, 0x4e, 0x4f, 0x6a, 0x3e, 0xcc, 0x3e, 0xd1, - 0xe6, 0xe4, 0xe7, 0xd2, 0xa6, 0xa2, 0xab, 0x99, 0x93, 0xca, 0x82, 0x8a, - 0x28, 0xa9, 0x28, 0x2a, 0x1f, 0xb3, 0x0f, 0xb4, 0x79, 0xb9, 0x39, 0xf4, - 0xa9, 0xa8, 0xa6, 0x9d, 0x84, 0xd5, 0xf7, 0x3e, 0x20, 0xff, 0x00, 0x87, - 0xa6, 0xf8, 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xa3, - 0xfe, 0x1e, 0x9b, 0xe1, 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, - 0xbe, 0x17, 0xfe, 0xc6, 0xb2, 0xff, 0x00, 0x9f, 0x64, 0xa7, 0x47, 0xa2, - 0x59, 0x31, 0xe6, 0xd9, 0x31, 0x45, 0x8e, 0x9e, 0x44, 0x7d, 0xcd, 0xff, - 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, 0xc7, 0xfe, 0x05, 0x45, - 0x47, 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, 0x1f, 0xf8, 0x15, - 0x15, 0x7c, 0x3b, 0xfd, 0x87, 0x61, 0xff, 0x00, 0x3e, 0xb1, 0xfe, 0x54, - 0xd9, 0x34, 0x6b, 0x05, 0x1c, 0x5b, 0x47, 0x9f, 0xa5, 0x16, 0x0e, 0x44, - 0x7d, 0xc9, 0xff, 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, 0xc7, - 0xfe, 0x05, 0x45, 0x47, 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, - 0x1f, 0xf8, 0x15, 0x15, 0x7c, 0x2f, 0xfd, 0x8d, 0x65, 0xff, 0x00, 0x3e, - 0xc9, 0xf9, 0x53, 0xe3, 0xd1, 0x2c, 0x4f, 0x26, 0xd9, 0x31, 0xf4, 0xa2, - 0xc1, 0xc8, 0x8f, 0xb9, 0x7f, 0xe1, 0xe9, 0xbe, 0x16, 0xff, 0x00, 0xa1, - 0x1f, 0x58, 0xff, 0x00, 0xc0, 0xa8, 0xa8, 0xff, 0x00, 0x87, 0xa6, 0xf8, - 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xaf, 0x87, 0x7f, - 0xb0, 0xec, 0x3f, 0xe7, 0xd6, 0x3f, 0xca, 0x99, 0x26, 0x8d, 0x62, 0x38, - 0x16, 0xc9, 0x9a, 0x2c, 0x1c, 0x88, 0xfb, 0x97, 0xfe, 0x1e, 0x9b, 0xe1, - 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, 0x8f, 0xf8, 0x7a, 0x6f, - 0x85, 0xbf, 0xe8, 0x47, 0xd6, 0x3f, 0xf0, 0x2a, 0x2a, 0xf8, 0x5f, 0xfb, - 0x1a, 0xcb, 0xfe, 0x7d, 0x93, 0xf2, 0xa9, 0x23, 0xd1, 0x2c, 0x48, 0xc9, - 0xb6, 0x4f, 0xca, 0x8b, 0x07, 0x22, 0x3e, 0xe4, 0xff, 0x00, 0x87, 0xa6, - 0xf8, 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xa3, 0xfe, - 0x1e, 0x9b, 0xe1, 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, 0xbe, - 0x1d, 0xfe, 0xc3, 0xb0, 0xff, 0x00, 0x9f, 0x58, 0xff, 0x00, 0x2a, 0x64, - 0x9a, 0x35, 0x88, 0x38, 0x16, 0xc9, 0xf9, 0x51, 0x60, 0xe4, 0x47, 0xdc, - 0xbf, 0xf0, 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, 0x7f, 0xe0, 0x54, - 0x54, 0x7f, 0xc3, 0xd3, 0x7c, 0x2d, 0xff, 0x00, 0x42, 0x3e, 0xb1, 0xff, - 0x00, 0x81, 0x51, 0x57, 0xc2, 0xff, 0x00, 0xd8, 0xd6, 0x5f, 0xf3, 0xec, - 0x9f, 0x95, 0x48, 0x9a, 0x1d, 0x89, 0x19, 0x36, 0xc9, 0xf9, 0x51, 0x60, - 0xe4, 0x47, 0xdc, 0x9f, 0xf0, 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, - 0x7f, 0xe0, 0x54, 0x54, 0x7f, 0xc3, 0xd3, 0x7c, 0x2d, 0xff, 0x00, 0x42, - 0x3e, 0xb1, 0xff, 0x00, 0x81, 0x51, 0x57, 0xc3, 0xbf, 0xd8, 0x76, 0x1f, - 0xf3, 0xeb, 0x1f, 0xe5, 0x51, 0xbe, 0x8d, 0x63, 0x9c, 0x0b, 0x64, 0xfc, - 0xa8, 0xb0, 0x72, 0x23, 0xee, 0x6f, 0xf8, 0x7a, 0x6f, 0x85, 0xbf, 0xe8, - 0x47, 0xd6, 0x3f, 0xf0, 0x2a, 0x2a, 0x3f, 0xe1, 0xe9, 0xbe, 0x16, 0xff, - 0x00, 0xa1, 0x1f, 0x58, 0xff, 0x00, 0xc0, 0xa8, 0xab, 0xe1, 0x7f, 0xec, - 0x7b, 0x2f, 0xf9, 0xf6, 0x4f, 0xca, 0x9b, 0x2e, 0x9d, 0xa7, 0xdb, 0xed, - 0x53, 0x67, 0xe6, 0xc8, 0xc0, 0x90, 0xb1, 0xae, 0x4e, 0x3d, 0x68, 0xb0, - 0x72, 0x23, 0xee, 0xaf, 0xf8, 0x7a, 0x6f, 0x85, 0xbf, 0xe8, 0x47, 0xd6, - 0x3f, 0xf0, 0x2a, 0x2a, 0x3f, 0xe1, 0xe9, 0xbe, 0x16, 0xff, 0x00, 0xa1, - 0x1f, 0x58, 0xff, 0x00, 0xc0, 0xa8, 0xab, 0xe1, 0x1f, 0x23, 0x4b, 0xd8, - 0x1b, 0xec, 0x44, 0x8d, 0xbb, 0x9b, 0x09, 0xf7, 0x06, 0x48, 0xc9, 0xe7, - 0xd8, 0xfe, 0x54, 0x3d, 0x9e, 0x9e, 0x2e, 0x04, 0x66, 0xcb, 0x60, 0x27, - 0x68, 0x72, 0xbf, 0x29, 0x3f, 0x5c, 0xd1, 0x60, 0xe4, 0x47, 0xdd, 0xdf, - 0xf0, 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, 0x7f, 0xe0, 0x54, 0x54, - 0x7f, 0xc3, 0xd3, 0x7c, 0x2d, 0xff, 0x00, 0x42, 0x3e, 0xb1, 0xff, 0x00, - 0x81, 0x51, 0x57, 0xc1, 0x82, 0xdf, 0x4e, 0x1b, 0xb7, 0xd9, 0x98, 0x88, - 0x19, 0xda, 0xeb, 0xc9, 0xe7, 0x1c, 0x73, 0xea, 0x45, 0x4d, 0x1d, 0x9e, - 0x9a, 0x46, 0x0d, 0x91, 0x12, 0x02, 0x41, 0x8c, 0xaf, 0xcd, 0x9c, 0x67, - 0xd7, 0xd2, 0x8b, 0x07, 0x22, 0x3e, 0xed, 0xff, 0x00, 0x87, 0xa6, 0xf8, - 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xa3, 0xfe, 0x1e, - 0x9b, 0xe1, 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, 0xbe, 0x16, - 0xb7, 0xd3, 0xf4, 0xeb, 0x86, 0x65, 0xfb, 0x1f, 0x96, 0xea, 0x01, 0x2b, - 0x22, 0xe0, 0xe0, 0xf7, 0xa7, 0x36, 0x8f, 0x62, 0x49, 0xc5, 0xb2, 0x62, - 0x8b, 0x07, 0x22, 0x3e, 0xe7, 0xff, 0x00, 0x87, 0xa6, 0xf8, 0x5b, 0xfe, - 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xa3, 0xfe, 0x1e, 0x9b, 0xe1, - 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, 0x8a, 0xbe, 0x17, 0xfe, 0xc6, - 0xb2, 0x3f, 0xf2, 0xec, 0x95, 0x30, 0xd0, 0xec, 0x40, 0xff, 0x00, 0x8f, - 0x64, 0xa2, 0xc1, 0xc8, 0x8f, 0xb8, 0xbf, 0xe1, 0xe9, 0xbe, 0x16, 0xff, - 0x00, 0xa1, 0x1f, 0x58, 0xff, 0x00, 0xc0, 0xa8, 0xa8, 0xff, 0x00, 0x87, - 0xa6, 0xf8, 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, 0xaf, - 0x87, 0x7f, 0xb1, 0x2c, 0x07, 0xfc, 0xba, 0xc7, 0xf9, 0x54, 0x27, 0x47, - 0xb2, 0x27, 0xfe, 0x3d, 0x92, 0x8b, 0x07, 0x22, 0x3e, 0xe8, 0xff, 0x00, - 0x87, 0xa6, 0xf8, 0x5b, 0xfe, 0x84, 0x7d, 0x63, 0xff, 0x00, 0x02, 0xa2, - 0xa3, 0xfe, 0x1e, 0x9b, 0xe1, 0x6f, 0xfa, 0x11, 0xf5, 0x8f, 0xfc, 0x0a, - 0x8a, 0xbe, 0x17, 0x1a, 0x2d, 0x91, 0x38, 0xfb, 0x32, 0x54, 0xdf, 0xd8, - 0x76, 0x1f, 0xf3, 0xea, 0x9f, 0x95, 0x16, 0x0e, 0x44, 0x7d, 0xc5, 0xff, - 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, 0xc7, 0xfe, 0x05, 0x45, - 0x47, 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, 0x1f, 0xf8, 0x15, - 0x15, 0x7c, 0x3a, 0x74, 0x4b, 0x00, 0x33, 0xf6, 0x58, 0xff, 0x00, 0x2a, - 0x84, 0xe8, 0xd6, 0x5f, 0xf3, 0xec, 0x94, 0x58, 0x39, 0x11, 0xf7, 0x47, - 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, 0x1f, 0xf8, 0x15, 0x15, - 0x1f, 0xf0, 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, 0x7f, 0xe0, 0x54, - 0x55, 0xf0, 0xc2, 0xe8, 0xb6, 0x4c, 0x40, 0xfb, 0x32, 0x54, 0xbf, 0xd8, - 0x76, 0x1f, 0xf3, 0xea, 0x94, 0x58, 0x39, 0x11, 0xf7, 0x17, 0xfc, 0x3d, - 0x37, 0xc2, 0xdf, 0xf4, 0x23, 0xeb, 0x1f, 0xf8, 0x15, 0x15, 0x1f, 0xf0, - 0xf4, 0xdf, 0x0b, 0x7f, 0xd0, 0x8f, 0xac, 0x7f, 0xe0, 0x54, 0x55, 0xf0, - 0xe3, 0x68, 0xb6, 0x0a, 0x09, 0xfb, 0x2c, 0x7f, 0x95, 0x45, 0xfd, 0x8d, - 0x65, 0xff, 0x00, 0x3e, 0xc9, 0x45, 0x83, 0x91, 0x1f, 0x74, 0x7f, 0xc3, - 0xd3, 0x7c, 0x2d, 0xff, 0x00, 0x42, 0x3e, 0xb1, 0xff, 0x00, 0x81, 0x51, - 0x51, 0xff, 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, 0xc7, 0xfe, - 0x05, 0x45, 0x5f, 0x0c, 0x26, 0x89, 0x64, 0xcd, 0xff, 0x00, 0x1e, 0xc9, - 0x52, 0xff, 0x00, 0x61, 0xd8, 0x7f, 0xcf, 0xac, 0x7f, 0x95, 0x16, 0x0e, - 0x44, 0x7d, 0xc5, 0xff, 0x00, 0x0f, 0x4d, 0xf0, 0xb7, 0xfd, 0x08, 0xfa, - 0xc7, 0xfe, 0x05, 0x45, 0x47, 0xfc, 0x3d, 0x37, 0xc2, 0xdf, 0xf4, 0x23, - 0xeb, 0x1f, 0xf8, 0x15, 0x15, 0x7c, 0x38, 0xfa, 0x2d, 0x82, 0xaf, 0xfc, - 0x7a, 0xc7, 0xf9, 0x54, 0x5f, 0xd8, 0xd6, 0x5f, 0xf3, 0xec, 0x94, 0x58, - 0x39, 0x11, 0x72, 0xa7, 0x45, 0xda, 0xb8, 0xa8, 0xe3, 0x5c, 0xb6, 0x7d, - 0x2a, 0x5a, 0xa2, 0xc2, 0xa1, 0x66, 0xdc, 0xd9, 0xa7, 0xc8, 0xd8, 0x18, - 0xf5, 0xa8, 0xa8, 0x01, 0x40, 0xc9, 0xa9, 0xd4, 0x6d, 0x18, 0xa8, 0xe2, - 0x5e, 0x73, 0x52, 0x50, 0x00, 0x4e, 0x05, 0x40, 0xc7, 0x71, 0xcd, 0x49, - 0x2b, 0x71, 0x8a, 0x8a, 0x80, 0x14, 0x0c, 0x9c, 0x54, 0xe0, 0x60, 0x62, - 0xa3, 0x89, 0x7b, 0xd4, 0x94, 0x00, 0x13, 0x80, 0x4d, 0x40, 0x4e, 0x4e, - 0x69, 0xf2, 0xb7, 0x6a, 0x8e, 0x80, 0x15, 0x46, 0xe3, 0x8a, 0x9e, 0x99, - 0x1a, 0xe0, 0x67, 0xd6, 0x9f, 0x40, 0x08, 0xc7, 0x68, 0xcd, 0x41, 0x4f, - 0x91, 0xb2, 0x71, 0xe9, 0x4c, 0xa0, 0x07, 0x22, 0xee, 0x6f, 0x6a, 0x27, - 0xb5, 0x59, 0xa4, 0x49, 0x37, 0xbc, 0x6e, 0xbc, 0x06, 0x43, 0x8c, 0x8f, - 0x43, 0xed, 0x52, 0x46, 0xb8, 0x5f, 0x73, 0x4e, 0xa0, 0x0a, 0xa6, 0xca, - 0x38, 0xa2, 0x95, 0x41, 0x6f, 0xde, 0x2e, 0xc3, 0xcf, 0x6c, 0x93, 0xff, - 0x00, 0xb3, 0x1a, 0x87, 0xec, 0x60, 0xca, 0xae, 0xd2, 0x48, 0xe1, 0x4e, - 0x42, 0x12, 0x36, 0x83, 0xf9, 0x55, 0xa9, 0x1b, 0x73, 0x7b, 0x0a, 0x6d, - 0x00, 0x57, 0x8b, 0x49, 0xb7, 0x0c, 0xfb, 0x53, 0xcb, 0x0c, 0x00, 0x21, - 0x38, 0xe8, 0x72, 0x0f, 0xd6, 0xa6, 0x5d, 0x39, 0x17, 0x90, 0xf2, 0x79, - 0x99, 0x2c, 0x64, 0x27, 0x2c, 0x4e, 0xdc, 0x7e, 0x82, 0xac, 0xa2, 0xed, - 0x5a, 0x5a, 0x00, 0xad, 0x1d, 0xaa, 0xdb, 0x16, 0x6f, 0x31, 0xe5, 0x91, - 0xc0, 0x05, 0xe4, 0x39, 0x38, 0x1d, 0xbf, 0x5a, 0x5a, 0x73, 0xb6, 0xe6, - 0xa6, 0xf5, 0xa0, 0x07, 0xc4, 0xb9, 0x39, 0xf4, 0xa9, 0x69, 0x14, 0x6d, - 0x18, 0xa5, 0xe9, 0x40, 0x0c, 0x95, 0xb0, 0x31, 0x51, 0x52, 0xb1, 0xdc, - 0x49, 0xa0, 0x0c, 0x9c, 0x50, 0x03, 0xe2, 0x5e, 0xf5, 0x25, 0x00, 0x60, - 0x62, 0x82, 0x70, 0x33, 0x40, 0x11, 0xca, 0xdd, 0xaa, 0x3a, 0x52, 0x72, - 0x73, 0x42, 0x8d, 0xc4, 0x0a, 0x00, 0x92, 0x25, 0xc0, 0xcd, 0x3e, 0x8e, - 0x94, 0x8c, 0x76, 0x82, 0x68, 0x02, 0x39, 0x5b, 0x27, 0x1e, 0x94, 0xca, - 0x3a, 0xd3, 0x91, 0x77, 0x35, 0x00, 0x49, 0x1a, 0xe1, 0x7d, 0xcd, 0x3a, - 0x8a, 0x47, 0x6d, 0xab, 0x40, 0x11, 0xc8, 0xd9, 0x6f, 0x61, 0x4c, 0xa2, - 0x9d, 0x1a, 0xee, 0x6f, 0x61, 0x40, 0x1f, 0xff, 0xd9 -}; diff --git a/services/camera/libcameraservice/FakeCamera.cpp b/services/camera/libcameraservice/FakeCamera.cpp deleted file mode 100644 index f3a6a67e..00000000 --- a/services/camera/libcameraservice/FakeCamera.cpp +++ /dev/null @@ -1,433 +0,0 @@ -/* -** -** Copyright 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 "FakeCamera" -#include <utils/Log.h> - -#include <string.h> -#include <stdlib.h> -#include <utils/String8.h> - -#include "FakeCamera.h" - - -namespace android { - -// TODO: All this rgb to yuv should probably be in a util class. - -// TODO: I think something is wrong in this class because the shadow is kBlue -// and the square color should alternate between kRed and kGreen. However on the -// emulator screen these are all shades of gray. Y seems ok but the U and V are -// probably not. - -static int tables_initialized = 0; -uint8_t *gYTable, *gCbTable, *gCrTable; - -static int -clamp(int x) -{ - if (x > 255) return 255; - if (x < 0) return 0; - return x; -} - -/* the equation used by the video code to translate YUV to RGB looks like this - * - * Y = (Y0 - 16)*k0 - * Cb = Cb0 - 128 - * Cr = Cr0 - 128 - * - * G = ( Y - k1*Cr - k2*Cb ) - * R = ( Y + k3*Cr ) - * B = ( Y + k4*Cb ) - * - */ - -static const double k0 = 1.164; -static const double k1 = 0.813; -static const double k2 = 0.391; -static const double k3 = 1.596; -static const double k4 = 2.018; - -/* let's try to extract the value of Y - * - * G + k1/k3*R + k2/k4*B = Y*( 1 + k1/k3 + k2/k4 ) - * - * Y = ( G + k1/k3*R + k2/k4*B ) / (1 + k1/k3 + k2/k4) - * Y0 = ( G0 + k1/k3*R0 + k2/k4*B0 ) / ((1 + k1/k3 + k2/k4)*k0) + 16 - * - * let define: - * kYr = k1/k3 - * kYb = k2/k4 - * kYy = k0 * ( 1 + kYr + kYb ) - * - * we have: - * Y = ( G + kYr*R + kYb*B ) - * Y0 = clamp[ Y/kYy + 16 ] - */ - -static const double kYr = k1/k3; -static const double kYb = k2/k4; -static const double kYy = k0*( 1. + kYr + kYb ); - -static void -initYtab( void ) -{ - const int imax = (int)( (kYr + kYb)*(31 << 2) + (61 << 3) + 0.1 ); - int i; - - gYTable = (uint8_t *)malloc(imax); - - for(i=0; i<imax; i++) { - int x = (int)(i/kYy + 16.5); - if (x < 16) x = 16; - else if (x > 235) x = 235; - gYTable[i] = (uint8_t) x; - } -} - -/* - * the source is RGB565, so adjust for 8-bit range of input values: - * - * G = (pixels >> 3) & 0xFC; - * R = (pixels >> 8) & 0xF8; - * B = (pixels & 0x1f) << 3; - * - * R2 = (pixels >> 11) R = R2*8 - * B2 = (pixels & 0x1f) B = B2*8 - * - * kYr*R = kYr2*R2 => kYr2 = kYr*8 - * kYb*B = kYb2*B2 => kYb2 = kYb*8 - * - * we want to use integer multiplications: - * - * SHIFT1 = 9 - * - * (ALPHA*R2) >> SHIFT1 == R*kYr => ALPHA = kYr*8*(1 << SHIFT1) - * - * ALPHA = kYr*(1 << (SHIFT1+3)) - * BETA = kYb*(1 << (SHIFT1+3)) - */ - -static const int SHIFT1 = 9; -static const int ALPHA = (int)( kYr*(1 << (SHIFT1+3)) + 0.5 ); -static const int BETA = (int)( kYb*(1 << (SHIFT1+3)) + 0.5 ); - -/* - * now let's try to get the values of Cb and Cr - * - * R-B = (k3*Cr - k4*Cb) - * - * k3*Cr = k4*Cb + (R-B) - * k4*Cb = k3*Cr - (R-B) - * - * R-G = (k1+k3)*Cr + k2*Cb - * = (k1+k3)*Cr + k2/k4*(k3*Cr - (R-B)/k0) - * = (k1 + k3 + k2*k3/k4)*Cr - k2/k4*(R-B) - * - * kRr*Cr = (R-G) + kYb*(R-B) - * - * Cr = ((R-G) + kYb*(R-B))/kRr - * Cr0 = clamp(Cr + 128) - */ - -static const double kRr = (k1 + k3 + k2*k3/k4); - -static void -initCrtab( void ) -{ - uint8_t *pTable; - int i; - - gCrTable = (uint8_t *)malloc(768*2); - - pTable = gCrTable + 384; - for(i=-384; i<384; i++) - pTable[i] = (uint8_t) clamp( i/kRr + 128.5 ); -} - -/* - * B-G = (k2 + k4)*Cb + k1*Cr - * = (k2 + k4)*Cb + k1/k3*(k4*Cb + (R-B)) - * = (k2 + k4 + k1*k4/k3)*Cb + k1/k3*(R-B) - * - * kBb*Cb = (B-G) - kYr*(R-B) - * - * Cb = ((B-G) - kYr*(R-B))/kBb - * Cb0 = clamp(Cb + 128) - * - */ - -static const double kBb = (k2 + k4 + k1*k4/k3); - -static void -initCbtab( void ) -{ - uint8_t *pTable; - int i; - - gCbTable = (uint8_t *)malloc(768*2); - - pTable = gCbTable + 384; - for(i=-384; i<384; i++) - pTable[i] = (uint8_t) clamp( i/kBb + 128.5 ); -} - -/* - * SHIFT2 = 16 - * - * DELTA = kYb*(1 << SHIFT2) - * GAMMA = kYr*(1 << SHIFT2) - */ - -static const int SHIFT2 = 16; -static const int DELTA = kYb*(1 << SHIFT2); -static const int GAMMA = kYr*(1 << SHIFT2); - -int32_t ccrgb16toyuv_wo_colorkey(uint8_t *rgb16, uint8_t *yuv420, - uint32_t *param, uint8_t *table[]) -{ - uint16_t *inputRGB = (uint16_t*)rgb16; - uint8_t *outYUV = yuv420; - int32_t width_dst = param[0]; - int32_t height_dst = param[1]; - int32_t pitch_dst = param[2]; - int32_t mheight_dst = param[3]; - int32_t pitch_src = param[4]; - uint8_t *y_tab = table[0]; - uint8_t *cb_tab = table[1]; - uint8_t *cr_tab = table[2]; - - int32_t size16 = pitch_dst*mheight_dst; - int32_t i,j,count; - int32_t ilimit,jlimit; - uint8_t *tempY,*tempU,*tempV; - uint16_t pixels; - int tmp; -uint32_t temp; - - tempY = outYUV; - tempU = outYUV + (height_dst * pitch_dst); - tempV = tempU + 1; - - jlimit = height_dst; - ilimit = width_dst; - - for(j=0; j<jlimit; j+=1) - { - for (i=0; i<ilimit; i+=2) - { - int32_t G_ds = 0, B_ds = 0, R_ds = 0; - uint8_t y0, y1, u, v; - - pixels = inputRGB[i]; - temp = (BETA*(pixels & 0x001F) + ALPHA*(pixels>>11) ); - y0 = y_tab[(temp>>SHIFT1) + ((pixels>>3) & 0x00FC)]; - - G_ds += (pixels>>1) & 0x03E0; - B_ds += (pixels<<5) & 0x03E0; - R_ds += (pixels>>6) & 0x03E0; - - pixels = inputRGB[i+1]; - temp = (BETA*(pixels & 0x001F) + ALPHA*(pixels>>11) ); - y1 = y_tab[(temp>>SHIFT1) + ((pixels>>3) & 0x00FC)]; - - G_ds += (pixels>>1) & 0x03E0; - B_ds += (pixels<<5) & 0x03E0; - R_ds += (pixels>>6) & 0x03E0; - - R_ds >>= 1; - B_ds >>= 1; - G_ds >>= 1; - - tmp = R_ds - B_ds; - - u = cb_tab[(((B_ds-G_ds)<<SHIFT2) - GAMMA*tmp)>>(SHIFT2+2)]; - v = cr_tab[(((R_ds-G_ds)<<SHIFT2) + DELTA*tmp)>>(SHIFT2+2)]; - - tempY[0] = y0; - tempY[1] = y1; - tempY += 2; - - if ((j&1) == 0) { - tempU[0] = u; - tempV[0] = v; - tempU += 2; - tempV += 2; - } - } - - inputRGB += pitch_src; - } - - return 1; -} - -#define min(a,b) ((a)<(b)?(a):(b)) -#define max(a,b) ((a)>(b)?(a):(b)) - -static void convert_rgb16_to_yuv420(uint8_t *rgb, uint8_t *yuv, int width, int height) -{ - if (!tables_initialized) { - initYtab(); - initCrtab(); - initCbtab(); - tables_initialized = 1; - } - - uint32_t param[6]; - param[0] = (uint32_t) width; - param[1] = (uint32_t) height; - param[2] = (uint32_t) width; - param[3] = (uint32_t) height; - param[4] = (uint32_t) width; - param[5] = (uint32_t) 0; - - uint8_t *table[3]; - table[0] = gYTable; - table[1] = gCbTable + 384; - table[2] = gCrTable + 384; - - ccrgb16toyuv_wo_colorkey(rgb, yuv, param, table); -} - -const int FakeCamera::kRed; -const int FakeCamera::kGreen; -const int FakeCamera::kBlue; - -FakeCamera::FakeCamera(int width, int height) - : mTmpRgb16Buffer(0) -{ - setSize(width, height); -} - -FakeCamera::~FakeCamera() -{ - delete[] mTmpRgb16Buffer; -} - -void FakeCamera::setSize(int width, int height) -{ - mWidth = width; - mHeight = height; - mCounter = 0; - mCheckX = 0; - mCheckY = 0; - - // This will cause it to be reallocated on the next call - // to getNextFrameAsYuv420(). - delete[] mTmpRgb16Buffer; - mTmpRgb16Buffer = 0; -} - -void FakeCamera::getNextFrameAsRgb565(uint16_t *buffer) -{ - int size = mWidth / 10; - - drawCheckerboard(buffer, size); - - int x = ((mCounter*3)&255); - if(x>128) x = 255 - x; - int y = ((mCounter*5)&255); - if(y>128) y = 255 - y; - - drawSquare(buffer, x*size/32, y*size/32, (size*5)>>1, (mCounter&0x100)?kRed:kGreen, kBlue); - - mCounter++; -} - -void FakeCamera::getNextFrameAsYuv420(uint8_t *buffer) -{ - if (mTmpRgb16Buffer == 0) - mTmpRgb16Buffer = new uint16_t[mWidth * mHeight]; - - getNextFrameAsRgb565(mTmpRgb16Buffer); - convert_rgb16_to_yuv420((uint8_t*)mTmpRgb16Buffer, buffer, mWidth, mHeight); -} - -void FakeCamera::drawSquare(uint16_t *dst, int x, int y, int size, int color, int shadow) -{ - int square_xstop, square_ystop, shadow_xstop, shadow_ystop; - - square_xstop = min(mWidth, x+size); - square_ystop = min(mHeight, y+size); - shadow_xstop = min(mWidth, x+size+(size/4)); - shadow_ystop = min(mHeight, y+size+(size/4)); - - // Do the shadow. - uint16_t *sh = &dst[(y+(size/4))*mWidth]; - for (int j = y + (size/4); j < shadow_ystop; j++) { - for (int i = x + (size/4); i < shadow_xstop; i++) { - sh[i] &= shadow; - } - sh += mWidth; - } - - // Draw the square. - uint16_t *sq = &dst[y*mWidth]; - for (int j = y; j < square_ystop; j++) { - for (int i = x; i < square_xstop; i++) { - sq[i] = color; - } - sq += mWidth; - } -} - -void FakeCamera::drawCheckerboard(uint16_t *dst, int size) -{ - bool black = true; - - if((mCheckX/size)&1) - black = false; - if((mCheckY/size)&1) - black = !black; - - int county = mCheckY%size; - int checkxremainder = mCheckX%size; - - for(int y=0;y<mHeight;y++) { - int countx = checkxremainder; - bool current = black; - for(int x=0;x<mWidth;x++) { - dst[y*mWidth+x] = current?0:0xffff; - if(countx++ >= size) { - countx=0; - current = !current; - } - } - if(county++ >= size) { - county=0; - black = !black; - } - } - mCheckX += 3; - mCheckY++; -} - - -void FakeCamera::dump(int fd) const -{ - const size_t SIZE = 256; - char buffer[SIZE]; - String8 result; - snprintf(buffer, 255, " width x height (%d x %d), counter (%d), check x-y coordinate(%d, %d)\n", mWidth, mHeight, mCounter, mCheckX, mCheckY); - result.append(buffer); - ::write(fd, result.string(), result.size()); -} - - -}; // namespace android diff --git a/services/camera/libcameraservice/FakeCamera.h b/services/camera/libcameraservice/FakeCamera.h deleted file mode 100644 index 724de207..00000000 --- a/services/camera/libcameraservice/FakeCamera.h +++ /dev/null @@ -1,67 +0,0 @@ -/* -** -** Copyright 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_FAKECAMERA_H -#define ANDROID_HARDWARE_FAKECAMERA_H - -#include <sys/types.h> -#include <stdint.h> - -namespace android { - -/* - * FakeCamera is used in the CameraHardwareStub to provide a fake video feed - * when the system does not have a camera in hardware. - * The fake video is a moving black and white checkerboard background with a - * bouncing gray square in the foreground. - * This class is not thread-safe. - * - * TODO: Since the major methods provides a raw/uncompressed video feed, rename - * this class to RawVideoSource. - */ - -class FakeCamera { -public: - FakeCamera(int width, int height); - ~FakeCamera(); - - void setSize(int width, int height); - void getNextFrameAsYuv420(uint8_t *buffer); - // Write to the fd a string representing the current state. - void dump(int fd) const; - -private: - // TODO: remove the uint16_t buffer param everywhere since it is a field of - // this class. - void getNextFrameAsRgb565(uint16_t *buffer); - - void drawSquare(uint16_t *buffer, int x, int y, int size, int color, int shadow); - void drawCheckerboard(uint16_t *buffer, int size); - - static const int kRed = 0xf800; - static const int kGreen = 0x07c0; - static const int kBlue = 0x003e; - - int mWidth, mHeight; - int mCounter; - int mCheckX, mCheckY; - uint16_t *mTmpRgb16Buffer; -}; - -}; // namespace android - -#endif // ANDROID_HARDWARE_FAKECAMERA_H diff --git a/services/camera/libcameraservice/camera2/BurstCapture.cpp b/services/camera/libcameraservice/camera2/BurstCapture.cpp new file mode 100644 index 00000000..f56c50cf --- /dev/null +++ b/services/camera/libcameraservice/camera2/BurstCapture.cpp @@ -0,0 +1,112 @@ +/* + * 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "Camera2-BurstCapture" + +#include <utils/Log.h> +#include <utils/Trace.h> + +#include "BurstCapture.h" + +#include "../Camera2Client.h" +#include "JpegCompressor.h" + +namespace android { +namespace camera2 { + +BurstCapture::BurstCapture(wp<Camera2Client> client, wp<CaptureSequencer> sequencer): + mCaptureStreamId(NO_STREAM), + mClient(client), + mSequencer(sequencer) +{ +} + +BurstCapture::~BurstCapture() { +} + +status_t BurstCapture::start(Vector<CameraMetadata> &metadatas, int32_t firstCaptureId) { + ALOGE("Not completely implemented"); + return INVALID_OPERATION; +} + +void BurstCapture::onFrameAvailable() { + ALOGV("%s", __FUNCTION__); + Mutex::Autolock l(mInputMutex); + if(!mInputChanged) { + mInputChanged = true; + mInputSignal.signal(); + } +} + +bool BurstCapture::threadLoop() { + status_t res; + { + Mutex::Autolock l(mInputMutex); + while(!mInputChanged) { + res = mInputSignal.waitRelative(mInputMutex, kWaitDuration); + if(res == TIMED_OUT) return true; + } + mInputChanged = false; + } + + do { + sp<Camera2Client> client = mClient.promote(); + if(client == 0) return false; + ALOGV("%s: Calling processFrameAvailable()", __FUNCTION__); + res = processFrameAvailable(client); + } while(res == OK); + + return true; +} + +CpuConsumer::LockedBuffer* BurstCapture::jpegEncode( + CpuConsumer::LockedBuffer *imgBuffer, + int quality) +{ + ALOGV("%s", __FUNCTION__); + + CpuConsumer::LockedBuffer *imgEncoded = new CpuConsumer::LockedBuffer; + uint8_t *data = new uint8_t[ANDROID_JPEG_MAX_SIZE]; + imgEncoded->data = data; + imgEncoded->width = imgBuffer->width; + imgEncoded->height = imgBuffer->height; + imgEncoded->stride = imgBuffer->stride; + + Vector<CpuConsumer::LockedBuffer*> buffers; + buffers.push_back(imgBuffer); + buffers.push_back(imgEncoded); + + sp<JpegCompressor> jpeg = new JpegCompressor(); + status_t res = jpeg->start(buffers, 1); + + bool success = jpeg->waitForDone(10 * 1e9); + if(success) { + return buffers[1]; + } + else { + ALOGE("%s: JPEG encode timed out", __FUNCTION__); + return NULL; // TODO: maybe change function return value to status_t + } +} + +status_t BurstCapture::processFrameAvailable(sp<Camera2Client> &client) { + ALOGE("Not implemented"); + return INVALID_OPERATION; +} + +} // namespace camera2 +} // namespace android diff --git a/services/camera/libcameraservice/camera2/BurstCapture.h b/services/camera/libcameraservice/camera2/BurstCapture.h new file mode 100644 index 00000000..dfc45eb3 --- /dev/null +++ b/services/camera/libcameraservice/camera2/BurstCapture.h @@ -0,0 +1,71 @@ +/* + * 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_SERVERS_CAMERA_BURST_CAPTURE_H +#define ANDROID_SERVERS_CAMERA_BURST_CAPTURE_H + +#include "camera2/CameraMetadata.h" +#include <binder/MemoryBase.h> +#include <binder/MemoryHeapBase.h> +#include <gui/CpuConsumer.h> +#include "Camera2Device.h" + +namespace android { + +class Camera2Client; + +namespace camera2 { + +class CaptureSequencer; + +class BurstCapture : public virtual Thread, + public virtual CpuConsumer::FrameAvailableListener +{ +public: + BurstCapture(wp<Camera2Client> client, wp<CaptureSequencer> sequencer); + virtual ~BurstCapture(); + + virtual void onFrameAvailable(); + virtual status_t start(Vector<CameraMetadata> &metadatas, int32_t firstCaptureId); + +protected: + Mutex mInputMutex; + bool mInputChanged; + Condition mInputSignal; + int mCaptureStreamId; + wp<Camera2Client> mClient; + wp<CaptureSequencer> mSequencer; + + // Should only be accessed by processing thread + enum { + NO_STREAM = -1 + }; + + CpuConsumer::LockedBuffer* jpegEncode( + CpuConsumer::LockedBuffer *imgBuffer, + int quality); + + virtual status_t processFrameAvailable(sp<Camera2Client> &client); + +private: + virtual bool threadLoop(); + static const nsecs_t kWaitDuration = 10000000; // 10 ms +}; + +} // namespace camera2 +} // namespace android + +#endif diff --git a/services/camera/libcameraservice/camera2/CallbackProcessor.cpp b/services/camera/libcameraservice/camera2/CallbackProcessor.cpp new file mode 100644 index 00000000..3e9c2555 --- /dev/null +++ b/services/camera/libcameraservice/camera2/CallbackProcessor.cpp @@ -0,0 +1,301 @@ +/* + * 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. + */ + +#define LOG_TAG "Camera2-CallbackProcessor" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/Trace.h> + +#include "CallbackProcessor.h" +#include <gui/SurfaceTextureClient.h> +#include "../Camera2Device.h" +#include "../Camera2Client.h" + + +namespace android { +namespace camera2 { + +CallbackProcessor::CallbackProcessor(wp<Camera2Client> client): + Thread(false), + mClient(client), + mCallbackAvailable(false), + mCallbackStreamId(NO_STREAM) { +} + +CallbackProcessor::~CallbackProcessor() { + ALOGV("%s: Exit", __FUNCTION__); + deleteStream(); +} + +void CallbackProcessor::onFrameAvailable() { + Mutex::Autolock l(mInputMutex); + if (!mCallbackAvailable) { + mCallbackAvailable = true; + mCallbackAvailableSignal.signal(); + } +} + +status_t CallbackProcessor::updateStream(const Parameters ¶ms) { + ATRACE_CALL(); + status_t res; + + Mutex::Autolock l(mInputMutex); + + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return OK; + sp<Camera2Device> device = client->getCameraDevice(); + + if (mCallbackConsumer == 0) { + // Create CPU buffer queue endpoint + mCallbackConsumer = new CpuConsumer(kCallbackHeapCount); + mCallbackConsumer->setFrameAvailableListener(this); + mCallbackConsumer->setName(String8("Camera2Client::CallbackConsumer")); + mCallbackWindow = new SurfaceTextureClient( + mCallbackConsumer->getProducerInterface()); + } + + if (mCallbackStreamId != NO_STREAM) { + // Check if stream parameters have to change + uint32_t currentWidth, currentHeight, currentFormat; + res = device->getStreamInfo(mCallbackStreamId, + ¤tWidth, ¤tHeight, ¤tFormat); + if (res != OK) { + ALOGE("%s: Camera %d: Error querying callback output stream info: " + "%s (%d)", __FUNCTION__, client->getCameraId(), + strerror(-res), res); + return res; + } + if (currentWidth != (uint32_t)params.previewWidth || + currentHeight != (uint32_t)params.previewHeight || + currentFormat != (uint32_t)params.previewFormat) { + // Since size should only change while preview is not running, + // assuming that all existing use of old callback stream is + // completed. + ALOGV("%s: Camera %d: Deleting stream %d since the buffer dimensions changed", + __FUNCTION__, client->getCameraId(), mCallbackStreamId); + res = device->deleteStream(mCallbackStreamId); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to delete old output stream " + "for callbacks: %s (%d)", __FUNCTION__, client->getCameraId(), + strerror(-res), res); + return res; + } + mCallbackStreamId = NO_STREAM; + } + } + + if (mCallbackStreamId == NO_STREAM) { + ALOGV("Creating callback stream: %d %d format 0x%x", + params.previewWidth, params.previewHeight, + params.previewFormat); + res = device->createStream(mCallbackWindow, + params.previewWidth, params.previewHeight, + params.previewFormat, 0, &mCallbackStreamId); + if (res != OK) { + ALOGE("%s: Camera %d: Can't create output stream for callbacks: " + "%s (%d)", __FUNCTION__, client->getCameraId(), + strerror(-res), res); + return res; + } + } + + return OK; +} + +status_t CallbackProcessor::deleteStream() { + ATRACE_CALL(); + status_t res; + + Mutex::Autolock l(mInputMutex); + + if (mCallbackStreamId != NO_STREAM) { + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return OK; + sp<Camera2Device> device = client->getCameraDevice(); + + device->deleteStream(mCallbackStreamId); + + mCallbackHeap.clear(); + mCallbackWindow.clear(); + mCallbackConsumer.clear(); + + mCallbackStreamId = NO_STREAM; + } + return OK; +} + +int CallbackProcessor::getStreamId() const { + Mutex::Autolock l(mInputMutex); + return mCallbackStreamId; +} + +void CallbackProcessor::dump(int fd, const Vector<String16>& args) const { +} + +bool CallbackProcessor::threadLoop() { + status_t res; + + { + Mutex::Autolock l(mInputMutex); + while (!mCallbackAvailable) { + res = mCallbackAvailableSignal.waitRelative(mInputMutex, + kWaitDuration); + if (res == TIMED_OUT) return true; + } + mCallbackAvailable = false; + } + + do { + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return false; + res = processNewCallback(client); + } while (res == OK); + + return true; +} + +status_t CallbackProcessor::processNewCallback(sp<Camera2Client> &client) { + ATRACE_CALL(); + status_t res; + + int callbackHeapId; + sp<Camera2Heap> callbackHeap; + size_t heapIdx; + + CpuConsumer::LockedBuffer imgBuffer; + ALOGV("%s: Getting buffer", __FUNCTION__); + res = mCallbackConsumer->lockNextBuffer(&imgBuffer); + if (res != OK) { + if (res != BAD_VALUE) { + ALOGE("%s: Camera %d: Error receiving next callback buffer: " + "%s (%d)", __FUNCTION__, client->getCameraId(), strerror(-res), res); + } + return res; + } + ALOGV("%s: Camera %d: Preview callback available", __FUNCTION__, + client->getCameraId()); + + { + SharedParameters::Lock l(client->getParameters()); + + if ( l.mParameters.state != Parameters::PREVIEW + && l.mParameters.state != Parameters::RECORD + && l.mParameters.state != Parameters::VIDEO_SNAPSHOT) { + ALOGV("%s: Camera %d: No longer streaming", + __FUNCTION__, client->getCameraId()); + mCallbackConsumer->unlockBuffer(imgBuffer); + return OK; + } + + if (! (l.mParameters.previewCallbackFlags & + CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK) ) { + ALOGV("%s: No longer enabled, dropping", __FUNCTION__); + mCallbackConsumer->unlockBuffer(imgBuffer); + return OK; + } + if ((l.mParameters.previewCallbackFlags & + CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) && + !l.mParameters.previewCallbackOneShot) { + ALOGV("%s: One shot mode, already sent, dropping", __FUNCTION__); + mCallbackConsumer->unlockBuffer(imgBuffer); + return OK; + } + + if (imgBuffer.format != l.mParameters.previewFormat) { + ALOGE("%s: Camera %d: Unexpected format for callback: " + "%x, expected %x", __FUNCTION__, client->getCameraId(), + imgBuffer.format, l.mParameters.previewFormat); + mCallbackConsumer->unlockBuffer(imgBuffer); + return INVALID_OPERATION; + } + + // In one-shot mode, stop sending callbacks after the first one + if (l.mParameters.previewCallbackFlags & + CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) { + ALOGV("%s: clearing oneshot", __FUNCTION__); + l.mParameters.previewCallbackOneShot = false; + } + } + + size_t bufferSize = Camera2Client::calculateBufferSize( + imgBuffer.width, imgBuffer.height, + imgBuffer.format, imgBuffer.stride); + size_t currentBufferSize = (mCallbackHeap == 0) ? + 0 : (mCallbackHeap->mHeap->getSize() / kCallbackHeapCount); + if (bufferSize != currentBufferSize) { + mCallbackHeap.clear(); + mCallbackHeap = new Camera2Heap(bufferSize, kCallbackHeapCount, + "Camera2Client::CallbackHeap"); + if (mCallbackHeap->mHeap->getSize() == 0) { + ALOGE("%s: Camera %d: Unable to allocate memory for callbacks", + __FUNCTION__, client->getCameraId()); + mCallbackConsumer->unlockBuffer(imgBuffer); + return INVALID_OPERATION; + } + + mCallbackHeapHead = 0; + mCallbackHeapFree = kCallbackHeapCount; + } + + if (mCallbackHeapFree == 0) { + ALOGE("%s: Camera %d: No free callback buffers, dropping frame", + __FUNCTION__, client->getCameraId()); + mCallbackConsumer->unlockBuffer(imgBuffer); + return OK; + } + + heapIdx = mCallbackHeapHead; + + mCallbackHeapHead = (mCallbackHeapHead + 1) & kCallbackHeapCount; + mCallbackHeapFree--; + + // TODO: Get rid of this memcpy by passing the gralloc queue all the way + // to app + + ssize_t offset; + size_t size; + sp<IMemoryHeap> heap = + mCallbackHeap->mBuffers[heapIdx]->getMemory(&offset, + &size); + uint8_t *data = (uint8_t*)heap->getBase() + offset; + memcpy(data, imgBuffer.data, bufferSize); + + ALOGV("%s: Freeing buffer", __FUNCTION__); + mCallbackConsumer->unlockBuffer(imgBuffer); + + // Call outside parameter lock to allow re-entrancy from notification + { + Camera2Client::SharedCameraClient::Lock l(client->mSharedCameraClient); + if (l.mCameraClient != 0) { + ALOGV("%s: Camera %d: Invoking client data callback", + __FUNCTION__, client->getCameraId()); + l.mCameraClient->dataCallback(CAMERA_MSG_PREVIEW_FRAME, + mCallbackHeap->mBuffers[heapIdx], NULL); + } + } + + // Only increment free if we're still using the same heap + mCallbackHeapFree++; + + ALOGV("%s: exit", __FUNCTION__); + + return OK; +} + +}; // namespace camera2 +}; // namespace android diff --git a/services/camera/libcameraservice/camera2/CallbackProcessor.h b/services/camera/libcameraservice/camera2/CallbackProcessor.h new file mode 100644 index 00000000..c2a13724 --- /dev/null +++ b/services/camera/libcameraservice/camera2/CallbackProcessor.h @@ -0,0 +1,82 @@ +/* + * 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_SERVERS_CAMERA_CAMERA2_CALLBACKPROCESSOR_H +#define ANDROID_SERVERS_CAMERA_CAMERA2_CALLBACKPROCESSOR_H + +#include <utils/Thread.h> +#include <utils/String16.h> +#include <utils/Vector.h> +#include <utils/Mutex.h> +#include <utils/Condition.h> +#include <gui/CpuConsumer.h> +#include "Parameters.h" +#include "CameraMetadata.h" +#include "Camera2Heap.h" + +namespace android { + +class Camera2Client; + +namespace camera2 { + +/*** + * Still image capture output image processing + */ +class CallbackProcessor: + public Thread, public CpuConsumer::FrameAvailableListener { + public: + CallbackProcessor(wp<Camera2Client> client); + ~CallbackProcessor(); + + void onFrameAvailable(); + + status_t updateStream(const Parameters ¶ms); + status_t deleteStream(); + int getStreamId() const; + + void dump(int fd, const Vector<String16>& args) const; + private: + static const nsecs_t kWaitDuration = 10000000; // 10 ms + wp<Camera2Client> mClient; + + mutable Mutex mInputMutex; + bool mCallbackAvailable; + Condition mCallbackAvailableSignal; + + enum { + NO_STREAM = -1 + }; + + int mCallbackStreamId; + static const size_t kCallbackHeapCount = 6; + sp<CpuConsumer> mCallbackConsumer; + sp<ANativeWindow> mCallbackWindow; + sp<Camera2Heap> mCallbackHeap; + int mCallbackHeapId; + size_t mCallbackHeapHead, mCallbackHeapFree; + + virtual bool threadLoop(); + + status_t processNewCallback(sp<Camera2Client> &client); + +}; + + +}; //namespace camera2 +}; //namespace android + +#endif diff --git a/services/camera/libcameraservice/camera2/Camera2Heap.h b/services/camera/libcameraservice/camera2/Camera2Heap.h new file mode 100644 index 00000000..9c72d762 --- /dev/null +++ b/services/camera/libcameraservice/camera2/Camera2Heap.h @@ -0,0 +1,55 @@ +/* + * 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_SERVERS_CAMERA_CAMERA2HEAP_H +#define ANDROiD_SERVERS_CAMERA_CAMERA2HEAP_H + +#include <binder/MemoryBase.h> +#include <binder/MemoryHeapBase.h> + +namespace android { +namespace camera2 { + +// Utility class for managing a set of IMemory blocks +class Camera2Heap : public RefBase { + public: + Camera2Heap(size_t buf_size, uint_t num_buffers = 1, + const char *name = NULL) : + mBufSize(buf_size), + mNumBufs(num_buffers) { + mHeap = new MemoryHeapBase(buf_size * num_buffers, 0, name); + mBuffers = new sp<MemoryBase>[mNumBufs]; + for (uint_t i = 0; i < mNumBufs; i++) + mBuffers[i] = new MemoryBase(mHeap, + i * mBufSize, + mBufSize); + } + + virtual ~Camera2Heap() + { + delete [] mBuffers; + } + + size_t mBufSize; + uint_t mNumBufs; + sp<MemoryHeapBase> mHeap; + sp<MemoryBase> *mBuffers; +}; + +}; // namespace camera2 +}; // namespace android + +#endif diff --git a/services/camera/libcameraservice/camera2/CameraMetadata.cpp b/services/camera/libcameraservice/camera2/CameraMetadata.cpp new file mode 100644 index 00000000..835587db --- /dev/null +++ b/services/camera/libcameraservice/camera2/CameraMetadata.cpp @@ -0,0 +1,296 @@ +/* + * 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. + */ + +#define LOG_TAG "Camera2-Metadata" +#include <utils/Log.h> +#include <utils/Errors.h> + +#include "CameraMetadata.h" + +namespace android { + +namespace camera2 { +CameraMetadata::CameraMetadata() : + mBuffer(NULL) { +} + +CameraMetadata::CameraMetadata(size_t entryCapacity, size_t dataCapacity) +{ + mBuffer = allocate_camera_metadata(entryCapacity, dataCapacity); +} + +CameraMetadata::CameraMetadata(const CameraMetadata &other) { + mBuffer = clone_camera_metadata(other.mBuffer); +} + +CameraMetadata &CameraMetadata::operator=(const CameraMetadata &other) { + return operator=(other.mBuffer); +} + +CameraMetadata &CameraMetadata::operator=(const camera_metadata_t *buffer) { + if (CC_LIKELY(buffer != mBuffer)) { + camera_metadata_t *newBuffer = clone_camera_metadata(buffer); + clear(); + mBuffer = newBuffer; + } + return *this; +} + +CameraMetadata::~CameraMetadata() { + clear(); +} + +camera_metadata_t* CameraMetadata::release() { + camera_metadata_t *released = mBuffer; + mBuffer = NULL; + return released; +} + +void CameraMetadata::clear() { + if (mBuffer) { + free_camera_metadata(mBuffer); + mBuffer = NULL; + } +} + +void CameraMetadata::acquire(camera_metadata_t *buffer) { + clear(); + mBuffer = buffer; +} + +void CameraMetadata::acquire(CameraMetadata &other) { + acquire(other.release()); +} + +status_t CameraMetadata::append(const CameraMetadata &other) { + return append_camera_metadata(mBuffer, other.mBuffer); +} + +size_t CameraMetadata::entryCount() const { + return (mBuffer == NULL) ? 0 : + get_camera_metadata_entry_count(mBuffer); +} + +bool CameraMetadata::isEmpty() const { + return entryCount() == 0; +} + +status_t CameraMetadata::sort() { + return sort_camera_metadata(mBuffer); +} + +status_t CameraMetadata::checkType(uint32_t tag, uint8_t expectedType) { + int tagType = get_camera_metadata_tag_type(tag); + if ( CC_UNLIKELY(tagType == -1)) { + ALOGE("Update metadata entry: Unknown tag %d", tag); + return INVALID_OPERATION; + } + if ( CC_UNLIKELY(tagType != expectedType) ) { + ALOGE("Mismatched tag type when updating entry %s (%d) of type %s; " + "got type %s data instead ", + get_camera_metadata_tag_name(tag), tag, + camera_metadata_type_names[tagType], + camera_metadata_type_names[expectedType]); + return INVALID_OPERATION; + } + return OK; +} + +status_t CameraMetadata::update(uint32_t tag, + const int32_t *data, size_t data_count) { + status_t res; + if ( (res = checkType(tag, TYPE_INT32)) != OK) { + return res; + } + return update(tag, (const void*)data, data_count); +} + +status_t CameraMetadata::update(uint32_t tag, + const uint8_t *data, size_t data_count) { + status_t res; + if ( (res = checkType(tag, TYPE_BYTE)) != OK) { + return res; + } + return update(tag, (const void*)data, data_count); +} + +status_t CameraMetadata::update(uint32_t tag, + const float *data, size_t data_count) { + status_t res; + if ( (res = checkType(tag, TYPE_FLOAT)) != OK) { + return res; + } + return update(tag, (const void*)data, data_count); +} + +status_t CameraMetadata::update(uint32_t tag, + const int64_t *data, size_t data_count) { + status_t res; + if ( (res = checkType(tag, TYPE_INT64)) != OK) { + return res; + } + return update(tag, (const void*)data, data_count); +} + +status_t CameraMetadata::update(uint32_t tag, + const double *data, size_t data_count) { + status_t res; + if ( (res = checkType(tag, TYPE_DOUBLE)) != OK) { + return res; + } + return update(tag, (const void*)data, data_count); +} + +status_t CameraMetadata::update(uint32_t tag, + const camera_metadata_rational_t *data, size_t data_count) { + status_t res; + if ( (res = checkType(tag, TYPE_RATIONAL)) != OK) { + return res; + } + return update(tag, (const void*)data, data_count); +} + +status_t CameraMetadata::update(uint32_t tag, + const String8 &string) { + status_t res; + if ( (res = checkType(tag, TYPE_BYTE)) != OK) { + return res; + } + return update(tag, (const void*)string.string(), string.size()); +} + +status_t CameraMetadata::update(uint32_t tag, const void *data, + size_t data_count) { + status_t res; + int type = get_camera_metadata_tag_type(tag); + if (type == -1) { + ALOGE("%s: Tag %d not found", __FUNCTION__, tag); + return BAD_VALUE; + } + size_t data_size = calculate_camera_metadata_entry_data_size(type, + data_count); + + res = resizeIfNeeded(1, data_size); + + if (res == OK) { + camera_metadata_entry_t entry; + res = find_camera_metadata_entry(mBuffer, tag, &entry); + if (res == NAME_NOT_FOUND) { + res = add_camera_metadata_entry(mBuffer, + tag, data, data_count); + } else if (res == OK) { + res = update_camera_metadata_entry(mBuffer, + entry.index, data, data_count, NULL); + } + } + + if (res != OK) { + ALOGE("%s: Unable to update metadata entry %s.%s (%x): %s (%d)", + __FUNCTION__, get_camera_metadata_section_name(tag), + get_camera_metadata_tag_name(tag), tag, strerror(-res), res); + } + return res; +} + +camera_metadata_entry_t CameraMetadata::find(uint32_t tag) { + status_t res; + camera_metadata_entry entry; + res = find_camera_metadata_entry(mBuffer, tag, &entry); + if (CC_UNLIKELY( res != OK )) { + entry.count = 0; + entry.data.u8 = NULL; + } + return entry; +} + +camera_metadata_ro_entry_t CameraMetadata::find(uint32_t tag) const { + status_t res; + camera_metadata_ro_entry entry; + res = find_camera_metadata_ro_entry(mBuffer, tag, &entry); + if (CC_UNLIKELY( res != OK )) { + entry.count = 0; + entry.data.u8 = NULL; + } + return entry; +} + +status_t CameraMetadata::erase(uint32_t tag) { + camera_metadata_entry_t entry; + status_t res; + res = find_camera_metadata_entry(mBuffer, tag, &entry); + if (res == NAME_NOT_FOUND) { + return OK; + } else if (res != OK) { + ALOGE("%s: Error looking for entry %s.%s (%x): %s %d", + __FUNCTION__, + get_camera_metadata_section_name(tag), + get_camera_metadata_tag_name(tag), tag, strerror(-res), res); + return res; + } + res = delete_camera_metadata_entry(mBuffer, entry.index); + if (res != OK) { + ALOGE("%s: Error deleting entry %s.%s (%x): %s %d", + __FUNCTION__, + get_camera_metadata_section_name(tag), + get_camera_metadata_tag_name(tag), tag, strerror(-res), res); + } + return res; +} + +void CameraMetadata::dump(int fd, int verbosity, int indentation) const { + dump_indented_camera_metadata(mBuffer, fd, verbosity, indentation); +} + +status_t CameraMetadata::resizeIfNeeded(size_t extraEntries, size_t extraData) { + if (mBuffer == NULL) { + mBuffer = allocate_camera_metadata(extraEntries * 2, extraData * 2); + if (mBuffer == NULL) { + ALOGE("%s: Can't allocate larger metadata buffer", __FUNCTION__); + return NO_MEMORY; + } + } else { + size_t currentEntryCount = get_camera_metadata_entry_count(mBuffer); + size_t currentEntryCap = get_camera_metadata_entry_capacity(mBuffer); + size_t newEntryCount = currentEntryCount + + extraEntries; + newEntryCount = (newEntryCount > currentEntryCap) ? + newEntryCount * 2 : currentEntryCap; + + size_t currentDataCount = get_camera_metadata_data_count(mBuffer); + size_t currentDataCap = get_camera_metadata_data_capacity(mBuffer); + size_t newDataCount = currentDataCount + + extraData; + newDataCount = (newDataCount > currentDataCap) ? + newDataCount * 2 : currentDataCap; + + if (newEntryCount > currentEntryCap || + newDataCount > currentDataCap) { + camera_metadata_t *oldBuffer = mBuffer; + mBuffer = allocate_camera_metadata(newEntryCount, + newDataCount); + if (mBuffer == NULL) { + ALOGE("%s: Can't allocate larger metadata buffer", __FUNCTION__); + return NO_MEMORY; + } + append_camera_metadata(mBuffer, oldBuffer); + free_camera_metadata(oldBuffer); + } + } + return OK; +} + +}; // namespace camera2 +}; // namespace android diff --git a/services/camera/libcameraservice/camera2/CameraMetadata.h b/services/camera/libcameraservice/camera2/CameraMetadata.h new file mode 100644 index 00000000..aee6cd78 --- /dev/null +++ b/services/camera/libcameraservice/camera2/CameraMetadata.h @@ -0,0 +1,173 @@ +/* + * 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_SERVERS_CAMERA_CAMERA2METADATA_CPP +#define ANDROID_SERVERS_CAMERA_CAMERA2METADATA_CPP + +#include "system/camera_metadata.h" +#include <utils/String8.h> +#include <utils/Vector.h> + +namespace android { +namespace camera2 { + +/** + * A convenience wrapper around the C-based camera_metadata_t library. + */ +class CameraMetadata { + public: + /** Creates an empty object; best used when expecting to acquire contents + * from elsewhere */ + CameraMetadata(); + /** Creates an object with space for entryCapacity entries, with + * dataCapacity extra storage */ + CameraMetadata(size_t entryCapacity, size_t dataCapacity = 10); + + ~CameraMetadata(); + + /** Takes ownership of passed-in buffer */ + CameraMetadata(camera_metadata_t *buffer); + /** Clones the metadata */ + CameraMetadata(const CameraMetadata &other); + + /** + * Assignment clones metadata buffer. + */ + CameraMetadata &operator=(const CameraMetadata &other); + CameraMetadata &operator=(const camera_metadata_t *buffer); + + /** + * Release a raw metadata buffer to the caller. After this call, + * CameraMetadata no longer references the buffer, and the caller takes + * responsibility for freeing the raw metadata buffer (using + * free_camera_metadata()), or for handing it to another CameraMetadata + * instance. + */ + camera_metadata_t* release(); + + /** + * Clear the metadata buffer and free all storage used by it + */ + void clear(); + + /** + * Acquire a raw metadata buffer from the caller. After this call, + * the caller no longer owns the raw buffer, and must not free or manipulate it. + * If CameraMetadata already contains metadata, it is freed. + */ + void acquire(camera_metadata_t* buffer); + + /** + * Acquires raw buffer from other CameraMetadata object. After the call, the argument + * object no longer has any metadata. + */ + void acquire(CameraMetadata &other); + + /** + * Append metadata from another CameraMetadata object. + */ + status_t append(const CameraMetadata &other); + + /** + * Number of metadata entries. + */ + size_t entryCount() const; + + /** + * Is the buffer empty (no entires) + */ + bool isEmpty() const; + + /** + * Sort metadata buffer for faster find + */ + status_t sort(); + + /** + * Update metadata entry. Will create entry if it doesn't exist already, and + * will reallocate the buffer if insufficient space exists. Overloaded for + * the various types of valid data. + */ + status_t update(uint32_t tag, + const uint8_t *data, size_t data_count); + status_t update(uint32_t tag, + const int32_t *data, size_t data_count); + status_t update(uint32_t tag, + const float *data, size_t data_count); + status_t update(uint32_t tag, + const int64_t *data, size_t data_count); + status_t update(uint32_t tag, + const double *data, size_t data_count); + status_t update(uint32_t tag, + const camera_metadata_rational_t *data, size_t data_count); + status_t update(uint32_t tag, + const String8 &string); + + template<typename T> + status_t update(uint32_t tag, Vector<T> data) { + return update(tag, data.array(), data.size()); + } + + /** + * Get metadata entry by tag id + */ + camera_metadata_entry find(uint32_t tag); + + /** + * Get metadata entry by tag id, with no editing + */ + camera_metadata_ro_entry find(uint32_t tag) const; + + /** + * Delete metadata entry by tag + */ + status_t erase(uint32_t tag); + + /** + * Dump contents into FD for debugging. The verbosity levels are + * 0: Tag entry information only, no data values + * 1: Level 0 plus at most 16 data values per entry + * 2: All information + * + * The indentation parameter sets the number of spaces to add to the start + * each line of output. + */ + void dump(int fd, int verbosity = 1, int indentation = 0) const; + + private: + camera_metadata_t *mBuffer; + + /** + * Check if tag has a given type + */ + status_t checkType(uint32_t tag, uint8_t expectedType); + + /** + * Base update entry method + */ + status_t update(uint32_t tag, const void *data, size_t data_count); + + /** + * Resize metadata buffer if needed by reallocating it and copying it over. + */ + status_t resizeIfNeeded(size_t extraEntries, size_t extraData); + +}; + +}; // namespace camera2 +}; // namespace android + +#endif diff --git a/services/camera/libcameraservice/camera2/CaptureSequencer.cpp b/services/camera/libcameraservice/camera2/CaptureSequencer.cpp new file mode 100644 index 00000000..072453b0 --- /dev/null +++ b/services/camera/libcameraservice/camera2/CaptureSequencer.cpp @@ -0,0 +1,673 @@ +/* + * 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. + */ + +#define LOG_TAG "Camera2-CaptureSequencer" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/Trace.h> +#include <utils/Vector.h> + +#include "CaptureSequencer.h" +#include "BurstCapture.h" +#include "../Camera2Device.h" +#include "../Camera2Client.h" +#include "Parameters.h" + +namespace android { +namespace camera2 { + +/** Public members */ + +CaptureSequencer::CaptureSequencer(wp<Camera2Client> client): + Thread(false), + mStartCapture(false), + mBusy(false), + mNewAEState(false), + mNewFrameReceived(false), + mNewCaptureReceived(false), + mShutterNotified(false), + mClient(client), + mCaptureState(IDLE), + mTriggerId(0), + mTimeoutCount(0), + mCaptureId(Camera2Client::kCaptureRequestIdStart), + mMsgType(0) { + ALOGV("%s", __FUNCTION__); +} + +CaptureSequencer::~CaptureSequencer() { + ALOGV("%s: Exit", __FUNCTION__); +} + +void CaptureSequencer::setZslProcessor(wp<ZslProcessor> processor) { + Mutex::Autolock l(mInputMutex); + mZslProcessor = processor; +} + +status_t CaptureSequencer::startCapture(int msgType) { + ALOGV("%s", __FUNCTION__); + ATRACE_CALL(); + Mutex::Autolock l(mInputMutex); + if (mBusy) { + ALOGE("%s: Already busy capturing!", __FUNCTION__); + return INVALID_OPERATION; + } + if (!mStartCapture) { + mMsgType = msgType; + mStartCapture = true; + mStartCaptureSignal.signal(); + } + return OK; +} + +status_t CaptureSequencer::waitUntilIdle(nsecs_t timeout) { + ATRACE_CALL(); + ALOGV("%s: Waiting for idle", __FUNCTION__); + Mutex::Autolock l(mStateMutex); + status_t res = -1; + while (mCaptureState != IDLE) { + nsecs_t startTime = systemTime(); + + res = mStateChanged.waitRelative(mStateMutex, timeout); + if (res != OK) return res; + + timeout -= (systemTime() - startTime); + } + ALOGV("%s: Now idle", __FUNCTION__); + return OK; +} + +void CaptureSequencer::notifyAutoExposure(uint8_t newState, int triggerId) { + ATRACE_CALL(); + Mutex::Autolock l(mInputMutex); + mAEState = newState; + mAETriggerId = triggerId; + if (!mNewAEState) { + mNewAEState = true; + mNewNotifySignal.signal(); + } +} + +void CaptureSequencer::onFrameAvailable(int32_t frameId, + const CameraMetadata &frame) { + ALOGV("%s: Listener found new frame", __FUNCTION__); + ATRACE_CALL(); + Mutex::Autolock l(mInputMutex); + mNewFrameId = frameId; + mNewFrame = frame; + if (!mNewFrameReceived) { + mNewFrameReceived = true; + mNewFrameSignal.signal(); + } +} + +void CaptureSequencer::onCaptureAvailable(nsecs_t timestamp, + sp<MemoryBase> captureBuffer) { + ATRACE_CALL(); + ALOGV("%s", __FUNCTION__); + Mutex::Autolock l(mInputMutex); + mCaptureTimestamp = timestamp; + mCaptureBuffer = captureBuffer; + if (!mNewCaptureReceived) { + mNewCaptureReceived = true; + mNewCaptureSignal.signal(); + } +} + + +void CaptureSequencer::dump(int fd, const Vector<String16>& args) { + String8 result; + if (mCaptureRequest.entryCount() != 0) { + result = " Capture request:\n"; + write(fd, result.string(), result.size()); + mCaptureRequest.dump(fd, 2, 6); + } else { + result = " Capture request: undefined\n"; + write(fd, result.string(), result.size()); + } + result = String8::format(" Current capture state: %s\n", + kStateNames[mCaptureState]); + result.append(" Latest captured frame:\n"); + write(fd, result.string(), result.size()); + mNewFrame.dump(fd, 2, 6); +} + +/** Private members */ + +const char* CaptureSequencer::kStateNames[CaptureSequencer::NUM_CAPTURE_STATES+1] = +{ + "IDLE", + "START", + "ZSL_START", + "ZSL_WAITING", + "ZSL_REPROCESSING", + "STANDARD_START", + "STANDARD_PRECAPTURE_WAIT", + "STANDARD_CAPTURE", + "STANDARD_CAPTURE_WAIT", + "BURST_CAPTURE_START", + "BURST_CAPTURE_WAIT", + "DONE", + "ERROR", + "UNKNOWN" +}; + +const CaptureSequencer::StateManager + CaptureSequencer::kStateManagers[CaptureSequencer::NUM_CAPTURE_STATES-1] = { + &CaptureSequencer::manageIdle, + &CaptureSequencer::manageStart, + &CaptureSequencer::manageZslStart, + &CaptureSequencer::manageZslWaiting, + &CaptureSequencer::manageZslReprocessing, + &CaptureSequencer::manageStandardStart, + &CaptureSequencer::manageStandardPrecaptureWait, + &CaptureSequencer::manageStandardCapture, + &CaptureSequencer::manageStandardCaptureWait, + &CaptureSequencer::manageBurstCaptureStart, + &CaptureSequencer::manageBurstCaptureWait, + &CaptureSequencer::manageDone, +}; + +bool CaptureSequencer::threadLoop() { + status_t res; + + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return false; + + CaptureState currentState; + { + Mutex::Autolock l(mStateMutex); + currentState = mCaptureState; + } + + currentState = (this->*kStateManagers[currentState])(client); + + Mutex::Autolock l(mStateMutex); + if (currentState != mCaptureState) { + mCaptureState = currentState; + ATRACE_INT("cam2_capt_state", mCaptureState); + ALOGV("Camera %d: New capture state %s", + client->getCameraId(), kStateNames[mCaptureState]); + mStateChanged.signal(); + } + + if (mCaptureState == ERROR) { + ALOGE("Camera %d: Stopping capture sequencer due to error", + client->getCameraId()); + return false; + } + + return true; +} + +CaptureSequencer::CaptureState CaptureSequencer::manageIdle(sp<Camera2Client> &client) { + status_t res; + Mutex::Autolock l(mInputMutex); + while (!mStartCapture) { + res = mStartCaptureSignal.waitRelative(mInputMutex, + kWaitDuration); + if (res == TIMED_OUT) break; + } + if (mStartCapture) { + mStartCapture = false; + mBusy = true; + return START; + } + return IDLE; +} + +CaptureSequencer::CaptureState CaptureSequencer::manageDone(sp<Camera2Client> &client) { + status_t res = OK; + ATRACE_CALL(); + mCaptureId++; + if (mCaptureId >= Camera2Client::kCaptureRequestIdEnd) { + mCaptureId = Camera2Client::kCaptureRequestIdStart; + } + { + Mutex::Autolock l(mInputMutex); + mBusy = false; + } + + { + SharedParameters::Lock l(client->getParameters()); + switch (l.mParameters.state) { + case Parameters::DISCONNECTED: + ALOGW("%s: Camera %d: Discarding image data during shutdown ", + __FUNCTION__, client->getCameraId()); + res = INVALID_OPERATION; + break; + case Parameters::STILL_CAPTURE: + l.mParameters.state = Parameters::STOPPED; + break; + case Parameters::VIDEO_SNAPSHOT: + l.mParameters.state = Parameters::RECORD; + break; + default: + ALOGE("%s: Camera %d: Still image produced unexpectedly " + "in state %s!", + __FUNCTION__, client->getCameraId(), + Parameters::getStateName(l.mParameters.state)); + res = INVALID_OPERATION; + } + } + sp<ZslProcessor> processor = mZslProcessor.promote(); + if (processor != 0) { + processor->clearZslQueue(); + } + + if (mCaptureBuffer != 0 && res == OK) { + Camera2Client::SharedCameraClient::Lock l(client->mSharedCameraClient); + ALOGV("%s: Sending still image to client", __FUNCTION__); + if (l.mCameraClient != 0) { + l.mCameraClient->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, + mCaptureBuffer, NULL); + } else { + ALOGV("%s: No client!", __FUNCTION__); + } + } + mCaptureBuffer.clear(); + + return IDLE; +} + +CaptureSequencer::CaptureState CaptureSequencer::manageStart( + sp<Camera2Client> &client) { + ALOGV("%s", __FUNCTION__); + status_t res; + ATRACE_CALL(); + SharedParameters::Lock l(client->getParameters()); + CaptureState nextState = DONE; + + res = updateCaptureRequest(l.mParameters, client); + if (res != OK ) { + ALOGE("%s: Camera %d: Can't update still image capture request: %s (%d)", + __FUNCTION__, client->getCameraId(), strerror(-res), res); + return DONE; + } + + if(l.mParameters.lightFx != Parameters::LIGHTFX_NONE && + l.mParameters.state == Parameters::STILL_CAPTURE) { + nextState = BURST_CAPTURE_START; + } + else if (l.mParameters.zslMode && + l.mParameters.state == Parameters::STILL_CAPTURE && + l.mParameters.flashMode != Parameters::FLASH_MODE_ON) { + nextState = ZSL_START; + } else { + nextState = STANDARD_START; + } + mShutterNotified = false; + + return nextState; +} + +CaptureSequencer::CaptureState CaptureSequencer::manageZslStart( + sp<Camera2Client> &client) { + ALOGV("%s", __FUNCTION__); + status_t res; + sp<ZslProcessor> processor = mZslProcessor.promote(); + if (processor == 0) { + ALOGE("%s: No ZSL queue to use!", __FUNCTION__); + return DONE; + } + + client->registerFrameListener(mCaptureId, mCaptureId + 1, + this); + + // TODO: Actually select the right thing here. + res = processor->pushToReprocess(mCaptureId); + if (res != OK) { + if (res == NOT_ENOUGH_DATA) { + ALOGV("%s: Camera %d: ZSL queue doesn't have good frame, " + "falling back to normal capture", __FUNCTION__, + client->getCameraId()); + } else { + ALOGE("%s: Camera %d: Error in ZSL queue: %s (%d)", + __FUNCTION__, client->getCameraId(), strerror(-res), res); + } + return STANDARD_START; + } + + SharedParameters::Lock l(client->getParameters()); + /* warning: this also locks a SharedCameraClient */ + shutterNotifyLocked(l.mParameters, client, mMsgType); + mShutterNotified = true; + mTimeoutCount = kMaxTimeoutsForCaptureEnd; + return STANDARD_CAPTURE_WAIT; +} + +CaptureSequencer::CaptureState CaptureSequencer::manageZslWaiting( + sp<Camera2Client> &client) { + ALOGV("%s", __FUNCTION__); + return DONE; +} + +CaptureSequencer::CaptureState CaptureSequencer::manageZslReprocessing( + sp<Camera2Client> &client) { + ALOGV("%s", __FUNCTION__); + return START; +} + +CaptureSequencer::CaptureState CaptureSequencer::manageStandardStart( + sp<Camera2Client> &client) { + ATRACE_CALL(); + client->registerFrameListener(mCaptureId, mCaptureId + 1, + this); + { + SharedParameters::Lock l(client->getParameters()); + mTriggerId = l.mParameters.precaptureTriggerCounter++; + } + client->getCameraDevice()->triggerPrecaptureMetering(mTriggerId); + + mAeInPrecapture = false; + mTimeoutCount = kMaxTimeoutsForPrecaptureStart; + return STANDARD_PRECAPTURE_WAIT; +} + +CaptureSequencer::CaptureState CaptureSequencer::manageStandardPrecaptureWait( + sp<Camera2Client> &client) { + status_t res; + ATRACE_CALL(); + Mutex::Autolock l(mInputMutex); + while (!mNewAEState) { + res = mNewNotifySignal.waitRelative(mInputMutex, kWaitDuration); + if (res == TIMED_OUT) { + mTimeoutCount--; + break; + } + } + if (mTimeoutCount <= 0) { + ALOGW("Timed out waiting for precapture %s", + mAeInPrecapture ? "end" : "start"); + return STANDARD_CAPTURE; + } + 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; + } + } else { + // Waiting to see PRECAPTURE state end + if (mAETriggerId == mTriggerId && + mAEState != ANDROID_CONTROL_AE_STATE_PRECAPTURE) { + ALOGV("%s: Got precapture end", __FUNCTION__); + return STANDARD_CAPTURE; + } + } + mNewAEState = false; + } + return STANDARD_PRECAPTURE_WAIT; +} + +CaptureSequencer::CaptureState CaptureSequencer::manageStandardCapture( + sp<Camera2Client> &client) { + status_t res; + ATRACE_CALL(); + SharedParameters::Lock l(client->getParameters()); + Vector<uint8_t> outputStreams; + + outputStreams.push(client->getPreviewStreamId()); + outputStreams.push(client->getCaptureStreamId()); + + if (l.mParameters.previewCallbackFlags & + CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK) { + outputStreams.push(client->getCallbackStreamId()); + } + + if (l.mParameters.state == Parameters::VIDEO_SNAPSHOT) { + outputStreams.push(client->getRecordingStreamId()); + } + + res = mCaptureRequest.update(ANDROID_REQUEST_OUTPUT_STREAMS, + outputStreams); + if (res == OK) { + res = mCaptureRequest.update(ANDROID_REQUEST_ID, + &mCaptureId, 1); + } + if (res == OK) { + res = mCaptureRequest.sort(); + } + + if (res != OK) { + ALOGE("%s: Camera %d: Unable to set up still capture request: %s (%d)", + __FUNCTION__, client->getCameraId(), strerror(-res), res); + return DONE; + } + + CameraMetadata captureCopy = mCaptureRequest; + if (captureCopy.entryCount() == 0) { + ALOGE("%s: Camera %d: Unable to copy capture request for HAL device", + __FUNCTION__, client->getCameraId()); + return DONE; + } + + if (l.mParameters.state == Parameters::STILL_CAPTURE) { + res = client->stopStream(); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to stop preview for still capture: " + "%s (%d)", + __FUNCTION__, client->getCameraId(), strerror(-res), res); + return DONE; + } + } + // TODO: Capture should be atomic with setStreamingRequest here + res = client->getCameraDevice()->capture(captureCopy); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to submit still image capture request: " + "%s (%d)", + __FUNCTION__, client->getCameraId(), strerror(-res), res); + return DONE; + } + + mTimeoutCount = kMaxTimeoutsForCaptureEnd; + return STANDARD_CAPTURE_WAIT; +} + +CaptureSequencer::CaptureState CaptureSequencer::manageStandardCaptureWait( + sp<Camera2Client> &client) { + status_t res; + ATRACE_CALL(); + Mutex::Autolock l(mInputMutex); + while (!mNewFrameReceived) { + res = mNewFrameSignal.waitRelative(mInputMutex, kWaitDuration); + if (res == TIMED_OUT) { + mTimeoutCount--; + break; + } + } + if (mNewFrameReceived && !mShutterNotified) { + SharedParameters::Lock l(client->getParameters()); + /* warning: this also locks a SharedCameraClient */ + shutterNotifyLocked(l.mParameters, client, mMsgType); + mShutterNotified = true; + } + while (mNewFrameReceived && !mNewCaptureReceived) { + res = mNewCaptureSignal.waitRelative(mInputMutex, kWaitDuration); + if (res == TIMED_OUT) { + mTimeoutCount--; + break; + } + } + if (mTimeoutCount <= 0) { + ALOGW("Timed out waiting for capture to complete"); + return DONE; + } + if (mNewFrameReceived && mNewCaptureReceived) { + if (mNewFrameId != mCaptureId) { + ALOGW("Mismatched capture frame IDs: Expected %d, got %d", + mCaptureId, mNewFrameId); + } + camera_metadata_entry_t entry; + 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 %lld," + " captured buffer %lld", entry.data.i64[0], mCaptureTimestamp); + } + client->removeFrameListener(mCaptureId, mCaptureId + 1, this); + + mNewFrameReceived = false; + mNewCaptureReceived = false; + return DONE; + } + return STANDARD_CAPTURE_WAIT; +} + +CaptureSequencer::CaptureState CaptureSequencer::manageBurstCaptureStart( + sp<Camera2Client> &client) { + ALOGV("%s", __FUNCTION__); + status_t res; + ATRACE_CALL(); + + // check which burst mode is set, create respective burst object + { + SharedParameters::Lock l(client->getParameters()); + + res = updateCaptureRequest(l.mParameters, client); + if(res != OK) { + return DONE; + } + + // + // check for burst mode type in mParameters here + // + mBurstCapture = new BurstCapture(client, this); + } + + res = mCaptureRequest.update(ANDROID_REQUEST_ID, &mCaptureId, 1); + if (res == OK) { + res = mCaptureRequest.sort(); + } + if (res != OK) { + ALOGE("%s: Camera %d: Unable to set up still capture request: %s (%d)", + __FUNCTION__, client->getCameraId(), strerror(-res), res); + return DONE; + } + + CameraMetadata captureCopy = mCaptureRequest; + if (captureCopy.entryCount() == 0) { + ALOGE("%s: Camera %d: Unable to copy capture request for HAL device", + __FUNCTION__, client->getCameraId()); + return DONE; + } + + Vector<CameraMetadata> requests; + requests.push(mCaptureRequest); + res = mBurstCapture->start(requests, mCaptureId); + mTimeoutCount = kMaxTimeoutsForCaptureEnd * 10; + return BURST_CAPTURE_WAIT; +} + +CaptureSequencer::CaptureState CaptureSequencer::manageBurstCaptureWait( + sp<Camera2Client> &client) { + status_t res; + ATRACE_CALL(); + + while (!mNewCaptureReceived) { + res = mNewCaptureSignal.waitRelative(mInputMutex, kWaitDuration); + if (res == TIMED_OUT) { + mTimeoutCount--; + break; + } + } + + if (mTimeoutCount <= 0) { + ALOGW("Timed out waiting for burst capture to complete"); + return DONE; + } + if (mNewCaptureReceived) { + mNewCaptureReceived = false; + // TODO: update mCaptureId to last burst's capture ID + 1? + return DONE; + } + + return BURST_CAPTURE_WAIT; +} + +status_t CaptureSequencer::updateCaptureRequest(const Parameters ¶ms, + sp<Camera2Client> &client) { + ATRACE_CALL(); + status_t res; + if (mCaptureRequest.entryCount() == 0) { + res = client->getCameraDevice()->createDefaultRequest( + CAMERA2_TEMPLATE_STILL_CAPTURE, + &mCaptureRequest); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to create default still image request:" + " %s (%d)", __FUNCTION__, client->getCameraId(), + strerror(-res), res); + return res; + } + } + + res = params.updateRequest(&mCaptureRequest); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update common entries of capture " + "request: %s (%d)", __FUNCTION__, client->getCameraId(), + strerror(-res), res); + return res; + } + + res = params.updateRequestJpeg(&mCaptureRequest); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update JPEG entries of capture " + "request: %s (%d)", __FUNCTION__, client->getCameraId(), + strerror(-res), res); + return res; + } + + return OK; +} + +/*static*/ void CaptureSequencer::shutterNotifyLocked(const Parameters ¶ms, + sp<Camera2Client> client, int msgType) { + ATRACE_CALL(); + + if (params.state == Parameters::STILL_CAPTURE + && params.playShutterSound + && (msgType & CAMERA_MSG_SHUTTER)) { + client->getCameraService()->playSound(CameraService::SOUND_SHUTTER); + } + + { + Camera2Client::SharedCameraClient::Lock l(client->mSharedCameraClient); + + ALOGV("%s: Notifying of shutter close to client", __FUNCTION__); + if (l.mCameraClient != 0) { + // ShutterCallback + l.mCameraClient->notifyCallback(CAMERA_MSG_SHUTTER, + /*ext1*/0, /*ext2*/0); + + // RawCallback with null buffer + l.mCameraClient->notifyCallback(CAMERA_MSG_RAW_IMAGE_NOTIFY, + /*ext1*/0, /*ext2*/0); + } else { + ALOGV("%s: No client!", __FUNCTION__); + } + } +} + + +}; // namespace camera2 +}; // namespace android diff --git a/services/camera/libcameraservice/camera2/CaptureSequencer.h b/services/camera/libcameraservice/camera2/CaptureSequencer.h new file mode 100644 index 00000000..c42df053 --- /dev/null +++ b/services/camera/libcameraservice/camera2/CaptureSequencer.h @@ -0,0 +1,177 @@ +/* + * 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_SERVERS_CAMERA_CAMERA2_CAPTURESEQUENCER_H +#define ANDROID_SERVERS_CAMERA_CAMERA2_CAPTURESEQUENCER_H + +#include <binder/MemoryBase.h> +#include <utils/Thread.h> +#include <utils/String16.h> +#include <utils/Vector.h> +#include <utils/Mutex.h> +#include <utils/Condition.h> +#include "CameraMetadata.h" +#include "Parameters.h" +#include "FrameProcessor.h" + +namespace android { + +class Camera2Client; + +namespace camera2 { + +class ZslProcessor; +class BurstCapture; + +/** + * Manages the still image capture process for + * zero-shutter-lag, regular, and video snapshots. + */ +class CaptureSequencer: + virtual public Thread, + virtual public FrameProcessor::FilteredListener { + public: + CaptureSequencer(wp<Camera2Client> client); + ~CaptureSequencer(); + + // Get reference to the ZslProcessor, which holds the ZSL buffers and frames + void setZslProcessor(wp<ZslProcessor> processor); + + // Begin still image capture + status_t startCapture(int msgType); + + // Wait until current image capture completes; returns immediately if no + // capture is active. Returns TIMED_OUT if capture does not complete during + // the specified duration. + status_t waitUntilIdle(nsecs_t timeout); + + // Notifications about AE state changes + void notifyAutoExposure(uint8_t newState, int triggerId); + + // Notifications from the frame processor + virtual void onFrameAvailable(int32_t frameId, const CameraMetadata &frame); + + // Notifications from the JPEG processor + void onCaptureAvailable(nsecs_t timestamp, sp<MemoryBase> captureBuffer); + + void dump(int fd, const Vector<String16>& args); + + private: + /** + * Accessed by other threads + */ + Mutex mInputMutex; + + bool mStartCapture; + bool mBusy; + Condition mStartCaptureSignal; + + bool mNewAEState; + uint8_t mAEState; + int mAETriggerId; + Condition mNewNotifySignal; + + bool mNewFrameReceived; + int32_t mNewFrameId; + CameraMetadata mNewFrame; + Condition mNewFrameSignal; + + bool mNewCaptureReceived; + nsecs_t mCaptureTimestamp; + sp<MemoryBase> mCaptureBuffer; + Condition mNewCaptureSignal; + + bool mShutterNotified; + + /** + * Internal to CaptureSequencer + */ + static const nsecs_t kWaitDuration = 100000000; // 100 ms + static const int kMaxTimeoutsForPrecaptureStart = 2; // 200 ms + static const int kMaxTimeoutsForPrecaptureEnd = 20; // 2 sec + static const int kMaxTimeoutsForCaptureEnd = 40; // 4 sec + + wp<Camera2Client> mClient; + wp<ZslProcessor> mZslProcessor; + sp<BurstCapture> mBurstCapture; + + enum CaptureState { + IDLE, + START, + ZSL_START, + ZSL_WAITING, + ZSL_REPROCESSING, + STANDARD_START, + STANDARD_PRECAPTURE_WAIT, + STANDARD_CAPTURE, + STANDARD_CAPTURE_WAIT, + BURST_CAPTURE_START, + BURST_CAPTURE_WAIT, + DONE, + ERROR, + NUM_CAPTURE_STATES + } mCaptureState; + static const char* kStateNames[]; + Mutex mStateMutex; // Guards mCaptureState + Condition mStateChanged; + + typedef CaptureState (CaptureSequencer::*StateManager)(sp<Camera2Client> &client); + static const StateManager kStateManagers[]; + + CameraMetadata mCaptureRequest; + + int mTriggerId; + int mTimeoutCount; + bool mAeInPrecapture; + + int32_t mCaptureId; + int mMsgType; + + // Main internal methods + + virtual bool threadLoop(); + + CaptureState manageIdle(sp<Camera2Client> &client); + CaptureState manageStart(sp<Camera2Client> &client); + + CaptureState manageZslStart(sp<Camera2Client> &client); + CaptureState manageZslWaiting(sp<Camera2Client> &client); + CaptureState manageZslReprocessing(sp<Camera2Client> &client); + + CaptureState manageStandardStart(sp<Camera2Client> &client); + CaptureState manageStandardPrecaptureWait(sp<Camera2Client> &client); + CaptureState manageStandardCapture(sp<Camera2Client> &client); + CaptureState manageStandardCaptureWait(sp<Camera2Client> &client); + + CaptureState manageBurstCaptureStart(sp<Camera2Client> &client); + CaptureState manageBurstCaptureWait(sp<Camera2Client> &client); + + CaptureState manageDone(sp<Camera2Client> &client); + + // Utility methods + + status_t updateCaptureRequest(const Parameters ¶ms, + sp<Camera2Client> &client); + + // Emit Shutter/Raw callback to java, and maybe play a shutter sound + static void shutterNotifyLocked(const Parameters ¶ms, + sp<Camera2Client> client, int msgType); +}; + +}; // namespace camera2 +}; // namespace android + +#endif diff --git a/services/camera/libcameraservice/camera2/FrameProcessor.cpp b/services/camera/libcameraservice/camera2/FrameProcessor.cpp new file mode 100644 index 00000000..76e84ccd --- /dev/null +++ b/services/camera/libcameraservice/camera2/FrameProcessor.cpp @@ -0,0 +1,308 @@ +/* + * 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. + */ + +#define LOG_TAG "Camera2-FrameProcessor" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/Trace.h> + +#include "FrameProcessor.h" +#include "../Camera2Device.h" +#include "../Camera2Client.h" + +namespace android { +namespace camera2 { + +FrameProcessor::FrameProcessor(wp<Camera2Client> client): + Thread(false), mClient(client), mLastFrameNumberOfFaces(0) { +} + +FrameProcessor::~FrameProcessor() { + ALOGV("%s: Exit", __FUNCTION__); +} + +status_t FrameProcessor::registerListener(int32_t minId, + int32_t maxId, wp<FilteredListener> listener) { + Mutex::Autolock l(mInputMutex); + ALOGV("%s: Registering listener for frame id range %d - %d", + __FUNCTION__, minId, maxId); + RangeListener rListener = { minId, maxId, listener }; + mRangeListeners.push_back(rListener); + return OK; +} + +status_t FrameProcessor::removeListener(int32_t minId, + int32_t maxId, wp<FilteredListener> listener) { + Mutex::Autolock l(mInputMutex); + List<RangeListener>::iterator item = mRangeListeners.begin(); + while (item != mRangeListeners.end()) { + if (item->minId == minId && + item->maxId == maxId && + item->listener == listener) { + item = mRangeListeners.erase(item); + } else { + item++; + } + } + return OK; +} + +void FrameProcessor::dump(int fd, const Vector<String16>& args) { + String8 result(" Latest received frame:\n"); + write(fd, result.string(), result.size()); + mLastFrame.dump(fd, 2, 6); +} + +bool FrameProcessor::threadLoop() { + status_t res; + + sp<Camera2Device> device; + { + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return false; + device = client->getCameraDevice(); + if (device == 0) return false; + } + + res = device->waitForNextFrame(kWaitDuration); + if (res == OK) { + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return false; + processNewFrames(client); + } else if (res != TIMED_OUT) { + ALOGE("Camera2Client::FrameProcessor: Error waiting for new " + "frames: %s (%d)", strerror(-res), res); + } + + return true; +} + +void FrameProcessor::processNewFrames(sp<Camera2Client> &client) { + status_t res; + ATRACE_CALL(); + CameraMetadata frame; + while ( (res = client->getCameraDevice()->getNextFrame(&frame)) == OK) { + camera_metadata_entry_t entry; + + entry = frame.find(ANDROID_REQUEST_FRAME_COUNT); + if (entry.count == 0) { + ALOGE("%s: Camera %d: Error reading frame number", + __FUNCTION__, client->getCameraId()); + break; + } + ATRACE_INT("cam2_frame", entry.data.i32[0]); + + res = processFaceDetect(frame, client); + if (res != OK) break; + + res = processListeners(frame, client); + if (res != OK) break; + + if (!frame.isEmpty()) { + mLastFrame.acquire(frame); + } + } + if (res != NOT_ENOUGH_DATA) { + ALOGE("%s: Camera %d: Error getting next frame: %s (%d)", + __FUNCTION__, client->getCameraId(), strerror(-res), res); + return; + } + + return; +} + +status_t FrameProcessor::processListeners(const CameraMetadata &frame, + sp<Camera2Client> &client) { + status_t res; + ATRACE_CALL(); + camera_metadata_ro_entry_t entry; + + entry = frame.find(ANDROID_REQUEST_ID); + if (entry.count == 0) { + ALOGE("%s: Camera %d: Error reading frame id", + __FUNCTION__, client->getCameraId()); + return BAD_VALUE; + } + int32_t frameId = entry.data.i32[0]; + + List<sp<FilteredListener> > listeners; + { + Mutex::Autolock l(mInputMutex); + + List<RangeListener>::iterator item = mRangeListeners.begin(); + while (item != mRangeListeners.end()) { + if (frameId >= item->minId && + frameId < item->maxId) { + sp<FilteredListener> listener = item->listener.promote(); + if (listener == 0) { + item = mRangeListeners.erase(item); + continue; + } else { + listeners.push_back(listener); + } + } + item++; + } + } + ALOGV("Got %d range listeners out of %d", listeners.size(), mRangeListeners.size()); + List<sp<FilteredListener> >::iterator item = listeners.begin(); + for (; item != listeners.end(); item++) { + (*item)->onFrameAvailable(frameId, frame); + } + return OK; +} + +status_t FrameProcessor::processFaceDetect(const CameraMetadata &frame, + sp<Camera2Client> &client) { + status_t res = BAD_VALUE; + ATRACE_CALL(); + camera_metadata_ro_entry_t entry; + bool enableFaceDetect; + int maxFaces; + { + SharedParameters::Lock l(client->getParameters()); + enableFaceDetect = l.mParameters.enableFaceDetect; + } + entry = frame.find(ANDROID_STATISTICS_FACE_DETECT_MODE); + + // TODO: This should be an error once implementations are compliant + if (entry.count == 0) { + return OK; + } + + uint8_t faceDetectMode = entry.data.u8[0]; + + camera_frame_metadata metadata; + Vector<camera_face_t> faces; + metadata.number_of_faces = 0; + + if (enableFaceDetect && faceDetectMode != ANDROID_STATISTICS_FACE_DETECT_MODE_OFF) { + SharedParameters::Lock l(client->getParameters()); + entry = frame.find(ANDROID_STATISTICS_FACE_RECTANGLES); + if (entry.count == 0) { + // No faces this frame + /* warning: locks SharedCameraClient */ + callbackFaceDetection(client, metadata); + return OK; + } + metadata.number_of_faces = entry.count / 4; + if (metadata.number_of_faces > + l.mParameters.fastInfo.maxFaces) { + ALOGE("%s: Camera %d: More faces than expected! (Got %d, max %d)", + __FUNCTION__, client->getCameraId(), + metadata.number_of_faces, l.mParameters.fastInfo.maxFaces); + return res; + } + const int32_t *faceRects = entry.data.i32; + + entry = frame.find(ANDROID_STATISTICS_FACE_SCORES); + if (entry.count == 0) { + ALOGE("%s: Camera %d: Unable to read face scores", + __FUNCTION__, client->getCameraId()); + return res; + } + const uint8_t *faceScores = entry.data.u8; + + const int32_t *faceLandmarks = NULL; + const int32_t *faceIds = NULL; + + if (faceDetectMode == ANDROID_STATISTICS_FACE_DETECT_MODE_FULL) { + entry = frame.find(ANDROID_STATISTICS_FACE_LANDMARKS); + if (entry.count == 0) { + ALOGE("%s: Camera %d: Unable to read face landmarks", + __FUNCTION__, client->getCameraId()); + return res; + } + faceLandmarks = entry.data.i32; + + entry = frame.find(ANDROID_STATISTICS_FACE_IDS); + + if (entry.count == 0) { + ALOGE("%s: Camera %d: Unable to read face IDs", + __FUNCTION__, client->getCameraId()); + return res; + } + faceIds = entry.data.i32; + } + + faces.setCapacity(metadata.number_of_faces); + + size_t maxFaces = metadata.number_of_faces; + for (size_t i = 0; i < maxFaces; i++) { + if (faceScores[i] == 0) { + metadata.number_of_faces--; + continue; + } + + camera_face_t face; + + face.rect[0] = l.mParameters.arrayXToNormalized(faceRects[i*4 + 0]); + face.rect[1] = l.mParameters.arrayYToNormalized(faceRects[i*4 + 1]); + face.rect[2] = l.mParameters.arrayXToNormalized(faceRects[i*4 + 2]); + face.rect[3] = l.mParameters.arrayYToNormalized(faceRects[i*4 + 3]); + + face.score = faceScores[i]; + if (faceDetectMode == ANDROID_STATISTICS_FACE_DETECT_MODE_FULL) { + face.id = faceIds[i]; + face.left_eye[0] = + l.mParameters.arrayXToNormalized(faceLandmarks[i*6 + 0]); + face.left_eye[1] = + l.mParameters.arrayYToNormalized(faceLandmarks[i*6 + 1]); + face.right_eye[0] = + l.mParameters.arrayXToNormalized(faceLandmarks[i*6 + 2]); + face.right_eye[1] = + l.mParameters.arrayYToNormalized(faceLandmarks[i*6 + 3]); + face.mouth[0] = + l.mParameters.arrayXToNormalized(faceLandmarks[i*6 + 4]); + face.mouth[1] = + l.mParameters.arrayYToNormalized(faceLandmarks[i*6 + 5]); + } else { + face.id = 0; + face.left_eye[0] = face.left_eye[1] = -2000; + face.right_eye[0] = face.right_eye[1] = -2000; + face.mouth[0] = face.mouth[1] = -2000; + } + faces.push_back(face); + } + + metadata.faces = faces.editArray(); + } + + /* warning: locks SharedCameraClient */ + callbackFaceDetection(client, metadata); + + return OK; +} + +void FrameProcessor::callbackFaceDetection(sp<Camera2Client> client, + /*in*/camera_frame_metadata &metadata) { + + /* Filter out repeated 0-face callbacks, but not when the last frame was >0 */ + if (metadata.number_of_faces != 0 || mLastFrameNumberOfFaces != metadata.number_of_faces) { + Camera2Client::SharedCameraClient::Lock l(client->mSharedCameraClient); + if (l.mCameraClient != NULL) { + l.mCameraClient->dataCallback(CAMERA_MSG_PREVIEW_METADATA, + NULL, &metadata); + } + } + + mLastFrameNumberOfFaces = metadata.number_of_faces; +} + +}; // namespace camera2 +}; // namespace android diff --git a/services/camera/libcameraservice/camera2/FrameProcessor.h b/services/camera/libcameraservice/camera2/FrameProcessor.h new file mode 100644 index 00000000..3bd4e257 --- /dev/null +++ b/services/camera/libcameraservice/camera2/FrameProcessor.h @@ -0,0 +1,89 @@ +/* + * 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_SERVERS_CAMERA_CAMERA2_FRAMEPROCESSOR_H +#define ANDROID_SERVERS_CAMERA_CAMERA2_FRAMEPROCESSOR_H + +#include <utils/Thread.h> +#include <utils/String16.h> +#include <utils/Vector.h> +#include <utils/KeyedVector.h> +#include <utils/List.h> +#include "CameraMetadata.h" + +struct camera_frame_metadata; + +namespace android { + +class Camera2Client; + +namespace camera2 { + +/* Output frame metadata processing thread. This thread waits for new + * frames from the device, and analyzes them as necessary. + */ +class FrameProcessor: public Thread { + public: + FrameProcessor(wp<Camera2Client> client); + ~FrameProcessor(); + + struct FilteredListener: virtual public RefBase { + virtual void onFrameAvailable(int32_t frameId, + const CameraMetadata &frame) = 0; + }; + + // Register a listener for a range of IDs [minId, maxId). Multiple listeners + // can be listening to the same range + status_t registerListener(int32_t minId, int32_t maxId, wp<FilteredListener> listener); + status_t removeListener(int32_t minId, int32_t maxId, wp<FilteredListener> listener); + + void dump(int fd, const Vector<String16>& args); + private: + static const nsecs_t kWaitDuration = 10000000; // 10 ms + wp<Camera2Client> mClient; + + virtual bool threadLoop(); + + Mutex mInputMutex; + + struct RangeListener { + int32_t minId; + int32_t maxId; + wp<FilteredListener> listener; + }; + List<RangeListener> mRangeListeners; + + void processNewFrames(sp<Camera2Client> &client); + + status_t processFaceDetect(const CameraMetadata &frame, + sp<Camera2Client> &client); + + status_t processListeners(const CameraMetadata &frame, + sp<Camera2Client> &client); + + CameraMetadata mLastFrame; + int mLastFrameNumberOfFaces; + + // Emit FaceDetection event to java if faces changed + void callbackFaceDetection(sp<Camera2Client> client, + camera_frame_metadata &metadata); +}; + + +}; //namespace camera2 +}; //namespace android + +#endif diff --git a/services/camera/libcameraservice/camera2/JpegCompressor.cpp b/services/camera/libcameraservice/camera2/JpegCompressor.cpp new file mode 100644 index 00000000..702ef58b --- /dev/null +++ b/services/camera/libcameraservice/camera2/JpegCompressor.cpp @@ -0,0 +1,220 @@ +/* + * 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "Camera2-JpegCompressor" + +#include <utils/Log.h> +#include <ui/GraphicBufferMapper.h> + +#include "JpegCompressor.h" + +namespace android { +namespace camera2 { + +JpegCompressor::JpegCompressor(): + Thread(false), + mIsBusy(false), + mCaptureTime(0) { +} + +JpegCompressor::~JpegCompressor() { + ALOGV("%s", __FUNCTION__); + Mutex::Autolock lock(mMutex); +} + +status_t JpegCompressor::start(Vector<CpuConsumer::LockedBuffer*> buffers, + nsecs_t captureTime) { + ALOGV("%s", __FUNCTION__); + Mutex::Autolock busyLock(mBusyMutex); + + if (mIsBusy) { + ALOGE("%s: Already processing a buffer!", __FUNCTION__); + return INVALID_OPERATION; + } + + mIsBusy = true; + + mBuffers = buffers; + mCaptureTime = captureTime; + + status_t res; + res = run("JpegCompressor"); + if (res != OK) { + ALOGE("%s: Unable to start up compression thread: %s (%d)", + __FUNCTION__, strerror(-res), res); + //delete mBuffers; // necessary? + } + return res; +} + +status_t JpegCompressor::cancel() { + ALOGV("%s", __FUNCTION__); + requestExitAndWait(); + return OK; +} + +status_t JpegCompressor::readyToRun() { + ALOGV("%s", __FUNCTION__); + return OK; +} + +bool JpegCompressor::threadLoop() { + ALOGV("%s", __FUNCTION__); + + mAuxBuffer = mBuffers[0]; // input + mJpegBuffer = mBuffers[1]; // output + + // Set up error management + mJpegErrorInfo = NULL; + JpegError error; + error.parent = this; + + mCInfo.err = jpeg_std_error(&error); + mCInfo.err->error_exit = jpegErrorHandler; + + jpeg_create_compress(&mCInfo); + if (checkError("Error initializing compression")) return false; + + // Route compressed data straight to output stream buffer + JpegDestination jpegDestMgr; + jpegDestMgr.parent = this; + jpegDestMgr.init_destination = jpegInitDestination; + jpegDestMgr.empty_output_buffer = jpegEmptyOutputBuffer; + jpegDestMgr.term_destination = jpegTermDestination; + + mCInfo.dest = &jpegDestMgr; + + // Set up compression parameters + mCInfo.image_width = mAuxBuffer->width; + mCInfo.image_height = mAuxBuffer->height; + mCInfo.input_components = 1; // 3; + mCInfo.in_color_space = JCS_GRAYSCALE; // JCS_RGB + + ALOGV("%s: image_width = %d, image_height = %d", __FUNCTION__, mCInfo.image_width, mCInfo.image_height); + + jpeg_set_defaults(&mCInfo); + if (checkError("Error configuring defaults")) return false; + + // Do compression + jpeg_start_compress(&mCInfo, TRUE); + if (checkError("Error starting compression")) return false; + + size_t rowStride = mAuxBuffer->stride;// * 3; + const size_t kChunkSize = 32; + while (mCInfo.next_scanline < mCInfo.image_height) { + JSAMPROW chunk[kChunkSize]; + for (size_t i = 0 ; i < kChunkSize; i++) { + chunk[i] = (JSAMPROW) + (mAuxBuffer->data + (i + mCInfo.next_scanline) * rowStride); + } + jpeg_write_scanlines(&mCInfo, chunk, kChunkSize); + if (checkError("Error while compressing")) return false; + if (exitPending()) { + ALOGV("%s: Cancel called, exiting early", __FUNCTION__); + cleanUp(); + return false; + } + } + + jpeg_finish_compress(&mCInfo); + if (checkError("Error while finishing compression")) return false; + + cleanUp(); + return false; +} + +bool JpegCompressor::isBusy() { + ALOGV("%s", __FUNCTION__); + Mutex::Autolock busyLock(mBusyMutex); + return mIsBusy; +} + +// old function -- TODO: update for new buffer type +bool JpegCompressor::isStreamInUse(uint32_t id) { + ALOGV("%s", __FUNCTION__); + Mutex::Autolock lock(mBusyMutex); + + if (mBuffers.size() && mIsBusy) { + for (size_t i = 0; i < mBuffers.size(); i++) { +// if ( mBuffers[i].streamId == (int)id ) return true; + } + } + return false; +} + +bool JpegCompressor::waitForDone(nsecs_t timeout) { + ALOGV("%s", __FUNCTION__); + Mutex::Autolock lock(mBusyMutex); + status_t res = OK; + if (mIsBusy) { + res = mDone.waitRelative(mBusyMutex, timeout); + } + return (res == OK); +} + +bool JpegCompressor::checkError(const char *msg) { + ALOGV("%s", __FUNCTION__); + if (mJpegErrorInfo) { + char errBuffer[JMSG_LENGTH_MAX]; + mJpegErrorInfo->err->format_message(mJpegErrorInfo, errBuffer); + ALOGE("%s: %s: %s", + __FUNCTION__, msg, errBuffer); + cleanUp(); + mJpegErrorInfo = NULL; + return true; + } + return false; +} + +void JpegCompressor::cleanUp() { + ALOGV("%s", __FUNCTION__); + jpeg_destroy_compress(&mCInfo); + Mutex::Autolock lock(mBusyMutex); + mIsBusy = false; + mDone.signal(); +} + +void JpegCompressor::jpegErrorHandler(j_common_ptr cinfo) { + ALOGV("%s", __FUNCTION__); + JpegError *error = static_cast<JpegError*>(cinfo->err); + error->parent->mJpegErrorInfo = cinfo; +} + +void JpegCompressor::jpegInitDestination(j_compress_ptr cinfo) { + ALOGV("%s", __FUNCTION__); + JpegDestination *dest= static_cast<JpegDestination*>(cinfo->dest); + ALOGV("%s: Setting destination to %p, size %d", + __FUNCTION__, dest->parent->mJpegBuffer->data, kMaxJpegSize); + dest->next_output_byte = (JOCTET*)(dest->parent->mJpegBuffer->data); + dest->free_in_buffer = kMaxJpegSize; +} + +boolean JpegCompressor::jpegEmptyOutputBuffer(j_compress_ptr cinfo) { + ALOGV("%s", __FUNCTION__); + ALOGE("%s: JPEG destination buffer overflow!", + __FUNCTION__); + return true; +} + +void JpegCompressor::jpegTermDestination(j_compress_ptr cinfo) { + ALOGV("%s", __FUNCTION__); + ALOGV("%s: Done writing JPEG data. %d bytes left in buffer", + __FUNCTION__, cinfo->dest->free_in_buffer); +} + +}; // namespace camera2 +}; // namespace android diff --git a/services/camera/libcameraservice/camera2/JpegCompressor.h b/services/camera/libcameraservice/camera2/JpegCompressor.h new file mode 100644 index 00000000..945b1def --- /dev/null +++ b/services/camera/libcameraservice/camera2/JpegCompressor.h @@ -0,0 +1,107 @@ +/* + * 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. + */ + + +/** + * This class simulates a hardware JPEG compressor. It receives image buffers + * in RGBA_8888 format, processes them in a worker thread, and then pushes them + * out to their destination stream. + */ + +#ifndef ANDROID_SERVERS_CAMERA_JPEGCOMPRESSOR_H +#define ANDROID_SERVERS_CAMERA_JPEGCOMPRESSOR_H + +#include "utils/Thread.h" +#include "utils/Mutex.h" +#include "utils/Timers.h" +#include "utils/Vector.h" +//#include "Base.h" +#include <stdio.h> +#include <gui/CpuConsumer.h> + +extern "C" { +#include <jpeglib.h> +} + + +namespace android { +namespace camera2 { + +class JpegCompressor: private Thread, public virtual RefBase { + public: + + JpegCompressor(); + ~JpegCompressor(); + + // Start compressing COMPRESSED format buffers; JpegCompressor takes + // ownership of the Buffers vector. + status_t start(Vector<CpuConsumer::LockedBuffer*> buffers, + nsecs_t captureTime); + + status_t cancel(); + + bool isBusy(); + bool isStreamInUse(uint32_t id); + + bool waitForDone(nsecs_t timeout); + + // TODO: Measure this + static const size_t kMaxJpegSize = 300000; + + private: + Mutex mBusyMutex; + Mutex mMutex; + bool mIsBusy; + Condition mDone; + nsecs_t mCaptureTime; + + Vector<CpuConsumer::LockedBuffer*> mBuffers; + CpuConsumer::LockedBuffer *mJpegBuffer; + CpuConsumer::LockedBuffer *mAuxBuffer; + bool mFoundJpeg, mFoundAux; + + jpeg_compress_struct mCInfo; + + struct JpegError : public jpeg_error_mgr { + JpegCompressor *parent; + }; + j_common_ptr mJpegErrorInfo; + + struct JpegDestination : public jpeg_destination_mgr { + JpegCompressor *parent; + }; + + static void jpegErrorHandler(j_common_ptr cinfo); + + static void jpegInitDestination(j_compress_ptr cinfo); + static boolean jpegEmptyOutputBuffer(j_compress_ptr cinfo); + static void jpegTermDestination(j_compress_ptr cinfo); + + bool checkError(const char *msg); + void cleanUp(); + + /** + * Inherited Thread virtual overrides + */ + private: + virtual status_t readyToRun(); + virtual bool threadLoop(); +}; + +}; // namespace camera2 +}; // namespace android + +#endif diff --git a/services/camera/libcameraservice/camera2/JpegProcessor.cpp b/services/camera/libcameraservice/camera2/JpegProcessor.cpp new file mode 100644 index 00000000..ffc072ba --- /dev/null +++ b/services/camera/libcameraservice/camera2/JpegProcessor.cpp @@ -0,0 +1,380 @@ +/* + * 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. + */ + +#define LOG_TAG "Camera2-JpegProcessor" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +#include <netinet/in.h> + +#include <binder/MemoryBase.h> +#include <binder/MemoryHeapBase.h> +#include <utils/Log.h> +#include <utils/Trace.h> + +#include "JpegProcessor.h" +#include <gui/SurfaceTextureClient.h> +#include "../Camera2Device.h" +#include "../Camera2Client.h" + + +namespace android { +namespace camera2 { + +JpegProcessor::JpegProcessor( + wp<Camera2Client> client, + wp<CaptureSequencer> sequencer): + Thread(false), + mClient(client), + mSequencer(sequencer), + mCaptureAvailable(false), + mCaptureStreamId(NO_STREAM) { +} + +JpegProcessor::~JpegProcessor() { + ALOGV("%s: Exit", __FUNCTION__); + deleteStream(); +} + +void JpegProcessor::onFrameAvailable() { + Mutex::Autolock l(mInputMutex); + if (!mCaptureAvailable) { + mCaptureAvailable = true; + mCaptureAvailableSignal.signal(); + } +} + +status_t JpegProcessor::updateStream(const Parameters ¶ms) { + ATRACE_CALL(); + ALOGV("%s", __FUNCTION__); + status_t res; + + Mutex::Autolock l(mInputMutex); + + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return OK; + sp<Camera2Device> device = client->getCameraDevice(); + + // 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__, client->getCameraId()); + return INVALID_OPERATION; + } + + if (mCaptureConsumer == 0) { + // Create CPU buffer queue endpoint + mCaptureConsumer = new CpuConsumer(1); + mCaptureConsumer->setFrameAvailableListener(this); + mCaptureConsumer->setName(String8("Camera2Client::CaptureConsumer")); + mCaptureWindow = new SurfaceTextureClient( + mCaptureConsumer->getProducerInterface()); + // Create memory for API consumption + mCaptureHeap = new MemoryHeapBase(maxJpegSize.data.i32[0], 0, + "Camera2Client::CaptureHeap"); + if (mCaptureHeap->getSize() == 0) { + ALOGE("%s: Camera %d: Unable to allocate memory for capture", + __FUNCTION__, client->getCameraId()); + return NO_MEMORY; + } + } + + if (mCaptureStreamId != NO_STREAM) { + // Check if stream parameters have to change + uint32_t currentWidth, currentHeight; + res = device->getStreamInfo(mCaptureStreamId, + ¤tWidth, ¤tHeight, 0); + if (res != OK) { + ALOGE("%s: Camera %d: Error querying capture output stream info: " + "%s (%d)", __FUNCTION__, + client->getCameraId(), strerror(-res), res); + return res; + } + if (currentWidth != (uint32_t)params.pictureWidth || + currentHeight != (uint32_t)params.pictureHeight) { + ALOGV("%s: Camera %d: Deleting stream %d since the buffer dimensions changed", + __FUNCTION__, client->getCameraId(), mCaptureStreamId); + res = device->deleteStream(mCaptureStreamId); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to delete old output stream " + "for capture: %s (%d)", __FUNCTION__, + client->getCameraId(), strerror(-res), res); + return res; + } + mCaptureStreamId = NO_STREAM; + } + } + + if (mCaptureStreamId == NO_STREAM) { + // Create stream for HAL production + res = device->createStream(mCaptureWindow, + params.pictureWidth, params.pictureHeight, + HAL_PIXEL_FORMAT_BLOB, maxJpegSize.data.i32[0], + &mCaptureStreamId); + if (res != OK) { + ALOGE("%s: Camera %d: Can't create output stream for capture: " + "%s (%d)", __FUNCTION__, client->getCameraId(), + strerror(-res), res); + return res; + } + + } + return OK; +} + +status_t JpegProcessor::deleteStream() { + ATRACE_CALL(); + status_t res; + + Mutex::Autolock l(mInputMutex); + + if (mCaptureStreamId != NO_STREAM) { + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return OK; + sp<Camera2Device> device = client->getCameraDevice(); + + device->deleteStream(mCaptureStreamId); + + mCaptureHeap.clear(); + mCaptureWindow.clear(); + mCaptureConsumer.clear(); + + mCaptureStreamId = NO_STREAM; + } + return OK; +} + +int JpegProcessor::getStreamId() const { + Mutex::Autolock l(mInputMutex); + return mCaptureStreamId; +} + +void JpegProcessor::dump(int fd, const Vector<String16>& args) const { +} + +bool JpegProcessor::threadLoop() { + status_t res; + + { + Mutex::Autolock l(mInputMutex); + while (!mCaptureAvailable) { + res = mCaptureAvailableSignal.waitRelative(mInputMutex, + kWaitDuration); + if (res == TIMED_OUT) return true; + } + mCaptureAvailable = false; + } + + do { + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return false; + res = processNewCapture(client); + } while (res == OK); + + return true; +} + +status_t JpegProcessor::processNewCapture(sp<Camera2Client> &client) { + ATRACE_CALL(); + status_t res; + sp<Camera2Heap> captureHeap; + + CpuConsumer::LockedBuffer imgBuffer; + + res = mCaptureConsumer->lockNextBuffer(&imgBuffer); + if (res != OK) { + if (res != BAD_VALUE) { + ALOGE("%s: Camera %d: Error receiving still image buffer: " + "%s (%d)", __FUNCTION__, + client->getCameraId(), strerror(-res), res); + } + return res; + } + + ALOGV("%s: Camera %d: Still capture available", __FUNCTION__, + client->getCameraId()); + + if (imgBuffer.format != HAL_PIXEL_FORMAT_BLOB) { + ALOGE("%s: Camera %d: Unexpected format for still image: " + "%x, expected %x", __FUNCTION__, client->getCameraId(), + imgBuffer.format, + HAL_PIXEL_FORMAT_BLOB); + mCaptureConsumer->unlockBuffer(imgBuffer); + return OK; + } + + // Find size of JPEG image + size_t jpegSize = findJpegSize(imgBuffer.data, imgBuffer.width); + if (jpegSize == 0) { // failed to find size, default to whole buffer + jpegSize = imgBuffer.width; + } + size_t heapSize = mCaptureHeap->getSize(); + if (jpegSize > heapSize) { + ALOGW("%s: JPEG image is larger than expected, truncating " + "(got %d, expected at most %d bytes)", + __FUNCTION__, jpegSize, heapSize); + jpegSize = heapSize; + } + + // TODO: Optimize this to avoid memcopy + sp<MemoryBase> captureBuffer = new MemoryBase(mCaptureHeap, 0, jpegSize); + void* captureMemory = mCaptureHeap->getBase(); + memcpy(captureMemory, imgBuffer.data, jpegSize); + + mCaptureConsumer->unlockBuffer(imgBuffer); + + sp<CaptureSequencer> sequencer = mSequencer.promote(); + if (sequencer != 0) { + sequencer->onCaptureAvailable(imgBuffer.timestamp, captureBuffer); + } + + return OK; +} + +/* + * JPEG FILE FORMAT OVERVIEW. + * http://www.jpeg.org/public/jfif.pdf + * (JPEG is the image compression algorithm, actual file format is called JFIF) + * + * "Markers" are 2-byte patterns used to distinguish parts of JFIF files. The + * first byte is always 0xFF, and the second byte is between 0x01 and 0xFE + * (inclusive). Because every marker begins with the same byte, they are + * referred to by the second byte's value. + * + * JFIF files all begin with the Start of Image (SOI) marker, which is 0xD8. + * Following it, "segment" sections begin with other markers, followed by a + * 2-byte length (in network byte order), then the segment data. + * + * For our purposes we will ignore the data, and just use the length to skip to + * the next segment. This is necessary because the data inside segments are + * allowed to contain the End of Image marker (0xFF 0xD9), preventing us from + * naievely scanning until the end. + * + * After all the segments are processed, the jpeg compressed image stream begins. + * This can be considered an opaque format with one requirement: all 0xFF bytes + * in this stream must be followed with a 0x00 byte. This prevents any of the + * image data to be interpreted as a segment. The only exception to this is at + * the end of the image stream there is an End of Image (EOI) marker, which is + * 0xFF followed by a non-zero (0xD9) byte. + */ + +const uint8_t MARK = 0xFF; // First byte of marker +const uint8_t SOI = 0xD8; // Start of Image +const uint8_t EOI = 0xD9; // End of Image +const size_t MARKER_LENGTH = 2; // length of a marker + +#pragma pack(push) +#pragma pack(1) +typedef struct segment { + uint8_t marker[MARKER_LENGTH]; + uint16_t length; +} segment_t; +#pragma pack(pop) + +/* HELPER FUNCTIONS */ + +// check for Start of Image marker +bool checkJpegStart(uint8_t* buf) { + return buf[0] == MARK && buf[1] == SOI; +} +// check for End of Image marker +bool checkJpegEnd(uint8_t *buf) { + return buf[0] == MARK && buf[1] == EOI; +} +// check for arbitrary marker, returns marker type (second byte) +// returns 0 if no marker found. Note: 0x00 is not a valid marker type +uint8_t checkJpegMarker(uint8_t *buf) { + if (buf[0] == MARK && buf[1] > 0 && buf[1] < 0xFF) { + return buf[1]; + } + return 0; +} + +// Return the size of the JPEG, 0 indicates failure +size_t JpegProcessor::findJpegSize(uint8_t* jpegBuffer, size_t maxSize) { + size_t size; + + // First check for JPEG transport header at the end of the buffer + uint8_t *header = jpegBuffer + (maxSize - sizeof(struct camera2_jpeg_blob)); + struct camera2_jpeg_blob *blob = (struct camera2_jpeg_blob*)(header); + if (blob->jpeg_blob_id == CAMERA2_JPEG_BLOB_ID) { + size = blob->jpeg_size; + if (size > 0 && size <= maxSize - sizeof(struct camera2_jpeg_blob)) { + // Verify SOI and EOI markers + size_t offset = size - MARKER_LENGTH; + uint8_t *end = jpegBuffer + offset; + if (checkJpegStart(jpegBuffer) && checkJpegEnd(end)) { + ALOGV("Found JPEG transport header, img size %d", size); + return size; + } else { + ALOGW("Found JPEG transport header with bad Image Start/End"); + } + } else { + ALOGW("Found JPEG transport header with bad size %d", size); + } + } + + // Check Start of Image + if ( !checkJpegStart(jpegBuffer) ) { + ALOGE("Could not find start of JPEG marker"); + return 0; + } + + // Read JFIF segment markers, skip over segment data + size = 0; + while (size <= maxSize - MARKER_LENGTH) { + segment_t *segment = (segment_t*)(jpegBuffer + size); + uint8_t type = checkJpegMarker(segment->marker); + if (type == 0) { // invalid marker, no more segments, begin JPEG data + ALOGV("JPEG stream found beginning at offset %d", size); + break; + } + if (type == EOI || size > maxSize - sizeof(segment_t)) { + ALOGE("Got premature End before JPEG data, offset %d", size); + return 0; + } + size_t length = ntohs(segment->length); + ALOGV("JFIF Segment, type %x length %x", type, length); + size += length + MARKER_LENGTH; + } + + // Find End of Image + // Scan JPEG buffer until End of Image (EOI) + bool foundEnd = false; + for (size; size <= maxSize - MARKER_LENGTH; size++) { + if ( checkJpegEnd(jpegBuffer + size) ) { + foundEnd = true; + size += MARKER_LENGTH; + break; + } + } + if (!foundEnd) { + ALOGE("Could not find end of JPEG marker"); + return 0; + } + + if (size > maxSize) { + ALOGW("JPEG size %d too large, reducing to maxSize %d", size, maxSize); + size = maxSize; + } + ALOGV("Final JPEG size %d", size); + return size; +} + +}; // namespace camera2 +}; // namespace android diff --git a/services/camera/libcameraservice/camera2/JpegProcessor.h b/services/camera/libcameraservice/camera2/JpegProcessor.h new file mode 100644 index 00000000..836bd02b --- /dev/null +++ b/services/camera/libcameraservice/camera2/JpegProcessor.h @@ -0,0 +1,83 @@ +/* + * 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_SERVERS_CAMERA_CAMERA2_JPEGPROCESSOR_H +#define ANDROID_SERVERS_CAMERA_CAMERA2_JPEGPROCESSOR_H + +#include <utils/Thread.h> +#include <utils/String16.h> +#include <utils/Vector.h> +#include <utils/Mutex.h> +#include <utils/Condition.h> +#include <gui/CpuConsumer.h> +#include "Parameters.h" +#include "CameraMetadata.h" + +namespace android { + +class Camera2Client; +class MemoryHeapBase; + +namespace camera2 { + +class CaptureSequencer; + +/*** + * Still image capture output image processing + */ +class JpegProcessor: + public Thread, public CpuConsumer::FrameAvailableListener { + public: + JpegProcessor(wp<Camera2Client> client, wp<CaptureSequencer> sequencer); + ~JpegProcessor(); + + void onFrameAvailable(); + + status_t updateStream(const Parameters ¶ms); + status_t deleteStream(); + int getStreamId() const; + + void dump(int fd, const Vector<String16>& args) const; + private: + static const nsecs_t kWaitDuration = 10000000; // 10 ms + wp<Camera2Client> mClient; + wp<CaptureSequencer> mSequencer; + + mutable Mutex mInputMutex; + bool mCaptureAvailable; + Condition mCaptureAvailableSignal; + + enum { + NO_STREAM = -1 + }; + + int mCaptureStreamId; + sp<CpuConsumer> mCaptureConsumer; + sp<ANativeWindow> mCaptureWindow; + sp<MemoryHeapBase> mCaptureHeap; + + virtual bool threadLoop(); + + status_t processNewCapture(sp<Camera2Client> &client); + size_t findJpegSize(uint8_t* jpegBuffer, size_t maxSize); + +}; + + +}; //namespace camera2 +}; //namespace android + +#endif diff --git a/services/camera/libcameraservice/camera2/Parameters.cpp b/services/camera/libcameraservice/camera2/Parameters.cpp new file mode 100644 index 00000000..278a19cb --- /dev/null +++ b/services/camera/libcameraservice/camera2/Parameters.cpp @@ -0,0 +1,2431 @@ +/* + * 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. + */ + +#define LOG_TAG "Camera2-Parameters" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/Trace.h> +#include <utils/Vector.h> +#include <utils/SortedVector.h> + +#include <math.h> +#include <stdlib.h> +#include <cutils/properties.h> + +#include "Parameters.h" +#include "system/camera.h" + +namespace android { +namespace camera2 { + +Parameters::Parameters(int cameraId, + int cameraFacing) : + cameraId(cameraId), + cameraFacing(cameraFacing), + info(NULL) { +} + +Parameters::~Parameters() { +} + +status_t Parameters::initialize(const CameraMetadata *info) { + status_t res; + + if (info->entryCount() == 0) { + ALOGE("%s: No static information provided!", __FUNCTION__); + return BAD_VALUE; + } + Parameters::info = info; + + res = buildFastInfo(); + if (res != OK) return res; + + res = buildQuirks(); + if (res != OK) return res; + + camera_metadata_ro_entry_t availableProcessedSizes = + staticInfo(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES, 2); + if (!availableProcessedSizes.count) return NO_INIT; + + // TODO: Pick more intelligently + previewWidth = availableProcessedSizes.data.i32[0]; + previewHeight = availableProcessedSizes.data.i32[1]; + videoWidth = previewWidth; + videoHeight = previewHeight; + + params.setPreviewSize(previewWidth, previewHeight); + params.setVideoSize(videoWidth, videoHeight); + params.set(CameraParameters::KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO, + String8::format("%dx%d", + previewWidth, previewHeight)); + { + String8 supportedPreviewSizes; + for (size_t i=0; i < availableProcessedSizes.count; i += 2) { + if (i != 0) supportedPreviewSizes += ","; + supportedPreviewSizes += String8::format("%dx%d", + availableProcessedSizes.data.i32[i], + availableProcessedSizes.data.i32[i+1]); + } + params.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES, + supportedPreviewSizes); + params.set(CameraParameters::KEY_SUPPORTED_VIDEO_SIZES, + supportedPreviewSizes); + } + + camera_metadata_ro_entry_t availableFpsRanges = + staticInfo(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, 2); + if (!availableFpsRanges.count) return NO_INIT; + + previewFpsRange[0] = availableFpsRanges.data.i32[0]; + previewFpsRange[1] = availableFpsRanges.data.i32[1]; + + params.set(CameraParameters::KEY_PREVIEW_FPS_RANGE, + String8::format("%d,%d", + previewFpsRange[0] * kFpsToApiScale, + previewFpsRange[1] * kFpsToApiScale)); + + { + String8 supportedPreviewFpsRange; + for (size_t i=0; i < availableFpsRanges.count; i += 2) { + if (i != 0) supportedPreviewFpsRange += ","; + supportedPreviewFpsRange += String8::format("(%d,%d)", + availableFpsRanges.data.i32[i] * kFpsToApiScale, + availableFpsRanges.data.i32[i+1] * kFpsToApiScale); + } + params.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE, + supportedPreviewFpsRange); + } + + previewFormat = HAL_PIXEL_FORMAT_YCrCb_420_SP; + params.set(CameraParameters::KEY_PREVIEW_FORMAT, + formatEnumToString(previewFormat)); // NV21 + + previewTransform = degToTransform(0, + cameraFacing == CAMERA_FACING_FRONT); + + camera_metadata_ro_entry_t availableFormats = + staticInfo(ANDROID_SCALER_AVAILABLE_FORMATS); + + { + String8 supportedPreviewFormats; + bool addComma = false; + for (size_t i=0; i < availableFormats.count; i++) { + if (addComma) supportedPreviewFormats += ","; + addComma = true; + switch (availableFormats.data.i32[i]) { + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + supportedPreviewFormats += + CameraParameters::PIXEL_FORMAT_YUV422SP; + break; + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + supportedPreviewFormats += + CameraParameters::PIXEL_FORMAT_YUV420SP; + break; + case HAL_PIXEL_FORMAT_YCbCr_422_I: + supportedPreviewFormats += + CameraParameters::PIXEL_FORMAT_YUV422I; + break; + case HAL_PIXEL_FORMAT_YV12: + supportedPreviewFormats += + CameraParameters::PIXEL_FORMAT_YUV420P; + break; + case HAL_PIXEL_FORMAT_RGB_565: + supportedPreviewFormats += + CameraParameters::PIXEL_FORMAT_RGB565; + break; + case HAL_PIXEL_FORMAT_RGBA_8888: + supportedPreviewFormats += + CameraParameters::PIXEL_FORMAT_RGBA8888; + break; + // Not advertizing JPEG, RAW_SENSOR, etc, for preview formats + case HAL_PIXEL_FORMAT_RAW_SENSOR: + case HAL_PIXEL_FORMAT_BLOB: + addComma = false; + break; + + default: + ALOGW("%s: Camera %d: Unknown preview format: %x", + __FUNCTION__, cameraId, availableFormats.data.i32[i]); + addComma = false; + break; + } + } + params.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS, + supportedPreviewFormats); + } + + // PREVIEW_FRAME_RATE / SUPPORTED_PREVIEW_FRAME_RATES are deprecated, but + // still have to do something sane for them + + // NOTE: Not scaled like FPS range values are. + previewFps = fpsFromRange(previewFpsRange[0], previewFpsRange[1]); + params.set(CameraParameters::KEY_PREVIEW_FRAME_RATE, + previewFps); + + { + SortedVector<int32_t> sortedPreviewFrameRates; + + String8 supportedPreviewFrameRates; + for (size_t i=0; i < availableFpsRanges.count; i += 2) { + // from the [min, max] fps range use the max value + int fps = fpsFromRange(availableFpsRanges.data.i32[i], + availableFpsRanges.data.i32[i+1]); + + // de-dupe frame rates + if (sortedPreviewFrameRates.indexOf(fps) == NAME_NOT_FOUND) { + sortedPreviewFrameRates.add(fps); + } + else { + continue; + } + + if (sortedPreviewFrameRates.size() > 1) { + supportedPreviewFrameRates += ","; + } + + supportedPreviewFrameRates += String8::format("%d", + fps); + + ALOGV("%s: Supported preview frame rates: %s", + __FUNCTION__, supportedPreviewFrameRates.string()); + } + params.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES, + supportedPreviewFrameRates); + } + + camera_metadata_ro_entry_t availableJpegSizes = + staticInfo(ANDROID_SCALER_AVAILABLE_JPEG_SIZES, 2); + if (!availableJpegSizes.count) return NO_INIT; + + // TODO: Pick maximum + pictureWidth = availableJpegSizes.data.i32[0]; + pictureHeight = availableJpegSizes.data.i32[1]; + + params.setPictureSize(pictureWidth, + pictureHeight); + + { + String8 supportedPictureSizes; + for (size_t i=0; i < availableJpegSizes.count; i += 2) { + if (i != 0) supportedPictureSizes += ","; + supportedPictureSizes += String8::format("%dx%d", + availableJpegSizes.data.i32[i], + availableJpegSizes.data.i32[i+1]); + } + params.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES, + supportedPictureSizes); + } + + params.setPictureFormat(CameraParameters::PIXEL_FORMAT_JPEG); + params.set(CameraParameters::KEY_SUPPORTED_PICTURE_FORMATS, + CameraParameters::PIXEL_FORMAT_JPEG); + + camera_metadata_ro_entry_t availableJpegThumbnailSizes = + staticInfo(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, 4); + if (!availableJpegThumbnailSizes.count) return NO_INIT; + + // TODO: Pick default thumbnail size sensibly + jpegThumbSize[0] = availableJpegThumbnailSizes.data.i32[0]; + jpegThumbSize[1] = availableJpegThumbnailSizes.data.i32[1]; + + params.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, + jpegThumbSize[0]); + params.set(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, + jpegThumbSize[1]); + + { + String8 supportedJpegThumbSizes; + for (size_t i=0; i < availableJpegThumbnailSizes.count; i += 2) { + if (i != 0) supportedJpegThumbSizes += ","; + supportedJpegThumbSizes += String8::format("%dx%d", + availableJpegThumbnailSizes.data.i32[i], + availableJpegThumbnailSizes.data.i32[i+1]); + } + params.set(CameraParameters::KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES, + supportedJpegThumbSizes); + } + + jpegThumbQuality = 90; + params.set(CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY, + jpegThumbQuality); + jpegQuality = 90; + params.set(CameraParameters::KEY_JPEG_QUALITY, + jpegQuality); + jpegRotation = 0; + params.set(CameraParameters::KEY_ROTATION, + jpegRotation); + + gpsEnabled = false; + gpsCoordinates[0] = 0.0; + gpsCoordinates[1] = 0.0; + gpsCoordinates[2] = 0.0; + gpsTimestamp = 0; + gpsProcessingMethod = "unknown"; + // GPS fields in CameraParameters are not set by implementation + + wbMode = ANDROID_CONTROL_AWB_MODE_AUTO; + params.set(CameraParameters::KEY_WHITE_BALANCE, + CameraParameters::WHITE_BALANCE_AUTO); + + camera_metadata_ro_entry_t availableWhiteBalanceModes = + staticInfo(ANDROID_CONTROL_AWB_AVAILABLE_MODES); + { + String8 supportedWhiteBalance; + bool addComma = false; + for (size_t i=0; i < availableWhiteBalanceModes.count; i++) { + if (addComma) supportedWhiteBalance += ","; + addComma = true; + switch (availableWhiteBalanceModes.data.u8[i]) { + case ANDROID_CONTROL_AWB_MODE_AUTO: + supportedWhiteBalance += + CameraParameters::WHITE_BALANCE_AUTO; + break; + case ANDROID_CONTROL_AWB_MODE_INCANDESCENT: + supportedWhiteBalance += + CameraParameters::WHITE_BALANCE_INCANDESCENT; + break; + case ANDROID_CONTROL_AWB_MODE_FLUORESCENT: + supportedWhiteBalance += + CameraParameters::WHITE_BALANCE_FLUORESCENT; + break; + case ANDROID_CONTROL_AWB_MODE_WARM_FLUORESCENT: + supportedWhiteBalance += + CameraParameters::WHITE_BALANCE_WARM_FLUORESCENT; + break; + case ANDROID_CONTROL_AWB_MODE_DAYLIGHT: + supportedWhiteBalance += + CameraParameters::WHITE_BALANCE_DAYLIGHT; + break; + case ANDROID_CONTROL_AWB_MODE_CLOUDY_DAYLIGHT: + supportedWhiteBalance += + CameraParameters::WHITE_BALANCE_CLOUDY_DAYLIGHT; + break; + case ANDROID_CONTROL_AWB_MODE_TWILIGHT: + supportedWhiteBalance += + CameraParameters::WHITE_BALANCE_TWILIGHT; + break; + case ANDROID_CONTROL_AWB_MODE_SHADE: + supportedWhiteBalance += + CameraParameters::WHITE_BALANCE_SHADE; + break; + // Skipping values not mappable to v1 API + case ANDROID_CONTROL_AWB_MODE_OFF: + addComma = false; + break; + default: + ALOGW("%s: Camera %d: Unknown white balance value: %d", + __FUNCTION__, cameraId, + availableWhiteBalanceModes.data.u8[i]); + addComma = false; + break; + } + } + params.set(CameraParameters::KEY_SUPPORTED_WHITE_BALANCE, + supportedWhiteBalance); + } + + effectMode = ANDROID_CONTROL_EFFECT_MODE_OFF; + params.set(CameraParameters::KEY_EFFECT, + CameraParameters::EFFECT_NONE); + + camera_metadata_ro_entry_t availableEffects = + staticInfo(ANDROID_CONTROL_AVAILABLE_EFFECTS); + if (!availableEffects.count) return NO_INIT; + { + String8 supportedEffects; + bool addComma = false; + for (size_t i=0; i < availableEffects.count; i++) { + if (addComma) supportedEffects += ","; + addComma = true; + switch (availableEffects.data.u8[i]) { + case ANDROID_CONTROL_EFFECT_MODE_OFF: + supportedEffects += + CameraParameters::EFFECT_NONE; + break; + case ANDROID_CONTROL_EFFECT_MODE_MONO: + supportedEffects += + CameraParameters::EFFECT_MONO; + break; + case ANDROID_CONTROL_EFFECT_MODE_NEGATIVE: + supportedEffects += + CameraParameters::EFFECT_NEGATIVE; + break; + case ANDROID_CONTROL_EFFECT_MODE_SOLARIZE: + supportedEffects += + CameraParameters::EFFECT_SOLARIZE; + break; + case ANDROID_CONTROL_EFFECT_MODE_SEPIA: + supportedEffects += + CameraParameters::EFFECT_SEPIA; + break; + case ANDROID_CONTROL_EFFECT_MODE_POSTERIZE: + supportedEffects += + CameraParameters::EFFECT_POSTERIZE; + break; + case ANDROID_CONTROL_EFFECT_MODE_WHITEBOARD: + supportedEffects += + CameraParameters::EFFECT_WHITEBOARD; + break; + case ANDROID_CONTROL_EFFECT_MODE_BLACKBOARD: + supportedEffects += + CameraParameters::EFFECT_BLACKBOARD; + break; + case ANDROID_CONTROL_EFFECT_MODE_AQUA: + supportedEffects += + CameraParameters::EFFECT_AQUA; + break; + default: + ALOGW("%s: Camera %d: Unknown effect value: %d", + __FUNCTION__, cameraId, availableEffects.data.u8[i]); + addComma = false; + break; + } + } + params.set(CameraParameters::KEY_SUPPORTED_EFFECTS, supportedEffects); + } + + antibandingMode = ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO; + params.set(CameraParameters::KEY_ANTIBANDING, + CameraParameters::ANTIBANDING_AUTO); + + camera_metadata_ro_entry_t availableAntibandingModes = + staticInfo(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES); + if (!availableAntibandingModes.count) return NO_INIT; + { + String8 supportedAntibanding; + bool addComma = false; + for (size_t i=0; i < availableAntibandingModes.count; i++) { + if (addComma) supportedAntibanding += ","; + addComma = true; + switch (availableAntibandingModes.data.u8[i]) { + case ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF: + supportedAntibanding += + CameraParameters::ANTIBANDING_OFF; + break; + case ANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ: + supportedAntibanding += + CameraParameters::ANTIBANDING_50HZ; + break; + case ANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ: + supportedAntibanding += + CameraParameters::ANTIBANDING_60HZ; + break; + case ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO: + supportedAntibanding += + CameraParameters::ANTIBANDING_AUTO; + break; + default: + ALOGW("%s: Camera %d: Unknown antibanding value: %d", + __FUNCTION__, cameraId, + availableAntibandingModes.data.u8[i]); + addComma = false; + break; + } + } + params.set(CameraParameters::KEY_SUPPORTED_ANTIBANDING, + supportedAntibanding); + } + + sceneMode = ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED; + params.set(CameraParameters::KEY_SCENE_MODE, + CameraParameters::SCENE_MODE_AUTO); + + camera_metadata_ro_entry_t availableSceneModes = + staticInfo(ANDROID_CONTROL_AVAILABLE_SCENE_MODES); + if (!availableSceneModes.count) return NO_INIT; + { + String8 supportedSceneModes(CameraParameters::SCENE_MODE_AUTO); + bool addComma = true; + bool noSceneModes = false; + for (size_t i=0; i < availableSceneModes.count; i++) { + if (addComma) supportedSceneModes += ","; + addComma = true; + switch (availableSceneModes.data.u8[i]) { + case ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED: + noSceneModes = true; + break; + case ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY: + // Not in old API + addComma = false; + break; + case ANDROID_CONTROL_SCENE_MODE_ACTION: + supportedSceneModes += + CameraParameters::SCENE_MODE_ACTION; + break; + case ANDROID_CONTROL_SCENE_MODE_PORTRAIT: + supportedSceneModes += + CameraParameters::SCENE_MODE_PORTRAIT; + break; + case ANDROID_CONTROL_SCENE_MODE_LANDSCAPE: + supportedSceneModes += + CameraParameters::SCENE_MODE_LANDSCAPE; + break; + case ANDROID_CONTROL_SCENE_MODE_NIGHT: + supportedSceneModes += + CameraParameters::SCENE_MODE_NIGHT; + break; + case ANDROID_CONTROL_SCENE_MODE_NIGHT_PORTRAIT: + supportedSceneModes += + CameraParameters::SCENE_MODE_NIGHT_PORTRAIT; + break; + case ANDROID_CONTROL_SCENE_MODE_THEATRE: + supportedSceneModes += + CameraParameters::SCENE_MODE_THEATRE; + break; + case ANDROID_CONTROL_SCENE_MODE_BEACH: + supportedSceneModes += + CameraParameters::SCENE_MODE_BEACH; + break; + case ANDROID_CONTROL_SCENE_MODE_SNOW: + supportedSceneModes += + CameraParameters::SCENE_MODE_SNOW; + break; + case ANDROID_CONTROL_SCENE_MODE_SUNSET: + supportedSceneModes += + CameraParameters::SCENE_MODE_SUNSET; + break; + case ANDROID_CONTROL_SCENE_MODE_STEADYPHOTO: + supportedSceneModes += + CameraParameters::SCENE_MODE_STEADYPHOTO; + break; + case ANDROID_CONTROL_SCENE_MODE_FIREWORKS: + supportedSceneModes += + CameraParameters::SCENE_MODE_FIREWORKS; + break; + case ANDROID_CONTROL_SCENE_MODE_SPORTS: + supportedSceneModes += + CameraParameters::SCENE_MODE_SPORTS; + break; + case ANDROID_CONTROL_SCENE_MODE_PARTY: + supportedSceneModes += + CameraParameters::SCENE_MODE_PARTY; + break; + case ANDROID_CONTROL_SCENE_MODE_CANDLELIGHT: + supportedSceneModes += + CameraParameters::SCENE_MODE_CANDLELIGHT; + break; + case ANDROID_CONTROL_SCENE_MODE_BARCODE: + supportedSceneModes += + CameraParameters::SCENE_MODE_BARCODE; + break; + default: + ALOGW("%s: Camera %d: Unknown scene mode value: %d", + __FUNCTION__, cameraId, + availableSceneModes.data.u8[i]); + addComma = false; + break; + } + } + if (!noSceneModes) { + params.set(CameraParameters::KEY_SUPPORTED_SCENE_MODES, + supportedSceneModes); + } + } + + camera_metadata_ro_entry_t flashAvailable = + staticInfo(ANDROID_FLASH_INFO_AVAILABLE, 1, 1); + if (!flashAvailable.count) return NO_INIT; + + camera_metadata_ro_entry_t availableAeModes = + staticInfo(ANDROID_CONTROL_AE_AVAILABLE_MODES); + if (!availableAeModes.count) return NO_INIT; + + if (flashAvailable.data.u8[0]) { + flashMode = Parameters::FLASH_MODE_OFF; + params.set(CameraParameters::KEY_FLASH_MODE, + CameraParameters::FLASH_MODE_OFF); + + String8 supportedFlashModes(CameraParameters::FLASH_MODE_OFF); + supportedFlashModes = supportedFlashModes + + "," + CameraParameters::FLASH_MODE_AUTO + + "," + CameraParameters::FLASH_MODE_ON + + "," + CameraParameters::FLASH_MODE_TORCH; + for (size_t i=0; i < availableAeModes.count; i++) { + if (availableAeModes.data.u8[i] == + ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE) { + supportedFlashModes = supportedFlashModes + "," + + CameraParameters::FLASH_MODE_RED_EYE; + break; + } + } + params.set(CameraParameters::KEY_SUPPORTED_FLASH_MODES, + supportedFlashModes); + } else { + flashMode = Parameters::FLASH_MODE_OFF; + params.set(CameraParameters::KEY_FLASH_MODE, + CameraParameters::FLASH_MODE_OFF); + params.set(CameraParameters::KEY_SUPPORTED_FLASH_MODES, + CameraParameters::FLASH_MODE_OFF); + } + + camera_metadata_ro_entry_t minFocusDistance = + staticInfo(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE, 1, 1); + if (!minFocusDistance.count) return NO_INIT; + + camera_metadata_ro_entry_t availableAfModes = + staticInfo(ANDROID_CONTROL_AF_AVAILABLE_MODES); + if (!availableAfModes.count) return NO_INIT; + + if (minFocusDistance.data.f[0] == 0) { + // Fixed-focus lens + focusMode = Parameters::FOCUS_MODE_FIXED; + params.set(CameraParameters::KEY_FOCUS_MODE, + CameraParameters::FOCUS_MODE_FIXED); + params.set(CameraParameters::KEY_SUPPORTED_FOCUS_MODES, + CameraParameters::FOCUS_MODE_FIXED); + } else { + focusMode = Parameters::FOCUS_MODE_AUTO; + params.set(CameraParameters::KEY_FOCUS_MODE, + CameraParameters::FOCUS_MODE_AUTO); + String8 supportedFocusModes(CameraParameters::FOCUS_MODE_INFINITY); + bool addComma = true; + + for (size_t i=0; i < availableAfModes.count; i++) { + if (addComma) supportedFocusModes += ","; + addComma = true; + switch (availableAfModes.data.u8[i]) { + case ANDROID_CONTROL_AF_MODE_AUTO: + supportedFocusModes += + CameraParameters::FOCUS_MODE_AUTO; + break; + case ANDROID_CONTROL_AF_MODE_MACRO: + supportedFocusModes += + CameraParameters::FOCUS_MODE_MACRO; + break; + case ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO: + supportedFocusModes += + CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO; + break; + case ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE: + supportedFocusModes += + CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE; + break; + case ANDROID_CONTROL_AF_MODE_EDOF: + supportedFocusModes += + CameraParameters::FOCUS_MODE_EDOF; + break; + // Not supported in old API + case ANDROID_CONTROL_AF_MODE_OFF: + addComma = false; + break; + default: + ALOGW("%s: Camera %d: Unknown AF mode value: %d", + __FUNCTION__, cameraId, availableAfModes.data.u8[i]); + addComma = false; + break; + } + } + params.set(CameraParameters::KEY_SUPPORTED_FOCUS_MODES, + supportedFocusModes); + } + 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; + + int32_t maxNumFocusAreas = 0; + if (focusMode != Parameters::FOCUS_MODE_FIXED) { + maxNumFocusAreas = max3aRegions.data.i32[0]; + } + params.set(CameraParameters::KEY_MAX_NUM_FOCUS_AREAS, maxNumFocusAreas); + params.set(CameraParameters::KEY_FOCUS_AREAS, + "(0,0,0,0,0)"); + focusingAreas.clear(); + focusingAreas.add(Parameters::Area(0,0,0,0,0)); + + camera_metadata_ro_entry_t availableFocalLengths = + staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS); + if (!availableFocalLengths.count) return NO_INIT; + + float minFocalLength = availableFocalLengths.data.f[0]; + params.setFloat(CameraParameters::KEY_FOCAL_LENGTH, minFocalLength); + + camera_metadata_ro_entry_t sensorSize = + staticInfo(ANDROID_SENSOR_INFO_PHYSICAL_SIZE, 2, 2); + if (!sensorSize.count) return NO_INIT; + + // The fields of view here assume infinity focus, maximum wide angle + float horizFov = 180 / M_PI * + 2 * atanf(sensorSize.data.f[0] / (2 * minFocalLength)); + float vertFov = 180 / M_PI * + 2 * atanf(sensorSize.data.f[1] / (2 * minFocalLength)); + params.setFloat(CameraParameters::KEY_HORIZONTAL_VIEW_ANGLE, horizFov); + params.setFloat(CameraParameters::KEY_VERTICAL_VIEW_ANGLE, vertFov); + + exposureCompensation = 0; + params.set(CameraParameters::KEY_EXPOSURE_COMPENSATION, + exposureCompensation); + + camera_metadata_ro_entry_t exposureCompensationRange = + staticInfo(ANDROID_CONTROL_AE_COMPENSATION_RANGE, 2, 2); + if (!exposureCompensationRange.count) return NO_INIT; + + params.set(CameraParameters::KEY_MAX_EXPOSURE_COMPENSATION, + exposureCompensationRange.data.i32[1]); + params.set(CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION, + exposureCompensationRange.data.i32[0]); + + camera_metadata_ro_entry_t exposureCompensationStep = + staticInfo(ANDROID_CONTROL_AE_COMPENSATION_STEP, 1, 1); + if (!exposureCompensationStep.count) return NO_INIT; + + params.setFloat(CameraParameters::KEY_EXPOSURE_COMPENSATION_STEP, + (float)exposureCompensationStep.data.r[0].numerator / + exposureCompensationStep.data.r[0].denominator); + + autoExposureLock = false; + params.set(CameraParameters::KEY_AUTO_EXPOSURE_LOCK, + CameraParameters::FALSE); + params.set(CameraParameters::KEY_AUTO_EXPOSURE_LOCK_SUPPORTED, + CameraParameters::TRUE); + + autoWhiteBalanceLock = false; + params.set(CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK, + CameraParameters::FALSE); + params.set(CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK_SUPPORTED, + CameraParameters::TRUE); + + meteringAreas.add(Parameters::Area(0, 0, 0, 0, 0)); + params.set(CameraParameters::KEY_MAX_NUM_METERING_AREAS, + max3aRegions.data.i32[0]); + params.set(CameraParameters::KEY_METERING_AREAS, + "(0,0,0,0,0)"); + + zoom = 0; + params.set(CameraParameters::KEY_ZOOM, zoom); + params.set(CameraParameters::KEY_MAX_ZOOM, NUM_ZOOM_STEPS - 1); + + camera_metadata_ro_entry_t maxDigitalZoom = + staticInfo(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, /*minCount*/1, /*maxCount*/1); + if (!maxDigitalZoom.count) return NO_INIT; + + { + String8 zoomRatios; + float zoom = 1.f; + float zoomIncrement = (maxDigitalZoom.data.f[0] - zoom) / + (NUM_ZOOM_STEPS-1); + bool addComma = false; + for (size_t i=0; i < NUM_ZOOM_STEPS; i++) { + if (addComma) zoomRatios += ","; + addComma = true; + zoomRatios += String8::format("%d", static_cast<int>(zoom * 100)); + zoom += zoomIncrement; + } + params.set(CameraParameters::KEY_ZOOM_RATIOS, zoomRatios); + } + + params.set(CameraParameters::KEY_ZOOM_SUPPORTED, + CameraParameters::TRUE); + params.set(CameraParameters::KEY_SMOOTH_ZOOM_SUPPORTED, + CameraParameters::FALSE); + + params.set(CameraParameters::KEY_FOCUS_DISTANCES, + "Infinity,Infinity,Infinity"); + + params.set(CameraParameters::KEY_MAX_NUM_DETECTED_FACES_HW, + fastInfo.maxFaces); + params.set(CameraParameters::KEY_MAX_NUM_DETECTED_FACES_SW, + 0); + + params.set(CameraParameters::KEY_VIDEO_FRAME_FORMAT, + CameraParameters::PIXEL_FORMAT_ANDROID_OPAQUE); + + recordingHint = false; + params.set(CameraParameters::KEY_RECORDING_HINT, + CameraParameters::FALSE); + + params.set(CameraParameters::KEY_VIDEO_SNAPSHOT_SUPPORTED, + CameraParameters::TRUE); + + videoStabilization = false; + params.set(CameraParameters::KEY_VIDEO_STABILIZATION, + CameraParameters::FALSE); + + camera_metadata_ro_entry_t availableVideoStabilizationModes = + staticInfo(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES); + if (!availableVideoStabilizationModes.count) return NO_INIT; + + if (availableVideoStabilizationModes.count > 1) { + params.set(CameraParameters::KEY_VIDEO_STABILIZATION_SUPPORTED, + CameraParameters::TRUE); + } else { + params.set(CameraParameters::KEY_VIDEO_STABILIZATION_SUPPORTED, + CameraParameters::FALSE); + } + + // Set up initial state for non-Camera.Parameters state variables + + storeMetadataInBuffers = true; + playShutterSound = true; + enableFaceDetect = false; + + enableFocusMoveMessages = false; + afTriggerCounter = 1; + currentAfTriggerId = -1; + afInMotion = false; + + precaptureTriggerCounter = 1; + + previewCallbackFlags = 0; + previewCallbackOneShot = false; + + char value[PROPERTY_VALUE_MAX]; + property_get("camera.disable_zsl_mode", value, "0"); + if (!strcmp(value,"1")) { + ALOGI("Camera %d: Disabling ZSL mode", cameraId); + zslMode = false; + } else { + zslMode = true; + } + + lightFx = LIGHTFX_NONE; + + state = STOPPED; + + paramsFlattened = params.flatten(); + + return OK; +} + +String8 Parameters::get() const { + return paramsFlattened; +} + +status_t Parameters::buildFastInfo() { + + camera_metadata_ro_entry_t activeArraySize = + staticInfo(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, 2, 2); + if (!activeArraySize.count) return NO_INIT; + int32_t arrayWidth = activeArraySize.data.i32[0]; + int32_t arrayHeight = activeArraySize.data.i32[1]; + + camera_metadata_ro_entry_t availableFaceDetectModes = + staticInfo(ANDROID_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES); + if (!availableFaceDetectModes.count) return NO_INIT; + + uint8_t bestFaceDetectMode = + ANDROID_STATISTICS_FACE_DETECT_MODE_OFF; + for (size_t i = 0 ; i < availableFaceDetectModes.count; i++) { + switch (availableFaceDetectModes.data.u8[i]) { + case ANDROID_STATISTICS_FACE_DETECT_MODE_OFF: + break; + case ANDROID_STATISTICS_FACE_DETECT_MODE_SIMPLE: + if (bestFaceDetectMode != + ANDROID_STATISTICS_FACE_DETECT_MODE_FULL) { + bestFaceDetectMode = + ANDROID_STATISTICS_FACE_DETECT_MODE_SIMPLE; + } + break; + case ANDROID_STATISTICS_FACE_DETECT_MODE_FULL: + bestFaceDetectMode = + ANDROID_STATISTICS_FACE_DETECT_MODE_FULL; + break; + default: + ALOGE("%s: Camera %d: Unknown face detect mode %d:", + __FUNCTION__, cameraId, + availableFaceDetectModes.data.u8[i]); + return NO_INIT; + } + } + + camera_metadata_ro_entry_t maxFacesDetected = + staticInfo(ANDROID_STATISTICS_INFO_MAX_FACE_COUNT, 1, 1); + if (!maxFacesDetected.count) return NO_INIT; + + int32_t maxFaces = maxFacesDetected.data.i32[0]; + + camera_metadata_ro_entry_t availableSceneModes = + staticInfo(ANDROID_CONTROL_AVAILABLE_SCENE_MODES); + camera_metadata_ro_entry_t sceneModeOverrides = + staticInfo(ANDROID_CONTROL_SCENE_MODE_OVERRIDES); + camera_metadata_ro_entry_t minFocusDistance = + staticInfo(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE); + bool fixedLens = (minFocusDistance.data.f[0] == 0); + + if (sceneModeOverrides.count > 0) { + // sceneModeOverrides is defined to have 3 entries for each scene mode, + // which are AE, AWB, and AF override modes the HAL wants for that scene + // mode. + const size_t kModesPerSceneMode = 3; + if (sceneModeOverrides.count != + availableSceneModes.count * kModesPerSceneMode) { + ALOGE("%s: Camera %d: Scene mode override list is an " + "unexpected size: %d (expected %d)", __FUNCTION__, + cameraId, sceneModeOverrides.count, + availableSceneModes.count); + return NO_INIT; + } + for (size_t i = 0; i < availableSceneModes.count; i++) { + DeviceInfo::OverrideModes modes; + uint8_t aeMode = + sceneModeOverrides.data.u8[i * kModesPerSceneMode + 0]; + switch(aeMode) { + case ANDROID_CONTROL_AE_MODE_ON: + modes.flashMode = FLASH_MODE_OFF; + break; + case ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH: + modes.flashMode = FLASH_MODE_AUTO; + break; + case ANDROID_CONTROL_AE_MODE_ON_ALWAYS_FLASH: + modes.flashMode = FLASH_MODE_ON; + break; + case ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE: + modes.flashMode = FLASH_MODE_RED_EYE; + break; + default: + ALOGE("%s: Unknown override AE mode: %d", __FUNCTION__, + aeMode); + modes.flashMode = FLASH_MODE_INVALID; + break; + } + modes.wbMode = + sceneModeOverrides.data.u8[i * kModesPerSceneMode + 1]; + uint8_t afMode = + sceneModeOverrides.data.u8[i * kModesPerSceneMode + 2]; + switch(afMode) { + case ANDROID_CONTROL_AF_MODE_OFF: + modes.focusMode = fixedLens ? + FOCUS_MODE_FIXED : FOCUS_MODE_INFINITY; + break; + case ANDROID_CONTROL_AF_MODE_AUTO: + case ANDROID_CONTROL_AF_MODE_MACRO: + case ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO: + case ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE: + case ANDROID_CONTROL_AF_MODE_EDOF: + modes.focusMode = static_cast<focusMode_t>(afMode); + break; + default: + ALOGE("%s: Unknown override AF mode: %d", __FUNCTION__, + afMode); + modes.focusMode = FOCUS_MODE_INVALID; + break; + } + fastInfo.sceneModeOverrides.add(availableSceneModes.data.u8[i], + modes); + } + } + + fastInfo.arrayWidth = arrayWidth; + fastInfo.arrayHeight = arrayHeight; + fastInfo.bestFaceDetectMode = bestFaceDetectMode; + fastInfo.maxFaces = maxFaces; + return OK; +} + +status_t Parameters::buildQuirks() { + camera_metadata_ro_entry_t entry; + entry = info->find(ANDROID_QUIRKS_TRIGGER_AF_WITH_AUTO); + quirks.triggerAfWithAuto = (entry.count != 0 && entry.data.u8[0] == 1); + ALOGV_IF(quirks.triggerAfWithAuto, "Camera %d: Quirk triggerAfWithAuto enabled", + cameraId); + + entry = info->find(ANDROID_QUIRKS_USE_ZSL_FORMAT); + quirks.useZslFormat = (entry.count != 0 && entry.data.u8[0] == 1); + ALOGV_IF(quirks.useZslFormat, "Camera %d: Quirk useZslFormat enabled", + cameraId); + + entry = info->find(ANDROID_QUIRKS_METERING_CROP_REGION); + quirks.meteringCropRegion = (entry.count != 0 && entry.data.u8[0] == 1); + ALOGV_IF(quirks.meteringCropRegion, "Camera %d: Quirk meteringCropRegion" + " enabled", cameraId); + + return OK; +} + +camera_metadata_ro_entry_t Parameters::staticInfo(uint32_t tag, + size_t minCount, size_t maxCount) const { + status_t res; + camera_metadata_ro_entry_t entry = info->find(tag); + + if (CC_UNLIKELY( entry.count == 0 )) { + const char* tagSection = get_camera_metadata_section_name(tag); + if (tagSection == NULL) tagSection = "<unknown>"; + const char* tagName = get_camera_metadata_tag_name(tag); + if (tagName == NULL) tagName = "<unknown>"; + + ALOGE("Error finding static metadata entry '%s.%s' (%x)", + tagSection, tagName, tag); + } else if (CC_UNLIKELY( + (minCount != 0 && entry.count < minCount) || + (maxCount != 0 && entry.count > maxCount) ) ) { + const char* tagSection = get_camera_metadata_section_name(tag); + if (tagSection == NULL) tagSection = "<unknown>"; + const char* tagName = get_camera_metadata_tag_name(tag); + if (tagName == NULL) tagName = "<unknown>"; + ALOGE("Malformed static metadata entry '%s.%s' (%x):" + "Expected between %d and %d values, but got %d values", + tagSection, tagName, tag, minCount, maxCount, entry.count); + } + + return entry; +} + +status_t Parameters::set(const String8& paramString) { + status_t res; + + CameraParameters newParams(paramString); + + // TODO: Currently ignoring any changes to supposedly read-only parameters + // such as supported preview sizes, etc. Should probably produce an error if + // they're changed. + + /** Extract and verify new parameters */ + + size_t i; + + Parameters validatedParams(*this); + + // PREVIEW_SIZE + newParams.getPreviewSize(&validatedParams.previewWidth, + &validatedParams.previewHeight); + + if (validatedParams.previewWidth != previewWidth || + validatedParams.previewHeight != previewHeight) { + if (state >= PREVIEW) { + ALOGE("%s: Preview size cannot be updated when preview " + "is active! (Currently %d x %d, requested %d x %d", + __FUNCTION__, + previewWidth, previewHeight, + validatedParams.previewWidth, validatedParams.previewHeight); + return BAD_VALUE; + } + camera_metadata_ro_entry_t availablePreviewSizes = + staticInfo(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES); + for (i = 0; i < availablePreviewSizes.count; i += 2 ) { + if ((availablePreviewSizes.data.i32[i] == + validatedParams.previewWidth) && + (availablePreviewSizes.data.i32[i+1] == + validatedParams.previewHeight)) break; + } + if (i == availablePreviewSizes.count) { + ALOGE("%s: Requested preview size %d x %d is not supported", + __FUNCTION__, validatedParams.previewWidth, + validatedParams.previewHeight); + return BAD_VALUE; + } + } + + // RECORDING_HINT (always supported) + validatedParams.recordingHint = boolFromString( + newParams.get(CameraParameters::KEY_RECORDING_HINT) ); + bool recordingHintChanged = validatedParams.recordingHint != recordingHint; + ALOGV_IF(recordingHintChanged, "%s: Recording hint changed to %d", + __FUNCTION__, recordingHintChanged); + + // PREVIEW_FPS_RANGE + bool fpsRangeChanged = false; + newParams.getPreviewFpsRange(&validatedParams.previewFpsRange[0], + &validatedParams.previewFpsRange[1]); + validatedParams.previewFpsRange[0] /= kFpsToApiScale; + validatedParams.previewFpsRange[1] /= kFpsToApiScale; + + if (validatedParams.previewFpsRange[0] != previewFpsRange[0] || + validatedParams.previewFpsRange[1] != previewFpsRange[1]) { + fpsRangeChanged = true; + camera_metadata_ro_entry_t availablePreviewFpsRanges = + staticInfo(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, 2); + for (i = 0; i < availablePreviewFpsRanges.count; i += 2) { + if ((availablePreviewFpsRanges.data.i32[i] == + validatedParams.previewFpsRange[0]) && + (availablePreviewFpsRanges.data.i32[i+1] == + validatedParams.previewFpsRange[1]) ) { + break; + } + } + if (i == availablePreviewFpsRanges.count) { + ALOGE("%s: Requested preview FPS range %d - %d is not supported", + __FUNCTION__, validatedParams.previewFpsRange[0], + validatedParams.previewFpsRange[1]); + return BAD_VALUE; + } + validatedParams.previewFps = + fpsFromRange(validatedParams.previewFpsRange[0], + validatedParams.previewFpsRange[1]); + newParams.setPreviewFrameRate(validatedParams.previewFps); + } + + // PREVIEW_FORMAT + validatedParams.previewFormat = + formatStringToEnum(newParams.getPreviewFormat()); + if (validatedParams.previewFormat != previewFormat) { + if (state >= PREVIEW) { + ALOGE("%s: Preview format cannot be updated when preview " + "is active!", __FUNCTION__); + return BAD_VALUE; + } + camera_metadata_ro_entry_t availableFormats = + staticInfo(ANDROID_SCALER_AVAILABLE_FORMATS); + for (i = 0; i < availableFormats.count; i++) { + if (availableFormats.data.i32[i] == validatedParams.previewFormat) + break; + } + if (i == availableFormats.count) { + ALOGE("%s: Requested preview format %s (0x%x) is not supported", + __FUNCTION__, newParams.getPreviewFormat(), + validatedParams.previewFormat); + return BAD_VALUE; + } + } + + // PREVIEW_FRAME_RATE + // Deprecated, only use if the preview fps range is unchanged this time. + // The single-value FPS is the same as the minimum of the range. + if (!fpsRangeChanged) { + validatedParams.previewFps = newParams.getPreviewFrameRate(); + if (validatedParams.previewFps != previewFps || recordingHintChanged) { + camera_metadata_ro_entry_t availableFrameRates = + staticInfo(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES); + /** + * If recording hint is set, find the range that encompasses + * previewFps with the largest min index. + * + * If recording hint is not set, find the range with previewFps + * with the smallest min index. + * + * Either way, in case of multiple ranges, break the tie by + * selecting the smaller range. + */ + int targetFps = validatedParams.previewFps; + // all ranges which have targetFps + Vector<Range> candidateRanges; + for (i = 0; i < availableFrameRates.count; i+=2) { + Range r = { + availableFrameRates.data.i32[i], + availableFrameRates.data.i32[i+1] + }; + + if (r.min <= targetFps && targetFps <= r.max) { + candidateRanges.push(r); + } + } + if (candidateRanges.isEmpty()) { + ALOGE("%s: Requested preview frame rate %d is not supported", + __FUNCTION__, validatedParams.previewFps); + return BAD_VALUE; + } + // most applicable range with targetFps + Range bestRange = candidateRanges[0]; + for (i = 1; i < candidateRanges.size(); ++i) { + Range r = candidateRanges[i]; + + // Find by largest minIndex in recording mode + if (validatedParams.recordingHint) { + if (r.min > bestRange.min) { + bestRange = r; + } + else if (r.min == bestRange.min && r.max < bestRange.max) { + bestRange = r; + } + } + // Find by smallest minIndex in preview mode + else { + if (r.min < bestRange.min) { + bestRange = r; + } + else if (r.min == bestRange.min && r.max < bestRange.max) { + bestRange = r; + } + } + } + + validatedParams.previewFpsRange[0] = + bestRange.min; + validatedParams.previewFpsRange[1] = + bestRange.max; + + ALOGV("%s: New preview FPS range: %d, %d, recordingHint = %d", + __FUNCTION__, + validatedParams.previewFpsRange[0], + validatedParams.previewFpsRange[1], + validatedParams.recordingHint); + } + newParams.set(CameraParameters::KEY_PREVIEW_FPS_RANGE, + String8::format("%d,%d", + validatedParams.previewFpsRange[0] * kFpsToApiScale, + validatedParams.previewFpsRange[1] * kFpsToApiScale)); + + } + + // 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] == + validatedParams.pictureWidth) && + (availablePictureSizes.data.i32[i+1] == + validatedParams.pictureHeight)) break; + } + if (i == availablePictureSizes.count) { + ALOGE("%s: Requested picture size %d x %d is not supported", + __FUNCTION__, validatedParams.pictureWidth, + validatedParams.pictureHeight); + return BAD_VALUE; + } + } + + // JPEG_THUMBNAIL_WIDTH/HEIGHT + validatedParams.jpegThumbSize[0] = + newParams.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH); + validatedParams.jpegThumbSize[1] = + newParams.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT); + if (validatedParams.jpegThumbSize[0] != jpegThumbSize[0] || + validatedParams.jpegThumbSize[1] != jpegThumbSize[1]) { + camera_metadata_ro_entry_t availableJpegThumbSizes = + staticInfo(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES); + for (i = 0; i < availableJpegThumbSizes.count; i+=2) { + if ((availableJpegThumbSizes.data.i32[i] == + validatedParams.jpegThumbSize[0]) && + (availableJpegThumbSizes.data.i32[i+1] == + validatedParams.jpegThumbSize[1])) break; + } + if (i == availableJpegThumbSizes.count) { + ALOGE("%s: Requested JPEG thumbnail size %d x %d is not supported", + __FUNCTION__, validatedParams.jpegThumbSize[0], + validatedParams.jpegThumbSize[1]); + return BAD_VALUE; + } + } + + // JPEG_THUMBNAIL_QUALITY + int quality = newParams.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY); + // also makes sure quality fits in uint8_t + if (quality < 0 || quality > 100) { + ALOGE("%s: Requested JPEG thumbnail quality %d is not supported", + __FUNCTION__, quality); + return BAD_VALUE; + } + validatedParams.jpegThumbQuality = quality; + + // JPEG_QUALITY + quality = newParams.getInt(CameraParameters::KEY_JPEG_QUALITY); + // also makes sure quality fits in uint8_t + if (quality < 0 || quality > 100) { + ALOGE("%s: Requested JPEG quality %d is not supported", + __FUNCTION__, quality); + return BAD_VALUE; + } + validatedParams.jpegQuality = quality; + + // ROTATION + validatedParams.jpegRotation = + newParams.getInt(CameraParameters::KEY_ROTATION); + if (validatedParams.jpegRotation != 0 && + validatedParams.jpegRotation != 90 && + validatedParams.jpegRotation != 180 && + validatedParams.jpegRotation != 270) { + ALOGE("%s: Requested picture rotation angle %d is not supported", + __FUNCTION__, validatedParams.jpegRotation); + return BAD_VALUE; + } + + // GPS + + const char *gpsLatStr = + newParams.get(CameraParameters::KEY_GPS_LATITUDE); + if (gpsLatStr != NULL) { + const char *gpsLongStr = + newParams.get(CameraParameters::KEY_GPS_LONGITUDE); + const char *gpsAltitudeStr = + newParams.get(CameraParameters::KEY_GPS_ALTITUDE); + const char *gpsTimeStr = + newParams.get(CameraParameters::KEY_GPS_TIMESTAMP); + const char *gpsProcMethodStr = + newParams.get(CameraParameters::KEY_GPS_PROCESSING_METHOD); + if (gpsLongStr == NULL || + gpsAltitudeStr == NULL || + gpsTimeStr == NULL || + gpsProcMethodStr == NULL) { + ALOGE("%s: Incomplete set of GPS parameters provided", + __FUNCTION__); + return BAD_VALUE; + } + char *endPtr; + errno = 0; + validatedParams.gpsCoordinates[0] = strtod(gpsLatStr, &endPtr); + if (errno || endPtr == gpsLatStr) { + ALOGE("%s: Malformed GPS latitude: %s", __FUNCTION__, gpsLatStr); + return BAD_VALUE; + } + errno = 0; + validatedParams.gpsCoordinates[1] = strtod(gpsLongStr, &endPtr); + if (errno || endPtr == gpsLongStr) { + ALOGE("%s: Malformed GPS longitude: %s", __FUNCTION__, gpsLongStr); + return BAD_VALUE; + } + errno = 0; + validatedParams.gpsCoordinates[2] = strtod(gpsAltitudeStr, &endPtr); + if (errno || endPtr == gpsAltitudeStr) { + ALOGE("%s: Malformed GPS altitude: %s", __FUNCTION__, + gpsAltitudeStr); + return BAD_VALUE; + } + errno = 0; + validatedParams.gpsTimestamp = strtoll(gpsTimeStr, &endPtr, 10); + if (errno || endPtr == gpsTimeStr) { + ALOGE("%s: Malformed GPS timestamp: %s", __FUNCTION__, gpsTimeStr); + return BAD_VALUE; + } + validatedParams.gpsProcessingMethod = gpsProcMethodStr; + + validatedParams.gpsEnabled = true; + } else { + validatedParams.gpsEnabled = false; + } + + // EFFECT + validatedParams.effectMode = effectModeStringToEnum( + newParams.get(CameraParameters::KEY_EFFECT) ); + if (validatedParams.effectMode != effectMode) { + camera_metadata_ro_entry_t availableEffectModes = + staticInfo(ANDROID_CONTROL_AVAILABLE_EFFECTS); + for (i = 0; i < availableEffectModes.count; i++) { + if (validatedParams.effectMode == availableEffectModes.data.u8[i]) break; + } + if (i == availableEffectModes.count) { + ALOGE("%s: Requested effect mode \"%s\" is not supported", + __FUNCTION__, + newParams.get(CameraParameters::KEY_EFFECT) ); + return BAD_VALUE; + } + } + + // ANTIBANDING + validatedParams.antibandingMode = abModeStringToEnum( + newParams.get(CameraParameters::KEY_ANTIBANDING) ); + if (validatedParams.antibandingMode != antibandingMode) { + camera_metadata_ro_entry_t availableAbModes = + staticInfo(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES); + for (i = 0; i < availableAbModes.count; i++) { + if (validatedParams.antibandingMode == availableAbModes.data.u8[i]) + break; + } + if (i == availableAbModes.count) { + ALOGE("%s: Requested antibanding mode \"%s\" is not supported", + __FUNCTION__, + newParams.get(CameraParameters::KEY_ANTIBANDING)); + return BAD_VALUE; + } + } + + // SCENE_MODE + validatedParams.sceneMode = sceneModeStringToEnum( + newParams.get(CameraParameters::KEY_SCENE_MODE) ); + if (validatedParams.sceneMode != sceneMode && + validatedParams.sceneMode != + ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED) { + camera_metadata_ro_entry_t availableSceneModes = + staticInfo(ANDROID_CONTROL_AVAILABLE_SCENE_MODES); + for (i = 0; i < availableSceneModes.count; i++) { + if (validatedParams.sceneMode == availableSceneModes.data.u8[i]) + break; + } + if (i == availableSceneModes.count) { + ALOGE("%s: Requested scene mode \"%s\" is not supported", + __FUNCTION__, + newParams.get(CameraParameters::KEY_SCENE_MODE)); + return BAD_VALUE; + } + } + bool sceneModeSet = + validatedParams.sceneMode != ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED; + + // FLASH_MODE + if (sceneModeSet) { + validatedParams.flashMode = + fastInfo.sceneModeOverrides. + valueFor(validatedParams.sceneMode).flashMode; + } else { + validatedParams.flashMode = FLASH_MODE_INVALID; + } + if (validatedParams.flashMode == FLASH_MODE_INVALID) { + validatedParams.flashMode = flashModeStringToEnum( + newParams.get(CameraParameters::KEY_FLASH_MODE) ); + } + + if (validatedParams.flashMode != flashMode) { + camera_metadata_ro_entry_t flashAvailable = + staticInfo(ANDROID_FLASH_INFO_AVAILABLE, 1, 1); + if (!flashAvailable.data.u8[0] && + validatedParams.flashMode != Parameters::FLASH_MODE_OFF) { + ALOGE("%s: Requested flash mode \"%s\" is not supported: " + "No flash on device", __FUNCTION__, + newParams.get(CameraParameters::KEY_FLASH_MODE)); + return BAD_VALUE; + } else if (validatedParams.flashMode == Parameters::FLASH_MODE_RED_EYE) { + camera_metadata_ro_entry_t availableAeModes = + staticInfo(ANDROID_CONTROL_AE_AVAILABLE_MODES); + for (i = 0; i < availableAeModes.count; i++) { + if (validatedParams.flashMode == availableAeModes.data.u8[i]) + break; + } + if (i == availableAeModes.count) { + ALOGE("%s: Requested flash mode \"%s\" is not supported", + __FUNCTION__, + newParams.get(CameraParameters::KEY_FLASH_MODE)); + return BAD_VALUE; + } + } else if (validatedParams.flashMode == -1) { + ALOGE("%s: Requested flash mode \"%s\" is unknown", + __FUNCTION__, + newParams.get(CameraParameters::KEY_FLASH_MODE)); + return BAD_VALUE; + } + // Update in case of override + newParams.set(CameraParameters::KEY_FLASH_MODE, + flashModeEnumToString(validatedParams.flashMode)); + } + + // WHITE_BALANCE + if (sceneModeSet) { + validatedParams.wbMode = + fastInfo.sceneModeOverrides. + valueFor(validatedParams.sceneMode).wbMode; + } else { + validatedParams.wbMode = ANDROID_CONTROL_AWB_MODE_OFF; + } + if (validatedParams.wbMode == ANDROID_CONTROL_AWB_MODE_OFF) { + validatedParams.wbMode = wbModeStringToEnum( + newParams.get(CameraParameters::KEY_WHITE_BALANCE) ); + } + if (validatedParams.wbMode != wbMode) { + camera_metadata_ro_entry_t availableWbModes = + staticInfo(ANDROID_CONTROL_AWB_AVAILABLE_MODES); + for (i = 0; i < availableWbModes.count; i++) { + if (validatedParams.wbMode == availableWbModes.data.u8[i]) break; + } + if (i == availableWbModes.count) { + ALOGE("%s: Requested white balance mode %s is not supported", + __FUNCTION__, + newParams.get(CameraParameters::KEY_WHITE_BALANCE)); + return BAD_VALUE; + } + // Update in case of override + newParams.set(CameraParameters::KEY_WHITE_BALANCE, + wbModeEnumToString(validatedParams.wbMode)); + } + + // FOCUS_MODE + if (sceneModeSet) { + validatedParams.focusMode = + fastInfo.sceneModeOverrides. + valueFor(validatedParams.sceneMode).focusMode; + } else { + validatedParams.focusMode = FOCUS_MODE_INVALID; + } + if (validatedParams.focusMode == FOCUS_MODE_INVALID) { + validatedParams.focusMode = focusModeStringToEnum( + newParams.get(CameraParameters::KEY_FOCUS_MODE) ); + } + if (validatedParams.focusMode != focusMode) { + validatedParams.currentAfTriggerId = -1; + if (validatedParams.focusMode != Parameters::FOCUS_MODE_FIXED) { + camera_metadata_ro_entry_t minFocusDistance = + staticInfo(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE); + if (minFocusDistance.data.f[0] == 0) { + ALOGE("%s: Requested focus mode \"%s\" is not available: " + "fixed focus lens", + __FUNCTION__, + newParams.get(CameraParameters::KEY_FOCUS_MODE)); + return BAD_VALUE; + } else if (validatedParams.focusMode != + Parameters::FOCUS_MODE_INFINITY) { + camera_metadata_ro_entry_t availableFocusModes = + staticInfo(ANDROID_CONTROL_AF_AVAILABLE_MODES); + for (i = 0; i < availableFocusModes.count; i++) { + if (validatedParams.focusMode == + availableFocusModes.data.u8[i]) break; + } + if (i == availableFocusModes.count) { + ALOGE("%s: Requested focus mode \"%s\" is not supported", + __FUNCTION__, + newParams.get(CameraParameters::KEY_FOCUS_MODE)); + return BAD_VALUE; + } + } + } + validatedParams.focusState = ANDROID_CONTROL_AF_STATE_INACTIVE; + // Always reset shadow focus mode to avoid reverting settings + validatedParams.shadowFocusMode = FOCUS_MODE_INVALID; + // Update in case of override + newParams.set(CameraParameters::KEY_FOCUS_MODE, + focusModeEnumToString(validatedParams.focusMode)); + } else { + validatedParams.currentAfTriggerId = currentAfTriggerId; + } + + // 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]; + if (res == OK) res = validateAreas(validatedParams.focusingAreas, + max3aRegions, AREA_KIND_FOCUS); + if (res != OK) { + ALOGE("%s: Requested focus areas are malformed: %s", + __FUNCTION__, newParams.get(CameraParameters::KEY_FOCUS_AREAS)); + return BAD_VALUE; + } + + // EXPOSURE_COMPENSATION + validatedParams.exposureCompensation = + newParams.getInt(CameraParameters::KEY_EXPOSURE_COMPENSATION); + camera_metadata_ro_entry_t exposureCompensationRange = + staticInfo(ANDROID_CONTROL_AE_COMPENSATION_RANGE); + if ((validatedParams.exposureCompensation < + exposureCompensationRange.data.i32[0]) || + (validatedParams.exposureCompensation > + exposureCompensationRange.data.i32[1])) { + ALOGE("%s: Requested exposure compensation index is out of bounds: %d", + __FUNCTION__, validatedParams.exposureCompensation); + return BAD_VALUE; + } + + // AUTO_EXPOSURE_LOCK (always supported) + validatedParams.autoExposureLock = boolFromString( + newParams.get(CameraParameters::KEY_AUTO_EXPOSURE_LOCK)); + + // AUTO_WHITEBALANCE_LOCK (always supported) + validatedParams.autoWhiteBalanceLock = boolFromString( + newParams.get(CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK)); + + // METERING_AREAS + res = parseAreas(newParams.get(CameraParameters::KEY_METERING_AREAS), + &validatedParams.meteringAreas); + if (res == OK) { + res = validateAreas(validatedParams.meteringAreas, max3aRegions, + AREA_KIND_METERING); + } + if (res != OK) { + ALOGE("%s: Requested metering areas are malformed: %s", + __FUNCTION__, + newParams.get(CameraParameters::KEY_METERING_AREAS)); + return BAD_VALUE; + } + + // ZOOM + validatedParams.zoom = newParams.getInt(CameraParameters::KEY_ZOOM); + if (validatedParams.zoom < 0 + || validatedParams.zoom >= (int)NUM_ZOOM_STEPS) { + ALOGE("%s: Requested zoom level %d is not supported", + __FUNCTION__, validatedParams.zoom); + return BAD_VALUE; + } + + // VIDEO_SIZE + newParams.getVideoSize(&validatedParams.videoWidth, + &validatedParams.videoHeight); + 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; + } + camera_metadata_ro_entry_t availableVideoSizes = + staticInfo(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES); + for (i = 0; i < availableVideoSizes.count; i += 2 ) { + if ((availableVideoSizes.data.i32[i] == + validatedParams.videoWidth) && + (availableVideoSizes.data.i32[i+1] == + validatedParams.videoHeight)) break; + } + if (i == availableVideoSizes.count) { + ALOGE("%s: Requested video size %d x %d is not supported", + __FUNCTION__, validatedParams.videoWidth, + validatedParams.videoHeight); + return BAD_VALUE; + } + } + + // VIDEO_STABILIZATION + validatedParams.videoStabilization = boolFromString( + newParams.get(CameraParameters::KEY_VIDEO_STABILIZATION) ); + camera_metadata_ro_entry_t availableVideoStabilizationModes = + staticInfo(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES); + if (validatedParams.videoStabilization && + availableVideoStabilizationModes.count == 1) { + ALOGE("%s: Video stabilization not supported", __FUNCTION__); + } + + /** Update internal parameters */ + + *this = validatedParams; + + // Need to flatten again in case of overrides + paramsFlattened = newParams.flatten(); + params = newParams; + + return OK; +} + +status_t Parameters::updateRequest(CameraMetadata *request) const { + ATRACE_CALL(); + status_t res; + + uint8_t metadataMode = ANDROID_REQUEST_METADATA_MODE_FULL; + res = request->update(ANDROID_REQUEST_METADATA_MODE, + &metadataMode, 1); + if (res != OK) return res; + + res = request->update(ANDROID_CONTROL_AE_TARGET_FPS_RANGE, + previewFpsRange, 2); + if (res != OK) return res; + + uint8_t reqWbLock = autoWhiteBalanceLock ? + ANDROID_CONTROL_AWB_LOCK_ON : ANDROID_CONTROL_AWB_LOCK_OFF; + res = request->update(ANDROID_CONTROL_AWB_LOCK, + &reqWbLock, 1); + + res = request->update(ANDROID_CONTROL_EFFECT_MODE, + &effectMode, 1); + if (res != OK) return res; + res = request->update(ANDROID_CONTROL_AE_ANTIBANDING_MODE, + &antibandingMode, 1); + if (res != OK) return res; + + // android.hardware.Camera requires that when face detect is enabled, the + // camera is in a face-priority mode. HAL2 splits this into separate parts + // (face detection statistics and face priority scene mode). Map from other + // to the other. + bool sceneModeActive = + sceneMode != (uint8_t)ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED; + uint8_t reqControlMode = ANDROID_CONTROL_MODE_AUTO; + if (enableFaceDetect || sceneModeActive) { + reqControlMode = ANDROID_CONTROL_MODE_USE_SCENE_MODE; + } + res = request->update(ANDROID_CONTROL_MODE, + &reqControlMode, 1); + if (res != OK) return res; + + uint8_t reqSceneMode = + sceneModeActive ? sceneMode : + enableFaceDetect ? (uint8_t)ANDROID_CONTROL_SCENE_MODE_FACE_PRIORITY : + (uint8_t)ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED; + res = request->update(ANDROID_CONTROL_SCENE_MODE, + &reqSceneMode, 1); + if (res != OK) return res; + + uint8_t reqFlashMode = ANDROID_FLASH_MODE_OFF; + uint8_t reqAeMode = ANDROID_CONTROL_AE_MODE_OFF; + switch (flashMode) { + case Parameters::FLASH_MODE_OFF: + reqAeMode = ANDROID_CONTROL_AE_MODE_ON; break; + case Parameters::FLASH_MODE_AUTO: + reqAeMode = ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH; break; + case Parameters::FLASH_MODE_ON: + reqAeMode = ANDROID_CONTROL_AE_MODE_ON_ALWAYS_FLASH; break; + case Parameters::FLASH_MODE_TORCH: + reqAeMode = ANDROID_CONTROL_AE_MODE_ON; + reqFlashMode = ANDROID_FLASH_MODE_TORCH; + break; + case Parameters::FLASH_MODE_RED_EYE: + reqAeMode = ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE; break; + default: + ALOGE("%s: Camera %d: Unknown flash mode %d", __FUNCTION__, + cameraId, flashMode); + return BAD_VALUE; + } + res = request->update(ANDROID_FLASH_MODE, + &reqFlashMode, 1); + if (res != OK) return res; + res = request->update(ANDROID_CONTROL_AE_MODE, + &reqAeMode, 1); + if (res != OK) return res; + + uint8_t reqAeLock = autoExposureLock ? + ANDROID_CONTROL_AE_LOCK_ON : ANDROID_CONTROL_AE_LOCK_OFF; + res = request->update(ANDROID_CONTROL_AE_LOCK, + &reqAeLock, 1); + if (res != OK) return res; + + res = request->update(ANDROID_CONTROL_AWB_MODE, + &wbMode, 1); + if (res != OK) return res; + + float reqFocusDistance = 0; // infinity focus in diopters + uint8_t reqFocusMode = ANDROID_CONTROL_AF_MODE_OFF; + switch (focusMode) { + case Parameters::FOCUS_MODE_AUTO: + case Parameters::FOCUS_MODE_MACRO: + case Parameters::FOCUS_MODE_CONTINUOUS_VIDEO: + case Parameters::FOCUS_MODE_CONTINUOUS_PICTURE: + case Parameters::FOCUS_MODE_EDOF: + reqFocusMode = focusMode; + break; + case Parameters::FOCUS_MODE_INFINITY: + case Parameters::FOCUS_MODE_FIXED: + reqFocusMode = ANDROID_CONTROL_AF_MODE_OFF; + break; + default: + ALOGE("%s: Camera %d: Unknown focus mode %d", __FUNCTION__, + cameraId, focusMode); + return BAD_VALUE; + } + res = request->update(ANDROID_LENS_FOCUS_DISTANCE, + &reqFocusDistance, 1); + if (res != OK) return res; + res = request->update(ANDROID_CONTROL_AF_MODE, + &reqFocusMode, 1); + if (res != OK) return res; + + size_t reqFocusingAreasSize = focusingAreas.size() * 5; + int32_t *reqFocusingAreas = new int32_t[reqFocusingAreasSize]; + for (size_t i = 0; i < reqFocusingAreasSize; i += 5) { + if (focusingAreas[i].weight != 0) { + reqFocusingAreas[i + 0] = + normalizedXToArray(focusingAreas[i].left); + reqFocusingAreas[i + 1] = + normalizedYToArray(focusingAreas[i].top); + reqFocusingAreas[i + 2] = + normalizedXToArray(focusingAreas[i].right); + reqFocusingAreas[i + 3] = + normalizedYToArray(focusingAreas[i].bottom); + } else { + reqFocusingAreas[i + 0] = 0; + reqFocusingAreas[i + 1] = 0; + reqFocusingAreas[i + 2] = 0; + reqFocusingAreas[i + 3] = 0; + } + reqFocusingAreas[i + 4] = focusingAreas[i].weight; + } + res = request->update(ANDROID_CONTROL_AF_REGIONS, + reqFocusingAreas, reqFocusingAreasSize); + if (res != OK) return res; + delete[] reqFocusingAreas; + + res = request->update(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION, + &exposureCompensation, 1); + if (res != OK) return res; + + size_t reqMeteringAreasSize = meteringAreas.size() * 5; + int32_t *reqMeteringAreas = new int32_t[reqMeteringAreasSize]; + for (size_t i = 0; i < reqMeteringAreasSize; i += 5) { + if (meteringAreas[i].weight != 0) { + reqMeteringAreas[i + 0] = + normalizedXToArray(meteringAreas[i].left); + reqMeteringAreas[i + 1] = + normalizedYToArray(meteringAreas[i].top); + reqMeteringAreas[i + 2] = + normalizedXToArray(meteringAreas[i].right); + reqMeteringAreas[i + 3] = + normalizedYToArray(meteringAreas[i].bottom); + } else { + reqMeteringAreas[i + 0] = 0; + reqMeteringAreas[i + 1] = 0; + reqMeteringAreas[i + 2] = 0; + reqMeteringAreas[i + 3] = 0; + } + reqMeteringAreas[i + 4] = meteringAreas[i].weight; + } + res = request->update(ANDROID_CONTROL_AE_REGIONS, + reqMeteringAreas, reqMeteringAreasSize); + if (res != OK) return res; + + delete[] reqMeteringAreas; + + /* don't include jpeg thumbnail size - it's valid for + it to be set to (0,0), meaning 'no thumbnail' */ + CropRegion crop = calculateCropRegion( (CropRegion::Outputs)( + CropRegion::OUTPUT_PREVIEW | + CropRegion::OUTPUT_VIDEO | + CropRegion::OUTPUT_PICTURE )); + int32_t reqCropRegion[3] = { + static_cast<int32_t>(crop.left), + static_cast<int32_t>(crop.top), + static_cast<int32_t>(crop.width) + }; + res = request->update(ANDROID_SCALER_CROP_REGION, + reqCropRegion, 3); + if (res != OK) return res; + + uint8_t reqVstabMode = videoStabilization ? + ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_ON : + ANDROID_CONTROL_VIDEO_STABILIZATION_MODE_OFF; + res = request->update(ANDROID_CONTROL_VIDEO_STABILIZATION_MODE, + &reqVstabMode, 1); + if (res != OK) return res; + + uint8_t reqFaceDetectMode = enableFaceDetect ? + fastInfo.bestFaceDetectMode : + (uint8_t)ANDROID_STATISTICS_FACE_DETECT_MODE_OFF; + res = request->update(ANDROID_STATISTICS_FACE_DETECT_MODE, + &reqFaceDetectMode, 1); + if (res != OK) return res; + + return OK; +} + +status_t Parameters::updateRequestJpeg(CameraMetadata *request) const { + status_t res; + + res = request->update(ANDROID_JPEG_THUMBNAIL_SIZE, + jpegThumbSize, 2); + if (res != OK) return res; + res = request->update(ANDROID_JPEG_THUMBNAIL_QUALITY, + &jpegThumbQuality, 1); + if (res != OK) return res; + res = request->update(ANDROID_JPEG_QUALITY, + &jpegQuality, 1); + if (res != OK) return res; + res = request->update( + ANDROID_JPEG_ORIENTATION, + &jpegRotation, 1); + if (res != OK) return res; + + if (gpsEnabled) { + res = request->update( + ANDROID_JPEG_GPS_COORDINATES, + gpsCoordinates, 3); + if (res != OK) return res; + res = request->update( + ANDROID_JPEG_GPS_TIMESTAMP, + &gpsTimestamp, 1); + if (res != OK) return res; + res = request->update( + ANDROID_JPEG_GPS_PROCESSING_METHOD, + gpsProcessingMethod); + if (res != OK) return res; + } else { + res = request->erase(ANDROID_JPEG_GPS_COORDINATES); + if (res != OK) return res; + res = request->erase(ANDROID_JPEG_GPS_TIMESTAMP); + if (res != OK) return res; + res = request->erase(ANDROID_JPEG_GPS_PROCESSING_METHOD); + if (res != OK) return res; + } + return OK; +} + + +const char* Parameters::getStateName(State state) { +#define CASE_ENUM_TO_CHAR(x) case x: return(#x); break; + switch(state) { + CASE_ENUM_TO_CHAR(DISCONNECTED) + CASE_ENUM_TO_CHAR(STOPPED) + CASE_ENUM_TO_CHAR(WAITING_FOR_PREVIEW_WINDOW) + CASE_ENUM_TO_CHAR(PREVIEW) + CASE_ENUM_TO_CHAR(RECORD) + CASE_ENUM_TO_CHAR(STILL_CAPTURE) + CASE_ENUM_TO_CHAR(VIDEO_SNAPSHOT) + default: + return "Unknown state!"; + break; + } +#undef CASE_ENUM_TO_CHAR +} + +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; +} + +const char* Parameters::formatEnumToString(int format) { + const char *fmt; + switch(format) { + case HAL_PIXEL_FORMAT_YCbCr_422_SP: // NV16 + fmt = CameraParameters::PIXEL_FORMAT_YUV422SP; + break; + case HAL_PIXEL_FORMAT_YCrCb_420_SP: // NV21 + fmt = CameraParameters::PIXEL_FORMAT_YUV420SP; + break; + case HAL_PIXEL_FORMAT_YCbCr_422_I: // YUY2 + fmt = CameraParameters::PIXEL_FORMAT_YUV422I; + break; + case HAL_PIXEL_FORMAT_YV12: // YV12 + fmt = CameraParameters::PIXEL_FORMAT_YUV420P; + break; + case HAL_PIXEL_FORMAT_RGB_565: // RGB565 + fmt = CameraParameters::PIXEL_FORMAT_RGB565; + break; + case HAL_PIXEL_FORMAT_RGBA_8888: // RGBA8888 + fmt = CameraParameters::PIXEL_FORMAT_RGBA8888; + break; + case HAL_PIXEL_FORMAT_RAW_SENSOR: + ALOGW("Raw sensor preview format requested."); + fmt = CameraParameters::PIXEL_FORMAT_BAYER_RGGB; + break; + default: + ALOGE("%s: Unknown preview format: %x", + __FUNCTION__, format); + fmt = NULL; + break; + } + return fmt; +} + +int Parameters::wbModeStringToEnum(const char *wbMode) { + return + !wbMode ? + ANDROID_CONTROL_AWB_MODE_AUTO : + !strcmp(wbMode, CameraParameters::WHITE_BALANCE_AUTO) ? + ANDROID_CONTROL_AWB_MODE_AUTO : + !strcmp(wbMode, CameraParameters::WHITE_BALANCE_INCANDESCENT) ? + ANDROID_CONTROL_AWB_MODE_INCANDESCENT : + !strcmp(wbMode, CameraParameters::WHITE_BALANCE_FLUORESCENT) ? + ANDROID_CONTROL_AWB_MODE_FLUORESCENT : + !strcmp(wbMode, CameraParameters::WHITE_BALANCE_WARM_FLUORESCENT) ? + ANDROID_CONTROL_AWB_MODE_WARM_FLUORESCENT : + !strcmp(wbMode, CameraParameters::WHITE_BALANCE_DAYLIGHT) ? + ANDROID_CONTROL_AWB_MODE_DAYLIGHT : + !strcmp(wbMode, CameraParameters::WHITE_BALANCE_CLOUDY_DAYLIGHT) ? + ANDROID_CONTROL_AWB_MODE_CLOUDY_DAYLIGHT : + !strcmp(wbMode, CameraParameters::WHITE_BALANCE_TWILIGHT) ? + ANDROID_CONTROL_AWB_MODE_TWILIGHT : + !strcmp(wbMode, CameraParameters::WHITE_BALANCE_SHADE) ? + ANDROID_CONTROL_AWB_MODE_SHADE : + -1; +} + +const char* Parameters::wbModeEnumToString(uint8_t wbMode) { + switch (wbMode) { + case ANDROID_CONTROL_AWB_MODE_AUTO: + return CameraParameters::WHITE_BALANCE_AUTO; + case ANDROID_CONTROL_AWB_MODE_INCANDESCENT: + return CameraParameters::WHITE_BALANCE_INCANDESCENT; + case ANDROID_CONTROL_AWB_MODE_FLUORESCENT: + return CameraParameters::WHITE_BALANCE_FLUORESCENT; + case ANDROID_CONTROL_AWB_MODE_WARM_FLUORESCENT: + return CameraParameters::WHITE_BALANCE_WARM_FLUORESCENT; + case ANDROID_CONTROL_AWB_MODE_DAYLIGHT: + return CameraParameters::WHITE_BALANCE_DAYLIGHT; + case ANDROID_CONTROL_AWB_MODE_CLOUDY_DAYLIGHT: + return CameraParameters::WHITE_BALANCE_CLOUDY_DAYLIGHT; + case ANDROID_CONTROL_AWB_MODE_TWILIGHT: + return CameraParameters::WHITE_BALANCE_TWILIGHT; + case ANDROID_CONTROL_AWB_MODE_SHADE: + return CameraParameters::WHITE_BALANCE_SHADE; + default: + ALOGE("%s: Unknown AWB mode enum: %d", + __FUNCTION__, wbMode); + return "unknown"; + } +} + +int Parameters::effectModeStringToEnum(const char *effectMode) { + return + !effectMode ? + ANDROID_CONTROL_EFFECT_MODE_OFF : + !strcmp(effectMode, CameraParameters::EFFECT_NONE) ? + ANDROID_CONTROL_EFFECT_MODE_OFF : + !strcmp(effectMode, CameraParameters::EFFECT_MONO) ? + ANDROID_CONTROL_EFFECT_MODE_MONO : + !strcmp(effectMode, CameraParameters::EFFECT_NEGATIVE) ? + ANDROID_CONTROL_EFFECT_MODE_NEGATIVE : + !strcmp(effectMode, CameraParameters::EFFECT_SOLARIZE) ? + ANDROID_CONTROL_EFFECT_MODE_SOLARIZE : + !strcmp(effectMode, CameraParameters::EFFECT_SEPIA) ? + ANDROID_CONTROL_EFFECT_MODE_SEPIA : + !strcmp(effectMode, CameraParameters::EFFECT_POSTERIZE) ? + ANDROID_CONTROL_EFFECT_MODE_POSTERIZE : + !strcmp(effectMode, CameraParameters::EFFECT_WHITEBOARD) ? + ANDROID_CONTROL_EFFECT_MODE_WHITEBOARD : + !strcmp(effectMode, CameraParameters::EFFECT_BLACKBOARD) ? + ANDROID_CONTROL_EFFECT_MODE_BLACKBOARD : + !strcmp(effectMode, CameraParameters::EFFECT_AQUA) ? + ANDROID_CONTROL_EFFECT_MODE_AQUA : + -1; +} + +int Parameters::abModeStringToEnum(const char *abMode) { + return + !abMode ? + ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO : + !strcmp(abMode, CameraParameters::ANTIBANDING_AUTO) ? + ANDROID_CONTROL_AE_ANTIBANDING_MODE_AUTO : + !strcmp(abMode, CameraParameters::ANTIBANDING_OFF) ? + ANDROID_CONTROL_AE_ANTIBANDING_MODE_OFF : + !strcmp(abMode, CameraParameters::ANTIBANDING_50HZ) ? + ANDROID_CONTROL_AE_ANTIBANDING_MODE_50HZ : + !strcmp(abMode, CameraParameters::ANTIBANDING_60HZ) ? + ANDROID_CONTROL_AE_ANTIBANDING_MODE_60HZ : + -1; +} + +int Parameters::sceneModeStringToEnum(const char *sceneMode) { + return + !sceneMode ? + ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED : + !strcmp(sceneMode, CameraParameters::SCENE_MODE_AUTO) ? + ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED : + !strcmp(sceneMode, CameraParameters::SCENE_MODE_ACTION) ? + ANDROID_CONTROL_SCENE_MODE_ACTION : + !strcmp(sceneMode, CameraParameters::SCENE_MODE_PORTRAIT) ? + ANDROID_CONTROL_SCENE_MODE_PORTRAIT : + !strcmp(sceneMode, CameraParameters::SCENE_MODE_LANDSCAPE) ? + ANDROID_CONTROL_SCENE_MODE_LANDSCAPE : + !strcmp(sceneMode, CameraParameters::SCENE_MODE_NIGHT) ? + ANDROID_CONTROL_SCENE_MODE_NIGHT : + !strcmp(sceneMode, CameraParameters::SCENE_MODE_NIGHT_PORTRAIT) ? + ANDROID_CONTROL_SCENE_MODE_NIGHT_PORTRAIT : + !strcmp(sceneMode, CameraParameters::SCENE_MODE_THEATRE) ? + ANDROID_CONTROL_SCENE_MODE_THEATRE : + !strcmp(sceneMode, CameraParameters::SCENE_MODE_BEACH) ? + ANDROID_CONTROL_SCENE_MODE_BEACH : + !strcmp(sceneMode, CameraParameters::SCENE_MODE_SNOW) ? + ANDROID_CONTROL_SCENE_MODE_SNOW : + !strcmp(sceneMode, CameraParameters::SCENE_MODE_SUNSET) ? + ANDROID_CONTROL_SCENE_MODE_SUNSET : + !strcmp(sceneMode, CameraParameters::SCENE_MODE_STEADYPHOTO) ? + ANDROID_CONTROL_SCENE_MODE_STEADYPHOTO : + !strcmp(sceneMode, CameraParameters::SCENE_MODE_FIREWORKS) ? + ANDROID_CONTROL_SCENE_MODE_FIREWORKS : + !strcmp(sceneMode, CameraParameters::SCENE_MODE_SPORTS) ? + ANDROID_CONTROL_SCENE_MODE_SPORTS : + !strcmp(sceneMode, CameraParameters::SCENE_MODE_PARTY) ? + ANDROID_CONTROL_SCENE_MODE_PARTY : + !strcmp(sceneMode, CameraParameters::SCENE_MODE_CANDLELIGHT) ? + ANDROID_CONTROL_SCENE_MODE_CANDLELIGHT : + !strcmp(sceneMode, CameraParameters::SCENE_MODE_BARCODE) ? + ANDROID_CONTROL_SCENE_MODE_BARCODE: + -1; +} + +Parameters::Parameters::flashMode_t Parameters::flashModeStringToEnum( + const char *flashMode) { + return + !flashMode ? + Parameters::FLASH_MODE_INVALID : + !strcmp(flashMode, CameraParameters::FLASH_MODE_OFF) ? + Parameters::FLASH_MODE_OFF : + !strcmp(flashMode, CameraParameters::FLASH_MODE_AUTO) ? + Parameters::FLASH_MODE_AUTO : + !strcmp(flashMode, CameraParameters::FLASH_MODE_ON) ? + Parameters::FLASH_MODE_ON : + !strcmp(flashMode, CameraParameters::FLASH_MODE_RED_EYE) ? + Parameters::FLASH_MODE_RED_EYE : + !strcmp(flashMode, CameraParameters::FLASH_MODE_TORCH) ? + Parameters::FLASH_MODE_TORCH : + Parameters::FLASH_MODE_INVALID; +} + +const char *Parameters::flashModeEnumToString(flashMode_t flashMode) { + switch (flashMode) { + case FLASH_MODE_OFF: + return CameraParameters::FLASH_MODE_OFF; + case FLASH_MODE_AUTO: + return CameraParameters::FLASH_MODE_AUTO; + case FLASH_MODE_ON: + return CameraParameters::FLASH_MODE_ON; + case FLASH_MODE_RED_EYE: + return CameraParameters::FLASH_MODE_RED_EYE; + case FLASH_MODE_TORCH: + return CameraParameters::FLASH_MODE_TORCH; + default: + ALOGE("%s: Unknown flash mode enum %d", + __FUNCTION__, flashMode); + return "unknown"; + } +} + +Parameters::Parameters::focusMode_t Parameters::focusModeStringToEnum( + const char *focusMode) { + return + !focusMode ? + Parameters::FOCUS_MODE_INVALID : + !strcmp(focusMode, CameraParameters::FOCUS_MODE_AUTO) ? + Parameters::FOCUS_MODE_AUTO : + !strcmp(focusMode, CameraParameters::FOCUS_MODE_INFINITY) ? + Parameters::FOCUS_MODE_INFINITY : + !strcmp(focusMode, CameraParameters::FOCUS_MODE_MACRO) ? + Parameters::FOCUS_MODE_MACRO : + !strcmp(focusMode, CameraParameters::FOCUS_MODE_FIXED) ? + Parameters::FOCUS_MODE_FIXED : + !strcmp(focusMode, CameraParameters::FOCUS_MODE_EDOF) ? + Parameters::FOCUS_MODE_EDOF : + !strcmp(focusMode, CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO) ? + Parameters::FOCUS_MODE_CONTINUOUS_VIDEO : + !strcmp(focusMode, CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE) ? + Parameters::FOCUS_MODE_CONTINUOUS_PICTURE : + Parameters::FOCUS_MODE_INVALID; +} + +const char *Parameters::focusModeEnumToString(focusMode_t focusMode) { + switch (focusMode) { + case FOCUS_MODE_AUTO: + return CameraParameters::FOCUS_MODE_AUTO; + case FOCUS_MODE_MACRO: + return CameraParameters::FOCUS_MODE_MACRO; + case FOCUS_MODE_CONTINUOUS_VIDEO: + return CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO; + case FOCUS_MODE_CONTINUOUS_PICTURE: + return CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE; + case FOCUS_MODE_EDOF: + return CameraParameters::FOCUS_MODE_EDOF; + case FOCUS_MODE_INFINITY: + return CameraParameters::FOCUS_MODE_INFINITY; + case FOCUS_MODE_FIXED: + return CameraParameters::FOCUS_MODE_FIXED; + default: + ALOGE("%s: Unknown focus mode enum: %d", + __FUNCTION__, focusMode); + return "unknown"; + } +} + +status_t Parameters::parseAreas(const char *areasCStr, + Vector<Parameters::Area> *areas) { + static const size_t NUM_FIELDS = 5; + areas->clear(); + if (areasCStr == NULL) { + // If no key exists, use default (0,0,0,0,0) + areas->push(); + return OK; + } + String8 areasStr(areasCStr); + ssize_t areaStart = areasStr.find("(", 0) + 1; + while (areaStart != 0) { + const char* area = areasStr.string() + areaStart; + char *numEnd; + int vals[NUM_FIELDS]; + for (size_t i = 0; i < NUM_FIELDS; i++) { + errno = 0; + vals[i] = strtol(area, &numEnd, 10); + if (errno || numEnd == area) return BAD_VALUE; + area = numEnd + 1; + } + areas->push(Parameters::Area( + vals[0], vals[1], vals[2], vals[3], vals[4]) ); + areaStart = areasStr.find("(", areaStart) + 1; + } + return OK; +} + +status_t Parameters::validateAreas(const Vector<Parameters::Area> &areas, + size_t maxRegions, + AreaKind areaKind) const { + // Definition of valid area can be found in + // include/camera/CameraParameters.h + if (areas.size() == 0) return BAD_VALUE; + if (areas.size() == 1) { + if (areas[0].left == 0 && + areas[0].top == 0 && + areas[0].right == 0 && + areas[0].bottom == 0 && + areas[0].weight == 0) { + // Single (0,0,0,0,0) entry is always valid (== driver decides) + return OK; + } + } + + // fixed focus can only set (0,0,0,0,0) focus area + if (areaKind == AREA_KIND_FOCUS && focusMode == FOCUS_MODE_FIXED) { + return BAD_VALUE; + } + + if (areas.size() > maxRegions) { + ALOGE("%s: Too many areas requested: %d", + __FUNCTION__, areas.size()); + return BAD_VALUE; + } + + for (Vector<Parameters::Area>::const_iterator a = areas.begin(); + a != areas.end(); a++) { + if (a->weight < 1 || a->weight > 1000) return BAD_VALUE; + if (a->left < -1000 || a->left > 1000) return BAD_VALUE; + if (a->top < -1000 || a->top > 1000) return BAD_VALUE; + if (a->right < -1000 || a->right > 1000) return BAD_VALUE; + if (a->bottom < -1000 || a->bottom > 1000) return BAD_VALUE; + if (a->left >= a->right) return BAD_VALUE; + if (a->top >= a->bottom) return BAD_VALUE; + } + return OK; +} + +bool Parameters::boolFromString(const char *boolStr) { + return !boolStr ? false : + !strcmp(boolStr, CameraParameters::TRUE) ? true : + false; +} + +int Parameters::degToTransform(int degrees, bool mirror) { + if (!mirror) { + if (degrees == 0) return 0; + else if (degrees == 90) return HAL_TRANSFORM_ROT_90; + else if (degrees == 180) return HAL_TRANSFORM_ROT_180; + else if (degrees == 270) return HAL_TRANSFORM_ROT_270; + } else { // Do mirror (horizontal flip) + if (degrees == 0) { // FLIP_H and ROT_0 + return HAL_TRANSFORM_FLIP_H; + } else if (degrees == 90) { // FLIP_H and ROT_90 + return HAL_TRANSFORM_FLIP_H | HAL_TRANSFORM_ROT_90; + } else if (degrees == 180) { // FLIP_H and ROT_180 + return HAL_TRANSFORM_FLIP_V; + } else if (degrees == 270) { // FLIP_H and ROT_270 + return HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_ROT_90; + } + } + ALOGE("%s: Bad input: %d", __FUNCTION__, degrees); + return -1; +} + +int Parameters::cropXToArray(int x) const { + ALOG_ASSERT(x >= 0, "Crop-relative X coordinate = '%d' is out of bounds" + "(lower = 0)", x); + + CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW); + ALOG_ASSERT(x < previewCrop.width, "Crop-relative X coordinate = '%d' " + "is out of bounds (upper = %d)", x, previewCrop.width); + + int ret = x + previewCrop.left; + + ALOG_ASSERT( (ret >= 0 && ret < fastInfo.arrayWidth), + "Calculated pixel array value X = '%d' is out of bounds (upper = %d)", + ret, fastInfo.arrayWidth); + return ret; +} + +int Parameters::cropYToArray(int y) const { + ALOG_ASSERT(y >= 0, "Crop-relative Y coordinate = '%d' is out of bounds " + "(lower = 0)", y); + + CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW); + ALOG_ASSERT(y < previewCrop.height, "Crop-relative Y coordinate = '%d' is " + "out of bounds (upper = %d)", y, previewCrop.height); + + int ret = y + previewCrop.top; + + ALOG_ASSERT( (ret >= 0 && ret < fastInfo.arrayHeight), + "Calculated pixel array value Y = '%d' is out of bounds (upper = %d)", + ret, fastInfo.arrayHeight); + + return ret; + +} + +int Parameters::normalizedXToCrop(int x) const { + CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW); + return (x + 1000) * (previewCrop.width - 1) / 2000; +} + +int Parameters::normalizedYToCrop(int y) const { + CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW); + return (y + 1000) * (previewCrop.height - 1) / 2000; +} + +int Parameters::arrayXToCrop(int x) const { + CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW); + return x - previewCrop.left; +} + +int Parameters::arrayYToCrop(int y) const { + CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW); + return y - previewCrop.top; +} + +int Parameters::cropXToNormalized(int x) const { + CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW); + return x * 2000 / (previewCrop.width - 1) - 1000; +} + +int Parameters::cropYToNormalized(int y) const { + CropRegion previewCrop = calculateCropRegion(CropRegion::OUTPUT_PREVIEW); + return y * 2000 / (previewCrop.height - 1) - 1000; +} + +int Parameters::arrayXToNormalized(int width) const { + int ret = cropXToNormalized(arrayXToCrop(width)); + + ALOG_ASSERT(ret >= -1000, "Calculated normalized value out of " + "lower bounds %d", ret); + ALOG_ASSERT(ret <= 1000, "Calculated normalized value out of " + "upper bounds %d", ret); + + // Work-around for HAL pre-scaling the coordinates themselves + if (quirks.meteringCropRegion) { + return width * 2000 / (fastInfo.arrayWidth - 1) - 1000; + } + + return ret; +} + +int Parameters::arrayYToNormalized(int height) const { + int ret = cropYToNormalized(arrayYToCrop(height)); + + ALOG_ASSERT(ret >= -1000, "Calculated normalized value out of lower bounds" + " %d", ret); + ALOG_ASSERT(ret <= 1000, "Calculated normalized value out of upper bounds" + " %d", ret); + + // Work-around for HAL pre-scaling the coordinates themselves + if (quirks.meteringCropRegion) { + return height * 2000 / (fastInfo.arrayHeight - 1) - 1000; + } + + return ret; +} + +int Parameters::normalizedXToArray(int x) const { + + // Work-around for HAL pre-scaling the coordinates themselves + if (quirks.meteringCropRegion) { + return (x + 1000) * (fastInfo.arrayWidth - 1) / 2000; + } + + return cropXToArray(normalizedXToCrop(x)); +} + +int Parameters::normalizedYToArray(int y) const { + // Work-around for HAL pre-scaling the coordinates themselves + if (quirks.meteringCropRegion) { + return (y + 1000) * (fastInfo.arrayHeight - 1) / 2000; + } + + return cropYToArray(normalizedYToCrop(y)); +} + +Parameters::CropRegion Parameters::calculateCropRegion( + Parameters::CropRegion::Outputs outputs) const { + + float zoomLeft, zoomTop, zoomWidth, zoomHeight; + + // Need to convert zoom index into a crop rectangle. The rectangle is + // chosen to maximize its area on the sensor + + camera_metadata_ro_entry_t maxDigitalZoom = + staticInfo(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM); + // For each zoom step by how many pixels more do we change the zoom + float zoomIncrement = (maxDigitalZoom.data.f[0] - 1) / + (NUM_ZOOM_STEPS-1); + // The desired activeAreaWidth/cropAreaWidth ratio (or height if h>w) + // via interpolating zoom step into a zoom ratio + float zoomRatio = 1 + zoomIncrement * zoom; + ALOG_ASSERT( (zoomRatio >= 1.f && zoomRatio <= maxDigitalZoom.data.f[0]), + "Zoom ratio calculated out of bounds. Expected 1 - %f, actual: %f", + maxDigitalZoom.data.f[0], zoomRatio); + + ALOGV("Zoom maxDigital=%f, increment=%f, ratio=%f, previewWidth=%d, " + "previewHeight=%d, activeWidth=%d, activeHeight=%d", + maxDigitalZoom.data.f[0], zoomIncrement, zoomRatio, previewWidth, + previewHeight, fastInfo.arrayWidth, fastInfo.arrayHeight); + + /* + * Assumption: On the HAL side each stream buffer calculates its crop + * rectangle as follows: + * cropRect = (zoomLeft, zoomRight, + * zoomWidth, zoomHeight * zoomWidth / outputWidth); + * + * Note that if zoomWidth > bufferWidth, the new cropHeight > zoomHeight + * (we can then get into trouble if the cropHeight > arrayHeight). + * By selecting the zoomRatio based on the smallest outputRatio, we + * guarantee this will never happen. + */ + + // Enumerate all possible output sizes, select the one with the smallest + // aspect ratio + float minOutputWidth, minOutputHeight, minOutputRatio; + { + float outputSizes[][2] = { + { static_cast<float>(previewWidth), + static_cast<float>(previewHeight) }, + { static_cast<float>(videoWidth), + static_cast<float>(videoHeight) }, + { static_cast<float>(jpegThumbSize[0]), + static_cast<float>(jpegThumbSize[1]) }, + { static_cast<float>(pictureWidth), + static_cast<float>(pictureHeight) }, + }; + + minOutputWidth = outputSizes[0][0]; + minOutputHeight = outputSizes[0][1]; + minOutputRatio = minOutputWidth / minOutputHeight; + for (unsigned int i = 0; + i < sizeof(outputSizes) / sizeof(outputSizes[0]); + ++i) { + + // skip over outputs we don't want to consider for the crop region + if ( !((1 << i) & outputs) ) { + continue; + } + + float outputWidth = outputSizes[i][0]; + float outputHeight = outputSizes[i][1]; + float outputRatio = outputWidth / outputHeight; + + if (minOutputRatio > outputRatio) { + minOutputRatio = outputRatio; + minOutputWidth = outputWidth; + minOutputHeight = outputHeight; + } + + // and then use this output ratio instead of preview output ratio + ALOGV("Enumerating output ratio %f = %f / %f, min is %f", + outputRatio, outputWidth, outputHeight, minOutputRatio); + } + } + + /* Ensure that the width/height never go out of bounds + * by scaling across a diffent dimension if an out-of-bounds + * possibility exists. + * + * e.g. if the previewratio < arrayratio and e.g. zoomratio = 1.0, then by + * calculating the zoomWidth from zoomHeight we'll actually get a + * zoomheight > arrayheight + */ + float arrayRatio = 1.f * fastInfo.arrayWidth / fastInfo.arrayHeight; + if (minOutputRatio >= arrayRatio) { + // Adjust the height based on the width + zoomWidth = fastInfo.arrayWidth / zoomRatio; + zoomHeight = zoomWidth * + minOutputHeight / minOutputWidth; + + } else { + // Adjust the width based on the height + zoomHeight = fastInfo.arrayHeight / zoomRatio; + zoomWidth = zoomHeight * + minOutputWidth / minOutputHeight; + } + // centering the zoom area within the active area + zoomLeft = (fastInfo.arrayWidth - zoomWidth) / 2; + zoomTop = (fastInfo.arrayHeight - zoomHeight) / 2; + + ALOGV("Crop region calculated (x=%d,y=%d,w=%f,h=%f) for zoom=%d", + (int32_t)zoomLeft, (int32_t)zoomTop, zoomWidth, zoomHeight, this->zoom); + + + CropRegion crop = { zoomLeft, zoomTop, zoomWidth, zoomHeight }; + return crop; +} + +int32_t Parameters::fpsFromRange(int32_t min, int32_t max) const { + return max; +} + +}; // namespace camera2 +}; // namespace android diff --git a/services/camera/libcameraservice/camera2/Parameters.h b/services/camera/libcameraservice/camera2/Parameters.h new file mode 100644 index 00000000..dc2cdcf9 --- /dev/null +++ b/services/camera/libcameraservice/camera2/Parameters.h @@ -0,0 +1,365 @@ +/* + * 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_SERVERS_CAMERA_CAMERA2PARAMETERS_H +#define ANDROID_SERVERS_CAMERA_CAMERA2PARAMETERS_H + +#include <system/graphics.h> + +#include <utils/Errors.h> +#include <utils/Mutex.h> +#include <utils/String8.h> +#include <utils/Vector.h> +#include <utils/KeyedVector.h> +#include <camera/CameraParameters.h> + +#include "CameraMetadata.h" + +namespace android { +namespace camera2 { + +/** + * Current camera state; this is the full state of the Camera under the old + * camera API (contents of the CameraParameters object in a more-efficient + * format, plus other state). The enum values are mostly based off the + * corresponding camera2 enums, not the camera1 strings. A few are defined here + * if they don't cleanly map to camera2 values. + */ +struct Parameters { + /** + * Parameters and other state + */ + int cameraId; + int cameraFacing; + + int previewWidth, previewHeight; + int32_t previewFpsRange[2]; + int previewFps; // deprecated, here only for tracking changes + int previewFormat; + + int previewTransform; // set by CAMERA_CMD_SET_DISPLAY_ORIENTATION + + int pictureWidth, pictureHeight; + + int32_t jpegThumbSize[2]; + uint8_t jpegQuality, jpegThumbQuality; + int32_t jpegRotation; + + bool gpsEnabled; + double gpsCoordinates[3]; + int64_t gpsTimestamp; + String8 gpsProcessingMethod; + + uint8_t wbMode; + uint8_t effectMode; + uint8_t antibandingMode; + uint8_t sceneMode; + + enum flashMode_t { + FLASH_MODE_OFF = 0, + FLASH_MODE_AUTO, + FLASH_MODE_ON, + FLASH_MODE_TORCH, + FLASH_MODE_RED_EYE = ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE, + FLASH_MODE_INVALID = -1 + } flashMode; + + enum focusMode_t { + FOCUS_MODE_AUTO = ANDROID_CONTROL_AF_MODE_AUTO, + FOCUS_MODE_MACRO = ANDROID_CONTROL_AF_MODE_MACRO, + FOCUS_MODE_CONTINUOUS_VIDEO = ANDROID_CONTROL_AF_MODE_CONTINUOUS_VIDEO, + FOCUS_MODE_CONTINUOUS_PICTURE = ANDROID_CONTROL_AF_MODE_CONTINUOUS_PICTURE, + FOCUS_MODE_EDOF = ANDROID_CONTROL_AF_MODE_EDOF, + FOCUS_MODE_INFINITY, + FOCUS_MODE_FIXED, + FOCUS_MODE_INVALID = -1 + } focusMode; + + uint8_t focusState; // Latest focus state from HAL + + // For use with triggerAfWithAuto quirk + focusMode_t shadowFocusMode; + + struct Area { + int left, top, right, bottom; + int weight; + Area() {} + Area(int left, int top, int right, int bottom, int weight): + left(left), top(top), right(right), bottom(bottom), + weight(weight) {} + bool isEmpty() const { + return (left == 0) && (top == 0) && (right == 0) && (bottom == 0); + } + }; + Vector<Area> focusingAreas; + + int32_t exposureCompensation; + bool autoExposureLock; + bool autoWhiteBalanceLock; + + Vector<Area> meteringAreas; + + int zoom; + + int videoWidth, videoHeight; + + bool recordingHint; + bool videoStabilization; + + enum lightFxMode_t { + LIGHTFX_NONE = 0, + LIGHTFX_LOWLIGHT, + LIGHTFX_HDR + } lightFx; + + CameraParameters params; + String8 paramsFlattened; + + // These parameters are also part of the camera API-visible state, but not + // directly listed in Camera.Parameters + bool storeMetadataInBuffers; + bool playShutterSound; + bool enableFaceDetect; + + bool enableFocusMoveMessages; + int afTriggerCounter; + int currentAfTriggerId; + bool afInMotion; + + int precaptureTriggerCounter; + + uint32_t previewCallbackFlags; + bool previewCallbackOneShot; + + bool zslMode; + + // Overall camera state + enum State { + DISCONNECTED, + STOPPED, + WAITING_FOR_PREVIEW_WINDOW, + PREVIEW, + RECORD, + STILL_CAPTURE, + VIDEO_SNAPSHOT + } state; + + // Number of zoom steps to simulate + static const unsigned int NUM_ZOOM_STEPS = 30; + + // Full static camera info, object owned by someone else, such as + // Camera2Device. + const CameraMetadata *info; + + // Fast-access static device information; this is a subset of the + // information available through the staticInfo() method, used for + // frequently-accessed values or values that have to be calculated from the + // static information. + struct DeviceInfo { + int32_t arrayWidth; + int32_t arrayHeight; + uint8_t bestFaceDetectMode; + int32_t maxFaces; + struct OverrideModes { + flashMode_t flashMode; + uint8_t wbMode; + focusMode_t focusMode; + OverrideModes(): + flashMode(FLASH_MODE_INVALID), + wbMode(ANDROID_CONTROL_AWB_MODE_OFF), + focusMode(FOCUS_MODE_INVALID) { + } + }; + DefaultKeyedVector<uint8_t, OverrideModes> sceneModeOverrides; + } fastInfo; + + // Quirks information; these are short-lived flags to enable workarounds for + // incomplete HAL implementations + struct Quirks { + bool triggerAfWithAuto; + bool useZslFormat; + bool meteringCropRegion; + } quirks; + + /** + * Parameter manipulation and setup methods + */ + + Parameters(int cameraId, int cameraFacing); + ~Parameters(); + + // Sets up default parameters + status_t initialize(const CameraMetadata *info); + + // Build fast-access device static info from static info + status_t buildFastInfo(); + // Query for quirks from static info + status_t buildQuirks(); + + // Get entry from camera static characteristics information. min/maxCount + // are used for error checking the number of values in the entry. 0 for + // max/minCount means to do no bounds check in that direction. In case of + // error, the entry data pointer is null and the count is 0. + camera_metadata_ro_entry_t staticInfo(uint32_t tag, + size_t minCount=0, size_t maxCount=0) const; + + // Validate and update camera parameters based on new settings + status_t set(const String8 ¶mString); + + // Retrieve the current settings + String8 get() const; + + // Update passed-in request for common parameters + status_t updateRequest(CameraMetadata *request) const; + + // Add/update JPEG entries in metadata + status_t updateRequestJpeg(CameraMetadata *request) const; + + // Calculate the crop region rectangle based on current stream sizes + struct CropRegion { + float left; + float top; + float width; + float height; + + enum Outputs { + OUTPUT_PREVIEW = 0x01, + OUTPUT_VIDEO = 0x02, + OUTPUT_JPEG_THUMBNAIL = 0x04, + OUTPUT_PICTURE = 0x08, + }; + }; + CropRegion calculateCropRegion(CropRegion::Outputs outputs) const; + + // Static methods for debugging and converting between camera1 and camera2 + // parameters + + static const char *getStateName(State state); + + static int formatStringToEnum(const char *format); + static const char *formatEnumToString(int format); + + static int wbModeStringToEnum(const char *wbMode); + static const char* wbModeEnumToString(uint8_t wbMode); + static int effectModeStringToEnum(const char *effectMode); + static int abModeStringToEnum(const char *abMode); + static int sceneModeStringToEnum(const char *sceneMode); + static flashMode_t flashModeStringToEnum(const char *flashMode); + static const char* flashModeEnumToString(flashMode_t flashMode); + static focusMode_t focusModeStringToEnum(const char *focusMode); + static const char* focusModeEnumToString(focusMode_t focusMode); + static status_t parseAreas(const char *areasCStr, + Vector<Area> *areas); + + enum AreaKind + { + AREA_KIND_FOCUS, + AREA_KIND_METERING + }; + status_t validateAreas(const Vector<Area> &areas, + size_t maxRegions, + AreaKind areaKind) const; + static bool boolFromString(const char *boolStr); + + // Map from camera orientation + facing to gralloc transform enum + static int degToTransform(int degrees, bool mirror); + + // API specifies FPS ranges are done in fixed point integer, with LSB = 0.001. + // Note that this doesn't apply to the (deprecated) single FPS value. + static const int kFpsToApiScale = 1000; + + // Transform between (-1000,-1000)-(1000,1000) normalized coords from camera + // API and HAL2 (0,0)-(activePixelArray.width/height) coordinates + int arrayXToNormalized(int width) const; + int arrayYToNormalized(int height) const; + int normalizedXToArray(int x) const; + int normalizedYToArray(int y) const; + + struct Range { + int min; + int max; + }; + + int32_t fpsFromRange(int32_t min, int32_t max) const; + +private: + + // Convert between HAL2 sensor array coordinates and + // viewfinder crop-region relative array coordinates + int cropXToArray(int x) const; + int cropYToArray(int y) const; + int arrayXToCrop(int x) const; + int arrayYToCrop(int y) const; + + // Convert between viewfinder crop-region relative array coordinates + // and camera API (-1000,1000)-(1000,1000) normalized coords + int cropXToNormalized(int x) const; + int cropYToNormalized(int y) const; + int normalizedXToCrop(int x) const; + int normalizedYToCrop(int y) const; +}; + +// This class encapsulates the Parameters class so that it can only be accessed +// by constructing a Lock object, which locks the SharedParameter's mutex. +class SharedParameters { + public: + SharedParameters(int cameraId, int cameraFacing): + mParameters(cameraId, cameraFacing) { + } + + template<typename S, typename P> + class BaseLock { + public: + BaseLock(S &p): + mParameters(p.mParameters), + mSharedParameters(p) { + mSharedParameters.mLock.lock(); + } + + ~BaseLock() { + mSharedParameters.mLock.unlock(); + } + P &mParameters; + private: + // Disallow copying, default construction + BaseLock(); + BaseLock(const BaseLock &); + BaseLock &operator=(const BaseLock &); + S &mSharedParameters; + }; + typedef BaseLock<SharedParameters, Parameters> Lock; + typedef BaseLock<const SharedParameters, const Parameters> ReadLock; + + // Access static info, read-only and immutable, so no lock needed + camera_metadata_ro_entry_t staticInfo(uint32_t tag, + size_t minCount=0, size_t maxCount=0) const { + return mParameters.staticInfo(tag, minCount, maxCount); + } + + // Only use for dumping or other debugging + const Parameters &unsafeAccess() { + return mParameters; + } + private: + Parameters mParameters; + mutable Mutex mLock; +}; + + +}; // namespace camera2 +}; // namespace android + +#endif diff --git a/services/camera/libcameraservice/camera2/StreamingProcessor.cpp b/services/camera/libcameraservice/camera2/StreamingProcessor.cpp new file mode 100644 index 00000000..207f7807 --- /dev/null +++ b/services/camera/libcameraservice/camera2/StreamingProcessor.cpp @@ -0,0 +1,657 @@ +/* + * 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. + */ + +#define LOG_TAG "Camera2-StreamingProcessor" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> +#include <utils/Trace.h> +#include <gui/SurfaceTextureClient.h> +#include <media/hardware/MetadataBufferType.h> + +#include "StreamingProcessor.h" +#include "Camera2Heap.h" +#include "../Camera2Client.h" +#include "../Camera2Device.h" + +namespace android { +namespace camera2 { + +StreamingProcessor::StreamingProcessor(wp<Camera2Client> client): + mClient(client), + mActiveRequest(NONE), + mPreviewRequestId(Camera2Client::kPreviewRequestIdStart), + mPreviewStreamId(NO_STREAM), + mRecordingRequestId(Camera2Client::kRecordingRequestIdStart), + mRecordingStreamId(NO_STREAM), + mRecordingHeapCount(kDefaultRecordingHeapCount) +{ + +} + +StreamingProcessor::~StreamingProcessor() { + deletePreviewStream(); + deleteRecordingStream(); +} + +status_t StreamingProcessor::setPreviewWindow(sp<ANativeWindow> window) { + ATRACE_CALL(); + status_t res; + + res = deletePreviewStream(); + if (res != OK) return res; + + Mutex::Autolock m(mMutex); + + mPreviewWindow = window; + + return OK; +} + +bool StreamingProcessor::haveValidPreviewWindow() const { + Mutex::Autolock m(mMutex); + return mPreviewWindow != 0; +} + +status_t StreamingProcessor::updatePreviewRequest(const Parameters ¶ms) { + ATRACE_CALL(); + status_t res; + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return INVALID_OPERATION; + + Mutex::Autolock m(mMutex); + if (mPreviewRequest.entryCount() == 0) { + res = client->getCameraDevice()->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW, + &mPreviewRequest); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to create default preview request: " + "%s (%d)", __FUNCTION__, client->getCameraId(), strerror(-res), res); + return res; + } + } + + res = params.updateRequest(&mPreviewRequest); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update common entries of preview " + "request: %s (%d)", __FUNCTION__, client->getCameraId(), + strerror(-res), res); + return res; + } + + res = mPreviewRequest.update(ANDROID_REQUEST_ID, + &mPreviewRequestId, 1); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update request id for preview: %s (%d)", + __FUNCTION__, client->getCameraId(), strerror(-res), res); + return res; + } + + return OK; +} + +status_t StreamingProcessor::updatePreviewStream(const Parameters ¶ms) { + ATRACE_CALL(); + Mutex::Autolock m(mMutex); + + status_t res; + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return INVALID_OPERATION; + sp<Camera2Device> device = client->getCameraDevice(); + + if (mPreviewStreamId != NO_STREAM) { + // Check if stream parameters have to change + uint32_t currentWidth, currentHeight; + res = device->getStreamInfo(mPreviewStreamId, + ¤tWidth, ¤tHeight, 0); + if (res != OK) { + ALOGE("%s: Camera %d: Error querying preview stream info: " + "%s (%d)", __FUNCTION__, client->getCameraId(), strerror(-res), res); + return res; + } + if (currentWidth != (uint32_t)params.previewWidth || + currentHeight != (uint32_t)params.previewHeight) { + ALOGV("%s: Camera %d: Preview size switch: %d x %d -> %d x %d", + __FUNCTION__, client->getCameraId(), currentWidth, currentHeight, + params.previewWidth, params.previewHeight); + res = device->waitUntilDrained(); + if (res != OK) { + ALOGE("%s: Camera %d: Error waiting for preview to drain: " + "%s (%d)", __FUNCTION__, client->getCameraId(), strerror(-res), res); + return res; + } + res = device->deleteStream(mPreviewStreamId); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to delete old output stream " + "for preview: %s (%d)", __FUNCTION__, client->getCameraId(), + strerror(-res), res); + return res; + } + mPreviewStreamId = NO_STREAM; + } + } + + if (mPreviewStreamId == NO_STREAM) { + res = device->createStream(mPreviewWindow, + params.previewWidth, params.previewHeight, + CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, 0, + &mPreviewStreamId); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to create preview stream: %s (%d)", + __FUNCTION__, client->getCameraId(), strerror(-res), res); + return res; + } + } + + res = device->setStreamTransform(mPreviewStreamId, + params.previewTransform); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to set preview stream transform: " + "%s (%d)", __FUNCTION__, client->getCameraId(), strerror(-res), res); + return res; + } + + return OK; +} + +status_t StreamingProcessor::deletePreviewStream() { + ATRACE_CALL(); + status_t res; + + Mutex::Autolock m(mMutex); + + if (mPreviewStreamId != NO_STREAM) { + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return INVALID_OPERATION; + sp<Camera2Device> device = client->getCameraDevice(); + + ALOGV("%s: for cameraId %d on streamId %d", + __FUNCTION__, client->getCameraId(), mPreviewStreamId); + + res = device->waitUntilDrained(); + if (res != OK) { + ALOGE("%s: Error waiting for preview to drain: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + res = device->deleteStream(mPreviewStreamId); + if (res != OK) { + ALOGE("%s: Unable to delete old preview stream: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + mPreviewStreamId = NO_STREAM; + } + return OK; +} + +int StreamingProcessor::getPreviewStreamId() const { + Mutex::Autolock m(mMutex); + return mPreviewStreamId; +} + +status_t StreamingProcessor::setRecordingBufferCount(size_t count) { + ATRACE_CALL(); + // 32 is the current upper limit on the video buffer count for BufferQueue + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return INVALID_OPERATION; + if (count > 32) { + ALOGE("%s: Camera %d: Error setting %d as video buffer count value", + __FUNCTION__, client->getCameraId(), count); + return BAD_VALUE; + } + + Mutex::Autolock m(mMutex); + + // Need to reallocate memory for heap + if (mRecordingHeapCount != count) { + if (mRecordingHeap != 0) { + mRecordingHeap.clear(); + mRecordingHeap = NULL; + } + mRecordingHeapCount = count; + } + + return OK; +} + +status_t StreamingProcessor::updateRecordingRequest(const Parameters ¶ms) { + ATRACE_CALL(); + status_t res; + Mutex::Autolock m(mMutex); + + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return INVALID_OPERATION; + + if (mRecordingRequest.entryCount() == 0) { + res = client->getCameraDevice()->createDefaultRequest(CAMERA2_TEMPLATE_VIDEO_RECORD, + &mRecordingRequest); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to create default recording request:" + " %s (%d)", __FUNCTION__, client->getCameraId(), strerror(-res), res); + return res; + } + } + + res = params.updateRequest(&mRecordingRequest); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update common entries of recording " + "request: %s (%d)", __FUNCTION__, client->getCameraId(), + strerror(-res), res); + return res; + } + + res = mRecordingRequest.update(ANDROID_REQUEST_ID, + &mRecordingRequestId, 1); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update request id for request: %s (%d)", + __FUNCTION__, client->getCameraId(), strerror(-res), res); + return res; + } + + return OK; +} + +status_t StreamingProcessor::updateRecordingStream(const Parameters ¶ms) { + ATRACE_CALL(); + status_t res; + Mutex::Autolock m(mMutex); + + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return INVALID_OPERATION; + sp<Camera2Device> device = client->getCameraDevice(); + + if (mRecordingConsumer == 0) { + // 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. + mRecordingConsumer = new BufferItemConsumer( + GRALLOC_USAGE_HW_VIDEO_ENCODER, + mRecordingHeapCount + 1, + true); + mRecordingConsumer->setFrameAvailableListener(this); + mRecordingConsumer->setName(String8("Camera2-RecordingConsumer")); + mRecordingWindow = new SurfaceTextureClient( + mRecordingConsumer->getProducerInterface()); + // Allocate memory later, since we don't know buffer size until receipt + } + + if (mRecordingStreamId != NO_STREAM) { + // Check if stream parameters have to change + uint32_t currentWidth, currentHeight; + res = device->getStreamInfo(mRecordingStreamId, + ¤tWidth, ¤tHeight, 0); + if (res != OK) { + ALOGE("%s: Camera %d: Error querying recording output stream info: " + "%s (%d)", __FUNCTION__, client->getCameraId(), + strerror(-res), res); + return res; + } + if (currentWidth != (uint32_t)params.videoWidth || + currentHeight != (uint32_t)params.videoHeight) { + // TODO: Should wait to be sure previous recording has finished + res = device->deleteStream(mRecordingStreamId); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to delete old output stream " + "for recording: %s (%d)", __FUNCTION__, + client->getCameraId(), strerror(-res), res); + return res; + } + mRecordingStreamId = NO_STREAM; + } + } + + if (mRecordingStreamId == NO_STREAM) { + mRecordingFrameCount = 0; + res = device->createStream(mRecordingWindow, + params.videoWidth, params.videoHeight, + CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, 0, &mRecordingStreamId); + if (res != OK) { + ALOGE("%s: Camera %d: Can't create output stream for recording: " + "%s (%d)", __FUNCTION__, client->getCameraId(), + strerror(-res), res); + return res; + } + } + + return OK; +} + +status_t StreamingProcessor::deleteRecordingStream() { + ATRACE_CALL(); + status_t res; + + Mutex::Autolock m(mMutex); + + if (mRecordingStreamId != NO_STREAM) { + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return INVALID_OPERATION; + sp<Camera2Device> device = client->getCameraDevice(); + + res = device->waitUntilDrained(); + if (res != OK) { + ALOGE("%s: Error waiting for HAL to drain: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + res = device->deleteStream(mRecordingStreamId); + if (res != OK) { + ALOGE("%s: Unable to delete recording stream: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + mRecordingStreamId = NO_STREAM; + } + return OK; +} + +int StreamingProcessor::getRecordingStreamId() const { + return mRecordingStreamId; +} + +status_t StreamingProcessor::startStream(StreamType type, + const Vector<uint8_t> &outputStreams) { + ATRACE_CALL(); + status_t res; + + if (type == NONE) return INVALID_OPERATION; + + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return INVALID_OPERATION; + + ALOGV("%s: Camera %d: type = %d", __FUNCTION__, client->getCameraId(), type); + + Mutex::Autolock m(mMutex); + + CameraMetadata &request = (type == PREVIEW) ? + mPreviewRequest : mRecordingRequest; + + res = request.update( + ANDROID_REQUEST_OUTPUT_STREAMS, + outputStreams); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to set up preview request: %s (%d)", + __FUNCTION__, client->getCameraId(), strerror(-res), res); + return res; + } + + res = request.sort(); + if (res != OK) { + ALOGE("%s: Camera %d: Error sorting preview request: %s (%d)", + __FUNCTION__, client->getCameraId(), strerror(-res), res); + return res; + } + + res = client->getCameraDevice()->setStreamingRequest(request); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to set preview request to start preview: " + "%s (%d)", + __FUNCTION__, client->getCameraId(), strerror(-res), res); + return res; + } + mActiveRequest = type; + + return OK; +} + +status_t StreamingProcessor::stopStream() { + ATRACE_CALL(); + status_t res; + + Mutex::Autolock m(mMutex); + + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return INVALID_OPERATION; + sp<Camera2Device> device = client->getCameraDevice(); + + res = device->clearStreamingRequest(); + if (res != OK) { + ALOGE("%s: Camera %d: Can't clear stream request: %s (%d)", + __FUNCTION__, client->getCameraId(), strerror(-res), res); + return res; + } + mActiveRequest = NONE; + + return OK; +} + +int32_t StreamingProcessor::getActiveRequestId() const { + Mutex::Autolock m(mMutex); + switch (mActiveRequest) { + case NONE: + return 0; + case PREVIEW: + return mPreviewRequestId; + case RECORD: + return mRecordingRequestId; + default: + ALOGE("%s: Unexpected mode %d", __FUNCTION__, mActiveRequest); + return 0; + } +} + +status_t StreamingProcessor::incrementStreamingIds() { + ATRACE_CALL(); + Mutex::Autolock m(mMutex); + + status_t res; + mPreviewRequestId++; + if (mPreviewRequestId >= Camera2Client::kPreviewRequestIdEnd) { + mPreviewRequestId = Camera2Client::kPreviewRequestIdStart; + } + mRecordingRequestId++; + if (mRecordingRequestId >= Camera2Client::kRecordingRequestIdEnd) { + mRecordingRequestId = Camera2Client::kRecordingRequestIdStart; + } + return OK; +} + +void StreamingProcessor::onFrameAvailable() { + ATRACE_CALL(); + status_t res; + sp<Camera2Heap> recordingHeap; + size_t heapIdx = 0; + nsecs_t timestamp; + + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return; + + { + /* acquire SharedParameters before mMutex so we don't dead lock + with Camera2Client code calling into StreamingProcessor */ + SharedParameters::Lock l(client->getParameters()); + Mutex::Autolock m(mMutex); + BufferItemConsumer::BufferItem imgBuffer; + res = mRecordingConsumer->acquireBuffer(&imgBuffer); + if (res != OK) { + ALOGE("%s: Camera %d: Error receiving recording buffer: %s (%d)", + __FUNCTION__, client->getCameraId(), strerror(-res), res); + return; + } + timestamp = imgBuffer.mTimestamp; + + mRecordingFrameCount++; + ALOGV("OnRecordingFrame: Frame %d", mRecordingFrameCount); + + // TODO: Signal errors here upstream + if (l.mParameters.state != Parameters::RECORD && + l.mParameters.state != Parameters::VIDEO_SNAPSHOT) { + ALOGV("%s: Camera %d: Discarding recording image buffers " + "received after recording done", __FUNCTION__, + client->getCameraId()); + mRecordingConsumer->releaseBuffer(imgBuffer); + return; + } + + if (mRecordingHeap == 0) { + const size_t bufferSize = 4 + sizeof(buffer_handle_t); + ALOGV("%s: Camera %d: Creating recording heap with %d buffers of " + "size %d bytes", __FUNCTION__, client->getCameraId(), + mRecordingHeapCount, bufferSize); + + mRecordingHeap = new Camera2Heap(bufferSize, mRecordingHeapCount, + "Camera2Client::RecordingHeap"); + if (mRecordingHeap->mHeap->getSize() == 0) { + ALOGE("%s: Camera %d: Unable to allocate memory for recording", + __FUNCTION__, client->getCameraId()); + mRecordingConsumer->releaseBuffer(imgBuffer); + return; + } + for (size_t i = 0; i < mRecordingBuffers.size(); i++) { + if (mRecordingBuffers[i].mBuf != + BufferItemConsumer::INVALID_BUFFER_SLOT) { + ALOGE("%s: Camera %d: Non-empty recording buffers list!", + __FUNCTION__, client->getCameraId()); + } + } + mRecordingBuffers.clear(); + mRecordingBuffers.setCapacity(mRecordingHeapCount); + mRecordingBuffers.insertAt(0, mRecordingHeapCount); + + mRecordingHeapHead = 0; + mRecordingHeapFree = mRecordingHeapCount; + } + + if ( mRecordingHeapFree == 0) { + ALOGE("%s: Camera %d: No free recording buffers, dropping frame", + __FUNCTION__, client->getCameraId()); + mRecordingConsumer->releaseBuffer(imgBuffer); + return; + } + + heapIdx = mRecordingHeapHead; + mRecordingHeapHead = (mRecordingHeapHead + 1) % mRecordingHeapCount; + mRecordingHeapFree--; + + ALOGV("%s: Camera %d: Timestamp %lld", + __FUNCTION__, client->getCameraId(), timestamp); + + ssize_t offset; + size_t size; + sp<IMemoryHeap> heap = + mRecordingHeap->mBuffers[heapIdx]->getMemory(&offset, + &size); + + uint8_t *data = (uint8_t*)heap->getBase() + offset; + uint32_t type = kMetadataBufferTypeGrallocSource; + *((uint32_t*)data) = type; + *((buffer_handle_t*)(data + 4)) = imgBuffer.mGraphicBuffer->handle; + ALOGV("%s: Camera %d: Sending out buffer_handle_t %p", + __FUNCTION__, client->getCameraId(), + imgBuffer.mGraphicBuffer->handle); + mRecordingBuffers.replaceAt(imgBuffer, heapIdx); + recordingHeap = mRecordingHeap; + } + + // Call outside locked parameters to allow re-entrancy from notification + Camera2Client::SharedCameraClient::Lock l(client->mSharedCameraClient); + if (l.mCameraClient != 0) { + l.mCameraClient->dataCallbackTimestamp(timestamp, + CAMERA_MSG_VIDEO_FRAME, + recordingHeap->mBuffers[heapIdx]); + } +} + +void StreamingProcessor::releaseRecordingFrame(const sp<IMemory>& mem) { + ATRACE_CALL(); + status_t res; + + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return; + + Mutex::Autolock m(mMutex); + // Make sure this is for the current heap + ssize_t offset; + size_t size; + sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); + if (heap->getHeapID() != mRecordingHeap->mHeap->getHeapID()) { + ALOGW("%s: Camera %d: Mismatched heap ID, ignoring release " + "(got %x, expected %x)", __FUNCTION__, client->getCameraId(), + heap->getHeapID(), mRecordingHeap->mHeap->getHeapID()); + return; + } + uint8_t *data = (uint8_t*)heap->getBase() + offset; + uint32_t type = *(uint32_t*)data; + if (type != kMetadataBufferTypeGrallocSource) { + ALOGE("%s: Camera %d: Recording frame type invalid (got %x, expected %x)", + __FUNCTION__, client->getCameraId(), type, + kMetadataBufferTypeGrallocSource); + return; + } + + // Release the buffer back to the recording queue + + buffer_handle_t imgHandle = *(buffer_handle_t*)(data + 4); + + size_t itemIndex; + for (itemIndex = 0; itemIndex < mRecordingBuffers.size(); itemIndex++) { + const BufferItemConsumer::BufferItem item = + mRecordingBuffers[itemIndex]; + if (item.mBuf != BufferItemConsumer::INVALID_BUFFER_SLOT && + item.mGraphicBuffer->handle == imgHandle) { + break; + } + } + if (itemIndex == mRecordingBuffers.size()) { + ALOGE("%s: Camera %d: Can't find buffer_handle_t %p in list of " + "outstanding buffers", __FUNCTION__, client->getCameraId(), + imgHandle); + return; + } + + ALOGV("%s: Camera %d: Freeing buffer_handle_t %p", __FUNCTION__, + client->getCameraId(), imgHandle); + + res = mRecordingConsumer->releaseBuffer(mRecordingBuffers[itemIndex]); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to free recording frame " + "(buffer_handle_t: %p): %s (%d)", __FUNCTION__, + client->getCameraId(), imgHandle, strerror(-res), res); + return; + } + mRecordingBuffers.replaceAt(itemIndex); + + mRecordingHeapFree++; +} + + +status_t StreamingProcessor::dump(int fd, const Vector<String16>& args) { + String8 result; + + result.append(" Current requests:\n"); + if (mPreviewRequest.entryCount() != 0) { + result.append(" Preview request:\n"); + write(fd, result.string(), result.size()); + mPreviewRequest.dump(fd, 2, 6); + } else { + result.append(" Preview request: undefined\n"); + write(fd, result.string(), result.size()); + } + + if (mRecordingRequest.entryCount() != 0) { + result = " Recording request:\n"; + write(fd, result.string(), result.size()); + mRecordingRequest.dump(fd, 2, 6); + } else { + result = " Recording request: undefined\n"; + write(fd, result.string(), result.size()); + } + + return OK; +} + +}; // namespace camera2 +}; // namespace android diff --git a/services/camera/libcameraservice/camera2/StreamingProcessor.h b/services/camera/libcameraservice/camera2/StreamingProcessor.h new file mode 100644 index 00000000..96b100f0 --- /dev/null +++ b/services/camera/libcameraservice/camera2/StreamingProcessor.h @@ -0,0 +1,118 @@ +/* + * 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_SERVERS_CAMERA_CAMERA2_STREAMINGPROCESSOR_H +#define ANDROID_SERVERS_CAMERA_CAMERA2_STREAMINGPROCESSOR_H + +#include <utils/Mutex.h> +#include <utils/String16.h> +#include <gui/BufferItemConsumer.h> + +#include "Parameters.h" +#include "CameraMetadata.h" + +namespace android { + +class Camera2Client; +class IMemory; + +namespace camera2 { + +class Camera2Heap; + +/** + * Management and processing for preview and recording streams + */ +class StreamingProcessor: public BufferItemConsumer::FrameAvailableListener { + public: + StreamingProcessor(wp<Camera2Client> client); + ~StreamingProcessor(); + + status_t setPreviewWindow(sp<ANativeWindow> window); + + bool haveValidPreviewWindow() const; + + status_t updatePreviewRequest(const Parameters ¶ms); + status_t updatePreviewStream(const Parameters ¶ms); + status_t deletePreviewStream(); + int getPreviewStreamId() const; + + status_t setRecordingBufferCount(size_t count); + status_t updateRecordingRequest(const Parameters ¶ms); + status_t updateRecordingStream(const Parameters ¶ms); + status_t deleteRecordingStream(); + int getRecordingStreamId() const; + + enum StreamType { + NONE, + PREVIEW, + RECORD + }; + status_t startStream(StreamType type, + const Vector<uint8_t> &outputStreams); + + status_t stopStream(); + + // Returns the request ID for the currently streaming request + // Returns 0 if there is no active request. + status_t getActiveRequestId() const; + status_t incrementStreamingIds(); + + // Callback for new recording frames from HAL + virtual void onFrameAvailable(); + // Callback from stagefright which returns used recording frames + void releaseRecordingFrame(const sp<IMemory>& mem); + + status_t dump(int fd, const Vector<String16>& args); + + private: + mutable Mutex mMutex; + + enum { + NO_STREAM = -1 + }; + + wp<Camera2Client> mClient; + + StreamType mActiveRequest; + + // Preview-related members + int32_t mPreviewRequestId; + int mPreviewStreamId; + CameraMetadata mPreviewRequest; + sp<ANativeWindow> mPreviewWindow; + + // Recording-related members + int32_t mRecordingRequestId; + int mRecordingStreamId; + int mRecordingFrameCount; + sp<BufferItemConsumer> mRecordingConsumer; + sp<ANativeWindow> mRecordingWindow; + CameraMetadata mRecordingRequest; + sp<camera2::Camera2Heap> mRecordingHeap; + + static const size_t kDefaultRecordingHeapCount = 8; + size_t mRecordingHeapCount; + Vector<BufferItemConsumer::BufferItem> mRecordingBuffers; + size_t mRecordingHeapHead, mRecordingHeapFree; + +}; + + +}; // namespace camera2 +}; // namespace android + +#endif diff --git a/services/camera/libcameraservice/camera2/ZslProcessor.cpp b/services/camera/libcameraservice/camera2/ZslProcessor.cpp new file mode 100644 index 00000000..1937955a --- /dev/null +++ b/services/camera/libcameraservice/camera2/ZslProcessor.cpp @@ -0,0 +1,538 @@ +/* + * 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. + */ + +#define LOG_TAG "Camera2-ZslProcessor" +#define ATRACE_TAG ATRACE_TAG_CAMERA +//#define LOG_NDEBUG 0 +//#define LOG_NNDEBUG 0 + +#ifdef LOG_NNDEBUG +#define ALOGVV(...) ALOGV(__VA_ARGS__) +#else +#define ALOGVV(...) ((void)0) +#endif + +#include <utils/Log.h> +#include <utils/Trace.h> + +#include "ZslProcessor.h" +#include <gui/SurfaceTextureClient.h> +#include "../Camera2Device.h" +#include "../Camera2Client.h" + + +namespace android { +namespace camera2 { + +ZslProcessor::ZslProcessor( + wp<Camera2Client> client, + wp<CaptureSequencer> sequencer): + Thread(false), + mState(RUNNING), + mClient(client), + mSequencer(sequencer), + mZslBufferAvailable(false), + mZslStreamId(NO_STREAM), + mZslReprocessStreamId(NO_STREAM), + mFrameListHead(0), + mZslQueueHead(0), + mZslQueueTail(0) { + mZslQueue.insertAt(0, kZslBufferDepth); + mFrameList.insertAt(0, kFrameListDepth); + sp<CaptureSequencer> captureSequencer = mSequencer.promote(); + if (captureSequencer != 0) captureSequencer->setZslProcessor(this); +} + +ZslProcessor::~ZslProcessor() { + ALOGV("%s: Exit", __FUNCTION__); + deleteStream(); +} + +void ZslProcessor::onFrameAvailable() { + Mutex::Autolock l(mInputMutex); + if (!mZslBufferAvailable) { + mZslBufferAvailable = true; + mZslBufferAvailableSignal.signal(); + } +} + +void ZslProcessor::onFrameAvailable(int32_t frameId, const CameraMetadata &frame) { + Mutex::Autolock l(mInputMutex); + camera_metadata_ro_entry_t entry; + entry = frame.find(ANDROID_SENSOR_TIMESTAMP); + nsecs_t timestamp = entry.data.i64[0]; + ALOGVV("Got preview frame for timestamp %lld", timestamp); + + if (mState != RUNNING) return; + + mFrameList.editItemAt(mFrameListHead) = frame; + mFrameListHead = (mFrameListHead + 1) % kFrameListDepth; + + findMatchesLocked(); +} + +void ZslProcessor::onBufferReleased(buffer_handle_t *handle) { + Mutex::Autolock l(mInputMutex); + + // Verify that the buffer is in our queue + size_t i = 0; + for (; i < mZslQueue.size(); i++) { + if (&(mZslQueue[i].buffer.mGraphicBuffer->handle) == handle) break; + } + if (i == mZslQueue.size()) { + ALOGW("%s: Released buffer %p not found in queue", + __FUNCTION__, handle); + } + + // Erase entire ZSL queue since we've now completed the capture and preview + // is stopped. + clearZslQueueLocked(); + + mState = RUNNING; +} + +status_t ZslProcessor::updateStream(const Parameters ¶ms) { + ATRACE_CALL(); + ALOGV("%s: Configuring ZSL streams", __FUNCTION__); + status_t res; + + Mutex::Autolock l(mInputMutex); + + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return OK; + sp<Camera2Device> device = client->getCameraDevice(); + + if (mZslConsumer == 0) { + // Create CPU buffer queue endpoint + mZslConsumer = new BufferItemConsumer( + GRALLOC_USAGE_HW_CAMERA_ZSL, + kZslBufferDepth, + true); + mZslConsumer->setFrameAvailableListener(this); + mZslConsumer->setName(String8("Camera2Client::ZslConsumer")); + mZslWindow = new SurfaceTextureClient( + mZslConsumer->getProducerInterface()); + } + + if (mZslStreamId != NO_STREAM) { + // Check if stream parameters have to change + uint32_t currentWidth, currentHeight; + res = device->getStreamInfo(mZslStreamId, + ¤tWidth, ¤tHeight, 0); + if (res != OK) { + ALOGE("%s: Camera %d: Error querying capture output stream info: " + "%s (%d)", __FUNCTION__, + client->getCameraId(), strerror(-res), res); + return res; + } + if (currentWidth != (uint32_t)params.fastInfo.arrayWidth || + currentHeight != (uint32_t)params.fastInfo.arrayHeight) { + res = device->deleteReprocessStream(mZslReprocessStreamId); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to delete old reprocess stream " + "for ZSL: %s (%d)", __FUNCTION__, + client->getCameraId(), strerror(-res), res); + return res; + } + ALOGV("%s: Camera %d: Deleting stream %d since the buffer dimensions changed", + __FUNCTION__, client->getCameraId(), mZslStreamId); + res = device->deleteStream(mZslStreamId); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to delete old output stream " + "for ZSL: %s (%d)", __FUNCTION__, + client->getCameraId(), strerror(-res), res); + return res; + } + mZslStreamId = NO_STREAM; + } + } + + if (mZslStreamId == NO_STREAM) { + // Create stream for HAL production + // TODO: Sort out better way to select resolution for ZSL + int streamType = params.quirks.useZslFormat ? + (int)CAMERA2_HAL_PIXEL_FORMAT_ZSL : + (int)HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; + res = device->createStream(mZslWindow, + params.fastInfo.arrayWidth, params.fastInfo.arrayHeight, + streamType, 0, + &mZslStreamId); + if (res != OK) { + ALOGE("%s: Camera %d: Can't create output stream for ZSL: " + "%s (%d)", __FUNCTION__, client->getCameraId(), + strerror(-res), res); + return res; + } + res = device->createReprocessStreamFromStream(mZslStreamId, + &mZslReprocessStreamId); + if (res != OK) { + ALOGE("%s: Camera %d: Can't create reprocess stream for ZSL: " + "%s (%d)", __FUNCTION__, client->getCameraId(), + strerror(-res), res); + return res; + } + } + client->registerFrameListener(Camera2Client::kPreviewRequestIdStart, + Camera2Client::kPreviewRequestIdEnd, + this); + + return OK; +} + +status_t ZslProcessor::deleteStream() { + ATRACE_CALL(); + status_t res; + + Mutex::Autolock l(mInputMutex); + + if (mZslStreamId != NO_STREAM) { + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return OK; + sp<Camera2Device> device = client->getCameraDevice(); + + res = device->deleteReprocessStream(mZslReprocessStreamId); + if (res != OK) { + ALOGE("%s: Camera %d: Cannot delete ZSL reprocessing stream %d: " + "%s (%d)", __FUNCTION__, client->getCameraId(), + mZslReprocessStreamId, strerror(-res), res); + return res; + } + + mZslReprocessStreamId = NO_STREAM; + res = device->deleteStream(mZslStreamId); + if (res != OK) { + ALOGE("%s: Camera %d: Cannot delete ZSL output stream %d: " + "%s (%d)", __FUNCTION__, client->getCameraId(), + mZslStreamId, strerror(-res), res); + return res; + } + + mZslWindow.clear(); + mZslConsumer.clear(); + + mZslStreamId = NO_STREAM; + } + return OK; +} + +int ZslProcessor::getStreamId() const { + Mutex::Autolock l(mInputMutex); + return mZslStreamId; +} + +int ZslProcessor::getReprocessStreamId() const { + Mutex::Autolock l(mInputMutex); + return mZslReprocessStreamId; +} + +status_t ZslProcessor::pushToReprocess(int32_t requestId) { + ALOGV("%s: Send in reprocess request with id %d", + __FUNCTION__, requestId); + Mutex::Autolock l(mInputMutex); + status_t res; + sp<Camera2Client> client = mClient.promote(); + + if (client == 0) return INVALID_OPERATION; + + IF_ALOGV() { + dumpZslQueue(-1); + } + + if (mZslQueueTail != mZslQueueHead) { + CameraMetadata request; + size_t index = mZslQueueTail; + while (index != mZslQueueHead) { + if (!mZslQueue[index].frame.isEmpty()) { + request = mZslQueue[index].frame; + break; + } + index = (index + 1) % kZslBufferDepth; + } + if (index == mZslQueueHead) { + ALOGV("%s: ZSL queue has no valid frames to send yet.", + __FUNCTION__); + return NOT_ENOUGH_DATA; + } + // Verify that the frame is reasonable for reprocessing + + camera_metadata_entry_t entry; + entry = request.find(ANDROID_CONTROL_AE_STATE); + if (entry.count == 0) { + ALOGE("%s: ZSL queue frame has no AE state field!", + __FUNCTION__); + return BAD_VALUE; + } + if (entry.data.u8[0] != ANDROID_CONTROL_AE_STATE_CONVERGED && + entry.data.u8[0] != ANDROID_CONTROL_AE_STATE_LOCKED) { + ALOGV("%s: ZSL queue frame AE state is %d, need full capture", + __FUNCTION__, entry.data.u8[0]); + return NOT_ENOUGH_DATA; + } + + buffer_handle_t *handle = + &(mZslQueue[index].buffer.mGraphicBuffer->handle); + + uint8_t requestType = ANDROID_REQUEST_TYPE_REPROCESS; + res = request.update(ANDROID_REQUEST_TYPE, + &requestType, 1); + uint8_t inputStreams[1] = { mZslReprocessStreamId }; + if (res == OK) request.update(ANDROID_REQUEST_INPUT_STREAMS, + inputStreams, 1); + uint8_t outputStreams[1] = { client->getCaptureStreamId() }; + if (res == OK) request.update(ANDROID_REQUEST_OUTPUT_STREAMS, + outputStreams, 1); + res = request.update(ANDROID_REQUEST_ID, + &requestId, 1); + + if (res != OK ) { + ALOGE("%s: Unable to update frame to a reprocess request", __FUNCTION__); + return INVALID_OPERATION; + } + + res = client->stopStream(); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to stop preview for ZSL capture: " + "%s (%d)", + __FUNCTION__, client->getCameraId(), strerror(-res), res); + return INVALID_OPERATION; + } + // TODO: have push-and-clear be atomic + res = client->getCameraDevice()->pushReprocessBuffer(mZslReprocessStreamId, + handle, this); + if (res != OK) { + ALOGE("%s: Unable to push buffer for reprocessing: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + + // Update JPEG settings + { + SharedParameters::Lock l(client->getParameters()); + res = l.mParameters.updateRequestJpeg(&request); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update JPEG entries of ZSL " + "capture request: %s (%d)", __FUNCTION__, + client->getCameraId(), + strerror(-res), res); + return res; + } + } + + mLatestCapturedRequest = request; + res = client->getCameraDevice()->capture(request); + if (res != OK ) { + ALOGE("%s: Unable to send ZSL reprocess request to capture: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + + mState = LOCKED; + } else { + ALOGV("%s: No ZSL buffers yet", __FUNCTION__); + return NOT_ENOUGH_DATA; + } + return OK; +} + +status_t ZslProcessor::clearZslQueue() { + Mutex::Autolock l(mInputMutex); + // If in middle of capture, can't clear out queue + if (mState == LOCKED) return OK; + + return clearZslQueueLocked(); +} + +status_t ZslProcessor::clearZslQueueLocked() { + for (size_t i = 0; i < mZslQueue.size(); i++) { + if (mZslQueue[i].buffer.mTimestamp != 0) { + mZslConsumer->releaseBuffer(mZslQueue[i].buffer); + } + mZslQueue.replaceAt(i); + } + mZslQueueHead = 0; + mZslQueueTail = 0; + return OK; +} + +void ZslProcessor::dump(int fd, const Vector<String16>& args) const { + Mutex::Autolock l(mInputMutex); + if (!mLatestCapturedRequest.isEmpty()) { + String8 result(" Latest ZSL capture request:\n"); + write(fd, result.string(), result.size()); + mLatestCapturedRequest.dump(fd, 2, 6); + } else { + String8 result(" Latest ZSL capture request: none yet\n"); + write(fd, result.string(), result.size()); + } + dumpZslQueue(fd); +} + +bool ZslProcessor::threadLoop() { + status_t res; + + { + Mutex::Autolock l(mInputMutex); + while (!mZslBufferAvailable) { + res = mZslBufferAvailableSignal.waitRelative(mInputMutex, + kWaitDuration); + if (res == TIMED_OUT) return true; + } + mZslBufferAvailable = false; + } + + do { + sp<Camera2Client> client = mClient.promote(); + if (client == 0) return false; + res = processNewZslBuffer(client); + } while (res == OK); + + return true; +} + +status_t ZslProcessor::processNewZslBuffer(sp<Camera2Client> &client) { + ATRACE_CALL(); + status_t res; + + ALOGVV("Trying to get next buffer"); + BufferItemConsumer::BufferItem item; + res = mZslConsumer->acquireBuffer(&item); + if (res != OK) { + if (res != BufferItemConsumer::NO_BUFFER_AVAILABLE) { + ALOGE("%s: Camera %d: Error receiving ZSL image buffer: " + "%s (%d)", __FUNCTION__, + client->getCameraId(), strerror(-res), res); + } else { + ALOGVV(" No buffer"); + } + return res; + } + + Mutex::Autolock l(mInputMutex); + + if (mState == LOCKED) { + ALOGVV("In capture, discarding new ZSL buffers"); + mZslConsumer->releaseBuffer(item); + return OK; + } + + ALOGVV("Got ZSL buffer: head: %d, tail: %d", mZslQueueHead, mZslQueueTail); + + if ( (mZslQueueHead + 1) % kZslBufferDepth == mZslQueueTail) { + ALOGVV("Releasing oldest buffer"); + mZslConsumer->releaseBuffer(mZslQueue[mZslQueueTail].buffer); + mZslQueue.replaceAt(mZslQueueTail); + mZslQueueTail = (mZslQueueTail + 1) % kZslBufferDepth; + } + + ZslPair &queueHead = mZslQueue.editItemAt(mZslQueueHead); + + queueHead.buffer = item; + queueHead.frame.release(); + + mZslQueueHead = (mZslQueueHead + 1) % kZslBufferDepth; + + ALOGVV(" Acquired buffer, timestamp %lld", queueHead.buffer.mTimestamp); + + findMatchesLocked(); + + return OK; +} + +void ZslProcessor::findMatchesLocked() { + ALOGVV("Scanning"); + for (size_t i = 0; i < mZslQueue.size(); i++) { + ZslPair &queueEntry = mZslQueue.editItemAt(i); + nsecs_t bufferTimestamp = queueEntry.buffer.mTimestamp; + IF_ALOGV() { + camera_metadata_entry_t entry; + nsecs_t frameTimestamp = 0; + if (!queueEntry.frame.isEmpty()) { + entry = queueEntry.frame.find(ANDROID_SENSOR_TIMESTAMP); + frameTimestamp = entry.data.i64[0]; + } + ALOGVV(" %d: b: %lld\tf: %lld", i, + bufferTimestamp, frameTimestamp ); + } + if (queueEntry.frame.isEmpty() && bufferTimestamp != 0) { + // Have buffer, no matching frame. Look for one + for (size_t j = 0; j < mFrameList.size(); j++) { + bool match = false; + CameraMetadata &frame = mFrameList.editItemAt(j); + if (!frame.isEmpty()) { + camera_metadata_entry_t entry; + entry = frame.find(ANDROID_SENSOR_TIMESTAMP); + if (entry.count == 0) { + ALOGE("%s: Can't find timestamp in frame!", + __FUNCTION__); + continue; + } + nsecs_t frameTimestamp = entry.data.i64[0]; + if (bufferTimestamp == frameTimestamp) { + ALOGVV("%s: Found match %lld", __FUNCTION__, + frameTimestamp); + match = true; + } else { + int64_t delta = abs(bufferTimestamp - frameTimestamp); + if ( delta < 1000000) { + ALOGVV("%s: Found close match %lld (delta %lld)", + __FUNCTION__, bufferTimestamp, delta); + match = true; + } + } + } + if (match) { + queueEntry.frame.acquire(frame); + break; + } + } + } + } +} + +void ZslProcessor::dumpZslQueue(int fd) const { + String8 header("ZSL queue contents:"); + String8 indent(" "); + ALOGV("%s", header.string()); + if (fd != -1) { + header = indent + header + "\n"; + write(fd, header.string(), header.size()); + } + for (size_t i = 0; i < mZslQueue.size(); i++) { + const ZslPair &queueEntry = mZslQueue[i]; + nsecs_t bufferTimestamp = queueEntry.buffer.mTimestamp; + camera_metadata_ro_entry_t entry; + nsecs_t frameTimestamp = 0; + int frameAeState = -1; + if (!queueEntry.frame.isEmpty()) { + entry = queueEntry.frame.find(ANDROID_SENSOR_TIMESTAMP); + if (entry.count > 0) frameTimestamp = entry.data.i64[0]; + entry = queueEntry.frame.find(ANDROID_CONTROL_AE_STATE); + if (entry.count > 0) frameAeState = entry.data.u8[0]; + } + String8 result = + String8::format(" %d: b: %lld\tf: %lld, AE state: %d", i, + bufferTimestamp, frameTimestamp, frameAeState); + ALOGV("%s", result.string()); + if (fd != -1) { + result = indent + result + "\n"; + write(fd, result.string(), result.size()); + } + + } +} + +}; // namespace camera2 +}; // namespace android diff --git a/services/camera/libcameraservice/camera2/ZslProcessor.h b/services/camera/libcameraservice/camera2/ZslProcessor.h new file mode 100644 index 00000000..c80e7f4d --- /dev/null +++ b/services/camera/libcameraservice/camera2/ZslProcessor.h @@ -0,0 +1,126 @@ +/* + * 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_SERVERS_CAMERA_CAMERA2_ZSLPROCESSOR_H +#define ANDROID_SERVERS_CAMERA_CAMERA2_ZSLPROCESSOR_H + +#include <utils/Thread.h> +#include <utils/String16.h> +#include <utils/Vector.h> +#include <utils/Mutex.h> +#include <utils/Condition.h> +#include <gui/BufferItemConsumer.h> +#include "Parameters.h" +#include "FrameProcessor.h" +#include "CameraMetadata.h" +#include "Camera2Heap.h" +#include "../Camera2Device.h" + +namespace android { + +class Camera2Client; + +namespace camera2 { + +class CaptureSequencer; + +/*** + * ZSL queue processing + */ +class ZslProcessor: + virtual public Thread, + virtual public BufferItemConsumer::FrameAvailableListener, + virtual public FrameProcessor::FilteredListener, + virtual public Camera2Device::BufferReleasedListener { + public: + ZslProcessor(wp<Camera2Client> client, wp<CaptureSequencer> sequencer); + ~ZslProcessor(); + + // From mZslConsumer + virtual void onFrameAvailable(); + // From FrameProcessor + virtual void onFrameAvailable(int32_t frameId, const CameraMetadata &frame); + + virtual void onBufferReleased(buffer_handle_t *handle); + + status_t updateStream(const Parameters ¶ms); + status_t deleteStream(); + int getStreamId() const; + int getReprocessStreamId() const; + + status_t pushToReprocess(int32_t requestId); + status_t clearZslQueue(); + + void dump(int fd, const Vector<String16>& args) const; + private: + static const nsecs_t kWaitDuration = 10000000; // 10 ms + + enum { + RUNNING, + LOCKED + } mState; + + wp<Camera2Client> mClient; + wp<CaptureSequencer> mSequencer; + + mutable Mutex mInputMutex; + bool mZslBufferAvailable; + Condition mZslBufferAvailableSignal; + + enum { + NO_STREAM = -1 + }; + + int mZslStreamId; + int mZslReprocessStreamId; + sp<BufferItemConsumer> mZslConsumer; + sp<ANativeWindow> mZslWindow; + + struct ZslPair { + BufferItemConsumer::BufferItem buffer; + CameraMetadata frame; + }; + + static const size_t kZslBufferDepth = 4; + static const size_t kFrameListDepth = kZslBufferDepth * 2; + Vector<CameraMetadata> mFrameList; + size_t mFrameListHead; + + ZslPair mNextPair; + + Vector<ZslPair> mZslQueue; + size_t mZslQueueHead; + size_t mZslQueueTail; + + CameraMetadata mLatestCapturedRequest; + + virtual bool threadLoop(); + + status_t processNewZslBuffer(sp<Camera2Client> &client); + + // Match up entries from frame list to buffers in ZSL queue + void findMatchesLocked(); + + status_t clearZslQueueLocked(); + + void dumpZslQueue(int id) const; +}; + + +}; //namespace camera2 +}; //namespace android + +#endif |