2

我有一个标准的 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 类中显示此纹理的内容?

4

1 回答 1

2

对于帧来自 Camera 或 MediaCodec 的情况,有一些非常有效的解决方案。不过,听起来您是在软件中生成或解码视频,这对事物产生了轻微的影响(尽管您的代码中确实声明了 SurfaceTexture,这很奇怪)。诀窍是“OpenGL ES 纹理”部分,因为纹理与 EGL 上下文相关联,而 EGL 上下文一次只能在一个线程中处于活动状态。

因为您使用的是 GLSurfaceView,而不是普通的 SurfaceView,所以您无法控制 GLSurfaceView 渲染器线程中的 EGL 上下文。解决此问题的最简单方法是跳过一些环节来创建与第一个共享的第二个 EGL 上下文。完成后,在单独的 JNI 线程中创建的纹理将可用于 GLSurfaceView 渲染器线程。

您可以在Grafika 的“显示 + 捕获相机”活动中找到一个示例。如果您查看 CameraCaptureActivity.java 中的onDrawFrame()方法,您可以看到它正在调用updateSharedContext(),它将消息传递给运行 TextureMovieEncoder 的线程以使其运行handleUpdateSharedContext(),从而(重新)创建用于馈送视频编码器的表面。

如果您改用普通的 SurfaceView,并进行自己的 EGL 和线程管理,则代码将不那么曲折。您可以一次创建两个上下文,然后只需将一个传递给生成图像的线程。您还可以创建单个上下文并用于eglMakeCurrent()在线程之间转移它,但这在某些平台上可能会很昂贵。

更新: Grafika“显示 + 捕捉相机”实现有一个竞争条件;有关问题和解决方案的详细信息,请参阅此错误报告。在一个线程中创建纹理并在另一个线程中使用它时,您必须执行一些额外的步骤。通常最好在一个线程上在一个上下文中完成所有事情。Grafika 中的其他活动使用普通的 SurfaceView 并进行自己的 EGL 上下文和线程管理。

于 2015-06-02T16:22:49.177 回答