diff --git a/app/build.gradle b/app/build.gradle
index b656093a..4d13d131 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -59,4 +59,8 @@ dependencies {
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'com.googlecode.mp4parser:isoparser:1.1.21'
+ // implementation 'com.tencent.mars:mars-core:1.2.5'
+ // implementation 'com.tencent:mmkv-static:1.3.0'
+
+
}
\ No newline at end of file
diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json
deleted file mode 100644
index eb879bff..00000000
--- a/app/release/output-metadata.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "version": 3,
- "artifactType": {
- "type": "APK",
- "kind": "Directory"
- },
- "applicationId": "com.xinyingpower.microphoto",
- "variantName": "release",
- "elements": [
- {
- "type": "SINGLE",
- "filters": [],
- "attributes": [],
- "versionCode": 1,
- "versionName": "1.0",
- "outputFile": "app-release.apk"
- }
- ],
- "elementType": "File"
-}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 33ec6bbb..b196c098 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -53,6 +53,7 @@
+
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
+CCamera::CCamera()
+{
+}
+
+CCamera::~CCamera()
+{
+ closeCamera();
+}
+
+int CCamera::initCamera(ANativeWindow *imgReaderAnw) {
+ if (imgReaderAnw == nullptr) {
+ ALOGE("Cannot initialize camera before image reader get initialized.");
+ return -1;
+ }
+
+ mImgReaderAnw = imgReaderAnw;
+ mCameraManager = ACameraManager_create();
+ if (mCameraManager == nullptr) {
+ ALOGE("Failed to create ACameraManager.");
+ return -1;
+ }
+
+ int ret = ACameraManager_getCameraIdList(mCameraManager, &mCameraIdList);
+ if (ret != AMEDIA_OK) {
+ ALOGE("Failed to get cameraIdList: ret=%d", ret);
+ return ret;
+ }
+ if (mCameraIdList->numCameras < 1) {
+ ALOGW("Device has no NDK compatible camera.");
+ return 0;
+ }
+ ALOGI("Found %d camera(s).", mCameraIdList->numCameras);
+
+ // We always use the first camera.
+ mCameraId = mCameraIdList->cameraIds[0];
+ if (mCameraId == nullptr) {
+ ALOGE("Failed to get cameraId.");
+ return -1;
+ }
+
+ ret = ACameraManager_openCamera(mCameraManager, mCameraId, &mDeviceCb,&mDevice);
+ if (ret != AMEDIA_OK || mDevice == nullptr) {
+ ALOGE("Failed to open camera, ret=%d, mDevice=%p.", ret, mDevice);
+ ret = ACAMERA_ERROR_INVALID_PARAMETER;
+ return -1;
+ }
+
+ ret = ACameraManager_getCameraCharacteristics(mCameraManager, mCameraId,
+ &mCameraMetadata);
+ if (ret != ACAMERA_OK || mCameraMetadata == nullptr) {
+ ALOGE("Get camera %s characteristics failure. ret %d, metadata %p",
+ mCameraId, ret, mCameraMetadata);
+ return -1;
+ }
+
+ if (!isCapabilitySupported(
+ ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE)) {
+ ALOGW("Camera does not support BACKWARD_COMPATIBLE.");
+ return 0;
+ }
+
+ // Create capture session
+ ret = ACaptureSessionOutputContainer_create(&mOutputs);
+ if (ret != AMEDIA_OK) {
+ ALOGE("ACaptureSessionOutputContainer_create failed, ret=%d", ret);
+ return ret;
+ }
+ ret = ACaptureSessionOutput_create(mImgReaderAnw, &mImgReaderOutput);
+ if (ret != AMEDIA_OK) {
+ ALOGE("ACaptureSessionOutput_create failed, ret=%d", ret);
+ return ret;
+ }
+ ret = ACaptureSessionOutputContainer_add(mOutputs, mImgReaderOutput);
+ if (ret != AMEDIA_OK) {
+ ALOGE("ACaptureSessionOutputContainer_add failed, ret=%d", ret);
+ return ret;
+ }
+ ret = ACameraDevice_createCaptureSession(mDevice, mOutputs, &mSessionCb,
+ &mSession);
+ if (ret != AMEDIA_OK) {
+ ALOGE("ACameraDevice_createCaptureSession failed, ret=%d", ret);
+ return ret;
+ }
+
+ // Create capture request
+ ret = ACameraDevice_createCaptureRequest(mDevice, TEMPLATE_RECORD,
+ &mCaptureRequest);
+ if (ret != AMEDIA_OK) {
+ ALOGE("ACameraDevice_createCaptureRequest failed, ret=%d", ret);
+ return ret;
+ }
+ ret = ACameraOutputTarget_create(mImgReaderAnw, &mReqImgReaderOutput);
+ if (ret != AMEDIA_OK) {
+ ALOGE("ACameraOutputTarget_create failed, ret=%d", ret);
+ return ret;
+ }
+ ret = ACaptureRequest_addTarget(mCaptureRequest, mReqImgReaderOutput);
+ if (ret != AMEDIA_OK) {
+ ALOGE("ACaptureRequest_addTarget failed, ret=%d", ret);
+ return ret;
+ }
+
+ mIsCameraReady = true;
+ return 0;
+}
+
+bool CCamera::isCapabilitySupported(
+ acamera_metadata_enum_android_request_available_capabilities_t cap) {
+ ACameraMetadata_const_entry entry;
+ ACameraMetadata_getConstEntry(mCameraMetadata,
+ ACAMERA_REQUEST_AVAILABLE_CAPABILITIES, &entry);
+ for (uint32_t i = 0; i < entry.count; i++) {
+ if (entry.data.u8[i] == cap) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void CCamera::closeCamera() {
+ // Destroy capture request
+ if (mReqImgReaderOutput) {
+ ACameraOutputTarget_free(mReqImgReaderOutput);
+ mReqImgReaderOutput = nullptr;
+ }
+ if (mCaptureRequest) {
+ ACaptureRequest_free(mCaptureRequest);
+ mCaptureRequest = nullptr;
+ }
+ // Destroy capture session
+ if (mSession != nullptr) {
+ ACameraCaptureSession_close(mSession);
+ mSession = nullptr;
+ }
+ if (mImgReaderOutput) {
+ ACaptureSessionOutput_free(mImgReaderOutput);
+ mImgReaderOutput = nullptr;
+ }
+ if (mOutputs) {
+ ACaptureSessionOutputContainer_free(mOutputs);
+ mOutputs = nullptr;
+ }
+ // Destroy camera device
+ if (mDevice) {
+ ACameraDevice_close(mDevice);
+ mDevice = nullptr;
+ }
+ if (mCameraMetadata) {
+ ACameraMetadata_free(mCameraMetadata);
+ mCameraMetadata = nullptr;
+ }
+ // Destroy camera manager
+ if (mCameraIdList) {
+ ACameraManager_deleteCameraIdList(mCameraIdList);
+ mCameraIdList = nullptr;
+ }
+ if (mCameraManager) {
+ ACameraManager_delete(mCameraManager);
+ mCameraManager = nullptr;
+ }
+ mIsCameraReady = false;
+}
+
+int CCamera::takePicture()
+{
+ return ACameraCaptureSession_capture(mSession, nullptr, 1, &mCaptureRequest,
+ nullptr);
+}
diff --git a/app/src/main/cpp/Camera.h b/app/src/main/cpp/Camera.h
new file mode 100644
index 00000000..59e99156
--- /dev/null
+++ b/app/src/main/cpp/Camera.h
@@ -0,0 +1,73 @@
+#ifndef __CAMERA_H__
+#define __CAMERA_H__
+
+
+
+// Must come before NdkCameraCaptureSession.h
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+class CCamera
+{
+public:
+ CCamera();
+ ~CCamera();
+
+ int initCamera(ANativeWindow *imgReaderAnw);
+ bool isCapabilitySupported(acamera_metadata_enum_android_request_available_capabilities_t cap);
+ bool isCameraReady() { return mIsCameraReady; }
+ void closeCamera();
+ int takePicture();
+
+ static void onDeviceDisconnected(void * /*obj*/, ACameraDevice * /*device*/) {}
+ static void onDeviceError(void * /*obj*/, ACameraDevice * /*device*/,
+ int /*errorCode*/) {}
+ static void onSessionClosed(void * /*obj*/,
+ ACameraCaptureSession * /*session*/) {}
+ static void onSessionReady(void * /*obj*/,
+ ACameraCaptureSession * /*session*/) {}
+ static void onSessionActive(void * /*obj*/,
+ ACameraCaptureSession * /*session*/) {}
+
+private:
+ ACameraDevice_StateCallbacks mDeviceCb{ this, onDeviceDisconnected,
+ onDeviceError };
+ ACameraCaptureSession_stateCallbacks mSessionCb{
+ this, onSessionClosed, onSessionReady, onSessionActive };
+
+ ANativeWindow *mImgReaderAnw{ nullptr }; // not owned by us.
+
+ // Camera manager
+ ACameraManager *mCameraManager{ nullptr };
+ ACameraIdList *mCameraIdList{ nullptr };
+ // Camera device
+ ACameraMetadata *mCameraMetadata{ nullptr };
+ ACameraDevice *mDevice{ nullptr };
+ // Capture session
+ ACaptureSessionOutputContainer *mOutputs{ nullptr };
+ ACaptureSessionOutput *mImgReaderOutput{ nullptr };
+ ACameraCaptureSession *mSession{ nullptr };
+ // Capture request
+ ACaptureRequest *mCaptureRequest{ nullptr };
+ ACameraOutputTarget *mReqImgReaderOutput{ nullptr };
+
+ bool mIsCameraReady{ false };
+ const char *mCameraId{ nullptr };
+
+private:
+
+};
+
+
+#endif // __CAMERA_H__
\ No newline at end of file
diff --git a/app/src/main/cpp/Camera2Reader.cpp b/app/src/main/cpp/Camera2Reader.cpp
new file mode 100644
index 00000000..4b204c10
--- /dev/null
+++ b/app/src/main/cpp/Camera2Reader.cpp
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2015 Rockchip Electronics Co. LTD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define MODULE_TAG "CameraReader"
+
+// #include "mpp_log.h"
+// #include "mpp_mem.h"
+#include "Camera2Reader.h"
+#include
+
+#include
+#include
+#include
+
+Camera2Reader::Camera2Reader(const char *device, int bufcnt, int width, int height, int fmt)
+ : mBufcnt(bufcnt),
+ mWidth(width),
+ mHeight(height),
+ mFmt(fmt)
+{
+ strcpy(mDevice, device);
+}
+Camera2Reader::Camera2Reader(int cameraId) : mCameraId(cameraId)
+{
+}
+
+int Camera2Reader::Init(const char *device, int bufcnt, int width, int height, int format)
+{
+ return 0;
+}
+
+// Free a context to capture frames from .
+// Returns NULL on error.
+int Camera2Reader::Deinit()
+{
+ return 0;
+}
+
+bool Camera2Reader::Open(const char* path, const char* fileName)
+{
+ mPath.assign(path);
+ mFileName.assign(fileName);
+
+ ACameraIdList *cameraIdList = NULL;
+ ACameraMetadata *cameraMetadata = NULL;
+
+ const char *selectedCameraId = NULL;
+ camera_status_t camera_status = ACAMERA_OK;
+ ACameraManager *cameraManager = ACameraManager_create();
+
+ camera_status = ACameraManager_getCameraIdList(cameraManager, &cameraIdList);
+ if (camera_status != ACAMERA_OK) {
+ LOGI("Failed to get camera id list (reason: %d)\n", camera_status);
+ return false;
+ }
+
+ if (cameraIdList->numCameras < 1 ) {
+ LOGI("No camera device detected.\n");
+ return false;
+ }
+
+ if (cameraIdList->numCameras <= mCameraId ) {
+ LOGI("No required camera device %d detected.\n", mCameraId);
+ return false;
+ }
+
+ selectedCameraId = cameraIdList->cameraIds[mCameraId];
+
+ LOGI("Trying to open Camera2 (id: %s, num of camera : %d)\n", selectedCameraId,
+ cameraIdList->numCameras);
+
+ camera_status = ACameraManager_getCameraCharacteristics(cameraManager, selectedCameraId,
+ &cameraMetadata);
+
+ if (camera_status != ACAMERA_OK) {
+ LOGI("Failed to get camera meta data of ID:%s\n", selectedCameraId);
+ }
+
+ deviceStateCallbacks.onDisconnected = camera_device_on_disconnected;
+ deviceStateCallbacks.onError = camera_device_on_error;
+
+ camera_status = ACameraManager_openCamera(cameraManager, selectedCameraId,
+ &deviceStateCallbacks, &cameraDevice);
+
+ if (camera_status != ACAMERA_OK) {
+ LOGI("Failed to open camera device (id: %s)\n", selectedCameraId);
+ }
+
+ camera_status = ACameraDevice_createCaptureRequest(cameraDevice, TEMPLATE_PREVIEW,
+ &captureRequest);
+
+ if (camera_status != ACAMERA_OK) {
+ LOGI("Failed to create preview capture request (id: %s)\n", selectedCameraId);
+ }
+
+ ACaptureSessionOutputContainer_create(&captureSessionOutputContainer);
+
+ captureSessionStateCallbacks.onReady = capture_session_on_ready;
+ captureSessionStateCallbacks.onActive = capture_session_on_active;
+ captureSessionStateCallbacks.onClosed = capture_session_on_closed;
+
+ ACameraMetadata_free(cameraMetadata);
+ ACameraManager_deleteCameraIdList(cameraIdList);
+ ACameraManager_delete(cameraManager);
+
+ media_status_t status;
+ // status = AImageReader_new(1920, 1080, AIMAGE_FORMAT_YUV_420_888, 5, &mAImageReader);
+ status = AImageReader_new(1920, 1080, AIMAGE_FORMAT_JPEG, 5, &mAImageReader);
+ if (status != AMEDIA_OK)
+ {
+ LOGI("AImageReader_new error\n");
+ return false;
+ }
+
+ AImageReader_ImageListener listener{
+ .context = this,
+ .onImageAvailable = OnImageCallback,
+ };
+ AImageReader_setImageListener(mAImageReader, &listener);
+
+ //ANativeWindow *mNativeWindow;
+ status = AImageReader_getWindow(mAImageReader, &theNativeWindow);
+ if (status != AMEDIA_OK)
+ {
+ LOGI("AImageReader_getWindow error\n");
+ return false;
+ }
+
+ LOGI("Surface is prepared in %p.\n", theNativeWindow);
+
+ ACameraOutputTarget_create(theNativeWindow, &cameraOutputTarget);
+ ACaptureRequest_addTarget(captureRequest, cameraOutputTarget);
+
+ ACaptureSessionOutput_create(theNativeWindow, &sessionOutput);
+ ACaptureSessionOutputContainer_add(captureSessionOutputContainer, sessionOutput);
+
+ ACameraDevice_createCaptureSession(cameraDevice, captureSessionOutputContainer,
+ &captureSessionStateCallbacks, &captureSession);
+
+ // ACameraCaptureSession_setRepeatingRequest(captureSession, NULL, 1, &captureRequest, NULL);
+ ACameraCaptureSession_capture(captureSession, NULL, 1, &captureRequest, NULL);
+ LOGI("Surface is prepared in here.\n");
+ return true;
+}
+
+ACameraCaptureSession_stateCallbacks *Camera2Reader::GetSessionListener()
+{
+ static ACameraCaptureSession_stateCallbacks sessionListener = {
+ .context = this,
+ .onClosed = Camera2Reader::capture_session_on_closed,
+ .onReady = Camera2Reader::capture_session_on_ready,
+ .onActive = Camera2Reader::capture_session_on_active,
+ };
+ return &sessionListener;
+}
+
+void Camera2Reader::ImageCallback(AImageReader *reader)
+{
+ int32_t format;
+ AImage *image = nullptr;
+ media_status_t status = AImageReader_acquireNextImage(reader, &image);
+ LOGI("ImageCallback\n");
+ if (status == AMEDIA_OK && image)
+ {
+ LOGI("ImageCallback\n");
+ AImage_delete(image);
+ }
+}
+void Camera2Reader::OnImageCallback(void *ctx, AImageReader *reader)
+{
+ Camera2Reader* pThis = reinterpret_cast(ctx);
+ AImage *image = nullptr;
+ media_status_t status = AImageReader_acquireNextImage(reader, &image);
+ if (status == AMEDIA_OK && image)
+ {
+ WriteFile(pThis, image, pThis->mPath);
+ // image.
+ AImage_delete(image);
+ pThis->Deinit();
+ delete pThis;
+ }
+}
+
+bool Camera2Reader::readyToRun()
+{
+ if (!Init(mDevice, mBufcnt, mWidth, mHeight, mFmt))
+ {
+ LOGI("Init false\n");
+ return false;
+ }
+
+ return Open("", "");
+}
+void Camera2Reader::start()
+{
+ //run();
+}
+
+void Camera2Reader::stop()
+{
+ //threadStop();
+}
+
+bool Camera2Reader::threadLoop()
+{
+ usleep(1000);
+ return true;
+}
+
+Camera2Reader::~Camera2Reader()
+{
+ LOGI("~CameraReader\n");
+}
+
+void *Camera2Reader::readImage(int chn)
+{
+ return NULL;
+}
+
+
+void Camera2Reader::WriteFile(Camera2Reader* pThis, AImage *image, const std::string& path)
+{
+ // static const char *kFileName = "capture";
+ int planeCount;
+ media_status_t status = AImage_getNumberOfPlanes(image, &planeCount);
+
+ LOGI("Info: getNumberOfPlanes() planeCount = %d", planeCount);
+ if (!(status == AMEDIA_OK && planeCount == 1))
+ {
+ LOGE("Error: getNumberOfPlanes() planeCount = %d", planeCount);
+ return;
+ }
+
+ uint8_t *data = nullptr;
+ int len = 0;
+ AImage_getPlaneData(image, 0, &data, &len);
+
+ DIR *dir = opendir(path.c_str());
+ if (dir)
+ {
+ closedir(dir);
+ }
+ else
+ {
+ std::string cmd = "mkdir -p ";
+ cmd += path;
+ system(cmd.c_str());
+ }
+
+ std::string fileName = path + pThis->mFileName;
+
+ FILE *file = fopen(fileName.c_str(), "wb");
+ if (file && data && len)
+ {
+ fwrite(data, 1, len, file);
+ fclose(file);
+
+ LOGE("Capture: %s", fileName.c_str());
+ }
+ else
+ {
+ if (file)
+ fclose(file);
+ }
+}
diff --git a/app/src/main/cpp/Camera2Reader.h b/app/src/main/cpp/Camera2Reader.h
new file mode 100644
index 00000000..15c8bc78
--- /dev/null
+++ b/app/src/main/cpp/Camera2Reader.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2015 Rockchip Electronics Co. LTD
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CAMERA2_READER_H__
+#define __CAMERA2_READER_H__
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#define LOG_TAG "native-camera2-jni"
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
+#define FMT_NUM_PLANES 1
+
+// struct CameraParam
+// {
+// int id;
+// const char *device;
+// RK_U32 bufcnt;
+// RK_U32 width;
+// RK_U32 height;
+// MppFrameFormat fmt;
+// };
+
+#include
+
+class Camera2Reader
+{
+private:
+ AImageReader *mAImageReader;
+ ANativeWindow *theNativeWindow;
+ ACameraDevice *cameraDevice;
+ ACaptureRequest *captureRequest;
+ ACameraOutputTarget *cameraOutputTarget;
+ ACaptureSessionOutput *sessionOutput;
+ ACaptureSessionOutputContainer *captureSessionOutputContainer;
+ ACameraCaptureSession *captureSession;
+
+ ACameraDevice_StateCallbacks deviceStateCallbacks;
+ ACameraCaptureSession_stateCallbacks captureSessionStateCallbacks;
+
+ int sessionSequenceId;
+
+ int mCameraId = 0;
+ char mDevice[20];
+ int mBufcnt;
+ int mWidth;
+ int mHeight;
+ int mFmt;
+
+ std::string mPath;
+ std::string mFileName;
+
+ unsigned char *mYuv720p = NULL;
+ unsigned char *mYuv420i = NULL;
+ unsigned char *mArgb1080p = NULL;
+ // Create a new context to capture frames from . Returns NULL on error.
+ int Init(const char *device, int bufcnt, int width, int height, int fmt);
+
+ // Stop capturing and free a context.
+ int Deinit();
+ bool readyToRun();
+ bool threadLoop();
+ ACameraCaptureSession_stateCallbacks *GetSessionListener();
+ void ImageCallback(AImageReader *reader);
+ static void OnImageCallback(void *ctx, AImageReader *reader);
+
+ static void camera_device_on_disconnected(void *context, ACameraDevice *device)
+ {
+ LOGI("Camera(id: %s) is diconnected.\n", ACameraDevice_getId(device));
+ }
+
+ static void camera_device_on_error(void *context, ACameraDevice *device, int error)
+ {
+ LOGI("Error(code: %d) on Camera(id: %s).\n", error, ACameraDevice_getId(device));
+ }
+
+ static void capture_session_on_ready(void *context, ACameraCaptureSession *session)
+ {
+ LOGI("Session is ready. %p\n", session);
+ }
+
+ static void capture_session_on_active(void *context, ACameraCaptureSession *session)
+ {
+ LOGI("Session is activated. %p\n", session);
+ }
+
+ static void capture_session_on_closed(void *context, ACameraCaptureSession *session)
+ {
+ LOGI("Session is closed. %p\n", session);
+ }
+
+ static void WriteFile(Camera2Reader* pThis, AImage *image, const std::string& path);
+
+public:
+ bool Open(const char* path, const char* fileName);
+ Camera2Reader(const char *device, int bufcnt, int width, int height, int fmt);
+ Camera2Reader(int cameraId);
+ void *readImage(int chn);
+ // Camera2Reader &operator=(CameraParam *cameraParam);
+ ~Camera2Reader();
+ void start();
+ void stop();
+};
+#endif /* __CAMERA_READER_H__ */
diff --git a/app/src/main/cpp/MicroPhoto.cpp b/app/src/main/cpp/MicroPhoto.cpp
index 21cffe21..e274651a 100644
--- a/app/src/main/cpp/MicroPhoto.cpp
+++ b/app/src/main/cpp/MicroPhoto.cpp
@@ -2,6 +2,17 @@
#include
#include
#include "TerminalDevice.h"
+#include "PhoneDevice.h"
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "Camera.h"
+#include "Camera2Reader.h"
// #include "client/linux/handler/exception_handler.h"
// #include "client/linux/handler/minidump_descriptor.h"
@@ -14,6 +25,45 @@ Java_com_xinyingpower_microphoto_MainActivity_stringFromJNI(
return env->NewStringUTF(hello.c_str());
}
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_xinyingpower_microphoto_MainActivity_takePhoto(
+ JNIEnv* env,
+ jobject pThis, jint channel, jint preset, jstring path, jstring fileName) {
+
+ // ANativeWindow *imgReaderAnw = ANativeWindow_fromSurface(env, surface);
+
+ /*
+ CCamera camera;
+ camera.initCamera(imgReaderAnw);
+ if (camera.isCameraReady())
+ {
+ camera.takePicture();
+ }
+
+ camera.closeCamera();
+ */
+
+ if (channel < 1 || channel > 0xFF)
+ {
+ return JNI_FALSE;
+ }
+
+ unsigned char id = (unsigned char)channel - 1;
+
+
+ Camera2Reader *camera = new Camera2Reader(id);
+ const char *pathStr = env->GetStringUTFChars(path, 0);
+ const char *fileNameStr = env->GetStringUTFChars(fileName, 0);
+
+ camera->Open(pathStr, fileNameStr);
+ env->ReleaseStringUTFChars(fileName, fileNameStr);
+ env->ReleaseStringUTFChars(path, pathStr);
+
+ camera->start();
+
+ return JNI_TRUE;
+}
+
extern "C" JNIEXPORT jboolean JNICALL
Java_com_xinyingpower_microphoto_MicroPhotoService_init(
JNIEnv* env,
@@ -34,7 +84,8 @@ Java_com_xinyingpower_microphoto_MicroPhotoService_init(
jint ret = env->GetJavaVM(&vm);
// const string& appPath, const string& termId, const string& server, unsigned short port, const string& bindIp
CTermClient& service = CTermClient::GetService();
- CTerminalDevice* device = new CTerminalDevice(vm, pThis);
+ // CTerminalDevice* device = new CTerminalDevice(vm, pThis);
+ CPhoneDevice* device = new CPhoneDevice();
bool res = service.InitService(appPathStr, cmdidStr, ipStr, (unsigned short)port, "", device);
env->ReleaseStringUTFChars(appPath, appPathStr);
@@ -44,6 +95,32 @@ Java_com_xinyingpower_microphoto_MicroPhotoService_init(
return res ? JNI_TRUE : JNI_FALSE;
}
+
+extern "C" JNIEXPORT jboolean JNICALL
+Java_com_xinyingpower_microphoto_MicroPhotoService_takePhoto(
+ JNIEnv* env,
+ jobject pThis, jint channel, jint preset, jstring path, jstring fileName) {
+
+ if (channel < 1 || channel > 0xFF)
+ {
+ return JNI_FALSE;
+ }
+
+ unsigned char id = (unsigned char)channel - 1;
+
+ Camera2Reader *camera = new Camera2Reader(id);
+ const char *pathStr = env->GetStringUTFChars(path, 0);
+ const char *fileNameStr = env->GetStringUTFChars(fileName, 0);
+
+ camera->Open(pathStr, fileNameStr);
+ env->ReleaseStringUTFChars(fileName, fileNameStr);
+ env->ReleaseStringUTFChars(path, pathStr);
+
+ camera->start();
+
+ return JNI_TRUE;
+}
+
extern "C" JNIEXPORT jboolean JNICALL
Java_com_xinyingpower_microphoto_MicroPhotoService_uninit(
JNIEnv* env,
@@ -53,3 +130,15 @@ Java_com_xinyingpower_microphoto_MicroPhotoService_uninit(
return JNI_TRUE;
}
+
+
+extern "C" JNIEXPORT jlong JNICALL
+Java_com_xinyingpower_microphoto_MicroPhotoService_getHeartbeatDuration(
+ JNIEnv* env,
+ jobject pThis) {
+
+ // CTermClient::GetService().ExitService();
+
+ return 60000;
+}
+
diff --git a/app/src/main/cpp/PhoneDevice.cpp b/app/src/main/cpp/PhoneDevice.cpp
new file mode 100644
index 00000000..94c06f82
--- /dev/null
+++ b/app/src/main/cpp/PhoneDevice.cpp
@@ -0,0 +1,269 @@
+#include "TerminalDevice.h"
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#define LOG_TAG "CameraTestHelpers"
+
+#include "PhoneDevice.h"
+
+#include
+
+#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
+CPhoneDevice::CPhoneDevice()
+{
+}
+
+CPhoneDevice::~CPhoneDevice()
+{
+
+}
+
+
+IDevice::timer_uid_t CPhoneDevice::registerTimer(unsigned int timerType, unsigned int timeout)
+{
+
+ return 0;
+}
+
+bool CPhoneDevice::unregisterTimer(IDevice::timer_uid_t uid)
+{
+ return true;
+}
+
+bool CPhoneDevice::onTimeout(IDevice::timer_uid_t uid, unsigned int timerType, unsigned int times)
+{
+ return true;
+}
+
+
+bool CPhoneDevice::TakePhoto(unsigned char channel, unsigned char preset, const string& path, bool photo)
+{
+ int cameraId = (int)channel - 1;
+
+ ACameraIdList *cameraIdList = NULL;
+ ACameraMetadata *cameraMetadata = NULL;
+
+ const char *selectedCameraId = NULL;
+ camera_status_t camera_status = ACAMERA_OK;
+ ACameraManager *cameraManager = ACameraManager_create();
+
+ camera_status = ACameraManager_getCameraIdList(cameraManager, &cameraIdList);
+ if (camera_status != ACAMERA_OK) {
+ LOGI("Failed to get camera id list (reason: %d)\n", camera_status);
+ return false;
+ }
+
+ if (cameraIdList->numCameras < 1 ) {
+ LOGI("No camera device detected.\n");
+ return false;
+ }
+
+ if (cameraIdList->numCameras <= cameraId ) {
+ LOGI("No required camera device %d detected.\n", cameraId);
+ return false;
+ }
+
+ selectedCameraId = cameraIdList->cameraIds[cameraId];
+
+ LOGI("Trying to open Camera2 (id: %s, num of camera : %d)\n", selectedCameraId,
+ cameraIdList->numCameras);
+
+ camera_status = ACameraManager_getCameraCharacteristics(cameraManager, selectedCameraId,
+ &cameraMetadata);
+
+ if (camera_status != ACAMERA_OK) {
+ LOGI("Failed to get camera meta data of ID:%s\n", selectedCameraId);
+ }
+
+ deviceStateCallbacks.onDisconnected = camera_device_on_disconnected;
+ deviceStateCallbacks.onError = camera_device_on_error;
+
+ camera_status = ACameraManager_openCamera(cameraManager, selectedCameraId,
+ &deviceStateCallbacks, &cameraDevice);
+
+ if (camera_status != ACAMERA_OK) {
+ LOGI("Failed to open camera device (id: %s)\n", selectedCameraId);
+ }
+
+ camera_status = ACameraDevice_createCaptureRequest(cameraDevice, TEMPLATE_PREVIEW,
+ &captureRequest);
+
+ if (camera_status != ACAMERA_OK) {
+ LOGI("Failed to create preview capture request (id: %s)\n", selectedCameraId);
+ }
+
+ ACaptureSessionOutputContainer_create(&captureSessionOutputContainer);
+
+ captureSessionStateCallbacks.onReady = capture_session_on_ready;
+ captureSessionStateCallbacks.onActive = capture_session_on_active;
+ captureSessionStateCallbacks.onClosed = capture_session_on_closed;
+
+ ACameraMetadata_free(cameraMetadata);
+ ACameraManager_deleteCameraIdList(cameraIdList);
+ ACameraManager_delete(cameraManager);
+
+ media_status_t status;
+ // status = AImageReader_new(1920, 1080, AIMAGE_FORMAT_YUV_420_888, 5, &mAImageReader);
+ status = AImageReader_new(1920, 1080, AIMAGE_FORMAT_JPEG, 5, &mAImageReader);
+ if (status != AMEDIA_OK)
+ {
+ LOGI("AImageReader_new error\n");
+ return false;
+ }
+
+ AImageReader_ImageListener listener{
+ .context = this,
+ .onImageAvailable = OnImageCallback,
+ };
+ AImageReader_setImageListener(mAImageReader, &listener);
+
+ //ANativeWindow *mNativeWindow;
+ status = AImageReader_getWindow(mAImageReader, &theNativeWindow);
+ if (status != AMEDIA_OK)
+ {
+ LOGI("AImageReader_getWindow error\n");
+ return false;
+ }
+
+ LOGI("Surface is prepared in %p.\n", theNativeWindow);
+
+ ACameraOutputTarget_create(theNativeWindow, &cameraOutputTarget);
+ ACaptureRequest_addTarget(captureRequest, cameraOutputTarget);
+
+ ACaptureSessionOutput_create(theNativeWindow, &sessionOutput);
+ ACaptureSessionOutputContainer_add(captureSessionOutputContainer, sessionOutput);
+
+ ACameraDevice_createCaptureSession(cameraDevice, captureSessionOutputContainer,
+ &captureSessionStateCallbacks, &captureSession);
+
+ // ACameraCaptureSession_setRepeatingRequest(captureSession, NULL, 1, &captureRequest, NULL);
+ ACameraCaptureSession_capture(captureSession, NULL, 1, &captureRequest, NULL);
+ LOGI("Surface is prepared in here.\n");
+
+
+ return true;
+}
+
+
+ACameraCaptureSession_stateCallbacks* CPhoneDevice::GetSessionListener()
+{
+ static ACameraCaptureSession_stateCallbacks sessionListener = {
+ .context = this,
+ .onClosed = CPhoneDevice::capture_session_on_closed,
+ .onReady = CPhoneDevice::capture_session_on_ready,
+ .onActive = CPhoneDevice::capture_session_on_active,
+ };
+ return &sessionListener;
+}
+
+void CPhoneDevice::ImageCallback(AImageReader *reader)
+{
+ int32_t format;
+ AImage *image = nullptr;
+ media_status_t status = AImageReader_acquireNextImage(reader, &image);
+ LOGI("ImageCallback\n");
+ if (status == AMEDIA_OK && image)
+ {
+ LOGI("ImageCallback\n");
+ AImage_delete(image);
+ }
+}
+void CPhoneDevice::OnImageCallback(void *ctx, AImageReader *reader)
+{
+ CPhoneDevice* pThis = reinterpret_cast(ctx);
+ AImage *image = nullptr;
+ media_status_t status = AImageReader_acquireNextImage(reader, &image);
+ if (status == AMEDIA_OK && image)
+ {
+ WriteFile(pThis, image);
+ AImage_delete(image);
+
+ // delete pThis;
+ }
+}
+
+bool CPhoneDevice::WriteFile(CPhoneDevice* pThis, AImage *image)
+{
+ int planeCount = 0;
+ media_status_t status = AImage_getNumberOfPlanes(image, &planeCount);
+
+ LOGI("Info: getNumberOfPlanes() planeCount = %d", planeCount);
+ if (!(status == AMEDIA_OK && planeCount == 1))
+ {
+ LOGE("Error: getNumberOfPlanes() planeCount = %d", planeCount);
+ return false;
+ }
+
+ uint8_t *data = nullptr;
+ int len = 0;
+ AImage_getPlaneData(image, 0, &data, &len);
+
+ std::string path = pThis->GetFileName();
+
+ bool res = false;
+ FILE *file = fopen(path.c_str(), "wb");
+ if (file && data && len)
+ {
+ fwrite(data, 1, len, file);
+ fclose(file);
+
+ LOGE("Capture: %s", path.c_str());
+ res = true;
+ }
+ else
+ {
+ if (file)
+ fclose(file);
+ }
+
+ return res;
+}
+
+std::string CPhoneDevice::GetFileName() const
+{
+ return mPath;
+}
+
+void CPhoneDevice::camera_device_on_disconnected(void *context, ACameraDevice *device)
+{
+ LOGI("Camera(id: %s) is diconnected.\n", ACameraDevice_getId(device));
+ CPhoneDevice* pThis = (CPhoneDevice*)context;
+ delete pThis;
+}
+
+void CPhoneDevice::camera_device_on_error(void *context, ACameraDevice *device, int error)
+{
+ LOGI("Error(code: %d) on Camera(id: %s).\n", error, ACameraDevice_getId(device));
+}
+
+void CPhoneDevice::capture_session_on_ready(void *context, ACameraCaptureSession *session)
+{
+ LOGI("Session is ready. %p\n", session);
+}
+
+void CPhoneDevice::capture_session_on_active(void *context, ACameraCaptureSession *session)
+{
+ LOGI("Session is activated. %p\n", session);
+}
+
+void CPhoneDevice::capture_session_on_closed(void *context, ACameraCaptureSession *session)
+{
+ LOGI("Session is closed. %p\n", session);
+}
\ No newline at end of file
diff --git a/app/src/main/cpp/PhoneDevice.h b/app/src/main/cpp/PhoneDevice.h
new file mode 100644
index 00000000..f0ef8c6d
--- /dev/null
+++ b/app/src/main/cpp/PhoneDevice.h
@@ -0,0 +1,70 @@
+#ifndef __PHONE_DEVICE_H__
+#define __PHONE_DEVICE_H__
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+// #define LOG_TAG "native-camera2-jni"
+#define PD_LOG_TAG "PhoneDev"
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,PD_LOG_TAG,__VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,PD_LOG_TAG,__VA_ARGS__)
+#include
+#include
+
+class CPhoneDevice : public IDevice
+{
+public:
+ CPhoneDevice();
+ ~CPhoneDevice();
+
+ virtual bool TakePhoto(unsigned char channel, unsigned char preset, const string& path, bool photo);
+ virtual timer_uid_t registerTimer(unsigned int timerType, unsigned int timeout);
+ virtual bool unregisterTimer(timer_uid_t uid);
+ virtual bool onTimeout(timer_uid_t uid, unsigned int timerType, unsigned int times);
+
+protected:
+
+ ACameraCaptureSession_stateCallbacks *GetSessionListener();
+ std::string GetFileName() const;
+
+ static void camera_device_on_disconnected(void *context, ACameraDevice *device);
+ static void camera_device_on_error(void *context, ACameraDevice *device, int error);
+ static void capture_session_on_ready(void *context, ACameraCaptureSession *session);
+ static void capture_session_on_active(void *context, ACameraCaptureSession *session);
+ static void capture_session_on_closed(void *context, ACameraCaptureSession *session);
+
+ void ImageCallback(AImageReader *reader);
+ static void OnImageCallback(void *ctx, AImageReader *reader);
+ static bool WriteFile(CPhoneDevice* pThis, AImage *image);
+
+protected:
+
+ std::string mPath;
+
+ AImageReader *mAImageReader;
+ ANativeWindow *theNativeWindow;
+ ACameraDevice *cameraDevice;
+ ACaptureRequest *captureRequest;
+ ACameraOutputTarget *cameraOutputTarget;
+ ACaptureSessionOutput *sessionOutput;
+ ACaptureSessionOutputContainer *captureSessionOutputContainer;
+ ACameraCaptureSession *captureSession;
+
+ ACameraDevice_StateCallbacks deviceStateCallbacks;
+ ACameraCaptureSession_stateCallbacks captureSessionStateCallbacks;
+
+};
+
+
+#endif // __PHONE_DEVICE_H__
\ No newline at end of file
diff --git a/app/src/main/cpp/TerminalDevice.cpp b/app/src/main/cpp/TerminalDevice.cpp
index 50e79bbf..fef0ded0 100644
--- a/app/src/main/cpp/TerminalDevice.cpp
+++ b/app/src/main/cpp/TerminalDevice.cpp
@@ -1,5 +1,6 @@
#include "TerminalDevice.h"
#include
+#include "Camera.h"
typedef jbyteArray (*TakePhotoFunc)(int, int, int, int);
@@ -50,12 +51,24 @@ CTerminalDevice::~CTerminalDevice()
bool CTerminalDevice::TakePhoto(unsigned char channel, unsigned char preset, const string& path, bool photo)
{
+ jboolean res = JNI_FALSE;
+
+ CCamera camera;
+ camera.initCamera(NULL);
+ if (camera.isCameraReady())
+ {
+ camera.takePicture();
+ }
+
+ camera.closeCamera();
+
+#if 0
JNIEnv* env = NULL;
bool attached = GetJniEnv(m_vm, &env);
jclass serviceClass = env->GetObjectClass(m_javaService);
jmethodID mid = env->GetMethodID(serviceClass, "takePhoto", "(SSLjava/lang/String;)Z");
jstring str = env->NewStringUTF(path.c_str());
- jboolean res = env->CallBooleanMethod (m_javaService, mid, (jint)channel, (jint)preset, str);
+ res = env->CallBooleanMethod (m_javaService, mid, (jint)channel, (jint)preset, str);
env->ReleaseStringUTFChars(str, path.c_str());
env->DeleteLocalRef(serviceClass);
@@ -67,6 +80,8 @@ bool CTerminalDevice::TakePhoto(unsigned char channel, unsigned char preset, con
{
m_vm->DetachCurrentThread();
}
+#endif
+
return res == JNI_TRUE;
}
diff --git a/app/src/main/cpp/camera2/android_main.cpp b/app/src/main/cpp/camera2/android_main.cpp
new file mode 100644
index 00000000..f0b2e855
--- /dev/null
+++ b/app/src/main/cpp/camera2/android_main.cpp
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "camera_engine.h"
+#include "utils/native_debug.h"
+
+/*
+ * SampleEngine global object
+ */
+static CameraEngine* pEngineObj = nullptr;
+CameraEngine* GetAppEngine(void) {
+ ASSERT(pEngineObj, "AppEngine has not initialized");
+ return pEngineObj;
+}
+
+/**
+ * Teamplate function for NativeActivity derived applications
+ * Create/Delete camera object with
+ * INIT_WINDOW/TERM_WINDOW command, ignoring other event.
+ */
+static void ProcessAndroidCmd(struct android_app* app, int32_t cmd) {
+ CameraEngine* engine = reinterpret_cast(app->userData);
+ switch (cmd) {
+ case APP_CMD_INIT_WINDOW:
+ if (engine->AndroidApp()->window != NULL) {
+ engine->SaveNativeWinRes(ANativeWindow_getWidth(app->window),
+ ANativeWindow_getHeight(app->window),
+ ANativeWindow_getFormat(app->window));
+ engine->OnAppInitWindow();
+ }
+ break;
+ case APP_CMD_TERM_WINDOW:
+ engine->OnAppTermWindow();
+ ANativeWindow_setBuffersGeometry(
+ app->window, engine->GetSavedNativeWinWidth(),
+ engine->GetSavedNativeWinHeight(), engine->GetSavedNativeWinFormat());
+ break;
+ case APP_CMD_CONFIG_CHANGED:
+ engine->OnAppConfigChange();
+ break;
+ case APP_CMD_LOST_FOCUS:
+ break;
+ }
+}
+
+extern "C" void android_main(struct android_app* state) {
+ CameraEngine engine(state);
+ pEngineObj = &engine;
+
+ state->userData = reinterpret_cast(&engine);
+ state->onAppCmd = ProcessAndroidCmd;
+
+ // loop waiting for stuff to do.
+ while (1) {
+ // Read all pending events.
+ int events;
+ struct android_poll_source* source;
+
+ while (ALooper_pollAll(0, NULL, &events, (void**)&source) >= 0) {
+ // Process this event.
+ if (source != NULL) {
+ source->process(state, source);
+ }
+
+ // Check if we are exiting.
+ if (state->destroyRequested != 0) {
+ LOGI("CameraEngine thread destroy requested!");
+ engine.DeleteCamera();
+ pEngineObj = nullptr;
+ return;
+ }
+ }
+ pEngineObj->DrawFrame();
+ }
+}
+
+/**
+ * Handle Android System APP_CMD_INIT_WINDOW message
+ * Request camera persmission from Java side
+ * Create camera object if camera has been granted
+ */
+void CameraEngine::OnAppInitWindow(void) {
+ if (!cameraGranted_) {
+ // Not permitted to use camera yet, ask(again) and defer other events
+ RequestCameraPermission();
+ return;
+ }
+
+ rotation_ = GetDisplayRotation();
+
+ CreateCamera();
+ ASSERT(camera_, "CameraCreation Failed");
+
+ EnableUI();
+
+ // NativeActivity end is ready to display, start pulling images
+ cameraReady_ = true;
+ camera_->StartPreview(true);
+}
+
+/**
+ * Handle APP_CMD_TEMR_WINDOW
+ */
+void CameraEngine::OnAppTermWindow(void) {
+ cameraReady_ = false;
+ DeleteCamera();
+}
+
+/**
+ * Handle APP_CMD_CONFIG_CHANGED
+ */
+void CameraEngine::OnAppConfigChange(void) {
+ int newRotation = GetDisplayRotation();
+
+ if (newRotation != rotation_) {
+ OnAppTermWindow();
+
+ rotation_ = newRotation;
+ OnAppInitWindow();
+ }
+}
+
+/**
+ * Retrieve saved native window width.
+ * @return width of native window
+ */
+int32_t CameraEngine::GetSavedNativeWinWidth(void) {
+ return savedNativeWinRes_.width;
+}
+
+/**
+ * Retrieve saved native window height.
+ * @return height of native window
+ */
+int32_t CameraEngine::GetSavedNativeWinHeight(void) {
+ return savedNativeWinRes_.height;
+}
+
+/**
+ * Retrieve saved native window format
+ * @return format of native window
+ */
+int32_t CameraEngine::GetSavedNativeWinFormat(void) {
+ return savedNativeWinRes_.format;
+}
+
+/**
+ * Save original NativeWindow Resolution
+ * @param w width of native window in pixel
+ * @param h height of native window in pixel
+ * @param format
+ */
+void CameraEngine::SaveNativeWinRes(int32_t w, int32_t h, int32_t format) {
+ savedNativeWinRes_.width = w;
+ savedNativeWinRes_.height = h;
+ savedNativeWinRes_.format = format;
+}
diff --git a/app/src/main/cpp/camera2/camera_engine.cpp b/app/src/main/cpp/camera2/camera_engine.cpp
new file mode 100644
index 00000000..b99bd5cb
--- /dev/null
+++ b/app/src/main/cpp/camera2/camera_engine.cpp
@@ -0,0 +1,175 @@
+/**
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** Description
+ * Demonstrate NDK Camera interface added to android-24
+ */
+
+#include "camera_engine.h"
+
+#include
+
+#include "utils/native_debug.h"
+
+/**
+ * constructor and destructor for main application class
+ * @param app native_app_glue environment
+ * @return none
+ */
+CameraEngine::CameraEngine(android_app* app)
+ : app_(app),
+ cameraGranted_(false),
+ rotation_(0),
+ cameraReady_(false),
+ camera_(nullptr),
+ yuvReader_(nullptr),
+ jpgReader_(nullptr) {
+ memset(&savedNativeWinRes_, 0, sizeof(savedNativeWinRes_));
+}
+
+CameraEngine::~CameraEngine() {
+ cameraReady_ = false;
+ DeleteCamera();
+}
+
+struct android_app* CameraEngine::AndroidApp(void) const {
+ return app_;
+}
+
+/**
+ * Create a camera object for onboard BACK_FACING camera
+ */
+void CameraEngine::CreateCamera(void) {
+ // Camera needed to be requested at the run-time from Java SDK
+ // if Not granted, do nothing.
+ if (!cameraGranted_ || !app_->window) {
+ LOGW("Camera Sample requires Full Camera access");
+ return;
+ }
+
+ int32_t displayRotation = GetDisplayRotation();
+ rotation_ = displayRotation;
+
+ camera_ = new NDKCamera();
+ ASSERT(camera_, "Failed to Create CameraObject");
+
+ int32_t facing = 0, angle = 0, imageRotation = 0;
+ if (camera_->GetSensorOrientation(&facing, &angle)) {
+ if (facing == ACAMERA_LENS_FACING_FRONT) {
+ imageRotation = (angle + rotation_) % 360;
+ imageRotation = (360 - imageRotation) % 360;
+ } else {
+ imageRotation = (angle - rotation_ + 360) % 360;
+ }
+ }
+ LOGI("Phone Rotation: %d, Present Rotation Angle: %d", rotation_,
+ imageRotation);
+ ImageFormat view{0, 0, 0}, capture{0, 0, 0};
+ camera_->MatchCaptureSizeRequest(app_->window, &view, &capture);
+
+ ASSERT(view.width && view.height, "Could not find supportable resolution");
+
+ // Request the necessary nativeWindow to OS
+ bool portraitNativeWindow =
+ (savedNativeWinRes_.width < savedNativeWinRes_.height);
+ ANativeWindow_setBuffersGeometry(
+ app_->window, portraitNativeWindow ? view.height : view.width,
+ portraitNativeWindow ? view.width : view.height, WINDOW_FORMAT_RGBA_8888);
+
+ yuvReader_ = new ImageReader(&view, AIMAGE_FORMAT_YUV_420_888);
+ yuvReader_->SetPresentRotation(imageRotation);
+ jpgReader_ = new ImageReader(&capture, AIMAGE_FORMAT_JPEG);
+ jpgReader_->SetPresentRotation(imageRotation);
+ jpgReader_->RegisterCallback(
+ this, [this](void* ctx, const char* str) -> void {
+ reinterpret_cast(ctx)->OnPhotoTaken(str);
+ });
+
+ // now we could create session
+ camera_->CreateSession(yuvReader_->GetNativeWindow(),
+ jpgReader_->GetNativeWindow(), imageRotation);
+}
+
+void CameraEngine::DeleteCamera(void) {
+ cameraReady_ = false;
+ if (camera_) {
+ delete camera_;
+ camera_ = nullptr;
+ }
+ if (yuvReader_) {
+ delete yuvReader_;
+ yuvReader_ = nullptr;
+ }
+ if (jpgReader_) {
+ delete jpgReader_;
+ jpgReader_ = nullptr;
+ }
+}
+
+/**
+ * Initiate a Camera Run-time usage request to Java side implementation
+ * [ The request result will be passed back in function
+ * notifyCameraPermission()]
+ */
+void CameraEngine::RequestCameraPermission() {
+ if (!app_) return;
+
+ JNIEnv* env;
+ ANativeActivity* activity = app_->activity;
+ activity->vm->GetEnv((void**)&env, JNI_VERSION_1_6);
+
+ activity->vm->AttachCurrentThread(&env, NULL);
+
+ jobject activityObj = env->NewGlobalRef(activity->clazz);
+ jclass clz = env->GetObjectClass(activityObj);
+ env->CallVoidMethod(activityObj,
+ env->GetMethodID(clz, "RequestCamera", "()V"));
+ env->DeleteGlobalRef(activityObj);
+
+ activity->vm->DetachCurrentThread();
+}
+/**
+ * Process to user's sensitivity and exposure value change
+ * all values are represented in int64_t even exposure is just int32_t
+ * @param code ACAMERA_SENSOR_EXPOSURE_TIME or ACAMERA_SENSOR_SENSITIVITY
+ * @param val corresponding value from user
+ */
+void CameraEngine::OnCameraParameterChanged(int32_t code, int64_t val) {
+ camera_->UpdateCameraRequestParameter(code, val);
+}
+
+/**
+ * The main function rendering a frame. In our case, it is yuv to RGBA8888
+ * converter
+ */
+void CameraEngine::DrawFrame(void) {
+ if (!cameraReady_ || !yuvReader_) return;
+ AImage* image = yuvReader_->GetNextImage();
+ if (!image) {
+ return;
+ }
+
+ ANativeWindow_acquire(app_->window);
+ ANativeWindow_Buffer buf;
+ if (ANativeWindow_lock(app_->window, &buf, nullptr) < 0) {
+ yuvReader_->DeleteImage(image);
+ return;
+ }
+
+ yuvReader_->DisplayImage(&buf, image);
+ ANativeWindow_unlockAndPost(app_->window);
+ ANativeWindow_release(app_->window);
+}
diff --git a/app/src/main/cpp/camera2/camera_engine.h b/app/src/main/cpp/camera2/camera_engine.h
new file mode 100644
index 00000000..d10ffc6c
--- /dev/null
+++ b/app/src/main/cpp/camera2/camera_engine.h
@@ -0,0 +1,81 @@
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CAMERA_ENGINE_H__
+#define __CAMERA_ENGINE_H__
+
+#include
+#include
+
+#include
+#include
+
+#include "camera_manager.h"
+
+/**
+ * basic CameraAppEngine
+ */
+class CameraEngine {
+ public:
+ explicit CameraEngine(android_app* app);
+ ~CameraEngine();
+
+ // Interfaces to android application framework
+ struct android_app* AndroidApp(void) const;
+ void OnAppInitWindow(void);
+ void DrawFrame(void);
+ void OnAppConfigChange(void);
+ void OnAppTermWindow(void);
+
+ // Native Window handlers
+ int32_t GetSavedNativeWinWidth(void);
+ int32_t GetSavedNativeWinHeight(void);
+ int32_t GetSavedNativeWinFormat(void);
+ void SaveNativeWinRes(int32_t w, int32_t h, int32_t format);
+
+ // UI handlers
+ void RequestCameraPermission();
+ void OnCameraPermission(jboolean granted);
+ void EnableUI(void);
+ void OnTakePhoto(void);
+ void OnCameraParameterChanged(int32_t code, int64_t val);
+
+ // Manage NDKCamera Object
+ void CreateCamera(void);
+ void DeleteCamera(void);
+
+ private:
+ void OnPhotoTaken(const char* fileName);
+ int GetDisplayRotation(void);
+
+ struct android_app* app_;
+ ImageFormat savedNativeWinRes_;
+ bool cameraGranted_;
+ int rotation_;
+ volatile bool cameraReady_;
+ NDKCamera* camera_;
+ ImageReader* yuvReader_;
+ ImageReader* jpgReader_;
+};
+
+/**
+ * retrieve global singleton CameraEngine instance
+ * @return the only instance of CameraEngine in the app
+ */
+CameraEngine* GetAppEngine(void);
+
+#endif // __CAMERA_ENGINE_H__
diff --git a/app/src/main/cpp/camera2/camera_listeners.cpp b/app/src/main/cpp/camera2/camera_listeners.cpp
new file mode 100644
index 00000000..4a3ef4a4
--- /dev/null
+++ b/app/src/main/cpp/camera2/camera_listeners.cpp
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include
+
+#include
+#include
+#include
+#include
+
+#include "camera_manager.h"
+#include "utils/camera_utils.h"
+#include "utils/native_debug.h"
+
+/*
+ * Camera Manager Listener object
+ */
+void OnCameraAvailable(void* ctx, const char* id) {
+ reinterpret_cast(ctx)->OnCameraStatusChanged(id, true);
+}
+void OnCameraUnavailable(void* ctx, const char* id) {
+ reinterpret_cast(ctx)->OnCameraStatusChanged(id, false);
+}
+
+/**
+ * OnCameraStatusChanged()
+ * handles Callback from ACameraManager
+ */
+void NDKCamera::OnCameraStatusChanged(const char* id, bool available) {
+ if (valid_) {
+ cameras_[std::string(id)].available_ = available ? true : false;
+ }
+}
+
+/**
+ * Construct a camera manager listener on the fly and return to caller
+ *
+ * @return ACameraManager_AvailabilityCallback
+ */
+ACameraManager_AvailabilityCallbacks* NDKCamera::GetManagerListener() {
+ static ACameraManager_AvailabilityCallbacks cameraMgrListener = {
+ .context = this,
+ .onCameraAvailable = ::OnCameraAvailable,
+ .onCameraUnavailable = ::OnCameraUnavailable,
+ };
+ return &cameraMgrListener;
+}
+
+/*
+ * CameraDevice callbacks
+ */
+void OnDeviceStateChanges(void* ctx, ACameraDevice* dev) {
+ reinterpret_cast(ctx)->OnDeviceState(dev);
+}
+
+void OnDeviceErrorChanges(void* ctx, ACameraDevice* dev, int err) {
+ reinterpret_cast(ctx)->OnDeviceError(dev, err);
+}
+ACameraDevice_stateCallbacks* NDKCamera::GetDeviceListener() {
+ static ACameraDevice_stateCallbacks cameraDeviceListener = {
+ .context = this,
+ .onDisconnected = ::OnDeviceStateChanges,
+ .onError = ::OnDeviceErrorChanges,
+ };
+ return &cameraDeviceListener;
+}
+/**
+ * Handle Camera DeviceStateChanges msg, notify device is disconnected
+ * simply close the camera
+ */
+void NDKCamera::OnDeviceState(ACameraDevice* dev) {
+ std::string id(ACameraDevice_getId(dev));
+ LOGW("device %s is disconnected", id.c_str());
+
+ cameras_[id].available_ = false;
+ ACameraDevice_close(cameras_[id].device_);
+ cameras_.erase(id);
+}
+/**
+ * Handles Camera's deviceErrorChanges message, no action;
+ * mainly debugging purpose
+ *
+ *
+ */
+void NDKCamera::OnDeviceError(ACameraDevice* dev, int err) {
+ std::string id(ACameraDevice_getId(dev));
+
+ LOGI("CameraDevice %s is in error %#x", id.c_str(), err);
+ PrintCameraDeviceError(err);
+
+ CameraId& cam = cameras_[id];
+
+ switch (err) {
+ case ERROR_CAMERA_IN_USE:
+ cam.available_ = false;
+ cam.owner_ = false;
+ break;
+ case ERROR_CAMERA_SERVICE:
+ case ERROR_CAMERA_DEVICE:
+ case ERROR_CAMERA_DISABLED:
+ case ERROR_MAX_CAMERAS_IN_USE:
+ cam.available_ = false;
+ cam.owner_ = false;
+ break;
+ default:
+ LOGI("Unknown Camera Device Error: %#x", err);
+ }
+}
+
+// CaptureSession state callbacks
+void OnSessionClosed(void* ctx, ACameraCaptureSession* ses) {
+ LOGW("session %p closed", ses);
+ reinterpret_cast(ctx)->OnSessionState(
+ ses, CaptureSessionState::CLOSED);
+}
+void OnSessionReady(void* ctx, ACameraCaptureSession* ses) {
+ LOGW("session %p ready", ses);
+ reinterpret_cast(ctx)->OnSessionState(ses,
+ CaptureSessionState::READY);
+}
+void OnSessionActive(void* ctx, ACameraCaptureSession* ses) {
+ LOGW("session %p active", ses);
+ reinterpret_cast(ctx)->OnSessionState(
+ ses, CaptureSessionState::ACTIVE);
+}
+
+ACameraCaptureSession_stateCallbacks* NDKCamera::GetSessionListener() {
+ static ACameraCaptureSession_stateCallbacks sessionListener = {
+ .context = this,
+ .onClosed = ::OnSessionClosed,
+ .onReady = ::OnSessionReady,
+ .onActive = ::OnSessionActive,
+ };
+ return &sessionListener;
+}
+
+/**
+ * Handles capture session state changes.
+ * Update into internal session state.
+ */
+void NDKCamera::OnSessionState(ACameraCaptureSession* ses,
+ CaptureSessionState state) {
+ if (!ses || ses != captureSession_) {
+ LOGW("CaptureSession is %s", (ses ? "NOT our session" : "NULL"));
+ return;
+ }
+
+ ASSERT(state < CaptureSessionState::MAX_STATE, "Wrong state %d", state);
+
+ captureSessionState_ = state;
+}
+
+// Capture callbacks, mostly information purpose
+void SessionCaptureCallback_OnFailed(void* context,
+ ACameraCaptureSession* session,
+ ACaptureRequest* request,
+ ACameraCaptureFailure* failure) {
+ std::thread captureFailedThread(&NDKCamera::OnCaptureFailed,
+ static_cast(context), session,
+ request, failure);
+ captureFailedThread.detach();
+}
+
+void SessionCaptureCallback_OnSequenceEnd(void* context,
+ ACameraCaptureSession* session,
+ int sequenceId, int64_t frameNumber) {
+ std::thread sequenceThread(&NDKCamera::OnCaptureSequenceEnd,
+ static_cast(context), session,
+ sequenceId, frameNumber);
+ sequenceThread.detach();
+}
+void SessionCaptureCallback_OnSequenceAborted(void* context,
+ ACameraCaptureSession* session,
+ int sequenceId) {
+ std::thread sequenceThread(&NDKCamera::OnCaptureSequenceEnd,
+ static_cast(context), session,
+ sequenceId, static_cast(-1));
+ sequenceThread.detach();
+}
+
+ACameraCaptureSession_captureCallbacks* NDKCamera::GetCaptureCallback() {
+ static ACameraCaptureSession_captureCallbacks captureListener{
+ .context = this,
+ .onCaptureStarted = nullptr,
+ .onCaptureProgressed = nullptr,
+ .onCaptureCompleted = nullptr,
+ .onCaptureFailed = SessionCaptureCallback_OnFailed,
+ .onCaptureSequenceCompleted = SessionCaptureCallback_OnSequenceEnd,
+ .onCaptureSequenceAborted = SessionCaptureCallback_OnSequenceAborted,
+ .onCaptureBufferLost = nullptr,
+ };
+ return &captureListener;
+}
+
+/**
+ * Process JPG capture SessionCaptureCallback_OnFailed event
+ * If this is current JPG capture session, simply resume preview
+ * @param session the capture session that failed
+ * @param request the capture request that failed
+ * @param failure for additional fail info.
+ */
+void NDKCamera::OnCaptureFailed(ACameraCaptureSession* session,
+ ACaptureRequest* request,
+ ACameraCaptureFailure* failure) {
+ if (valid_ && request == requests_[JPG_CAPTURE_REQUEST_IDX].request_) {
+ ASSERT(failure->sequenceId ==
+ requests_[JPG_CAPTURE_REQUEST_IDX].sessionSequenceId_,
+ "Error jpg sequence id")
+ StartPreview(true);
+ }
+}
+
+/**
+ * Process event from JPEG capture
+ * SessionCaptureCallback_OnSequenceEnd()
+ * SessionCaptureCallback_OnSequenceAborted()
+ *
+ * If this is jpg capture, turn back on preview after a catpure.
+ */
+void NDKCamera::OnCaptureSequenceEnd(ACameraCaptureSession* session,
+ int sequenceId, int64_t frameNumber) {
+ if (sequenceId != requests_[JPG_CAPTURE_REQUEST_IDX].sessionSequenceId_)
+ return;
+
+ // resume preview
+ CALL_SESSION(setRepeatingRequest(captureSession_, nullptr, 1,
+ &requests_[PREVIEW_REQUEST_IDX].request_,
+ nullptr));
+}
diff --git a/app/src/main/cpp/camera2/camera_manager.cpp b/app/src/main/cpp/camera2/camera_manager.cpp
new file mode 100644
index 00000000..58dcfc7e
--- /dev/null
+++ b/app/src/main/cpp/camera2/camera_manager.cpp
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "camera_manager.h"
+
+#include
+#include
+
+#include
+#include
+#include
+
+#include "utils/camera_utils.h"
+#include "utils/native_debug.h"
+
+/**
+ * Range of Camera Exposure Time:
+ * Camera's capability range have a very long range which may be disturbing
+ * on camera. For this sample purpose, clamp to a range showing visible
+ * video on preview: 100000ns ~ 250000000ns
+ */
+static const uint64_t kMinExposureTime = static_cast(1000000);
+static const uint64_t kMaxExposureTime = static_cast(250000000);
+
+NDKCamera::NDKCamera()
+ : cameraMgr_(nullptr),
+ activeCameraId_(""),
+ cameraFacing_(ACAMERA_LENS_FACING_BACK),
+ cameraOrientation_(0),
+ outputContainer_(nullptr),
+ captureSessionState_(CaptureSessionState::MAX_STATE),
+ exposureTime_(static_cast(0)) {
+ valid_ = false;
+ requests_.resize(CAPTURE_REQUEST_COUNT);
+ memset(requests_.data(), 0, requests_.size() * sizeof(requests_[0]));
+ cameras_.clear();
+ cameraMgr_ = ACameraManager_create();
+ ASSERT(cameraMgr_, "Failed to create cameraManager");
+
+ // Pick up a back-facing camera to preview
+ EnumerateCamera();
+ ASSERT(activeCameraId_.size(), "Unknown ActiveCameraIdx");
+
+ // Create back facing camera device
+ CALL_MGR(openCamera(cameraMgr_, activeCameraId_.c_str(), GetDeviceListener(),
+ &cameras_[activeCameraId_].device_));
+
+ CALL_MGR(registerAvailabilityCallback(cameraMgr_, GetManagerListener()));
+
+ // Initialize camera controls(exposure time and sensitivity), pick
+ // up value of 2% * range + min as starting value (just a number, no magic)
+ ACameraMetadata* metadataObj;
+ CALL_MGR(getCameraCharacteristics(cameraMgr_, activeCameraId_.c_str(),
+ &metadataObj));
+ ACameraMetadata_const_entry val = {
+ 0,
+ };
+ camera_status_t status = ACameraMetadata_getConstEntry(
+ metadataObj, ACAMERA_SENSOR_INFO_EXPOSURE_TIME_RANGE, &val);
+ if (status == ACAMERA_OK) {
+ exposureRange_.min_ = val.data.i64[0];
+ if (exposureRange_.min_ < kMinExposureTime) {
+ exposureRange_.min_ = kMinExposureTime;
+ }
+ exposureRange_.max_ = val.data.i64[1];
+ if (exposureRange_.max_ > kMaxExposureTime) {
+ exposureRange_.max_ = kMaxExposureTime;
+ }
+ exposureTime_ = exposureRange_.value(2);
+ } else {
+ LOGW("Unsupported ACAMERA_SENSOR_INFO_EXPOSURE_TIME_RANGE");
+ exposureRange_.min_ = exposureRange_.max_ = 0l;
+ exposureTime_ = 0l;
+ }
+ status = ACameraMetadata_getConstEntry(
+ metadataObj, ACAMERA_SENSOR_INFO_SENSITIVITY_RANGE, &val);
+
+ if (status == ACAMERA_OK) {
+ sensitivityRange_.min_ = val.data.i32[0];
+ sensitivityRange_.max_ = val.data.i32[1];
+
+ sensitivity_ = sensitivityRange_.value(2);
+ } else {
+ LOGW("failed for ACAMERA_SENSOR_INFO_SENSITIVITY_RANGE");
+ sensitivityRange_.min_ = sensitivityRange_.max_ = 0;
+ sensitivity_ = 0;
+ }
+ valid_ = true;
+}
+
+/**
+ * A helper class to assist image size comparison, by comparing the absolute
+ * size
+ * regardless of the portrait or landscape mode.
+ */
+class DisplayDimension {
+ public:
+ DisplayDimension(int32_t w, int32_t h) : w_(w), h_(h), portrait_(false) {
+ if (h > w) {
+ // make it landscape
+ w_ = h;
+ h_ = w;
+ portrait_ = true;
+ }
+ }
+ DisplayDimension(const DisplayDimension& other) {
+ w_ = other.w_;
+ h_ = other.h_;
+ portrait_ = other.portrait_;
+ }
+
+ DisplayDimension(void) {
+ w_ = 0;
+ h_ = 0;
+ portrait_ = false;
+ }
+ DisplayDimension& operator=(const DisplayDimension& other) {
+ w_ = other.w_;
+ h_ = other.h_;
+ portrait_ = other.portrait_;
+
+ return (*this);
+ }
+
+ bool IsSameRatio(DisplayDimension& other) {
+ return (w_ * other.h_ == h_ * other.w_);
+ }
+ bool operator>(DisplayDimension& other) {
+ return (w_ >= other.w_ & h_ >= other.h_);
+ }
+ bool operator==(DisplayDimension& other) {
+ return (w_ == other.w_ && h_ == other.h_ && portrait_ == other.portrait_);
+ }
+ DisplayDimension operator-(DisplayDimension& other) {
+ DisplayDimension delta(w_ - other.w_, h_ - other.h_);
+ return delta;
+ }
+ void Flip(void) { portrait_ = !portrait_; }
+ bool IsPortrait(void) { return portrait_; }
+ int32_t width(void) { return w_; }
+ int32_t height(void) { return h_; }
+ int32_t org_width(void) { return (portrait_ ? h_ : w_); }
+ int32_t org_height(void) { return (portrait_ ? w_ : h_); }
+
+ private:
+ int32_t w_, h_;
+ bool portrait_;
+};
+
+/**
+ * Find a compatible camera modes:
+ * 1) the same aspect ration as the native display window, which should be a
+ * rotated version of the physical device
+ * 2) the smallest resolution in the camera mode list
+ * This is to minimize the later color space conversion workload.
+ */
+bool NDKCamera::MatchCaptureSizeRequest(ANativeWindow* display,
+ ImageFormat* resView,
+ ImageFormat* resCap) {
+ DisplayDimension disp(ANativeWindow_getWidth(display),
+ ANativeWindow_getHeight(display));
+ if (cameraOrientation_ == 90 || cameraOrientation_ == 270) {
+ disp.Flip();
+ }
+
+ ACameraMetadata* metadata;
+ CALL_MGR(
+ getCameraCharacteristics(cameraMgr_, activeCameraId_.c_str(), &metadata));
+ ACameraMetadata_const_entry entry;
+ CALL_METADATA(getConstEntry(
+ metadata, ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &entry));
+ // format of the data: format, width, height, input?, type int32
+ bool foundIt = false;
+ DisplayDimension foundRes(4000, 4000);
+ DisplayDimension maxJPG(0, 0);
+
+ for (int i = 0; i < entry.count; i += 4) {
+ int32_t input = entry.data.i32[i + 3];
+ int32_t format = entry.data.i32[i + 0];
+ if (input) continue;
+
+ if (format == AIMAGE_FORMAT_YUV_420_888 || format == AIMAGE_FORMAT_JPEG) {
+ DisplayDimension res(entry.data.i32[i + 1], entry.data.i32[i + 2]);
+ if (!disp.IsSameRatio(res)) continue;
+ if (format == AIMAGE_FORMAT_YUV_420_888 && foundRes > res) {
+ foundIt = true;
+ foundRes = res;
+ } else if (format == AIMAGE_FORMAT_JPEG && res > maxJPG) {
+ maxJPG = res;
+ }
+ }
+ }
+
+ if (foundIt) {
+ resView->width = foundRes.org_width();
+ resView->height = foundRes.org_height();
+ resCap->width = maxJPG.org_width();
+ resCap->height = maxJPG.org_height();
+ } else {
+ LOGW("Did not find any compatible camera resolution, taking 640x480");
+ if (disp.IsPortrait()) {
+ resView->width = 480;
+ resView->height = 640;
+ } else {
+ resView->width = 640;
+ resView->height = 480;
+ }
+ *resCap = *resView;
+ }
+ resView->format = AIMAGE_FORMAT_YUV_420_888;
+ resCap->format = AIMAGE_FORMAT_JPEG;
+ return foundIt;
+}
+
+void NDKCamera::CreateSession(ANativeWindow* previewWindow,
+ ANativeWindow* jpgWindow, int32_t imageRotation) {
+ // Create output from this app's ANativeWindow, and add into output container
+ requests_[PREVIEW_REQUEST_IDX].outputNativeWindow_ = previewWindow;
+ requests_[PREVIEW_REQUEST_IDX].template_ = TEMPLATE_PREVIEW;
+ requests_[JPG_CAPTURE_REQUEST_IDX].outputNativeWindow_ = jpgWindow;
+ requests_[JPG_CAPTURE_REQUEST_IDX].template_ = TEMPLATE_STILL_CAPTURE;
+
+ CALL_CONTAINER(create(&outputContainer_));
+ for (auto& req : requests_) {
+ ANativeWindow_acquire(req.outputNativeWindow_);
+ CALL_OUTPUT(create(req.outputNativeWindow_, &req.sessionOutput_));
+ CALL_CONTAINER(add(outputContainer_, req.sessionOutput_));
+ CALL_TARGET(create(req.outputNativeWindow_, &req.target_));
+ CALL_DEV(createCaptureRequest(cameras_[activeCameraId_].device_,
+ req.template_, &req.request_));
+ CALL_REQUEST(addTarget(req.request_, req.target_));
+ }
+
+ // Create a capture session for the given preview request
+ captureSessionState_ = CaptureSessionState::READY;
+ CALL_DEV(createCaptureSession(cameras_[activeCameraId_].device_,
+ outputContainer_, GetSessionListener(),
+ &captureSession_));
+
+ ACaptureRequest_setEntry_i32(requests_[JPG_CAPTURE_REQUEST_IDX].request_,
+ ACAMERA_JPEG_ORIENTATION, 1, &imageRotation);
+
+ /*
+ * Only preview request is in manual mode, JPG is always in Auto mode
+ * JPG capture mode could also be switch into manual mode and control
+ * the capture parameters, this sample leaves JPG capture to be auto mode
+ * (auto control has better effect than author's manual control)
+ */
+ uint8_t aeModeOff = ACAMERA_CONTROL_AE_MODE_OFF;
+ CALL_REQUEST(setEntry_u8(requests_[PREVIEW_REQUEST_IDX].request_,
+ ACAMERA_CONTROL_AE_MODE, 1, &aeModeOff));
+ CALL_REQUEST(setEntry_i32(requests_[PREVIEW_REQUEST_IDX].request_,
+ ACAMERA_SENSOR_SENSITIVITY, 1, &sensitivity_));
+ CALL_REQUEST(setEntry_i64(requests_[PREVIEW_REQUEST_IDX].request_,
+ ACAMERA_SENSOR_EXPOSURE_TIME, 1, &exposureTime_));
+}
+
+NDKCamera::~NDKCamera() {
+ valid_ = false;
+ // stop session if it is on:
+ if (captureSessionState_ == CaptureSessionState::ACTIVE) {
+ ACameraCaptureSession_stopRepeating(captureSession_);
+ }
+ ACameraCaptureSession_close(captureSession_);
+
+ for (auto& req : requests_) {
+ CALL_REQUEST(removeTarget(req.request_, req.target_));
+ ACaptureRequest_free(req.request_);
+ ACameraOutputTarget_free(req.target_);
+
+ CALL_CONTAINER(remove(outputContainer_, req.sessionOutput_));
+ ACaptureSessionOutput_free(req.sessionOutput_);
+
+ ANativeWindow_release(req.outputNativeWindow_);
+ }
+
+ requests_.resize(0);
+ ACaptureSessionOutputContainer_free(outputContainer_);
+
+ for (auto& cam : cameras_) {
+ if (cam.second.device_) {
+ CALL_DEV(close(cam.second.device_));
+ }
+ }
+ cameras_.clear();
+ if (cameraMgr_) {
+ CALL_MGR(unregisterAvailabilityCallback(cameraMgr_, GetManagerListener()));
+ ACameraManager_delete(cameraMgr_);
+ cameraMgr_ = nullptr;
+ }
+}
+
+/**
+ * EnumerateCamera()
+ * Loop through cameras on the system, pick up
+ * 1) back facing one if available
+ * 2) otherwise pick the first one reported to us
+ */
+void NDKCamera::EnumerateCamera() {
+ ACameraIdList* cameraIds = nullptr;
+ CALL_MGR(getCameraIdList(cameraMgr_, &cameraIds));
+
+ for (int i = 0; i < cameraIds->numCameras; ++i) {
+ const char* id = cameraIds->cameraIds[i];
+
+ ACameraMetadata* metadataObj;
+ CALL_MGR(getCameraCharacteristics(cameraMgr_, id, &metadataObj));
+
+ int32_t count = 0;
+ const uint32_t* tags = nullptr;
+ ACameraMetadata_getAllTags(metadataObj, &count, &tags);
+ for (int tagIdx = 0; tagIdx < count; ++tagIdx) {
+ if (ACAMERA_LENS_FACING == tags[tagIdx]) {
+ ACameraMetadata_const_entry lensInfo = {
+ 0,
+ };
+ CALL_METADATA(getConstEntry(metadataObj, tags[tagIdx], &lensInfo));
+ CameraId cam(id);
+ cam.facing_ = static_cast(
+ lensInfo.data.u8[0]);
+ cam.owner_ = false;
+ cam.device_ = nullptr;
+ cameras_[cam.id_] = cam;
+ if (cam.facing_ == ACAMERA_LENS_FACING_BACK) {
+ activeCameraId_ = cam.id_;
+ }
+ break;
+ }
+ }
+ ACameraMetadata_free(metadataObj);
+ }
+
+ ASSERT(cameras_.size(), "No Camera Available on the device");
+ if (activeCameraId_.length() == 0) {
+ // if no back facing camera found, pick up the first one to use...
+ activeCameraId_ = cameras_.begin()->second.id_;
+ }
+ // activeCameraId_ = cameras_.rbegin()->second.id_;
+
+ ACameraManager_deleteCameraIdList(cameraIds);
+}
+
+/**
+ * GetSensorOrientation()
+ * Retrieve current sensor orientation regarding to the phone device
+ * orientation
+ * SensorOrientation is NOT settable.
+ */
+bool NDKCamera::GetSensorOrientation(int32_t* facing, int32_t* angle) {
+ if (!cameraMgr_) {
+ return false;
+ }
+
+ ACameraMetadata* metadataObj;
+ ACameraMetadata_const_entry face, orientation;
+ CALL_MGR(getCameraCharacteristics(cameraMgr_, activeCameraId_.c_str(),
+ &metadataObj));
+ CALL_METADATA(getConstEntry(metadataObj, ACAMERA_LENS_FACING, &face));
+ cameraFacing_ = static_cast(face.data.u8[0]);
+
+ CALL_METADATA(
+ getConstEntry(metadataObj, ACAMERA_SENSOR_ORIENTATION, &orientation));
+
+ LOGI("====Current SENSOR_ORIENTATION: %8d", orientation.data.i32[0]);
+
+ ACameraMetadata_free(metadataObj);
+ cameraOrientation_ = orientation.data.i32[0];
+
+ if (facing) *facing = cameraFacing_;
+ if (angle) *angle = cameraOrientation_;
+ return true;
+}
+
+/**
+ * StartPreview()
+ * Toggle preview start/stop
+ */
+void NDKCamera::StartPreview(bool start) {
+ if (start) {
+ CALL_SESSION(setRepeatingRequest(captureSession_, nullptr, 1,
+ &requests_[PREVIEW_REQUEST_IDX].request_,
+ nullptr));
+ } else if (!start && captureSessionState_ == CaptureSessionState::ACTIVE) {
+ ACameraCaptureSession_stopRepeating(captureSession_);
+ } else {
+ ASSERT(false, "Conflict states(%s, %d)", (start ? "true" : "false"),
+ captureSessionState_);
+ }
+}
+
+/**
+ * Capture one jpg photo into
+ * /sdcard/DCIM/Camera
+ * refer to WriteFile() for details
+ */
+bool NDKCamera::TakePhoto(void) {
+ if (captureSessionState_ == CaptureSessionState::ACTIVE) {
+ ACameraCaptureSession_stopRepeating(captureSession_);
+ }
+
+ CALL_SESSION(capture(captureSession_, GetCaptureCallback(), 1,
+ &requests_[JPG_CAPTURE_REQUEST_IDX].request_,
+ &requests_[JPG_CAPTURE_REQUEST_IDX].sessionSequenceId_));
+ return true;
+}
+
+void NDKCamera::UpdateCameraRequestParameter(int32_t code, int64_t val) {
+ ACaptureRequest* request = requests_[PREVIEW_REQUEST_IDX].request_;
+ switch (code) {
+ case ACAMERA_SENSOR_EXPOSURE_TIME:
+ if (exposureRange_.Supported()) {
+ exposureTime_ = val;
+ CALL_REQUEST(setEntry_i64(request, ACAMERA_SENSOR_EXPOSURE_TIME, 1,
+ &exposureTime_));
+ }
+ break;
+
+ case ACAMERA_SENSOR_SENSITIVITY:
+ if (sensitivityRange_.Supported()) {
+ sensitivity_ = val;
+ CALL_REQUEST(setEntry_i32(request, ACAMERA_SENSOR_SENSITIVITY, 1,
+ &sensitivity_));
+ }
+ break;
+ default:
+ ASSERT(false, "==ERROR==: error code for CameraParameterChange: %d",
+ code);
+ return;
+ }
+
+ uint8_t aeModeOff = ACAMERA_CONTROL_AE_MODE_OFF;
+ CALL_REQUEST(setEntry_u8(request, ACAMERA_CONTROL_AE_MODE, 1, &aeModeOff));
+ CALL_SESSION(
+ setRepeatingRequest(captureSession_, nullptr, 1, &request,
+ &requests_[PREVIEW_REQUEST_IDX].sessionSequenceId_));
+}
+
+/**
+ * Retrieve Camera Exposure adjustable range.
+ *
+ * @param min Camera minimium exposure time in nanoseconds
+ * @param max Camera maximum exposure tiem in nanoseconds
+ *
+ * @return true min and max are loaded with the camera's exposure values
+ * false camera has not initialized, no value available
+ */
+bool NDKCamera::GetExposureRange(int64_t* min, int64_t* max, int64_t* curVal) {
+ if (!exposureRange_.Supported() || !exposureTime_ || !min || !max ||
+ !curVal) {
+ return false;
+ }
+ *min = exposureRange_.min_;
+ *max = exposureRange_.max_;
+ *curVal = exposureTime_;
+
+ return true;
+}
+
+/**
+ * Retrieve Camera sensitivity range.
+ *
+ * @param min Camera minimium sensitivity
+ * @param max Camera maximum sensitivity
+ *
+ * @return true min and max are loaded with the camera's sensitivity values
+ * false camera has not initialized, no value available
+ */
+bool NDKCamera::GetSensitivityRange(int64_t* min, int64_t* max,
+ int64_t* curVal) {
+ if (!sensitivityRange_.Supported() || !sensitivity_ || !min || !max ||
+ !curVal) {
+ return false;
+ }
+ *min = static_cast(sensitivityRange_.min_);
+ *max = static_cast(sensitivityRange_.max_);
+ *curVal = sensitivity_;
+ return true;
+}
diff --git a/app/src/main/cpp/camera2/camera_manager.h b/app/src/main/cpp/camera2/camera_manager.h
new file mode 100644
index 00000000..5993f776
--- /dev/null
+++ b/app/src/main/cpp/camera2/camera_manager.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CAMERA_NATIVE_CAMERA_H
+#define CAMERA_NATIVE_CAMERA_H
+#include
+#include
+#include
+#include
+
+#include