From fc22ce5871e1affb15ea595c7b939c2b4885e793 Mon Sep 17 00:00:00 2001 From: Milan Nikolic Date: Tue, 3 Nov 2020 19:36:26 +0100 Subject: [PATCH] Support for Android, API >= 24, WIP --- camera/camera_android.go | 140 +++++++++++++++++++++++++-------------- 1 file changed, 90 insertions(+), 50 deletions(-) diff --git a/camera/camera_android.go b/camera/camera_android.go index 8cb94c8..f0aa8d0 100644 --- a/camera/camera_android.go +++ b/camera/camera_android.go @@ -17,8 +17,11 @@ package camera #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__) #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) +AImage *image; AImageReader *imageReader; +ANativeWindow *nativeWindow; + ACameraDevice *cameraDevice; ACameraManager *cameraManager; ACameraOutputTarget *cameraOutputTarget; @@ -28,53 +31,60 @@ 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) { +void 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) { +void 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) { +ACameraDevice_stateCallbacks deviceStateCallbacks = { + .context = NULL, + .onDisconnected = device_on_disconnected, + .onError = device_on_error, +}; + +void session_on_ready(void *context, ACameraCaptureSession *session) { LOGI("session is ready. %p\n", session); } -void capture_session_on_active(void *context, ACameraCaptureSession *session) { +void session_on_active(void *context, ACameraCaptureSession *session) { LOGI("session is activated. %p\n", session); } -void capture_session_on_closed(void *context, ACameraCaptureSession *session) { +void session_on_closed(void *context, ACameraCaptureSession *session) { LOGI("session is closed. %p\n", session); } -void imageCallback(void* context, AImageReader* reader) { - LOGD("imageCallback"); +ACameraCaptureSession_stateCallbacks captureSessionStateCallbacks = { + .context = NULL, + .onActive = session_on_active, + .onReady = session_on_ready, + .onClosed = session_on_closed, +}; - AImage *image; - media_status_t status = AImageReader_acquireNextImage(reader, &image); - if(status != ACAMERA_OK) { +void image_callback(void *context, AImageReader *reader) { + LOGD("image_callback"); + + media_status_t status = AImageReader_acquireLatestImage(reader, &image); + if(status != AMEDIA_OK) { LOGE("failed to acquire next image (reason: %d).\n", status); - return; } - - AImage_getPlaneData(image, 0, &image_data, &image_len); - AImage_delete(image); } +AImageReader_ImageListener imageListener = { + .context = NULL, + .onImageAvailable = image_callback, +}; + int openCamera(int index, int width, int height) { ACameraIdList *cameraIdList; const char *selectedCameraId; camera_status_t status = ACAMERA_OK; - ACameraManager *cameraManager = ACameraManager_create(); + cameraManager = ACameraManager_create(); status = ACameraManager_getCameraIdList(cameraManager, &cameraIdList); if(status != ACAMERA_OK) { @@ -93,49 +103,50 @@ int openCamera(int index, int width, int height) { 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); + status = ACameraDevice_createCaptureRequest(cameraDevice, TEMPLATE_STILL_CAPTURE, &captureRequest); if(status != ACAMERA_OK) { LOGE("failed to create snapshot capture request (id: %s)\n", selectedCameraId); return status; } - ACaptureSessionOutputContainer_create(&captureSessionOutputContainer); + status = ACaptureSessionOutputContainer_create(&captureSessionOutputContainer); + if(status != ACAMERA_OK) { + LOGE("failed to create session output container (id: %s)\n", selectedCameraId); + return status; + } - 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); + media_status_t mstatus = AImageReader_new(width, height, AIMAGE_FORMAT_YUV_420_888, 2, &imageReader); + if(mstatus != AMEDIA_OK) { + LOGE("failed to create image reader (reason: %d).\n", mstatus); return mstatus; } - AImageReader_ImageListener listener = { - .context = NULL, - .onImageAvailable = imageCallback, - }; + mstatus = AImageReader_setImageListener(imageReader, &imageListener); + if(mstatus != AMEDIA_OK) { + LOGE("failed to set image listener (reason: %d).\n", mstatus); + return mstatus; + } - 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); + ACaptureSessionOutput_create(nativeWindow, &captureSessionOutput); + ACaptureSessionOutputContainer_add(captureSessionOutputContainer, captureSessionOutput); + + status = ACameraDevice_createCaptureSession(cameraDevice, captureSessionOutputContainer, &captureSessionStateCallbacks, &cameraCaptureSession); + if(status != ACAMERA_OK) { + LOGE("failed to create capture session (reason: %d).\n", status); + return status; + } ACameraManager_deleteCameraIdList(cameraIdList); ACameraManager_delete(cameraManager); @@ -152,7 +163,7 @@ int captureCamera() { return status; } -void closeCamera() { +int closeCamera() { camera_status_t status = ACAMERA_OK; if(captureRequest != NULL) { @@ -170,6 +181,7 @@ void closeCamera() { if(status != ACAMERA_OK) { LOGE("failed to close camera device.\n"); + return status; } cameraDevice = NULL; @@ -190,12 +202,18 @@ void closeCamera() { imageReader = NULL; } + if(image != NULL) { + AImage_delete(image); + image = NULL; + } + LOGI("camera closed.\n"); + return ACAMERA_OK; } int openCamera(int index, int width, int height); int captureCamera(); -void closeCamera(); +int closeCamera(); #cgo android CFLAGS: -D__ANDROID_API__=24 #cgo android LDFLAGS: -lcamera2ndk -lmediandk -llog -landroid @@ -211,7 +229,7 @@ import ( // Camera represents camera. type Camera struct { opts Options - img *image.RGBA + img *image.YCbCr } // New returns new Camera for given camera index. @@ -219,7 +237,7 @@ 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))) + camera.img = image.NewYCbCr(image.Rect(0, 0, int(opts.Width), int(opts.Height)), image.YCbCrSubsampleRatio420) ret := C.openCamera(C.int(opts.Index), C.int(opts.Width), C.int(opts.Height)) if bool(int(ret) != 0) { @@ -238,11 +256,29 @@ func (c *Camera) Read() (img image.Image, err error) { 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 + if C.image == nil { + err = fmt.Errorf("camera: can not retrieve frame") + return } + var yStride C.int + var yLen, cbLen, crLen C.int + var yPtr, cbPtr, crPtr *C.uint8_t + + C.AImage_getPlaneRowStride(C.image, 0, &yStride) + C.AImage_getPlaneData(C.image, 0, &yPtr, &yLen) + C.AImage_getPlaneData(C.image, 1, &cbPtr, &cbLen) + C.AImage_getPlaneData(C.image, 2, &crPtr, &crLen) + + c.img.YStride = int(yStride) + c.img.CStride = int(yStride) / 2 + + c.img.Y = C.GoBytes(unsafe.Pointer(yPtr), yLen) + c.img.Cb = C.GoBytes(unsafe.Pointer(cbPtr), cbLen) + c.img.Cr = C.GoBytes(unsafe.Pointer(crPtr), crLen) + + img = c.img + return } @@ -257,7 +293,11 @@ func (c *Camera) SetProperty(id int, value float64) { // Close closes camera. func (c *Camera) Close() (err error) { - C.closeCamera() + ret := C.closeCamera() + if bool(int(ret) != 0) { + err = fmt.Errorf("camera: can not close camera %d: error %d", c.opts.Index, int(ret)) + return + } return }