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