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; |
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 // Layout properties update lock. Guards |updateLayoutProperties|, |screenWi
dth|, |
141 private final Object updateTextureLock = new Object(); | 142 // |screenHeight|, |videoWidth|, |videoHeight|, |rotationDegree|, |scalingTy
pe|, and |mirror|. |
| 143 private final Object updateLayoutLock = new Object(); |
| 144 // Texture sampling matrix. |
| 145 private float[] samplingMatrix; |
142 // Viewport dimensions. | 146 // Viewport dimensions. |
143 private int screenWidth; | 147 private int screenWidth; |
144 private int screenHeight; | 148 private int screenHeight; |
145 // Video dimension. | 149 // Video dimension. |
146 private int videoWidth; | 150 private int videoWidth; |
147 private int videoHeight; | 151 private int videoHeight; |
148 | 152 |
149 // This is the degree that the frame should be rotated clockwisely to have | 153 // This is the degree that the frame should be rotated clockwisely to have |
150 // it rendered up right. | 154 // it rendered up right. |
151 private int rotationDegree; | 155 private int rotationDegree; |
152 | 156 |
153 private YuvImageRenderer( | 157 private YuvImageRenderer( |
154 GLSurfaceView surface, int id, | 158 GLSurfaceView surface, int id, |
155 int x, int y, int width, int height, | 159 int x, int y, int width, int height, |
156 RendererCommon.ScalingType scalingType, boolean mirror) { | 160 RendererCommon.ScalingType scalingType, boolean mirror) { |
157 Log.d(TAG, "YuvImageRenderer.Create id: " + id); | 161 Log.d(TAG, "YuvImageRenderer.Create id: " + id); |
158 this.surface = surface; | 162 this.surface = surface; |
159 this.id = id; | 163 this.id = id; |
160 this.scalingType = scalingType; | 164 this.scalingType = scalingType; |
161 this.mirror = mirror; | 165 this.mirror = mirror; |
162 layoutInPercentage = new Rect(x, y, Math.min(100, x + width), Math.min(100
, y + height)); | 166 layoutInPercentage = new Rect(x, y, Math.min(100, x + width), Math.min(100
, y + height)); |
163 updateTextureProperties = false; | 167 updateLayoutProperties = false; |
164 rotationDegree = 0; | 168 rotationDegree = 0; |
165 } | 169 } |
166 | 170 |
167 private synchronized void release() { | 171 private synchronized void release() { |
168 surface = null; | 172 surface = null; |
169 synchronized (pendingFrameLock) { | 173 synchronized (pendingFrameLock) { |
170 if (pendingFrame != null) { | 174 if (pendingFrame != null) { |
171 VideoRenderer.renderFrameDone(pendingFrame); | 175 VideoRenderer.renderFrameDone(pendingFrame); |
172 pendingFrame = null; | 176 pendingFrame = null; |
173 } | 177 } |
174 } | 178 } |
175 } | 179 } |
176 | 180 |
177 private void createTextures() { | 181 private void createTextures() { |
178 Log.d(TAG, " YuvImageRenderer.createTextures " + id + " on GL thread:" + | 182 Log.d(TAG, " YuvImageRenderer.createTextures " + id + " on GL thread:" + |
179 Thread.currentThread().getId()); | 183 Thread.currentThread().getId()); |
180 | 184 |
181 // Generate 3 texture ids for Y/U/V and place them into |yuvTextures|. | 185 // Generate 3 texture ids for Y/U/V and place them into |yuvTextures|. |
182 for (int i = 0; i < 3; i++) { | 186 for (int i = 0; i < 3; i++) { |
183 yuvTextures[i] = GlUtil.generateTexture(GLES20.GL_TEXTURE_2D); | 187 yuvTextures[i] = GlUtil.generateTexture(GLES20.GL_TEXTURE_2D); |
184 } | 188 } |
185 } | 189 } |
186 | 190 |
187 private void checkAdjustTextureCoords() { | 191 private void updateLayoutMatrix() { |
188 synchronized(updateTextureLock) { | 192 synchronized(updateLayoutLock) { |
189 if (!updateTextureProperties) { | 193 if (!updateLayoutProperties) { |
190 return; | 194 return; |
191 } | 195 } |
192 // Initialize to maximum allowed area. Round to integer coordinates inwa
rds the layout | 196 // 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. | 197 // bounding box (ceil left/top and floor right/bottom) to not break cons
traints. |
194 displayLayout.set( | 198 displayLayout.set( |
195 (screenWidth * layoutInPercentage.left + 99) / 100, | 199 (screenWidth * layoutInPercentage.left + 99) / 100, |
196 (screenHeight * layoutInPercentage.top + 99) / 100, | 200 (screenHeight * layoutInPercentage.top + 99) / 100, |
197 (screenWidth * layoutInPercentage.right) / 100, | 201 (screenWidth * layoutInPercentage.right) / 100, |
198 (screenHeight * layoutInPercentage.bottom) / 100); | 202 (screenHeight * layoutInPercentage.bottom) / 100); |
199 Log.d(TAG, "ID: " + id + ". AdjustTextureCoords. Allowed display size:
" | 203 Log.d(TAG, "ID: " + id + ". AdjustTextureCoords. Allowed display size:
" |
200 + displayLayout.width() + " x " + displayLayout.height() + ". Video:
" + videoWidth | 204 + displayLayout.width() + " x " + displayLayout.height() + ". Video:
" + videoWidth |
201 + " x " + videoHeight + ". Rotation: " + rotationDegree + ". Mirror:
" + mirror); | 205 + " x " + videoHeight + ". Rotation: " + rotationDegree + ". Mirror:
" + mirror); |
202 final float videoAspectRatio = (rotationDegree % 180 == 0) | 206 final float videoAspectRatio = (rotationDegree % 180 == 0) |
203 ? (float) videoWidth / videoHeight | 207 ? (float) videoWidth / videoHeight |
204 : (float) videoHeight / videoWidth; | 208 : (float) videoHeight / videoWidth; |
205 // Adjust display size based on |scalingType|. | 209 // Adjust display size based on |scalingType|. |
206 final Point displaySize = RendererCommon.getDisplaySize(scalingType, | 210 final Point displaySize = RendererCommon.getDisplaySize(scalingType, |
207 videoAspectRatio, displayLayout.width(), displayLayout.height()); | 211 videoAspectRatio, displayLayout.width(), displayLayout.height()); |
208 displayLayout.inset((displayLayout.width() - displaySize.x) / 2, | 212 displayLayout.inset((displayLayout.width() - displaySize.x) / 2, |
209 (displayLayout.height() - displaySize.y) / 2); | 213 (displayLayout.height() - displaySize.y) / 2); |
210 Log.d(TAG, " Adjusted display size: " + displayLayout.width() + " x " | 214 Log.d(TAG, " Adjusted display size: " + displayLayout.width() + " x " |
211 + displayLayout.height()); | 215 + displayLayout.height()); |
212 RendererCommon.getTextureMatrix(texMatrix, rotationDegree, mirror, video
AspectRatio, | 216 layoutMatrix = RendererCommon.getLayoutMatrix( |
213 (float) displayLayout.width() / displayLayout.height()); | 217 mirror, videoAspectRatio, (float) displayLayout.width() / displayLay
out.height()); |
214 updateTextureProperties = false; | 218 updateLayoutProperties = false; |
215 Log.d(TAG, " AdjustTextureCoords done"); | 219 Log.d(TAG, " AdjustTextureCoords done"); |
216 } | 220 } |
217 } | 221 } |
218 | 222 |
219 private void draw(GlRectDrawer drawer) { | 223 private void draw(GlRectDrawer drawer) { |
220 if (!seenFrame) { | 224 if (!seenFrame) { |
221 // No frame received yet - nothing to render. | 225 // No frame received yet - nothing to render. |
222 return; | 226 return; |
223 } | 227 } |
224 long now = System.nanoTime(); | 228 long now = System.nanoTime(); |
225 | 229 |
226 // OpenGL defaults to lower left origin. | 230 // OpenGL defaults to lower left origin. |
227 GLES20.glViewport(displayLayout.left, screenHeight - displayLayout.bottom, | 231 GLES20.glViewport(displayLayout.left, screenHeight - displayLayout.bottom, |
228 displayLayout.width(), displayLayout.height()); | 232 displayLayout.width(), displayLayout.height()); |
229 | 233 |
230 final boolean isNewFrame; | 234 final boolean isNewFrame; |
231 synchronized (pendingFrameLock) { | 235 synchronized (pendingFrameLock) { |
232 // Check if texture vertices/coordinates adjustment is required when | |
233 // screen orientation changes or video frame size changes. | |
234 checkAdjustTextureCoords(); | |
235 | |
236 isNewFrame = (pendingFrame != null); | 236 isNewFrame = (pendingFrame != null); |
237 if (isNewFrame && startTimeNs == -1) { | 237 if (isNewFrame && startTimeNs == -1) { |
238 startTimeNs = now; | 238 startTimeNs = now; |
239 } | 239 } |
240 | 240 |
241 if (isNewFrame) { | 241 if (isNewFrame) { |
242 if (pendingFrame.yuvFrame) { | 242 if (pendingFrame.yuvFrame) { |
243 rendererType = RendererType.RENDERER_YUV; | 243 rendererType = RendererType.RENDERER_YUV; |
244 drawer.uploadYuvData(yuvTextures, pendingFrame.width, pendingFrame.h
eight, | 244 drawer.uploadYuvData(yuvTextures, pendingFrame.width, pendingFrame.h
eight, |
245 pendingFrame.yuvStrides, pendingFrame.yuvPlanes); | 245 pendingFrame.yuvStrides, pendingFrame.yuvPlanes); |
246 } else { | 246 } else { |
247 rendererType = RendererType.RENDERER_TEXTURE; | 247 rendererType = RendererType.RENDERER_TEXTURE; |
248 // External texture rendering. Copy texture id and update texture im
age to latest. | 248 // External texture rendering. Copy texture id and update texture im
age to latest. |
249 // TODO(magjed): We should not make an unmanaged copy of texture id.
Also, this is not | 249 // TODO(magjed): We should not make an unmanaged copy of texture id.
Also, this is not |
250 // the best place to call updateTexImage. | 250 // the best place to call updateTexImage. |
251 oesTexture = pendingFrame.textureId; | 251 oesTexture = pendingFrame.textureId; |
252 if (pendingFrame.textureObject instanceof SurfaceTexture) { | 252 if (pendingFrame.textureObject instanceof SurfaceTexture) { |
253 SurfaceTexture surfaceTexture = | 253 SurfaceTexture surfaceTexture = |
254 (SurfaceTexture) pendingFrame.textureObject; | 254 (SurfaceTexture) pendingFrame.textureObject; |
255 surfaceTexture.updateTexImage(); | 255 surfaceTexture.updateTexImage(); |
256 } | 256 } |
257 } | 257 } |
| 258 samplingMatrix = RendererCommon.getSamplingMatrix( |
| 259 (SurfaceTexture) pendingFrame.textureObject, pendingFrame.rotation
Degree); |
258 copyTimeNs += (System.nanoTime() - now); | 260 copyTimeNs += (System.nanoTime() - now); |
259 VideoRenderer.renderFrameDone(pendingFrame); | 261 VideoRenderer.renderFrameDone(pendingFrame); |
260 pendingFrame = null; | 262 pendingFrame = null; |
261 } | 263 } |
262 } | 264 } |
263 | 265 |
| 266 updateLayoutMatrix(); |
| 267 final float[] texMatrix = new float[16]; |
| 268 Matrix.multiplyMM(texMatrix, 0, samplingMatrix, 0, layoutMatrix, 0); |
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 227 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
581 GLES20.glViewport(0, 0, screenWidth, screenHeight); | 586 GLES20.glViewport(0, 0, screenWidth, screenHeight); |
582 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); | 587 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); |
583 synchronized (yuvImageRenderers) { | 588 synchronized (yuvImageRenderers) { |
584 for (YuvImageRenderer yuvImageRenderer : yuvImageRenderers) { | 589 for (YuvImageRenderer yuvImageRenderer : yuvImageRenderers) { |
585 yuvImageRenderer.draw(drawer); | 590 yuvImageRenderer.draw(drawer); |
586 } | 591 } |
587 } | 592 } |
588 } | 593 } |
589 | 594 |
590 } | 595 } |
OLD | NEW |