OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright 2016 The WebRTC project authors. All Rights Reserved. | 2 * Copyright 2016 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 package org.webrtc; | 11 package org.webrtc; |
12 | 12 |
13 import android.graphics.Bitmap; | |
13 import android.graphics.SurfaceTexture; | 14 import android.graphics.SurfaceTexture; |
14 import android.opengl.GLES20; | 15 import android.opengl.GLES20; |
15 import android.os.Handler; | 16 import android.os.Handler; |
16 import android.os.HandlerThread; | 17 import android.os.HandlerThread; |
17 import android.os.Looper; | 18 import android.os.Looper; |
18 import android.view.Surface; | 19 import android.view.Surface; |
20 import java.nio.IntBuffer; | |
21 import java.util.ArrayList; | |
22 import java.util.Iterator; | |
19 import java.util.concurrent.CountDownLatch; | 23 import java.util.concurrent.CountDownLatch; |
20 import java.util.concurrent.TimeUnit; | 24 import java.util.concurrent.TimeUnit; |
21 | 25 |
22 /** | 26 /** |
23 * Implements org.webrtc.VideoRenderer.Callbacks by displaying the video stream on an EGL Surface. | 27 * Implements org.webrtc.VideoRenderer.Callbacks by displaying the video stream on an EGL Surface. |
24 * This class is intended to be used as a helper class for rendering on SurfaceV iews and | 28 * This class is intended to be used as a helper class for rendering on SurfaceV iews and |
25 * TextureViews. | 29 * TextureViews. |
26 */ | 30 */ |
27 public class EglRenderer implements VideoRenderer.Callbacks { | 31 public class EglRenderer implements VideoRenderer.Callbacks { |
28 private static final String TAG = "EglRenderer"; | 32 private static final String TAG = "EglRenderer"; |
29 private static final long LOG_INTERVAL_SEC = 4; | 33 private static final long LOG_INTERVAL_SEC = 4; |
30 private static final int MAX_SURFACE_CLEAR_COUNT = 3; | 34 private static final int MAX_SURFACE_CLEAR_COUNT = 3; |
31 | 35 |
36 public interface FrameListener { void onFrame(Bitmap frame); } | |
37 | |
38 private static class ScaleAndFrameListener { | |
39 public final float scale; | |
40 public final FrameListener listener; | |
41 | |
42 public ScaleAndFrameListener(float scale, FrameListener listener) { | |
43 this.scale = scale; | |
44 this.listener = listener; | |
45 } | |
46 } | |
47 | |
32 private class EglSurfaceCreation implements Runnable { | 48 private class EglSurfaceCreation implements Runnable { |
33 private Object surface; | 49 private Object surface; |
34 | 50 |
35 public synchronized void setSurface(Object surface) { | 51 public synchronized void setSurface(Object surface) { |
36 this.surface = surface; | 52 this.surface = surface; |
37 } | 53 } |
38 | 54 |
39 @Override | 55 @Override |
40 public synchronized void run() { | 56 public synchronized void run() { |
41 if (surface != null && eglBase != null && !eglBase.hasSurface()) { | 57 if (surface != null && eglBase != null && !eglBase.hasSurface()) { |
(...skipping 11 matching lines...) Expand all Loading... | |
53 } | 69 } |
54 } | 70 } |
55 | 71 |
56 private final String name; | 72 private final String name; |
57 | 73 |
58 // |renderThreadHandler| is a handler for communicating with |renderThread|, a nd is synchronized | 74 // |renderThreadHandler| is a handler for communicating with |renderThread|, a nd is synchronized |
59 // on |handlerLock|. | 75 // on |handlerLock|. |
60 private final Object handlerLock = new Object(); | 76 private final Object handlerLock = new Object(); |
61 private Handler renderThreadHandler; | 77 private Handler renderThreadHandler; |
62 | 78 |
79 private final Object frameListenerLock = new Object(); | |
80 private final ArrayList<ScaleAndFrameListener> frameListeners = new ArrayList< >(); | |
81 | |
63 // Variables for fps reduction. | 82 // Variables for fps reduction. |
64 private final Object fpsReductionLock = new Object(); | 83 private final Object fpsReductionLock = new Object(); |
65 // Time for when next frame should be rendered. | 84 // Time for when next frame should be rendered. |
66 private long nextFrameTimeNs; | 85 private long nextFrameTimeNs; |
67 // Minimum duration between frames when fps reduction is active, or -1 if vide o is completely | 86 // Minimum duration between frames when fps reduction is active, or -1 if vide o is completely |
68 // paused. | 87 // paused. |
69 private long minRenderPeriodNs; | 88 private long minRenderPeriodNs; |
70 | 89 |
71 // EGL and GL resources for drawing YUV/OES textures. After initilization, the se are only accessed | 90 // EGL and GL resources for drawing YUV/OES textures. After initilization, the se are only accessed |
72 // from the render thread. | 91 // from the render thread. |
73 private EglBase eglBase; | 92 private EglBase eglBase; |
74 private final RendererCommon.YuvUploader yuvUploader = new RendererCommon.YuvU ploader(); | 93 private final RendererCommon.YuvUploader yuvUploader = new RendererCommon.YuvU ploader(); |
94 private YuvConverter yuvConverter; | |
magjed_webrtc
2016/11/01 18:06:46
Remove this now since you didn't end up using it.
sakal
2016/11/02 08:34:29
Done.
| |
75 private RendererCommon.GlDrawer drawer; | 95 private RendererCommon.GlDrawer drawer; |
76 // Texture ids for YUV frames. Allocated on first arrival of a YUV frame. | 96 // Texture ids for YUV frames. Allocated on first arrival of a YUV frame. |
77 private int[] yuvTextures = null; | 97 private int[] yuvTextures = null; |
78 | 98 |
79 // Pending frame to render. Serves as a queue with size 1. Synchronized on |fr ameLock|. | 99 // Pending frame to render. Serves as a queue with size 1. Synchronized on |fr ameLock|. |
80 private final Object frameLock = new Object(); | 100 private final Object frameLock = new Object(); |
81 private VideoRenderer.I420Frame pendingFrame; | 101 private VideoRenderer.I420Frame pendingFrame; |
82 | 102 |
83 // These variables are synchronized on |layoutLock|. | 103 // These variables are synchronized on |layoutLock|. |
84 private final Object layoutLock = new Object(); | 104 private final Object layoutLock = new Object(); |
(...skipping 12 matching lines...) Expand all Loading... | |
97 private int framesDropped; | 117 private int framesDropped; |
98 // Number of rendered video frames. | 118 // Number of rendered video frames. |
99 private int framesRendered; | 119 private int framesRendered; |
100 // Start time for counting these statistics, or 0 if we haven't started measur ing yet. | 120 // Start time for counting these statistics, or 0 if we haven't started measur ing yet. |
101 private long statisticsStartTimeNs; | 121 private long statisticsStartTimeNs; |
102 // Time in ns spent in renderFrameOnRenderThread() function. | 122 // Time in ns spent in renderFrameOnRenderThread() function. |
103 private long renderTimeNs; | 123 private long renderTimeNs; |
104 // Time in ns spent by the render thread in the swapBuffers() function. | 124 // Time in ns spent by the render thread in the swapBuffers() function. |
105 private long renderSwapBufferTimeNs; | 125 private long renderSwapBufferTimeNs; |
106 | 126 |
127 // Used for bitmap capturing. | |
128 private final int[] bitmapTexture = new int[1]; | |
magjed_webrtc
2016/11/01 18:06:46
Keep this as an int instead of int[1]. Same for bi
sakal
2016/11/02 08:34:29
Done.
| |
129 private final int[] bitmapFramebuffer = new int[1]; | |
130 private int bitmapTextureWidth; | |
131 private int bitmapTextureHeight; | |
132 | |
107 // Runnable for posting frames to render thread. | 133 // Runnable for posting frames to render thread. |
108 private final Runnable renderFrameRunnable = new Runnable() { | 134 private final Runnable renderFrameRunnable = new Runnable() { |
109 @Override | 135 @Override |
110 public void run() { | 136 public void run() { |
111 renderFrameOnRenderThread(); | 137 renderFrameOnRenderThread(); |
112 } | 138 } |
113 }; | 139 }; |
114 | 140 |
115 private final Runnable logStatisticsRunnable = new Runnable() { | 141 private final Runnable logStatisticsRunnable = new Runnable() { |
116 @Override | 142 @Override |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
209 } | 235 } |
210 renderThreadHandler.removeCallbacks(logStatisticsRunnable); | 236 renderThreadHandler.removeCallbacks(logStatisticsRunnable); |
211 // Release EGL and GL resources on render thread. | 237 // Release EGL and GL resources on render thread. |
212 renderThreadHandler.postAtFrontOfQueue(new Runnable() { | 238 renderThreadHandler.postAtFrontOfQueue(new Runnable() { |
213 @Override | 239 @Override |
214 public void run() { | 240 public void run() { |
215 if (drawer != null) { | 241 if (drawer != null) { |
216 drawer.release(); | 242 drawer.release(); |
217 drawer = null; | 243 drawer = null; |
218 } | 244 } |
245 if (yuvConverter != null) { | |
246 yuvConverter.release(); | |
247 yuvConverter = null; | |
248 } | |
219 if (yuvTextures != null) { | 249 if (yuvTextures != null) { |
220 GLES20.glDeleteTextures(3, yuvTextures, 0); | 250 GLES20.glDeleteTextures(3, yuvTextures, 0); |
221 yuvTextures = null; | 251 yuvTextures = null; |
222 } | 252 } |
253 GLES20.glDeleteFramebuffers(1, bitmapFramebuffer, 0); | |
magjed_webrtc
2016/11/01 18:06:46
Add 'if (bitmapFramebuffer[0] != 0)' check here. S
sakal
2016/11/02 08:34:29
"glDeleteFramebuffers silently ignores 0's and nam
| |
254 bitmapFramebuffer[0] = 0; | |
255 GLES20.glDeleteTextures(1, bitmapTexture, 0); | |
256 bitmapTexture[0] = 0; | |
223 if (eglBase != null) { | 257 if (eglBase != null) { |
224 logD("eglBase detach and release."); | 258 logD("eglBase detach and release."); |
225 eglBase.detachCurrent(); | 259 eglBase.detachCurrent(); |
226 eglBase.release(); | 260 eglBase.release(); |
227 eglBase = null; | 261 eglBase = null; |
228 } | 262 } |
229 eglCleanupBarrier.countDown(); | 263 eglCleanupBarrier.countDown(); |
230 } | 264 } |
231 }); | 265 }); |
232 final Looper renderLooper = renderThreadHandler.getLooper(); | 266 final Looper renderLooper = renderThreadHandler.getLooper(); |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
326 } | 360 } |
327 | 361 |
328 public void disableFpsReduction() { | 362 public void disableFpsReduction() { |
329 setFpsReduction(Float.POSITIVE_INFINITY /* fps */); | 363 setFpsReduction(Float.POSITIVE_INFINITY /* fps */); |
330 } | 364 } |
331 | 365 |
332 public void pauseVideo() { | 366 public void pauseVideo() { |
333 setFpsReduction(0 /* fps */); | 367 setFpsReduction(0 /* fps */); |
334 } | 368 } |
335 | 369 |
370 /** | |
371 * Register a callback to be invoked when a new video frame has been received. | |
372 * | |
373 * @param listener The callback to be invoked. | |
374 * @param scale The scale of the Bitmap passed to the callback, or 0 if no Bitmap is | |
375 * required. | |
376 */ | |
377 public void addFrameListener(FrameListener listener, float scale) { | |
378 synchronized (frameListenerLock) { | |
379 frameListeners.add(new ScaleAndFrameListener(scale, listener)); | |
380 } | |
381 } | |
382 | |
383 /** | |
384 * Remove any pending callback that was added with addFrameListener. If the ca llback is not in | |
385 * the queue, nothing happens. | |
386 * | |
387 * @param runnable The callback to remove. | |
388 */ | |
389 public void removeFrameListener(FrameListener listener) { | |
390 synchronized (frameListenerLock) { | |
391 final Iterator<ScaleAndFrameListener> iter = frameListeners.iterator(); | |
392 while (iter.hasNext()) { | |
393 if (iter.next().listener == listener) { | |
394 iter.remove(); | |
395 } | |
396 } | |
397 } | |
398 } | |
399 | |
336 // VideoRenderer.Callbacks interface. | 400 // VideoRenderer.Callbacks interface. |
337 @Override | 401 @Override |
338 public void renderFrame(VideoRenderer.I420Frame frame) { | 402 public void renderFrame(VideoRenderer.I420Frame frame) { |
339 synchronized (statisticsLock) { | 403 synchronized (statisticsLock) { |
340 ++framesReceived; | 404 ++framesReceived; |
341 } | 405 } |
342 final boolean dropOldFrame; | 406 final boolean dropOldFrame; |
343 synchronized (handlerLock) { | 407 synchronized (handlerLock) { |
344 if (renderThreadHandler == null) { | 408 if (renderThreadHandler == null) { |
345 logD("Dropping frame - Not initialized or already released."); | 409 logD("Dropping frame - Not initialized or already released."); |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
465 frame = pendingFrame; | 529 frame = pendingFrame; |
466 pendingFrame = null; | 530 pendingFrame = null; |
467 } | 531 } |
468 if (eglBase == null || !eglBase.hasSurface()) { | 532 if (eglBase == null || !eglBase.hasSurface()) { |
469 logD("Dropping frame - No surface"); | 533 logD("Dropping frame - No surface"); |
470 VideoRenderer.renderFrameDone(frame); | 534 VideoRenderer.renderFrameDone(frame); |
471 return; | 535 return; |
472 } | 536 } |
473 | 537 |
474 final long startTimeNs = System.nanoTime(); | 538 final long startTimeNs = System.nanoTime(); |
475 float[] texMatrix = | 539 final float[] texMatrix = |
476 RendererCommon.rotateTextureMatrix(frame.samplingMatrix, frame.rotationD egree); | 540 RendererCommon.rotateTextureMatrix(frame.samplingMatrix, frame.rotationD egree); |
541 final float[] drawMatrix; | |
477 | 542 |
478 // After a surface size change, the EGLSurface might still have a buffer of the old size in the | 543 // After a surface size change, the EGLSurface might still have a buffer of the old size in the |
479 // pipeline. Querying the EGLSurface will show if the underlying buffer dime nsions haven't yet | 544 // pipeline. Querying the EGLSurface will show if the underlying buffer dime nsions haven't yet |
480 // changed. Such a buffer will be rendered incorrectly, so flush it with a b lack frame. | 545 // changed. Such a buffer will be rendered incorrectly, so flush it with a b lack frame. |
481 synchronized (layoutLock) { | 546 synchronized (layoutLock) { |
482 int surfaceClearCount = 0; | 547 int surfaceClearCount = 0; |
483 while (eglBase.surfaceWidth() != surfaceWidth || eglBase.surfaceHeight() ! = surfaceHeight) { | 548 while (eglBase.surfaceWidth() != surfaceWidth || eglBase.surfaceHeight() ! = surfaceHeight) { |
484 ++surfaceClearCount; | 549 ++surfaceClearCount; |
485 if (surfaceClearCount > MAX_SURFACE_CLEAR_COUNT) { | 550 if (surfaceClearCount > MAX_SURFACE_CLEAR_COUNT) { |
486 logD("Failed to get surface of expected size - dropping frame."); | 551 logD("Failed to get surface of expected size - dropping frame."); |
487 VideoRenderer.renderFrameDone(frame); | 552 VideoRenderer.renderFrameDone(frame); |
488 return; | 553 return; |
489 } | 554 } |
490 logD("Surface size mismatch - clearing surface."); | 555 logD("Surface size mismatch - clearing surface. Size: " + eglBase.surfac eWidth() + "x" |
556 + eglBase.surfaceHeight() + " Expected: " + surfaceWidth + "x" + sur faceHeight); | |
491 clearSurfaceOnRenderThread(); | 557 clearSurfaceOnRenderThread(); |
492 } | 558 } |
493 final float[] layoutMatrix; | 559 final float[] layoutMatrix; |
494 if (layoutAspectRatio > 0) { | 560 if (layoutAspectRatio > 0) { |
495 layoutMatrix = RendererCommon.getLayoutMatrix( | 561 layoutMatrix = RendererCommon.getLayoutMatrix( |
496 mirror, frame.rotatedWidth() / (float) frame.rotatedHeight(), layout AspectRatio); | 562 mirror, frame.rotatedWidth() / (float) frame.rotatedHeight(), layout AspectRatio); |
497 } else { | 563 } else { |
498 layoutMatrix = | 564 layoutMatrix = |
499 mirror ? RendererCommon.horizontalFlipMatrix() : RendererCommon.iden tityMatrix(); | 565 mirror ? RendererCommon.horizontalFlipMatrix() : RendererCommon.iden tityMatrix(); |
500 } | 566 } |
501 texMatrix = RendererCommon.multiplyMatrices(texMatrix, layoutMatrix); | 567 drawMatrix = RendererCommon.multiplyMatrices(texMatrix, layoutMatrix); |
502 } | 568 } |
503 | 569 |
504 GLES20.glClearColor(0 /* red */, 0 /* green */, 0 /* blue */, 0 /* alpha */) ; | 570 GLES20.glClearColor(0 /* red */, 0 /* green */, 0 /* blue */, 0 /* alpha */) ; |
505 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); | 571 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); |
506 if (frame.yuvFrame) { | 572 if (frame.yuvFrame) { |
507 // Make sure YUV textures are allocated. | 573 // Make sure YUV textures are allocated. |
508 if (yuvTextures == null) { | 574 if (yuvTextures == null) { |
509 yuvTextures = new int[3]; | 575 yuvTextures = new int[3]; |
510 for (int i = 0; i < 3; i++) { | 576 for (int i = 0; i < 3; i++) { |
511 yuvTextures[i] = GlUtil.generateTexture(GLES20.GL_TEXTURE_2D); | 577 yuvTextures[i] = GlUtil.generateTexture(GLES20.GL_TEXTURE_2D); |
512 } | 578 } |
513 } | 579 } |
514 yuvUploader.uploadYuvData( | 580 yuvUploader.uploadYuvData( |
515 yuvTextures, frame.width, frame.height, frame.yuvStrides, frame.yuvPla nes); | 581 yuvTextures, frame.width, frame.height, frame.yuvStrides, frame.yuvPla nes); |
516 drawer.drawYuv(yuvTextures, texMatrix, frame.rotatedWidth(), frame.rotated Height(), 0, 0, | 582 drawer.drawYuv(yuvTextures, drawMatrix, frame.rotatedWidth(), frame.rotate dHeight(), 0, 0, |
517 surfaceWidth, surfaceHeight); | 583 surfaceWidth, surfaceHeight); |
518 } else { | 584 } else { |
519 drawer.drawOes(frame.textureId, texMatrix, frame.rotatedWidth(), frame.rot atedHeight(), 0, 0, | 585 drawer.drawOes(frame.textureId, drawMatrix, frame.rotatedWidth(), frame.ro tatedHeight(), 0, 0, |
520 surfaceWidth, surfaceHeight); | 586 surfaceWidth, surfaceHeight); |
521 } | 587 } |
522 | 588 |
589 // Notify callbacks. Make temporary copy of callback list to avoid | |
magjed_webrtc
2016/11/01 18:06:46
Move this block of code into a helper function. Al
sakal
2016/11/02 08:34:29
Done.
| |
590 // ConcurrentModificationException, in case callbacks call addFramelistener or | |
591 // removeFrameListener. | |
592 final ArrayList<ScaleAndFrameListener> tmpList = new ArrayList<>(frameListen ers); | |
593 frameListeners.clear(); | |
594 for (ScaleAndFrameListener scaleAndListener : tmpList) { | |
595 final int scaledWidth = (int) (scaleAndListener.scale * frame.rotatedWidth ()); | |
596 final int scaledHeight = (int) (scaleAndListener.scale * frame.rotatedHeig ht()); | |
597 final float[] bitmapMatrix = RendererCommon.multiplyMatrices( | |
magjed_webrtc
2016/11/01 18:06:46
Move this outside the loop.
sakal
2016/11/02 08:34:29
Done.
| |
598 RendererCommon.multiplyMatrices(texMatrix, | |
599 mirror ? RendererCommon.horizontalFlipMatrix() : RendererCommon.id entityMatrix()), | |
600 RendererCommon.verticalFlipMatrix()); | |
601 | |
602 if (scaledWidth == 0 || scaledHeight == 0) { | |
603 scaleAndListener.listener.onFrame(null); | |
604 continue; | |
605 } | |
606 if (bitmapTexture[0] == 0) { | |
607 bitmapTexture[0] = GlUtil.generateTexture(GLES20.GL_TEXTURE_2D); | |
608 bitmapTextureWidth = bitmapTextureHeight = 0; | |
609 } | |
610 if (bitmapFramebuffer[0] == 0) { | |
611 GLES20.glGenFramebuffers(1, bitmapFramebuffer, 0); | |
612 } | |
613 | |
614 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, bitmapTexture[0]); | |
615 if (scaledWidth != bitmapTextureWidth || scaledHeight != bitmapTextureHeig ht) { | |
magjed_webrtc
2016/11/01 18:06:46
Wait a minute... Maybe you can use the helper clas
sakal
2016/11/02 08:34:29
Done.
| |
616 GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, scaledWidth , scaledHeight, 0, | |
617 GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null); | |
618 bitmapTextureWidth = scaledWidth; | |
619 bitmapTextureHeight = scaledHeight; | |
620 } | |
621 GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, bitmapFramebuffer[0]); | |
622 GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTAC HMENT0, | |
623 GLES20.GL_TEXTURE_2D, bitmapTexture[0], 0); | |
624 | |
625 if (frame.yuvFrame) { | |
626 drawer.drawYuv(yuvTextures, bitmapMatrix, frame.rotatedWidth(), frame.ro tatedHeight(), 0, 0, | |
627 scaledWidth, scaledHeight); | |
628 } else { | |
629 drawer.drawOes(frame.textureId, bitmapMatrix, frame.rotatedWidth(), fram e.rotatedHeight(), | |
630 0, 0, scaledWidth, scaledHeight); | |
631 } | |
632 | |
633 final IntBuffer bitmapBuffer = IntBuffer.allocate(scaledWidth * scaledHeig ht); | |
634 GLES20.glViewport(0, 0, scaledWidth, scaledHeight); | |
635 GLES20.glReadPixels( | |
636 0, 0, scaledWidth, scaledHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BY TE, bitmapBuffer); | |
637 | |
638 GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); | |
639 GlUtil.checkNoGLES2Error("EglRenderer.renderFrameOnRenderThread - bitmap c apture"); | |
640 | |
641 // RGBA to ARGB conversion. | |
642 final int[] bitmapArray = bitmapBuffer.array(); | |
643 nativeABGRToARGB(bitmapArray, scaledWidth, scaledHeight); | |
644 | |
645 final Bitmap bitmap = Bitmap.createBitmap( | |
646 bitmapBuffer.array(), scaledWidth, scaledHeight, Bitmap.Config.ARGB_88 88); | |
647 scaleAndListener.listener.onFrame(bitmap); | |
648 } | |
649 | |
523 final long swapBuffersStartTimeNs = System.nanoTime(); | 650 final long swapBuffersStartTimeNs = System.nanoTime(); |
524 eglBase.swapBuffers(); | 651 eglBase.swapBuffers(); |
525 VideoRenderer.renderFrameDone(frame); | 652 VideoRenderer.renderFrameDone(frame); |
526 | 653 |
527 final long currentTimeNs = System.nanoTime(); | 654 final long currentTimeNs = System.nanoTime(); |
528 synchronized (statisticsLock) { | 655 synchronized (statisticsLock) { |
529 ++framesRendered; | 656 ++framesRendered; |
530 renderTimeNs += (currentTimeNs - startTimeNs); | 657 renderTimeNs += (currentTimeNs - startTimeNs); |
531 renderSwapBufferTimeNs += (currentTimeNs - swapBuffersStartTimeNs); | 658 renderSwapBufferTimeNs += (currentTimeNs - swapBuffersStartTimeNs); |
532 } | 659 } |
(...skipping 19 matching lines...) Expand all Loading... | |
552 + " Average render time: " + averageTimeAsString(renderTimeNs, framesR endered) + "." | 679 + " Average render time: " + averageTimeAsString(renderTimeNs, framesR endered) + "." |
553 + " Average swapBuffer time: " | 680 + " Average swapBuffer time: " |
554 + averageTimeAsString(renderSwapBufferTimeNs, framesRendered) + "."); | 681 + averageTimeAsString(renderSwapBufferTimeNs, framesRendered) + "."); |
555 resetStatistics(currentTimeNs); | 682 resetStatistics(currentTimeNs); |
556 } | 683 } |
557 } | 684 } |
558 | 685 |
559 private void logD(String string) { | 686 private void logD(String string) { |
560 Logging.d(TAG, name + string); | 687 Logging.d(TAG, name + string); |
561 } | 688 } |
689 | |
690 private static native void nativeABGRToARGB(int[] array, int width, int height ); | |
562 } | 691 } |
OLD | NEW |