一文看懂 Camera2 framework:以具体 preview 流程为例 独家原创

目录

一、总体流程环节

二、Camera Framework 核心类

三、Camera Metadata 获取、设置及查询

四、预览整体流程

五、深入:关于openCamera


其实很简单,纸老虎,一捅就破。

一、总体流程环节

相机预览时,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);

欢迎交流~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值