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