#if !defined(ANDROID_r2_2_2) && !defined(ANDROID_r2_3_3) && !defined(ANDROID_r3_0_1) #error unsupported version of Android #endif #include #include "camera_wrapper.h" #include "../include/camera_properties.h" #include //undef logging macro from /system/core/libcutils/loghack.h #ifdef LOGD #undef LOGD #endif #ifdef LOGI #undef LOGI #endif #ifdef LOGW #undef LOGW #endif #ifdef LOGE #undef LOGE #endif // LOGGING #include #define CAMERA_LOG_TAG "OpenCV_NativeCamera" #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, CAMERA_LOG_TAG, __VA_ARGS__)) #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, CAMERA_LOG_TAG, __VA_ARGS__)) #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, CAMERA_LOG_TAG, __VA_ARGS__)) #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, CAMERA_LOG_TAG, __VA_ARGS__)) using namespace android; void debugShowFPS() { static int mFrameCount = 0; static int mLastFrameCount = 0; static nsecs_t mLastFpsTime = systemTime(); static float mFps = 0; mFrameCount++; if (( mFrameCount % 30 ) != 0) return; nsecs_t now = systemTime(); nsecs_t diff = now - mLastFpsTime; if (diff==0) return; mFps = ((mFrameCount - mLastFrameCount) * float(s2ns(1))) / diff; mLastFpsTime = now; mLastFrameCount = mFrameCount; LOGI("### Camera FPS ### [%d] Frames, %.2f FPS", mFrameCount, mFps); } class CameraHandler: public CameraListener { protected: int cameraId; sp camera; CameraParameters params; CameraCallback cameraCallback; void* userData; int emptyCameraCallbackReported; void doCall(void* buffer, size_t bufferSize) { if (cameraCallback == 0) { if (!emptyCameraCallbackReported) LOGE("CameraHandler::doCall(void*, size_t): Camera callback is empty!"); emptyCameraCallbackReported++; } else { bool res = (*cameraCallback)(buffer, bufferSize, userData); if(!res) { LOGE("CameraHandler::doCall(void*, size_t): cameraCallback returns false (camera connection will be closed)"); closeCameraConnect(); } } } void doCall(const sp& dataPtr) { if (dataPtr == NULL) { LOGE("CameraHandler::doCall(const sp&): dataPtr==NULL (no frame to handle)"); return; } size_t size = dataPtr->size(); if (size <= 0) { LOGE("CameraHandler::doCall(const sp&): IMemory object is of zero size"); return; } void* buffer = (void *)dataPtr->pointer(); if (!buffer) { LOGE("CameraHandler::doCall(const sp&): Buffer pointer is NULL"); return; } doCall(buffer, size); } virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp& dataPtr) { static uint32_t count = 0; count++; LOGE("Recording cb: %d %lld %%p Offset:%%d Stride:%%d\n", msgType, timestamp); if (dataPtr == NULL) { LOGE("postDataTimestamp: dataPtr IS ZERO -- returning"); camera->releaseRecordingFrame(dataPtr); LOGE("postDataTimestamp: camera->releaseRecordingFrame(dataPtr) is done"); return; } uint8_t *ptr = (uint8_t*) dataPtr->pointer(); if (ptr) LOGE("VID_CB: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5], ptr[6], ptr[7], ptr[8], ptr[9]); else LOGE("postDataTimestamp: Ptr is zero"); camera->releaseRecordingFrame(dataPtr); } public: CameraHandler(CameraCallback callback = 0, void* _userData = 0): cameraId(0), cameraCallback(callback), userData(_userData), emptyCameraCallbackReported(0) { LOGD("Instantiated new CameraHandler (%p, %p)", callback, _userData); } virtual ~CameraHandler() { LOGD("CameraHandler destructor is called"); } virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2) { LOGE("CameraHandler::Notify: msgType=%d ext1=%d ext2=%d\n", msgType, ext1, ext2); #if 0 if ( msgType & CAMERA_MSG_FOCUS ) LOGE("CameraHandler::Notify AutoFocus %s in %llu us\n", (ext1) ? "OK" : "FAIL", timevalDelay(&autofocus_start)); if ( msgType & CAMERA_MSG_SHUTTER ) LOGE("CameraHandler::Notify Shutter done in %llu us\n", timeval_delay(&picture_start)); #endif } virtual void postData(int32_t msgType, const sp& dataPtr) { debugShowFPS(); if ( msgType & CAMERA_MSG_PREVIEW_FRAME ) { doCall(dataPtr); return; } if (msgType != CAMERA_MSG_PREVIEW_FRAME) LOGE("CameraHandler::postData Recieved message %d is not equal to CAMERA_MSG_PREVIEW_FRAME (%d)", (int) msgType, CAMERA_MSG_PREVIEW_FRAME); if ( msgType & CAMERA_MSG_RAW_IMAGE ) LOGE("CameraHandler::postData Unexpected data format: RAW\n"); if (msgType & CAMERA_MSG_POSTVIEW_FRAME) LOGE("CameraHandler::postData Unexpected data format: Postview frame\n"); if (msgType & CAMERA_MSG_COMPRESSED_IMAGE ) LOGE("CameraHandler::postData Unexpected data format: JPEG"); } static CameraHandler* initCameraConnect(const CameraCallback& callback, int cameraId, void* userData, CameraParameters* prevCameraParameters); void closeCameraConnect(); double getProperty(int propIdx); void setProperty(int propIdx, double value); static void applyProperties(CameraHandler** ppcameraHandler); std::string cameraPropertySupportedPreviewSizesString; std::string cameraPropertyPreviewFormatString; }; CameraHandler* CameraHandler::initCameraConnect(const CameraCallback& callback, int cameraId, void* userData, CameraParameters* prevCameraParameters) { LOGD("CameraHandler::initCameraConnect(%p, %d, %p, %p)", callback, cameraId, userData, prevCameraParameters); sp camera = 0; #ifdef ANDROID_r2_2_2 camera = Camera::connect(); #endif #ifdef ANDROID_r2_3_3 camera = Camera::connect(cameraId); #endif if ( 0 == camera.get() ) { LOGE("initCameraConnect: Unable to connect to CameraService\n"); return 0; } CameraHandler* handler = new CameraHandler(callback, userData); camera->setListener(handler); handler->camera = camera; handler->cameraId = cameraId; if (prevCameraParameters != 0) { LOGI("initCameraConnect: Setting paramers from previous camera handler"); camera->setParameters(prevCameraParameters->flatten()); handler->params.unflatten(prevCameraParameters->flatten()); } else { android::String8 params_str = camera->getParameters(); LOGI("initCameraConnect: [%s]", params_str.string()); handler->params.unflatten(params_str); LOGD("Supported Cameras: %s", handler->params.get("camera-indexes")); LOGD("Supported Picture Sizes: %s", handler->params.get(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES)); LOGD("Supported Picture Formats: %s", handler->params.get(CameraParameters::KEY_SUPPORTED_PICTURE_FORMATS)); LOGD("Supported Preview Sizes: %s", handler->params.get(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES)); LOGD("Supported Preview Formats: %s", handler->params.get(CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS)); LOGD("Supported Preview Frame Rates: %s", handler->params.get(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES)); LOGD("Supported Thumbnail Sizes: %s", handler->params.get(CameraParameters::KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES)); LOGD("Supported Whitebalance Modes: %s", handler->params.get(CameraParameters::KEY_SUPPORTED_WHITE_BALANCE)); LOGD("Supported Effects: %s", handler->params.get(CameraParameters::KEY_SUPPORTED_EFFECTS)); LOGD("Supported Scene Modes: %s", handler->params.get(CameraParameters::KEY_SUPPORTED_SCENE_MODES)); LOGD("Supported Focus Modes: %s", handler->params.get(CameraParameters::KEY_SUPPORTED_FOCUS_MODES)); LOGD("Supported Antibanding Options: %s", handler->params.get(CameraParameters::KEY_SUPPORTED_ANTIBANDING)); LOGD("Supported Flash Modes: %s", handler->params.get(CameraParameters::KEY_SUPPORTED_FLASH_MODES)); //check if yuv420sp format available. Set this format as preview format. const char* available_formats = handler->params.get(CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS); if (available_formats != 0) { const char* format_to_set = 0; const char* pos = available_formats; const char* ptr = pos; while(true) { while(*ptr != 0 && *ptr != ',') ++ptr; if (ptr != pos) { if (0 == strncmp(pos, "yuv420sp", ptr - pos)) { format_to_set = "yuv420sp"; break; } if (0 == strncmp(pos, "yuv420i", ptr - pos)) format_to_set = "yuv420i"; } if (*ptr == 0) break; pos = ++ptr; } if (0 != format_to_set) { handler->params.setPreviewFormat(format_to_set); status_t resParams = handler->camera->setParameters(handler->params.flatten()); if (resParams != 0) LOGE("initCameraConnect: failed to set preview format to %s", format_to_set); else LOGD("initCameraConnect: preview format is set to %s", format_to_set); } } } #ifdef ANDROID_r2_2_2 status_t pdstatus = camera->setPreviewDisplay(sp(0 /*new DummySurface*/)); if (pdstatus != 0) LOGE("initCameraConnect: failed setPreviewDisplay(0) call; camera migth not work correcttly on some devices"); #endif ////ATTENTION: switching between two versions: with and without copying memory inside Android OS //// see the method CameraService::Client::copyFrameAndPostCopiedFrame and where it is used #if 1 camera->setPreviewCallbackFlags( FRAME_CALLBACK_FLAG_ENABLE_MASK | FRAME_CALLBACK_FLAG_COPY_OUT_MASK);//with copy #else camera->setPreviewCallbackFlags( FRAME_CALLBACK_FLAG_ENABLE_MASK );//without copy #endif status_t resStart = camera->startPreview(); if (resStart != 0) { LOGE("initCameraConnect: startPreview() fails. Closing camera connection..."); handler->closeCameraConnect(); handler = 0; } return handler; } void CameraHandler::closeCameraConnect() { if (camera == NULL) { LOGI("... camera is already NULL"); return; } camera->stopPreview(); camera->disconnect(); camera.clear(); camera=NULL; // ATTENTION!!!!!!!!!!!!!!!!!!!!!!!!!! // When we set // camera=NULL // above, the pointed instance of android::Camera object is destructed, // since this member `camera' has type android::sp (android smart pointer template class), // and this is the only pointer to it. // // BUT this instance of CameraHandler is set as a listener for that android::Camera object // (see the function CameraHandler::initCameraConnect above), // so this instance of CameraHandler is pointed from that android::Camera object as // sp mListener // and there is no other android smart pointers to this. // // It means, when that instance of the android::Camera object is destructed, // it calls destructor for this CameraHandler instance too. // // So, this line `camera=NULL' causes to the call `delete this' // (see destructor of the template class android::sp) // // So, we must not call `delete this' after the line, since it just has been called indeed } double CameraHandler::getProperty(int propIdx) { LOGD("CameraHandler::getProperty(%d)", propIdx); switch (propIdx) { case ANDROID_CAMERA_PROPERTY_FRAMEWIDTH: { int w,h; params.getPreviewSize(&w, &h); return w; } case ANDROID_CAMERA_PROPERTY_FRAMEHEIGHT: { int w,h; params.getPreviewSize(&w, &h); return h; } case ANDROID_CAMERA_PROPERTY_SUPPORTED_PREVIEW_SIZES_STRING: { cameraPropertySupportedPreviewSizesString = params.get(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES); union {const char* str;double res;} u; memset(&u.res, 0, sizeof(u.res)); u.str = cameraPropertySupportedPreviewSizesString.c_str(); return u.res; } case ANDROID_CAMERA_PROPERTY_PREVIEW_FORMAT_STRING: { const char* fmt = params.get(CameraParameters::KEY_PREVIEW_FORMAT); if (fmt == CameraParameters::PIXEL_FORMAT_YUV422SP) fmt = "yuv422sp"; else if (fmt == CameraParameters::PIXEL_FORMAT_YUV420SP) fmt = "yuv420sp"; else if (fmt == CameraParameters::PIXEL_FORMAT_YUV422I) fmt = "yuv422i"; else if (fmt == CameraParameters::PIXEL_FORMAT_RGB565) fmt = "rgb565"; else if (fmt == CameraParameters::PIXEL_FORMAT_JPEG) fmt = "jpeg"; cameraPropertyPreviewFormatString = fmt; union {const char* str;double res;} u; memset(&u.res, 0, sizeof(u.res)); u.str = cameraPropertySupportedPreviewSizesString.c_str(); return u.res; } }; return -1; } void CameraHandler::setProperty(int propIdx, double value) { LOGD("CameraHandler::setProperty(%d, %f)", propIdx, value); switch (propIdx) { case ANDROID_CAMERA_PROPERTY_FRAMEWIDTH: { int w,h; params.getPreviewSize(&w, &h); w = (int)value; params.setPreviewSize(w, h); } break; case ANDROID_CAMERA_PROPERTY_FRAMEHEIGHT: { int w,h; params.getPreviewSize(&w, &h); h = (int)value; params.setPreviewSize(w, h); } break; }; } void CameraHandler::applyProperties(CameraHandler** ppcameraHandler) { LOGD("CameraHandler::applyProperties()"); if (ppcameraHandler == 0) { LOGE("applyProperties: Passed NULL ppcameraHandler"); return; } if (*ppcameraHandler == 0) { LOGE("applyProperties: Passed null *ppcameraHandler"); return; } LOGD("CameraHandler::applyProperties()"); CameraHandler* previousCameraHandler=*ppcameraHandler; CameraParameters curCameraParameters(previousCameraHandler->params.flatten()); CameraCallback cameraCallback=previousCameraHandler->cameraCallback; void* userData=previousCameraHandler->userData; int cameraId=previousCameraHandler->cameraId; LOGD("CameraHandler::applyProperties(): before previousCameraHandler->closeCameraConnect"); previousCameraHandler->closeCameraConnect(); LOGD("CameraHandler::applyProperties(): after previousCameraHandler->closeCameraConnect"); LOGD("CameraHandler::applyProperties(): before initCameraConnect"); CameraHandler* handler=initCameraConnect(cameraCallback, cameraId, userData, &curCameraParameters); LOGD("CameraHandler::applyProperties(): after initCameraConnect, handler=0x%x", (int)handler); if (handler == NULL) { LOGE("ERROR in applyProperties --- cannot reinit camera"); handler=initCameraConnect(cameraCallback, cameraId, userData, NULL); LOGD("CameraHandler::applyProperties(): repeate initCameraConnect after ERROR, handler=0x%x", (int)handler); if (handler == NULL) { LOGE("ERROR in applyProperties --- cannot reinit camera AGAIN --- cannot do anything else"); } } (*ppcameraHandler)=handler; } extern "C" { void* initCameraConnectC(void* callback, int cameraId, void* userData) { return CameraHandler::initCameraConnect((CameraCallback)callback, cameraId, userData, NULL); } void closeCameraConnectC(void** camera) { CameraHandler** cc = (CameraHandler**)camera; (*cc)->closeCameraConnect(); *cc = 0; } double getCameraPropertyC(void* camera, int propIdx) { return ((CameraHandler*)camera)->getProperty(propIdx); } void setCameraPropertyC(void* camera, int propIdx, double value) { ((CameraHandler*)camera)->setProperty(propIdx,value); } void applyCameraPropertiesC(void** camera) { CameraHandler::applyProperties((CameraHandler**)camera); } }