如何将相机预览传递给 MediaCodec.createInputSurface() 创建的 Surface?

Posted

技术标签:

【中文标题】如何将相机预览传递给 MediaCodec.createInputSurface() 创建的 Surface?【英文标题】:How to pass Camera preview to the Surface created by MediaCodec.createInputSurface()? 【发布时间】:2013-11-07 17:06:39 【问题描述】:

理想情况下,我想实现两个目标:

    通过SurfaceCamera 预览数据传递给MediaCodec 编码器。我可以使用MediaCodec.createInputSurface() 创建Surface,但Camera.setPreviewDisplay() 需要SurfaceHolder,而不是Surface。 除了将Camera 预览数据传递给编码器之外,我还想在屏幕上显示预览(这样用户就可以实际看到他们正在编码的内容)。如果不涉及编码器,那么我会使用SurfaceView,但这似乎在这种情况下不起作用,因为SurfaceView 创建了自己的Surface,我想我需要使用@ 创建的那个987654334@.

我在网上搜索了很多解决方案,但没有找到。 bigflake.com 上的一些示例似乎是朝着正确方向迈出的一步,但它们采用的方法增加了我想避免的一堆 EGL/SurfaceTexture 开销。我希望有一个更简单的示例或解决方案,我可以让CameraMediaCodec 更直接地交谈,而不涉及 EGL 或纹理。

【问题讨论】:

【参考方案1】:

android 4.3 (API 18) 开始,bigflake CameraToMpegTest 方法是正确的方法。

EGL/SurfaceTexture 开销目前是不可避免的,尤其是对于您想要在目标 #2 中执行的操作。这个想法是:

将摄像头配置为将输出发送到SurfaceTexture。这使得 GLES 可以将相机输出作为“外部纹理”使用。 将SurfaceTexture 渲染到MediaCodec#createInputSurface() 返回的Surface。这为视频编码器提供数据。 再次将SurfaceTexture 渲染到GLSurfaceView。这会将其显示在显示屏上以供实时预览。

发生的唯一数据复制由 GLES 驱动程序执行,因此您正在执行硬件加速的 blit,这将很快。

唯一棘手的一点是您希望外部纹理可用于两种不同的 EGL 上下文(一种用于MediaCodec,一种用于GLSurfaceView)。您可以在 bigflake 上的“Android Breakout 游戏记录器补丁”示例中看到创建共享上下文的示例——它将游戏渲染两次,一次渲染到屏幕,一次渲染到 MediaCodec 编码器。

更新:这是在Grafika(“显示+捕获相机”)中实现的。

更新:“show + capture camera”方法中的多上下文方法是somewhat flawed。 “连续捕获” Activity 使用普通的 SurfaceView,并且能够使用单个 EGL 上下文进行屏幕渲染和视频录制。这是推荐的。

【讨论】:

关于棘手的部分,我发现 CameraToMpegTest 和突破游戏记录器之间的主要区别在于,在记录器中,实际上在为记录器部分创建上下文时定义了一个共享的 egl 上下文。所以只要确保上下文是共享的,在绘制“主”图片后,使辅助上下文和表面成为当前的,然后再次使用相同的绘制代码。 或者,使用具有两个 EGL 表面的单个 EGL 上下文。 (自从我写这篇文章以来,Grafika 已经扩展了很多;请参阅“连续捕获”活动以获取另一个示例。)

以上是关于如何将相机预览传递给 MediaCodec.createInputSurface() 创建的 Surface?的主要内容,如果未能解决你的问题,请参考以下文章

Jcrop 中的多张图片和预览。如何将许多 id 传递给 Javascript 函数

如何将相机预览设置为壁纸?

如何将相机预览流从android连接到Qt5?

如何使用 Android API 将相机预览大小设置为全屏?

React Native / Expo - 将图像传递到预览屏幕

如何获得相机预览的高度?