1 /*
2 * Copyright (C) 2018 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 //#define LOG_NDEBUG 0
18 #define LOG_TAG "Codec2InfoBuilder"
19 #include <log/log.h>
20
21 #include <codec2/hidl/client.h>
22
23 #include <C2Component.h>
24 #include <C2Config.h>
25 #include <C2Debug.h>
26 #include <C2PlatformSupport.h>
27 #include <C2V4l2Support.h>
28 #include <Codec2Mapper.h>
29
30 #include <android-base/properties.h>
31 #include <media/stagefright/MediaCodecConstants.h>
32 #include <media/stagefright/foundation/MediaDefs.h>
33 #include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
34
35 #include "Codec2InfoBuilder.h"
36
37 namespace android {
38
39 using Traits = C2Component::Traits;
40
41 namespace /* unnamed */ {
42
hasPrefix(const std::string & s,const char * prefix)43 bool hasPrefix(const std::string& s, const char* prefix) {
44 size_t prefixLen = strlen(prefix);
45 return s.compare(0, prefixLen, prefix) == 0;
46 }
47
hasSuffix(const std::string & s,const char * suffix)48 bool hasSuffix(const std::string& s, const char* suffix) {
49 size_t suffixLen = strlen(suffix);
50 return suffixLen > s.size() ? false :
51 s.compare(s.size() - suffixLen, suffixLen, suffix) == 0;
52 }
53
54 } // unnamed namespace
55
buildMediaCodecList(MediaCodecListWriter * writer)56 status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) {
57 // TODO: Remove run-time configurations once all codecs are working
58 // properly. (Assume "full" behavior eventually.)
59 //
60 // debug.stagefright.ccodec supports 5 values.
61 // 0 - Only OMX components are available.
62 // 1 - Codec2.0 software audio decoders/encoders are available and
63 // ranked 1st.
64 // Components with "c2.vda." prefix are available with their normal
65 // ranks.
66 // Other components with ".avc.decoder" or ".avc.encoder" suffix are
67 // available, but ranked last.
68 // 2 - All Codec2.0 components are available.
69 // Codec2.0 software audio decoders are ranked 1st.
70 // The other Codec2.0 components have their normal ranks.
71 // 3 - All Codec2.0 components are available.
72 // Codec2.0 software components are ranked 1st.
73 // The other Codec2.0 components have their normal ranks.
74 // 4 - All Codec2.0 components are available with their normal ranks.
75 //
76 // The default value (boot time) is 1.
77 //
78 // Note: Currently, OMX components have default rank 0x100, while all
79 // Codec2.0 software components have default rank 0x200.
80 int option = ::android::base::GetIntProperty("debug.stagefright.ccodec", 1);
81
82 // Obtain Codec2Client
83 std::vector<Traits> traits = Codec2Client::ListComponents();
84
85 MediaCodecsXmlParser parser(
86 MediaCodecsXmlParser::defaultSearchDirs,
87 "media_codecs_c2.xml");
88 if (parser.getParsingStatus() != OK) {
89 ALOGD("XML parser no good");
90 return OK;
91 }
92 for (const Traits& trait : traits) {
93 if (parser.getCodecMap().count(trait.name.c_str()) == 0) {
94 ALOGD("%s not found in xml", trait.name.c_str());
95 continue;
96 }
97
98 // TODO: Remove this block once all codecs are enabled by default.
99 C2Component::rank_t rank = trait.rank;
100 switch (option) {
101 case 0:
102 continue;
103 case 1:
104 if (hasPrefix(trait.name, "c2.vda.")) {
105 break;
106 }
107 if (hasPrefix(trait.name, "c2.android.")) {
108 if (trait.domain == C2Component::DOMAIN_AUDIO) {
109 rank = 1;
110 break;
111 }
112 continue;
113 }
114 if (hasSuffix(trait.name, ".avc.decoder") ||
115 hasSuffix(trait.name, ".avc.encoder")) {
116 rank = std::numeric_limits<decltype(rank)>::max();
117 break;
118 }
119 continue;
120 case 2:
121 if (trait.domain == C2Component::DOMAIN_AUDIO &&
122 trait.kind == C2Component::KIND_DECODER) {
123 case 3:
124 if (hasPrefix(trait.name, "c2.android.")) {
125 rank = 1;
126 }
127 }
128 break;
129 }
130
131 const MediaCodecsXmlParser::CodecProperties &codec = parser.getCodecMap().at(trait.name);
132 std::unique_ptr<MediaCodecInfoWriter> codecInfo = writer->addMediaCodecInfo();
133 codecInfo->setName(trait.name.c_str());
134 codecInfo->setOwner("dummy");
135 // TODO: get this from trait.kind
136 bool encoder = (trait.name.find("encoder") != std::string::npos);
137 codecInfo->setEncoder(encoder);
138 codecInfo->setRank(rank);
139 for (auto typeIt = codec.typeMap.begin(); typeIt != codec.typeMap.end(); ++typeIt) {
140 const std::string &mediaType = typeIt->first;
141 const MediaCodecsXmlParser::AttributeMap &attrMap = typeIt->second;
142 std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
143 codecInfo->addMime(mediaType.c_str());
144 for (auto attrIt = attrMap.begin(); attrIt != attrMap.end(); ++attrIt) {
145 std::string key, value;
146 std::tie(key, value) = *attrIt;
147 if (key.find("feature-") == 0 && key.find("feature-bitrate-modes") != 0) {
148 caps->addDetail(key.c_str(), std::stoi(value));
149 } else {
150 caps->addDetail(key.c_str(), value.c_str());
151 }
152 }
153
154 bool gotProfileLevels = false;
155 std::shared_ptr<Codec2Client::Interface> intf =
156 Codec2Client::CreateInterfaceByName(trait.name.c_str());
157 if (intf) {
158 std::shared_ptr<C2Mapper::ProfileLevelMapper> mapper =
159 C2Mapper::GetProfileLevelMapper(trait.mediaType);
160 // if we don't know the media type, pass through all values unmapped
161
162 // TODO: we cannot find levels that are local 'maxima' without knowing the coding
163 // e.g. H.263 level 45 and level 30 could be two values for highest level as
164 // they don't include one another. For now we use the last supported value.
165 C2StreamProfileLevelInfo pl(encoder /* output */, 0u);
166 std::vector<C2FieldSupportedValuesQuery> profileQuery = {
167 C2FieldSupportedValuesQuery::Possible(C2ParamField(&pl, &pl.profile))
168 };
169
170 c2_status_t err = intf->querySupportedValues(profileQuery, C2_DONT_BLOCK);
171 ALOGV("query supported profiles -> %s | %s",
172 asString(err), asString(profileQuery[0].status));
173 if (err == C2_OK && profileQuery[0].status == C2_OK) {
174 if (profileQuery[0].values.type == C2FieldSupportedValues::VALUES) {
175 for (C2Value::Primitive profile : profileQuery[0].values.values) {
176 pl.profile = (C2Config::profile_t)profile.ref<uint32_t>();
177 std::vector<std::unique_ptr<C2SettingResult>> failures;
178 err = intf->config({&pl}, C2_DONT_BLOCK, &failures);
179 ALOGV("set profile to %u -> %s", pl.profile, asString(err));
180 std::vector<C2FieldSupportedValuesQuery> levelQuery = {
181 C2FieldSupportedValuesQuery::Current(C2ParamField(&pl, &pl.level))
182 };
183 err = intf->querySupportedValues(levelQuery, C2_DONT_BLOCK);
184 ALOGV("query supported levels -> %s | %s",
185 asString(err), asString(levelQuery[0].status));
186 if (err == C2_OK && levelQuery[0].status == C2_OK) {
187 if (levelQuery[0].values.type == C2FieldSupportedValues::VALUES
188 && levelQuery[0].values.values.size() > 0) {
189 C2Value::Primitive level = levelQuery[0].values.values.back();
190 pl.level = (C2Config::level_t)level.ref<uint32_t>();
191 ALOGV("supporting level: %u", pl.level);
192 int32_t sdkProfile, sdkLevel;
193 if (mapper && mapper->mapProfile(pl.profile, &sdkProfile)
194 && mapper->mapLevel(pl.level, &sdkLevel)) {
195 caps->addProfileLevel(
196 (uint32_t)sdkProfile, (uint32_t)sdkLevel);
197 gotProfileLevels = true;
198 } else if (!mapper) {
199 caps->addProfileLevel(pl.profile, pl.level);
200 gotProfileLevels = true;
201 }
202 }
203 }
204 }
205 }
206 }
207 }
208
209 if (!gotProfileLevels) {
210 if (mediaType == MIMETYPE_VIDEO_VP9) {
211 if (encoder) {
212 caps->addProfileLevel(VP9Profile0, VP9Level41);
213 } else {
214 caps->addProfileLevel(VP9Profile0, VP9Level5);
215 caps->addProfileLevel(VP9Profile2, VP9Level5);
216 caps->addProfileLevel(VP9Profile2HDR, VP9Level5);
217 }
218 } else if (mediaType == MIMETYPE_VIDEO_HEVC && !encoder) {
219 caps->addProfileLevel(HEVCProfileMain, HEVCMainTierLevel51);
220 caps->addProfileLevel(HEVCProfileMainStill, HEVCMainTierLevel51);
221 } else if (mediaType == MIMETYPE_VIDEO_VP8) {
222 if (encoder) {
223 caps->addProfileLevel(VP8ProfileMain, VP8Level_Version0);
224 } else {
225 caps->addProfileLevel(VP8ProfileMain, VP8Level_Version0);
226 }
227 } else if (mediaType == MIMETYPE_VIDEO_AVC) {
228 if (encoder) {
229 caps->addProfileLevel(AVCProfileBaseline, AVCLevel41);
230 // caps->addProfileLevel(AVCProfileConstrainedBaseline, AVCLevel41);
231 caps->addProfileLevel(AVCProfileMain, AVCLevel41);
232 } else {
233 caps->addProfileLevel(AVCProfileBaseline, AVCLevel52);
234 caps->addProfileLevel(AVCProfileConstrainedBaseline, AVCLevel52);
235 caps->addProfileLevel(AVCProfileMain, AVCLevel52);
236 caps->addProfileLevel(AVCProfileConstrainedHigh, AVCLevel52);
237 caps->addProfileLevel(AVCProfileHigh, AVCLevel52);
238 }
239 } else if (mediaType == MIMETYPE_VIDEO_MPEG4) {
240 if (encoder) {
241 caps->addProfileLevel(MPEG4ProfileSimple, MPEG4Level2);
242 } else {
243 caps->addProfileLevel(MPEG4ProfileSimple, MPEG4Level3);
244 }
245 } else if (mediaType == MIMETYPE_VIDEO_H263) {
246 if (encoder) {
247 caps->addProfileLevel(H263ProfileBaseline, H263Level45);
248 } else {
249 caps->addProfileLevel(H263ProfileBaseline, H263Level30);
250 caps->addProfileLevel(H263ProfileBaseline, H263Level45);
251 caps->addProfileLevel(H263ProfileISWV2, H263Level30);
252 caps->addProfileLevel(H263ProfileISWV2, H263Level45);
253 }
254 } else if (mediaType == MIMETYPE_VIDEO_MPEG2 && !encoder) {
255 caps->addProfileLevel(MPEG2ProfileSimple, MPEG2LevelHL);
256 caps->addProfileLevel(MPEG2ProfileMain, MPEG2LevelHL);
257 }
258 }
259
260 // TODO: get this from intf() as well, but how do we map them to
261 // MediaCodec color formats?
262 if (mediaType.find("video") != std::string::npos) {
263 // vendor video codecs prefer opaque format
264 if (trait.name.find("android") == std::string::npos) {
265 caps->addColorFormat(COLOR_FormatSurface);
266 }
267 caps->addColorFormat(COLOR_FormatYUV420Flexible);
268 caps->addColorFormat(COLOR_FormatYUV420Planar);
269 caps->addColorFormat(COLOR_FormatYUV420SemiPlanar);
270 caps->addColorFormat(COLOR_FormatYUV420PackedPlanar);
271 caps->addColorFormat(COLOR_FormatYUV420PackedSemiPlanar);
272 // framework video encoders must support surface format, though it is unclear
273 // that they will be able to map it if it is opaque
274 if (encoder && trait.name.find("android") != std::string::npos) {
275 caps->addColorFormat(COLOR_FormatSurface);
276 }
277 }
278 }
279 }
280 return OK;
281 }
282
283 } // namespace android
284
CreateBuilder()285 extern "C" android::MediaCodecListBuilderBase *CreateBuilder() {
286 return new android::Codec2InfoBuilder;
287 }
288
289