1 /* 2 * Copyright 2015 The WebRTC project authors. All Rights Reserved. 3 * 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 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 package org.webrtc; 12 13 import static org.junit.Assert.assertEquals; 14 import static org.junit.Assert.fail; 15 16 import android.annotation.SuppressLint; 17 import android.graphics.Point; 18 import android.support.test.InstrumentationRegistry; 19 import android.support.test.annotation.UiThreadTest; 20 import android.support.test.filters.MediumTest; 21 import android.support.test.rule.UiThreadTestRule; 22 import android.view.View.MeasureSpec; 23 import java.nio.ByteBuffer; 24 import java.util.Arrays; 25 import java.util.List; 26 import org.chromium.base.test.BaseJUnit4ClassRunner; 27 import org.junit.Rule; 28 import org.junit.Test; 29 import org.junit.runner.RunWith; 30 31 @RunWith(BaseJUnit4ClassRunner.class) 32 public class SurfaceViewRendererOnMeasureTest { 33 @Rule public UiThreadTestRule uiThreadTestRule = new UiThreadTestRule(); 34 35 /** 36 * List with all possible scaling types. 37 */ 38 private static final List<RendererCommon.ScalingType> scalingTypes = Arrays.asList( 39 RendererCommon.ScalingType.SCALE_ASPECT_FIT, RendererCommon.ScalingType.SCALE_ASPECT_FILL, 40 RendererCommon.ScalingType.SCALE_ASPECT_BALANCED); 41 42 /** 43 * List with MeasureSpec modes. 44 */ 45 private static final List<Integer> measureSpecModes = 46 Arrays.asList(MeasureSpec.EXACTLY, MeasureSpec.AT_MOST); 47 48 /** 49 * Returns a dummy YUV frame. 50 */ createFrame(int width, int height, int rotationDegree)51 static VideoFrame createFrame(int width, int height, int rotationDegree) { 52 final int[] yuvStrides = new int[] {width, (width + 1) / 2, (width + 1) / 2}; 53 final int[] yuvHeights = new int[] {height, (height + 1) / 2, (height + 1) / 2}; 54 final ByteBuffer[] yuvPlanes = new ByteBuffer[3]; 55 for (int i = 0; i < 3; ++i) { 56 yuvPlanes[i] = ByteBuffer.allocateDirect(yuvStrides[i] * yuvHeights[i]); 57 } 58 final VideoFrame.I420Buffer buffer = 59 JavaI420Buffer.wrap(width, height, yuvPlanes[0], yuvStrides[0], yuvPlanes[1], yuvStrides[1], 60 yuvPlanes[2], yuvStrides[2], null /* releaseCallback */); 61 return new VideoFrame(buffer, rotationDegree, 0 /* timestamp */); 62 } 63 64 /** 65 * Assert onMeasure() with given parameters will result in expected measured size. 66 */ 67 @SuppressLint("WrongCall") assertMeasuredSize(SurfaceViewRenderer surfaceViewRenderer, RendererCommon.ScalingType scalingType, String frameDimensions, int expectedWidth, int expectedHeight, int widthSpec, int heightSpec)68 private static void assertMeasuredSize(SurfaceViewRenderer surfaceViewRenderer, 69 RendererCommon.ScalingType scalingType, String frameDimensions, int expectedWidth, 70 int expectedHeight, int widthSpec, int heightSpec) { 71 surfaceViewRenderer.setScalingType(scalingType); 72 surfaceViewRenderer.onMeasure(widthSpec, heightSpec); 73 final int measuredWidth = surfaceViewRenderer.getMeasuredWidth(); 74 final int measuredHeight = surfaceViewRenderer.getMeasuredHeight(); 75 if (measuredWidth != expectedWidth || measuredHeight != expectedHeight) { 76 fail("onMeasure(" + MeasureSpec.toString(widthSpec) + ", " + MeasureSpec.toString(heightSpec) 77 + ")" 78 + " with scaling type " + scalingType + " and frame: " + frameDimensions 79 + " expected measured size " + expectedWidth + "x" + expectedHeight + ", but was " 80 + measuredWidth + "x" + measuredHeight); 81 } 82 } 83 84 /** 85 * Test how SurfaceViewRenderer.onMeasure() behaves when no frame has been delivered. 86 */ 87 @Test 88 @UiThreadTest 89 @MediumTest testNoFrame()90 public void testNoFrame() { 91 final SurfaceViewRenderer surfaceViewRenderer = 92 new SurfaceViewRenderer(InstrumentationRegistry.getContext()); 93 final String frameDimensions = "null"; 94 95 // Test behaviour before SurfaceViewRenderer.init() is called. 96 for (RendererCommon.ScalingType scalingType : scalingTypes) { 97 for (int measureSpecMode : measureSpecModes) { 98 final int zeroMeasureSize = MeasureSpec.makeMeasureSpec(0, measureSpecMode); 99 assertMeasuredSize(surfaceViewRenderer, scalingType, frameDimensions, 0, 0, zeroMeasureSize, 100 zeroMeasureSize); 101 assertMeasuredSize(surfaceViewRenderer, scalingType, frameDimensions, 1280, 720, 102 MeasureSpec.makeMeasureSpec(1280, measureSpecMode), 103 MeasureSpec.makeMeasureSpec(720, measureSpecMode)); 104 } 105 } 106 107 // Test behaviour after SurfaceViewRenderer.init() is called, but still no frame. 108 surfaceViewRenderer.init((EglBase.Context) null, null); 109 for (RendererCommon.ScalingType scalingType : scalingTypes) { 110 for (int measureSpecMode : measureSpecModes) { 111 final int zeroMeasureSize = MeasureSpec.makeMeasureSpec(0, measureSpecMode); 112 assertMeasuredSize(surfaceViewRenderer, scalingType, frameDimensions, 0, 0, zeroMeasureSize, 113 zeroMeasureSize); 114 assertMeasuredSize(surfaceViewRenderer, scalingType, frameDimensions, 1280, 720, 115 MeasureSpec.makeMeasureSpec(1280, measureSpecMode), 116 MeasureSpec.makeMeasureSpec(720, measureSpecMode)); 117 } 118 } 119 120 surfaceViewRenderer.release(); 121 } 122 123 /** 124 * Test how SurfaceViewRenderer.onMeasure() behaves with a 1280x720 frame. 125 */ 126 @Test 127 @UiThreadTest 128 @MediumTest testFrame1280x720()129 public void testFrame1280x720() throws InterruptedException { 130 final SurfaceViewRenderer surfaceViewRenderer = 131 new SurfaceViewRenderer(InstrumentationRegistry.getContext()); 132 /** 133 * Mock renderer events with blocking wait functionality for frame size changes. 134 */ 135 class MockRendererEvents implements RendererCommon.RendererEvents { 136 private int frameWidth; 137 private int frameHeight; 138 private int rotation; 139 140 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 141 @SuppressWarnings("NoSynchronizedMethodCheck") 142 public synchronized void waitForFrameSize(int frameWidth, int frameHeight, int rotation) 143 throws InterruptedException { 144 while (this.frameWidth != frameWidth || this.frameHeight != frameHeight 145 || this.rotation != rotation) { 146 wait(); 147 } 148 } 149 150 @Override 151 public void onFirstFrameRendered() {} 152 153 @Override 154 // TODO(bugs.webrtc.org/8491): Remove NoSynchronizedMethodCheck suppression. 155 @SuppressWarnings("NoSynchronizedMethodCheck") 156 public synchronized void onFrameResolutionChanged( 157 int frameWidth, int frameHeight, int rotation) { 158 this.frameWidth = frameWidth; 159 this.frameHeight = frameHeight; 160 this.rotation = rotation; 161 notifyAll(); 162 } 163 } 164 final MockRendererEvents rendererEvents = new MockRendererEvents(); 165 surfaceViewRenderer.init((EglBase.Context) null, rendererEvents); 166 167 // Test different rotation degress, but same rotated size. 168 for (int rotationDegree : new int[] {0, 90, 180, 270}) { 169 final int rotatedWidth = 1280; 170 final int rotatedHeight = 720; 171 final int unrotatedWidth = (rotationDegree % 180 == 0 ? rotatedWidth : rotatedHeight); 172 final int unrotatedHeight = (rotationDegree % 180 == 0 ? rotatedHeight : rotatedWidth); 173 final VideoFrame frame = createFrame(unrotatedWidth, unrotatedHeight, rotationDegree); 174 assertEquals(rotatedWidth, frame.getRotatedWidth()); 175 assertEquals(rotatedHeight, frame.getRotatedHeight()); 176 final String frameDimensions = 177 unrotatedWidth + "x" + unrotatedHeight + " with rotation " + rotationDegree; 178 surfaceViewRenderer.onFrame(frame); 179 frame.release(); 180 rendererEvents.waitForFrameSize(unrotatedWidth, unrotatedHeight, rotationDegree); 181 182 // Test forcing to zero size. 183 for (RendererCommon.ScalingType scalingType : scalingTypes) { 184 for (int measureSpecMode : measureSpecModes) { 185 final int zeroMeasureSize = MeasureSpec.makeMeasureSpec(0, measureSpecMode); 186 assertMeasuredSize(surfaceViewRenderer, scalingType, frameDimensions, 0, 0, 187 zeroMeasureSize, zeroMeasureSize); 188 } 189 } 190 191 // Test perfect fit. 192 for (RendererCommon.ScalingType scalingType : scalingTypes) { 193 for (int measureSpecMode : measureSpecModes) { 194 assertMeasuredSize(surfaceViewRenderer, scalingType, frameDimensions, rotatedWidth, 195 rotatedHeight, MeasureSpec.makeMeasureSpec(rotatedWidth, measureSpecMode), 196 MeasureSpec.makeMeasureSpec(rotatedHeight, measureSpecMode)); 197 } 198 } 199 200 // Force spec size with different aspect ratio than frame aspect ratio. 201 for (RendererCommon.ScalingType scalingType : scalingTypes) { 202 assertMeasuredSize(surfaceViewRenderer, scalingType, frameDimensions, 720, 1280, 203 MeasureSpec.makeMeasureSpec(720, MeasureSpec.EXACTLY), 204 MeasureSpec.makeMeasureSpec(1280, MeasureSpec.EXACTLY)); 205 } 206 207 final float videoAspectRatio = (float) rotatedWidth / rotatedHeight; 208 { 209 // Relax both width and height constraints. 210 final int widthSpec = MeasureSpec.makeMeasureSpec(720, MeasureSpec.AT_MOST); 211 final int heightSpec = MeasureSpec.makeMeasureSpec(1280, MeasureSpec.AT_MOST); 212 for (RendererCommon.ScalingType scalingType : scalingTypes) { 213 final Point expectedSize = 214 RendererCommon.getDisplaySize(scalingType, videoAspectRatio, 720, 1280); 215 assertMeasuredSize(surfaceViewRenderer, scalingType, frameDimensions, expectedSize.x, 216 expectedSize.y, widthSpec, heightSpec); 217 } 218 } 219 { 220 // Force width to 720, but relax height constraint. This will give the same result as 221 // above, because width is already the limiting factor and will be maxed out. 222 final int widthSpec = MeasureSpec.makeMeasureSpec(720, MeasureSpec.EXACTLY); 223 final int heightSpec = MeasureSpec.makeMeasureSpec(1280, MeasureSpec.AT_MOST); 224 for (RendererCommon.ScalingType scalingType : scalingTypes) { 225 final Point expectedSize = 226 RendererCommon.getDisplaySize(scalingType, videoAspectRatio, 720, 1280); 227 assertMeasuredSize(surfaceViewRenderer, scalingType, frameDimensions, expectedSize.x, 228 expectedSize.y, widthSpec, heightSpec); 229 } 230 } 231 { 232 // Force height, but relax width constraint. This will force a bad layout size. 233 final int widthSpec = MeasureSpec.makeMeasureSpec(720, MeasureSpec.AT_MOST); 234 final int heightSpec = MeasureSpec.makeMeasureSpec(1280, MeasureSpec.EXACTLY); 235 for (RendererCommon.ScalingType scalingType : scalingTypes) { 236 assertMeasuredSize( 237 surfaceViewRenderer, scalingType, frameDimensions, 720, 1280, widthSpec, heightSpec); 238 } 239 } 240 } 241 242 surfaceViewRenderer.release(); 243 } 244 } 245