论Camera和MediaRecorder的友情(安卓学习年度总结篇)

前言

  2016年即将过去,在这一时刻总觉得应该写点什么有价值的东西来纪念一下。思前想后,觉得脱离M$体系转向Android/Linux的开发有两年时间了,在这一过程中还是有很多收获的:澄清了一些模糊概念、验证并确认了一些个人做法、领会了开源项目中的精妙内容。由于Android体系的庞大代码量,当初为尽快入门采用的是“提纲挈领、重点突破、由线及面”的策略,在了解Android的整体设计哲学思想(详见引用一,别看是2010年的文章但没有之一只有唯一)前提下深入学习其重要且极具代表性的Camera和MediaRecorder子系统,很好地达到了预期目的。

  做为移动设备服务而推出的Android,录像是其主要功能。而录像功能使用了Camera子系统启动摄像头获取原始数据、使用MediaRecorder子系统启动软或硬编码器进行编码。两大子系统涉及的服务CameraService和MediaPlayerService均以mediaserver进程做为运行平台。

  在Camera和MediaRecorder子系统实现中,借鉴了大量的面向对象思想:如设计原则中的接口隔离原则,设计模式中的代理(委托)模式、适配(包装)模式、建造者模式,类关系中的继承、组合、依赖,等等。以下将从Framework层开始(不包括JNI)往下分析录像的实现。


一、Camera子系统分析

  以下是GingerBread(2.3)的Camera子系统框图,之后的版本尽管在具体代码实现上做了很多调整,但是其核心实现思想是大同小异的:即面向接口实现、Binder方式双向访问、HAL隐藏硬件相关信息。
  
论Camera和MediaRecorder的友情(安卓学习年度总结篇)_第1张图片
分析:

  • 连接1(红线)是应用端访问服务端Binder命名对象CameraService,连接2(红线)是应用端访问服务端Binder匿名对象CameraClient,连接3(红线)是服务端访问应用端Binder匿名对象Camera(考虑到版面框图中省略了binder proxy对象的继承关系说明)。
  • CameraHardwareInterface是操作Camera HAL(V1.0)的辅助类,通过CameraClient实现对它的调用。对于HAL V1.0之后的实现会略有不同(详见引用二),但并不影响上层建筑的核心实现。

上面的框架图是基于接口实现分类角度的初步分析,下面将从接口运行载体(应用/服务)角度进一步分析(注:红色双向箭头仅表明app和service间的存在RPC逻辑联系,类依赖关系也仅是表明binder对象间逻辑联系,实际的关联在上面的框图中体现):
论Camera和MediaRecorder的友情(安卓学习年度总结篇)_第2张图片
分析:

  • Camera是整个Camera子系统的核心,起着承上启下的作用,通过继承、依赖、组合关系来使用ICameraService、ICamera、ICameraClient的相关功能。
  • CameraService::Client做为CameraService的内部类,负责调用CameraHardwareInterface来实现硬件操作。此处利用了面向对象的多态性来适配不同版本的HAL:CameraClient(对应操作V1.0版HAL)和Camera2Client(对应操作V2.0、V3.0版HAL)均是CameraService::Client的子类,父类是虚类、实例化的是子类。在上面的框图中由于版面限制仅列举了CameraClient。

  • 参照常规的网络模型进行类比,来理解Binder方式RPC通讯的使用原理:

    • Camera和CameraService是RPC两端的运行平台,ICameraService、ICameraClient、ICamera是平台提供的RPC支持(如Socket API)。
    • 必须遵循一定的规则来使用RPC支持(如对Socket API的正确使用)。Android 的“binder proxy/ binder native”方式定义了使用RPC支持的规则:发起通讯的一方遵循binder proxy使用规则,响应通讯请求的一方遵循binder native使用规则。
  • 关于binder proxy/binder native的使用规则涉及Bp和Bn两个概念,其英文解释是:

    “n” is native, that is the class you inherit from to implement the interface; “p” is proxy, that is the class that is created to perform interface calls through IPC.

      主要意思就是“binder proxy和binder native对象均继承自同一抽象接口,binder native对象在RPC通讯响应端(显式)实例化,以抽象接口的方式反馈给RPC发起端(隐式)创建binder proxy对象实例,但最终是以抽象接口的形式供RPC发起端使用”。在这一点上,和传统的“client/server”相比是有差异的。

  • ICameraService和ICamera在应用端做为binder proxy使用,对应服务端的binder native为CameraService和CameraClient,其作用是提供应用端正向控制服务端的支持;ICameraClient在服务端做为binder proxy使用,对应应用端的Camera,其作用是提供服务端反向通知应用端的支持。

强调一点,上面所有的分析都是基于 Camera API v1进行,Camera API v2与之有较大的不同,在未做深入了解前先放一张从网上找到的框图做简单说明:
论Camera和MediaRecorder的友情(安卓学习年度总结篇)_第3张图片


二、MediaRecorder子系统分析

  MediaRecorder子系统和Camera子系统在结构设计上有异曲同工之妙。只不过因为编码存在软硬件方式,具体实现上是分为官方部分和厂商部分的,以适配不同的编码方案。接下来的分析基于Camera API V1.0进行。
