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}
This commit is contained in:
Magnus Jedvert 2016-10-05 15:56:06 +02:00
parent 3360352c2b
commit 62b1c35d30
4 changed files with 88 additions and 48 deletions

View File

@ -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).

View File

@ -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

View File

@ -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 =

View File

@ -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..