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