Как визуализировать текстуру для Android GLSurfaceView

У меня есть стандартный класс 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);
    }
}

У меня есть класс Renderer, который реализует GLSurfaceView.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 уведомление о том, что доступна новая текстура, и у меня есть соответствующий идентификатор текстуры.

Как я могу отобразить содержимое этой текстуры в классе Renderer с минимальным влиянием на производительность?

1 ответ

Решение

Для случаев, когда кадры исходят от Camera или MediaCodec, есть несколько очень эффективных решений. Звучит так, будто вы генерируете или декодируете видео в программном обеспечении, что слегка смягчает ситуацию (хотя в вашем коде объявлена ​​SurfaceTexture, что странно). Уловка - это часть "Текстура OpenGL ES", потому что текстура связана с контекстом EGL, и контекст EGL может быть активным только в одном потоке за один раз.

Поскольку вы используете GLSurfaceView, а не обычный SurfaceView, у вас нет контроля над контекстом EGL в потоке рендерера GLSurfaceView. Самый простой способ обойти это - перепрыгнуть через несколько обручей, чтобы создать второй контекст EGL, который используется совместно с первым. Как только вы это сделаете, текстура, созданная в отдельном потоке JNI, будет доступна для потока визуализации GLSurfaceView.

Вы можете найти пример этого в деятельности Grafika "показать + захватить камеру". Если вы посмотрите на onDrawFrame() метод в CameraCaptureActivity.java вы можете увидеть, что он вызывает updateSharedContext(), который передает сообщение потоку, выполняющему TextureMovieEncoder, чтобы запустить его handleUpdateSharedContext(), который (повторно) создает поверхность, используемую для подачи видеокодера.

Если вместо этого вы используете обычный SurfaceView и выполняете собственные EGL и управление потоками, код будет менее сложным. Вы можете создать оба контекста одновременно, а затем просто передать один потоку, производящему изображения. Вы также можете создать один контекст и использовать eglMakeCurrent() переключать его между потоками, но это может быть дорого на некоторых платформах.

Обновление: реализация Grafika "show + capture camera" имеет состояние гонки; см. этот отчет об ошибке для деталей о проблеме и решении. Вы должны выполнить некоторые дополнительные шаги при создании текстуры в одном потоке и использовании ее в другом. Обычно лучше делать все в одном контексте в одном потоке. Другие действия в Grafika используют обычный SurfaceView и имеют собственный контекст EGL и управление потоками.

Другие вопросы по тегам