Android SurfaceTexture说明

2023-10-23 08:40

本文主要是介绍Android SurfaceTexture说明,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

what

1. SurfaceTexture用来捕获视频流中的图像帧,视频流可以是相机预览或者是视频解码数据。

里边承接图像数据的也是GraphicBuffer, GLConsumer 作为其BufferQueue的消费方,取得数据后可以通过eglImage挂到opengles texture 里边对其做纹理采样。

how

1. 就用法来讲,主要就是有数据后BufferQueue Producer在queueBuffer时 回调应用的onFrameAvailable(),应用自己决定是否、何时调用updateTetxtureImage()去做纹理绑定、采样

2. BufferQueue:

native层,在SurfaceTexture native对象 创建前创建了生产者和消费者;

消费者作为其构造函数传入给SurfaceTexture;

生产者作为全局变量保存,同时也保存在SurfaceTexture java 对象的mProducer里边 ;

3.GraphicBuffer的获取

生产方:通过 SurfaceTexture_getProducer()获取到BufferQueueProducer ;

消费方:SurfaceTexture 在UpdateTexImage()时去acquireBuffer

相关技术点

SurfaceView

1. what

只拥有BufferQueue的生产端,消费端为SurfaceFlinger,渲染后必须上屏。

TextureView

1. what

由于其里边拥有SurfaceTexture, 可用于离屏渲染,拥有BufferQueue的生产端和消费端,

生产端可为camera、MediaCodec、任意render 模块, 可以跨进程(底层是GraphicBuffer嘛),消费端为SurfaceTexture GLConsumer, 可对图像做二次处理。

YUV

1. why

一般情况下SurfaceTexture承载的数据都是YUV类型(来自mediacodec、camera),有必要简单说下。

2. what

一种图像数据编码格式,常用的为YUV420,俗称I420,

各格式说明:

4:4:4 ,完全取样

4:2:2 ,2:1水平取样,垂直完全采样

4:2:0 ,2:1水平取样,垂直2:1采样

4:1:1 ,4:1水平取样,垂直完全采样

3. 格式 packed,planar、semi-planar

packed:不带后缀,如YUV420,数据以逐像素为单位排列,如

planar:带后缀P,如YUV420P,Y、U、V 分开存储,如

 

semi-planar:带后缀SP,如YUV420SP,UV交叉存储,与Y分开存储,如

这边可能要注意,排列可能为YYY.....UVUV, 或YYY...VUVU,即UV的第一个值到底是U还是V,

前者准确点叫YUV,后者叫YVU,又如 NV12、NV21 哈哈哈是不是有点迷糊了😜,

4. rgb与yuv的转换

一般可以用gpu,做个纹理采样去转,若gpu不支持硬件转换,需自己写shader;

有的gpu硬件支持直接转,如高通平台adreno,SurfaceTexture.updateTexImage()把YUV 图像数据挂上后,shader里边通过samplerExternal2D 采出来就是RGB数据。

5. android 系统 + 高通平台,如何获取不同YUV数据类型的GraphicBuffer大小,

接口:hardware/qcom/display/gralloc/gr_utils.cpp:: GetBufferSizeAndDimensions()

对于抓帧工具而言,将GraphicBuffer lock后,获取size,buffer里边分为Y、UV数据块,若是ubwc类型,头部还带meta数据,分块读取保存比较麻烦,由于这些分块数据在内存里边连续存储,所以可以直接整大块读取保存。

//notes: 该功能只有vendor才有能力开发,因为GraphicBuffer接口不对开发者开放,开放的AHardwareBuffer 相关的接口也起不了什么作用。

参考 kernel/msm-4.19/include/uapi/media/msm_media_info.h

EGLImage

1. what

 EGLImageKHR是EGL定义的一种专门用于共享2D图像数据的扩展格式,它可以在EGL的各种client api之间共享gpu侧数据(如OpenGL,OpenVG),也可称之为direct texture(Using direct textures on Android), 当我们需要对一块GraphicBuffer数据做纹理采样就可以使用EGLImage,所以其底层数据载体当然为GraphicBuffer。

对于Android系统,既然共享,又用了GraphicBuffer,那么就是不仅可以在不同线程间共享,还可以跨进程共享。

适用场景 : 需要经常更新纹理数据的场合,比如逐帧更新;

                   对camera、mediacode的输出做采样处理;

                   跨进程、跨线程共享纹理;

数据类型: 由于做为扩展给gpu厂家去自行支持, EGLImage里边可以存的数据其格式可以是opengles标准不支持的, 具体看厂家自己去实现,理论上可以存各种数据, 但一般在高通平台主要也就RGB和YUV,再加上是否带UBWC压缩。

2. why

性能优化,能跨进程、跨线程在gpu 侧直接共享纹理数据,减少拷贝次数;

在跨进程、跨线程共享texture上统一定义了标准接口与用法。

3. how

底层通过GraphicBuffer 实现(基于EGL_ANDROID_image_native_buffer 扩展),

所以原理上其实EGLImage只是一种接口封装,对GraphicBuffer的封装,将其封装成texture,

eglCreateImageKHR()
glBindTexture()
glEGLImageTargetTexture2DOES()
//glDrawXXX()
eglDestroyImageKHR()

4. 关于EGL_ANDROID_image_native_buffer 扩展

说明:

This extension enables using an Android window buffer (struct

ANativeWindowBuffer) as an EGLImage source.

该扩展使得可以将ANativeWindowBuffer 作为EGLImage的输入源

限制 :

If <target> is EGL_NATIVE_BUFFER_ANDROID, <dpy> must be a valid display,

<ctx> must be EGL_NO_CONTEXT, <buffer> must be a pointer to a valid

ANativeWindowBuffer object (cast into the type EGLClientBuffer), and

attributes other than EGL_IMAGE_PRESERVED_KHR are ignored."

即  对于EGLImageKHR eglCreateImageKHR( EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list)

  dpy必须合法,上下文必须为EGL_NO_CONTEXT,

  buffer输入源必须为ANativeWindowBuffer,GraphicBuffer通过getNativeBuffer()类型转换而来。

引用自: frameworks/native/opengl/specs/EGL_ANDROID_image_native_buffer.txt

5. bind texture

可以通过glEGLImageTargetTexture2DOES() 与GL_TEXTURE_2D、GL_TEXTURE_EXTERNAL_OES绑定,

6. eglImage和eglImageKHR

带KHR表示khronos 加的扩展,整体用法在Android上应该都差不多,一般都用带KHR后缀的,

具体细节倒没理得那么清😂。

7. EGLImage和EGLSurface

相同点: 底层都是GraphicBuffer,都是可以作为RenderTarget或texture使用;

不同点:创建的接口不同,eglCreateWindowSurface()、eglCreatePixmapSurface()、eglCreatePbufferSurface()、eglCreatePbufferFromClientBuffer(),eglCreateImageKHR()

             用法不同,EGLImage上下文无关,可以跨进程、线程共享、

              EGLSurface 上下文绑定。

8. GraphicBuffer 与eglImage 关系

等我看看freedreno代码先。。。

9. 进程间共享图像数据方式

GPU侧:GraphicBuffer + EGLImage + oes texture;

CPU侧:共享内存;

10. 共进程,不同opengl es线程共享纹理方式

(1)share context后,共享texture;

(2)EGLImage;

GL_TEXTURE_EXTERNAL_OES

1. what

俗称oes texture, 专门为EGLImage而加的texute类型,对于装有YUV数据的EGLImage,EGLImage一般通过挂到GL_TEXTURE_EXTERNAL_OES去做处理;

shader里边的sampler类型: samplerExternalOES;

如果是采样YUV数据类型的纹理,会自动转成RGB(GPU厂商需要实现这个功能),

Sampling an external texture will return an RGBA vector in the same colorspace as the source image. If the source image is stored in YUV (or some other basis) then the YUV values will be transformed to RGB values.

2. why

为何不直接挂到GL_TEXTURE_2D处理呢? 为什么要加上这个扩展?

这块理的不是特别清楚,相关资料都没讲得那么清,以下是一些整理+自己理解

/**

* mTexTarget is the GL texture target with which the GL texture object is

* associated. It is set in the constructor and never changed. It is

* almost always GL_TEXTURE_EXTERNAL_OES except for one use case in Android

* Browser. In that case it is set to GL_TEXTURE_2D to allow

* glCopyTexSubImage to read from the texture. This is a hack to work

* around a GL driver limitation on the number of FBO attachments, which the

* browser's tile cache exceeds.

*/

来自:frameworks/base/libs/hwui/surfacetexture/SurfaceTexture.h

即,除非需要从fbo拷贝数据到该texture挂的eglimage,才会将该eglImage挂到GL_TEXTURE_2D上,

而咱们一般都是对这块eglimage做采样嘛,所以要挂到GL_TEXTURE_EXTERNAL_OES上。

(感觉有点牵强,可以先接着往下看)

(1)外部纹理,即gpu侧图像数据来自其他上下文,对其采样时,目前的GL_TEXTURE2D不能挂,需要用eglimage挂到GL_TEXTURE_EXTERNAL_OES,

(2)硬件对YUV转RGB的支持

对于装YUV数据格式的EGLImage,一般GPU也不支持硬件YUV转RGB,若直接挂到GL_TEXTURE_2D做采样,需要自己在shader做RGB转换;

TEXTURE_EXTERNAL_OES 作为扩展,当GPU支持硬件YUV转RGB时,就会通过支持该扩展来表明。

3. how

怎么把gpu这个功能用上呢?

使用TEXTURE_EXTERNAL_OES,把EGLImage挂上去。

eglCreateImageKHR()
glBindTexture(TEXTURE_EXTERNAL_OES, textureId)
//eglImage 装YUV格式数据
glEGLImageTargetTexture2DOES(TEXTURE_EXTERNAL_OES, eglImage)
//glDrawXXX()
eglDestroyImageKHR()

