Android 相机预览方向及其适配探索

mtcd1669 8年前
   <h2><strong>1. android相机简介</strong></h2>    <p>由于Android系统的开放策略,Android手机呈现碎片化的趋势,兼容性问题一直是Android App 开发者头疼的难题。本文以Android相机预览方向为例,探索在Android机型适配上的一些思路。</p>    <p>1.1 相机架构</p>    <p>先了解下Camera的框架,它是分层的结构。由上向下分别是</p>    <p>1)应用层</p>    <p>2)Camera系统的Java类</p>    <p>3)Camera的JNI代码</p>    <p>4)Camera的本地框架</p>    <p>5)Camera服务部分</p>    <p>6)Camera HAL(Hardware Abstraction Layer)硬件抽象层</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/f60fd07f548dd700aecc4dbdd7fa8aff.png"></p>    <p>图一Camera architecture</p>    <p>其中Camera HAL主要的接口文件为CameraHardwareInterface.h ,需要各个系统根据自己的情况实现。由于设备底层硬件的千变万化,android框架不可能提供统一的硬件驱动以及接口实现,只能提供标准的接口,因此硬件提供商需要自己开发设备驱动,并去实现android框架提供的接口。</p>    <p>而实际上, 由于机器的硬件配置不同,厂商的底层实现不同,Andriod版本的不同,在一部手机上调试正常的程序,不一定能在其他机型上正常运行 。其中与摄像头相关的适配问题包括摄像头个数、preview size大小、预览方向、闪光灯、对焦方式、帧率等等。</p>    <p>本文以相机预览方向为例,探讨这个问题产生的原因,以及一些可行的解决方法。</p>    <h2><strong>2. 相机预览方向适配问题的产生</strong></h2>    <p>2.1相机的安装方向</p>    <p>相机图像数据都是来自于相机硬件的图像传感器(Image Sensor),这个Sensor被固定到手机之后是有一个默认的取景方向,且不会改变。比如MI3手机的屏幕“自然”方向和后置相机的图像传感器方向是如图二所示的。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/01c917a7cc701028dee6f664481f7a1b.png"></p>    <p>图二MI3手机的屏幕“自然”方向和后置相机的图像传感器方向</p>    <p>在MI3手机上,对于一个横屏应用来说,屏幕“自然”方向和后置相机的图像传感器方向一致,因此看到的图像是正的,如图三所示。而对于一个竖屏应用来说,屏幕“自然”方向和后置相机的图像传感器方向是不一致,从图像传感器的角度看,它看到的图像是侧过来的,如图四所示,需要将相机预览图像顺时针旋转90度,才和屏幕“自然”方向一致。在Android系统中,提供camera.setDisplayOrientation(angle)方法,用来设置相机预览图像顺时针旋转的角度。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/e77aeed76e03570424a81c2af8501029.png"></p>    <p>图三MI3手机横屏的相机应用</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/b1c6756149037307d6afe0e70e8ec8ea.png"></p>    <p>图四MI3手机竖屏的相机应用</p>    <p>2.2相机的安装方向如何获取?</p>    <p>Android官方提供orientation这个属性来读取,官方定义如图五所示:orientation表示相机图像的方向。它的值是相机图像顺时针旋转到设备自然方向一致时的角度。例如假设设备是竖屏的。后置相机传感器是横屏安装的。当你面向屏幕时,如果后置相机传感器顶边的和设备自然方向的右边是平行的,则后置相机的orientation是90。如果前置相机传感器顶边和设备自然方向的右边是平行的,则前置相机的orientation是270。</p>    <pre>  <code class="language-java">int android.hardware.Camera.CameraInfo.orientation  public int orientation</code></pre>    <p>Added in API level 9</p>    <p>The orientation of the camera image. The value is the angle that the camera image needs to be rotated clockwise so it shows correctly on the display in its natural orientation. It should be 0, 90, 180, or 270.</p>    <p>For example, suppose a device has a naturally tall screen. The back-facing camera sensor is mounted in landscape. You are looking at the screen. If the top side of the camera sensor is aligned with the right edge of the screen in natural orientation, the value should be 90. If the top side of a front-facing camera sensor is aligned with the right of the screen, the value should be 270.</p>    <p>图五camerainfo.orientation定义</p>    <p>以MI3为例,通过程序调用取到的后置相机的orientation是90,前置相机的orientation是270。我理解的orientation是相机采集的图像顺时针旋转到屏幕自然方向的角度。图六所示是MI3手机的后置相机和前置相机对准同一个小人,后置前置相机采集到的图像及前置相机预览的图像。 需要特别说明的是,对于前置相机来说,相机预览的图像是相机采集到的图像的镜像, 关注图六中小人头发在前置采集到的图像和预览的图像中头发的不同 。因此在MI3手机上做竖屏应用时,对于后置相机的预览方向,只需要旋转后置相机的orientation,即90度即可和屏幕方向保持一致;对于前置相机的预览方向,由于系统对前置相机采集到的图像做了镜像,因此需要旋转270-180,也是90度即可和屏幕方向保持一致。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/b1c6756149037307d6afe0e70e8ec8ea.png"></p>    <p>图六MI3手机上orientation的例子。</p>    <p>2.3官方推荐的相机预览方向适配做法</p>    <p>通过orientation属性的含义可以知道,我们可以用它和应用的方向来做相机预览方向的适配,图七是官方网站推荐的代码。但并不是所有手机的orientation值都靠谱,比如VIVO V1手机第一次获取后置相机的CameraInfo的orientation值是90,而当执行了mCamera = Camera.open();之后再获取CameraInfo的orientation值就是0,而且以后获取的都是 0 ,除非重启手机。无论是这款手机上的哪个应用,只要执行了一次Camera.open()之后,其他所有程序中获取CameraInfo的orientation都是是0。因此按照此方法做适配不能解决所有手机上的问题。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/8373a80d8b8fcc615730050b519c2ad1.png"></p>    <p>图七 Android官方推荐的相机预览方向的适配做法</p>    <h2><strong>3. 相机预览方向适配</strong></h2>    <p>3.1方案一:App端添加兼容性代码</p>    <p>思路是在App端对特殊的手机添加兼容性代码,如下所示。这样可以解决部分问题,但当版本发出后,又发现问题只能等版本升级修复;同时由于手机型号,系统版本的很多组合,而且客户端要维护一套很长的代码。很自然的想到把相机预览方向的角度做成后台配置,就有了方案二。</p>    <pre>  <code class="language-java">if (is VIVO V1 mobile) {      camera.setDisplayOrientation(90);  } else if (is MotoXPro mobile) {      camera.setDisplayOrientation(270);  } else {      Android官方推荐的相机预览方向适配做法  }</code></pre>    <p>3.2方案二:Server端动态配置</p>    <p>为了解决方案一的弊端,可以将相机预览方向顺时针旋转的角度抽取出来,作为一个变量从server端下发。这样可以做到机型无关,版本发布后,出现问题可以通过Server端上线新的配置来解决。但新机型上问题的发现,确认,解决,上线均需要一定的人力耗费,而且有滞后性。还有没有更好的解决方案呢?</p>    <pre>  <code class="language-java">if (get angle from server) {      camera.setDisplayOrientation(angle);  } else {      Android官方推荐的相机预览方向适配做法  }</code></pre>    <p>3.3方案三:Server端动态配置与用户自助引导相结合</p>    <p>公司一直提倡开发人员有产品思维。从产品的角度说,用户在使用相机应用时,他是知道相机预览方向是正确的还是错误的,我们可以给用户一个旋转相机预览方向的按钮,当用户发现方向不对时,通过自助点击按钮解决问题。如下图所示</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/0c20940ce9b483ffe0d8b52442fd7d1f.png"></p>    <p>图八 发生问题时,引导用户自助旋转相机预览方向</p>    <p>这样即具有方案二的优点,同时又可以实时解决用户的问题,不需要用户等待我们后台下发正确的配置。进一步的,如果发现用户点击了旋转按钮,同时正确使用了相机功能,可以将本机型对应的angle上报给Server端。Server端可以自动或辅助人工以更新配置。对于QQ安全中心APP来说,在人脸验证过程需要打开前置相机,如果用户本次人脸验证点击了旋转预览方向按钮,同时又通过了人脸验证,我们可以认为用户正确使用了相机功能,Server端可以自动更新用户上报的angle,使和该用户同机型的其他用户都受益。</p>    <pre>  <code class="language-java">if (get angle from server) {      camera.setDisplayOrientation(angle);  } else {      Android官方推荐的相机预览方向适配做法  }  ...  if (user click rotate icon) {      angle = angle + 90;      camera.setDisplayOrientation(angle);  }  ...  report(angle);</code></pre>    <h2><strong>4 结语</strong></h2>    <p>在做相机相关的App时,需要考虑的适配问题还有很多,远不止相机预览方向这一种。总的思路就是能实时解决的就不滞后,能动态配置的就不要写死在App端,有时可以换个角度,考虑技术和产品相结合的方案。</p>    <p> </p>    <p>来自:http://blog.csdn.net/tencent_bugly/article/details/53375311</p>    <p> </p>