【Android Camera1】Camera1初始化销毁流程(二) —— 初始化基本框架和CameraView几种实现方式及其伪代码

本文主要是介绍【Android Camera1】Camera1初始化销毁流程(二) —— 初始化基本框架和CameraView几种实现方式及其伪代码,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

初始化基本框架和CameraView几种实现方式

  • 一、摘要
  • 二、算法思想讲解
    • 2.1 基本框架
      • 2.1.1 CameraView
      • 2.1.2 CameraManager
      • 2.1.3 Camera1Impl
    • 2.2 承载CameraView的消费者。
      • 2.2.1 Android系统 图形架构
        • 2.1.1.1 图像流生产方
        • 2.1.1.2 图像流消耗方
      • 2.2.2 Surface、SurfaceHolder、SurfaceView、TextureView和GLSurfaceView
        • 2.2.2.1 Surface
        • 2.2.2.2 SurfaceHolder
        • 2.2.2.3 SurfaceView
        • 2.2.2.4 TextureView
        • 2.2.2.5 总结
      • 2.2.3 SurfaceView实现方案
      • 2.2.4 TextureView实现方案
      • 2.2.5 GLSurfaceView实现方案

一、摘要

本篇文章阐述如何开发一个健壮的Camera1相机应用。可结合【Camera1】Camera1初始化流程(上) —— 官方Demo初始化流程分析一起参看。

本篇文章只阐述算法和思想。具体的代码开发,可参考如下系列文章

  • Camera1源码分析
  • Camera1官方Demo
  • Camera1开源项目分析

二、算法思想讲解

2.1 基本框架

一个相机应用可以抽象为如下具体的类:

2.1.1 CameraView

承载相机预览画面的CameraView,该CameraView应该支持如下功能:

  1. 初始化SurfaceHolder/ SurfaceTexture
  2. 监听Camera1Impl类状态来更改相关的UI
  3. 权限检查和申请
  4. 初始化和参数设置
  5. 对焦、闪光灯、曝光调节、缩放基本功能UI
  6. 拍照、录制功能
  7. 其他功能UI:分辨率、画幅、场景模式、网格、白平衡、定时拍照、水平检测

初始化功能只涉及到1、2、3点。

2.1.2 CameraManager

Camera管理类

  1. 提供线程调度管理
  2. Camera抽象层

2.1.3 Camera1Impl

Camera1具体实现类、实现对应【2.1.1】里相应的功能

2.2 承载CameraView的消费者。

承载CameraView的消费者可以为如下:

  • SurfaceView
  • TextureView
  • GLSurfaceView

在具体阐述这几个View之前,我们先额外说一下Android系统的图形架构以及这几个View之间的差异点。详细的参考资料如下:

  • Android系统图形架构
  • Surface、SurfaceHolder、SurfaceView、TextureView和GLSurfaceView

2.2.1 Android系统 图形架构

Android 框架提供了各种用于 2D 和 3D 图形渲染的 API,应用开发者可通过三种方式将图像绘制到屏幕上:使用画布、OpenGL ES 或 Vulkan。

无论开发者用什么渲染 API,一切内容都会渲染到 Surface 上。Surface 表示缓冲区队列中的生产方,而缓冲区队列通常会被 SurfaceFlinger 消耗。在 Android 平台上创建的每个窗口都由 Surface 提供支持。所有被渲染的可见 Surface 都被 SurfaceFlinger 合成到屏幕。

下图显示了关键组件如何协同工作
在这里插入图片描述

2.1.1.1 图像流生产方

生成图形缓冲区以供消耗的任何内容。

  • OpenGL ES
  • Canvas 2D
  • mediaserver 视频解码器
2.1.1.2 图像流消耗方

最常见消耗方是 SurfaceFlinger,其他 OpenGL ES 应用也可以消耗图像流如:相机应用会消耗相机预览图像流ImageReader 类

2.2.2 Surface、SurfaceHolder、SurfaceView、TextureView和GLSurfaceView

