Android Camera架构简析

Android Camera架构简析

1 整体架构

由上图得知,Android Camera软件上主要分4层:

1.应用层 :APP开发者调用AOSP(Android Open Source Project )提供的接口,这些接口通过Binder与Framework层的相机服务传递控制流数据流

2.Framework:上承APP,下启HAL

  • frameworks/av/camera/Camera.cpp,提供了ICameraService、ICameraDeviceUser、ICameraDeviceCallbacks、ICameraServiceListener等aidl(Android interface definition language)接口的实现。以及camera server的main函数。这些类调用IPC binder来获得camera service。
  • frameworks/av/services/camera/libcameraservice/CameraService.cpp CameraServer承上启下,向上对应用层提供Aosp的接口服务,下和Hal直接交互。(一般而言,CamerServer出现问题的概率极低,大部分还是App层及HAL层出现的问题居多。)

3.HAL硬件抽象层:Android定义好Framework service与HAL层通信的协议和接口,HAL层由各Vender自己实现(QCOM的老架构mm-Camera,新架构Camx;MTK的HAL3架构)。HAL层对上的接口也由Android定义,在这个框架下平台厂商进行适配。HAL主要用于把底层Camera Driver的实现接口进行封装,再经过算法处理,提供接口给framework连接起来

4.Driver驱动层:数据由硬件到驱动层处理。驱动层接收HAL层数据,传递Sensor数据到HAL层,Driver根据Sensor芯片区分不同------Linux内核层

复习一下Android平台架构:

整个Camera架构中,主要包括3个进程------APP进程、CameraServer进程、HAL进程(provider进程),进程间通信都使用Binder实现,

其中APP和CameraServer使用AIDL ,CameraServer和HAL使用HIDL(HAL interface definition language),

下面简单介绍Binder:

2 Binder通信机制

Binder IPC原理:

采用C/S架构。从组件视角看,包含Client、Server、Service、ServiceManager以及binder驱动,其中ServiceManager用于管理系统中的各种服务。

在frameworks/av/camera目录下有三个camera binder类的定义。ICameraService是cameraservice的接口,ICamera是被打开的camera设备的接口,ICameraClient是camera设备返回给application framework层的接口。

3 Camera工作流程

绿:APP

蓝:AOSP提供的API

黄:Native Framework Service

紫:HAL层Service

4 Camera Framework

Camera Framework层即CameraServer服务实现.CameraServer是Native Service,代码在 frameworks/av/services/camera/libcameraservice/ CameraServer承上启下,上对应用提供Aosp的接口服务,下和HAL直接交互

4.1 CameraServer初始化

4.2 APP调用CameraServer过程

详细过程:

openCamera:

configureStream

preview / captureRequest

flush / close

5 Camera HAL3

Android 的相机硬件抽象层 (HAL) 可将 android.hardware.camera2 中较高级别的相机框架 API 连接到底层的相机驱动程序和硬件。Android 8.0 引入了 Treble,用于将 CameraHal API 切换到由 HAL 接口描述语言 (HIDL) 定义的稳定接口。

The API models the camera subsystem as a pipeline that converts incoming requests for frame captures into frames, on a 1:1 basis. The requests encapsulate all configuration information about the capture and processing of a frame. This includes resolution and pixel format; manual sensor, lens and flash control; 3A operating modes; RAW->YUV processing control; statistics generation; and so on.

总结:将camera子系统建立成一种pipeline; 将捕获帧的输入请求1:1转换为帧,即request&result一 一对应

简单来说,framework 从相机子系统请求帧,相机子系统将结果返回到一个 output stream。此外,还会为每组result生成包含color spaces和lens shading等信息的metadata 。它将每个capture request转换为传感器捕获的一个image,该image被处理为:

  • A Result object with metadata about the capture.
  • One to N buffers of image data, each into its own destination Surface.

The set of possible output Surfaces is preconfigured:

  • 每个 Surface 都是固定分辨率的image buffer流的目标。
  • 一次只能将少量Surface配置为输出 (~3)。

请求包含所有所需的捕获设置以及要为此请求将图像缓冲区推送到的输出 Surface 列表(超出配置的总集)。请求可以是capture,也可以是死循环setRepeatingRequest。优先级capture高于setRepeatingRequest

Hal层对底层Camera Driver的实现接口进行封装,向Camera service封装了各种算法接口,供其调用。在hal3中有这么几个接口,ICameraProvider, ICameraDevice, ICameraDeviceSession, ICameraDeviceCallback。

ICameraProvider主要是向上层提供能力值查询,比如属性类的metadata,并且通过它可以获取CameraDevice3lmpl(例如open camera)和cameradevice3sessionimpl实例。

由图可知,request中携带了数据容器Surface,交到framework cameraserver中,打包成Camera3OutputStream实例,在一次CameraCaptureSession中包装成Hal request交给HAL层处理. Hal层获取到处理数据后返回给CameraServer,即CaptureResult通知到Framework,Framework cameraserver则得到HAL层传来的数据给他放进Stream中的容器Surface中.而这些Surface正是来自应用层封装了Surface的控件,这样App就得到了相机子系统传来的数据.