1、官方实现

分析:

  • 图中binder对象及类关系等要素和Camera图各要素定义类似。如果把文字Camera和MediaRecorder理解成同一变量的不同取值,则一个典型的子系统模板跃然纸上。
  • MediaRecorderClient类实际上是StagefrightRecorder类的代理,StagefrightRecorder是平台编码方案的适配,也就是所谓的官方实现和厂商实现的适配层。StagefrightRecorder可根据平台加载指定的软件或硬件编码器(media_codecs.xml表明设备支持哪些编码器),官方的默认实现是软件编码器方式。

2、厂商实现
  从StagefrightRecorder往下的实现由于硬件平台的差异性实现会有所不同,但做为适配器它能很好地向上提供消除差异性后的通用使用方式。而做为官方的“Stagefright+OpenMAX”多媒体框架也能降低由于平台差异性而导致的开发难度。在本文中不再对官方多媒体框架做展开描述。


三、Camera和MediaRecorder的配合

  单独使用MediaRecorder子系统也能实现预览和录像,但更多见到Camera和MediaRecorder配合使用有两个原因:第一,拍照功能仅由Camera提供;第二,原生实现限制多应用对同一摄像头binder native对象的代理访问。

  两大子系统通过访问者模式建立控制和数据传输通道联系,然后分时代理访问(操作流程在Camera.java文件中有很详细的介绍)服务端摄像头binder native对象,以达到预览、录像、拍照目的。在整个操作过程中,Camera(特指具体的类)在中间承担了很重要的责任,看一张省略很多中间环节的简图:
论Camera和MediaRecorder的友情(安卓学习年度总结篇)_第4张图片
分析:

  • 红字(1)(2)是访问者模式中让访问者和被访问者建立关联的必要环节。
  • 建立联系后,MediaRecorder子系统通过Camera(特指具体的类)控制Camera子系统;Camera子系统通过Camera(特指具体的类)传递原始数据给MediaRecorder子系统。

最后以一张两大子系统配合工作(基于kitkat分析)的关键类的类图结束本章:
论Camera和MediaRecorder的友情(安卓学习年度总结篇)_第5张图片
分析:

  • 黑色线表示类关系,红色虚线表示控制流,绿色虚线表示数据流。
  • 红色框内对象实例归属于应用进程,黑色框内对象实例归属于mediaserver进程(分属不同服务);进程的边界通过接口(黑体斜体字表示)标识。
  • VendorRecordr仅代表抽象的厂商实现部分。

四、学习心得总结

  文到此处才真的到了画龙点睛的时候。如前面所言,我学习Android的整体原则是“提纲挈领、重点突破、由线及面“,让这三大原则落地的方法是:

1、先领会(请注意不是“了解”)Android整体设计思想。如果个人领悟力有限完全可以站在别人的肩膀上看风景。如引用一的文章系列,别看是2010年的文章但其深度在我看来没有之一只有唯一,秒杀当前市面上大部分商业书籍的内容,再次证明了“文字贵在精而不在多”这一观点的正确性。

2、然后重点学习系统中具有代表性的子系统或框架,这一类型的实现往往涵盖和体现了系统整体设计的精华。我是从Camera和MediaRecorder两大子系统开始的,为达到掌握细节的目的,我采取的是两个荆棘遍布的非常规步骤:

  • 把Binder机制从框架和驱动相关部分移植到Windows平台运行并调试调用流程。这个过程是极其繁琐和痛苦的,一度有抗拒并放弃的打算,万幸的是坚持了下来。

  • 把两大子系统从上往下的实现剥离出来做为私有子系统用于新增的模拟摄像头和应用开发。由于有第一个步骤的心理准备,所以尽管有难度但心理上已经不抗拒了。

通过这两个实施步骤,我就对Android RPC的核心binder机制原理、以及“应用/服务”的组件思想之妙有了切实的领会,真正做到了“知其然且知其所以然”。

当然,我所采取的方式不一定对所有的人合适,涉及到大量平时躲在角落里的知识点。我之所以采取这种方式,是因为:第一,我之前一直是以Windows平台为主,所以借助自己熟悉的工作环境学习更容易出成果,也能验证其思想的通用性和生命力;第二,我对于平台开发和跨平台移植有多年的经验和心得。但不管采取什么方式,核心观点是“实践是检验真理的唯一标准”。

3、有了前两点的基础,在接下来的工作中根据需要再学习其它的其实就游刃有余了。当然,如果手中有更多的底牌会更有帮助,比如说对于面向对象思想理解(如通过分析类关系在海量代码中快速定位具体实现)、设计模式理解(如快速理解框架整体或局部设计的目的)、操作系统原理及驱动机制理解(如建立更完整的调用层次概念来提升眼界)、跨平台移植了解(如进行平台横向比较分析优劣性来举一反三)等等。


五、引用

1、Android核心分析
http://blog.csdn.net/column/details/androidcore.html

2、Android Camera从Camera HAL1到Camera HAL3的过渡
http://blog.csdn.net/gzzaigcnforever/article/details/48974523

你可能感兴趣的:(linux)