> 技术文档 > Android Camera openCamera

Android Camera openCamera


由头

今日调休,终于终于闲下来了,可以写一下博客了,刚好打开自己电脑,就有四年前下的谷歌Android 12源码,不是很旧,刚好够用,不用再另外下载新源码了,不得不感慨这时间过得真快啊~废话不多说,开整!

过程分解

我们知道Camera操作过程中最重要的四个步骤(这边仅先以preview,capture流说明,video流还得另说):

CameraManager–>openCamera —> 打开相机
CameraDeviceImpl–>createCaptureSession —> 创建捕获会话
CameraCaptureSession–>setRepeatingRequest —> 设置预览界面
CameraDeviceImpl–>capture —> 开始捕获图片

后面有空的话会按序讲解,这篇文章仅以openCamera过程进行说明

获取CameraManager

首先我们看下上层如果要openCamera需要做什么,以下是一个常见的应用调用api2接口打开相机过程

 private void openCamera() { CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { // 获取相机ID cameraId = getCameraId(manager); if (cameraId == null) { Toast.makeText(this, \"未找到可用相机\", Toast.LENGTH_SHORT).show(); return; } // 检查相机权限 if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)  != PackageManager.PERMISSION_GRANTED) { // 请求相机权限 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 101); return; } // 获取相机特性 CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); if (map != null) { // 获取最佳预览尺寸 imageDimension = map.getOutputSizes(SurfaceTexture.class)[0]; } // 打开相机 manager.openCamera(cameraId, stateCallback, mBackgroundHandler); } catch (CameraAccessException e) { e.printStackTrace(); } }

这篇文章我们只关注其中两行代码

CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);manager.openCamera(cameraId, stateCallback, mBackgroundHandler);

首先(CameraManager) getSystemService(Context.CAMERA_SERVICE);为啥能返回CameraManger实例,这就涉及到getSystemService函数,那我们先来看下getSystemService函数实现
frameworks/base/core/java/android/app/ContextImpl.java

 @Override public Object getSystemService(String name) { //直接到关键函数 return SystemServiceRegistry.getSystemService(this, name); }

这里需要跳转到另一个类SystemServiceRegistry.java
frameworks/base/core/java/android/app/SystemServiceRegistry.java

 /** * Gets a system service from a given context. * @hide */ public static Object getSystemService(ContextImpl ctx, String name) { final ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name); if (fetcher == null) { if (sEnableServiceNotFoundWtf) { Slog.wtf(TAG, \"Unknown manager requested: \" + name); } return null; } final Object ret = fetcher.getService(ctx); return ret }}

根据返回值可以倒推这个ret就是CameraManger实例,那这个是怎么来的呢,这个是根据
SYSTEM_SERVICE_FETCHERS.get(name);和fetcher.getService(ctx);共同决定的,但要看这两个逻辑具体做了啥需要先知道以下的逻辑,然后我们再回来看

当应用进程启动并首次使用任何系统服务(如 getSystemService)时,SystemServiceRegistry 类会被 ClassLoader 加载,此时静态代码块执行,所有系统服务(包括相机服务)的注册逻辑会一次性完成其中就包括CameraManager

static { registerService(Context.CAMERA_SERVICE, CameraManager.class, new CachedServiceFetcher<CameraManager>() { @Override public CameraManager createService(ContextImpl ctx) { return new CameraManager(ctx); }}); }

我们需要知道registerService做了什么,可以看到SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);

 /** * Statically registers a system service with the context. * This method must be called during static initialization only. */ private static <T> void registerService(@NonNull String serviceName, @NonNull Class<T> serviceClass, @NonNull ServiceFetcher<T> serviceFetcher) { SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName); SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher); SYSTEM_SERVICE_CLASS_NAMES.put(serviceName, serviceClass.getSimpleName()); }

根据注册的匹配规则直接看CachedServiceFetcher

 /** * Override this class when the system service constructor needs a * ContextImpl and should be cached and retained by that context. */ static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> { private final int mCacheIndex; @Override @SuppressWarnings(\"unchecked\") public final T getService(ContextImpl ctx) { T service = null;  @ServiceInitializationState int newState = ContextImpl.STATE_NOT_FOUND;  try { // This thread is the first one to get here. Instantiate the service // *without* the cache lock held. service = createService(ctx); newState = ContextImpl.STATE_READY;  } catch (ServiceNotFoundException e) { onServiceNotFound(e);  } finally { synchronized (cache) { cache[mCacheIndex] = service; gates[mCacheIndex] = newState; cache.notifyAll(); }  }  ret = service;  return ret; }

所以上面的 final ServiceFetcher fetcher = SYSTEM_SERVICE_FETCHERS.get(name);根据匹配Context.CAMERA_SERVICE对应到CachedServiceFetcher,然后再在final Object ret = fetcher.getService(ctx);这里调用createService回调

 registerService(Context.CAMERA_SERVICE, CameraManager.class, new CachedServiceFetcher<CameraManager>() { @Override public CameraManager createService(ContextImpl ctx) { return new CameraManager(ctx); }});

从而获取到了CameraManager对象实例

manager.openCamera

那下面就继续说下manager.openCamera(cameraId, stateCallback, mBackgroundHandler);做了什么吧,首先

frameworks/base/core/java/android/hardware/camera2/CameraManager.java

 @RequiresPermission(android.Manifest.permission.CAMERA) public void openCamera(@NonNull String cameraId, @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler) throws CameraAccessException { openCameraForUid(cameraId, callback, CameraDeviceImpl.checkAndWrapHandler(handler), USE_CALLING_UID); }

这里的源码提示了需要android.Manifest.permission.CAMERA权限,如果应用调用openCamera的时候需要动态申请,并且这是公共方法,应用开发者可以直接调用

 /** * Open a connection to a camera with the given ID, on behalf of another application * specified by clientUid. * * <p>The behavior of this method matches that of {@link #openCamera}, except that it allows * the caller to specify the UID to use for permission/etc verification. This can only be * done by services trusted by the camera subsystem to act on behalf of applications and * to forward the real UID.</p> * * @param clientUid * The UID of the application on whose behalf the camera is being opened. * Must be USE_CALLING_UID unless the caller is a trusted service. * * @hide */ public void openCameraForUid(@NonNull String cameraId, @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor, int clientUid) throws CameraAccessException { openCameraForUid(cameraId, callback, executor, clientUid, /*oomScoreOffset*/0); } //此方法已经是隐藏方法了 /** * Open a connection to a camera with the given ID, on behalf of another application * specified by clientUid. Also specify the minimum oom score and process state the application * should have, as seen by the cameraserver. * * <p>The behavior of this method matches that of {@link #openCamera}, except that it allows * the caller to specify the UID to use for permission/etc verification. This can only be * done by services trusted by the camera subsystem to act on behalf of applications and * to forward the real UID.</p> * * @param clientUid * The UID of the application on whose behalf the camera is being opened. * Must be USE_CALLING_UID unless the caller is a trusted service. * @param oomScoreOffset * The minimum oom score that cameraservice must see for this client. * @hide */ public void openCameraForUid(@NonNull String cameraId, @NonNull final CameraDevice.StateCallback callback, @NonNull Executor executor, int clientUid, int oomScoreOffset) throws CameraAccessException { if (cameraId == null) { throw new IllegalArgumentException(\"cameraId was null\"); } else if (callback == null) { throw new IllegalArgumentException(\"callback was null\"); } if (CameraManagerGlobal.sCameraServiceDisabled) { throw new IllegalArgumentException(\"No cameras available on device\"); } openCameraDeviceUserAsync(cameraId, callback, executor, clientUid, oomScoreOffset); }这个方法上个openCameraForUid的进一步延续,接着继续往下看```bash private CameraDevice openCameraDeviceUserAsync(String cameraId, CameraDevice.StateCallback callback, Executor executor, final int uid, final int oomScoreOffset) throws CameraAccessException { ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); //中间代码已省略 if (cameraService == null) {  throw new ServiceSpecificException( ICameraService.ERROR_DISCONNECTED, \"Camera service is currently unavailable\"); } cameraUser = cameraService.connectDevice(callbacks, cameraId,  mContext.getOpPackageName(), mContext.getAttributionTag(), uid,  oomScoreOffset, mContext.getApplicationInfo().targetSdkVersion); }

连接cameraService 并调用connectDevice函数,下来看下connectDevice具体做了什么

frameworks/av/services/camera/libcameraservice/CameraService.cpp

下面代码已有删减,只讲一些重点

Status CameraService::connectDevice( const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb, const String16& cameraId, const String16& clientPackageName, const std::optional<String16>& clientFeatureId, int clientUid, int oomScoreOffset, int targetSdkVersion, /*out*/ sp<hardware::camera2::ICameraDeviceUser>* device) { int callingPid = CameraThreadState::getCallingPid(); //这里通常可以加一下日志,看下是谁调用的相机,再加一下调用的时间戳,可以帮助分析线上问题 //顺带提一下,这里会强行校验前面提到的相机权限,所以没有权限是行不通的,老实申请 // enforce system camera permissions if (oomScoreOffset > 0 && !hasPermissionsForSystemCamera(callingPid, CameraThreadState::getCallingUid())) { String8 msg = String8::format(\"Cannot change the priority of a client %s pid %d for \" \"camera id %s without SYSTEM_CAMERA permissions\", String8(clientPackageNameAdj).string(), callingPid, id.string()); ALOGE(\"%s: %s\", __FUNCTION__, msg.string()); return STATUS_ERROR(ERROR_PERMISSION_DENIED, msg.string()); } //这里是openCamera的关键流程,主要是调用到了connectHelper,其他的我们先不关注,这里主要关注的就是open camera 设备流程 ret = connectHelper<hardware::camera2::ICameraDeviceCallbacks,CameraDeviceClient>(cameraCb, id, /*api1CameraId*/-1, clientPackageNameAdj, clientFeatureId, clientUid, USE_CALLING_PID, API_2, /*shimUpdateOnly*/ false, oomScoreOffset, targetSdkVersion, /*out*/client); }

这里直接跳转到connectHelper函数,只贴了下需要关注的代码

template<class CALLBACK, class CLIENT>Status CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId, int api1CameraId, const String16& clientPackageName, const std::optional<String16>& clientFeatureId, int clientUid, int clientPid, apiLevel effectiveApiLevel, bool shimUpdateOnly, int oomScoreOffset, int targetSdkVersion, /*out*/sp<CLIENT>& device) {**//handleEvictionsLocked这个函数很关键,是进行相机多进程互斥逻辑的方法。多个应用无法同时打开相机就是在这个方法中进行管理的,所以建议为了方便调试定位问题,在这里加一下日志** if ((err = handleEvictionsLocked(cameraId, originalClientPid, effectiveApiLevel, IInterface::asBinder(cameraCb), clientName8, oomScoreOffset, /*out*/&clientTmp, /*out*/&partial)) != NO_ERROR) { switch (err) { case -ENODEV:  return STATUS_ERROR_FMT(ERROR_DISCONNECTED, \"No camera device with ID \\\"%s\\\" currently available\", cameraId.string()); case -EBUSY:  return STATUS_ERROR_FMT(ERROR_CAMERA_IN_USE, \"Higher-priority client using camera, ID \\\"%s\\\" currently unavailable\", cameraId.string()); case -EUSERS:  return STATUS_ERROR_FMT(ERROR_MAX_CAMERAS_IN_USE, \"Too many cameras already open, cannot open camera \\\"%s\\\"\", cameraId.string()); default:  return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION, \"Unexpected error %s (%d) opening camera \\\"%s\\\"\", strerror(-err), err, cameraId.string()); } //相机打开的情况下,此时会关闭所有闪光灯的控制,这也是为了保持相机相应硬件资源的独占性,保证相机功能的正常使用 // give flashlight a chance to close devices if necessary. mFlashlight->prepareDeviceOpen(cameraId); // 这里进入了真正的client流程,通过getDeviceVersion方法取得对应底层HAL的版本,这里的版本与HAL本身的版本略有不同。作用是根据版本的不同确定HAL中的小版本,各个版本的区别是库中方法有所不同。 int deviceVersion = getDeviceVersion(cameraId, /*out*/&facing, /*out*/&orientation); //根据Device version调用makeClient方法创建Camera Client对象,makeClient方法主要有两个操作,1是判断API+HAL版本的通路; 2是根据判定结果决定创建哪个client。client类有三个,分别是CameraClient、Camera2Client和CameraDeviceClient。 if(!(ret = makeClient(this, cameraCb, clientPackageName, clientFeatureId, cameraId, api1CameraId, facing, orientation, clientPid, clientUid, getpid(), deviceVersion, effectiveApiLevel, overrideForPerfClass, /*out*/&tmp)).isOk()) { return ret; } //下面是连接CameraProvider ,然后连接hal,最后操作驱动打开相机的,这里也可以加一下日志打印连接过程如果出现异常可以方便排查异常原因 err = client->initialize(mCameraProviderManager, mMonitorTags); if (err != OK) { ALOGE(\"%s: Could not initialize client from HAL.\", __FUNCTION__); // Errors could be from the HAL module open call or from AppOpsManager switch(err) { case BAD_VALUE:  return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT, \"Illegal argument to HAL module for camera \\\"%s\\\"\", cameraId.string()); case -EBUSY:  return STATUS_ERROR_FMT(ERROR_CAMERA_IN_USE, \"Camera \\\"%s\\\" is already open\", cameraId.string()); case -EUSERS:  return STATUS_ERROR_FMT(ERROR_MAX_CAMERAS_IN_USE, \"Too many cameras already open, cannot open camera \\\"%s\\\"\", cameraId.string()); case PERMISSION_DENIED:  return STATUS_ERROR_FMT(ERROR_PERMISSION_DENIED, \"No permission to open camera \\\"%s\\\"\", cameraId.string()); case -EACCES:  return STATUS_ERROR_FMT(ERROR_DISABLED, \"Camera \\\"%s\\\" disabled by policy\", cameraId.string()); case -ENODEV: default:  return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION, \"Failed to initialize camera \\\"%s\\\": %s (%d)\", cameraId.string(), strerror(-err), err); } }

接着
frameworks/av/services/camera/libcameraservice/api2/CameraDeviceClient.cpp

status_t CameraDeviceClient::initialize(sp<CameraProviderManager> manager, const String8& monitorTags) { return initializeImpl(manager, monitorTags);}template<typename TProviderPtr>status_t CameraDeviceClient::initializeImpl(TProviderPtr providerPtr, const String8& monitorTags) { //关键跳转 res = Camera2ClientBase::initialize(providerPtr, monitorTags);}

直接到下一步:

frameworks/av/services/camera/libcameraservice/common/Camera2ClientBase.cpp

template <typename TClientBase>status_t Camera2ClientBase<TClientBase>::initialize(sp<CameraProviderManager> manager, const String8& monitorTags) { return initializeImpl(manager, monitorTags);}template <typename TClientBase>template <typename TProviderPtr>status_t Camera2ClientBase<TClientBase>::initializeImpl(TProviderPtr providerPtr, const String8& monitorTags) { res = mDevice->initialize(providerPtr, monitorTags); if (res != OK) { ALOGE(\"%s: Camera %s: unable to initialize device: %s (%d)\", __FUNCTION__, TClientBase::mCameraIdStr.string(), strerror(-res), res); return res; }}

接着跳转到Camera3Device.cpp,继续往下看
frameworks/av/services/camera/libcameraservice/device3/Camera3Device.cpp

status_t Camera3Device::initialize(sp<CameraProviderManager> manager, const String8& monitorTags) { status_t res = manager->openSession(mId.string(), this, /*out*/ &session);}

frameworks/av/services/camera/libcameraservice/common/CameraProviderManager.cpp

通过HIDL最终调用到了HAL层实现,由于CameraProvider就是谷歌专门用来隔离HAL和层和native service的耦合,所以接下来都是各个厂家自行的实现了,而interface->open作为统一开发接口

status_t CameraProviderManager::openSession(const std::string &id, const sp<device::V3_2::ICameraDeviceCallback>& callback, /*out*/ sp<device::V3_2::ICameraDeviceSession> *session) { ret = interface->open(callback, [&status, &session] (Status s, const sp<device::V3_2::ICameraDeviceSession>& cameraSession) { status = s; if (status == Status::OK) {  *session = cameraSession; } }); if (!ret.isOk()) {  removeRef(DeviceMode::CAMERA, id);  ALOGE(\"%s: Transaction error opening a session for camera device %s: %s\",  __FUNCTION__, id.c_str(), ret.description().c_str());  return DEAD_OBJECT; } }

interface对应代码

hardware/interfaces/camera/common/1.0/default/CameraModule.cpp

int CameraModule::open(const char* id, struct hw_device_t** device) { int res; ATRACE_BEGIN(\"camera_module->open\"); res = filterOpenErrorCode(mModule->common.methods->open(&mModule->common, id, device)); ATRACE_END(); return res;}

该方法非常简洁,就是调用mModule类的common.methods的open方法处理,它的mModule也是在CameraModule类的构造函数中传入的,而CameraModule的构造方法是在CameraProvider类的initialize()方法中调用的,源码如下
hardware/interfaces/camera/provider/2.4/default/LegacyCameraProviderImpl_2_4.cpp

bool LegacyCameraProviderImpl_2_4::initialize() { camera_module_t *rawModule; int err = hw_get_module(CAMERA_HARDWARE_MODULE_ID, (const hw_module_t **)&rawModule); if (err < 0) { ALOGE(\"Could not load camera HAL module: %d (%s)\", err, strerror(-err)); return true; } mModule = new CameraModule(rawModule); err = mModule->init(); if (err != OK) { ALOGE(\"Could not initialize camera HAL module: %d (%s)\", err, strerror(-err)); mModule.clear(); return true; }

在CameraProvider类的initialize()方法中调用hw_get_module获取到的就是这里定义的camera_module_t

hardware/qcom/camera/msm8998/QCamera2/QCamera2Hal.cpp

static hw_module_t camera_common = { .tag  = HARDWARE_MODULE_TAG, .module_api_version = CAMERA_MODULE_API_VERSION_2_5, .hal_api_version = HARDWARE_HAL_API_VERSION, .id  = CAMERA_HARDWARE_MODULE_ID, .name  = \"QCamera Module\", .author  = \"Qualcomm Innovation Center Inc\", .methods = &qcamera::QCamera2Factory::mModuleMethods, .dso  = NULL, .reserved  = {0}};camera_module_t HAL_MODULE_INFO_SYM = { .common  = camera_common, .get_number_of_cameras = qcamera::QCamera2Factory::get_number_of_cameras, .get_camera_info = qcamera::QCamera2Factory::get_camera_info, .set_callbacks = qcamera::QCamera2Factory::set_callbacks, .get_vendor_tag_ops = qcamera::QCamera3VendorTags::get_vendor_tag_ops, .open_legacy = NULL, .set_torch_mode = qcamera::QCamera2Factory::set_torch_mode, .init  = NULL, .get_physical_camera_info = qcamera::QCamera2Factory::get_physical_camera_info, .is_stream_combination_supported = qcamera::QCamera2Factory::is_stream_combination_supported, .reserved  = {0}}; 

hardware/qcom/camera/msm8998/QCamera2/QCamera2Hal.cpp

struct hw_module_methods_t QCamera2Factory::mModuleMethods = { .open = QCamera2Factory::camera_device_open,};

这里的open又指向了QCamera2Factory类的camera_device_open方法,该方法的源码如下:

int QCamera2Factory::camera_device_open( const struct hw_module_t *module, const char *id, struct hw_device_t **hw_device){ int rc = NO_ERROR; if (module != &HAL_MODULE_INFO_SYM.common) { LOGE(\"Invalid module. Trying to open %p, expect %p\", module, &HAL_MODULE_INFO_SYM.common); return INVALID_OPERATION; } if (!id) { LOGE(\"Invalid camera id\"); return BAD_VALUE; }#ifdef QCAMERA_HAL1_SUPPORT if(gQCameraMuxer) rc = gQCameraMuxer->camera_device_open(module, id, hw_device); else#endif rc = gQCamera2Factory->cameraDeviceOpen(atoi(id), hw_device); return rc;}

调用cameraDeviceOpen方法来处理,它的cameraDeviceOpen方法的源码如下:

int QCamera2Factory::cameraDeviceOpen(int camera_id,  struct hw_device_t **hw_device){ rc = hw->openCamera(&hw_dev[i]); return rc;}

hardware/qcom/camera/msm8998/QCamera2/HAL3/QCamera3HWI.cpp

int QCamera3HardwareInterface::openCamera(struct hw_device_t **hw_device){ rc = openCamera(); return rc;}
int QCamera3HardwareInterface::openCamera(){ rc = camera_open((uint8_t)mCameraId, &mCameraHandle); return NO_ERROR; }

hardware/qcom/camera/msm8998/QCamera2/stack/mm-camera-interface/src/mm_camera_interface.c

int32_t camera_open(uint8_t camera_idx, mm_camera_vtbl_t **camera_vtbl){ rc = mm_camera_open(cam_obj); return rc;}

hardware/qcom/camera/msm8998/QCamera2/stack/mm-camera-interface/src/mm_camera.c

int32_t mm_camera_open(mm_camera_obj_t *my_obj){ char dev_name[MM_CAMERA_DEV_NAME_LEN]; int32_t rc = 0; int8_t n_try=MM_CAMERA_DEV_OPEN_TRIES; uint8_t sleep_msec=MM_CAMERA_DEV_OPEN_RETRY_SLEEP; int cam_idx = 0; const char *dev_name_value = NULL; int l_errno = 0; LOGD(\"begin\\n\"); if (NULL == my_obj) { goto on_error; } dev_name_value = mm_camera_util_get_dev_name_by_num(my_obj->my_num, my_obj->my_hdl); if (NULL == dev_name_value) { goto on_error; } snprintf(dev_name, sizeof(dev_name), \"/dev/%s\", dev_name_value); sscanf(dev_name, \"/dev/video%d\", &cam_idx); LOGD(\"dev name = %s, cam_idx = %d\", dev_name, cam_idx); do{ n_try--; errno = 0; my_obj->ctrl_fd = open(dev_name, O_RDWR | O_NONBLOCK); l_errno = errno; LOGD(\"ctrl_fd = %d, errno == %d\", my_obj->ctrl_fd, l_errno); if((my_obj->ctrl_fd >= 0) || (errno != EIO && errno != ETIMEDOUT) || (n_try <= 0 )) { break; } LOGE(\"Failed with %s error, retrying after %d milli-seconds\",  strerror(errno), sleep_msec); usleep(sleep_msec * 1000U); }while (n_try > 0); if (my_obj->ctrl_fd < 0) { LOGE(\"cannot open control fd of \'%s\' (%s)\\n\",  dev_name, strerror(l_errno)); if (l_errno == EBUSY) rc = -EUSERS; else rc = -1; goto on_error; } else { mm_camera_get_session_id(my_obj, &my_obj->sessionid); LOGH(\"Camera Opened id = %d sessionid = %d\", cam_idx, my_obj->sessionid); } return rc;}

HAL端也就是从这里进入内核,调用驱动来处理的,这里的逻辑是通过do/while循环来处理的,有一个重试机制,重试次数n_try不断的减小,当它等于0时,相机设备还未正常打开,就退出do/while循环了,它的初值为MM_CAMERA_DEV_OPEN_TRIES,该宏定义的值为20, (my_obj->ctrl_fd >= 0)的意思是就camera打开成功,返回的FD有效;(errno != EIO && errno != ETIMEDOUT)的意思是未出现IO或者超时错误;(n_try <= 0 )意思是重试次数已用完,打开成功后,还要进行一些其他初始化的操作。

接下来的驱动层调用不再赘述,以实际平台实现为准。

题外话

这篇文章仅是简单说明Camera 打开设备的一个过程说明,并且给了些加日志调试分析的一些建议,先当个草稿吧,后续有时间了再完善一下,哈哈哈,连个图都没有,直接啃代码~

注:
此文皆为通用谷歌实现方案为参考,由于各个手机厂家都会自定义hal及其驱动实现,需以实际厂家指导操作文档为准。