diff options
author | Mathias Agopian <mathias@google.com> | 2010-07-14 17:59:35 -0700 |
---|---|---|
committer | Mathias Agopian <mathias@google.com> | 2010-07-14 17:59:35 -0700 |
commit | 08e83bb3b7cc41f603867acbeb1168019cf535fe (patch) | |
tree | 79594e01ef6c3306b890cb944d6f15d3098075c4 /services/camera | |
parent | c9a11088e503b9e3ae52a3f671b2d21f5cd54f06 (diff) | |
download | frameworks_base-08e83bb3b7cc41f603867acbeb1168019cf535fe.zip frameworks_base-08e83bb3b7cc41f603867acbeb1168019cf535fe.tar.gz frameworks_base-08e83bb3b7cc41f603867acbeb1168019cf535fe.tar.bz2 |
move native services under services/
moved surfaceflinger, audioflinger, cameraservice
all native services should now reside in this location.
Change-Id: Iee42b83dd2a94c3bf5107ab0895fe2dfcd5337a8
Diffstat (limited to 'services/camera')
-rw-r--r-- | services/camera/libcameraservice/Android.mk | 66 | ||||
-rw-r--r-- | services/camera/libcameraservice/CameraHardwareStub.cpp | 410 | ||||
-rw-r--r-- | services/camera/libcameraservice/CameraHardwareStub.h | 133 | ||||
-rw-r--r-- | services/camera/libcameraservice/CameraService.cpp | 1273 | ||||
-rw-r--r-- | services/camera/libcameraservice/CameraService.h | 194 | ||||
-rw-r--r-- | services/camera/libcameraservice/CannedJpeg.h | 734 | ||||
-rw-r--r-- | services/camera/libcameraservice/FakeCamera.cpp | 433 | ||||
-rw-r--r-- | services/camera/libcameraservice/FakeCamera.h | 67 | ||||
-rw-r--r-- | services/camera/tests/CameraServiceTest/Android.mk | 26 | ||||
-rw-r--r-- | services/camera/tests/CameraServiceTest/CameraServiceTest.cpp | 919 |
10 files changed, 4255 insertions, 0 deletions
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk new file mode 100644 index 0000000..87975af --- /dev/null +++ b/services/camera/libcameraservice/Android.mk @@ -0,0 +1,66 @@ +LOCAL_PATH:= $(call my-dir) + +# Set USE_CAMERA_STUB if you don't want to use the hardware camera. + +# force these builds to use camera stub only +ifneq ($(filter sooner generic sim,$(TARGET_DEVICE)),) + USE_CAMERA_STUB:=true +endif + +ifeq ($(USE_CAMERA_STUB),) + USE_CAMERA_STUB:=false +endif + +ifeq ($(USE_CAMERA_STUB),true) +# +# libcamerastub +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + CameraHardwareStub.cpp \ + FakeCamera.cpp + +LOCAL_MODULE:= libcamerastub + +ifeq ($(TARGET_SIMULATOR),true) +LOCAL_CFLAGS += -DSINGLE_PROCESS +endif + +LOCAL_SHARED_LIBRARIES:= libui + +include $(BUILD_STATIC_LIBRARY) +endif # USE_CAMERA_STUB + +# +# libcameraservice +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + CameraService.cpp + +LOCAL_SHARED_LIBRARIES:= \ + libui \ + libutils \ + libbinder \ + libcutils \ + libmedia \ + libcamera_client \ + libsurfaceflinger_client + +LOCAL_MODULE:= libcameraservice + +ifeq ($(TARGET_SIMULATOR),true) +LOCAL_CFLAGS += -DSINGLE_PROCESS +endif + +ifeq ($(USE_CAMERA_STUB), true) +LOCAL_STATIC_LIBRARIES += libcamerastub +else +LOCAL_SHARED_LIBRARIES += libcamera +endif + +include $(BUILD_SHARED_LIBRARY) diff --git a/services/camera/libcameraservice/CameraHardwareStub.cpp b/services/camera/libcameraservice/CameraHardwareStub.cpp new file mode 100644 index 0000000..b3e0ee6 --- /dev/null +++ b/services/camera/libcameraservice/CameraHardwareStub.cpp @@ -0,0 +1,410 @@ +/* +** +** 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) { + LOGE("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); + LOGD("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 +} + +sp<IMemoryHeap> CameraHardwareStub::getPreviewHeap() const +{ + return mPreviewHeap; +} + +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); + + //LOGV("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, 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, 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, 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) { + LOGE("Only yuv420sp preview is supported"); + return -1; + } + + if (strcmp(params.getPictureFormat(), + CameraParameters::PIXEL_FORMAT_JPEG) != 0) { + LOGE("Only jpeg still pictures are supported"); + return -1; + } + + int w, h; + params.getPictureSize(&w, &h); + if (w != kCannedJpegWidth && h != kCannedJpegHeight) { + LOGE("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 new file mode 100644 index 0000000..d3427ba --- /dev/null +++ b/services/camera/libcameraservice/CameraHardwareStub.h @@ -0,0 +1,133 @@ +/* +** +** 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 sp<IMemoryHeap> getPreviewHeap() const; + 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) : +#ifdef SINGLE_PROCESS + // In single process mode this thread needs to be a java thread, + // since we won't be calling through the binder. + Thread(true), +#else + Thread(false), +#endif + 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 new file mode 100644 index 0000000..10668a4 --- /dev/null +++ b/services/camera/libcameraservice/CameraService.cpp @@ -0,0 +1,1273 @@ +/* +** +** Copyright (C) 2008, The Android Open Source Project +** Copyright (C) 2008 HTC Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "CameraService" + +#include <stdio.h> +#include <sys/types.h> +#include <pthread.h> + +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/MemoryBase.h> +#include <binder/MemoryHeapBase.h> +#include <cutils/atomic.h> +#include <hardware/hardware.h> +#include <media/AudioSystem.h> +#include <media/mediaplayer.h> +#include <surfaceflinger/ISurface.h> +#include <ui/Overlay.h> +#include <utils/Errors.h> +#include <utils/Log.h> +#include <utils/String16.h> + +#include "CameraService.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; + +#define LOG1(...) LOGD_IF(gLogLevel >= 1, __VA_ARGS__); +#define LOG2(...) LOGD_IF(gLogLevel >= 2, __VA_ARGS__); + +static void setLogLevel(int level) { + android_atomic_write(level, &gLogLevel); +} + +// ---------------------------------------------------------------------------- + +static int getCallingPid() { + return IPCThreadState::self()->getCallingPid(); +} + +static int getCallingUid() { + return IPCThreadState::self()->getCallingUid(); +} + +// ---------------------------------------------------------------------------- + +// This is ugly and only safe if we never re-create the CameraService, but +// should be ok for now. +static CameraService *gCameraService; + +CameraService::CameraService() +:mSoundRef(0) +{ + LOGI("CameraService started (pid=%d)", getpid()); + + mNumberOfCameras = HAL_getNumberOfCameras(); + if (mNumberOfCameras > MAX_CAMERAS) { + LOGE("Number of cameras(%d) > MAX_CAMERAS(%d).", + mNumberOfCameras, MAX_CAMERAS); + mNumberOfCameras = MAX_CAMERAS; + } + + for (int i = 0; i < mNumberOfCameras; i++) { + setCameraFree(i); + } + + gCameraService = this; +} + +CameraService::~CameraService() { + for (int i = 0; i < mNumberOfCameras; i++) { + if (mBusy[i]) { + LOGE("camera %d is still in use in destructor!", i); + } + } + + gCameraService = NULL; +} + +int32_t CameraService::getNumberOfCameras() { + return mNumberOfCameras; +} + +status_t CameraService::getCameraInfo(int cameraId, + struct CameraInfo* cameraInfo) { + if (cameraId < 0 || cameraId >= mNumberOfCameras) { + return BAD_VALUE; + } + + HAL_getCameraInfo(cameraId, cameraInfo); + return OK; +} + +sp<ICamera> CameraService::connect( + const sp<ICameraClient>& cameraClient, int cameraId) { + int callingPid = getCallingPid(); + LOG1("CameraService::connect E (pid %d, id %d)", callingPid, cameraId); + + sp<Client> client; + if (cameraId < 0 || cameraId >= mNumberOfCameras) { + LOGE("CameraService::connect X (pid %d) rejected (invalid cameraId %d).", + callingPid, cameraId); + return NULL; + } + + Mutex::Autolock lock(mServiceLock); + if (mClient[cameraId] != 0) { + client = mClient[cameraId].promote(); + if (client != 0) { + if (cameraClient->asBinder() == client->getCameraClient()->asBinder()) { + LOG1("CameraService::connect X (pid %d) (the same client)", + callingPid); + return client; + } else { + LOGW("CameraService::connect X (pid %d) rejected (existing client).", + callingPid); + return NULL; + } + } + mClient[cameraId].clear(); + } + + if (mBusy[cameraId]) { + LOGW("CameraService::connect X (pid %d) rejected" + " (camera %d is still busy).", callingPid, cameraId); + return NULL; + } + + client = new Client(this, cameraClient, cameraId, callingPid); + mClient[cameraId] = client; + LOG1("CameraService::connect X"); + return client; +} + +void CameraService::removeClient(const sp<ICameraClient>& cameraClient) { + int callingPid = getCallingPid(); + LOG1("CameraService::removeClient E (pid %d)", callingPid); + + for (int i = 0; i < mNumberOfCameras; i++) { + // Declare this before the lock to make absolutely sure the + // destructor won't be called with the lock held. + sp<Client> client; + + Mutex::Autolock lock(mServiceLock); + + // This happens when we have already disconnected (or this is + // just another unused camera). + if (mClient[i] == 0) continue; + + // Promote mClient. It can fail if we are called from this path: + // Client::~Client() -> disconnect() -> removeClient(). + client = mClient[i].promote(); + + if (client == 0) { + mClient[i].clear(); + continue; + } + + if (cameraClient->asBinder() == client->getCameraClient()->asBinder()) { + // Found our camera, clear and leave. + LOG1("removeClient: clear camera %d", i); + mClient[i].clear(); + break; + } + } + + LOG1("CameraService::removeClient X (pid %d)", callingPid); +} + +sp<CameraService::Client> CameraService::getClientById(int cameraId) { + if (cameraId < 0 || cameraId >= mNumberOfCameras) return NULL; + return mClient[cameraId].promote(); +} + +void CameraService::instantiate() { + defaultServiceManager()->addService(String16("media.camera"), + new CameraService()); +} + +status_t CameraService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + // Permission checks + switch (code) { + case BnCameraService::CONNECT: + const int pid = getCallingPid(); + const int self_pid = getpid(); + if (pid != self_pid) { + // we're called from a different process, do the real check + if (!checkCallingPermission( + String16("android.permission.CAMERA"))) { + const int uid = getCallingUid(); + LOGE("Permission Denial: " + "can't use the camera pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + } + break; + } + + return BnCameraService::onTransact(code, data, reply, flags); +} + +// The reason we need this busy bit is a new CameraService::connect() request +// may come in while the previous Client's destructor has not been run or is +// still running. If the last strong reference of the previous Client is gone +// but the destructor has not been finished, we should not allow the new Client +// to be created because we need to wait for the previous Client to tear down +// the hardware first. +void CameraService::setCameraBusy(int cameraId) { + android_atomic_write(1, &mBusy[cameraId]); +} + +void CameraService::setCameraFree(int cameraId) { + android_atomic_write(0, &mBusy[cameraId]); +} + +// We share the media players for shutter and recording sound for all clients. +// A reference count is kept to determine when we will actually release the +// media players. + +static MediaPlayer* newMediaPlayer(const char *file) { + MediaPlayer* mp = new MediaPlayer(); + if (mp->setDataSource(file, NULL) == NO_ERROR) { + mp->setAudioStreamType(AudioSystem::ENFORCED_AUDIBLE); + mp->prepare(); + } else { + LOGE("Failed to load CameraService sounds: %s", file); + return NULL; + } + return mp; +} + +void CameraService::loadSound() { + Mutex::Autolock lock(mSoundLock); + LOG1("CameraService::loadSound ref=%d", mSoundRef); + if (mSoundRef++) return; + + mSoundPlayer[SOUND_SHUTTER] = newMediaPlayer("/system/media/audio/ui/camera_click.ogg"); + mSoundPlayer[SOUND_RECORDING] = newMediaPlayer("/system/media/audio/ui/VideoRecord.ogg"); +} + +void CameraService::releaseSound() { + Mutex::Autolock lock(mSoundLock); + LOG1("CameraService::releaseSound ref=%d", mSoundRef); + if (--mSoundRef) return; + + for (int i = 0; i < NUM_SOUNDS; i++) { + if (mSoundPlayer[i] != 0) { + mSoundPlayer[i]->disconnect(); + mSoundPlayer[i].clear(); + } + } +} + +void CameraService::playSound(sound_kind kind) { + LOG1("playSound(%d)", kind); + Mutex::Autolock lock(mSoundLock); + sp<MediaPlayer> player = mSoundPlayer[kind]; + if (player != 0) { + // do not play the sound if stream volume is 0 + // (typically because ringer mode is silent). + int index; + AudioSystem::getStreamVolumeIndex(AudioSystem::ENFORCED_AUDIBLE, &index); + if (index != 0) { + player->seekTo(0); + player->start(); + } + } +} + +// ---------------------------------------------------------------------------- + +CameraService::Client::Client(const sp<CameraService>& cameraService, + const sp<ICameraClient>& cameraClient, int cameraId, int clientPid) { + int callingPid = getCallingPid(); + LOG1("Client::Client E (pid %d)", callingPid); + + mCameraService = cameraService; + mCameraClient = cameraClient; + mCameraId = cameraId; + mClientPid = clientPid; + + mHardware = HAL_openCameraHardware(cameraId); + mUseOverlay = mHardware->useOverlay(); + mMsgEnabled = 0; + + mHardware->setCallbacks(notifyCallback, + dataCallback, + dataCallbackTimestamp, + (void *)cameraId); + + // Enable zoom, error, and focus messages by default + enableMsgType(CAMERA_MSG_ERROR | + CAMERA_MSG_ZOOM | + CAMERA_MSG_FOCUS); + mOverlayW = 0; + mOverlayH = 0; + + // Callback is disabled by default + mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP; + mOrientation = 0; + cameraService->setCameraBusy(cameraId); + cameraService->loadSound(); + LOG1("Client::Client X (pid %d)", callingPid); +} + +static void *unregister_surface(void *arg) { + ISurface *surface = (ISurface *)arg; + surface->unregisterBuffers(); + IPCThreadState::self()->flushCommands(); + return NULL; +} + +// tear down the client +CameraService::Client::~Client() { + int callingPid = getCallingPid(); + LOG1("Client::~Client E (pid %d, this %p)", callingPid, this); + + if (mSurface != 0 && !mUseOverlay) { + pthread_t thr; + // We unregister the buffers in a different thread because binder does + // not let us make sychronous transactions in a binder destructor (that + // is, upon our reaching a refcount of zero.) + pthread_create(&thr, + NULL, // attr + unregister_surface, + mSurface.get()); + pthread_join(thr, NULL); + } + + // 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; + + LOGW("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) { + LOGE("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 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 lock(mLock); + + // allow anyone to use camera (after they lock the camera) + status_t result = checkPid(); + if (result == NO_ERROR) { + 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 lock(mLock); + + if (mClientPid != 0 && checkPid() != NO_ERROR) { + LOGW("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 = FRAME_CALLBACK_FLAG_NOOP; + mClientPid = callingPid; + mCameraClient = client; + + LOG1("connect X (pid %d)", callingPid); + return NO_ERROR; +} + +void CameraService::Client::disconnect() { + int callingPid = getCallingPid(); + LOG1("disconnect E (pid %d)", callingPid); + Mutex::Autolock lock(mLock); + + if (checkPid() != NO_ERROR) { + LOGW("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 overlay resources. + if (mUseOverlay) { + mOverlayRef = 0; + } + mHardware.clear(); + + mCameraService->removeClient(mCameraClient); + mCameraService->setCameraFree(mCameraId); + + LOG1("disconnect X (pid %d)", callingPid); +} + +// ---------------------------------------------------------------------------- + +// set the ISurface that the preview will use +status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface) { + LOG1("setPreviewDisplay(%p) (pid %d)", surface.get(), getCallingPid()); + Mutex::Autolock lock(mLock); + status_t result = checkPidAndHardware(); + if (result != NO_ERROR) return result; + + result = NO_ERROR; + + // return if no change in surface. + // asBinder() is safe on NULL (returns NULL) + if (surface->asBinder() == mSurface->asBinder()) { + return result; + } + + if (mSurface != 0) { + LOG1("clearing old preview surface %p", mSurface.get()); + if (mUseOverlay) { + // Force the destruction of any previous overlay + sp<Overlay> dummy; + mHardware->setOverlay(dummy); + } else { + mSurface->unregisterBuffers(); + } + } + mSurface = surface; + mOverlayRef = 0; + // If preview has been already started, set overlay or register preview + // buffers now. + if (mHardware->previewEnabled()) { + if (mUseOverlay) { + result = setOverlay(); + } else if (mSurface != 0) { + result = registerPreviewBuffers(); + } + } + + return result; +} + +status_t CameraService::Client::registerPreviewBuffers() { + int w, h; + CameraParameters params(mHardware->getParameters()); + params.getPreviewSize(&w, &h); + + // FIXME: don't use a hardcoded format here. + ISurface::BufferHeap buffers(w, h, w, h, + HAL_PIXEL_FORMAT_YCrCb_420_SP, + mOrientation, + 0, + mHardware->getPreviewHeap()); + + status_t result = mSurface->registerBuffers(buffers); + if (result != NO_ERROR) { + LOGE("registerBuffers failed with status %d", result); + } + return result; +} + +status_t CameraService::Client::setOverlay() { + int w, h; + CameraParameters params(mHardware->getParameters()); + params.getPreviewSize(&w, &h); + + if (w != mOverlayW || h != mOverlayH) { + // Force the destruction of any previous overlay + sp<Overlay> dummy; + mHardware->setOverlay(dummy); + mOverlayRef = 0; + } + + status_t result = NO_ERROR; + if (mSurface == 0) { + result = mHardware->setOverlay(NULL); + } else { + if (mOverlayRef == 0) { + // FIXME: + // Surfaceflinger may hold onto the previous overlay reference for some + // time after we try to destroy it. retry a few times. In the future, we + // should make the destroy call block, or possibly specify that we can + // wait in the createOverlay call if the previous overlay is in the + // process of being destroyed. + for (int retry = 0; retry < 50; ++retry) { + mOverlayRef = mSurface->createOverlay(w, h, OVERLAY_FORMAT_DEFAULT, + mOrientation); + if (mOverlayRef != 0) break; + LOGW("Overlay create failed - retrying"); + usleep(20000); + } + if (mOverlayRef == 0) { + LOGE("Overlay Creation Failed!"); + return -EINVAL; + } + result = mHardware->setOverlay(new Overlay(mOverlayRef)); + } + } + if (result != NO_ERROR) { + LOGE("mHardware->setOverlay() failed with status %d\n", result); + return result; + } + + mOverlayW = w; + mOverlayH = h; + + return result; +} + +// 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 lock(mLock); + if (checkPidAndHardware() != NO_ERROR) return; + + mPreviewCallbackFlag = callback_flag; + + // If we don't use overlay, we always need the preview frame for display. + // If we do use overlay, we only need the preview frame if the user + // wants the data. + if (mUseOverlay) { + if(mPreviewCallbackFlag & 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 lock(mLock); + status_t result = checkPidAndHardware(); + if (result != NO_ERROR) return result; + + switch(mode) { + case CAMERA_PREVIEW_MODE: + if (mSurface == 0) { + LOG1("mSurface is not set yet."); + // still able to start preview in this case. + } + return startPreviewMode(); + case CAMERA_RECORDING_MODE: + if (mSurface == 0) { + LOGE("mSurface 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 (mUseOverlay) { + // If preview display has been set, set overlay now. + if (mSurface != 0) { + result = setOverlay(); + } + if (result != NO_ERROR) return result; + result = mHardware->startPreview(); + } else { + enableMsgType(CAMERA_MSG_PREVIEW_FRAME); + result = mHardware->startPreview(); + if (result != NO_ERROR) return result; + // If preview display has been set, register preview buffers now. + if (mSurface != 0) { + // Unregister here because the surface may be previously registered + // with the raw (snapshot) heap. + mSurface->unregisterBuffers(); + result = registerPreviewBuffers(); + } + } + 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) { + LOGE("mHardware->startRecording() failed with status %d", result); + } + return result; +} + +// stop preview mode +void CameraService::Client::stopPreview() { + LOG1("stopPreview (pid %d)", getCallingPid()); + Mutex::Autolock lock(mLock); + if (checkPidAndHardware() != NO_ERROR) return; + + disableMsgType(CAMERA_MSG_PREVIEW_FRAME); + mHardware->stopPreview(); + + if (mSurface != 0 && !mUseOverlay) { + mSurface->unregisterBuffers(); + } + + mPreviewBuffer.clear(); +} + +// stop recording mode +void CameraService::Client::stopRecording() { + LOG1("stopRecording (pid %d)", getCallingPid()); + 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 lock(mLock); + if (checkPidAndHardware() != NO_ERROR) return; + mHardware->releaseRecordingFrame(mem); +} + +bool CameraService::Client::previewEnabled() { + LOG1("previewEnabled (pid %d)", getCallingPid()); + + Mutex::Autolock lock(mLock); + if (checkPidAndHardware() != NO_ERROR) return false; + return mHardware->previewEnabled(); +} + +bool CameraService::Client::recordingEnabled() { + LOG1("recordingEnabled (pid %d)", getCallingPid()); + + 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 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 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() { + LOG1("takePicture (pid %d)", getCallingPid()); + + Mutex::Autolock lock(mLock); + status_t result = checkPidAndHardware(); + if (result != NO_ERROR) return result; + + enableMsgType(CAMERA_MSG_SHUTTER | + CAMERA_MSG_POSTVIEW_FRAME | + CAMERA_MSG_RAW_IMAGE | + CAMERA_MSG_COMPRESSED_IMAGE); + + 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 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 lock(mLock); + if (checkPidAndHardware() != NO_ERROR) return String8(); + + String8 params(mHardware->getParameters().flatten()); + LOG1("getParameters (pid %d) (%s)", getCallingPid(), params.string()); + return params; +} + +status_t CameraService::Client::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) { + LOG1("sendCommand (pid %d)", getCallingPid()); + Mutex::Autolock lock(mLock); + status_t result = checkPidAndHardware(); + if (result != NO_ERROR) return result; + + if (cmd == CAMERA_CMD_SET_DISPLAY_ORIENTATION) { + // The orientation cannot be set during preview. + if (mHardware->previewEnabled()) { + return INVALID_OPERATION; + } + switch (arg1) { + case 0: + mOrientation = ISurface::BufferHeap::ROT_0; + break; + case 90: + mOrientation = ISurface::BufferHeap::ROT_90; + break; + case 180: + mOrientation = ISurface::BufferHeap::ROT_180; + break; + case 270: + mOrientation = ISurface::BufferHeap::ROT_270; + break; + default: + return BAD_VALUE; + } + 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); + } + LOGW("lockIfMessageWanted(%d): dropped unwanted message", msgType); + return false; +} + +// ---------------------------------------------------------------------------- + +// Converts from a raw pointer to the client to a strong pointer during a +// hardware callback. This requires the callbacks only happen when the client +// is still alive. +sp<CameraService::Client> CameraService::Client::getClientFromCookie(void* user) { + sp<Client> client = gCameraService->getClientById((int) user); + + // This could happen if the Client is in the process of shutting down (the + // last strong reference is gone, but the destructor hasn't finished + // stopping the hardware). + if (client == 0) return NULL; + + // The checks below are not necessary and are for debugging only. + if (client->mCameraService.get() != gCameraService) { + LOGE("mismatch service!"); + return NULL; + } + + if (client->mHardware == 0) { + LOGE("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); + + sp<Client> client = getClientFromCookie(user); + if (client == 0) return; + if (!client->lockIfMessageWanted(msgType)) return; + + switch (msgType) { + case CAMERA_MSG_SHUTTER: + // ext1 is the dimension of the yuv picture. + client->handleShutter((image_rect_type *)ext1); + break; + default: + client->handleGenericNotify(msgType, ext1, ext2); + break; + } +} + +void CameraService::Client::dataCallback(int32_t msgType, + const sp<IMemory>& dataPtr, void* user) { + LOG2("dataCallback(%d)", msgType); + + sp<Client> client = getClientFromCookie(user); + if (client == 0) return; + if (!client->lockIfMessageWanted(msgType)) return; + + if (dataPtr == 0) { + LOGE("Null data returned in data callback"); + client->handleGenericNotify(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0); + return; + } + + switch (msgType) { + case CAMERA_MSG_PREVIEW_FRAME: + client->handlePreviewData(dataPtr); + 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); + break; + } +} + +void CameraService::Client::dataCallbackTimestamp(nsecs_t timestamp, + int32_t msgType, const sp<IMemory>& dataPtr, void* user) { + LOG2("dataCallbackTimestamp(%d)", msgType); + + sp<Client> client = getClientFromCookie(user); + if (client == 0) return; + if (!client->lockIfMessageWanted(msgType)) return; + + if (dataPtr == 0) { + LOGE("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 +// "size" is the width and height of yuv picture for registerBuffer. +// If it is NULL, use the picture size from parameters. +void CameraService::Client::handleShutter(image_rect_type *size) { + mCameraService->playSound(SOUND_SHUTTER); + + // Screen goes black after the buffer is unregistered. + if (mSurface != 0 && !mUseOverlay) { + mSurface->unregisterBuffers(); + } + + 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); + + // It takes some time before yuvPicture callback to be called. + // Register the buffer for raw image here to reduce latency. + if (mSurface != 0 && !mUseOverlay) { + int w, h; + CameraParameters params(mHardware->getParameters()); + if (size == NULL) { + params.getPictureSize(&w, &h); + } else { + w = size->width; + h = size->height; + w &= ~1; + h &= ~1; + LOG1("Snapshot image width=%d, height=%d", w, h); + } + // FIXME: don't use hardcoded format constants here + ISurface::BufferHeap buffers(w, h, w, h, + HAL_PIXEL_FORMAT_YCrCb_420_SP, mOrientation, 0, + mHardware->getRawHeap()); + + mSurface->registerBuffers(buffers); + IPCThreadState::self()->flushCommands(); + } + + mLock.unlock(); +} + +// preview callback - frame buffer update +void CameraService::Client::handlePreviewData(const sp<IMemory>& mem) { + ssize_t offset; + size_t size; + sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); + + if (!mUseOverlay) { + if (mSurface != 0) { + mSurface->postBuffer(offset); + } + } + + // local copy of the callback flags + int flags = mPreviewCallbackFlag; + + // is callback enabled? + if (!(flags & 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 & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK)) { + LOG2("Disable preview callback"); + mPreviewCallbackFlag &= ~(FRAME_CALLBACK_FLAG_ONE_SHOT_MASK | + FRAME_CALLBACK_FLAG_COPY_OUT_MASK | + FRAME_CALLBACK_FLAG_ENABLE_MASK); + if (mUseOverlay) { + disableMsgType(CAMERA_MSG_PREVIEW_FRAME); + } + } + + if (c != 0) { + // Is the received frame copied out or not? + if (flags & FRAME_CALLBACK_FLAG_COPY_OUT_MASK) { + LOG2("frame is copied"); + copyFrameAndPostCopiedFrame(c, heap, offset, size); + } else { + LOG2("frame is forwarded"); + mLock.unlock(); + c->dataCallback(CAMERA_MSG_PREVIEW_FRAME, mem); + } + } 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); + } +} + +// 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); + + // Put the YUV version of the snapshot in the preview display. + if (mSurface != 0 && !mUseOverlay) { + mSurface->postBuffer(offset); + } + + sp<ICameraClient> c = mCameraClient; + mLock.unlock(); + if (c != 0) { + c->dataCallback(CAMERA_MSG_RAW_IMAGE, mem); + } +} + +// 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); + } +} + + +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) { + sp<ICameraClient> c = mCameraClient; + mLock.unlock(); + if (c != 0) { + c->dataCallback(msgType, dataPtr); + } +} + +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( + const sp<ICameraClient>& client, const sp<IMemoryHeap>& heap, + size_t offset, size_t size) { + 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) { + LOGE("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) { + LOGE("failed to allocate space for frame callback"); + mLock.unlock(); + return; + } + + mLock.unlock(); + client->dataCallback(CAMERA_MSG_PREVIEW_FRAME, frame); +} + +// ---------------------------------------------------------------------------- + +static const int kDumpLockRetries = 50; +static const int kDumpLockSleep = 60000; + +static bool tryLock(Mutex& mutex) +{ + bool locked = false; + for (int i = 0; i < kDumpLockRetries; ++i) { + if (mutex.tryLock() == NO_ERROR) { + locked = true; + break; + } + usleep(kDumpLockSleep); + } + return locked; +} + +status_t 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: " + "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()); + } + + bool hasClient = false; + for (int i = 0; i < mNumberOfCameras; i++) { + 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); + } + if (!hasClient) { + result.append("No camera client yet.\n"); + write(fd, result.string(), result.size()); + } + + if (locked) mServiceLock.unlock(); + + // change logging level + int n = args.size(); + for (int i = 0; i + 1 < n; i++) { + if (args[i] == String16("-v")) { + String8 levelStr(args[i+1]); + int level = atoi(levelStr.string()); + sprintf(buffer, "Set Log Level to %d", level); + result.append(buffer); + setLogLevel(level); + } + } + } + return NO_ERROR; +} + +}; // namespace android diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h new file mode 100644 index 0000000..8193e77 --- /dev/null +++ b/services/camera/libcameraservice/CameraService.h @@ -0,0 +1,194 @@ +/* +** +** Copyright (C) 2008, The Android Open Source Project +** Copyright (C) 2008 HTC Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef ANDROID_SERVERS_CAMERA_CAMERASERVICE_H +#define ANDROID_SERVERS_CAMERA_CAMERASERVICE_H + +#include <camera/ICameraService.h> +#include <camera/CameraHardwareInterface.h> + +/* This needs to be increased if we can have more cameras */ +#define MAX_CAMERAS 2 + +namespace android { + +class MemoryHeapBase; +class MediaPlayer; + +class CameraService: public BnCameraService +{ + class Client; +public: + static void instantiate(); + + CameraService(); + virtual ~CameraService(); + + virtual int32_t getNumberOfCameras(); + virtual status_t getCameraInfo(int cameraId, + struct CameraInfo* cameraInfo); + virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId); + virtual void removeClient(const sp<ICameraClient>& cameraClient); + virtual sp<Client> getClientById(int cameraId); + + virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); + + enum sound_kind { + SOUND_SHUTTER = 0, + SOUND_RECORDING = 1, + NUM_SOUNDS + }; + + void loadSound(); + void playSound(sound_kind kind); + void releaseSound(); + +private: + Mutex mServiceLock; + wp<Client> mClient[MAX_CAMERAS]; // protected by mServiceLock + 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 + 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<ISurface>& surface); + virtual void setPreviewCallbackFlag(int flag); + 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 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, + int cameraId, + 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(); + status_t setOverlay(); + + // 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(); + + // 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, void* user); + static void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr, void* user); + // convert client from cookie + static sp<Client> getClientFromCookie(void* user); + // handlers for messages + void handleShutter(image_rect_type *size); + void handlePreviewData(const sp<IMemory>& mem); + 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); + void handleGenericDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr); + + void copyFrameAndPostCopiedFrame( + const sp<ICameraClient>& client, + const sp<IMemoryHeap>& heap, + size_t offset, size_t size); + + // these are initialized in the constructor. + sp<CameraService> mCameraService; // immutable after constructor + sp<ICameraClient> mCameraClient; + int mCameraId; // immutable after constructor + pid_t mClientPid; + sp<CameraHardwareInterface> mHardware; // cleared after disconnect() + bool mUseOverlay; // immutable after constructor + sp<OverlayRef> mOverlayRef; + int mOverlayW; + int mOverlayH; + int mPreviewCallbackFlag; + int mOrientation; + + // Ensures atomicity among the public methods + mutable Mutex mLock; + sp<ISurface> mSurface; + + // 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); + }; +}; + +} // namespace android + +#endif diff --git a/services/camera/libcameraservice/CannedJpeg.h b/services/camera/libcameraservice/CannedJpeg.h new file mode 100644 index 0000000..b6266fb --- /dev/null +++ b/services/camera/libcameraservice/CannedJpeg.h @@ -0,0 +1,734 @@ +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 new file mode 100644 index 0000000..f3a6a67 --- /dev/null +++ b/services/camera/libcameraservice/FakeCamera.cpp @@ -0,0 +1,433 @@ +/* +** +** 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 new file mode 100644 index 0000000..724de20 --- /dev/null +++ b/services/camera/libcameraservice/FakeCamera.h @@ -0,0 +1,67 @@ +/* +** +** Copyright 2008, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef ANDROID_HARDWARE_FAKECAMERA_H +#define ANDROID_HARDWARE_FAKECAMERA_H + +#include <sys/types.h> +#include <stdint.h> + +namespace android { + +/* + * FakeCamera is used in the CameraHardwareStub to provide a fake video feed + * when the system does not have a camera in hardware. + * The fake video is a moving black and white checkerboard background with a + * bouncing gray square in the foreground. + * This class is not thread-safe. + * + * TODO: Since the major methods provides a raw/uncompressed video feed, rename + * this class to RawVideoSource. + */ + +class FakeCamera { +public: + FakeCamera(int width, int height); + ~FakeCamera(); + + void setSize(int width, int height); + void getNextFrameAsYuv420(uint8_t *buffer); + // Write to the fd a string representing the current state. + void dump(int fd) const; + +private: + // TODO: remove the uint16_t buffer param everywhere since it is a field of + // this class. + void getNextFrameAsRgb565(uint16_t *buffer); + + void drawSquare(uint16_t *buffer, int x, int y, int size, int color, int shadow); + void drawCheckerboard(uint16_t *buffer, int size); + + static const int kRed = 0xf800; + static const int kGreen = 0x07c0; + static const int kBlue = 0x003e; + + int mWidth, mHeight; + int mCounter; + int mCheckX, mCheckY; + uint16_t *mTmpRgb16Buffer; +}; + +}; // namespace android + +#endif // ANDROID_HARDWARE_FAKECAMERA_H diff --git a/services/camera/tests/CameraServiceTest/Android.mk b/services/camera/tests/CameraServiceTest/Android.mk new file mode 100644 index 0000000..cf4e42f --- /dev/null +++ b/services/camera/tests/CameraServiceTest/Android.mk @@ -0,0 +1,26 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= CameraServiceTest.cpp + +LOCAL_MODULE:= CameraServiceTest + +LOCAL_MODULE_TAGS := tests + +LOCAL_C_INCLUDES += \ + frameworks/base/libs + +LOCAL_CFLAGS := + +LOCAL_SHARED_LIBRARIES += \ + libbinder \ + libcutils \ + libutils \ + libui \ + libcamera_client \ + libsurfaceflinger_client + +# Disable it because the ISurface interface may change, and before we have a +# chance to fix this test, we don't want to break normal builds. +#include $(BUILD_EXECUTABLE) diff --git a/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp b/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp new file mode 100644 index 0000000..3c8d553 --- /dev/null +++ b/services/camera/tests/CameraServiceTest/CameraServiceTest.cpp @@ -0,0 +1,919 @@ +#define LOG_TAG "CameraServiceTest" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <surfaceflinger/ISurface.h> +#include <camera/Camera.h> +#include <camera/CameraParameters.h> +#include <ui/GraphicBuffer.h> +#include <camera/ICamera.h> +#include <camera/ICameraClient.h> +#include <camera/ICameraService.h> +#include <ui/Overlay.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/ProcessState.h> +#include <utils/KeyedVector.h> +#include <utils/Log.h> +#include <utils/Vector.h> +#include <utils/threads.h> + +using namespace android; + +// +// Assertion and Logging utilities +// +#define INFO(...) \ + do { \ + printf(__VA_ARGS__); \ + printf("\n"); \ + LOGD(__VA_ARGS__); \ + } while(0) + +void assert_fail(const char *file, int line, const char *func, const char *expr) { + INFO("assertion failed at file %s, line %d, function %s:", + file, line, func); + INFO("%s", expr); + abort(); +} + +void assert_eq_fail(const char *file, int line, const char *func, + const char *expr, int actual) { + INFO("assertion failed at file %s, line %d, function %s:", + file, line, func); + INFO("(expected) %s != (actual) %d", expr, actual); + abort(); +} + +#define ASSERT(e) \ + do { \ + if (!(e)) \ + assert_fail(__FILE__, __LINE__, __func__, #e); \ + } while(0) + +#define ASSERT_EQ(expected, actual) \ + do { \ + int _x = (actual); \ + if (_x != (expected)) \ + assert_eq_fail(__FILE__, __LINE__, __func__, #expected, _x); \ + } while(0) + +// +// Holder service for pass objects between processes. +// +class IHolder : public IInterface { +protected: + enum { + HOLDER_PUT = IBinder::FIRST_CALL_TRANSACTION, + HOLDER_GET, + HOLDER_CLEAR + }; +public: + DECLARE_META_INTERFACE(Holder); + + virtual void put(sp<IBinder> obj) = 0; + virtual sp<IBinder> get() = 0; + virtual void clear() = 0; +}; + +class BnHolder : public BnInterface<IHolder> { + virtual status_t onTransact(uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +class BpHolder : public BpInterface<IHolder> { +public: + BpHolder(const sp<IBinder>& impl) + : BpInterface<IHolder>(impl) { + } + + virtual void put(sp<IBinder> obj) { + Parcel data, reply; + data.writeStrongBinder(obj); + remote()->transact(HOLDER_PUT, data, &reply, IBinder::FLAG_ONEWAY); + } + + virtual sp<IBinder> get() { + Parcel data, reply; + remote()->transact(HOLDER_GET, data, &reply); + return reply.readStrongBinder(); + } + + virtual void clear() { + Parcel data, reply; + remote()->transact(HOLDER_CLEAR, data, &reply); + } +}; + +IMPLEMENT_META_INTERFACE(Holder, "CameraServiceTest.Holder"); + +status_t BnHolder::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + switch(code) { + case HOLDER_PUT: { + put(data.readStrongBinder()); + return NO_ERROR; + } break; + case HOLDER_GET: { + reply->writeStrongBinder(get()); + return NO_ERROR; + } break; + case HOLDER_CLEAR: { + clear(); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +class HolderService : public BnHolder { + virtual void put(sp<IBinder> obj) { + mObj = obj; + } + virtual sp<IBinder> get() { + return mObj; + } + virtual void clear() { + mObj.clear(); + } +private: + sp<IBinder> mObj; +}; + +// +// A mock CameraClient +// +class MCameraClient : public BnCameraClient { +public: + virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2); + virtual void dataCallback(int32_t msgType, const sp<IMemory>& data); + virtual void dataCallbackTimestamp(nsecs_t timestamp, + int32_t msgType, const sp<IMemory>& data); + + // new functions + void clearStat(); + enum OP { EQ, GE, LE, GT, LT }; + void assertNotify(int32_t msgType, OP op, int count); + void assertData(int32_t msgType, OP op, int count); + void waitNotify(int32_t msgType, OP op, int count); + void waitData(int32_t msgType, OP op, int count); + void assertDataSize(int32_t msgType, OP op, int dataSize); + + void setReleaser(ICamera *releaser) { + mReleaser = releaser; + } +private: + Mutex mLock; + Condition mCond; + DefaultKeyedVector<int32_t, int> mNotifyCount; + DefaultKeyedVector<int32_t, int> mDataCount; + DefaultKeyedVector<int32_t, int> mDataSize; + bool test(OP op, int v1, int v2); + void assertTest(OP op, int v1, int v2); + + ICamera *mReleaser; +}; + +void MCameraClient::clearStat() { + Mutex::Autolock _l(mLock); + mNotifyCount.clear(); + mDataCount.clear(); + mDataSize.clear(); +} + +bool MCameraClient::test(OP op, int v1, int v2) { + switch (op) { + case EQ: return v1 == v2; + case GT: return v1 > v2; + case LT: return v1 < v2; + case GE: return v1 >= v2; + case LE: return v1 <= v2; + default: ASSERT(0); break; + } + return false; +} + +void MCameraClient::assertTest(OP op, int v1, int v2) { + if (!test(op, v1, v2)) { + LOGE("assertTest failed: op=%d, v1=%d, v2=%d", op, v1, v2); + ASSERT(0); + } +} + +void MCameraClient::assertNotify(int32_t msgType, OP op, int count) { + Mutex::Autolock _l(mLock); + int v = mNotifyCount.valueFor(msgType); + assertTest(op, v, count); +} + +void MCameraClient::assertData(int32_t msgType, OP op, int count) { + Mutex::Autolock _l(mLock); + int v = mDataCount.valueFor(msgType); + assertTest(op, v, count); +} + +void MCameraClient::assertDataSize(int32_t msgType, OP op, int dataSize) { + Mutex::Autolock _l(mLock); + int v = mDataSize.valueFor(msgType); + assertTest(op, v, dataSize); +} + +void MCameraClient::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) { + INFO("%s", __func__); + Mutex::Autolock _l(mLock); + ssize_t i = mNotifyCount.indexOfKey(msgType); + if (i < 0) { + mNotifyCount.add(msgType, 1); + } else { + ++mNotifyCount.editValueAt(i); + } + mCond.signal(); +} + +void MCameraClient::dataCallback(int32_t msgType, const sp<IMemory>& data) { + INFO("%s", __func__); + int dataSize = data->size(); + INFO("data type = %d, size = %d", msgType, dataSize); + Mutex::Autolock _l(mLock); + ssize_t i = mDataCount.indexOfKey(msgType); + if (i < 0) { + mDataCount.add(msgType, 1); + mDataSize.add(msgType, dataSize); + } else { + ++mDataCount.editValueAt(i); + mDataSize.editValueAt(i) = dataSize; + } + mCond.signal(); + + if (msgType == CAMERA_MSG_VIDEO_FRAME) { + ASSERT(mReleaser != NULL); + mReleaser->releaseRecordingFrame(data); + } +} + +void MCameraClient::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, + const sp<IMemory>& data) { + dataCallback(msgType, data); +} + +void MCameraClient::waitNotify(int32_t msgType, OP op, int count) { + INFO("waitNotify: %d, %d, %d", msgType, op, count); + Mutex::Autolock _l(mLock); + while (true) { + int v = mNotifyCount.valueFor(msgType); + if (test(op, v, count)) { + break; + } + mCond.wait(mLock); + } +} + +void MCameraClient::waitData(int32_t msgType, OP op, int count) { + INFO("waitData: %d, %d, %d", msgType, op, count); + Mutex::Autolock _l(mLock); + while (true) { + int v = mDataCount.valueFor(msgType); + if (test(op, v, count)) { + break; + } + mCond.wait(mLock); + } +} + +// +// A mock Surface +// +class MSurface : public BnSurface { +public: + virtual status_t registerBuffers(const BufferHeap& buffers); + virtual void postBuffer(ssize_t offset); + virtual void unregisterBuffers(); + virtual sp<OverlayRef> createOverlay( + uint32_t w, uint32_t h, int32_t format, int32_t orientation); + virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage); + virtual status_t setBufferCount(int bufferCount); + + // new functions + void clearStat(); + void waitUntil(int c0, int c1, int c2); + +private: + // check callback count + Condition mCond; + Mutex mLock; + int registerBuffersCount; + int postBufferCount; + int unregisterBuffersCount; +}; + +status_t MSurface::registerBuffers(const BufferHeap& buffers) { + INFO("%s", __func__); + Mutex::Autolock _l(mLock); + ++registerBuffersCount; + mCond.signal(); + return NO_ERROR; +} + +void MSurface::postBuffer(ssize_t offset) { + // INFO("%s", __func__); + Mutex::Autolock _l(mLock); + ++postBufferCount; + mCond.signal(); +} + +void MSurface::unregisterBuffers() { + INFO("%s", __func__); + Mutex::Autolock _l(mLock); + ++unregisterBuffersCount; + mCond.signal(); +} + +sp<GraphicBuffer> MSurface::requestBuffer(int bufferIdx, int usage) { + INFO("%s", __func__); + return NULL; +} + +status_t MSurface::setBufferCount(int bufferCount) { + INFO("%s", __func__); + return NULL; +} + +void MSurface::clearStat() { + Mutex::Autolock _l(mLock); + registerBuffersCount = 0; + postBufferCount = 0; + unregisterBuffersCount = 0; +} + +void MSurface::waitUntil(int c0, int c1, int c2) { + INFO("waitUntil: %d %d %d", c0, c1, c2); + Mutex::Autolock _l(mLock); + while (true) { + if (registerBuffersCount >= c0 && + postBufferCount >= c1 && + unregisterBuffersCount >= c2) { + break; + } + mCond.wait(mLock); + } +} + +sp<OverlayRef> MSurface::createOverlay(uint32_t w, uint32_t h, int32_t format, + int32_t orientation) { + // Not implemented. + ASSERT(0); + return NULL; +} + +// +// Utilities to use the Holder service +// +sp<IHolder> getHolder() { + sp<IServiceManager> sm = defaultServiceManager(); + ASSERT(sm != 0); + sp<IBinder> binder = sm->getService(String16("CameraServiceTest.Holder")); + ASSERT(binder != 0); + sp<IHolder> holder = interface_cast<IHolder>(binder); + ASSERT(holder != 0); + return holder; +} + +void putTempObject(sp<IBinder> obj) { + INFO("%s", __func__); + getHolder()->put(obj); +} + +sp<IBinder> getTempObject() { + INFO("%s", __func__); + return getHolder()->get(); +} + +void clearTempObject() { + INFO("%s", __func__); + getHolder()->clear(); +} + +// +// Get a Camera Service +// +sp<ICameraService> getCameraService() { + sp<IServiceManager> sm = defaultServiceManager(); + ASSERT(sm != 0); + sp<IBinder> binder = sm->getService(String16("media.camera")); + ASSERT(binder != 0); + sp<ICameraService> cs = interface_cast<ICameraService>(binder); + ASSERT(cs != 0); + return cs; +} + +int getNumberOfCameras() { + sp<ICameraService> cs = getCameraService(); + return cs->getNumberOfCameras(); +} + +// +// Various Connect Tests +// +void testConnect(int cameraId) { + INFO("%s", __func__); + sp<ICameraService> cs = getCameraService(); + sp<MCameraClient> cc = new MCameraClient(); + sp<ICamera> c = cs->connect(cc, cameraId); + ASSERT(c != 0); + c->disconnect(); +} + +void testAllowConnectOnceOnly(int cameraId) { + INFO("%s", __func__); + sp<ICameraService> cs = getCameraService(); + // Connect the first client. + sp<MCameraClient> cc = new MCameraClient(); + sp<ICamera> c = cs->connect(cc, cameraId); + ASSERT(c != 0); + // Same client -- ok. + ASSERT(cs->connect(cc, cameraId) != 0); + // Different client -- not ok. + sp<MCameraClient> cc2 = new MCameraClient(); + ASSERT(cs->connect(cc2, cameraId) == 0); + c->disconnect(); +} + +void testReconnectFailed() { + INFO("%s", __func__); + sp<ICamera> c = interface_cast<ICamera>(getTempObject()); + sp<MCameraClient> cc = new MCameraClient(); + ASSERT(c->connect(cc) != NO_ERROR); +} + +void testReconnectSuccess() { + INFO("%s", __func__); + sp<ICamera> c = interface_cast<ICamera>(getTempObject()); + sp<MCameraClient> cc = new MCameraClient(); + ASSERT(c->connect(cc) == NO_ERROR); + c->disconnect(); +} + +void testLockFailed() { + INFO("%s", __func__); + sp<ICamera> c = interface_cast<ICamera>(getTempObject()); + ASSERT(c->lock() != NO_ERROR); +} + +void testLockUnlockSuccess() { + INFO("%s", __func__); + sp<ICamera> c = interface_cast<ICamera>(getTempObject()); + ASSERT(c->lock() == NO_ERROR); + ASSERT(c->unlock() == NO_ERROR); +} + +void testLockSuccess() { + INFO("%s", __func__); + sp<ICamera> c = interface_cast<ICamera>(getTempObject()); + ASSERT(c->lock() == NO_ERROR); + c->disconnect(); +} + +// +// Run the connect tests in another process. +// +const char *gExecutable; + +struct FunctionTableEntry { + const char *name; + void (*func)(); +}; + +FunctionTableEntry function_table[] = { +#define ENTRY(x) {#x, &x} + ENTRY(testReconnectFailed), + ENTRY(testReconnectSuccess), + ENTRY(testLockUnlockSuccess), + ENTRY(testLockFailed), + ENTRY(testLockSuccess), +#undef ENTRY +}; + +void runFunction(const char *tag) { + INFO("runFunction: %s", tag); + int entries = sizeof(function_table) / sizeof(function_table[0]); + for (int i = 0; i < entries; i++) { + if (strcmp(function_table[i].name, tag) == 0) { + (*function_table[i].func)(); + return; + } + } + ASSERT(0); +} + +void runInAnotherProcess(const char *tag) { + pid_t pid = fork(); + if (pid == 0) { + execlp(gExecutable, gExecutable, tag, NULL); + ASSERT(0); + } else { + int status; + ASSERT_EQ(pid, wait(&status)); + ASSERT_EQ(0, status); + } +} + +void testReconnect(int cameraId) { + INFO("%s", __func__); + sp<ICameraService> cs = getCameraService(); + sp<MCameraClient> cc = new MCameraClient(); + sp<ICamera> c = cs->connect(cc, cameraId); + ASSERT(c != 0); + // Reconnect to the same client -- ok. + ASSERT(c->connect(cc) == NO_ERROR); + // Reconnect to a different client (but the same pid) -- ok. + sp<MCameraClient> cc2 = new MCameraClient(); + ASSERT(c->connect(cc2) == NO_ERROR); + c->disconnect(); + cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); +} + +void testLockUnlock(int cameraId) { + sp<ICameraService> cs = getCameraService(); + sp<MCameraClient> cc = new MCameraClient(); + sp<ICamera> c = cs->connect(cc, cameraId); + ASSERT(c != 0); + // We can lock as many times as we want. + ASSERT(c->lock() == NO_ERROR); + ASSERT(c->lock() == NO_ERROR); + // Lock from a different process -- not ok. + putTempObject(c->asBinder()); + runInAnotherProcess("testLockFailed"); + // Unlock then lock from a different process -- ok. + ASSERT(c->unlock() == NO_ERROR); + runInAnotherProcess("testLockUnlockSuccess"); + // Unlock then lock from a different process -- ok. + runInAnotherProcess("testLockSuccess"); + clearTempObject(); +} + +void testReconnectFromAnotherProcess(int cameraId) { + INFO("%s", __func__); + + sp<ICameraService> cs = getCameraService(); + sp<MCameraClient> cc = new MCameraClient(); + sp<ICamera> c = cs->connect(cc, cameraId); + ASSERT(c != 0); + // Reconnect from a different process -- not ok. + putTempObject(c->asBinder()); + runInAnotherProcess("testReconnectFailed"); + // Unlock then reconnect from a different process -- ok. + ASSERT(c->unlock() == NO_ERROR); + runInAnotherProcess("testReconnectSuccess"); + clearTempObject(); +} + +// We need to flush the command buffer after the reference +// to ICamera is gone. The sleep is for the server to run +// the destructor for it. +static void flushCommands() { + IPCThreadState::self()->flushCommands(); + usleep(200000); // 200ms +} + +// Run a test case +#define RUN(class_name, cameraId) do { \ + { \ + INFO(#class_name); \ + class_name instance; \ + instance.init(cameraId); \ + instance.run(); \ + } \ + flushCommands(); \ +} while(0) + +// Base test case after the the camera is connected. +class AfterConnect { +public: + void init(int cameraId) { + cs = getCameraService(); + cc = new MCameraClient(); + c = cs->connect(cc, cameraId); + ASSERT(c != 0); + } + +protected: + sp<ICameraService> cs; + sp<MCameraClient> cc; + sp<ICamera> c; + + ~AfterConnect() { + c->disconnect(); + c.clear(); + cc.clear(); + cs.clear(); + } +}; + +class TestSetPreviewDisplay : public AfterConnect { +public: + void run() { + sp<MSurface> surface = new MSurface(); + ASSERT(c->setPreviewDisplay(surface) == NO_ERROR); + c->disconnect(); + cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); + } +}; + +class TestStartPreview : public AfterConnect { +public: + void run() { + sp<MSurface> surface = new MSurface(); + ASSERT(c->setPreviewDisplay(surface) == NO_ERROR); + + ASSERT(c->startPreview() == NO_ERROR); + ASSERT(c->previewEnabled() == true); + + surface->waitUntil(1, 10, 0); // needs 1 registerBuffers and 10 postBuffer + surface->clearStat(); + + sp<MSurface> another_surface = new MSurface(); + c->setPreviewDisplay(another_surface); // just to make sure unregisterBuffers + // is called. + surface->waitUntil(0, 0, 1); // needs unregisterBuffers + + cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); + } +}; + +class TestStartPreviewWithoutDisplay : public AfterConnect { +public: + void run() { + ASSERT(c->startPreview() == NO_ERROR); + ASSERT(c->previewEnabled() == true); + c->disconnect(); + cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); + } +}; + +// Base test case after the the camera is connected and the preview is started. +class AfterStartPreview : public AfterConnect { +public: + void init(int cameraId) { + AfterConnect::init(cameraId); + surface = new MSurface(); + ASSERT(c->setPreviewDisplay(surface) == NO_ERROR); + ASSERT(c->startPreview() == NO_ERROR); + } + +protected: + sp<MSurface> surface; + + ~AfterStartPreview() { + surface.clear(); + } +}; + +class TestAutoFocus : public AfterStartPreview { +public: + void run() { + cc->assertNotify(CAMERA_MSG_FOCUS, MCameraClient::EQ, 0); + c->autoFocus(); + cc->waitNotify(CAMERA_MSG_FOCUS, MCameraClient::EQ, 1); + c->disconnect(); + cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); + } +}; + +class TestStopPreview : public AfterStartPreview { +public: + void run() { + ASSERT(c->previewEnabled() == true); + c->stopPreview(); + ASSERT(c->previewEnabled() == false); + c->disconnect(); + cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); + } +}; + +class TestTakePicture: public AfterStartPreview { +public: + void run() { + ASSERT(c->takePicture() == NO_ERROR); + cc->waitNotify(CAMERA_MSG_SHUTTER, MCameraClient::EQ, 1); + cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1); + cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1); + c->stopPreview(); + c->disconnect(); + cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); + } +}; + +class TestTakeMultiplePictures: public AfterStartPreview { +public: + void run() { + for (int i = 0; i < 10; i++) { + cc->clearStat(); + ASSERT(c->takePicture() == NO_ERROR); + cc->waitNotify(CAMERA_MSG_SHUTTER, MCameraClient::EQ, 1); + cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1); + cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1); + } + c->disconnect(); + cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); + } +}; + +class TestGetParameters: public AfterStartPreview { +public: + void run() { + String8 param_str = c->getParameters(); + INFO("%s", static_cast<const char*>(param_str)); + } +}; + +static bool getNextSize(const char **ptrS, int *w, int *h) { + const char *s = *ptrS; + + // skip over ',' + if (*s == ',') s++; + + // remember start position in p + const char *p = s; + while (*s != '\0' && *s != 'x') { + s++; + } + if (*s == '\0') return false; + + // get the width + *w = atoi(p); + + // skip over 'x' + ASSERT(*s == 'x'); + p = s + 1; + while (*s != '\0' && *s != ',') { + s++; + } + + // get the height + *h = atoi(p); + *ptrS = s; + return true; +} + +class TestPictureSize : public AfterStartPreview { +public: + void checkOnePicture(int w, int h) { + const float rate = 0.9; // byte per pixel limit + int pixels = w * h; + + CameraParameters param(c->getParameters()); + param.setPictureSize(w, h); + // disable thumbnail to get more accurate size. + param.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, 0); + param.set(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, 0); + c->setParameters(param.flatten()); + + cc->clearStat(); + ASSERT(c->takePicture() == NO_ERROR); + cc->waitData(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, 1); + //cc->assertDataSize(CAMERA_MSG_RAW_IMAGE, MCameraClient::EQ, pixels*3/2); + cc->waitData(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::EQ, 1); + cc->assertDataSize(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::LT, + int(pixels * rate)); + cc->assertDataSize(CAMERA_MSG_COMPRESSED_IMAGE, MCameraClient::GT, 0); + cc->assertNotify(CAMERA_MSG_ERROR, MCameraClient::EQ, 0); + } + + void run() { + CameraParameters param(c->getParameters()); + int w, h; + const char *s = param.get(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES); + while (getNextSize(&s, &w, &h)) { + LOGD("checking picture size %dx%d", w, h); + checkOnePicture(w, h); + } + } +}; + +class TestPreviewCallbackFlag : public AfterConnect { +public: + void run() { + sp<MSurface> surface = new MSurface(); + ASSERT(c->setPreviewDisplay(surface) == NO_ERROR); + + // Try all flag combinations. + for (int v = 0; v < 8; v++) { + LOGD("TestPreviewCallbackFlag: flag=%d", v); + usleep(100000); // sleep a while to clear the in-flight callbacks. + cc->clearStat(); + c->setPreviewCallbackFlag(v); + ASSERT(c->previewEnabled() == false); + ASSERT(c->startPreview() == NO_ERROR); + ASSERT(c->previewEnabled() == true); + sleep(2); + c->stopPreview(); + if ((v & FRAME_CALLBACK_FLAG_ENABLE_MASK) == 0) { + cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, 0); + } else { + if ((v & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK) == 0) { + cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::GE, 10); + } else { + cc->assertData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, 1); + } + } + } + } +}; + +class TestRecording : public AfterConnect { +public: + void run() { + ASSERT(c->recordingEnabled() == false); + sp<MSurface> surface = new MSurface(); + ASSERT(c->setPreviewDisplay(surface) == NO_ERROR); + c->setPreviewCallbackFlag(FRAME_CALLBACK_FLAG_ENABLE_MASK); + cc->setReleaser(c.get()); + c->startRecording(); + ASSERT(c->recordingEnabled() == true); + sleep(2); + c->stopRecording(); + usleep(100000); // sleep a while to clear the in-flight callbacks. + cc->setReleaser(NULL); + cc->assertData(CAMERA_MSG_VIDEO_FRAME, MCameraClient::GE, 10); + } +}; + +class TestPreviewSize : public AfterStartPreview { +public: + void checkOnePicture(int w, int h) { + int size = w*h*3/2; // should read from parameters + + c->stopPreview(); + + CameraParameters param(c->getParameters()); + param.setPreviewSize(w, h); + c->setPreviewCallbackFlag(FRAME_CALLBACK_FLAG_ENABLE_MASK); + c->setParameters(param.flatten()); + + c->startPreview(); + + cc->clearStat(); + cc->waitData(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::GE, 1); + cc->assertDataSize(CAMERA_MSG_PREVIEW_FRAME, MCameraClient::EQ, size); + } + + void run() { + CameraParameters param(c->getParameters()); + int w, h; + const char *s = param.get(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES); + while (getNextSize(&s, &w, &h)) { + LOGD("checking preview size %dx%d", w, h); + checkOnePicture(w, h); + } + } +}; + +void runHolderService() { + defaultServiceManager()->addService( + String16("CameraServiceTest.Holder"), new HolderService()); + ProcessState::self()->startThreadPool(); +} + +int main(int argc, char **argv) +{ + if (argc != 1) { + runFunction(argv[1]); + return 0; + } + INFO("CameraServiceTest start"); + gExecutable = argv[0]; + runHolderService(); + int n = getNumberOfCameras(); + INFO("%d Cameras available", n); + + for (int id = 0; id < n; id++) { + INFO("Testing camera %d", id); + testConnect(id); flushCommands(); + testAllowConnectOnceOnly(id); flushCommands(); + testReconnect(id); flushCommands(); + testLockUnlock(id); flushCommands(); + testReconnectFromAnotherProcess(id); flushCommands(); + + RUN(TestSetPreviewDisplay, id); + RUN(TestStartPreview, id); + RUN(TestStartPreviewWithoutDisplay, id); + RUN(TestAutoFocus, id); + RUN(TestStopPreview, id); + RUN(TestTakePicture, id); + RUN(TestTakeMultiplePictures, id); + RUN(TestGetParameters, id); + RUN(TestPictureSize, id); + RUN(TestPreviewCallbackFlag, id); + RUN(TestRecording, id); + RUN(TestPreviewSize, id); + } + + INFO("CameraServiceTest finished"); +} |