Skip to content

Commit

Permalink
Add support for Android, API >= 24, WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
gen2brain committed Apr 14, 2020
1 parent be5863d commit 19ea541
Show file tree
Hide file tree
Showing 4 changed files with 273 additions and 2 deletions.
263 changes: 263 additions & 0 deletions camera/camera_android.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
// +build android

// Package camera.
package camera

/*
#include <android/log.h>
#include <media/NdkImageReader.h>
#include <camera/NdkCameraDevice.h>
#include <camera/NdkCameraManager.h>
#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
}
2 changes: 1 addition & 1 deletion camera/camera_const_linux.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// +build !cv2,!cv4
// +build !cv2,!cv4,!android

package camera

Expand Down
2 changes: 1 addition & 1 deletion camera/camera_linux.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// +build !cv2,!cv4
// +build !cv2,!cv4,!android

// Package camera.
package camera
Expand Down
8 changes: 8 additions & 0 deletions make.bash
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

0 comments on commit 19ea541

Please sign in to comment.