From 4382d800d225a3d6ce93011ff9a573e4ff613f35 Mon Sep 17 00:00:00 2001 From: Magnus Jedvert Date: Thu, 8 Oct 2015 14:45:34 +0200 Subject: [PATCH] Android SurfaceViewRenderer: Allow to re-init after release() has been called This CL makes a thorough reset of all variables in release() and clears the last rendered image so that the SurfaceViewRenderer object can be reinitialized with init() and work properly. This CL also removes an implicit assumption that init() is called before surfaceCreated() - now they can be called in any order. BUG=webrtc:4742 R=hbos@webrtc.org Review URL: https://codereview.webrtc.org/1389203003 . Cr-Commit-Position: refs/heads/master@{#10217} --- .../org/webrtc/SurfaceViewRenderer.java | 79 ++++++++++++++----- 1 file changed, 60 insertions(+), 19 deletions(-) diff --git a/talk/app/webrtc/java/android/org/webrtc/SurfaceViewRenderer.java b/talk/app/webrtc/java/android/org/webrtc/SurfaceViewRenderer.java index b4e687f79e..28404c9d6f 100644 --- a/talk/app/webrtc/java/android/org/webrtc/SurfaceViewRenderer.java +++ b/talk/app/webrtc/java/android/org/webrtc/SurfaceViewRenderer.java @@ -91,6 +91,8 @@ public class SurfaceViewRenderer extends SurfaceView // layout and surface size. private int surfaceWidth; private int surfaceHeight; + // |isSurfaceCreated| keeps track of the current status in surfaceCreated()/surfaceDestroyed(). + private boolean isSurfaceCreated; // Last rendered frame dimensions, or 0 if no frame has been rendered yet. private int frameWidth; private int frameHeight; @@ -128,6 +130,7 @@ public class SurfaceViewRenderer extends SurfaceView */ public SurfaceViewRenderer(Context context) { super(context); + getHolder().addCallback(this); } /** @@ -135,24 +138,48 @@ public class SurfaceViewRenderer extends SurfaceView */ public SurfaceViewRenderer(Context context, AttributeSet attrs) { super(context, attrs); + getHolder().addCallback(this); } /** - * Initialize this class, sharing resources with |sharedContext|. + * Initialize this class, sharing resources with |sharedContext|. It is allowed to call init() to + * reinitialize the renderer after a previous init()/release() cycle. */ public void init( EGLContext sharedContext, RendererCommon.RendererEvents rendererEvents) { - if (renderThreadHandler != null) { - throw new IllegalStateException("Already initialized"); + synchronized (handlerLock) { + if (renderThreadHandler != null) { + throw new IllegalStateException("Already initialized"); + } + Logging.d(TAG, "Initializing"); + this.rendererEvents = rendererEvents; + renderThread = new HandlerThread(TAG); + renderThread.start(); + drawer = new GlRectDrawer(); + eglBase = new EglBase(sharedContext, EglBase.ConfigType.PLAIN); + renderThreadHandler = new Handler(renderThread.getLooper()); } - Logging.d(TAG, "Initializing"); - this.rendererEvents = rendererEvents; - renderThread = new HandlerThread(TAG); - renderThread.start(); - renderThreadHandler = new Handler(renderThread.getLooper()); - eglBase = new EglBase(sharedContext, EglBase.ConfigType.PLAIN); - drawer = new GlRectDrawer(); - getHolder().addCallback(this); + tryCreateEglSurface(); + } + + /** + * Create and make an EGLSurface current if both init() and surfaceCreated() have been called. + */ + public void tryCreateEglSurface() { + // |renderThreadHandler| is only created after |eglBase| is created in init(), so the + // following code will only execute if eglBase != null. + runOnRenderThread(new Runnable() { + @Override public void run() { + synchronized (layoutLock) { + if (isSurfaceCreated) { + eglBase.createSurface(getHolder().getSurface()); + eglBase.makeCurrent(); + // Necessary for YUV frames with odd width. + GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1); + } + } + } + }); } /** @@ -179,6 +206,9 @@ public class SurfaceViewRenderer extends SurfaceView GLES20.glDeleteTextures(3, yuvTextures, 0); yuvTextures = null; } + // Clear last rendered image to black. + GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); + eglBase.swapBuffers(); eglBase.release(); eglBase = null; } @@ -197,6 +227,20 @@ public class SurfaceViewRenderer extends SurfaceView // The |renderThread| cleanup is not safe to cancel and we need to wait until it's done. ThreadUtils.joinUninterruptibly(renderThread); renderThread = null; + // Reset statistics and event reporting. + synchronized (layoutLock) { + frameWidth = 0; + frameHeight = 0; + frameRotation = 0; + rendererEvents = null; + } + synchronized (statisticsLock) { + framesReceived = 0; + framesDropped = 0; + framesRendered = 0; + firstFrameTimeNs = 0; + renderTimeNs = 0; + } } /** @@ -286,20 +330,17 @@ public class SurfaceViewRenderer extends SurfaceView @Override public void surfaceCreated(final SurfaceHolder holder) { Logging.d(TAG, "Surface created"); - runOnRenderThread(new Runnable() { - @Override public void run() { - eglBase.createSurface(holder.getSurface()); - eglBase.makeCurrent(); - // Necessary for YUV frames with odd width. - GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1); - } - }); + synchronized (layoutLock) { + isSurfaceCreated = true; + } + tryCreateEglSurface(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { Logging.d(TAG, "Surface destroyed"); synchronized (layoutLock) { + isSurfaceCreated = false; surfaceWidth = 0; surfaceHeight = 0; }