| Index: webrtc/api/android/java/src/org/webrtc/YuvConverter.java
|
| diff --git a/webrtc/api/android/java/src/org/webrtc/YuvConverter.java b/webrtc/api/android/java/src/org/webrtc/YuvConverter.java
|
| index 1203d86515840db873750cf555b3196844ec3812..050d69de8edbaa3c95d37cd8f1750bf740e974d9 100644
|
| --- a/webrtc/api/android/java/src/org/webrtc/YuvConverter.java
|
| +++ b/webrtc/api/android/java/src/org/webrtc/YuvConverter.java
|
| @@ -16,13 +16,10 @@ import java.nio.ByteBuffer;
|
| import java.nio.FloatBuffer;
|
|
|
| /**
|
| - * Class for converting OES textures to a YUV ByteBuffer.
|
| + * Class for converting OES textures to a YUV ByteBuffer. It should be constructed on a thread with
|
| + * an active EGL context, and only be used from that thread.
|
| */
|
| class YuvConverter {
|
| - private final EglBase eglBase;
|
| - private final GlShader shader;
|
| - private boolean released = false;
|
| -
|
| // Vertex coordinates in Normalized Device Coordinates, i.e.
|
| // (-1, -1) is bottom-left and (1, 1) is top-right.
|
| private static final FloatBuffer DEVICE_RECTANGLE = GlUtil.createFloatBuffer(new float[] {
|
| @@ -83,14 +80,40 @@ class YuvConverter {
|
| + "}\n";
|
| // clang-format on
|
|
|
| - private int texMatrixLoc;
|
| - private int xUnitLoc;
|
| - private int coeffsLoc;
|
| + private final int frameBufferId;
|
| + private final int frameTextureId;
|
| + private final GlShader shader;
|
| + private final int texMatrixLoc;
|
| + private final int xUnitLoc;
|
| + private final int coeffsLoc;
|
| + private final ThreadUtils.ThreadChecker threadChecker = new ThreadUtils.ThreadChecker();
|
| + private int frameBufferWidth;
|
| + private int frameBufferHeight;
|
| + private boolean released = false;
|
| +
|
| + /**
|
| + * This class should be constructed on a thread that has an active EGL context.
|
| + */
|
| + public YuvConverter() {
|
| + threadChecker.checkIsOnValidThread();
|
| + frameTextureId = GlUtil.generateTexture(GLES20.GL_TEXTURE_2D);
|
| + this.frameBufferWidth = 0;
|
| + this.frameBufferHeight = 0;
|
|
|
| - public YuvConverter(EglBase.Context sharedContext) {
|
| - eglBase = EglBase.create(sharedContext, EglBase.CONFIG_PIXEL_RGBA_BUFFER);
|
| - eglBase.createDummyPbufferSurface();
|
| - eglBase.makeCurrent();
|
| + // Create framebuffer object and bind it.
|
| + final int frameBuffers[] = new int[1];
|
| + GLES20.glGenFramebuffers(1, frameBuffers, 0);
|
| + frameBufferId = frameBuffers[0];
|
| + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBufferId);
|
| + GlUtil.checkNoGLES2Error("Generate framebuffer");
|
| +
|
| + // Attach the texture to the framebuffer as color attachment.
|
| + GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
|
| + GLES20.GL_TEXTURE_2D, frameTextureId, 0);
|
| + GlUtil.checkNoGLES2Error("Attach texture to framebuffer");
|
| +
|
| + // Restore normal framebuffer.
|
| + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
|
|
|
| shader = new GlShader(VERTEX_SHADER, FRAGMENT_SHADER);
|
| shader.useProgram();
|
| @@ -104,11 +127,11 @@ class YuvConverter {
|
| // If the width is not a multiple of 4 pixels, the texture
|
| // will be scaled up slightly and clipped at the right border.
|
| shader.setVertexAttribArray("in_tc", 2, TEXTURE_RECTANGLE);
|
| - eglBase.detachCurrent();
|
| }
|
|
|
| - synchronized public void convert(
|
| - ByteBuffer buf, int width, int height, int stride, int textureId, float[] transformMatrix) {
|
| + public void convert(ByteBuffer buf, int width, int height, int stride, int srcTextureId,
|
| + float[] transformMatrix) {
|
| + threadChecker.checkIsOnValidThread();
|
| if (released) {
|
| throw new IllegalStateException("YuvConverter.convert called on released object");
|
| }
|
| @@ -163,20 +186,28 @@ class YuvConverter {
|
| transformMatrix =
|
| RendererCommon.multiplyMatrices(transformMatrix, RendererCommon.verticalFlipMatrix());
|
|
|
| - // Create new pBuffferSurface with the correct size if needed.
|
| - if (eglBase.hasSurface()) {
|
| - if (eglBase.surfaceWidth() != stride / 4 || eglBase.surfaceHeight() != total_height) {
|
| - eglBase.releaseSurface();
|
| - eglBase.createPbufferSurface(stride / 4, total_height);
|
| + // Bind our framebuffer.
|
| + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBufferId);
|
| + GlUtil.checkNoGLES2Error("glBindFramebuffer");
|
| +
|
| + if (frameBufferWidth != stride / 4 || frameBufferHeight != total_height) {
|
| + frameBufferWidth = stride / 4;
|
| + frameBufferHeight = total_height;
|
| + // (Re)-Allocate texture.
|
| + GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
|
| + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, frameTextureId);
|
| + GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, frameBufferWidth,
|
| + frameBufferHeight, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
|
| +
|
| + // Check that the framebuffer is in a good state.
|
| + final int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
|
| + if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
|
| + throw new IllegalStateException("Framebuffer not complete, status: " + status);
|
| }
|
| - } else {
|
| - eglBase.createPbufferSurface(stride / 4, total_height);
|
| }
|
|
|
| - eglBase.makeCurrent();
|
| -
|
| GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
|
| - GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
|
| + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, srcTextureId);
|
| GLES20.glUniformMatrix4fv(texMatrixLoc, 1, false, transformMatrix, 0);
|
|
|
| // Draw Y
|
| @@ -203,20 +234,26 @@ class YuvConverter {
|
| GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
|
|
| GLES20.glReadPixels(
|
| - 0, 0, stride / 4, total_height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf);
|
| + 0, 0, frameBufferWidth, frameBufferHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf);
|
|
|
| GlUtil.checkNoGLES2Error("YuvConverter.convert");
|
|
|
| + // Restore normal framebuffer.
|
| + GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
|
| + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
|
| +
|
| // Unbind texture. Reportedly needed on some devices to get
|
| // the texture updated from the camera.
|
| GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
|
| - eglBase.detachCurrent();
|
| }
|
|
|
| - synchronized public void release() {
|
| + public void release() {
|
| + threadChecker.checkIsOnValidThread();
|
| released = true;
|
| - eglBase.makeCurrent();
|
| shader.release();
|
| - eglBase.release();
|
| + GLES20.glDeleteTextures(1, new int[] {frameTextureId}, 0);
|
| + GLES20.glDeleteFramebuffers(1, new int[] {frameBufferId}, 0);
|
| + frameBufferWidth = 0;
|
| + frameBufferHeight = 0;
|
| }
|
| }
|
|
|