写在之前
因业务需求需要使用c++代码打开camera,之前没怎么接触过c++的应用,也算是一种全新的挑战吧。其实代码很简单,但是这中间有很多的坑,记录一下避免大家出现同样的问题。此方法打开camera的代码全是c++的,不需要使用到java,这样可以提高响应速度提升性能,优化用户体验。如果想要在打开camera后集成一些别的库实现一些接口,这样会比java更好。
但是这中间的坑就在这里,如果不在AndroidManifest.xml申请权限,camera是一定打不开的,而且它还不会报错,真是晕了。。。
附上代码
ACameraManager* cameraManager = nullptr;
ACameraDevice* cameraDevice = nullptr;
ACaptureRequest* captureRequest = nullptr;
ACameraOutputTarget* cameraOutputTarget = nullptr;
ACameraCaptureSession* captureSession = nullptr;
AImageReader* imageReader = nullptr;
ANativeWindow* nativeWindow = nullptr;
AImage* image = nullptr;
// Callback functions
void onCameraDeviceError(void* context, ACameraDevice* device, int error) {
// Handle camera device error
}
void onCameraDeviceDisconnected(void* context, ACameraDevice* device) {
// Handle camera device disconnected
}
void onCaptureSessionClosed(void* context, ACameraCaptureSession* session) {
// Handle capture session closed
}
void onCaptureSessionActive(void* context, ACameraCaptureSession* session) {
// Handle capture session active
}
void eventCallback(void* context ,func_getHandEvent event){
cont = context;
getHandEvent = event;
}
void onImageAvailable(void* context, AImageReader* reader) {
media_status_t mStatus = AImageReader_acquireLatestImage(reader, &image);
if (mStatus != AMEDIA_OK) {
return;
}else{
//一般打开摄像头后最重要的就在这里
//如果想要做什么事一定是在这个回调处获取得到的image进行处理
//handleHandData(image);
}
//得到image处理完后记得删除,不然缓冲区溢出了你打开的camera就寄了
AImage_delete(image);
}
bool openCamera(const char* cameraId) {
// 根据cameraID打开摄像头
ACameraDevice_StateCallbacks deviceStateCallbacks {
.context = nullptr,
.onDisconnected = onCameraDeviceDisconnected,
.onError = onCameraDeviceError
};
camera_status_t status = ACameraManager_openCamera(cameraManager, cameraId, &deviceStateCallbacks, &cameraDevice);
return status == ACAMERA_OK;
}
bool createCaptureRequest(ACameraDevice_request_template templateId) {
camera_status_t status = ACameraDevice_createCaptureRequest(cameraDevice, templateId, &captureRequest);
if (status != ACAMERA_OK) return false;
status = ACaptureRequest_addTarget(captureRequest, cameraOutputTarget);
return status == ACAMERA_OK;
}
bool createCaptureSession() {
int *i = nullptr;
ACaptureSessionOutputContainer* outputContainer = nullptr;
ACaptureSessionOutput* sessionOutput = nullptr;
//初始化ACaptureSessionOutputContainer
camera_status_t status = ACaptureSessionOutputContainer_create(&outputContainer);
if (status != ACAMERA_OK) return false;
//初始化ACaptureSessionOutput
status = ACaptureSessionOutput_create(nativeWindow, &sessionOutput);
if (status != ACAMERA_OK) return false;
status = ACaptureSessionOutputContainer_add(outputContainer, sessionOutput);
if (status != ACAMERA_OK) return false;
ACameraCaptureSession_stateCallbacks sessionStateCallbacks {
.context = nullptr,
.onClosed = onCaptureSessionClosed,
.onReady = nullptr,
.onActive = onCaptureSessionActive
};
//初始化ACameraCaptureSession
status = ACameraDevice_createCaptureSession(cameraDevice, outputContainer, &sessionStateCallbacks, &captureSession);
if (status != ACAMERA_OK) return false;
//在这里开始去重复请求拿到图片进行输出
status = ACameraCaptureSession_setRepeatingRequest(captureSession, nullptr, 1, &captureRequest,
i++);
return status == ACAMERA_OK;
}
bool createImageReader(int width, int height, int format, int maxImages) {
//要循环去读取图片,创建一个reader
media_status_t status = AImageReader_new(width, height, format, maxImages, &imageReader);
if (status != AMEDIA_OK) {
LOGI(TAG,"创建失败");
return false;
}
//在这里注册AImage的回调,重中之重
AImageReader_ImageListener imageListener {
.context = nullptr,
.onImageAvailable = onImageAvailable
};
status = AImageReader_setImageListener(imageReader, &imageListener);
if (status != AMEDIA_OK) return false;
status = AImageReader_getWindow(imageReader, &nativeWindow);
return status == AMEDIA_OK;
}
// 帮助类end
//以上都是帮助类,使用startCamera()函数对所需的类初始化并打开camera
void startCamera()
{
// 开始使用摄像头预览
cameraManager = ACameraManager_create();
// 获取摄像头id
ACameraIdList *cameraIdList = nullptr;
ACameraManager_getCameraIdList(cameraManager, &cameraIdList);
const char *cameraId = nullptr;
for (int i = 0; i < cameraIdList->numCameras; i++)
{
const char *id = cameraIdList->cameraIds[i];
ACameraMetadata *metadata = nullptr;
ACameraManager_getCameraCharacteristics(cameraManager, id, &metadata);
ACameraMetadata_const_entry entry;
ACameraMetadata_getConstEntry(metadata, ACAMERA_LENS_FACING, &entry);
auto facing = static_cast<acamera_metadata_enum_android_lens_facing_t>(entry.data.u8[0]);
if (facing == ACAMERA_LENS_FACING_FRONT)
{
cameraId = id;
LOGI(TAG,"cameraid=====%s",cameraId);
break;
}
}
// 打开摄像头并且读取image
//camera的参数自己定制
if (openCamera(cameraId) && createImageReader(1440 , 1080, AIMAGE_FORMAT_YUV_420_888, 2)){
// 创建一个输出的目标
ACameraOutputTarget_create(nativeWindow, &cameraOutputTarget);
// 创建捕获请求和捕获会话
if (createCaptureRequest(TEMPLATE_RECORD)){
createCaptureSession();
}
}
}
//简单的暂停一下
void pauseCamera(){
ACameraDevice_close(cameraDevice);
cameraDevice = nullptr;
}
//关闭camera,注意要释放掉不为空的结构
void stopCamera(){
// Stop the camera and preview and release the resources
if (captureSession != nullptr) {
ACameraCaptureSession_stopRepeating(captureSession);
ACameraCaptureSession_close(captureSession);
captureSession = nullptr;
}
if (captureRequest != nullptr) {
ACaptureRequest_removeTarget(captureRequest, cameraOutputTarget);
ACaptureRequest_free(captureRequest);
captureRequest = nullptr;
}
if (cameraOutputTarget != nullptr) {
ACameraOutputTarget_free(cameraOutputTarget);
cameraOutputTarget = nullptr;
}
if (cameraDevice != nullptr) {
ACameraDevice_close(cameraDevice);
cameraDevice = nullptr;
}
if (imageReader != nullptr) {
AImageReader_delete(imageReader);
imageReader = nullptr;
}
if (cameraManager != nullptr) {
ACameraManager_delete(cameraManager);
cameraManager = nullptr;
}
// Closed the camera and preview successfully
}
想说的话
一
相信大家使用ndk打开camera不仅仅只是想打开camera这么简单,那么最重要的一环就是在onImageAvailable()回调函数中去进行需要的数据处理,每一个回调得到的AImage都可以获取并通过本地集成的算法库解析出想要的数据,一定要注意要在AndroidManifest.xml中注册camera权限,不然啥错也不会报但就是打不开,顺手可以请求其他的权限
<uses-permission android:name="android.permission.CAMERA"
tools:ignore="PermissionImpliesUnsupportedChromeOsHardware" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
二
这段代码只是打开了camera,但是没有实现预览,预览的代码可以在网上搜一搜很简单。那么我们这里不进行预览的话可以怎样做呢?诶,我们可以把这个apk的mainActivity也隐藏掉,将他直接当成一个系统服务需要的时候进行开启。我猜人脸识别的功能原理大概也是这样吧,只是集成了不同的算法库。
那有的朋友就要问了,不进行预览我们怎么知道究竟有没有成功打开camera呢?这里有两种方法
- 在onImageAvailable()函数中保存得到的AImage,push到本地进行查看
- 查看日志
CamX会告诉你答案~
结束
以上就是全流程了,因为把公司业务相关代码删除了所以整理的可能不太清晰。如有错漏的地方欢迎指出让我修改到位QAQ