实现Service和定时器
parent
00433db12f
commit
9952fcbd8c
@ -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"
|
||||
}
|
@ -0,0 +1,196 @@
|
||||
#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 "Camera.h"
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#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);
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
#ifndef __CAMERA_H__
|
||||
#define __CAMERA_H__
|
||||
|
||||
|
||||
|
||||
// Must come before NdkCameraCaptureSession.h
|
||||
#include <camera/NdkCaptureRequest.h>
|
||||
|
||||
#include <camera/NdkCameraCaptureSession.h>
|
||||
#include <camera/NdkCameraDevice.h>
|
||||
#include <camera/NdkCameraError.h>
|
||||
#include <camera/NdkCameraManager.h>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <jni.h>
|
||||
#include <media/NdkImage.h>
|
||||
#include <media/NdkImageReader.h>
|
||||
|
||||
|
||||
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__
|
@ -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 <dirent.h>
|
||||
|
||||
#include <iomanip>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
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 <fname>.
|
||||
// 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<Camera2Reader*>(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);
|
||||
}
|
||||
}
|
@ -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 <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <camera/NdkCameraManager.h>
|
||||
#include <camera/NdkCameraError.h>
|
||||
#include <camera/NdkCameraDevice.h>
|
||||
#include <camera/NdkCameraMetadataTags.h>
|
||||
#include <media/NdkImageReader.h>
|
||||
#include <android/log.h>
|
||||
#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 <string>
|
||||
|
||||
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 <fname>. 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__ */
|
@ -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 <android/log.h>
|
||||
|
||||
#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<CPhoneDevice*>(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);
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
#ifndef __PHONE_DEVICE_H__
|
||||
#define __PHONE_DEVICE_H__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <camera/NdkCameraManager.h>
|
||||
#include <camera/NdkCameraError.h>
|
||||
#include <camera/NdkCameraDevice.h>
|
||||
#include <camera/NdkCameraMetadataTags.h>
|
||||
#include <media/NdkImageReader.h>
|
||||
#include <android/log.h>
|
||||
// #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 <Client/Device.h>
|
||||
#include <string>
|
||||
|
||||
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__
|
@ -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<CameraEngine*>(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<void*>(&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;
|
||||
}
|
@ -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 <cstdio>
|
||||
|
||||
#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<CameraEngine*>(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);
|
||||
}
|
@ -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 <android/native_window.h>
|
||||
#include <android_native_app_glue.h>
|
||||
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
|
||||
#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__
|
@ -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 <camera/NdkCameraManager.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
#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<NDKCamera*>(ctx)->OnCameraStatusChanged(id, true);
|
||||
}
|
||||
void OnCameraUnavailable(void* ctx, const char* id) {
|
||||
reinterpret_cast<NDKCamera*>(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<NDKCamera*>(ctx)->OnDeviceState(dev);
|
||||
}
|
||||
|
||||
void OnDeviceErrorChanges(void* ctx, ACameraDevice* dev, int err) {
|
||||
reinterpret_cast<NDKCamera*>(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<NDKCamera*>(ctx)->OnSessionState(
|
||||
ses, CaptureSessionState::CLOSED);
|
||||
}
|
||||
void OnSessionReady(void* ctx, ACameraCaptureSession* ses) {
|
||||
LOGW("session %p ready", ses);
|
||||
reinterpret_cast<NDKCamera*>(ctx)->OnSessionState(ses,
|
||||
CaptureSessionState::READY);
|
||||
}
|
||||
void OnSessionActive(void* ctx, ACameraCaptureSession* ses) {
|
||||
LOGW("session %p active", ses);
|
||||
reinterpret_cast<NDKCamera*>(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<NDKCamera*>(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<NDKCamera*>(context), session,
|
||||
sequenceId, frameNumber);
|
||||
sequenceThread.detach();
|
||||
}
|
||||
void SessionCaptureCallback_OnSequenceAborted(void* context,
|
||||
ACameraCaptureSession* session,
|
||||
int sequenceId) {
|
||||
std::thread sequenceThread(&NDKCamera::OnCaptureSequenceEnd,
|
||||
static_cast<NDKCamera*>(context), session,
|
||||
sequenceId, static_cast<int64_t>(-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));
|
||||
}
|
@ -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 <camera/NdkCameraManager.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
|
||||
#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<uint64_t>(1000000);
|
||||
static const uint64_t kMaxExposureTime = static_cast<uint64_t>(250000000);
|
||||
|
||||
NDKCamera::NDKCamera()
|
||||
: cameraMgr_(nullptr),
|
||||
activeCameraId_(""),
|
||||
cameraFacing_(ACAMERA_LENS_FACING_BACK),
|
||||
cameraOrientation_(0),
|
||||
outputContainer_(nullptr),
|
||||
captureSessionState_(CaptureSessionState::MAX_STATE),
|
||||
exposureTime_(static_cast<int64_t>(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<acamera_metadata_enum_android_lens_facing_t>(
|
||||
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<int32_t>(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<int64_t>(sensitivityRange_.min_);
|
||||
*max = static_cast<int64_t>(sensitivityRange_.max_);
|
||||
*curVal = sensitivity_;
|
||||
return true;
|
||||
}
|
@ -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 <camera/NdkCameraDevice.h>
|
||||
#include <camera/NdkCameraError.h>
|
||||
#include <camera/NdkCameraManager.h>
|
||||
#include <camera/NdkCameraMetadataTags.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "image_reader.h"
|
||||
|
||||
enum class CaptureSessionState : int32_t {
|
||||
READY = 0, // session is ready
|
||||
ACTIVE, // session is busy
|
||||
CLOSED, // session is closed(by itself or a new session evicts)
|
||||
MAX_STATE
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class RangeValue {
|
||||
public:
|
||||
T min_, max_;
|
||||
/**
|
||||
* return absolute value from relative value
|
||||
* value: in percent (50 for 50%)
|
||||
*/
|
||||
T value(int percent) {
|
||||
return static_cast<T>(min_ + (max_ - min_) * percent / 100);
|
||||
}
|
||||
RangeValue() { min_ = max_ = static_cast<T>(0); }
|
||||
|
||||
bool Supported(void) const { return (min_ != max_); }
|
||||
};
|
||||
|
||||
enum PREVIEW_INDICES {
|
||||
PREVIEW_REQUEST_IDX = 0,
|
||||
JPG_CAPTURE_REQUEST_IDX,
|
||||
CAPTURE_REQUEST_COUNT,
|
||||
};
|
||||
|
||||
struct CaptureRequestInfo {
|
||||
ANativeWindow* outputNativeWindow_;
|
||||
ACaptureSessionOutput* sessionOutput_;
|
||||
ACameraOutputTarget* target_;
|
||||
ACaptureRequest* request_;
|
||||
ACameraDevice_request_template template_;
|
||||
int sessionSequenceId_;
|
||||
};
|
||||
|
||||
class CameraId;
|
||||
class NDKCamera {
|
||||
private:
|
||||
ACameraManager* cameraMgr_;
|
||||
std::map<std::string, CameraId> cameras_;
|
||||
std::string activeCameraId_;
|
||||
uint32_t cameraFacing_;
|
||||
uint32_t cameraOrientation_;
|
||||
|
||||
std::vector<CaptureRequestInfo> requests_;
|
||||
|
||||
ACaptureSessionOutputContainer* outputContainer_;
|
||||
ACameraCaptureSession* captureSession_;
|
||||
CaptureSessionState captureSessionState_;
|
||||
|
||||
// set up exposure control
|
||||
int64_t exposureTime_;
|
||||
RangeValue<int64_t> exposureRange_;
|
||||
int32_t sensitivity_;
|
||||
RangeValue<int32_t> sensitivityRange_;
|
||||
volatile bool valid_;
|
||||
|
||||
ACameraManager_AvailabilityCallbacks* GetManagerListener();
|
||||
ACameraDevice_stateCallbacks* GetDeviceListener();
|
||||
ACameraCaptureSession_stateCallbacks* GetSessionListener();
|
||||
ACameraCaptureSession_captureCallbacks* GetCaptureCallback();
|
||||
|
||||
public:
|
||||
NDKCamera();
|
||||
~NDKCamera();
|
||||
void EnumerateCamera(void);
|
||||
bool MatchCaptureSizeRequest(ANativeWindow* display, ImageFormat* view,
|
||||
ImageFormat* capture);
|
||||
void CreateSession(ANativeWindow* previewWindow, ANativeWindow* jpgWindow,
|
||||
int32_t imageRotation);
|
||||
bool GetSensorOrientation(int32_t* facing, int32_t* angle);
|
||||
void OnCameraStatusChanged(const char* id, bool available);
|
||||
void OnDeviceState(ACameraDevice* dev);
|
||||
void OnDeviceError(ACameraDevice* dev, int err);
|
||||
void OnSessionState(ACameraCaptureSession* ses, CaptureSessionState state);
|
||||
void OnCaptureSequenceEnd(ACameraCaptureSession* session, int sequenceId,
|
||||
int64_t frameNumber);
|
||||
void OnCaptureFailed(ACameraCaptureSession* session, ACaptureRequest* request,
|
||||
ACameraCaptureFailure* failure);
|
||||
void StartPreview(bool start);
|
||||
bool TakePhoto(void);
|
||||
bool GetExposureRange(int64_t* min, int64_t* max, int64_t* curVal);
|
||||
bool GetSensitivityRange(int64_t* min, int64_t* max, int64_t* curVal);
|
||||
|
||||
void UpdateCameraRequestParameter(int32_t code, int64_t val);
|
||||
};
|
||||
|
||||
// helper classes to hold enumerated camera
|
||||
class CameraId {
|
||||
public:
|
||||
ACameraDevice* device_;
|
||||
std::string id_;
|
||||
acamera_metadata_enum_android_lens_facing_t facing_;
|
||||
bool available_; // free to use ( no other apps are using
|
||||
bool owner_; // we are the owner of the camera
|
||||
explicit CameraId(const char* id)
|
||||
: device_(nullptr),
|
||||
facing_(ACAMERA_LENS_FACING_FRONT),
|
||||
available_(false),
|
||||
owner_(false) {
|
||||
id_ = id;
|
||||
}
|
||||
|
||||
explicit CameraId(void) { CameraId(""); }
|
||||
};
|
||||
|
||||
#endif // CAMERA_NATIVE_CAMERA_H
|
@ -0,0 +1,156 @@
|
||||
|
||||
/*
|
||||
* 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 <utils/native_debug.h>
|
||||
|
||||
#include "camera_engine.h"
|
||||
|
||||
/**
|
||||
* Retrieve current rotation from Java side
|
||||
*
|
||||
* @return current rotation angle
|
||||
*/
|
||||
int CameraEngine::GetDisplayRotation() {
|
||||
ASSERT(app_, "Application is not initialized");
|
||||
|
||||
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);
|
||||
jint newOrientation = env->CallIntMethod(
|
||||
activityObj, env->GetMethodID(clz, "getRotationDegree", "()I"));
|
||||
env->DeleteGlobalRef(activityObj);
|
||||
|
||||
activity->vm->DetachCurrentThread();
|
||||
return newOrientation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializate UI on Java side. The 2 seekBars' values are passed in
|
||||
* array in the tuple of ( min, max, curVal )
|
||||
* 0: exposure min
|
||||
* 1: exposure max
|
||||
* 2: exposure val
|
||||
* 3: sensitivity min
|
||||
* 4: sensitivity max
|
||||
* 5: sensitivity val
|
||||
*/
|
||||
const int kInitDataLen = 6;
|
||||
void CameraEngine::EnableUI(void) {
|
||||
JNIEnv *jni;
|
||||
app_->activity->vm->AttachCurrentThread(&jni, NULL);
|
||||
int64_t range[3];
|
||||
|
||||
// Default class retrieval
|
||||
jclass clazz = jni->GetObjectClass(app_->activity->clazz);
|
||||
jmethodID methodID = jni->GetMethodID(clazz, "EnableUI", "([J)V");
|
||||
jlongArray initData = jni->NewLongArray(kInitDataLen);
|
||||
|
||||
ASSERT(initData && methodID, "JavaUI interface Object failed(%p, %p)",
|
||||
methodID, initData);
|
||||
|
||||
if (!camera_->GetExposureRange(&range[0], &range[1], &range[2])) {
|
||||
memset(range, 0, sizeof(int64_t) * 3);
|
||||
}
|
||||
|
||||
jni->SetLongArrayRegion(initData, 0, 3, range);
|
||||
|
||||
if (!camera_->GetSensitivityRange(&range[0], &range[1], &range[2])) {
|
||||
memset(range, 0, sizeof(int64_t) * 3);
|
||||
}
|
||||
jni->SetLongArrayRegion(initData, 3, 3, range);
|
||||
jni->CallVoidMethod(app_->activity->clazz, methodID, initData);
|
||||
app_->activity->vm->DetachCurrentThread();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles UI request to take a photo into
|
||||
* /sdcard/DCIM/Camera
|
||||
*/
|
||||
void CameraEngine::OnTakePhoto() {
|
||||
if (camera_) {
|
||||
camera_->TakePhoto();
|
||||
}
|
||||
}
|
||||
|
||||
void CameraEngine::OnPhotoTaken(const char *fileName) {
|
||||
JNIEnv *jni;
|
||||
app_->activity->vm->AttachCurrentThread(&jni, NULL);
|
||||
|
||||
// Default class retrieval
|
||||
jclass clazz = jni->GetObjectClass(app_->activity->clazz);
|
||||
jmethodID methodID =
|
||||
jni->GetMethodID(clazz, "OnPhotoTaken", "(Ljava/lang/String;)V");
|
||||
jstring javaName = jni->NewStringUTF(fileName);
|
||||
|
||||
jni->CallVoidMethod(app_->activity->clazz, methodID, javaName);
|
||||
app_->activity->vm->DetachCurrentThread();
|
||||
}
|
||||
/**
|
||||
* Process user camera and disk writing permission
|
||||
* Resume application initialization after user granted camera and disk usage
|
||||
* If user denied permission, do nothing: no camera
|
||||
*
|
||||
* @param granted user's authorization for camera and disk usage.
|
||||
* @return none
|
||||
*/
|
||||
void CameraEngine::OnCameraPermission(jboolean granted) {
|
||||
cameraGranted_ = (granted != JNI_FALSE);
|
||||
|
||||
if (cameraGranted_) {
|
||||
OnAppInitWindow();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A couple UI handles ( from UI )
|
||||
* user camera and disk permission
|
||||
* exposure and sensitivity SeekBars
|
||||
* takePhoto button
|
||||
*/
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_sample_camera_basic_CameraActivity_notifyCameraPermission(
|
||||
JNIEnv *env, jclass type, jboolean permission) {
|
||||
std::thread permissionHandler(&CameraEngine::OnCameraPermission,
|
||||
GetAppEngine(), permission);
|
||||
permissionHandler.detach();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_sample_camera_basic_CameraActivity_TakePhoto(JNIEnv *env,
|
||||
jclass type) {
|
||||
std::thread takePhotoHandler(&CameraEngine::OnTakePhoto, GetAppEngine());
|
||||
takePhotoHandler.detach();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_sample_camera_basic_CameraActivity_OnExposureChanged(
|
||||
JNIEnv *env, jobject instance, jlong exposurePercent) {
|
||||
GetAppEngine()->OnCameraParameterChanged(ACAMERA_SENSOR_EXPOSURE_TIME,
|
||||
exposurePercent);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_sample_camera_basic_CameraActivity_OnSensitivityChanged(
|
||||
JNIEnv *env, jobject instance, jlong sensitivity) {
|
||||
GetAppEngine()->OnCameraParameterChanged(ACAMERA_SENSOR_SENSITIVITY,
|
||||
sensitivity);
|
||||
}
|
@ -0,0 +1,459 @@
|
||||
/*
|
||||
* 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 "image_reader.h"
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "utils/native_debug.h"
|
||||
|
||||
/*
|
||||
* For JPEG capture, captured files are saved under
|
||||
* DirName
|
||||
* File names are incrementally appended an index number as
|
||||
* capture0.jpg, capture1.jpg, capture2.jpg
|
||||
*/
|
||||
static const char *kDirName = "/sdcard/DCIM/Camera/";
|
||||
static const char *kFileName = "capture";
|
||||
|
||||
/**
|
||||
* MAX_BUF_COUNT:
|
||||
* Max buffers in this ImageReader.
|
||||
*/
|
||||
#define MAX_BUF_COUNT 4
|
||||
|
||||
/**
|
||||
* ImageReader listener: called by AImageReader for every frame captured
|
||||
* We pass the event to ImageReader class, so it could do some housekeeping
|
||||
* about
|
||||
* the loaded queue. For example, we could keep a counter to track how many
|
||||
* buffers are full and idle in the queue. If camera almost has no buffer to
|
||||
* capture
|
||||
* we could release ( skip ) some frames by AImageReader_getNextImage() and
|
||||
* AImageReader_delete().
|
||||
*/
|
||||
void OnImageCallback(void *ctx, AImageReader *reader) {
|
||||
reinterpret_cast<ImageReader *>(ctx)->ImageCallback(reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
ImageReader::ImageReader(ImageFormat *res, enum AIMAGE_FORMATS format)
|
||||
: presentRotation_(0), reader_(nullptr) {
|
||||
callback_ = nullptr;
|
||||
callbackCtx_ = nullptr;
|
||||
|
||||
media_status_t status = AImageReader_new(res->width, res->height, format,
|
||||
MAX_BUF_COUNT, &reader_);
|
||||
ASSERT(reader_ && status == AMEDIA_OK, "Failed to create AImageReader");
|
||||
|
||||
AImageReader_ImageListener listener{
|
||||
.context = this,
|
||||
.onImageAvailable = OnImageCallback,
|
||||
};
|
||||
AImageReader_setImageListener(reader_, &listener);
|
||||
}
|
||||
|
||||
ImageReader::~ImageReader() {
|
||||
ASSERT(reader_, "NULL Pointer to %s", __FUNCTION__);
|
||||
AImageReader_delete(reader_);
|
||||
}
|
||||
|
||||
void ImageReader::RegisterCallback(
|
||||
void *ctx, std::function<void(void *ctx, const char *fileName)> func) {
|
||||
callbackCtx_ = ctx;
|
||||
callback_ = func;
|
||||
}
|
||||
|
||||
void ImageReader::ImageCallback(AImageReader *reader) {
|
||||
int32_t format;
|
||||
media_status_t status = AImageReader_getFormat(reader, &format);
|
||||
ASSERT(status == AMEDIA_OK, "Failed to get the media format");
|
||||
if (format == AIMAGE_FORMAT_JPEG) {
|
||||
AImage *image = nullptr;
|
||||
media_status_t status = AImageReader_acquireNextImage(reader, &image);
|
||||
ASSERT(status == AMEDIA_OK && image, "Image is not available");
|
||||
|
||||
// Create a thread and write out the jpeg files
|
||||
std::thread writeFileHandler(&ImageReader::WriteFile, this, image);
|
||||
writeFileHandler.detach();
|
||||
}
|
||||
}
|
||||
|
||||
ANativeWindow *ImageReader::GetNativeWindow(void) {
|
||||
if (!reader_) return nullptr;
|
||||
ANativeWindow *nativeWindow;
|
||||
media_status_t status = AImageReader_getWindow(reader_, &nativeWindow);
|
||||
ASSERT(status == AMEDIA_OK, "Could not get ANativeWindow");
|
||||
|
||||
return nativeWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetNextImage()
|
||||
* Retrieve the next image in ImageReader's bufferQueue, NOT the last image so
|
||||
* no image is skipped. Recommended for batch/background processing.
|
||||
*/
|
||||
AImage *ImageReader::GetNextImage(void) {
|
||||
AImage *image;
|
||||
media_status_t status = AImageReader_acquireNextImage(reader_, &image);
|
||||
if (status != AMEDIA_OK) {
|
||||
return nullptr;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* GetLatestImage()
|
||||
* Retrieve the last image in ImageReader's bufferQueue, deleting images in
|
||||
* in front of it on the queue. Recommended for real-time processing.
|
||||
*/
|
||||
AImage *ImageReader::GetLatestImage(void) {
|
||||
AImage *image;
|
||||
media_status_t status = AImageReader_acquireLatestImage(reader_, &image);
|
||||
if (status != AMEDIA_OK) {
|
||||
return nullptr;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete Image
|
||||
* @param image {@link AImage} instance to be deleted
|
||||
*/
|
||||
void ImageReader::DeleteImage(AImage *image) {
|
||||
if (image) AImage_delete(image);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for YUV_420 to RGB conversion. Courtesy of Tensorflow
|
||||
* ImageClassifier Sample:
|
||||
* https://github.com/tensorflow/tensorflow/blob/master/tensorflow/examples/android/jni/yuv2rgb.cc
|
||||
* The difference is that here we have to swap UV plane when calling it.
|
||||
*/
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) \
|
||||
({ \
|
||||
__typeof__(a) _a = (a); \
|
||||
__typeof__(b) _b = (b); \
|
||||
_a > _b ? _a : _b; \
|
||||
})
|
||||
#define MIN(a, b) \
|
||||
({ \
|
||||
__typeof__(a) _a = (a); \
|
||||
__typeof__(b) _b = (b); \
|
||||
_a < _b ? _a : _b; \
|
||||
})
|
||||
#endif
|
||||
|
||||
// This value is 2 ^ 18 - 1, and is used to clamp the RGB values before their
|
||||
// ranges
|
||||
// are normalized to eight bits.
|
||||
static const int kMaxChannelValue = 262143;
|
||||
|
||||
static inline uint32_t YUV2RGB(int nY, int nU, int nV) {
|
||||
nY -= 16;
|
||||
nU -= 128;
|
||||
nV -= 128;
|
||||
if (nY < 0) nY = 0;
|
||||
|
||||
// This is the floating point equivalent. We do the conversion in integer
|
||||
// because some Android devices do not have floating point in hardware.
|
||||
// nR = (int)(1.164 * nY + 1.596 * nV);
|
||||
// nG = (int)(1.164 * nY - 0.813 * nV - 0.391 * nU);
|
||||
// nB = (int)(1.164 * nY + 2.018 * nU);
|
||||
|
||||
int nR = (int)(1192 * nY + 1634 * nV);
|
||||
int nG = (int)(1192 * nY - 833 * nV - 400 * nU);
|
||||
int nB = (int)(1192 * nY + 2066 * nU);
|
||||
|
||||
nR = MIN(kMaxChannelValue, MAX(0, nR));
|
||||
nG = MIN(kMaxChannelValue, MAX(0, nG));
|
||||
nB = MIN(kMaxChannelValue, MAX(0, nB));
|
||||
|
||||
nR = (nR >> 10) & 0xff;
|
||||
nG = (nG >> 10) & 0xff;
|
||||
nB = (nB >> 10) & 0xff;
|
||||
|
||||
return 0xff000000 | (nR << 16) | (nG << 8) | nB;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert yuv image inside AImage into ANativeWindow_Buffer
|
||||
* ANativeWindow_Buffer format is guaranteed to be
|
||||
* WINDOW_FORMAT_RGBX_8888
|
||||
* WINDOW_FORMAT_RGBA_8888
|
||||
* @param buf a {@link ANativeWindow_Buffer } instance, destination of
|
||||
* image conversion
|
||||
* @param image a {@link AImage} instance, source of image conversion.
|
||||
* it will be deleted via {@link AImage_delete}
|
||||
*/
|
||||
bool ImageReader::DisplayImage(ANativeWindow_Buffer *buf, AImage *image) {
|
||||
ASSERT(buf->format == WINDOW_FORMAT_RGBX_8888 ||
|
||||
buf->format == WINDOW_FORMAT_RGBA_8888,
|
||||
"Not supported buffer format");
|
||||
|
||||
int32_t srcFormat = -1;
|
||||
AImage_getFormat(image, &srcFormat);
|
||||
ASSERT(AIMAGE_FORMAT_YUV_420_888 == srcFormat, "Failed to get format");
|
||||
int32_t srcPlanes = 0;
|
||||
AImage_getNumberOfPlanes(image, &srcPlanes);
|
||||
ASSERT(srcPlanes == 3, "Is not 3 planes");
|
||||
|
||||
switch (presentRotation_) {
|
||||
case 0:
|
||||
PresentImage(buf, image);
|
||||
break;
|
||||
case 90:
|
||||
PresentImage90(buf, image);
|
||||
break;
|
||||
case 180:
|
||||
PresentImage180(buf, image);
|
||||
break;
|
||||
case 270:
|
||||
PresentImage270(buf, image);
|
||||
break;
|
||||
default:
|
||||
ASSERT(0, "NOT recognized display rotation: %d", presentRotation_);
|
||||
}
|
||||
|
||||
AImage_delete(image);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* PresentImage()
|
||||
* Converting yuv to RGB
|
||||
* No rotation: (x,y) --> (x, y)
|
||||
* Refer to:
|
||||
* https://mathbits.com/MathBits/TISection/Geometry/Transformations2.htm
|
||||
*/
|
||||
void ImageReader::PresentImage(ANativeWindow_Buffer *buf, AImage *image) {
|
||||
AImageCropRect srcRect;
|
||||
AImage_getCropRect(image, &srcRect);
|
||||
|
||||
int32_t yStride, uvStride;
|
||||
uint8_t *yPixel, *uPixel, *vPixel;
|
||||
int32_t yLen, uLen, vLen;
|
||||
AImage_getPlaneRowStride(image, 0, &yStride);
|
||||
AImage_getPlaneRowStride(image, 1, &uvStride);
|
||||
AImage_getPlaneData(image, 0, &yPixel, &yLen);
|
||||
AImage_getPlaneData(image, 1, &vPixel, &vLen);
|
||||
AImage_getPlaneData(image, 2, &uPixel, &uLen);
|
||||
int32_t uvPixelStride;
|
||||
AImage_getPlanePixelStride(image, 1, &uvPixelStride);
|
||||
|
||||
int32_t height = MIN(buf->height, (srcRect.bottom - srcRect.top));
|
||||
int32_t width = MIN(buf->width, (srcRect.right - srcRect.left));
|
||||
|
||||
uint32_t *out = static_cast<uint32_t *>(buf->bits);
|
||||
for (int32_t y = 0; y < height; y++) {
|
||||
const uint8_t *pY = yPixel + yStride * (y + srcRect.top) + srcRect.left;
|
||||
|
||||
int32_t uv_row_start = uvStride * ((y + srcRect.top) >> 1);
|
||||
const uint8_t *pU = uPixel + uv_row_start + (srcRect.left >> 1);
|
||||
const uint8_t *pV = vPixel + uv_row_start + (srcRect.left >> 1);
|
||||
|
||||
for (int32_t x = 0; x < width; x++) {
|
||||
const int32_t uv_offset = (x >> 1) * uvPixelStride;
|
||||
out[x] = YUV2RGB(pY[x], pU[uv_offset], pV[uv_offset]);
|
||||
}
|
||||
out += buf->stride;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* PresentImage90()
|
||||
* Converting YUV to RGB
|
||||
* Rotation image anti-clockwise 90 degree -- (x, y) --> (-y, x)
|
||||
*/
|
||||
void ImageReader::PresentImage90(ANativeWindow_Buffer *buf, AImage *image) {
|
||||
AImageCropRect srcRect;
|
||||
AImage_getCropRect(image, &srcRect);
|
||||
|
||||
int32_t yStride, uvStride;
|
||||
uint8_t *yPixel, *uPixel, *vPixel;
|
||||
int32_t yLen, uLen, vLen;
|
||||
AImage_getPlaneRowStride(image, 0, &yStride);
|
||||
AImage_getPlaneRowStride(image, 1, &uvStride);
|
||||
AImage_getPlaneData(image, 0, &yPixel, &yLen);
|
||||
AImage_getPlaneData(image, 1, &vPixel, &vLen);
|
||||
AImage_getPlaneData(image, 2, &uPixel, &uLen);
|
||||
int32_t uvPixelStride;
|
||||
AImage_getPlanePixelStride(image, 1, &uvPixelStride);
|
||||
|
||||
int32_t height = MIN(buf->width, (srcRect.bottom - srcRect.top));
|
||||
int32_t width = MIN(buf->height, (srcRect.right - srcRect.left));
|
||||
|
||||
uint32_t *out = static_cast<uint32_t *>(buf->bits);
|
||||
out += height - 1;
|
||||
for (int32_t y = 0; y < height; y++) {
|
||||
const uint8_t *pY = yPixel + yStride * (y + srcRect.top) + srcRect.left;
|
||||
|
||||
int32_t uv_row_start = uvStride * ((y + srcRect.top) >> 1);
|
||||
const uint8_t *pU = uPixel + uv_row_start + (srcRect.left >> 1);
|
||||
const uint8_t *pV = vPixel + uv_row_start + (srcRect.left >> 1);
|
||||
|
||||
for (int32_t x = 0; x < width; x++) {
|
||||
const int32_t uv_offset = (x >> 1) * uvPixelStride;
|
||||
// [x, y]--> [-y, x]
|
||||
out[x * buf->stride] = YUV2RGB(pY[x], pU[uv_offset], pV[uv_offset]);
|
||||
}
|
||||
out -= 1; // move to the next column
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* PresentImage180()
|
||||
* Converting yuv to RGB
|
||||
* Rotate image 180 degree: (x, y) --> (-x, -y)
|
||||
*/
|
||||
void ImageReader::PresentImage180(ANativeWindow_Buffer *buf, AImage *image) {
|
||||
AImageCropRect srcRect;
|
||||
AImage_getCropRect(image, &srcRect);
|
||||
|
||||
int32_t yStride, uvStride;
|
||||
uint8_t *yPixel, *uPixel, *vPixel;
|
||||
int32_t yLen, uLen, vLen;
|
||||
AImage_getPlaneRowStride(image, 0, &yStride);
|
||||
AImage_getPlaneRowStride(image, 1, &uvStride);
|
||||
AImage_getPlaneData(image, 0, &yPixel, &yLen);
|
||||
AImage_getPlaneData(image, 1, &vPixel, &vLen);
|
||||
AImage_getPlaneData(image, 2, &uPixel, &uLen);
|
||||
int32_t uvPixelStride;
|
||||
AImage_getPlanePixelStride(image, 1, &uvPixelStride);
|
||||
|
||||
int32_t height = MIN(buf->height, (srcRect.bottom - srcRect.top));
|
||||
int32_t width = MIN(buf->width, (srcRect.right - srcRect.left));
|
||||
|
||||
uint32_t *out = static_cast<uint32_t *>(buf->bits);
|
||||
out += (height - 1) * buf->stride;
|
||||
for (int32_t y = 0; y < height; y++) {
|
||||
const uint8_t *pY = yPixel + yStride * (y + srcRect.top) + srcRect.left;
|
||||
|
||||
int32_t uv_row_start = uvStride * ((y + srcRect.top) >> 1);
|
||||
const uint8_t *pU = uPixel + uv_row_start + (srcRect.left >> 1);
|
||||
const uint8_t *pV = vPixel + uv_row_start + (srcRect.left >> 1);
|
||||
|
||||
for (int32_t x = 0; x < width; x++) {
|
||||
const int32_t uv_offset = (x >> 1) * uvPixelStride;
|
||||
// mirror image since we are using front camera
|
||||
out[width - 1 - x] = YUV2RGB(pY[x], pU[uv_offset], pV[uv_offset]);
|
||||
// out[x] = YUV2RGB(pY[x], pU[uv_offset], pV[uv_offset]);
|
||||
}
|
||||
out -= buf->stride;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* PresentImage270()
|
||||
* Converting image from YUV to RGB
|
||||
* Rotate Image counter-clockwise 270 degree: (x, y) --> (y, x)
|
||||
*/
|
||||
void ImageReader::PresentImage270(ANativeWindow_Buffer *buf, AImage *image) {
|
||||
AImageCropRect srcRect;
|
||||
AImage_getCropRect(image, &srcRect);
|
||||
|
||||
int32_t yStride, uvStride;
|
||||
uint8_t *yPixel, *uPixel, *vPixel;
|
||||
int32_t yLen, uLen, vLen;
|
||||
AImage_getPlaneRowStride(image, 0, &yStride);
|
||||
AImage_getPlaneRowStride(image, 1, &uvStride);
|
||||
AImage_getPlaneData(image, 0, &yPixel, &yLen);
|
||||
AImage_getPlaneData(image, 1, &vPixel, &vLen);
|
||||
AImage_getPlaneData(image, 2, &uPixel, &uLen);
|
||||
int32_t uvPixelStride;
|
||||
AImage_getPlanePixelStride(image, 1, &uvPixelStride);
|
||||
|
||||
int32_t height = MIN(buf->width, (srcRect.bottom - srcRect.top));
|
||||
int32_t width = MIN(buf->height, (srcRect.right - srcRect.left));
|
||||
|
||||
uint32_t *out = static_cast<uint32_t *>(buf->bits);
|
||||
for (int32_t y = 0; y < height; y++) {
|
||||
const uint8_t *pY = yPixel + yStride * (y + srcRect.top) + srcRect.left;
|
||||
|
||||
int32_t uv_row_start = uvStride * ((y + srcRect.top) >> 1);
|
||||
const uint8_t *pU = uPixel + uv_row_start + (srcRect.left >> 1);
|
||||
const uint8_t *pV = vPixel + uv_row_start + (srcRect.left >> 1);
|
||||
|
||||
for (int32_t x = 0; x < width; x++) {
|
||||
const int32_t uv_offset = (x >> 1) * uvPixelStride;
|
||||
out[(width - 1 - x) * buf->stride] =
|
||||
YUV2RGB(pY[x], pU[uv_offset], pV[uv_offset]);
|
||||
}
|
||||
out += 1; // move to the next column
|
||||
}
|
||||
}
|
||||
void ImageReader::SetPresentRotation(int32_t angle) {
|
||||
presentRotation_ = angle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write out jpeg files to kDirName directory
|
||||
* @param image point capture jpg image
|
||||
*/
|
||||
void ImageReader::WriteFile(AImage *image) {
|
||||
int planeCount;
|
||||
media_status_t status = AImage_getNumberOfPlanes(image, &planeCount);
|
||||
ASSERT(status == AMEDIA_OK && planeCount == 1,
|
||||
"Error: getNumberOfPlanes() planeCount = %d", planeCount);
|
||||
uint8_t *data = nullptr;
|
||||
int len = 0;
|
||||
AImage_getPlaneData(image, 0, &data, &len);
|
||||
|
||||
DIR *dir = opendir(kDirName);
|
||||
if (dir) {
|
||||
closedir(dir);
|
||||
} else {
|
||||
std::string cmd = "mkdir -p ";
|
||||
cmd += kDirName;
|
||||
system(cmd.c_str());
|
||||
}
|
||||
|
||||
struct timespec ts {
|
||||
0, 0
|
||||
};
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
struct tm localTime;
|
||||
localtime_r(&ts.tv_sec, &localTime);
|
||||
|
||||
std::string fileName = kDirName;
|
||||
std::string dash("-");
|
||||
fileName += kFileName + std::to_string(localTime.tm_mon) +
|
||||
std::to_string(localTime.tm_mday) + dash +
|
||||
std::to_string(localTime.tm_hour) +
|
||||
std::to_string(localTime.tm_min) +
|
||||
std::to_string(localTime.tm_sec) + ".jpg";
|
||||
FILE *file = fopen(fileName.c_str(), "wb");
|
||||
if (file && data && len) {
|
||||
fwrite(data, 1, len, file);
|
||||
fclose(file);
|
||||
|
||||
if (callback_) {
|
||||
callback_(callbackCtx_, fileName.c_str());
|
||||
}
|
||||
} else {
|
||||
if (file) fclose(file);
|
||||
}
|
||||
AImage_delete(image);
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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_IMAGE_READER_H
|
||||
#define CAMERA_IMAGE_READER_H
|
||||
#include <media/NdkImageReader.h>
|
||||
|
||||
#include <functional>
|
||||
/*
|
||||
* ImageFormat:
|
||||
* A Data Structure to communicate resolution between camera and ImageReader
|
||||
*/
|
||||
struct ImageFormat {
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
|
||||
int32_t format; // Through out this demo, the format is fixed to
|
||||
// YUV_420 format
|
||||
};
|
||||
|
||||
class ImageReader {
|
||||
public:
|
||||
/**
|
||||
* Ctor and Dtor()
|
||||
*/
|
||||
explicit ImageReader(ImageFormat* res, enum AIMAGE_FORMATS format);
|
||||
|
||||
~ImageReader();
|
||||
|
||||
/**
|
||||
* Report cached ANativeWindow, which was used to create camera's capture
|
||||
* session output.
|
||||
*/
|
||||
ANativeWindow* GetNativeWindow(void);
|
||||
|
||||
/**
|
||||
* Retrieve Image on the top of Reader's queue
|
||||
*/
|
||||
AImage* GetNextImage(void);
|
||||
|
||||
/**
|
||||
* Retrieve Image on the back of Reader's queue, dropping older images
|
||||
*/
|
||||
AImage* GetLatestImage(void);
|
||||
|
||||
/**
|
||||
* Delete Image
|
||||
* @param image {@link AImage} instance to be deleted
|
||||
*/
|
||||
void DeleteImage(AImage* image);
|
||||
|
||||
/**
|
||||
* AImageReader callback handler. Called by AImageReader when a frame is
|
||||
* captured
|
||||
* (Internal function, not to be called by clients)
|
||||
*/
|
||||
void ImageCallback(AImageReader* reader);
|
||||
|
||||
/**
|
||||
* DisplayImage()
|
||||
* Present camera image to the given display buffer. Avaliable image is
|
||||
* converted
|
||||
* to display buffer format. Supported display format:
|
||||
* WINDOW_FORMAT_RGBX_8888
|
||||
* WINDOW_FORMAT_RGBA_8888
|
||||
* @param buf {@link ANativeWindow_Buffer} for image to display to.
|
||||
* @param image a {@link AImage} instance, source of image conversion.
|
||||
* it will be deleted via {@link AImage_delete}
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
bool DisplayImage(ANativeWindow_Buffer* buf, AImage* image);
|
||||
/**
|
||||
* Configure the rotation angle necessary to apply to
|
||||
* Camera image when presenting: all rotations should be accumulated:
|
||||
* CameraSensorOrientation + Android Device Native Orientation +
|
||||
* Human Rotation (rotated degree related to Phone native orientation
|
||||
*/
|
||||
void SetPresentRotation(int32_t angle);
|
||||
|
||||
/**
|
||||
* regsiter a callback function for client to be notified that jpeg already
|
||||
* written out.
|
||||
* @param ctx is client context when callback is invoked
|
||||
* @param callback is the actual callback function
|
||||
*/
|
||||
void RegisterCallback(void* ctx,
|
||||
std::function<void(void* ctx, const char* fileName)>);
|
||||
|
||||
private:
|
||||
int32_t presentRotation_;
|
||||
AImageReader* reader_;
|
||||
|
||||
std::function<void(void* ctx, const char* fileName)> callback_;
|
||||
void* callbackCtx_;
|
||||
|
||||
void PresentImage(ANativeWindow_Buffer* buf, AImage* image);
|
||||
void PresentImage90(ANativeWindow_Buffer* buf, AImage* image);
|
||||
void PresentImage180(ANativeWindow_Buffer* buf, AImage* image);
|
||||
void PresentImage270(ANativeWindow_Buffer* buf, AImage* image);
|
||||
|
||||
void WriteFile(AImage* image);
|
||||
};
|
||||
|
||||
#endif // CAMERA_IMAGE_READER_H
|
@ -0,0 +1,459 @@
|
||||
/*
|
||||
* 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_utils.h"
|
||||
|
||||
#include <camera/NdkCameraManager.h>
|
||||
#include <media/NdkImage.h>
|
||||
|
||||
#include <cinttypes>
|
||||
#include <string>
|
||||
#include <typeinfo>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "utils/native_debug.h"
|
||||
|
||||
#define UKNOWN_TAG "UNKNOW_TAG"
|
||||
#define MAKE_PAIR(val) std::make_pair(val, #val)
|
||||
template <typename T>
|
||||
const char* GetPairStr(T key, std::vector<std::pair<T, const char*>>& store) {
|
||||
typedef typename std::vector<std::pair<T, const char*>>::iterator iterator;
|
||||
for (iterator it = store.begin(); it != store.end(); ++it) {
|
||||
if (it->first == key) {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
LOGW("(%#08x) : UNKNOWN_TAG for %s", key, typeid(store[0].first).name());
|
||||
return UKNOWN_TAG;
|
||||
}
|
||||
|
||||
/*
|
||||
* camera_status_t error translation
|
||||
*/
|
||||
using ERROR_PAIR = std::pair<camera_status_t, const char*>;
|
||||
static std::vector<ERROR_PAIR> errorInfo{
|
||||
MAKE_PAIR(ACAMERA_OK),
|
||||
MAKE_PAIR(ACAMERA_ERROR_UNKNOWN),
|
||||
MAKE_PAIR(ACAMERA_ERROR_INVALID_PARAMETER),
|
||||
MAKE_PAIR(ACAMERA_ERROR_CAMERA_DISCONNECTED),
|
||||
MAKE_PAIR(ACAMERA_ERROR_NOT_ENOUGH_MEMORY),
|
||||
MAKE_PAIR(ACAMERA_ERROR_METADATA_NOT_FOUND),
|
||||
MAKE_PAIR(ACAMERA_ERROR_CAMERA_DEVICE),
|
||||
MAKE_PAIR(ACAMERA_ERROR_CAMERA_SERVICE),
|
||||
MAKE_PAIR(ACAMERA_ERROR_SESSION_CLOSED),
|
||||
MAKE_PAIR(ACAMERA_ERROR_INVALID_OPERATION),
|
||||
MAKE_PAIR(ACAMERA_ERROR_STREAM_CONFIGURE_FAIL),
|
||||
MAKE_PAIR(ACAMERA_ERROR_CAMERA_IN_USE),
|
||||
MAKE_PAIR(ACAMERA_ERROR_MAX_CAMERA_IN_USE),
|
||||
MAKE_PAIR(ACAMERA_ERROR_CAMERA_DISABLED),
|
||||
MAKE_PAIR(ACAMERA_ERROR_PERMISSION_DENIED),
|
||||
};
|
||||
const char* GetErrorStr(camera_status_t err) {
|
||||
return GetPairStr<camera_status_t>(err, errorInfo);
|
||||
}
|
||||
|
||||
/*
|
||||
* camera_metadata_tag_t translation. Useful to look at available tags
|
||||
* on the underneath platform
|
||||
*/
|
||||
using TAG_PAIR = std::pair<acamera_metadata_tag_t, const char*>;
|
||||
static std::vector<TAG_PAIR> tagInfo{
|
||||
MAKE_PAIR(ACAMERA_COLOR_CORRECTION_MODE),
|
||||
MAKE_PAIR(ACAMERA_COLOR_CORRECTION_TRANSFORM),
|
||||
MAKE_PAIR(ACAMERA_COLOR_CORRECTION_GAINS),
|
||||
MAKE_PAIR(ACAMERA_COLOR_CORRECTION_ABERRATION_MODE),
|
||||
MAKE_PAIR(ACAMERA_COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES),
|
||||
MAKE_PAIR(ACAMERA_COLOR_CORRECTION_END),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AE_ANTIBANDING_MODE),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AE_EXPOSURE_COMPENSATION),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AE_LOCK),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AE_MODE),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AE_REGIONS),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AE_TARGET_FPS_RANGE),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AE_PRECAPTURE_TRIGGER),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AF_MODE),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AF_REGIONS),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AF_TRIGGER),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AWB_LOCK),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AWB_MODE),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AWB_REGIONS),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_CAPTURE_INTENT),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_EFFECT_MODE),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_MODE),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_SCENE_MODE),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_VIDEO_STABILIZATION_MODE),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AE_AVAILABLE_ANTIBANDING_MODES),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AE_AVAILABLE_MODES),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AE_COMPENSATION_RANGE),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AE_COMPENSATION_STEP),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AF_AVAILABLE_MODES),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AVAILABLE_EFFECTS),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AVAILABLE_SCENE_MODES),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AWB_AVAILABLE_MODES),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_MAX_REGIONS),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AE_STATE),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AF_STATE),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AWB_STATE),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AE_LOCK_AVAILABLE),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AWB_LOCK_AVAILABLE),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_AVAILABLE_MODES),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_POST_RAW_SENSITIVITY_BOOST),
|
||||
MAKE_PAIR(ACAMERA_CONTROL_END),
|
||||
MAKE_PAIR(ACAMERA_EDGE_MODE),
|
||||
MAKE_PAIR(ACAMERA_EDGE_AVAILABLE_EDGE_MODES),
|
||||
MAKE_PAIR(ACAMERA_EDGE_END),
|
||||
MAKE_PAIR(ACAMERA_FLASH_MODE),
|
||||
MAKE_PAIR(ACAMERA_FLASH_STATE),
|
||||
MAKE_PAIR(ACAMERA_FLASH_END),
|
||||
MAKE_PAIR(ACAMERA_FLASH_INFO_AVAILABLE),
|
||||
MAKE_PAIR(ACAMERA_FLASH_INFO_END),
|
||||
MAKE_PAIR(ACAMERA_HOT_PIXEL_MODE),
|
||||
MAKE_PAIR(ACAMERA_HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES),
|
||||
MAKE_PAIR(ACAMERA_HOT_PIXEL_END),
|
||||
MAKE_PAIR(ACAMERA_JPEG_GPS_COORDINATES),
|
||||
MAKE_PAIR(ACAMERA_JPEG_GPS_PROCESSING_METHOD),
|
||||
MAKE_PAIR(ACAMERA_JPEG_GPS_TIMESTAMP),
|
||||
MAKE_PAIR(ACAMERA_JPEG_ORIENTATION),
|
||||
MAKE_PAIR(ACAMERA_JPEG_QUALITY),
|
||||
MAKE_PAIR(ACAMERA_JPEG_THUMBNAIL_QUALITY),
|
||||
MAKE_PAIR(ACAMERA_JPEG_THUMBNAIL_SIZE),
|
||||
MAKE_PAIR(ACAMERA_JPEG_AVAILABLE_THUMBNAIL_SIZES),
|
||||
MAKE_PAIR(ACAMERA_JPEG_END),
|
||||
MAKE_PAIR(ACAMERA_LENS_APERTURE),
|
||||
MAKE_PAIR(ACAMERA_LENS_FILTER_DENSITY),
|
||||
MAKE_PAIR(ACAMERA_LENS_FOCAL_LENGTH),
|
||||
MAKE_PAIR(ACAMERA_LENS_FOCUS_DISTANCE),
|
||||
MAKE_PAIR(ACAMERA_LENS_OPTICAL_STABILIZATION_MODE),
|
||||
MAKE_PAIR(ACAMERA_LENS_FACING),
|
||||
MAKE_PAIR(ACAMERA_LENS_POSE_ROTATION),
|
||||
MAKE_PAIR(ACAMERA_LENS_POSE_TRANSLATION),
|
||||
MAKE_PAIR(ACAMERA_LENS_FOCUS_RANGE),
|
||||
MAKE_PAIR(ACAMERA_LENS_STATE),
|
||||
MAKE_PAIR(ACAMERA_LENS_INTRINSIC_CALIBRATION),
|
||||
MAKE_PAIR(ACAMERA_LENS_RADIAL_DISTORTION),
|
||||
MAKE_PAIR(ACAMERA_LENS_END),
|
||||
|
||||
MAKE_PAIR(ACAMERA_LENS_INFO_AVAILABLE_APERTURES),
|
||||
MAKE_PAIR(ACAMERA_LENS_INFO_AVAILABLE_FILTER_DENSITIES),
|
||||
MAKE_PAIR(ACAMERA_LENS_INFO_AVAILABLE_FOCAL_LENGTHS),
|
||||
MAKE_PAIR(ACAMERA_LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION),
|
||||
MAKE_PAIR(ACAMERA_LENS_INFO_HYPERFOCAL_DISTANCE),
|
||||
MAKE_PAIR(ACAMERA_LENS_INFO_MINIMUM_FOCUS_DISTANCE),
|
||||
MAKE_PAIR(ACAMERA_LENS_INFO_SHADING_MAP_SIZE),
|
||||
MAKE_PAIR(ACAMERA_LENS_INFO_FOCUS_DISTANCE_CALIBRATION),
|
||||
MAKE_PAIR(ACAMERA_LENS_INFO_END),
|
||||
|
||||
MAKE_PAIR(ACAMERA_NOISE_REDUCTION_MODE),
|
||||
MAKE_PAIR(ACAMERA_NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES),
|
||||
MAKE_PAIR(ACAMERA_NOISE_REDUCTION_END),
|
||||
|
||||
MAKE_PAIR(ACAMERA_REQUEST_MAX_NUM_OUTPUT_STREAMS),
|
||||
MAKE_PAIR(ACAMERA_REQUEST_PIPELINE_DEPTH),
|
||||
MAKE_PAIR(ACAMERA_REQUEST_PIPELINE_MAX_DEPTH),
|
||||
MAKE_PAIR(ACAMERA_REQUEST_PARTIAL_RESULT_COUNT),
|
||||
MAKE_PAIR(ACAMERA_REQUEST_AVAILABLE_CAPABILITIES),
|
||||
MAKE_PAIR(ACAMERA_REQUEST_AVAILABLE_REQUEST_KEYS),
|
||||
MAKE_PAIR(ACAMERA_REQUEST_AVAILABLE_RESULT_KEYS),
|
||||
MAKE_PAIR(ACAMERA_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS),
|
||||
MAKE_PAIR(ACAMERA_REQUEST_END),
|
||||
|
||||
MAKE_PAIR(ACAMERA_SCALER_CROP_REGION),
|
||||
MAKE_PAIR(ACAMERA_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM),
|
||||
MAKE_PAIR(ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS),
|
||||
MAKE_PAIR(ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS),
|
||||
MAKE_PAIR(ACAMERA_SCALER_AVAILABLE_STALL_DURATIONS),
|
||||
MAKE_PAIR(ACAMERA_SCALER_CROPPING_TYPE),
|
||||
MAKE_PAIR(ACAMERA_SCALER_END),
|
||||
|
||||
MAKE_PAIR(ACAMERA_SENSOR_EXPOSURE_TIME),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_FRAME_DURATION),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_SENSITIVITY),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_REFERENCE_ILLUMINANT1),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_REFERENCE_ILLUMINANT2),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_CALIBRATION_TRANSFORM1),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_CALIBRATION_TRANSFORM2),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_COLOR_TRANSFORM1),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_COLOR_TRANSFORM2),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_FORWARD_MATRIX1),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_FORWARD_MATRIX2),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_BLACK_LEVEL_PATTERN),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_MAX_ANALOG_SENSITIVITY),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_ORIENTATION),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_TIMESTAMP),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_NEUTRAL_COLOR_POINT),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_NOISE_PROFILE),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_GREEN_SPLIT),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_TEST_PATTERN_DATA),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_TEST_PATTERN_MODE),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_AVAILABLE_TEST_PATTERN_MODES),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_ROLLING_SHUTTER_SKEW),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_OPTICAL_BLACK_REGIONS),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_DYNAMIC_BLACK_LEVEL),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_DYNAMIC_WHITE_LEVEL),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_END),
|
||||
|
||||
MAKE_PAIR(ACAMERA_SENSOR_INFO_ACTIVE_ARRAY_SIZE),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_INFO_SENSITIVITY_RANGE),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_INFO_EXPOSURE_TIME_RANGE),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_INFO_MAX_FRAME_DURATION),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_INFO_PHYSICAL_SIZE),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_INFO_PIXEL_ARRAY_SIZE),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_INFO_WHITE_LEVEL),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_INFO_TIMESTAMP_SOURCE),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_INFO_LENS_SHADING_APPLIED),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE),
|
||||
MAKE_PAIR(ACAMERA_SENSOR_INFO_END),
|
||||
|
||||
MAKE_PAIR(ACAMERA_SHADING_MODE),
|
||||
MAKE_PAIR(ACAMERA_SHADING_AVAILABLE_MODES),
|
||||
MAKE_PAIR(ACAMERA_SHADING_END),
|
||||
|
||||
MAKE_PAIR(ACAMERA_STATISTICS_FACE_DETECT_MODE),
|
||||
MAKE_PAIR(ACAMERA_STATISTICS_HOT_PIXEL_MAP_MODE),
|
||||
MAKE_PAIR(ACAMERA_STATISTICS_FACE_IDS),
|
||||
MAKE_PAIR(ACAMERA_STATISTICS_FACE_LANDMARKS),
|
||||
MAKE_PAIR(ACAMERA_STATISTICS_FACE_RECTANGLES),
|
||||
MAKE_PAIR(ACAMERA_STATISTICS_FACE_SCORES),
|
||||
MAKE_PAIR(ACAMERA_STATISTICS_LENS_SHADING_MAP),
|
||||
MAKE_PAIR(ACAMERA_STATISTICS_SCENE_FLICKER),
|
||||
MAKE_PAIR(ACAMERA_STATISTICS_HOT_PIXEL_MAP),
|
||||
MAKE_PAIR(ACAMERA_STATISTICS_LENS_SHADING_MAP_MODE),
|
||||
MAKE_PAIR(ACAMERA_STATISTICS_END),
|
||||
|
||||
MAKE_PAIR(ACAMERA_STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES),
|
||||
MAKE_PAIR(ACAMERA_STATISTICS_INFO_MAX_FACE_COUNT),
|
||||
MAKE_PAIR(ACAMERA_STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES),
|
||||
MAKE_PAIR(ACAMERA_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES),
|
||||
MAKE_PAIR(ACAMERA_STATISTICS_INFO_END),
|
||||
|
||||
MAKE_PAIR(ACAMERA_TONEMAP_CURVE_BLUE),
|
||||
MAKE_PAIR(ACAMERA_TONEMAP_CURVE_GREEN),
|
||||
MAKE_PAIR(ACAMERA_TONEMAP_CURVE_RED),
|
||||
MAKE_PAIR(ACAMERA_TONEMAP_MODE),
|
||||
MAKE_PAIR(ACAMERA_TONEMAP_MAX_CURVE_POINTS),
|
||||
MAKE_PAIR(ACAMERA_TONEMAP_AVAILABLE_TONE_MAP_MODES),
|
||||
MAKE_PAIR(ACAMERA_TONEMAP_GAMMA),
|
||||
MAKE_PAIR(ACAMERA_TONEMAP_PRESET_CURVE),
|
||||
MAKE_PAIR(ACAMERA_TONEMAP_END),
|
||||
|
||||
MAKE_PAIR(ACAMERA_INFO_SUPPORTED_HARDWARE_LEVEL),
|
||||
MAKE_PAIR(ACAMERA_INFO_END),
|
||||
MAKE_PAIR(ACAMERA_BLACK_LEVEL_LOCK),
|
||||
MAKE_PAIR(ACAMERA_BLACK_LEVEL_END),
|
||||
MAKE_PAIR(ACAMERA_SYNC_FRAME_NUMBER),
|
||||
MAKE_PAIR(ACAMERA_SYNC_MAX_LATENCY),
|
||||
MAKE_PAIR(ACAMERA_SYNC_END),
|
||||
MAKE_PAIR(ACAMERA_DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS),
|
||||
|
||||
MAKE_PAIR(ACAMERA_DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS),
|
||||
MAKE_PAIR(ACAMERA_DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS),
|
||||
MAKE_PAIR(ACAMERA_DEPTH_DEPTH_IS_EXCLUSIVE),
|
||||
MAKE_PAIR(ACAMERA_DEPTH_END),
|
||||
};
|
||||
const char* GetTagStr(acamera_metadata_tag_t tag) {
|
||||
return GetPairStr<acamera_metadata_tag_t>(tag, tagInfo);
|
||||
}
|
||||
|
||||
using FORMAT_PAIR = std::pair<int, const char*>;
|
||||
static std::vector<FORMAT_PAIR> formatInfo{
|
||||
MAKE_PAIR(AIMAGE_FORMAT_YUV_420_888),
|
||||
MAKE_PAIR(AIMAGE_FORMAT_JPEG),
|
||||
MAKE_PAIR(AIMAGE_FORMAT_RAW16),
|
||||
MAKE_PAIR(AIMAGE_FORMAT_RAW_PRIVATE),
|
||||
MAKE_PAIR(AIMAGE_FORMAT_RAW10),
|
||||
MAKE_PAIR(AIMAGE_FORMAT_RAW12),
|
||||
MAKE_PAIR(AIMAGE_FORMAT_DEPTH16),
|
||||
MAKE_PAIR(AIMAGE_FORMAT_DEPTH_POINT_CLOUD),
|
||||
MAKE_PAIR(AIMAGE_FORMAT_PRIVATE),
|
||||
};
|
||||
const char* GetFormatStr(int fmt) { return GetPairStr<int>(fmt, formatInfo); }
|
||||
|
||||
void PrintMetadataTags(int32_t entries, const uint32_t* pTags) {
|
||||
LOGI("MetadataTag (start):");
|
||||
for (int32_t idx = 0; idx < entries; ++idx) {
|
||||
const char* name =
|
||||
GetTagStr(static_cast<acamera_metadata_tag_t>(pTags[idx]));
|
||||
LOGI("(%#08x) : %s", pTags[idx], name);
|
||||
}
|
||||
LOGI("MetadataTag (end)");
|
||||
}
|
||||
|
||||
void PrintLensFacing(ACameraMetadata_const_entry& lens) {
|
||||
ASSERT(lens.tag == ACAMERA_LENS_FACING, "Wrong tag(%#x) of %s to %s",
|
||||
lens.tag, GetTagStr((acamera_metadata_tag_t)lens.tag), __FUNCTION__);
|
||||
LOGI("LensFacing: tag(%#x), type(%d), count(%d), val(%#x)", lens.tag,
|
||||
lens.type, lens.count, lens.data.u8[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Stream_Configuration is in format of:
|
||||
* format, width, height, input?
|
||||
* ACAMERA_TYPE_INT32 type
|
||||
*/
|
||||
void PrintStreamConfigurations(ACameraMetadata_const_entry& val) {
|
||||
#define MODE_LABLE "ModeInfo:"
|
||||
const char* tagName = GetTagStr(static_cast<acamera_metadata_tag_t>(val.tag));
|
||||
ASSERT(!(val.count & 0x3), "STREAM_CONFIGURATION (%d) should multiple of 4",
|
||||
val.count);
|
||||
ASSERT(val.type == ACAMERA_TYPE_INT32,
|
||||
"STREAM_CONFIGURATION TYPE(%d) is not ACAMERA_TYPE_INT32(1)",
|
||||
val.type);
|
||||
LOGI("%s -- %s:", tagName, MODE_LABLE);
|
||||
for (uint32_t i = 0; i < val.count; i += 4) {
|
||||
LOGI("%s: %08d x %08d %s", GetFormatStr(val.data.i32[i]),
|
||||
val.data.i32[i + 1], val.data.i32[i + 2],
|
||||
val.data.i32[i + 3] ? "INPUT" : "OUTPUT");
|
||||
}
|
||||
#undef MODE_LABLE
|
||||
}
|
||||
void PrintTagVal(const char* printLabel, ACameraMetadata_const_entry& val) {
|
||||
if (val.tag == ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS) {
|
||||
PrintStreamConfigurations(val);
|
||||
return;
|
||||
}
|
||||
const char* name = GetTagStr(static_cast<acamera_metadata_tag_t>(val.tag));
|
||||
for (uint32_t i = 0; i < val.count; ++i) {
|
||||
switch (val.type) {
|
||||
case ACAMERA_TYPE_INT32:
|
||||
LOGI("%s %s: %08d", printLabel, name, val.data.i32[i]);
|
||||
break;
|
||||
case ACAMERA_TYPE_BYTE:
|
||||
LOGI("%s %s: %#02x", printLabel, name, val.data.u8[i]);
|
||||
break;
|
||||
case ACAMERA_TYPE_INT64:
|
||||
LOGI("%s %s: %" PRIu64, printLabel, name, (int64_t)val.data.i64[i]);
|
||||
break;
|
||||
case ACAMERA_TYPE_FLOAT:
|
||||
LOGI("%s %s: %f", printLabel, name, val.data.f[i]);
|
||||
break;
|
||||
case ACAMERA_TYPE_DOUBLE:
|
||||
LOGI("%s %s: %" PRIx64, printLabel, name, val.data.i64[i]);
|
||||
break;
|
||||
case ACAMERA_TYPE_RATIONAL:
|
||||
LOGI("%s %s: %08x, %08x", printLabel, name, val.data.r[i].numerator,
|
||||
val.data.r[i].denominator);
|
||||
break;
|
||||
default:
|
||||
ASSERT(false, "Unknown tag value type: %d", val.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* PrintCamera():
|
||||
* Enumerate existing camera and its metadata.
|
||||
*/
|
||||
void PrintCameras(ACameraManager* cmrMgr) {
|
||||
if (!cmrMgr) return;
|
||||
|
||||
ACameraIdList* cameraIds = nullptr;
|
||||
camera_status_t status = ACameraManager_getCameraIdList(cmrMgr, &cameraIds);
|
||||
LOGI("camera Status = %d, %s", status, GetErrorStr(status));
|
||||
|
||||
for (int i = 0; i < cameraIds->numCameras; ++i) {
|
||||
const char* id = cameraIds->cameraIds[i];
|
||||
LOGI("=====cameraId = %d, cameraName = %s=====", i, id);
|
||||
ACameraMetadata* metadataObj;
|
||||
CALL_MGR(getCameraCharacteristics(cmrMgr, id, &metadataObj));
|
||||
|
||||
int32_t count = 0;
|
||||
const uint32_t* tags = nullptr;
|
||||
ACameraMetadata_getAllTags(metadataObj, &count, &tags);
|
||||
|
||||
for (int tagIdx = 0; tagIdx < count; ++tagIdx) {
|
||||
ACameraMetadata_const_entry val = {
|
||||
0,
|
||||
};
|
||||
camera_status_t status =
|
||||
ACameraMetadata_getConstEntry(metadataObj, tags[tagIdx], &val);
|
||||
if (status != ACAMERA_OK) {
|
||||
LOGW("Unsupported Tag: %s",
|
||||
GetTagStr(static_cast<acamera_metadata_tag_t>(tags[tagIdx])));
|
||||
continue;
|
||||
}
|
||||
|
||||
PrintTagVal("Camera Tag:", val);
|
||||
|
||||
if (ACAMERA_LENS_FACING == tags[tagIdx]) {
|
||||
PrintLensFacing(val);
|
||||
}
|
||||
}
|
||||
ACameraMetadata_free(metadataObj);
|
||||
}
|
||||
|
||||
ACameraManager_deleteCameraIdList(cameraIds);
|
||||
}
|
||||
|
||||
void PrintRequestMetadata(ACaptureRequest* req) {
|
||||
if (!req) return;
|
||||
int32_t count;
|
||||
const uint32_t* tags;
|
||||
CALL_REQUEST(getAllTags(req, &count, &tags));
|
||||
for (int32_t idx = 0; idx < count; ++idx) {
|
||||
ACameraMetadata_const_entry val;
|
||||
CALL_REQUEST(getConstEntry(req, tags[idx], &val));
|
||||
const char* name =
|
||||
GetTagStr(static_cast<acamera_metadata_tag_t>(tags[idx]));
|
||||
|
||||
for (uint32_t i = 0; i < val.count; ++i) {
|
||||
switch (val.type) {
|
||||
case ACAMERA_TYPE_INT32:
|
||||
LOGI("Capture Tag %s: %08d", name, val.data.i32[i]);
|
||||
break;
|
||||
case ACAMERA_TYPE_BYTE:
|
||||
LOGI("Capture Tag %s: %#08x", name, val.data.u8[i]);
|
||||
break;
|
||||
case ACAMERA_TYPE_INT64:
|
||||
LOGI("Capture Tag %s: %" PRIu64, name, (int64_t)val.data.i64[i]);
|
||||
break;
|
||||
case ACAMERA_TYPE_FLOAT:
|
||||
LOGI("Capture Tag %s: %f", name, val.data.f[i]);
|
||||
break;
|
||||
case ACAMERA_TYPE_DOUBLE:
|
||||
LOGI("Capture Tag %s: %" PRIx64, name, val.data.i64[i]);
|
||||
break;
|
||||
case ACAMERA_TYPE_RATIONAL:
|
||||
LOGI("Capture Tag %s: %08x, %08x", name, val.data.r[i].numerator,
|
||||
val.data.r[i].denominator);
|
||||
break;
|
||||
default:
|
||||
ASSERT(false, "Unknown tag value type: %d", val.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* CameraDevice error state translation, used in
|
||||
* ACameraDevice_ErrorStateCallback
|
||||
*/
|
||||
using DEV_ERROR_PAIR = std::pair<int, const char*>;
|
||||
static std::vector<DEV_ERROR_PAIR> devErrors{
|
||||
MAKE_PAIR(ERROR_CAMERA_IN_USE), MAKE_PAIR(ERROR_MAX_CAMERAS_IN_USE),
|
||||
MAKE_PAIR(ERROR_CAMERA_DISABLED), MAKE_PAIR(ERROR_CAMERA_DEVICE),
|
||||
MAKE_PAIR(ERROR_CAMERA_SERVICE),
|
||||
};
|
||||
|
||||
const char* GetCameraDeviceErrorStr(int err) {
|
||||
return GetPairStr<int>(err, devErrors);
|
||||
}
|
||||
void PrintCameraDeviceError(int err) {
|
||||
LOGI("CameraDeviceError(%#x): %s", err, GetCameraDeviceErrorStr(err));
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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_CAMERA_UTILS_H__
|
||||
#define __CAMERA_CAMERA_UTILS_H__
|
||||
|
||||
#include <camera/NdkCameraError.h>
|
||||
#include <camera/NdkCameraManager.h>
|
||||
|
||||
/*
|
||||
* A set of macros to call into Camera APIs. The API is grouped with a few
|
||||
* objects, with object name as the prefix of function names.
|
||||
*/
|
||||
#define CALL_CAMERA(func) \
|
||||
{ \
|
||||
camera_status_t status = func; \
|
||||
ASSERT(status == ACAMERA_OK, "%s call failed with code: %#x, %s", \
|
||||
__FUNCTION__, status, GetErrorStr(status)); \
|
||||
}
|
||||
#define CALL_MGR(func) CALL_CAMERA(ACameraManager_##func)
|
||||
#define CALL_DEV(func) CALL_CAMERA(ACameraDevice_##func)
|
||||
#define CALL_METADATA(func) CALL_CAMERA(ACameraMetadata_##func)
|
||||
#define CALL_CONTAINER(func) CALL_CAMERA(ACaptureSessionOutputContainer_##func)
|
||||
#define CALL_OUTPUT(func) CALL_CAMERA(ACaptureSessionOutput_##func)
|
||||
#define CALL_TARGET(func) CALL_CAMERA(ACameraOutputTarget_##func)
|
||||
#define CALL_REQUEST(func) CALL_CAMERA(ACaptureRequest_##func)
|
||||
#define CALL_SESSION(func) CALL_CAMERA(ACameraCaptureSession_##func)
|
||||
|
||||
/*
|
||||
* A few debugging functions for error code strings etc
|
||||
*/
|
||||
const char* GetErrorStr(camera_status_t err);
|
||||
const char* GetTagStr(acamera_metadata_tag_t tag);
|
||||
void PrintMetadataTags(int32_t entries, const uint32_t* pTags);
|
||||
void PrintLensFacing(ACameraMetadata_const_entry& lensData);
|
||||
void PrintCameras(ACameraManager* cameraMgr);
|
||||
void PrintCameraDeviceError(int err);
|
||||
|
||||
void PrintRequestMetadata(ACaptureRequest* req);
|
||||
#endif // __CAMERA_CAMERA_UTILS_H__
|
@ -0,0 +1,10 @@
|
||||
#include <android/log.h>
|
||||
|
||||
#define LOG_TAG "CAMERA-SAMPLE"
|
||||
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
#define ASSERT(cond, fmt, ...) \
|
||||
if (!(cond)) { \
|
||||
__android_log_assert(#cond, LOG_TAG, fmt, ##__VA_ARGS__); \
|
||||
}
|
@ -0,0 +1,217 @@
|
||||
package com.xinyingpower.microphoto;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class FloatingWindow extends Service {
|
||||
|
||||
private Context mContext;
|
||||
private WindowManager mWindowManager;
|
||||
private View mView;
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
mContext = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
|
||||
|
||||
allAboutLayout(intent);
|
||||
moveView();
|
||||
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
|
||||
if (mView != null) {
|
||||
mWindowManager.removeView(mView);
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
WindowManager.LayoutParams mWindowsParams;
|
||||
private void moveView() {
|
||||
DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
|
||||
int width = (int) (metrics.widthPixels * 1f);
|
||||
int height = (int) (metrics.heightPixels * 1f);
|
||||
|
||||
mWindowsParams = new WindowManager.LayoutParams(
|
||||
width,//WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
height,//WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
//WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
|
||||
|
||||
(Build.VERSION.SDK_INT <= 25) ? WindowManager.LayoutParams.TYPE_PHONE : WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
|
||||
,
|
||||
|
||||
//WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
|
||||
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||
| WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN // Not displaying keyboard on bg activity's EditText
|
||||
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
||||
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
|
||||
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
|
||||
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON,
|
||||
//WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, //Not work with EditText on keyboard
|
||||
PixelFormat.TRANSLUCENT);
|
||||
|
||||
|
||||
mWindowsParams.gravity = Gravity.TOP | Gravity.LEFT;
|
||||
//params.x = 0;
|
||||
mWindowsParams.y = 100;
|
||||
mWindowManager.addView(mView, mWindowsParams);
|
||||
|
||||
mView.setOnTouchListener(new View.OnTouchListener() {
|
||||
private int initialX;
|
||||
private int initialY;
|
||||
private float initialTouchX;
|
||||
private float initialTouchY;
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (System.currentTimeMillis() - startTime <= 300) {
|
||||
return false;
|
||||
}
|
||||
if (isViewInBounds(mView, (int) (event.getRawX()), (int) (event.getRawY()))) {
|
||||
editTextReceiveFocus();
|
||||
} else {
|
||||
editTextDontReceiveFocus();
|
||||
}
|
||||
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
initialX = mWindowsParams.x;
|
||||
initialY = mWindowsParams.y;
|
||||
initialTouchX = event.getRawX();
|
||||
initialTouchY = event.getRawY();
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
mWindowsParams.x = initialX + (int) (event.getRawX() - initialTouchX);
|
||||
mWindowsParams.y = initialY + (int) (event.getRawY() - initialTouchY);
|
||||
mWindowManager.updateViewLayout(mView, mWindowsParams);
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isViewInBounds(View view, int x, int y) {
|
||||
Rect outRect = new Rect();
|
||||
int[] location = new int[2];
|
||||
view.getDrawingRect(outRect);
|
||||
view.getLocationOnScreen(location);
|
||||
outRect.offset(location[0], location[1]);
|
||||
return outRect.contains(x, y);
|
||||
}
|
||||
|
||||
private void editTextReceiveFocus() {
|
||||
if (!wasInFocus) {
|
||||
mWindowsParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
|
||||
mWindowManager.updateViewLayout(mView, mWindowsParams);
|
||||
wasInFocus = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void editTextDontReceiveFocus() {
|
||||
if (wasInFocus) {
|
||||
mWindowsParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
|
||||
mWindowManager.updateViewLayout(mView, mWindowsParams);
|
||||
wasInFocus = false;
|
||||
hideKeyboard(mContext, edt1);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean wasInFocus = true;
|
||||
private EditText edt1;
|
||||
private void allAboutLayout(Intent intent) {
|
||||
|
||||
LayoutInflater layoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
mView = layoutInflater.inflate(R.layout.ovelay_window, null);
|
||||
|
||||
edt1 = (EditText) mView.findViewById(R.id.edt1);
|
||||
final TextView tvValue = (TextView) mView.findViewById(R.id.tvValue);
|
||||
Button btnClose = (Button) mView.findViewById(R.id.btnClose);
|
||||
|
||||
edt1.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mWindowsParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
|
||||
mWindowsParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE;
|
||||
mWindowManager.updateViewLayout(mView, mWindowsParams);
|
||||
wasInFocus = true;
|
||||
showSoftKeyboard(v);
|
||||
}
|
||||
});
|
||||
|
||||
edt1.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
tvValue.setText(edt1.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
btnClose.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
stopSelf();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void hideKeyboard(Context context, View view) {
|
||||
if (view != null) {
|
||||
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void showSoftKeyboard(View view) {
|
||||
if (view.requestFocus()) {
|
||||
InputMethodManager imm = (InputMethodManager)
|
||||
getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
package com.xinyingpower.microphoto;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class ScreenActionReceiver extends BroadcastReceiver {
|
||||
|
||||
private String TAG = "ScreenActionReceiver";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
|
||||
|
||||
//LOG
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Action: " + intent.getAction() + "\n");
|
||||
sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\n");
|
||||
String log = sb.toString();
|
||||
Log.d(TAG, log);
|
||||
Toast.makeText(context, log, Toast.LENGTH_LONG).show();
|
||||
|
||||
String action = intent.getAction();
|
||||
|
||||
if(Intent.ACTION_SCREEN_ON.equals(action))
|
||||
{
|
||||
Log.d(TAG, "screen is on...");
|
||||
Toast.makeText(context,"screen ON",Toast.LENGTH_LONG);
|
||||
|
||||
//Run the locker
|
||||
|
||||
context.startService(new Intent(context, FloatingWindow.class));
|
||||
|
||||
|
||||
}
|
||||
|
||||
else if(Intent.ACTION_SCREEN_OFF.equals(action))
|
||||
{
|
||||
Log.d(TAG, "screen is off...");
|
||||
Toast.makeText(context,"screen OFF",Toast.LENGTH_LONG);
|
||||
|
||||
}
|
||||
|
||||
else if(Intent.ACTION_USER_PRESENT.equals(action))
|
||||
{
|
||||
Log.d(TAG, "screen is unlock...");
|
||||
Toast.makeText(context,"screen UNLOCK",Toast.LENGTH_LONG);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(new Intent(context, FloatingWindow.class));
|
||||
} else {
|
||||
context.startService(new Intent(context, FloatingWindow.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
else if(Intent.ACTION_BOOT_COMPLETED.equals(action)){
|
||||
Log.d(TAG, "boot completed...");
|
||||
Toast.makeText(context,"BOOTED..",Toast.LENGTH_LONG);
|
||||
//Run the locker
|
||||
/* Intent i = new Intent(context, FloatingWindow.class);
|
||||
context.startService(i);
|
||||
|
||||
*/
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(new Intent(context, FloatingWindow.class));
|
||||
} else {
|
||||
context.startService(new Intent(context, FloatingWindow.class));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IntentFilter getFilter(){
|
||||
final IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_SCREEN_OFF);
|
||||
filter.addAction(Intent.ACTION_SCREEN_ON);
|
||||
return filter;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:weightSum="4">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="3"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_state"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="STATES"
|
||||
android:textSize="16sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_stop"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="STOP"
|
||||
android:textSize="13sp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/activity_main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
android:background="#548F32"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edt1"
|
||||
android:hint="Type here"
|
||||
android:layout_toLeftOf="@+id/btnClose"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Close"
|
||||
android:layout_alignParentRight="true"
|
||||
android:id="@+id/btnClose"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/tvValue"
|
||||
android:layout_below="@+id/edt1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
|
||||
</RelativeLayout>
|
@ -0,0 +1,5 @@
|
||||
<resources>
|
||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
</resources>
|
@ -1,3 +1,4 @@
|
||||
<resources>
|
||||
<string name="app_name">MicroPhoto</string>
|
||||
<string name="text_name_notification">Notification Name</string>
|
||||
</resources>
|
Loading…
Reference in New Issue