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