Android5.0 Camera Framework 简介

CameraService启动

CameraService是在MediaServer启动过程中进行的

main_mediaserver.cpp (frameworks\av\media\mediaserver)

......
AudioFlinger::instantiate(); //audioflinger服务,音频相关
MediaPlayerService::instantiate(); //mediaplayerservice,媒体播放相关
CameraService::instantiate();//CameraService
AudioPolicyService::instantiate(); //音频相关
......

在 main函数中会执行到CameraService::instantiate(), CameraService 本身并没有实现这个方法

CameraService.cpp (frameworks\av\services\camera\libcameraservice)

CameraService.h (frameworks\av\services\camera\libcameraservice)

BinderService.h (frameworks\native\include\binder)

class CameraService :
public BinderService<CameraService>,
public BnCameraService,
public IBinder::DeathRecipient,
public camera_module_callbacks_t
{
......
}

在其父类中寻找instantiate()函数,BinderService是一个模板类

template<typename SERVICE>
class BinderService
{
public:
static status_t publish(bool allowIsolated = false) { //BinderService::publish
sp<IServiceManager> sm(defaultServiceManager()); //拿到ServiceManager的Bp
return sm->addService(
String16(SERVICE::getServiceName()),
new SERVICE(), allowIsolated); //这里的SERVICE就是CameraService
}
.....
static void instantiate() { publish(); } //BinderService::instantiate
.....
};

这里会new CameraService(),

CameraService::CameraService()
:mSoundRef(0), mModule(0)
{
ALOGI("CameraService started (pid=%d)", getpid());
gCameraService = this; //保存一个本地指针
for (size_t i = 0; i < MAX_CAMERAS; ++i) {
mStatusList[i] = ICameraServiceListener::STATUS_PRESENT;
}
this->camera_device_status_change = android::camera_device_status_change;
}

到这里,CameraService就启动了。

Camera连接过程

Camera.java (frameworks\base\core\java\android\hardware)

Camera.cpp (frameworks\av\camera)

android_hardware_Camera.cpp (frameworks\base\core\jni)

从java->jni->CPP的典型过程

首先从Camera.java入手,这里通过open()方法,创建Camera

    public static Camera open(int cameraId) {
return new Camera(cameraId);
} public static Camera open() {
int numberOfCameras = getNumberOfCameras();
CameraInfo cameraInfo = new CameraInfo();
for (int i = 0; i < numberOfCameras; i++) {
getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
return new Camera(i);
}
}
return null;
}

两个open()方法,默认打开后置摄像头,new Camera()对象,

    Camera(int cameraId) {
int err = cameraInitNormal(cameraId);//做事的主要地方
if (checkInitErrors(err)) {
switch(err) { //通过返回的错误信息,抛不同的异常信息
case EACCESS:
throw new RuntimeException("Fail to connect to camera service");
case ENODEV:
throw new RuntimeException("Camera initialization failed");
default:
// Should never hit this.
throw new RuntimeException("Unknown camera error");
}
}
}

接下来看下代码,最后会落脚到哪一块呢?

    private int cameraInitNormal(int cameraId) {
//这里的CAMERA_HAL_API_VERSION_NORMAL_CONNECT后面会提到用来区别不同的connect
return cameraInitVersion(cameraId, CAMERA_HAL_API_VERSION_NORMAL_CONNECT);
}
......
private int cameraInitVersion(int cameraId, int halVersion) {
mShutterCallback = null;
mRawImageCallback = null;
mJpegCallback = null;
mPreviewCallback = null;
mPostviewCallback = null;
mUsingPreviewAllocation = false;
mZoomListener = null; //初始化几个callback和一些变量 Looper looper; //mEventHandler后面会讲到是对底层上报内容的处理handler
if ((looper = Looper.myLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else if ((looper = Looper.getMainLooper()) != null) {
mEventHandler = new EventHandler(this, looper);
} else {
mEventHandler = null;
} String packageName = ActivityThread.currentPackageName(); return native_setup(new WeakReference<Camera>(this), cameraId, halVersion, packageName);
}

可以看到native_setup是一个native方法,具体实现在

android_hardware_Camera.cpp (frameworks\base\core\jni)

static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
jobject weak_this, jint cameraId, jint halVersion, jstring clientPackageName)
{
......
sp<Camera> camera;
//之前在创建Camera对象的时候设置的一个常量,此时走到connect方法
if (halVersion == CAMERA_HAL_API_VERSION_NORMAL_CONNECT) {
// Default path: hal version is don't care, do normal camera connect.
camera = Camera::connect(cameraId, clientName,
Camera::USE_CALLING_UID);
} else {
jint status = Camera::connectLegacy(cameraId, halVersion, clientName,
Camera::USE_CALLING_UID, camera);
if (status != NO_ERROR) {
return status;
}
}
......
// We use a weak reference so the Camera object can be garbage collected.
// The reference is only used as a proxy for callbacks.
sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera);
context->incStrong((void*)android_hardware_Camera_native_setup);
camera->setListener(context);//listener用于处理底层数据上报 // save context in opaque field
env->SetLongField(thiz, fields.context, (jlong)context.get());
return NO_ERROR;
}