2.2.2.1 Surface
/*** Handle onto a raw buffer that is being managed by the screen compositor.** <p>A Surface is generally created by or from a consumer of image buffers (such as a* {@link android.graphics.SurfaceTexture}, {@link android.media.MediaRecorder}, or* {@link android.renderscript.Allocation}), and is handed to some kind of producer (such as* {@link android.opengl.EGL14#eglCreateWindowSurface(android.opengl.EGLDisplay,android.opengl.EGLConfig,java.lang.Object,int[],int) OpenGL},* {@link android.media.MediaPlayer#setSurface MediaPlayer}, or* {@link android.hardware.camera2.CameraDevice#createCaptureSession CameraDevice}) to draw* into.</p>** <p><strong>Note:</strong> A Surface acts like a* {@link java.lang.ref.WeakReference weak reference} to the consumer it is associated with. By* itself it will not keep its parent consumer from being reclaimed.</p>*/
  • Surface 是一个接口,供生产方与消耗方交换缓冲区。
  • Consumer
    SurfaceTexture
    MediaRecorder
    Allocation
  • Producer
    android.opengl.EGL14#eglCreateWindowSurface
    android.media.MediaPlayer#setSurface MediaPlayer
    android.hardware.camera2.CameraDevice#createCaptureSession CameraDevice}
2.2.2.2 SurfaceHolder
/*** Abstract interface to someone holding a display surface.  Allows you to* control the surface size and format, edit the pixels in the surface, and* monitor changes to the surface.  This interface is typically available* through the {@link SurfaceView} class.*/

SurfaceHolder 是系统用于与应用共享 Surface 所有权的接口。与 Surface 配合使用的一些客户端需要 SurfaceHolder,因为用于获取和设置 Surface 参数的 API 是通过 SurfaceHolder 实现的。一个 SurfaceView 包含一个 SurfaceHolder

View 交互的大多数组件都涉及到 SurfaceHolder。一些其他 API(如 MediaCodec)将在 Surface 本身上运行。

2.2.2.3 SurfaceView

SurfaceView 是一个组件,可用于在 View 层次结构中嵌入其他合成层。SurfaceView 采用与其他 View 相同的布局参数,因此可以像对待其他任何 View 一样对其进行操作,但 SurfaceView 的内容是透明的。

当使用外部缓冲区来源(例如 GL 上下文和媒体解码器)进行渲染时,您需要从缓冲区来源复制缓冲区,以便在屏幕上显示这些缓冲区。为此,您可以使用 SurfaceView。

当 SurfaceView 的 View 组件即将变得可见时,框架会要求 SurfaceControl 从 SurfaceFlinger 请求新的 surface。如需在创建或销毁 Surface 时收到回调,请使用 SurfaceHolder 接口。默认情况下,新创建的 Surface 放置在应用界面 Surface 的后面。您可以替换默认的 Z 轴顺序,将新的 Surface 放在前面。

在需要渲染到单独的 Surface(例如,使用 Camera API 或 OpenGL ES 上下文进行渲染)时,使用 SurfaceView 进行渲染很有帮助。使用 SurfaceView 进行渲染时,SurfaceFlinger 会直接将缓冲区合成到屏幕上。如果没有 SurfaceView,需要将缓冲区合成到屏幕外的 Surface,然后该 Surface 会合成到屏幕上,而使用 SurfaceView 进行渲染可以省去额外的工作。使用 SurfaceView 进行渲染后,请使用界面线程与 Activity 生命周期相协调,并根据需要调整 View 的大小或位置。然后,硬件混合渲染器会将应用界面与其他层混合在一起。

新的 Surface 是 BufferQueue 的生产方,其使用方是 SurfaceFlinger 层。可以通过任何可向 BufferQueue 馈送资源的机制更新 Surface,例如,使用提供 Surface 的 Canvas 函数、附加 EGLSurface 并使用 GLES 在 Surface 上绘制,或者配置媒体解码器以写入 Surface。

2.2.2.4 TextureView

TextureView 对象会对 SurfaceTexture 进行包装,从而响应回调以及获取新的缓冲区。在 TextureView 获取新的缓冲区时,TextureView 会发出 View 失效请求,并使用最新缓冲区的内容作为数据源进行绘图,根据 View 状态的指示,以相应的方式在相应的位置进行呈现。

OpenGL ES (GLES) 可以将 SurfaceTexture 传递到 EGL 创建调用,从而在 TextureView 上呈现内容,但这样会引发问题。当 GLES 在 TextureView 上呈现内容时,BufferQueue 生产方和使用方位于同一线程中,这可能导致缓冲区交换调用暂停或失败。例如,如果生产方以快速连续的方式从界面线程提交多个缓冲区,则 EGL 缓冲区交换调用需要使一个缓冲区从 BufferQueue 出列。不过,由于使用方和生产方位于同一线程中,因此不存在任何可用的缓冲区,而且交换调用会挂起或失败。