6 openCamera流程:

从Framework层往下深入探索源码:

6.1 CameraManager

CameraManager是本地的systemService集合中的一个service,在SystemServiceRegistry中注册:

typescript 复制代码
registerService(Context.CAMERA_SERVICE, CameraManager.class,

        new CachedServiceFetcher<CameraManager>() {

    @Override

    public CameraManager createService(ContextImpl ctx) {

        return new CameraManager(ctx);

    }});
  • SystemServiceRegistry中有两个HashMap集合来存储本地的SystemService数据,有一点要注意点一些,这和Binder的service不同,他们不是binder service,只是普通的调用模块,集成到一个本地service中,便于管理。
swift 复制代码
// Service registry information.

// This information is never changed once static initialization has completed.

private static final Map<Class<?>, String> SYSTEM_SERVICE_NAMES =

        new ArrayMap<Class<?>, String>();

private static final Map<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =

        new ArrayMap<String, ServiceFetcher<?>>();

6.2 openCamera

  • CameraManager中两个openCamera(...),只是一个传入Handler,一个传入Executor(操作线程池),是想用线程池来执行Camera中耗时操作。

    • cameraId 是一个标识,标识当前要打开的camera
    • callback 是一个状态回调,当前camera被打开的时候,这个状态回调会被触发的。
    • handler 是传入的一个执行耗时操作的handler
    • executor 操作线程池
less 复制代码
public void openCamera(@NonNull String cameraId,

        @NonNull final CameraDevice.StateCallback callback, @Nullable Handler handler)

        throws CameraAccessException 

        

public void openCamera(@NonNull String cameraId,

        @NonNull @CallbackExecutor Executor executor,

        @NonNull final CameraDevice.StateCallback callback)

        throws CameraAccessException {

    if (executor == null) {

        throw new IllegalArgumentException("executor was null");

    }

    openCameraForUid(cameraId, callback, executor, USE_CALLING_UID);//USE_CALLING_UID默认为-1

}

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

6.3 ConfigStream

  • openCamera成功之后就会执行CameraDeviceImpl-->createCaptureSession,执行成功的回调CameraDevice.StateCallback的onOpened(CameraDevice cameraDevice)方法,当前这个CameraDevice参数就是当前已经打开的相机设备。
  • 获取了相机设备,接下来要创建捕捉会话,会话建立成功,可以在当前会话的基础上设置相机预览界面,这时候我们调整摄像头,就能看到屏幕上渲染的相机输入流了,接下来我们可以操作拍照片、拍视频等操作。

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

frameworks/av/services/camera/libcameraservice/device3/Camera3Device.cpp

  • CameraDeviceImpl->createCaptureSession

frameworks/base/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java

csharp 复制代码
public void createCaptureSession(List<Surface> outputs,

        CameraCaptureSession.StateCallback callback, Handler handler)

        throws CameraAccessException {

    List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size());

    for (Surface surface : outputs) {

        outConfigurations.add(new OutputConfiguration(surface));

    }

    createCaptureSessionInternal(null, outConfigurations, callback,

            checkAndWrapHandler(handler), /*operatingMode*/ICameraDeviceUser.NORMAL_MODE,

            /*sessionParams*/ null);

}
  • 将Surface转化为OutputConfiguration,OutputConfiguration是一个描述camera输出数据的类,其中包括Surface和捕获camera会话的特定设置。从其类的定义来看,它是一个实现Parcelable的类,说明其必定要跨进程传输。

  • CameraDeviceImpl->createCaptureSession传入的Surface列表:

    • 这儿的一个Surface表示输出流,Surface表示有多个输出流,我们有几个显示载体,就需要几个输出流。
    • 对于拍照而言,有两个输出流:一个用于预览、一个用于拍照。
    • 对于录制视频而言,有两个输出流:一个用于预览、一个用于录制视频。
  • CameraDeviceImpl->createCaptureSessionInternal
java 复制代码
private void createCaptureSessionInternal(InputConfiguration inputConfig,

        List<OutputConfiguration> outputConfigurations,

        CameraCaptureSession.StateCallback callback, Executor executor,

        int operatingMode, CaptureRequest sessionParams)
  • 传入的几个参数中,inputConfig为null,我们只需关注outputConfigurations即可。

createCaptureSessionInternal函数中代码很多,但是重要的就是执行配置Surface

ini 复制代码
                configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations,

                        operatingMode, sessionParams);

                if (configureSuccess == true && inputConfig != null) {

                    input = mRemoteDevice.getInputSurface();

                }
  • 如果配置surface成功,返回一个Input surface这个input surface是用户本地设置的一个输入流。接下来这个input对象会在构造CameraCaptureSessionImpl对象时被传入。 具体参考CameraCaptureSessionImpl构造函数
  • CameraDeviceImpl->configureStreamsChecked