从JNI往下就是CPP,继续探寻Camera连接过程

Camera.cpp (frameworks\av\camera)

CameraBase.cpp (frameworks\av\camera)

sp<Camera> Camera::connect(int cameraId, const String16& clientPackageName,
int clientUid)
{ //CameraBaseT为模板类
return CameraBaseT::connect(cameraId, clientPackageName, clientUid);
}

CameraBase中connect函数模板替换成

sp<Camera> CameraBase<Camera, CameraTraits<Camera>>::connect(int cameraId,
const String16& clientPackageName,
int clientUid)
{
ALOGV("%s: connect", __FUNCTION__);
sp<Camera> c = new Camera(cameraId);
sp<ICameraClient> cl = c;
status_t status = NO_ERROR;
const sp<ICameraService>& cs = getCameraService();//获取CameraService的Bp if (cs != 0) {
//这里TCamConncectService是一个函数指针,指向的是ICameraService中的connect方法
TCamConnectService fnConnectService = TCamTraits::fnConnectService;
status = (cs.get()->*fnConnectService)(cl, cameraId, clientPackageName, clientUid,
/*out*/ c->mCamera);
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;
}

这里可能有点绕,稍微讲解一下,模板上

template

template <typename TCam>
struct CameraTraits {
};
template <typename TCam, typename TCamTraits = CameraTraits<TCam> >

然后我们再到Camera.h中看

template <>

struct CameraTraits

{

typedef CameraListener TCamListener;

typedef ICamera TCamUser;

typedef ICameraClient TCamCallbacks;

typedef status_t (ICameraService::*TCamConnectService)(const sp&,

int, const String16&, int,

/out/

sp&);

static TCamConnectService fnConnectService;

};

中间有些过程,应该好理解了,这里绕了一下,最后落脚到ICameraService.cpp中的connect方法,这一部分涉及到Binder机制比较多,暂时先不讲解具体内容,注意调用的地方即可。

ICameraService.cpp (frameworks\av\camera)

    // connect to camera service (android.hardware.Camera)
virtual status_t connect(const sp<ICameraClient>& cameraClient, int cameraId,
const String16 &clientPackageName, int clientUid,
/*out*/
sp<ICamera>& device)
{
Parcel data, reply;
data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
data.writeStrongBinder(cameraClient->asBinder());
data.writeInt32(cameraId);
data.writeString16(clientPackageName);
data.writeInt32(clientUid);
remote()->transact(BnCameraService::CONNECT, data, &reply);//通过binder远端调用传入的code为BnCameraservice::CONNECT if (readExceptionCode(reply)) return -EPROTO;
status_t status = reply.readInt32();
if (reply.readInt32() != 0) {
device = interface_cast<ICamera>(reply.readStrongBinder());//转换为BpCamera
}
return status;
}

接下来或执行到BnCameraService的onTransact()方法,主要就是switch_case,上面传入的是CONNECT

status_t BnCameraService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
......
case CONNECT: {
CHECK_INTERFACE(ICameraService, data, reply);
sp<ICameraClient> cameraClient =
interface_cast<ICameraClient>(data.readStrongBinder());
int32_t cameraId = data.readInt32();
const String16 clientName = data.readString16();
int32_t clientUid = data.readInt32();
sp<ICamera> camera;
status_t status = connect(cameraClient, cameraId,
clientName, clientUid, /*out*/camera);
//这里BnCameraService并没有实现connect函数,实际实现在CameraService中
reply->writeNoException();
reply->writeInt32(status);
if (camera != NULL) {
reply->writeInt32(1);
reply->writeStrongBinder(camera->asBinder());
} else {
reply->writeInt32(0);
}
return NO_ERROR;
} break;
......
}
}

我们来看一下CameraService中的connect方法到底做了哪些事情

