Home | History | Annotate | Download | only in cts
      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