1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.media.cts; 17 18 import static junit.framework.TestCase.assertTrue; 19 20 import static org.junit.Assert.fail; 21 22 import android.media.cts.R; 23 24 import android.annotation.TargetApi; 25 import android.content.Context; 26 import android.graphics.Bitmap; 27 import android.media.MediaFormat; 28 import android.platform.test.annotations.AppModeFull; 29 import android.util.Log; 30 import android.view.View; 31 32 import com.android.compatibility.common.util.MediaUtils; 33 34 import java.lang.reflect.Field; 35 import java.util.ArrayList; 36 import java.util.Collection; 37 import java.util.List; 38 import java.util.regex.Matcher; 39 import java.util.regex.Pattern; 40 41 import org.junit.After; 42 import org.junit.Before; 43 import org.junit.Rule; 44 import org.junit.rules.Timeout; 45 import org.junit.runner.RunWith; 46 import org.junit.runners.Parameterized; 47 import org.junit.runners.Parameterized.Parameters; 48 import org.junit.Test; 49 50 @TargetApi(24) 51 @RunWith(Parameterized.class) 52 @MediaHeavyPresubmitTest 53 @AppModeFull(reason = "There should be no instant apps specific behavior related to accuracy") 54 public class DecodeAccuracyTest extends DecodeAccuracyTestBase { 55 56 private static final String TAG = DecodeAccuracyTest.class.getSimpleName(); 57 private static final Field[] fields = R.raw.class.getFields(); 58 private static final int ALLOWED_GREATEST_PIXEL_DIFFERENCE = 90; 59 private static final int OFFSET = 10; 60 private static final long PER_TEST_TIMEOUT_MS = 60000; 61 private static final String[] VIDEO_FILES = { 62 // 144p 63 "video_decode_accuracy_and_capability-h264_256x108_30fps.mp4", 64 "video_decode_accuracy_and_capability-h264_256x144_30fps.mp4", 65 "video_decode_accuracy_and_capability-h264_192x144_30fps.mp4", 66 "video_decode_accuracy_and_capability-h264_82x144_30fps.mp4", 67 "video_decode_accuracy_and_capability-vp9_256x108_30fps.webm", 68 "video_decode_accuracy_and_capability-vp9_256x144_30fps.webm", 69 "video_decode_accuracy_and_capability-vp9_192x144_30fps.webm", 70 "video_decode_accuracy_and_capability-vp9_82x144_30fps.webm", 71 // 240p 72 "video_decode_accuracy_and_capability-h264_426x182_30fps.mp4", 73 "video_decode_accuracy_and_capability-h264_426x240_30fps.mp4", 74 "video_decode_accuracy_and_capability-h264_320x240_30fps.mp4", 75 "video_decode_accuracy_and_capability-h264_136x240_30fps.mp4", 76 "video_decode_accuracy_and_capability-vp9_426x182_30fps.webm", 77 "video_decode_accuracy_and_capability-vp9_426x240_30fps.webm", 78 "video_decode_accuracy_and_capability-vp9_320x240_30fps.webm", 79 "video_decode_accuracy_and_capability-vp9_136x240_30fps.webm", 80 // 360p 81 "video_decode_accuracy_and_capability-h264_640x272_30fps.mp4", 82 "video_decode_accuracy_and_capability-h264_640x360_30fps.mp4", 83 "video_decode_accuracy_and_capability-h264_480x360_30fps.mp4", 84 "video_decode_accuracy_and_capability-h264_202x360_30fps.mp4", 85 "video_decode_accuracy_and_capability-vp9_640x272_30fps.webm", 86 "video_decode_accuracy_and_capability-vp9_640x360_30fps.webm", 87 "video_decode_accuracy_and_capability-vp9_480x360_30fps.webm", 88 "video_decode_accuracy_and_capability-vp9_202x360_30fps.webm", 89 // 480p 90 "video_decode_accuracy_and_capability-h264_854x362_30fps.mp4", 91 "video_decode_accuracy_and_capability-h264_854x480_30fps.mp4", 92 "video_decode_accuracy_and_capability-h264_640x480_30fps.mp4", 93 "video_decode_accuracy_and_capability-h264_270x480_30fps.mp4", 94 "video_decode_accuracy_and_capability-vp9_854x362_30fps.webm", 95 "video_decode_accuracy_and_capability-vp9_854x480_30fps.webm", 96 "video_decode_accuracy_and_capability-vp9_640x480_30fps.webm", 97 "video_decode_accuracy_and_capability-vp9_270x480_30fps.webm", 98 // 720p 99 "video_decode_accuracy_and_capability-h264_1280x544_30fps.mp4", 100 "video_decode_accuracy_and_capability-h264_1280x720_30fps.mp4", 101 "video_decode_accuracy_and_capability-h264_960x720_30fps.mp4", 102 "video_decode_accuracy_and_capability-h264_406x720_30fps.mp4", 103 "video_decode_accuracy_and_capability-vp9_1280x544_30fps.webm", 104 "video_decode_accuracy_and_capability-vp9_1280x720_30fps.webm", 105 "video_decode_accuracy_and_capability-vp9_960x720_30fps.webm", 106 "video_decode_accuracy_and_capability-vp9_406x720_30fps.webm", 107 // 1080p 108 "video_decode_accuracy_and_capability-h264_1920x818_30fps.mp4", 109 "video_decode_accuracy_and_capability-h264_1920x1080_30fps.mp4", 110 "video_decode_accuracy_and_capability-h264_1440x1080_30fps.mp4", 111 "video_decode_accuracy_and_capability-h264_608x1080_30fps.mp4", 112 "video_decode_accuracy_and_capability-vp9_1920x818_30fps.webm", 113 "video_decode_accuracy_and_capability-vp9_1920x1080_30fps.webm", 114 "video_decode_accuracy_and_capability-vp9_1440x1080_30fps.webm", 115 "video_decode_accuracy_and_capability-vp9_608x1080_30fps.webm", 116 // 1440p 117 "video_decode_accuracy_and_capability-h264_2560x1090_30fps.mp4", 118 "video_decode_accuracy_and_capability-h264_2560x1440_30fps.mp4", 119 "video_decode_accuracy_and_capability-h264_1920x1440_30fps.mp4", 120 "video_decode_accuracy_and_capability-h264_810x1440_30fps.mp4", 121 "video_decode_accuracy_and_capability-vp9_2560x1090_30fps.webm", 122 "video_decode_accuracy_and_capability-vp9_2560x1440_30fps.webm", 123 "video_decode_accuracy_and_capability-vp9_1920x1440_30fps.webm", 124 "video_decode_accuracy_and_capability-vp9_810x1440_30fps.webm", 125 // 2160p 126 "video_decode_accuracy_and_capability-h264_3840x1634_30fps.mp4", 127 "video_decode_accuracy_and_capability-h264_3840x2160_30fps.mp4", 128 "video_decode_accuracy_and_capability-h264_2880x2160_30fps.mp4", 129 "video_decode_accuracy_and_capability-h264_1216x2160_30fps.mp4", 130 "video_decode_accuracy_and_capability-vp9_3840x1634_30fps.webm", 131 "video_decode_accuracy_and_capability-vp9_3840x2160_30fps.webm", 132 "video_decode_accuracy_and_capability-vp9_2880x2160_30fps.webm", 133 "video_decode_accuracy_and_capability-vp9_1216x2160_30fps.webm", 134 // cropped 135 "video_decode_with_cropping-h264_520x360_30fps.mp4", 136 "video_decode_with_cropping-vp9_520x360_30fps.webm" 137 }; 138 139 private View videoView; 140 private VideoViewFactory videoViewFactory; 141 private String fileName; 142 private SimplePlayer player; 143 144 @After 145 @Override tearDown()146 public void tearDown() throws Exception { 147 if (player != null) { 148 player.release(); 149 } 150 if (videoView != null) { 151 getHelper().cleanUpView(videoView); 152 } 153 if (videoViewFactory != null) { 154 videoViewFactory.release(); 155 } 156 super.tearDown(); 157 } 158 159 @Parameters data()160 public static Collection<Object[]> data() { 161 final List<Object[]> testParams = new ArrayList<>(); 162 for (int i = 0; i < VIDEO_FILES.length; i++) { 163 final String file = VIDEO_FILES[i]; 164 Pattern regex = Pattern.compile("^\\w+-(\\w+)_\\d+fps.\\w+"); 165 Matcher matcher = regex.matcher(file); 166 String testName = ""; 167 if (matcher.matches()) { 168 testName = matcher.group(1); 169 } 170 testParams.add(new Object[] { testName.replace("_", " ").toUpperCase(), file }); 171 } 172 return testParams; 173 } 174 DecodeAccuracyTest(String testname, String fileName)175 public DecodeAccuracyTest(String testname, String fileName) { 176 this.fileName = fileName; 177 } 178 179 @Test(timeout = PER_TEST_TIMEOUT_MS) testGLViewDecodeAccuracy()180 public void testGLViewDecodeAccuracy() throws Exception { 181 runTest(new GLSurfaceViewFactory(), new VideoFormat(fileName)); 182 } 183 184 @Test(timeout = PER_TEST_TIMEOUT_MS) testGLViewLargerHeightDecodeAccuracy()185 public void testGLViewLargerHeightDecodeAccuracy() throws Exception { 186 runTest(new GLSurfaceViewFactory(), getLargerHeightVideoFormat(new VideoFormat(fileName))); 187 } 188 189 @Test(timeout = PER_TEST_TIMEOUT_MS) testGLViewLargerWidthDecodeAccuracy()190 public void testGLViewLargerWidthDecodeAccuracy() throws Exception { 191 runTest(new GLSurfaceViewFactory(), getLargerWidthVideoFormat(new VideoFormat(fileName))); 192 } 193 194 @Test(timeout = PER_TEST_TIMEOUT_MS) testSurfaceViewVideoDecodeAccuracy()195 public void testSurfaceViewVideoDecodeAccuracy() throws Exception { 196 runTest(new SurfaceViewFactory(), new VideoFormat(fileName)); 197 } 198 199 @Test(timeout = PER_TEST_TIMEOUT_MS) testSurfaceViewLargerHeightDecodeAccuracy()200 public void testSurfaceViewLargerHeightDecodeAccuracy() throws Exception { 201 runTest(new SurfaceViewFactory(), getLargerHeightVideoFormat(new VideoFormat(fileName))); 202 } 203 204 @Test(timeout = PER_TEST_TIMEOUT_MS) testSurfaceViewLargerWidthDecodeAccuracy()205 public void testSurfaceViewLargerWidthDecodeAccuracy() throws Exception { 206 runTest(new SurfaceViewFactory(), getLargerWidthVideoFormat(new VideoFormat(fileName))); 207 } 208 runTest(VideoViewFactory videoViewFactory, VideoFormat vf)209 private void runTest(VideoViewFactory videoViewFactory, VideoFormat vf) { 210 Log.i(TAG, "Running test for " + vf.toPrettyString()); 211 if (!MediaUtils.canDecodeVideo(vf.getMimeType(), vf.getWidth(), vf.getHeight(), 30)) { 212 MediaUtils.skipTest(TAG, "No supported codec is found."); 213 return; 214 } 215 this.videoViewFactory = checkNotNull(videoViewFactory); 216 this.videoView = videoViewFactory.createView(getHelper().getContext()); 217 final int maxRetries = 3; 218 for (int retry = 1; retry <= maxRetries; retry++) { 219 // If view is intended and available to display. 220 if (videoView != null) { 221 getHelper().generateView(videoView); 222 } 223 try { 224 videoViewFactory.waitForViewIsAvailable(); 225 break; 226 } catch (Exception exception) { 227 Log.e(TAG, exception.getMessage()); 228 if (retry == maxRetries) { 229 fail("Timeout waiting for a valid surface."); 230 } else { 231 Log.w(TAG, "Try again..."); 232 bringActivityToFront(); 233 } 234 } 235 } 236 final int golden = getGoldenId(vf.getDescription(), vf.getOriginalSize()); 237 assertTrue("No golden found.", golden != 0); 238 decodeVideo(vf, videoViewFactory); 239 validateResult(vf, videoViewFactory.getVideoViewSnapshot(), golden); 240 } 241 decodeVideo(VideoFormat videoFormat, VideoViewFactory videoViewFactory)242 private void decodeVideo(VideoFormat videoFormat, VideoViewFactory videoViewFactory) { 243 this.player = new SimplePlayer(getHelper().getContext()); 244 final SimplePlayer.PlayerResult playerResult = player.decodeVideoFrames( 245 videoViewFactory.getSurface(), videoFormat, 10); 246 assertTrue(playerResult.getFailureMessage(), playerResult.isSuccess()); 247 } 248 validateResult( VideoFormat videoFormat, VideoViewSnapshot videoViewSnapshot, int goldenId)249 private void validateResult( 250 VideoFormat videoFormat, VideoViewSnapshot videoViewSnapshot, int goldenId) { 251 final Bitmap result = checkNotNull("The expected bitmap from snapshot is null", 252 getHelper().generateBitmapFromVideoViewSnapshot(videoViewSnapshot)); 253 final Bitmap golden = getHelper().generateBitmapFromImageResourceId(goldenId); 254 final BitmapCompare.Difference difference = BitmapCompare.computeMinimumDifference( 255 result, golden, videoFormat.getOriginalWidth(), videoFormat.getOriginalHeight()); 256 assertTrue("With the best matched border crop (" 257 + difference.bestMatchBorderCrop.first + ", " 258 + difference.bestMatchBorderCrop.second + "), " 259 + "greatest pixel difference is " 260 + difference.greatestPixelDifference 261 + (difference.greatestPixelDifferenceCoordinates != null 262 ? " at (" + difference.greatestPixelDifferenceCoordinates.first + ", " 263 + difference.greatestPixelDifferenceCoordinates.second + ")" : "") 264 + " which is over the allowed difference " + ALLOWED_GREATEST_PIXEL_DIFFERENCE, 265 difference.greatestPixelDifference <= ALLOWED_GREATEST_PIXEL_DIFFERENCE); 266 } 267 getLargerHeightVideoFormat(VideoFormat videoFormat)268 private static VideoFormat getLargerHeightVideoFormat(VideoFormat videoFormat) { 269 return new VideoFormat(videoFormat) { 270 @Override 271 public int getHeight() { 272 return super.getHeight() + OFFSET; 273 } 274 275 @Override 276 public boolean isAbrEnabled() { 277 return true; 278 } 279 }; 280 } 281 282 private static VideoFormat getLargerWidthVideoFormat(VideoFormat videoFormat) { 283 return new VideoFormat(videoFormat) { 284 @Override 285 public int getWidth() { 286 return super.getWidth() + OFFSET; 287 } 288 289 @Override 290 public boolean isAbrEnabled() { 291 return true; 292 } 293 }; 294 } 295 296 /** 297 * Returns the resource id by matching parts of the video and golden file name. 298 */ 299 private static int getGoldenId(String description, String size) { 300 for (Field field : fields) { 301 try { 302 final String name = field.getName(); 303 if (name.contains("golden") && name.contains(description) && name.contains(size)) { 304 int id = field.getInt(null); 305 return field.getInt(null); 306 } 307 } catch (IllegalAccessException | NullPointerException e) { 308 // No file found. 309 } 310 } 311 return 0; 312 } 313 314 } 315