SurfaceTextureHelper fixes
Fixed a problem where eglBase.makecurrent() could be called after the context had been released if SurfaceTextureHelper was first created and immedately disconnected. Add the possibility to inject a thread to use instead of creating a new. BUG= webrtc:4993 R=magjed@webrtc.org Review URL: https://codereview.webrtc.org/1384923002 . Cr-Commit-Position: refs/heads/master@{#10174}
This commit is contained in:
parent
418503275c
commit
1b33da1298
@ -28,10 +28,11 @@ package org.webrtc;
|
||||
|
||||
import android.test.ActivityTestCase;
|
||||
import android.test.suitebuilder.annotation.MediumTest;
|
||||
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.opengl.EGL14;
|
||||
import android.opengl.GLES20;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
@ -45,10 +46,23 @@ public final class SurfaceTextureHelperTest extends ActivityTestCase {
|
||||
public int oesTextureId;
|
||||
public float[] transformMatrix;
|
||||
private boolean hasNewFrame = false;
|
||||
// Thread where frames are expected to be received on.
|
||||
private final Thread expectedThread;
|
||||
|
||||
MockTextureListener() {
|
||||
this.expectedThread = null;
|
||||
}
|
||||
|
||||
MockTextureListener(Thread expectedThread) {
|
||||
this.expectedThread = expectedThread;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onTextureFrameAvailable(
|
||||
int oesTextureId, float[] transformMatrix, long timestampNs) {
|
||||
if (expectedThread != null && Thread.currentThread() != expectedThread) {
|
||||
throw new IllegalStateException("onTextureFrameAvailable called on wrong thread.");
|
||||
}
|
||||
this.oesTextureId = oesTextureId;
|
||||
this.transformMatrix = transformMatrix;
|
||||
hasNewFrame = true;
|
||||
@ -252,4 +266,51 @@ public final class SurfaceTextureHelperTest extends ActivityTestCase {
|
||||
|
||||
eglBase.release();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test disconnecting the SurfaceTextureHelper immediately after is has been setup to use a
|
||||
* shared context. No frames should be delivered to the listener.
|
||||
*/
|
||||
@SmallTest
|
||||
public static void testDisconnectImmediately() {
|
||||
final SurfaceTextureHelper surfaceTextureHelper =
|
||||
new SurfaceTextureHelper(EGL14.EGL_NO_CONTEXT);
|
||||
surfaceTextureHelper.disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test use SurfaceTextureHelper on a separate thread. A uniform texture frame is created and
|
||||
* received on a thread separate from the test thread.
|
||||
*/
|
||||
@MediumTest
|
||||
public static void testFrameOnSeparateThread() throws InterruptedException {
|
||||
final HandlerThread thread = new HandlerThread("SurfaceTextureHelperTestThread");
|
||||
thread.start();
|
||||
|
||||
// Create SurfaceTextureHelper and listener.
|
||||
final SurfaceTextureHelper surfaceTextureHelper =
|
||||
new SurfaceTextureHelper(EGL14.EGL_NO_CONTEXT, thread);
|
||||
// Create a mock listener and expect frames to be delivered on |thread|.
|
||||
final MockTextureListener listener = new MockTextureListener(thread);
|
||||
surfaceTextureHelper.setListener(listener);
|
||||
|
||||
// Create resources for stubbing an OES texture producer. |eglOesBase| has the
|
||||
// SurfaceTexture in |surfaceTextureHelper| as the target EGLSurface.
|
||||
final EglBase eglOesBase = new EglBase(EGL14.EGL_NO_CONTEXT, EglBase.ConfigType.PLAIN);
|
||||
eglOesBase.createSurface(surfaceTextureHelper.getSurfaceTexture());
|
||||
eglOesBase.makeCurrent();
|
||||
// Draw a frame onto the SurfaceTexture.
|
||||
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
||||
// swapBuffers() will ultimately trigger onTextureFrameAvailable().
|
||||
eglOesBase.swapBuffers();
|
||||
eglOesBase.release();
|
||||
|
||||
// Wait for an OES texture to arrive.
|
||||
listener.waitForNewFrame();
|
||||
|
||||
// Return the frame from this thread.
|
||||
surfaceTextureHelper.returnTextureFrame();
|
||||
surfaceTextureHelper.disconnect();
|
||||
thread.quitSafely();
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,6 +65,7 @@ final class SurfaceTextureHelper {
|
||||
|
||||
private final HandlerThread thread;
|
||||
private final Handler handler;
|
||||
private final boolean isOwningThread;
|
||||
private final EglBase eglBase;
|
||||
private final SurfaceTexture surfaceTexture;
|
||||
private final int oesTextureId;
|
||||
@ -78,9 +79,19 @@ final class SurfaceTextureHelper {
|
||||
* Construct a new SurfaceTextureHelper sharing OpenGL resources with |sharedContext|.
|
||||
*/
|
||||
public SurfaceTextureHelper(EGLContext sharedContext) {
|
||||
thread = new HandlerThread(TAG);
|
||||
thread.start();
|
||||
handler = new Handler(thread.getLooper());
|
||||
this(sharedContext, null);
|
||||
}
|
||||
|
||||
public SurfaceTextureHelper(EGLContext sharedContext, HandlerThread thread) {
|
||||
if (thread == null) {
|
||||
this.thread = new HandlerThread(TAG);
|
||||
this.thread.start();
|
||||
this.isOwningThread = true;
|
||||
} else {
|
||||
this.thread = thread;
|
||||
this.isOwningThread = false;
|
||||
}
|
||||
this.handler = new Handler(this.thread.getLooper());
|
||||
|
||||
eglBase = new EglBase(sharedContext, EglBase.ConfigType.PIXEL_BUFFER);
|
||||
eglBase.createDummyPbufferSurface();
|
||||
@ -89,13 +100,8 @@ final class SurfaceTextureHelper {
|
||||
oesTextureId = GlUtil.generateTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES);
|
||||
surfaceTexture = new SurfaceTexture(oesTextureId);
|
||||
|
||||
// Reattach EGL context to private thread.
|
||||
// The EGL context will be re-attached to the private thread.
|
||||
eglBase.detachCurrent();
|
||||
handler.post(new Runnable() {
|
||||
@Override public void run() {
|
||||
eglBase.makeCurrent();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -148,6 +154,13 @@ final class SurfaceTextureHelper {
|
||||
* onTextureFrameAvailable() after this function returns.
|
||||
*/
|
||||
public void disconnect() {
|
||||
if (Thread.currentThread() == thread) {
|
||||
isQuitting = true;
|
||||
if (!isTextureInUse) {
|
||||
release();
|
||||
}
|
||||
return;
|
||||
}
|
||||
final CountDownLatch barrier = new CountDownLatch(1);
|
||||
handler.postAtFrontOfQueue(new Runnable() {
|
||||
@Override public void run() {
|
||||
@ -171,7 +184,9 @@ final class SurfaceTextureHelper {
|
||||
isTextureInUse = true;
|
||||
hasPendingTexture = false;
|
||||
|
||||
eglBase.makeCurrent();
|
||||
surfaceTexture.updateTexImage();
|
||||
|
||||
final float[] transformMatrix = new float[16];
|
||||
surfaceTexture.getTransformMatrix(transformMatrix);
|
||||
final long timestampNs = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
@ -187,9 +202,12 @@ final class SurfaceTextureHelper {
|
||||
if (isTextureInUse || !isQuitting) {
|
||||
throw new IllegalStateException("Unexpected release.");
|
||||
}
|
||||
eglBase.makeCurrent();
|
||||
GLES20.glDeleteTextures(1, new int[] {oesTextureId}, 0);
|
||||
surfaceTexture.release();
|
||||
eglBase.release();
|
||||
thread.quitSafely();
|
||||
if (isOwningThread) {
|
||||
thread.quitSafely();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user