稀有猿诉

十年磨一剑,历炼出锋芒,说话千百句,不如码二行。

Camera 2 API学习之小结

通过前面的一系列文章,到现在已经算是学完了Camera 2 API的使用了,也做出一个具体基础功能的相机应用,目前可称得上是一个1.0版本了,后续会在此基础上进行迭代。本篇先进行一个小结。

整体架构

上一篇文章加入了录像模式,让整体复杂度又提升了一个层次,需要做一次业务逻辑抽离。前面一直使用CameraContext作为整体外部调用的接口,一部分业务逻辑,如启动预览,打开相机,关闭相机,和拍照都放在了CameraContext之中。现在因为多了录像模式,且涉及模式切换,如果仍都把逻辑放入到CameraContext之中,会非常的混乱,于是需要引入新的组件:

classDiagram CameraScene <|-- BaseScene BaseScene <|-- PhotoScene BaseScene <|-- VideoScene PhotoOutputConfig --|> OutputConfig VideoOutputConfig --|> OutputConfig PhotoScene *-- PhotoOutputConfig VideoScene *-- VideoOutputConfig BaseScene <-- CameraContext BaseScene *-- CameraAgent CameraContext <-- CameraAgent class CameraScene { <<interface>> attachCameraContext(CameraContext cameraContext) attachSaveAgent(PhotoSaveAgent saveAgent) activate() onResume() onPause() deactivate() clickShutter(Consumer~PhotoCaptureStatus~ consumer) updatePreviewSurface(Surface surface) setFlashMode(FlashMode flashMode) changeOutputConfig(OutputConfig config) switchCamera(Config.CameraFacing facing) addPreviewSizeListener(Consumer~CameraSize~ consumer) addStatusListener(Consumer~SceneStatus~ consumer) getSupportedFlashModes() List~FlashMode~ FlashMode getFlashMode() getSupportedOutputConfigs() List~OutputConfig~ getOutputConfig() OutputConfig } class BaseScene { Context context CameraContext.CameraThreadHandler cameraHandler List~Consumer~CameraSize~~ previewSizeListeners List~Consumer~SceneStatus~~ moduleStatusListeners List~FlashMode~ supportedFlashModes CameraContext cameraContext CameraAgent cameraAgent Surface previewSurface PhotoSaveAgent saveAgent FlashMode flashMode notifyStatus(SceneStatus status) notifyPreviewSize(CameraSize size) generateOutputTargets() List~Surface~ checkCameraContext() onCameraAvailable() } class VideoScene { Surface recorderSurface MediaRecorder mediaRecorder File emptyFile List~VideoOutputConfig~ supportedOutputConfigs VideoOutputConfig currentOutputConfig } class PhotoScene { Optional~OrientationEventListener~ orientationEventListener int deviceOrientation List~PhotoOutputConfig~ supportedOutputConfigs PhotoOutputConfig currentOutputConfig } class OutputConfig { int id String displayName Config.PictureRatio ratio } class PhotoOutputConfig { int width int height int format } class VideoOutputConfig { int width int height int fps } class CameraContext { Context context CameraManagerWrapper cameraManager Map~Config.CameraFacing, CameraAgent~ cameraCache HandlerThread cameraManageThread CameraThreadHandler cameraThreadHandler CameraAgent currentCamera attachThread() detachThread() openCamera() closeCamera() createScene() getCurrentCamera() CameraAgent } class CameraAgent { CameraCharacteristics characteristics Optional~CameraDevice~ cameraDevice Optional~CameraCaptureSession~ captureSession PreviewCaptureCallback previewCallback CaptureRequest.Builder requestBuilder CameraParameters parameters calculateSupportedRatios() List~PhotoOutputConfig~ getFlashModes() List~FlashMode~ calculateVideoRecordMode() List~VideoOutputConfig~ connect() disconnect() startPreview(List~Surface~) stopPreview() capturePhoto() setFlashMode(FlashMode mode) }

CameraScene和BaseScene

一个Scene可以定义为一个典型的用户使用场景,比如拍照场景和录像场景,场景是业务逻辑的主要实现者,它的前面是UI层,后面则是平台API封装层。

VideoScene和PhotoScene

目前为止两个使用场景,PhotoScenne负责拍照,从UI处接收命令,完成拍照;VideoScene则负责录像。

camera2.png

与CameraContext的关系

现在CameraContext则是一个纯的线程管理和camera open/close管理,除此之外的其他逻辑全部都挪到了Scene中去。Scene与Context的关系是组合关系,Scene的所有操作都应该只发生在CameraContext的线程中。

线程模型

三个长驻线程,一个是主线程负责UI交互,一个是CameraContext线程,负责所有的业务逻辑,还有一个是存储,一旦结果出来了后余下的所有事情都在存储线程中。

sequenceDiagram MainThread->>CameraManageThread: startPreview CameraManageThread-->>MainThread: onPreviewSize MainThread->>CameraManageThread: capturePhoto CameraManageThread-->>MainThread: captureStatus CameraManageThread-) ImageSaveThread: onCaptureComplete ImageSaveThread-->>MainThread: onThumbnailArrive

组件通信

通过接口进行隔离,均不产生强依赖关系。因为数据 都不复杂,所以用Consumer就可以了,具体的状态根据不同的场景定义一些enum即可。

camera状态反馈

用于隔离Scene和CameraAgent,反馈camera的连接状态,方便做一些与camera强相关的业务处理,比如一些事情(预览和拍照)只能是在camera处于连接状态才能做的事。

1
2
3
4
cameraContext.openCamera(Config.currentFacing, status -> {
        onCameraAvailable();
        notifyStatus(SceneStatus.ACTIVE);
    });

Scene状态反馈

隔离UI和CameraScene,UI的状态,如一些按扭的可点态和显示与隐藏,以及一些显示内容需要知道CameraScene的状态,当CameraScene处于ACTIVE状态时UI才会完全处于可用态。

1
2
3
4
5
6
7
8
9
10
private Consumer<SceneStatus> moduleStatusListener = status -> mainHandler.post(
        () -> {
            statusView.setText(status.toString());
            if (status == SceneStatus.ACTIVE) {
                onSceneActive();
            } else {
                onSceneInactive();
            }
        }
);

预览尺寸变化 反馈

比较纯,就是用于控制Surface的显示,它需要反馈当前预览的尺寸和比例。

1
2
3
private Consumer<CameraSize> previewSizeListener = size -> mainHandler.post(
        () -> viewFinder.setAspectRatio(size.height, size.width)
);

拍照状态反馈

这个仅在拍照时才需要。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private final Consumer<PhotoCaptureStatus> captureStatusListener = status -> {
    Log.d(LOG_TAG, "capture status " + status);
    statusView.setText("Capture Status: " + status);
    if (status == PhotoCaptureStatus.STARTED) {
        overlayView.setBackground(new ColorDrawable(Color.argb(150, 255, 255, 255)));
        mainHandler.postDelayed(() -> overlayView.setBackground(null), Config.CAPTURE_ANIM_DURATION);
    } else if (status == PhotoCaptureStatus.COMPLETED) {
        shutterView.setClickable(true);
        cameraSwitchView.setClickable(true);
    } else if (status == PhotoCaptureStatus.FAILED) {
        thumbnailSwitcher.setClickable(true);
        shutterView.setClickable(true);
        cameraSwitchView.setClickable(true);
    }
};

后续计划

架构仍需要优化,后面会加入新的功能,未完待续。

Comments