目录
其实很简单,纸老虎,一捅就破。
一、总体流程环节
相机预览时,app 应用会调用 framework 的 java 接口,framework 又会调用 native camera service,然后 camera service 再调用 camera hal 的接口,最后对 driver 发指令。层层调用,service 只是中间透传环节,关键在于开始的 framework 流程与最后的 hal 接口实现,那么我们就从头看起,更好理解。
二、Camera Framework 核心类
一般来说,camera service (c++ 代码) 也在 framework 目录下,但 service 是起到中间传递的作用,所以这里主要讲的是 java 类。camera service 后续根据需要发表博客。
Camera Framework 核心类如下:
文件目录及每个类的作用已注明。这里参数的获取与传递依靠 MetaData,关于 MetaData 的定义、内存分布以及在 native 侧的使用方式见本人写的另一篇博客。
Camera MetaData介绍_独家原创_alibli的博客-CSDN博客
在 framework 层分析的好处就是现在可以更加具体到实际的 Metadata。
三、Camera Metadata 获取、设置及查询
CameraCharacteristics 类里定义了大量元数据类似,我们会在 Camera open 之前获取这些元数据,简单点说,获取参数的可设置值或范围。那么常用的 key 有哪些呢?如下:
当然这里只举常用的一类,比如 SENSOR_INFO_PIXEL_ARRAY_SIZE,相关的还有 SENSOR_INFO_ACTIVE_ARRAY_SIZE、SCALER_CROP_REGION 等等。所有的key值见Android开发文档:CameraCharacteristics | Android Developers
核心流程:open camera 之前我们获取到了可以各属性可设置的范围,在有效范围内,创建 session 发 request 就可以传入我们实际要设置的值,设置结果则是查询 CaptureResult 的 key。那么你可能有疑惑,整个流程是啥样的呢?下面来打消你的疑惑。
四、预览整体流程
关键接口入参与返回值罗列:
// 以下接口只列举入参最多的一个
public CameraCharacteristics getCameraCharacteristics(@NonNull String cameraId)
throws CameraAccessException
public void openCamera(@NonNull String cameraId,
int oomScoreOffset,
@NonNull @CallbackExecutor Executor executor,
@NonNull final CameraDevice.StateCallback callback)
throws CameraAccessException
// 旧版已废弃
@Deprecated
public abstract void createCaptureSession(@NonNull List<Surface> outputs,
@NonNull CameraCaptureSession.StateCallback callback,
@Nullable Handler handler)
throws CameraAccessException;
// 新版一个接口一统江湖
public void createCaptureSession(SessionConfiguration config)
throws CameraAccessException
// SessionConfiguration构造函数
public SessionConfiguration(@SessionMode int sessionType,
@NonNull List<OutputConfiguration> outputs,
@NonNull @CallbackExecutor Executor executor,
@NonNull CameraCaptureSession.StateCallback cb)
public CaptureRequest.Builder createCaptureRequest(@RequestTemplate int templateType,
Set<String> physicalCameraIdSet)
throws CameraAccessException
public abstract int setRepeatingRequest(@NonNull CaptureRequest request,
@Nullable CaptureCallback listener,
@Nullable Handler handler)
throws CameraAccessException
应用层调用 getSystemService 即可获取 CameraManager,CameraManger 是 framework 中总体管理者,通过他的 openCamera(...) 接口创建 CameraDevice 示例,因为 openCamera(...) 入参传入了 cameraId 以及 StateCallback 实例。注意,在 openCamera(...) 之前会调用 manager.getCameraCharacteristics(cameraId) 方法,这样就获取了 Camera 设备支持的参数设置范围。
当 StateCallback 的 onOpened(...) 回调被调用时,说明相机开启完成,这时候创建 CameraCaptureSession 实例。然后创建 CaptureRequest 实例,session 调用 setRepeatingRequest(...) 将 CaptureRequest 实例下发到 hal 层。
最后 CaptureCallback 会返回获取到的数据与状态。
预览和拍照整体流程是一致的,如下图:
对于output surface:
- 预览组件:SurfaceView、GLSurfaceView、TextureView
- 拍照组件:ImageReader
- 录像组件:MediaRecorder、MediaCodec
五、深入:关于openCamera
来看 camera framework(java) 与 camera service (c++) 之间的联系。CameraManager 的 openCamera 接口最终调用自己的 openCameraDeviceUserAsync(...) 方法:
/**
* Helper for opening a connection to a camera with the given ID.
*
* @param cameraId The unique identifier of the camera device to open
* @param callback The callback for the camera. Must not be null.
* @param executor The executor to invoke the callback with. Must not be null.
* @param uid The UID of the application actually opening the camera.
* Must be USE_CALLING_UID unless the caller is a service
* that is trusted to open the device on behalf of an
* application and to forward the real UID.
*
* @throws CameraAccessException if the camera is disabled by device policy,
* too many camera devices are already open, or the cameraId does not match
* any currently available camera device.
*
* @throws SecurityException if the application does not have permission to
* access the camera
* @throws IllegalArgumentException if callback or handler is null.
* @return A handle to the newly-created camera device.
*
* @see #getCameraIdList
* @see android.app.admin.DevicePolicyManager#setCameraDisabled
*/
private CameraDevice openCameraDeviceUserAsync(String cameraId,
CameraDevice.StateCallback callback, Executor executor, final int uid,
final int oomScoreOffset) throws CameraAccessException {
CameraCharacteristics characteristics = getCameraCharacteristics(cameraId);
CameraDevice device = null;
Map<String, CameraCharacteristics> physicalIdsToChars =
getPhysicalIdToCharsMap(characteristics);
synchronized (mLock) {
ICameraDeviceUser cameraUser = null;
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
new android.hardware.camera2.impl.CameraDeviceImpl(
cameraId,
callback,
executor,
characteristics,
physicalIdsToChars,
mContext.getApplicationInfo().targetSdkVersion,
mContext);
ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
try {
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);
} catch (ServiceSpecificException e) {
if (e.errorCode == ICameraService.ERROR_DEPRECATED_HAL) {
throw new AssertionError("Should've gone down the shim path");
} else if (e.errorCode == ICameraService.ERROR_CAMERA_IN_USE ||
e.errorCode == ICameraService.ERROR_MAX_CAMERAS_IN_USE ||
e.errorCode == ICameraService.ERROR_DISABLED ||
e.errorCode == ICameraService.ERROR_DISCONNECTED ||
e.errorCode == ICameraService.ERROR_INVALID_OPERATION) {
// Received one of the known connection errors
// The remote camera device cannot be connected to, so
// set the local camera to the startup error state
deviceImpl.setRemoteFailure(e);
if (e.errorCode == ICameraService.ERROR_DISABLED ||
e.errorCode == ICameraService.ERROR_DISCONNECTED ||
e.errorCode == ICameraService.ERROR_CAMERA_IN_USE) {
// Per API docs, these failures call onError and throw
throwAsPublicException(e);
}
} else {
// Unexpected failure - rethrow
throwAsPublicException(e);
}
} catch (RemoteException e) {
// Camera service died - act as if it's a CAMERA_DISCONNECTED case
ServiceSpecificException sse = new ServiceSpecificException(
ICameraService.ERROR_DISCONNECTED,
"Camera service is currently unavailable");
deviceImpl.setRemoteFailure(sse);
throwAsPublicException(sse);
}
// TODO: factor out callback to be non-nested, then move setter to constructor
// For now, calling setRemoteDevice will fire initial
// onOpened/onUnconfigured callbacks.
// This function call may post onDisconnected and throw CAMERA_DISCONNECTED if
// cameraUser dies during setup.
deviceImpl.setRemoteDevice(cameraUser);
device = deviceImpl;
}
return device;
}
来看核心的部分:
ICameraService cameraService = CameraManagerGlobal.get().getCameraService();
这行即获取到 ICameraService 对象,这个类是什么呢?在 camera framework 中以 I 开头的基本都是 aidl 文件,这个文件就在 framework/av/camera/aidl/android/hardware/ICameraService.aidl。aidl 声明了接口,具体实现就对应在 camera service 中,这样就跟 native 层联系起来了。
接下来调用 connetDevice,里面传入了 callbacks,cameraId 等,返回 cameraUser:
cameraUser = cameraService.connectDevice(callbacks, cameraId,
mContext.getOpPackageName(), mContext.getAttributionTag(), uid,
oomScoreOffset, mContext.getApplicationInfo().targetSdkVersion);
cameraUser 又是 ICameraDeviceUser 对象的实例,I 打头又是 aidl 类,对应实现是在 camera service 侧的 BnCameraDeviceUser,这个类最终被 CameraDeviceClient 继承。传入的 callback 则是 ICameraDeviceCallbacks 类型,这个对应 service 侧的 BnCameraDeviceCallbacks,被 ServiceCallback 继承。
deviceImpl.setRemoteDevice(cameraUser);
device = deviceImpl;
deviceImpl 是 CameraDeviceImpl 实例,CameraDeviceImpl 是抽象类 CameraDevice 的具体实现类。setRemoteDevice 传入 cameraUser 会被用于创建 ICameraDeviceUserWrapper 对象,Wrapper 代表是 ICameraDeviceUser 的包装类。这里 mRemoteDevice 即是 framework 与 service 之间沟通的关键桥梁。
// CameraDeviceImpl.java setRemoteDevice(...)方法中
mRemoteDevice = new ICameraDeviceUserWrapper(remoteDevice);
欢迎交流~