文章目录

  • 一、ICameraClient接口
  • 1.类关系和一些接口宏说明
  • 2.ICameraClient创建过程发生了什么
  • 二、ICamera接口
  • 三、ICameraService接口
  • 四、总结



备注:本文基于Android 5.1分析,可能已经过时了,不过里面的原理和现在7.0基本一样。博文为个人看代码笔记,如有问题,请发表意见大家一起学习,进步。后续的博文会沿着下面几步来走,把自己对Camera分析总结一下。

  • 1.简单介绍Camera几大接口类,以及相关类的继承关系(也就当前博文)
  • 2.app->jni->native详细分析open camera操作过程中发生了什么
  • 3.startPreview详细过程分析
  • 4.生产者消费者,bufferqueue模型介绍,以及详细介绍preview buffer的生产消费过程
  • 5.takepicture详细过程分析
  • 6.Recording详细过程分析
  • 7.由添加一个CameraMetadata tag引发的实战分析
  • 8.v4l2 架构详细分析

Android Camera中的类很多,刚开始看的时候,感觉各个类之间的关系相当复杂。不过在学习过Binder后,它们的关系就渐渐浮出水面。在开始学习类之间关系时,我们先从整体上了解Camera在工作时,都有哪些对象在作用。

android fopen函数 android fw_android


图一、工作时存在的3个鲜活对象

学过binder之后,我们就会知道服务端和客户端需要实现公共的接口,彼此才能默契的工作,要不然是没法友好沟通的。如上图一我们发现各个代理对象和本地对象都实现了相应的接口,各接口的作用下面做简要介绍。

  • 1.ICameraClient: 这主要是一些消息发送的接口,包括帧可用通知,回调一些信息给client等消息。不过这里要注意的是,BnCameraClient对象其实是在client这端,不在CameraService端。
  • 2.ICamera:camera的一些标准操作接口,比如startpreview,takepicuture,autofocus,所有的操作动作都是用的这一套接口。
  • 3.ICameraService: 链接Camera服务,Camera device,获取Camera数量,Camera硬件信息,视厂角,镜头等信息。

下面分别介绍实现这些接口各个类的关系。

一、ICameraClient接口

1.类关系和一些接口宏说明

继承IcameraClient各类的继承关系如下图所示。

android fopen函数 android fw_android fopen函数_02


图二、ICameraClient相关类继承关系

看过源代码的同学可能比较好奇,为什么camera类没有继承ICamera类呢,但是camera类中也实现了ICamera接口。其实这里Camera类没有直接继承ICamera接口类,而是直接实现了ICamera的接口,在各个接口中调用ICamera代理对象相应的接口就行了。这里只是打了个幌子。大家看看下面的代码就知道了

路径:frameworks/av/include/camera.h
//Camera的继承的类,可以看到模板参数就是Camera类。
class Camera :
    public CameraBase<Camera>,
    public BnCameraClient
{
//......
}

//路径:frameworks/av/include/cameraBase.h
//CameraBase类继承了IBinder::DeathRecipient
template <typename TCam, typename TCamTraits = CameraTraits<TCam> >
class CameraBase : public IBinder::DeathRecipient
{
public: //下面几个类型请看下面类的CameraTraits声明
    typedef typename TCamTraits::TCamListener       TCamListener;
    //由下面的CameraTraits类可以发现TCamUser就是ICamera的强指针类型。TCamCallbacks就是ICameraClient类型,我们这里留个心眼。
    typedef typename TCamTraits::TCamUser           TCamUser;
    typedef typename TCamTraits::TCamCallbacks      TCamCallbacks;
    typedef typename TCamTraits::TCamConnectService TCamConnectService;
//......
    sp<TCamUser>                     mCamera;
    status_t                         mStatus;
    sp<TCamListener>                 mListener;
    const int                        mCameraId;
    typedef CameraBase<TCam>         CameraBaseT;
};

