Android上最低的摄像头到CPU到GPU的方法

时间:2016-05-29 13:30:59

标签: android opengl-es android-ndk android-camera

我的应用程序需要在CPU上对实时相机帧进行一些处理,然后再在GPU上进行渲染。还有一些其他东西在GPU上呈现,这取决于CPU处理的结果;因此,保持所有内容同步非常重要,这样我们就不会在GPU上渲染帧本身,直到该帧的CPU处理结果也可用。

问题是什么是Android上最低的开销方法?

在我的情况下CPU处理只需要一个灰度图像,因此Y平面打包的YUV格式是理想的(并且往往与相机设备的原生格式很好地匹配)。 NV12,NV21或全平面YUV都可以提供理想的低开销灰度访问,因此在CPU方面是首选。

在原始相机API中,setPreviewCallbackWithBuffer()是将数据输入CPU进行处理的唯一合理方法。这使Y平面分离,因此非常适合CPU处理。将此框架提供给OpenGL以便以低开销方式呈现是更具挑战性的方面。最后,我编写了一个NEON颜色转换例程来输出RGB565,并且只需使用glTexSubImage2d就可以在GPU上使用它。这是在Nexus 1时间帧中首次实现的,即使是320x240 glTexSubImage2d调用占用了50ms的CPU时间(我猜想也会尝试进行纹理调配 - 这在以后的系统更新中得到了显着改善)。

在那天我回顾了像eglImage扩展这样的东西,但它们似乎没有足够的用户应用程序可用或有足够的文档记录。我对内部android GraphicsBuffer类进行了一些研究,但理想情况下希望保留在受支持的公共API的世界中。

android.hardware.camera2 API承诺能够将ImageReader和SurfaceTexture连接到捕获会话。不幸的是,我无法在这里看到任何确保正确的顺序管道的方法 - 在CPU处理之前阻止调用updateTexImage()是很容易的,但如果在该处理期间另一帧已经到达,那么updateTexImage()将直接跳到最新的框架。对于多个输出,似乎每个队列中都会有独立的帧副本,理想情况下我想避免这些副本。

理想情况下,这就是我所希望的:

  1. 相机驱动程序使用最新的帧
  2. 填充一些内存
  3. CPU获取指向内存中数据的指针,可以在不复制的情况下读取Y数据
  4. CPU处理数据并在帧准备就绪时在我的代码中设置一个标志
  5. 开始渲染帧时,检查新帧是否准备就绪
  6. 调用某些API以将与GL纹理相同的内存绑定
  7. 当较新的帧准备就绪时,将保存前一帧的缓冲区释放回池中
  8. 我无法在Android上看到使用公共API完全实现零复制风格的方法,但它最接近的是什么?

    我试过的一个疯狂的事情似乎有用,但没有记录:ANativeWindow NDK API可以接受数据NV12格式,即使适当的格式常量不是公共标题中的那个。这允许通过memcpy()填充SurfaceTxture的NV12数据,以避免CPU端颜色转换以及glTexImage2d中驱动程序端发生的任何混乱。这仍然是数据的额外副本,虽然感觉它应该是不必要的,并且因为它没有记录可能不适用于所有设备。支持的连续零拷贝相机 - > ImageReader - > SurfaceTexture或同等产品将是完美的。

1 个答案:

答案 0 :(得分:5)

处理视频的最有效方法是完全避免CPU,但听起来这不适合你。公共API通常面向硬件中的所有内容,因为这是框架本身所需要的,尽管RenderScript有一些路径。 (我假设您已经看到使用片段着色器的Grafika filter demo。)

访问CPU上的数据用于表示慢速Camera API或使用GraphicBuffer和相对模糊的EGL功能(例如this question)。 ImageReader的目的是提供对来自相机的YUV数据的零拷贝访问。

你无法真正序列化相机 - > ImageReader - > SurfaceTexture作为ImageReader没有“转发缓冲区”API。这是不幸的,因为这将使这个微不足道。您可以尝试复制SurfaceTexture所做的事情,使用EGL函数将缓冲区打包为外部纹理,但是再次进入非公共GraphicBuffer-land,我担心缓冲区的所有权/生命周期问题。

我不确定并行路径如何帮助您(Camera2 - > ImageReader,Camera2 - > SurfaceTexture),因为发送到SurfaceTexture的内容不会对您进行修改。 FWIW,它不涉及额外的副本 - 在Lollipop或其周围,BufferQueue已更新,允许单个缓冲区在多个队列中移动。

完全有可能有一些我尚未见过的新奇的API,但据我所知,你的ANativeWindow方法可能是赢家。我怀疑使用其中一种相机格式(YV12或NV21)比NV12更好,但我不确定。

FWIW,如果您的处理时间太长,您将丢帧,但除非您的处理不均匀(某些帧比其他帧花费的时间长),否则无论如何都必须丢帧。再次进入非公共API的领域,您可以将SurfaceTexture切换到“同步”模式,但如果缓冲区填满,您仍然会丢帧。