Java SurfaceTextureHelper: Remove support for external thread
Currently, VideoCapturerAndroid owns a dedicated tread, and
SurfaceTextureHelper get this thread passed in the ctor. In
VideoCapturerAndroid.dispose(), ownership of the thread is passed to
SurfaceTextureHelper so that we can return directly instead of waiting
for the last frame to return.
This CL makes the SurfaceTextureHelper own the thread the whole time
instead, and VideoCapturerAndroid calls getHandler() to get it instead.
BUG=webrtc:5519
Review URL: https://codereview.webrtc.org/1738123002
Cr-Commit-Position: refs/heads/master@{#11790}
diff --git a/webrtc/api/androidtests/src/org/webrtc/SurfaceTextureHelperTest.java b/webrtc/api/androidtests/src/org/webrtc/SurfaceTextureHelperTest.java
index e6a788f..f2a8a6f 100644
--- a/webrtc/api/androidtests/src/org/webrtc/SurfaceTextureHelperTest.java
+++ b/webrtc/api/androidtests/src/org/webrtc/SurfaceTextureHelperTest.java
@@ -11,8 +11,6 @@
import android.graphics.SurfaceTexture;
import android.opengl.GLES20;
-import android.os.Handler;
-import android.os.HandlerThread;
import android.os.SystemClock;
import android.test.ActivityTestCase;
import android.test.suitebuilder.annotation.MediumTest;
@@ -271,78 +269,6 @@
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();
- final Handler handler = new Handler(thread.getLooper());
-
- // Create SurfaceTextureHelper and listener.
- final SurfaceTextureHelper surfaceTextureHelper =
- SurfaceTextureHelper.create(null, handler);
- // 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 = EglBase.create(null, EglBase.CONFIG_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(handler);
- }
-
- /**
- * Test use SurfaceTextureHelper on a separate thread. A uniform texture frame is created and
- * received on a thread separate from the test thread and returned after disconnect.
- */
- @MediumTest
- public static void testLateReturnFrameOnSeparateThread() throws InterruptedException {
- final HandlerThread thread = new HandlerThread("SurfaceTextureHelperTestThread");
- thread.start();
- final Handler handler = new Handler(thread.getLooper());
-
- // Create SurfaceTextureHelper and listener.
- final SurfaceTextureHelper surfaceTextureHelper =
- SurfaceTextureHelper.create(null, handler);
- // 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 = EglBase.create(null, EglBase.CONFIG_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();
-
- surfaceTextureHelper.disconnect(handler);
-
- surfaceTextureHelper.returnTextureFrame();
- }
-
@MediumTest
public static void testTexturetoYUV() throws InterruptedException {
final int width = 16;
diff --git a/webrtc/api/java/android/org/webrtc/SurfaceTextureHelper.java b/webrtc/api/java/android/org/webrtc/SurfaceTextureHelper.java
index 4b1dda6..1781047 100644
--- a/webrtc/api/java/android/org/webrtc/SurfaceTextureHelper.java
+++ b/webrtc/api/java/android/org/webrtc/SurfaceTextureHelper.java
@@ -48,32 +48,23 @@
int oesTextureId, float[] transformMatrix, long timestampNs);
}
- public static SurfaceTextureHelper create(EglBase.Context sharedContext) {
- return create(sharedContext, null);
- }
-
/**
- * Construct a new SurfaceTextureHelper sharing OpenGL resources with |sharedContext|. If
- * |handler| is non-null, the callback will be executed on that handler's thread. If |handler| is
- * null, a dedicated private thread is created for the callbacks.
+ * Construct a new SurfaceTextureHelper sharing OpenGL resources with |sharedContext|. A dedicated
+ * thread and handler is created for handling the SurfaceTexture.
*/
- public static SurfaceTextureHelper create(final EglBase.Context sharedContext,
- final Handler handler) {
- final Handler finalHandler;
- if (handler != null) {
- finalHandler = handler;
- } else {
- final HandlerThread thread = new HandlerThread(TAG);
- thread.start();
- finalHandler = new Handler(thread.getLooper());
- }
+ public static SurfaceTextureHelper create(final EglBase.Context sharedContext) {
+ final HandlerThread thread = new HandlerThread(TAG);
+ thread.start();
+ final Handler handler = new Handler(thread.getLooper());
+
// The onFrameAvailable() callback will be executed on the SurfaceTexture ctor thread. See:
// http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/graphics/SurfaceTexture.java#195.
// Therefore, in order to control the callback thread on API lvl < 21, the SurfaceTextureHelper
// is constructed on the |handler| thread.
- return ThreadUtils.invokeUninterruptibly(finalHandler, new Callable<SurfaceTextureHelper>() {
- @Override public SurfaceTextureHelper call() {
- return new SurfaceTextureHelper(sharedContext, finalHandler, (handler == null));
+ return ThreadUtils.invokeUninterruptibly(handler, new Callable<SurfaceTextureHelper>() {
+ @Override
+ public SurfaceTextureHelper call() {
+ return new SurfaceTextureHelper(sharedContext, handler);
}
});
}
@@ -291,7 +282,6 @@
}
private final Handler handler;
- private boolean isOwningThread;
private final EglBase eglBase;
private final SurfaceTexture surfaceTexture;
private final int oesTextureId;
@@ -303,13 +293,11 @@
private volatile boolean isTextureInUse = false;
private boolean isQuitting = false;
- private SurfaceTextureHelper(EglBase.Context sharedContext,
- Handler handler, boolean isOwningThread) {
+ private SurfaceTextureHelper(EglBase.Context sharedContext, Handler handler) {
if (handler.getLooper().getThread() != Thread.currentThread()) {
throw new IllegalStateException("SurfaceTextureHelper must be created on the handler thread");
}
this.handler = handler;
- this.isOwningThread = isOwningThread;
eglBase = EglBase.create(sharedContext, EglBase.CONFIG_PIXEL_BUFFER);
eglBase.createDummyPbufferSurface();
@@ -358,6 +346,14 @@
}
/**
+ * Retrieve the handler that calls onTextureFrameAvailable(). This handler is valid until
+ * disconnect() is called.
+ */
+ public Handler getHandler() {
+ return handler;
+ }
+
+ /**
* Call this function to signal that you are done with the frame received in
* onTextureFrameAvailable(). Only one texture frame can be in flight at once, so you must call
* this function in order to receive a new frame.
@@ -380,14 +376,11 @@
}
/**
- * Call disconnect() to stop receiving frames. Resources are released when the texture frame has
- * been returned by a call to returnTextureFrame(). You are guaranteed to not receive any more
- * onTextureFrameAvailable() after this function returns.
+ * Call disconnect() to stop receiving frames. OpenGL resources are released and the handler is
+ * stopped when the texture frame has been returned by a call to returnTextureFrame(). You are
+ * guaranteed to not receive any more onTextureFrameAvailable() after this function returns.
*/
public void disconnect() {
- if (!isOwningThread) {
- throw new IllegalStateException("Must call disconnect(handler).");
- }
if (handler.getLooper().getThread() == Thread.currentThread()) {
isQuitting = true;
if (!isTextureInUse) {
@@ -408,20 +401,6 @@
ThreadUtils.awaitUninterruptibly(barrier);
}
- /**
- * Call disconnect() to stop receiving frames and quit the looper used by |handler|.
- * Resources are released when the texture frame has been returned by a call to
- * returnTextureFrame(). You are guaranteed to not receive any more
- * onTextureFrameAvailable() after this function returns.
- */
- public void disconnect(Handler handler) {
- if (this.handler != handler) {
- throw new IllegalStateException("Wrong handler.");
- }
- isOwningThread = true;
- disconnect();
- }
-
public void textureToYUV(ByteBuffer buf,
int width, int height, int stride, int textureId, float [] transformMatrix) {
if (textureId != oesTextureId)
diff --git a/webrtc/api/java/android/org/webrtc/VideoCapturerAndroid.java b/webrtc/api/java/android/org/webrtc/VideoCapturerAndroid.java
index f0536c6..a696805 100644
--- a/webrtc/api/java/android/org/webrtc/VideoCapturerAndroid.java
+++ b/webrtc/api/java/android/org/webrtc/VideoCapturerAndroid.java
@@ -12,7 +12,6 @@
import android.content.Context;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.SystemClock;
import android.view.Surface;
import android.view.WindowManager;
@@ -53,7 +52,7 @@
private final static int CAMERA_FREEZE_REPORT_TIMOUT_MS = 6000;
private android.hardware.Camera camera; // Only non-null while capturing.
- private HandlerThread cameraThread;
+ private Thread cameraThread;
private final Handler cameraThreadHandler;
private Context applicationContext;
// Synchronization lock for |id|.
@@ -302,12 +301,11 @@
EglBase.Context sharedContext) {
this.id = cameraId;
this.eventsHandler = eventsHandler;
- cameraThread = new HandlerThread(TAG);
- cameraThread.start();
- cameraThreadHandler = new Handler(cameraThread.getLooper());
isCapturingToTexture = (sharedContext != null);
cameraStatistics = new CameraStatistics();
- surfaceHelper = SurfaceTextureHelper.create(sharedContext, cameraThreadHandler);
+ surfaceHelper = SurfaceTextureHelper.create(sharedContext);
+ cameraThreadHandler = surfaceHelper.getHandler();
+ cameraThread = cameraThreadHandler.getLooper().getThread();
if (isCapturingToTexture) {
surfaceHelper.setListener(this);
}
@@ -354,11 +352,11 @@
}
}
});
- surfaceHelper.disconnect(cameraThreadHandler);
+ surfaceHelper.disconnect();
cameraThread = null;
}
- // Used for testing purposes to check if release() has been called.
+ // Used for testing purposes to check if dispose() has been called.
public boolean isDisposed() {
return (cameraThread == null);
}