Index: talk/app/webrtc/java/android/org/webrtc/VideoRendererGui.java |
diff --git a/talk/app/webrtc/java/android/org/webrtc/VideoRendererGui.java b/talk/app/webrtc/java/android/org/webrtc/VideoRendererGui.java |
index 26285252f3a9bac244cbe09083ec0611ebd6c2e8..9dd2f02c4bd2aff331724f175259de4e071644dd 100644 |
--- a/talk/app/webrtc/java/android/org/webrtc/VideoRendererGui.java |
+++ b/talk/app/webrtc/java/android/org/webrtc/VideoRendererGui.java |
@@ -55,6 +55,8 @@ import org.webrtc.VideoRenderer.I420Frame; |
* Only one instance of the class can be created. |
*/ |
public class VideoRendererGui implements GLSurfaceView.Renderer { |
+ // |instance|, |instance.surface|, |eglContext|, and |eglContextReady| are synchronized on |
+ // |VideoRendererGui.class|. |
private static VideoRendererGui instance = null; |
private static Runnable eglContextReady = null; |
private static final String TAG = "VideoRendererGui"; |
@@ -68,7 +70,8 @@ public class VideoRendererGui implements GLSurfaceView.Renderer { |
private int screenWidth; |
private int screenHeight; |
// List of yuv renderers. |
- private ArrayList<YuvImageRenderer> yuvImageRenderers; |
+ private final ArrayList<YuvImageRenderer> yuvImageRenderers; |
+ // |drawer| is synchronized on |yuvImageRenderers|. |
private GlRectDrawer drawer; |
// The minimum fraction of the frame content that will be shown for |SCALE_ASPECT_BALANCED|. |
// This limits excessive cropping when adjusting display size. |
@@ -101,12 +104,32 @@ public class VideoRendererGui implements GLSurfaceView.Renderer { |
yuvImageRenderers = new ArrayList<YuvImageRenderer>(); |
} |
+ public static synchronized void dispose() { |
+ if (instance == null){ |
+ return; |
+ } |
+ synchronized (instance.yuvImageRenderers) { |
+ for (YuvImageRenderer yuvImageRenderer : instance.yuvImageRenderers) { |
+ yuvImageRenderer.release(); |
+ } |
+ instance.yuvImageRenderers.clear(); |
+ if (instance.drawer != null) { |
+ instance.drawer.release(); |
+ } |
+ } |
+ instance.surface = null; |
+ instance.eglContext = null; |
+ instance.eglContextReady = null; |
+ instance = null; |
+ } |
+ |
/** |
* Class used to display stream of YUV420 frames at particular location |
* on a screen. New video frames are sent to display using renderFrame() |
* call. |
*/ |
private static class YuvImageRenderer implements VideoRenderer.Callbacks { |
+ // |surface| is synchronized on |this|. |
private GLSurfaceView surface; |
private int id; |
private int[] yuvTextures = { -1, -1, -1 }; |
@@ -116,8 +139,8 @@ public class VideoRendererGui implements GLSurfaceView.Renderer { |
// an offer (writing I420Frame to render) and early-returns (recording |
// a dropped frame) if that queue is full. draw() call does a peek(), |
// copies frame to texture and then removes it from a queue using poll(). |
- LinkedBlockingQueue<I420Frame> frameToRenderQueue; |
- // Local copy of incoming video frame. |
+ private final LinkedBlockingQueue<I420Frame> frameToRenderQueue; |
+ // Local copy of incoming video frame. Synchronized on |frameToRenderQueue|. |
private I420Frame yuvFrameToRender; |
private I420Frame textureFrameToRender; |
// Type of video frame used for recent frame rendering. |
@@ -178,6 +201,15 @@ public class VideoRendererGui implements GLSurfaceView.Renderer { |
rotationDegree = 0; |
} |
+ private synchronized void release() { |
+ surface = null; |
+ synchronized (frameToRenderQueue) { |
+ frameToRenderQueue.clear(); |
+ yuvFrameToRender = null; |
+ textureFrameToRender = null; |
+ } |
+ } |
+ |
private void createTextures() { |
Log.d(TAG, " YuvImageRenderer.createTextures " + id + " on GL thread:" + |
Thread.currentThread().getId()); |
@@ -425,46 +457,52 @@ public class VideoRendererGui implements GLSurfaceView.Renderer { |
@Override |
public synchronized void renderFrame(I420Frame frame) { |
+ if (surface == null) { |
+ // This object has been released. |
+ return; |
+ } |
setSize(frame.width, frame.height, frame.rotationDegree); |
long now = System.nanoTime(); |
framesReceived++; |
- // Skip rendering of this frame if setSize() was not called. |
- if (yuvFrameToRender == null || textureFrameToRender == null) { |
- framesDropped++; |
- return; |
- } |
- // Check input frame parameters. |
- if (frame.yuvFrame) { |
- if (frame.yuvStrides[0] < frame.width || |
- frame.yuvStrides[1] < frame.width / 2 || |
- frame.yuvStrides[2] < frame.width / 2) { |
- Log.e(TAG, "Incorrect strides " + frame.yuvStrides[0] + ", " + |
- frame.yuvStrides[1] + ", " + frame.yuvStrides[2]); |
+ synchronized (frameToRenderQueue) { |
+ // Skip rendering of this frame if setSize() was not called. |
+ if (yuvFrameToRender == null || textureFrameToRender == null) { |
+ framesDropped++; |
return; |
} |
- // Check incoming frame dimensions. |
- if (frame.width != yuvFrameToRender.width || |
- frame.height != yuvFrameToRender.height) { |
- throw new RuntimeException("Wrong frame size " + |
- frame.width + " x " + frame.height); |
+ // Check input frame parameters. |
+ if (frame.yuvFrame) { |
+ if (frame.yuvStrides[0] < frame.width || |
+ frame.yuvStrides[1] < frame.width / 2 || |
+ frame.yuvStrides[2] < frame.width / 2) { |
+ Log.e(TAG, "Incorrect strides " + frame.yuvStrides[0] + ", " + |
+ frame.yuvStrides[1] + ", " + frame.yuvStrides[2]); |
+ return; |
+ } |
+ // Check incoming frame dimensions. |
+ if (frame.width != yuvFrameToRender.width || |
+ frame.height != yuvFrameToRender.height) { |
+ throw new RuntimeException("Wrong frame size " + |
+ frame.width + " x " + frame.height); |
+ } |
} |
- } |
- if (frameToRenderQueue.size() > 0) { |
- // Skip rendering of this frame if previous frame was not rendered yet. |
- framesDropped++; |
- return; |
- } |
+ if (frameToRenderQueue.size() > 0) { |
+ // Skip rendering of this frame if previous frame was not rendered yet. |
+ framesDropped++; |
+ return; |
+ } |
- // Create a local copy of the frame. |
- if (frame.yuvFrame) { |
- yuvFrameToRender.copyFrom(frame); |
- rendererType = RendererType.RENDERER_YUV; |
- frameToRenderQueue.offer(yuvFrameToRender); |
- } else { |
- textureFrameToRender.copyFrom(frame); |
- rendererType = RendererType.RENDERER_TEXTURE; |
- frameToRenderQueue.offer(textureFrameToRender); |
+ // Create a local copy of the frame. |
+ if (frame.yuvFrame) { |
+ yuvFrameToRender.copyFrom(frame); |
+ rendererType = RendererType.RENDERER_YUV; |
+ frameToRenderQueue.offer(yuvFrameToRender); |
+ } else { |
+ textureFrameToRender.copyFrom(frame); |
+ rendererType = RendererType.RENDERER_TEXTURE; |
+ frameToRenderQueue.offer(textureFrameToRender); |
+ } |
} |
copyTimeNs += (System.nanoTime() - now); |
seenFrame = true; |
@@ -481,14 +519,14 @@ public class VideoRendererGui implements GLSurfaceView.Renderer { |
} |
/** Passes GLSurfaceView to video renderer. */ |
- public static void setView(GLSurfaceView surface, |
+ public static synchronized void setView(GLSurfaceView surface, |
Runnable eglContextReadyCallback) { |
Log.d(TAG, "VideoRendererGui.setView"); |
instance = new VideoRendererGui(surface); |
eglContextReady = eglContextReadyCallback; |
} |
- public static EGLContext getEGLContext() { |
+ public static synchronized EGLContext getEGLContext() { |
return eglContext; |
} |
@@ -514,7 +552,7 @@ public class VideoRendererGui implements GLSurfaceView.Renderer { |
* resolution (width, height). All parameters are in percentage of |
* screen resolution. |
*/ |
- public static YuvImageRenderer create(int x, int y, int width, int height, |
+ public static synchronized YuvImageRenderer create(int x, int y, int width, int height, |
ScalingType scalingType, boolean mirror) { |
// Check display region parameters. |
if (x < 0 || x > 100 || y < 0 || y > 100 || |
@@ -557,7 +595,7 @@ public class VideoRendererGui implements GLSurfaceView.Renderer { |
return yuvImageRenderer; |
} |
- public static void update( |
+ public static synchronized void update( |
VideoRenderer.Callbacks renderer, |
int x, int y, int width, int height, ScalingType scalingType, boolean mirror) { |
Log.d(TAG, "VideoRendererGui.update"); |
@@ -574,15 +612,18 @@ public class VideoRendererGui implements GLSurfaceView.Renderer { |
} |
} |
- public static void remove(VideoRenderer.Callbacks renderer) { |
+ public static synchronized void remove(VideoRenderer.Callbacks renderer) { |
Log.d(TAG, "VideoRendererGui.remove"); |
if (instance == null) { |
throw new RuntimeException( |
"Attempt to remove yuv renderer before setting GLSurfaceView"); |
} |
synchronized (instance.yuvImageRenderers) { |
- if (!instance.yuvImageRenderers.remove(renderer)) { |
+ final int index = instance.yuvImageRenderers.indexOf(renderer); |
+ if (index == -1) { |
Log.w(TAG, "Couldn't remove renderer (not present in current list)"); |
+ } else { |
+ instance.yuvImageRenderers.remove(index).release(); |
} |
} |
} |
@@ -593,14 +634,15 @@ public class VideoRendererGui implements GLSurfaceView.Renderer { |
Log.d(TAG, "VideoRendererGui.onSurfaceCreated"); |
// Store render EGL context. |
if (CURRENT_SDK_VERSION >= EGL14_SDK_VERSION) { |
- eglContext = EGL14.eglGetCurrentContext(); |
- Log.d(TAG, "VideoRendererGui EGL Context: " + eglContext); |
+ synchronized (VideoRendererGui.class) { |
+ eglContext = EGL14.eglGetCurrentContext(); |
+ Log.d(TAG, "VideoRendererGui EGL Context: " + eglContext); |
+ } |
} |
- // Create drawer for YUV/OES frames. |
- drawer = new GlRectDrawer(); |
- |
synchronized (yuvImageRenderers) { |
+ // Create drawer for YUV/OES frames. |
+ drawer = new GlRectDrawer(); |
// Create textures for all images. |
for (YuvImageRenderer yuvImageRenderer : yuvImageRenderers) { |
yuvImageRenderer.createTextures(); |
@@ -612,8 +654,10 @@ public class VideoRendererGui implements GLSurfaceView.Renderer { |
GLES20.glClearColor(0.15f, 0.15f, 0.15f, 1.0f); |
// Fire EGL context ready event. |
- if (eglContextReady != null) { |
- eglContextReady.run(); |
+ synchronized (VideoRendererGui.class) { |
+ if (eglContextReady != null) { |
+ eglContextReady.run(); |
+ } |
} |
} |