From 62b1c35d300152c456102a304104953af6c781b4 Mon Sep 17 00:00:00 2001 From: Magnus Jedvert Date: Wed, 5 Oct 2016 15:56:06 +0200 Subject: [PATCH] Android: Move layout measure code from SurfaceViewRenderer to RendererCommon BUG=webrtc:6470 R=sakal@webrtc.org Review URL: https://codereview.webrtc.org/2386253003 . Cr-Commit-Position: refs/heads/master@{#14526} --- .../java/src/org/webrtc/RendererCommon.java | 51 +++++++++++++ .../src/org/webrtc/SurfaceViewRenderer.java | 71 +++++++------------ .../SurfaceViewRendererOnMeasureTest.java | 4 +- .../base/java/src/org/webrtc/ThreadUtils.java | 10 +++ 4 files changed, 88 insertions(+), 48 deletions(-) diff --git a/webrtc/api/android/java/src/org/webrtc/RendererCommon.java b/webrtc/api/android/java/src/org/webrtc/RendererCommon.java index 3cec5b0cd3..8eb046128d 100644 --- a/webrtc/api/android/java/src/org/webrtc/RendererCommon.java +++ b/webrtc/api/android/java/src/org/webrtc/RendererCommon.java @@ -13,6 +13,7 @@ package org.webrtc; import android.graphics.Point; import android.opengl.GLES20; import android.opengl.Matrix; +import android.view.View; import java.nio.ByteBuffer; @@ -103,6 +104,56 @@ public class RendererCommon { } } + /** + * Helper class for determining layout size based on layout requirements, scaling type, and video + * aspect ratio. + */ + public static class VideoLayoutMeasure { + // The scaling type determines how the video will fill the allowed layout area in measure(). It + // can be specified separately for the case when video has matched orientation with layout size + // and when there is an orientation mismatch. + private ScalingType scalingTypeMatchOrientation = ScalingType.SCALE_ASPECT_BALANCED; + private ScalingType scalingTypeMismatchOrientation = ScalingType.SCALE_ASPECT_BALANCED; + + public void setScalingType(ScalingType scalingType) { + this.scalingTypeMatchOrientation = scalingType; + this.scalingTypeMismatchOrientation = scalingType; + } + + public void setScalingType( + ScalingType scalingTypeMatchOrientation, ScalingType scalingTypeMismatchOrientation) { + this.scalingTypeMatchOrientation = scalingTypeMatchOrientation; + this.scalingTypeMismatchOrientation = scalingTypeMismatchOrientation; + } + + public Point measure(int widthSpec, int heightSpec, int frameWidth, int frameHeight) { + // Calculate max allowed layout size. + final int maxWidth = View.getDefaultSize(Integer.MAX_VALUE, widthSpec); + final int maxHeight = View.getDefaultSize(Integer.MAX_VALUE, heightSpec); + if (frameWidth == 0 || frameHeight == 0 || maxWidth == 0 || maxHeight == 0) { + return new Point(maxWidth, maxHeight); + } + // Calculate desired display size based on scaling type, video aspect ratio, + // and maximum layout size. + final float frameAspect = frameWidth / (float) frameHeight; + final float displayAspect = maxWidth / (float) maxHeight; + final RendererCommon.ScalingType scalingType = (frameAspect > 1.0f) == (displayAspect > 1.0f) + ? scalingTypeMatchOrientation + : scalingTypeMismatchOrientation; + final Point layoutSize = + RendererCommon.getDisplaySize(scalingType, frameAspect, maxWidth, maxHeight); + + // If the measure specification is forcing a specific size - yield. + if (View.MeasureSpec.getMode(widthSpec) == View.MeasureSpec.EXACTLY) { + layoutSize.x = maxWidth; + } + if (View.MeasureSpec.getMode(heightSpec) == View.MeasureSpec.EXACTLY) { + layoutSize.y = maxHeight; + } + return layoutSize; + } + } + // Types of video scaling: // SCALE_ASPECT_FIT - video frame is scaled to fit the size of the view by // maintaining the aspect ratio (black borders may be displayed). diff --git a/webrtc/api/android/java/src/org/webrtc/SurfaceViewRenderer.java b/webrtc/api/android/java/src/org/webrtc/SurfaceViewRenderer.java index eaaf24b173..5b19c43179 100644 --- a/webrtc/api/android/java/src/org/webrtc/SurfaceViewRenderer.java +++ b/webrtc/api/android/java/src/org/webrtc/SurfaceViewRenderer.java @@ -77,11 +77,12 @@ public class SurfaceViewRenderer // |isSurfaceCreated| keeps track of the current status in surfaceCreated()/surfaceDestroyed(). private boolean isSurfaceCreated; // Last rendered frame dimensions, or 0 if no frame has been rendered yet. - private int frameWidth; - private int frameHeight; + private int rotatedFrameWidth; + private int rotatedFrameHeight; private int frameRotation; - // |scalingType| determines how the video will fill the allowed layout area in onMeasure(). - private RendererCommon.ScalingType scalingType = RendererCommon.ScalingType.SCALE_ASPECT_BALANCED; + private final RendererCommon.VideoLayoutMeasure videoLayoutMeasure = + new RendererCommon.VideoLayoutMeasure(); + // If true, mirrors the video stream horizontally. private boolean mirror; // Callback for reporting renderer events. @@ -243,8 +244,8 @@ public class SurfaceViewRenderer renderThread = null; // Reset statistics and event reporting. synchronized (layoutLock) { - frameWidth = 0; - frameHeight = 0; + rotatedFrameWidth = 0; + rotatedFrameHeight = 0; frameRotation = 0; rendererEvents = null; } @@ -278,9 +279,14 @@ public class SurfaceViewRenderer * Set how the video will fill the allowed layout area. */ public void setScalingType(RendererCommon.ScalingType scalingType) { - synchronized (layoutLock) { - this.scalingType = scalingType; - } + ThreadUtils.checkIsOnMainThread(); + videoLayoutMeasure.setScalingType(scalingType); + } + + public void setScalingType(RendererCommon.ScalingType scalingTypeMatchOrientation, + RendererCommon.ScalingType scalingTypeMismatchOrientation) { + ThreadUtils.checkIsOnMainThread(); + videoLayoutMeasure.setScalingType(scalingTypeMatchOrientation, scalingTypeMismatchOrientation); } // VideoRenderer.Callbacks interface. @@ -309,33 +315,14 @@ public class SurfaceViewRenderer } } - // Returns desired layout size given current measure specification and video aspect ratio. - private Point getDesiredLayoutSize(int widthSpec, int heightSpec) { - synchronized (layoutLock) { - final int maxWidth = getDefaultSize(Integer.MAX_VALUE, widthSpec); - final int maxHeight = getDefaultSize(Integer.MAX_VALUE, heightSpec); - final Point size = - RendererCommon.getDisplaySize(scalingType, frameAspectRatio(), maxWidth, maxHeight); - if (MeasureSpec.getMode(widthSpec) == MeasureSpec.EXACTLY) { - size.x = maxWidth; - } - if (MeasureSpec.getMode(heightSpec) == MeasureSpec.EXACTLY) { - size.y = maxHeight; - } - return size; - } - } - // View layout interface. @Override protected void onMeasure(int widthSpec, int heightSpec) { + ThreadUtils.checkIsOnMainThread(); final boolean isNewSize; synchronized (layoutLock) { - if (frameWidth == 0 || frameHeight == 0) { - super.onMeasure(widthSpec, heightSpec); - return; - } - desiredLayoutSize = getDesiredLayoutSize(widthSpec, heightSpec); + desiredLayoutSize = + videoLayoutMeasure.measure(widthSpec, heightSpec, rotatedFrameWidth, rotatedFrameHeight); isNewSize = (desiredLayoutSize.x != getMeasuredWidth() || desiredLayoutSize.y != getMeasuredHeight()); setMeasuredDimension(desiredLayoutSize.x, desiredLayoutSize.y); @@ -487,8 +474,9 @@ public class SurfaceViewRenderer synchronized (layoutLock) { final float[] rotatedSamplingMatrix = RendererCommon.rotateTextureMatrix(frame.samplingMatrix, frame.rotationDegree); - final float[] layoutMatrix = RendererCommon.getLayoutMatrix( - mirror, frameAspectRatio(), (float) layoutSize.x / layoutSize.y); + final float[] layoutMatrix = RendererCommon.getLayoutMatrix(mirror, + frame.rotatedWidth() / (float) frame.rotatedHeight(), + layoutSize.x / (float) layoutSize.y); texMatrix = RendererCommon.multiplyMatrices(rotatedSamplingMatrix, layoutMatrix); } @@ -532,29 +520,18 @@ public class SurfaceViewRenderer } } - // Return current frame aspect ratio, taking rotation into account. - private float frameAspectRatio() { - synchronized (layoutLock) { - if (frameWidth == 0 || frameHeight == 0) { - return 0.0f; - } - return (frameRotation % 180 == 0) ? (float) frameWidth / frameHeight - : (float) frameHeight / frameWidth; - } - } - // Update frame dimensions and report any changes to |rendererEvents|. private void updateFrameDimensionsAndReportEvents(VideoRenderer.I420Frame frame) { synchronized (layoutLock) { - if (frameWidth != frame.width || frameHeight != frame.height + if (rotatedFrameWidth != frame.rotatedWidth() || rotatedFrameHeight != frame.rotatedHeight() || frameRotation != frame.rotationDegree) { Logging.d(TAG, getResourceName() + "Reporting frame resolution changed to " + frame.width + "x" + frame.height + " with rotation " + frame.rotationDegree); if (rendererEvents != null) { rendererEvents.onFrameResolutionChanged(frame.width, frame.height, frame.rotationDegree); } - frameWidth = frame.width; - frameHeight = frame.height; + rotatedFrameWidth = frame.rotatedWidth(); + rotatedFrameHeight = frame.rotatedHeight(); frameRotation = frame.rotationDegree; post(new Runnable() { @Override diff --git a/webrtc/api/androidtests/src/org/webrtc/SurfaceViewRendererOnMeasureTest.java b/webrtc/api/androidtests/src/org/webrtc/SurfaceViewRendererOnMeasureTest.java index 300cdf8131..dc4d9b7d78 100644 --- a/webrtc/api/androidtests/src/org/webrtc/SurfaceViewRendererOnMeasureTest.java +++ b/webrtc/api/androidtests/src/org/webrtc/SurfaceViewRendererOnMeasureTest.java @@ -12,9 +12,9 @@ package org.webrtc; import android.graphics.Point; import android.test.ActivityTestCase; +import android.test.UiThreadTest; import android.test.suitebuilder.annotation.MediumTest; import android.view.View.MeasureSpec; - import java.nio.ByteBuffer; import java.util.Arrays; import java.util.List; @@ -68,6 +68,7 @@ public final class SurfaceViewRendererOnMeasureTest extends ActivityTestCase { /** * Test how SurfaceViewRenderer.onMeasure() behaves when no frame has been delivered. */ + @UiThreadTest @MediumTest public void testNoFrame() { final SurfaceViewRenderer surfaceViewRenderer = @@ -105,6 +106,7 @@ public final class SurfaceViewRendererOnMeasureTest extends ActivityTestCase { /** * Test how SurfaceViewRenderer.onMeasure() behaves with a 1280x720 frame. */ + @UiThreadTest @MediumTest public void testFrame1280x720() throws InterruptedException { final SurfaceViewRenderer surfaceViewRenderer = diff --git a/webrtc/base/java/src/org/webrtc/ThreadUtils.java b/webrtc/base/java/src/org/webrtc/ThreadUtils.java index 4337fd0349..efe1fbd884 100644 --- a/webrtc/base/java/src/org/webrtc/ThreadUtils.java +++ b/webrtc/base/java/src/org/webrtc/ThreadUtils.java @@ -11,6 +11,7 @@ package org.webrtc; import android.os.Handler; +import android.os.Looper; import android.os.SystemClock; import java.util.concurrent.Callable; @@ -38,6 +39,15 @@ public class ThreadUtils { } } + /** + * Throws exception if called from other than main thread. + */ + public static void checkIsOnMainThread() { + if (Thread.currentThread() != Looper.getMainLooper().getThread()) { + throw new IllegalStateException("Not on main thread!"); + } + } + /** * Utility interface to be used with executeUninterruptibly() to wait for blocking operations * to complete without getting interrupted..