From 747c1bccd961a88285d6bfeebcec0cb25f719dfb Mon Sep 17 00:00:00 2001 From: Magnus Jedvert Date: Mon, 12 Oct 2015 09:27:48 +0200 Subject: [PATCH] Android SurfaceTextureHelper: Replace API 21 with API 11 version of setOnFrameAvailableListener() BUG=b/24809429 R=glaznev@webrtc.org, perkj@webrtc.org Review URL: https://codereview.webrtc.org/1395543004 . Cr-Commit-Position: refs/heads/master@{#10247} --- .../src/org/webrtc/GlRectDrawerTest.java | 2 +- .../org/webrtc/SurfaceTextureHelperTest.java | 12 ++-- .../org/webrtc/SurfaceTextureHelper.java | 65 ++++++++++++------- .../org/webrtc/VideoCapturerAndroid.java | 5 +- .../java/jni/surfacetexturehelper_jni.cc | 25 ++++--- 5 files changed, 62 insertions(+), 47 deletions(-) diff --git a/talk/app/webrtc/androidtests/src/org/webrtc/GlRectDrawerTest.java b/talk/app/webrtc/androidtests/src/org/webrtc/GlRectDrawerTest.java index 6fef81b908..3408e6e667 100644 --- a/talk/app/webrtc/androidtests/src/org/webrtc/GlRectDrawerTest.java +++ b/talk/app/webrtc/androidtests/src/org/webrtc/GlRectDrawerTest.java @@ -271,7 +271,7 @@ public final class GlRectDrawerTest extends ActivityTestCase { // Create resources for generating OES textures. final SurfaceTextureHelper surfaceTextureHelper = - new SurfaceTextureHelper(eglBase.getContext()); + SurfaceTextureHelper.create(eglBase.getContext()); final StubOesTextureProducer oesProducer = new StubOesTextureProducer( eglBase.getContext(), surfaceTextureHelper.getSurfaceTexture(), WIDTH, HEIGHT); final SurfaceTextureHelperTest.MockTextureListener listener = diff --git a/talk/app/webrtc/androidtests/src/org/webrtc/SurfaceTextureHelperTest.java b/talk/app/webrtc/androidtests/src/org/webrtc/SurfaceTextureHelperTest.java index 611693bbd0..2aa1719709 100644 --- a/talk/app/webrtc/androidtests/src/org/webrtc/SurfaceTextureHelperTest.java +++ b/talk/app/webrtc/androidtests/src/org/webrtc/SurfaceTextureHelperTest.java @@ -32,6 +32,7 @@ import android.test.suitebuilder.annotation.SmallTest; import android.graphics.SurfaceTexture; import android.opengl.EGL14; import android.opengl.GLES20; +import android.os.Handler; import android.os.HandlerThread; import android.os.SystemClock; @@ -113,7 +114,7 @@ public final class SurfaceTextureHelperTest extends ActivityTestCase { // Create SurfaceTextureHelper and listener. final SurfaceTextureHelper surfaceTextureHelper = - new SurfaceTextureHelper(eglBase.getContext()); + SurfaceTextureHelper.create(eglBase.getContext()); final MockTextureListener listener = new MockTextureListener(); surfaceTextureHelper.setListener(listener); surfaceTextureHelper.getSurfaceTexture().setDefaultBufferSize(width, height); @@ -179,7 +180,7 @@ public final class SurfaceTextureHelperTest extends ActivityTestCase { // Create SurfaceTextureHelper and listener. final SurfaceTextureHelper surfaceTextureHelper = - new SurfaceTextureHelper(eglBase.getContext()); + SurfaceTextureHelper.create(eglBase.getContext()); final MockTextureListener listener = new MockTextureListener(); surfaceTextureHelper.setListener(listener); surfaceTextureHelper.getSurfaceTexture().setDefaultBufferSize(width, height); @@ -238,7 +239,7 @@ public final class SurfaceTextureHelperTest extends ActivityTestCase { public static void testDisconnect() throws InterruptedException { // Create SurfaceTextureHelper and listener. final SurfaceTextureHelper surfaceTextureHelper = - new SurfaceTextureHelper(EGL14.EGL_NO_CONTEXT); + SurfaceTextureHelper.create(EGL14.EGL_NO_CONTEXT); final MockTextureListener listener = new MockTextureListener(); surfaceTextureHelper.setListener(listener); // Create EglBase with the SurfaceTexture as target EGLSurface. @@ -274,7 +275,7 @@ public final class SurfaceTextureHelperTest extends ActivityTestCase { @SmallTest public static void testDisconnectImmediately() { final SurfaceTextureHelper surfaceTextureHelper = - new SurfaceTextureHelper(EGL14.EGL_NO_CONTEXT); + SurfaceTextureHelper.create(EGL14.EGL_NO_CONTEXT); surfaceTextureHelper.disconnect(); } @@ -286,10 +287,11 @@ public final class SurfaceTextureHelperTest extends ActivityTestCase { 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 = - new SurfaceTextureHelper(EGL14.EGL_NO_CONTEXT, thread); + SurfaceTextureHelper.create(EGL14.EGL_NO_CONTEXT, handler); // Create a mock listener and expect frames to be delivered on |thread|. final MockTextureListener listener = new MockTextureListener(thread); surfaceTextureHelper.setListener(listener); diff --git a/talk/app/webrtc/java/android/org/webrtc/SurfaceTextureHelper.java b/talk/app/webrtc/java/android/org/webrtc/SurfaceTextureHelper.java index 4d5a2e62f1..175a00ed21 100644 --- a/talk/app/webrtc/java/android/org/webrtc/SurfaceTextureHelper.java +++ b/talk/app/webrtc/java/android/org/webrtc/SurfaceTextureHelper.java @@ -36,6 +36,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.SystemClock; +import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -63,7 +64,35 @@ final class SurfaceTextureHelper { int oesTextureId, float[] transformMatrix, long timestampNs); } - private final HandlerThread thread; + public static SurfaceTextureHelper create(EGLContext 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. + */ + public static SurfaceTextureHelper create(final EGLContext 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()); + } + // 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() { + @Override public SurfaceTextureHelper call() { + return new SurfaceTextureHelper(sharedContext, finalHandler, (handler == null)); + } + }); + } + private final Handler handler; private final boolean isOwningThread; private final EglBase eglBase; @@ -75,23 +104,12 @@ final class SurfaceTextureHelper { private boolean isTextureInUse = false; private boolean isQuitting = false; - /** - * Construct a new SurfaceTextureHelper sharing OpenGL resources with |sharedContext|. - */ - public SurfaceTextureHelper(EGLContext sharedContext) { - 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; + private SurfaceTextureHelper(EGLContext sharedContext, Handler handler, boolean isOwningThread) { + if (!handler.getLooper().isCurrentThread()) { + throw new IllegalStateException("SurfaceTextureHelper must be created on the handler thread"); } - this.handler = new Handler(this.thread.getLooper()); + this.handler = handler; + this.isOwningThread = isOwningThread; eglBase = new EglBase(sharedContext, EglBase.ConfigType.PIXEL_BUFFER); eglBase.createDummyPbufferSurface(); @@ -99,9 +117,6 @@ final class SurfaceTextureHelper { oesTextureId = GlUtil.generateTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES); surfaceTexture = new SurfaceTexture(oesTextureId); - - // The EGL context will be re-attached to the private thread. - eglBase.detachCurrent(); } /** @@ -119,7 +134,7 @@ final class SurfaceTextureHelper { hasPendingTexture = true; tryDeliverTextureFrame(); } - }, handler); + }); } /** @@ -154,7 +169,7 @@ final class SurfaceTextureHelper { * onTextureFrameAvailable() after this function returns. */ public void disconnect() { - if (Thread.currentThread() == thread) { + if (handler.getLooper().isCurrentThread()) { isQuitting = true; if (!isTextureInUse) { release(); @@ -175,7 +190,7 @@ final class SurfaceTextureHelper { } private void tryDeliverTextureFrame() { - if (Thread.currentThread() != thread) { + if (!handler.getLooper().isCurrentThread()) { throw new IllegalStateException("Wrong thread."); } if (isQuitting || !hasPendingTexture || isTextureInUse) { @@ -196,7 +211,7 @@ final class SurfaceTextureHelper { } private void release() { - if (Thread.currentThread() != thread) { + if (!handler.getLooper().isCurrentThread()) { throw new IllegalStateException("Wrong thread."); } if (isTextureInUse || !isQuitting) { @@ -207,7 +222,7 @@ final class SurfaceTextureHelper { surfaceTexture.release(); eglBase.release(); if (isOwningThread) { - thread.quitSafely(); + handler.getLooper().quitSafely(); } } } diff --git a/talk/app/webrtc/java/android/org/webrtc/VideoCapturerAndroid.java b/talk/app/webrtc/java/android/org/webrtc/VideoCapturerAndroid.java index 7ec86b005b..ec68ed504d 100644 --- a/talk/app/webrtc/java/android/org/webrtc/VideoCapturerAndroid.java +++ b/talk/app/webrtc/java/android/org/webrtc/VideoCapturerAndroid.java @@ -314,9 +314,8 @@ public class VideoCapturerAndroid extends VideoCapturer implements PreviewCallba cameraThread.start(); cameraThreadHandler = new Handler(cameraThread.getLooper()); videoBuffers = new FramePool(cameraThread); - surfaceHelper = - new SurfaceTextureHelper(sharedContext == null ? EGL14.EGL_NO_CONTEXT : sharedContext, - cameraThread); + surfaceHelper = SurfaceTextureHelper.create( + sharedContext == null ? EGL14.EGL_NO_CONTEXT : sharedContext, cameraThreadHandler); if (sharedContext != null) { surfaceHelper.setListener(this); } diff --git a/talk/app/webrtc/java/jni/surfacetexturehelper_jni.cc b/talk/app/webrtc/java/jni/surfacetexturehelper_jni.cc index 93dc4e64f9..d161aa4aff 100644 --- a/talk/app/webrtc/java/jni/surfacetexturehelper_jni.cc +++ b/talk/app/webrtc/java/jni/surfacetexturehelper_jni.cc @@ -64,19 +64,18 @@ SurfaceTextureHelper::SurfaceTextureHelper(JNIEnv* jni, : j_surface_texture_helper_class_( jni, FindClass(jni, "org/webrtc/SurfaceTextureHelper")), - j_surface_texture_helper_( - jni, - jni->NewObject(*j_surface_texture_helper_class_, - GetMethodID(jni, - *j_surface_texture_helper_class_, - "", - "(Landroid/opengl/EGLContext;)V"), - egl_shared_context)), - j_return_texture_method_( - GetMethodID(jni, - *j_surface_texture_helper_class_, - "returnTextureFrame", - "()V")) { + j_surface_texture_helper_(jni, jni->CallStaticObjectMethod( + *j_surface_texture_helper_class_, + GetStaticMethodID( + jni, + *j_surface_texture_helper_class_, + "create", + "(Landroid/opengl/EGLContext;)Lorg/webrtc/SurfaceTextureHelper;"), + egl_shared_context)), + j_return_texture_method_(GetMethodID(jni, + *j_surface_texture_helper_class_, + "returnTextureFrame", + "()V")) { CHECK_EXCEPTION(jni) << "error during initialization of SurfaceTextureHelper"; }