status_t CameraService::connect(
const sp<ICameraClient>& cameraClient,
int cameraId,
const String16& clientPackageName,
int clientUid,
/*out*/
sp<ICamera>& device) { String8 clientName8(clientPackageName);
int callingPid = getCallingPid(); LOG1("CameraService::connect E (pid %d \"%s\", id %d)", callingPid,
clientName8.string(), cameraId);
//对当前连接请求合法性的判断
status_t status = validateConnect(cameraId, /*inout*/clientUid);
if (status != OK) {
return status;
} //Client类继承BnCamera BasicClient
sp<Client> client;
{
Mutex::Autolock lock(mServiceLock);
sp<BasicClient> clientTmp;
//判断当前设备是否被占有或者是重复请求
if (!canConnectUnsafe(cameraId, clientPackageName,
cameraClient->asBinder(),
/*out*/clientTmp)) {
return -EBUSY;//从返回结果可以看出这个函数的用途
} else if (client.get() != NULL) {
device = static_cast<Client*>(clientTmp.get());
return OK;
}
//考虑当前没有其他程序占用摄像头,走到下一步
status = connectHelperLocked(/*out*/client,
cameraClient,
cameraId,
clientPackageName,
clientUid,
callingPid);
if (status != OK) {
return status;
} }
// important: release the mutex here so the client can call back
// into the service from its destructor (can be at the end of the call)
//赋值给device作为传出参数
device = client;
return OK;
}

继续跟踪到connectHelperLocked()函数中

status_t CameraService::connectHelperLocked(
/*out*/
sp<Client>& client,
/*in*/
const sp<ICameraClient>& cameraClient,
int cameraId,
const String16& clientPackageName,
int clientUid,
int callingPid,
int halVersion,
bool legacyMode) { int facing = -1;
int deviceVersion = getDeviceVersion(cameraId, &facing); if (halVersion < 0 || halVersion == deviceVersion) {
// Default path: HAL version is unspecified by caller, create CameraClient
// based on device version reported by the HAL.
switch(deviceVersion) {
case CAMERA_DEVICE_API_VERSION_1_0:
//创建CameraClient对象
client = new CameraClient(this, cameraClient,
clientPackageName, cameraId,
facing, callingPid, clientUid, getpid(), legacyMode);
break;
case CAMERA_DEVICE_API_VERSION_2_0:
case CAMERA_DEVICE_API_VERSION_2_1:
case CAMERA_DEVICE_API_VERSION_3_0:
case CAMERA_DEVICE_API_VERSION_3_1:
case CAMERA_DEVICE_API_VERSION_3_2:
client = new Camera2Client(this, cameraClient,
clientPackageName, cameraId,
facing, callingPid, clientUid, getpid(), legacyMode);
break;
case -1:
ALOGE("Invalid camera id %d", cameraId);
return BAD_VALUE;
default:
ALOGE("Unknown camera device HAL version: %d", deviceVersion);
return INVALID_OPERATION;
}
} else {
// A particular HAL version is requested by caller. Create CameraClient
// based on the requested HAL version.
if (deviceVersion > CAMERA_DEVICE_API_VERSION_1_0 &&
halVersion == CAMERA_DEVICE_API_VERSION_1_0) {
// Only support higher HAL version device opened as HAL1.0 device.
client = new CameraClient(this, cameraClient,
clientPackageName, cameraId,
facing, callingPid, clientUid, getpid(), legacyMode);
} else {
// Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet.
ALOGE("Invalid camera HAL version %x: HAL %x device can only be"
" opened as HAL %x device", halVersion, deviceVersion,
CAMERA_DEVICE_API_VERSION_1_0);
return INVALID_OPERATION;
}
}
//主要是对CameraClient的初始化过程
status_t status = connectFinishUnsafe(client, client->getRemote());
if (status != OK) {
// this is probably not recoverable.. maybe the client can try again
return status;
}
//保存CameraClient对象到本地数组中,以备CameraService使用
mClient[cameraId] = client;
LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId,
getpid()); return OK;
}

这里的client创建成功后会赋值给device,而device就是之前连接过程中的传入参数,到这里Camera的连接过程就基本完成了。

本文主要顺着代码理了一下过程,具体细节地方可能有所忽略。主要的流程图如下,欢迎交流指正。

本文中代码使用的是Android5.1原始代码

版权声明:本文为博主原创文章,未经博主允许不得转载。

