备注:本文基于Android 5.1分析,可能已经过时了,不过里面的原理和现在7.0基本一样。博文为个人看代码笔记,如有问题,请发表意见大家一起学习,进步。后续的博文会沿着下面几步来走,把自己对Camera分析总结一下。
Android Camera中的类很多,刚开始看的时候,感觉各个类之间的关系相当复杂。不过在学习过Binder后,它们的关系就渐渐浮出水面。在开始学习类之间关系时,我们先从整体上了解Camera在工作时,都有哪些对象在作用。
学过binder之后,我们就会知道服务端和客户端需要实现公共的接口,彼此才能默契的工作,要不然是没法友好沟通的。如上图一我们发现各个代理对象和本地对象都实现了相应的接口,各接口的作用下面做简要介绍。
下面分别介绍实现这些接口各个类的关系。
继承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++中模板的介绍,再过来看博文会有事半功倍的效果。上面几个类中有几个声明需要我们知道。
CameraTraits<Camera>::TCamConnectService CameraTraits<Camera>::fnConnectService = &ICameraService::connect;
ICamera = TCamUser,
ICameraClient = TCamCallbacks,
typedef CameraBase<TCam> CameraBaseT;
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对象。
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连接成功后,会有下面几个新对象生成。
status = (cs.get()->*fnConnectService)(cl, cameraId, clientPackageName, clientUid, /*out*/ c->mCamera);//最后一个参数。
到这个时候,Camera运行需要的主要对象,都创建完毕。
该类是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”,官方解释就是释放录像帧,等后面我们分析了录像,在来这里把这个补充好吧 |
上面我只列举了部分函数,大家可以结合源码看看其它函数都有什么用,反正都是有很大用处的。
这里是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等流程,后续的博文会一一展开,一起进步。