| Index: webrtc/sdk/android/api/org/webrtc/EglRenderer.java
|
| diff --git a/webrtc/sdk/android/api/org/webrtc/EglRenderer.java b/webrtc/sdk/android/api/org/webrtc/EglRenderer.java
|
| index e5863d3d716a144be9ca65a7cde93e1735faf73d..38d79951d34372db4cf640301170f3c3241a68ae 100644
|
| --- a/webrtc/sdk/android/api/org/webrtc/EglRenderer.java
|
| +++ b/webrtc/sdk/android/api/org/webrtc/EglRenderer.java
|
| @@ -11,6 +11,7 @@
|
| package org.webrtc;
|
|
|
| import android.graphics.Bitmap;
|
| +import android.graphics.Matrix;
|
| import android.graphics.SurfaceTexture;
|
| import android.opengl.GLES20;
|
| import android.os.Handler;
|
| @@ -29,7 +30,7 @@ import java.util.concurrent.TimeUnit;
|
| * This class is intended to be used as a helper class for rendering on SurfaceViews and
|
| * TextureViews.
|
| */
|
| -public class EglRenderer implements VideoRenderer.Callbacks {
|
| +public class EglRenderer implements VideoRenderer.Callbacks, VideoSink {
|
| private static final String TAG = "EglRenderer";
|
| private static final long LOG_INTERVAL_SEC = 4;
|
| private static final int MAX_SURFACE_CLEAR_COUNT = 3;
|
| @@ -100,7 +101,7 @@ public class EglRenderer implements VideoRenderer.Callbacks {
|
|
|
| // Pending frame to render. Serves as a queue with size 1. Synchronized on |frameLock|.
|
| private final Object frameLock = new Object();
|
| - private VideoRenderer.I420Frame pendingFrame;
|
| + private VideoFrame pendingFrame;
|
|
|
| // These variables are synchronized on |layoutLock|.
|
| private final Object layoutLock = new Object();
|
| @@ -252,7 +253,7 @@ public class EglRenderer implements VideoRenderer.Callbacks {
|
| ThreadUtils.awaitUninterruptibly(eglCleanupBarrier);
|
| synchronized (frameLock) {
|
| if (pendingFrame != null) {
|
| - VideoRenderer.renderFrameDone(pendingFrame);
|
| + pendingFrame.release();
|
| pendingFrame = null;
|
| }
|
| }
|
| @@ -414,6 +415,14 @@ public class EglRenderer implements VideoRenderer.Callbacks {
|
| // VideoRenderer.Callbacks interface.
|
| @Override
|
| public void renderFrame(VideoRenderer.I420Frame frame) {
|
| + VideoFrame videoFrame = frame.toVideoFrame();
|
| + onFrame(videoFrame);
|
| + videoFrame.release();
|
| + }
|
| +
|
| + // VideoSink interface.
|
| + @Override
|
| + public void onFrame(VideoFrame frame) {
|
| synchronized (statisticsLock) {
|
| ++framesReceived;
|
| }
|
| @@ -421,15 +430,16 @@ public class EglRenderer implements VideoRenderer.Callbacks {
|
| synchronized (handlerLock) {
|
| if (renderThreadHandler == null) {
|
| logD("Dropping frame - Not initialized or already released.");
|
| - VideoRenderer.renderFrameDone(frame);
|
| + frame.release();
|
| return;
|
| }
|
| synchronized (frameLock) {
|
| dropOldFrame = (pendingFrame != null);
|
| if (dropOldFrame) {
|
| - VideoRenderer.renderFrameDone(pendingFrame);
|
| + pendingFrame.release();
|
| }
|
| pendingFrame = frame;
|
| + pendingFrame.retain();
|
| renderThreadHandler.post(this ::renderFrameOnRenderThread);
|
| }
|
| }
|
| @@ -507,7 +517,7 @@ public class EglRenderer implements VideoRenderer.Callbacks {
|
| */
|
| private void renderFrameOnRenderThread() {
|
| // Fetch and render |pendingFrame|.
|
| - final VideoRenderer.I420Frame frame;
|
| + final VideoFrame frame;
|
| synchronized (frameLock) {
|
| if (pendingFrame == null) {
|
| return;
|
| @@ -517,7 +527,7 @@ public class EglRenderer implements VideoRenderer.Callbacks {
|
| }
|
| if (eglBase == null || !eglBase.hasSurface()) {
|
| logD("Dropping frame - No surface");
|
| - VideoRenderer.renderFrameDone(frame);
|
| + frame.release();
|
| return;
|
| }
|
| // Check if fps reduction is active.
|
| @@ -544,38 +554,25 @@ public class EglRenderer implements VideoRenderer.Callbacks {
|
| }
|
|
|
| final long startTimeNs = System.nanoTime();
|
| - final float[] texMatrix =
|
| - RendererCommon.rotateTextureMatrix(frame.samplingMatrix, frame.rotationDegree);
|
| - final float[] drawMatrix;
|
| -
|
| - // After a surface size change, the EGLSurface might still have a buffer of the old size in the
|
| - // pipeline. Querying the EGLSurface will show if the underlying buffer dimensions haven't yet
|
| - // changed. Such a buffer will be rendered incorrectly, so flush it with a black frame.
|
| - final int drawnFrameWidth;
|
| - final int drawnFrameHeight;
|
| +
|
| + final float frameAspectRatio = frame.getRotatedWidth() / (float) frame.getRotatedHeight();
|
| + final float drawnAspectRatio;
|
| synchronized (layoutLock) {
|
| - final float[] layoutMatrix;
|
| - if (layoutAspectRatio > 0) {
|
| - final float frameAspectRatio = frame.rotatedWidth() / (float) frame.rotatedHeight();
|
| - layoutMatrix = RendererCommon.getLayoutMatrix(mirror, frameAspectRatio, layoutAspectRatio);
|
| - if (frameAspectRatio > layoutAspectRatio) {
|
| - drawnFrameWidth = (int) (frame.rotatedHeight() * layoutAspectRatio);
|
| - drawnFrameHeight = frame.rotatedHeight();
|
| - } else {
|
| - drawnFrameWidth = frame.rotatedWidth();
|
| - drawnFrameHeight = (int) (frame.rotatedWidth() / layoutAspectRatio);
|
| - }
|
| - } else {
|
| - layoutMatrix =
|
| - mirror ? RendererCommon.horizontalFlipMatrix() : RendererCommon.identityMatrix();
|
| - drawnFrameWidth = frame.rotatedWidth();
|
| - drawnFrameHeight = frame.rotatedHeight();
|
| - }
|
| - drawMatrix = RendererCommon.multiplyMatrices(texMatrix, layoutMatrix);
|
| + drawnAspectRatio = layoutAspectRatio != 0f ? layoutAspectRatio : frameAspectRatio;
|
| }
|
|
|
| + VideoFrame.Buffer buffer = frame.getBuffer();
|
| + final boolean isYuvBuffer;
|
| + if (buffer instanceof VideoFrame.TextureBuffer) {
|
| + isYuvBuffer = false;
|
| + } else {
|
| + isYuvBuffer = true;
|
| + VideoFrame.Buffer oldBuffer = buffer;
|
| + buffer = buffer.toI420();
|
| + oldBuffer.release();
|
| + }
|
| boolean shouldUploadYuvTextures = false;
|
| - if (frame.yuvFrame) {
|
| + if (isYuvBuffer) {
|
| shouldUploadYuvTextures = shouldRenderFrame;
|
| // Check if there are frame listeners that we want to render a bitmap for regardless of if the
|
| // frame was rendered. This is the case when there are frameListeners with scale != 0f.
|
| @@ -590,17 +587,43 @@ public class EglRenderer implements VideoRenderer.Callbacks {
|
| }
|
| }
|
| final int[] yuvTextures = shouldUploadYuvTextures
|
| - ? yuvUploader.uploadYuvData(frame.width, frame.height, frame.yuvStrides, frame.yuvPlanes)
|
| + ? yuvUploader.uploadFromBuffer((VideoFrame.I420Buffer) buffer)
|
| : null;
|
|
|
| + final float scaleX;
|
| + final float scaleY;
|
| +
|
| + if (frameAspectRatio > drawnAspectRatio) {
|
| + scaleX = drawnAspectRatio / frameAspectRatio;
|
| + scaleY = 1f;
|
| + } else {
|
| + scaleX = 1f;
|
| + scaleY = frameAspectRatio / drawnAspectRatio;
|
| + }
|
| +
|
| + final int drawnFrameWidth = (int) (scaleX * frame.getRotatedWidth());
|
| + final int drawnFrameHeight = (int) (scaleY * frame.getRotatedHeight());
|
| +
|
| + final Matrix drawMatrix = new Matrix();
|
| + drawMatrix.preTranslate(0.5f, 0.5f);
|
| + if (isYuvBuffer)
|
| + drawMatrix.preScale(1f, -1f); // I420-frames are upside down
|
| + drawMatrix.preRotate(frame.getRotation());
|
| + if (mirror)
|
| + drawMatrix.preScale(-1f, 1f);
|
| + drawMatrix.preScale(scaleX, scaleY);
|
| + drawMatrix.preTranslate(-0.5f, -0.5f);
|
| +
|
| if (shouldRenderFrame) {
|
| GLES20.glClearColor(0 /* red */, 0 /* green */, 0 /* blue */, 0 /* alpha */);
|
| GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
| - if (frame.yuvFrame) {
|
| - drawer.drawYuv(yuvTextures, drawMatrix, drawnFrameWidth, drawnFrameHeight, 0, 0,
|
| - eglBase.surfaceWidth(), eglBase.surfaceHeight());
|
| + if (isYuvBuffer) {
|
| + drawer.drawYuv(yuvTextures,
|
| + RendererCommon.convertMatrixFromAndroidGraphicsMatrix(drawMatrix), drawnFrameWidth,
|
| + drawnFrameHeight, 0, 0, eglBase.surfaceWidth(), eglBase.surfaceHeight());
|
| } else {
|
| - drawer.drawOes(frame.textureId, drawMatrix, drawnFrameWidth, drawnFrameHeight, 0, 0,
|
| + VideoFrame.TextureBuffer textureBuffer = (VideoFrame.TextureBuffer) buffer;
|
| + drawer.drawTexture(textureBuffer, drawMatrix, drawnFrameWidth, drawnFrameHeight, 0, 0,
|
| eglBase.surfaceWidth(), eglBase.surfaceHeight());
|
| }
|
|
|
| @@ -615,19 +638,24 @@ public class EglRenderer implements VideoRenderer.Callbacks {
|
| }
|
| }
|
|
|
| - notifyCallbacks(frame, yuvTextures, texMatrix, shouldRenderFrame);
|
| - VideoRenderer.renderFrameDone(frame);
|
| + notifyCallbacks(frame, isYuvBuffer, yuvTextures, shouldRenderFrame);
|
| + frame.release();
|
| }
|
|
|
| private void notifyCallbacks(
|
| - VideoRenderer.I420Frame frame, int[] yuvTextures, float[] texMatrix, boolean wasRendered) {
|
| + VideoFrame frame, boolean isYuvBuffer, int[] yuvTextures, boolean wasRendered) {
|
| if (frameListeners.isEmpty())
|
| return;
|
|
|
| - final float[] bitmapMatrix = RendererCommon.multiplyMatrices(
|
| - RendererCommon.multiplyMatrices(texMatrix,
|
| - mirror ? RendererCommon.horizontalFlipMatrix() : RendererCommon.identityMatrix()),
|
| - RendererCommon.verticalFlipMatrix());
|
| + final Matrix drawMatrix = new Matrix();
|
| + drawMatrix.preTranslate(0.5f, 0.5f);
|
| + if (isYuvBuffer)
|
| + drawMatrix.preScale(1f, -1f); // I420-frames are upside down
|
| + drawMatrix.preRotate(frame.getRotation());
|
| + if (mirror)
|
| + drawMatrix.preScale(-1f, 1f);
|
| + drawMatrix.preScale(1f, -1f); // We want the output to be upside down for Bitmap.
|
| + drawMatrix.preTranslate(-0.5f, -0.5f);
|
|
|
| Iterator<FrameListenerAndParams> it = frameListeners.iterator();
|
| while (it.hasNext()) {
|
| @@ -637,8 +665,8 @@ public class EglRenderer implements VideoRenderer.Callbacks {
|
| }
|
| it.remove();
|
|
|
| - final int scaledWidth = (int) (listenerAndParams.scale * frame.rotatedWidth());
|
| - final int scaledHeight = (int) (listenerAndParams.scale * frame.rotatedHeight());
|
| + final int scaledWidth = (int) (listenerAndParams.scale * frame.getRotatedWidth());
|
| + final int scaledHeight = (int) (listenerAndParams.scale * frame.getRotatedHeight());
|
|
|
| if (scaledWidth == 0 || scaledHeight == 0) {
|
| listenerAndParams.listener.onFrame(null);
|
| @@ -656,12 +684,14 @@ public class EglRenderer implements VideoRenderer.Callbacks {
|
|
|
| GLES20.glClearColor(0 /* red */, 0 /* green */, 0 /* blue */, 0 /* alpha */);
|
| GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
|
| - if (frame.yuvFrame) {
|
| - listenerAndParams.drawer.drawYuv(yuvTextures, bitmapMatrix, frame.rotatedWidth(),
|
| - frame.rotatedHeight(), 0 /* viewportX */, 0 /* viewportY */, scaledWidth, scaledHeight);
|
| + if (isYuvBuffer) {
|
| + drawer.drawYuv(yuvTextures,
|
| + RendererCommon.convertMatrixFromAndroidGraphicsMatrix(drawMatrix),
|
| + frame.getRotatedWidth(), frame.getRotatedHeight(), 0, 0, scaledWidth, scaledHeight);
|
| } else {
|
| - listenerAndParams.drawer.drawOes(frame.textureId, bitmapMatrix, frame.rotatedWidth(),
|
| - frame.rotatedHeight(), 0 /* viewportX */, 0 /* viewportY */, scaledWidth, scaledHeight);
|
| + VideoFrame.TextureBuffer textureBuffer = (VideoFrame.TextureBuffer) frame.getBuffer();
|
| + drawer.drawTexture(textureBuffer, drawMatrix, frame.getRotatedWidth(),
|
| + frame.getRotatedHeight(), 0, 0, scaledWidth, scaledHeight);
|
| }
|
|
|
| final ByteBuffer bitmapBuffer = ByteBuffer.allocateDirect(scaledWidth * scaledHeight * 4);
|
|
|