From 19ea541157a10d42eb53f2aeaf75cdc63fac2cb8 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Tue, 14 Apr 2020 15:52:33 +0200 Subject: [PATCH] Add support for Android, API >= 24, WIP --- camera/camera_android.go | 263 +++++++++++++++++++++++++++++++++++ camera/camera_const_linux.go | 2 +- camera/camera_linux.go | 2 +- make.bash | 8 ++ 4 files changed, 273 insertions(+), 2 deletions(-) create mode 100644 camera/camera_android.go diff --git a/camera/camera_android.go b/camera/camera_android.go new file mode 100644 index 0000000..8cb94c8 --- /dev/null +++ b/camera/camera_android.go @@ -0,0 +1,263 @@ +// +build android + +// Package camera. +package camera + +/* +#include + +#include + +#include +#include + +#define TAG "camera" +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__) +#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__) +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__) +#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) + +AImageReader *imageReader; + +ACameraDevice *cameraDevice; +ACameraManager *cameraManager; +ACameraOutputTarget *cameraOutputTarget; +ACameraCaptureSession *cameraCaptureSession; + +ACaptureRequest *captureRequest; +ACaptureSessionOutput *captureSessionOutput; +ACaptureSessionOutputContainer *captureSessionOutputContainer; + +ACameraDevice_StateCallbacks deviceStateCallbacks; +ACameraCaptureSession_stateCallbacks captureSessionStateCallbacks; + +uint8_t *image_data; +int image_len = 0; + +void camera_device_on_disconnected(void *context, ACameraDevice *device) { + LOGI("camera %s is diconnected.\n", ACameraDevice_getId(device)); +} + +void camera_device_on_error(void *context, ACameraDevice *device, int error) { + LOGE("error %d on camera %s.\n", error, ACameraDevice_getId(device)); +} + +void capture_session_on_ready(void *context, ACameraCaptureSession *session) { + LOGI("session is ready. %p\n", session); +} + +void capture_session_on_active(void *context, ACameraCaptureSession *session) { + LOGI("session is activated. %p\n", session); +} + +void capture_session_on_closed(void *context, ACameraCaptureSession *session) { + LOGI("session is closed. %p\n", session); +} + +void imageCallback(void* context, AImageReader* reader) { + LOGD("imageCallback"); + + AImage *image; + media_status_t status = AImageReader_acquireNextImage(reader, &image); + if(status != ACAMERA_OK) { + LOGE("failed to acquire next image (reason: %d).\n", status); + return; + } + + AImage_getPlaneData(image, 0, &image_data, &image_len); + AImage_delete(image); +} + +int openCamera(int index, int width, int height) { + ACameraIdList *cameraIdList; + const char *selectedCameraId; + + camera_status_t status = ACAMERA_OK; + + ACameraManager *cameraManager = ACameraManager_create(); + + status = ACameraManager_getCameraIdList(cameraManager, &cameraIdList); + if(status != ACAMERA_OK) { + LOGE("failed to get camera id list (reason: %d).\n", status); + return status; + } + + if(cameraIdList->numCameras < 1) { + LOGE("no camera device detected.\n"); + } + + if(cameraIdList->numCameras < index+1) { + LOGE("no camera at index %d.\n", index); + } + + selectedCameraId = cameraIdList->cameraIds[index]; + LOGI("open camera (id: %s, num of cameras: %d).\n", selectedCameraId, cameraIdList->numCameras); + + deviceStateCallbacks.onDisconnected = camera_device_on_disconnected; + deviceStateCallbacks.onError = camera_device_on_error; + + status = ACameraManager_openCamera(cameraManager, selectedCameraId, &deviceStateCallbacks, &cameraDevice); + if(status != ACAMERA_OK) { + LOGE("failed to open camera device (id: %s)\n", selectedCameraId); + return status; + } + + status = ACameraDevice_createCaptureRequest(cameraDevice, TEMPLATE_VIDEO_SNAPSHOT, &captureRequest); + if(status != ACAMERA_OK) { + LOGE("failed to create snapshot capture request (id: %s)\n", selectedCameraId); + return status; + } + + ACaptureSessionOutputContainer_create(&captureSessionOutputContainer); + + captureSessionStateCallbacks.onReady = capture_session_on_ready; + captureSessionStateCallbacks.onActive = capture_session_on_active; + captureSessionStateCallbacks.onClosed = capture_session_on_closed; + + media_status_t mstatus = AImageReader_new(width, height, AIMAGE_FORMAT_RGBA_8888, 1, &imageReader); + if(mstatus != ACAMERA_OK) { + LOGE("failed to create image reader (reason: %d).\n", status); + return mstatus; + } + + AImageReader_ImageListener listener = { + .context = NULL, + .onImageAvailable = imageCallback, + }; + + AImageReader_setImageListener(imageReader, &listener); + + ANativeWindow *nativeWindow; + AImageReader_getWindow(imageReader, &nativeWindow); + ANativeWindow_acquire(nativeWindow); + + ACameraOutputTarget_create(nativeWindow, &cameraOutputTarget); + ACaptureRequest_addTarget(captureRequest, cameraOutputTarget); + ACaptureSessionOutput_create(nativeWindow, &captureSessionOutput); + + ACameraDevice_createCaptureSession(cameraDevice, captureSessionOutputContainer, &captureSessionStateCallbacks, &cameraCaptureSession); + + ACameraManager_deleteCameraIdList(cameraIdList); + ACameraManager_delete(cameraManager); + + return ACAMERA_OK; +} + +int captureCamera() { + camera_status_t status = ACameraCaptureSession_capture(cameraCaptureSession, NULL, 1, &captureRequest, NULL); + if(status != ACAMERA_OK) { + LOGE("failed to capture image (reason: %d).\n", status); + } + + return status; +} + +void closeCamera() { + camera_status_t status = ACAMERA_OK; + + if(captureRequest != NULL) { + ACaptureRequest_free(captureRequest); + captureRequest = NULL; + } + + if(cameraOutputTarget != NULL) { + ACameraOutputTarget_free(cameraOutputTarget); + cameraOutputTarget = NULL; + } + + if(cameraDevice != NULL) { + status = ACameraDevice_close(cameraDevice); + + if(status != ACAMERA_OK) { + LOGE("failed to close camera device.\n"); + } + + cameraDevice = NULL; + } + + if(captureSessionOutput != NULL) { + ACaptureSessionOutput_free(captureSessionOutput); + captureSessionOutput = NULL; + } + + if(captureSessionOutputContainer != NULL) { + ACaptureSessionOutputContainer_free(captureSessionOutputContainer); + captureSessionOutputContainer = NULL; + } + + if(imageReader != NULL) { + AImageReader_delete(imageReader); + imageReader = NULL; + } + + LOGI("camera closed.\n"); +} + +int openCamera(int index, int width, int height); +int captureCamera(); +void closeCamera(); + +#cgo android CFLAGS: -D__ANDROID_API__=24 +#cgo android LDFLAGS: -lcamera2ndk -lmediandk -llog -landroid +*/ +import "C" + +import ( + "fmt" + "image" + "unsafe" +) + +// Camera represents camera. +type Camera struct { + opts Options + img *image.RGBA +} + +// New returns new Camera for given camera index. +func New(opts Options) (camera *Camera, err error) { + camera = &Camera{} + camera.opts = opts + + camera.img = image.NewRGBA(image.Rect(0, 0, int(opts.Width), int(opts.Height))) + + ret := C.openCamera(C.int(opts.Index), C.int(opts.Width), C.int(opts.Height)) + if bool(int(ret) != 0) { + err = fmt.Errorf("camera: can not open camera %d: error %d", opts.Index, int(ret)) + return + } + + return +} + +// Read reads next frame from camera and returns image. +func (c *Camera) Read() (img image.Image, err error) { + ret := C.captureCamera() + if bool(int(ret) != 0) { + err = fmt.Errorf("camera: can not grab frame: error %d", int(ret)) + return + } + + if C.image_data != nil { + c.img.Pix = (*[1 << 24]uint8)(unsafe.Pointer(&C.image_data))[0:int(C.image_len)] + img = c.img + } + + return +} + +// GetProperty returns the specified camera property. +func (c *Camera) GetProperty(id int) float64 { + return 0 +} + +// SetProperty sets a camera property. +func (c *Camera) SetProperty(id int, value float64) { +} + +// Close closes camera. +func (c *Camera) Close() (err error) { + C.closeCamera() + + return +} diff --git a/camera/camera_const_linux.go b/camera/camera_const_linux.go index 82a249d..dad2133 100644 --- a/camera/camera_const_linux.go +++ b/camera/camera_const_linux.go @@ -1,4 +1,4 @@ -// +build !cv2,!cv4 +// +build !cv2,!cv4,!android package camera diff --git a/camera/camera_linux.go b/camera/camera_linux.go index 4f6745f..0162c5e 100644 --- a/camera/camera_linux.go +++ b/camera/camera_linux.go @@ -1,4 +1,4 @@ -// +build !cv2,!cv4 +// +build !cv2,!cv4,!android // Package camera. package camera diff --git a/make.bash b/make.bash index e1e758a..2d0e77e 100755 --- a/make.bash +++ b/make.bash @@ -6,6 +6,7 @@ MINGW64="/usr/x86_64-w64-mingw32" RPI="/usr/armv6j-hardfloat-linux-gnueabi" RPI3="/usr/armv7a-hardfloat-linux-gnueabi" APPLE="/usr/x86_64-apple-darwin14" +ANDROID="/usr/arm-linux-androideabi" mkdir -p build @@ -101,3 +102,10 @@ CGO_LDFLAGS="-L$APPLE/SDK/MacOSX10.10.sdk/usr/lib -mmacosx-version-min=10.10" \ CGO_CFLAGS="-I$APPLE/SDK/MacOSX10.10.sdk/usr/include -mmacosx-version-min=10.10" \ CC="$APPLE/bin/x86_64-apple-darwin14-clang" CXX="$APPLE/bin/x86_64-apple-darwin14-clang++" \ CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 go build -tags cv2 -o build/cam2ip.darwin.amd64 -ldflags "-linkmode external -s -w '-extldflags=-mmacosx-version-min=10.10'" github.com/gen2brain/cam2ip/cmd/cam2ip + +PKG_CONFIG_PATH="$ANDROID/lib/pkgconfig" \ +PKG_CONFIG_LIBDIR="$ANDROID/lib/pkgconfig" \ +CGO_LDFLAGS="-L$ANDROID/sysroot/usr/lib" \ +CGO_CFLAGS="-I$ANDROID/sysroot/usr/include --sysroot=$ANDROID/sysroot" \ +CC="$ANDROID/bin/arm-linux-androideabi-clang" CXX="$ANDROID/bin/arm-linux-androideabi-clang++" \ +CGO_ENABLED=1 GOOS=android GOARCH=arm go build -tags jpeg -o build/cam2ip.android.arm7 -ldflags "-linkmode external -s -w" github.com/gen2brain/cam2ip/cmd/cam2ip