diff --git a/webrtc/api/androidtests/src/org/webrtc/SurfaceTextureHelperTest.java b/webrtc/api/androidtests/src/org/webrtc/SurfaceTextureHelperTest.java index a2a3391709..fc66386493 100644 --- a/webrtc/api/androidtests/src/org/webrtc/SurfaceTextureHelperTest.java +++ b/webrtc/api/androidtests/src/org/webrtc/SurfaceTextureHelperTest.java @@ -272,17 +272,6 @@ public final class SurfaceTextureHelperTest extends ActivityTestCase { surfaceTextureHelper.dispose(); } - // Helper method to call stopListening() on correct thread. - private static void stopListeningOnHandlerThread(final SurfaceTextureHelper surfaceTextureHelper) - throws InterruptedException { - ThreadUtils.invokeUninterruptibly(surfaceTextureHelper.getHandler(), new Runnable() { - @Override - public void run() { - surfaceTextureHelper.stopListening(); - } - }); - } - /** * Call stopListening(), but keep trying to produce more texture frames. No frames should be * delivered to the listener. @@ -308,7 +297,7 @@ public final class SurfaceTextureHelperTest extends ActivityTestCase { surfaceTextureHelper.returnTextureFrame(); // Stop listening - we should not receive any textures after this. - stopListeningOnHandlerThread(surfaceTextureHelper); + surfaceTextureHelper.stopListening(); // Draw one frame. GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); @@ -330,7 +319,7 @@ public final class SurfaceTextureHelperTest extends ActivityTestCase { "SurfaceTextureHelper test" /* threadName */, null); final MockTextureListener listener = new MockTextureListener(); surfaceTextureHelper.startListening(listener); - stopListeningOnHandlerThread(surfaceTextureHelper); + surfaceTextureHelper.stopListening(); surfaceTextureHelper.dispose(); } @@ -362,10 +351,13 @@ public final class SurfaceTextureHelperTest extends ActivityTestCase { stopListeningBarrier.countDown(); stopListeningBarrierDone.await(); // Wait until handler thread is idle to try to catch late startListening() call. - ThreadUtils.invokeUninterruptibly(surfaceTextureHelper.getHandler(), new Runnable() { - @Override - public void run() {} + final CountDownLatch barrier = new CountDownLatch(1); + surfaceTextureHelper.getHandler().post(new Runnable() { + @Override public void run() { + barrier.countDown(); + } }); + ThreadUtils.awaitUninterruptibly(barrier); // Previous startListening() call should never have taken place and it should be ok to call it // again. surfaceTextureHelper.startListening(listener); @@ -397,7 +389,7 @@ public final class SurfaceTextureHelperTest extends ActivityTestCase { surfaceTextureHelper.returnTextureFrame(); // Stop listening - |listener1| should not receive any textures after this. - stopListeningOnHandlerThread(surfaceTextureHelper); + surfaceTextureHelper.stopListening(); // Connect different listener. final MockTextureListener listener2 = new MockTextureListener(); diff --git a/webrtc/api/java/android/org/webrtc/SurfaceTextureHelper.java b/webrtc/api/java/android/org/webrtc/SurfaceTextureHelper.java index ef43c5e4dc..5677209a98 100644 --- a/webrtc/api/java/android/org/webrtc/SurfaceTextureHelper.java +++ b/webrtc/api/java/android/org/webrtc/SurfaceTextureHelper.java @@ -63,7 +63,7 @@ class SurfaceTextureHelper { // 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(handler, new Callable() { + return ThreadUtils.invokeAtFrontUninterruptibly(handler, new Callable() { @Override public SurfaceTextureHelper call() { try { @@ -373,17 +373,18 @@ class SurfaceTextureHelper { /** * Stop listening. The listener set in startListening() is guaranteded to not receive any more - * onTextureFrameAvailable() callbacks after this function returns. This function must be called - * on the getHandler() thread. + * onTextureFrameAvailable() callbacks after this function returns. */ public void stopListening() { - if (handler.getLooper().getThread() != Thread.currentThread()) { - throw new IllegalStateException("Wrong thread."); - } Logging.d(TAG, "stopListening()"); handler.removeCallbacks(setListenerRunnable); - this.listener = null; - this.pendingListener = null; + ThreadUtils.invokeAtFrontUninterruptibly(handler, new Runnable() { + @Override + public void run() { + listener = null; + pendingListener = null; + } + }); } /** @@ -431,24 +432,15 @@ class SurfaceTextureHelper { */ public void dispose() { Logging.d(TAG, "dispose()"); - if (handler.getLooper().getThread() == Thread.currentThread()) { - isQuitting = true; - if (!isTextureInUse) { - release(); - } - return; - } - final CountDownLatch barrier = new CountDownLatch(1); - handler.postAtFrontOfQueue(new Runnable() { - @Override public void run() { + ThreadUtils.invokeAtFrontUninterruptibly(handler, new Runnable() { + @Override + public void run() { isQuitting = true; - barrier.countDown(); if (!isTextureInUse) { release(); } } }); - ThreadUtils.awaitUninterruptibly(barrier); } public void textureToYUV(ByteBuffer buf, diff --git a/webrtc/api/java/jni/androidvideocapturer_jni.cc b/webrtc/api/java/jni/androidvideocapturer_jni.cc index bc85cc4146..3f79f3a321 100644 --- a/webrtc/api/java/jni/androidvideocapturer_jni.cc +++ b/webrtc/api/java/jni/androidvideocapturer_jni.cc @@ -53,13 +53,6 @@ AndroidVideoCapturerJni::~AndroidVideoCapturerJni() { *j_video_capturer_, GetMethodID(jni(), *j_video_capturer_class_, "dispose", "()V")); CHECK_EXCEPTION(jni()) << "error during VideoCapturer.dispose()"; - if (surface_texture_helper_) { - jni()->CallVoidMethod( - surface_texture_helper_->GetJavaSurfaceTextureHelper(), - GetMethodID(jni(), FindClass(jni(), "org/webrtc/SurfaceTextureHelper"), - "dispose", "()V")); - } - CHECK_EXCEPTION(jni()) << "error during SurfaceTextureHelper.dispose()"; } void AndroidVideoCapturerJni::Start(int width, int height, int framerate, diff --git a/webrtc/api/java/jni/surfacetexturehelper_jni.cc b/webrtc/api/java/jni/surfacetexturehelper_jni.cc index 04623a791d..0cc0d793f9 100644 --- a/webrtc/api/java/jni/surfacetexturehelper_jni.cc +++ b/webrtc/api/java/jni/surfacetexturehelper_jni.cc @@ -14,6 +14,7 @@ #include "webrtc/api/java/jni/classreferenceholder.h" #include "webrtc/base/bind.h" #include "webrtc/base/checks.h" +#include "webrtc/base/logging.h" namespace webrtc_jni { @@ -48,6 +49,14 @@ SurfaceTextureHelper::SurfaceTextureHelper(JNIEnv* jni, } SurfaceTextureHelper::~SurfaceTextureHelper() { + LOG(LS_INFO) << "SurfaceTextureHelper dtor"; + JNIEnv* jni = AttachCurrentThreadIfNeeded(); + jni->CallVoidMethod( + *j_surface_texture_helper_, + GetMethodID(jni, FindClass(jni, "org/webrtc/SurfaceTextureHelper"), + "dispose", "()V")); + + CHECK_EXCEPTION(jni) << "error during SurfaceTextureHelper.dispose()"; } jobject SurfaceTextureHelper::GetJavaSurfaceTextureHelper() const { diff --git a/webrtc/api/java/jni/surfacetexturehelper_jni.h b/webrtc/api/java/jni/surfacetexturehelper_jni.h index 6bee728d31..1d8bafc99e 100644 --- a/webrtc/api/java/jni/surfacetexturehelper_jni.h +++ b/webrtc/api/java/jni/surfacetexturehelper_jni.h @@ -30,6 +30,8 @@ namespace webrtc_jni { // SurfaceTextureHelper is reference counted to make sure that it is not // destroyed while a VideoFrameBuffer is in use. // This class is the C++ counterpart of the java class SurfaceTextureHelper. +// It owns the corresponding java object, and calls the java dispose +// method when destroyed. // Usage: // 1. Create an instance of this class. // 2. Get the Java SurfaceTextureHelper with GetJavaSurfaceTextureHelper(). diff --git a/webrtc/api/java/src/org/webrtc/MediaCodecVideoDecoder.java b/webrtc/api/java/src/org/webrtc/MediaCodecVideoDecoder.java index 92a678976d..e8470c408f 100644 --- a/webrtc/api/java/src/org/webrtc/MediaCodecVideoDecoder.java +++ b/webrtc/api/java/src/org/webrtc/MediaCodecVideoDecoder.java @@ -547,10 +547,10 @@ public class MediaCodecVideoDecoder { } public void release() { - // SurfaceTextureHelper.dispose() will block until any onTextureFrameAvailable() in - // progress is done. Therefore, the call to dispose() must be outside any synchronized + // SurfaceTextureHelper.stopListening() will block until any onTextureFrameAvailable() in + // progress is done. Therefore, the call must be outside any synchronized // statement that is also used in the onTextureFrameAvailable() above to avoid deadlocks. - surfaceTextureHelper.dispose(); + surfaceTextureHelper.stopListening(); synchronized (newFrameLock) { if (renderedBuffer != null) { surfaceTextureHelper.returnTextureFrame(); diff --git a/webrtc/base/java/src/org/webrtc/ThreadUtils.java b/webrtc/base/java/src/org/webrtc/ThreadUtils.java index 8eeebc84dc..95d41ba563 100644 --- a/webrtc/base/java/src/org/webrtc/ThreadUtils.java +++ b/webrtc/base/java/src/org/webrtc/ThreadUtils.java @@ -139,7 +139,20 @@ public class ThreadUtils { /** * Post |callable| to |handler| and wait for the result. */ - public static V invokeUninterruptibly(final Handler handler, final Callable callable) { + public static V invokeAtFrontUninterruptibly( + final Handler handler, final Callable callable) { + if (handler.getLooper().getThread() == Thread.currentThread()) { + V value; + try { + value = callable.call(); + } catch (Exception e) { + final RuntimeException runtimeException = + new RuntimeException("Callable threw exception: " + e); + runtimeException.setStackTrace(e.getStackTrace()); + throw runtimeException; + } + return value; + } class Result { public V value; } @@ -163,13 +176,19 @@ public class ThreadUtils { } /** - * Post |runner| to |handler| and wait for the result. + * Post |runner| to |handler|, at the front, and wait for + * completion. */ - public static void invokeUninterruptibly(final Handler handler, final Runnable runner) { + public static void invokeAtFrontUninterruptibly(final Handler handler, final Runnable runner) { + if (handler.getLooper().getThread() == Thread.currentThread()) { + runner.run(); + return; + } final CountDownLatch barrier = new CountDownLatch(1); - handler.post(new Runnable() { - @Override public void run() { - runner.run(); + handler.postAtFrontOfQueue(new Runnable() { + @Override + public void run() { + runner.run(); barrier.countDown(); } });