diff options
58 files changed, 6594 insertions, 3494 deletions
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h index ef77692..f7ebd39 100644 --- a/include/media/AudioRecord.h +++ b/include/media/AudioRecord.h @@ -17,22 +17,15 @@ #ifndef AUDIORECORD_H_ #define AUDIORECORD_H_ -#include <stdint.h> -#include <sys/types.h> - -#include <media/IAudioFlinger.h> +#include <binder/IMemory.h> +#include <cutils/sched_policy.h> +#include <media/AudioSystem.h> #include <media/IAudioRecord.h> - +#include <system/audio.h> #include <utils/RefBase.h> #include <utils/Errors.h> -#include <binder/IInterface.h> -#include <binder/IMemory.h> -#include <cutils/sched_policy.h> #include <utils/threads.h> -#include <system/audio.h> -#include <media/AudioSystem.h> - namespace android { class audio_track_cblk_t; @@ -46,11 +39,10 @@ public: static const int DEFAULT_SAMPLE_RATE = 8000; /* Events used by AudioRecord callback function (callback_t). - * - * to keep in sync with frameworks/base/media/java/android/media/AudioRecord.java + * Keep in sync with frameworks/base/media/java/android/media/AudioRecord.java NATIVE_EVENT_*. */ enum event_type { - EVENT_MORE_DATA = 0, // Request to reqd more data from PCM buffer. + EVENT_MORE_DATA = 0, // Request to read more data from PCM buffer. EVENT_OVERRUN = 1, // PCM buffer overrun occured. EVENT_MARKER = 2, // Record head is at the specified marker position // (See setMarkerPosition()). @@ -72,7 +64,7 @@ public: int channelCount; audio_format_t format; size_t frameCount; - size_t size; + size_t size; // total size in bytes == frameCount * frameSize union { void* raw; short* i16; @@ -80,12 +72,6 @@ public: }; }; - /* These are static methods to control the system-wide AudioFlinger - * only privileged processes can have access to them - */ - -// static status_t setMasterMute(bool mute); - /* As a convenience, if a callback is supplied, a handler thread * is automatically created with the appropriate priority. This thread * invokes the callback when a new buffer becomes ready or an overrun condition occurs. @@ -115,7 +101,7 @@ public: static status_t getMinFrameCount(int* frameCount, uint32_t sampleRate, audio_format_t format, - int channelCount); + audio_channel_mask_t channelMask); /* Constructs an uninitialized AudioRecord. No connection with * AudioFlinger takes place. @@ -133,32 +119,22 @@ public: * sampleRate: Track sampling rate in Hz. * format: Audio format (e.g AUDIO_FORMAT_PCM_16_BIT for signed * 16 bits per sample). - * channelMask: Channel mask: see audio_channels_t. + * channelMask: Channel mask. * frameCount: Total size of track PCM buffer in frames. This defines the * latency of the track. - * flags: A bitmask of acoustic values from enum record_flags. It enables - * AGC, NS, and IIR. * cbf: Callback function. If not null, this function is called periodically * to provide new PCM data. + * user: Context for use by the callback receiver. * notificationFrames: The callback function is called each time notificationFrames PCM * frames are ready in record track output buffer. - * user Context for use by the callback receiver. + * sessionId: Not yet supported. */ - // FIXME consider removing this alias and replacing it by audio_in_acoustics_t - // or removing the parameter entirely if it is unused - enum record_flags { - RECORD_AGC_ENABLE = AUDIO_IN_ACOUSTICS_AGC_ENABLE, - RECORD_NS_ENABLE = AUDIO_IN_ACOUSTICS_NS_ENABLE, - RECORD_IIR_ENABLE = AUDIO_IN_ACOUSTICS_TX_IIR_ENABLE, - }; - AudioRecord(audio_source_t inputSource, uint32_t sampleRate = 0, audio_format_t format = AUDIO_FORMAT_DEFAULT, - uint32_t channelMask = AUDIO_CHANNEL_IN_MONO, + audio_channel_mask_t channelMask = AUDIO_CHANNEL_IN_MONO, int frameCount = 0, - record_flags flags = (record_flags) 0, callback_t cbf = NULL, void* user = NULL, int notificationFrames = 0, @@ -166,7 +142,7 @@ public: /* Terminates the AudioRecord and unregisters it from AudioFlinger. - * Also destroys all resources assotiated with the AudioRecord. + * Also destroys all resources associated with the AudioRecord. */ ~AudioRecord(); @@ -182,9 +158,8 @@ public: status_t set(audio_source_t inputSource = AUDIO_SOURCE_DEFAULT, uint32_t sampleRate = 0, audio_format_t format = AUDIO_FORMAT_DEFAULT, - uint32_t channelMask = AUDIO_CHANNEL_IN_MONO, + audio_channel_mask_t channelMask = AUDIO_CHANNEL_IN_MONO, int frameCount = 0, - record_flags flags = (record_flags) 0, callback_t cbf = NULL, void* user = NULL, int notificationFrames = 0, @@ -205,11 +180,10 @@ public: */ uint32_t latency() const; - /* getters, see constructor */ + /* getters, see constructor and set() */ audio_format_t format() const; int channelCount() const; - int channels() const; uint32_t frameCount() const; size_t frameSize() const; audio_source_t inputSource() const; @@ -271,7 +245,7 @@ public: status_t getPositionUpdatePeriod(uint32_t *updatePeriod) const; - /* Gets record head position. The position is the total number of frames + /* Gets record head position. The position is the total number of frames * recorded since record start. * * Parameters: @@ -294,7 +268,7 @@ public: */ audio_io_handle_t getInput() const; - /* returns the audio session ID associated to this AudioRecord. + /* returns the audio session ID associated with this AudioRecord. * * Parameters: * none. @@ -342,57 +316,72 @@ private: AudioRecord& operator = (const AudioRecord& other); /* a small internal class to handle the callback */ - class ClientRecordThread : public Thread + class AudioRecordThread : public Thread { public: - ClientRecordThread(AudioRecord& receiver, bool bCanCallJava = false); + AudioRecordThread(AudioRecord& receiver, bool bCanCallJava = false); + + // Do not call Thread::requestExitAndWait() without first calling requestExit(). + // Thread::requestExitAndWait() is not virtual, and the implementation doesn't do enough. + virtual void requestExit(); + + void pause(); // suspend thread from execution at next loop boundary + void resume(); // allow thread to execute, if not requested to exit + private: friend class AudioRecord; virtual bool threadLoop(); - virtual status_t readyToRun(); - virtual void onFirstRef() {} AudioRecord& mReceiver; + virtual ~AudioRecordThread(); + Mutex mMyLock; // Thread::mLock is private + Condition mMyCond; // Thread::mThreadExitedCondition is private + bool mPaused; // whether thread is currently paused }; - bool processAudioBuffer(const sp<ClientRecordThread>& thread); + // body of AudioRecordThread::threadLoop() + bool processAudioBuffer(const sp<AudioRecordThread>& thread); + status_t openRecord_l(uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, audio_io_handle_t input); audio_io_handle_t getInput_l(); status_t restoreRecord_l(audio_track_cblk_t*& cblk); - sp<IAudioRecord> mAudioRecord; - sp<IMemory> mCblkMemory; - sp<ClientRecordThread> mClientRecordThread; - status_t mReadyToRun; + sp<AudioRecordThread> mAudioRecordThread; mutable Mutex mLock; - Condition mCondition; - uint32_t mFrameCount; + bool mActive; // protected by mLock - audio_track_cblk_t* mCblk; + // for client callback handler + callback_t mCbf; + void* mUserData; + + // for notification APIs + uint32_t mNotificationFrames; + uint32_t mRemainingFrames; + uint32_t mMarkerPosition; // in frames + bool mMarkerReached; + uint32_t mNewPosition; // in frames + uint32_t mUpdatePeriod; // in ms + + // constant after constructor or set() + uint32_t mFrameCount; audio_format_t mFormat; uint8_t mChannelCount; audio_source_t mInputSource; status_t mStatus; uint32_t mLatency; + audio_channel_mask_t mChannelMask; + audio_io_handle_t mInput; // returned by AudioSystem::getInput() + int mSessionId; - volatile int32_t mActive; + // may be changed if IAudioRecord object is re-created + sp<IAudioRecord> mAudioRecord; + sp<IMemory> mCblkMemory; + audio_track_cblk_t* mCblk; - callback_t mCbf; - void* mUserData; - uint32_t mNotificationFrames; - uint32_t mRemainingFrames; - uint32_t mMarkerPosition; - bool mMarkerReached; - uint32_t mNewPosition; - uint32_t mUpdatePeriod; - record_flags mFlags; - uint32_t mChannelMask; - audio_io_handle_t mInput; - int mSessionId; int mPreviousPriority; // before start() SchedPolicy mPreviousSchedulingGroup; }; diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h index e2662f2..61d62b0 100644 --- a/include/media/AudioSystem.h +++ b/include/media/AudioSystem.h @@ -110,8 +110,8 @@ public: static bool routedToA2dpOutput(audio_stream_type_t streamType); - static status_t getInputBufferSize(uint32_t sampleRate, audio_format_t format, int channelCount, - size_t* buffSize); + static status_t getInputBufferSize(uint32_t sampleRate, audio_format_t format, + audio_channel_mask_t channelMask, size_t* buffSize); static status_t setVoiceVolume(float volume); @@ -126,6 +126,7 @@ public: // necessary to check returned status before using the returned values. static status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, audio_stream_type_t stream = AUDIO_STREAM_DEFAULT); + // return the number of input frames lost by HAL implementation, or 0 if the handle is invalid static unsigned int getInputFramesLost(audio_io_handle_t ioHandle); static int newAudioSessionId(); @@ -188,7 +189,7 @@ public: static audio_io_handle_t getOutput(audio_stream_type_t stream, uint32_t samplingRate = 0, audio_format_t format = AUDIO_FORMAT_DEFAULT, - uint32_t channels = AUDIO_CHANNEL_OUT_STEREO, + audio_channel_mask_t channelMask = AUDIO_CHANNEL_OUT_STEREO, audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE); static status_t startOutput(audio_io_handle_t output, audio_stream_type_t stream, @@ -200,8 +201,7 @@ public: static audio_io_handle_t getInput(audio_source_t inputSource, uint32_t samplingRate = 0, audio_format_t format = AUDIO_FORMAT_DEFAULT, - uint32_t channels = AUDIO_CHANNEL_IN_MONO, - audio_in_acoustics_t acoustics = (audio_in_acoustics_t)0, + audio_channel_mask_t channelMask = AUDIO_CHANNEL_IN_MONO, int sessionId = 0); static status_t startInput(audio_io_handle_t input); static status_t stopInput(audio_io_handle_t input); @@ -277,7 +277,7 @@ private: // previous parameters for recording buffer size queries static uint32_t gPrevInSamplingRate; static audio_format_t gPrevInFormat; - static int gPrevInChannelCount; + static audio_channel_mask_t gPrevInChannelMask; static sp<IAudioPolicyService> gAudioPolicyService; diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h index 639d6d2..325f780 100644 --- a/include/media/AudioTrack.h +++ b/include/media/AudioTrack.h @@ -472,8 +472,6 @@ protected: private: friend class AudioTrack; virtual bool threadLoop(); - virtual status_t readyToRun(); - virtual void onFirstRef(); AudioTrack& mReceiver; ~AudioTrackThread(); Mutex mMyLock; // Thread::mLock is private diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h index 86e228b..bdd0142 100644 --- a/include/media/IAudioFlinger.h +++ b/include/media/IAudioFlinger.h @@ -48,7 +48,7 @@ public: enum { TRACK_DEFAULT = 0, // client requests a default AudioTrack TRACK_TIMED = 1, // client requests a TimedAudioTrack - TRACK_FAST = 2, // client requests a fast AudioTrack + TRACK_FAST = 2, // client requests a fast AudioTrack or AudioRecord }; typedef uint32_t track_flags_t; @@ -60,7 +60,7 @@ public: audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, track_flags_t flags, const sp<IMemory>& sharedBuffer, @@ -74,9 +74,10 @@ public: audio_io_handle_t input, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, track_flags_t flags, + pid_t tid, // -1 means unused, otherwise must be valid non-0 int *sessionId, status_t *status) = 0; @@ -84,7 +85,9 @@ public: * and therefore can be cached. */ virtual uint32_t sampleRate(audio_io_handle_t output) const = 0; +#if 0 virtual int channelCount(audio_io_handle_t output) const = 0; +#endif virtual audio_format_t format(audio_io_handle_t output) const = 0; virtual size_t frameCount(audio_io_handle_t output) const = 0; @@ -126,7 +129,8 @@ public: virtual void registerClient(const sp<IAudioFlingerClient>& client) = 0; // retrieve the audio recording buffer size - virtual size_t getInputBufferSize(uint32_t sampleRate, audio_format_t format, int channelCount) const = 0; + virtual size_t getInputBufferSize(uint32_t sampleRate, audio_format_t format, + audio_channel_mask_t channelMask) const = 0; virtual audio_io_handle_t openOutput(audio_module_handle_t module, audio_devices_t *pDevices, diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h index e160d70..bdc12db 100644 --- a/include/media/IAudioPolicyService.h +++ b/include/media/IAudioPolicyService.h @@ -51,7 +51,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) = 0; virtual status_t startOutput(audio_io_handle_t output, audio_stream_type_t stream, @@ -63,8 +63,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_channel_mask_t channelMask = 0, int audioSession = 0) = 0; virtual status_t startInput(audio_io_handle_t input) = 0; virtual status_t stopInput(audio_io_handle_t input) = 0; diff --git a/include/media/Visualizer.h b/include/media/Visualizer.h index fdec5ee..aa58905 100644 --- a/include/media/Visualizer.h +++ b/include/media/Visualizer.h @@ -142,8 +142,6 @@ private: private: friend class Visualizer; virtual bool threadLoop(); - virtual status_t readyToRun(); - virtual void onFirstRef(); Visualizer& mReceiver; Mutex mLock; uint32_t mSleepTimeUs; diff --git a/libvideoeditor/lvpp/Android.mk b/libvideoeditor/lvpp/Android.mk index c018d74..0ed7e6c 100755 --- a/libvideoeditor/lvpp/Android.mk +++ b/libvideoeditor/lvpp/Android.mk @@ -59,6 +59,7 @@ LOCAL_SHARED_LIBRARIES := \ libstagefright \ libstagefright_foundation \ libstagefright_omx \ + libsync \ libui \ libutils \ libvideoeditor_osal \ diff --git a/libvideoeditor/lvpp/NativeWindowRenderer.cpp b/libvideoeditor/lvpp/NativeWindowRenderer.cpp index b2c2675..2e15ff9 100755 --- a/libvideoeditor/lvpp/NativeWindowRenderer.cpp +++ b/libvideoeditor/lvpp/NativeWindowRenderer.cpp @@ -22,9 +22,9 @@ #include <cutils/log.h> #include <gui/SurfaceTexture.h> #include <gui/SurfaceTextureClient.h> -#include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/MediaBuffer.h> #include <media/stagefright/MetaData.h> +#include <media/stagefright/foundation/ADebug.h> #include "VideoEditorTools.h" #define CHECK_EGL_ERROR CHECK(EGL_SUCCESS == eglGetError()) @@ -382,7 +382,7 @@ void NativeWindowRenderer::queueInternalBuffer(ANativeWindow *anw, int64_t timeUs; CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs)); native_window_set_buffers_timestamp(anw, timeUs * 1000); - status_t err = anw->queueBuffer(anw, buffer->graphicBuffer().get()); + status_t err = anw->queueBuffer(anw, buffer->graphicBuffer().get(), -1); if (err != 0) { ALOGE("queueBuffer failed with error %s (%d)", strerror(-err), -err); return; @@ -399,18 +399,16 @@ void NativeWindowRenderer::queueExternalBuffer(ANativeWindow* anw, native_window_set_usage(anw, GRALLOC_USAGE_SW_WRITE_OFTEN); ANativeWindowBuffer* anb; - anw->dequeueBuffer(anw, &anb); + CHECK(NO_ERROR == native_window_dequeue_buffer_and_wait(anw, &anb)); CHECK(anb != NULL); - sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); - CHECK(NO_ERROR == anw->lockBuffer(anw, buf->getNativeBuffer())); - // Copy the buffer uint8_t* img = NULL; + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); copyI420Buffer(buffer, img, width, height, buf->getStride()); buf->unlock(); - CHECK(NO_ERROR == anw->queueBuffer(anw, buf->getNativeBuffer())); + CHECK(NO_ERROR == anw->queueBuffer(anw, buf->getNativeBuffer(), -1)); } void NativeWindowRenderer::copyI420Buffer(MediaBuffer* src, uint8_t* dst, diff --git a/libvideoeditor/lvpp/PreviewRenderer.cpp b/libvideoeditor/lvpp/PreviewRenderer.cpp index 4aa4eb3..b1cfc8e 100755 --- a/libvideoeditor/lvpp/PreviewRenderer.cpp +++ b/libvideoeditor/lvpp/PreviewRenderer.cpp @@ -97,13 +97,12 @@ PreviewRenderer::~PreviewRenderer() { void PreviewRenderer::getBufferYV12(uint8_t **data, size_t *stride) { int err = OK; - if ((err = mSurface->ANativeWindow::dequeueBuffer(mSurface.get(), &mBuf)) != 0) { - ALOGW("Surface::dequeueBuffer returned error %d", err); + if ((err = native_window_dequeue_buffer_and_wait(mSurface.get(), + &mBuf)) != 0) { + ALOGW("native_window_dequeue_buffer_and_wait returned error %d", err); return; } - CHECK_EQ(0, mSurface->ANativeWindow::lockBuffer(mSurface.get(), mBuf)); - GraphicBufferMapper &mapper = GraphicBufferMapper::get(); Rect bounds(mWidth, mHeight); @@ -131,7 +130,7 @@ void PreviewRenderer::renderYV12() { if (mBuf!= NULL) { CHECK_EQ(0, mapper.unlock(mBuf->handle)); - if ((err = mSurface->ANativeWindow::queueBuffer(mSurface.get(), mBuf)) != 0) { + if ((err = mSurface->ANativeWindow::queueBuffer(mSurface.get(), mBuf, -1)) != 0) { ALOGW("Surface::queueBuffer returned error %d", err); } } diff --git a/libvideoeditor/vss/stagefrightshells/src/VideoEditor3gpReader.cpp b/libvideoeditor/vss/stagefrightshells/src/VideoEditor3gpReader.cpp index 5026073..f735c0b 100755 --- a/libvideoeditor/vss/stagefrightshells/src/VideoEditor3gpReader.cpp +++ b/libvideoeditor/vss/stagefrightshells/src/VideoEditor3gpReader.cpp @@ -1483,11 +1483,15 @@ M4OSA_ERR VideoEditor3gpReader_getNextStreamHandler(M4OSA_Context context, (int32_t*)&(pVideoStreamHandler->m_videoHeight)); (*pStreamHandler) = (M4_StreamHandler*)(pVideoStreamHandler); - meta->findInt64(kKeyDuration, - (int64_t*)&(Duration)); - ((*pStreamHandler)->m_duration) = - (int32_t)((Duration)/1000); // conversion to mS + meta->findInt64(kKeyDuration, (int64_t*)&(Duration)); + ((*pStreamHandler)->m_duration) = (int32_t)((Duration)/1000); // conversion to mS pC->mMaxDuration = ((*pStreamHandler)->m_duration); + if (pC->mMaxDuration == 0) { + ALOGE("Video is too short: %lld Us", Duration); + delete pVideoStreamHandler; + pVideoStreamHandler = NULL; + return M4ERR_PARAMETER; + } ALOGV("VideoEditor3gpReader_getNextStreamHandler m_duration %d", (*pStreamHandler)->m_duration); diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index 0562f8e..807f812 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -18,28 +18,18 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "AudioRecord" -#include <stdint.h> -#include <sys/types.h> - -#include <sched.h> #include <sys/resource.h> +#include <sys/types.h> -#include <private/media/AudioTrackShared.h> - -#include <media/AudioSystem.h> +#include <binder/IPCThreadState.h> +#include <cutils/atomic.h> +#include <cutils/compiler.h> #include <media/AudioRecord.h> -#include <media/mediarecorder.h> - -#include <binder/IServiceManager.h> +#include <media/AudioSystem.h> +#include <system/audio.h> #include <utils/Log.h> -#include <binder/Parcel.h> -#include <binder/IPCThreadState.h> -#include <utils/Timers.h> -#include <utils/Atomic.h> -#include <system/audio.h> -#include <cutils/bitops.h> -#include <cutils/compiler.h> +#include <private/media/AudioTrackShared.h> namespace android { // --------------------------------------------------------------------------- @@ -49,18 +39,23 @@ status_t AudioRecord::getMinFrameCount( int* frameCount, uint32_t sampleRate, audio_format_t format, - int channelCount) + audio_channel_mask_t channelMask) { + if (frameCount == NULL) return BAD_VALUE; + + // default to 0 in case of error + *frameCount = 0; + size_t size = 0; - if (AudioSystem::getInputBufferSize(sampleRate, format, channelCount, &size) + if (AudioSystem::getInputBufferSize(sampleRate, format, channelMask, &size) != NO_ERROR) { ALOGE("AudioSystem could not query the input buffer size."); return NO_INIT; } if (size == 0) { - ALOGE("Unsupported configuration: sampleRate %d, format %d, channelCount %d", - sampleRate, format, channelCount); + ALOGE("Unsupported configuration: sampleRate %d, format %d, channelMask %#x", + sampleRate, format, channelMask); return BAD_VALUE; } @@ -68,6 +63,7 @@ status_t AudioRecord::getMinFrameCount( size <<= 1; if (audio_is_linear_pcm(format)) { + int channelCount = popcount(channelMask); size /= channelCount * audio_bytes_per_sample(format); } @@ -87,9 +83,8 @@ AudioRecord::AudioRecord( audio_source_t inputSource, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, - record_flags flags, callback_t cbf, void* user, int notificationFrames, @@ -98,7 +93,7 @@ AudioRecord::AudioRecord( mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT) { mStatus = set(inputSource, sampleRate, format, channelMask, - frameCount, flags, cbf, user, notificationFrames, sessionId); + frameCount, cbf, user, notificationFrames, sessionId); } AudioRecord::~AudioRecord() @@ -108,9 +103,10 @@ AudioRecord::~AudioRecord() // it is looping on buffer empty condition in obtainBuffer(). // Otherwise the callback thread will never exit. stop(); - if (mClientRecordThread != 0) { - mClientRecordThread->requestExitAndWait(); - mClientRecordThread.clear(); + if (mAudioRecordThread != 0) { + mAudioRecordThread->requestExit(); // see comment in AudioRecord.h + mAudioRecordThread->requestExitAndWait(); + mAudioRecordThread.clear(); } mAudioRecord.clear(); IPCThreadState::self()->flushCommands(); @@ -122,9 +118,8 @@ status_t AudioRecord::set( audio_source_t inputSource, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, - record_flags flags, callback_t cbf, void* user, int notificationFrames, @@ -132,7 +127,7 @@ status_t AudioRecord::set( int sessionId) { - ALOGV("set(): sampleRate %d, channelMask %d, frameCount %d",sampleRate, channelMask, frameCount); + ALOGV("set(): sampleRate %d, channelMask %#x, frameCount %d",sampleRate, channelMask, frameCount); AutoMutex lock(mLock); @@ -174,7 +169,6 @@ status_t AudioRecord::set( sampleRate, format, channelMask, - (audio_in_acoustics_t)flags, mSessionId); if (input == 0) { ALOGE("Could not get audio input for record source %d", inputSource); @@ -207,7 +201,8 @@ status_t AudioRecord::set( } if (cbf != NULL) { - mClientRecordThread = new ClientRecordThread(*this, threadCanCallJava); + mAudioRecordThread = new AudioRecordThread(*this, threadCanCallJava); + mAudioRecordThread->run("AudioRecord", ANDROID_PRIORITY_AUDIO); } mStatus = NO_ERROR; @@ -217,7 +212,7 @@ status_t AudioRecord::set( mFrameCount = mCblk->frameCount; mChannelCount = (uint8_t)channelCount; mChannelMask = channelMask; - mActive = 0; + mActive = false; mCbf = cbf; mNotificationFrames = notificationFrames; mRemainingFrames = notificationFrames; @@ -229,7 +224,6 @@ status_t AudioRecord::set( mNewPosition = 0; mUpdatePeriod = 0; mInputSource = inputSource; - mFlags = flags; mInput = input; AudioSystem::acquireAudioSessionId(mSessionId); @@ -282,41 +276,19 @@ audio_source_t AudioRecord::inputSource() const status_t AudioRecord::start(AudioSystem::sync_event_t event, int triggerSession) { status_t ret = NO_ERROR; - sp<ClientRecordThread> t = mClientRecordThread; + sp<AudioRecordThread> t = mAudioRecordThread; ALOGV("start, sync event %d trigger session %d", event, triggerSession); - if (t != 0) { - if (t->exitPending()) { - if (t->requestExitAndWait() == WOULD_BLOCK) { - ALOGE("AudioRecord::start called from thread"); - return WOULD_BLOCK; - } - } - } - AutoMutex lock(mLock); // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed // while we are accessing the cblk sp<IAudioRecord> audioRecord = mAudioRecord; sp<IMemory> iMem = mCblkMemory; audio_track_cblk_t* cblk = mCblk; - if (mActive == 0) { - mActive = 1; - pid_t tid; - if (t != 0) { - mReadyToRun = WOULD_BLOCK; - t->run("AudioRecord", ANDROID_PRIORITY_AUDIO); - tid = t->getTid(); // pid_t is unknown until run() - ALOGV("getTid=%d", tid); - if (tid == -1) { - tid = 0; - } - // thread blocks in readyToRun() - } else { - tid = 0; // not gettid() - } + if (!mActive) { + mActive = true; cblk->lock.lock(); if (!(cblk->flags & CBLK_INVALID_MSK)) { @@ -338,19 +310,14 @@ status_t AudioRecord::start(AudioSystem::sync_event_t event, int triggerSession) AudioSystem::kSyncRecordStartTimeOutMs; cblk->waitTimeMs = 0; if (t != 0) { - // thread unblocks in readyToRun() and returns NO_ERROR - mReadyToRun = NO_ERROR; - mCondition.signal(); + t->resume(); } else { mPreviousPriority = getpriority(PRIO_PROCESS, 0); get_sched_policy(0, &mPreviousSchedulingGroup); androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO); } } else { - mActive = 0; - // thread unblocks in readyToRun() and returns NO_INIT - mReadyToRun = NO_INIT; - mCondition.signal(); + mActive = false; } } @@ -359,20 +326,20 @@ status_t AudioRecord::start(AudioSystem::sync_event_t event, int triggerSession) status_t AudioRecord::stop() { - sp<ClientRecordThread> t = mClientRecordThread; + sp<AudioRecordThread> t = mAudioRecordThread; ALOGV("stop"); AutoMutex lock(mLock); - if (mActive == 1) { - mActive = 0; + if (mActive) { + mActive = false; mCblk->cv.signal(); mAudioRecord->stop(); // the record head position will reset to 0, so if a marker is set, we need // to activate it again mMarkerReached = false; if (t != 0) { - t->requestExit(); + t->pause(); } else { setpriority(PRIO_PROCESS, 0, mPreviousPriority); set_sched_policy(0, mPreviousSchedulingGroup); @@ -384,6 +351,7 @@ status_t AudioRecord::stop() bool AudioRecord::stopped() const { + AutoMutex lock(mLock); return !mActive; } @@ -445,10 +413,8 @@ status_t AudioRecord::getPosition(uint32_t *position) const unsigned int AudioRecord::getInputFramesLost() const { - if (mActive) - return AudioSystem::getInputFramesLost(mInput); - else - return 0; + // no need to check mActive, because if inactive this will return 0, which is what we want + return AudioSystem::getInputFramesLost(mInput); } // ------------------------------------------------------------------------- @@ -457,7 +423,7 @@ unsigned int AudioRecord::getInputFramesLost() const status_t AudioRecord::openRecord_l( uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, audio_io_handle_t input) { @@ -467,11 +433,15 @@ status_t AudioRecord::openRecord_l( return NO_INIT; } + pid_t tid = -1; + // FIXME see similar logic at AudioTrack + sp<IAudioRecord> record = audioFlinger->openRecord(getpid(), input, sampleRate, format, channelMask, frameCount, IAudioFlinger::TRACK_DEFAULT, + tid, &mSessionId, &status); @@ -499,7 +469,7 @@ status_t AudioRecord::openRecord_l( status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) { AutoMutex lock(mLock); - int active; + bool active; status_t result = NO_ERROR; audio_track_cblk_t* cblk = mCblk; uint32_t framesReq = audioBuffer->frameCount; @@ -528,7 +498,7 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount) result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs)); cblk->lock.unlock(); mLock.lock(); - if (mActive == 0) { + if (!mActive) { return status_t(STOPPED); } cblk->lock.lock(); @@ -613,7 +583,6 @@ audio_io_handle_t AudioRecord::getInput_l() mCblk->sampleRate, mFormat, mChannelMask, - (audio_in_acoustics_t)mFlags, mSessionId); return mInput; } @@ -678,7 +647,7 @@ ssize_t AudioRecord::read(void* buffer, size_t userSize) // ------------------------------------------------------------------------- -bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread) +bool AudioRecord::processAudioBuffer(const sp<AudioRecordThread>& thread) { Buffer audioBuffer; uint32_t frames = mRemainingFrames; @@ -690,6 +659,7 @@ bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread) sp<IAudioRecord> audioRecord = mAudioRecord; sp<IMemory> iMem = mCblkMemory; audio_track_cblk_t* cblk = mCblk; + bool active = mActive; mLock.unlock(); // Manage marker callback @@ -748,7 +718,9 @@ bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread) // Manage overrun callback - if (mActive && (cblk->framesAvailable() == 0)) { + if (active && (cblk->framesAvailable() == 0)) { + // The value of active is stale, but we are almost sure to be active here because + // otherwise we would have exited when obtainBuffer returned STOPPED earlier. ALOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags); if (!(android_atomic_or(CBLK_UNDERRUN_ON, &cblk->flags) & CBLK_UNDERRUN_MSK)) { mCbf(EVENT_OVERRUN, mUserData, 0); @@ -805,7 +777,7 @@ status_t AudioRecord::restoreRecord_l(audio_track_cblk_t*& cblk) result = NO_ERROR; cblk->lock.unlock(); } - if (result != NO_ERROR || mActive == 0) { + if (result != NO_ERROR || !mActive) { result = status_t(STOPPED); } } @@ -825,23 +797,51 @@ status_t AudioRecord::restoreRecord_l(audio_track_cblk_t*& cblk) // ========================================================================= -AudioRecord::ClientRecordThread::ClientRecordThread(AudioRecord& receiver, bool bCanCallJava) - : Thread(bCanCallJava), mReceiver(receiver) +AudioRecord::AudioRecordThread::AudioRecordThread(AudioRecord& receiver, bool bCanCallJava) + : Thread(bCanCallJava), mReceiver(receiver), mPaused(true) +{ +} + +AudioRecord::AudioRecordThread::~AudioRecordThread() +{ +} + +bool AudioRecord::AudioRecordThread::threadLoop() +{ + { + AutoMutex _l(mMyLock); + if (mPaused) { + mMyCond.wait(mMyLock); + // caller will check for exitPending() + return true; + } + } + if (!mReceiver.processAudioBuffer(this)) { + pause(); + } + return true; +} + +void AudioRecord::AudioRecordThread::requestExit() { + // must be in this order to avoid a race condition + Thread::requestExit(); + resume(); } -bool AudioRecord::ClientRecordThread::threadLoop() +void AudioRecord::AudioRecordThread::pause() { - return mReceiver.processAudioBuffer(this); + AutoMutex _l(mMyLock); + mPaused = true; } -status_t AudioRecord::ClientRecordThread::readyToRun() +void AudioRecord::AudioRecordThread::resume() { - AutoMutex(mReceiver.mLock); - while (mReceiver.mReadyToRun == WOULD_BLOCK) { - mReceiver.mCondition.wait(mReceiver.mLock); + AutoMutex _l(mMyLock); + if (mPaused) { + mPaused = false; + mMyCond.signal(); } - return mReceiver.mReadyToRun; } // ------------------------------------------------------------------------- diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp index 4c41ba5..d880989 100644 --- a/media/libmedia/AudioSystem.cpp +++ b/media/libmedia/AudioSystem.cpp @@ -41,7 +41,7 @@ DefaultKeyedVector<audio_io_handle_t, AudioSystem::OutputDescriptor *> AudioSyst // Cached values for recording queries, all protected by gLock uint32_t AudioSystem::gPrevInSamplingRate = 16000; audio_format_t AudioSystem::gPrevInFormat = AUDIO_FORMAT_PCM_16_BIT; -int AudioSystem::gPrevInChannelCount = 1; +audio_channel_mask_t AudioSystem::gPrevInChannelMask = AUDIO_CHANNEL_IN_MONO; size_t AudioSystem::gInBuffSize = 0; @@ -334,25 +334,25 @@ status_t AudioSystem::getLatency(audio_io_handle_t output, return NO_ERROR; } -status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, audio_format_t format, int channelCount, - size_t* buffSize) +status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, audio_format_t format, + audio_channel_mask_t channelMask, size_t* buffSize) { gLock.lock(); // Do we have a stale gInBufferSize or are we requesting the input buffer size for new values size_t inBuffSize = gInBuffSize; if ((inBuffSize == 0) || (sampleRate != gPrevInSamplingRate) || (format != gPrevInFormat) - || (channelCount != gPrevInChannelCount)) { + || (channelMask != gPrevInChannelMask)) { gLock.unlock(); const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger(); if (af == 0) { return PERMISSION_DENIED; } - inBuffSize = af->getInputBufferSize(sampleRate, format, channelCount); + inBuffSize = af->getInputBufferSize(sampleRate, format, channelMask); gLock.lock(); // save the request params gPrevInSamplingRate = sampleRate; gPrevInFormat = format; - gPrevInChannelCount = channelCount; + gPrevInChannelMask = channelMask; gInBuffSize = inBuffSize; } @@ -449,7 +449,7 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle OutputDescriptor *outputDesc = new OutputDescriptor(*desc); gOutputs.add(ioHandle, outputDesc); - ALOGV("ioConfigChanged() new output samplingRate %d, format %d channels %d frameCount %d latency %d", + ALOGV("ioConfigChanged() new output samplingRate %d, format %d channels %#x frameCount %d latency %d", outputDesc->samplingRate, outputDesc->format, outputDesc->channels, outputDesc->frameCount, outputDesc->latency); } break; case OUTPUT_CLOSED: { @@ -471,7 +471,7 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle if (param2 == NULL) break; desc = (const OutputDescriptor *)param2; - ALOGV("ioConfigChanged() new config for output %d samplingRate %d, format %d channels %d frameCount %d latency %d", + ALOGV("ioConfigChanged() new config for output %d samplingRate %d, format %d channels %#x frameCount %d latency %d", ioHandle, desc->samplingRate, desc->format, desc->channels, desc->frameCount, desc->latency); OutputDescriptor *outputDesc = gOutputs.valueAt(index); @@ -588,12 +588,12 @@ audio_policy_forced_cfg_t AudioSystem::getForceUse(audio_policy_force_use_t usag audio_io_handle_t AudioSystem::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) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return 0; - return aps->getOutput(stream, samplingRate, format, channels, flags); + return aps->getOutput(stream, samplingRate, format, channelMask, flags); } status_t AudioSystem::startOutput(audio_io_handle_t output, @@ -624,13 +624,12 @@ void AudioSystem::releaseOutput(audio_io_handle_t output) audio_io_handle_t AudioSystem::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 sessionId) { const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service(); if (aps == 0) return 0; - return aps->getInput(inputSource, samplingRate, format, channels, acoustics, sessionId); + return aps->getInput(inputSource, samplingRate, format, channelMask, sessionId); } status_t AudioSystem::startInput(audio_io_handle_t input) diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index e5a60f5..177359b 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -54,6 +54,11 @@ status_t AudioTrack::getMinFrameCount( audio_stream_type_t streamType, uint32_t sampleRate) { + if (frameCount == NULL) return BAD_VALUE; + + // default to 0 in case of error + *frameCount = 0; + // FIXME merge with similar code in createTrack_l(), except we're missing // some information here that is available in createTrack_l(): // audio_io_handle_t output @@ -367,7 +372,6 @@ sp<IMemory>& AudioTrack::sharedBuffer() void AudioTrack::start() { sp<AudioTrackThread> t = mAudioTrackThread; - status_t status = NO_ERROR; ALOGV("start %p", this); @@ -395,6 +399,7 @@ void AudioTrack::start() } ALOGV("start %p before lock cblk %p", this, mCblk); + status_t status = NO_ERROR; if (!(cblk->flags & CBLK_INVALID_MSK)) { cblk->lock.unlock(); ALOGV("mAudioTrack->start()"); @@ -1472,15 +1477,6 @@ bool AudioTrack::AudioTrackThread::threadLoop() return true; } -status_t AudioTrack::AudioTrackThread::readyToRun() -{ - return NO_ERROR; -} - -void AudioTrack::AudioTrackThread::onFirstRef() -{ -} - void AudioTrack::AudioTrackThread::requestExit() { // must be in this order to avoid a race condition diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index e8dd438..f5d6fd6 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -32,7 +32,7 @@ enum { CREATE_TRACK = IBinder::FIRST_CALL_TRANSACTION, OPEN_RECORD, SAMPLE_RATE, - CHANNEL_COUNT, + CHANNEL_COUNT, // obsolete FORMAT, FRAME_COUNT, LATENCY, @@ -86,7 +86,7 @@ public: audio_stream_type_t streamType, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount, track_flags_t flags, const sp<IMemory>& sharedBuffer, @@ -138,6 +138,7 @@ public: uint32_t channelMask, int frameCount, track_flags_t flags, + pid_t tid, int *sessionId, status_t *status) { @@ -151,6 +152,7 @@ public: data.writeInt32(channelMask); data.writeInt32(frameCount); data.writeInt32(flags); + data.writeInt32((int32_t) tid); int lSessionId = 0; if (sessionId != NULL) { lSessionId = *sessionId; @@ -182,6 +184,7 @@ public: return reply.readInt32(); } +#if 0 virtual int channelCount(audio_io_handle_t output) const { Parcel data, reply; @@ -190,6 +193,7 @@ public: remote()->transact(CHANNEL_COUNT, data, &reply); return reply.readInt32(); } +#endif virtual audio_format_t format(audio_io_handle_t output) const { @@ -347,13 +351,14 @@ public: remote()->transact(REGISTER_CLIENT, data, &reply); } - 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 { Parcel data, reply; data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor()); data.writeInt32(sampleRate); data.writeInt32(format); - data.writeInt32(channelCount); + data.writeInt32(channelMask); remote()->transact(GET_INPUTBUFFERSIZE, data, &reply); return reply.readInt32(); } @@ -698,7 +703,7 @@ status_t BnAudioFlinger::onTransact( int streamType = data.readInt32(); uint32_t sampleRate = data.readInt32(); audio_format_t format = (audio_format_t) data.readInt32(); - int channelCount = data.readInt32(); + audio_channel_mask_t channelMask = data.readInt32(); size_t bufferCount = data.readInt32(); track_flags_t flags = (track_flags_t) data.readInt32(); sp<IMemory> buffer = interface_cast<IMemory>(data.readStrongBinder()); @@ -708,7 +713,7 @@ status_t BnAudioFlinger::onTransact( status_t status; sp<IAudioTrack> track = createTrack(pid, (audio_stream_type_t) streamType, sampleRate, format, - channelCount, bufferCount, flags, buffer, output, tid, &sessionId, &status); + channelMask, bufferCount, flags, buffer, output, tid, &sessionId, &status); reply->writeInt32(sessionId); reply->writeInt32(status); reply->writeStrongBinder(track->asBinder()); @@ -720,13 +725,14 @@ status_t BnAudioFlinger::onTransact( audio_io_handle_t input = (audio_io_handle_t) data.readInt32(); uint32_t sampleRate = data.readInt32(); audio_format_t format = (audio_format_t) data.readInt32(); - int channelCount = data.readInt32(); + audio_channel_mask_t channelMask = data.readInt32(); size_t bufferCount = data.readInt32(); track_flags_t flags = (track_flags_t) data.readInt32(); + pid_t tid = (pid_t) data.readInt32(); int sessionId = data.readInt32(); status_t status; sp<IAudioRecord> record = openRecord(pid, input, - sampleRate, format, channelCount, bufferCount, flags, &sessionId, &status); + sampleRate, format, channelMask, bufferCount, flags, tid, &sessionId, &status); reply->writeInt32(sessionId); reply->writeInt32(status); reply->writeStrongBinder(record->asBinder()); @@ -737,11 +743,13 @@ status_t BnAudioFlinger::onTransact( reply->writeInt32( sampleRate((audio_io_handle_t) data.readInt32()) ); return NO_ERROR; } break; +#if 0 case CHANNEL_COUNT: { CHECK_INTERFACE(IAudioFlinger, data, reply); reply->writeInt32( channelCount((audio_io_handle_t) data.readInt32()) ); return NO_ERROR; } break; +#endif case FORMAT: { CHECK_INTERFACE(IAudioFlinger, data, reply); reply->writeInt32( format((audio_io_handle_t) data.readInt32()) ); @@ -846,8 +854,8 @@ status_t BnAudioFlinger::onTransact( CHECK_INTERFACE(IAudioFlinger, data, reply); uint32_t sampleRate = data.readInt32(); audio_format_t format = (audio_format_t) data.readInt32(); - int channelCount = data.readInt32(); - reply->writeInt32( getInputBufferSize(sampleRate, format, channelCount) ); + audio_channel_mask_t channelMask = data.readInt32(); + reply->writeInt32( getInputBufferSize(sampleRate, format, channelMask) ); return NO_ERROR; } break; case OPEN_OUTPUT: { diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp index 7aab8d6..f0c0f2e 100644 --- a/media/libmedia/IAudioPolicyService.cpp +++ b/media/libmedia/IAudioPolicyService.cpp @@ -123,7 +123,7 @@ public: 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) { Parcel data, reply; @@ -131,7 +131,7 @@ public: data.writeInt32(static_cast <uint32_t>(stream)); data.writeInt32(samplingRate); data.writeInt32(static_cast <uint32_t>(format)); - data.writeInt32(channels); + data.writeInt32(channelMask); data.writeInt32(static_cast <uint32_t>(flags)); remote()->transact(GET_OUTPUT, data, &reply); return static_cast <audio_io_handle_t> (reply.readInt32()); @@ -175,8 +175,7 @@ public: 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) { Parcel data, reply; @@ -184,8 +183,7 @@ public: data.writeInt32((int32_t) inputSource); data.writeInt32(samplingRate); data.writeInt32(static_cast <uint32_t>(format)); - data.writeInt32(channels); - data.writeInt32(static_cast <uint32_t>(acoustics)); + data.writeInt32(channelMask); data.writeInt32(audioSession); remote()->transact(GET_INPUT, data, &reply); return static_cast <audio_io_handle_t> (reply.readInt32()); @@ -417,14 +415,14 @@ status_t BnAudioPolicyService::onTransact( static_cast <audio_stream_type_t>(data.readInt32()); uint32_t samplingRate = data.readInt32(); audio_format_t format = (audio_format_t) data.readInt32(); - uint32_t channels = data.readInt32(); + audio_channel_mask_t channelMask = data.readInt32(); audio_output_flags_t flags = static_cast <audio_output_flags_t>(data.readInt32()); audio_io_handle_t output = getOutput(stream, samplingRate, format, - channels, + channelMask, flags); reply->writeInt32(static_cast <int>(output)); return NO_ERROR; @@ -464,15 +462,12 @@ status_t BnAudioPolicyService::onTransact( audio_source_t inputSource = (audio_source_t) data.readInt32(); uint32_t samplingRate = data.readInt32(); audio_format_t format = (audio_format_t) data.readInt32(); - uint32_t channels = data.readInt32(); - audio_in_acoustics_t acoustics = - static_cast <audio_in_acoustics_t>(data.readInt32()); + audio_channel_mask_t channelMask = data.readInt32(); int audioSession = data.readInt32(); audio_io_handle_t input = getInput(inputSource, samplingRate, format, - channels, - acoustics, + channelMask, audioSession); reply->writeInt32(static_cast <int>(input)); return NO_ERROR; diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp index de0bf7d..8196e10 100644 --- a/media/libmedia/Visualizer.cpp +++ b/media/libmedia/Visualizer.cpp @@ -353,13 +353,4 @@ bool Visualizer::CaptureThread::threadLoop() return false; } -status_t Visualizer::CaptureThread::readyToRun() -{ - return NO_ERROR; -} - -void Visualizer::CaptureThread::onFirstRef() -{ -} - }; // namespace android diff --git a/media/libstagefright/AACWriter.cpp b/media/libstagefright/AACWriter.cpp index 284ba01..33b7bd5 100644 --- a/media/libstagefright/AACWriter.cpp +++ b/media/libstagefright/AACWriter.cpp @@ -28,6 +28,8 @@ #include <media/stagefright/MetaData.h> #include <media/mediarecorder.h> #include <sys/prctl.h> +#include <sys/types.h> +#include <sys/stat.h> #include <fcntl.h> namespace android { @@ -44,7 +46,7 @@ AACWriter::AACWriter(const char *filename) ALOGV("AACWriter Constructor"); - mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR); + mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); if (mFd >= 0) { mInitCheck = OK; } diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index c4743a1..b4894e9 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -595,7 +595,7 @@ status_t ACodec::allocateOutputBuffersFromNativeWindow() { // Dequeue buffers and send them to OMX for (OMX_U32 i = 0; i < def.nBufferCountActual; i++) { ANativeWindowBuffer *buf; - err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf); + err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf); if (err != 0) { ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err); break; @@ -653,7 +653,7 @@ status_t ACodec::cancelBufferToNativeWindow(BufferInfo *info) { mComponentName.c_str(), info->mBufferID); int err = mNativeWindow->cancelBuffer( - mNativeWindow.get(), info->mGraphicBuffer.get()); + mNativeWindow.get(), info->mGraphicBuffer.get(), -1); CHECK_EQ(err, 0); @@ -664,7 +664,8 @@ status_t ACodec::cancelBufferToNativeWindow(BufferInfo *info) { ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() { ANativeWindowBuffer *buf; - if (mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf) != 0) { + int fenceFd = -1; + if (native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf) != 0) { ALOGE("dequeueBuffer failed."); return NULL; } @@ -2188,7 +2189,8 @@ status_t ACodec::pushBlankBuffersToNativeWindow() { // on the screen and then been replaced, so an previous video frames are // guaranteed NOT to be currently displayed. for (int i = 0; i < numBufs + 1; i++) { - err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &anb); + int fenceFd = -1; + err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &anb); if (err != NO_ERROR) { ALOGE("error pushing blank frames: dequeueBuffer failed: %s (%d)", strerror(-err), -err); @@ -2196,13 +2198,6 @@ status_t ACodec::pushBlankBuffersToNativeWindow() { } sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); - err = mNativeWindow->lockBuffer(mNativeWindow.get(), - buf->getNativeBuffer()); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: lockBuffer failed: %s (%d)", - strerror(-err), -err); - goto error; - } // Fill the buffer with the a 1x1 checkerboard pattern ;) uint32_t* img = NULL; @@ -2223,7 +2218,7 @@ status_t ACodec::pushBlankBuffersToNativeWindow() { } err = mNativeWindow->queueBuffer(mNativeWindow.get(), - buf->getNativeBuffer()); + buf->getNativeBuffer(), -1); if (err != NO_ERROR) { ALOGE("error pushing blank frames: queueBuffer failed: %s (%d)", strerror(-err), -err); @@ -2238,7 +2233,7 @@ error: if (err != NO_ERROR) { // Clean up after an error. if (anb != NULL) { - mNativeWindow->cancelBuffer(mNativeWindow.get(), anb); + mNativeWindow->cancelBuffer(mNativeWindow.get(), anb, -1); } native_window_api_disconnect(mNativeWindow.get(), @@ -2751,7 +2746,7 @@ void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) { status_t err; if ((err = mCodec->mNativeWindow->queueBuffer( mCodec->mNativeWindow.get(), - info->mGraphicBuffer.get())) == OK) { + info->mGraphicBuffer.get(), -1)) == OK) { info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW; } else { mCodec->signalError(OMX_ErrorUndefined, err); @@ -3253,11 +3248,6 @@ void ACodec::ExecutingState::submitOutputBuffers() { if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) { continue; } - - status_t err = mCodec->mNativeWindow->lockBuffer( - mCodec->mNativeWindow.get(), - info->mGraphicBuffer.get()); - CHECK_EQ(err, (status_t)OK); } else { CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US); } diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp index ca85640..15a7143 100644 --- a/media/libstagefright/AMRWriter.cpp +++ b/media/libstagefright/AMRWriter.cpp @@ -36,7 +36,7 @@ AMRWriter::AMRWriter(const char *filename) mPaused(false), mResumed(false) { - mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR); + mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); if (mFd >= 0) { mInitCheck = OK; } diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 8ad1cb9..e5b4d75 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -81,6 +81,7 @@ LOCAL_SHARED_LIBRARIES := \ libssl \ libstagefright_omx \ libstagefright_yuv \ + libsync \ libui \ libutils \ libvorbisidec \ @@ -97,12 +98,12 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_id3 \ libFLAC \ -ifneq ($(TARGET_BUILD_PDK), true) -LOCAL_STATIC_LIBRARIES += \ - libstagefright_chromium_http -LOCAL_SHARED_LIBRARIES += \ - libchromium_net +LOCAL_SRC_FILES += \ + chromium_http_stub.cpp LOCAL_CPPFLAGS += -DCHROMIUM_AVAILABLE=1 + +ifneq ($(TARGET_BUILD_PDK), true) +LOCAL_REQUIRED_MODULES := libstagefright_chromium_http endif LOCAL_SHARED_LIBRARIES += libstlport diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp index 72d797e..ed142a4 100644 --- a/media/libstagefright/AudioSource.cpp +++ b/media/libstagefright/AudioSource.cpp @@ -48,7 +48,8 @@ static void AudioRecordCallbackFunction(int event, void *user, void *info) { AudioSource::AudioSource( audio_source_t inputSource, uint32_t sampleRate, uint32_t channelCount) - : mStarted(false), + : mRecord(NULL), + mStarted(false), mSampleRate(sampleRate), mPrevSampleTimeUs(0), mNumFramesReceived(0), @@ -61,7 +62,7 @@ AudioSource::AudioSource( status_t status = AudioRecord::getMinFrameCount(&minFrameCount, sampleRate, AUDIO_FORMAT_PCM_16_BIT, - channelCount); + audio_channel_in_mask_from_count(channelCount)); if (status == OK) { // make sure that the AudioRecord callback never returns more than the maximum // buffer size @@ -73,15 +74,10 @@ AudioSource::AudioSource( bufCount++; } - AudioRecord::record_flags flags = (AudioRecord::record_flags) - (AudioRecord::RECORD_AGC_ENABLE | - AudioRecord::RECORD_NS_ENABLE | - AudioRecord::RECORD_IIR_ENABLE); mRecord = new AudioRecord( inputSource, sampleRate, AUDIO_FORMAT_PCM_16_BIT, audio_channel_in_mask_from_count(channelCount), bufCount * frameCount, - flags, AudioRecordCallbackFunction, this, frameCount); diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 0f346d8..2c68075 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -130,7 +130,7 @@ struct AwesomeNativeWindowRenderer : public AwesomeRenderer { CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs)); native_window_set_buffers_timestamp(mNativeWindow.get(), timeUs * 1000); status_t err = mNativeWindow->queueBuffer( - mNativeWindow.get(), buffer->graphicBuffer().get()); + mNativeWindow.get(), buffer->graphicBuffer().get(), -1); if (err != 0) { ALOGE("queueBuffer failed with error %s (%d)", strerror(-err), -err); diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp index c75f100..1de808e 100644 --- a/media/libstagefright/DataSource.cpp +++ b/media/libstagefright/DataSource.cpp @@ -17,7 +17,7 @@ #include "include/AMRExtractor.h" #if CHROMIUM_AVAILABLE -#include "include/DataUriSource.h" +#include "include/chromium_http_stub.h" #endif #include "include/MP3Extractor.h" @@ -173,7 +173,7 @@ sp<DataSource> DataSource::CreateFromURI( # if CHROMIUM_AVAILABLE } else if (!strncasecmp("data:", uri, 5)) { - source = new DataUriSource(uri); + source = createDataUriSource(uri); #endif } else { // Assume it's a filename. diff --git a/media/libstagefright/FLACExtractor.cpp b/media/libstagefright/FLACExtractor.cpp index 668d7f7..29bb056 100644 --- a/media/libstagefright/FLACExtractor.cpp +++ b/media/libstagefright/FLACExtractor.cpp @@ -350,7 +350,7 @@ void FLACParser::metadataCallback(const FLAC__StreamMetadata *metadata) for (FLAC__uint32 i = 0; i < vc->num_comments; ++i) { FLAC__StreamMetadata_VorbisComment_Entry *vce; vce = &vc->comments[i]; - if (mFileMetadata != 0) { + if (mFileMetadata != 0 && vce->entry != NULL) { parseVorbisComment(mFileMetadata, (const char *) vce->entry, vce->length); } diff --git a/media/libstagefright/HTTPBase.cpp b/media/libstagefright/HTTPBase.cpp index d7eea3f..40bfc55 100644 --- a/media/libstagefright/HTTPBase.cpp +++ b/media/libstagefright/HTTPBase.cpp @@ -21,7 +21,7 @@ #include "include/HTTPBase.h" #if CHROMIUM_AVAILABLE -#include "include/ChromiumHTTPDataSource.h" +#include "include/chromium_http_stub.h" #endif #include <media/stagefright/foundation/ADebug.h> @@ -46,7 +46,10 @@ HTTPBase::HTTPBase() // static sp<HTTPBase> HTTPBase::Create(uint32_t flags) { #if CHROMIUM_AVAILABLE - return new ChromiumHTTPDataSource(flags); + HTTPBase *dataSource = createChromiumHTTPDataSource(flags); + if (dataSource) { + return dataSource; + } #endif { TRESPASS(); diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 755b502..cc18a1d 100755 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -269,7 +269,7 @@ MPEG4Writer::MPEG4Writer(const char *filename) mAreGeoTagsAvailable(false), mStartTimeOffsetMs(-1) { - mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR); + mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); if (mFd >= 0) { mInitCheck = OK; } diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index fde7ebf..1d4ab32 100755 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -1776,7 +1776,7 @@ status_t OMXCodec::allocateOutputBuffersFromNativeWindow() { // Dequeue buffers and send them to OMX for (OMX_U32 i = 0; i < def.nBufferCountActual; i++) { ANativeWindowBuffer* buf; - err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf); + err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf); if (err != 0) { ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err); break; @@ -1832,7 +1832,7 @@ status_t OMXCodec::cancelBufferToNativeWindow(BufferInfo *info) { CHECK_EQ((int)info->mStatus, (int)OWNED_BY_US); CODEC_LOGV("Calling cancelBuffer on buffer %p", info->mBuffer); int err = mNativeWindow->cancelBuffer( - mNativeWindow.get(), info->mMediaBuffer->graphicBuffer().get()); + mNativeWindow.get(), info->mMediaBuffer->graphicBuffer().get(), -1); if (err != 0) { CODEC_LOGE("cancelBuffer failed w/ error 0x%08x", err); @@ -1846,7 +1846,8 @@ status_t OMXCodec::cancelBufferToNativeWindow(BufferInfo *info) { OMXCodec::BufferInfo* OMXCodec::dequeueBufferFromNativeWindow() { // Dequeue the next buffer from the native window. ANativeWindowBuffer* buf; - int err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf); + int fenceFd = -1; + int err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf); if (err != 0) { CODEC_LOGE("dequeueBuffer failed w/ error 0x%08x", err); @@ -1950,7 +1951,8 @@ status_t OMXCodec::pushBlankBuffersToNativeWindow() { // on the screen and then been replaced, so an previous video frames are // guaranteed NOT to be currently displayed. for (int i = 0; i < numBufs + 1; i++) { - err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &anb); + int fenceFd = -1; + err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &anb); if (err != NO_ERROR) { ALOGE("error pushing blank frames: dequeueBuffer failed: %s (%d)", strerror(-err), -err); @@ -1958,13 +1960,6 @@ status_t OMXCodec::pushBlankBuffersToNativeWindow() { } sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); - err = mNativeWindow->lockBuffer(mNativeWindow.get(), - buf->getNativeBuffer()); - if (err != NO_ERROR) { - ALOGE("error pushing blank frames: lockBuffer failed: %s (%d)", - strerror(-err), -err); - goto error; - } // Fill the buffer with the a 1x1 checkerboard pattern ;) uint32_t* img = NULL; @@ -1985,7 +1980,7 @@ status_t OMXCodec::pushBlankBuffersToNativeWindow() { } err = mNativeWindow->queueBuffer(mNativeWindow.get(), - buf->getNativeBuffer()); + buf->getNativeBuffer(), -1); if (err != NO_ERROR) { ALOGE("error pushing blank frames: queueBuffer failed: %s (%d)", strerror(-err), -err); @@ -2000,7 +1995,7 @@ error: if (err != NO_ERROR) { // Clean up after an error. if (anb != NULL) { - mNativeWindow->cancelBuffer(mNativeWindow.get(), anb); + mNativeWindow->cancelBuffer(mNativeWindow.get(), anb, -1); } native_window_api_disconnect(mNativeWindow.get(), @@ -3199,23 +3194,6 @@ void OMXCodec::fillOutputBuffer(BufferInfo *info) { return; } - if (info->mMediaBuffer != NULL) { - sp<GraphicBuffer> graphicBuffer = info->mMediaBuffer->graphicBuffer(); - if (graphicBuffer != 0) { - // When using a native buffer we need to lock the buffer before - // giving it to OMX. - CODEC_LOGV("Calling lockBuffer on %p", info->mBuffer); - int err = mNativeWindow->lockBuffer(mNativeWindow.get(), - graphicBuffer.get()); - if (err != 0) { - CODEC_LOGE("lockBuffer failed w/ error 0x%08x", err); - - setState(ERROR); - return; - } - } - } - CODEC_LOGV("Calling fillBuffer on buffer %p", info->mBuffer); status_t err = mOMX->fillBuffer(mNode, info->mBuffer); diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp index 300d2fc..f1f444e 100644 --- a/media/libstagefright/SurfaceMediaSource.cpp +++ b/media/libstagefright/SurfaceMediaSource.cpp @@ -244,7 +244,8 @@ status_t SurfaceMediaSource::read( MediaBuffer **buffer, if (mStartTimeNs > 0) { if (item.mTimestamp < mStartTimeNs) { // This frame predates start of record, discard - mBufferQueue->releaseBuffer(item.mBuf, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); + mBufferQueue->releaseBuffer(item.mBuf, EGL_NO_DISPLAY, + EGL_NO_SYNC_KHR, Fence::NO_FENCE); continue; } mStartTimeNs = item.mTimestamp - mStartTimeNs; @@ -333,7 +334,8 @@ void SurfaceMediaSource::signalBufferReturned(MediaBuffer *buffer) { ALOGV("Slot %d returned, matches handle = %p", id, mBufferSlot[id]->handle); - mBufferQueue->releaseBuffer(id, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); + mBufferQueue->releaseBuffer(id, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, + Fence::NO_FENCE); buffer->setObserver(0); buffer->release(); diff --git a/media/libstagefright/chromium_http/Android.mk b/media/libstagefright/chromium_http/Android.mk index 6ab2a00..2c6d84c 100644 --- a/media/libstagefright/chromium_http/Android.mk +++ b/media/libstagefright/chromium_http/Android.mk @@ -6,7 +6,8 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ DataUriSource.cpp \ ChromiumHTTPDataSource.cpp \ - support.cpp + support.cpp \ + chromium_http_stub.cpp LOCAL_C_INCLUDES:= \ $(TOP)/frameworks/av/media/libstagefright \ @@ -16,10 +17,20 @@ LOCAL_C_INCLUDES:= \ LOCAL_CFLAGS += -Wno-multichar -LOCAL_SHARED_LIBRARIES += libstlport +LOCAL_SHARED_LIBRARIES += \ + libstlport \ + libchromium_net \ + libutils \ + libcutils \ + libstagefright_foundation \ + libstagefright \ + libdrmframework + include external/stlport/libstlport.mk LOCAL_MODULE:= libstagefright_chromium_http -include $(BUILD_STATIC_LIBRARY) +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) endif diff --git a/services/audioflinger/Soaker.h b/media/libstagefright/chromium_http/chromium_http_stub.cpp index 43d9d2f..560a61f 100644 --- a/services/audioflinger/Soaker.h +++ b/media/libstagefright/chromium_http/chromium_http_stub.cpp @@ -14,32 +14,20 @@ * limitations under the License. */ -#ifndef _ANDROID_AUDIO_SOAKER_H -#define _ANDROID_AUDIO_SOAKER_H +#include <dlfcn.h> -#include <utils/Thread.h> +#include <include/chromium_http_stub.h> +#include <include/ChromiumHTTPDataSource.h> +#include <include/DataUriSource.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; - } -}; +HTTPBase *createChromiumHTTPDataSource(uint32_t flags) { + return new ChromiumHTTPDataSource(flags); +} -} // namespace android +DataSource *createDataUriSource(const char *uri) { + return new DataUriSource(uri); +} -#endif // _ANDROID_AUDIO_SOAKER_H +} diff --git a/media/libstagefright/chromium_http_stub.cpp b/media/libstagefright/chromium_http_stub.cpp new file mode 100644 index 0000000..cbd8796 --- /dev/null +++ b/media/libstagefright/chromium_http_stub.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <dlfcn.h> + +#include <media/stagefright/DataSource.h> + +#include "include/chromium_http_stub.h" +#include "include/HTTPBase.h" + +namespace android { + +static bool gFirst = true; +static void *gHandle; +static Mutex gLibMutex; + +HTTPBase *(*gLib_createChromiumHTTPDataSource)(uint32_t flags); +DataSource *(*gLib_createDataUriSource)(const char *uri); + +static bool load_libstagefright_chromium_http() { + Mutex::Autolock autoLock(gLibMutex); + void *sym; + + if (!gFirst) { + return (gHandle != NULL); + } + + gFirst = false; + + gHandle = dlopen("libstagefright_chromium_http.so", RTLD_NOW); + if (gHandle == NULL) { + return false; + } + + sym = dlsym(gHandle, "createChromiumHTTPDataSource"); + if (sym == NULL) { + gHandle = NULL; + return false; + } + gLib_createChromiumHTTPDataSource = (HTTPBase *(*)(uint32_t))sym; + + sym = dlsym(gHandle, "createDataUriSource"); + if (sym == NULL) { + gHandle = NULL; + return false; + } + gLib_createDataUriSource = (DataSource *(*)(const char *))sym; + + return true; +} + +HTTPBase *createChromiumHTTPDataSource(uint32_t flags) { + if (!load_libstagefright_chromium_http()) { + return NULL; + } + + return gLib_createChromiumHTTPDataSource(flags); +} + +DataSource *createDataUriSource(const char *uri) { + if (!load_libstagefright_chromium_http()) { + return NULL; + } + + return gLib_createDataUriSource(uri); +} + +} diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp index 8673bad..2704a37 100644 --- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp +++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp @@ -141,13 +141,12 @@ void SoftwareRenderer::render( const void *data, size_t size, void *platformPrivate) { ANativeWindowBuffer *buf; int err; - if ((err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf)) != 0) { + if ((err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), + &buf)) != 0) { ALOGW("Surface::dequeueBuffer returned error %d", err); return; } - CHECK_EQ(0, mNativeWindow->lockBuffer(mNativeWindow.get(), buf)); - GraphicBufferMapper &mapper = GraphicBufferMapper::get(); Rect bounds(mCropWidth, mCropHeight); @@ -231,7 +230,8 @@ void SoftwareRenderer::render( CHECK_EQ(0, mapper.unlock(buf->handle)); - if ((err = mNativeWindow->queueBuffer(mNativeWindow.get(), buf)) != 0) { + if ((err = mNativeWindow->queueBuffer(mNativeWindow.get(), buf, + -1)) != 0) { ALOGW("Surface::queueBuffer returned error %d", err); } buf = NULL; diff --git a/media/libstagefright/include/chromium_http_stub.h b/media/libstagefright/include/chromium_http_stub.h new file mode 100644 index 0000000..869d4ac --- /dev/null +++ b/media/libstagefright/include/chromium_http_stub.h @@ -0,0 +1,30 @@ +/* + * 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 CHROMIUM_HTTP_STUB_H_ +#define CHROMIUM_HTTP_STUB_H_ + +#include <include/HTTPBase.h> +#include <media/stagefright/DataSource.h> + +namespace android { +extern "C" { +HTTPBase *createChromiumHTTPDataSource(uint32_t flags); +DataSource *createDataUriSource(const char *uri); +} +} + +#endif diff --git a/media/libstagefright/tests/Android.mk b/media/libstagefright/tests/Android.mk index a1e6be7..57fff0b 100644 --- a/media/libstagefright/tests/Android.mk +++ b/media/libstagefright/tests/Android.mk @@ -20,9 +20,10 @@ LOCAL_SHARED_LIBRARIES := \ libgui \ libmedia \ libstagefright \ - libstagefright_omx \ libstagefright_foundation \ + libstagefright_omx \ libstlport \ + libsync \ libui \ libutils \ diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp index 466f521..cc2aca7 100644 --- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp +++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp @@ -509,31 +509,31 @@ void SurfaceMediaSourceGLTest::setUpEGLSurfaceFromMediaRecorder(sp<MediaRecorder // cpu YV12 buffer void SurfaceMediaSourceTest::oneBufferPass(int width, int height ) { ANativeWindowBuffer* anb; - ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); ASSERT_TRUE(anb != NULL); - sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); - ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())); // Fill the buffer with the a checkerboard pattern uint8_t* img = NULL; + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); SurfaceMediaSourceTest::fillYV12Buffer(img, width, height, buf->getStride()); buf->unlock(); - ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(), + -1)); } // Dequeuing and queuing the buffer without really filling it in. void SurfaceMediaSourceTest::oneBufferPassNoFill(int width, int height ) { ANativeWindowBuffer* anb; - ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); ASSERT_TRUE(anb != NULL); - sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); - // ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())); // We do not fill the buffer in. Just queue it back. - ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(), + -1)); } // Fill a YV12 buffer with a multi-colored checkerboard pattern @@ -652,7 +652,7 @@ TEST_F(SurfaceMediaSourceTest, DISABLED_DummyEncodingFromCpuFilledYV12BufferNpot ANativeWindowBuffer* anb; // Note: make sure we get an ERROR back when dequeuing! - ASSERT_NE(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_NE(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); } // pass multiple buffers from the native_window the SurfaceMediaSource diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp index bf7795c..d672dff 100644 --- a/media/mtp/MtpDevice.cpp +++ b/media/mtp/MtpDevice.cpp @@ -667,7 +667,7 @@ fail: // reads the object's data and writes it to the specified file path bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) { ALOGD("readObject: %s", destPath); - int fd = ::open(destPath, O_RDWR | O_CREAT | O_TRUNC); + int fd = ::open(destPath, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (fd < 0) { ALOGE("open failed for %s", destPath); return false; diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index 5606187..662a93d 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -931,7 +931,7 @@ MtpResponseCode MtpServer::doSendObject() { initialData = ret - MTP_CONTAINER_HEADER_SIZE; mtp_file_range mfr; - mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC); + mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (mfr.fd < 0) { result = MTP_RESPONSE_GENERAL_ERROR; goto done; 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/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index aab9984..e2b29f7 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 // ---------------------------------------------------------------------------- @@ -216,8 +210,9 @@ out: AudioFlinger::AudioFlinger() : BnAudioFlinger(), mPrimaryHardwareDev(NULL), - mHardwareStatus(AUDIO_HW_IDLE), // see also onFirstRef() + mHardwareStatus(AUDIO_HW_IDLE), mMasterVolume(1.0f), + mMasterVolumeSW(1.0f), mMasterVolumeSupportLvl(MVS_NONE), mMasterMute(false), mNextUniqueId(1), @@ -247,9 +242,6 @@ void AudioFlinger::onFirstRef() } mMode = AUDIO_MODE_NORMAL; - mMasterVolumeSW = 1.0; - mMasterVolume = 1.0; - mHardwareStatus = AUDIO_HW_IDLE; } AudioFlinger::~AudioFlinger() @@ -440,7 +432,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, @@ -908,7 +900,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 +956,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,7 +968,7 @@ 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); @@ -985,10 +978,6 @@ size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t form unsigned int AudioFlinger::getInputFramesLost(audio_io_handle_t ioHandle) const { - if (ioHandle == 0) { - return 0; - } - Mutex::Autolock _l(mLock); RecordThread *recordThread = checkRecordThread_l(ioHandle); @@ -1132,8 +1121,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((audio_devices_t) device), mId(id), mDeathRecipient(new PMDeathRecipient(this)) { } @@ -1660,7 +1648,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 +1703,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 +1777,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,7 +1792,6 @@ 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, @@ -1815,7 +1802,6 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrac 1, callingPid, tid, err); } } -#endif lStatus = NO_ERROR; @@ -2195,9 +2181,6 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud audio_io_handle_t id, uint32_t device, type_t type) : PlaybackThread(audioFlinger, output, id, device, type), // mAudioMixer below -#ifdef SOAKER - mSoaker(NULL), -#endif // mFastMixer below mFastMixerFutex(0) // mOutputSink below @@ -2205,15 +2188,15 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud // 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("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 +2250,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 +2281,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); if (err != 0) { ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d", 2, getpid_cached, tid, err); } -#endif #ifdef AUDIO_WATCHDOG // create and start the watchdog @@ -2370,12 +2344,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 +2476,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 +2484,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 +2513,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 +2567,7 @@ if (mType == MIXER) { threadLoop_sleepTime(); } - if (mSuspended > 0) { + if (isSuspended()) { sleepTime = suspendSleepTimeUs(); } @@ -2635,11 +2600,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 +2627,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 +2752,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); } @@ -2847,11 +2805,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 } @@ -3373,7 +3330,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()); @@ -3482,7 +3439,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 = (audio_devices_t) value; for (size_t i = 0; i < mEffectChains.size(); i++) { mEffectChains[i]->setDevice_l(mDevice); } @@ -3505,7 +3462,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 @@ -3602,7 +3559,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; } @@ -4070,6 +4027,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 +4057,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 +4232,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 +4258,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"); @@ -4496,8 +4454,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 +4777,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 +4795,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 +5017,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 +5032,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 +5059,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 +5068,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 +5290,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, @@ -5389,8 +5345,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; } @@ -5448,7 +5402,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 +5790,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 +5832,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 @@ -5941,13 +5891,13 @@ status_t AudioFlinger::RecordHandle::onTransact( AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, AudioStreamIn *input, uint32_t sampleRate, - uint32_t channels, + audio_channel_mask_t channelMask, audio_io_handle_t id, uint32_t device) : ThreadBase(audioFlinger, id, device, RECORD), mInput(input), mTrack(NULL), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL), // 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?) @@ -6151,7 +6101,7 @@ bool AudioFlinger::RecordThread::threadLoop() } } } - mActiveTrack->overflow(); + mActiveTrack->clearOverflow(); } // client isn't retrieving buffers fast enough else { @@ -6191,9 +6141,11 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createR 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 +6157,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); @@ -6508,11 +6462,12 @@ 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() + uint32_t /*audio_devices_t*/ newDevice = mDevice; if (value & AUDIO_DEVICE_OUT_ALL) { - mDevice &= (uint32_t)~(value & AUDIO_DEVICE_OUT_ALL); + newDevice &= (uint32_t)~(value & AUDIO_DEVICE_OUT_ALL); status = BAD_VALUE; } else { - mDevice &= (uint32_t)~(value & AUDIO_DEVICE_IN_ALL); + newDevice &= (uint32_t)~(value & AUDIO_DEVICE_IN_ALL); // disable AEC and NS if the device is a BT SCO headset supporting those pre processings if (mTrack != NULL) { bool suspend = audio_is_bluetooth_sco_device( @@ -6521,7 +6476,8 @@ bool AudioFlinger::RecordThread::checkForNewParameters_l() setEffectSuspended_l(FX_IID_NS, suspend, mTrack->sessionId()); } } - mDevice |= (uint32_t)value; + newDevice |= value; + mDevice = (audio_devices_t) 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()); @@ -6669,12 +6625,6 @@ AudioFlinger::RecordThread::RecordTrack* AudioFlinger::RecordThread::track() return mTrack; } -AudioFlinger::AudioStreamIn* AudioFlinger::RecordThread::getInput() const -{ - Mutex::Autolock _l(mLock); - return mInput; -} - AudioFlinger::AudioStreamIn* AudioFlinger::RecordThread::clearInput() { Mutex::Autolock _l(mLock); @@ -6969,7 +6919,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; @@ -7012,7 +6962,7 @@ 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); } @@ -7054,7 +7004,7 @@ status_t AudioFlinger::closeInput(audio_io_handle_t input) { Mutex::Autolock _l(mLock); thread = checkRecordThread_l(input); - if (thread == NULL) { + if (thread == 0) { return BAD_VALUE; } @@ -7078,21 +7028,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 +7126,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()); } @@ -7670,7 +7604,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 +7744,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,8 +7936,15 @@ 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 is set below + // 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; @@ -8025,9 +7966,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 +7993,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 +8038,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; } -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 +8085,7 @@ void AudioFlinger::EffectModule::disconnect(const wp<EffectHandle>& handle, bool thread->disconnectEffect(keep, handle, unpinIfLast); } } + return mHandles.size(); } void AudioFlinger::EffectModule::updateState() { @@ -8240,7 +8183,6 @@ void AudioFlinger::EffectModule::reset_l() status_t AudioFlinger::EffectModule::configure() { - uint32_t channels; if (mEffectInterface == NULL) { return NO_INIT; } @@ -8251,18 +8193,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 +8390,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 +8401,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 +8443,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); } } @@ -8649,6 +8593,22 @@ bool AudioFlinger::EffectModule::suspended() const return mSuspended; } +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; +} + status_t AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args) { const size_t SIZE = 256; @@ -8715,8 +8675,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); } @@ -8746,7 +8706,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 +8731,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 +8810,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()); @@ -9428,10 +9394,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..a6fd0a0 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); @@ -142,7 +143,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, @@ -380,7 +382,7 @@ 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); @@ -412,10 +414,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 +464,7 @@ private: bool mStepServerFailed; const int mSessionId; uint8_t mChannelCount; - uint32_t mChannelMask; + audio_channel_mask_t mChannelMask; Vector < sp<SyncEvent> >mSyncEvents; }; @@ -483,14 +492,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 +516,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 +532,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) @@ -617,7 +634,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 +663,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 +716,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 +733,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 +790,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,7 +840,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); @@ -856,7 +883,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 +932,7 @@ private: DuplicatingThread *sourceThread, uint32_t sampleRate, audio_format_t format, - uint32_t channelMask, + audio_channel_mask_t channelMask, int frameCount); virtual ~OutputTrack(); @@ -984,7 +1011,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 +1023,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 +1055,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 @@ -1076,6 +1122,7 @@ public: // 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 +1147,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 @@ -1145,7 +1191,6 @@ public: // Thread virtuals - void invalidateTracks(audio_stream_type_t streamType); virtual bool checkForNewParameters_l(); virtual status_t dumpInternals(int fd, const Vector<String16>& args); @@ -1167,9 +1212,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 @@ -1331,7 +1373,7 @@ 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(); @@ -1340,7 +1382,9 @@ private: int triggerSession = 0); virtual void stop(); - bool overflow() { bool tmp = mOverflow; mOverflow = false; return tmp; } + // 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; } void dump(char* buffer, size_t size); @@ -1355,14 +1399,13 @@ 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); virtual ~RecordThread(); @@ -1379,9 +1422,11 @@ 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, @@ -1389,7 +1434,6 @@ private: int triggerSession); void stop(RecordTrack* recordTrack); status_t dump(int fd, const Vector<String16>& args); - AudioStreamIn* getInput() const; AudioStreamIn* clearInput(); virtual audio_stream_t* stream() const; @@ -1417,7 +1461,6 @@ private: private: void clearSyncStartEvent(); - RecordThread(); AudioStreamIn *mInput; RecordTrack* mTrack; sp<RecordTrack> mActiveTrack; @@ -1510,6 +1553,7 @@ private: return mSessionId; } status_t setEnabled(bool enabled); + status_t setEnabled_l(bool enabled); bool isEnabled() const; bool isProcessEnabled() const; @@ -1521,9 +1565,9 @@ 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; } wp<EffectChain>& chain() { return mChain; } @@ -1536,10 +1580,13 @@ 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); @@ -1559,14 +1606,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 + const int mId; // this instance unique ID + const int mSessionId; // audio session ID 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 +1671,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 +1691,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. @@ -1703,12 +1754,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) @@ -1760,8 +1811,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 diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp index 0c8b3ce..3a8c54d 100644 --- a/services/audioflinger/AudioMixer.cpp +++ b/services/audioflinger/AudioMixer.cpp @@ -412,7 +412,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..f07fe8e 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, @@ -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..7c6fa74 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); diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk index 3cae1f5..8cccf49 100644 --- a/services/camera/libcameraservice/Android.mk +++ b/services/camera/libcameraservice/Android.mk @@ -7,7 +7,10 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - CameraService.cpp + CameraService.cpp \ + CameraClient.cpp \ + Camera2Client.cpp \ + Camera2Device.cpp LOCAL_SHARED_LIBRARIES:= \ libui \ @@ -18,7 +21,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..8d4add4 --- /dev/null +++ b/services/camera/libcameraservice/Camera2Client.cpp @@ -0,0 +1,3084 @@ +/* + * 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 <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(NOT_INITIALIZED), + mPreviewStreamId(NO_STREAM), + mPreviewRequest(NULL), + mCaptureStreamId(NO_STREAM), + mCaptureRequest(NULL), + mRecordingStreamId(NO_STREAM), + mRecordingRequest(NULL) +{ + ATRACE_CALL(); + + mDevice = new Camera2Device(cameraId); +} + +status_t Camera2Client::initialize(camera_module_t *module) +{ + ATRACE_CALL(); + 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 = 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) { + ALOGD("%s: Default parameters converted from camera %d:", __FUNCTION__, + mCameraId); + ALOGD("%s", mParamsFlattened.string()); + } + + mState = STOPPED; + + return OK; +} + +Camera2Client::~Camera2Client() { + ATRACE_CALL(); + ALOGV("%s: Camera %d: Shutting down", __FUNCTION__, mCameraId); + + mDestructionStarted = true; + + 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; + + result.append(getStateName(mState)); + + result.append("\n Current parameters:\n"); + result.appendFormat(" Preview size: %d x %d\n", + mParameters.previewWidth, mParameters.previewHeight); + result.appendFormat(" Preview FPS range: %d - %d\n", + mParameters.previewFpsRange[0], mParameters.previewFpsRange[1]); + result.appendFormat(" Preview HAL pixel format: 0x%x\n", + mParameters.previewFormat); + result.appendFormat(" Preview transform: %x\n", + mParameters.previewTransform); + result.appendFormat(" Picture size: %d x %d\n", + mParameters.pictureWidth, mParameters.pictureHeight); + result.appendFormat(" Jpeg thumbnail size: %d x %d\n", + mParameters.jpegThumbSize[0], mParameters.jpegThumbSize[1]); + result.appendFormat(" Jpeg quality: %d, thumbnail quality: %d\n", + mParameters.jpegQuality, mParameters.jpegThumbQuality); + result.appendFormat(" Jpeg rotation: %d\n", mParameters.jpegRotation); + result.appendFormat(" GPS tags %s\n", + mParameters.gpsEnabled ? "enabled" : "disabled"); + if (mParameters.gpsEnabled) { + result.appendFormat(" GPS lat x long x alt: %f x %f x %f\n", + mParameters.gpsCoordinates[0], mParameters.gpsCoordinates[1], + mParameters.gpsCoordinates[2]); + result.appendFormat(" GPS timestamp: %lld\n", + mParameters.gpsTimestamp); + result.appendFormat(" GPS processing method: %s\n", + mParameters.gpsProcessingMethod.string()); + } + + result.append(" White balance mode: "); + switch (mParameters.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 (mParameters.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 (mParameters.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 (mParameters.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 (mParameters.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 (mParameters.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 < mParameters.focusingAreas.size(); i++) { + result.appendFormat(" [ (%d, %d, %d, %d), weight %d ]\n", + mParameters.focusingAreas[i].left, + mParameters.focusingAreas[i].top, + mParameters.focusingAreas[i].right, + mParameters.focusingAreas[i].bottom, + mParameters.focusingAreas[i].weight); + } + + result.appendFormat(" Exposure compensation index: %d\n", + mParameters.exposureCompensation); + + result.appendFormat(" AE lock %s, AWB lock %s\n", + mParameters.autoExposureLock ? "enabled" : "disabled", + mParameters.autoWhiteBalanceLock ? "enabled" : "disabled" ); + + result.appendFormat(" Metering areas:\n"); + for (size_t i = 0; i < mParameters.meteringAreas.size(); i++) { + result.appendFormat(" [ (%d, %d, %d, %d), weight %d ]\n", + mParameters.meteringAreas[i].left, + mParameters.meteringAreas[i].top, + mParameters.meteringAreas[i].right, + mParameters.meteringAreas[i].bottom, + mParameters.meteringAreas[i].weight); + } + + result.appendFormat(" Zoom index: %d\n", mParameters.zoom); + result.appendFormat(" Video size: %d x %d\n", mParameters.videoWidth, + mParameters.videoHeight); + + result.appendFormat(" Recording hint is %s\n", + mParameters.recordingHint ? "set" : "not set"); + + result.appendFormat(" Video stabilization is %s\n", + mParameters.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.append(" Current requests:\n"); + if (mPreviewRequest != NULL) { + result.append(" Preview request:\n"); + write(fd, result.string(), result.size()); + dump_camera_metadata(mPreviewRequest, fd, 2); + } 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_camera_metadata(mCaptureRequest, fd, 2); + } else { + result = " Capture 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(NOT_INITIALIZED) + 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(); + Mutex::Autolock icl(mICameraLock); + + if (mDevice == 0) return; + + stopPreviewLocked(); + + mDevice->waitUntilDrained(); + + if (mPreviewStreamId != NO_STREAM) { + mDevice->deleteStream(mPreviewStreamId); + mPreviewStreamId = NO_STREAM; + } + + if (mCaptureStreamId != NO_STREAM) { + mDevice->deleteStream(mCaptureStreamId); + mCaptureStreamId = NO_STREAM; + } + + CameraService::Client::disconnect(); +} + +status_t Camera2Client::connect(const sp<ICameraClient>& client) { + ATRACE_CALL(); + + 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(); + 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(); + 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(); + Mutex::Autolock icl(mICameraLock); + + sp<IBinder> binder; + sp<ANativeWindow> window; + if (surface != 0) { + binder = surface->asBinder(); + window = surface; + } + + return setPreviewWindowLocked(binder,window); +} + +status_t Camera2Client::setPreviewTexture( + const sp<ISurfaceTexture>& surfaceTexture) { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + + sp<IBinder> binder; + sp<ANativeWindow> window; + if (surfaceTexture != 0) { + binder = surfaceTexture->asBinder(); + window = new SurfaceTextureClient(surfaceTexture); + } + return setPreviewWindowLocked(binder, window); +} + +status_t Camera2Client::setPreviewWindowLocked(const sp<IBinder>& binder, + sp<ANativeWindow> window) { + ATRACE_CALL(); + status_t res; + + if (binder == mPreviewSurface) { + return NO_ERROR; + } + + switch (mState) { + case NOT_INITIALIZED: + 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 startPreviewLocked(); + } + + return OK; +} + +void Camera2Client::setPreviewCallbackFlag(int flag) { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); +} + +status_t Camera2Client::startPreview() { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + return startPreviewLocked(); +} + +status_t Camera2Client::startPreviewLocked() { + 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; + + Mutex::Autolock pl(mParamsLock); + + res = updatePreviewStream(); + 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(); + 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(); + Mutex::Autolock icl(mICameraLock); + stopPreviewLocked(); +} + +void Camera2Client::stopPreviewLocked() { + ATRACE_CALL(); + switch (mState) { + case NOT_INITIALIZED: + 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); + 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); + return mState == PREVIEW; +} + +status_t Camera2Client::storeMetaDataInBuffers(bool enabled) { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + return BAD_VALUE; +} + +status_t Camera2Client::startRecording() { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + status_t res; + switch (mState) { + case STOPPED: + res = startPreviewLocked(); + 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; + }; + + Mutex::Autolock pl(mParamsLock); + + res = updateRecordingStream(); + 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(); + 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(); + Mutex::Autolock icl(mICameraLock); + status_t res; + 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); + return (mState == RECORD || mState == VIDEO_SNAPSHOT); +} + +void Camera2Client::releaseRecordingFrame(const sp<IMemory>& mem) { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + // 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; + } + mRecordingHeapFree++; +} + +status_t Camera2Client::autoFocus() { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + return OK; +} + +status_t Camera2Client::cancelAutoFocus() { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + return OK; +} + +status_t Camera2Client::takePicture(int msgType) { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + status_t res; + + switch (mState) { + case NOT_INITIALIZED: + 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; + } + + Mutex::Autolock pl(mParamsLock); + + res = updateCaptureStream(); + 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(); + 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(); + Mutex::Autolock icl(mICameraLock); + Mutex::Autolock pl(mParamsLock); + status_t res; + + 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 != mParameters.previewWidth || + previewHeight != 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__, + mParameters.previewWidth, 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] != mParameters.previewFpsRange[0] || + previewFpsRange[1] != 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 != 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 != 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 == mParameters.pictureWidth || + pictureHeight == 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] != mParameters.jpegThumbSize[0] || + jpegThumbSize[1] != 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 != 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 != 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 != 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 != 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 != 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 != 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 != mParameters.videoWidth || + videoHeight != 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 */ + + mParameters.previewWidth = previewWidth; + mParameters.previewHeight = previewHeight; + mParameters.previewFpsRange[0] = previewFpsRange[0]; + mParameters.previewFpsRange[1] = previewFpsRange[1]; + mParameters.previewFps = previewFps; + mParameters.previewFormat = previewFormat; + + mParameters.pictureWidth = pictureWidth; + mParameters.pictureHeight = pictureHeight; + + mParameters.jpegThumbSize[0] = jpegThumbSize[0]; + mParameters.jpegThumbSize[1] = jpegThumbSize[1]; + mParameters.jpegQuality = jpegQuality; + mParameters.jpegThumbQuality = jpegThumbQuality; + + mParameters.gpsEnabled = gpsEnabled; + mParameters.gpsCoordinates[0] = gpsCoordinates[0]; + mParameters.gpsCoordinates[1] = gpsCoordinates[1]; + mParameters.gpsCoordinates[2] = gpsCoordinates[2]; + mParameters.gpsTimestamp = gpsTimestamp; + mParameters.gpsProcessingMethod = gpsProcessingMethod; + + mParameters.wbMode = wbMode; + mParameters.effectMode = effectMode; + mParameters.antibandingMode = antibandingMode; + mParameters.sceneMode = sceneMode; + + mParameters.flashMode = flashMode; + mParameters.focusMode = focusMode; + + mParameters.focusingAreas = focusingAreas; + mParameters.exposureCompensation = exposureCompensation; + mParameters.autoExposureLock = autoExposureLock; + mParameters.autoWhiteBalanceLock = autoWhiteBalanceLock; + mParameters.meteringAreas = meteringAreas; + mParameters.zoom = zoom; + + mParameters.videoWidth = videoWidth; + mParameters.videoHeight = videoHeight; + + mParameters.recordingHint = recordingHint; + mParameters.videoStabilization = videoStabilization; + + res = updatePreviewRequest(); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update preview request: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + res = updateCaptureRequest(); + if (res != OK) { + ALOGE("%s: Camera %d: Unable to update capture request: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return res; + } + + res = updateRecordingRequest(); + 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; + } + } + + mParamsFlattened = params; + + return OK; +} + +String8 Camera2Client::getParameters() const { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + + Mutex::Autolock pl(mParamsLock); + + // TODO: Deal with focus distances + return mParamsFlattened; +} + +status_t Camera2Client::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) { + ATRACE_CALL(); + Mutex::Autolock icl(mICameraLock); + + ALOGV("%s: Camera %d: Command %d (%d, %d)", __FUNCTION__, mCameraId, + cmd, arg1, arg2); + + if (cmd == CAMERA_CMD_SET_DISPLAY_ORIENTATION) { + int transform = degToTransform(arg1, + mCameraFacing == CAMERA_FACING_FRONT); + if (transform == -1) { + ALOGE("%s: Camera %d: Error setting %d as display orientation value", + __FUNCTION__, mCameraId, arg1); + return BAD_VALUE; + } + if (transform != mParameters.previewTransform && + mPreviewStreamId != NO_STREAM) { + mDevice->setStreamTransform(mPreviewStreamId, transform); + } + mParameters.previewTransform = transform; + return OK; + } + + ALOGE("%s: Camera %d: Unimplemented command %d (%d, %d)", __FUNCTION__, + mCameraId, cmd, arg1, arg2); + + return OK; +} + +/** Device-related methods */ + +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 + if (mState != RECORD && mState != VIDEO_SNAPSHOT) { + ALOGE("%s: Camera %d: Recording image buffer produced unexpectedly!", + __FUNCTION__, mCameraId); + return; + } + + CpuConsumer::LockedBuffer imgBuffer; + res = mRecordingConsumer->lockNextBuffer(&imgBuffer); + if (res != OK) { + ALOGE("%s: Camera %d: Error receiving recording buffer: %s (%d)", + __FUNCTION__, mCameraId, strerror(-res), res); + return; + } + + if (imgBuffer.format != (int)kRecordingFormat) { + ALOGE("%s: Camera %d: Unexpected recording format: %x", + __FUNCTION__, mCameraId, imgBuffer.format); + mRecordingConsumer->unlockBuffer(imgBuffer); + return; + } + size_t bufferSize = imgBuffer.width * imgBuffer.height * 3 / 2; + + if (mRecordingHeap == 0 || + bufferSize > + mRecordingHeap->mHeap->getSize() / kRecordingHeapCount) { + ALOGV("%s: Camera %d: Creating recording heap with %d buffers of " + "size %d bytes", __FUNCTION__, mCameraId, + kRecordingHeapCount, bufferSize); + if (mRecordingHeap != 0) { + ALOGV("%s: Camera %d: Previous heap has size %d " + "(new will be %d) bytes", __FUNCTION__, mCameraId, + mRecordingHeap->mHeap->getSize(), + bufferSize * kRecordingHeapCount); + } + // Need to allocate memory for heap + mRecordingHeap.clear(); + + mRecordingHeap = new Camera2Heap(bufferSize, kRecordingHeapCount, + "Camera2Client::RecordingHeap"); + if (mRecordingHeap->mHeap->getSize() == 0) { + ALOGE("%s: Camera %d: Unable to allocate memory for recording", + __FUNCTION__, mCameraId); + mRecordingConsumer->unlockBuffer(imgBuffer); + return; + } + mRecordingHeapHead = 0; + mRecordingHeapFree = kRecordingHeapCount; + } + + // TODO: Optimize this to avoid memcopy + if ( mRecordingHeapFree == 0) { + ALOGE("%s: Camera %d: No free recording buffers, dropping frame", + __FUNCTION__, mCameraId); + mRecordingConsumer->unlockBuffer(imgBuffer); + return; + } + heapIdx = mRecordingHeapHead; + timestamp = imgBuffer.timestamp; + mRecordingHeapHead = (mRecordingHeapHead + 1) % kRecordingHeapCount; + 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); + + memcpy((uint8_t*)heap->getBase() + offset, imgBuffer.data, size); + + mRecordingConsumer->unlockBuffer(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(); + Mutex::Autolock pl(mParamsLock); + + 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 + mParameters.previewWidth = availableProcessedSizes.data.i32[0]; + mParameters.previewHeight = availableProcessedSizes.data.i32[1]; + mParameters.videoWidth = mParameters.previewWidth; + mParameters.videoHeight = mParameters.previewHeight; + + params.setPreviewSize(mParameters.previewWidth, mParameters.previewHeight); + params.setVideoSize(mParameters.videoWidth, mParameters.videoHeight); + params.set(CameraParameters::KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO, + String8::format("%dx%d", + mParameters.previewWidth, 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; + + mParameters.previewFpsRange[0] = availableFpsRanges.data.i32[0]; + mParameters.previewFpsRange[1] = availableFpsRanges.data.i32[1]; + + params.set(CameraParameters::KEY_PREVIEW_FPS_RANGE, + String8::format("%d,%d", + mParameters.previewFpsRange[0], + 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); + } + + mParameters.previewFormat = HAL_PIXEL_FORMAT_YCrCb_420_SP; + params.set(CameraParameters::KEY_PREVIEW_FORMAT, + formatEnumToString(mParameters.previewFormat)); // NV21 + + 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, + 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 + mParameters.pictureWidth = availableJpegSizes.data.i32[0]; + mParameters.pictureHeight = availableJpegSizes.data.i32[1]; + + params.setPictureSize(mParameters.pictureWidth, + 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 + mParameters.jpegThumbSize[0] = availableJpegThumbnailSizes.data.i32[0]; + mParameters.jpegThumbSize[1] = availableJpegThumbnailSizes.data.i32[1]; + + params.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, + mParameters.jpegThumbSize[0]); + params.set(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, + 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); + } + + mParameters.jpegThumbQuality = 90; + params.set(CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY, + mParameters.jpegThumbQuality); + mParameters.jpegQuality = 90; + params.set(CameraParameters::KEY_JPEG_QUALITY, + mParameters.jpegQuality); + mParameters.jpegRotation = 0; + params.set(CameraParameters::KEY_ROTATION, + mParameters.jpegRotation); + + mParameters.gpsEnabled = false; + mParameters.gpsProcessingMethod = "unknown"; + // GPS fields in CameraParameters are not set by implementation + + 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); + } + + 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); + } + + 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); + } + + 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]) { + 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 { + 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 + 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 { + mParameters.focusMode = Parameters::FOCUS_MODE_AUTO; + params.set(CameraParameters::KEY_FOCUS_MODE, + CameraParameters::FOCUS_MODE_AUTO); + String8 supportedFocusModes(CameraParameters::FOCUS_MODE_FIXED); + supportedFocusModes = 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)"); + mParameters.focusingAreas.clear(); + 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); + + mParameters.exposureCompensation = 0; + params.set(CameraParameters::KEY_EXPOSURE_COMPENSATION, + 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, + exposureCompensationStep.data.r[0].numerator / + exposureCompensationStep.data.r[0].denominator); + + mParameters.autoExposureLock = false; + params.set(CameraParameters::KEY_AUTO_EXPOSURE_LOCK, + CameraParameters::FALSE); + params.set(CameraParameters::KEY_AUTO_EXPOSURE_LOCK_SUPPORTED, + CameraParameters::TRUE); + + mParameters.autoWhiteBalanceLock = false; + params.set(CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK, + CameraParameters::FALSE); + params.set(CameraParameters::KEY_AUTO_WHITEBALANCE_LOCK_SUPPORTED, + CameraParameters::TRUE); + + 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)"); + + mParameters.zoom = 0; + params.set(CameraParameters::KEY_ZOOM, 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, + formatEnumToString(kRecordingFormat)); + + 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); + } + + mParamsFlattened = params.flatten(); + + return OK; +} + +status_t Camera2Client::updatePreviewStream() { + 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)mParameters.previewWidth || + currentHeight != (uint32_t)mParameters.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, + mParameters.previewWidth, mParameters.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, + mParameters.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() { + 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); + 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() { + 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)mParameters.pictureWidth || + currentHeight != (uint32_t)mParameters.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, + mParameters.pictureWidth, mParameters.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() { + 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); + 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, + mParameters.jpegThumbSize, 2); + if (res != OK) return res; + res = updateEntry(mCaptureRequest, + ANDROID_JPEG_THUMBNAIL_QUALITY, + &mParameters.jpegThumbQuality, 1); + if (res != OK) return res; + res = updateEntry(mCaptureRequest, + ANDROID_JPEG_QUALITY, + &mParameters.jpegQuality, 1); + if (res != OK) return res; + res = updateEntry(mCaptureRequest, + ANDROID_JPEG_ORIENTATION, + &mParameters.jpegRotation, 1); + if (res != OK) return res; + + if (mParameters.gpsEnabled) { + res = updateEntry(mCaptureRequest, + ANDROID_JPEG_GPS_COORDINATES, + mParameters.gpsCoordinates, 3); + if (res != OK) return res; + res = updateEntry(mCaptureRequest, + ANDROID_JPEG_GPS_TIMESTAMP, + &mParameters.gpsTimestamp, 1); + if (res != OK) return res; + res = updateEntry(mCaptureRequest, + ANDROID_JPEG_GPS_PROCESSING_METHOD, + mParameters.gpsProcessingMethod.string(), + mParameters.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() { + 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); + 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() { + status_t res; + + if (mRecordingConsumer == 0) { + // Create CPU buffer queue endpoint + mRecordingConsumer = new CpuConsumer(1); + 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)mParameters.videoWidth || + currentHeight != (uint32_t)mParameters.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, + mParameters.videoWidth, mParameters.videoHeight, + kRecordingFormat, 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) { + ATRACE_CALL(); + status_t res; + res = updateEntry(request, + ANDROID_CONTROL_AE_TARGET_FPS_RANGE, mParameters.previewFpsRange, 2); + if (res != OK) return res; + + uint8_t wbMode = mParameters.autoWhiteBalanceLock ? + ANDROID_CONTROL_AWB_LOCKED : mParameters.wbMode; + res = updateEntry(request, + ANDROID_CONTROL_AWB_MODE, &wbMode, 1); + if (res != OK) return res; + res = updateEntry(request, + ANDROID_CONTROL_EFFECT_MODE, &mParameters.effectMode, 1); + if (res != OK) return res; + res = updateEntry(request, + ANDROID_CONTROL_AE_ANTIBANDING_MODE, + &mParameters.antibandingMode, 1); + if (res != OK) return res; + + uint8_t controlMode = + (mParameters.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, + &mParameters.sceneMode, 1); + if (res != OK) return res; + } + + uint8_t flashMode = ANDROID_FLASH_OFF; + uint8_t aeMode; + switch (mParameters.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, mParameters.flashMode); + return BAD_VALUE; + } + if (mParameters.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 (mParameters.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 = mParameters.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, mParameters.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 = mParameters.focusingAreas.size() * 5; + int32_t *focusingAreas = new int32_t[focusingAreasSize]; + for (size_t i = 0; i < focusingAreasSize; i += 5) { + focusingAreas[i + 0] = mParameters.focusingAreas[i].left; + focusingAreas[i + 1] = mParameters.focusingAreas[i].top; + focusingAreas[i + 2] = mParameters.focusingAreas[i].right; + focusingAreas[i + 3] = mParameters.focusingAreas[i].bottom; + focusingAreas[i + 4] = mParameters.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, + &mParameters.exposureCompensation, 1); + if (res != OK) return res; + + size_t meteringAreasSize = mParameters.meteringAreas.size() * 5; + int32_t *meteringAreas = new int32_t[meteringAreasSize]; + for (size_t i = 0; i < meteringAreasSize; i += 5) { + meteringAreas[i + 0] = mParameters.meteringAreas[i].left; + meteringAreas[i + 1] = mParameters.meteringAreas[i].top; + meteringAreas[i + 2] = mParameters.meteringAreas[i].right; + meteringAreas[i + 3] = mParameters.meteringAreas[i].bottom; + meteringAreas[i + 4] = mParameters.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 * mParameters.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 (mParameters.previewWidth >= mParameters.previewHeight) { + zoomWidth = arrayWidth / zoomRatio; + zoomHeight = zoomWidth * + mParameters.previewHeight / mParameters.previewWidth; + } else { + zoomHeight = arrayHeight / zoomRatio; + zoomWidth = zoomHeight * + mParameters.previewWidth / mParameters.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 = mParameters.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..8d410f1 --- /dev/null +++ b/services/camera/libcameraservice/Camera2Client.h @@ -0,0 +1,327 @@ +/* + * 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> + +namespace android { + +/** + * Implements the android.hardware.camera API on top of + * camera device HAL version 2. + */ +class Camera2Client : 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 + Camera2Client(const sp<CameraService>& cameraService, + const sp<ICameraClient>& cameraClient, + int cameraId, + int cameraFacing, + int clientPid); + ~Camera2Client(); + + status_t initialize(camera_module_t *module); + + virtual status_t dump(int fd, const Vector<String16>& args); + +private: + enum State { + NOT_INITIALIZED, + 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 + mutable Mutex mICameraLock; + + // The following must be called with mICamaeraLock already locked + + status_t setPreviewWindowLocked(const sp<IBinder>& binder, + sp<ANativeWindow> window); + + void stopPreviewLocked(); + status_t startPreviewLocked(); + + // Mutex that must be locked before accessing mParameters, mParamsFlattened + mutable Mutex mParamsLock; + String8 mParamsFlattened; + // 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; + } 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; + // Update preview request based on mParameters + status_t updatePreviewRequest(); + // Update preview stream based on mParameters + status_t updatePreviewStream(); + + /* 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(); + // Update capture request based on mParameters + status_t updateCaptureRequest(); + // Update capture stream based on mParameters + status_t updateCaptureStream(); + + /* Recording related members */ + + int mRecordingStreamId; + sp<CpuConsumer> mRecordingConsumer; + sp<ANativeWindow> mRecordingWindow; + // Simple listener that forwards frame available notifications from + // a CPU consumer to the recording notification + class RecordingWaiter: public CpuConsumer::FrameAvailableListener { + public: + RecordingWaiter(Camera2Client *parent) : mParent(parent) {} + void onFrameAvailable() { mParent->onRecordingFrameAvailable(); } + private: + Camera2Client *mParent; + }; + sp<RecordingWaiter> mRecordingWaiter; + camera_metadata_t *mRecordingRequest; + sp<Camera2Heap> mRecordingHeap; + + // TODO: This needs to be queried from somewhere, or the BufferQueue needs + // to be passed all the way to stagefright + static const size_t kRecordingHeapCount = 4; + static const uint32_t kRecordingFormat = HAL_PIXEL_FORMAT_YCrCb_420_SP; + size_t mRecordingHeapHead, mRecordingHeapFree; + // Handle new recording image buffers + void onRecordingFrameAvailable(); + // Update recording request based on mParameters + status_t updateRecordingRequest(); + // Update recording stream based on mParameters + status_t updateRecordingStream(); + + /** Camera2Device instance wrapping HAL2 entry */ + + sp<Camera2Device> mDevice; + + /** Utility members */ + + // 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); + + // 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..54dde80 --- /dev/null +++ b/services/camera/libcameraservice/Camera2Device.cpp @@ -0,0 +1,951 @@ +/* + * 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; + } + + return OK; +} + +status_t Camera2Device::dump(int fd, const Vector<String16>& args) { + + String8 result; + + result.appendFormat(" Camera2Device[%d] dump:\n", mId); + + result.appendFormat(" Static camera information metadata:\n"); + write(fd, result.string(), result.size()); + dump_camera_metadata(mDeviceInfo, fd, 2); + + 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() { + ALOGV("%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 + + 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; +} + +/** + * 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_camera_metadata(*r, fd, 2); + 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_camera_metadata(*r, fd, 2); + 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; + + 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; + } + + 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: + 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) return res; + + *buffer = &(anb->handle); + stream->mActiveBuffers++; + + ALOGVV("%s: Buffer %p", __FUNCTION__, *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("%s: Stream %d: Buffer %p captured at %lld ns", + __FUNCTION__, stream->mId, 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); + } + stream->mActiveBuffers--; + stream->mFrameCount++; + stream->mLastTimestamp = timestamp; + return err; +} + +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)); + if (stream->mState != ACTIVE) { + ALOGE("%s: Called when in bad state: %d", __FUNCTION__, stream->mState); + return INVALID_OPERATION; + } + stream->mActiveBuffers--; + ANativeWindow *a = toANW(w); + return a->cancelBuffer(a, + container_of(buffer, ANativeWindowBuffer, handle), -1); +} + +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..4ac63df --- /dev/null +++ b/services/camera/libcameraservice/Camera2Device.h @@ -0,0 +1,289 @@ +/* + * 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(); + + 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; + +}; // 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..80ccb43 --- /dev/null +++ b/services/camera/libcameraservice/CameraClient.cpp @@ -0,0 +1,958 @@ +/* + * 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; + + mCameraService->playSound(CameraService::SOUND_RECORDING); + disableMsgType(CAMERA_MSG_VIDEO_FRAME); + mHardware->stopRecording(); + + 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_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 bf07f8b..912ee4a 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; - - mCameraService->playSound(SOUND_RECORDING); - disableMsgType(CAMERA_MSG_VIDEO_FRAME); - mHardware->stopRecording(); - - 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,24 +417,18 @@ 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); - write(fd, result.string(), result.size()); + result.append("CameraService may be deadlocked\n"); } bool hasClient = false; @@ -1318,17 +436,10 @@ status_t CameraService::dump(int fd, const Vector<String16>& args) { sp<Client> client = mClient[i].promote(); if (client == 0) continue; hasClient = true; - sprintf(buffer, "Client[%d] (%p) PID: %d\n", - i, - client->getCameraClient()->asBinder().get(), - client->mClientPid); - result.append(buffer); - write(fd, result.string(), result.size()); - client->mHardware->dump(fd, args); + client->dump(fd, args); } if (!hasClient) { - result.append("No camera client yet.\n"); - write(fd, result.string(), result.size()); + result.append("No camera clients yet.\n"); } if (locked) mServiceLock.unlock(); @@ -1336,14 +447,15 @@ 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.appendFormat("Setting 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 |
