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 |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
89 private final Object fpsReductionLock = new Object(); | 89 private final Object fpsReductionLock = new Object(); |
90 // Time for when next frame should be rendered. | 90 // Time for when next frame should be rendered. |
91 private long nextFrameTimeNs; | 91 private long nextFrameTimeNs; |
92 // Minimum duration between frames when fps reduction is active, or -1 if vide
o is completely | 92 // Minimum duration between frames when fps reduction is active, or -1 if vide
o is completely |
93 // paused. | 93 // paused. |
94 private long minRenderPeriodNs; | 94 private long minRenderPeriodNs; |
95 | 95 |
96 // EGL and GL resources for drawing YUV/OES textures. After initilization, the
se are only accessed | 96 // EGL and GL resources for drawing YUV/OES textures. After initilization, the
se are only accessed |
97 // from the render thread. | 97 // from the render thread. |
98 private EglBase eglBase; | 98 private EglBase eglBase; |
99 private final RendererCommon.YuvUploader yuvUploader = new RendererCommon.YuvU
ploader(); | 99 private final VideoFrameDrawer frameDrawer = new VideoFrameDrawer(); |
100 private RendererCommon.GlDrawer drawer; | 100 private RendererCommon.GlDrawer drawer; |
| 101 private final Matrix drawMatrix = new Matrix(); |
101 | 102 |
102 // Pending frame to render. Serves as a queue with size 1. Synchronized on |fr
ameLock|. | 103 // Pending frame to render. Serves as a queue with size 1. Synchronized on |fr
ameLock|. |
103 private final Object frameLock = new Object(); | 104 private final Object frameLock = new Object(); |
104 private VideoFrame pendingFrame; | 105 private VideoFrame pendingFrame; |
105 | 106 |
106 // These variables are synchronized on |layoutLock|. | 107 // These variables are synchronized on |layoutLock|. |
107 private final Object layoutLock = new Object(); | 108 private final Object layoutLock = new Object(); |
108 private float layoutAspectRatio; | 109 private float layoutAspectRatio; |
109 // If true, mirrors the video stream horizontally. | 110 // If true, mirrors the video stream horizontally. |
110 private boolean mirror; | 111 private boolean mirror; |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
220 logD("Already released"); | 221 logD("Already released"); |
221 return; | 222 return; |
222 } | 223 } |
223 renderThreadHandler.removeCallbacks(logStatisticsRunnable); | 224 renderThreadHandler.removeCallbacks(logStatisticsRunnable); |
224 // Release EGL and GL resources on render thread. | 225 // Release EGL and GL resources on render thread. |
225 renderThreadHandler.postAtFrontOfQueue(() -> { | 226 renderThreadHandler.postAtFrontOfQueue(() -> { |
226 if (drawer != null) { | 227 if (drawer != null) { |
227 drawer.release(); | 228 drawer.release(); |
228 drawer = null; | 229 drawer = null; |
229 } | 230 } |
230 yuvUploader.release(); | 231 frameDrawer.release(); |
231 if (bitmapTextureFramebuffer != null) { | 232 if (bitmapTextureFramebuffer != null) { |
232 bitmapTextureFramebuffer.release(); | 233 bitmapTextureFramebuffer.release(); |
233 bitmapTextureFramebuffer = null; | 234 bitmapTextureFramebuffer = null; |
234 } | 235 } |
235 if (eglBase != null) { | 236 if (eglBase != null) { |
236 logD("eglBase detach and release."); | 237 logD("eglBase detach and release."); |
237 eglBase.detachCurrent(); | 238 eglBase.detachCurrent(); |
238 eglBase.release(); | 239 eglBase.release(); |
239 eglBase = null; | 240 eglBase = null; |
240 } | 241 } |
(...skipping 312 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
553 } | 554 } |
554 | 555 |
555 final long startTimeNs = System.nanoTime(); | 556 final long startTimeNs = System.nanoTime(); |
556 | 557 |
557 final float frameAspectRatio = frame.getRotatedWidth() / (float) frame.getRo
tatedHeight(); | 558 final float frameAspectRatio = frame.getRotatedWidth() / (float) frame.getRo
tatedHeight(); |
558 final float drawnAspectRatio; | 559 final float drawnAspectRatio; |
559 synchronized (layoutLock) { | 560 synchronized (layoutLock) { |
560 drawnAspectRatio = layoutAspectRatio != 0f ? layoutAspectRatio : frameAspe
ctRatio; | 561 drawnAspectRatio = layoutAspectRatio != 0f ? layoutAspectRatio : frameAspe
ctRatio; |
561 } | 562 } |
562 | 563 |
563 VideoFrame.Buffer buffer = frame.getBuffer(); | |
564 final boolean isYuvBuffer; | |
565 if (buffer instanceof VideoFrame.TextureBuffer) { | |
566 isYuvBuffer = false; | |
567 } else { | |
568 isYuvBuffer = true; | |
569 VideoFrame.Buffer oldBuffer = buffer; | |
570 buffer = buffer.toI420(); | |
571 oldBuffer.release(); | |
572 } | |
573 boolean shouldUploadYuvTextures = false; | |
574 if (isYuvBuffer) { | |
575 shouldUploadYuvTextures = shouldRenderFrame; | |
576 // Check if there are frame listeners that we want to render a bitmap for
regardless of if the | |
577 // frame was rendered. This is the case when there are frameListeners with
scale != 0f. | |
578 if (!shouldUploadYuvTextures) { | |
579 for (FrameListenerAndParams listenerAndParams : frameListeners) { | |
580 if (listenerAndParams.scale != 0f | |
581 && (shouldRenderFrame || !listenerAndParams.applyFpsReduction)) { | |
582 shouldUploadYuvTextures = true; | |
583 break; | |
584 } | |
585 } | |
586 } | |
587 } | |
588 final int[] yuvTextures = shouldUploadYuvTextures | |
589 ? yuvUploader.uploadFromBuffer((VideoFrame.I420Buffer) buffer) | |
590 : null; | |
591 | |
592 final float scaleX; | 564 final float scaleX; |
593 final float scaleY; | 565 final float scaleY; |
594 | 566 |
595 if (frameAspectRatio > drawnAspectRatio) { | 567 if (frameAspectRatio > drawnAspectRatio) { |
596 scaleX = drawnAspectRatio / frameAspectRatio; | 568 scaleX = drawnAspectRatio / frameAspectRatio; |
597 scaleY = 1f; | 569 scaleY = 1f; |
598 } else { | 570 } else { |
599 scaleX = 1f; | 571 scaleX = 1f; |
600 scaleY = frameAspectRatio / drawnAspectRatio; | 572 scaleY = frameAspectRatio / drawnAspectRatio; |
601 } | 573 } |
602 | 574 |
603 final int drawnFrameWidth = (int) (scaleX * frame.getRotatedWidth()); | 575 drawMatrix.reset(); |
604 final int drawnFrameHeight = (int) (scaleY * frame.getRotatedHeight()); | |
605 | |
606 final Matrix drawMatrix = new Matrix(); | |
607 drawMatrix.preTranslate(0.5f, 0.5f); | 576 drawMatrix.preTranslate(0.5f, 0.5f); |
608 if (isYuvBuffer) | |
609 drawMatrix.preScale(1f, -1f); // I420-frames are upside down | |
610 drawMatrix.preRotate(frame.getRotation()); | |
611 if (mirror) | 577 if (mirror) |
612 drawMatrix.preScale(-1f, 1f); | 578 drawMatrix.preScale(-1f, 1f); |
613 drawMatrix.preScale(scaleX, scaleY); | 579 drawMatrix.preScale(scaleX, scaleY); |
614 drawMatrix.preTranslate(-0.5f, -0.5f); | 580 drawMatrix.preTranslate(-0.5f, -0.5f); |
615 | 581 |
616 if (shouldRenderFrame) { | 582 if (shouldRenderFrame) { |
617 GLES20.glClearColor(0 /* red */, 0 /* green */, 0 /* blue */, 0 /* alpha *
/); | 583 GLES20.glClearColor(0 /* red */, 0 /* green */, 0 /* blue */, 0 /* alpha *
/); |
618 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); | 584 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); |
619 if (isYuvBuffer) { | 585 frameDrawer.drawFrame(frame, drawer, drawMatrix, 0 /* viewportX */, 0 /* v
iewportY */, |
620 drawer.drawYuv(yuvTextures, | 586 eglBase.surfaceWidth(), eglBase.surfaceHeight()); |
621 RendererCommon.convertMatrixFromAndroidGraphicsMatrix(drawMatrix), d
rawnFrameWidth, | |
622 drawnFrameHeight, 0, 0, eglBase.surfaceWidth(), eglBase.surfaceHeigh
t()); | |
623 } else { | |
624 VideoFrame.TextureBuffer textureBuffer = (VideoFrame.TextureBuffer) buff
er; | |
625 RendererCommon.drawTexture(drawer, textureBuffer, drawMatrix, drawnFrame
Width, | |
626 drawnFrameHeight, 0, 0, eglBase.surfaceWidth(), eglBase.surfaceHeigh
t()); | |
627 } | |
628 | 587 |
629 final long swapBuffersStartTimeNs = System.nanoTime(); | 588 final long swapBuffersStartTimeNs = System.nanoTime(); |
630 eglBase.swapBuffers(); | 589 eglBase.swapBuffers(); |
631 | 590 |
632 final long currentTimeNs = System.nanoTime(); | 591 final long currentTimeNs = System.nanoTime(); |
633 synchronized (statisticsLock) { | 592 synchronized (statisticsLock) { |
634 ++framesRendered; | 593 ++framesRendered; |
635 renderTimeNs += (currentTimeNs - startTimeNs); | 594 renderTimeNs += (currentTimeNs - startTimeNs); |
636 renderSwapBufferTimeNs += (currentTimeNs - swapBuffersStartTimeNs); | 595 renderSwapBufferTimeNs += (currentTimeNs - swapBuffersStartTimeNs); |
637 } | 596 } |
638 } | 597 } |
639 | 598 |
640 notifyCallbacks(frame, isYuvBuffer, yuvTextures, shouldRenderFrame); | 599 notifyCallbacks(frame, shouldRenderFrame); |
641 buffer.release(); | 600 frame.release(); |
642 } | 601 } |
643 | 602 |
644 private void notifyCallbacks( | 603 private void notifyCallbacks(VideoFrame frame, boolean wasRendered) { |
645 VideoFrame frame, boolean isYuvBuffer, int[] yuvTextures, boolean wasRende
red) { | |
646 if (frameListeners.isEmpty()) | 604 if (frameListeners.isEmpty()) |
647 return; | 605 return; |
648 | 606 |
649 final Matrix drawMatrix = new Matrix(); | 607 drawMatrix.reset(); |
650 drawMatrix.preTranslate(0.5f, 0.5f); | 608 drawMatrix.preTranslate(0.5f, 0.5f); |
651 if (isYuvBuffer) | |
652 drawMatrix.preScale(1f, -1f); // I420-frames are upside down | |
653 drawMatrix.preRotate(frame.getRotation()); | |
654 if (mirror) | 609 if (mirror) |
655 drawMatrix.preScale(-1f, 1f); | 610 drawMatrix.preScale(-1f, 1f); |
656 drawMatrix.preScale(1f, -1f); // We want the output to be upside down for Bi
tmap. | 611 drawMatrix.preScale(1f, -1f); // We want the output to be upside down for Bi
tmap. |
657 drawMatrix.preTranslate(-0.5f, -0.5f); | 612 drawMatrix.preTranslate(-0.5f, -0.5f); |
658 | 613 |
659 Iterator<FrameListenerAndParams> it = frameListeners.iterator(); | 614 Iterator<FrameListenerAndParams> it = frameListeners.iterator(); |
660 while (it.hasNext()) { | 615 while (it.hasNext()) { |
661 FrameListenerAndParams listenerAndParams = it.next(); | 616 FrameListenerAndParams listenerAndParams = it.next(); |
662 if (!wasRendered && listenerAndParams.applyFpsReduction) { | 617 if (!wasRendered && listenerAndParams.applyFpsReduction) { |
663 continue; | 618 continue; |
(...skipping 12 matching lines...) Expand all Loading... |
676 bitmapTextureFramebuffer = new GlTextureFrameBuffer(GLES20.GL_RGBA); | 631 bitmapTextureFramebuffer = new GlTextureFrameBuffer(GLES20.GL_RGBA); |
677 } | 632 } |
678 bitmapTextureFramebuffer.setSize(scaledWidth, scaledHeight); | 633 bitmapTextureFramebuffer.setSize(scaledWidth, scaledHeight); |
679 | 634 |
680 GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, bitmapTextureFramebuffer.g
etFrameBufferId()); | 635 GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, bitmapTextureFramebuffer.g
etFrameBufferId()); |
681 GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTAC
HMENT0, | 636 GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTAC
HMENT0, |
682 GLES20.GL_TEXTURE_2D, bitmapTextureFramebuffer.getTextureId(), 0); | 637 GLES20.GL_TEXTURE_2D, bitmapTextureFramebuffer.getTextureId(), 0); |
683 | 638 |
684 GLES20.glClearColor(0 /* red */, 0 /* green */, 0 /* blue */, 0 /* alpha *
/); | 639 GLES20.glClearColor(0 /* red */, 0 /* green */, 0 /* blue */, 0 /* alpha *
/); |
685 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); | 640 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); |
686 if (isYuvBuffer) { | 641 frameDrawer.drawFrame(frame, listenerAndParams.drawer, drawMatrix, 0 /* vi
ewportX */, |
687 listenerAndParams.drawer.drawYuv(yuvTextures, | 642 0 /* viewportY */, scaledWidth, scaledHeight); |
688 RendererCommon.convertMatrixFromAndroidGraphicsMatrix(drawMatrix), | |
689 frame.getRotatedWidth(), frame.getRotatedHeight(), 0, 0, scaledWidth
, scaledHeight); | |
690 } else { | |
691 VideoFrame.TextureBuffer textureBuffer = (VideoFrame.TextureBuffer) fram
e.getBuffer(); | |
692 RendererCommon.drawTexture(listenerAndParams.drawer, textureBuffer, draw
Matrix, | |
693 frame.getRotatedWidth(), frame.getRotatedHeight(), 0, 0, scaledWidth
, scaledHeight); | |
694 } | |
695 | 643 |
696 final ByteBuffer bitmapBuffer = ByteBuffer.allocateDirect(scaledWidth * sc
aledHeight * 4); | 644 final ByteBuffer bitmapBuffer = ByteBuffer.allocateDirect(scaledWidth * sc
aledHeight * 4); |
697 GLES20.glViewport(0, 0, scaledWidth, scaledHeight); | 645 GLES20.glViewport(0, 0, scaledWidth, scaledHeight); |
698 GLES20.glReadPixels( | 646 GLES20.glReadPixels( |
699 0, 0, scaledWidth, scaledHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BY
TE, bitmapBuffer); | 647 0, 0, scaledWidth, scaledHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BY
TE, bitmapBuffer); |
700 | 648 |
701 GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); | 649 GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); |
702 GlUtil.checkNoGLES2Error("EglRenderer.notifyCallbacks"); | 650 GlUtil.checkNoGLES2Error("EglRenderer.notifyCallbacks"); |
703 | 651 |
704 final Bitmap bitmap = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitma
p.Config.ARGB_8888); | 652 final Bitmap bitmap = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitma
p.Config.ARGB_8888); |
(...skipping 23 matching lines...) Expand all Loading... |
728 + " Average swapBuffer time: " | 676 + " Average swapBuffer time: " |
729 + averageTimeAsString(renderSwapBufferTimeNs, framesRendered) + "."); | 677 + averageTimeAsString(renderSwapBufferTimeNs, framesRendered) + "."); |
730 resetStatistics(currentTimeNs); | 678 resetStatistics(currentTimeNs); |
731 } | 679 } |
732 } | 680 } |
733 | 681 |
734 private void logD(String string) { | 682 private void logD(String string) { |
735 Logging.d(TAG, name + string); | 683 Logging.d(TAG, name + string); |
736 } | 684 } |
737 } | 685 } |
OLD | NEW |