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

相关文章

Before和BeforeClass的区别及说明

《Before和BeforeClass的区别及说明》:本文主要介绍Before和BeforeClass的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Before和BeforeClass的区别一个简单的例子当运行这个测试类时总结Before和Befor

Python pip下载包及所有依赖到指定文件夹的步骤说明

《Pythonpip下载包及所有依赖到指定文件夹的步骤说明》为了方便开发和部署,我们常常需要将Python项目所依赖的第三方包导出到本地文件夹中,:本文主要介绍Pythonpip下载包及所有依... 目录步骤说明命令格式示例参数说明离线安装方法注意事项总结要使用pip下载包及其所有依赖到指定文件夹,请按照以

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

Spring中管理bean对象的方式(专业级说明)

《Spring中管理bean对象的方式(专业级说明)》在Spring框架中,Bean的管理是核心功能,主要通过IoC(控制反转)容器实现,下面给大家介绍Spring中管理bean对象的方式,感兴趣的朋... 目录1.Bean的声明与注册1.1 基于XML配置1.2 基于注解(主流方式)1.3 基于Java

Android NDK版本迭代与FFmpeg交叉编译完全指南

《AndroidNDK版本迭代与FFmpeg交叉编译完全指南》在Android开发中,使用NDK进行原生代码开发是一项常见需求,特别是当我们需要集成FFmpeg这样的多媒体处理库时,本文将深入分析A... 目录一、android NDK版本迭代分界线二、FFmpeg交叉编译关键注意事项三、完整编译脚本示例四

Android与iOS设备MAC地址生成原理及Java实现详解

《Android与iOS设备MAC地址生成原理及Java实现详解》在无线网络通信中,MAC(MediaAccessControl)地址是设备的唯一网络标识符,本文主要介绍了Android与iOS设备M... 目录引言1. MAC地址基础1.1 MAC地址的组成1.2 MAC地址的分类2. android与I

idea报错java: 非法字符: ‘\ufeff‘的解决步骤以及说明

《idea报错java:非法字符:‘ufeff‘的解决步骤以及说明》:本文主要介绍idea报错java:非法字符:ufeff的解决步骤以及说明,文章详细解释了为什么在Java中会出现uf... 目录BOM是什么?1. BOM的作用2. 为什么会出现 \ufeff 错误?3. 如何解决 \ufeff 问题?最

Android 实现一个隐私弹窗功能

《Android实现一个隐私弹窗功能》:本文主要介绍Android实现一个隐私弹窗功能,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 效果图如下:1. 设置同意、退出、点击用户协议、点击隐私协议的函数参数2. 《用户协议》、《隐私政策》设置成可点击的,且颜色要区分出来res/l

Android实现一键录屏功能(附源码)

《Android实现一键录屏功能(附源码)》在Android5.0及以上版本,系统提供了MediaProjectionAPI,允许应用在用户授权下录制屏幕内容并输出到视频文件,所以本文将基于此实现一个... 目录一、项目介绍二、相关技术与原理三、系统权限与用户授权四、项目架构与流程五、环境配置与依赖六、完整

Android 12解决push framework.jar无法开机的方法小结

《Android12解决pushframework.jar无法开机的方法小结》:本文主要介绍在Android12中解决pushframework.jar无法开机的方法,包括编译指令、框架层和s... 目录1. android 编译指令1.1 framework层的编译指令1.2 替换framework.ja