为了确保缓冲区交换不会停止,BufferQueue 始终需要有一个可用的缓冲区能够出列。为了实现这一点,BufferQueue 在新缓冲区加入队列时舍弃之前加入队列的缓冲区的内容,并对最小缓冲区计数和最大缓冲区计数施加限制,以防使用方一次性消耗所有缓冲区。

2.2.2.5 总结

SurfaceView 和 GLSurfaceView。SurfaceView 结合了 Surface 和 View。SurfaceView 的 View 组件由 SurfaceFlinger(而不是应用)合成,从而可以通过单独的线程/进程渲染,并与应用界面渲染隔离。GLSurfaceView 提供了用于管理 EGL 上下文、线程间通信以及与 Activity 生命周期的交互的辅助程序类(但不是必须使用 GLES)。

SurfaceTexture。 SurfaceTexture 将 Surface 和 GLES 纹理相结合来创建 BufferQueue,而应用是 BufferQueue 的消费者。当生产者将新的缓冲区排入队列时,它会通知应用。应用会依次释放先前占用的缓冲区,从队列中获取新缓冲区并执行 EGL 调用,从而使 GLES 可将此缓冲区作为外部纹理使用。

TextureView。 TextureView 结合了 View 和 SurfaceTexture。TextureView 对 SurfaceTexture 进行包装,并负责响应回调以及获取新的缓冲区。在绘图时,TextureView 使用最近收到的缓冲区的内容作为其数据源,根据 View 状态指示,在它应该渲染的任何位置和以它应该采用的任何渲染方式进行渲染。View 合成始终通过 GLES 来执行,这意味着内容更新可能会导致其他 View 元素重绘。

2.2.3 SurfaceView实现方案

注意以下皆为伪代码

CameraSurfaceView.java

A. 新建一个SurfaceView基本实现类

	public class CameraSurfaceView extends SurfaceView {public CameraSurfaceView(Context context) {this(context,null);}public CameraSurfaceView(Context context, AttributeSet attrs) {this(context, attrs,0);}public CameraSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}public Surface getSurface() {return getHolder().getSurface();}public SurfaceHolder getSurfaceHolder() {return getHolder();}public boolean isReady() {return getWidth() != 0 && getHeight() != 0;}
}

B. 更新代码->添加SurfaceCallBack

	public CameraSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {SurfaceHolder holder = getHolder();holder.addCallback(new SurfaceHolder.Callback() {@Overridepublic void surfaceCreated(SurfaceHolder holder) {Log.i(TAG,"surfaceCreated");}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {Log.i(TAG,"surfaceChanged size = "+width+","+height);}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {Log.i(TAG,"surfaceDestroyed");}});}

C. 计算宽高比,传递SurfaceHolder给Camera1Impl.java。

float aspectRatio = 1;@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {Log.i(TAG,"surfaceChanged size = "+width+","+height);aspectRatio = width*1.0f/height;CameraManager.of().initCamera(holder);}

D.记录CameraId

//默认后置
float mCameraId = 1;@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {Log.i(TAG,"surfaceChanged size = "+width+","+height);//...CameraManager.of().createCamera(mCameraId);CameraManager.of().initCamera(holder);}

E.在surfaceDestroyed 添加releaseCamera方法

            @Overridepublic void surfaceDestroyed(SurfaceHolder holder) {Log.i(TAG,"surfaceDestroyed");CameraManager.of().releaseCamera();}

以上为CameraView之SurfaceView实现方式的基本模版代码参考。

2.2.4 TextureView实现方案

基本上和SurfaceView没有太大区别,只需要把SurfaceCallBack修改为如下即可

        setSurfaceTextureListener(new SurfaceTextureListener() {@Overridepublic void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {}@Overridepublic void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}@Overridepublic boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {return false;}@Overridepublic void onSurfaceTextureUpdated(SurfaceTexture surface) {}});

其他基本保持一致。

2.2.5 GLSurfaceView实现方案

其他和【2.2.3】保持一致

