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 <utils/Log.h>
23 #include <media/stagefright/MediaErrors.h>
24 #include <media/stagefright/omx/OMXUtils.h>
25 #include <sys/stat.h>
26 #include <expat.h>
27
28 #include <cctype>
29 #include <algorithm>
30
31 namespace android {
32
33 namespace {
34
35 /**
36 * Search for a file in a list of search directories.
37 *
38 * For each string `searchDir` in `searchDirs`, `searchDir/fileName` will be
39 * tested whether it is a valid file name or not. If it is a valid file name,
40 * the concatenated name (`searchDir/fileName`) will be stored in the output
41 * variable `outPath`, and the function will return `true`. Otherwise, the
42 * search continues until the `nullptr` element in `searchDirs` is reached, at
43 * which point the function returns `false`.
44 *
45 * \param[in] searchDirs Null-terminated array of search paths.
46 * \param[in] fileName Name of the file to search.
47 * \param[out] outPath Full path of the file. `outPath` will hold a valid file
48 * name if the return value of this function is `true`.
49 * \return `true` if some element in `searchDirs` combined with `fileName` is a
50 * valid file name; `false` otherwise.
51 */
findFileInDirs(const char * const * searchDirs,const char * fileName,std::string * outPath)52 bool findFileInDirs(
53 const char* const* searchDirs,
54 const char *fileName,
55 std::string *outPath) {
56 for (; *searchDirs != nullptr; ++searchDirs) {
57 *outPath = std::string(*searchDirs) + "/" + fileName;
58 struct stat fileStat;
59 if (stat(outPath->c_str(), &fileStat) == 0 &&
60 S_ISREG(fileStat.st_mode)) {
61 return true;
62 }
63 }
64 return false;
65 }
66
strnEq(const char * s1,const char * s2,size_t count)67 bool strnEq(const char* s1, const char* s2, size_t count) {
68 return strncmp(s1, s2, count) == 0;
69 }
70
strEq(const char * s1,const char * s2)71 bool strEq(const char* s1, const char* s2) {
72 return strcmp(s1, s2) == 0;
73 }
74
striEq(const char * s1,const char * s2)75 bool striEq(const char* s1, const char* s2) {
76 return strcasecmp(s1, s2) == 0;
77 }
78
strHasPrefix(const char * test,const char * prefix)79 bool strHasPrefix(const char* test, const char* prefix) {
80 return strnEq(test, prefix, strlen(prefix));
81 }
82
parseBoolean(const char * s)83 bool parseBoolean(const char* s) {
84 return striEq(s, "y") ||
85 striEq(s, "yes") ||
86 striEq(s, "t") ||
87 striEq(s, "true") ||
88 striEq(s, "1");
89 }
90
limitFoundMissingAttr(const char * name,const char * attr,bool found=true)91 status_t limitFoundMissingAttr(const char* name, const char *attr, bool found = true) {
92 ALOGE("limit '%s' with %s'%s' attribute", name,
93 (found ? "" : "no "), attr);
94 return -EINVAL;
95 }
96
limitError(const char * name,const char * msg)97 status_t limitError(const char* name, const char *msg) {
98 ALOGE("limit '%s' %s", name, msg);
99 return -EINVAL;
100 }
101
limitInvalidAttr(const char * name,const char * attr,const char * value)102 status_t limitInvalidAttr(const char* name, const char* attr, const char* value) {
103 ALOGE("limit '%s' with invalid '%s' attribute (%s)", name,
104 attr, value);
105 return -EINVAL;
106 }
107
108 }; // unnamed namespace
109
110 constexpr char const* MediaCodecsXmlParser::defaultSearchDirs[];
111 constexpr char const* MediaCodecsXmlParser::defaultMainXmlName;
112 constexpr char const* MediaCodecsXmlParser::defaultPerformanceXmlName;
113 constexpr char const* MediaCodecsXmlParser::defaultProfilingResultsXmlPath;
114
MediaCodecsXmlParser(const char * const * searchDirs,const char * mainXmlName,const char * performanceXmlName,const char * profilingResultsXmlPath)115 MediaCodecsXmlParser::MediaCodecsXmlParser(
116 const char* const* searchDirs,
117 const char* mainXmlName,
118 const char* performanceXmlName,
119 const char* profilingResultsXmlPath) :
120 mParsingStatus(NO_INIT),
121 mUpdate(false),
122 mCodecCounter(0) {
123 std::string path;
124 if (findFileInDirs(searchDirs, mainXmlName, &path)) {
125 parseTopLevelXMLFile(path.c_str(), false);
126 } else {
127 ALOGE("Cannot find %s", mainXmlName);
128 mParsingStatus = NAME_NOT_FOUND;
129 }
130 if (findFileInDirs(searchDirs, performanceXmlName, &path)) {
131 parseTopLevelXMLFile(path.c_str(), true);
132 }
133 if (profilingResultsXmlPath != nullptr) {
134 parseTopLevelXMLFile(profilingResultsXmlPath, true);
135 }
136 }
137
parseTopLevelXMLFile(const char * codecs_xml,bool ignore_errors)138 bool MediaCodecsXmlParser::parseTopLevelXMLFile(
139 const char *codecs_xml,
140 bool ignore_errors) {
141 // get href_base
142 const char *href_base_end = strrchr(codecs_xml, '/');
143 if (href_base_end != nullptr) {
144 mHrefBase = std::string(codecs_xml, href_base_end - codecs_xml + 1);
145 }
146
147 mParsingStatus = OK; // keeping this here for safety
148 mCurrentSection = SECTION_TOPLEVEL;
149
150 parseXMLFile(codecs_xml);
151
152 if (mParsingStatus != OK) {
153 ALOGW("parseTopLevelXMLFile(%s) failed", codecs_xml);
154 if (ignore_errors) {
155 mParsingStatus = OK;
156 return false;
157 }
158 mCodecMap.clear();
159 return false;
160 }
161 return true;
162 }
163
~MediaCodecsXmlParser()164 MediaCodecsXmlParser::~MediaCodecsXmlParser() {
165 }
166
parseXMLFile(const char * path)167 void MediaCodecsXmlParser::parseXMLFile(const char *path) {
168 FILE *file = fopen(path, "r");
169
170 if (file == nullptr) {
171 ALOGW("unable to open media codecs configuration xml file: %s", path);
172 mParsingStatus = NAME_NOT_FOUND;
173 return;
174 }
175
176 XML_Parser parser = ::XML_ParserCreate(nullptr);
177 LOG_FATAL_IF(parser == nullptr, "XML_MediaCodecsXmlParserCreate() failed.");
178
179 ::XML_SetUserData(parser, this);
180 ::XML_SetElementHandler(
181 parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
182
183 static constexpr int BUFF_SIZE = 512;
184 while (mParsingStatus == OK) {
185 void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
186 if (buff == nullptr) {
187 ALOGE("failed in call to XML_GetBuffer()");
188 mParsingStatus = UNKNOWN_ERROR;
189 break;
190 }
191
192 int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
193 if (bytes_read < 0) {
194 ALOGE("failed in call to read");
195 mParsingStatus = ERROR_IO;
196 break;
197 }
198
199 XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0);
200 if (status != XML_STATUS_OK) {
201 ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser)));
202 mParsingStatus = ERROR_MALFORMED;
203 break;
204 }
205
206 if (bytes_read == 0) {
207 break;
208 }
209 }
210
211 ::XML_ParserFree(parser);
212
213 fclose(file);
214 file = nullptr;
215 }
216
217 // static
StartElementHandlerWrapper(void * me,const char * name,const char ** attrs)218 void MediaCodecsXmlParser::StartElementHandlerWrapper(
219 void *me, const char *name, const char **attrs) {
220 static_cast<MediaCodecsXmlParser*>(me)->startElementHandler(name, attrs);
221 }
222
223 // static
EndElementHandlerWrapper(void * me,const char * name)224 void MediaCodecsXmlParser::EndElementHandlerWrapper(void *me, const char *name) {
225 static_cast<MediaCodecsXmlParser*>(me)->endElementHandler(name);
226 }
227
includeXMLFile(const char ** attrs)228 status_t MediaCodecsXmlParser::includeXMLFile(const char **attrs) {
229 const char *href = nullptr;
230 size_t i = 0;
231 while (attrs[i] != nullptr) {
232 if (strEq(attrs[i], "href")) {
233 if (attrs[++i] == nullptr) {
234 return -EINVAL;
235 }
236 href = attrs[i];
237 } else {
238 ALOGE("includeXMLFile: unrecognized attribute: %s", attrs[i]);
239 return -EINVAL;
240 }
241 ++i;
242 }
243
244 // For security reasons and for simplicity, file names can only contain
245 // [a-zA-Z0-9_.] and must start with media_codecs_ and end with .xml
246 for (i = 0; href[i] != '\0'; i++) {
247 if (href[i] == '.' || href[i] == '_' ||
248 (href[i] >= '0' && href[i] <= '9') ||
249 (href[i] >= 'A' && href[i] <= 'Z') ||
250 (href[i] >= 'a' && href[i] <= 'z')) {
251 continue;
252 }
253 ALOGE("invalid include file name: %s", href);
254 return -EINVAL;
255 }
256
257 std::string filename = href;
258 if (filename.compare(0, 13, "media_codecs_") != 0 ||
259 filename.compare(filename.size() - 4, 4, ".xml") != 0) {
260 ALOGE("invalid include file name: %s", href);
261 return -EINVAL;
262 }
263 filename.insert(0, mHrefBase);
264
265 parseXMLFile(filename.c_str());
266 return mParsingStatus;
267 }
268
startElementHandler(const char * name,const char ** attrs)269 void MediaCodecsXmlParser::startElementHandler(
270 const char *name, const char **attrs) {
271 if (mParsingStatus != OK) {
272 return;
273 }
274
275 bool inType = true;
276
277 if (strEq(name, "Include")) {
278 mParsingStatus = includeXMLFile(attrs);
279 if (mParsingStatus == OK) {
280 mSectionStack.push_back(mCurrentSection);
281 mCurrentSection = SECTION_INCLUDE;
282 }
283 return;
284 }
285
286 switch (mCurrentSection) {
287 case SECTION_TOPLEVEL:
288 {
289 if (strEq(name, "Decoders")) {
290 mCurrentSection = SECTION_DECODERS;
291 } else if (strEq(name, "Encoders")) {
292 mCurrentSection = SECTION_ENCODERS;
293 } else if (strEq(name, "Settings")) {
294 mCurrentSection = SECTION_SETTINGS;
295 }
296 break;
297 }
298
299 case SECTION_SETTINGS:
300 {
301 if (strEq(name, "Setting")) {
302 mParsingStatus = addSettingFromAttributes(attrs);
303 }
304 break;
305 }
306
307 case SECTION_DECODERS:
308 {
309 if (strEq(name, "MediaCodec")) {
310 mParsingStatus =
311 addMediaCodecFromAttributes(false /* encoder */, attrs);
312
313 mCurrentSection = SECTION_DECODER;
314 }
315 break;
316 }
317
318 case SECTION_ENCODERS:
319 {
320 if (strEq(name, "MediaCodec")) {
321 mParsingStatus =
322 addMediaCodecFromAttributes(true /* encoder */, attrs);
323
324 mCurrentSection = SECTION_ENCODER;
325 }
326 break;
327 }
328
329 case SECTION_DECODER:
330 case SECTION_ENCODER:
331 {
332 if (strEq(name, "Quirk")) {
333 mParsingStatus = addQuirk(attrs);
334 } else if (strEq(name, "Type")) {
335 mParsingStatus = addTypeFromAttributes(attrs,
336 (mCurrentSection == SECTION_ENCODER));
337 mCurrentSection =
338 (mCurrentSection == SECTION_DECODER ?
339 SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
340 }
341 }
342 inType = false;
343 // fall through
344
345 case SECTION_DECODER_TYPE:
346 case SECTION_ENCODER_TYPE:
347 {
348 // ignore limits and features specified outside of type
349 bool outside = !inType &&
350 mCurrentType == mCurrentCodec->second.typeMap.end();
351 if (outside &&
352 (strEq(name, "Limit") || strEq(name, "Feature"))) {
353 ALOGW("ignoring %s specified outside of a Type", name);
354 } else if (strEq(name, "Limit")) {
355 mParsingStatus = addLimit(attrs);
356 } else if (strEq(name, "Feature")) {
357 mParsingStatus = addFeature(attrs);
358 }
359 break;
360 }
361
362 default:
363 break;
364 }
365
366 }
367
endElementHandler(const char * name)368 void MediaCodecsXmlParser::endElementHandler(const char *name) {
369 if (mParsingStatus != OK) {
370 return;
371 }
372
373 switch (mCurrentSection) {
374 case SECTION_SETTINGS:
375 {
376 if (strEq(name, "Settings")) {
377 mCurrentSection = SECTION_TOPLEVEL;
378 }
379 break;
380 }
381
382 case SECTION_DECODERS:
383 {
384 if (strEq(name, "Decoders")) {
385 mCurrentSection = SECTION_TOPLEVEL;
386 }
387 break;
388 }
389
390 case SECTION_ENCODERS:
391 {
392 if (strEq(name, "Encoders")) {
393 mCurrentSection = SECTION_TOPLEVEL;
394 }
395 break;
396 }
397
398 case SECTION_DECODER_TYPE:
399 case SECTION_ENCODER_TYPE:
400 {
401 if (strEq(name, "Type")) {
402 mCurrentSection =
403 (mCurrentSection == SECTION_DECODER_TYPE ?
404 SECTION_DECODER : SECTION_ENCODER);
405
406 mCurrentType = mCurrentCodec->second.typeMap.end();
407 }
408 break;
409 }
410
411 case SECTION_DECODER:
412 {
413 if (strEq(name, "MediaCodec")) {
414 mCurrentSection = SECTION_DECODERS;
415 mCurrentName.clear();
416 }
417 break;
418 }
419
420 case SECTION_ENCODER:
421 {
422 if (strEq(name, "MediaCodec")) {
423 mCurrentSection = SECTION_ENCODERS;
424 mCurrentName.clear();
425 }
426 break;
427 }
428
429 case SECTION_INCLUDE:
430 {
431 if (strEq(name, "Include") && (mSectionStack.size() > 0)) {
432 mCurrentSection = mSectionStack.back();
433 mSectionStack.pop_back();
434 }
435 break;
436 }
437
438 default:
439 break;
440 }
441
442 }
443
addSettingFromAttributes(const char ** attrs)444 status_t MediaCodecsXmlParser::addSettingFromAttributes(const char **attrs) {
445 const char *name = nullptr;
446 const char *value = nullptr;
447 const char *update = nullptr;
448
449 size_t i = 0;
450 while (attrs[i] != nullptr) {
451 if (strEq(attrs[i], "name")) {
452 if (attrs[++i] == nullptr) {
453 ALOGE("addSettingFromAttributes: name is null");
454 return -EINVAL;
455 }
456 name = attrs[i];
457 } else if (strEq(attrs[i], "value")) {
458 if (attrs[++i] == nullptr) {
459 ALOGE("addSettingFromAttributes: value is null");
460 return -EINVAL;
461 }
462 value = attrs[i];
463 } else if (strEq(attrs[i], "update")) {
464 if (attrs[++i] == nullptr) {
465 ALOGE("addSettingFromAttributes: update is null");
466 return -EINVAL;
467 }
468 update = attrs[i];
469 } else {
470 ALOGE("addSettingFromAttributes: unrecognized attribute: %s", attrs[i]);
471 return -EINVAL;
472 }
473 ++i;
474 }
475
476 if (name == nullptr || value == nullptr) {
477 ALOGE("addSettingFromAttributes: name or value unspecified");
478 return -EINVAL;
479 }
480
481 // Boolean values are converted to "0" or "1".
482 if (strHasPrefix(name, "supports-")) {
483 value = parseBoolean(value) ? "1" : "0";
484 }
485
486 mUpdate = (update != nullptr) && parseBoolean(update);
487 auto attribute = mServiceAttributeMap.find(name);
488 if (attribute == mServiceAttributeMap.end()) { // New attribute name
489 if (mUpdate) {
490 ALOGE("addSettingFromAttributes: updating non-existing setting");
491 return -EINVAL;
492 }
493 mServiceAttributeMap.insert(Attribute(name, value));
494 } else { // Existing attribute name
495 if (!mUpdate) {
496 ALOGE("addSettingFromAttributes: adding existing setting");
497 }
498 attribute->second = value;
499 }
500
501 return OK;
502 }
503
addMediaCodecFromAttributes(bool encoder,const char ** attrs)504 status_t MediaCodecsXmlParser::addMediaCodecFromAttributes(
505 bool encoder, const char **attrs) {
506 const char *name = nullptr;
507 const char *type = nullptr;
508 const char *update = nullptr;
509
510 size_t i = 0;
511 while (attrs[i] != nullptr) {
512 if (strEq(attrs[i], "name")) {
513 if (attrs[++i] == nullptr) {
514 ALOGE("addMediaCodecFromAttributes: name is null");
515 return -EINVAL;
516 }
517 name = attrs[i];
518 } else if (strEq(attrs[i], "type")) {
519 if (attrs[++i] == nullptr) {
520 ALOGE("addMediaCodecFromAttributes: type is null");
521 return -EINVAL;
522 }
523 type = attrs[i];
524 } else if (strEq(attrs[i], "update")) {
525 if (attrs[++i] == nullptr) {
526 ALOGE("addMediaCodecFromAttributes: update is null");
527 return -EINVAL;
528 }
529 update = attrs[i];
530 } else {
531 ALOGE("addMediaCodecFromAttributes: unrecognized attribute: %s", attrs[i]);
532 return -EINVAL;
533 }
534 ++i;
535 }
536
537 if (name == nullptr) {
538 ALOGE("addMediaCodecFromAttributes: name not found");
539 return -EINVAL;
540 }
541
542 mUpdate = (update != nullptr) && parseBoolean(update);
543 mCurrentCodec = mCodecMap.find(name);
544 if (mCurrentCodec == mCodecMap.end()) { // New codec name
545 if (mUpdate) {
546 ALOGE("addMediaCodecFromAttributes: updating non-existing codec");
547 return -EINVAL;
548 }
549 // Create a new codec in mCodecMap
550 mCurrentCodec = mCodecMap.insert(
551 Codec(name, CodecProperties())).first;
552 if (type != nullptr) {
553 mCurrentType = mCurrentCodec->second.typeMap.insert(
554 Type(type, AttributeMap())).first;
555 } else {
556 mCurrentType = mCurrentCodec->second.typeMap.end();
557 }
558 mCurrentCodec->second.isEncoder = encoder;
559 mCurrentCodec->second.order = mCodecCounter++;
560 } else { // Existing codec name
561 if (!mUpdate) {
562 ALOGE("addMediaCodecFromAttributes: adding existing codec");
563 return -EINVAL;
564 }
565 if (type != nullptr) {
566 mCurrentType = mCurrentCodec->second.typeMap.find(type);
567 if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
568 ALOGE("addMediaCodecFromAttributes: updating non-existing type");
569 return -EINVAL;
570 }
571 } else {
572 // This should happen only when the codec has at most one type.
573 mCurrentType = mCurrentCodec->second.typeMap.begin();
574 }
575 }
576
577 return OK;
578 }
579
addQuirk(const char ** attrs)580 status_t MediaCodecsXmlParser::addQuirk(const char **attrs) {
581 const char *name = nullptr;
582
583 size_t i = 0;
584 while (attrs[i] != nullptr) {
585 if (strEq(attrs[i], "name")) {
586 if (attrs[++i] == nullptr) {
587 ALOGE("addQuirk: name is null");
588 return -EINVAL;
589 }
590 name = attrs[i];
591 } else {
592 ALOGE("addQuirk: unrecognized attribute: %s", attrs[i]);
593 return -EINVAL;
594 }
595 ++i;
596 }
597
598 if (name == nullptr) {
599 ALOGE("addQuirk: name not found");
600 return -EINVAL;
601 }
602
603 mCurrentCodec->second.quirkSet.emplace(name);
604 return OK;
605 }
606
addTypeFromAttributes(const char ** attrs,bool encoder)607 status_t MediaCodecsXmlParser::addTypeFromAttributes(const char **attrs, bool encoder) {
608 const char *name = nullptr;
609 const char *update = nullptr;
610
611 size_t i = 0;
612 while (attrs[i] != nullptr) {
613 if (strEq(attrs[i], "name")) {
614 if (attrs[++i] == nullptr) {
615 ALOGE("addTypeFromAttributes: name is null");
616 return -EINVAL;
617 }
618 name = attrs[i];
619 } else if (strEq(attrs[i], "update")) {
620 if (attrs[++i] == nullptr) {
621 ALOGE("addTypeFromAttributes: update is null");
622 return -EINVAL;
623 }
624 update = attrs[i];
625 } else {
626 ALOGE("addTypeFromAttributes: unrecognized attribute: %s", attrs[i]);
627 return -EINVAL;
628 }
629 ++i;
630 }
631
632 if (name == nullptr) {
633 return -EINVAL;
634 }
635
636 mCurrentCodec->second.isEncoder = encoder;
637 mCurrentType = mCurrentCodec->second.typeMap.find(name);
638 if (!mUpdate) {
639 if (mCurrentType != mCurrentCodec->second.typeMap.end()) {
640 ALOGE("addTypeFromAttributes: re-defining existing type without update");
641 return -EINVAL;
642 }
643 mCurrentType = mCurrentCodec->second.typeMap.insert(
644 Type(name, AttributeMap())).first;
645 } else if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
646 ALOGE("addTypeFromAttributes: updating non-existing type");
647 }
648 return OK;
649 }
650
addLimit(const char ** attrs)651 status_t MediaCodecsXmlParser::addLimit(const char **attrs) {
652 const char* a_name = nullptr;
653 const char* a_default = nullptr;
654 const char* a_in = nullptr;
655 const char* a_max = nullptr;
656 const char* a_min = nullptr;
657 const char* a_range = nullptr;
658 const char* a_ranges = nullptr;
659 const char* a_scale = nullptr;
660 const char* a_value = nullptr;
661
662 size_t i = 0;
663 while (attrs[i] != nullptr) {
664 if (strEq(attrs[i], "name")) {
665 if (attrs[++i] == nullptr) {
666 ALOGE("addLimit: name is null");
667 return -EINVAL;
668 }
669 a_name = attrs[i];
670 } else if (strEq(attrs[i], "default")) {
671 if (attrs[++i] == nullptr) {
672 ALOGE("addLimit: default is null");
673 return -EINVAL;
674 }
675 a_default = attrs[i];
676 } else if (strEq(attrs[i], "in")) {
677 if (attrs[++i] == nullptr) {
678 ALOGE("addLimit: in is null");
679 return -EINVAL;
680 }
681 a_in = attrs[i];
682 } else if (strEq(attrs[i], "max")) {
683 if (attrs[++i] == nullptr) {
684 ALOGE("addLimit: max is null");
685 return -EINVAL;
686 }
687 a_max = attrs[i];
688 } else if (strEq(attrs[i], "min")) {
689 if (attrs[++i] == nullptr) {
690 ALOGE("addLimit: min is null");
691 return -EINVAL;
692 }
693 a_min = attrs[i];
694 } else if (strEq(attrs[i], "range")) {
695 if (attrs[++i] == nullptr) {
696 ALOGE("addLimit: range is null");
697 return -EINVAL;
698 }
699 a_range = attrs[i];
700 } else if (strEq(attrs[i], "ranges")) {
701 if (attrs[++i] == nullptr) {
702 ALOGE("addLimit: ranges is null");
703 return -EINVAL;
704 }
705 a_ranges = attrs[i];
706 } else if (strEq(attrs[i], "scale")) {
707 if (attrs[++i] == nullptr) {
708 ALOGE("addLimit: scale is null");
709 return -EINVAL;
710 }
711 a_scale = attrs[i];
712 } else if (strEq(attrs[i], "value")) {
713 if (attrs[++i] == nullptr) {
714 ALOGE("addLimit: value is null");
715 return -EINVAL;
716 }
717 a_value = attrs[i];
718 } else {
719 ALOGE("addLimit: unrecognized limit: %s", attrs[i]);
720 return -EINVAL;
721 }
722 ++i;
723 }
724
725 if (a_name == nullptr) {
726 ALOGE("limit with no 'name' attribute");
727 return -EINVAL;
728 }
729
730 // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio,
731 // measured-frame-rate, measured-blocks-per-second: range
732 // quality: range + default + [scale]
733 // complexity: range + default
734 if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
735 ALOGW("ignoring null type");
736 return OK;
737 }
738
739 std::string range;
740 if (strEq(a_name, "aspect-ratio") ||
741 strEq(a_name, "bitrate") ||
742 strEq(a_name, "block-count") ||
743 strEq(a_name, "blocks-per-second") ||
744 strEq(a_name, "complexity") ||
745 strEq(a_name, "frame-rate") ||
746 strEq(a_name, "quality") ||
747 strEq(a_name, "size") ||
748 strEq(a_name, "measured-blocks-per-second") ||
749 strHasPrefix(a_name, "measured-frame-rate-")) {
750 // "range" is specified in exactly one of the following forms:
751 // 1) min-max
752 // 2) value-value
753 // 3) range
754 if (a_min != nullptr && a_max != nullptr) {
755 // min-max
756 if (a_range != nullptr || a_value != nullptr) {
757 return limitError(a_name, "has 'min' and 'max' as well as 'range' or "
758 "'value' attributes");
759 }
760 range = a_min;
761 range += '-';
762 range += a_max;
763 } else if (a_min != nullptr || a_max != nullptr) {
764 return limitError(a_name, "has only 'min' or 'max' attribute");
765 } else if (a_value != nullptr) {
766 // value-value
767 if (a_range != nullptr) {
768 return limitError(a_name, "has both 'range' and 'value' attributes");
769 }
770 range = a_value;
771 range += '-';
772 range += a_value;
773 } else if (a_range == nullptr) {
774 return limitError(a_name, "with no 'range', 'value' or 'min'/'max' attributes");
775 } else {
776 // range
777 range = a_range;
778 }
779
780 // "aspect-ratio" requires some special treatment.
781 if (strEq(a_name, "aspect-ratio")) {
782 // "aspect-ratio" must have "in".
783 if (a_in == nullptr) {
784 return limitFoundMissingAttr(a_name, "in", false);
785 }
786 // "in" must be either "pixels" or "blocks".
787 if (!strEq(a_in, "pixels") && !strEq(a_in, "blocks")) {
788 return limitInvalidAttr(a_name, "in", a_in);
789 }
790 // name will be "pixel-aspect-ratio-range" or
791 // "block-aspect-ratio-range".
792 mCurrentType->second[
793 std::string(a_in).substr(0, strlen(a_in) - 1) +
794 "-aspect-ratio-range"] = range;
795 } else {
796 // For everything else (apart from "aspect-ratio"), simply append
797 // "-range" to the name for the range-type property.
798 mCurrentType->second[std::string(a_name) + "-range"] = range;
799
800 // Only "quality" may have "scale".
801 if (!strEq(a_name, "quality") && a_scale != nullptr) {
802 return limitFoundMissingAttr(a_name, "scale");
803 } else if (strEq(a_name, "quality")) {
804 // The default value of "quality-scale" is "linear".
805 mCurrentType->second["quality-scale"] = a_scale == nullptr ?
806 "linear" : a_scale;
807 }
808
809 // "quality" and "complexity" must have "default".
810 // Other limits must not have "default".
811 if (strEq(a_name, "quality") || strEq(a_name, "complexity")) {
812 if (a_default == nullptr) {
813 return limitFoundMissingAttr(a_name, "default", false);
814 }
815 // name will be "quality-default" or "complexity-default".
816 mCurrentType->second[std::string(a_name) + "-default"] = a_default;
817 } else if (a_default != nullptr) {
818 return limitFoundMissingAttr(a_name, "default", true);
819 }
820 }
821 } else {
822 if (a_default != nullptr) {
823 return limitFoundMissingAttr(a_name, "default");
824 }
825 if (a_in != nullptr) {
826 return limitFoundMissingAttr(a_name, "in");
827 }
828 if (a_scale != nullptr) {
829 return limitFoundMissingAttr(a_name, "scale");
830 }
831 if (a_range != nullptr) {
832 return limitFoundMissingAttr(a_name, "range");
833 }
834 if (a_min != nullptr) {
835 return limitFoundMissingAttr(a_name, "min");
836 }
837
838 if (a_max != nullptr) {
839 // "max" must exist if and only if name is "channel-count" or
840 // "concurrent-instances".
841 // "min" is not ncessary.
842 if (strEq(a_name, "channel-count") ||
843 strEq(a_name, "concurrent-instances")) {
844 mCurrentType->second[std::string("max-") + a_name] = a_max;
845 } else {
846 return limitFoundMissingAttr(a_name, "max", false);
847 }
848 } else if (strEq(a_name, "channel-count") ||
849 strEq(a_name, "concurrent-instances")) {
850 return limitFoundMissingAttr(a_name, "max");
851 }
852
853 if (a_ranges != nullptr) {
854 // "ranges" must exist if and only if name is "sample-rate".
855 if (strEq(a_name, "sample-rate")) {
856 mCurrentType->second["sample-rate-ranges"] = a_ranges;
857 } else {
858 return limitFoundMissingAttr(a_name, "ranges", false);
859 }
860 } else if (strEq(a_name, "sample-rate")) {
861 return limitFoundMissingAttr(a_name, "ranges");
862 }
863
864 if (a_value != nullptr) {
865 // "value" must exist if and only if name is "alignment" or
866 // "block-size".
867 if (strEq(a_name, "alignment") || strEq(a_name, "block-size")) {
868 mCurrentType->second[a_name] = a_value;
869 } else {
870 return limitFoundMissingAttr(a_name, "value", false);
871 }
872 } else if (strEq(a_name, "alignment") || strEq(a_name, "block-size")) {
873 return limitFoundMissingAttr(a_name, "value", false);
874 }
875
876 }
877
878 return OK;
879 }
880
addFeature(const char ** attrs)881 status_t MediaCodecsXmlParser::addFeature(const char **attrs) {
882 size_t i = 0;
883 const char *name = nullptr;
884 int32_t optional = -1;
885 int32_t required = -1;
886 const char *value = nullptr;
887
888 while (attrs[i] != nullptr) {
889 if (strEq(attrs[i], "name")) {
890 if (attrs[++i] == nullptr) {
891 ALOGE("addFeature: name is null");
892 return -EINVAL;
893 }
894 name = attrs[i];
895 } else if (strEq(attrs[i], "optional")) {
896 if (attrs[++i] == nullptr) {
897 ALOGE("addFeature: optional is null");
898 return -EINVAL;
899 }
900 optional = parseBoolean(attrs[i]) ? 1 : 0;
901 } else if (strEq(attrs[i], "required")) {
902 if (attrs[++i] == nullptr) {
903 ALOGE("addFeature: required is null");
904 return -EINVAL;
905 }
906 required = parseBoolean(attrs[i]) ? 1 : 0;
907 } else if (strEq(attrs[i], "value")) {
908 if (attrs[++i] == nullptr) {
909 ALOGE("addFeature: value is null");
910 return -EINVAL;
911 }
912 value = attrs[i];
913 } else {
914 ALOGE("addFeature: unrecognized attribute: %s", attrs[i]);
915 return -EINVAL;
916 }
917 ++i;
918 }
919
920 // Every feature must have a name.
921 if (name == nullptr) {
922 ALOGE("feature with no 'name' attribute");
923 return -EINVAL;
924 }
925
926 if (mCurrentType == mCurrentCodec->second.typeMap.end()) {
927 ALOGW("ignoring null type");
928 return OK;
929 }
930
931 if ((optional != -1) || (required != -1)) {
932 if (optional == required) {
933 ALOGE("feature '%s' is both/neither optional and required", name);
934 return -EINVAL;
935 }
936 if ((optional == 1) || (required == 1)) {
937 if (value != nullptr) {
938 ALOGE("feature '%s' cannot have extra 'value'", name);
939 return -EINVAL;
940 }
941 mCurrentType->second[std::string("feature-") + name] =
942 optional == 1 ? "0" : "1";
943 return OK;
944 }
945 }
946 mCurrentType->second[std::string("feature-") + name] = value == nullptr ?
947 "0" : value;
948 return OK;
949 }
950
951 const MediaCodecsXmlParser::AttributeMap&
getServiceAttributeMap() const952 MediaCodecsXmlParser::getServiceAttributeMap() const {
953 return mServiceAttributeMap;
954 }
955
956 const MediaCodecsXmlParser::CodecMap&
getCodecMap() const957 MediaCodecsXmlParser::getCodecMap() const {
958 return mCodecMap;
959 }
960
961 const MediaCodecsXmlParser::RoleMap&
getRoleMap() const962 MediaCodecsXmlParser::getRoleMap() const {
963 if (mRoleMap.empty()) {
964 generateRoleMap();
965 }
966 return mRoleMap;
967 }
968
getCommonPrefix() const969 const char* MediaCodecsXmlParser::getCommonPrefix() const {
970 if (mCommonPrefix.empty()) {
971 generateCommonPrefix();
972 }
973 return mCommonPrefix.data();
974 }
975
getParsingStatus() const976 status_t MediaCodecsXmlParser::getParsingStatus() const {
977 return mParsingStatus;
978 }
979
generateRoleMap() const980 void MediaCodecsXmlParser::generateRoleMap() const {
981 for (const auto& codec : mCodecMap) {
982 const auto& codecName = codec.first;
983 bool isEncoder = codec.second.isEncoder;
984 size_t order = codec.second.order;
985 const auto& typeMap = codec.second.typeMap;
986 for (const auto& type : typeMap) {
987 const auto& typeName = type.first;
988 const char* roleName = GetComponentRole(isEncoder, typeName.data());
989 if (roleName == nullptr) {
990 ALOGE("Cannot find the role for %s of type %s",
991 isEncoder ? "an encoder" : "a decoder",
992 typeName.data());
993 continue;
994 }
995 const auto& typeAttributeMap = type.second;
996
997 auto roleIterator = mRoleMap.find(roleName);
998 std::multimap<size_t, NodeInfo>* nodeList;
999 if (roleIterator == mRoleMap.end()) {
1000 RoleProperties roleProperties;
1001 roleProperties.type = typeName;
1002 roleProperties.isEncoder = isEncoder;
1003 auto insertResult = mRoleMap.insert(
1004 std::make_pair(roleName, roleProperties));
1005 if (!insertResult.second) {
1006 ALOGE("Cannot add role %s", roleName);
1007 continue;
1008 }
1009 nodeList = &insertResult.first->second.nodeList;
1010 } else {
1011 if (roleIterator->second.type != typeName) {
1012 ALOGE("Role %s has mismatching types: %s and %s",
1013 roleName,
1014 roleIterator->second.type.data(),
1015 typeName.data());
1016 continue;
1017 }
1018 if (roleIterator->second.isEncoder != isEncoder) {
1019 ALOGE("Role %s cannot be both an encoder and a decoder",
1020 roleName);
1021 continue;
1022 }
1023 nodeList = &roleIterator->second.nodeList;
1024 }
1025
1026 NodeInfo nodeInfo;
1027 nodeInfo.name = codecName;
1028 nodeInfo.attributeList.reserve(typeAttributeMap.size());
1029 for (const auto& attribute : typeAttributeMap) {
1030 nodeInfo.attributeList.push_back(
1031 Attribute{attribute.first, attribute.second});
1032 }
1033 nodeList->insert(std::make_pair(
1034 std::move(order), std::move(nodeInfo)));
1035 }
1036 }
1037 }
1038
generateCommonPrefix() const1039 void MediaCodecsXmlParser::generateCommonPrefix() const {
1040 if (mCodecMap.empty()) {
1041 return;
1042 }
1043 auto i = mCodecMap.cbegin();
1044 auto first = i->first.cbegin();
1045 auto last = i->first.cend();
1046 for (++i; i != mCodecMap.cend(); ++i) {
1047 last = std::mismatch(
1048 first, last, i->first.cbegin(), i->first.cend()).first;
1049 }
1050 mCommonPrefix.insert(mCommonPrefix.begin(), first, last);
1051 }
1052
1053 } // namespace android
1054
1055