diff --git a/3rdparty/lib/libnative_camera_r2.2.2.so b/3rdparty/lib/libnative_camera_r2.2.2.so new file mode 100644 index 000000000..0866e4348 Binary files /dev/null and b/3rdparty/lib/libnative_camera_r2.2.2.so differ diff --git a/3rdparty/lib/libnative_camera_r2.3.3.so b/3rdparty/lib/libnative_camera_r2.3.3.so new file mode 100644 index 000000000..54e6c09b2 Binary files /dev/null and b/3rdparty/lib/libnative_camera_r2.3.3.so differ diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b2647bc8..282ece7f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -553,19 +553,6 @@ if(WITH_JASPER AND NOT JASPER_FOUND) set(JASPER_LIBRARIES libjasper) endif() -if (ANDROID) - #android camera support - set(NativeCamera_DIR "${OpenCV_SOURCE_DIR}/android/native-camera/build") - FIND_PACKAGE(NativeCamera QUIET) - if(NativeCamera_FOUND) - set(HAVE_ANDROID_NATIVE_CAMERA TRUE) - set(OPENCV_LINKER_LIBS ${OPENCV_LINKER_LIBS} ${NativeCamera_LIBS}) - else() - set(HAVE_ANDROID_NATIVE_CAMERA FALSE) - message(STATUS "Could NOT find NativeCamera for Android") - endif() -endif() - #message(STATUS "Graphic libraries: ${PNG_LIBRARIES} ${JPEG_LIBRARIES} ${TIFF_LIBRARIES} ${JASPER_LIBRARIES}") if(WITH_OPENEXR) @@ -667,7 +654,7 @@ endif() ############################### TBB ################################ if (WITH_TBB) - if (UNIX AND NOT APPLE) + if (UNIX AND NOT APPLE AND NOT ANDROID) PKG_CHECK_MODULES(TBB tbb) message(STATUS "TBB detected: ${TBBLIB_FOUND}") @@ -682,7 +669,7 @@ if (WITH_TBB) endif() if (NOT HAVE_TBB) - set(TBB_DEFAULT_INCLUDE_DIRS "/opt/intel/tbb" "/usr/local/include" "/usr/include" "C:/Program Files/Intel/TBB" "C:/Program Files (x86)/Intel/TBB" "C:/Program Files (x86)/TBB") + set(TBB_DEFAULT_INCLUDE_DIRS "/opt/intel/tbb" "/usr/local/include" "/usr/include" "C:/Program Files/Intel/TBB" "C:/Program Files (x86)/Intel/TBB" "C:/Program Files (x86)/TBB" "${CMAKE_INSTALL_PREFIX}/include") find_path(TBB_INCLUDE_DIR "tbb/tbb.h" PATHS ${TBB_DEFAULT_INCLUDE_DIRS} DOC "The path to TBB headers") if (TBB_INCLUDE_DIR) @@ -692,6 +679,9 @@ if (WITH_TBB) endif() if (APPLE) set(OPENCV_LINKER_LIBS ${OPENCV_LINKER_LIBS} libtbb.dylib) + elseif (ANDROID) + set(OPENCV_LINKER_LIBS ${OPENCV_LINKER_LIBS} tbbmalloc tbb) + add_definitions(-DTBB_USE_GCC_BUILTINS) elseif (UNIX) set(OPENCV_LINKER_LIBS ${OPENCV_LINKER_LIBS} tbb) elseif (WIN32) @@ -1170,10 +1160,6 @@ set(CMAKE_BASE_INCLUDE_DIRS_CONFIGCMAKE "\"${CMAKE_CURRENT_SOURCE_DIR}\"") set(CMAKE_LIB_DIRS_CONFIGCMAKE "${LIBRARY_OUTPUT_PATH}") -if( HAVE_ANDROID_NATIVE_CAMERA ) - set(CMAKE_LIB_DIRS_CONFIGCMAKE ${CMAKE_LIB_DIRS_CONFIGCMAKE} ${NativeCamera_LIB_DIR}) -endif() - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/OpenCVConfig.cmake.in" "${CMAKE_BINARY_DIR}/OpenCVConfig.cmake" IMMEDIATE @ONLY) # -------------------------------------------------------------------------------------------- @@ -1467,7 +1453,7 @@ message(STATUS " V4L/V4L2: ${HAVE_CAMV4L}/${HAVE_CAMV4L2}") endif() message(STATUS " Xine: ${HAVE_XINE}") if(ANDROID) -message(STATUS " AndroidNativeCamera: ${HAVE_ANDROID_NATIVE_CAMERA}") +message(STATUS " AndroidNativeCamera: build") endif() endif() #if(UNIX AND NOT APPLE) diff --git a/OpenCVConfig.cmake.in b/OpenCVConfig.cmake.in index 5864419af..cac601b13 100644 --- a/OpenCVConfig.cmake.in +++ b/OpenCVConfig.cmake.in @@ -58,6 +58,9 @@ if(NOT ANDROID) else() #libraries order is very important because linker from Android NDK is one-pass linker set(OPENCV_LIB_COMPONENTS opencv_contrib opencv_calib3d opencv_objdetect opencv_features2d opencv_imgproc opencv_video opencv_highgui opencv_ml opencv_legacy opencv_flann opencv_core ) + IF (NOT @BUILD_SHARED_LIBS@) + set(OPENCV_LIB_COMPONENTS opencv_androidcamera ${OPENCV_LIB_COMPONENTS}) + ENDIF() endif() SET(OpenCV_LIBS "") @@ -114,6 +117,23 @@ IF (NOT @BUILD_SHARED_LIBS@) ENDIF(NOT @BUILD_SHARED_LIBS@) +# ====================================================== +# Android camera helper macro +# ====================================================== +IF (ANDROID) + macro( COPY_NATIVE_CAMERA_LIBS target ) + get_target_property(target_location ${target} LOCATION) + get_filename_component(target_location "${target_location}" PATH) + file(GLOB camera_wrappers "${OpenCV_LIB_DIR}/libnative_camera_r*.so") + foreach(wrapper ${camera_wrappers}) + ADD_CUSTOM_COMMAND( + TARGET ${target} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${wrapper}" "${target_location}" + ) + endforeach() + endmacro() +ENDIF(ANDROID) # ====================================================== # Version variables: diff --git a/android/android.toolchain.cmake b/android/android.toolchain.cmake index ba540e1d4..912c1bf8d 100644 --- a/android/android.toolchain.cmake +++ b/android/android.toolchain.cmake @@ -321,7 +321,7 @@ set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS}" CACHE STRING "c flags" ) #-L${LIBCPP_LINK_DIR} -lstdc++ -lsupc++ #Also, this is *required* to use the following linker flags that routes around #a CPU bug in some Cortex-A8 implementations: -set( LINKER_FLAGS "-Wl,--fix-cortex-a8 -L${STL_LIBRARIES_PATH} -lstdc++ -lsupc++ " ) +set( LINKER_FLAGS "-Wl,--fix-cortex-a8 -L${STL_LIBRARIES_PATH} -L${CMAKE_INSTALL_PREFIX}/lib -lstdc++ -lsupc++ " ) set( NO_UNDEFINED ON CACHE BOOL "Don't all undefined symbols" ) if( NO_UNDEFINED ) diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index 87bd90171..77bb7d1a8 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -3,6 +3,8 @@ if(ANDROID) if(ANDROID_API_LEVEL LESS 8) ADD_DEFINITIONS(-DGTEST_HAS_CLONE=0) endif() + + add_subdirectory(androidcamera) endif() add_subdirectory(calib3d) diff --git a/modules/androidcamera/CMakeLists.txt b/modules/androidcamera/CMakeLists.txt new file mode 100644 index 000000000..751bbecac --- /dev/null +++ b/modules/androidcamera/CMakeLists.txt @@ -0,0 +1,48 @@ +IF(NOT ANDROID) + MESSAGE( FATAL_ERROR "This project is for ANDROID only" ) +ENDIF() + +if (BUILD_ANDROID_CAMERA_WRAPPER) + add_subdirectory(camera_wrapper) +endif() + +project(opencv_androidcamera) + +INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/camera_wrapper ) +SET( LIBRARY_DEPS ${LIBRARY_DEPS} log dl ) + +SET( the_target opencv_androidcamera ) + +ADD_LIBRARY( ${the_target} STATIC src/camera_activity.cpp ) + +if (BUILD_SHARED_LIBS) + add_definitions(-DCVAPI_EXPORTS) +endif() + +TARGET_LINK_LIBRARIES( ${the_target} ${LIBRARY_DEPS} ) + +SET_TARGET_PROPERTIES(${the_target} PROPERTIES + DEBUG_POSTFIX "${OPENCV_DEBUG_POSTFIX}" + OUTPUT_NAME "${the_target}${OPENCV_DLLVERSION}" + DEFINE_SYMBOL "CVAPI_EXPORTS" + ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH} + RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH} + ) + +IF (NOT BUILD_SHARED_LIBS) + install(TARGETS ${the_target} + RUNTIME DESTINATION bin COMPONENT main + ARCHIVE DESTINATION lib COMPONENT main + LIBRARY DESTINATION lib COMPONENT main + ) +ENDIF() + +file(GLOB camera_wrappers "${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/lib/libnative_camera_r*.so") + +foreach(wrapper ${camera_wrappers}) + ADD_CUSTOM_COMMAND( + TARGET ${the_target} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${wrapper}" "${LIBRARY_OUTPUT_PATH}" + ) +endforeach() \ No newline at end of file diff --git a/modules/androidcamera/camera_wrapper/camera_wrapper.cpp b/modules/androidcamera/camera_wrapper/camera_wrapper.cpp new file mode 100644 index 000000000..2071d6e32 --- /dev/null +++ b/modules/androidcamera/camera_wrapper/camera_wrapper.cpp @@ -0,0 +1,398 @@ +#define USE_RECORDING_INSTEAD_PREVIEW 0 + +#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 "../camera_wrapper_connector/camera_properties.h" +#include + +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 ) { + 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("####### [%d] Frames, %f FPS", mFrameCount, mFps); + } +} + +class CameraHandler: public CameraListener +{ +protected: + sp camera; + CameraCallback cameraCallback; + CameraParameters params; + void* userData; + int cameraId; + + bool isEmptyCameraCallbackReported; + virtual void doCall(void* buffer, size_t bufferSize) + { + if (cameraCallback == 0) + { + if (!isEmptyCameraCallbackReported) + LOGE("Camera callback is empty!"); + + isEmptyCameraCallbackReported = true; + return; + } + + bool res = (*cameraCallback)(buffer, bufferSize, userData); + + if(!res) closeCameraConnect(); + } + + virtual void doCall(const sp& dataPtr) + { + LOGI("doCall started"); + + if (dataPtr == NULL) + { + LOGE("CameraBuffer: dataPtr==NULL"); + return; + } + + size_t size = dataPtr->size(); + if (size <= 0) + { + LOGE("CameraBuffer: IMemory object is of zero size"); + return; + } + + unsigned char* buffer = (unsigned char *)dataPtr->pointer(); + if (!buffer) + { + LOGE("CameraBuffer: Buffer pointer is invalid"); + return; + } + + doCall(buffer, size); + } + +public: + CameraHandler(CameraCallback callback = 0, void* _userData = 0):cameraCallback(callback), userData(_userData), cameraId(0), isEmptyCameraCallbackReported(false) {} + virtual ~CameraHandler() + { + LOGW("CameraHandler destructor is called!"); + } + + virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2) + { + LOGE("Notify cb: %d %d %d\n", msgType, ext1, ext2); +#if 0 + if ( msgType & CAMERA_MSG_FOCUS ) + LOGE("AutoFocus %s in %llu us\n", (ext1) ? "OK" : "FAIL", timevalDelay(&autofocus_start)); + + if ( msgType & CAMERA_MSG_SHUTTER ) + LOGE("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("Recieved not CAMERA_MSG_PREVIEW_FRAME message %d", (int) msgType); + + if ( msgType & CAMERA_MSG_RAW_IMAGE ) + LOGE("Unexpected data format: RAW\n"); + + if (msgType & CAMERA_MSG_POSTVIEW_FRAME) + LOGE("Unexpected data format: Postview frame\n"); + + if (msgType & CAMERA_MSG_COMPRESSED_IMAGE ) + LOGE("Unexpected data format: JPEG"); + } + + 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); + } + + 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; +}; + + +CameraHandler* CameraHandler::initCameraConnect(const CameraCallback& callback, int cameraId, void* userData, CameraParameters* prevCameraParameters) +{ +// if (camera != NULL) +// { +// LOGE("initCameraConnect: camera have been connected already"); +// return false; +// } + + sp camera = 0; + +#ifdef ANDROID_r2_2_2 + camera = Camera::connect(); +#endif +#ifdef ANDROID_r2_3_3 + camera = Camera::connect(cameraId); +#endif + + if ( NULL == 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 1 + //setting paramers from previous camera handler + if (prevCameraParameters != NULL) { + camera->setParameters(prevCameraParameters->flatten()); + } +#endif + handler->params.unflatten(camera->getParameters()); + + + 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)); + + + //TODO: check if yuv420i format available. Set this format as preview format. + +#if USE_RECORDING_INSTEAD_PREVIEW + status_t err = camera->setPreviewDisplay(sp(NULL /*new DummySurface1*/)); +#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 + +#if USE_RECORDING_INSTEAD_PREVIEW + status_t resStart = camera->startRecording(); +#else + status_t resStart = camera->startPreview(); +#endif + + if (resStart != 0) + { + handler->closeCameraConnect(); + handler = 0; + } + return handler; +} + +void CameraHandler::closeCameraConnect() +{ + if (camera == NULL) + { + LOGI("... camera is NULL"); + return; + } + + //TODO: ATTENTION! should we do it ALWAYS??? +#if USE_RECORDING_INSTEAD_PREVIEW + camera->stopRecording(); +#else + camera->stopPreview(); +#endif + + 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) +{ + 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); + double res; + memset(&res, 0, sizeof(res)); + (*( (void**)&res ))= (void*)( cameraPropertySupportedPreviewSizesString.c_str() ); + + return res; + } + + }; + return -1; +} + +void CameraHandler::setProperty(int propIdx, double 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()"); + 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); +} + +} diff --git a/modules/androidcamera/camera_wrapper/camera_wrapper.h b/modules/androidcamera/camera_wrapper/camera_wrapper.h new file mode 100644 index 000000000..f1ae6bda6 --- /dev/null +++ b/modules/androidcamera/camera_wrapper/camera_wrapper.h @@ -0,0 +1,24 @@ +enum CameraWrapperErrorCode { + ERROR_NATIVE_CAMERA_WRAPPER_NOERROR = 0, + ERROR_NATIVE_CAMERA_WRAPPER_CANNOT_FIND_CLASS = 1, + ERROR_NATIVE_CAMERA_WRAPPER_CANNOT_FIND_FIELD = 2, + ERROR_NATIVE_CAMERA_WRAPPER_CANNOT_SET_PREVIEW_DISPLAY = 3 +}; + +typedef bool (*CameraCallback)(void* buffer, size_t bufferSize, void* userData); + +typedef void* (*InitCameraConnectC)(void* cameraCallback, int cameraId, void* userData); +typedef void (*CloseCameraConnectC)(void**); +typedef double (*GetCameraPropertyC)(void* camera, int propIdx); +typedef void (*SetCameraPropertyC)(void* camera, int propIdx, double value); +typedef void (*ApplyCameraPropertiesC)(void** camera); + +extern "C" +{ +void* initCameraConnectC(void* cameraCallback, int cameraId, void* userData); +void closeCameraConnectC(void**); +double getCameraPropertyC(void* camera, int propIdx); +void setCameraPropertyC(void* camera, int propIdx, double value); +void applyCameraPropertiesC(void** camera); +} + diff --git a/modules/androidcamera/include/camera_activity.hpp b/modules/androidcamera/include/camera_activity.hpp new file mode 100644 index 000000000..76a63b06e --- /dev/null +++ b/modules/androidcamera/include/camera_activity.hpp @@ -0,0 +1,48 @@ +#ifndef _CAMERAACTIVITY_H_ +#define _CAMERAACTIVITY_H_ + +#include +//#include + +class CameraActivity +{ +public: + enum ErrorCode { + NO_ERROR=0, + ERROR_WRONG_FRAME_SIZE, + ERROR_WRONG_POINTER_CAMERA_WRAPPER, + ERROR_CAMERA_CONNECTED, + ERROR_CANNOT_OPEN_CAMERA_WRAPPER_LIB, + ERROR_CANNOT_GET_FUNCTION_FROM_CAMERA_WRAPPER_LIB, + ERROR_CANNOT_INITIALIZE_CONNECTION, + ERROR_ISNT_CONNECTED, + ERROR_JAVA_VM_CANNOT_GET_CLASS, + ERROR_JAVA_VM_CANNOT_GET_FIELD, + ERROR_CANNOT_SET_PREVIEW_DISPLAY, + + ERROR_UNKNOWN=255 + }; + + CameraActivity(); + virtual ~CameraActivity(); + virtual bool onFrameBuffer(void* buffer, int bufferSize); + + ErrorCode connect(int cameraId = 0); + void disconnect(); + bool isConnected() const; + + double getProperty(int propIdx); + void setProperty(int propIdx, double value); + void applyProperties(); + + int getFrameWidth(); + int getFrameHeight(); + + static void setPathLibFolder(const char* path); +private: + void* camera; + int frameWidth; + int frameHeight; +}; + +#endif \ No newline at end of file diff --git a/modules/androidcamera/include/camera_properties.h b/modules/androidcamera/include/camera_properties.h new file mode 100644 index 000000000..42202e369 --- /dev/null +++ b/modules/androidcamera/include/camera_properties.h @@ -0,0 +1,10 @@ +#ifndef CAMERA_PROPERTIES_H +#define CAMERA_PROPERTIES_H + +enum { + ANDROID_CAMERA_PROPERTY_FRAMEWIDTH = 0, + ANDROID_CAMERA_PROPERTY_FRAMEHEIGHT = 1, + ANDROID_CAMERA_PROPERTY_SUPPORTED_PREVIEW_SIZES_STRING = 2 +}; + +#endif // CAMERA_PROPERTIES_H diff --git a/modules/androidcamera/src/camera_activity.cpp b/modules/androidcamera/src/camera_activity.cpp new file mode 100644 index 000000000..ca612b354 --- /dev/null +++ b/modules/androidcamera/src/camera_activity.cpp @@ -0,0 +1,405 @@ +#include +#include +#include +#include +#include "camera_activity.hpp" +#include "camera_wrapper.h" + +#define LOG_TAG "CAMERA_ACTIVITY" +#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) +#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) +#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) + +/////// +// Debug +#include +#include +#include + + +using namespace std; + +class CameraWrapperConnector +{ +public: + static CameraActivity::ErrorCode connect(int cameraId, CameraActivity* pCameraActivity, void** camera); + static CameraActivity::ErrorCode disconnect(void** camera); + static CameraActivity::ErrorCode setProperty(void* camera, int propIdx, double value); + static CameraActivity::ErrorCode getProperty(void* camera, int propIdx, double* value); + static CameraActivity::ErrorCode applyProperties(void** ppcamera); + + static void setPathLibFolder(const std::string& path); + +private: + static std::string pathLibFolder; + static bool isConnectedToLib; + + static std::string getPathLibFolder(); + static CameraActivity::ErrorCode connectToLib(); + static CameraActivity::ErrorCode getSymbolFromLib(void * libHandle, const char* symbolName, void** ppSymbol); + static void fillListWrapperLibs(const string& folderPath, vector& listLibs); + + static InitCameraConnectC pInitCameraC; + static CloseCameraConnectC pCloseCameraC; + static GetCameraPropertyC pGetPropertyC; + static SetCameraPropertyC pSetPropertyC; + static ApplyCameraPropertiesC pApplyPropertiesC; + + friend bool nextFrame(void* buffer, size_t bufferSize, void* userData); +}; + +std::string CameraWrapperConnector::pathLibFolder; +#define DEFAULT_WRAPPER_PACKAGE_NAME "com.NativeCamera" +#define DEFAULT_PATH_LIB_FOLDER "/data/data/" DEFAULT_WRAPPER_PACKAGE_NAME "/lib/" + +bool CameraWrapperConnector::isConnectedToLib=false; +InitCameraConnectC CameraWrapperConnector::pInitCameraC = 0; +CloseCameraConnectC CameraWrapperConnector::pCloseCameraC = 0; +GetCameraPropertyC CameraWrapperConnector::pGetPropertyC = 0; +SetCameraPropertyC CameraWrapperConnector::pSetPropertyC = 0; +ApplyCameraPropertiesC CameraWrapperConnector::pApplyPropertiesC = 0; + +#define INIT_CAMERA_SYMBOL_NAME "initCameraConnectC" +#define CLOSE_CAMERA_SYMBOL_NAME "closeCameraConnectC" +#define SET_CAMERA_PROPERTY_SYMBOL_NAME "setCameraPropertyC" +#define GET_CAMERA_PROPERTY_SYMBOL_NAME "getCameraPropertyC" +#define APPLY_CAMERA_PROPERTIES_SYMBOL_NAME "applyCameraPropertiesC" +#define PREFIX_CAMERA_WRAPPER_LIB "libnative_camera" + + +bool nextFrame(void* buffer, size_t bufferSize, void* userData) +{ + if (userData == NULL) + return true; + + return ((CameraActivity*)userData)->onFrameBuffer(buffer, bufferSize); +} + +CameraActivity::ErrorCode CameraWrapperConnector::connect(int cameraId, CameraActivity* pCameraActivity, void** camera) +{ + if (pCameraActivity == NULL) + { + LOGE("CameraWrapperConnector::connect error: wrong pointer to CameraActivity object"); + return CameraActivity::ERROR_WRONG_POINTER_CAMERA_WRAPPER; + } + + CameraActivity::ErrorCode errcode=connectToLib(); + if (errcode) return errcode; + + void* cmr = (*pInitCameraC)((void*)nextFrame, cameraId, (void*)pCameraActivity); + if (!cmr) + { + LOGE("CameraWrapperConnector::connectWrapper ERROR: the initializing function returned false"); + return CameraActivity::ERROR_CANNOT_INITIALIZE_CONNECTION; + } + + *camera = cmr; + return CameraActivity::NO_ERROR; +} + +CameraActivity::ErrorCode CameraWrapperConnector::disconnect(void** camera) +{ + if (camera == NULL || *camera == NULL) + { + LOGE("CameraWrapperConnector::disconnect error: wrong pointer to camera object"); + return CameraActivity::ERROR_WRONG_POINTER_CAMERA_WRAPPER; + } + + CameraActivity::ErrorCode errcode=connectToLib(); + if (errcode) return errcode; + + (*pCloseCameraC)(camera); + + return CameraActivity::NO_ERROR; +} + +CameraActivity::ErrorCode CameraWrapperConnector::setProperty(void* camera, int propIdx, double value) +{ + if (camera == NULL) + { + LOGE("CameraWrapperConnector::setProperty error: wrong pointer to camera object"); + return CameraActivity::ERROR_WRONG_POINTER_CAMERA_WRAPPER; + } + + (*pSetPropertyC)(camera, propIdx, value); + + return CameraActivity::NO_ERROR; +} + +CameraActivity::ErrorCode CameraWrapperConnector::getProperty(void* camera, int propIdx, double* value) +{ + if (camera == NULL) + { + LOGE("CameraWrapperConnector::getProperty error: wrong pointer to camera object"); + return CameraActivity::ERROR_WRONG_POINTER_CAMERA_WRAPPER; + } + + *value = (*pGetPropertyC)(camera, propIdx); + return CameraActivity::NO_ERROR; +} + +CameraActivity::ErrorCode CameraWrapperConnector::applyProperties(void** ppcamera) +{ + if ((ppcamera == NULL) || (*ppcamera == NULL)) + { + LOGE("CameraWrapperConnector::applyProperties error: wrong pointer to camera object"); + return CameraActivity::ERROR_WRONG_POINTER_CAMERA_WRAPPER; + } + + (*pApplyPropertiesC)(ppcamera); + return CameraActivity::NO_ERROR; +} + +CameraActivity::ErrorCode CameraWrapperConnector::connectToLib() +{ + if (isConnectedToLib) { + return CameraActivity::NO_ERROR; + } + + dlerror(); + string folderPath=getPathLibFolder(); + LOGD("CameraWrapperConnector::connectToLib: folderPath=%s", folderPath.c_str()); + + vector listLibs; + fillListWrapperLibs(folderPath, listLibs); + + void * libHandle=0; + string cur_path; + for(size_t i=0; i < listLibs.size(); i++) { + cur_path=folderPath + listLibs[i]; + LOGD("try to load library '%s'", listLibs[i].c_str()); + libHandle=dlopen(cur_path.c_str(), RTLD_LAZY); + if (libHandle) { + LOGD("Loaded library '%s'", cur_path.c_str()); + break; + } else { + LOGD("CameraWrapperConnector::connectToLib ERROR: cannot dlopen camera wrapper library %s, dlerror=\"%s\"", + cur_path.c_str(), dlerror()); + } + } + + if (!libHandle) { + LOGE("CameraWrapperConnector::connectToLib ERROR: cannot dlopen camera wrapper library"); + return CameraActivity::ERROR_CANNOT_OPEN_CAMERA_WRAPPER_LIB; + } + + InitCameraConnectC pInit_C; + CloseCameraConnectC pClose_C; + GetCameraPropertyC pGetProp_C; + SetCameraPropertyC pSetProp_C; + ApplyCameraPropertiesC pApplyProp_C; + + CameraActivity::ErrorCode res; + + res = getSymbolFromLib(libHandle, (const char*)INIT_CAMERA_SYMBOL_NAME, (void**)(&pInit_C)); + if (res) return res; + + res = getSymbolFromLib(libHandle, CLOSE_CAMERA_SYMBOL_NAME, (void**)(&pClose_C)); + if (res) return res; + + res = getSymbolFromLib(libHandle, GET_CAMERA_PROPERTY_SYMBOL_NAME, (void**)(&pGetProp_C)); + if (res) return res; + + res = getSymbolFromLib(libHandle, SET_CAMERA_PROPERTY_SYMBOL_NAME, (void**)(&pSetProp_C)); + if (res) return res; + \ + res = getSymbolFromLib(libHandle, APPLY_CAMERA_PROPERTIES_SYMBOL_NAME, (void**)(&pApplyProp_C)); + if (res) return res; + + pInitCameraC = pInit_C; + pCloseCameraC = pClose_C; + pGetPropertyC = pGetProp_C; + pSetPropertyC = pSetProp_C; + pApplyPropertiesC = pApplyProp_C; + isConnectedToLib=true; + + return CameraActivity::NO_ERROR; +} + +CameraActivity::ErrorCode CameraWrapperConnector::getSymbolFromLib(void* libHandle, const char* symbolName, void** ppSymbol) +{ + dlerror(); + *(void **) (ppSymbol)=dlsym(libHandle, symbolName); + + const char* error_dlsym_init=dlerror(); + if (error_dlsym_init) { + LOGE("CameraWrapperConnector::getSymbolFromLib ERROR: cannot get symbol of the function '%s' from the camera wrapper library, dlerror=\"%s\"", + symbolName, error_dlsym_init); + return CameraActivity::ERROR_CANNOT_GET_FUNCTION_FROM_CAMERA_WRAPPER_LIB; + } + return CameraActivity::NO_ERROR; +} + +void CameraWrapperConnector::fillListWrapperLibs(const string& folderPath, vector& listLibs) +{ + DIR *dp; + struct dirent *ep; + + dp = opendir (folderPath.c_str()); + if (dp != NULL) + { + while (ep = readdir (dp)) { + const char* cur_name=ep->d_name; + if (strstr(cur_name, PREFIX_CAMERA_WRAPPER_LIB)) { + listLibs.push_back(cur_name); + LOGE("||%s", cur_name); + } + } + (void) closedir (dp); + } +} + +std::string CameraWrapperConnector::getPathLibFolder() +{ + if (!pathLibFolder.empty()) + return pathLibFolder; + + Dl_info dl_info; + if(0 != dladdr((void *)nextFrame, &dl_info)) + { + LOGD("Library name: %s", dl_info.dli_fname); + LOGD("Library base address: %p", dl_info.dli_fbase); + + char addrBuf[18]; + sprintf(addrBuf, "%x-", dl_info.dli_fbase); + int addrLength = strlen(addrBuf); + + char lineBuf[2048]; + FILE* file = fopen("/proc/self/smaps", "rt"); + + if(file) + { + while (fgets(lineBuf, sizeof lineBuf, file) != NULL) + { + if(0 == strncmp(lineBuf, addrBuf, addrLength)) + { + //verify that line ends with library name + int lineLength = strlen(lineBuf); + int libNameLength = strlen(dl_info.dli_fname); + + //trim end + for(int i = lineLength - 1; i >= 0 && isspace(lineBuf[i]); --i) + { + lineBuf[i] = 0; + --lineLength; + } + + if (0 != strncmp(lineBuf + lineLength - libNameLength, dl_info.dli_fname, libNameLength)) + { + LOGE("Strange error: line \"%s\" does not ends with library name %s", lineBuf, dl_info.dli_fname); + continue; + } + + //extract path from smaps line + char* pathBegin = strchr(lineBuf, '/'); + if (0 == pathBegin) + { + LOGE("Strange error: could not find path beginning in lin \"%s\"", lineBuf); + continue; + } + + char* pathEnd = strrchr(pathBegin, '/'); + pathEnd[1] = 0; + + LOGD("Libraries folder found: %s", pathBegin); + + fclose(file); + return pathBegin; + } + } + fclose(file); + LOGE("Could not find library path."); + } + else + { + LOGE("Could not read /proc/self/smaps"); + } + } + else + { + LOGE("Could not get library name and base address."); + } + + return DEFAULT_PATH_LIB_FOLDER ; +} + +void CameraWrapperConnector::setPathLibFolder(const string& path) +{ + pathLibFolder=path; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////// + +CameraActivity::CameraActivity() : camera(0), frameWidth(-1), frameHeight(-1) +{ +} + +CameraActivity::~CameraActivity() +{ + if (camera != 0) + disconnect(); +} + +bool CameraActivity::onFrameBuffer(void* buffer, int bufferSize) +{ + LOGD("CameraActivity::onFrameBuffer - empty callback"); + return true; +} + +void CameraActivity::disconnect() +{ + CameraWrapperConnector::disconnect(&camera); +} + +bool CameraActivity::isConnected() const +{ + return camera != 0; +} + +CameraActivity::ErrorCode CameraActivity::connect(int cameraId) +{ + ErrorCode rescode = CameraWrapperConnector::connect(cameraId, this, &camera); + if (rescode) return rescode; + + return NO_ERROR; +} + +double CameraActivity::getProperty(int propIdx) +{ + double propVal; + ErrorCode rescode = CameraWrapperConnector::getProperty(camera, propIdx, &propVal); + if (rescode) return -1; + return propVal; +} + +void CameraActivity::setProperty(int propIdx, double value) +{ + CameraWrapperConnector::setProperty(camera, propIdx, value); +} + +void CameraActivity::applyProperties() +{ + frameWidth = -1; + frameHeight = -1; + CameraWrapperConnector::applyProperties(&camera); +} + +int CameraActivity::getFrameWidth() +{ + if (frameWidth < 0) + frameWidth = getProperty(ANDROID_CAMERA_PROPERTY_FRAMEWIDTH); + return frameWidth; +} + +int CameraActivity::getFrameHeight() +{ + if (frameHeight < 0) + frameHeight = getProperty(ANDROID_CAMERA_PROPERTY_FRAMEHEIGHT); + return frameHeight; +} + +void CameraActivity::setPathLibFolder(const char* path) +{ + CameraWrapperConnector::setPathLibFolder(path); +} diff --git a/modules/highgui/CMakeLists.txt b/modules/highgui/CMakeLists.txt index cf654953b..a1469aa93 100644 --- a/modules/highgui/CMakeLists.txt +++ b/modules/highgui/CMakeLists.txt @@ -211,9 +211,11 @@ if(APPLE) endif() endif(APPLE) -if(HAVE_ANDROID_NATIVE_CAMERA) +if(ANDROID) + include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../androidcamera/include") set(highgui_srcs ${highgui_srcs} src/cap_android.cpp) add_definitions(-DHAVE_ANDROID_NATIVE_CAMERA) + set(HIGHGUI_LIBRARIES ${HIGHGUI_LIBRARIES} opencv_androidcamera) endif() if(COMMAND get_module_external_sources) @@ -331,6 +333,10 @@ if(NOT ZLIB_FOUND) add_dependencies(${the_target} zlib) endif() +if(ANDROID) + add_dependencies(${the_target} opencv_androidcamera) +endif() + #message(STATUS "GRFMT: ${GRFMT_LIBS}") #message(STATUS "HIGHGUI_LIBS: ${HIGHGUI_LIBRARIES}") #message(STATUS "OPENCV_LIBS: ${OPENCV_LINKER_LIBS}") @@ -371,6 +377,9 @@ if(BUILD_TESTS) "${CMAKE_CURRENT_BINARY_DIR}") set(test_deps opencv_ts opencv_highgui opencv_imgproc) + if(ANDROID) + set(test_deps ${test_deps} opencv_androidcamera) + endif() foreach(d ${test_deps}) if(${d} MATCHES "opencv_") diff --git a/modules/highgui/src/cap_android.cpp b/modules/highgui/src/cap_android.cpp index 26fe28e91..8aabf67b6 100644 --- a/modules/highgui/src/cap_android.cpp +++ b/modules/highgui/src/cap_android.cpp @@ -46,7 +46,7 @@ #include #include #include -#include "camera_activity.h" +#include #if !defined(LOGD) && !defined(LOGI) && !defined(LOGE) #define LOG_TAG "CV_CAP"