OLD | NEW |
---|---|
1 /* | 1 /* |
2 * libjingle | 2 * libjingle |
3 * Copyright 2014 Google Inc. | 3 * Copyright 2014 Google Inc. |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions are met: | 6 * modification, are permitted provided that the following conditions are met: |
7 * | 7 * |
8 * 1. Redistributions of source code must retain the above copyright notice, | 8 * 1. Redistributions of source code must retain the above copyright notice, |
9 * this list of conditions and the following disclaimer. | 9 * this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright notice, | 10 * 2. Redistributions in binary form must reproduce the above copyright notice, |
(...skipping 23 matching lines...) Expand all Loading... | |
34 import javax.microedition.khronos.opengles.GL10; | 34 import javax.microedition.khronos.opengles.GL10; |
35 | 35 |
36 import android.annotation.SuppressLint; | 36 import android.annotation.SuppressLint; |
37 import android.graphics.Point; | 37 import android.graphics.Point; |
38 import android.graphics.Rect; | 38 import android.graphics.Rect; |
39 import android.graphics.SurfaceTexture; | 39 import android.graphics.SurfaceTexture; |
40 import android.opengl.EGL14; | 40 import android.opengl.EGL14; |
41 import android.opengl.EGLContext; | 41 import android.opengl.EGLContext; |
42 import android.opengl.GLES20; | 42 import android.opengl.GLES20; |
43 import android.opengl.GLSurfaceView; | 43 import android.opengl.GLSurfaceView; |
44 import android.opengl.Matrix; | |
44 import android.util.Log; | 45 import android.util.Log; |
45 | 46 |
46 import org.webrtc.VideoRenderer.I420Frame; | 47 import org.webrtc.VideoRenderer.I420Frame; |
47 | 48 |
48 /** | 49 /** |
49 * Efficiently renders YUV frames using the GPU for CSC. | 50 * Efficiently renders YUV frames using the GPU for CSC. |
50 * Clients will want first to call setView() to pass GLSurfaceView | 51 * Clients will want first to call setView() to pass GLSurfaceView |
51 * and then for each video stream either create instance of VideoRenderer using | 52 * and then for each video stream either create instance of VideoRenderer using |
52 * createGui() call or VideoRenderer.Callbacks interface using create() call. | 53 * createGui() call or VideoRenderer.Callbacks interface using create() call. |
53 * Only one instance of the class can be created. | 54 * Only one instance of the class can be created. |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
126 // Time in ns spent in draw() function. | 127 // Time in ns spent in draw() function. |
127 private long drawTimeNs; | 128 private long drawTimeNs; |
128 // Time in ns spent in draw() copying resources from |pendingFrame| - includ ing uploading frame | 129 // Time in ns spent in draw() copying resources from |pendingFrame| - includ ing uploading frame |
129 // data to rendering planes. | 130 // data to rendering planes. |
130 private long copyTimeNs; | 131 private long copyTimeNs; |
131 // The allowed view area in percentage of screen size. | 132 // The allowed view area in percentage of screen size. |
132 private final Rect layoutInPercentage; | 133 private final Rect layoutInPercentage; |
133 // The actual view area in pixels. It is a centered subrectangle of the rect angle defined by | 134 // The actual view area in pixels. It is a centered subrectangle of the rect angle defined by |
134 // |layoutInPercentage|. | 135 // |layoutInPercentage|. |
135 private final Rect displayLayout = new Rect(); | 136 private final Rect displayLayout = new Rect(); |
136 // Cached texture transformation matrix, calculated from current layout para meters. | 137 // Cached layout transformation matrix, calculated from current layout param eters. |
137 private final float[] texMatrix = new float[16]; | 138 private float[] layoutMatrix = new float[16]; |
hbos
2015/09/08 14:08:47
This probably shouldn't be used at all until it ha
magjed_webrtc
2015/09/08 15:24:28
True, I will default to null instead.
hbos
2015/09/09 08:27:54
Acknowledged.
| |
138 // Flag if texture vertices or coordinates update is needed. | 139 // Flag if layout transformation matrix update is needed. |
139 private boolean updateTextureProperties; | 140 private boolean updateLayoutProperties; |
140 // Texture properties update lock. | 141 // Texture properties update lock. |
141 private final Object updateTextureLock = new Object(); | 142 private final Object updateLayoutLock = new Object(); |
hbos
2015/09/08 14:08:47
What variables are guarded by this lock? Can you l
magjed_webrtc
2015/09/08 15:24:28
Done.
| |
143 // Texture sampling matrix. | |
144 private float[] samplingMatrix; | |
142 // Viewport dimensions. | 145 // Viewport dimensions. |
143 private int screenWidth; | 146 private int screenWidth; |
144 private int screenHeight; | 147 private int screenHeight; |
145 // Video dimension. | 148 // Video dimension. |
146 private int videoWidth; | 149 private int videoWidth; |
147 private int videoHeight; | 150 private int videoHeight; |
148 | 151 |
149 // This is the degree that the frame should be rotated clockwisely to have | 152 // This is the degree that the frame should be rotated clockwisely to have |
150 // it rendered up right. | 153 // it rendered up right. |
151 private int rotationDegree; | 154 private int rotationDegree; |
152 | 155 |
153 private YuvImageRenderer( | 156 private YuvImageRenderer( |
154 GLSurfaceView surface, int id, | 157 GLSurfaceView surface, int id, |
155 int x, int y, int width, int height, | 158 int x, int y, int width, int height, |
156 RendererCommon.ScalingType scalingType, boolean mirror) { | 159 RendererCommon.ScalingType scalingType, boolean mirror) { |
157 Log.d(TAG, "YuvImageRenderer.Create id: " + id); | 160 Log.d(TAG, "YuvImageRenderer.Create id: " + id); |
158 this.surface = surface; | 161 this.surface = surface; |
159 this.id = id; | 162 this.id = id; |
160 this.scalingType = scalingType; | 163 this.scalingType = scalingType; |
161 this.mirror = mirror; | 164 this.mirror = mirror; |
162 layoutInPercentage = new Rect(x, y, Math.min(100, x + width), Math.min(100 , y + height)); | 165 layoutInPercentage = new Rect(x, y, Math.min(100, x + width), Math.min(100 , y + height)); |
163 updateTextureProperties = false; | 166 updateLayoutProperties = false; |
164 rotationDegree = 0; | 167 rotationDegree = 0; |
165 } | 168 } |
166 | 169 |
167 private synchronized void release() { | 170 private synchronized void release() { |
168 surface = null; | 171 surface = null; |
169 synchronized (pendingFrameLock) { | 172 synchronized (pendingFrameLock) { |
170 if (pendingFrame != null) { | 173 if (pendingFrame != null) { |
171 VideoRenderer.renderFrameDone(pendingFrame); | 174 VideoRenderer.renderFrameDone(pendingFrame); |
172 pendingFrame = null; | 175 pendingFrame = null; |
173 } | 176 } |
174 } | 177 } |
175 } | 178 } |
176 | 179 |
177 private void createTextures() { | 180 private void createTextures() { |
178 Log.d(TAG, " YuvImageRenderer.createTextures " + id + " on GL thread:" + | 181 Log.d(TAG, " YuvImageRenderer.createTextures " + id + " on GL thread:" + |
179 Thread.currentThread().getId()); | 182 Thread.currentThread().getId()); |
180 | 183 |
181 // Generate 3 texture ids for Y/U/V and place them into |yuvTextures|. | 184 // Generate 3 texture ids for Y/U/V and place them into |yuvTextures|. |
182 for (int i = 0; i < 3; i++) { | 185 for (int i = 0; i < 3; i++) { |
183 yuvTextures[i] = GlUtil.generateTexture(GLES20.GL_TEXTURE_2D); | 186 yuvTextures[i] = GlUtil.generateTexture(GLES20.GL_TEXTURE_2D); |
184 } | 187 } |
185 } | 188 } |
186 | 189 |
187 private void checkAdjustTextureCoords() { | 190 private void checkAdjustTextureCoords() { |
hbos
2015/09/08 14:08:47
Rename to checkAdjustLayoutMatrix?
magjed_webrtc
2015/09/08 15:24:28
Renamed to getLayoutMatrix() and changed it a bit.
| |
188 synchronized(updateTextureLock) { | 191 synchronized(updateLayoutLock) { |
189 if (!updateTextureProperties) { | 192 if (!updateLayoutProperties) { |
190 return; | 193 return; |
191 } | 194 } |
192 // Initialize to maximum allowed area. Round to integer coordinates inwa rds the layout | 195 // Initialize to maximum allowed area. Round to integer coordinates inwa rds the layout |
193 // bounding box (ceil left/top and floor right/bottom) to not break cons traints. | 196 // bounding box (ceil left/top and floor right/bottom) to not break cons traints. |
194 displayLayout.set( | 197 displayLayout.set( |
195 (screenWidth * layoutInPercentage.left + 99) / 100, | 198 (screenWidth * layoutInPercentage.left + 99) / 100, |
196 (screenHeight * layoutInPercentage.top + 99) / 100, | 199 (screenHeight * layoutInPercentage.top + 99) / 100, |
197 (screenWidth * layoutInPercentage.right) / 100, | 200 (screenWidth * layoutInPercentage.right) / 100, |
198 (screenHeight * layoutInPercentage.bottom) / 100); | 201 (screenHeight * layoutInPercentage.bottom) / 100); |
199 Log.d(TAG, "ID: " + id + ". AdjustTextureCoords. Allowed display size: " | 202 Log.d(TAG, "ID: " + id + ". AdjustTextureCoords. Allowed display size: " |
200 + displayLayout.width() + " x " + displayLayout.height() + ". Video: " + videoWidth | 203 + displayLayout.width() + " x " + displayLayout.height() + ". Video: " + videoWidth |
201 + " x " + videoHeight + ". Rotation: " + rotationDegree + ". Mirror: " + mirror); | 204 + " x " + videoHeight + ". Rotation: " + rotationDegree + ". Mirror: " + mirror); |
202 final float videoAspectRatio = (rotationDegree % 180 == 0) | 205 final float videoAspectRatio = (rotationDegree % 180 == 0) |
203 ? (float) videoWidth / videoHeight | 206 ? (float) videoWidth / videoHeight |
204 : (float) videoHeight / videoWidth; | 207 : (float) videoHeight / videoWidth; |
205 // Adjust display size based on |scalingType|. | 208 // Adjust display size based on |scalingType|. |
206 final Point displaySize = RendererCommon.getDisplaySize(scalingType, | 209 final Point displaySize = RendererCommon.getDisplaySize(scalingType, |
207 videoAspectRatio, displayLayout.width(), displayLayout.height()); | 210 videoAspectRatio, displayLayout.width(), displayLayout.height()); |
208 displayLayout.inset((displayLayout.width() - displaySize.x) / 2, | 211 displayLayout.inset((displayLayout.width() - displaySize.x) / 2, |
209 (displayLayout.height() - displaySize.y) / 2); | 212 (displayLayout.height() - displaySize.y) / 2); |
210 Log.d(TAG, " Adjusted display size: " + displayLayout.width() + " x " | 213 Log.d(TAG, " Adjusted display size: " + displayLayout.width() + " x " |
211 + displayLayout.height()); | 214 + displayLayout.height()); |
212 RendererCommon.getTextureMatrix(texMatrix, rotationDegree, mirror, video AspectRatio, | 215 layoutMatrix = RendererCommon.getLayoutMatrix( |
213 (float) displayLayout.width() / displayLayout.height()); | 216 mirror, videoAspectRatio, (float) displayLayout.width() / displayLay out.height()); |
hbos
2015/09/08 14:08:47
What are the implications of mirroring? I'm thinki
magjed_webrtc
2015/09/08 15:24:28
We need to take care when we multiply matrices tog
hbos
2015/09/09 08:27:54
Acknowledged.
| |
214 updateTextureProperties = false; | 217 updateLayoutProperties = false; |
215 Log.d(TAG, " AdjustTextureCoords done"); | 218 Log.d(TAG, " AdjustTextureCoords done"); |
216 } | 219 } |
217 } | 220 } |
218 | 221 |
219 private void draw(GlRectDrawer drawer) { | 222 private void draw(GlRectDrawer drawer) { |
220 if (!seenFrame) { | 223 if (!seenFrame) { |
221 // No frame received yet - nothing to render. | 224 // No frame received yet - nothing to render. |
222 return; | 225 return; |
223 } | 226 } |
224 long now = System.nanoTime(); | 227 long now = System.nanoTime(); |
(...skipping 29 matching lines...) Expand all Loading... | |
254 (SurfaceTexture) pendingFrame.textureObject; | 257 (SurfaceTexture) pendingFrame.textureObject; |
255 surfaceTexture.updateTexImage(); | 258 surfaceTexture.updateTexImage(); |
256 } | 259 } |
257 } | 260 } |
258 copyTimeNs += (System.nanoTime() - now); | 261 copyTimeNs += (System.nanoTime() - now); |
259 VideoRenderer.renderFrameDone(pendingFrame); | 262 VideoRenderer.renderFrameDone(pendingFrame); |
260 pendingFrame = null; | 263 pendingFrame = null; |
261 } | 264 } |
262 } | 265 } |
263 | 266 |
267 final float[] texMatrix = new float[16]; | |
268 Matrix.multiplyMM(texMatrix, 0, samplingMatrix, 0, layoutMatrix, 0); | |
hbos
2015/09/08 14:08:47
Is reading from samplingMatrix thread-safe without
magjed_webrtc
2015/09/08 15:24:28
Thanks, this is a bug. I moved all usage of |sampl
hbos
2015/09/09 08:27:54
You're still updating and returning a reference to
| |
264 if (rendererType == RendererType.RENDERER_YUV) { | 269 if (rendererType == RendererType.RENDERER_YUV) { |
265 drawer.drawYuv(yuvTextures, texMatrix); | 270 drawer.drawYuv(yuvTextures, texMatrix); |
266 } else { | 271 } else { |
267 drawer.drawOes(oesTexture, texMatrix); | 272 drawer.drawOes(oesTexture, texMatrix); |
268 } | 273 } |
269 | 274 |
270 if (isNewFrame) { | 275 if (isNewFrame) { |
271 framesRendered++; | 276 framesRendered++; |
272 drawTimeNs += (System.nanoTime() - now); | 277 drawTimeNs += (System.nanoTime() - now); |
273 if ((framesRendered % 300) == 0) { | 278 if ((framesRendered % 300) == 0) { |
(...skipping 10 matching lines...) Expand all Loading... | |
284 if (framesReceived > 0 && framesRendered > 0) { | 289 if (framesReceived > 0 && framesRendered > 0) { |
285 Log.d(TAG, "Duration: " + (int)(timeSinceFirstFrameNs / 1e6) + | 290 Log.d(TAG, "Duration: " + (int)(timeSinceFirstFrameNs / 1e6) + |
286 " ms. FPS: " + (float)framesRendered * 1e9 / timeSinceFirstFrameNs); | 291 " ms. FPS: " + (float)framesRendered * 1e9 / timeSinceFirstFrameNs); |
287 Log.d(TAG, "Draw time: " + | 292 Log.d(TAG, "Draw time: " + |
288 (int) (drawTimeNs / (1000 * framesRendered)) + " us. Copy time: " + | 293 (int) (drawTimeNs / (1000 * framesRendered)) + " us. Copy time: " + |
289 (int) (copyTimeNs / (1000 * framesReceived)) + " us"); | 294 (int) (copyTimeNs / (1000 * framesReceived)) + " us"); |
290 } | 295 } |
291 } | 296 } |
292 | 297 |
293 public void setScreenSize(final int screenWidth, final int screenHeight) { | 298 public void setScreenSize(final int screenWidth, final int screenHeight) { |
294 synchronized(updateTextureLock) { | 299 synchronized(updateLayoutLock) { |
295 if (screenWidth == this.screenWidth && screenHeight == this.screenHeight ) { | 300 if (screenWidth == this.screenWidth && screenHeight == this.screenHeight ) { |
296 return; | 301 return; |
297 } | 302 } |
298 Log.d(TAG, "ID: " + id + ". YuvImageRenderer.setScreenSize: " + | 303 Log.d(TAG, "ID: " + id + ". YuvImageRenderer.setScreenSize: " + |
299 screenWidth + " x " + screenHeight); | 304 screenWidth + " x " + screenHeight); |
300 this.screenWidth = screenWidth; | 305 this.screenWidth = screenWidth; |
301 this.screenHeight = screenHeight; | 306 this.screenHeight = screenHeight; |
302 updateTextureProperties = true; | 307 updateLayoutProperties = true; |
303 } | 308 } |
304 } | 309 } |
305 | 310 |
306 public void setPosition(int x, int y, int width, int height, | 311 public void setPosition(int x, int y, int width, int height, |
307 RendererCommon.ScalingType scalingType, boolean mirror) { | 312 RendererCommon.ScalingType scalingType, boolean mirror) { |
308 final Rect layoutInPercentage = | 313 final Rect layoutInPercentage = |
309 new Rect(x, y, Math.min(100, x + width), Math.min(100, y + height)); | 314 new Rect(x, y, Math.min(100, x + width), Math.min(100, y + height)); |
310 synchronized(updateTextureLock) { | 315 synchronized(updateLayoutLock) { |
311 if (layoutInPercentage.equals(this.layoutInPercentage) && scalingType == this.scalingType | 316 if (layoutInPercentage.equals(this.layoutInPercentage) && scalingType == this.scalingType |
312 && mirror == this.mirror) { | 317 && mirror == this.mirror) { |
313 return; | 318 return; |
314 } | 319 } |
315 Log.d(TAG, "ID: " + id + ". YuvImageRenderer.setPosition: (" + x + ", " + y + | 320 Log.d(TAG, "ID: " + id + ". YuvImageRenderer.setPosition: (" + x + ", " + y + |
316 ") " + width + " x " + height + ". Scaling: " + scalingType + | 321 ") " + width + " x " + height + ". Scaling: " + scalingType + |
317 ". Mirror: " + mirror); | 322 ". Mirror: " + mirror); |
318 this.layoutInPercentage.set(layoutInPercentage); | 323 this.layoutInPercentage.set(layoutInPercentage); |
319 this.scalingType = scalingType; | 324 this.scalingType = scalingType; |
320 this.mirror = mirror; | 325 this.mirror = mirror; |
321 updateTextureProperties = true; | 326 updateLayoutProperties = true; |
322 } | 327 } |
323 } | 328 } |
324 | 329 |
325 private void setSize(final int videoWidth, final int videoHeight, final int rotation) { | 330 private void setSize(final int videoWidth, final int videoHeight, final int rotation) { |
326 if (videoWidth == this.videoWidth && videoHeight == this.videoHeight | 331 if (videoWidth == this.videoWidth && videoHeight == this.videoHeight |
327 && rotation == rotationDegree) { | 332 && rotation == rotationDegree) { |
328 return; | 333 return; |
329 } | 334 } |
330 if (rendererEvents != null) { | 335 if (rendererEvents != null) { |
331 Log.d(TAG, "ID: " + id + | 336 Log.d(TAG, "ID: " + id + |
332 ". Reporting frame resolution changed to " + videoWidth + " x " + vi deoHeight); | 337 ". Reporting frame resolution changed to " + videoWidth + " x " + vi deoHeight); |
333 rendererEvents.onFrameResolutionChanged(videoWidth, videoHeight, rotatio n); | 338 rendererEvents.onFrameResolutionChanged(videoWidth, videoHeight, rotatio n); |
334 } | 339 } |
335 | 340 |
336 synchronized (updateTextureLock) { | 341 synchronized (updateLayoutLock) { |
337 Log.d(TAG, "ID: " + id + ". YuvImageRenderer.setSize: " + | 342 Log.d(TAG, "ID: " + id + ". YuvImageRenderer.setSize: " + |
338 videoWidth + " x " + videoHeight + " rotation " + rotation); | 343 videoWidth + " x " + videoHeight + " rotation " + rotation); |
339 | 344 |
340 this.videoWidth = videoWidth; | 345 this.videoWidth = videoWidth; |
341 this.videoHeight = videoHeight; | 346 this.videoHeight = videoHeight; |
342 rotationDegree = rotation; | 347 rotationDegree = rotation; |
343 updateTextureProperties = true; | 348 updateLayoutProperties = true; |
344 Log.d(TAG, " YuvImageRenderer.setSize done."); | 349 Log.d(TAG, " YuvImageRenderer.setSize done."); |
345 } | 350 } |
346 } | 351 } |
347 | 352 |
348 @Override | 353 @Override |
349 public synchronized void renderFrame(I420Frame frame) { | 354 public synchronized void renderFrame(I420Frame frame) { |
350 if (surface == null) { | 355 if (surface == null) { |
351 // This object has been released. | 356 // This object has been released. |
352 VideoRenderer.renderFrameDone(frame); | 357 VideoRenderer.renderFrameDone(frame); |
353 return; | 358 return; |
(...skipping 17 matching lines...) Expand all Loading... | |
371 } | 376 } |
372 | 377 |
373 if (pendingFrame != null) { | 378 if (pendingFrame != null) { |
374 // Skip rendering of this frame if previous frame was not rendered yet . | 379 // Skip rendering of this frame if previous frame was not rendered yet . |
375 framesDropped++; | 380 framesDropped++; |
376 VideoRenderer.renderFrameDone(frame); | 381 VideoRenderer.renderFrameDone(frame); |
377 return; | 382 return; |
378 } | 383 } |
379 pendingFrame = frame; | 384 pendingFrame = frame; |
380 } | 385 } |
386 samplingMatrix = RendererCommon.getSamplingMatrix( | |
387 (SurfaceTexture) frame.textureObject, frame.rotationDegree); | |
381 setSize(frame.width, frame.height, frame.rotationDegree); | 388 setSize(frame.width, frame.height, frame.rotationDegree); |
382 seenFrame = true; | 389 seenFrame = true; |
383 | 390 |
384 // Request rendering. | 391 // Request rendering. |
385 surface.requestRender(); | 392 surface.requestRender(); |
386 } | 393 } |
387 } | 394 } |
388 | 395 |
389 /** Passes GLSurfaceView to video renderer. */ | 396 /** Passes GLSurfaceView to video renderer. */ |
390 public static synchronized void setView(GLSurfaceView surface, | 397 public static synchronized void setView(GLSurfaceView surface, |
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
581 GLES20.glViewport(0, 0, screenWidth, screenHeight); | 588 GLES20.glViewport(0, 0, screenWidth, screenHeight); |
582 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); | 589 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); |
583 synchronized (yuvImageRenderers) { | 590 synchronized (yuvImageRenderers) { |
584 for (YuvImageRenderer yuvImageRenderer : yuvImageRenderers) { | 591 for (YuvImageRenderer yuvImageRenderer : yuvImageRenderers) { |
585 yuvImageRenderer.draw(drawer); | 592 yuvImageRenderer.draw(drawer); |
586 } | 593 } |
587 } | 594 } |
588 } | 595 } |
589 | 596 |
590 } | 597 } |
OLD | NEW |