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 <strings.h>
22 
23 #include <C2Component.h>
24 #include <C2Config.h>
25 #include <C2Debug.h>
26 #include <C2PlatformSupport.h>
27 #include <Codec2Mapper.h>
28 
29 #include <OMX_Audio.h>
30 #include <OMX_AudioExt.h>
31 #include <OMX_IndexExt.h>
32 #include <OMX_Types.h>
33 #include <OMX_Video.h>
34 #include <OMX_VideoExt.h>
35 #include <OMX_AsString.h>
36 
37 #include <android/hardware/media/omx/1.0/IOmx.h>
38 #include <android/hardware/media/omx/1.0/IOmxObserver.h>
39 #include <android/hardware/media/omx/1.0/IOmxNode.h>
40 #include <android/hardware/media/omx/1.0/types.h>
41 
42 #include <android-base/properties.h>
43 #include <codec2/hidl/client.h>
44 #include <cutils/native_handle.h>
45 #include <media/omx/1.0/WOmxNode.h>
46 #include <media/stagefright/foundation/ALookup.h>
47 #include <media/stagefright/foundation/MediaDefs.h>
48 #include <media/stagefright/omx/OMXUtils.h>
49 #include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
50 #include <media/stagefright/Codec2InfoBuilder.h>
51 #include <media/stagefright/MediaCodecConstants.h>
52 
53 namespace android {
54 
55 using Traits = C2Component::Traits;
56 
57 namespace /* unnamed */ {
58 
hasPrefix(const std::string & s,const char * prefix)59 bool hasPrefix(const std::string& s, const char* prefix) {
60     size_t prefixLen = strlen(prefix);
61     return s.compare(0, prefixLen, prefix) == 0;
62 }
63 
hasSuffix(const std::string & s,const char * suffix)64 bool hasSuffix(const std::string& s, const char* suffix) {
65     size_t suffixLen = strlen(suffix);
66     return suffixLen > s.size() ? false :
67             s.compare(s.size() - suffixLen, suffixLen, suffix) == 0;
68 }
69 
addSupportedProfileLevels(std::shared_ptr<Codec2Client::Interface> intf,MediaCodecInfo::CapabilitiesWriter * caps,const Traits & trait,const std::string & mediaType)70 void addSupportedProfileLevels(
71         std::shared_ptr<Codec2Client::Interface> intf,
72         MediaCodecInfo::CapabilitiesWriter *caps,
73         const Traits& trait, const std::string &mediaType) {
74     std::shared_ptr<C2Mapper::ProfileLevelMapper> mapper =
75         C2Mapper::GetProfileLevelMapper(trait.mediaType);
76     // if we don't know the media type, pass through all values unmapped
77 
78     // TODO: we cannot find levels that are local 'maxima' without knowing the coding
79     // e.g. H.263 level 45 and level 30 could be two values for highest level as
80     // they don't include one another. For now we use the last supported value.
81     bool encoder = trait.kind == C2Component::KIND_ENCODER;
82     C2StreamProfileLevelInfo pl(encoder /* output */, 0u);
83     std::vector<C2FieldSupportedValuesQuery> profileQuery = {
84         C2FieldSupportedValuesQuery::Possible(C2ParamField(&pl, &pl.profile))
85     };
86 
87     c2_status_t err = intf->querySupportedValues(profileQuery, C2_DONT_BLOCK);
88     ALOGV("query supported profiles -> %s | %s", asString(err), asString(profileQuery[0].status));
89     if (err != C2_OK || profileQuery[0].status != C2_OK) {
90         return;
91     }
92 
93     // we only handle enumerated values
94     if (profileQuery[0].values.type != C2FieldSupportedValues::VALUES) {
95         return;
96     }
97 
98     // determine if codec supports HDR
99     bool supportsHdr = false;
100     bool supportsHdr10Plus = false;
101 
102     std::vector<std::shared_ptr<C2ParamDescriptor>> paramDescs;
103     c2_status_t err1 = intf->querySupportedParams(&paramDescs);
104     if (err1 == C2_OK) {
105         for (const std::shared_ptr<C2ParamDescriptor> &desc : paramDescs) {
106             switch ((uint32_t)desc->index()) {
107             case C2StreamHdr10PlusInfo::output::PARAM_TYPE:
108                 supportsHdr10Plus = true;
109                 break;
110             case C2StreamHdrStaticInfo::output::PARAM_TYPE:
111                 supportsHdr = true;
112                 break;
113             default:
114                 break;
115             }
116         }
117     }
118 
119     // For VP9/AV1, the static info is always propagated by framework.
120     supportsHdr |= (mediaType == MIMETYPE_VIDEO_VP9);
121     supportsHdr |= (mediaType == MIMETYPE_VIDEO_AV1);
122 
123     for (C2Value::Primitive profile : profileQuery[0].values.values) {
124         pl.profile = (C2Config::profile_t)profile.ref<uint32_t>();
125         std::vector<std::unique_ptr<C2SettingResult>> failures;
126         err = intf->config({&pl}, C2_DONT_BLOCK, &failures);
127         ALOGV("set profile to %u -> %s", pl.profile, asString(err));
128         std::vector<C2FieldSupportedValuesQuery> levelQuery = {
129             C2FieldSupportedValuesQuery::Current(C2ParamField(&pl, &pl.level))
130         };
131         err = intf->querySupportedValues(levelQuery, C2_DONT_BLOCK);
132         ALOGV("query supported levels -> %s | %s", asString(err), asString(levelQuery[0].status));
133         if (err != C2_OK || levelQuery[0].status != C2_OK
134                 || levelQuery[0].values.type != C2FieldSupportedValues::VALUES
135                 || levelQuery[0].values.values.size() == 0) {
136             continue;
137         }
138 
139         C2Value::Primitive level = levelQuery[0].values.values.back();
140         pl.level = (C2Config::level_t)level.ref<uint32_t>();
141         ALOGV("supporting level: %u", pl.level);
142         int32_t sdkProfile, sdkLevel;
143         if (mapper && mapper->mapProfile(pl.profile, &sdkProfile)
144                 && mapper->mapLevel(pl.level, &sdkLevel)) {
145             caps->addProfileLevel((uint32_t)sdkProfile, (uint32_t)sdkLevel);
146             // also list HDR profiles if component supports HDR
147             if (supportsHdr) {
148                 auto hdrMapper = C2Mapper::GetHdrProfileLevelMapper(trait.mediaType);
149                 if (hdrMapper && hdrMapper->mapProfile(pl.profile, &sdkProfile)) {
150                     caps->addProfileLevel((uint32_t)sdkProfile, (uint32_t)sdkLevel);
151                 }
152                 if (supportsHdr10Plus) {
153                     hdrMapper = C2Mapper::GetHdrProfileLevelMapper(
154                             trait.mediaType, true /*isHdr10Plus*/);
155                     if (hdrMapper && hdrMapper->mapProfile(pl.profile, &sdkProfile)) {
156                         caps->addProfileLevel((uint32_t)sdkProfile, (uint32_t)sdkLevel);
157                     }
158                 }
159             }
160         } else if (!mapper) {
161             caps->addProfileLevel(pl.profile, pl.level);
162         }
163 
164         // for H.263 also advertise the second highest level if the
165         // codec supports level 45, as level 45 only covers level 10
166         // TODO: move this to some form of a setting so it does not
167         // have to be here
168         if (mediaType == MIMETYPE_VIDEO_H263) {
169             C2Config::level_t nextLevel = C2Config::LEVEL_UNUSED;
170             for (C2Value::Primitive v : levelQuery[0].values.values) {
171                 C2Config::level_t level = (C2Config::level_t)v.ref<uint32_t>();
172                 if (level < C2Config::LEVEL_H263_45 && level > nextLevel) {
173                     nextLevel = level;
174                 }
175             }
176             if (nextLevel != C2Config::LEVEL_UNUSED
177                     && nextLevel != pl.level
178                     && mapper
179                     && mapper->mapProfile(pl.profile, &sdkProfile)
180                     && mapper->mapLevel(nextLevel, &sdkLevel)) {
181                 caps->addProfileLevel(
182                         (uint32_t)sdkProfile, (uint32_t)sdkLevel);
183             }
184         }
185     }
186 }
187 
addSupportedColorFormats(std::shared_ptr<Codec2Client::Interface> intf,MediaCodecInfo::CapabilitiesWriter * caps,const Traits & trait,const std::string & mediaType)188 void addSupportedColorFormats(
189         std::shared_ptr<Codec2Client::Interface> intf,
190         MediaCodecInfo::CapabilitiesWriter *caps,
191         const Traits& trait, const std::string &mediaType) {
192     (void)intf;
193 
194     // TODO: get this from intf() as well, but how do we map them to
195     // MediaCodec color formats?
196     bool encoder = trait.kind == C2Component::KIND_ENCODER;
197     if (mediaType.find("video") != std::string::npos
198             || mediaType.find("image") != std::string::npos) {
199         // vendor video codecs prefer opaque format
200         if (trait.name.find("android") == std::string::npos) {
201             caps->addColorFormat(COLOR_FormatSurface);
202         }
203         caps->addColorFormat(COLOR_FormatYUV420Flexible);
204         caps->addColorFormat(COLOR_FormatYUV420Planar);
205         caps->addColorFormat(COLOR_FormatYUV420SemiPlanar);
206         caps->addColorFormat(COLOR_FormatYUV420PackedPlanar);
207         caps->addColorFormat(COLOR_FormatYUV420PackedSemiPlanar);
208         // framework video encoders must support surface format, though it is unclear
209         // that they will be able to map it if it is opaque
210         if (encoder && trait.name.find("android") != std::string::npos) {
211             caps->addColorFormat(COLOR_FormatSurface);
212         }
213     }
214 }
215 
216 class Switch {
217     enum Flags : uint8_t {
218         // flags
219         IS_ENABLED = (1 << 0),
220         BY_DEFAULT = (1 << 1),
221     };
222 
Switch(uint8_t flags)223     constexpr Switch(uint8_t flags) : mFlags(flags) {}
224 
225     uint8_t mFlags;
226 
227 public:
228     // have to create class due to this bool conversion operator...
operator bool() const229     constexpr operator bool() const {
230         return mFlags & IS_ENABLED;
231     }
232 
operator !() const233     constexpr Switch operator!() const {
234         return Switch(mFlags ^ IS_ENABLED);
235     }
236 
DISABLED()237     static constexpr Switch DISABLED() { return 0; };
ENABLED()238     static constexpr Switch ENABLED() { return IS_ENABLED; };
DISABLED_BY_DEFAULT()239     static constexpr Switch DISABLED_BY_DEFAULT() { return BY_DEFAULT; };
ENABLED_BY_DEFAULT()240     static constexpr Switch ENABLED_BY_DEFAULT() { return IS_ENABLED | BY_DEFAULT; };
241 
toString(const char * def="??") const242     const char *toString(const char *def = "??") const {
243         switch (mFlags) {
244         case 0:                         return "0";
245         case IS_ENABLED:                return "1";
246         case BY_DEFAULT:                return "(0)";
247         case IS_ENABLED | BY_DEFAULT:   return "(1)";
248         default: return def;
249         }
250     }
251 
252 };
253 
asString(const Switch & s,const char * def="??")254 const char *asString(const Switch &s, const char *def = "??") {
255     return s.toString(def);
256 }
257 
isSettingEnabled(std::string setting,const MediaCodecsXmlParser::AttributeMap & settings,Switch def=Switch::DISABLED_BY_DEFAULT ())258 Switch isSettingEnabled(
259         std::string setting, const MediaCodecsXmlParser::AttributeMap &settings,
260         Switch def = Switch::DISABLED_BY_DEFAULT()) {
261     const auto enablement = settings.find(setting);
262     if (enablement == settings.end()) {
263         return def;
264     }
265     return enablement->second == "1" ? Switch::ENABLED() : Switch::DISABLED();
266 }
267 
isVariantEnabled(std::string variant,const MediaCodecsXmlParser::AttributeMap & settings)268 Switch isVariantEnabled(
269         std::string variant, const MediaCodecsXmlParser::AttributeMap &settings) {
270     return isSettingEnabled("variant-" + variant, settings);
271 }
272 
isVariantExpressionEnabled(std::string exp,const MediaCodecsXmlParser::AttributeMap & settings)273 Switch isVariantExpressionEnabled(
274         std::string exp, const MediaCodecsXmlParser::AttributeMap &settings) {
275     if (!exp.empty() && exp.at(0) == '!') {
276         return !isVariantEnabled(exp.substr(1, exp.size() - 1), settings);
277     }
278     return isVariantEnabled(exp, settings);
279 }
280 
isDomainEnabled(std::string domain,const MediaCodecsXmlParser::AttributeMap & settings)281 Switch isDomainEnabled(
282         std::string domain, const MediaCodecsXmlParser::AttributeMap &settings) {
283     return isSettingEnabled("domain-" + domain, settings);
284 }
285 
286 } // unnamed namespace
287 
buildMediaCodecList(MediaCodecListWriter * writer)288 status_t Codec2InfoBuilder::buildMediaCodecList(MediaCodecListWriter* writer) {
289     // TODO: Remove run-time configurations once all codecs are working
290     // properly. (Assume "full" behavior eventually.)
291     //
292     // debug.stagefright.ccodec supports 5 values.
293     //   0 - No Codec 2.0 components are available.
294     //   1 - Audio decoders and encoders with prefix "c2.android." are available
295     //       and ranked first.
296     //       All other components with prefix "c2.android." are available with
297     //       their normal ranks.
298     //       Components with prefix "c2.vda." are available with their normal
299     //       ranks.
300     //       All other components with suffix ".avc.decoder" or ".avc.encoder"
301     //       are available but ranked last.
302     //   2 - Components with prefix "c2.android." are available and ranked
303     //       first.
304     //       Components with prefix "c2.vda." are available with their normal
305     //       ranks.
306     //       All other components with suffix ".avc.decoder" or ".avc.encoder"
307     //       are available but ranked last.
308     //   3 - Components with prefix "c2.android." are available and ranked
309     //       first.
310     //       All other components are available with their normal ranks.
311     //   4 - All components are available with their normal ranks.
312     //
313     // The default value (boot time) is 1.
314     //
315     // Note: Currently, OMX components have default rank 0x100, while all
316     // Codec2.0 software components have default rank 0x200.
317     int option = ::android::base::GetIntProperty("debug.stagefright.ccodec", 4);
318 
319     // Obtain Codec2Client
320     std::vector<Traits> traits = Codec2Client::ListComponents();
321 
322     // parse APEX XML first, followed by vendor XML.
323     // Note: APEX XML names do not depend on ro.media.xml_variant.* properties.
324     MediaCodecsXmlParser parser;
325     parser.parseXmlFilesInSearchDirs(
326             { "media_codecs.xml", "media_codecs_performance.xml" },
327             { "/apex/com.android.media.swcodec/etc" });
328 
329     // TODO: remove these c2-specific files once product moved to default file names
330     parser.parseXmlFilesInSearchDirs(
331             { "media_codecs_c2.xml", "media_codecs_performance_c2.xml" });
332 
333     // parse default XML files
334     parser.parseXmlFilesInSearchDirs();
335 
336     if (parser.getParsingStatus() != OK) {
337         ALOGD("XML parser no good");
338         return OK;
339     }
340 
341     MediaCodecsXmlParser::AttributeMap settings = parser.getServiceAttributeMap();
342     for (const auto &v : settings) {
343         if (!hasPrefix(v.first, "media-type-")
344                 && !hasPrefix(v.first, "domain-")
345                 && !hasPrefix(v.first, "variant-")) {
346             writer->addGlobalSetting(v.first.c_str(), v.second.c_str());
347         }
348     }
349 
350     for (const Traits& trait : traits) {
351         C2Component::rank_t rank = trait.rank;
352 
353         // Interface must be accessible for us to list the component, and there also
354         // must be an XML entry for the codec. Codec aliases listed in the traits
355         // allow additional XML entries to be specified for each alias. These will
356         // be listed as separate codecs. If no XML entry is specified for an alias,
357         // those will be treated as an additional alias specified in the XML entry
358         // for the interface name.
359         std::vector<std::string> nameAndAliases = trait.aliases;
360         nameAndAliases.insert(nameAndAliases.begin(), trait.name);
361         for (const std::string &nameOrAlias : nameAndAliases) {
362             bool isAlias = trait.name != nameOrAlias;
363             std::shared_ptr<Codec2Client::Interface> intf =
364                 Codec2Client::CreateInterfaceByName(nameOrAlias.c_str());
365             if (!intf) {
366                 ALOGD("could not create interface for %s'%s'",
367                         isAlias ? "alias " : "",
368                         nameOrAlias.c_str());
369                 continue;
370             }
371             if (parser.getCodecMap().count(nameOrAlias) == 0) {
372                 if (isAlias) {
373                     std::unique_ptr<MediaCodecInfoWriter> baseCodecInfo =
374                         writer->findMediaCodecInfo(trait.name.c_str());
375                     if (!baseCodecInfo) {
376                         ALOGD("alias '%s' not found in xml but canonical codec info '%s' missing",
377                                 nameOrAlias.c_str(),
378                                 trait.name.c_str());
379                     } else {
380                         ALOGD("alias '%s' not found in xml; use an XML <Alias> tag for this",
381                                 nameOrAlias.c_str());
382                         // merge alias into existing codec
383                         baseCodecInfo->addAlias(nameOrAlias.c_str());
384                     }
385                 } else {
386                     ALOGD("component '%s' not found in xml", trait.name.c_str());
387                 }
388                 continue;
389             }
390             std::string canonName = trait.name;
391 
392             // TODO: Remove this block once all codecs are enabled by default.
393             switch (option) {
394             case 0:
395                 continue;
396             case 1:
397                 if (hasPrefix(canonName, "c2.vda.")) {
398                     break;
399                 }
400                 if (hasPrefix(canonName, "c2.android.")) {
401                     if (trait.domain == C2Component::DOMAIN_AUDIO) {
402                         rank = 1;
403                         break;
404                     }
405                     break;
406                 }
407                 if (hasSuffix(canonName, ".avc.decoder") ||
408                         hasSuffix(canonName, ".avc.encoder")) {
409                     rank = std::numeric_limits<decltype(rank)>::max();
410                     break;
411                 }
412                 continue;
413             case 2:
414                 if (hasPrefix(canonName, "c2.vda.")) {
415                     break;
416                 }
417                 if (hasPrefix(canonName, "c2.android.")) {
418                     rank = 1;
419                     break;
420                 }
421                 if (hasSuffix(canonName, ".avc.decoder") ||
422                         hasSuffix(canonName, ".avc.encoder")) {
423                     rank = std::numeric_limits<decltype(rank)>::max();
424                     break;
425                 }
426                 continue;
427             case 3:
428                 if (hasPrefix(canonName, "c2.android.")) {
429                     rank = 1;
430                 }
431                 break;
432             }
433 
434             const MediaCodecsXmlParser::CodecProperties &codec =
435                 parser.getCodecMap().at(nameOrAlias);
436 
437             // verify that either the codec is explicitly enabled, or one of its domains is
438             bool codecEnabled = codec.quirkSet.find("attribute::disabled") == codec.quirkSet.end();
439             if (!codecEnabled) {
440                 for (const std::string &domain : codec.domainSet) {
441                     const Switch enabled = isDomainEnabled(domain, settings);
442                     ALOGV("codec entry '%s' is in domain '%s' that is '%s'",
443                             nameOrAlias.c_str(), domain.c_str(), asString(enabled));
444                     if (enabled) {
445                         codecEnabled = true;
446                         break;
447                     }
448                 }
449             }
450             // if codec has variants, also check that at least one of them is enabled
451             bool variantEnabled = codec.variantSet.empty();
452             for (const std::string &variant : codec.variantSet) {
453                 const Switch enabled = isVariantExpressionEnabled(variant, settings);
454                 ALOGV("codec entry '%s' has a variant '%s' that is '%s'",
455                         nameOrAlias.c_str(), variant.c_str(), asString(enabled));
456                 if (enabled) {
457                     variantEnabled = true;
458                     break;
459                 }
460             }
461             if (!codecEnabled || !variantEnabled) {
462                 ALOGD("codec entry for '%s' is disabled", nameOrAlias.c_str());
463                 continue;
464             }
465 
466             ALOGV("adding codec entry for '%s'", nameOrAlias.c_str());
467             std::unique_ptr<MediaCodecInfoWriter> codecInfo = writer->addMediaCodecInfo();
468             codecInfo->setName(nameOrAlias.c_str());
469             codecInfo->setOwner(("codec2::" + trait.owner).c_str());
470 
471             bool encoder = trait.kind == C2Component::KIND_ENCODER;
472             typename std::underlying_type<MediaCodecInfo::Attributes>::type attrs = 0;
473 
474             if (encoder) {
475                 attrs |= MediaCodecInfo::kFlagIsEncoder;
476             }
477             if (trait.owner == "software") {
478                 attrs |= MediaCodecInfo::kFlagIsSoftwareOnly;
479             } else {
480                 attrs |= MediaCodecInfo::kFlagIsVendor;
481                 if (trait.owner == "vendor-software") {
482                     attrs |= MediaCodecInfo::kFlagIsSoftwareOnly;
483                 } else if (codec.quirkSet.find("attribute::software-codec")
484                         == codec.quirkSet.end()) {
485                     attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated;
486                 }
487             }
488             codecInfo->setAttributes(attrs);
489             if (!codec.rank.empty()) {
490                 uint32_t xmlRank;
491                 char dummy;
492                 if (sscanf(codec.rank.c_str(), "%u%c", &xmlRank, &dummy) == 1) {
493                     rank = xmlRank;
494                 }
495             }
496             ALOGV("rank: %u", (unsigned)rank);
497             codecInfo->setRank(rank);
498 
499             for (const std::string &alias : codec.aliases) {
500                 ALOGV("adding alias '%s'", alias.c_str());
501                 codecInfo->addAlias(alias.c_str());
502             }
503 
504             for (auto typeIt = codec.typeMap.begin(); typeIt != codec.typeMap.end(); ++typeIt) {
505                 const std::string &mediaType = typeIt->first;
506                 const Switch typeEnabled = isSettingEnabled(
507                         "media-type-" + mediaType, settings, Switch::ENABLED_BY_DEFAULT());
508                 const Switch domainTypeEnabled = isSettingEnabled(
509                         "media-type-" + mediaType + (encoder ? "-encoder" : "-decoder"),
510                         settings, Switch::ENABLED_BY_DEFAULT());
511                 ALOGV("type '%s-%s' is '%s/%s'",
512                         mediaType.c_str(), (encoder ? "encoder" : "decoder"),
513                         asString(typeEnabled), asString(domainTypeEnabled));
514                 if (!typeEnabled || !domainTypeEnabled) {
515                     ALOGD("media type '%s' for codec entry '%s' is disabled", mediaType.c_str(),
516                             nameOrAlias.c_str());
517                     continue;
518                 }
519 
520                 ALOGI("adding type '%s'", typeIt->first.c_str());
521                 const MediaCodecsXmlParser::AttributeMap &attrMap = typeIt->second;
522                 std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
523                     codecInfo->addMediaType(mediaType.c_str());
524                 for (const auto &v : attrMap) {
525                     std::string key = v.first;
526                     std::string value = v.second;
527 
528                     size_t variantSep = key.find(":::");
529                     if (variantSep != std::string::npos) {
530                         std::string variant = key.substr(0, variantSep);
531                         const Switch enabled = isVariantExpressionEnabled(variant, settings);
532                         ALOGV("variant '%s' is '%s'", variant.c_str(), asString(enabled));
533                         if (!enabled) {
534                             continue;
535                         }
536                         key = key.substr(variantSep + 3);
537                     }
538 
539                     if (key.find("feature-") == 0 && key.find("feature-bitrate-modes") != 0) {
540                         int32_t intValue = 0;
541                         // Ignore trailing bad characters and default to 0.
542                         (void)sscanf(value.c_str(), "%d", &intValue);
543                         caps->addDetail(key.c_str(), intValue);
544                     } else {
545                         caps->addDetail(key.c_str(), value.c_str());
546                     }
547                 }
548 
549                 addSupportedProfileLevels(intf, caps.get(), trait, mediaType);
550                 addSupportedColorFormats(intf, caps.get(), trait, mediaType);
551             }
552         }
553     }
554     return OK;
555 }
556 
557 }  // namespace android
558 
CreateBuilder()559 extern "C" android::MediaCodecListBuilderBase *CreateBuilder() {
560     return new android::Codec2InfoBuilder;
561 }
562