Home | History | Annotate | Download | only in sfplugin
      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/MediaCodecConstants.h>
     47 #include <media/stagefright/foundation/ALookup.h>
     48 #include <media/stagefright/foundation/MediaDefs.h>
     49 #include <media/stagefright/omx/OMXUtils.h>
     50 #include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
     51 
     52 #include "Codec2InfoBuilder.h"
     53 
     54 namespace android {
     55 
     56 using Traits = C2Component::Traits;
     57 
     58 namespace /* unnamed */ {
     59 
     60 bool hasPrefix(const std::string& s, const char* prefix) {
     61     size_t prefixLen = strlen(prefix);
     62     return s.compare(0, prefixLen, prefix) == 0;
     63 }
     64 
     65 bool hasSuffix(const std::string& s, const char* suffix) {
     66     size_t suffixLen = strlen(suffix);
     67     return suffixLen > s.size() ? false :
     68             s.compare(s.size() - suffixLen, suffixLen, suffix) == 0;
     69 }
     70 
     71 void addSupportedProfileLevels(
     72         std::shared_ptr<Codec2Client::Interface> intf,
     73         MediaCodecInfo::CapabilitiesWriter *caps,
     74         const Traits& trait, const std::string &mediaType) {
     75     std::shared_ptr<C2Mapper::ProfileLevelMapper> mapper =
     76         C2Mapper::GetProfileLevelMapper(trait.mediaType);
     77     // if we don't know the media type, pass through all values unmapped
     78 
     79     // TODO: we cannot find levels that are local 'maxima' without knowing the coding
     80     // e.g. H.263 level 45 and level 30 could be two values for highest level as
     81     // they don't include one another. For now we use the last supported value.
     82     bool encoder = trait.kind == C2Component::KIND_ENCODER;
     83     C2StreamProfileLevelInfo pl(encoder /* output */, 0u);
     84     std::vector<C2FieldSupportedValuesQuery> profileQuery = {
     85         C2FieldSupportedValuesQuery::Possible(C2ParamField(&pl, &pl.profile))
     86     };
     87 
     88     c2_status_t err = intf->querySupportedValues(profileQuery, C2_DONT_BLOCK);
     89     ALOGV("query supported profiles -> %s | %s", asString(err), asString(profileQuery[0].status));
     90     if (err != C2_OK || profileQuery[0].status != C2_OK) {
     91         return;
     92     }
     93 
     94     // we only handle enumerated values
     95     if (profileQuery[0].values.type != C2FieldSupportedValues::VALUES) {
     96         return;
     97     }
     98 
     99     // determine if codec supports HDR
    100     bool supportsHdr = false;
    101     bool supportsHdr10Plus = false;
    102 
    103     std::vector<std::shared_ptr<C2ParamDescriptor>> paramDescs;
    104     c2_status_t err1 = intf->querySupportedParams(&paramDescs);
    105     if (err1 == C2_OK) {
    106         for (const std::shared_ptr<C2ParamDescriptor> &desc : paramDescs) {
    107             switch ((uint32_t)desc->index()) {
    108             case C2StreamHdr10PlusInfo::output::PARAM_TYPE:
    109                 supportsHdr10Plus = true;
    110                 break;
    111             case C2StreamHdrStaticInfo::output::PARAM_TYPE:
    112                 supportsHdr = true;
    113                 break;
    114             default:
    115                 break;
    116             }
    117         }
    118     }
    119 
    120     // For VP9, the static info is always propagated by framework.
    121     supportsHdr |= (mediaType == MIMETYPE_VIDEO_VP9);
    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 
    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 
    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...
    229     constexpr operator bool() const {
    230         return mFlags & IS_ENABLED;
    231     }
    232 
    233     constexpr Switch operator!() const {
    234         return Switch(mFlags ^ IS_ENABLED);
    235     }
    236 
    237     static constexpr Switch DISABLED() { return 0; };
    238     static constexpr Switch ENABLED() { return IS_ENABLED; };
    239     static constexpr Switch DISABLED_BY_DEFAULT() { return BY_DEFAULT; };
    240     static constexpr Switch ENABLED_BY_DEFAULT() { return IS_ENABLED | BY_DEFAULT; };
    241 
    242     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 
    254 const char *asString(const Switch &s, const char *def = "??") {
    255     return s.toString(def);
    256 }
    257 
    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 
    268 Switch isVariantEnabled(
    269         std::string variant, const MediaCodecsXmlParser::AttributeMap &settings) {
    270     return isSettingEnabled("variant-" + variant, settings);
    271 }
    272 
    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 
    281 Switch isDomainEnabled(
    282         std::string domain, const MediaCodecsXmlParser::AttributeMap &settings) {
    283     return isSettingEnabled("domain-" + domain, settings);
    284 }
    285 
    286 } // unnamed namespace
    287 
    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     MediaCodecsXmlParser parser;
    324     parser.parseXmlFilesInSearchDirs(
    325             parser.getDefaultXmlNames(),
    326             { "/apex/com.android.media.swcodec/etc" });
    327 
    328     // TODO: remove these c2-specific files once product moved to default file names
    329     parser.parseXmlFilesInSearchDirs(
    330             { "media_codecs_c2.xml", "media_codecs_performance_c2.xml" });
    331 
    332     // parse default XML files
    333     parser.parseXmlFilesInSearchDirs();
    334 
    335     if (parser.getParsingStatus() != OK) {
    336         ALOGD("XML parser no good");
    337         return OK;
    338     }
    339 
    340     MediaCodecsXmlParser::AttributeMap settings = parser.getServiceAttributeMap();
    341     for (const auto &v : settings) {
    342         if (!hasPrefix(v.first, "media-type-")
    343                 && !hasPrefix(v.first, "domain-")
    344                 && !hasPrefix(v.first, "variant-")) {
    345             writer->addGlobalSetting(v.first.c_str(), v.second.c_str());
    346         }
    347     }
    348 
    349     for (const Traits& trait : traits) {
    350         C2Component::rank_t rank = trait.rank;
    351 
    352         // Interface must be accessible for us to list the component, and there also
    353         // must be an XML entry for the codec. Codec aliases listed in the traits
    354         // allow additional XML entries to be specified for each alias. These will
    355         // be listed as separate codecs. If no XML entry is specified for an alias,
    356         // those will be treated as an additional alias specified in the XML entry
    357         // for the interface name.
    358         std::vector<std::string> nameAndAliases = trait.aliases;
    359         nameAndAliases.insert(nameAndAliases.begin(), trait.name);
    360         for (const std::string &nameOrAlias : nameAndAliases) {
    361             bool isAlias = trait.name != nameOrAlias;
    362             std::shared_ptr<Codec2Client::Interface> intf =
    363                 Codec2Client::CreateInterfaceByName(nameOrAlias.c_str());
    364             if (!intf) {
    365                 ALOGD("could not create interface for %s'%s'",
    366                         isAlias ? "alias " : "",
    367                         nameOrAlias.c_str());
    368                 continue;
    369             }
    370             if (parser.getCodecMap().count(nameOrAlias) == 0) {
    371                 if (isAlias) {
    372                     std::unique_ptr<MediaCodecInfoWriter> baseCodecInfo =
    373                         writer->findMediaCodecInfo(trait.name.c_str());
    374                     if (!baseCodecInfo) {
    375                         ALOGD("alias '%s' not found in xml but canonical codec info '%s' missing",
    376                                 nameOrAlias.c_str(),
    377                                 trait.name.c_str());
    378                     } else {
    379                         ALOGD("alias '%s' not found in xml; use an XML <Alias> tag for this",
    380                                 nameOrAlias.c_str());
    381                         // merge alias into existing codec
    382                         baseCodecInfo->addAlias(nameOrAlias.c_str());
    383                     }
    384                 } else {
    385                     ALOGD("component '%s' not found in xml", trait.name.c_str());
    386                 }
    387                 continue;
    388             }
    389             std::string canonName = trait.name;
    390 
    391             // TODO: Remove this block once all codecs are enabled by default.
    392             switch (option) {
    393             case 0:
    394                 continue;
    395             case 1:
    396                 if (hasPrefix(canonName, "c2.vda.")) {
    397                     break;
    398                 }
    399                 if (hasPrefix(canonName, "c2.android.")) {
    400                     if (trait.domain == C2Component::DOMAIN_AUDIO) {
    401                         rank = 1;
    402                         break;
    403                     }
    404                     break;
    405                 }
    406                 if (hasSuffix(canonName, ".avc.decoder") ||
    407                         hasSuffix(canonName, ".avc.encoder")) {
    408                     rank = std::numeric_limits<decltype(rank)>::max();
    409                     break;
    410                 }
    411                 continue;
    412             case 2:
    413                 if (hasPrefix(canonName, "c2.vda.")) {
    414                     break;
    415                 }
    416                 if (hasPrefix(canonName, "c2.android.")) {
    417                     rank = 1;
    418                     break;
    419                 }
    420                 if (hasSuffix(canonName, ".avc.decoder") ||
    421                         hasSuffix(canonName, ".avc.encoder")) {
    422                     rank = std::numeric_limits<decltype(rank)>::max();
    423                     break;
    424                 }
    425                 continue;
    426             case 3:
    427                 if (hasPrefix(canonName, "c2.android.")) {
    428                     rank = 1;
    429                 }
    430                 break;
    431             }
    432 
    433             const MediaCodecsXmlParser::CodecProperties &codec =
    434                 parser.getCodecMap().at(nameOrAlias);
    435 
    436             // verify that either the codec is explicitly enabled, or one of its domains is
    437             bool codecEnabled = codec.quirkSet.find("attribute::disabled") == codec.quirkSet.end();
    438             if (!codecEnabled) {
    439                 for (const std::string &domain : codec.domainSet) {
    440                     const Switch enabled = isDomainEnabled(domain, settings);
    441                     ALOGV("codec entry '%s' is in domain '%s' that is '%s'",
    442                             nameOrAlias.c_str(), domain.c_str(), asString(enabled));
    443                     if (enabled) {
    444                         codecEnabled = true;
    445                         break;
    446                     }
    447                 }
    448             }
    449             // if codec has variants, also check that at least one of them is enabled
    450             bool variantEnabled = codec.variantSet.empty();
    451             for (const std::string &variant : codec.variantSet) {
    452                 const Switch enabled = isVariantExpressionEnabled(variant, settings);
    453                 ALOGV("codec entry '%s' has a variant '%s' that is '%s'",
    454                         nameOrAlias.c_str(), variant.c_str(), asString(enabled));
    455                 if (enabled) {
    456                     variantEnabled = true;
    457                     break;
    458                 }
    459             }
    460             if (!codecEnabled || !variantEnabled) {
    461                 ALOGD("codec entry for '%s' is disabled", nameOrAlias.c_str());
    462                 continue;
    463             }
    464 
    465             ALOGV("adding codec entry for '%s'", nameOrAlias.c_str());
    466             std::unique_ptr<MediaCodecInfoWriter> codecInfo = writer->addMediaCodecInfo();
    467             codecInfo->setName(nameOrAlias.c_str());
    468             codecInfo->setOwner(("codec2::" + trait.owner).c_str());
    469 
    470             bool encoder = trait.kind == C2Component::KIND_ENCODER;
    471             typename std::underlying_type<MediaCodecInfo::Attributes>::type attrs = 0;
    472 
    473             if (encoder) {
    474                 attrs |= MediaCodecInfo::kFlagIsEncoder;
    475             }
    476             if (trait.owner == "software") {
    477                 attrs |= MediaCodecInfo::kFlagIsSoftwareOnly;
    478             } else {
    479                 attrs |= MediaCodecInfo::kFlagIsVendor;
    480                 if (trait.owner == "vendor-software") {
    481                     attrs |= MediaCodecInfo::kFlagIsSoftwareOnly;
    482                 } else if (codec.quirkSet.find("attribute::software-codec")
    483                         == codec.quirkSet.end()) {
    484                     attrs |= MediaCodecInfo::kFlagIsHardwareAccelerated;
    485                 }
    486             }
    487             codecInfo->setAttributes(attrs);
    488             if (!codec.rank.empty()) {
    489                 uint32_t xmlRank;
    490                 char dummy;
    491                 if (sscanf(codec.rank.c_str(), "%u%c", &xmlRank, &dummy) == 1) {
    492                     rank = xmlRank;
    493                 }
    494             }
    495             ALOGV("rank: %u", (unsigned)rank);
    496             codecInfo->setRank(rank);
    497 
    498             for (const std::string &alias : codec.aliases) {
    499                 ALOGV("adding alias '%s'", alias.c_str());
    500                 codecInfo->addAlias(alias.c_str());
    501             }
    502 
    503             for (auto typeIt = codec.typeMap.begin(); typeIt != codec.typeMap.end(); ++typeIt) {
    504                 const std::string &mediaType = typeIt->first;
    505                 const Switch typeEnabled = isSettingEnabled(
    506                         "media-type-" + mediaType, settings, Switch::ENABLED_BY_DEFAULT());
    507                 const Switch domainTypeEnabled = isSettingEnabled(
    508                         "media-type-" + mediaType + (encoder ? "-encoder" : "-decoder"),
    509                         settings, Switch::ENABLED_BY_DEFAULT());
    510                 ALOGV("type '%s-%s' is '%s/%s'",
    511                         mediaType.c_str(), (encoder ? "encoder" : "decoder"),
    512                         asString(typeEnabled), asString(domainTypeEnabled));
    513                 if (!typeEnabled || !domainTypeEnabled) {
    514                     ALOGD("media type '%s' for codec entry '%s' is disabled", mediaType.c_str(),
    515                             nameOrAlias.c_str());
    516                     continue;
    517                 }
    518 
    519                 ALOGI("adding type '%s'", typeIt->first.c_str());
    520                 const MediaCodecsXmlParser::AttributeMap &attrMap = typeIt->second;
    521                 std::unique_ptr<MediaCodecInfo::CapabilitiesWriter> caps =
    522                     codecInfo->addMediaType(mediaType.c_str());
    523                 for (const auto &v : attrMap) {
    524                     std::string key = v.first;
    525                     std::string value = v.second;
    526 
    527                     size_t variantSep = key.find(":::");
    528                     if (variantSep != std::string::npos) {
    529                         std::string variant = key.substr(0, variantSep);
    530                         const Switch enabled = isVariantExpressionEnabled(variant, settings);
    531                         ALOGV("variant '%s' is '%s'", variant.c_str(), asString(enabled));
    532                         if (!enabled) {
    533                             continue;
    534                         }
    535                         key = key.substr(variantSep + 3);
    536                     }
    537 
    538                     if (key.find("feature-") == 0 && key.find("feature-bitrate-modes") != 0) {
    539                         int32_t intValue = 0;
    540                         // Ignore trailing bad characters and default to 0.
    541                         (void)sscanf(value.c_str(), "%d", &intValue);
    542                         caps->addDetail(key.c_str(), intValue);
    543                     } else {
    544                         caps->addDetail(key.c_str(), value.c_str());
    545                     }
    546                 }
    547 
    548                 addSupportedProfileLevels(intf, caps.get(), trait, mediaType);
    549                 addSupportedColorFormats(intf, caps.get(), trait, mediaType);
    550             }
    551         }
    552     }
    553     return OK;
    554 }
    555 
    556 }  // namespace android
    557 
    558 extern "C" android::MediaCodecListBuilderBase *CreateBuilder() {
    559     return new android::Codec2InfoBuilder;
    560 }
    561