通过samplerExternalOES类型的采样器,从YUV数据类型的TEXTURE_EXTERNAL_OES里边采样出来的数据,就是RGB,gpu做了转换,无需shader去转,eg:

            //声明使用该扩展"#extension GL_OES_EGL_image_external : require\n" +"precision mediump float;\n" +//指定sampler类型"uniform samplerExternalOES tex_sampler_0;\n" +"varying vec2 v_texcoord;\n" +"void main() {\n" +"  gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n" +"}\n";

跨线程使用

1. 准确的说是跨拥有不同opengles context的线程

attachToGLContext()
detachToGLContext()//通过这2个接口跨线context

代码分析

1. 主要代码流程

SurfaceTexture.java 
-->
base/core/jni/android/graphics/SurfaceTexture.cpp
-->
base/libs/hwui/surfacetexture/SurfaceTexture.cpp
--> 
EGLConsumer.cpp 
--> 
opengl es

SurfaceTexture.java:

主要是做类的作用、用法说明,对每个接口做了详细的说明,没什么逻辑,都是直接转调native 接口,

base/core/jni/android/graphics/SurfaceTexture.cpp:

实现SurfaceTexture.jave 的jni接口,里边没什么具体逻辑, 主要是承接 SurfaceTexture.jave 和base/libs/hwui/surfacetexture/SurfaceTexture.cpp

base/libs/hwui/surfacetexture/SurfaceTexture.cpp:

对SurfaceTexture.java的实现,主要是GraphicBuffer 的处理

EGLConsumer.cpp :

opengl es接口的封装,主要做了 基于acquireBuffer()出来的GraphicBuffer创建eglImageKHR, 将其挂到指定的oes texture 上

2. 时序图

注册SurfaceTexture.OnFrameAvailableListener:

 生产者生产完一块GraphicBuffer 数据后回调onFrameAvailable() 流程:

UpdateTextureImage(),消费者消费该GrapicBuffer,将其挂到opengles texture做纹理采样流程:

参考

https://blog.csdn.net/xkuzhang/article/details/115423061

https://paaatrick.com/2020-01-26-yuv-pixel-formats/

https://registry.khronos.org/EGL/extensions/KHR/EGL_KHR_image_base.txt

https://registry.khronos.org/OpenGL/extensions/OES/OES_EGL_image_external.txt

http://www.khronos.org/registry/gles/extensions/OES/OES_EGL_image_external.txt

这篇关于Android SurfaceTexture说明的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

zookeeper端口说明及介绍

《zookeeper端口说明及介绍》:本文主要介绍zookeeper端口说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、zookeeper有三个端口(可以修改)aVNMqvZ二、3个端口的作用三、部署时注意总China编程结一、zookeeper有三个端口(可以

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期

Android ClassLoader加载机制详解

《AndroidClassLoader加载机制详解》Android的ClassLoader负责加载.dex文件,基于双亲委派模型,支持热修复和插件化,需注意类冲突、内存泄漏和兼容性问题,本文给大家介... 目录一、ClassLoader概述1.1 类加载的基本概念1.2 android与Java Class

Go语言中make和new的区别及说明

《Go语言中make和new的区别及说明》:本文主要介绍Go语言中make和new的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 概述2 new 函数2.1 功能2.2 语法2.3 初始化案例3 make 函数3.1 功能3.2 语法3.3 初始化

java中新生代和老生代的关系说明

《java中新生代和老生代的关系说明》:本文主要介绍java中新生代和老生代的关系说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、内存区域划分新生代老年代二、对象生命周期与晋升流程三、新生代与老年代的协作机制1. 跨代引用处理2. 动态年龄判定3. 空间分

MySQL之InnoDB存储引擎中的索引用法及说明

《MySQL之InnoDB存储引擎中的索引用法及说明》:本文主要介绍MySQL之InnoDB存储引擎中的索引用法及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录1、背景2、准备3、正篇【1】存储用户记录的数据页【2】存储目录项记录的数据页【3】聚簇索引【4】二

mysql中的数据目录用法及说明

《mysql中的数据目录用法及说明》:本文主要介绍mysql中的数据目录用法及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、版本3、数据目录4、总结1、背景安装mysql之后,在安装目录下会有一个data目录,我们创建的数据库、创建的表、插入的

Maven中的profiles使用及说明

《Maven中的profiles使用及说明》:本文主要介绍Maven中的profiles使用及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录主要用途定义 Profiles示例:多环境配置激活 Profiles示例:资源过滤示例:依赖管理总结Maven 中的

Android DataBinding 与 MVVM使用详解

《AndroidDataBinding与MVVM使用详解》本文介绍AndroidDataBinding库,其通过绑定UI组件与数据源实现自动更新,支持双向绑定和逻辑运算,减少模板代码,结合MV... 目录一、DataBinding 核心概念二、配置与基础使用1. 启用 DataBinding 2. 基础布局

Android ViewBinding使用流程

《AndroidViewBinding使用流程》AndroidViewBinding是Jetpack组件,替代findViewById,提供类型安全、空安全和编译时检查,代码简洁且性能优化,相比Da... 目录一、核心概念二、ViewBinding优点三、使用流程1. 启用 ViewBinding (模块级