mirror of
https://github.com/gen2brain/cam2ip.git
synced 2025-12-16 04:18:39 +00:00
Support for Android, API >= 24, WIP
This commit is contained in:
@@ -17,8 +17,11 @@ package camera
|
|||||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, 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__)
|
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
|
||||||
|
|
||||||
|
AImage *image;
|
||||||
AImageReader *imageReader;
|
AImageReader *imageReader;
|
||||||
|
|
||||||
|
ANativeWindow *nativeWindow;
|
||||||
|
|
||||||
ACameraDevice *cameraDevice;
|
ACameraDevice *cameraDevice;
|
||||||
ACameraManager *cameraManager;
|
ACameraManager *cameraManager;
|
||||||
ACameraOutputTarget *cameraOutputTarget;
|
ACameraOutputTarget *cameraOutputTarget;
|
||||||
@@ -28,45 +31,52 @@ ACaptureRequest *captureRequest;
|
|||||||
ACaptureSessionOutput *captureSessionOutput;
|
ACaptureSessionOutput *captureSessionOutput;
|
||||||
ACaptureSessionOutputContainer *captureSessionOutputContainer;
|
ACaptureSessionOutputContainer *captureSessionOutputContainer;
|
||||||
|
|
||||||
ACameraDevice_StateCallbacks deviceStateCallbacks;
|
void device_on_disconnected(void *context, ACameraDevice *device) {
|
||||||
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));
|
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));
|
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);
|
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);
|
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);
|
LOGI("session is closed. %p\n", session);
|
||||||
}
|
}
|
||||||
|
|
||||||
void imageCallback(void* context, AImageReader* reader) {
|
ACameraCaptureSession_stateCallbacks captureSessionStateCallbacks = {
|
||||||
LOGD("imageCallback");
|
.context = NULL,
|
||||||
|
.onActive = session_on_active,
|
||||||
|
.onReady = session_on_ready,
|
||||||
|
.onClosed = session_on_closed,
|
||||||
|
};
|
||||||
|
|
||||||
AImage *image;
|
void image_callback(void *context, AImageReader *reader) {
|
||||||
media_status_t status = AImageReader_acquireNextImage(reader, &image);
|
LOGD("image_callback");
|
||||||
if(status != ACAMERA_OK) {
|
|
||||||
|
media_status_t status = AImageReader_acquireLatestImage(reader, &image);
|
||||||
|
if(status != AMEDIA_OK) {
|
||||||
LOGE("failed to acquire next image (reason: %d).\n", status);
|
LOGE("failed to acquire next image (reason: %d).\n", status);
|
||||||
return;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AImage_getPlaneData(image, 0, &image_data, &image_len);
|
AImageReader_ImageListener imageListener = {
|
||||||
AImage_delete(image);
|
.context = NULL,
|
||||||
}
|
.onImageAvailable = image_callback,
|
||||||
|
};
|
||||||
|
|
||||||
int openCamera(int index, int width, int height) {
|
int openCamera(int index, int width, int height) {
|
||||||
ACameraIdList *cameraIdList;
|
ACameraIdList *cameraIdList;
|
||||||
@@ -74,7 +84,7 @@ int openCamera(int index, int width, int height) {
|
|||||||
|
|
||||||
camera_status_t status = ACAMERA_OK;
|
camera_status_t status = ACAMERA_OK;
|
||||||
|
|
||||||
ACameraManager *cameraManager = ACameraManager_create();
|
cameraManager = ACameraManager_create();
|
||||||
|
|
||||||
status = ACameraManager_getCameraIdList(cameraManager, &cameraIdList);
|
status = ACameraManager_getCameraIdList(cameraManager, &cameraIdList);
|
||||||
if(status != ACAMERA_OK) {
|
if(status != ACAMERA_OK) {
|
||||||
@@ -93,49 +103,50 @@ int openCamera(int index, int width, int height) {
|
|||||||
selectedCameraId = cameraIdList->cameraIds[index];
|
selectedCameraId = cameraIdList->cameraIds[index];
|
||||||
LOGI("open camera (id: %s, num of cameras: %d).\n", selectedCameraId, cameraIdList->numCameras);
|
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);
|
status = ACameraManager_openCamera(cameraManager, selectedCameraId, &deviceStateCallbacks, &cameraDevice);
|
||||||
if(status != ACAMERA_OK) {
|
if(status != ACAMERA_OK) {
|
||||||
LOGE("failed to open camera device (id: %s)\n", selectedCameraId);
|
LOGE("failed to open camera device (id: %s)\n", selectedCameraId);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = ACameraDevice_createCaptureRequest(cameraDevice, TEMPLATE_VIDEO_SNAPSHOT, &captureRequest);
|
status = ACameraDevice_createCaptureRequest(cameraDevice, TEMPLATE_STILL_CAPTURE, &captureRequest);
|
||||||
if(status != ACAMERA_OK) {
|
if(status != ACAMERA_OK) {
|
||||||
LOGE("failed to create snapshot capture request (id: %s)\n", selectedCameraId);
|
LOGE("failed to create snapshot capture request (id: %s)\n", selectedCameraId);
|
||||||
return status;
|
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;
|
media_status_t mstatus = AImageReader_new(width, height, AIMAGE_FORMAT_YUV_420_888, 2, &imageReader);
|
||||||
captureSessionStateCallbacks.onActive = capture_session_on_active;
|
if(mstatus != AMEDIA_OK) {
|
||||||
captureSessionStateCallbacks.onClosed = capture_session_on_closed;
|
LOGE("failed to create image reader (reason: %d).\n", mstatus);
|
||||||
|
|
||||||
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;
|
return mstatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
AImageReader_ImageListener listener = {
|
mstatus = AImageReader_setImageListener(imageReader, &imageListener);
|
||||||
.context = NULL,
|
if(mstatus != AMEDIA_OK) {
|
||||||
.onImageAvailable = imageCallback,
|
LOGE("failed to set image listener (reason: %d).\n", mstatus);
|
||||||
};
|
return mstatus;
|
||||||
|
}
|
||||||
|
|
||||||
AImageReader_setImageListener(imageReader, &listener);
|
|
||||||
|
|
||||||
ANativeWindow *nativeWindow;
|
|
||||||
AImageReader_getWindow(imageReader, &nativeWindow);
|
AImageReader_getWindow(imageReader, &nativeWindow);
|
||||||
ANativeWindow_acquire(nativeWindow);
|
ANativeWindow_acquire(nativeWindow);
|
||||||
|
|
||||||
ACameraOutputTarget_create(nativeWindow, &cameraOutputTarget);
|
ACameraOutputTarget_create(nativeWindow, &cameraOutputTarget);
|
||||||
ACaptureRequest_addTarget(captureRequest, 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_deleteCameraIdList(cameraIdList);
|
||||||
ACameraManager_delete(cameraManager);
|
ACameraManager_delete(cameraManager);
|
||||||
@@ -152,7 +163,7 @@ int captureCamera() {
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
void closeCamera() {
|
int closeCamera() {
|
||||||
camera_status_t status = ACAMERA_OK;
|
camera_status_t status = ACAMERA_OK;
|
||||||
|
|
||||||
if(captureRequest != NULL) {
|
if(captureRequest != NULL) {
|
||||||
@@ -170,6 +181,7 @@ void closeCamera() {
|
|||||||
|
|
||||||
if(status != ACAMERA_OK) {
|
if(status != ACAMERA_OK) {
|
||||||
LOGE("failed to close camera device.\n");
|
LOGE("failed to close camera device.\n");
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
cameraDevice = NULL;
|
cameraDevice = NULL;
|
||||||
@@ -190,12 +202,18 @@ void closeCamera() {
|
|||||||
imageReader = NULL;
|
imageReader = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(image != NULL) {
|
||||||
|
AImage_delete(image);
|
||||||
|
image = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
LOGI("camera closed.\n");
|
LOGI("camera closed.\n");
|
||||||
|
return ACAMERA_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int openCamera(int index, int width, int height);
|
int openCamera(int index, int width, int height);
|
||||||
int captureCamera();
|
int captureCamera();
|
||||||
void closeCamera();
|
int closeCamera();
|
||||||
|
|
||||||
#cgo android CFLAGS: -D__ANDROID_API__=24
|
#cgo android CFLAGS: -D__ANDROID_API__=24
|
||||||
#cgo android LDFLAGS: -lcamera2ndk -lmediandk -llog -landroid
|
#cgo android LDFLAGS: -lcamera2ndk -lmediandk -llog -landroid
|
||||||
@@ -211,7 +229,7 @@ import (
|
|||||||
// Camera represents camera.
|
// Camera represents camera.
|
||||||
type Camera struct {
|
type Camera struct {
|
||||||
opts Options
|
opts Options
|
||||||
img *image.RGBA
|
img *image.YCbCr
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns new Camera for given camera index.
|
// New returns new Camera for given camera index.
|
||||||
@@ -219,7 +237,7 @@ func New(opts Options) (camera *Camera, err error) {
|
|||||||
camera = &Camera{}
|
camera = &Camera{}
|
||||||
camera.opts = opts
|
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))
|
ret := C.openCamera(C.int(opts.Index), C.int(opts.Width), C.int(opts.Height))
|
||||||
if bool(int(ret) != 0) {
|
if bool(int(ret) != 0) {
|
||||||
@@ -238,11 +256,29 @@ func (c *Camera) Read() (img image.Image, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if C.image_data != nil {
|
if C.image == nil {
|
||||||
c.img.Pix = (*[1 << 24]uint8)(unsafe.Pointer(&C.image_data))[0:int(C.image_len)]
|
err = fmt.Errorf("camera: can not retrieve frame")
|
||||||
img = c.img
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +293,11 @@ func (c *Camera) SetProperty(id int, value float64) {
|
|||||||
|
|
||||||
// Close closes camera.
|
// Close closes camera.
|
||||||
func (c *Camera) Close() (err error) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user