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.mediastress.cts.preconditions.app;
17 
18 import android.app.Instrumentation;
19 import android.media.MediaFormat;
20 import android.os.Bundle;
21 import android.util.Log;
22 
23 import androidx.test.InstrumentationRegistry;
24 
25 import com.android.compatibility.common.util.DynamicConfigDeviceSide;
26 import com.android.compatibility.common.util.MediaUtils;
27 
28 import org.junit.Test;
29 import org.junit.runner.RunWith;
30 import org.junit.runners.JUnit4;
31 
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.regex.Matcher;
35 import java.util.regex.Pattern;
36 
37 /**
38  * Test class that uses device-side media APIs to determine up to which resolution MediaPreparer
39  * should copy media files for CtsMediaStressTestCases.
40  */
41 @RunWith(JUnit4.class)
42 public class MediaPreparerAppTest {
43 
44     private static final String TAG = MediaPreparerAppTest.class.getSimpleName();
45 
46     /** The module name used to retrieve Dynamic Configuration data */
47     private static final String MODULE_NAME = "CtsMediaStressTestCases";
48 
49     /** The default (minimum) resolution of media file to copy to the device */
50     private static final int DEFAULT_MAX_WIDTH = 480;
51     private static final int DEFAULT_MAX_HEIGHT = 360;
52 
53     /** Instrumentation status code used to write resolution to metrics */
54     private static final int INST_STATUS_IN_PROGRESS = 2;
55 
56     /** Helper class for generating and retrieving width-height pairs */
57     private static final class Resolution {
58         // regex that matches a resolution string
59         private static final String PATTERN = "(\\d+)x(\\d+)";
60         // group indices for accessing resolution witdh/height from a Matcher created from PATTERN
61         private static final int WIDTH_INDEX = 1;
62         private static final int HEIGHT_INDEX = 2;
63 
64         private final int width;
65         private final int height;
66 
Resolution(int width, int height)67         private Resolution(int width, int height) {
68             this.width = width;
69             this.height = height;
70         }
71 
Resolution(String resolution)72         private Resolution(String resolution) {
73             Pattern pattern = Pattern.compile(PATTERN);
74             Matcher matcher = pattern.matcher(resolution);
75             matcher.find();
76             this.width = Integer.parseInt(matcher.group(WIDTH_INDEX));
77             this.height = Integer.parseInt(matcher.group(HEIGHT_INDEX));
78         }
79 
80         @Override
toString()81         public String toString() {
82             return String.format("%dx%d", width, height);
83         }
84     }
85 
86     @Test
testGetResolutions()87     public void testGetResolutions() throws Exception {
88         String moduleName = InstrumentationRegistry.getArguments().getString("module-name");
89         if (moduleName == null) {
90             moduleName = MODULE_NAME;
91             Log.i(TAG, "module-name argument is null. Using the default module name: "
92                     + moduleName);
93         } else {
94             Log.i(TAG, "module-name is set from the argument: " + moduleName);
95         }
96         Resolution maxRes = new Resolution(DEFAULT_MAX_WIDTH, DEFAULT_MAX_HEIGHT);
97         DynamicConfigDeviceSide config = new DynamicConfigDeviceSide(moduleName);
98         for (String key : config.keySet()) {
99             int width = 0;
100             int height = 0;
101             for (MediaFormat format : stringsToFormats(config.getValues(key))) {
102                 try {
103                     width = Math.max(width, format.getInteger(MediaFormat.KEY_WIDTH));
104                     height = Math.max(height, format.getInteger(MediaFormat.KEY_HEIGHT));
105                 } catch (NullPointerException | ClassCastException e) {
106                     // audio format, or invalid format created by unrelated dynamic config entry
107                     // simply continue in this case
108                 }
109             }
110             Resolution fileResolution = new Resolution(width, height);
111             // if the file is of greater resolution than maxRes, check for support
112             if (fileResolution.width > maxRes.width) {
113                 boolean supported = true;
114                 for (MediaFormat format : stringsToFormats(config.getValues(key))) {
115                     supported &= MediaUtils.checkDecoderForFormat(format);
116                 }
117                 if (supported) {
118                     // update if all MediaFormats for file are supported by device
119                     maxRes = fileResolution;
120                 }
121             }
122         }
123         // write resolution string to metrics
124         Instrumentation inst = InstrumentationRegistry.getInstrumentation();
125         Bundle maxResBundle = new Bundle();
126         maxResBundle.putString("resolution", maxRes.toString());
127         inst.sendStatus(INST_STATUS_IN_PROGRESS, maxResBundle);
128     }
129 
130     /**
131      * Converts string representations of MediaFormats into actual MediaFormats
132      * @param formatStrings a list of string representations of MediaFormats. Each input string
133      * may represent one or more MediaFormats
134      * @return a list of MediaFormats
135      */
stringsToFormats(List<String> formatStrings)136     private List<MediaFormat> stringsToFormats(List<String> formatStrings) {
137         List<MediaFormat> formats = new ArrayList<MediaFormat>();
138         for (String formatString : formatStrings) {
139             for (String trackFormatString : formatString.split(";")) {
140                 formats.add(parseTrackFormat(trackFormatString));
141             }
142         }
143         return formats;
144     }
145 
146     /**
147      * Converts a single media track format string into a MediaFormat object
148      * @param trackFormatString a string representation of the format of one media track
149      * @return a MediaFormat
150      */
parseTrackFormat(String trackFormatString)151     private static MediaFormat parseTrackFormat(String trackFormatString) {
152         MediaFormat format = new MediaFormat();
153         format.setString(MediaFormat.KEY_MIME, "");
154         for (String entry : trackFormatString.split(",")) {
155             String[] kv = entry.split("=");
156             if (kv.length < 2) {
157                 continue;
158             }
159             String k = kv[0];
160             String v = kv[1];
161             try {
162                 format.setInteger(k, Integer.parseInt(v));
163             } catch(NumberFormatException e) {
164                 format.setString(k, v);
165             }
166         }
167         return format;
168     }
169 }
170