现在我通过向MediaCodec对象的Configure调用提供Surface并调用releaSEOutputBuffer并将render标志设置为true来让android解码视频帧.
我发现访问解码表面数据的唯一方法(除了解码返回的bytebuffer,其格式似乎与设备有关)是调用链接到Surface的SurfaceTexture上的updateTeximage,将其附加到GL_TEXTURE_EXTERNAL_OES目标并将其渲染到GL_TEXTURE2D我自己创建的目标纹理是为了缓存它.
我想优化此缓存过程,并能够解码不同线程上的帧.使用我当前的方法,这意味着我将不得不为视频解码器创建另一个EGL上下文,共享上下文等…
我的问题是:是否可以在不调用updateTexImage的情况下访问与Surface关联的EGL图像或本机缓冲区数据?
这样我就可以缓存egl图像(根据EGL_ANDROID_image_native_buffer不需要EGL上下文).这也将以YUV格式缓存,这比我现在缓存的原始RGB纹理更具存储效率.
解决方法
更长的答案:Surface封装了一个queue of buffers.(编辑:系统现在详细解释了here.)当你调用updateTexImage()时,如果有一个新的数据帧,那么头部的缓冲区将被删除,下一个缓冲区将被删除在队列中变为当前.调用updateTexImage()是查看连续帧所必需的;没有机制可以检查不在头部的缓冲区.
SurfaceTexture包装GLConsumer的实例.该消费者要求生产者(视频解码器)以可用作“硬件纹理”的格式生成数据,即设备的GL实现可以理解的内容.它可能是也可能不是YUV.更重要的是,消费者不要求缓冲区可用于“软件”,这意味着您不能假设您可以直接访问数据 – 您需要使用GLES. (有关标志的完整列表,请参阅gralloc header.)
这里更好的是能够将缓冲区从BufferQueue的头部复制到单独的数据结构(BufferArrayList?)而无需进行格式转换,但目前还没有类似的机制(Android 4.3).我不知道比你描述的更好的方法(共享EGL上下文等).
更新:我的办公室伙伴提出了一个建议:使用着色器将缓冲区渲染为两个纹理,一个用于Y,另一个用于CbCr(在GLES 3中,您可以使用RG纹理).这样可以保持GLES中的所有操作,而不会扩展到完整的RGB.在内部,它会将MediaCodec输出转换为RGB并通过它进行两次研磨,但这可能比将其复制到用户空间并在cpu上自行完成更便宜.