【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

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

基本知识点

1、c++的输入加上ios::sync_with_stdio(false);  等价于 c的输入,读取速度会加快(但是在字符串的题里面和容易出现问题) 2、lower_bound()和upper_bound() iterator lower_bound( const key_type &key ): 返回一个迭代器,指向键值>= key的第一个元素。 iterator upper_bou

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo