一、 Camera模块的工作原理和组件 二、 Camera常用技术指标
三、 基于Qualcomm平台的Camera相关代码
一、Camera模块的工作原理和组件 Camera模块是影响捕获的重要电子设备。其工作原理:物体通过镜头(LENS)聚集的光,通过CMOS或者CCD通过内部图像处理器,将光信号转换为电信号(ISP)数字信号处理器转换为数字图像信号输出(DSP)加工,转化为标准RGB、YUV等格式图像信号。 Camera模块包括四件:镜头(LENS)、传感器(Sensor)、芯片的图像处理(ISP)以及软板(FPC),决定一个Camera好坏的重要部分是lens、isp和sensor。 镜头(lens)是相机的灵魂,镜头(lens)它在成像效果中起着非常重要的作用。它利用透镜的折射原理,通过镜头在聚焦平面上形成清晰的图像,并通过感光材料CMOS或CCD传感器记录了景物的图像。 传感器(sensor)是CCM目前广泛使用的核心模块有两种:一种是广泛使用CCD(电荷藕合)元件;另一个是CMOS(补充金属氧化物导体)设备。目前市场上常用的手机是CMOS具体工作原理可查百度。 芯片的图像处理(ISP)是CCM其重要组成部分是及时、快速地将感光芯片获得的数据传输到中央处理器,刷新感光芯片,因此ISP芯片的质量直接影响画面质量(如色彩饱和度、清晰度等)。 FPC柔性电路板(柔性PCB): 简称"软板", 又称"柔性线路板",连接芯片和手机,起到电信号传输的作用。 根据Camera可分为定焦模块和自动变焦模块。
二、 Camera常用技术指标 在我们Tuning Camera一些经常处理的技术指标: 1、 图像分析/分辨率(Resolution): 与分辨率相关的参数在软件代码中很常见 Preview_sizes:Camera 如果该参数与模块的最佳尺寸不匹配,则会影响相机预览界面的清晰度; Picture_sizes: 图片大小,即Camera模组能支持的拍照图片的尺寸大小; Video_sizes:Camera模块可支持录制视频的分辨率; 2、 图像饱和度(Saturation): 饱和度是指色彩的鲜艳程度,也成色彩的纯度。饱和度取决于该色中含色成分和灰阶等级的比例。含色成分越大,饱和度越大;灰阶等级越高,饱和度越小。 3、 图像对比度(Contrast): 对比度参数简单来说就是相机预览时整个屏幕的亮度与全黑状态的亮度相去除。高对比度对图像的清晰度、细节性能和灰度水平性能有很大帮助。一般对比度越大,图像越清晰醒目,颜色越鲜艳。相反,它可能会让整个画感到灰色。 4、 图像锐度/清晰度对(Sharpness): 图像的锐度/清晰度反映了图像平面的清晰度和图像边缘的锐度。在适当的情况下,该指标值可以使图像平面上的细节更加真实和清晰,但过高的锐度会严重扭曲图像。 5、 图像曝光(Exposure): 曝光是控制camera预览和拍照时模块的进光量。例如,在拍摄黑暗图片时,可以增加曝光值,使图片在预览时更加清晰。 6、 图像白平衡(White Balance): 图像白平衡是指通过加强相应的补色来补偿在特定光源下拍摄时的偏色现象。在手机相机中,一般选择自动白平衡(AWB)就足够了。 在Camera Tuning在这个过程中,不同的平台有不同的平台Tuning但归根结底,它是通过调试上述参数来允许的Camera效果最好。
三、 基于Qualcomm平台的Camera相关代码 基于Qualcomm平台Camera硬件模块的主要设计: 1、 Application processor 主要用于装载操作系统和控制Camera时钟、GPIO的设置,Camera Sensor 驱动加载; 2、 Camera Control Interface (CCI):用于Camera子系统的特殊控制接口,如I2C总线、Reset、Standby等GPIO的控制; 3、 Video Front End(VFE4) 用于处理Camera帧数据图像处理硬件模块;平台对Camera像素限制的主要因素。MSM8916平台的VFE可以支持13M的后置Camera和5M的前置Camera。正常模式下VFE4时钟频率为320MHz,Turbo mode时钟频率可达465MHz. 4、 JPEG Encoder/Decoder 专门用于高通平台JPEG Encoding硬件模块,Decoding的LIBGPEG软解码库。 5、 Camera Postprocessor(CPP) 用于对VFE翻转/旋转输出帧数据,去噪,光滑/锐化,修剪和缩放尺寸。在正常模式下VFE4时钟频率为320MHz,Turbo mode时钟频率可达465MHz。 Qualcomm的Camera Code分布情况: Kernel Space: kernel/drivers/media/platform/msm/camera_v2 (我们调试的时候sensor本目录中放置的驱动代码部分是sensor文件夹) kernel/arch/arm/boot/dts(本文件主要是配置Camera i2c地址、各种电源、引脚配置等信息) kernel/arch/arm/mach-msm/clock-xxxx.c(配置Camera的MCLK时序)
Vendor Space: vendor/qcom/proprietary/mm-camera/mm-camera2 HAL Space: hardware/qcom/camera/QCamera2 Framework Space: frameworks/av/camera(代码生成libcamera_client.so) frameworks/av/services/camera/libcameraservice/(代码生成libcameraservice.so) frameworks/base/core/java/android/hardware/Camera.java(提供直接和JNI界面交互的类别中定义) APK Space: packages/apps/Gallery2 Android原生的Camera采用Android标准的C/S架构,Client与Server两个独立的线程之间使用Binder通信,Camera在Android系统启动时,将注册一个CameraService,以提供给Client随时使用。
Camera apk调用的Camera相关的操作API都是由android.hardware.Camera,android.hardware.Camera :frameworks/base/core/java/android/hardware/Camera.java frameworks/base/core/java/android/hardware/Camera.java主要通过调用JNI层的API实现。 frameworks/base/core/jni/android_hardware_Camera.cpp frameworks/av/camera/*:提供此目录下的代码Camera client和和Camera Service 之前基于IPC的Binder实现通信接口。在Camera Client与底层没有实质性的交互。它的主要功能是和Camera Service通信实现操作Camera硬件。 frameworks/av/services/camera/libcameraservice/*:提供此目录下的代码Camera Service的类。在CameraService里可以与Camera HAL层层交互,获得Camera硬件信息。通过与SurfaceFlinger通信将Camera刷数据LCD实现预览。 hardware/qcom/camera/QCamera2:CameraHardwareInterface,在高通实现高通Camera驱动架构相关Google 标准的HAL APIs。 QTI Linux V4L2 camera driver:用来控制Camera硬件,高通专有模块,(vendor/qcom/proprietary/mm-camera/mm-camera2)互动。
Camera HAL层与mm-camera(Qualcomm特殊)通过内存buffer域套接字的共享机制(Domain Socket)通信交互。 通过buffer 信息分类可分为以下几类: CAM_MAPPING_BUF_TYPE_CAPABILITY — Camera capability buffer CAM_MAPPING_BUF_TYPE_PARM_BUF—Camera parameters buffer CAM_MAPPING_BUF_TYPE_STREAM_BUF—Stream buffers CAM_MAPPING_BUF_TYPE_STREAM_INFO—Stream information buffer CAM_MAPPING_BUF_TYPE_OFFLINE_BUF—Offline reprocess input buffer 相关代码路径: Socket implementation –&nbp; QCamera2\stack\mm-camera-interface\src\ mm_camera_sock.c Message transmitter– QCamera2\stack\mm-camera-interface\src\mm_camera_stream.c Message receiver – mm-camera\mm-camera2\server-imaging\server.c
QTI’s camera HAL communicates with kernel through V4L2 IOCTLs on the /dev/videoX nodes 高通Camera HAL与Kernel层通过dev/videox节点用标准的Linux V4L2 协议进行交互: VIDIOC_S_FMT – Used to set preview and snapshot frame format to YUV420 VIDIOC_S_CTRL – Used to pass control information to device VIDIOC_G_CTRL – Used to get control information from device VIDIOC_QBUF – Used to queue/allocate preview buffers VIDIOC_DQBUF – Used to dequeue/deallocate preview buffers VIDIOC_STREAMON – Used to start camera preview VIDIOC_STREAMOFF – Used to stop camera preview VIDIOC_QUERYCAP – Used to query device capability VIDIOC_QUERYBUF – Used to query buffer information VIDIOC_REQBUFS – Used to request buffer registration VIDIOC_DQEVENT – Used to dequeue event from kernel
关于porting一个新的Camera模组到系统里面去,我们需要关心的是以下几点: 1、 新的Sensor驱动代码,比如ov8865.c、sp2529.c等,此文件放入kernel/drivers/media/platform/msm/camera_v2/sensor目录下; 2、 在相关的camera的dtsi文件中,将Camera的一些配置信息添加进去; qcom,camera@20 { compatible = "ovti,ov8865_ofilm"; reg = <0x20>; qcom,slave-id = <0x20 0x300b 0x8865>; qcom,csiphy-sd-index = <0>; qcom,csid-sd-index = <0>; qcom,actuator-src = <&actuator0>; // qcom,eeprom-src = <&eeprom3>; qcom,led-flash-src = <&led_flash0>; qcom,mount-angle = <90>; qcom,sensor-name = "ov8865_q8v18a"; cam_vdig-supply = <&pm8110_l14>; cam_vana-supply = <&pm8110_l19>; cam_vio-supply = <&pm8110_l14>; qcom,cam-vreg-name="cam_vdig", "cam_vio", "cam_vana"; qcom,cam-vreg-type = <0 0 0>;
qcom,cam-vreg-min-voltage=<1800000 1800000 2850000>; qcom,cam-vreg-max-voltage=<1800000 1800000 2850000>; qcom,cam-vreg-op-mode = <200000 200000 80000>; qcom,gpio-no-mux = <0>; gpios = <&msmgpio 13 0>, <&msmgpio 21 0>, <&msmgpio 20 0>, <&msmgpio 7 0>, <&pm8110_gpios 2 0>; qcom,gpio-reset = <1>; qcom,gpio-standby = <2>; qcom,gpio-vio = <3>; qcom,gpio-af-pwdm = <4>; qcom,gpio-req-tbl-num = <0 1 2 3 4>; qcom,gpio-req-tbl-flags = <1 0 0 0 0>;
qcom,gpio-req-tbl-label = "CAMIF_MCLK", "CAM_RESET", "CAM_STANDBY", "CAM_8MDVDD_EN", "CAM_AF_PWDM"; qcom,csi-lane-assign = <0x4320>; qcom,csi-lane-mask = <0x7>; qcom,sensor-position = <0>; qcom,sensor-mode = <0>; qcom,cci-master = <0>; status = "ok"; };
3、 在kernel/arch/arm/mach-msm/clock-xxxx.c中添加Camera的MCLK配置; 如CLK_LOOKUP("cam_src_clk", mclk0_clk_src.c, "6-0020"), CLK_LOOKUP("cam_clk", mclk0_clk.c, "6-0020"), 根据原理图确认模组需要配置哪路MCLK,6-0020:6是i2c适配器编号,0020即模组的i2c地址。 4、 在vendor/qcom/proprietary/mm-camera/mm-camera2目录下添加对应sensor的sensor_libs; 此目录是高通平台Camera部分的代码。而在porting时,Soc 和Rawdata格式的模组有一定的区别。 Soc格式的camera模组只需要在vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/sensor_libs目录添加相应的libs库即可,因为具体的参数调用都在驱动文件里已经完成了。而Rawdata格式的模组不仅要在此目录下添加libs文件,还需要在
vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/chromatix/0301/libchromatix目录下添加各种模式(preview、snapshot、video等)的详细模组参数,因为Rawdata的模组自身不带isp,它传输过来的原始数据需要平台的ISP去处理。 完成上面4个步骤,一个新sensor的模组porting基本上已经完成。在代码编译没问题且驱动配置正常的情况,相机的Camera基本上能正常使用了。 Porting过程中需要特别注意的几点: 1、 Sensor的电源配置,电源配置如有问题,Camera是不能正常工作的; 2、 Sensor的MCLK必须正常,在电源配置OK,MCLK异常同样可以导致Sensor不能正常工作; 3、 根据Sensor IC的Spec确认Sensor的上电时序,此处不正常会导致Sensor在powerup的时候失败,平台与sensor之间的i2c总线不能正常工作; 4、 上电正常后,根据Spec确认Sensor的i2c地址和ID,确保i2c通信正常; 驱动部分上面4步确认OK后,Sensor基本上能正常工作了。 5、确保vendor目录下相应的sensor_libs正常加载了,不然即使驱动工作正常,手机的相机也是不能正常打开的;该文件主要设置了VFE、Sensor的输出格式、LENS等模组和平台Camera相关的配置。(Rawdata格式的还要将libchromatix的参数porting进去,不然Camera也是打不开的)
Camera APP层分析之对camera framework层封装解析 Camera APP层分析之对camera framework层封装解析 Android4.4版本的camera和4.0版本的设计差距还是很大的,4.0版本以前的camera在是camera 主activity中直接调用camera hal层的的接口(如android.hardware.camera.open(), android.hardware.camera.setPreviewDisplay(),android.hardware.camera..startPreview()等)与camera device通信。Android4.4版本camera app对camera device的访问又封装了一次,对camera device的访问必须经过camera manager接口,camera manager接口定义了camera基本操作,这样便于控制和管理camera device。下图为相关类关系图。 1.CameraManager接口:提供的访问camera设备基本的操作,实现类必须调用 CameraManager.cameraOpen获取CameraManager.CameraProxy接口对象来控制camera,实现CameraManager接口的类必须拥有一个独立于主线程的线程来实现对camera的操作,CameraManager接口也包装回调函数 2.CameraProxy接口,封装在CameraManager接口之内,接收对camera操作请求,发送消息到camer handle。所有对camera的操作都经由改接口,进行异步处理 3. AndroidCameraManagerImpl类,该类实现了CameraManager接口,与camera framework层接口直接通信,起到camera app 与camera framework对话中介的作用。
4.AndroidCameraProxyImpl类,AndroidCameraManagerImpl内部类,实现CameraManager.CameraProxy接口,CameraProxy已经介绍过了就是控制对camera的访问,AndroidCameraProxyImpl类实现了具体的操作。 5. CameraManagerFactory类,这个类的实现很简单,看到Factory,会想到软件设计中的工厂模式,这里就是封装了创建CameraManager对象的细节 6. CameraHolder类,看名字就可以看出这个类的基本功能了,就用来保存camera实力对象的,用在不同的module之间快速切换
以上是我们对camera app对camera framework层接口封装了的介绍。下面我们来看camera初始化过程。时序图如下
Step 1. CameraHolder .instance() public static synchronized CameraHolder instance() { if (sHolder == null) { sHolder = new CameraHolder(); } return sHolder; } 这个函数定义在Camera2/src/com/android/cameraCameraHolder.java中 这个函数很简单就是创建一个CameraHolder实例,并保存在变量sHolder中,如果是第一次调用sHolder肯定是null,接着new 一个CameraHolder实例对象,这样的设计在android中经常见到,一种单例设计模式。现在我们来看看CameraHolder都做了些什么 private CameraHolder() {
HandlerThread ht = new HandlerThread("CameraHolder"); ht.start(); mHandler = new MyHandler(ht.getLooper()); if (mMockCameraInfo != null) { mNumberOfCameras = mMockCameraInfo.length; mInfo = mMockCameraInfo; } else { mNumberOfCameras = android.hardware.Camera.getNumberOfCameras(); mInfo = new CameraInfo[mNumberOfCameras]; for (int i = 0; i < mNumberOfCameras; i++) { mInfo[i] = new CameraInfo(); android.hardware.Camera.getCameraInfo(i, mInfo[i]); } }
// get the first (smallest) back and first front camera id for (int i = 0; i < mNumberOfCameras; i++) { if (mBackCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_BACK) { mBackCameraId = i; } else if (mFrontCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_FRONT) { mFrontCameraId = i; } } }
CameraHolder对象,首先创建了一个HandlerThread线程,独立于main thread,启动该线程。接着创建自己MyHandler,用于在HandlerThread中处理自己的事物。最后初始化mInfo变量,保存camerainfo。 Step 2. CameraHolder .open() public synchronized CameraProxy open( Handler handler, int cameraId, CameraManager.CameraOpenErrorCallback cb) {
mCameraDevice.setParameters(mParameters); } mCameraOpened = true; mHandler.removeMessages(RELEASE_CAMERA); mKeepBeforeTime = 0; return mCameraDevice; } 该函数首先判断当前camera是否已经打开,如果已经打开。需要先调用mCameraDevice.release()释放掉,在重新调用CameraManagerFactory获取mCameraDevice实例对象。
Step 3. CameraManagerFactory. getAndroidCameraManager () Public static synchronized CameraManager getAndroidCameraManager() { if (sAndroidCameraManager == null) { sAndroidCameraManager = new AndroidCameraManagerImpl(); } return sAndroidCameraManager; } 该函数定义在Camera2/src/com/android/camera/
CameraManagerFactory.java文件中,代码很简单new AndroidCameraManagerImpl对象返回给调用者。 AndroidCameraManagerImpl() { HandlerThread ht = new HandlerThread("Camera Handler Thread"); ht.start(); mCameraHandler = new CameraHandler(ht.getLooper()); } 从这个类的初始化中可以看到,该对象内部创建了自己的HandlerThread,并启动,这个很重要,后期的client对camera的操作都在这个线程中完成的。后面遇到具体操作我们在分析。
Step 4. AndroidCameraManagerImpl. cameraOpen () public CameraManager.CameraProxy cameraOpen(
Handler handler, int cameraId, CameraOpenErrorCallback callback) { mCameraHandler.obtainMessage(OPEN_CAMERA, cameraId, 0, CameraOpenErrorCallbackForward.getNewInstance( handler, callback)).sendToTarget(); mCameraHandler.waitDone(); if (mCamera != null) { return new AndroidCameraProxyImpl(); } else { return null; } } 这个函数定义在 Camera2/src/com/android/camera/ AndroidCameraManagerImpl.java文件中 函数首先通过mCameraHandler获取OPEN_CAMERA消息并发送给mCameraHandler处理,mCameraHandler处理是在AndroidCameraManagerImpl初始化的时候创建的HandlerThread线程中处理的。发送完消息之后调用mCameraHandler.waitDone()
阻塞当前主线程,等待framework层的camera开启。 Step 5. android.hardware.Camera.open 在上一步发送了OPEN_CAMERA,指令给mCameraHandler。mCameraHandler收到这个指令之后调用framework层camera接口android.hardware.Camera.open开启camera device。当这一步完成之后,主线程会被唤醒,继续执行下一步。 Step 6 AndroidCameraProxyImpl 当主线程再次被唤醒的时候,判断camera device是否成功开启,如果成功开启,将创建AndroidCameraProxyImpl实例,后续对camera所有的操作的都要经过AndroidCameraProxyImpl统一发送到mCameraHandler进行处理。