<Android Framework 之路>Android5.1 Camera Framework(一)的更多相关文章

  1. <Android Framework 之路>Android5.1 Camera Framework(三)

    上一次讲解了一下startPreview过程,主要是为了画出一条大致的从上到下的线条,今天我们看一下Camera在Framework的sendCommand和dataCallback,这部分属于衔接过 ...

  2. <Android Framework 之路>Android5.1 Camera Framework(四)——框架总结

    前言 从之前的几篇文件,可以基本弄清楚 Camera从APK,经过framework的衔接,与HAL层进行交互,最终通过驱动完成Camera的一些动作. Camera层次分析 APP层 Framewo ...

  3. <Android Framework 之路>Android5.1 Camera Framework(二)

    上一次讲解了一下CameraService的启动过程,今天梳理一下Camera预览的过程 StartPreview过程 首先,我们还是从应用层的使用入手 Camera.java (packages\a ...

  4. <Android Framework 之路>Android5.1 MediaScanner

    前言 MediaScanner是Android系统中针对媒体文件的扫描过程,将储存空间中的媒体文件通过扫描的方式遍历并存储在数据库中,然后通过MediaProvider提供接口使用,在Android多 ...

  5. Android学习之路——简易版微信为例(一)

    这是“Android学习之路”系列文章的开篇,可能会让大家有些失望——这篇文章中我们不介绍简易版微信的实现(不过不是标题党哦,我会在后续博文中一步步实现这个应用程序的).这里主要是和广大园友们聊聊一个 ...

  6. Android高薪之路-Android程序员面试宝典

    Android高薪之路-Android程序员面试宝典

  7. android从应用到驱动之—camera(2)---cameraHAL的实现

    本文是camera系列博客,上一篇是: android从应用到驱动之-camera(1)---程序调用流程 本来想用这一篇博客把cameraHAL的实现和流程都给写完的.搞了半天,东西实在是太多了.这 ...

  8. 小猪的Android入门之路 Day 3 - part 3

    小猪的Android入门之路 Day 3 - part 3 各种UI组件的学习 Part 3 本节引言: 在前面两个部分中我们对Android中一些比較经常使用的基本组件进行了一个了解, part 1 ...

  9. Android 举例说明自己的定义Camera图片和预览,以及前后摄像头切换

    如何调用本地图片,并调用系统拍摄的图像上一博文解释(http://blog.csdn.net/a123demi/article/details/40003695)的功能. 而本博文将通过实例实现自己定 ...

随机推荐

  1. (转)Oracle分区表和索引的创建与管理

    今天用到了Oracle表的分区,就顺便写几个例子把这个表的分区说一说: 一.创建分区表 1.范围分区 根据数据表字段值的范围进行分区 举个例子,根据学生的不同分数对分数表进行分区,创建一个分区表如下: ...

  2. mongodb主从副本集配置

    创建路径: mkdir -p /datassd/mongo_20011/{data,conf,log}     配置文件示例: #mongo.conf dbpath=/datassd/mongo/da ...

  3. js去除html标记

    function ff(str) { var dd = str.replace(/<\/?.+?>/g, ""); var dds = dd.replace(/ /g, ...

  4. sql_1

    order by SELECT Company, OrderNumber FROM Orders ORDER BY Company DESC; SELECT Company, OrderNumber ...

  5. MySQL数据库各个版本的区别

    MySQL数据库各个版本的区别 MySQL数据库 MySQL是一种开放源代码的关系型数据库管理系统(RDBMS),MySQL数据库系统使用最常用的数据库管理语言--结构化查询语言(SQL)进行数据库管 ...

  6. python面向对象的三大特性之一多态

    多态 多态的特性是调用不同的子类将会产生不同的行为,而无需明确知道这个子类实际上是什么 说白了就是,不同的对象调用相同的方法,产生不同的行为 例如:s1是字符串类型,w1是列表,两个完全不同的对象,他 ...

  7. [poj2288] Islands and Bridges (状压dp)

    Description Given a map of islands and bridges that connect these islands, a Hamilton path, as we al ...

  8. Spring框架的理解

    Spring 是一個开源的IOC和AOP容器框架! 具体描述为: 1.轻量级:Spring是非侵入性-基于Spring开发的应用中的对象可以不依赖API开发 2.依赖注入(DI---------dep ...

  9. UID和GID(详细说明)

    一.UID(User Identify)中文用户ID,相当于身份证一样,在系统中是唯一的. 用户分类centos6超级用户 UID=0 root普通用户 UID=500起 oldboy虚拟用户 UID ...

  10. 关于IP数据包首部校验字段的理解

    关于IP数据包首部校验字段的理解 IP数据包格式及首部个字段:  www.2cto.com   对上表的各个数据项就不一一解释了,这里具体关注以下几个数据项: 1.4位首部长度:这里的长度指的是4By ...