相机应用处于整个框架的上层,在现实生活中,为了满足各式各样的应用场景,会加入很多业务处理逻辑,但是一旦当我们拨开繁杂的业务逻辑,便会发现其核心部分依然是通过调用谷歌制订的一系列Camera Api接口来完成的,而所有的相机行为都包含在该接口中。
起初,相机系统采用的是Camera Api v1接口,它通过一个Camera 类以及该类中的几个标准方法来实现整个相机系统的预览、拍照以及录像功能,控制逻辑比较简单,同时也比较容易理解,但也正是这种简单,导致了它无法逐帧控制底层硬件,无法通过元数据进行修改进而增强帧的表达能力,再加之应用场景的多样化趋势,该接口在新功能的实现上显得些许力不从心。面对该接口难以进一步扩展相机功能这一局面,谷歌在Andorid 5.0(API Level 21)便重新对Camera进行了设计,摒弃了Camera Api v1的设计逻辑,提出了一个全新的API – camera2,引入了Session以及Request概念,将控制逻辑统一成一个视图,因此在使用上更加复杂,同时也支持了更多特性,比如逐帧控制曝光、感光度以及支持Raw格式的输出等。并且由于对控制逻辑的高度抽象化,使得该接口具有很高的灵活性,可以通过简单的操作实现30fps的全高清连拍的功能,总得来说,该接口极大地提高了对于相机框架的控制能力,同时也进一步大幅度提升了其整体性能。
谷歌提出Camera Api v2接口的同时,将其具体实现放入了Camera Framework中来完成,Framework内部负责解析来自App的请求,并且通过AIDL跨进程接口下发到Camera Service中进行处理,并且等待结果的回传。下面将通过 Camera Api v2打开Camera的流程,来介绍下如何使用该接口来控制整个相机体系。
一、打开Camera主流程
1.1 初始化TextureView并设置监听
1 | mTextureView = findViewById(R.id.textview); |
1.2 SurfaceTexture可用打开Camera
1 | //mStateCallback获取CameraDevice |
1.3 创建Camera Session,请求Camera数据
1 | //CameraDevice创建Session,请求Camera数据 |
1.4 拍照
1 | try { |
二、mCamManager.openCamera
2.1 openCamera
[->frameworks\base\core\java\android\hardware\camera2\CameraManager.java]
1 | @RequiresPermission(android.Manifest.permission.CAMERA) |
2.2 openCameraForUid
[->frameworks\base\core\java\android\hardware\camera2\CameraManager.java]
1 | public void openCameraForUid(@NonNull String cameraId, |
2.3 openCameraDeviceUserAsync
[->frameworks\base\core\java\android\hardware\camera2\CameraManager.java]
1 | private CameraDevice openCameraDeviceUserAsync(String cameraId, |
2.3.1 创建CameraDeviceImpl对象
[->frameworks\base\core\java\android\hardware\camera2\impl\CameraDeviceImpl.java]
1 | public CameraDeviceImpl(String cameraId, StateCallback callback, Executor executor, |
2.3.2 deviceImpl.getCallbacks
获取CameraDeviceCallbacks,用于下面的connectDevice传入,mCallbacks获取设备的状态,并将这些状态转发给StateCallback,从而同步给应用。mCallbacks起到了代理作用,其实现了ICameraDeviceCallbacks.Stub接口
1 | private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks(); |
2.3.3 getCameraService
获取cameraService,通过CameraManager中的CameraManagerGlobal类,再通过ServiceManager获取ICameraService实例,代理CameraService。如何通过CAMERA_SERVICE_BINDER_NAME获取getCameraService代理,将在后面的章节进行详细说明
[->frameworks\base\core\java\android\hardware\camera2\CameraManager.java]
1 | // Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices |
2.3.4 connectDevice
通过CameraService获取ICameraDeviceUser代理类,上面所有的代理实现aidl在源代码frameworks/av/camera/aidl/android/hardware/camera2中可以找到
1 | //frameworks/av/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl |
2.3.5 setRemoteDevice
将获取到的ICameraDeviceUser保存到CameraDeviceImpl用于管理
1 | deviceImpl.setRemoteDevice(cameraUser); |
2.3.6 mDeviceExecutor.execute
通过StateCallback返回给应用CameraDeviceImpl,用于后续的操作
1 | private final Runnable mCallOnOpened = new Runnable() { |
2.4 小结
当应用打开相机应用时,会去调用该方法打开一个相机设备,其中该方法最终经过层层调用会调用到Camera Framework中的openCameraDeviceUserAsync方法,在该方法中主要做了三件事:
- 首先是获取ICameraService代理,调用其getCameraInfo方法获取当前设备的属性。
- 其次是实例化了一个CameraDeviceImpl对象,并将来自App的CameraDevice.StateCallback接口存入该对象中,再将CameraDeviceImpl中的内部类CameraDeviceCallback作为参数通过ICameraService的connectDevice方法传入Camera Service去打开并获取一个ICameraDeviceUser代理,并将该代理存入CameraDeviceImpl中进行管理。
- 最后通过App传入的回调将CameraDeviceImpl返回给App使用,至此整个流程便完成了。
三、mCamDevice.createCaptureSession
3.1 createCaptureSession
1 | public void createCaptureSession(List<Surface> outputs, |
3.2 createCaptureSessionInternal
1 | private void createCaptureSessionInternal(InputConfiguration inputConfig, |
3.2.1 configureStreamsChecked
通过mRemoteDevice配置数据流,mRemoteDevice是在2.3.5中setRemoteDevice初始化,最终调用的是ICameraDeviceUser接口进行deleteStream、createStream等操作。
1 | public boolean configureStreamsChecked(InputConfiguration inputConfig, |
3.2.2 创建CameraCaptureSessionImpl
创建CameraCaptureSessionImpl,并传入CameraCaptureSession.StateCallback用于返回给应用侧
1 | newSession = new CameraCaptureSessionImpl(mNextSessionId++, input, |
将应用侧mStateCallback保存在CameraCaptureSessionImpl,用于结果的回调。配置流成功,返回给应用侧状态mStateCallback.onConfigured
[->frameworks\base\core\java\android\hardware\camera2\impl\CameraCaptureSessionImpl.java]
1 | CameraCaptureSessionImpl(int id, Surface input, |
3.3 小结
在打开相机设备之后便需要去创建一个相机会话,用于传输图像请求,其最终实现是调用该方法来进行实现的,而该方法会去调用到Camera Framework中的createCaptureSessionInternal方法,该方法主要做了两件事:
- 首先调用configureStreamsChecked方法来配置数据流。
- 其次实例化了一个CameraCaptureImpl对象,并通过传入CameraCaptureSession.StateCallback回调类将该对象发送至至App中。
而在configureStreamsChecked方法中会去调用ICameraDeviceUser代理的一系列方法进行数据流配置,其中调用cancelRequest方法停掉当前的预览流程,调用deleteStream方法删除之前的数据流,调用createStream创建新的数据流,最后调用endConfigure来进行数据流的配置工作,针对性的配置便在最后这个endConfigure方法中。
四、mCamDevice.createCaptureRequest
[->frameworks/base/core/java/android/hardware/camera2/CaptureRequest.java ]
1 | CaptureRequest.Builder b = mCamDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); |
请求类型有6种
TEMPLATE_PREVIEW : 创建预览的请求
TEMPLATE_STILL_CAPTURE: 创建一个适合于静态图像捕获的请求,图像质量优先于帧速率
TEMPLATE_RECORD : 创建视频录制的请求
TEMPLATE_VIDEO_SNAPSHOT : 创建视视频录制时截屏的请求
TEMPLATE_ZERO_SHUTTER_LAG : 创建一个适用于零快门延迟的请求。在不影响预览帧率的情况下最大化图像质量
TEMPLATE_MANUAL : 创建一个基本捕获请求,这种请求中所有的自动控制都是禁用的(自动曝光,自动白平衡、自动焦点)
4.1 createCaptureRequest
1 | public CaptureRequest.Builder createCaptureRequest(int templateType, |
4.2 小结
在创建并获取相机会话之后,便可以开始下发图像请求了,而在此之前,需要通过该方法来创建一个CaptureRequest,一旦调用该方法,最终会调用到Camera Service中ICameraDeviceUser的createDefaultRequest方法来创建一个默认配置的CameraMetadataNative,其次实例化一个CaptureRequest.Builder对象,并将刚才获取的CameraMetadataNative传入其中,之后返回该CaptureRequest.Builder对象,在App中,直接通过调用该Buidler对象的build方法,获取一个CaptureRequest对象。
CaptureRequest对象也创建成功了,接下来需要下发图像请求了,一般常用请求分为两种,一个是预览一个是拍照。
五、mCamSession.setRepeatingRequest
5.1 setRepeatingRequest
1 | public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback, |
5.2 addPendingSequence
1 | private int addPendingSequence(int sequenceId) { |
5.2.1 setRepeatingRequest
1 | public int setRepeatingRequest(CaptureRequest request, CaptureCallback callback, |
5.2.2 submitRequestList
最后调用的是ICameraDeviceUser的submitRequestList方法
1 | private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback, |
5.3 小结
应用调用该方法开始预览流程,通过层层调用最终会调用到Framework中的submitCaptureRequest方法,该方法主要做了两件事:
- 首先调用CameraService层CameraDeviceUser的submitRequestList方法,将此次Request下发到CameraService中。
- 其次将App通过参数传入的CameraCaptureSession.CaptureCallback对象存到CameraDeviceImpI对象中。
六、mCamSession.capture
6.1 capture
1 | public int capture(CaptureRequest request, CaptureCallback callback, |
6.2 mDeviceImpl.capture
1 | public int capture(CaptureRequest request, CaptureCallback callback, Executor executor) |
6.3 submitCaptureRequest
该过程和5.3.1类似,最后submitRequestList提交拍照请求
1 | private int submitCaptureRequest(List<CaptureRequest> requestList, CaptureCallback callback, |
6.4 onCaptureProgressed、onCaptureCompleted
一旦Request下发到Camera Service之后,当底层生成了Partial Meta Data数据,Camera Service会调用通过调用在打开相机设备时传入的ICameraDeviceCallback代理,通过其onResultReceived方法将数据传回Framework,之后调用App传入的CameraCaptureSession.CaptureCallback中的onCaputreProgressed、onCaptureCompleted方法将结果回传至App进行解析以及后处理。
1 | public class CameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub { |
6.5 onImageAvailable
[->frameworks/base/media/java/android/media/ImageReader.java]
1 | protected ImageReader(int width, int height, int format, int maxImages, long usage) { |
之前已经通过两个回调接口onCaptureProgressed以及onCaptureCompleted方法将meta data上传到了App,一般情况下,图像数据会在他们之后上传,而且这个上传过程并不经过Camera Framework,而是通过BufferQueue来进行的,当Camera Service接收到底层传来的图像数据,便会立即调用processCaptureResult_3_4方法,该方法中会去调用BufferQueue中生产者角色的Surface的queueBuffer方法,将数据入队并通知消费者去消费,而此时的消费者正是应用侧的ImageReader,并经过一层层回调,最终会通过调用ImageReader的onImageAvailable方法,通知ImageReader去将数据取出,并做后期操作。
6.6 小结
该方法最终也会调用到Framework中的submitCaptureRequest方法,接下来边和预览流程大致相同,会去调用Camera Service 中的ICameraDeviceUser的submitRequestList方法传入请求,之后将App实现的回调对象存入CameraDeviceImpl对象中。
七、总结
基于接口与实现相分离的基本设计原则,谷歌通过Camera Api 接口的定义,搭建起了App与相机系统的桥梁,而具体实现便是由Camera Framework来负责完成的。在采用Camera Api v1接口的时期,该部分是通过JNI层来进行java到C++的转换,进而到达native层,而在native层会通过实现CameraClient建立与Camera Service的通讯 ,整个过程比较繁琐,使得整体框架略显繁杂,而随着Camera Api v2的提出,在该层便大量使用AIDL机制,直接在Java层建立与Camera Service的通信,进一步简化了整体框架。接下来我们以几个主要接口为主线,简单梳理下其具体实现。
CameraManager
实现主要在CameraManager.java中,通过CameraManager查询、获取以及打开一个Camera 设备。在该类中还实现了内部类CameraManagerGlobal,该类继承于ICameraServiceListener.Stub,在打开相机设备的时候,在内部会获取到ICameraService远程代理,并且调用ICameraService的addListener方法将自己注册到Camera Service中,一旦Camera Service状态有所变更便会通过其实现的回调方法通知到Camera Manager服务,另外,该类还通过调用ICameraService.connectDevice()方法获取到Camera Service中的CameraDevice远程代理,并且将该代理传入CameraDeviceImpl中,进而与Camera Service建立了连接。
CameraDeviceImpl
该类定义在CameraDeviceImpl.java文件中,继承并实现了CameraDevice接口,代表了一个相机设备,可以完成CameraCaptureSession的创建以及CaptureRequest创建等工作,内部定义了CameraDeviceCallbacks类(该类继承于ICameraDeviceCallbacks.Stub,对应于Camera Service中的 ICameraDeviceCallbacks接口),用于接收来自Camera Service中的Camera Device的状态回调,并且内部维护着一个Camera Service 的远程ICameraDevice代理,进而可以下发图像请求到Camera Service中。
CameraCaptureSessionImpl
该类定义在CameraCaptureSessionImpl.java文件中,继承并实现了CameraCaptureSession接口,每一个相机设备在一个时间段中,只能创建并存在一个CameraCaptureSession,其中该类包含了两种Session,一种是普通的,适用于一般情况下的会话操作,另一种是用于Reprocess流程的会话操作,该流程主要用于对于现有的图像数据进行再处理的操作。该类维护着来自实例化时传入的Surface列表,这些Surface正是包含了每一个图像请求的数据缓冲区。除了以上这几个接口,还有几个接口是需要App部分进行实现的,用于返回App所需要的对象或者数据:
CameraDevice.StateCallback
被应用侧进行继承并实现,用于在调用CameraManager的openCamera方法时,通过参数的形式传入Framework,在Framework中,一旦CameraDeviceImpl创建成功便通过其中的onOpened方法将其返回给App,如果失败,便会通过其他方法返回给App错误信息。
CameraCaptureSession.StateCallback
被应用侧进行继承并实现,用于在调用CameraDevice的createCaptureSession方法时作为参数传入Framework中,一旦创建成功,Framework便会通过调用该类的onConfigured接口返回一个CameraCaptureSessionImpl的对象,如果失败,Framework会调用其onConfigureFailed方法将错误信息返回至App。
CameraCaptureSession.CaptureCallback
被应用侧进行继承并实现,App通过调用CameraCaptureSessionImpl的setReaptingRequest或者capture方法是作为参数传入Framework,一旦Framework接收到来自CameraService的数据时,便会通过调用这个回调类将数据发送至App中。