1 /*
2  * Copyright (C) 2021 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 
17 package android.mediapc.cts;
18 
19 import android.media.MediaCodec;
20 import android.media.MediaCodecInfo;
21 import android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint;
22 import android.media.MediaFormat;
23 import android.util.Pair;
24 
25 import org.junit.Before;
26 
27 import java.io.IOException;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Map;
33 
34 import static android.mediapc.cts.CodecTestBase.selectHardwareCodecs;
35 import static org.junit.Assert.assertTrue;
36 import static org.junit.Assume.assumeTrue;
37 
38 public class MultiCodecPerfTestBase {
39     private static final String LOG_TAG = MultiCodecPerfTestBase.class.getSimpleName();
40     static final boolean[] boolStates = {true, false};
41     static final int REQUIRED_MIN_CONCURRENT_INSTANCES = 6;
42     static final int REQUIRED_MIN_CONCURRENT_INSTANCES_FOR_VP9 = 2;
43     // allowed tolerance in measured fps vs expected fps in percentage, i.e. codecs achieving fps
44     // that is greater than (FPS_TOLERANCE_FACTOR * expectedFps) will be considered as
45     // passing the test
46     static final double FPS_TOLERANCE_FACTOR = 0.95;
47     static ArrayList<String> mMimeList = new ArrayList<String>();
48     static Map<String, String> mTestFiles = new HashMap<>();
49     static {
50         mMimeList.add(MediaFormat.MIMETYPE_VIDEO_AVC);
51         mMimeList.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
52 
mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_1280x720_3mbps_30fps_avc.mp4")53         mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_1280x720_3mbps_30fps_avc.mp4");
mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_1280x720_3mbps_30fps_hevc.mp4")54         mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_1280x720_3mbps_30fps_hevc.mp4");
55 
56         // Test VP9 and AV1 as well for Build.VERSION_CODES.S
57         if (Utils.isSPerfClass()) {
58             mMimeList.add(MediaFormat.MIMETYPE_VIDEO_VP9);
59             mMimeList.add(MediaFormat.MIMETYPE_VIDEO_AV1);
60 
mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_1280x720_3mbps_30fps_vp9.webm")61             mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_1280x720_3mbps_30fps_vp9.webm");
mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_1280x720_3mbps_30fps_av1.mp4")62             mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_1280x720_3mbps_30fps_av1.mp4");
63         }
64     }
65 
66     String mMime;
67     String mTestFile;
68     final boolean mIsAsync;
69 
70     double mMaxFrameRate;
71 
72     @Before
isPerformanceClass()73     public void isPerformanceClass() {
74         assumeTrue("Test requires performance class.", Utils.isPerfClass());
75     }
76 
MultiCodecPerfTestBase(String mime, String testFile, boolean isAsync)77     public MultiCodecPerfTestBase(String mime, String testFile, boolean isAsync) {
78         mMime = mime;
79         mTestFile = testFile;
80         mIsAsync = isAsync;
81     }
82 
83     // Returns the list of hardware codecs supporting the 720p 30fps format.
getHardwareCodecsFor720p(String mime, boolean isEncoder)84     public static ArrayList<String> getHardwareCodecsFor720p(String mime, boolean isEncoder) {
85         MediaFormat fmt = MediaFormat.createVideoFormat(mime, 1280, 720);
86         fmt.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
87         ArrayList<MediaFormat> formatsList = new ArrayList<>();
88         formatsList.add(fmt);
89         return selectHardwareCodecs(mime, formatsList, null, isEncoder);
90     }
91 
92     // Returns the max number of 720p 30 fps instances that the given list of mimeCodecPairs
93     // supports. It also checks that the each codec supports 720p 180 fps PerformancePoint.
checkAndGetMaxSupportedInstancesFor720p( ArrayList<Pair<String, String>> mimeCodecPairs)94     public int checkAndGetMaxSupportedInstancesFor720p(
95             ArrayList<Pair<String, String>> mimeCodecPairs) throws IOException {
96         int[] maxInstances = new int[mimeCodecPairs.size()];
97         int[] maxFrameRates = new int[mimeCodecPairs.size()];
98         int[] maxMacroBlockRates = new int[mimeCodecPairs.size()];
99         int loopCount = 0;
100         for (Pair<String, String> mimeCodecPair : mimeCodecPairs) {
101             MediaCodec codec = MediaCodec.createByCodecName(mimeCodecPair.second);
102             MediaCodecInfo.CodecCapabilities cap = codec.getCodecInfo()
103                     .getCapabilitiesForType(mimeCodecPair.first);
104             List<PerformancePoint> pps = cap.getVideoCapabilities().getSupportedPerformancePoints();
105             assertTrue(pps.size() > 0);
106 
107             int requiredFrameRate = 180;
108             // VP9 requires 60 fps at 720p and minimum of 2 instances
109             if (mimeCodecPair.first.equals(MediaFormat.MIMETYPE_VIDEO_VP9)) {
110                 requiredFrameRate = 60;
111             }
112 
113             maxInstances[loopCount] = cap.getMaxSupportedInstances();
114             PerformancePoint PP720p = new PerformancePoint(1280, 720, requiredFrameRate);
115 
116             maxMacroBlockRates[loopCount] = 0;
117             boolean supports720pPerformance = false;
118             for (PerformancePoint pp : pps) {
119                 if (pp.covers(PP720p)) {
120                     supports720pPerformance = true;
121                     if (pp.getMaxMacroBlockRate() > maxMacroBlockRates[loopCount]) {
122                         maxMacroBlockRates[loopCount] = (int) pp.getMaxMacroBlockRate();
123                         maxFrameRates[loopCount] = pp.getMaxFrameRate();
124                     }
125                 }
126             }
127             codec.release();
128             assertTrue("Codec " + mimeCodecPair.second + " doesn't support 720p " +
129                     requiredFrameRate + " performance point", supports720pPerformance);
130             loopCount++;
131         }
132         Arrays.sort(maxInstances);
133         Arrays.sort(maxFrameRates);
134         Arrays.sort(maxMacroBlockRates);
135         int minOfMaxInstances = maxInstances[0];
136         int minOfMaxFrameRates = maxFrameRates[0];
137         int minOfMaxMacroBlockRates = maxMacroBlockRates[0];
138 
139         // Allow a tolerance in expected frame rate
140         mMaxFrameRate = minOfMaxFrameRates * FPS_TOLERANCE_FACTOR;
141 
142         // Calculate how many 720p 30fps max instances it can support from it's mMaxFrameRate
143         // amd maxMacroBlockRate. (720p is 3,600 macro blocks assuming 16x16 macroblocks)
144         return Math.min(minOfMaxInstances, Math.min((int) (minOfMaxFrameRates / 30.0),
145                 (int) (minOfMaxMacroBlockRates / 3600.0 / 30)));
146     }
147 }
148