//CameraTraits该类是已结构体的形式展现出来的,不过在C++中的结构体也是类。可以看到模板参数还是Camera类。
struct CameraTraits<Camera>
{ //下面声明的几个宏,会在其他地方用到。
    typedef CameraListener        TCamListener;
    typedef ICamera               TCamUser;
    typedef ICameraClient         TCamCallbacks;
    typedef status_t (ICameraService::*TCamConnectService)(const     sp<ICameraClient>&,                                                           int, const String16&, int,                                        /*out*/ sp<ICamera>&);
    //下面这个静态函数指针,执行的是CameraService类中的Connect函数。
    static TCamConnectService     fnConnectService;
};

上面这些类继承关系,以及模板类的一些信息,如果有些C++基础的同学一看就明白了。如果不明白,最好去看看C++中模板的介绍,再过来看博文会有事半功倍的效果。上面几个类中有几个声明需要我们知道。

  • 1.fnConnectService:指向ICameraService::connect函数指针,这在camera.h文件开始位置已经严重声明了。
CameraTraits<Camera>::TCamConnectService CameraTraits<Camera>::fnConnectService = &ICameraService::connect;
  • 2.几个用的比较多的宏,要暂时记在心里
ICamera = TCamUser,
 ICameraClient = TCamCallbacks,
 typedef CameraBase<TCam>         CameraBaseT;

2.ICameraClient创建过程发生了什么

  • 1.android_hardware_Camera_native_setup()
static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
    jobject weak_this, jint cameraId, jint halVersion, jstring clientPackageName)
{
    // Convert jstring to String16
    const char16_t *rawClientName = env->GetStringChars(clientPackageName, NULL);
    jsize rawClientNameLen = env->GetStringLength(clientPackageName);
    String16 clientName(rawClientName, rawClientNameLen);
    env->ReleaseStringChars(clientPackageName, rawClientName);

    sp<Camera> camera;
    if (halVersion == CAMERA_HAL_API_VERSION_NORMAL_CONNECT) {
        // Default path: hal version is don't care, do normal camera connect.
        //这里调用Camera静态方法connect.请看下面代码
        camera = Camera::connect(cameraId, clientName,
                Camera::USE_CALLING_UID);
    } else {
    //......
}

当我们打开Camera时,第一个调用的就是native_setup(),它会找到包名,CameraID,UID宏与CameraService进行链接获取到camera对象。

  • 2.camera对象创建过程
sp<Camera> Camera::connect(int cameraId, const String16& clientPackageName,
        int clientUid)
{ //下面发现直接调用的是CameraBaseT::connect方法,大家应该还记得,前面说过
 //CameraBaseT就是带了camera模板参数的CameraBase类。请看下面CameraBase类。
    return CameraBaseT::connect(cameraId, clientPackageName, clientUid);
}
//CameraBase类,这里TCam = camera,TCamTraits = CameraTraits<camera>,要记住
template <typename TCam, typename TCamTraits>
sp<TCam> CameraBase<TCam, TCamTraits>::connect(int cameraId,
           const String16& clientPackageName, int clientUid)
{
    ALOGV("%s: connect", __FUNCTION__);
    sp<TCam> c = new TCam(cameraId); //这里直接new Camera()
    sp<TCamCallbacks> cl = c;//这里非常要强调一下,由于camera实现了ICameraClient的接口,所以这里cl,就是callback对象的引用了,实际都是同一个对象。
    status_t status = NO_ERROR;
    const sp<ICameraService>& cs = getCameraService();//这里获取CameraService代理对象,后续在继续分析binder getservice时,会详细分析这一流程。

    if (cs != 0) {//
        TCamConnectService fnConnectService = TCamTraits::fnConnectService;
//这里只需记着fnConnectService = ICameraService::connect,而参数c->mCamera是指向ICamera代理对象的强引用。连接成功后CameraService中会创建一个实现ICamera接口的Camera2Client类对象。这就具备友好通信的前提了。后续都是通过匿名binder实现直接通信的。
        status = (cs.get()->*fnConnectService)(cl, cameraId, clientPackageName, clientUid, /*out*/ c->mCamera);
    }
    if (status == OK && c->mCamera != 0) {
        c->mCamera->asBinder()->linkToDeath(c);
        c->mStatus = NO_ERROR;
    } else {
        ALOGW("An error occurred while connecting to camera: %d", cameraId);
        c.clear();
    }
    return c;
}

这里我们不做深入追究,后续的博文会深入介绍的。这里connect连接成功后,会有下面几个新对象生成。

  • (1).camera,显而易见在进行connect之前,就已经new出来一个camera对象了,然后定义TCamCallbacks强引用指针cl,用于conect时传给CameraService的BnCameraClient本地对象(这里的camera对象就是BnCameraClient本地对象)。注意在CameraService代理对象中,通过writeStrongBinder将该BnCameraClient注册到了当前camera客户端进程的binder_proc的refs_by_node链表中,同时会定义一个binder_ref对象传给CameraService中(这里就是匿名binder通信了,会在后续的binder学习中好好分析)。
  • (2).bnCameraClient 该对象就是上面new出来的camera对象的引用,以匿名binder通信方式,注册给camera client进程中,然后将binder_ref传给mediaServer中的CameraService对象中。
  • (3).Camera2Client,该对象为本地对象会在CameraService中诞生,引用对象会在connect返回时传给camera对象中ICamera强引用对象mCamera中,如下。
status = (cs.get()->*fnConnectService)(cl, cameraId, clientPackageName, clientUid, /*out*/ c->mCamera);//最后一个参数。

到这个时候,Camera运行需要的主要对象,都创建完毕。

二、ICamera接口

android fopen函数 android fw_android fopen函数_03


图三、ICamera相关类继承关系

该类是Camera控制的主要接口类,客户端和Service端都会实现ICamera接口类。客户端进程以匿名binder的形式,与mediaServer进程的CameraService进行通信。下面列举一些我个人认为有必要深入了解的接口。

函数名

功能介绍

virtual status_t setPreviewTarget(const sp& bufferProducer) = 0;

“pass the buffered IGraphicBufferProducer to the camera service”,这个就是设置buffer的地方,参数我们可以看到设下去的是buffer producer对象(这里的生产者-消费者模型我们后面介绍),其实preview申请一个Surface对象后,Sureface就对应一个buffer producer对象,应用是生产者,底层是消费者

virtual status_t startPreview() = 0;

“start preview mode, must call setPreviewTarget first”,官方给的解释是在startpreview时,一定要先设置buffer

virtual status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) = 0;

