了解Android相机SurfaceTexture和MediaCodec Surface使用情况

时间:2017-03-13 04:07:30

标签: android opengl-es android-camera mediacodec egl

我试图理解Android中的图形内存使用/流量,特别是使用MediaCodec来编码来自摄像头的帧。为了做到这一点,我必须了解一堆我不清楚的图形,OpenGL和Android术语/概念。我已经阅读了Android图形架构材料,一堆SO问题以及一堆来源,但我仍然感到困惑,主要是因为看起来术语在不同的上下文中有不同的含义。

我从fadden的网站here看了一下CameraToMpegTest。我的具体问题是MediaCodec::createInputSurface()如何与Camera::setPreviewTexture()一起使用。似乎创建了一个OpenGL纹理,然后用它来创建一个Android SurfaceTexture,然后可以将其传递给setPreviewTexture()。我的具体问题:

  1. 调用setPreviewTexture()实际上对帧从镜头传输的内存缓冲区做了什么?
  2. 根据我的理解,OpenGL纹理是GPU可访问的一块内存。在Android上,必须使用带有正确用法标志的gralloc进行分配。 SurfaceTexture的Android说明提到它允许您将图像流式传输到给定的OpenGL纹理":https://developer.android.com/reference/android/graphics/SurfaceTexture.html#SurfaceTexture(int)SurfaceTexture在OpenGL纹理之上做了什么?
  3. MediaCodec::createInputSurface()会返回Android Surface。据我所知,Android Surface代表缓冲区队列的生产者端,因此它可能是多个缓冲区。 API reference提到表面必须使用硬件加速API进行渲染,例如OpenGL ES"。相机捕获的帧如何从SurfaceTexture到输入到编码器的Surface?我看到CameraToMpegTest使用这个EGLSurface以某种方式创建Surface但不太了解EGL我不会得到这个部分。
  4. 有人可以澄清"渲染"?的用法我看到诸如"渲染到表面的东西","渲染到屏幕"其他用法似乎可能意味着不同的东西。
  5. 编辑:mstorsjo回复的后续行动:

    1. 我在SurfaceTexture中挖掘了CameraClient::setPreviewTarget()CameraService的代码,以便更好地了解Camera::setPreviewTexture()的内部运作方式并提出更多问题。至于我理解内存分配的原始问题,似乎SurfaceTexture创建了BufferQueueCameraService将关联的IGraphicBufferProducer传递给平台相机HAL实现。然后,摄像机HAL可以适当地设置gralloc使用标志(例如GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_NEVER | GRALLOC_USAGE_HW_TEXTURE),并且还将缓冲区从此BufferQueue出列。因此,摄像机捕获帧的缓冲区是gralloc分配的缓冲区,带有一些特殊的使用标记,如GRALLOC_USAGE_HW_TEXTURE。我在具有统一内存架构的ARM平台上工作,因此GPU和CPU可以访问相同的内存,因此GRALLOC_USAGE_HW_TEXTURE标志对缓冲区的分配方式会产生什么样的影响?
    2. SurfaceTexture的OpenGL(ES)部分似乎主要是作为GLConsumer的一部分实现的,而且魔法似乎在updateTexImage()中。是否为OpenGL(ES)纹理分配了额外的缓冲区,或者是否可以使用相机填充的相同gralloc缓冲区?是否需要进行一些内存复制才能将相机像素数据从gralloc缓冲区转换为OpenGL(ES)纹理?我想我不明白调用updateTexImage()会做什么。

1 个答案:

答案 0 :(得分:5)

  1. 这意味着摄像机通过不透明的句柄而不是应用程序地址空间内的用户提供的缓冲区提供输出帧(如果使用setPreviewCallbacksetPreviewCallbackWithBuffer)。这个不透明的手柄,纹理,可以在OpenGL绘图中使用。

  2. 几乎。在这种情况下,OpenGL纹理不是内存的物理块,而是EGL上​​下文中可变内存块的句柄。在这种情况下,示例代码本身并不实际分配或调整纹理大小,它只使用glGenTextures为纹理创建“名称”/句柄 - 它基本上只是一个整数。在普通的OpenGL(ES)中,您将使用OpenGL函数为纹理分配实际存储并用内容填充它。在此设置中,SurfaceTexture提供Android级API /抽象以使用数据填充纹理(即使用正确的标志为其分配存储,为其提供大小和内容) - 允许您传递{{1其他可以用数据填充它的类(SurfaceTexture直接获取Camera,或者包装在SurfaceTexture类中以便能够在其他上下文中使用它。这样可以有效地填充OpenGL纹理内容,而无需将原始数据缓冲区传递到应用程序的进程,并让您的应用程序将其上传到OpenGL。

  3. (以相反的顺序回答第3点和第4点。)OpenGL(ES)是绘图的通用API。在普通/原始设置中,考虑一个游戏,你有许多纹理用于游戏内容的不同部分(背景,道具,演员等),然后用OpenGL API将其绘制到屏幕上。纹理可以或多或少地只是复制到屏幕上,或者包裹在由三角形构建的3D对象周围。这是一个称为“渲染”的过程,它采用输入纹理和三角形集并绘制它。在最简单的情况下,您可以直接将内容呈现到屏幕上。 GPU通常也可以对任何其他输出缓冲区执行相同的渲染。在游戏中,通常将一些场景渲染到纹理中,并将预渲染的纹理用作最终渲染的一部分,最终渲染实际上最终显示在屏幕上。

  4. 创建EGL上下文,用于将摄像机的输出传递到编码器输入。 EGL上下文基本上是用于执行OpenGL渲染的上下文。渲染的目标是编码器的Surface。也就是说,使用OpenGL绘制的任何图形都会在编码器输入缓冲区中而不是在屏幕上。现在,使用OpenGL绘制的场景可以是任何OpenGL函数调用序列,将游戏场景渲染到编码器中。 (这是Android Breakout游戏记录器示例所做的。)在上下文中,创建纹理句柄。不像通常的游戏图形渲染那样通过从磁盘加载图片来填充内容,而是将其设置为Surface,以允许SurfaceTexture用相机图片填充它。 Camera类提供回调,在SurfaceTexture更新内容时发出信号。当收到此回调时,激活EGL上下文并将一帧渲染到EGL上下文输出目标(即编码器输入)中。渲染本身并没有做任何花哨的事情,但更多或者将输入纹理直接复制到输出中。

  5. 这可能听起来很迂回,但它确实带来了一些好处:

    • 不需要在应用程序代码中直接处理相机帧的实际原始位(并且可能永远不会在应用程序的进程和地址空间内)。对于低分辨率,这不是什么大问题,但Camera API是解决更高分辨率的瓶颈。
    • 您可以在OpenGL中进行颜色调整和其他任何操作,几乎可以免费使用GPU加速。