我有一个标准的 GLSurfaceView 类:
public class TestSurfaceView extends GLSurfaceView {
public MainRenderer mRenderer;
public GStreamerSurfaceView(Context context) {
super(context);
setEGLContextClientVersion(2);
mRenderer = new MainRenderer(context);
setRenderer(mRenderer);
setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
}
}
我有一个实现 GLSurfaceView.Renderer 的 Renderer 类:
public class MainRenderer implements GLSurfaceView.Renderer {
private int[] hTex;
private SurfaceTexture mSTexture;
private Context context;
MainRenderer(Context c) {
context = c;
}
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
}
public void onDrawFrame(GL10 unused) {
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
}
public void onSurfaceCreated(GL10 arg0, javax.microedition.khronos.egl.EGLConfig arg1) {
}
}
在一个单独的 JNI 线程中,我将一些视频数据(YUV 格式)上传到 OpenGLES 纹理。我在 Java 中收到新纹理可用的通知,并且我有相应的纹理 ID。
如何在 Renderer 类中显示此纹理的内容,同时对性能影响最小?
最佳答案
对于帧来自 Camera 或 MediaCodec 的情况,有一些非常有效的解决方案。不过,听起来您正在软件中生成或解码视频,这给事情带来了轻微的旋转(尽管您确实在代码中声明了 SurfaceTexture,这很奇怪)。诀窍在于“OpenGL ES 纹理”部分,因为纹理与 EGL 上下文关联,而 EGL 上下文一次只能在一个线程中处于 Activity 状态。
因为您使用的是 GLSurfaceView,而不是普通的 SurfaceView,所以您无法控制 GLSurfaceView 渲染器线程中的 EGL 上下文。解决这个问题的最简单方法是跳过一些麻烦来创建与第一个共享的第二个 EGL 上下文。完成此操作后,在单独的 JNI 线程中创建的纹理将可供 GLSurfaceView 渲染器线程使用。
您可以在 Grafika's 中找到这样的示例“秀+拍照” Activity 。如果您查看 CameraCaptureActivity.java 中的 onDrawFrame()
方法,您可以看到它调用 updateSharedContext()
,该方法将消息传递给运行 TextureMovieEncoder 的线程以使其运行handleUpdateSharedContext()
,它(重新)创建用于提供视频编码器的表面。
如果您使用普通的 SurfaceView,并执行自己的 EGL 和线程管理,代码将不会那么曲折。您可以同时创建两个上下文,然后将其中一个传递给生成图像的线程。您还可以创建一个上下文并使用 eglMakeCurrent()
在线程之间转移它,但这在某些平台上可能会很昂贵。
更新:Grafika“显示+捕获相机”实现存在竞争条件;请参阅this bug report有关问题和解决方案的详细信息。在一个线程中创建纹理并在另一个线程中使用它时,您必须执行一些额外的步骤。通常最好在一个线程的一个上下文中完成所有操作。 Grafika 中的其他 Activity 使用普通 SurfaceView 并执行自己的 EGL 上下文和线程管理。
关于android - 如何将纹理渲染到 Android GLSurfaceView,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30585727/