" send command to camera driver",google介绍简单直接,就是向sensor驱动发送命令,其实这个函数最终的是传给Camera hal 的CameraMetadata,进而间接就影响到了Camera driver

virtual void releaseRecordingFrame(const sp& mem) = 0;

“release a recording frame”,官方解释就是释放录像帧,等后面我们分析了录像,在来这里把这个补充好吧

上面我只列举了部分函数,大家可以结合源码看看其它函数都有什么用,反正都是有很大用处的。

三、ICameraService接口

android fopen函数 android fw_类继承_04


图四、ICameraService相关类继承关系

这里是CameraService的继承关系,右侧是我列举的一些个人认为比较重要的函数。ICameraService中总共有12接口,我们只介绍下面几个。

函数名

函数介绍

virtual int32_t getNumberOfCameras() = 0;

这个在CameraService起来是会调用这个接口,来探测Camera硬件上有几个Camera,进而会不会有前设旋转开关,在每次Open Camera时也会调用一次

virtual status_t getCameraInfo(int cameraId,/out/struct CameraInfo* cameraInfo) = 0;

获取Camera的信息,包括FOV,LENS,等信息

virtual status_t addListener(const sp& listener) = 0;

目前发现没用这个函数,这里就先放这吧

virtual status_t connect(const sp& cameraClient,int cameraId, const String16& clientPackageName,intclientUid, /out/sp& device) = 0;

这个函数是非常重要的,当Open Camera时,客户端会传过来一个bpCameraClientde代理对象。然后CameraService会创建一个实现ICamera接口的Camera2client类对象,紧接着就把这个Camera2Client的代理返回到客户端进程中,用于频繁的操作Camera

四、总结

博文介绍了操作Camera的三大接口类之间的类继承关系和常用的接口分析。还没开始去分析preview,takepicture,recording等流程,后续的博文会一一展开,一起进步。