栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【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
 
  • 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
 

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() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
				Log.i(TAG,"surfaceCreated");
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
				Log.i(TAG,"surfaceChanged size = "+width+","+height);
            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
				Log.i(TAG,"surfaceDestroyed");
            }
        });
    }

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

float aspectRatio = 1;

   			@Override
            public 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;

   			@Override
            public 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方法

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

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

2.2.4 TextureView实现方案

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

        setSurfaceTextureListener(new SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
                
            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

            }

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                return false;
            }

            @Override
            public 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);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        super.surfaceChanged(holder, format, w, h);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        super.surfaceCreated(holder);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        super.surfaceDestroyed(holder);
    }
}

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

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/685052.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号