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