Как визуализировать текстуру для 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 и управление потоками.