我试图了解Android中的图形内存使用/流动,特别是使用MediaCodec
从相机中编码帧。为此,我必须了解我不清楚的一堆图形,OpenGL和Android术语/概念。我已经阅读了Android图形体系结构材料,一堆问题和许多来源,但我仍然很困惑,主要是因为术语在不同的上下文中具有不同的含义。
我在这里查看了Fadden网站的CameratompegTest。我的具体问题是MediaCodec::createInputSurface()
如何与Camera::setPreviewTexture()
结合使用。似乎创建了一个openGL纹理,然后用于创建Android SurfaceTexture
,然后可以将其传递给setPreviewTexture()
。我的具体问题:
- 呼叫
setPreviewTexture()
实际上是在镜头从相机上转到的内存缓冲区的实际做法? - 从我的理解中,opengl纹理是GPU可以访问的大量内存。在Android上,必须使用Gralloc和正确的使用标志分配这一点。
SurfaceTexture
的Android描述提到它允许您"将图像流式传输到给定的OpenGL纹理":https://developer.android.com/reference/android/android/graphics/surfaceetexture.html#surfacetexture(int)。SurfaceTexture
在OpenGL纹理上做什么? -
MediaCodec::createInputSurface()
返回AndroidSurface
。据我了解,AndroidSurface
代表缓冲区队列的生产者侧,因此可能是多个缓冲区。API引用提到"必须使用硬件加速API(例如OpenGL ES)渲染表面"。相机捕获的帧如何从SurfaceTexture
到该Surface
输入到编码器?我看到cameratompegtest使用此Surface
以某种方式创建了EGLSurface
,但对EGL不了解,我没有得到此部分。 - 有人可以澄清"渲染"的用法吗?我看到了诸如"呈现到表面","向屏幕上渲染"之类的东西,这些用法似乎可能意味着不同的事情。
编辑:跟进Mstorsjo的回答:
- 我在
CameraService
中挖掘了SurfaceTexture
和CameraClient::setPreviewTarget()
的代码,更多地尝试更好地了解Camera::setPreviewTexture()
的内部工作原理,并有更多问题。对于我最初的理解内存分配的问题,似乎SurfaceTexture
创建了BufferQueue
和CameraService
将关联的IGraphicBufferProducer
传递给平台摄像机HAL实现。然后,相机HAL可以适当地设置Gralloc使用标志(例如GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_NEVER | GRALLOC_USAGE_HW_TEXTURE
),并从该BufferQueue
设置Dequeue缓冲区。因此,摄像机捕获框架的缓冲区是Gralloc分配的带有一些特殊用法标志(例如GRALLOC_USAGE_HW_TEXTURE
)。我在具有统一内存体系结构的ARM平台上工作,因此GPU和CPU可以访问相同的内存,因此GRALLOC_USAGE_HW_TEXTURE
标志对如何分配缓冲区有什么样的影响? -
SurfaceTexture
的OpenGL(ES)部分似乎主要是作为GLConsumer
的一部分实现的,而魔术似乎在updateTexImage()
中。是否还为OpenGL(ES)纹理分配了其他缓冲区,还是可以使用相机填充的Gralloc缓冲液?是否必须在此处进行一些内存复制,以将相机像素数据从Gralloc缓冲区中获取到OpenGL(ES)纹理?我想我不明白updateTexImage()
是什么。
-
这意味着相机通过不透明的手柄而不是在应用程序地址空间内的用户提供的缓冲区(如果使用
setPreviewCallback
或setPreviewCallbackWithBuffer
)中提供输出帧。这种不透明的手柄(纹理)可在OpenGL图中使用。 -
几乎。在这种情况下,OpenGL纹理不是内存的物理块,而是EGL上下文中可变内存块的句柄。在这种情况下,示例代码本身实际上并未分配或大小纹理,它仅创建使用
glGenTextures
纹理的"名称"/句柄 - 它基本上只是一个整数。在普通的OpenGL(ES)中,您将使用OpenGL功能分配纹理的实际存储空间并填充内容。在此设置中,SurfaceTexture
提供了一个Android级别的API/抽象,以使用数据填充纹理(即用正确的标志为其分配存储,提供大小和内容) - 允许您将SurfaceTexture
传递给其他可以填充的类的类别它使用数据(Camera
直接采用SurfaceTexture
,或在Surface
类中包装以便在其他情况下使用它)。这允许有效地填充OpenGL纹理,而无需将原始数据的缓冲区传递给您的应用程序过程并将应用程序上传到OpenGL。 -
(以相反的顺序为第3和4个答案。)OpenGL(ES)是用于绘图的通用API。在普通/原始设置中,考虑一个游戏,您将对游戏内容的不同部分(背景,道具,演员等)具有许多纹理,然后使用OpenGL API将其绘制到屏幕上。这些纹理可以或多或少仅将其复制到屏幕上,或者被包裹在由三角形构建的3D对象上。这是称为"渲染"的过程,采用输入纹理和一组三角形并绘制它。在最简单的情况下,您将直接渲染到屏幕上。GPU通常也可以在任何其他输出缓冲区中进行相同的渲染。在游戏中,通常将某些场景渲染成纹理,并将其使用的纹理用作最终渲染的一部分,该渲染实际上最终显示在屏幕上。
-
创建了一个EGL上下文,用于将输出从相机传递到编码器输入中。EGL上下文基本上是进行OpenGL渲染的上下文。渲染的目标是来自编码器的表面。也就是说,使用OpenGL绘制的任何图形最终都在编码器输入缓冲区而不是在屏幕上。现在,使用OpenGL绘制的场景可以是任何openGL函数调用序列,将游戏场景渲染到编码器中。(这是Android突破游戏录音机示例所做的。)在上下文中,创建了一个纹理句柄。与其像普通的游戏图形渲染一样从磁盘上加载图片来填充纹理,而是将其制成
SurfaceTexture
,以允许Camera
用相机图片填充它。SurfaceTexture
类提供回调,当Camera
更新内容时发出信号。收到此回调后,将激活EGL上下文,并将一个帧渲染到EGL上下文输出目标(即编码器输入)中。渲染本身不会做任何花哨的事情,或者更多地将输入纹理复制到输出中。
这听起来都很回旋,但确实给人带来了一些好处:
- 无需直接在应用程序代码中处理相机框架的实际原始位(并且可能永远不会在应用程序的过程和地址空间中)。对于低分辨率,这不是问题,但是
setPreviewCallback
API在更高的分辨率方面是瓶颈。 - 您可以进行颜色调整,并且可以在OpenGL中进行其他操作,几乎可以通过GPU加速免费。