1 /*
2 * Copyright 2017, 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 "MediaCodecsXmlParser"
19
20 #include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
21
22 #include <android/api-level.h>
23
24 #include <android-base/logging.h>
25 #include <android-base/macros.h>
26 #include <android-base/properties.h>
27 #include <utils/Log.h>
28
29 #include <media/stagefright/MediaErrors.h>
30 #include <media/stagefright/foundation/ADebug.h>
31 #include <media/stagefright/omx/OMXUtils.h>
32
33 #include <expat.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/stat.h>
38
39 #include <algorithm>
40 #include <cctype>
41 #include <string>
42
43 namespace android {
44
45 namespace {
46
fileExists(const std::string & path)47 bool fileExists(const std::string &path) {
48 struct stat fileStat;
49 return stat(path.c_str(), &fileStat) == 0 && S_ISREG(fileStat.st_mode);
50 }
51
52 /**
53 * Search for a file in a list of search directories.
54 *
55 * For each string `searchDir` in `searchDirs`, `searchDir/fileName` will be
56 * tested whether it is a valid file name or not. If it is a valid file name,
57 * the concatenated name (`searchDir/fileName`) will be stored in the output
58 * variable `outPath`, and the function will return `true`. Otherwise, the
59 * search continues until the `nullptr` element in `searchDirs` is reached, at
60 * which point the function returns `false`.
61 *
62 * \param[in] searchDirs array of search paths.
63 * \param[in] fileName Name of the file to search.
64 * \param[out] outPath Full path of the file. `outPath` will hold a valid file
65 * name if the return value of this function is `true`.
66 * \return `true` if some element in `searchDirs` combined with `fileName` is a
67 * valid file name; `false` otherwise.
68 */
findFileInDirs(const std::vector<std::string> & searchDirs,const std::string & fileName,std::string * outPath)69 bool findFileInDirs(
70 const std::vector<std::string> &searchDirs,
71 const std::string &fileName,
72 std::string *outPath) {
73 for (const std::string &searchDir : searchDirs) {
74 std::string path = searchDir + "/" + fileName;
75 if (fileExists(path)) {
76 *outPath = path;
77 return true;
78 }
79 }
80 return false;
81 }
82
strnEq(const char * s1,const char * s2,size_t count)83 bool strnEq(const char* s1, const char* s2, size_t count) {
84 return strncmp(s1, s2, count) == 0;
85 }
86
strEq(const char * s1,const char * s2)87 bool strEq(const char* s1, const char* s2) {
88 return strcmp(s1, s2) == 0;
89 }
90
striEq(const char * s1,const char * s2)91 bool striEq(const char* s1, const char* s2) {
92 return strcasecmp(s1, s2) == 0;
93 }
94
strHasPrefix(const char * test,const char * prefix)95 bool strHasPrefix(const char* test, const char* prefix) {
96 return strnEq(test, prefix, strlen(prefix));
97 }
98
parseBoolean(const char * s)99 bool parseBoolean(const char* s) {
100 return striEq(s, "y") ||
101 striEq(s, "yes") ||
102 striEq(s, "enabled") ||
103 striEq(s, "t") ||
104 striEq(s, "true") ||
105 striEq(s, "1");
106 }
107
108
combineStatus(status_t a,status_t b)109 status_t combineStatus(status_t a, status_t b) {
110 if (a == NO_INIT) {
111 return b;
112 } else if ((a == OK && (b == NAME_NOT_FOUND || b == ALREADY_EXISTS || b == NO_INIT))
113 || (b == OK && (a == NAME_NOT_FOUND || a == ALREADY_EXISTS))) {
114 // ignore NAME_NOT_FOUND and ALREADY_EXIST errors as long as the other error is OK
115 // also handle OK + NO_INIT here
116 return OK;
117 } else {
118 // prefer the first error result
119 return a ? : b;
120 }
121 }
122
parseCommaSeparatedStringSet(const char * s)123 MediaCodecsXmlParser::StringSet parseCommaSeparatedStringSet(const char *s) {
124 MediaCodecsXmlParser::StringSet result;
125 for (const char *ptr = s ? : ""; *ptr; ) {
126 const char *end = strchrnul(ptr, ',');
127 if (ptr != end) { // skip empty values
128 result.emplace(ptr, end - ptr);
129 }
130 ptr = end + ('\0' != *end);
131 }
132 return result;
133 }
134
135 #define PLOGD(msg, ...) \
136 ALOGD(msg " at line %zu of %s", ##__VA_ARGS__, \
137 (size_t)::XML_GetCurrentLineNumber(mParser.get()), mPath.c_str());
138
139 } // unnamed namespace
140
getDefaultXmlNames()141 std::vector<std::string> MediaCodecsXmlParser::getDefaultXmlNames() {
142 static constexpr char const* prefixes[] = {
143 "media_codecs",
144 "media_codecs_performance"
145 };
146 static std::vector<std::string> variants = {
147 android::base::GetProperty("ro.media.xml_variant.codecs", ""),
148 android::base::GetProperty("ro.media.xml_variant.codecs_performance", "")
149 };
150 static std::vector<std::string> names = {
151 prefixes[0] + variants[0] + ".xml",
152 prefixes[1] + variants[1] + ".xml",
153
154 // shaping information is not currently variant specific.
155 "media_codecs_shaping.xml"
156 };
157 return names;
158 }
159
160
161 struct MediaCodecsXmlParser::Impl {
162 // status + error message
163 struct Result {
164 private:
165 status_t mStatus;
166 std::string mError;
167
168 public:
Resultandroid::MediaCodecsXmlParser::Impl::Result169 Result(status_t s, std::string error = "")
170 : mStatus(s),
171 mError(error) {
172 if (error.empty() && s) {
173 mError = "Failed (" + std::string(asString(s)) + ")";
174 }
175 }
operator status_tandroid::MediaCodecsXmlParser::Impl::Result176 operator status_t() const { return mStatus; }
errorandroid::MediaCodecsXmlParser::Impl::Result177 std::string error() const { return mError; }
178 };
179
180
181 // Parsed data
182 struct Data {
183 // Service attributes
184 AttributeMap mServiceAttributeMap;
185 CodecMap mCodecMap;
186 Result addGlobal(std::string key, std::string value, bool updating);
187 };
188
189 enum Section {
190 SECTION_TOPLEVEL,
191 SECTION_SETTINGS,
192 SECTION_DECODERS,
193 SECTION_DECODER,
194 SECTION_DECODER_TYPE,
195 SECTION_ENCODERS,
196 SECTION_ENCODER,
197 SECTION_ENCODER_TYPE,
198 SECTION_INCLUDE,
199 SECTION_VARIANT,
200 SECTION_UNKNOWN,
201 };
202
203 // XML parsing state
204 struct State {
205 private:
206 Data *mData;
207
208 // current codec and/or type, plus whether we are updating
209 struct CodecAndType {
210 std::string mName;
211 CodecMap::iterator mCodec;
212 TypeMap::iterator mType;
213 bool mUpdating;
214 };
215
216 // using vectors as we need to reset their sizes
217 std::vector<std::string> mIncludeStack;
218 std::vector<Section> mSectionStack;
219 std::vector<StringSet> mVariantsStack;
220 std::vector<CodecAndType> mCurrent;
221
222 public:
223 State(Data *data);
224
dataandroid::MediaCodecsXmlParser::Impl::State225 Data &data() { return *mData; }
226
227 // used to restore parsing state at XML include boundaries, in case parsing the included
228 // file fails.
229 struct RestorePoint {
230 size_t numIncludes;
231 size_t numSections;
232 size_t numVariantSets;
233 size_t numCodecAndTypes;
234 };
235
236 // method manipulating restore points (all state stacks)
createRestorePointandroid::MediaCodecsXmlParser::Impl::State237 RestorePoint createRestorePoint() const {
238 return {
239 mIncludeStack.size(), mSectionStack.size(), mVariantsStack.size(), mCurrent.size()
240 };
241 }
242
restoreandroid::MediaCodecsXmlParser::Impl::State243 void restore(RestorePoint rp) {
244 CHECK_GE(mIncludeStack.size(), rp.numIncludes);
245 CHECK_GE(mSectionStack.size(), rp.numSections);
246 CHECK_GE(mVariantsStack.size(), rp.numVariantSets);
247 CHECK_GE(mCurrent.size(), rp.numCodecAndTypes);
248
249 mIncludeStack.resize(rp.numIncludes);
250 mSectionStack.resize(rp.numSections);
251 mVariantsStack.resize(rp.numVariantSets);
252 mCurrent.resize(rp.numCodecAndTypes);
253 }
254
255 // methods manipulating the include stack
256 Result enterInclude(const std::string &path);
exitIncludeandroid::MediaCodecsXmlParser::Impl::State257 void exitInclude() {
258 mIncludeStack.pop_back();
259 }
260
261 // methods manipulating the codec/type stack/state
inCodecandroid::MediaCodecsXmlParser::Impl::State262 bool inCodec() const {
263 return !mCurrent.empty() && mCurrent.back().mCodec != mData->mCodecMap.end();
264 }
265
inTypeandroid::MediaCodecsXmlParser::Impl::State266 bool inType() const {
267 return inCodec()
268 && mCurrent.back().mType != mCurrent.back().mCodec->second.typeMap.end();
269 }
270
271 Result enterMediaCodec(bool encoder, const char *name, const char *type, bool update);
272 Result enterType(const char *name, bool update);
exitCodecOrTypeandroid::MediaCodecsXmlParser::Impl::State273 void exitCodecOrType() {
274 mCurrent.pop_back();
275 }
276
277 // can only be called when inCodec()
codecandroid::MediaCodecsXmlParser::Impl::State278 MediaCodecsXmlParser::CodecProperties &codec() {
279 return mCurrent.back().mCodec->second;
280 }
281 // can only be called when inCodec()
codecNameandroid::MediaCodecsXmlParser::Impl::State282 std::string codecName() const {
283 return mCurrent.back().mName;
284 }
285 // can only be called when inCodec()
updatingandroid::MediaCodecsXmlParser::Impl::State286 bool updating() const {
287 return mCurrent.back().mUpdating;
288 }
289 // can only be called when inType()
typeandroid::MediaCodecsXmlParser::Impl::State290 MediaCodecsXmlParser::AttributeMap &type() {
291 return mCurrent.back().mType->second;
292 }
293
294 // methods manipulating the section stack
sectionandroid::MediaCodecsXmlParser::Impl::State295 Section section() const {
296 return mSectionStack.back();
297 }
298 Section lastNonIncludeSection() const;
enterSectionandroid::MediaCodecsXmlParser::Impl::State299 void enterSection(Section s) {
300 mSectionStack.push_back(s);
301 }
exitSectionandroid::MediaCodecsXmlParser::Impl::State302 void exitSection() {
303 mSectionStack.pop_back();
304 CHECK(!mSectionStack.empty());
305 }
306
307 // methods manipulating the variants stack
variantsandroid::MediaCodecsXmlParser::Impl::State308 StringSet variants() const {
309 return mVariantsStack.back();
310 }
enterVariantsandroid::MediaCodecsXmlParser::Impl::State311 void enterVariants(StringSet variants) {
312 mVariantsStack.push_back(variants);
313 }
exitVariantsandroid::MediaCodecsXmlParser::Impl::State314 void exitVariants() {
315 mVariantsStack.pop_back();
316 }
317
318 // utility methods
319
320 // updates rank, domains, variants and enabledness on the current codec/type
321 Result updateCodec(
322 const char *rank, StringSet domains, StringSet variants, const char *enabled);
323 // adds a key-value attribute detail to the current type of the current codec
324 void addDetail(const std::string &key, const std::string &value);
325 };
326
327 /** XML Parser (state) */
328 struct Parser {
329 State *mState;
330
331 Parser(State *state, std::string path);
332
333 // keep track of the parser state
334 std::shared_ptr<XML_ParserStruct> mParser;
335 std::string mPath;
336 std::string mHrefBase;
337 status_t mStatus;
338
339 void parseXmlFile();
340
341 // XML parser callbacks
342 static void StartElementHandlerWrapper(void *me, const char *name, const char **attrs);
343 static void EndElementHandlerWrapper(void *me, const char *name);
344
345 void startElementHandler(const char *name, const char **attrs);
346 void endElementHandler(const char *name);
347
348 void updateStatus(status_t status);
349 void logAnyErrors(const Result &status) const;
getStatusandroid::MediaCodecsXmlParser::Impl::Parser350 status_t getStatus() const { return mStatus; }
351
352 status_t addAlias(const char **attrs);
353 status_t addFeature(const char **attrs);
354 status_t addLimit(const char **attrs);
355 status_t addMapping(const char **attrs);
356 status_t addTuning(const char **attrs);
357 status_t addQuirk(const char **attrs, const char *prefix = nullptr);
358 status_t addSetting(const char **attrs, const char *prefix = nullptr);
359 status_t enterMediaCodec(const char **attrs, bool encoder);
360 status_t enterType(const char **attrs);
361 status_t includeXmlFile(const char **attrs);
362 status_t limitVariants(const char **attrs);
363
364 status_t updateMediaCodec(
365 const char *rank, const StringSet &domain, const StringSet &variants,
366 const char *enabled, const char *minsdk);
367 };
368
369 status_t parseXmlFilesInSearchDirs(
370 const std::vector<std::string> &fileNames,
371 const std::vector<std::string> &searchDirs);
372
373 status_t parseXmlPath(const std::string &path);
374
375 // Computed longest common prefix
376 Data mData;
377 State mState;
378
379 // Role map
380 mutable std::string mCommonPrefix;
381 mutable RoleMap mRoleMap;
382 mutable std::mutex mLock;
383
384 status_t mParsingStatus;
385
Implandroid::MediaCodecsXmlParser::Impl386 Impl()
387 : mState(&mData),
388 mParsingStatus(NO_INIT) {
389 }
390
391 void generateRoleMap() const;
392 void generateCommonPrefix() const;
393
getServiceAttributeMapandroid::MediaCodecsXmlParser::Impl394 const AttributeMap& getServiceAttributeMap() const {
395 std::lock_guard<std::mutex> guard(mLock);
396 return mData.mServiceAttributeMap;
397 }
398
getCodecMapandroid::MediaCodecsXmlParser::Impl399 const CodecMap& getCodecMap() const {
400 std::lock_guard<std::mutex> guard(mLock);
401 return mData.mCodecMap;
402 }
403
404 const RoleMap& getRoleMap() const;
405 const char* getCommonPrefix() const;
406
getParsingStatusandroid::MediaCodecsXmlParser::Impl407 status_t getParsingStatus() const {
408 std::lock_guard<std::mutex> guard(mLock);
409 return mParsingStatus;
410 }
411 };
412
413 constexpr char const* MediaCodecsXmlParser::defaultProfilingResultsXmlPath;
414
MediaCodecsXmlParser()415 MediaCodecsXmlParser::MediaCodecsXmlParser()
416 : mImpl(new Impl()) {
417 }
418
parseXmlFilesInSearchDirs(const std::vector<std::string> & fileNames,const std::vector<std::string> & searchDirs)419 status_t MediaCodecsXmlParser::parseXmlFilesInSearchDirs(
420 const std::vector<std::string> &fileNames,
421 const std::vector<std::string> &searchDirs) {
422 return mImpl->parseXmlFilesInSearchDirs(fileNames, searchDirs);
423 }
424
parseXmlPath(const std::string & path)425 status_t MediaCodecsXmlParser::parseXmlPath(const std::string &path) {
426 return mImpl->parseXmlPath(path);
427 }
428
parseXmlFilesInSearchDirs(const std::vector<std::string> & fileNames,const std::vector<std::string> & searchDirs)429 status_t MediaCodecsXmlParser::Impl::parseXmlFilesInSearchDirs(
430 const std::vector<std::string> &fileNames,
431 const std::vector<std::string> &searchDirs) {
432 status_t res = NO_INIT;
433 for (const std::string fileName : fileNames) {
434 status_t err = NO_INIT;
435 std::string path;
436 if (findFileInDirs(searchDirs, fileName, &path)) {
437 err = parseXmlPath(path);
438 } else {
439 ALOGI("Did not find %s in search path", fileName.c_str());
440 }
441 res = combineStatus(res, err);
442 }
443 return res;
444 }
445
parseXmlPath(const std::string & path)446 status_t MediaCodecsXmlParser::Impl::parseXmlPath(const std::string &path) {
447 std::lock_guard<std::mutex> guard(mLock);
448 if (!fileExists(path)) {
449 ALOGV("Cannot find %s", path.c_str());
450 mParsingStatus = combineStatus(mParsingStatus, NAME_NOT_FOUND);
451 return NAME_NOT_FOUND;
452 }
453
454 // save state (even though we should always be at toplevel here)
455 State::RestorePoint rp = mState.createRestorePoint();
456 Parser parser(&mState, path);
457 parser.parseXmlFile();
458 mState.restore(rp);
459
460 if (parser.getStatus() != OK) {
461 ALOGD("parseXmlPath(%s) failed with %s", path.c_str(), asString(parser.getStatus()));
462 }
463 mParsingStatus = combineStatus(mParsingStatus, parser.getStatus());
464 return parser.getStatus();
465 }
466
~MediaCodecsXmlParser()467 MediaCodecsXmlParser::~MediaCodecsXmlParser() {
468 }
469
State(MediaCodecsXmlParser::Impl::Data * data)470 MediaCodecsXmlParser::Impl::State::State(MediaCodecsXmlParser::Impl::Data *data)
471 : mData(data) {
472 mSectionStack.emplace_back(SECTION_TOPLEVEL);
473 }
474
475 MediaCodecsXmlParser::Impl::Section
lastNonIncludeSection() const476 MediaCodecsXmlParser::Impl::State::lastNonIncludeSection() const {
477 for (auto it = mSectionStack.end(); it != mSectionStack.begin(); --it) {
478 if (it[-1] != SECTION_INCLUDE) {
479 return it[-1];
480 }
481 }
482 TRESPASS("must have non-include section");
483 }
484
updateStatus(status_t status)485 void MediaCodecsXmlParser::Impl::Parser::updateStatus(status_t status) {
486 mStatus = combineStatus(mStatus, status);
487 }
488
logAnyErrors(const Result & status) const489 void MediaCodecsXmlParser::Impl::Parser::logAnyErrors(const Result &status) const {
490 if (status) {
491 if (status.error().empty()) {
492 PLOGD("error %s", asString((status_t)status));
493 } else {
494 PLOGD("%s", status.error().c_str());
495 }
496 }
497 }
498
499 // current SDK for this device; filled in when initializing the parser.
500 static int mysdk = 0;
501
Parser(State * state,std::string path)502 MediaCodecsXmlParser::Impl::Parser::Parser(State *state, std::string path)
503 : mState(state),
504 mPath(path),
505 mStatus(NO_INIT) {
506 // determine href_base
507 std::string::size_type end = path.rfind('/');
508 if (end != std::string::npos) {
509 mHrefBase = path.substr(0, end + 1);
510 }
511
512 #if defined(__ANDROID_API_U__)
513 // this is sdk calculation is intended only for devices >= U
514 static std::once_flag sCheckOnce;
515
516 std::call_once(sCheckOnce, [&](){
517 mysdk = android_get_device_api_level();
518
519 // work around main development branch being on same SDK as the last dessert release.
520 if (__ANDROID_API__ == __ANDROID_API_FUTURE__) {
521 mysdk++;
522 }
523 });
524 #endif // __ANDROID_API_U__
525 }
526
parseXmlFile()527 void MediaCodecsXmlParser::Impl::Parser::parseXmlFile() {
528 const char *path = mPath.c_str();
529 ALOGD("parsing %s...", path);
530 FILE *file = fopen(path, "r");
531
532 if (file == nullptr) {
533 ALOGD("unable to open media codecs configuration xml file: %s", path);
534 mStatus = NAME_NOT_FOUND;
535 return;
536 }
537
538 mParser = std::shared_ptr<XML_ParserStruct>(
539 ::XML_ParserCreate(nullptr),
540 [](XML_ParserStruct *parser) { ::XML_ParserFree(parser); });
541 LOG_FATAL_IF(!mParser, "XML_MediaCodecsXmlParserCreate() failed.");
542
543 ::XML_SetUserData(mParser.get(), this);
544 ::XML_SetElementHandler(mParser.get(), StartElementHandlerWrapper, EndElementHandlerWrapper);
545
546 static constexpr int BUFF_SIZE = 512;
547 // updateStatus(OK);
548 if (mStatus == NO_INIT) {
549 mStatus = OK;
550 }
551 while (mStatus == OK) {
552 void *buff = ::XML_GetBuffer(mParser.get(), BUFF_SIZE);
553 if (buff == nullptr) {
554 ALOGD("failed in call to XML_GetBuffer()");
555 mStatus = UNKNOWN_ERROR;
556 break;
557 }
558
559 int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
560 if (bytes_read < 0) {
561 ALOGD("failed in call to read");
562 mStatus = ERROR_IO;
563 break;
564 }
565
566 XML_Status status = ::XML_ParseBuffer(mParser.get(), bytes_read, bytes_read == 0);
567 if (status != XML_STATUS_OK) {
568 PLOGD("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(mParser.get())));
569 mStatus = ERROR_MALFORMED;
570 break;
571 }
572
573 if (bytes_read == 0) {
574 break;
575 }
576 }
577
578 mParser.reset();
579
580 fclose(file);
581 file = nullptr;
582 }
583
584 // static
StartElementHandlerWrapper(void * me,const char * name,const char ** attrs)585 void MediaCodecsXmlParser::Impl::Parser::StartElementHandlerWrapper(
586 void *me, const char *name, const char **attrs) {
587 static_cast<MediaCodecsXmlParser::Impl::Parser*>(me)->startElementHandler(name, attrs);
588 }
589
590 // static
EndElementHandlerWrapper(void * me,const char * name)591 void MediaCodecsXmlParser::Impl::Parser::EndElementHandlerWrapper(void *me, const char *name) {
592 static_cast<MediaCodecsXmlParser::Impl::Parser*>(me)->endElementHandler(name);
593 }
594
includeXmlFile(const char ** attrs)595 status_t MediaCodecsXmlParser::Impl::Parser::includeXmlFile(const char **attrs) {
596 const char *href = nullptr;
597 size_t i = 0;
598 while (attrs[i] != nullptr) {
599 CHECK((i & 1) == 0);
600 if (attrs[i + 1] == nullptr) {
601 PLOGD("Include: attribute '%s' is null", attrs[i]);
602 return BAD_VALUE;
603 }
604
605 if (strEq(attrs[i], "href")) {
606 href = attrs[++i];
607 } else {
608 PLOGD("Include: ignoring unrecognized attribute '%s'", attrs[i]);
609 ++i;
610 }
611 ++i;
612 }
613
614 if (href == nullptr) {
615 PLOGD("Include with no 'href' attribute");
616 return BAD_VALUE;
617 }
618
619 // For security reasons and for simplicity, file names can only contain
620 // [a-zA-Z0-9_.] and must start with media_codecs_ and end with .xml
621 for (i = 0; href[i] != '\0'; i++) {
622 if (href[i] == '.' || href[i] == '_' ||
623 (href[i] >= '0' && href[i] <= '9') ||
624 (href[i] >= 'A' && href[i] <= 'Z') ||
625 (href[i] >= 'a' && href[i] <= 'z')) {
626 continue;
627 }
628 PLOGD("invalid include file name: %s", href);
629 return BAD_VALUE;
630 }
631
632 std::string filename = href;
633 if (filename.compare(0, 13, "media_codecs_") != 0 ||
634 filename.compare(filename.size() - 4, 4, ".xml") != 0) {
635 PLOGD("invalid include file name: %s", href);
636 return BAD_VALUE;
637 }
638 filename.insert(0, mHrefBase);
639
640 Result res = mState->enterInclude(filename);
641 if (res) {
642 logAnyErrors(res);
643 return res;
644 }
645
646 // save state so that we can resume even if XML parsing of the included file failed midway
647 State::RestorePoint rp = mState->createRestorePoint();
648 Parser parser(mState, filename);
649 parser.parseXmlFile();
650 mState->restore(rp);
651 mState->exitInclude();
652 return parser.getStatus();
653 }
654
655 MediaCodecsXmlParser::Impl::Result
enterInclude(const std::string & fileName)656 MediaCodecsXmlParser::Impl::State::enterInclude(const std::string &fileName) {
657 if (std::find(mIncludeStack.begin(), mIncludeStack.end(), fileName)
658 != mIncludeStack.end()) {
659 return { BAD_VALUE, "recursive include chain" };
660 }
661 mIncludeStack.emplace_back(fileName);
662 return OK;
663 }
664
startElementHandler(const char * name,const char ** attrs)665 void MediaCodecsXmlParser::Impl::Parser::startElementHandler(
666 const char *name, const char **attrs) {
667 bool inType = true;
668 Result err = NO_INIT;
669
670 Section section = mState->section();
671
672 // handle include at any level
673 if (strEq(name, "Include")) {
674 mState->enterSection(SECTION_INCLUDE);
675 updateStatus(includeXmlFile(attrs));
676 return;
677 }
678
679 // handle include section (top level)
680 if (section == SECTION_INCLUDE) {
681 if (strEq(name, "Included")) {
682 return;
683 }
684 // imitate prior level
685 section = mState->lastNonIncludeSection();
686 }
687
688 switch (section) {
689 case SECTION_TOPLEVEL:
690 {
691 Section nextSection;
692 if (strEq(name, "Decoders")) {
693 nextSection = SECTION_DECODERS;
694 } else if (strEq(name, "Encoders")) {
695 nextSection = SECTION_ENCODERS;
696 } else if (strEq(name, "Settings")) {
697 nextSection = SECTION_SETTINGS;
698 } else if (strEq(name, "MediaCodecs") || strEq(name, "Included")) {
699 return;
700 } else {
701 break;
702 }
703 mState->enterSection(nextSection);
704 return;
705 }
706
707 case SECTION_SETTINGS:
708 {
709 if (strEq(name, "Setting")) {
710 err = addSetting(attrs);
711 } else if (strEq(name, "Variant")) {
712 err = addSetting(attrs, "variant-");
713 } else if (strEq(name, "Domain")) {
714 err = addSetting(attrs, "domain-");
715 } else {
716 break;
717 }
718 updateStatus(err);
719 return;
720 }
721
722 case SECTION_DECODERS:
723 case SECTION_ENCODERS:
724 {
725 if (strEq(name, "MediaCodec")) {
726 err = enterMediaCodec(attrs, section == SECTION_ENCODERS);
727 updateStatus(err);
728 if (err != OK) { // skip this element on error
729 mState->enterSection(SECTION_UNKNOWN);
730 } else {
731 mState->enterVariants(mState->codec().variantSet);
732 mState->enterSection(
733 section == SECTION_DECODERS ? SECTION_DECODER : SECTION_ENCODER);
734 }
735 return;
736 }
737 break;
738 }
739
740 case SECTION_DECODER:
741 case SECTION_ENCODER:
742 {
743 if (strEq(name, "Quirk")) {
744 err = addQuirk(attrs, "quirk::");
745 } else if (strEq(name, "Attribute")) {
746 err = addQuirk(attrs, "attribute::");
747 } else if (strEq(name, "Alias")) {
748 err = addAlias(attrs);
749 } else if (strEq(name, "Type")) {
750 err = enterType(attrs);
751 if (err != OK) { // skip this element on error
752 mState->enterSection(SECTION_UNKNOWN);
753 } else {
754 mState->enterSection(
755 section == SECTION_DECODER
756 ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
757 }
758 }
759 }
760 inType = false;
761 FALLTHROUGH_INTENDED;
762
763 case SECTION_DECODER_TYPE:
764 case SECTION_ENCODER_TYPE:
765 case SECTION_VARIANT:
766 {
767 // ignore limits and features specified outside of type
768 if (!mState->inType()
769 && (strEq(name, "Limit") || strEq(name, "Feature")
770 || strEq(name, "Variant") || strEq(name, "Mapping")
771 || strEq(name, "Tuning"))) {
772 PLOGD("ignoring %s specified outside of a Type", name);
773 return;
774 } else if (strEq(name, "Limit")) {
775 err = addLimit(attrs);
776 } else if (strEq(name, "Feature")) {
777 err = addFeature(attrs);
778 } else if (strEq(name, "Mapping")) {
779 err = addMapping(attrs);
780 } else if (strEq(name, "Tuning")) {
781 err = addTuning(attrs);
782 } else if (strEq(name, "Variant") && section != SECTION_VARIANT) {
783 err = limitVariants(attrs);
784 mState->enterSection(err == OK ? SECTION_VARIANT : SECTION_UNKNOWN);
785 } else if (inType
786 && (strEq(name, "Alias") || strEq(name, "Attribute") || strEq(name, "Quirk"))) {
787 PLOGD("ignoring %s specified not directly in a MediaCodec", name);
788 return;
789 } else if (err == NO_INIT) {
790 break;
791 }
792 updateStatus(err);
793 return;
794 }
795
796 default:
797 break;
798 }
799
800 if (section != SECTION_UNKNOWN) {
801 PLOGD("Ignoring unrecognized tag <%s>", name);
802 }
803 mState->enterSection(SECTION_UNKNOWN);
804 }
805
endElementHandler(const char * name)806 void MediaCodecsXmlParser::Impl::Parser::endElementHandler(const char *name) {
807 // XMLParser handles tag matching, so we really just need to handle the section state here
808 Section section = mState->section();
809 switch (section) {
810 case SECTION_INCLUDE:
811 {
812 // this could also be any of: Included, MediaCodecs
813 if (strEq(name, "Include")) {
814 mState->exitSection();
815 return;
816 }
817 break;
818 }
819
820 case SECTION_SETTINGS:
821 {
822 // this could also be any of: Domain, Variant, Setting
823 if (strEq(name, "Settings")) {
824 mState->exitSection();
825 }
826 break;
827 }
828
829 case SECTION_DECODERS:
830 case SECTION_ENCODERS:
831 case SECTION_UNKNOWN:
832 {
833 mState->exitSection();
834 break;
835 }
836
837 case SECTION_DECODER_TYPE:
838 case SECTION_ENCODER_TYPE:
839 {
840 // this could also be any of: Alias, Limit, Feature
841 if (strEq(name, "Type")) {
842 mState->exitSection();
843 mState->exitCodecOrType();
844 }
845 break;
846 }
847
848 case SECTION_DECODER:
849 case SECTION_ENCODER:
850 {
851 // this could also be any of: Alias, Limit, Quirk, Variant
852 if (strEq(name, "MediaCodec")) {
853 mState->exitSection();
854 mState->exitCodecOrType();
855 mState->exitVariants();
856 }
857 break;
858 }
859
860 case SECTION_VARIANT:
861 {
862 // this could also be any of: Alias, Limit, Quirk
863 if (strEq(name, "Variant")) {
864 mState->exitSection();
865 mState->exitVariants();
866 return;
867 }
868 break;
869 }
870
871 default:
872 break;
873 }
874 }
875
addSetting(const char ** attrs,const char * prefix)876 status_t MediaCodecsXmlParser::Impl::Parser::addSetting(const char **attrs, const char *prefix) {
877 const char *a_name = nullptr;
878 const char *a_value = nullptr;
879 const char *a_update = nullptr;
880 bool isBoolean = false;
881
882 size_t i = 0;
883 while (attrs[i] != nullptr) {
884 CHECK((i & 1) == 0);
885 if (attrs[i + 1] == nullptr) {
886 PLOGD("Setting: attribute '%s' is null", attrs[i]);
887 return BAD_VALUE;
888 }
889
890 if (strEq(attrs[i], "name")) {
891 a_name = attrs[++i];
892 } else if (strEq(attrs[i], "value") || strEq(attrs[i], "enabled")) {
893 if (a_value) {
894 PLOGD("Setting: redundant attribute '%s'", attrs[i]);
895 return BAD_VALUE;
896 }
897 isBoolean = strEq(attrs[i], "enabled");
898 a_value = attrs[++i];
899 } else if (strEq(attrs[i], "update")) {
900 a_update = attrs[++i];
901 } else {
902 PLOGD("Setting: ignoring unrecognized attribute '%s'", attrs[i]);
903 ++i;
904 }
905 ++i;
906 }
907
908 if (a_name == nullptr || a_value == nullptr) {
909 PLOGD("Setting with no 'name' or 'value' attribute");
910 return BAD_VALUE;
911 }
912
913 // Boolean values are converted to "0" or "1".
914 if (strHasPrefix(a_name, "supports-") || isBoolean) {
915 a_value = parseBoolean(a_value) ? "1" : "0";
916 }
917
918 bool update = (a_update != nullptr) && parseBoolean(a_update);
919 Result res = mState->data().addGlobal(std::string(prefix ? : "") + a_name, a_value, update);
920 if (res != OK) {
921 PLOGD("Setting: %s", res.error().c_str());
922 }
923 return res;
924 }
925
addGlobal(std::string key,std::string value,bool updating)926 MediaCodecsXmlParser::Impl::Result MediaCodecsXmlParser::Impl::Data::addGlobal(
927 std::string key, std::string value, bool updating) {
928 auto attribute = mServiceAttributeMap.find(key);
929 if (attribute == mServiceAttributeMap.end()) { // New attribute name
930 if (updating) {
931 return { NAME_NOT_FOUND, "cannot update non-existing setting" };
932 }
933 mServiceAttributeMap.insert(Attribute(key, value));
934 } else { // Existing attribute name
935 attribute->second = value;
936 if (!updating) {
937 return { ALREADY_EXISTS, "updating existing setting" };
938 }
939 }
940
941 return OK;
942 }
943
enterMediaCodec(const char ** attrs,bool encoder)944 status_t MediaCodecsXmlParser::Impl::Parser::enterMediaCodec(
945 const char **attrs, bool encoder) {
946 const char *a_name = nullptr;
947 const char *a_type = nullptr;
948 const char *a_update = nullptr;
949 const char *a_rank = nullptr;
950 const char *a_domain = nullptr;
951 const char *a_variant = nullptr;
952 const char *a_enabled = nullptr;
953 const char *a_minsdk = nullptr;
954
955 size_t i = 0;
956 while (attrs[i] != nullptr) {
957 CHECK((i & 1) == 0);
958 if (attrs[i + 1] == nullptr) {
959 PLOGD("MediaCodec: attribute '%s' is null", attrs[i]);
960 return BAD_VALUE;
961 }
962
963 if (strEq(attrs[i], "name")) {
964 a_name = attrs[++i];
965 } else if (strEq(attrs[i], "type")) {
966 a_type = attrs[++i];
967 } else if (strEq(attrs[i], "update")) {
968 a_update = attrs[++i];
969 } else if (strEq(attrs[i], "rank")) {
970 a_rank = attrs[++i];
971 } else if (strEq(attrs[i], "domain")) {
972 a_domain = attrs[++i];
973 } else if (strEq(attrs[i], "variant")) {
974 a_variant = attrs[++i];
975 } else if (strEq(attrs[i], "enabled")) {
976 a_enabled = attrs[++i];
977 } else if (strEq(attrs[i], "minsdk")) {
978 a_minsdk = attrs[++i];
979 } else {
980 PLOGD("MediaCodec: ignoring unrecognized attribute '%s'", attrs[i]);
981 ++i;
982 }
983 ++i;
984 }
985
986 if (a_name == nullptr) {
987 PLOGD("MediaCodec with no 'name' attribute");
988 return BAD_VALUE;
989 }
990
991 bool update = (a_update != nullptr) && parseBoolean(a_update);
992 if (a_domain != nullptr) {
993 // disable codecs with domain by default (unless updating)
994 if (!a_enabled && !update) {
995 a_enabled = "false";
996 }
997 }
998
999 Result res = mState->enterMediaCodec(encoder, a_name, a_type, update);
1000 if (res != OK) {
1001 logAnyErrors(res);
1002 return res;
1003 }
1004
1005 return updateMediaCodec(
1006 a_rank, parseCommaSeparatedStringSet(a_domain),
1007 parseCommaSeparatedStringSet(a_variant), a_enabled, a_minsdk);
1008 }
1009
1010 MediaCodecsXmlParser::Impl::Result
enterMediaCodec(bool encoder,const char * name,const char * type,bool updating)1011 MediaCodecsXmlParser::Impl::State::enterMediaCodec(
1012 bool encoder, const char *name, const char *type, bool updating) {
1013 // store name even in case of an error
1014 CodecMap::iterator codecIt = mData->mCodecMap.find(name);
1015 TypeMap::iterator typeIt;
1016 if (codecIt == mData->mCodecMap.end()) { // New codec name
1017 if (updating) {
1018 std::string msg = "MediaCodec: cannot update non-existing codec: ";
1019 msg = msg + name;
1020 return { NAME_NOT_FOUND, msg };
1021 }
1022 // Create a new codec in mCodecMap
1023 codecIt = mData->mCodecMap.insert(Codec(name, CodecProperties())).first;
1024 if (type != nullptr) {
1025 typeIt = codecIt->second.typeMap.insert(Type(type, AttributeMap())).first;
1026 } else {
1027 typeIt = codecIt->second.typeMap.end();
1028 }
1029 codecIt->second.isEncoder = encoder;
1030 codecIt->second.order = mData->mCodecMap.size();
1031 } else { // Existing codec name
1032 if (!updating) {
1033 std::string msg = "MediaCodec: cannot add existing codec: ";
1034 msg = msg + name;
1035 return { ALREADY_EXISTS, msg };
1036 }
1037 if (type != nullptr) {
1038 typeIt = codecIt->second.typeMap.find(type);
1039 if (typeIt == codecIt->second.typeMap.end()) {
1040 std::string msg = "MediaCodec: cannot update non-existing type for codec: ";
1041 msg = msg + name;
1042 return { NAME_NOT_FOUND, msg };
1043 }
1044 } else {
1045 // This should happen only when the codec has at most one type.
1046 typeIt = codecIt->second.typeMap.begin();
1047 if (typeIt == codecIt->second.typeMap.end()
1048 || codecIt->second.typeMap.size() != 1) {
1049 std::string msg = "MediaCodec: cannot update codec without type specified: ";
1050 msg = msg + name;
1051 return { BAD_VALUE, msg };
1052 }
1053 }
1054 }
1055 mCurrent.emplace_back(CodecAndType{name, codecIt, typeIt, updating});
1056 return OK;
1057 }
1058
updateMediaCodec(const char * rank,const StringSet & domains,const StringSet & variants,const char * enabled,const char * minsdk)1059 status_t MediaCodecsXmlParser::Impl::Parser::updateMediaCodec(
1060 const char *rank, const StringSet &domains, const StringSet &variants,
1061 const char *enabled, const char *minsdk) {
1062 CHECK(mState->inCodec());
1063 CodecProperties &codec = mState->codec();
1064
1065 if (rank != nullptr) {
1066 ALOGD_IF(!codec.rank.empty() && codec.rank != rank,
1067 "codec '%s' rank changed from '%s' to '%s'",
1068 mState->codecName().c_str(), codec.rank.c_str(), rank);
1069 codec.rank = rank;
1070 }
1071
1072 codec.variantSet = variants;
1073
1074 // we allow sets of domains...
1075 for (const std::string &domain : domains) {
1076 if (domain.size() && domain.at(0) == '!') {
1077 codec.domainSet.erase(domain.substr(1));
1078 } else {
1079 codec.domainSet.emplace(domain);
1080 }
1081 }
1082
1083 if (enabled != nullptr) {
1084 if (parseBoolean(enabled)) {
1085 codec.quirkSet.erase("attribute::disabled");
1086 ALOGD("enabling %s", mState->codecName().c_str());
1087 } else {
1088 codec.quirkSet.emplace("attribute::disabled");
1089 ALOGD("disabling %s", mState->codecName().c_str());
1090 }
1091 }
1092
1093 // evaluate against passed minsdk, with lots of logging to explain the logic
1094 //
1095 // if current sdk >= minsdk, we want to enable the codec
1096 // this OVERRIDES any enabled="true|false" setting on the codec.
1097 // (enabled=true minsdk=35 on a sdk 34 device results in a disabled codec)
1098 //
1099 // Although minsdk is not parsed before Android U, we can carry media_codecs.xml
1100 // using this to devices earlier (e.g. as part of mainline). An example is appropriate.
1101 //
1102 // we have a codec that we want enabled in Android V (sdk=35), so we use:
1103 // <MediaCodec ..... enabled="false" minsdk="35" >
1104 //
1105 // on Q/R/S/T: it sees enabled=false, but ignores the unrecognized minsdk
1106 // so the codec will be disabled
1107 // on U: it sees enabled=false, and sees minsdk=35, but U==34 and 34 < 35
1108 // so the codec will be disabled
1109 // on V: it sees enabled=false, and sees minsdk=35, V==35 and 35 >= 35
1110 // so the codec will be enabled
1111 //
1112 // if we know the XML files will be used only on devices >= U, we can skip the enabled=false
1113 // piece. Android mainline's support horizons say we will be using the enabled=false for
1114 // another 4-5 years after U.
1115 //
1116 if (minsdk != nullptr) {
1117 char *p = nullptr;
1118 int sdk = strtol(minsdk, &p, 0);
1119 if (p == minsdk || sdk < 0) {
1120 ALOGE("minsdk parsing '%s' yielded %d, mapping to 0", minsdk, sdk);
1121 sdk = 0;
1122 }
1123 // minsdk="#" means: "enable if sdk is >= #, disable otherwise"
1124 if (mysdk < sdk) {
1125 ALOGI("codec %s disabled, device sdk %d < required %d",
1126 mState->codecName().c_str(), mysdk, sdk);
1127 codec.quirkSet.emplace("attribute::disabled");
1128 } else {
1129 ALOGI("codec %s enabled, device sdk %d >= required %d",
1130 mState->codecName().c_str(), mysdk, sdk);
1131 codec.quirkSet.erase("attribute::disabled");
1132 }
1133 }
1134
1135 return OK;
1136 }
1137
addQuirk(const char ** attrs,const char * prefix)1138 status_t MediaCodecsXmlParser::Impl::Parser::addQuirk(const char **attrs, const char *prefix) {
1139 CHECK(mState->inCodec());
1140 const char *a_name = nullptr;
1141
1142 size_t i = 0;
1143 while (attrs[i] != nullptr) {
1144 CHECK((i & 1) == 0);
1145 if (attrs[i + 1] == nullptr) {
1146 PLOGD("Quirk: attribute '%s' is null", attrs[i]);
1147 return BAD_VALUE;
1148 }
1149
1150 if (strEq(attrs[i], "name")) {
1151 a_name = attrs[++i];
1152 } else {
1153 PLOGD("Quirk: ignoring unrecognized attribute '%s'", attrs[i]);
1154 ++i;
1155 }
1156 ++i;
1157 }
1158
1159 if (a_name == nullptr) {
1160 PLOGD("Quirk with no 'name' attribute");
1161 return BAD_VALUE;
1162 }
1163
1164 std::string key = std::string(prefix ? : "") + a_name;
1165 mState->codec().quirkSet.emplace(key);
1166 ALOGV("adding %s to %s", key.c_str(), mState->codecName().c_str());
1167 return OK;
1168 }
1169
enterType(const char ** attrs)1170 status_t MediaCodecsXmlParser::Impl::Parser::enterType(const char **attrs) {
1171 CHECK(mState->inCodec());
1172
1173 const char *a_name = nullptr;
1174 const char *a_update = nullptr;
1175
1176 size_t i = 0;
1177 while (attrs[i] != nullptr) {
1178 CHECK((i & 1) == 0);
1179 if (attrs[i + 1] == nullptr) {
1180 PLOGD("Type: attribute '%s' is null", attrs[i]);
1181 return BAD_VALUE;
1182 }
1183
1184 if (strEq(attrs[i], "name")) {
1185 a_name = attrs[++i];
1186 } else if (strEq(attrs[i], "update")) {
1187 a_update = attrs[++i];
1188 } else {
1189 PLOGD("Type: ignoring unrecognized attribute '%s'", attrs[i]);
1190 ++i;
1191 }
1192 ++i;
1193 }
1194
1195 if (a_name == nullptr) {
1196 PLOGD("Type with no 'name' attribute");
1197 return BAD_VALUE;
1198 }
1199
1200 bool update = (a_update != nullptr) && parseBoolean(a_update);
1201 return mState->enterType(a_name, update);
1202 }
1203
1204 MediaCodecsXmlParser::Impl::Result
enterType(const char * name,bool update)1205 MediaCodecsXmlParser::Impl::State::enterType(const char *name, bool update) {
1206 update = update || updating(); // handle parent
1207
1208 CodecMap::iterator codecIt = mCurrent.back().mCodec;
1209 TypeMap::iterator typeIt = codecIt->second.typeMap.find(name);
1210 if (!update) {
1211 if (typeIt != codecIt->second.typeMap.end()) {
1212 return { ALREADY_EXISTS, "trying to update existing type '" + std::string(name) + "'" };
1213 }
1214 typeIt = codecIt->second.typeMap.insert(Type(name, AttributeMap())).first;
1215 } else if (typeIt == codecIt->second.typeMap.end()) {
1216 return { NAME_NOT_FOUND, "addType: updating non-existing type" };
1217 }
1218 mCurrent.push_back({ codecName(), codecIt, typeIt, update });
1219 CHECK(inType());
1220 return OK;
1221 }
1222
addLimit(const char ** attrs)1223 status_t MediaCodecsXmlParser::Impl::Parser::addLimit(const char **attrs) {
1224 CHECK(mState->inType());
1225 const char* a_name = nullptr;
1226 const char* a_default = nullptr;
1227 const char* a_in = nullptr;
1228 const char* a_max = nullptr;
1229 const char* a_min = nullptr;
1230 const char* a_range = nullptr;
1231 const char* a_ranges = nullptr;
1232 const char* a_scale = nullptr;
1233 const char* a_value = nullptr;
1234
1235 size_t i = 0;
1236 while (attrs[i] != nullptr) {
1237 CHECK((i & 1) == 0);
1238 if (attrs[i + 1] == nullptr) {
1239 PLOGD("Limit: attribute '%s' is null", attrs[i]);
1240 return BAD_VALUE;
1241 }
1242
1243 if (strEq(attrs[i], "name")) {
1244 a_name = attrs[++i];
1245 } else if (strEq(attrs[i], "default")) {
1246 a_default = attrs[++i];
1247 } else if (strEq(attrs[i], "in")) {
1248 a_in = attrs[++i];
1249 } else if (strEq(attrs[i], "max")) {
1250 a_max = attrs[++i];
1251 } else if (strEq(attrs[i], "min")) {
1252 a_min = attrs[++i];
1253 } else if (strEq(attrs[i], "range")) {
1254 a_range = attrs[++i];
1255 } else if (strEq(attrs[i], "ranges")) {
1256 a_ranges = attrs[++i];
1257 } else if (strEq(attrs[i], "scale")) {
1258 a_scale = attrs[++i];
1259 } else if (strEq(attrs[i], "value")) {
1260 a_value = attrs[++i];
1261 } else {
1262 PLOGD("Limit: ignoring unrecognized limit: %s", attrs[i]);
1263 ++i;
1264 }
1265 ++i;
1266 }
1267
1268 if (a_name == nullptr) {
1269 PLOGD("Limit with no 'name' attribute");
1270 return BAD_VALUE;
1271 }
1272
1273 // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio,
1274 // measured-frame-rate, measured-blocks-per-second: range
1275 // quality: range + default + [scale]
1276 // complexity: range + default
1277 std::string key = a_name, value;
1278
1279 // don't allow specifying more than one of value, range or min/max
1280 if ((a_value != nullptr) + (a_range != nullptr) + (a_ranges != nullptr)
1281 + (a_min != nullptr || a_max != nullptr) > 1) {
1282 PLOGD("Limit '%s' has multiple 'min'/'max', 'range', 'ranges' or 'value' attributes",
1283 a_name);
1284 return BAD_VALUE;
1285 }
1286
1287 // Min/max limits (only containing min or max attribute)
1288 //
1289 // Current "max" limits are "channel-count", "concurrent-instances".
1290 // There are no current "min" limits
1291 //
1292 // Range limits. "range" is specified in exactly one of the following forms:
1293 // 1) min-max
1294 // 2) value-value
1295 // 3) range
1296 //
1297 // Current range limits are "aspect-ratio", "bitrate", "block-count", "blocks-per-second",
1298 // "complexity", "frame-rate", "quality", "size", "measured-blocks-per-second",
1299 // "performance-point-*", "measured-frame-rate-*"
1300 //
1301 // Other limits (containing only value or ranges)
1302 //
1303 // Current ranges limit is "sample-rate"
1304 if ((a_min != nullptr) ^ (a_max != nullptr)) {
1305 // min/max limit
1306 if (a_max != nullptr) {
1307 key = "max-" + key;
1308 value = a_max;
1309 } else if (a_min != nullptr) {
1310 key = "min-" + key;
1311 value = a_min;
1312 }
1313 } else if (a_min != nullptr && a_max != nullptr) {
1314 // min-max
1315 key += "-range";
1316 value = a_min + std::string("-") + a_max;
1317 } else if (a_value != nullptr) {
1318 // value-value or value
1319 value = a_value;
1320 if (strEq(a_name, "aspect-ratio") ||
1321 strEq(a_name, "bitrate") ||
1322 strEq(a_name, "block-count") ||
1323 strEq(a_name, "blocks-per-second") ||
1324 strEq(a_name, "complexity") ||
1325 strEq(a_name, "frame-rate") ||
1326 strEq(a_name, "quality") ||
1327 strEq(a_name, "size") ||
1328 strEq(a_name, "measured-blocks-per-second") ||
1329 strHasPrefix(a_name, "performance-point-") ||
1330 strHasPrefix(a_name, "measured-frame-rate-")) {
1331 key += "-range";
1332 value += std::string("-") + a_value;
1333 }
1334 } else if (a_range != nullptr) {
1335 // range
1336 key += "-range";
1337 value = a_range;
1338 } else if (a_ranges != nullptr) {
1339 // ranges
1340 key += "-ranges";
1341 value = a_ranges;
1342 } else {
1343 PLOGD("Limit '%s' with no 'range', 'value' or 'min'/'max' attributes", a_name);
1344 return BAD_VALUE;
1345 }
1346
1347 // handle 'in' attribute - this changes the key
1348 if (a_in != nullptr) {
1349 // Currently "aspect-ratio" uses in attribute
1350 const size_t a_in_len = strlen(a_in);
1351 key = std::string(a_in, a_in_len - a_in[a_in_len] == 's') + '-' + key;
1352 }
1353
1354 // handle 'scale' attribute - this adds a new detail
1355 if (a_scale != nullptr) {
1356 mState->addDetail(a_name + std::string("-scale"), a_scale);
1357 } else if (strEq(a_name, "quality")) {
1358 // The default value of "quality-scale" is "linear" even if unspecified.
1359 mState->addDetail(a_name + std::string("-scale"), "linear");
1360 }
1361
1362 // handle 'default' attribute - this adds a new detail
1363 if (a_default != nullptr) {
1364 mState->addDetail(a_name + std::string("-default"), a_default);
1365 }
1366
1367 mState->addDetail(key, value);
1368 return OK;
1369 }
1370
addDetail(const std::string & key,const std::string & value)1371 void MediaCodecsXmlParser::Impl::State::addDetail(
1372 const std::string &key, const std::string &value) {
1373 CHECK(inType());
1374 ALOGV("limit: %s = %s", key.c_str(), value.c_str());
1375 const StringSet &variants = mVariantsStack.back();
1376 if (variants.empty()) {
1377 type()[key] = value;
1378 } else {
1379 for (const std::string &variant : variants) {
1380 type()[variant + ":::" + key] = value;
1381 }
1382 }
1383 }
1384
limitVariants(const char ** attrs)1385 status_t MediaCodecsXmlParser::Impl::Parser::limitVariants(const char **attrs) {
1386 const char* a_name = nullptr;
1387
1388 size_t i = 0;
1389 while (attrs[i] != nullptr) {
1390 CHECK((i & 1) == 0);
1391 if (attrs[i + 1] == nullptr) {
1392 PLOGD("Variant: attribute '%s' is null", attrs[i]);
1393 return BAD_VALUE;
1394 }
1395 if (strEq(attrs[i], "name")) {
1396 a_name = attrs[++i];
1397 } else {
1398 PLOGD("Variant: ignoring unrecognized attribute: %s", attrs[i]);
1399 ++i;
1400 }
1401 ++i;
1402 }
1403
1404 if (a_name == nullptr || *a_name == '\0') {
1405 PLOGD("Variant with no or empty 'name' attribute");
1406 return BAD_VALUE;
1407 }
1408
1409 StringSet variants;
1410 for (const std::string &variant : parseCommaSeparatedStringSet(a_name)) {
1411 if (mState->variants().count(variant)) {
1412 variants.emplace(variant);
1413 } else {
1414 PLOGD("Variant: variant '%s' not in parent variants", variant.c_str());
1415 return BAD_VALUE;
1416 }
1417 }
1418 mState->enterVariants(variants);
1419 return OK;
1420 }
1421
addFeature(const char ** attrs)1422 status_t MediaCodecsXmlParser::Impl::Parser::addFeature(const char **attrs) {
1423 CHECK(mState->inType());
1424 size_t i = 0;
1425 const char *a_name = nullptr;
1426 int32_t optional = -1;
1427 int32_t required = -1;
1428 const char *a_value = nullptr;
1429
1430 while (attrs[i] != nullptr) {
1431 CHECK((i & 1) == 0);
1432 if (attrs[i + 1] == nullptr) {
1433 PLOGD("Feature: attribute '%s' is null", attrs[i]);
1434 return BAD_VALUE;
1435 }
1436
1437 if (strEq(attrs[i], "name")) {
1438 a_name = attrs[++i];
1439 } else if (strEq(attrs[i], "optional")) {
1440 optional = parseBoolean(attrs[++i]) ? 1 : 0;
1441 } else if (strEq(attrs[i], "required")) {
1442 required = parseBoolean(attrs[++i]) ? 1 : 0;
1443 } else if (strEq(attrs[i], "value")) {
1444 a_value = attrs[++i];
1445 } else {
1446 PLOGD("Feature: ignoring unrecognized attribute '%s'", attrs[i]);
1447 ++i;
1448 }
1449 ++i;
1450 }
1451
1452 // Every feature must have a name.
1453 if (a_name == nullptr) {
1454 PLOGD("Feature with no 'name' attribute");
1455 return BAD_VALUE;
1456 }
1457
1458 if (a_value != nullptr) {
1459 if (optional != -1 || required != -1) {
1460 PLOGD("Feature '%s' has both value and optional/required attributes", a_name);
1461 return BAD_VALUE;
1462 }
1463 } else {
1464 if (optional == required && optional != -1) {
1465 PLOGD("Feature '%s' is both/neither optional and required", a_name);
1466 return BAD_VALUE;
1467 }
1468 a_value = (required == 1 || optional == 0) ? "1" : "0";
1469 }
1470
1471 mState->addDetail(std::string("feature-") + a_name, a_value ? : "0");
1472 return OK;
1473 }
1474
addMapping(const char ** attrs)1475 status_t MediaCodecsXmlParser::Impl::Parser::addMapping(const char **attrs) {
1476 CHECK(mState->inType());
1477 size_t i = 0;
1478 const char *a_name = nullptr;
1479 const char *a_value = nullptr;
1480 const char *a_kind = nullptr;
1481
1482 while (attrs[i] != nullptr) {
1483 CHECK((i & 1) == 0);
1484 if (attrs[i + 1] == nullptr) {
1485 PLOGD("Mapping: attribute '%s' is null", attrs[i]);
1486 return BAD_VALUE;
1487 }
1488
1489 if (strEq(attrs[i], "name")) {
1490 a_name = attrs[++i];
1491 } else if (strEq(attrs[i], "kind")) {
1492 a_kind = attrs[++i];
1493 } else if (strEq(attrs[i], "value")) {
1494 a_value = attrs[++i];
1495 } else {
1496 PLOGD("Mapping: ignoring unrecognized attribute '%s'", attrs[i]);
1497 ++i;
1498 }
1499 ++i;
1500 }
1501
1502 // Every mapping must have all 3 fields
1503 if (a_name == nullptr) {
1504 PLOGD("Mapping with no 'name' attribute");
1505 return BAD_VALUE;
1506 }
1507
1508 if (a_kind == nullptr) {
1509 PLOGD("Mapping with no 'kind' attribute");
1510 return BAD_VALUE;
1511 }
1512
1513 if (a_value == nullptr) {
1514 PLOGD("Mapping with no 'value' attribute");
1515 return BAD_VALUE;
1516 }
1517
1518 mState->addDetail(std::string("mapping-") + a_kind + "-" + a_name, a_value);
1519 return OK;
1520 }
1521
addTuning(const char ** attrs)1522 status_t MediaCodecsXmlParser::Impl::Parser::addTuning(const char **attrs) {
1523 CHECK(mState->inType());
1524 size_t i = 0;
1525 const char *a_name = nullptr;
1526 const char *a_value = nullptr;
1527
1528 while (attrs[i] != nullptr) {
1529 CHECK((i & 1) == 0);
1530 if (attrs[i + 1] == nullptr) {
1531 PLOGD("Mapping: attribute '%s' is null", attrs[i]);
1532 return BAD_VALUE;
1533 }
1534
1535 if (strEq(attrs[i], "name")) {
1536 a_name = attrs[++i];
1537 } else if (strEq(attrs[i], "value")) {
1538 a_value = attrs[++i];
1539 } else {
1540 PLOGD("Tuning: ignoring unrecognized attribute '%s'", attrs[i]);
1541 ++i;
1542 }
1543 ++i;
1544 }
1545
1546 // Every tuning must have both fields
1547 if (a_name == nullptr) {
1548 PLOGD("Tuning with no 'name' attribute");
1549 return BAD_VALUE;
1550 }
1551
1552 if (a_value == nullptr) {
1553 PLOGD("Tuning with no 'value' attribute");
1554 return BAD_VALUE;
1555 }
1556
1557 mState->addDetail(std::string("tuning-") + a_name, a_value);
1558 return OK;
1559 }
1560
addAlias(const char ** attrs)1561 status_t MediaCodecsXmlParser::Impl::Parser::addAlias(const char **attrs) {
1562 CHECK(mState->inCodec());
1563 size_t i = 0;
1564 const char *a_name = nullptr;
1565
1566 while (attrs[i] != nullptr) {
1567 CHECK((i & 1) == 0);
1568 if (attrs[i + 1] == nullptr) {
1569 PLOGD("Alias: attribute '%s' is null", attrs[i]);
1570 return BAD_VALUE;
1571 }
1572
1573 if (strEq(attrs[i], "name")) {
1574 a_name = attrs[++i];
1575 } else {
1576 PLOGD("Alias: ignoring unrecognized attribute '%s'", attrs[i]);
1577 ++i;
1578 }
1579 ++i;
1580 }
1581
1582 // Every feature must have a name.
1583 if (a_name == nullptr) {
1584 PLOGD("Alias with no 'name' attribute");
1585 return BAD_VALUE;
1586 }
1587
1588 mState->codec().aliases.emplace_back(a_name);
1589 return OK;
1590 }
1591
1592 const MediaCodecsXmlParser::AttributeMap&
getServiceAttributeMap() const1593 MediaCodecsXmlParser::getServiceAttributeMap() const {
1594 return mImpl->getServiceAttributeMap();
1595 }
1596
1597 const MediaCodecsXmlParser::CodecMap&
getCodecMap() const1598 MediaCodecsXmlParser::getCodecMap() const {
1599 return mImpl->getCodecMap();
1600 }
1601
1602 const MediaCodecsXmlParser::RoleMap&
getRoleMap() const1603 MediaCodecsXmlParser::getRoleMap() const {
1604 return mImpl->getRoleMap();
1605 }
1606
1607 const MediaCodecsXmlParser::RoleMap&
getRoleMap() const1608 MediaCodecsXmlParser::Impl::getRoleMap() const {
1609 std::lock_guard<std::mutex> guard(mLock);
1610 if (mRoleMap.empty()) {
1611 generateRoleMap();
1612 }
1613 return mRoleMap;
1614 }
1615
getCommonPrefix() const1616 const char* MediaCodecsXmlParser::getCommonPrefix() const {
1617 return mImpl->getCommonPrefix();
1618 }
1619
getCommonPrefix() const1620 const char* MediaCodecsXmlParser::Impl::getCommonPrefix() const {
1621 std::lock_guard<std::mutex> guard(mLock);
1622 if (mCommonPrefix.empty()) {
1623 generateCommonPrefix();
1624 }
1625 return mCommonPrefix.data();
1626 }
1627
getParsingStatus() const1628 status_t MediaCodecsXmlParser::getParsingStatus() const {
1629 return mImpl->getParsingStatus();
1630 }
1631
generateRoleMap() const1632 void MediaCodecsXmlParser::Impl::generateRoleMap() const {
1633 for (const auto& codec : mData.mCodecMap) {
1634 const auto &codecName = codec.first;
1635 if (codecName == "<dummy>") {
1636 continue;
1637 }
1638 bool isEncoder = codec.second.isEncoder;
1639 size_t order = codec.second.order;
1640 std::string rank = codec.second.rank;
1641 const auto& typeMap = codec.second.typeMap;
1642 for (const auto& type : typeMap) {
1643 const auto& typeName = type.first;
1644 const char* roleName = GetComponentRole(isEncoder, typeName.data());
1645 if (roleName == nullptr) {
1646 ALOGE("Cannot find the role for %s of type %s",
1647 isEncoder ? "an encoder" : "a decoder",
1648 typeName.data());
1649 continue;
1650 }
1651 const auto& typeAttributeMap = type.second;
1652
1653 auto roleIterator = mRoleMap.find(roleName);
1654 std::multimap<size_t, NodeInfo>* nodeList;
1655 if (roleIterator == mRoleMap.end()) {
1656 RoleProperties roleProperties;
1657 roleProperties.type = typeName;
1658 roleProperties.isEncoder = isEncoder;
1659 auto insertResult = mRoleMap.insert(
1660 std::make_pair(roleName, roleProperties));
1661 if (!insertResult.second) {
1662 ALOGE("Cannot add role %s", roleName);
1663 continue;
1664 }
1665 nodeList = &insertResult.first->second.nodeList;
1666 } else {
1667 if (roleIterator->second.type != typeName) {
1668 ALOGE("Role %s has mismatching types: %s and %s",
1669 roleName,
1670 roleIterator->second.type.data(),
1671 typeName.data());
1672 continue;
1673 }
1674 if (roleIterator->second.isEncoder != isEncoder) {
1675 ALOGE("Role %s cannot be both an encoder and a decoder",
1676 roleName);
1677 continue;
1678 }
1679 nodeList = &roleIterator->second.nodeList;
1680 }
1681
1682 NodeInfo nodeInfo;
1683 nodeInfo.name = codecName;
1684 // NOTE: no aliases are exposed in role info
1685 // attribute quirks are exposed as node attributes
1686 nodeInfo.attributeList.reserve(typeAttributeMap.size());
1687 for (const auto& attribute : typeAttributeMap) {
1688 nodeInfo.attributeList.push_back(
1689 Attribute{attribute.first, attribute.second});
1690 }
1691 for (const std::string &quirk : codec.second.quirkSet) {
1692 if (strHasPrefix(quirk.c_str(), "attribute::")) {
1693 nodeInfo.attributeList.push_back(Attribute{quirk, "present"});
1694 }
1695 }
1696 if (!rank.empty()) {
1697 nodeInfo.attributeList.push_back(Attribute{"rank", rank});
1698 }
1699 nodeList->insert(std::make_pair(
1700 order, std::move(nodeInfo)));
1701 }
1702 }
1703 }
1704
generateCommonPrefix() const1705 void MediaCodecsXmlParser::Impl::generateCommonPrefix() const {
1706 if (mData.mCodecMap.empty()) {
1707 return;
1708 }
1709 auto i = mData.mCodecMap.cbegin();
1710 auto first = i->first.cbegin();
1711 auto last = i->first.cend();
1712 for (++i; i != mData.mCodecMap.cend(); ++i) {
1713 last = std::mismatch(
1714 first, last, i->first.cbegin(), i->first.cend()).first;
1715 }
1716 mCommonPrefix.insert(mCommonPrefix.begin(), first, last);
1717 }
1718
1719 } // namespace android
1720