diff options
Diffstat (limited to 'services')
36 files changed, 7974 insertions, 3565 deletions
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index 8473fab..c2d2790 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -89,7 +89,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 diff --git a/services/audioflinger/AudioBufferProviderSource.cpp b/services/audioflinger/AudioBufferProviderSource.cpp index 4342171..613e924 100644 --- a/services/audioflinger/AudioBufferProviderSource.cpp +++ b/services/audioflinger/AudioBufferProviderSource.cpp @@ -46,14 +46,16 @@ ssize_t AudioBufferProviderSource::availableToRead() return mBuffer.raw != NULL ? mBuffer.frameCount - mConsumed : 0; } -ssize_t AudioBufferProviderSource::read(void *buffer, size_t count) +ssize_t AudioBufferProviderSource::read(void *buffer, + size_t count, + int64_t readPTS) { if (CC_UNLIKELY(!mNegotiated)) { return NEGOTIATE; } if (CC_UNLIKELY(mBuffer.raw == NULL)) { mBuffer.frameCount = count; - status_t status = mProvider->getNextBuffer(&mBuffer, AudioBufferProvider::kInvalidPTS); + status_t status = mProvider->getNextBuffer(&mBuffer, readPTS); if (status != OK) { return status == NOT_ENOUGH_DATA ? (ssize_t) WOULD_BLOCK : (ssize_t) status; } @@ -79,7 +81,8 @@ ssize_t AudioBufferProviderSource::read(void *buffer, size_t count) return count; } -ssize_t AudioBufferProviderSource::readVia(readVia_t via, size_t total, void *user, size_t block) +ssize_t AudioBufferProviderSource::readVia(readVia_t via, size_t total, void *user, + int64_t readPTS, size_t block) { if (CC_UNLIKELY(!mNegotiated)) { return NEGOTIATE; @@ -99,7 +102,7 @@ ssize_t AudioBufferProviderSource::readVia(readVia_t via, size_t total, void *us // 1 <= count <= block if (CC_UNLIKELY(mBuffer.raw == NULL)) { mBuffer.frameCount = count; - status_t status = mProvider->getNextBuffer(&mBuffer, AudioBufferProvider::kInvalidPTS); + status_t status = mProvider->getNextBuffer(&mBuffer, readPTS); if (CC_LIKELY(status == OK)) { ALOG_ASSERT(mBuffer.raw != NULL && mBuffer.frameCount <= count); // mConsumed is 0 either from constructor or after releaseBuffer() @@ -117,7 +120,8 @@ ssize_t AudioBufferProviderSource::readVia(readVia_t via, size_t total, void *us count = available; } if (CC_LIKELY(count > 0)) { - ssize_t ret = via(user, (char *) mBuffer.raw + (mConsumed << mBitShift), count); + char* readTgt = (char *) mBuffer.raw + (mConsumed << mBitShift); + ssize_t ret = via(user, readTgt, count, readPTS); if (CC_UNLIKELY(ret <= 0)) { if (CC_LIKELY(accumulator > 0)) { return accumulator; diff --git a/services/audioflinger/AudioBufferProviderSource.h b/services/audioflinger/AudioBufferProviderSource.h index 2b39937..1435a84 100644 --- a/services/audioflinger/AudioBufferProviderSource.h +++ b/services/audioflinger/AudioBufferProviderSource.h @@ -42,8 +42,9 @@ public: //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); + virtual ssize_t read(void *buffer, size_t count, int64_t readPTS); + virtual ssize_t readVia(readVia_t via, size_t total, void *user, + int64_t readPTS, size_t block); private: AudioBufferProvider * const mProvider; diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index aab9984..7e5f102 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -83,13 +83,7 @@ #include "PipeReader.h" #include "SourceAudioBufferProvider.h" -#ifdef HAVE_REQUEST_PRIORITY #include "SchedulingPolicyService.h" -#endif - -#ifdef SOAKER -#include "Soaker.h" -#endif // ---------------------------------------------------------------------------- @@ -167,6 +161,10 @@ 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; + // ---------------------------------------------------------------------------- #ifdef ADD_BATTERY_DATA @@ -216,9 +214,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 +244,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(mRecordThreads.keyAt(0)); } while (!mPlaybackThreads.isEmpty()) { // closeOutput() will remove first entry from mPlaybackThreads - closeOutput(mPlaybackThreads.keyAt(0)); + closeOutput_nonvirtual(mPlaybackThreads.keyAt(0)); } for (size_t i = 0; i < mAudioHwDevs.size(); i++) { @@ -278,7 +271,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 @@ -289,22 +284,23 @@ audio_hw_device_t* AudioFlinger::findSuitableHwDev_l(audio_module_handle_t modul } } 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(); + AudioHwDevice *audioHwDevice = mAudioHwDevs.valueAt(i); + audio_hw_device_t *dev = audioHwDevice->hwDevice(); if ((dev->get_supported_devices(dev) & devices) == devices) - return dev; + return audioHwDevice; } 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 +323,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 +339,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 +352,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 +433,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, @@ -610,30 +603,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 +646,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 +675,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 +691,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 +741,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 +749,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 +875,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->device() & AUDIO_DEVICE_IN_ALL; + 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 +909,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 +965,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 +977,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 +1010,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; @@ -1124,7 +1124,7 @@ 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) + audio_devices_t device, type_t type) : Thread(false), mType(type), mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mNormalFrameCount(0), @@ -1132,8 +1132,7 @@ AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio mChannelCount(0), mFrameSize(1), mFormat(AUDIO_FORMAT_INVALID), mParamStatus(NO_ERROR), - mStandby(false), mId(id), - mDevice(device), + mStandby(false), mDevice(device), mId(id), mDeathRecipient(new PMDeathRecipient(this)) { } @@ -1226,7 +1225,7 @@ void AudioFlinger::ThreadBase::processConfigEvents() 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]; @@ -1283,10 +1282,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 +1299,6 @@ status_t AudioFlinger::ThreadBase::dumpEffectChains(int fd, const Vector<String1 chain->dump(fd, args); } } - return NO_ERROR; } void AudioFlinger::ThreadBase::acquireWakeLock() @@ -1397,8 +1394,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 +1421,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 +1429,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 +1446,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 +1506,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), 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 +1522,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 +1559,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 +1614,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 +1640,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 @@ -1660,7 +1665,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac 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, @@ -1715,7 +1720,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac 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 +1794,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; } @@ -1804,18 +1809,16 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac } } -#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); + int err = requestPriority(callingPid, tid, kPriorityAudioApp); if (err != 0) { ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d", - 1, callingPid, tid, err); + kPriorityAudioApp, callingPid, tid, err); } } -#endif lStatus = NO_ERROR; @@ -1857,13 +1860,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) @@ -2192,28 +2207,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 +2279,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 +2310,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 +2323,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,12 +2373,6 @@ AudioFlinger::MixerThread::~MixerThread() delete fastTrack->mBufferProvider; sq->end(false /*didModify*/); delete mFastMixer; -#ifdef SOAKER - if (mSoaker != NULL) { - mSoaker->requestExitAndWait(); - } - delete mSoaker; -#endif if (mAudioWatchdog != 0) { mAudioWatchdog->requestExit(); mAudioWatchdog->requestExitAndWait(); @@ -2508,9 +2505,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 +2513,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,7 +2542,7 @@ 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(); @@ -2602,7 +2596,7 @@ if (mType == MIXER) { threadLoop_sleepTime(); } - if (mSuspended > 0) { + if (isSuspended()) { sleepTime = suspendSleepTimeUs(); } @@ -2635,11 +2629,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 +2656,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(); @@ -2794,7 +2781,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 +2791,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 +2835,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 } @@ -3229,7 +3216,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac } //ALOGV("track %d u=%08x, s=%08x [NOT READY] on thread %p", name, cblk->user, cblk->server, this); - if ((track->sharedBuffer() != 0) || track->isTerminated() || + if ((track->sharedBuffer() != 0) || track->isStopped() || track->isPaused()) { // We have consumed all the buffers of this track. // Remove it from the list of active tracks. @@ -3249,8 +3236,8 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac track->mUnderrunCount++; // No buffers for this track. Give it a few chances to // fill a buffer, then remove it from active list. - if (--(track->mRetryCount) <= 0) { - ALOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", name, this); + if (--(track->mRetryCount) <= 0 || track->isTerminated()) { + ALOGV_IF(track->mRetryCount <= 0, "BUFFER TIMEOUT: remove(%d) from active list on thread %p", name, this); tracksToRemove->add(track); // indicate to client process that the track was disabled because of underrun; // it will then automatically call start() when data is available @@ -3373,7 +3360,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()); @@ -3460,14 +3447,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 (mDevice != 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,7 +3469,7 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l() // forward device change to effects that have requested to be // aware of attached audio device. - mDevice = (uint32_t)value; + mDevice = value; for (size_t i = 0; i < mEffectChains.size(); i++) { mEffectChains[i]->setDevice_l(mDevice); } @@ -3505,7 +3492,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); if (name < 0) break; mTracks[i]->mName = name; // limit track sample rate to 2 x new output sample rate @@ -3539,7 +3526,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 +3580,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 +3590,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; } @@ -3624,8 +3612,6 @@ status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16> AudioWatchdogDump wdCopy = mAudioWatchdogDump; wdCopy.dump(fd); } - - return NO_ERROR; } uint32_t AudioFlinger::MixerThread::idleSleepTimeUs() const @@ -3651,7 +3637,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 { @@ -3751,7 +3737,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep } //ALOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server); - if ((track->sharedBuffer() != 0) || track->isTerminated() || + if ((track->sharedBuffer() != 0) || track->isStopped() || track->isPaused()) { // We have consumed all the buffers of this track. // Remove it from the list of active tracks. @@ -3769,8 +3755,8 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep } else { // No buffers for this track. Give it a few chances to // fill a buffer, then remove it from active list. - if (--(track->mRetryCount) <= 0) { - ALOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name()); + if (--(track->mRetryCount) <= 0 || track->isTerminated()) { + ALOGV_IF(track->mRetryCount <= 0, "BUFFER TIMEOUT: remove(%d) from active list", track->name()); trackToRemove = track; } else { mixerStatus = MIXER_TRACKS_ENABLED; @@ -4070,6 +4056,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 +4086,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 +4261,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 +4287,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); mCblk->mName = mName; if (mName < 0) { ALOGE("no more track names available"); @@ -4329,11 +4316,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 +4478,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; } @@ -4821,12 +4801,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 +4819,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 +5041,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 +5056,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 +5083,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 +5092,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 +5314,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 +5335,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 +5366,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,14 +5393,26 @@ 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\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", @@ -5448,7 +5435,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 +5823,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 +5865,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 +5896,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 +5929,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) : + audio_devices_t device) : ThreadBase(audioFlinger, id, device, RECORD), - mInput(input), mTrack(NULL), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL), + 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,6 +5973,7 @@ bool AudioFlinger::RecordThread::threadLoop() nsecs_t lastWarning = 0; + inputStandBy(); acquireWakeLock(); // start recording @@ -5996,10 +5985,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,10 +5999,7 @@ 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) { @@ -6034,6 +6017,9 @@ bool AudioFlinger::RecordThread::threadLoop() mStartStopCond.broadcast(); } mStandby = false; + } else if (mActiveTrack->mState == TrackBase::TERMINATED) { + removeTrack_l(mActiveTrack); + mActiveTrack.clear(); } } lockEffectChains_l(effectChains); @@ -6068,18 +6054,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); } } } @@ -6097,7 +6077,7 @@ bool AudioFlinger::RecordThread::threadLoop() if (mActiveTrack->mState == TrackBase::ACTIVE) { // 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; @@ -6120,12 +6100,8 @@ bool AudioFlinger::RecordThread::threadLoop() 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 +6127,7 @@ bool AudioFlinger::RecordThread::threadLoop() } } } - mActiveTrack->overflow(); + mActiveTrack->clearOverflow(); } // client isn't retrieving buffers fast enough else { @@ -6173,12 +6149,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 +6163,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 +6196,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 +6208,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(mDevice & AUDIO_DEVICE_IN_ALL) && + mAudioFlinger->btNrecIsOff(); setEffectSuspended_l(FX_IID_AEC, suspend, sessionId); setEffectSuspended_l(FX_IID_NS, suspend, sessionId); } @@ -6337,27 +6330,23 @@ 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) @@ -6371,16 +6360,63 @@ status_t AudioFlinger::RecordThread::setSyncEvent(const sp<SyncEvent>& 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()) { + track->setSyncEvent(event); + ret = NO_ERROR; + } } - return NAME_NOT_FOUND; + return ret; +} + +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); + } +} + +void AudioFlinger::RecordThread::removeTrack_l(const sp<RecordTrack>& track) +{ + mTracks.remove(track); + // need anything related to effects here? } -status_t AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args) +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 +6426,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 +6436,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 @@ -6432,7 +6487,7 @@ status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer* if (mActiveTrack->mState == TrackBase::ACTIVE) { // 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; @@ -6508,25 +6563,30 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l() // 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() + audio_devices_t newDevice = mDevice; if (value & AUDIO_DEVICE_OUT_ALL) { - mDevice &= (uint32_t)~(value & AUDIO_DEVICE_OUT_ALL); + newDevice &= ~(value & AUDIO_DEVICE_OUT_ALL); status = BAD_VALUE; } else { - mDevice &= (uint32_t)~(value & AUDIO_DEVICE_IN_ALL); + newDevice &= ~(value & AUDIO_DEVICE_IN_ALL); // disable AEC and NS if the device is a BT SCO headset supporting those pre processings - if (mTrack != NULL) { + if (mTracks.size() > 0) { 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()); + 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; + newDevice |= value; + mDevice = newDevice; // since mDevice is read by other threads, only write to it once } 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()); } @@ -6656,23 +6716,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() { + 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 +6795,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); @@ -6764,11 +6865,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 +6885,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 +6934,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 +6966,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 +7002,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 +7043,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 +7056,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 +7068,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 +7087,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 +7098,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 +7111,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 +7121,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 +7147,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 +7156,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 +7254,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 +7299,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(); @@ -7401,7 +7463,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 +7479,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 +7498,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. @@ -7670,7 +7732,7 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l( } // create effect handle and connect it to effect module handle = new EffectHandle(effect, client, effectClient, priority); - lStatus = effect->addHandle(handle); + lStatus = effect->addHandle(handle.get()); if (enabled != NULL) { *enabled = (int)effect->isEnabled(); } @@ -7810,7 +7872,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 +8064,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 +8089,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 +8116,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 +8161,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 +8208,7 @@ void AudioFlinger::EffectModule::disconnect(const wp<EffectHandle>& handle, bool thread->disconnectEffect(keep, handle, unpinIfLast); } } + return mHandles.size(); } void AudioFlinger::EffectModule::updateState() { @@ -8240,7 +8306,6 @@ void AudioFlinger::EffectModule::reset_l() status_t AudioFlinger::EffectModule::configure() { - uint32_t channels; if (mEffectInterface == NULL) { return NO_INIT; } @@ -8251,18 +8316,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 +8513,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 +8524,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 +8566,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,14 +8640,14 @@ 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) { 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; + audio_devices_t dev = device & AUDIO_DEVICE_OUT_ALL; if (dev) { status_t cmdStatus; uint32_t size = sizeof(status_t); @@ -8649,7 +8716,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 +8798,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 +8812,6 @@ status_t AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args) if (locked) { mLock.unlock(); } - - return NO_ERROR; } // ---------------------------------------------------------------------------- @@ -8746,7 +8827,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 +8852,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 +8931,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 +9362,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++) { @@ -9350,7 +9437,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 +9471,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 +9486,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 +9513,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 cfd718f..e5176a9 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.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, @@ -255,6 +256,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 +271,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 +353,11 @@ 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 device, 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 +383,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 +415,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 { @@ -455,7 +465,7 @@ private: bool mStepServerFailed; const int mSessionId; uint8_t mChannelCount; - uint32_t mChannelMask; + audio_channel_mask_t mChannelMask; Vector < sp<SyncEvent> >mSyncEvents; }; @@ -483,14 +493,20 @@ 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. size_t frameCount() const { return mNormalFrameCount; } - void wakeUp() { mWaitWorkCV.broadcast(); } + // Should be "virtual status_t requestExitAndWait()" and override same // method in Thread, but Thread::requestExitAndWait() is not yet virtual. void exit(); @@ -501,9 +517,11 @@ private: void sendConfigEvent(int event, int param = 0); void sendConfigEvent_l(int event, int param = 0); void processConfigEvents(); - audio_io_handle_t id() const { return mId;} + + // see note at declaration of mStandby and mDevice bool standby() const { return mStandby; } - uint32_t device() const { return mDevice; } + audio_devices_t device() const { return mDevice; } + virtual audio_stream_t* stream() const = 0; sp<EffectHandle> createEffect_l( @@ -515,7 +533,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) @@ -598,7 +616,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, @@ -617,7 +635,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; @@ -646,11 +664,19 @@ private: status_t mParamStatus; Vector<ConfigEvent> mConfigEvents; - bool mStandby; + + // These fields are written and read by thread itself without lock or barrier, + // and read by other threads without lock or barrier via standby() and device(). + // 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 mDevice; // output device for PlaybackThread + // input + output devices for RecordThread + 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 +717,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 +734,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 +791,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 +841,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 +884,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 +933,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 +964,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(); @@ -984,7 +1012,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 +1024,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); @@ -1018,10 +1056,19 @@ public: virtual status_t setSyncEvent(const sp<SyncEvent>& event); virtual bool isValidSyncEvent(const sp<SyncEvent>& event); + 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 @@ -1069,13 +1116,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 +1148,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,15 +1186,14 @@ 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); @@ -1167,9 +1213,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 +1241,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 @@ -1287,7 +1330,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); @@ -1331,18 +1374,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,18 +1402,24 @@ 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(); + // 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 virtual bool threadLoop(); virtual status_t readyToRun(); @@ -1379,17 +1432,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; @@ -1406,7 +1464,11 @@ 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(); + + // 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(); virtual status_t setSyncEvent(const sp<SyncEvent>& event); virtual bool isValidSyncEvent(const sp<SyncEvent>& event); @@ -1417,9 +1479,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 +1514,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 +1582,7 @@ private: return mSessionId; } status_t setEnabled(bool enabled); + status_t setEnabled_l(bool enabled); bool isEnabled() const; bool isProcessEnabled() const; @@ -1521,14 +1594,14 @@ 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 start(); @@ -1536,12 +1609,15 @@ private: 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 +1635,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 +1700,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 +1720,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,7 +1764,7 @@ 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 setInBuffer(int16_t *buffer, bool ownsBuffer = false) { @@ -1703,12 +1783,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 +1805,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 +1840,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 +1861,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 +1925,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 +1934,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 +1958,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 @@ -1885,8 +1970,6 @@ mutable Mutex mLock; // mutex for process, commands and handl // both are protected by mLock float mMasterVolume; - float mMasterVolumeSW; - master_volume_support mMasterVolumeSupportLvl; bool mMasterMute; DefaultKeyedVector< audio_io_handle_t, sp<RecordThread> > mRecordThreads; @@ -1900,8 +1983,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 +1992,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 3e4c55e..a9814a1 100644 --- a/services/audioflinger/AudioMixer.cpp +++ b/services/audioflinger/AudioMixer.cpp @@ -416,7 +416,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); diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp index 0d13970..3a2dbe2 100644 --- a/services/audioflinger/AudioPolicyService.cpp +++ b/services/audioflinger/AudioPolicyService.cpp @@ -223,7 +223,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 +231,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 +271,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 +282,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 +373,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 +391,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 +412,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 +441,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 +450,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, @@ -512,7 +514,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) { @@ -778,7 +780,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 +791,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 +811,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", @@ -841,11 +836,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", @@ -868,11 +858,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); @@ -891,6 +876,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 +922,7 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma } else { data2->mKeyValuePairs = param2.toString(); } + time = command2->mTime; } break; case SET_VOLUME: { @@ -946,6 +933,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 +955,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 +1421,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 fbca000..a086734 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, diff --git a/services/audioflinger/AudioStreamOutSink.cpp b/services/audioflinger/AudioStreamOutSink.cpp index 8a5aa0c..bc2d15b 100644 --- a/services/audioflinger/AudioStreamOutSink.cpp +++ b/services/audioflinger/AudioStreamOutSink.cpp @@ -67,4 +67,16 @@ ssize_t AudioStreamOutSink::write(const void *buffer, size_t count) return ret; } +status_t AudioStreamOutSink::getNextWriteTimestamp(int64_t *timestamp) { + ALOG_ASSERT(timestamp != NULL); + + if (NULL == mStream) + return INVALID_OPERATION; + + if (NULL == mStream->get_next_write_timestamp) + return INVALID_OPERATION; + + return mStream->get_next_write_timestamp(mStream, timestamp); +} + } // namespace android diff --git a/services/audioflinger/AudioStreamOutSink.h b/services/audioflinger/AudioStreamOutSink.h index 1eff3f6..5976b18 100644 --- a/services/audioflinger/AudioStreamOutSink.h +++ b/services/audioflinger/AudioStreamOutSink.h @@ -47,6 +47,11 @@ public: virtual ssize_t write(const void *buffer, size_t count); + // AudioStreamOutSink wraps a HAL's output stream. Its + // getNextWriteTimestamp method is simply a passthru to the HAL's underlying + // implementation of GNWT (if any) + virtual status_t getNextWriteTimestamp(int64_t *timestamp); + // NBAIO_Sink end #if 0 // until necessary diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index 7652132..fbcc11a 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; @@ -399,8 +399,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; diff --git a/services/audioflinger/MonoPipe.cpp b/services/audioflinger/MonoPipe.cpp index f3fc19a..bd876b4 100644 --- a/services/audioflinger/MonoPipe.cpp +++ b/services/audioflinger/MonoPipe.cpp @@ -17,17 +17,22 @@ #define LOG_TAG "MonoPipe" //#define LOG_NDEBUG 0 +#include <common_time/cc_helper.h> #include <cutils/atomic.h> #include <cutils/compiler.h> +#include <utils/LinearTransform.h> #include <utils/Log.h> #include <utils/Trace.h> +#include "AudioBufferProvider.h" #include "MonoPipe.h" #include "roundup.h" + namespace android { MonoPipe::MonoPipe(size_t reqFrames, NBAIO_Format format, bool writeCanBlock) : NBAIO_Sink(format), + mUpdateSeq(0), mReqFrames(reqFrames), mMaxFrames(roundup(reqFrames)), mBuffer(malloc(mMaxFrames * Format_frameSize(format))), @@ -38,6 +43,37 @@ MonoPipe::MonoPipe(size_t reqFrames, NBAIO_Format format, bool writeCanBlock) : mSetpoint((reqFrames * 11) / 16), mWriteCanBlock(writeCanBlock) { + CCHelper tmpHelper; + status_t res; + uint64_t N, D; + + mNextRdPTS = AudioBufferProvider::kInvalidPTS; + + mSamplesToLocalTime.a_zero = 0; + mSamplesToLocalTime.b_zero = 0; + mSamplesToLocalTime.a_to_b_numer = 0; + mSamplesToLocalTime.a_to_b_denom = 0; + + D = Format_sampleRate(format); + if (OK != (res = tmpHelper.getLocalFreq(&N))) { + ALOGE("Failed to fetch local time frequency when constructing a" + " MonoPipe (res = %d). getNextWriteTimestamp calls will be" + " non-functional", res); + return; + } + + LinearTransform::reduce(&N, &D); + static const uint64_t kSignedHiBitsMask = ~(0x7FFFFFFFull); + static const uint64_t kUnsignedHiBitsMask = ~(0xFFFFFFFFull); + if ((N & kSignedHiBitsMask) || (D & kUnsignedHiBitsMask)) { + ALOGE("Cannot reduce sample rate to local clock frequency ratio to fit" + " in a 32/32 bit rational. (max reduction is 0x%016llx/0x%016llx" + "). getNextWriteTimestamp calls will be non-functional", N, D); + return; + } + + mSamplesToLocalTime.a_to_b_numer = static_cast<int32_t>(N); + mSamplesToLocalTime.a_to_b_denom = static_cast<uint32_t>(D); } MonoPipe::~MonoPipe() @@ -162,4 +198,102 @@ void MonoPipe::setAvgFrames(size_t setpoint) mSetpoint = setpoint; } +status_t MonoPipe::getNextWriteTimestamp(int64_t *timestamp) +{ + int32_t front; + + ALOG_ASSERT(NULL != timestamp); + + if (0 == mSamplesToLocalTime.a_to_b_denom) + return UNKNOWN_ERROR; + + observeFrontAndNRPTS(&front, timestamp); + + if (AudioBufferProvider::kInvalidPTS != *timestamp) { + // If we have a valid read-pointer and next read timestamp pair, then + // use the current value of the write pointer to figure out how many + // frames are in the buffer, and offset the timestamp by that amt. Then + // next time we write to the MonoPipe, the data will hit the speakers at + // the next read timestamp plus the current amount of data in the + // MonoPipe. + size_t pendingFrames = (mRear - front) & (mMaxFrames - 1); + *timestamp = offsetTimestampByAudioFrames(*timestamp, pendingFrames); + } + + return OK; +} + +void MonoPipe::updateFrontAndNRPTS(int32_t newFront, int64_t newNextRdPTS) +{ + // Set the MSB of the update sequence number to indicate that there is a + // multi-variable update in progress. Use an atomic store with an "acquire" + // barrier to make sure that the next operations cannot be re-ordered and + // take place before the change to mUpdateSeq is commited.. + int32_t tmp = mUpdateSeq | 0x80000000; + android_atomic_acquire_store(tmp, &mUpdateSeq); + + // Update mFront and mNextRdPTS + mFront = newFront; + mNextRdPTS = newNextRdPTS; + + // We are finished with the update. Compute the next sequnce number (which + // should be the old sequence number, plus one, and with the MSB cleared) + // and then store it in mUpdateSeq using an atomic store with a "release" + // barrier so our update operations cannot be re-ordered past the update of + // the sequence number. + tmp = (tmp + 1) & 0x7FFFFFFF; + android_atomic_release_store(tmp, &mUpdateSeq); +} + +void MonoPipe::observeFrontAndNRPTS(int32_t *outFront, int64_t *outNextRdPTS) +{ + // Perform an atomic observation of mFront and mNextRdPTS. Basically, + // atomically observe the sequence number, then observer the variables, then + // atomically observe the sequence number again. If the two observations of + // the sequence number match, and the update-in-progress bit was not set, + // then we know we have a successful atomic observation. Otherwise, we loop + // around and try again. + // + // Note, it is very important that the observer be a lower priority thread + // than the updater. If the updater is lower than the observer, or they are + // the same priority and running with SCHED_FIFO (implying that quantum + // based premption is disabled) then we run the risk of deadlock. + int32_t seqOne, seqTwo; + + do { + seqOne = android_atomic_acquire_load(&mUpdateSeq); + *outFront = mFront; + *outNextRdPTS = mNextRdPTS; + seqTwo = android_atomic_release_load(&mUpdateSeq); + } while ((seqOne != seqTwo) || (seqOne & 0x80000000)); +} + +int64_t MonoPipe::offsetTimestampByAudioFrames(int64_t ts, size_t audFrames) +{ + if (0 == mSamplesToLocalTime.a_to_b_denom) + return AudioBufferProvider::kInvalidPTS; + + if (ts == AudioBufferProvider::kInvalidPTS) + return AudioBufferProvider::kInvalidPTS; + + int64_t frame_lt_duration; + if (!mSamplesToLocalTime.doForwardTransform(audFrames, + &frame_lt_duration)) { + // This should never fail, but if there is a bug which is causing it + // to fail, this message would probably end up flooding the logs + // because the conversion would probably fail forever. Log the + // error, but then zero out the ratio in the linear transform so + // that we don't try to do any conversions from now on. This + // MonoPipe's getNextWriteTimestamp is now broken for good. + ALOGE("Overflow when attempting to convert %d audio frames to" + " duration in local time. getNextWriteTimestamp will fail from" + " now on.", audFrames); + mSamplesToLocalTime.a_to_b_numer = 0; + mSamplesToLocalTime.a_to_b_denom = 0; + return AudioBufferProvider::kInvalidPTS; + } + + return ts + frame_lt_duration; +} + } // namespace android diff --git a/services/audioflinger/MonoPipe.h b/services/audioflinger/MonoPipe.h index f6e2cb3..c47bf6c 100644 --- a/services/audioflinger/MonoPipe.h +++ b/services/audioflinger/MonoPipe.h @@ -18,6 +18,7 @@ #define ANDROID_AUDIO_MONO_PIPE_H #include <time.h> +#include <utils/LinearTransform.h> #include "NBAIO.h" namespace android { @@ -56,6 +57,20 @@ public: 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); + // MonoPipe's implementation of getNextWriteTimestamp works in conjunction + // with MonoPipeReader. Every time a MonoPipeReader reads from the pipe, it + // receives a "readPTS" indicating the point in time for which the reader + // would like to read data. This "last read PTS" is offset by the amt of + // data the reader is currently mixing and then cached cached along with the + // updated read pointer. This cached value is the local time for which the + // reader is going to request data next time it reads data (assuming we are + // in steady state and operating with no underflows). Writers to the + // MonoPipe who would like to know when their next write operation will hit + // the speakers can call getNextWriteTimestamp which will return the value + // of the last read PTS plus the duration of the amt of data waiting to be + // read in the MonoPipe. + virtual status_t getNextWriteTimestamp(int64_t *timestamp); + // average number of frames present in the pipe under normal conditions. // See throttling mechanism in MonoPipe::write() size_t getAvgFrames() const { return mSetpoint; } @@ -63,20 +78,42 @@ public: size_t maxFrames() const { return mMaxFrames; } private: + // A pair of methods and a helper variable which allows the reader and the + // writer to update and observe the values of mFront and mNextRdPTS in an + // atomic lock-less fashion. + // + // :: Important :: + // Two assumptions must be true in order for this lock-less approach to + // function properly on all systems. First, there may only be one updater + // thread in the system. Second, the updater thread must be running at a + // strictly higher priority than the observer threads. Currently, both of + // these assumptions are true. The only updater is always a single + // FastMixer thread (which runs with SCHED_FIFO/RT priority while the only + // observer is always an AudioFlinger::PlaybackThread running with + // traditional (non-RT) audio priority. + void updateFrontAndNRPTS(int32_t newFront, int64_t newNextRdPTS); + void observeFrontAndNRPTS(int32_t *outFront, int64_t *outNextRdPTS); + volatile int32_t mUpdateSeq; + 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 mFront; // written by the reader with updateFrontAndNRPTS, observed by + // the writer with observeFrontAndNRPTS volatile int32_t mRear; // written by writer with android_atomic_release_store, // read by reader with android_atomic_acquire_load + volatile int64_t mNextRdPTS; // written by the reader with updateFrontAndNRPTS, observed by + // the writer with observeFrontAndNRPTS 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 + + int64_t offsetTimestampByAudioFrames(int64_t ts, size_t audFrames); + LinearTransform mSamplesToLocalTime; }; } // namespace android diff --git a/services/audioflinger/MonoPipeReader.cpp b/services/audioflinger/MonoPipeReader.cpp index b80d0c0..39a07de 100644 --- a/services/audioflinger/MonoPipeReader.cpp +++ b/services/audioflinger/MonoPipeReader.cpp @@ -43,11 +43,25 @@ ssize_t MonoPipeReader::availableToRead() return ret; } -ssize_t MonoPipeReader::read(void *buffer, size_t count) +ssize_t MonoPipeReader::read(void *buffer, size_t count, int64_t readPTS) { + // Compute the "next read PTS" and cache it. Callers of read pass a read + // PTS indicating the local time for which they are requesting data along + // with a count (which is the number of audio frames they are going to + // ultimately pass to the next stage of the pipeline). Offsetting readPTS + // by the duration of count will give us the readPTS which will be passed to + // us next time, assuming they system continues to operate in steady state + // with no discontinuities. We stash this value so it can be used by the + // MonoPipe writer to imlement getNextWriteTimestamp. + int64_t nextReadPTS; + nextReadPTS = mPipe->offsetTimestampByAudioFrames(readPTS, count); + // count == 0 is unlikely and not worth checking for explicitly; will be handled automatically ssize_t red = availableToRead(); if (CC_UNLIKELY(red <= 0)) { + // Uh-oh, looks like we are underflowing. Update the next read PTS and + // get out. + mPipe->updateFrontAndNRPTS(mPipe->mFront, nextReadPTS); return red; } if (CC_LIKELY((size_t) red > count)) { @@ -66,7 +80,7 @@ ssize_t MonoPipeReader::read(void *buffer, size_t count) memcpy((char *) buffer + (part1 << mBitShift), mPipe->mBuffer, part2 << mBitShift); } } - android_atomic_release_store(red + mPipe->mFront, &mPipe->mFront); + mPipe->updateFrontAndNRPTS(red + mPipe->mFront, nextReadPTS); mFramesRead += red; } return red; diff --git a/services/audioflinger/MonoPipeReader.h b/services/audioflinger/MonoPipeReader.h index 9bb0a94..0e1c992 100644 --- a/services/audioflinger/MonoPipeReader.h +++ b/services/audioflinger/MonoPipeReader.h @@ -47,7 +47,7 @@ public: virtual ssize_t availableToRead(); - virtual ssize_t read(void *buffer, size_t count); + virtual ssize_t read(void *buffer, size_t count, int64_t readPTS); // NBAIO_Source end diff --git a/services/audioflinger/NBAIO.cpp b/services/audioflinger/NBAIO.cpp index 9d71eae..2c07ebf 100644 --- a/services/audioflinger/NBAIO.cpp +++ b/services/audioflinger/NBAIO.cpp @@ -128,7 +128,8 @@ ssize_t NBAIO_Sink::writeVia(writeVia_t via, size_t total, void *user, size_t bl } // 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) +ssize_t NBAIO_Source::readVia(readVia_t via, size_t total, void *user, + int64_t readPTS, size_t block) { if (!mNegotiated) { return (ssize_t) NEGOTIATE; @@ -147,11 +148,11 @@ ssize_t NBAIO_Source::readVia(readVia_t via, size_t total, void *user, size_t bl if (count > block) { count = block; } - ssize_t ret = read(buffer, count); + ssize_t ret = read(buffer, count, readPTS); if (ret > 0) { ALOG_ASSERT((size_t) ret <= count); size_t maxRet = ret; - ret = via(user, buffer, maxRet); + ret = via(user, buffer, maxRet, readPTS); if (ret > 0) { ALOG_ASSERT((size_t) ret <= maxRet); accumulator += ret; diff --git a/services/audioflinger/NBAIO.h b/services/audioflinger/NBAIO.h index b5ae0f1..81f42ed 100644 --- a/services/audioflinger/NBAIO.h +++ b/services/audioflinger/NBAIO.h @@ -26,6 +26,7 @@ #include <limits.h> #include <stdlib.h> +#include <utils/Errors.h> #include <utils/RefBase.h> namespace android { @@ -74,7 +75,8 @@ 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); +typedef ssize_t (*readVia_t)(void *user, const void *buffer, + size_t count, int64_t readPTS); // Abstract class (interface) representing a data port. class NBAIO_Port : public RefBase { @@ -198,6 +200,21 @@ public: // < 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); + // Get the time (on the LocalTime timeline) at which the first frame of audio of the next write + // operation to this sink will be eventually rendered by the HAL. + // Inputs: + // ts A pointer pointing to the int64_t which will hold the result. + // Return value: + // OK Everything went well, *ts holds the time at which the first audio frame of the next + // write operation will be rendered, or AudioBufferProvider::kInvalidPTS if this sink + // does not know the answer for some reason. Sinks which eventually lead to a HAL + // which implements get_next_write_timestamp may return Invalid temporarily if the DMA + // output of the audio driver has not started yet. Sinks which lead to a HAL which + // does not implement get_next_write_timestamp, or which don't lead to a HAL at all, + // will always return kInvalidPTS. + // <other> Something unexpected happened internally. Check the logs and start debugging. + virtual status_t getNextWriteTimestamp(int64_t *ts) { return INVALID_OPERATION; } + protected: NBAIO_Sink(NBAIO_Format format = Format_Invalid) : NBAIO_Port(format), mFramesWritten(0) { } virtual ~NBAIO_Sink() { } @@ -238,6 +255,8 @@ public: // Inputs: // buffer Non-NULL destination buffer owned by consumer. // count Maximum number of frames to transfer. + // readPTS The presentation time (on the LocalTime timeline) for which data + // is being requested, or kInvalidPTS if not known. // Return value: // > 0 Number of frames successfully transferred prior to first error. // = 0 Count was zero. @@ -247,7 +266,7 @@ public: // 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; + virtual ssize_t read(void *buffer, size_t count, int64_t readPTS) = 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). @@ -256,6 +275,8 @@ public: // 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. + // readPTS The presentation time (on the LocalTime timeline) for which data + // is being requested, or kInvalidPTS if not known. // 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: @@ -278,7 +299,8 @@ public: // > 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); + virtual ssize_t readVia(readVia_t via, size_t total, void *user, + int64_t readPTS, size_t block = 0); protected: NBAIO_Source(NBAIO_Format format = Format_Invalid) : NBAIO_Port(format), mFramesRead(0) { } diff --git a/services/audioflinger/Soaker.h b/services/audioflinger/Soaker.h deleted file mode 100644 index 43d9d2f..0000000 --- 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 index e9d6d2c..3343b53 100644 --- a/services/audioflinger/SourceAudioBufferProvider.cpp +++ b/services/audioflinger/SourceAudioBufferProvider.cpp @@ -65,7 +65,7 @@ status_t SourceAudioBufferProvider::getNextBuffer(Buffer *buffer, int64_t pts) mSize = buffer->frameCount; } // read from source - ssize_t actual = mSource->read(mAllocated, buffer->frameCount); + ssize_t actual = mSource->read(mAllocated, buffer->frameCount, pts); if (actual > 0) { ALOG_ASSERT((size_t) actual <= buffer->frameCount); mOffset = 0; diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk index 3cae1f5..c14ae22 100644 --- a/services/camera/libcameraservice/Android.mk +++ b/services/camera/libcameraservice/Android.mk @@ -7,7 +7,11 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - CameraService.cpp + CameraService.cpp \ + CameraClient.cpp \ + Camera2Client.cpp \ + Camera2Device.cpp \ + MediaConsumer.cpp LOCAL_SHARED_LIBRARIES:= \ libui \ @@ -18,7 +22,12 @@ LOCAL_SHARED_LIBRARIES:= \ libmedia_native \ libcamera_client \ libgui \ - libhardware + libhardware \ + libsync \ + libcamera_metadata + +LOCAL_C_INCLUDES += \ + system/media/camera/include LOCAL_MODULE:= libcameraservice diff --git a/services/camera/libcameraservice/Camera2Client.cpp b/services/camera/libcameraservice/Camera2Client.cpp new file mode 100644 index 0000000..9c2fbcb --- /dev/null +++ b/services/camera/libcameraservice/Camera2Client.cpp @@ -0,0 +1,3480 @@ +/* + * 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 "Camera2Client" +#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 <media/hardware/MetadataBufferType.h> + +#include <math.h> + +#include "Camera2Client.h" + +namespace android { + +#define ALOG1(...) ALOGD_IF(gLogLevel >= 1, __VA_ARGS__); +#define ALOG2(...) ALOGD_IF(gLogLevel >= 2, __VA_ARGS__); + +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): + Client(cameraService, cameraClient, + cameraId, cameraFacing, clientPid), + mState(DISCONNECTED), + mPreviewStreamId(NO_STREAM), + mPreviewRequest(NULL), + mCaptureStreamId(NO_STREAM), + mCaptureRequest(NULL), + mRecordingStreamId(NO_STREAM), + mRecordingRequest(NULL), + mRecordingHeapCount(kDefaultRecordingHeapCount) +{ + ATRACE_CALL(); + + mDevice = new Camera2Device(cameraId); +} + +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: E", __FUNCTION__); + 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); + + res = buildDefaultParameters(); + if (res != OK) { + ALOGE("%s: Camera %d: unable to build defaults: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return NO_INIT; + } + + if (gLogLevel >= 1) { + LockedParameters::Key k(mParameters); + ALOGD("%s: Default parameters converted from camera %d:", __FUNCTION__, + mCameraId); + ALOGD("%s", k.mParameters.paramsFlattened.string()); + } + + mState = STOPPED; + + return OK; +} + +Camera2Client::~Camera2Client() { + ATRACE_CALL(); + ALOGV("%s: Camera %d: Shutting down", __FUNCTION__, mCameraId); + + mDestructionStarted = true; + + // Rewrite mClientPid to allow shutdown by CameraService + mClientPid = getCallingPid(); + disconnect(); +} + +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.unsafeUnlock(); + + result.append(getStateName(mState)); + + 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_AUTO) + CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_INCANDESCENT) + CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_FLUORESCENT) + CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_WARM_FLUORESCENT) + CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_DAYLIGHT) + CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_CLOUDY_DAYLIGHT) + CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_TWILIGHT) + CASE_APPEND_ENUM(ANDROID_CONTROL_AWB_SHADE) + default: result.append("UNKNOWN\n"); + } + + result.append(" Effect mode: "); + switch (p.effectMode) { + CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_OFF) + CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_MONO) + CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_NEGATIVE) + CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_SOLARIZE) + CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_SEPIA) + CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_POSTERIZE) + CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_WHITEBOARD) + CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_BLACKBOARD) + CASE_APPEND_ENUM(ANDROID_CONTROL_EFFECT_AQUA) + default: result.append("UNKNOWN\n"); + } + + result.append(" Antibanding mode: "); + switch (p.antibandingMode) { + CASE_APPEND_ENUM(ANDROID_CONTROL_AE_ANTIBANDING_AUTO) + CASE_APPEND_ENUM(ANDROID_CONTROL_AE_ANTIBANDING_OFF) + CASE_APPEND_ENUM(ANDROID_CONTROL_AE_ANTIBANDING_50HZ) + CASE_APPEND_ENUM(ANDROID_CONTROL_AE_ANTIBANDING_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(" 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", mPreviewStreamId); + result.appendFormat(" Capture stream ID: %d\n", mCaptureStreamId); + result.appendFormat(" Recording stream ID: %d\n", mRecordingStreamId); + + result.append(" Current requests:\n"); + if (mPreviewRequest != NULL) { + result.append(" Preview request:\n"); + write(fd, result.string(), result.size()); + dump_indented_camera_metadata(mPreviewRequest, fd, 2, 6); + } else { + result.append(" Preview request: undefined\n"); + write(fd, result.string(), result.size()); + } + + if (mCaptureRequest != NULL) { + result = " Capture request:\n"; + write(fd, result.string(), result.size()); + dump_indented_camera_metadata(mCaptureRequest, fd, 2, 6); + } else { + result = " Capture request: undefined\n"; + write(fd, result.string(), result.size()); + } + + if (mRecordingRequest != NULL) { + result = " Recording request:\n"; + write(fd, result.string(), result.size()); + dump_indented_camera_metadata(mRecordingRequest, fd, 2, 6); + } else { + result = " Recording request: undefined\n"; + write(fd, result.string(), result.size()); + } + + 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; +} + +const char* Camera2Client::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 +} + +// ICamera interface + +void Camera2Client::disconnect() { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + Mutex::Autolock icl(mICameraLock); + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return; + + if (mDevice == 0) return; + + stopPreviewL(); + + mDevice->waitUntilDrained(); + + if (mPreviewStreamId != NO_STREAM) { + mDevice->deleteStream(mPreviewStreamId); + mPreviewStreamId = NO_STREAM; + } + + if (mCaptureStreamId != NO_STREAM) { + mDevice->deleteStream(mCaptureStreamId); + mCaptureStreamId = NO_STREAM; + } + + if (mRecordingStreamId != NO_STREAM) { + mDevice->deleteStream(mRecordingStreamId); + mRecordingStreamId = NO_STREAM; + } + + mDevice.clear(); + mState = DISCONNECTED; + + 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; + + 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); + + // TODO: Check for uninterruptable conditions + + if (mClientPid == getCallingPid()) { + mClientPid = 0; + mCameraClient.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; + } + + switch (mState) { + case DISCONNECTED: + case RECORD: + case STILL_CAPTURE: + case VIDEO_SNAPSHOT: + ALOGE("%s: Camera %d: Cannot set preview display while in state %s", + __FUNCTION__, mCameraId, getStateName(mState)); + return INVALID_OPERATION; + case STOPPED: + case WAITING_FOR_PREVIEW_WINDOW: + // OK + break; + case PREVIEW: + // Already running preview - need to stop and create a new stream + // TODO: Optimize this so that we don't wait for old stream to drain + // before spinning up new stream + mDevice->setStreamingRequest(NULL); + mState = WAITING_FOR_PREVIEW_WINDOW; + break; + } + + if (mPreviewStreamId != NO_STREAM) { + res = mDevice->waitUntilDrained(); + if (res != OK) { + ALOGE("%s: Error waiting for preview to drain: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + res = mDevice->deleteStream(mPreviewStreamId); + if (res != OK) { + ALOGE("%s: Unable to delete old preview stream: %s (%d)", + __FUNCTION__, strerror(-res), res); + return res; + } + mPreviewStreamId = NO_STREAM; + } + + mPreviewSurface = binder; + mPreviewWindow = window; + + if (mState == WAITING_FOR_PREVIEW_WINDOW) { + return startPreviewL(); + } + + return OK; +} + +void Camera2Client::setPreviewCallbackFlag(int flag) { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return; +} + +status_t Camera2Client::startPreview() { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + Mutex::Autolock icl(mICameraLock); + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return res; + return startPreviewL(); +} + +status_t Camera2Client::startPreviewL() { + ATRACE_CALL(); + status_t res; + if (mState >= PREVIEW) { + ALOGE("%s: Can't start preview in state %s", + __FUNCTION__, getStateName(mState)); + return INVALID_OPERATION; + } + + if (mPreviewWindow == 0) { + mState = WAITING_FOR_PREVIEW_WINDOW; + return OK; + } + mState = STOPPED; + + LockedParameters::Key k(mParameters); + + res = updatePreviewStream(k.mParameters); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update preview stream: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + + if (mPreviewRequest == NULL) { + res = updatePreviewRequest(k.mParameters); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to create preview request: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + } + + res = updateEntry(mPreviewRequest, + ANDROID_REQUEST_OUTPUT_STREAMS, + &mPreviewStreamId, 1); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to set up preview request: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + res = sort_camera_metadata(mPreviewRequest); + if (res != OK) { + ALOGE("%s: Camera %d: Error sorting preview request: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + + res = mDevice->setStreamingRequest(mPreviewRequest); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to set preview request to start preview: " + "%s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + mState = 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(); + switch (mState) { + case DISCONNECTED: + ALOGE("%s: Camera %d: Call before initialized", + __FUNCTION__, mCameraId); + break; + case STOPPED: + break; + case STILL_CAPTURE: + ALOGE("%s: Camera %d: Cannot stop preview during still capture.", + __FUNCTION__, mCameraId); + break; + case RECORD: + // TODO: Handle record stop here + case PREVIEW: + mDevice->setStreamingRequest(NULL); + mDevice->waitUntilDrained(); + case WAITING_FOR_PREVIEW_WINDOW: + mState = STOPPED; + break; + default: + ALOGE("%s: Camera %d: Unknown state %d", __FUNCTION__, mCameraId, + mState); + } +} + +bool Camera2Client::previewEnabled() { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return false; + + return mState == PREVIEW; +} + +status_t Camera2Client::storeMetaDataInBuffers(bool enabled) { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return res; + + switch (mState) { + case RECORD: + case VIDEO_SNAPSHOT: + ALOGE("%s: Camera %d: Can't be called in state %s", + __FUNCTION__, mCameraId, getStateName(mState)); + return INVALID_OPERATION; + default: + // OK + break; + } + LockedParameters::Key k(mParameters); + + k.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; + + switch (mState) { + case STOPPED: + res = startPreviewL(); + if (res != OK) return res; + break; + case PREVIEW: + // Ready to go + break; + case RECORD: + case VIDEO_SNAPSHOT: + // OK to call this when recording is already on + return OK; + break; + default: + ALOGE("%s: Camera %d: Can't start recording in state %s", + __FUNCTION__, mCameraId, getStateName(mState)); + return INVALID_OPERATION; + }; + + LockedParameters::Key k(mParameters); + + if (!k.mParameters.storeMetadataInBuffers) { + ALOGE("%s: Camera %d: Recording only supported in metadata mode, but " + "non-metadata recording mode requested!", __FUNCTION__, + mCameraId); + return INVALID_OPERATION; + } + + res = updateRecordingStream(k.mParameters); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update recording stream: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + + if (mRecordingRequest == NULL) { + res = updateRecordingRequest(k.mParameters); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to create recording request: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + } + + uint8_t outputStreams[2] = { mPreviewStreamId, mRecordingStreamId }; + res = updateEntry(mRecordingRequest, + ANDROID_REQUEST_OUTPUT_STREAMS, + outputStreams, 2); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to set up recording request: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + res = sort_camera_metadata(mRecordingRequest); + if (res != OK) { + ALOGE("%s: Camera %d: Error sorting recording request: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + + res = mDevice->setStreamingRequest(mRecordingRequest); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to set recording request to start " + "recording: %s (%d)", __FUNCTION__, mCameraId, + strerror(-res), res); + return res; + } + mState = RECORD; + + return OK; +} + +void Camera2Client::stopRecording() { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + Mutex::Autolock icl(mICameraLock); + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return; + + switch (mState) { + case RECORD: + // OK to stop + break; + case STOPPED: + case PREVIEW: + case STILL_CAPTURE: + case VIDEO_SNAPSHOT: + default: + ALOGE("%s: Camera %d: Can't stop recording in state %s", + __FUNCTION__, mCameraId, getStateName(mState)); + return; + }; + + // Back to preview. Since record can only be reached through preview, + // all preview stream setup should be up to date. + res = mDevice->setStreamingRequest(mPreviewRequest); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to switch back to preview request: " + "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); + return; + } + + // TODO: Should recording heap be freed? Can't do it yet since requests + // could still be in flight. + + mState = PREVIEW; +} + +bool Camera2Client::recordingEnabled() { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + + if ( checkPid(__FUNCTION__) != OK) return false; + + return recordingEnabledL(); +} + +bool Camera2Client::recordingEnabledL() { + ATRACE_CALL(); + + return (mState == RECORD || mState == VIDEO_SNAPSHOT); +} + +void Camera2Client::releaseRecordingFrame(const sp<IMemory>& mem) { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + status_t res; + if ( checkPid(__FUNCTION__) != OK) return; + // 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__, mCameraId, + 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__, mCameraId, type, kMetadataBufferTypeGrallocSource); + return; + } + buffer_handle_t imgBuffer = *(buffer_handle_t*)(data + 4); + ALOGV("%s: Camera %d: Freeing buffer_handle_t %p", __FUNCTION__, mCameraId, + imgBuffer); + res = mRecordingConsumer->freeBuffer(imgBuffer); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to free recording frame (buffer_handle_t: %p):" + "%s (%d)", + __FUNCTION__, mCameraId, imgBuffer, strerror(-res), res); + return; + } + + mRecordingHeapFree++; +} + +status_t Camera2Client::autoFocus() { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return res; + + int triggerId; + { + LockedParameters::Key k(mParameters); + k.mParameters.currentAfTriggerId = ++k.mParameters.afTriggerCounter; + triggerId = k.mParameters.currentAfTriggerId; + } + + mDevice->triggerAutofocus(triggerId); + + return OK; +} + +status_t Camera2Client::cancelAutoFocus() { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return res; + + int triggerId; + { + LockedParameters::Key k(mParameters); + triggerId = ++k.mParameters.afTriggerCounter; + } + + 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; + + switch (mState) { + case DISCONNECTED: + case STOPPED: + case WAITING_FOR_PREVIEW_WINDOW: + ALOGE("%s: Camera %d: Cannot take picture without preview enabled", + __FUNCTION__, mCameraId); + return INVALID_OPERATION; + case PREVIEW: + case RECORD: + // Good to go for takePicture + break; + case STILL_CAPTURE: + case VIDEO_SNAPSHOT: + ALOGE("%s: Camera %d: Already taking a picture", + __FUNCTION__, mCameraId); + return INVALID_OPERATION; + } + + LockedParameters::Key k(mParameters); + + res = updateCaptureStream(k.mParameters); + if (res != OK) { + ALOGE("%s: Camera %d: Can't set up still image stream: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + + if (mCaptureRequest == NULL) { + res = updateCaptureRequest(k.mParameters); + if (res != OK) { + ALOGE("%s: Camera %d: Can't create still image capture request: " + "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + } + + camera_metadata_entry_t outputStreams; + if (mState == PREVIEW) { + uint8_t streamIds[2] = { mPreviewStreamId, mCaptureStreamId }; + res = updateEntry(mCaptureRequest, ANDROID_REQUEST_OUTPUT_STREAMS, + &streamIds, 2); + } else if (mState == RECORD) { + uint8_t streamIds[3] = { mPreviewStreamId, mRecordingStreamId, + mCaptureStreamId }; + res = updateEntry(mCaptureRequest, ANDROID_REQUEST_OUTPUT_STREAMS, + &streamIds, 3); + } + + if (res != OK) { + ALOGE("%s: Camera %d: Unable to set up still image capture request: " + "%s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + res = sort_camera_metadata(mCaptureRequest); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to sort capture request: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + + camera_metadata_t *captureCopy = clone_camera_metadata(mCaptureRequest); + if (captureCopy == NULL) { + ALOGE("%s: Camera %d: Unable to copy capture request for HAL device", + __FUNCTION__, mCameraId); + return NO_MEMORY; + } + + if (mState == PREVIEW) { + res = mDevice->setStreamingRequest(NULL); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to stop preview for still capture: " + "%s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + } + // TODO: Capture should be atomic with setStreamingRequest here + res = mDevice->capture(captureCopy); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to submit still image capture request: " + "%s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + + switch (mState) { + case PREVIEW: + mState = STILL_CAPTURE; + break; + case RECORD: + mState = VIDEO_SNAPSHOT; + break; + default: + ALOGE("%s: Camera %d: Unknown state for still capture!", + __FUNCTION__, mCameraId); + return INVALID_OPERATION; + } + + return OK; +} + +status_t Camera2Client::setParameters(const String8& params) { + ATRACE_CALL(); + ALOGV("%s: E", __FUNCTION__); + Mutex::Autolock icl(mICameraLock); + status_t res; + if ( (res = checkPid(__FUNCTION__) ) != OK) return res; + + LockedParameters::Key k(mParameters); + + CameraParameters newParams(params); + + // 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; + + // PREVIEW_SIZE + int previewWidth, previewHeight; + newParams.getPreviewSize(&previewWidth, &previewHeight); + + if (previewWidth != k.mParameters.previewWidth || + previewHeight != k.mParameters.previewHeight) { + if (mState >= PREVIEW) { + ALOGE("%s: Preview size cannot be updated when preview " + "is active! (Currently %d x %d, requested %d x %d", + __FUNCTION__, + k.mParameters.previewWidth, k.mParameters.previewHeight, + previewWidth, previewHeight); + return BAD_VALUE; + } + camera_metadata_entry_t availablePreviewSizes = + staticInfo(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES); + for (i = 0; i < availablePreviewSizes.count; i += 2 ) { + if (availablePreviewSizes.data.i32[i] == previewWidth && + availablePreviewSizes.data.i32[i+1] == previewHeight) break; + } + if (i == availablePreviewSizes.count) { + ALOGE("%s: Requested preview size %d x %d is not supported", + __FUNCTION__, previewWidth, previewHeight); + return BAD_VALUE; + } + } + + // PREVIEW_FPS_RANGE + int previewFpsRange[2]; + int previewFps = 0; + bool fpsRangeChanged = false; + newParams.getPreviewFpsRange(&previewFpsRange[0], &previewFpsRange[1]); + if (previewFpsRange[0] != k.mParameters.previewFpsRange[0] || + previewFpsRange[1] != k.mParameters.previewFpsRange[1]) { + fpsRangeChanged = true; + camera_metadata_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] == + previewFpsRange[0]) && + (availablePreviewFpsRanges.data.i32[i+1] == + previewFpsRange[1]) ) { + break; + } + } + if (i == availablePreviewFpsRanges.count) { + ALOGE("%s: Requested preview FPS range %d - %d is not supported", + __FUNCTION__, previewFpsRange[0], previewFpsRange[1]); + return BAD_VALUE; + } + previewFps = previewFpsRange[0]; + } + + // PREVIEW_FORMAT + int previewFormat = formatStringToEnum(newParams.getPreviewFormat()); + if (previewFormat != k.mParameters.previewFormat) { + if (mState >= PREVIEW) { + ALOGE("%s: Preview format cannot be updated when preview " + "is active!", __FUNCTION__); + return BAD_VALUE; + } + camera_metadata_entry_t availableFormats = + staticInfo(ANDROID_SCALER_AVAILABLE_FORMATS); + for (i = 0; i < availableFormats.count; i++) { + if (availableFormats.data.i32[i] == previewFormat) break; + } + if (i == availableFormats.count) { + ALOGE("%s: Requested preview format %s (0x%x) is not supported", + __FUNCTION__, newParams.getPreviewFormat(), 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) { + previewFps = newParams.getPreviewFrameRate(); + if (previewFps != k.mParameters.previewFps) { + camera_metadata_entry_t availableFrameRates = + staticInfo(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES); + for (i = 0; i < availableFrameRates.count; i+=2) { + if (availableFrameRates.data.i32[i] == previewFps) break; + } + if (i == availableFrameRates.count) { + ALOGE("%s: Requested preview frame rate %d is not supported", + __FUNCTION__, previewFps); + return BAD_VALUE; + } + previewFpsRange[0] = availableFrameRates.data.i32[i]; + previewFpsRange[1] = availableFrameRates.data.i32[i+1]; + } + } + + // PICTURE_SIZE + int pictureWidth, pictureHeight; + newParams.getPictureSize(&pictureWidth, &pictureHeight); + if (pictureWidth == k.mParameters.pictureWidth || + pictureHeight == k.mParameters.pictureHeight) { + camera_metadata_entry_t availablePictureSizes = + staticInfo(ANDROID_SCALER_AVAILABLE_JPEG_SIZES); + for (i = 0; i < availablePictureSizes.count; i+=2) { + if (availablePictureSizes.data.i32[i] == pictureWidth && + availablePictureSizes.data.i32[i+1] == pictureHeight) break; + } + if (i == availablePictureSizes.count) { + ALOGE("%s: Requested picture size %d x %d is not supported", + __FUNCTION__, pictureWidth, pictureHeight); + return BAD_VALUE; + } + } + + // JPEG_THUMBNAIL_WIDTH/HEIGHT + int jpegThumbSize[2]; + jpegThumbSize[0] = + newParams.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH); + jpegThumbSize[1] = + newParams.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT); + if (jpegThumbSize[0] != k.mParameters.jpegThumbSize[0] || + jpegThumbSize[1] != k.mParameters.jpegThumbSize[1]) { + camera_metadata_entry_t availableJpegThumbSizes = + staticInfo(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES); + for (i = 0; i < availableJpegThumbSizes.count; i+=2) { + if (availableJpegThumbSizes.data.i32[i] == jpegThumbSize[0] && + availableJpegThumbSizes.data.i32[i+1] == jpegThumbSize[1]) { + break; + } + } + if (i == availableJpegThumbSizes.count) { + ALOGE("%s: Requested JPEG thumbnail size %d x %d is not supported", + __FUNCTION__, jpegThumbSize[0], jpegThumbSize[1]); + return BAD_VALUE; + } + } + + // JPEG_THUMBNAIL_QUALITY + int jpegThumbQuality = + newParams.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY); + if (jpegThumbQuality < 0 || jpegThumbQuality > 100) { + ALOGE("%s: Requested JPEG thumbnail quality %d is not supported", + __FUNCTION__, jpegThumbQuality); + return BAD_VALUE; + } + + // JPEG_QUALITY + int jpegQuality = + newParams.getInt(CameraParameters::KEY_JPEG_QUALITY); + if (jpegQuality < 0 || jpegQuality > 100) { + ALOGE("%s: Requested JPEG quality %d is not supported", + __FUNCTION__, jpegQuality); + return BAD_VALUE; + } + + // ROTATION + int jpegRotation = + newParams.getInt(CameraParameters::KEY_ROTATION); + if (jpegRotation != 0 && + jpegRotation != 90 && + jpegRotation != 180 && + jpegRotation != 270) { + ALOGE("%s: Requested picture rotation angle %d is not supported", + __FUNCTION__, jpegRotation); + return BAD_VALUE; + } + + // GPS + bool gpsEnabled = false; + double gpsCoordinates[3] = {0,0,0}; + int64_t gpsTimestamp = 0; + String8 gpsProcessingMethod; + 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; + gpsCoordinates[0] = strtod(gpsLatStr, &endPtr); + if (errno || endPtr == gpsLatStr) { + ALOGE("%s: Malformed GPS latitude: %s", __FUNCTION__, gpsLatStr); + return BAD_VALUE; + } + errno = 0; + gpsCoordinates[1] = strtod(gpsLongStr, &endPtr); + if (errno || endPtr == gpsLongStr) { + ALOGE("%s: Malformed GPS longitude: %s", __FUNCTION__, gpsLongStr); + return BAD_VALUE; + } + errno = 0; + gpsCoordinates[2] = strtod(gpsAltitudeStr, &endPtr); + if (errno || endPtr == gpsAltitudeStr) { + ALOGE("%s: Malformed GPS altitude: %s", __FUNCTION__, + gpsAltitudeStr); + return BAD_VALUE; + } + errno = 0; + gpsTimestamp = strtoll(gpsTimeStr, &endPtr, 10); + if (errno || endPtr == gpsTimeStr) { + ALOGE("%s: Malformed GPS timestamp: %s", __FUNCTION__, gpsTimeStr); + return BAD_VALUE; + } + gpsProcessingMethod = gpsProcMethodStr; + + gpsEnabled = true; + } + + // WHITE_BALANCE + int wbMode = wbModeStringToEnum( + newParams.get(CameraParameters::KEY_WHITE_BALANCE) ); + if (wbMode != k.mParameters.wbMode) { + camera_metadata_entry_t availableWbModes = + staticInfo(ANDROID_CONTROL_AWB_AVAILABLE_MODES); + for (i = 0; i < availableWbModes.count; i++) { + if (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; + } + } + + // EFFECT + int effectMode = effectModeStringToEnum( + newParams.get(CameraParameters::KEY_EFFECT) ); + if (effectMode != k.mParameters.effectMode) { + camera_metadata_entry_t availableEffectModes = + staticInfo(ANDROID_CONTROL_AVAILABLE_EFFECTS); + for (i = 0; i < availableEffectModes.count; i++) { + if (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 + int antibandingMode = abModeStringToEnum( + newParams.get(CameraParameters::KEY_ANTIBANDING) ); + if (antibandingMode != k.mParameters.antibandingMode) { + camera_metadata_entry_t availableAbModes = + staticInfo(ANDROID_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES); + for (i = 0; i < availableAbModes.count; i++) { + if (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 + int sceneMode = sceneModeStringToEnum( + newParams.get(CameraParameters::KEY_SCENE_MODE) ); + if (sceneMode != k.mParameters.sceneMode) { + camera_metadata_entry_t availableSceneModes = + staticInfo(ANDROID_CONTROL_AVAILABLE_SCENE_MODES); + for (i = 0; i < availableSceneModes.count; i++) { + if (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; + } + } + + // FLASH_MODE + Parameters::flashMode_t flashMode = flashModeStringToEnum( + newParams.get(CameraParameters::KEY_FLASH_MODE) ); + if (flashMode != k.mParameters.flashMode) { + camera_metadata_entry_t flashAvailable = + staticInfo(ANDROID_FLASH_AVAILABLE, 1, 1); + if (!flashAvailable.data.u8[0] && + 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 (flashMode == Parameters::FLASH_MODE_RED_EYE) { + camera_metadata_entry_t availableAeModes = + staticInfo(ANDROID_CONTROL_AE_AVAILABLE_MODES); + for (i = 0; i < availableAeModes.count; i++) { + if (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 (flashMode == -1) { + ALOGE("%s: Requested flash mode \"%s\" is unknown", + __FUNCTION__, + newParams.get(CameraParameters::KEY_FLASH_MODE)); + return BAD_VALUE; + } + } + + // FOCUS_MODE + Parameters::focusMode_t focusMode = focusModeStringToEnum( + newParams.get(CameraParameters::KEY_FOCUS_MODE)); + if (focusMode != k.mParameters.focusMode) { + if (focusMode != Parameters::FOCUS_MODE_FIXED) { + camera_metadata_entry_t minFocusDistance = + staticInfo(ANDROID_LENS_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 (focusMode != Parameters::FOCUS_MODE_INFINITY) { + camera_metadata_entry_t availableFocusModes = + staticInfo(ANDROID_CONTROL_AF_AVAILABLE_MODES); + for (i = 0; i < availableFocusModes.count; i++) { + if (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; + } + } + } + } + + // FOCUS_AREAS + Vector<Parameters::Area> focusingAreas; + res = parseAreas(newParams.get(CameraParameters::KEY_FOCUS_AREAS), + &focusingAreas); + size_t max3aRegions = + (size_t)staticInfo(ANDROID_CONTROL_MAX_REGIONS, 1, 1).data.i32[0]; + if (res == OK) res = validateAreas(focusingAreas, max3aRegions); + if (res != OK) { + ALOGE("%s: Requested focus areas are malformed: %s", + __FUNCTION__, newParams.get(CameraParameters::KEY_FOCUS_AREAS)); + return BAD_VALUE; + } + + // EXPOSURE_COMPENSATION + int exposureCompensation = + newParams.getInt(CameraParameters::KEY_EXPOSURE_COMPENSATION); + camera_metadata_entry_t exposureCompensationRange = + staticInfo(ANDROID_CONTROL_AE_EXP_COMPENSATION_RANGE); + if (exposureCompensation < exposureCompensationRange.data.i32[0] || + exposureCompensation > exposureCompensationRange.data.i32[1]) { + ALOGE("%s: Requested exposure compensation index is out of bounds: %d", + __FUNCTION__, exposureCompensation); + return BAD_VALUE; + } + + // AUTO_EXPOSURE_LOCK (always supported) + bool autoExposureLock = boolFromString( + newParams.get(CameraParameters::KEY_AUTO_EXPOSURE_LOCK)); + + // AUTO_WHITEBALANCE_LOCK (always supported) + bool autoWhiteBalanceLock = boolFromString( + newParams.get(CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK)); + + // METERING_AREAS + Vector<Parameters::Area> meteringAreas; + res = parseAreas(newParams.get(CameraParameters::KEY_METERING_AREAS), + &meteringAreas); + if (res == OK) res = validateAreas(focusingAreas, max3aRegions); + if (res != OK) { + ALOGE("%s: Requested metering areas are malformed: %s", + __FUNCTION__, + newParams.get(CameraParameters::KEY_METERING_AREAS)); + return BAD_VALUE; + } + + // ZOOM + int zoom = newParams.getInt(CameraParameters::KEY_ZOOM); + if (zoom < 0 || zoom > (int)NUM_ZOOM_STEPS) { + ALOGE("%s: Requested zoom level %d is not supported", + __FUNCTION__, zoom); + return BAD_VALUE; + } + + // VIDEO_SIZE + int videoWidth, videoHeight; + newParams.getVideoSize(&videoWidth, &videoHeight); + if (videoWidth != k.mParameters.videoWidth || + videoHeight != k.mParameters.videoHeight) { + if (mState == RECORD) { + ALOGE("%s: Video size cannot be updated when recording is active!", + __FUNCTION__); + return BAD_VALUE; + } + camera_metadata_entry_t availableVideoSizes = + staticInfo(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES); + for (i = 0; i < availableVideoSizes.count; i += 2 ) { + if (availableVideoSizes.data.i32[i] == videoWidth && + availableVideoSizes.data.i32[i+1] == videoHeight) break; + } + if (i == availableVideoSizes.count) { + ALOGE("%s: Requested video size %d x %d is not supported", + __FUNCTION__, videoWidth, videoHeight); + return BAD_VALUE; + } + } + + // RECORDING_HINT (always supported) + bool recordingHint = boolFromString( + newParams.get(CameraParameters::KEY_RECORDING_HINT) ); + + // VIDEO_STABILIZATION + bool videoStabilization = boolFromString( + newParams.get(CameraParameters::KEY_VIDEO_STABILIZATION) ); + camera_metadata_entry_t availableVideoStabilizationModes = + staticInfo(ANDROID_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES); + if (videoStabilization && availableVideoStabilizationModes.count == 1) { + ALOGE("%s: Video stabilization not supported", __FUNCTION__); + } + + /** Update internal parameters */ + + k.mParameters.previewWidth = previewWidth; + k.mParameters.previewHeight = previewHeight; + k.mParameters.previewFpsRange[0] = previewFpsRange[0]; + k.mParameters.previewFpsRange[1] = previewFpsRange[1]; + k.mParameters.previewFps = previewFps; + k.mParameters.previewFormat = previewFormat; + + k.mParameters.pictureWidth = pictureWidth; + k.mParameters.pictureHeight = pictureHeight; + + k.mParameters.jpegThumbSize[0] = jpegThumbSize[0]; + k.mParameters.jpegThumbSize[1] = jpegThumbSize[1]; + k.mParameters.jpegQuality = jpegQuality; + k.mParameters.jpegThumbQuality = jpegThumbQuality; + + k.mParameters.gpsEnabled = gpsEnabled; + k.mParameters.gpsCoordinates[0] = gpsCoordinates[0]; + k.mParameters.gpsCoordinates[1] = gpsCoordinates[1]; + k.mParameters.gpsCoordinates[2] = gpsCoordinates[2]; + k.mParameters.gpsTimestamp = gpsTimestamp; + k.mParameters.gpsProcessingMethod = gpsProcessingMethod; + + k.mParameters.wbMode = wbMode; + k.mParameters.effectMode = effectMode; + k.mParameters.antibandingMode = antibandingMode; + k.mParameters.sceneMode = sceneMode; + + k.mParameters.flashMode = flashMode; + if (focusMode != k.mParameters.focusMode) { + k.mParameters.currentAfTriggerId = -1; + } + k.mParameters.focusMode = focusMode; + + k.mParameters.focusingAreas = focusingAreas; + k.mParameters.exposureCompensation = exposureCompensation; + k.mParameters.autoExposureLock = autoExposureLock; + k.mParameters.autoWhiteBalanceLock = autoWhiteBalanceLock; + k.mParameters.meteringAreas = meteringAreas; + k.mParameters.zoom = zoom; + + k.mParameters.videoWidth = videoWidth; + k.mParameters.videoHeight = videoHeight; + + k.mParameters.recordingHint = recordingHint; + k.mParameters.videoStabilization = videoStabilization; + + res = updatePreviewRequest(k.mParameters); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update preview request: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + res = updateCaptureRequest(k.mParameters); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update capture request: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + + res = updateRecordingRequest(k.mParameters); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update recording request: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + + if (mState == PREVIEW) { + res = mDevice->setStreamingRequest(mPreviewRequest); + if (res != OK) { + ALOGE("%s: Camera %d: Error streaming new preview request: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + } else if (mState == RECORD || mState == VIDEO_SNAPSHOT) { + res = mDevice->setStreamingRequest(mRecordingRequest); + if (res != OK) { + ALOGE("%s: Camera %d: Error streaming new record request: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + } + + k.mParameters.paramsFlattened = params; + + return OK; +} + +String8 Camera2Client::getParameters() const { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + if ( checkPid(__FUNCTION__) != OK) return String8(); + + LockedParameters::ReadKey k(mParameters); + + // TODO: Deal with focus distances + return k.mParameters.paramsFlattened; +} + +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: + return commandStopFaceDetectionL(); + 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) { + LockedParameters::Key k(mParameters); + int transform = 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; + } + if (transform != k.mParameters.previewTransform && + mPreviewStreamId != NO_STREAM) { + mDevice->setStreamTransform(mPreviewStreamId, transform); + } + k.mParameters.previewTransform = transform; + return OK; +} + +status_t Camera2Client::commandEnableShutterSoundL(bool enable) { + LockedParameters::Key k(mParameters); + if (enable) { + k.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; + } + } + + k.mParameters.playShutterSound = false; + return OK; +} + +status_t Camera2Client::commandPlayRecordingSoundL() { + mCameraService->playSound(CameraService::SOUND_RECORDING); + return OK; +} + +status_t Camera2Client::commandStartFaceDetectionL(int type) { + ALOGE("%s: Unimplemented!", __FUNCTION__); + return OK; +} + +status_t Camera2Client::commandStopFaceDetectionL() { + ALOGE("%s: Unimplemented!", __FUNCTION__); + return OK; +} + +status_t Camera2Client::commandEnableFocusMoveMsgL(bool enable) { + LockedParameters::Key k(mParameters); + k.mParameters.enableFocusMoveMessages = enable; + + return OK; +} + +status_t Camera2Client::commandPingL() { + // Always ping back if access is proper and device is alive + if (mState != 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; + } + + // 32 is the current upper limit on the video buffer count for BufferQueue + if (count > 32) { + ALOGE("%s: Camera %d: Error setting %d as video buffer count value", + __FUNCTION__, mCameraId, count); + return BAD_VALUE; + } + + // Need to reallocate memory for heap + if (mRecordingHeapCount != count) { + if (mRecordingHeap != 0) { + mRecordingHeap.clear(); + mRecordingHeap = NULL; + } + mRecordingHeapCount = count; + } + + return OK; +} + +/** 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; + { + LockedParameters::Key k(mParameters); + switch (k.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 != k.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; + k.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 != k.mParameters.currentAfTriggerId) break; + sendCompletedMessage = true; + afInMotion = false; + if (k.mParameters.enableFocusMoveMessages && + k.mParameters.afInMotion) { + sendMovingMessage = true; + } + k.mParameters.currentAfTriggerId = -1; + break; + case ANDROID_CONTROL_AF_STATE_INACTIVE: + // Cancel was called, or we switched state; care if + // currently moving + afInMotion = false; + if (k.mParameters.enableFocusMoveMessages && + k.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 (k.mParameters.enableFocusMoveMessages) { + sendMovingMessage = true; + } + break; + } + k.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, k.mParameters.focusMode); + } + } + } + if (sendMovingMessage) { + mCameraClient->notifyCallback(CAMERA_MSG_FOCUS_MOVE, + afInMotion ? 1 : 0, 0); + } + if (sendCompletedMessage) { + 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); +} + +void Camera2Client::notifyAutoWhitebalance(uint8_t newState, int triggerId) { + ALOGV("%s: Auto-whitebalance state now %d, last trigger %d", + __FUNCTION__, newState, triggerId); +} + +void Camera2Client::onCaptureAvailable() { + ATRACE_CALL(); + status_t res; + sp<ICameraClient> currentClient; + ALOGV("%s: Camera %d: Still capture available", __FUNCTION__, mCameraId); + + CpuConsumer::LockedBuffer imgBuffer; + { + Mutex::Autolock icl(mICameraLock); + + // TODO: Signal errors here upstream + if (mState != STILL_CAPTURE && mState != VIDEO_SNAPSHOT) { + ALOGE("%s: Camera %d: Still image produced unexpectedly!", + __FUNCTION__, mCameraId); + return; + } + + res = mCaptureConsumer->lockNextBuffer(&imgBuffer); + if (res != OK) { + ALOGE("%s: Camera %d: Error receiving still image buffer: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return; + } + + if (imgBuffer.format != HAL_PIXEL_FORMAT_BLOB) { + ALOGE("%s: Camera %d: Unexpected format for still image: " + "%x, expected %x", __FUNCTION__, mCameraId, + imgBuffer.format, + HAL_PIXEL_FORMAT_BLOB); + mCaptureConsumer->unlockBuffer(imgBuffer); + return; + } + + // TODO: Optimize this to avoid memcopy + void* captureMemory = mCaptureHeap->mHeap->getBase(); + size_t size = mCaptureHeap->mHeap->getSize(); + memcpy(captureMemory, imgBuffer.data, size); + + mCaptureConsumer->unlockBuffer(imgBuffer); + + currentClient = mCameraClient; + switch (mState) { + case STILL_CAPTURE: + mState = STOPPED; + break; + case VIDEO_SNAPSHOT: + mState = RECORD; + break; + default: + ALOGE("%s: Camera %d: Unexpected state %d", __FUNCTION__, + mCameraId, mState); + break; + } + } + // Call outside mICameraLock to allow re-entrancy from notification + if (currentClient != 0) { + currentClient->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, + mCaptureHeap->mBuffers[0], NULL); + } +} + +void Camera2Client::onRecordingFrameAvailable() { + ATRACE_CALL(); + status_t res; + sp<ICameraClient> currentClient; + size_t heapIdx = 0; + nsecs_t timestamp; + { + Mutex::Autolock icl(mICameraLock); + // TODO: Signal errors here upstream + bool discardData = false; + if (mState != RECORD && mState != VIDEO_SNAPSHOT) { + ALOGV("%s: Camera %d: Discarding recording image buffers received after " + "recording done", + __FUNCTION__, mCameraId); + discardData = true; + } + + buffer_handle_t imgBuffer; + res = mRecordingConsumer->getNextBuffer(&imgBuffer, ×tamp); + if (res != OK) { + ALOGE("%s: Camera %d: Error receiving recording buffer: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return; + } + + if (discardData) { + mRecordingConsumer->freeBuffer(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__, mCameraId, + mRecordingHeapCount, bufferSize); + if (mRecordingHeap != 0) { + ALOGV("%s: Camera %d: Previous heap has size %d " + "(new will be %d) bytes", __FUNCTION__, mCameraId, + mRecordingHeap->mHeap->getSize(), + bufferSize * mRecordingHeapCount); + } + // Need to allocate memory for heap + mRecordingHeap.clear(); + + mRecordingHeap = new Camera2Heap(bufferSize, mRecordingHeapCount, + "Camera2Client::RecordingHeap"); + if (mRecordingHeap->mHeap->getSize() == 0) { + ALOGE("%s: Camera %d: Unable to allocate memory for recording", + __FUNCTION__, mCameraId); + mRecordingConsumer->freeBuffer(imgBuffer); + return; + } + mRecordingHeapHead = 0; + mRecordingHeapFree = mRecordingHeapCount; + } + + if ( mRecordingHeapFree == 0) { + ALOGE("%s: Camera %d: No free recording buffers, dropping frame", + __FUNCTION__, mCameraId); + mRecordingConsumer->freeBuffer(imgBuffer); + return; + } + heapIdx = mRecordingHeapHead; + mRecordingHeapHead = (mRecordingHeapHead + 1) % mRecordingHeapCount; + mRecordingHeapFree--; + + ALOGV("%s: Camera %d: Timestamp %lld", + __FUNCTION__, mCameraId, 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; + memcpy(data, &type, 4); + memcpy(data + 4, &imgBuffer, sizeof(buffer_handle_t)); + ALOGV("%s: Camera %d: Sending out buffer_handle_t %p", + __FUNCTION__, mCameraId, imgBuffer); + currentClient = mCameraClient; + } + // Call outside mICameraLock to allow re-entrancy from notification + if (currentClient != 0) { + currentClient->dataCallbackTimestamp(timestamp, + CAMERA_MSG_VIDEO_FRAME, + mRecordingHeap->mBuffers[heapIdx]); + } +} + +camera_metadata_entry_t Camera2Client::staticInfo(uint32_t tag, + size_t minCount, size_t maxCount) { + status_t res; + camera_metadata_entry_t entry; + res = find_camera_metadata_entry(mDevice->info(), + tag, + &entry); + if (CC_UNLIKELY( res != OK )) { + 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): %s (%d)", + tagSection, tagName, tag, strerror(-res), res); + entry.count = 0; + entry.data.u8 = NULL; + } 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); + entry.count = 0; + entry.data.u8 = NULL; + } + + return entry; +} + +/** Utility methods */ + + +status_t Camera2Client::buildDefaultParameters() { + ATRACE_CALL(); + LockedParameters::Key k(mParameters); + + status_t res; + CameraParameters params; + + camera_metadata_entry_t availableProcessedSizes = + staticInfo(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES, 2); + if (!availableProcessedSizes.count) return NO_INIT; + + // TODO: Pick more intelligently + k.mParameters.previewWidth = availableProcessedSizes.data.i32[0]; + k.mParameters.previewHeight = availableProcessedSizes.data.i32[1]; + k.mParameters.videoWidth = k.mParameters.previewWidth; + k.mParameters.videoHeight = k.mParameters.previewHeight; + + params.setPreviewSize(k.mParameters.previewWidth, k.mParameters.previewHeight); + params.setVideoSize(k.mParameters.videoWidth, k.mParameters.videoHeight); + params.set(CameraParameters::KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO, + String8::format("%dx%d", + k.mParameters.previewWidth, k.mParameters.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_entry_t availableFpsRanges = + staticInfo(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, 2); + if (!availableFpsRanges.count) return NO_INIT; + + k.mParameters.previewFpsRange[0] = availableFpsRanges.data.i32[0]; + k.mParameters.previewFpsRange[1] = availableFpsRanges.data.i32[1]; + + params.set(CameraParameters::KEY_PREVIEW_FPS_RANGE, + String8::format("%d,%d", + k.mParameters.previewFpsRange[0], + k.mParameters.previewFpsRange[1])); + + { + 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], + availableFpsRanges.data.i32[i+1]); + } + params.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE, + supportedPreviewFpsRange); + } + + k.mParameters.previewFormat = HAL_PIXEL_FORMAT_YCrCb_420_SP; + params.set(CameraParameters::KEY_PREVIEW_FORMAT, + formatEnumToString(k.mParameters.previewFormat)); // NV21 + + k.mParameters.previewTransform = degToTransform(0, + mCameraFacing == CAMERA_FACING_FRONT); + + camera_metadata_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__, mCameraId, 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 + + params.set(CameraParameters::KEY_PREVIEW_FRAME_RATE, + k.mParameters.previewFpsRange[0]); + + { + String8 supportedPreviewFrameRates; + for (size_t i=0; i < availableFpsRanges.count; i += 2) { + if (i != 0) supportedPreviewFrameRates += ","; + supportedPreviewFrameRates += String8::format("%d", + availableFpsRanges.data.i32[i]); + } + params.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES, + supportedPreviewFrameRates); + } + + camera_metadata_entry_t availableJpegSizes = + staticInfo(ANDROID_SCALER_AVAILABLE_JPEG_SIZES, 2); + if (!availableJpegSizes.count) return NO_INIT; + + // TODO: Pick maximum + k.mParameters.pictureWidth = availableJpegSizes.data.i32[0]; + k.mParameters.pictureHeight = availableJpegSizes.data.i32[1]; + + params.setPictureSize(k.mParameters.pictureWidth, + k.mParameters.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_entry_t availableJpegThumbnailSizes = + staticInfo(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, 2); + if (!availableJpegThumbnailSizes.count) return NO_INIT; + + // TODO: Pick default thumbnail size sensibly + k.mParameters.jpegThumbSize[0] = availableJpegThumbnailSizes.data.i32[0]; + k.mParameters.jpegThumbSize[1] = availableJpegThumbnailSizes.data.i32[1]; + + params.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, + k.mParameters.jpegThumbSize[0]); + params.set(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, + k.mParameters.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); + } + + k.mParameters.jpegThumbQuality = 90; + params.set(CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY, + k.mParameters.jpegThumbQuality); + k.mParameters.jpegQuality = 90; + params.set(CameraParameters::KEY_JPEG_QUALITY, + k.mParameters.jpegQuality); + k.mParameters.jpegRotation = 0; + params.set(CameraParameters::KEY_ROTATION, + k.mParameters.jpegRotation); + + k.mParameters.gpsEnabled = false; + k.mParameters.gpsProcessingMethod = "unknown"; + // GPS fields in CameraParameters are not set by implementation + + k.mParameters.wbMode = ANDROID_CONTROL_AWB_AUTO; + params.set(CameraParameters::KEY_WHITE_BALANCE, + CameraParameters::WHITE_BALANCE_AUTO); + + camera_metadata_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_AUTO: + supportedWhiteBalance += + CameraParameters::WHITE_BALANCE_AUTO; + break; + case ANDROID_CONTROL_AWB_INCANDESCENT: + supportedWhiteBalance += + CameraParameters::WHITE_BALANCE_INCANDESCENT; + break; + case ANDROID_CONTROL_AWB_FLUORESCENT: + supportedWhiteBalance += + CameraParameters::WHITE_BALANCE_FLUORESCENT; + break; + case ANDROID_CONTROL_AWB_WARM_FLUORESCENT: + supportedWhiteBalance += + CameraParameters::WHITE_BALANCE_WARM_FLUORESCENT; + break; + case ANDROID_CONTROL_AWB_DAYLIGHT: + supportedWhiteBalance += + CameraParameters::WHITE_BALANCE_DAYLIGHT; + break; + case ANDROID_CONTROL_AWB_CLOUDY_DAYLIGHT: + supportedWhiteBalance += + CameraParameters::WHITE_BALANCE_CLOUDY_DAYLIGHT; + break; + case ANDROID_CONTROL_AWB_TWILIGHT: + supportedWhiteBalance += + CameraParameters::WHITE_BALANCE_TWILIGHT; + break; + case ANDROID_CONTROL_AWB_SHADE: + supportedWhiteBalance += + CameraParameters::WHITE_BALANCE_SHADE; + break; + // Skipping values not mappable to v1 API + case ANDROID_CONTROL_AWB_OFF: + addComma = false; + break; + default: + ALOGW("%s: Camera %d: Unknown white balance value: %d", + __FUNCTION__, mCameraId, + availableWhiteBalanceModes.data.u8[i]); + addComma = false; + break; + } + } + params.set(CameraParameters::KEY_SUPPORTED_WHITE_BALANCE, + supportedWhiteBalance); + } + + k.mParameters.effectMode = ANDROID_CONTROL_EFFECT_OFF; + params.set(CameraParameters::KEY_EFFECT, + CameraParameters::EFFECT_NONE); + + camera_metadata_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_OFF: + supportedEffects += + CameraParameters::EFFECT_NONE; + break; + case ANDROID_CONTROL_EFFECT_MONO: + supportedEffects += + CameraParameters::EFFECT_MONO; + break; + case ANDROID_CONTROL_EFFECT_NEGATIVE: + supportedEffects += + CameraParameters::EFFECT_NEGATIVE; + break; + case ANDROID_CONTROL_EFFECT_SOLARIZE: + supportedEffects += + CameraParameters::EFFECT_SOLARIZE; + break; + case ANDROID_CONTROL_EFFECT_SEPIA: + supportedEffects += + CameraParameters::EFFECT_SEPIA; + break; + case ANDROID_CONTROL_EFFECT_POSTERIZE: + supportedEffects += + CameraParameters::EFFECT_POSTERIZE; + break; + case ANDROID_CONTROL_EFFECT_WHITEBOARD: + supportedEffects += + CameraParameters::EFFECT_WHITEBOARD; + break; + case ANDROID_CONTROL_EFFECT_BLACKBOARD: + supportedEffects += + CameraParameters::EFFECT_BLACKBOARD; + break; + case ANDROID_CONTROL_EFFECT_AQUA: + supportedEffects += + CameraParameters::EFFECT_AQUA; + break; + default: + ALOGW("%s: Camera %d: Unknown effect value: %d", + __FUNCTION__, mCameraId, availableEffects.data.u8[i]); + addComma = false; + break; + } + } + params.set(CameraParameters::KEY_SUPPORTED_EFFECTS, supportedEffects); + } + + k.mParameters.antibandingMode = ANDROID_CONTROL_AE_ANTIBANDING_AUTO; + params.set(CameraParameters::KEY_ANTIBANDING, + CameraParameters::ANTIBANDING_AUTO); + + camera_metadata_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_OFF: + supportedAntibanding += + CameraParameters::ANTIBANDING_OFF; + break; + case ANDROID_CONTROL_AE_ANTIBANDING_50HZ: + supportedAntibanding += + CameraParameters::ANTIBANDING_50HZ; + break; + case ANDROID_CONTROL_AE_ANTIBANDING_60HZ: + supportedAntibanding += + CameraParameters::ANTIBANDING_60HZ; + break; + case ANDROID_CONTROL_AE_ANTIBANDING_AUTO: + supportedAntibanding += + CameraParameters::ANTIBANDING_AUTO; + break; + default: + ALOGW("%s: Camera %d: Unknown antibanding value: %d", + __FUNCTION__, mCameraId, + availableAntibandingModes.data.u8[i]); + addComma = false; + break; + } + } + params.set(CameraParameters::KEY_SUPPORTED_ANTIBANDING, + supportedAntibanding); + } + + k.mParameters.sceneMode = ANDROID_CONTROL_OFF; + params.set(CameraParameters::KEY_SCENE_MODE, + CameraParameters::SCENE_MODE_AUTO); + + camera_metadata_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__, mCameraId, + availableSceneModes.data.u8[i]); + addComma = false; + break; + } + } + if (!noSceneModes) { + params.set(CameraParameters::KEY_SUPPORTED_SCENE_MODES, + supportedSceneModes); + } + } + + camera_metadata_entry_t flashAvailable = + staticInfo(ANDROID_FLASH_AVAILABLE, 1, 1); + if (!flashAvailable.count) return NO_INIT; + + camera_metadata_entry_t availableAeModes = + staticInfo(ANDROID_CONTROL_AE_AVAILABLE_MODES); + if (!availableAeModes.count) return NO_INIT; + + if (flashAvailable.data.u8[0]) { + k.mParameters.flashMode = Parameters::FLASH_MODE_AUTO; + params.set(CameraParameters::KEY_FLASH_MODE, + CameraParameters::FLASH_MODE_AUTO); + + 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_ON_AUTO_FLASH_REDEYE) { + supportedFlashModes = supportedFlashModes + "," + + CameraParameters::FLASH_MODE_RED_EYE; + break; + } + } + params.set(CameraParameters::KEY_SUPPORTED_FLASH_MODES, + supportedFlashModes); + } else { + k.mParameters.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_entry_t minFocusDistance = + staticInfo(ANDROID_LENS_MINIMUM_FOCUS_DISTANCE, 1, 1); + if (!minFocusDistance.count) return NO_INIT; + + camera_metadata_entry_t availableAfModes = + staticInfo(ANDROID_CONTROL_AF_AVAILABLE_MODES); + if (!availableAfModes.count) return NO_INIT; + + if (minFocusDistance.data.f[0] == 0) { + // Fixed-focus lens + k.mParameters.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 { + k.mParameters.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_AUTO: + supportedFocusModes += + CameraParameters::FOCUS_MODE_AUTO; + break; + case ANDROID_CONTROL_AF_MACRO: + supportedFocusModes += + CameraParameters::FOCUS_MODE_MACRO; + break; + case ANDROID_CONTROL_AF_CONTINUOUS_VIDEO: + supportedFocusModes += + CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO; + break; + case ANDROID_CONTROL_AF_CONTINUOUS_PICTURE: + supportedFocusModes += + CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE; + break; + case ANDROID_CONTROL_AF_EDOF: + supportedFocusModes += + CameraParameters::FOCUS_MODE_EDOF; + break; + // Not supported in old API + case ANDROID_CONTROL_AF_OFF: + addComma = false; + break; + default: + ALOGW("%s: Camera %d: Unknown AF mode value: %d", + __FUNCTION__, mCameraId, availableAfModes.data.u8[i]); + addComma = false; + break; + } + } + params.set(CameraParameters::KEY_SUPPORTED_FOCUS_MODES, + supportedFocusModes); + } + + camera_metadata_entry_t max3aRegions = + staticInfo(ANDROID_CONTROL_MAX_REGIONS, 1, 1); + if (!max3aRegions.count) return NO_INIT; + + params.set(CameraParameters::KEY_MAX_NUM_FOCUS_AREAS, + max3aRegions.data.i32[0]); + params.set(CameraParameters::KEY_FOCUS_AREAS, + "(0,0,0,0,0)"); + k.mParameters.focusingAreas.clear(); + k.mParameters.focusingAreas.add(Parameters::Area(0,0,0,0,0)); + + camera_metadata_entry_t availableFocalLengths = + staticInfo(ANDROID_LENS_AVAILABLE_FOCAL_LENGTHS); + if (!availableFocalLengths.count) return NO_INIT; + + float minFocalLength = availableFocalLengths.data.f[0]; + params.setFloat(CameraParameters::KEY_FOCAL_LENGTH, minFocalLength); + + camera_metadata_entry_t sensorSize = + staticInfo(ANDROID_SENSOR_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); + + k.mParameters.exposureCompensation = 0; + params.set(CameraParameters::KEY_EXPOSURE_COMPENSATION, + k.mParameters.exposureCompensation); + + camera_metadata_entry_t exposureCompensationRange = + staticInfo(ANDROID_CONTROL_AE_EXP_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_entry_t exposureCompensationStep = + staticInfo(ANDROID_CONTROL_AE_EXP_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); + + k.mParameters.autoExposureLock = false; + params.set(CameraParameters::KEY_AUTO_EXPOSURE_LOCK, + CameraParameters::FALSE); + params.set(CameraParameters::KEY_AUTO_EXPOSURE_LOCK_SUPPORTED, + CameraParameters::TRUE); + + k.mParameters.autoWhiteBalanceLock = false; + params.set(CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK, + CameraParameters::FALSE); + params.set(CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK_SUPPORTED, + CameraParameters::TRUE); + + k.mParameters.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)"); + + k.mParameters.zoom = 0; + params.set(CameraParameters::KEY_ZOOM, k.mParameters.zoom); + params.set(CameraParameters::KEY_MAX_ZOOM, NUM_ZOOM_STEPS - 1); + + camera_metadata_entry_t maxDigitalZoom = + staticInfo(ANDROID_SCALER_AVAILABLE_MAX_ZOOM, 1, 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::TRUE); + + params.set(CameraParameters::KEY_FOCUS_DISTANCES, + "Infinity,Infinity,Infinity"); + + camera_metadata_entry_t maxFacesDetected = + staticInfo(ANDROID_STATS_MAX_FACE_COUNT, 1, 1); + params.set(CameraParameters::KEY_MAX_NUM_DETECTED_FACES_HW, + maxFacesDetected.data.i32[0]); + params.set(CameraParameters::KEY_MAX_NUM_DETECTED_FACES_SW, + 0); + + params.set(CameraParameters::KEY_VIDEO_FRAME_FORMAT, + CameraParameters::PIXEL_FORMAT_ANDROID_OPAQUE); + + params.set(CameraParameters::KEY_RECORDING_HINT, + CameraParameters::FALSE); + + params.set(CameraParameters::KEY_VIDEO_SNAPSHOT_SUPPORTED, + CameraParameters::TRUE); + + params.set(CameraParameters::KEY_VIDEO_STABILIZATION, + CameraParameters::FALSE); + + camera_metadata_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 + + k.mParameters.storeMetadataInBuffers = true; + k.mParameters.playShutterSound = true; + k.mParameters.afTriggerCounter = 0; + k.mParameters.currentAfTriggerId = -1; + + k.mParameters.paramsFlattened = params.flatten(); + + return OK; +} + +status_t Camera2Client::updatePreviewStream(const Parameters ¶ms) { + ATRACE_CALL(); + status_t res; + + if (mPreviewStreamId != NO_STREAM) { + // Check if stream parameters have to change + uint32_t currentWidth, currentHeight; + res = mDevice->getStreamInfo(mPreviewStreamId, + ¤tWidth, ¤tHeight, 0); + if (res != OK) { + ALOGE("%s: Camera %d: Error querying preview stream info: " + "%s (%d)", __FUNCTION__, mCameraId, 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__, mCameraId, currentWidth, currentHeight, + params.previewWidth, params.previewHeight); + res = mDevice->waitUntilDrained(); + if (res != OK) { + ALOGE("%s: Camera %d: Error waiting for preview to drain: " + "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + res = mDevice->deleteStream(mPreviewStreamId); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to delete old output stream " + "for preview: %s (%d)", __FUNCTION__, mCameraId, + strerror(-res), res); + return res; + } + mPreviewStreamId = NO_STREAM; + } + } + + if (mPreviewStreamId == NO_STREAM) { + res = mDevice->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__, mCameraId, strerror(-res), res); + return res; + } + } + + res = mDevice->setStreamTransform(mPreviewStreamId, + params.previewTransform); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to set preview stream transform: " + "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + + return OK; +} + +status_t Camera2Client::updatePreviewRequest(const Parameters ¶ms) { + ATRACE_CALL(); + status_t res; + if (mPreviewRequest == NULL) { + res = mDevice->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW, + &mPreviewRequest); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to create default preview request: " + "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + } + + res = updateRequestCommon(mPreviewRequest, params); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update common entries of preview " + "request: %s (%d)", __FUNCTION__, mCameraId, + strerror(-res), res); + return res; + } + + return OK; +} + +status_t Camera2Client::updateCaptureStream(const Parameters ¶ms) { + ATRACE_CALL(); + status_t res; + // Find out buffer size for JPEG + camera_metadata_entry_t maxJpegSize = + staticInfo(ANDROID_JPEG_MAX_SIZE); + if (maxJpegSize.count == 0) { + ALOGE("%s: Camera %d: Can't find ANDROID_JPEG_MAX_SIZE!", + __FUNCTION__, mCameraId); + return INVALID_OPERATION; + } + + if (mCaptureConsumer == 0) { + // Create CPU buffer queue endpoint + mCaptureConsumer = new CpuConsumer(1); + mCaptureConsumer->setFrameAvailableListener(new CaptureWaiter(this)); + mCaptureConsumer->setName(String8("Camera2Client::CaptureConsumer")); + mCaptureWindow = new SurfaceTextureClient( + mCaptureConsumer->getProducerInterface()); + // Create memory for API consumption + mCaptureHeap = new Camera2Heap(maxJpegSize.data.i32[0], 1, + "Camera2Client::CaptureHeap"); + if (mCaptureHeap->mHeap->getSize() == 0) { + ALOGE("%s: Camera %d: Unable to allocate memory for capture", + __FUNCTION__, mCameraId); + return NO_MEMORY; + } + } + + if (mCaptureStreamId != NO_STREAM) { + // Check if stream parameters have to change + uint32_t currentWidth, currentHeight; + res = mDevice->getStreamInfo(mCaptureStreamId, + ¤tWidth, ¤tHeight, 0); + if (res != OK) { + ALOGE("%s: Camera %d: Error querying capture output stream info: " + "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + if (currentWidth != (uint32_t)params.pictureWidth || + currentHeight != (uint32_t)params.pictureHeight) { + res = mDevice->deleteStream(mCaptureStreamId); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to delete old output stream " + "for capture: %s (%d)", __FUNCTION__, mCameraId, + strerror(-res), res); + return res; + } + mCaptureStreamId = NO_STREAM; + } + } + + if (mCaptureStreamId == NO_STREAM) { + // Create stream for HAL production + res = mDevice->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__, mCameraId, strerror(-res), res); + return res; + } + + } + return OK; +} + +status_t Camera2Client::updateCaptureRequest(const Parameters ¶ms) { + ATRACE_CALL(); + status_t res; + if (mCaptureRequest == NULL) { + res = mDevice->createDefaultRequest(CAMERA2_TEMPLATE_STILL_CAPTURE, + &mCaptureRequest); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to create default still image request:" + " %s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + } + + res = updateRequestCommon(mCaptureRequest, params); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update common entries of capture " + "request: %s (%d)", __FUNCTION__, mCameraId, + strerror(-res), res); + return res; + } + + res = updateEntry(mCaptureRequest, + ANDROID_JPEG_THUMBNAIL_SIZE, + params.jpegThumbSize, 2); + if (res != OK) return res; + res = updateEntry(mCaptureRequest, + ANDROID_JPEG_THUMBNAIL_QUALITY, + ¶ms.jpegThumbQuality, 1); + if (res != OK) return res; + res = updateEntry(mCaptureRequest, + ANDROID_JPEG_QUALITY, + ¶ms.jpegQuality, 1); + if (res != OK) return res; + res = updateEntry(mCaptureRequest, + ANDROID_JPEG_ORIENTATION, + ¶ms.jpegRotation, 1); + if (res != OK) return res; + + if (params.gpsEnabled) { + res = updateEntry(mCaptureRequest, + ANDROID_JPEG_GPS_COORDINATES, + params.gpsCoordinates, 3); + if (res != OK) return res; + res = updateEntry(mCaptureRequest, + ANDROID_JPEG_GPS_TIMESTAMP, + ¶ms.gpsTimestamp, 1); + if (res != OK) return res; + res = updateEntry(mCaptureRequest, + ANDROID_JPEG_GPS_PROCESSING_METHOD, + params.gpsProcessingMethod.string(), + params.gpsProcessingMethod.size()); + if (res != OK) return res; + } else { + res = deleteEntry(mCaptureRequest, + ANDROID_JPEG_GPS_COORDINATES); + if (res != OK) return res; + res = deleteEntry(mCaptureRequest, + ANDROID_JPEG_GPS_TIMESTAMP); + if (res != OK) return res; + res = deleteEntry(mCaptureRequest, + ANDROID_JPEG_GPS_PROCESSING_METHOD); + if (res != OK) return res; + } + + return OK; +} + +status_t Camera2Client::updateRecordingRequest(const Parameters ¶ms) { + ATRACE_CALL(); + status_t res; + if (mRecordingRequest == NULL) { + res = mDevice->createDefaultRequest(CAMERA2_TEMPLATE_VIDEO_RECORD, + &mRecordingRequest); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to create default recording request:" + " %s (%d)", __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + } + + res = updateRequestCommon(mRecordingRequest, params); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update common entries of recording " + "request: %s (%d)", __FUNCTION__, mCameraId, + strerror(-res), res); + return res; + } + + return OK; +} + +status_t Camera2Client::updateRecordingStream(const Parameters ¶ms) { + status_t res; + + if (mRecordingConsumer == 0) { + // Create CPU buffer queue endpoint + mRecordingConsumer = new MediaConsumer(mRecordingHeapCount); + mRecordingConsumer->setFrameAvailableListener(new RecordingWaiter(this)); + mRecordingConsumer->setName(String8("Camera2Client::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 = mDevice->getStreamInfo(mRecordingStreamId, + ¤tWidth, ¤tHeight, 0); + if (res != OK) { + ALOGE("%s: Camera %d: Error querying recording output stream info: " + "%s (%d)", __FUNCTION__, mCameraId, 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 = mDevice->deleteStream(mRecordingStreamId); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to delete old output stream " + "for recording: %s (%d)", __FUNCTION__, mCameraId, + strerror(-res), res); + return res; + } + mRecordingStreamId = NO_STREAM; + } + } + + if (mRecordingStreamId == NO_STREAM) { + res = mDevice->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__, mCameraId, strerror(-res), res); + return res; + } + } + + return OK; +} + +status_t Camera2Client::updateRequestCommon(camera_metadata_t *request, + const Parameters ¶ms) { + ATRACE_CALL(); + status_t res; + res = updateEntry(request, + ANDROID_CONTROL_AE_TARGET_FPS_RANGE, params.previewFpsRange, 2); + if (res != OK) return res; + + uint8_t wbMode = params.autoWhiteBalanceLock ? + ANDROID_CONTROL_AWB_LOCKED : params.wbMode; + res = updateEntry(request, + ANDROID_CONTROL_AWB_MODE, &wbMode, 1); + if (res != OK) return res; + res = updateEntry(request, + ANDROID_CONTROL_EFFECT_MODE, ¶ms.effectMode, 1); + if (res != OK) return res; + res = updateEntry(request, + ANDROID_CONTROL_AE_ANTIBANDING_MODE, + ¶ms.antibandingMode, 1); + if (res != OK) return res; + + uint8_t controlMode = + (params.sceneMode == ANDROID_CONTROL_SCENE_MODE_UNSUPPORTED) ? + ANDROID_CONTROL_AUTO : ANDROID_CONTROL_USE_SCENE_MODE; + res = updateEntry(request, + ANDROID_CONTROL_MODE, &controlMode, 1); + if (res != OK) return res; + if (controlMode == ANDROID_CONTROL_USE_SCENE_MODE) { + res = updateEntry(request, + ANDROID_CONTROL_SCENE_MODE, + ¶ms.sceneMode, 1); + if (res != OK) return res; + } + + uint8_t flashMode = ANDROID_FLASH_OFF; + uint8_t aeMode; + switch (params.flashMode) { + case Parameters::FLASH_MODE_OFF: + aeMode = ANDROID_CONTROL_AE_ON; break; + case Parameters::FLASH_MODE_AUTO: + aeMode = ANDROID_CONTROL_AE_ON_AUTO_FLASH; break; + case Parameters::FLASH_MODE_ON: + aeMode = ANDROID_CONTROL_AE_ON_ALWAYS_FLASH; break; + case Parameters::FLASH_MODE_TORCH: + aeMode = ANDROID_CONTROL_AE_ON; + flashMode = ANDROID_FLASH_TORCH; + break; + case Parameters::FLASH_MODE_RED_EYE: + aeMode = ANDROID_CONTROL_AE_ON_AUTO_FLASH_REDEYE; break; + default: + ALOGE("%s: Camera %d: Unknown flash mode %d", __FUNCTION__, + mCameraId, params.flashMode); + return BAD_VALUE; + } + if (params.autoExposureLock) aeMode = ANDROID_CONTROL_AE_LOCKED; + + res = updateEntry(request, + ANDROID_FLASH_MODE, &flashMode, 1); + if (res != OK) return res; + res = updateEntry(request, + ANDROID_CONTROL_AE_MODE, &aeMode, 1); + if (res != OK) return res; + + float focusDistance = 0; // infinity focus in diopters + uint8_t focusMode; + switch (params.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: + focusMode = params.focusMode; + break; + case Parameters::FOCUS_MODE_INFINITY: + case Parameters::FOCUS_MODE_FIXED: + focusMode = ANDROID_CONTROL_AF_OFF; + break; + default: + ALOGE("%s: Camera %d: Unknown focus mode %d", __FUNCTION__, + mCameraId, params.focusMode); + return BAD_VALUE; + } + res = updateEntry(request, + ANDROID_LENS_FOCUS_DISTANCE, &focusDistance, 1); + if (res != OK) return res; + res = updateEntry(request, + ANDROID_CONTROL_AF_MODE, &focusMode, 1); + if (res != OK) return res; + + size_t focusingAreasSize = params.focusingAreas.size() * 5; + int32_t *focusingAreas = new int32_t[focusingAreasSize]; + for (size_t i = 0; i < focusingAreasSize; i += 5) { + focusingAreas[i + 0] = params.focusingAreas[i].left; + focusingAreas[i + 1] = params.focusingAreas[i].top; + focusingAreas[i + 2] = params.focusingAreas[i].right; + focusingAreas[i + 3] = params.focusingAreas[i].bottom; + focusingAreas[i + 4] = params.focusingAreas[i].weight; + } + res = updateEntry(request, + ANDROID_CONTROL_AF_REGIONS, focusingAreas,focusingAreasSize); + if (res != OK) return res; + delete[] focusingAreas; + + res = updateEntry(request, + ANDROID_CONTROL_AE_EXP_COMPENSATION, + ¶ms.exposureCompensation, 1); + if (res != OK) return res; + + size_t meteringAreasSize = params.meteringAreas.size() * 5; + int32_t *meteringAreas = new int32_t[meteringAreasSize]; + for (size_t i = 0; i < meteringAreasSize; i += 5) { + meteringAreas[i + 0] = params.meteringAreas[i].left; + meteringAreas[i + 1] = params.meteringAreas[i].top; + meteringAreas[i + 2] = params.meteringAreas[i].right; + meteringAreas[i + 3] = params.meteringAreas[i].bottom; + meteringAreas[i + 4] = params.meteringAreas[i].weight; + } + res = updateEntry(request, + ANDROID_CONTROL_AE_REGIONS, meteringAreas, meteringAreasSize); + if (res != OK) return res; + + res = updateEntry(request, + ANDROID_CONTROL_AWB_REGIONS, meteringAreas, meteringAreasSize); + if (res != OK) return res; + delete[] meteringAreas; + + // Need to convert zoom index into a crop rectangle. The rectangle is + // chosen to maximize its area on the sensor + + camera_metadata_entry_t maxDigitalZoom = + staticInfo(ANDROID_SCALER_AVAILABLE_MAX_ZOOM); + float zoomIncrement = (maxDigitalZoom.data.f[0] - 1) / + (NUM_ZOOM_STEPS-1); + float zoomRatio = 1 + zoomIncrement * params.zoom; + + camera_metadata_entry_t activePixelArraySize = + staticInfo(ANDROID_SENSOR_ACTIVE_ARRAY_SIZE, 2, 2); + int32_t arrayWidth = activePixelArraySize.data.i32[0]; + int32_t arrayHeight = activePixelArraySize.data.i32[1]; + float zoomLeft, zoomTop, zoomWidth, zoomHeight; + if (params.previewWidth >= params.previewHeight) { + zoomWidth = arrayWidth / zoomRatio; + zoomHeight = zoomWidth * + params.previewHeight / params.previewWidth; + } else { + zoomHeight = arrayHeight / zoomRatio; + zoomWidth = zoomHeight * + params.previewWidth / params.previewHeight; + } + zoomLeft = (arrayWidth - zoomWidth) / 2; + zoomTop = (arrayHeight - zoomHeight) / 2; + + int32_t cropRegion[3] = { zoomLeft, zoomTop, zoomWidth }; + res = updateEntry(request, + ANDROID_SCALER_CROP_REGION, cropRegion, 3); + if (res != OK) return res; + + // TODO: Decide how to map recordingHint, or whether just to ignore it + + uint8_t vstabMode = params.videoStabilization ? + ANDROID_CONTROL_VIDEO_STABILIZATION_ON : + ANDROID_CONTROL_VIDEO_STABILIZATION_OFF; + res = updateEntry(request, + ANDROID_CONTROL_VIDEO_STABILIZATION_MODE, + &vstabMode, 1); + if (res != OK) return res; + + return OK; +} + +status_t Camera2Client::updateEntry(camera_metadata_t *buffer, + uint32_t tag, const void *data, size_t data_count) { + camera_metadata_entry_t entry; + status_t res; + res = find_camera_metadata_entry(buffer, tag, &entry); + if (res == NAME_NOT_FOUND) { + res = add_camera_metadata_entry(buffer, + tag, data, data_count); + } else if (res == OK) { + res = update_camera_metadata_entry(buffer, + 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; +} + +status_t Camera2Client::deleteEntry(camera_metadata_t *buffer, uint32_t tag) { + camera_metadata_entry_t entry; + status_t res; + res = find_camera_metadata_entry(buffer, 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(buffer, 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; +} + +int Camera2Client::formatStringToEnum(const char *format) { + return + !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* Camera2Client::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 Camera2Client::wbModeStringToEnum(const char *wbMode) { + return + !strcmp(wbMode, CameraParameters::WHITE_BALANCE_AUTO) ? + ANDROID_CONTROL_AWB_AUTO : + !strcmp(wbMode, CameraParameters::WHITE_BALANCE_INCANDESCENT) ? + ANDROID_CONTROL_AWB_INCANDESCENT : + !strcmp(wbMode, CameraParameters::WHITE_BALANCE_FLUORESCENT) ? + ANDROID_CONTROL_AWB_FLUORESCENT : + !strcmp(wbMode, CameraParameters::WHITE_BALANCE_WARM_FLUORESCENT) ? + ANDROID_CONTROL_AWB_WARM_FLUORESCENT : + !strcmp(wbMode, CameraParameters::WHITE_BALANCE_DAYLIGHT) ? + ANDROID_CONTROL_AWB_DAYLIGHT : + !strcmp(wbMode, CameraParameters::WHITE_BALANCE_CLOUDY_DAYLIGHT) ? + ANDROID_CONTROL_AWB_CLOUDY_DAYLIGHT : + !strcmp(wbMode, CameraParameters::WHITE_BALANCE_TWILIGHT) ? + ANDROID_CONTROL_AWB_TWILIGHT : + !strcmp(wbMode, CameraParameters::WHITE_BALANCE_SHADE) ? + ANDROID_CONTROL_AWB_SHADE : + -1; +} + +int Camera2Client::effectModeStringToEnum(const char *effectMode) { + return + !strcmp(effectMode, CameraParameters::EFFECT_NONE) ? + ANDROID_CONTROL_EFFECT_OFF : + !strcmp(effectMode, CameraParameters::EFFECT_MONO) ? + ANDROID_CONTROL_EFFECT_MONO : + !strcmp(effectMode, CameraParameters::EFFECT_NEGATIVE) ? + ANDROID_CONTROL_EFFECT_NEGATIVE : + !strcmp(effectMode, CameraParameters::EFFECT_SOLARIZE) ? + ANDROID_CONTROL_EFFECT_SOLARIZE : + !strcmp(effectMode, CameraParameters::EFFECT_SEPIA) ? + ANDROID_CONTROL_EFFECT_SEPIA : + !strcmp(effectMode, CameraParameters::EFFECT_POSTERIZE) ? + ANDROID_CONTROL_EFFECT_POSTERIZE : + !strcmp(effectMode, CameraParameters::EFFECT_WHITEBOARD) ? + ANDROID_CONTROL_EFFECT_WHITEBOARD : + !strcmp(effectMode, CameraParameters::EFFECT_BLACKBOARD) ? + ANDROID_CONTROL_EFFECT_BLACKBOARD : + !strcmp(effectMode, CameraParameters::EFFECT_AQUA) ? + ANDROID_CONTROL_EFFECT_AQUA : + -1; +} + +int Camera2Client::abModeStringToEnum(const char *abMode) { + return + !strcmp(abMode, CameraParameters::ANTIBANDING_AUTO) ? + ANDROID_CONTROL_AE_ANTIBANDING_AUTO : + !strcmp(abMode, CameraParameters::ANTIBANDING_OFF) ? + ANDROID_CONTROL_AE_ANTIBANDING_OFF : + !strcmp(abMode, CameraParameters::ANTIBANDING_50HZ) ? + ANDROID_CONTROL_AE_ANTIBANDING_50HZ : + !strcmp(abMode, CameraParameters::ANTIBANDING_60HZ) ? + ANDROID_CONTROL_AE_ANTIBANDING_60HZ : + -1; +} + +int Camera2Client::sceneModeStringToEnum(const char *sceneMode) { + return + !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; +} + +Camera2Client::Parameters::flashMode_t Camera2Client::flashModeStringToEnum( + const char *flashMode) { + return + !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; +} + +Camera2Client::Parameters::focusMode_t Camera2Client::focusModeStringToEnum( + const char *focusMode) { + return + !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; +} + +status_t Camera2Client::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 Camera2Client::validateAreas(const Vector<Parameters::Area> &areas, + size_t maxRegions) { + // 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; + } + } + 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 Camera2Client::boolFromString(const char *boolStr) { + return !boolStr ? false : + !strcmp(boolStr, CameraParameters::TRUE) ? true : + false; +} + +int Camera2Client::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; +} + +} // namespace android diff --git a/services/camera/libcameraservice/Camera2Client.h b/services/camera/libcameraservice/Camera2Client.h new file mode 100644 index 0000000..dffd4ab --- /dev/null +++ b/services/camera/libcameraservice/Camera2Client.h @@ -0,0 +1,416 @@ +/* + * 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 "camera/CameraParameters.h" +#include <binder/MemoryBase.h> +#include <binder/MemoryHeapBase.h> +#include <gui/CpuConsumer.h> +#include "MediaConsumer.h" + +namespace android { + +/** + * 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); + virtual ~Camera2Client(); + + status_t initialize(camera_module_t *module); + + virtual status_t dump(int fd, const Vector<String16>& args); + + // Interface used by CameraDevice + + 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); + +private: + enum State { + DISCONNECTED, + STOPPED, + WAITING_FOR_PREVIEW_WINDOW, + PREVIEW, + RECORD, + STILL_CAPTURE, + VIDEO_SNAPSHOT + } mState; + + static const char *getStateName(State state); + + /** 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; + + status_t setPreviewWindowL(const sp<IBinder>& binder, + sp<ANativeWindow> window); + + void stopPreviewL(); + status_t startPreviewL(); + + 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(); + status_t commandEnableFocusMoveMsgL(bool enable); + status_t commandPingL(); + status_t commandSetVideoBufferCountL(size_t count); + + // Current camera state; this is the contents of the CameraParameters object + // in a more-efficient format. 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 { + 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]; + int32_t jpegQuality, jpegThumbQuality; + int32_t jpegRotation; + + bool gpsEnabled; + double gpsCoordinates[3]; + int64_t gpsTimestamp; + String8 gpsProcessingMethod; + + int wbMode; + int effectMode; + int antibandingMode; + int sceneMode; + + enum flashMode_t { + FLASH_MODE_OFF = 0, + FLASH_MODE_AUTO, + FLASH_MODE_ON, + FLASH_MODE_TORCH, + FLASH_MODE_RED_EYE = ANDROID_CONTROL_AE_ON_AUTO_FLASH_REDEYE, + FLASH_MODE_INVALID = -1 + } flashMode; + + enum focusMode_t { + FOCUS_MODE_AUTO = ANDROID_CONTROL_AF_AUTO, + FOCUS_MODE_MACRO = ANDROID_CONTROL_AF_MACRO, + FOCUS_MODE_CONTINUOUS_VIDEO = ANDROID_CONTROL_AF_CONTINUOUS_VIDEO, + FOCUS_MODE_CONTINUOUS_PICTURE = + ANDROID_CONTROL_AF_CONTINUOUS_PICTURE, + FOCUS_MODE_EDOF = ANDROID_CONTROL_AF_EDOF, + FOCUS_MODE_INFINITY, + FOCUS_MODE_FIXED, + FOCUS_MODE_INVALID = -1 + } focusMode; + + 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) {} + }; + Vector<Area> focusingAreas; + + int32_t exposureCompensation; + bool autoExposureLock; + bool autoWhiteBalanceLock; + + Vector<Area> meteringAreas; + + int zoom; + + int videoWidth, videoHeight; + + bool recordingHint; + bool videoStabilization; + + 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 enableFocusMoveMessages; + + int afTriggerCounter; + int currentAfTriggerId; + bool afInMotion; + }; + + class LockedParameters { + public: + class Key { + public: + Key(LockedParameters &p): + mParameters(p.mParameters), + mLockedParameters(p) { + mLockedParameters.mLock.lock(); + } + + ~Key() { + mLockedParameters.mLock.unlock(); + } + Parameters &mParameters; + private: + // Disallow copying, default construction + Key(); + Key(const Key &); + Key &operator=(const Key &); + LockedParameters &mLockedParameters; + }; + class ReadKey { + public: + ReadKey(const LockedParameters &p): + mParameters(p.mParameters), + mLockedParameters(p) { + mLockedParameters.mLock.lock(); + } + + ~ReadKey() { + mLockedParameters.mLock.unlock(); + } + const Parameters &mParameters; + private: + // Disallow copying, default construction + ReadKey(); + ReadKey(const ReadKey &); + ReadKey &operator=(const ReadKey &); + const LockedParameters &mLockedParameters; + }; + + // Only use for dumping or other debugging + const Parameters &unsafeUnlock() { + return mParameters; + } + private: + Parameters mParameters; + mutable Mutex mLock; + + } mParameters; + + /** Camera device-related private members */ + + class Camera2Heap; + + // Number of zoom steps to simulate + static const unsigned int NUM_ZOOM_STEPS = 10; + // Used with stream IDs + static const int NO_STREAM = -1; + + /* Preview related members */ + + int mPreviewStreamId; + camera_metadata_t *mPreviewRequest; + sp<IBinder> mPreviewSurface; + sp<ANativeWindow> mPreviewWindow; + + status_t updatePreviewRequest(const Parameters ¶ms); + status_t updatePreviewStream(const Parameters ¶ms); + + /* Still image capture related members */ + + int mCaptureStreamId; + sp<CpuConsumer> mCaptureConsumer; + sp<ANativeWindow> mCaptureWindow; + // Simple listener that forwards frame available notifications from + // a CPU consumer to the capture notification + class CaptureWaiter: public CpuConsumer::FrameAvailableListener { + public: + CaptureWaiter(Camera2Client *parent) : mParent(parent) {} + void onFrameAvailable() { mParent->onCaptureAvailable(); } + private: + Camera2Client *mParent; + }; + sp<CaptureWaiter> mCaptureWaiter; + camera_metadata_t *mCaptureRequest; + sp<Camera2Heap> mCaptureHeap; + // Handle captured image buffers + void onCaptureAvailable(); + + status_t updateCaptureRequest(const Parameters ¶ms); + status_t updateCaptureStream(const Parameters ¶ms); + + /* Recording related members */ + + int mRecordingStreamId; + sp<MediaConsumer> mRecordingConsumer; + sp<ANativeWindow> mRecordingWindow; + // Simple listener that forwards frame available notifications from + // a CPU consumer to the recording notification + class RecordingWaiter: public MediaConsumer::FrameAvailableListener { + public: + RecordingWaiter(Camera2Client *parent) : mParent(parent) {} + void onFrameAvailable() { mParent->onRecordingFrameAvailable(); } + private: + Camera2Client *mParent; + }; + sp<RecordingWaiter> mRecordingWaiter; + camera_metadata_t *mRecordingRequest; + sp<Camera2Heap> mRecordingHeap; + + static const size_t kDefaultRecordingHeapCount = 8; + size_t mRecordingHeapCount; + size_t mRecordingHeapHead, mRecordingHeapFree; + // Handle new recording image buffers + void onRecordingFrameAvailable(); + + status_t updateRecordingRequest(const Parameters ¶ms); + status_t updateRecordingStream(const Parameters ¶ms); + + /** Notification-related members */ + + bool mAfInMotion; + + /** Camera2Device instance wrapping HAL2 entry */ + + sp<Camera2Device> mDevice; + + /** Utility members */ + + // Verify that caller is the owner of the camera + status_t checkPid(const char *checkLocation) const; + + // 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; + }; + + // Get values for static camera info entry. 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_entry_t staticInfo(uint32_t tag, + size_t minCount=0, size_t maxCount=0); + + // Convert static camera info from a camera2 device to the + // old API parameter map. + status_t buildDefaultParameters(); + + // Update parameters all requests use, based on mParameters + status_t updateRequestCommon(camera_metadata_t *request, const Parameters ¶ms); + + // Update specific metadata entry with new values. Adds entry if it does not + // exist, which will invalidate sorting + static status_t updateEntry(camera_metadata_t *buffer, + uint32_t tag, const void *data, size_t data_count); + + // Remove metadata entry. Will invalidate sorting. If entry does not exist, + // does nothing. + static status_t deleteEntry(camera_metadata_t *buffer, + uint32_t tag); + + // Convert camera1 preview format string to camera2 enum + static int formatStringToEnum(const char *format); + static const char *formatEnumToString(int format); + + static int wbModeStringToEnum(const char *wbMode); + static int effectModeStringToEnum(const char *effectMode); + static int abModeStringToEnum(const char *abMode); + static int sceneModeStringToEnum(const char *sceneMode); + static Parameters::flashMode_t flashModeStringToEnum(const char *flashMode); + static Parameters::focusMode_t focusModeStringToEnum(const char *focusMode); + static status_t parseAreas(const char *areasCStr, + Vector<Parameters::Area> *areas); + static status_t validateAreas(const Vector<Parameters::Area> &areas, + size_t maxRegions); + static bool boolFromString(const char *boolStr); + + // Map from camera orientation + facing to gralloc transform enum + static int degToTransform(int degrees, bool mirror); + +}; + +}; // namespace android + +#endif diff --git a/services/camera/libcameraservice/Camera2Device.cpp b/services/camera/libcameraservice/Camera2Device.cpp new file mode 100644 index 0000000..7c97e1e --- /dev/null +++ b/services/camera/libcameraservice/Camera2Device.cpp @@ -0,0 +1,1068 @@ +/* + * 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 "Camera2Device" +//#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 "Camera2Device.h" + +namespace android { + +Camera2Device::Camera2Device(int id): + mId(id), + mDevice(NULL) +{ + ALOGV("%s: E", __FUNCTION__); +} + +Camera2Device::~Camera2Device() +{ + ALOGV("%s: E", __FUNCTION__); + if (mDevice) { + status_t res; + 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; + } +} + +status_t Camera2Device::initialize(camera_module_t *module) +{ + ALOGV("%s: E", __FUNCTION__); + + status_t res; + char name[10]; + snprintf(name, sizeof(name), "%d", mId); + + res = module->common.methods->open(&module->common, name, + reinterpret_cast<hw_device_t**>(&mDevice)); + + if (res != OK) { + ALOGE("%s: Could not open camera %d: %s (%d)", __FUNCTION__, + mId, strerror(-res), res); + return res; + } + + if (mDevice->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, + mDevice->common.version); + return BAD_VALUE; + } + + camera_info info; + res = module->get_camera_info(mId, &info); + if (res != OK ) return res; + + if (info.device_version != mDevice->common.version) { + ALOGE("%s: HAL reporting mismatched camera_info version (%x)" + " and device version (%x).", __FUNCTION__, + mDevice->common.version, info.device_version); + return BAD_VALUE; + } + + mDeviceInfo = info.static_camera_characteristics; + + res = mRequestQueue.setConsumerDevice(mDevice); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to connect request queue to device: %s (%d)", + __FUNCTION__, mId, strerror(-res), res); + return res; + } + res = mFrameQueue.setProducerDevice(mDevice); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to connect frame queue to device: %s (%d)", + __FUNCTION__, mId, strerror(-res), res); + return res; + } + + res = mDevice->ops->get_metadata_vendor_tag_ops(mDevice, &mVendorTagOps); + if (res != OK ) { + ALOGE("%s: Camera %d: Unable to retrieve tag ops from device: %s (%d)", + __FUNCTION__, mId, strerror(-res), res); + return res; + } + + setNotifyCallback(NULL); + + return OK; +} + +status_t Camera2Device::dump(int fd, const Vector<String16>& args) { + + 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; +} + +camera_metadata_t *Camera2Device::info() { + ALOGVV("%s: E", __FUNCTION__); + + return mDeviceInfo; +} + +status_t Camera2Device::capture(camera_metadata_t* request) { + ALOGV("%s: E", __FUNCTION__); + + mRequestQueue.enqueue(request); + return OK; +} + + +status_t Camera2Device::setStreamingRequest(camera_metadata_t* request) { + ALOGV("%s: E", __FUNCTION__); + + mRequestQueue.setStreamSlot(request); + return OK; +} + +status_t Camera2Device::createStream(sp<ANativeWindow> consumer, + uint32_t width, uint32_t height, int format, size_t size, int *id) { + 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::getStreamInfo(int id, + uint32_t *width, uint32_t *height, uint32_t *format) { + 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) { + 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) { + 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::createDefaultRequest(int templateId, + camera_metadata_t **request) { + ALOGV("%s: E", __FUNCTION__); + return mDevice->ops->construct_default_request( + mDevice, templateId, request); +} + +status_t Camera2Device::waitUntilDrained() { + static const uint32_t kSleepTime = 50000; // 50 ms + static const uint32_t kMaxSleepTime = 10000000; // 10 s + ALOGV("%s: E", __FUNCTION__); + 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, requests still in flight", __FUNCTION__, + totalTime); + return TIMED_OUT; + } + } + return OK; +} + +status_t Camera2Device::setNotifyCallback(NotificationListener *listener) { + 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) { + 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::triggerAutofocus(uint32_t id) { + 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) { + 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) { + 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; +} + +/** + * Camera2Device::NotificationListener + */ + +Camera2Device::NotificationListener::~NotificationListener() { +} + +/** + * Camera2Device::MetadataQueue + */ + +Camera2Device::MetadataQueue::MetadataQueue(): + mDevice(NULL), + mFrameCount(0), + mCount(0), + mStreamSlotCount(0), + mSignalConsumer(true) +{ + 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() { + 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) { + 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) { + 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) { + ALOGVV("%s: E", __FUNCTION__); + Mutex::Autolock l(mMutex); + + mCount++; + mEntries.push_back(buf); + + return signalConsumerLocked(); +} + +int Camera2Device::MetadataQueue::getBufferCount() { + 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) +{ + 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) { + 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++; + } + + *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::setStreamSlot(camera_metadata_t *buf) +{ + 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) +{ + 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) { + 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() { + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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() { + if (mState != RELEASED) { + release(); + } +} + +status_t Camera2Device::StreamAdapter::connectToDevice( + sp<ANativeWindow> consumer, + uint32_t width, uint32_t height, int format, size_t size) { + 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() { + 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); + if (res != OK) { + ALOGE("%s: Unable to disconnect stream %d from native window", + __FUNCTION__, mId); + return res; + } + } + mId = -1; + mState = RELEASED; + return OK; +} + +status_t Camera2Device::StreamAdapter::setTransform(int transform) { + 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) { + 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) { + 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) { + StreamAdapter *stream = + const_cast<StreamAdapter*>(static_cast<const StreamAdapter*>(w)); + ALOGVV("Stream %d enqueue: Buffer %p captured at %lld ns", + stream->mId, (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->mFrameCount++; + stream->mLastTimestamp = timestamp; + return OK; +} + +int Camera2Device::StreamAdapter::cancel_buffer(const camera2_stream_ops_t* w, + buffer_handle_t* buffer) { + 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) { + 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); +} + + +}; // namespace android diff --git a/services/camera/libcameraservice/Camera2Device.h b/services/camera/libcameraservice/Camera2Device.h new file mode 100644 index 0000000..9be370f --- /dev/null +++ b/services/camera/libcameraservice/Camera2Device.h @@ -0,0 +1,338 @@ +/* + * 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" + +namespace android { + +class Camera2Device : public virtual RefBase { + public: + Camera2Device(int id); + + ~Camera2Device(); + + status_t initialize(camera_module_t *module); + + status_t dump(int fd, const Vector<String16>& args); + + /** + * Get a pointer to the device's static characteristics metadata buffer + */ + camera_metadata_t* info(); + + /** + * Submit request for capture. The Camera2Device takes ownership of the + * passed-in buffer. + */ + status_t capture(camera_metadata_t *request); + + /** + * Submit request for streaming. The Camera2Device makes a copy of the + * passed-in buffer and the caller retains ownership. + */ + status_t setStreamingRequest(camera_metadata_t *request); + + /** + * 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); + + /** + * 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); + + /** + * Create a metadata buffer with fields that the HAL device believes are + * best for the given use case + */ + status_t createDefaultRequest(int templateId, + camera_metadata_t **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); + + /** + * 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); + + private: + + const int mId; + camera2_device_t *mDevice; + + camera_metadata_t *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 = true); + int getBufferCount(); + status_t waitForBuffer(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; + + 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; + + // 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 0000000..562384d --- /dev/null +++ b/services/camera/libcameraservice/CameraClient.cpp @@ -0,0 +1,961 @@ +/* + * 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): + Client(cameraService, cameraClient, + cameraId, cameraFacing, clientPid) +{ + 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); + 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); + + // set mClientPid to let disconnet() tear down the hardware + mClientPid = callingPid; + 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); + + 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(); + + 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: + enableShutterSound(false); + break; + case 1: + enableShutterSound(true); + break; + 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 0000000..256298d --- /dev/null +++ b/services/camera/libcameraservice/CameraClient.h @@ -0,0 +1,155 @@ +/* + * 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); + ~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 87a0802..05ac9fa 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 cdfb2f5..0000000 --- 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 c6d8756..0000000 --- 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 385be50..878afde 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,16 +186,31 @@ 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); + break; + case CAMERA_DEVICE_API_VERSION_2_0: + client = new Camera2Client(this, cameraClient, cameraId, + info.facing, callingPid); + 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); mClient[cameraId] = client; LOG1("CameraService::connect X (id %d)", cameraId); return client; @@ -335,34 +350,17 @@ 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 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; 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 +368,7 @@ 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; } // ---------------------------------------------------------------------------- @@ -967,311 +390,12 @@ 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; +void CameraService::Client::disconnect() { + mCameraService->removeClient(mCameraClient); + mCameraService->setCameraFree(mCameraId); } // ---------------------------------------------------------------------------- @@ -1293,41 +417,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,14 +500,16 @@ 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; } diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index 95ac197..630fca7 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -27,17 +27,18 @@ namespace android { +extern volatile int32_t gLogLevel; + class MemoryHeapBase; class MediaPlayer; -class CameraHardwareInterface; class CameraService : public BinderService<CameraService>, public BnCameraService { - class Client; friend class BinderService<CameraService>; public: + class Client; static char const* getServiceName() { return "media.camera"; } CameraService(); @@ -68,114 +69,58 @@ 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); + ~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,50 +128,27 @@ 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; - - // 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; + + // 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; }; diff --git a/services/camera/libcameraservice/CannedJpeg.h b/services/camera/libcameraservice/CannedJpeg.h deleted file mode 100644 index 6dd99c1..0000000 --- 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 f3a6a67..0000000 --- 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 724de20..0000000 --- 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/MediaConsumer.cpp b/services/camera/libcameraservice/MediaConsumer.cpp new file mode 100644 index 0000000..0d857cf --- /dev/null +++ b/services/camera/libcameraservice/MediaConsumer.cpp @@ -0,0 +1,196 @@ +/* + * 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 "MediaConsumer" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <utils/Log.h> + +#include "MediaConsumer.h" + +#define MC_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__) +#define MC_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__) +#define MC_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__) +#define MC_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__) +#define MC_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__) + +namespace android { + +// Get an ID that's unique within this process. +static int32_t createProcessUniqueId() { + static volatile int32_t globalCounter = 0; + return android_atomic_inc(&globalCounter); +} + +MediaConsumer::MediaConsumer(uint32_t maxLockedBuffers) : + mMaxLockedBuffers(maxLockedBuffers), + mCurrentLockedBuffers(0) +{ + mName = String8::format("mc-unnamed-%d-%d", getpid(), + createProcessUniqueId()); + + mBufferQueue = new BufferQueue(true); + + wp<BufferQueue::ConsumerListener> listener; + sp<BufferQueue::ConsumerListener> proxy; + listener = static_cast<BufferQueue::ConsumerListener*>(this); + proxy = new BufferQueue::ProxyConsumerListener(listener); + + status_t err = mBufferQueue->consumerConnect(proxy); + if (err != NO_ERROR) { + ALOGE("MediaConsumer: error connecting to BufferQueue: %s (%d)", + strerror(-err), err); + } else { + mBufferQueue->setSynchronousMode(true); + mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER); + mBufferQueue->setConsumerName(mName); + } +} + +MediaConsumer::~MediaConsumer() +{ + Mutex::Autolock _l(mMutex); + for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + freeBufferLocked(i); + } + mBufferQueue->consumerDisconnect(); + mBufferQueue.clear(); +} + +void MediaConsumer::setName(const String8& name) { + Mutex::Autolock _l(mMutex); + mName = name; + mBufferQueue->setConsumerName(name); +} + +status_t MediaConsumer::getNextBuffer(buffer_handle_t *buffer, nsecs_t *timestamp) { + status_t err; + + if (!buffer) return BAD_VALUE; + if (mCurrentLockedBuffers == mMaxLockedBuffers) { + return INVALID_OPERATION; + } + + BufferQueue::BufferItem b; + + Mutex::Autolock _l(mMutex); + + err = mBufferQueue->acquireBuffer(&b); + if (err != OK) { + if (err == BufferQueue::NO_BUFFER_AVAILABLE) { + return BAD_VALUE; + } else { + MC_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); + return err; + } + } + + int buf = b.mBuf; + + if (b.mGraphicBuffer != NULL) { + mBufferSlot[buf] = b.mGraphicBuffer; + } + + if (b.mFence.get()) { + err = b.mFence->wait(Fence::TIMEOUT_NEVER); + if (err != OK) { + MC_LOGE("Failed to wait for fence of acquired buffer: %s (%d)", + strerror(-err), err); + return err; + } + } + + *buffer = mBufferSlot[buf]->handle; + *timestamp = b.mTimestamp; + + mCurrentLockedBuffers++; + + return OK; +} + +status_t MediaConsumer::freeBuffer(buffer_handle_t buffer) { + Mutex::Autolock _l(mMutex); + int buf = 0; + status_t err; + + for (; buf < BufferQueue::NUM_BUFFER_SLOTS; buf++) { + if (buffer == mBufferSlot[buf]->handle) break; + } + if (buf == BufferQueue::NUM_BUFFER_SLOTS) { + MC_LOGE("%s: Can't find buffer to free", __FUNCTION__); + return BAD_VALUE; + } + + err = mBufferQueue->releaseBuffer(buf, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, + Fence::NO_FENCE); + if (err == BufferQueue::STALE_BUFFER_SLOT) { + freeBufferLocked(buf); + } else if (err != OK) { + MC_LOGE("%s: Unable to release graphic buffer %d to queue", __FUNCTION__, + buf); + return err; + } + + mCurrentLockedBuffers--; + + return OK; +} + +void MediaConsumer::setFrameAvailableListener( + const sp<FrameAvailableListener>& listener) { + MC_LOGV("setFrameAvailableListener"); + Mutex::Autolock lock(mMutex); + mFrameAvailableListener = listener; +} + + +void MediaConsumer::onFrameAvailable() { + MC_LOGV("onFrameAvailable"); + sp<FrameAvailableListener> listener; + { // scope for the lock + Mutex::Autolock _l(mMutex); + listener = mFrameAvailableListener; + } + + if (listener != NULL) { + MC_LOGV("actually calling onFrameAvailable"); + listener->onFrameAvailable(); + } +} + +void MediaConsumer::onBuffersReleased() { + MC_LOGV("onBuffersReleased"); + + Mutex::Autolock lock(mMutex); + + uint32_t mask = 0; + mBufferQueue->getReleasedBuffers(&mask); + for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + if (mask & (1 << i)) { + freeBufferLocked(i); + } + } + +} + +status_t MediaConsumer::freeBufferLocked(int buf) { + status_t err = OK; + + mBufferSlot[buf] = NULL; + return err; +} + +} // namespace android diff --git a/services/camera/libcameraservice/MediaConsumer.h b/services/camera/libcameraservice/MediaConsumer.h new file mode 100644 index 0000000..3377d94 --- /dev/null +++ b/services/camera/libcameraservice/MediaConsumer.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_MEDIACONSUMER_H +#define ANDROID_SERVERS_CAMERA_MEDIACONSUMER_H + +#include <gui/BufferQueue.h> + +#include <ui/GraphicBuffer.h> + +#include <utils/String8.h> +#include <utils/Vector.h> +#include <utils/threads.h> + +#define ANDROID_GRAPHICS_MEDIACONSUMER_JNI_ID "mMediaConsumer" + +namespace android { + +/** + * MediaConsumer is a BufferQueue consumer endpoint that makes it + * straightforward to bridge Camera 2 to the existing media recording framework. + * This queue is synchronous by default. + * + * TODO: This is a temporary replacement for the full camera->media recording + * path using SurfaceMediaEncoder or equivalent. + */ + +class MediaConsumer: public virtual RefBase, + protected BufferQueue::ConsumerListener +{ + public: + struct FrameAvailableListener : public virtual RefBase { + // onFrameAvailable() is called each time an additional frame becomes + // available for consumption. A new frame queued will always trigger the + // callback, whether the queue is empty or not. + // + // This is called without any lock held and can be called concurrently + // by multiple threads. + virtual void onFrameAvailable() = 0; + }; + + // Create a new media consumer. The maxBuffers parameter specifies + // how many buffers can be locked for user access at the same time. + MediaConsumer(uint32_t maxBuffers); + + virtual ~MediaConsumer(); + + // set the name of the MediaConsumer that will be used to identify it in + // log messages. + void setName(const String8& name); + + // Gets the next graphics buffer from the producer. Returns BAD_VALUE if no + // new buffer is available, and INVALID_OPERATION if the maximum number of + // buffers is already in use. + // + // Only a fixed number of buffers can be available at a time, determined by + // the construction-time maxBuffers parameter. If INVALID_OPERATION is + // returned by getNextBuffer, then old buffers must be returned to the + // queue by calling freeBuffer before more buffers can be acquired. + status_t getNextBuffer(buffer_handle_t *buffer, nsecs_t *timestamp); + + // Returns a buffer to the queue, allowing it to be reused. Since + // only a fixed number of buffers may be locked at a time, old buffers must + // be released by calling unlockBuffer to ensure new buffers can be acquired by + // lockNextBuffer. + status_t freeBuffer(buffer_handle_t buffer); + + // setFrameAvailableListener sets the listener object that will be notified + // when a new frame becomes available. + void setFrameAvailableListener(const sp<FrameAvailableListener>& listener); + + sp<ISurfaceTexture> getProducerInterface() const { return mBufferQueue; } + protected: + + // Implementation of the BufferQueue::ConsumerListener interface. These + // calls are used to notify the MediaConsumer of asynchronous events in the + // BufferQueue. + virtual void onFrameAvailable(); + virtual void onBuffersReleased(); + + private: + // Free local buffer state + status_t freeBufferLocked(int buf); + + // Maximum number of buffers that can be locked at a time + uint32_t mMaxLockedBuffers; + + // mName is a string used to identify the SurfaceTexture in log messages. + // It can be set by the setName method. + String8 mName; + + // mFrameAvailableListener is the listener object that will be called when a + // new frame becomes available. If it is not NULL it will be called from + // queueBuffer. + sp<FrameAvailableListener> mFrameAvailableListener; + + // Underlying buffer queue + sp<BufferQueue> mBufferQueue; + + // Array for caching buffers from the buffer queue + sp<GraphicBuffer> mBufferSlot[BufferQueue::NUM_BUFFER_SLOTS]; + // Count of currently outstanding buffers + uint32_t mCurrentLockedBuffers; + + // mMutex is the mutex used to prevent concurrent access to the member + // variables of MediaConsumer objects. It must be locked whenever the + // member variables are accessed. + mutable Mutex mMutex; +}; + +} // namespace android + +#endif // ANDROID_SERVERS_CAMERA_MEDIACONSUMER_H |