技术经验分享:Android源码笔记——Camera系统架构

简介: 技术经验分享:Android源码笔记——Camera系统架构

Camera的架构与Android系统的整体架构保持一致,如下图所示,本文主要从以下四个方面对其进行说明。


Framework:Camera.java Android Runtime:android_hardware_Camera.cpp Library:Camera Client和Camera Service HAL:CameraHardwareInterface


一、Framework:Camera.java


Camera是应用层软件直接使用的类,涵盖了启动、预览、拍摄及关闭等操作摄像头的全部接口。Camera.java在Android源码中的路径为:framework/base/core/java/android/hardware。为了说明整个Camera系统的架构,这里暂不横向分析Camera.java的功能,下面从open()方法着手:


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()方法需要注意以下几点:


getNumberOfCameras为native方法,实现在android_hardware_Camera.cpp中;


CameraInfo是Camera定义的静态内部类,包含facing、orientation、canDisableShutterSound;


getCameraInfo内部调用native方法_getCameraInfo获取摄像头信息;


open()默认启动的是后置摄像头(CAMERA_FACING_BACK)。


/* used by Camera#open, Camera#open(int) /


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");


}


}


}


Camera构造器的核心实现在cameraInitNormal中,cameraInitNormal调用cameraInitVersion,并传入参数cameraId和CAMERA_HAL_API_VERSION_NORMAL_CONNECT,后者代表HAL的版本。


private int cameraInitVersion(int cameraId, int halVersion) {


……


String packageName = ActivityThread.currentPackageName();


return native_setup(new WeakReference(this), cameraId, halVersion, packageName);


}


cameraInitNormal调用本地方法native_setup(),由此进入到android_hardware_Camera.cpp中,native_setup()的签名如下:


private native final int native_setup(Object camera_this, int cameraId, int halVersion, String packageName);


二、Android Runtime:android_hardware_Camera.cpp


native_setup()被动态注册到JNI,通过JNI调用android_hardware_Camera_native_setup()方法。


static JNINativeMethod camMethods【】 = {


……


{ "native_setup", "(Ljava/lang/Object;ILjava/lang/String;)V",


(void)android_hardware_Camera_native_setup }


……


};


JNI的重点是android_hardware_Camera_native_setup()方法的实现:


// connect to camera service


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;


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;


}


}


if (camera == NULL) {


return -EACCES;


}


// make sure camera hardware is alive


if (camera->getStatus() != NO_ERROR) {


return NO_INIT;


}


jclass clazz = env->GetObjectClass(thiz);


if (clazz == NULL) {


// This should never happen


jniThrowRuntimeException(env, "Can't find android/hardware/Camera");


return INVALID_OPERATION;


}


// We use a weak reference so the Camera object can be garbage collected.


// The reference is only used as a proxy for callbacks.


sp context = new JNICameraContext(env, weak_this, clazz, camera);


context->incStrong((void)android_hardware_Camera_native_setup);


camera->setListener(context);


// save context in opaque field


env->SetLongField(thiz, fields.context, (jlong)context.get());


return NO_ERROR;


}


android_hardware_Camera_native_setup()方法通过调用Camera::connect()方法请求连接CameraService服务。入参中:


clientName是通过将clientPackageName从jstring转换为String16格式得到;


Camera::USE_CALLING_UID是定义在Camera.h中的枚举类型,其值为ICameraService::USE_CALLING_UID(同样为枚举类型,值为-1)。


Camera::connect()位于Camera.cpp中,由此进入到Library层。


三、Library:Camera Client和Camera Service


如上述架构图中所示,ICameraService.h、ICameraClient.h和ICamera.h三个类定义了Camera的接口和架构,ICameraService.cpp和Camera.cpp两个文件用于Camera架构的实现,Camera的具体功能在下层调用硬件相关的接口来实现。Camera.h是Camera系统对上层的接口。


具体的,Camera类继承模板类CameraBase,Camera::connect()调用了CameraBase.cpp中的connect()方法。


sp Camera::connect(int cameraId, const String16& clientPackageName,


int clientUid)


{


return CameraBaseT::connect(cameraId, clientPackageName, clientUid);


}


CameraBase实际上又继承了IBinder的DeathRecipient内部类,DeathRecipient虚拟继承自RefBase。RefBase是Android中的引用计数基础类,其中定义了incStrong、decStrong、incWeak和decWeak等涉及sp/wp的指针操作函数,当然这扯远了。


template


struct CameraTraits {


};


template


class CameraBase : public IBinder::DeathRecipient


{


public:


static sp connect(int cameraId,


const String16& clientPackageName,


int clientUid);


……


}


class DeathRecipient : public virtual RefBase


{


public:


virtual void binderDied(const wp& who) = 0;


};


回到Camera::connect()的实现上,其中,new TCam(cameraId)生成BnCameraClient对象,BnCameraClient定义在ICameraClient.h文件中,继承自模板类BnInterface。getCameraService()方法返回CameraService的服务代理BpCameraService,BpCameraService同样继承自模板类BnInterface。然后通过Binder通信发送CONNECT命令,当BnCameraService收到CONNECT命令后调用CameraService的connect()成员函数来做相应的处理。


template //代码效果参考:http://www.lyjsj.net.cn/wx/art_23056.html


sp CameraBase::connect(int cameraId,


const String16& clientPackageName,


int clientUid)


{


ALOGV("%s: connect", FUNCTION);


sp c = new TCam(cameraId); // BnCameraClient


sp cl = c;


status_t status = NO_ERROR;


const sp& cs = getCameraService(); // BpCameraService


if (cs != 0) {


TCamConnectService fnConnectService = TCamTraits::fnConnectService;


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;


}


class BnCameraClient: public BnInterface


{


public:


virtual status_t onTransact( uint32_t code,


const Parcel& data,


Parcel reply,


uint32_t flags = 0);


};


class BpCameraService: public BpInterface


{


public:


BpCameraService(const sp& impl)


: BpInterface(impl)


{


}


……


}


注:connect()函数在BpCameraService和BnCameraService的父类ICameraService中声明为纯虚函数,在BpCameraService和CameraService中分别给出了实现,BpCameraService作为代理类,提供接口给客户端,真正实现在BnCameraService的子类CameraService中。


在BpCameraService中,connect()函数实现如下:


// connect to camera service (android.hardware.Camera)


virtual status_t connect(const sp& cameraClient, int cameraId,


const String16 &clientPackageName, int clientUid,


/out/


sp& 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); // BpBinder的transact()函数向IPCThreadState实例发送消息,通知其有消息要发送给binder driver


if (readExceptionCode(reply)) return -EPROTO;


status_t status = reply.readInt32();


if (reply.readInt32() != 0) {


device = interface_cast(reply.readStrongBinder()); // client端读出server返回的bind


}


return status;


}


首先将传递过来的Camera对象cameraClient转换成IBinder类型,将调用的参数写到Parcel(可理解为Binder通信的管道)中,通过BpBinder的transact()函数发送消息,然后由BnCameraService去响应该连接,最后就是等待服务端返回,如果成功则生成一个BpCamera实例。


真正的服务端响应实现在BnCameraService的onTransact()函数中,其负责解包收到的Parcel并执行client端的请求的方法。


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 cameraClient =


interface_cast(data.readStrongBinder()); // 使用Camera的Binder对象生成Camera客户代理BpCameraClient实例


int32_t cameraId = data.readInt32();


const String16 clientName = data.readString16();


int32_t clientUid = data.readInt32();


sp camera;


status_t status = connect(cameraClient, cameraId,


clientName, clientUid, /out/camera); // 将生成的BpCameraClient对象作为参数传递到CameraService的connect()函数中


reply->writeNoException();


reply->writeInt32(status); // 将BpCamera对象以IBinder的形式打包到Parcel中返回


if (camera != NULL) {


reply->writeInt32(1);


reply->writeStrongBinder(camera->asBinder());


} else {


reply->writeInt32(0);


}


return NO_ERROR;


} break;


……


}


}


主要的处理包括:


通过data中Camera的Binder对象生成Camera客户代理BpCameraClient实例;


将生成的BpCameraClient对象作为参数传递到CameraService(/frameworks/av/services/camera /libcameraservice/CameraService.cpp)的connect()函数中,该函数会返回一个BpCamera实例;


将在上述实例对象以IBinder的形式打包到Parcel中返回。


最后,BpCamera实例是通过CameraService::connect()函数返回的。CameraService::connect()实现的核心是调用connectHelperLocked()函数根据HAL不同API的版本创建不同的client实例(早期版本中好像没有connectHelperLocked()这个函数,但功能基本相似)。


status_t CameraService::connectHelperLocked(


/out/


sp& client,


/in

相关文章
|
3月前
|
JavaScript 前端开发 Android开发
【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
121 13
【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
|
2月前
|
网络协议 Java 应用服务中间件
框架源码私享笔记(01)Tomcat核心架构功能 | 配置详解
本文首先分享了《活出意义来》一书序言中的感悟,强调成功如同幸福,不是刻意追求就能得到,而是全心投入时的副产品。接着探讨了Tomcat的核心功能与架构解析,包括网络连接器(Connector)和Servlet容器(Container),并介绍了其处理HTTP请求的工作流程。文章还详细解释了Tomcat的server.xml配置文件,涵盖了从顶级容器Server到子组件Connector、Engine、Host、Context等的配置参数及作用,帮助读者理解Tomcat的内部机制和配置方法。
|
1月前
|
NoSQL 应用服务中间件 PHP
布谷一对一直播源码android版环境配置流程及功能明细
部署需基于 CentOS 7.9 系统,硬盘不低于 40G,使用宝塔面板安装环境,包括 PHP 7.3(含 Redis、Fileinfo 扩展)、Nginx、MySQL 5.6、Redis 和最新 Composer。Swoole 扩展需按步骤配置。2021.08.05 后部署需将站点目录设为 public 并用 ThinkPHP 伪静态。开发环境建议 Windows 操作系统与最新 Android Studio,基础配置涉及 APP 名称修改、接口域名更换、包名调整及第三方登录分享(如 QQ、微信)的配置,同时需完成阿里云与腾讯云相关设置。
|
2月前
|
存储 编解码 开发工具
Android平台毫秒级低延迟HTTP-FLV直播播放器技术探究与实现
本文详细探讨了在Android平台上实现HTTP-FLV播放器的过程。首先介绍了FLV格式的基础,包括文件头和标签结构。接着分析了HTTP-FLV传输原理,通过分块传输实现流畅播放。然后重点讲解了播放器的实现步骤,涵盖网络请求、数据解析、音视频解码与渲染,以及播放控制功能的设计。文章还讨论了性能优化和网络异常处理的方法,并总结了HTTP-FLV播放器的技术价值,尤其是在特定场景下的应用意义。
133 11
|
3月前
|
JavaScript 搜索推荐 Android开发
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
97 8
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
|
3月前
|
数据采集 JavaScript Android开发
【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
113 7
【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
|
3月前
|
Android开发 开发者 Kotlin
Android实战经验之Kotlin中快速实现MVI架构
MVI架构通过单向数据流和不可变状态,提供了一种清晰、可预测的状态管理方式。在Kotlin中实现MVI架构,不仅提高了代码的可维护性和可测试性,还能更好地应对复杂的UI交互和状态管理。通过本文的介绍,希望开发者能够掌握MVI架构的核心思想,并在实际项目中灵活应用。
94 8
|
3月前
|
安全 Android开发 iOS开发
escrcpy:【技术党必看】Android开发,Escrcpy 让你无线投屏新体验!图形界面掌控 Android,30-120fps 超流畅!🔥
escrcpy 是一款基于 Scrcpy 的开源项目,使用 Electron 构建,提供图形化界面来显示和控制 Android 设备。它支持 USB 和 Wi-Fi 连接,帧率可达 30-120fps,延迟低至 35-70ms,启动迅速且画质清晰。escrcpy 拥有丰富的功能,包括自动化任务、多设备管理、反向网络共享、批量操作等,无需注册账号或广告干扰。适用于游戏直播、办公协作和教育演示等多种场景,是一款轻量级、高性能的 Android 控制工具。
158 1
|
4月前
|
Java 网络安全 开发工具
Git进阶笔记系列(01)Git核心架构原理 | 常用命令实战集合
通过本文,读者可以深入了解Git的核心概念和实际操作技巧,提升版本管理能力。
|
5月前
|
网络协议 Linux Android开发
深入探索Android系统架构与性能优化
本文旨在为读者提供一个全面的视角,以理解Android系统的架构及其关键组件。我们将探讨Android的发展历程、核心特性以及如何通过有效的策略来提升应用的性能和用户体验。本文不包含常规的技术细节,而是聚焦于系统架构层面的深入分析,以及针对开发者的实际优化建议。
173 21