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