1 /*
2  * Copyright (C) 2015 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 com.android.compatibility.common.deviceinfo;
17 
18 import android.annotation.TargetApi;
19 import android.media.MediaCodecInfo;
20 import android.media.MediaCodecInfo.AudioCapabilities;
21 import android.media.MediaCodecInfo.CodecCapabilities;
22 import android.media.MediaCodecInfo.CodecProfileLevel;
23 import android.media.MediaCodecInfo.VideoCapabilities;
24 import android.media.MediaCodecList;
25 import android.os.Build;
26 import android.util.Range;
27 
28 import com.android.compatibility.common.util.ApiLevelUtil;
29 import com.android.compatibility.common.util.DeviceInfoStore;
30 
31 import java.util.Arrays;
32 
33 /**
34  * Media information collector.
35  */
36 public final class MediaDeviceInfo extends DeviceInfo {
37 
38     @Override
39     @TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
collectDeviceInfo(DeviceInfoStore store)40     protected void collectDeviceInfo(DeviceInfoStore store) throws Exception {
41         MediaCodecList allCodecs = new MediaCodecList(MediaCodecList.ALL_CODECS);
42         store.startArray("media_codec_info");
43         for (MediaCodecInfo info : allCodecs.getCodecInfos()) {
44 
45             store.startGroup();
46             store.addResult("name", info.getName());
47             if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.Q)) {
48                 store.addResult("canonical", info.getCanonicalName());
49             }
50             store.addResult("encoder", info.isEncoder());
51             if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.Q)) {
52                 store.addResult("alias", info.isAlias());
53                 store.addResult("software", info.isSoftwareOnly());
54                 store.addResult("hardware", info.isHardwareAccelerated());
55                 store.addResult("vendor", info.isVendor());
56             }
57 
58             store.startArray("supported_type");
59             for (String type : info.getSupportedTypes()) {
60                 store.startGroup();
61                 store.addResult("type", type);
62                 CodecCapabilities codecCapabilities = info.getCapabilitiesForType(type);
63                 if (codecCapabilities.profileLevels.length > 0) {
64                     store.startArray("codec_profile_level");
65                     for (CodecProfileLevel profileLevel : codecCapabilities.profileLevels) {
66                         store.startGroup();
67                         store.addResult("level", profileLevel.level);
68                         store.addResult("profile", profileLevel.profile);
69                         store.endGroup();
70                     }
71                     store.endArray(); // codec_profile_level
72                 }
73                 if (codecCapabilities.colorFormats.length > 0) {
74                     store.addArrayResult("codec_color_format", codecCapabilities.colorFormats);
75                 }
76                 store.addResult("supported_secure_playback", codecCapabilities.isFeatureSupported(
77                         CodecCapabilities.FEATURE_SecurePlayback));
78                 store.addResult("supported_hdr_editing", codecCapabilities.isFeatureSupported(
79                         CodecCapabilities.FEATURE_HdrEditing));
80                 VideoCapabilities videoCapabilities = codecCapabilities.getVideoCapabilities();
81                 if (videoCapabilities != null) {
82                     store.startGroup("supported_resolutions");
83                     store.addResult(
84                             "supported_360p_30fps",
85                             videoCapabilities.areSizeAndRateSupported(640, 360, 30));
86                     store.addResult(
87                             "supported_480p_30fps",
88                             videoCapabilities.areSizeAndRateSupported(720, 480, 30));
89                     store.addResult(
90                             "supported_720p_30fps",
91                             videoCapabilities.areSizeAndRateSupported(1280, 720, 30));
92                     store.addResult(
93                             "supported_1080p_30fps",
94                             videoCapabilities.areSizeAndRateSupported(1920, 1080, 30));
95                     // The QHD/WQHD 2560x1440 resolution is used to create YouTube and PlayMovies
96                     // 2k content, so use that resolution to determine if a device supports 2k.
97                     store.addResult(
98                             "supported_2k_30fps",
99                             videoCapabilities.areSizeAndRateSupported(2560, 1440, 30));
100                     store.addResult(
101                             "supported_4k_30fps",
102                             videoCapabilities.areSizeAndRateSupported(3840, 2160, 30));
103                     store.addResult(
104                             "supported_8k_30fps",
105                             videoCapabilities.areSizeAndRateSupported(7680, 4320, 30));
106                     store.addResult(
107                             "supported_360p_60fps",
108                             videoCapabilities.areSizeAndRateSupported(640, 360, 60));
109                     store.addResult(
110                             "supported_480p_60fps",
111                             videoCapabilities.areSizeAndRateSupported(720, 480, 60));
112                     store.addResult(
113                             "supported_720p_60fps",
114                             videoCapabilities.areSizeAndRateSupported(1280, 720, 60));
115                     store.addResult(
116                             "supported_1080p_60fps",
117                             videoCapabilities.areSizeAndRateSupported(1920, 1080, 60));
118                     store.addResult(
119                             "supported_2k_60fps",
120                             videoCapabilities.areSizeAndRateSupported(2560, 1440, 60));
121                     store.addResult(
122                             "supported_4k_60fps",
123                             videoCapabilities.areSizeAndRateSupported(3840, 2160, 60));
124                     store.addResult(
125                             "supported_8k_60fps",
126                             videoCapabilities.areSizeAndRateSupported(7680, 4320, 60));
127                     store.endGroup(); // supported_resolutions
128                     store.addResult("width_alignment", videoCapabilities.getWidthAlignment());
129                     store.addResult("height_alignment", videoCapabilities.getHeightAlignment());
130                     // get min & max resolution
131                     Range<Integer> widthRange = videoCapabilities.getSupportedWidths();
132                     int minWidth = widthRange.getLower();
133                     int minPixelCount = minWidth
134                             * videoCapabilities.getSupportedHeightsFor(minWidth).getLower();
135                     int maxWidth = widthRange.getUpper();
136                     int maxPixelCount = maxWidth
137                             * videoCapabilities.getSupportedHeightsFor(maxWidth).getUpper();
138                     store.addResult("min_pixel_count", minPixelCount);
139                     store.addResult("max_pixel_count", maxPixelCount);
140                 }
141                 AudioCapabilities audioCapabilities = codecCapabilities.getAudioCapabilities();
142                 if (audioCapabilities != null) {
143                     if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.Q)) {
144                         int minSampleRate = -1, maxSampleRate = -1;
145                         int[] discreteSampleRates = audioCapabilities.getSupportedSampleRates();
146                         if (discreteSampleRates != null) {  // codec supports only discrete sample rates
147                             minSampleRate = Arrays.stream(discreteSampleRates).min().getAsInt();
148                             maxSampleRate = Arrays.stream(discreteSampleRates).max().getAsInt();
149                         } else {  // codec supports continuous sample rates
150                             Range<Integer>[] sampleRateRanges =
151                                              audioCapabilities.getSupportedSampleRateRanges();
152                             minSampleRate = sampleRateRanges[0].getLower();
153                             maxSampleRate = sampleRateRanges[sampleRateRanges.length - 1].getUpper();
154                         }
155                         store.addResult("min_sample_rate", minSampleRate);
156                         store.addResult("max_sample_rate", maxSampleRate);
157                     }
158 
159                     store.addResult("max_channel_count",
160                                 audioCapabilities.getMaxInputChannelCount());
161                     if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S)) {
162                         store.addResult("min_channel_count",
163                                     audioCapabilities.getMinInputChannelCount());
164                     }
165                 }
166                 store.endGroup();
167             }
168             store.endArray();
169             store.endGroup();
170         }
171 
172         store.endArray(); // media_codec_profile
173     }
174 }
175