public class CameraGLSurfaceView extends GLSurfaceView {public CameraGLSurfaceView(Context context) {super(context);}public CameraGLSurfaceView(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {super.surfaceChanged(holder, format, w, h);}@Overridepublic void surfaceCreated(SurfaceHolder holder) {super.surfaceCreated(holder);}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {super.surfaceDestroyed(holder);}
}

本篇主要介绍了Camera1初始化的基本框架和CameraView几种实现方式,并介绍了SurfaceView、GLSurfaceView、TextureSurface的代码实现以及他们之间的差异点。

后一篇文章将会介绍:
【Camera1】Camera1初始化流程(三) —— 权限申请和线程调度

这篇关于【Android Camera1】Camera1初始化销毁流程(二) —— 初始化基本框架和CameraView几种实现方式及其伪代码的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/341081

相关文章

Redis实现RBAC权限管理

《Redis实现RBAC权限管理》本文主要介绍了Redis实现RBAC权限管理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1. 什么是 RBAC?2. 为什么使用 Redis 实现 RBAC?3. 设计 RBAC 数据结构

Python依赖库的几种离线安装方法总结

《Python依赖库的几种离线安装方法总结》:本文主要介绍如何在Python中使用pip工具进行依赖库的安装和管理,包括如何导出和导入依赖包列表、如何下载和安装单个或多个库包及其依赖,以及如何指定... 目录前言一、如何copy一个python环境二、如何下载一个包及其依赖并安装三、如何导出requirem

SpringBoot基于沙箱环境实现支付宝支付教程

《SpringBoot基于沙箱环境实现支付宝支付教程》本文介绍了如何使用支付宝沙箱环境进行开发测试,包括沙箱环境的介绍、准备步骤、在SpringBoot项目中结合支付宝沙箱进行支付接口的实现与测试... 目录一、支付宝沙箱环境介绍二、沙箱环境准备2.1 注册入驻支付宝开放平台2.2 配置沙箱环境2.3 沙箱

Nginx实现高并发的项目实践

《Nginx实现高并发的项目实践》本文主要介绍了Nginx实现高并发的项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录使用最新稳定版本的Nginx合理配置工作进程(workers)配置工作进程连接数(worker_co

python中列表list切分的实现

《python中列表list切分的实现》列表是Python中最常用的数据结构之一,经常需要对列表进行切分操作,本文主要介绍了python中列表list切分的实现,文中通过示例代码介绍的非常详细,对大家... 目录一、列表切片的基本用法1.1 基本切片操作1.2 切片的负索引1.3 切片的省略二、列表切分的高

基于Python实现一个PDF特殊字体提取工具

《基于Python实现一个PDF特殊字体提取工具》在PDF文档处理场景中,我们常常需要针对特定格式的文本内容进行提取分析,本文介绍的PDF特殊字体提取器是一款基于Python开发的桌面应用程序感兴趣的... 目录一、应用背景与功能概述二、技术架构与核心组件2.1 技术选型2.2 系统架构三、核心功能实现解析

Flutter监听当前页面可见与隐藏状态的代码详解

《Flutter监听当前页面可见与隐藏状态的代码详解》文章介绍了如何在Flutter中使用路由观察者来监听应用进入前台或后台状态以及页面的显示和隐藏,并通过代码示例讲解的非常详细,需要的朋友可以参考下... flutter 可以监听 app 进入前台还是后台状态,也可以监听当http://www.cppcn

Python使用PIL库将PNG图片转换为ICO图标的示例代码

《Python使用PIL库将PNG图片转换为ICO图标的示例代码》在软件开发和网站设计中,ICO图标是一种常用的图像格式,特别适用于应用程序图标、网页收藏夹图标等场景,本文将介绍如何使用Python的... 目录引言准备工作代码解析实践操作结果展示结语引言在软件开发和网站设计中,ICO图标是一种常用的图像

使用Python实现表格字段智能去重

《使用Python实现表格字段智能去重》在数据分析和处理过程中,数据清洗是一个至关重要的步骤,其中字段去重是一个常见且关键的任务,下面我们看看如何使用Python进行表格字段智能去重吧... 目录一、引言二、数据重复问题的常见场景与影响三、python在数据清洗中的优势四、基于Python的表格字段智能去重

java两个List的交集,并集方式

《java两个List的交集,并集方式》文章主要介绍了Java中两个List的交集和并集的处理方法,推荐使用Apache的CollectionUtils工具类,因为它简单且不会改变原有集合,同时,文章... 目录Java两个List的交集,并集方法一方法二方法三总结java两个List的交集,并集方法一