下面这张图详细列出配置输入输出流函数中执行的主要步骤,由于当前的inputConfig为null,所以核心的执行就是下面粉红色框中的过程------创建输出流

  • mRemoteDevice.beginConfigure();mRemoteDevice.endConfigure(operatingMode, null);中间的过程是IPC通知service端告知当前正在处理输入输出流。执行完mRemoteDevice.endConfigure(operatingMode, null);返回success = true;如果中间被终端了,那么success肯定不为true。
  • 根据配置stream结果来创建CameraCaptureSession
ini 复制代码
        try {

            // configure streams and then block until IDLE

            configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations,

                    operatingMode, sessionParams);

            if (configureSuccess == true && inputConfig != null) {

                input = mRemoteDevice.getInputSurface();

            }

        } catch (CameraAccessException e) {

            configureSuccess = false;

            pendingException = e;

            input = null;

            if (DEBUG) {

                Log.v(TAG, "createCaptureSession - failed with exception ", e);

            }

        }
  • 配置流完成之后,返回configureSuccess表示当前配置是否成功。

frameworks/base/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java

  • 然后创建CameraCaptureSessionImpl的时候要用到:
ini 复制代码
        CameraCaptureSessionCore newSession = null;

        if (isConstrainedHighSpeed) {

            ArrayList<Surface> surfaces = new ArrayList<>(outputConfigurations.size());

            for (OutputConfiguration outConfig : outputConfigurations) {

                surfaces.add(outConfig.getSurface());

            }

            StreamConfigurationMap config =

                getCharacteristics().get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

            SurfaceUtils.checkConstrainedHighSpeedSurfaces(surfaces, /*fpsRange*/null, config);



            newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++,

                    callback, executor, this, mDeviceExecutor, configureSuccess,

                    mCharacteristics);

        } else {

            newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,

                    callback, executor, this, mDeviceExecutor, configureSuccess);

        }



        // TODO: wait until current session closes, then create the new session

        mCurrentSession = newSession;



        if (pendingException != null) {

            throw pendingException;

        }



        mSessionStateCallback = mCurrentSession.getDeviceStateCallback();
  • CameraCaptureSessionImpl构造函数:
ini 复制代码
CameraCaptureSessionImpl(int id, Surface input,

        CameraCaptureSession.StateCallback callback, Executor stateExecutor,

        android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,

        Executor deviceStateExecutor, boolean configureSuccess) {

    if (callback == null) {

        throw new IllegalArgumentException("callback must not be null");

    }



    mId = id;

    mIdString = String.format("Session %d: ", mId);



    mInput = input;

    mStateExecutor = checkNotNull(stateExecutor, "stateExecutor must not be null");

    mStateCallback = createUserStateCallbackProxy(mStateExecutor, callback);



    mDeviceExecutor = checkNotNull(deviceStateExecutor,

            "deviceStateExecutor must not be null");

    mDeviceImpl = checkNotNull(deviceImpl, "deviceImpl must not be null");

    mSequenceDrainer = new TaskDrainer<>(mDeviceExecutor, new SequenceDrainListener(),

            /*name*/"seq");

    mIdleDrainer = new TaskSingleDrainer(mDeviceExecutor, new IdleDrainListener(),

            /*name*/"idle");

    mAbortDrainer = new TaskSingleDrainer(mDeviceExecutor, new AbortDrainListener(),

            /*name*/"abort");

    if (configureSuccess) {

        mStateCallback.onConfigured(this);

        if (DEBUG) Log.v(TAG, mIdString + "Created session successfully");

        mConfigureSuccess = true;

    } else {

        mStateCallback.onConfigureFailed(this);

        mClosed = true; // do not fire any other callbacks, do not allow any other work

        Log.e(TAG, mIdString + "Failed to create capture session; configuration failed");

        mConfigureSuccess = false;

    }

}

小结一般session创建成功,那么我们的预览就会正常,session创建失败,则预览一定黑屏,

session创建完成后,framework会通过CameraCaptureSession.StateCallback类的

public abstract void onConfigured(@NonNull CameraCaptureSession session)

回调到应用层,通知我们session创建成功了,那么我们就可以使用回调方法中的CameraCaptureSession参数,调用它的setRepeatingRequest方法来下预览了,该逻辑执行完成后,相机的预览就起来了。

相关推荐
网络研究院2 小时前
Android 安卓内存安全漏洞数量大幅下降的原因
android·安全·编程·安卓·内存·漏洞·技术
凉亭下2 小时前
android navigation 用法详细使用
android
小比卡丘5 小时前
C语言进阶版第17课—自定义类型:联合和枚举
android·java·c语言
前行的小黑炭5 小时前
一篇搞定Android 实现扫码支付:如何对接海外的第三方支付;项目中的真实经验分享;如何高效对接,高效开发
android
落落落sss7 小时前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
代码敲上天.7 小时前
数据库语句优化
android·数据库·adb
GEEKVIP10 小时前
手机使用技巧:8 个 Android 锁屏移除工具 [解锁 Android]
android·macos·ios·智能手机·电脑·手机·iphone
model200511 小时前
android + tflite 分类APP开发-2
android·分类·tflite
彭于晏68912 小时前
Android广播
android·java·开发语言
与衫13 小时前
掌握嵌套子查询:复杂 SQL 中 * 列的准确表列关系
android·javascript·sql