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 23da47bce3bc317c9cfc30009f2b5e08e62fbf10..85680073fd4d7e712a0bd23a0cb2e328413ea7be 100644 |
--- a/talk/app/webrtc/java/android/org/webrtc/VideoRendererGui.java |
+++ b/talk/app/webrtc/java/android/org/webrtc/VideoRendererGui.java |
@@ -29,7 +29,6 @@ package org.webrtc; |
import java.util.ArrayList; |
import java.util.concurrent.CountDownLatch; |
-import java.util.concurrent.LinkedBlockingQueue; |
import javax.microedition.khronos.egl.EGLConfig; |
import javax.microedition.khronos.opengles.GL10; |
@@ -102,14 +101,11 @@ public class VideoRendererGui implements GLSurfaceView.Renderer { |
private int[] yuvTextures = { 0, 0, 0 }; |
private int oesTexture = 0; |
- // Render frame queue - accessed by two threads. renderFrame() call does |
- // 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(). |
- private final LinkedBlockingQueue<I420Frame> frameToRenderQueue; |
- // Local copy of incoming video frame. Synchronized on |frameToRenderQueue|. |
- private I420Frame yuvFrameToRender; |
- private I420Frame textureFrameToRender; |
+ // Pending frame to render. Serves as a queue with size 1. |pendingFrame| is accessed by two |
+ // threads - frames are received in renderFrame() and consumed in draw(). Frames are dropped in |
+ // renderFrame() if the previous frame has not been rendered yet. |
+ private I420Frame pendingFrame; |
+ private final Object pendingFrameLock = new Object(); |
// Type of video frame used for recent frame rendering. |
private static enum RendererType { RENDERER_YUV, RENDERER_TEXTURE }; |
private RendererType rendererType; |
@@ -129,7 +125,7 @@ public class VideoRendererGui implements GLSurfaceView.Renderer { |
private long startTimeNs = -1; |
// Time in ns spent in draw() function. |
private long drawTimeNs; |
- // Time in ns spent in renderFrame() function - including copying frame |
+ // Time in ns spent in draw() copying resources from |pendingFrame| - including uploading frame |
// data to rendering planes. |
private long copyTimeNs; |
// The allowed view area in percentage of screen size. |
@@ -163,7 +159,6 @@ public class VideoRendererGui implements GLSurfaceView.Renderer { |
this.id = id; |
this.scalingType = scalingType; |
this.mirror = mirror; |
- frameToRenderQueue = new LinkedBlockingQueue<I420Frame>(1); |
layoutInPercentage = new Rect(x, y, Math.min(100, x + width), Math.min(100, y + height)); |
updateTextureProperties = false; |
rotationDegree = 0; |
@@ -171,10 +166,11 @@ public class VideoRendererGui implements GLSurfaceView.Renderer { |
private synchronized void release() { |
surface = null; |
- synchronized (frameToRenderQueue) { |
- frameToRenderQueue.clear(); |
- yuvFrameToRender = null; |
- textureFrameToRender = null; |
+ synchronized (pendingFrameLock) { |
+ if (pendingFrame != null) { |
+ VideoRenderer.renderFrameDone(pendingFrame); |
+ pendingFrame = null; |
+ } |
} |
} |
@@ -231,52 +227,47 @@ public class VideoRendererGui implements GLSurfaceView.Renderer { |
GLES20.glViewport(displayLayout.left, screenHeight - displayLayout.bottom, |
displayLayout.width(), displayLayout.height()); |
- I420Frame frameFromQueue; |
- synchronized (frameToRenderQueue) { |
+ final boolean isNewFrame; |
+ synchronized (pendingFrameLock) { |
// Check if texture vertices/coordinates adjustment is required when |
// screen orientation changes or video frame size changes. |
checkAdjustTextureCoords(); |
- frameFromQueue = frameToRenderQueue.peek(); |
- if (frameFromQueue != null && startTimeNs == -1) { |
+ isNewFrame = (pendingFrame != null); |
+ if (isNewFrame && startTimeNs == -1) { |
startTimeNs = now; |
} |
- if (frameFromQueue != null) { |
- if (frameFromQueue.yuvFrame) { |
- // YUV textures rendering. Upload YUV data as textures. |
- for (int i = 0; i < 3; ++i) { |
- GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i); |
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, yuvTextures[i]); |
- int w = (i == 0) ? frameFromQueue.width : frameFromQueue.width / 2; |
- int h = (i == 0) ? frameFromQueue.height : frameFromQueue.height / 2; |
- GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, |
- w, h, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, |
- frameFromQueue.yuvPlanes[i]); |
- } |
+ if (isNewFrame) { |
+ if (pendingFrame.yuvFrame) { |
+ rendererType = RendererType.RENDERER_YUV; |
+ drawer.uploadYuvData(yuvTextures, pendingFrame.width, pendingFrame.height, |
+ pendingFrame.yuvStrides, pendingFrame.yuvPlanes); |
} else { |
+ rendererType = RendererType.RENDERER_TEXTURE; |
// External texture rendering. Copy texture id and update texture image to latest. |
// TODO(magjed): We should not make an unmanaged copy of texture id. Also, this is not |
// the best place to call updateTexImage. |
- oesTexture = frameFromQueue.textureId; |
- if (frameFromQueue.textureObject instanceof SurfaceTexture) { |
+ oesTexture = pendingFrame.textureId; |
+ if (pendingFrame.textureObject instanceof SurfaceTexture) { |
SurfaceTexture surfaceTexture = |
- (SurfaceTexture) frameFromQueue.textureObject; |
+ (SurfaceTexture) pendingFrame.textureObject; |
surfaceTexture.updateTexImage(); |
} |
} |
- |
- frameToRenderQueue.poll(); |
+ copyTimeNs += (System.nanoTime() - now); |
+ VideoRenderer.renderFrameDone(pendingFrame); |
+ pendingFrame = null; |
} |
} |
if (rendererType == RendererType.RENDERER_YUV) { |
- drawer.drawYuv(videoWidth, videoHeight, yuvTextures, texMatrix); |
+ drawer.drawYuv(yuvTextures, texMatrix); |
} else { |
drawer.drawOes(oesTexture, texMatrix); |
} |
- if (frameFromQueue != null) { |
+ if (isNewFrame) { |
framesRendered++; |
drawTimeNs += (System.nanoTime() - now); |
if ((framesRendered % 300) == 0) { |
@@ -342,25 +333,13 @@ public class VideoRendererGui implements GLSurfaceView.Renderer { |
rendererEvents.onFrameResolutionChanged(videoWidth, videoHeight, rotation); |
} |
- // Frame re-allocation need to be synchronized with copying |
- // frame to textures in draw() function to avoid re-allocating |
- // the frame while it is being copied. |
- synchronized (frameToRenderQueue) { |
+ synchronized (updateTextureLock) { |
Log.d(TAG, "ID: " + id + ". YuvImageRenderer.setSize: " + |
videoWidth + " x " + videoHeight + " rotation " + rotation); |
this.videoWidth = videoWidth; |
this.videoHeight = videoHeight; |
rotationDegree = rotation; |
- int[] strides = { videoWidth, videoWidth / 2, videoWidth / 2 }; |
- |
- // Clear rendering queue. |
- frameToRenderQueue.poll(); |
- // Re-allocate / allocate the frame. |
- yuvFrameToRender = new I420Frame(videoWidth, videoHeight, rotationDegree, |
- strides, null, 0); |
- textureFrameToRender = new I420Frame(videoWidth, videoHeight, rotationDegree, |
- null, -1, 0); |
updateTextureProperties = true; |
Log.d(TAG, " YuvImageRenderer.setSize done."); |
} |
@@ -377,16 +356,8 @@ public class VideoRendererGui implements GLSurfaceView.Renderer { |
Log.d(TAG, "ID: " + id + ". Reporting first rendered frame."); |
rendererEvents.onFirstFrameRendered(); |
} |
- setSize(frame.width, frame.height, frame.rotationDegree); |
- long now = System.nanoTime(); |
framesReceived++; |
- synchronized (frameToRenderQueue) { |
- // Skip rendering of this frame if setSize() was not called. |
- if (yuvFrameToRender == null || textureFrameToRender == null) { |
- framesDropped++; |
- VideoRenderer.renderFrameDone(frame); |
- return; |
- } |
+ synchronized (pendingFrameLock) { |
// Check input frame parameters. |
if (frame.yuvFrame) { |
if (frame.yuvStrides[0] < frame.width || |
@@ -397,35 +368,18 @@ public class VideoRendererGui implements GLSurfaceView.Renderer { |
VideoRenderer.renderFrameDone(frame); |
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) { |
+ if (pendingFrame != null) { |
// Skip rendering of this frame if previous frame was not rendered yet. |
framesDropped++; |
VideoRenderer.renderFrameDone(frame); |
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); |
- } |
+ pendingFrame = frame; |
} |
- copyTimeNs += (System.nanoTime() - now); |
+ setSize(frame.width, frame.height, frame.rotationDegree); |
seenFrame = true; |
- VideoRenderer.renderFrameDone(frame); |
// Request rendering. |
surface.requestRender(); |