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 #include <utils/Log.h>
20
21 #include <media/vndk/xmlparser/1.0/MediaCodecsXmlParser.h>
22
23 #include <media/MediaCodecInfo.h>
24
25 #include <media/stagefright/foundation/ADebug.h>
26 #include <media/stagefright/foundation/AMessage.h>
27 #include <media/stagefright/foundation/AUtils.h>
28 #include <media/stagefright/MediaErrors.h>
29
30 #include <sys/stat.h>
31
32 #include <expat.h>
33 #include <string>
34
35 #define MEDIA_CODECS_CONFIG_FILE_PATH_MAX_LENGTH 256
36
37 namespace android {
38
39 namespace { // Local variables and functions
40
41 const char *kProfilingResults =
42 "/data/misc/media/media_codecs_profiling_results.xml";
43
44 // Treblized media codec list will be located in /odm/etc or /vendor/etc.
45 const char *kConfigLocationList[] =
46 {"/odm/etc", "/vendor/etc", "/etc"};
47 constexpr int kConfigLocationListSize =
48 (sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0]));
49
findMediaCodecListFileFullPath(const char * file_name,std::string * out_path)50 bool findMediaCodecListFileFullPath(
51 const char *file_name, std::string *out_path) {
52 for (int i = 0; i < kConfigLocationListSize; i++) {
53 *out_path = std::string(kConfigLocationList[i]) + "/" + file_name;
54 struct stat file_stat;
55 if (stat(out_path->c_str(), &file_stat) == 0 &&
56 S_ISREG(file_stat.st_mode)) {
57 return true;
58 }
59 }
60 return false;
61 }
62
63 // Find TypeInfo by name.
findTypeInfo(CodecInfo & codecInfo,const AString & typeName)64 std::vector<TypeInfo>::iterator findTypeInfo(
65 CodecInfo &codecInfo, const AString &typeName) {
66 return std::find_if(
67 codecInfo.mTypes.begin(), codecInfo.mTypes.end(),
68 [typeName](const auto &typeInfo) {
69 return typeInfo.mName == typeName;
70 });
71 }
72
73 // Convert a string into a boolean value.
ParseBoolean(const char * s)74 bool ParseBoolean(const char *s) {
75 if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) {
76 return true;
77 }
78 char *end;
79 unsigned long res = strtoul(s, &end, 10);
80 return *s != '\0' && *end == '\0' && res > 0;
81 }
82
83 } // unnamed namespace
84
MediaCodecsXmlParser()85 MediaCodecsXmlParser::MediaCodecsXmlParser() :
86 mInitCheck(NO_INIT),
87 mUpdate(false) {
88 std::string config_file_path;
89 if (findMediaCodecListFileFullPath(
90 "media_codecs.xml", &config_file_path)) {
91 parseTopLevelXMLFile(config_file_path.c_str(), false);
92 } else {
93 mInitCheck = NAME_NOT_FOUND;
94 }
95 if (findMediaCodecListFileFullPath(
96 "media_codecs_performance.xml", &config_file_path)) {
97 parseTopLevelXMLFile(config_file_path.c_str(), true);
98 }
99 parseTopLevelXMLFile(kProfilingResults, true);
100 }
101
parseTopLevelXMLFile(const char * codecs_xml,bool ignore_errors)102 void MediaCodecsXmlParser::parseTopLevelXMLFile(
103 const char *codecs_xml, bool ignore_errors) {
104 // get href_base
105 const char *href_base_end = strrchr(codecs_xml, '/');
106 if (href_base_end != NULL) {
107 mHrefBase = AString(codecs_xml, href_base_end - codecs_xml + 1);
108 }
109
110 mInitCheck = OK; // keeping this here for safety
111 mCurrentSection = SECTION_TOPLEVEL;
112 mDepth = 0;
113
114 parseXMLFile(codecs_xml);
115
116 if (mInitCheck != OK) {
117 if (ignore_errors) {
118 mInitCheck = OK;
119 return;
120 }
121 mCodecInfos.clear();
122 return;
123 }
124 }
125
~MediaCodecsXmlParser()126 MediaCodecsXmlParser::~MediaCodecsXmlParser() {
127 }
128
initCheck() const129 status_t MediaCodecsXmlParser::initCheck() const {
130 return mInitCheck;
131 }
132
parseXMLFile(const char * path)133 void MediaCodecsXmlParser::parseXMLFile(const char *path) {
134 FILE *file = fopen(path, "r");
135
136 if (file == NULL) {
137 ALOGW("unable to open media codecs configuration xml file: %s", path);
138 mInitCheck = NAME_NOT_FOUND;
139 return;
140 }
141
142 ALOGV("Start parsing %s", path);
143 XML_Parser parser = ::XML_ParserCreate(NULL);
144 CHECK(parser != NULL);
145
146 ::XML_SetUserData(parser, this);
147 ::XML_SetElementHandler(
148 parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
149
150 const int BUFF_SIZE = 512;
151 while (mInitCheck == OK) {
152 void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
153 if (buff == NULL) {
154 ALOGE("failed in call to XML_GetBuffer()");
155 mInitCheck = UNKNOWN_ERROR;
156 break;
157 }
158
159 int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
160 if (bytes_read < 0) {
161 ALOGE("failed in call to read");
162 mInitCheck = ERROR_IO;
163 break;
164 }
165
166 XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0);
167 if (status != XML_STATUS_OK) {
168 ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser)));
169 mInitCheck = ERROR_MALFORMED;
170 break;
171 }
172
173 if (bytes_read == 0) {
174 break;
175 }
176 }
177
178 ::XML_ParserFree(parser);
179
180 fclose(file);
181 file = NULL;
182 }
183
184 // static
StartElementHandlerWrapper(void * me,const char * name,const char ** attrs)185 void MediaCodecsXmlParser::StartElementHandlerWrapper(
186 void *me, const char *name, const char **attrs) {
187 static_cast<MediaCodecsXmlParser *>(me)->startElementHandler(name, attrs);
188 }
189
190 // static
EndElementHandlerWrapper(void * me,const char * name)191 void MediaCodecsXmlParser::EndElementHandlerWrapper(void *me, const char *name) {
192 static_cast<MediaCodecsXmlParser *>(me)->endElementHandler(name);
193 }
194
includeXMLFile(const char ** attrs)195 status_t MediaCodecsXmlParser::includeXMLFile(const char **attrs) {
196 const char *href = NULL;
197 size_t i = 0;
198 while (attrs[i] != NULL) {
199 if (!strcmp(attrs[i], "href")) {
200 if (attrs[i + 1] == NULL) {
201 return -EINVAL;
202 }
203 href = attrs[i + 1];
204 ++i;
205 } else {
206 ALOGE("includeXMLFile: unrecognized attribute: %s", attrs[i]);
207 return -EINVAL;
208 }
209 ++i;
210 }
211
212 // For security reasons and for simplicity, file names can only contain
213 // [a-zA-Z0-9_.] and must start with media_codecs_ and end with .xml
214 for (i = 0; href[i] != '\0'; i++) {
215 if (href[i] == '.' || href[i] == '_' ||
216 (href[i] >= '0' && href[i] <= '9') ||
217 (href[i] >= 'A' && href[i] <= 'Z') ||
218 (href[i] >= 'a' && href[i] <= 'z')) {
219 continue;
220 }
221 ALOGE("invalid include file name: %s", href);
222 return -EINVAL;
223 }
224
225 AString filename = href;
226 if (!filename.startsWith("media_codecs_") ||
227 !filename.endsWith(".xml")) {
228 ALOGE("invalid include file name: %s", href);
229 return -EINVAL;
230 }
231 filename.insert(mHrefBase, 0);
232
233 parseXMLFile(filename.c_str());
234 return mInitCheck;
235 }
236
startElementHandler(const char * name,const char ** attrs)237 void MediaCodecsXmlParser::startElementHandler(
238 const char *name, const char **attrs) {
239 if (mInitCheck != OK) {
240 return;
241 }
242
243 bool inType = true;
244
245 if (!strcmp(name, "Include")) {
246 mInitCheck = includeXMLFile(attrs);
247 if (mInitCheck == OK) {
248 mPastSections.push(mCurrentSection);
249 mCurrentSection = SECTION_INCLUDE;
250 }
251 ++mDepth;
252 return;
253 }
254
255 switch (mCurrentSection) {
256 case SECTION_TOPLEVEL:
257 {
258 if (!strcmp(name, "Decoders")) {
259 mCurrentSection = SECTION_DECODERS;
260 } else if (!strcmp(name, "Encoders")) {
261 mCurrentSection = SECTION_ENCODERS;
262 } else if (!strcmp(name, "Settings")) {
263 mCurrentSection = SECTION_SETTINGS;
264 }
265 break;
266 }
267
268 case SECTION_SETTINGS:
269 {
270 if (!strcmp(name, "Setting")) {
271 mInitCheck = addSettingFromAttributes(attrs);
272 }
273 break;
274 }
275
276 case SECTION_DECODERS:
277 {
278 if (!strcmp(name, "MediaCodec")) {
279 mInitCheck =
280 addMediaCodecFromAttributes(false /* encoder */, attrs);
281
282 mCurrentSection = SECTION_DECODER;
283 }
284 break;
285 }
286
287 case SECTION_ENCODERS:
288 {
289 if (!strcmp(name, "MediaCodec")) {
290 mInitCheck =
291 addMediaCodecFromAttributes(true /* encoder */, attrs);
292
293 mCurrentSection = SECTION_ENCODER;
294 }
295 break;
296 }
297
298 case SECTION_DECODER:
299 case SECTION_ENCODER:
300 {
301 if (!strcmp(name, "Quirk")) {
302 mInitCheck = addQuirk(attrs);
303 } else if (!strcmp(name, "Type")) {
304 mInitCheck = addTypeFromAttributes(attrs, (mCurrentSection == SECTION_ENCODER));
305 mCurrentSection =
306 (mCurrentSection == SECTION_DECODER
307 ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
308 }
309 }
310 inType = false;
311 // fall through
312
313 case SECTION_DECODER_TYPE:
314 case SECTION_ENCODER_TYPE:
315 {
316 // ignore limits and features specified outside of type
317 bool outside = !inType && mCurrentType == mCodecInfos[mCurrentName].mTypes.end();
318 if (outside && (!strcmp(name, "Limit") || !strcmp(name, "Feature"))) {
319 ALOGW("ignoring %s specified outside of a Type", name);
320 } else if (!strcmp(name, "Limit")) {
321 mInitCheck = addLimit(attrs);
322 } else if (!strcmp(name, "Feature")) {
323 mInitCheck = addFeature(attrs);
324 }
325 break;
326 }
327
328 default:
329 break;
330 }
331
332 ++mDepth;
333 }
334
endElementHandler(const char * name)335 void MediaCodecsXmlParser::endElementHandler(const char *name) {
336 if (mInitCheck != OK) {
337 return;
338 }
339
340 switch (mCurrentSection) {
341 case SECTION_SETTINGS:
342 {
343 if (!strcmp(name, "Settings")) {
344 mCurrentSection = SECTION_TOPLEVEL;
345 }
346 break;
347 }
348
349 case SECTION_DECODERS:
350 {
351 if (!strcmp(name, "Decoders")) {
352 mCurrentSection = SECTION_TOPLEVEL;
353 }
354 break;
355 }
356
357 case SECTION_ENCODERS:
358 {
359 if (!strcmp(name, "Encoders")) {
360 mCurrentSection = SECTION_TOPLEVEL;
361 }
362 break;
363 }
364
365 case SECTION_DECODER_TYPE:
366 case SECTION_ENCODER_TYPE:
367 {
368 if (!strcmp(name, "Type")) {
369 mCurrentSection =
370 (mCurrentSection == SECTION_DECODER_TYPE
371 ? SECTION_DECODER : SECTION_ENCODER);
372
373 mCurrentType = mCodecInfos[mCurrentName].mTypes.end();
374 }
375 break;
376 }
377
378 case SECTION_DECODER:
379 {
380 if (!strcmp(name, "MediaCodec")) {
381 mCurrentSection = SECTION_DECODERS;
382 mCurrentName.clear();
383 }
384 break;
385 }
386
387 case SECTION_ENCODER:
388 {
389 if (!strcmp(name, "MediaCodec")) {
390 mCurrentSection = SECTION_ENCODERS;
391 mCurrentName.clear();
392 }
393 break;
394 }
395
396 case SECTION_INCLUDE:
397 {
398 if (!strcmp(name, "Include") && mPastSections.size() > 0) {
399 mCurrentSection = mPastSections.top();
400 mPastSections.pop();
401 }
402 break;
403 }
404
405 default:
406 break;
407 }
408
409 --mDepth;
410 }
411
addSettingFromAttributes(const char ** attrs)412 status_t MediaCodecsXmlParser::addSettingFromAttributes(const char **attrs) {
413 const char *name = NULL;
414 const char *value = NULL;
415 const char *update = NULL;
416
417 size_t i = 0;
418 while (attrs[i] != NULL) {
419 if (!strcmp(attrs[i], "name")) {
420 if (attrs[i + 1] == NULL) {
421 ALOGE("addSettingFromAttributes: name is null");
422 return -EINVAL;
423 }
424 name = attrs[i + 1];
425 ++i;
426 } else if (!strcmp(attrs[i], "value")) {
427 if (attrs[i + 1] == NULL) {
428 ALOGE("addSettingFromAttributes: value is null");
429 return -EINVAL;
430 }
431 value = attrs[i + 1];
432 ++i;
433 } else if (!strcmp(attrs[i], "update")) {
434 if (attrs[i + 1] == NULL) {
435 ALOGE("addSettingFromAttributes: update is null");
436 return -EINVAL;
437 }
438 update = attrs[i + 1];
439 ++i;
440 } else {
441 ALOGE("addSettingFromAttributes: unrecognized attribute: %s", attrs[i]);
442 return -EINVAL;
443 }
444
445 ++i;
446 }
447
448 if (name == NULL || value == NULL) {
449 ALOGE("addSettingFromAttributes: name or value unspecified");
450 return -EINVAL;
451 }
452
453 mUpdate = (update != NULL) && ParseBoolean(update);
454 if (mUpdate != (mGlobalSettings.count(name) > 0)) {
455 ALOGE("addSettingFromAttributes: updating non-existing setting");
456 return -EINVAL;
457 }
458 mGlobalSettings[name] = value;
459
460 return OK;
461 }
462
addMediaCodecFromAttributes(bool encoder,const char ** attrs)463 status_t MediaCodecsXmlParser::addMediaCodecFromAttributes(
464 bool encoder, const char **attrs) {
465 const char *name = NULL;
466 const char *type = NULL;
467 const char *update = NULL;
468
469 size_t i = 0;
470 while (attrs[i] != NULL) {
471 if (!strcmp(attrs[i], "name")) {
472 if (attrs[i + 1] == NULL) {
473 ALOGE("addMediaCodecFromAttributes: name is null");
474 return -EINVAL;
475 }
476 name = attrs[i + 1];
477 ++i;
478 } else if (!strcmp(attrs[i], "type")) {
479 if (attrs[i + 1] == NULL) {
480 ALOGE("addMediaCodecFromAttributes: type is null");
481 return -EINVAL;
482 }
483 type = attrs[i + 1];
484 ++i;
485 } else if (!strcmp(attrs[i], "update")) {
486 if (attrs[i + 1] == NULL) {
487 ALOGE("addMediaCodecFromAttributes: update is null");
488 return -EINVAL;
489 }
490 update = attrs[i + 1];
491 ++i;
492 } else {
493 ALOGE("addMediaCodecFromAttributes: unrecognized attribute: %s", attrs[i]);
494 return -EINVAL;
495 }
496
497 ++i;
498 }
499
500 if (name == NULL) {
501 ALOGE("addMediaCodecFromAttributes: name not found");
502 return -EINVAL;
503 }
504
505 mUpdate = (update != NULL) && ParseBoolean(update);
506 if (mUpdate != (mCodecInfos.count(name) > 0)) {
507 ALOGE("addMediaCodecFromAttributes: updating non-existing codec or vice versa");
508 return -EINVAL;
509 }
510
511 CodecInfo *info = &mCodecInfos[name];
512 if (mUpdate) {
513 // existing codec
514 mCurrentName = name;
515 mCurrentType = info->mTypes.begin();
516 if (type != NULL) {
517 // existing type
518 mCurrentType = findTypeInfo(*info, type);
519 if (mCurrentType == info->mTypes.end()) {
520 ALOGE("addMediaCodecFromAttributes: updating non-existing type");
521 return -EINVAL;
522 }
523 }
524 } else {
525 // new codec
526 mCurrentName = name;
527 mQuirks[name].clear();
528 info->mTypes.clear();
529 info->mTypes.emplace_back();
530 mCurrentType = --info->mTypes.end();
531 mCurrentType->mName = type;
532 info->mIsEncoder = encoder;
533 }
534
535 return OK;
536 }
537
addQuirk(const char ** attrs)538 status_t MediaCodecsXmlParser::addQuirk(const char **attrs) {
539 const char *name = NULL;
540
541 size_t i = 0;
542 while (attrs[i] != NULL) {
543 if (!strcmp(attrs[i], "name")) {
544 if (attrs[i + 1] == NULL) {
545 ALOGE("addQuirk: name is null");
546 return -EINVAL;
547 }
548 name = attrs[i + 1];
549 ++i;
550 } else {
551 ALOGE("addQuirk: unrecognized attribute: %s", attrs[i]);
552 return -EINVAL;
553 }
554
555 ++i;
556 }
557
558 if (name == NULL) {
559 ALOGE("addQuirk: name not found");
560 return -EINVAL;
561 }
562
563 mQuirks[mCurrentName].emplace_back(name);
564 return OK;
565 }
566
addTypeFromAttributes(const char ** attrs,bool encoder)567 status_t MediaCodecsXmlParser::addTypeFromAttributes(const char **attrs, bool encoder) {
568 const char *name = NULL;
569 const char *update = NULL;
570
571 size_t i = 0;
572 while (attrs[i] != NULL) {
573 if (!strcmp(attrs[i], "name")) {
574 if (attrs[i + 1] == NULL) {
575 ALOGE("addTypeFromAttributes: name is null");
576 return -EINVAL;
577 }
578 name = attrs[i + 1];
579 ++i;
580 } else if (!strcmp(attrs[i], "update")) {
581 if (attrs[i + 1] == NULL) {
582 ALOGE("addTypeFromAttributes: update is null");
583 return -EINVAL;
584 }
585 update = attrs[i + 1];
586 ++i;
587 } else {
588 ALOGE("addTypeFromAttributes: unrecognized attribute: %s", attrs[i]);
589 return -EINVAL;
590 }
591
592 ++i;
593 }
594
595 if (name == NULL) {
596 return -EINVAL;
597 }
598
599 CodecInfo *info = &mCodecInfos[mCurrentName];
600 info->mIsEncoder = encoder;
601 mCurrentType = findTypeInfo(*info, name);
602 if (!mUpdate) {
603 if (mCurrentType != info->mTypes.end()) {
604 ALOGE("addTypeFromAttributes: re-defining existing type without update");
605 return -EINVAL;
606 }
607 info->mTypes.emplace_back();
608 mCurrentType = --info->mTypes.end();
609 } else if (mCurrentType == info->mTypes.end()) {
610 ALOGE("addTypeFromAttributes: updating non-existing type");
611 return -EINVAL;
612 }
613
614 return OK;
615 }
616
limitFoundMissingAttr(const AString & name,const char * attr,bool found=true)617 static status_t limitFoundMissingAttr(const AString &name, const char *attr, bool found = true) {
618 ALOGE("limit '%s' with %s'%s' attribute", name.c_str(),
619 (found ? "" : "no "), attr);
620 return -EINVAL;
621 }
622
limitError(const AString & name,const char * msg)623 static status_t limitError(const AString &name, const char *msg) {
624 ALOGE("limit '%s' %s", name.c_str(), msg);
625 return -EINVAL;
626 }
627
limitInvalidAttr(const AString & name,const char * attr,const AString & value)628 static status_t limitInvalidAttr(const AString &name, const char *attr, const AString &value) {
629 ALOGE("limit '%s' with invalid '%s' attribute (%s)", name.c_str(),
630 attr, value.c_str());
631 return -EINVAL;
632 }
633
addLimit(const char ** attrs)634 status_t MediaCodecsXmlParser::addLimit(const char **attrs) {
635 sp<AMessage> msg = new AMessage();
636
637 size_t i = 0;
638 while (attrs[i] != NULL) {
639 if (attrs[i + 1] == NULL) {
640 ALOGE("addLimit: limit is not given");
641 return -EINVAL;
642 }
643
644 // attributes with values
645 if (!strcmp(attrs[i], "name")
646 || !strcmp(attrs[i], "default")
647 || !strcmp(attrs[i], "in")
648 || !strcmp(attrs[i], "max")
649 || !strcmp(attrs[i], "min")
650 || !strcmp(attrs[i], "range")
651 || !strcmp(attrs[i], "ranges")
652 || !strcmp(attrs[i], "scale")
653 || !strcmp(attrs[i], "value")) {
654 msg->setString(attrs[i], attrs[i + 1]);
655 ++i;
656 } else {
657 ALOGE("addLimit: unrecognized limit: %s", attrs[i]);
658 return -EINVAL;
659 }
660 ++i;
661 }
662
663 AString name;
664 if (!msg->findString("name", &name)) {
665 ALOGE("limit with no 'name' attribute");
666 return -EINVAL;
667 }
668
669 // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio,
670 // measured-frame-rate, measured-blocks-per-second: range
671 // quality: range + default + [scale]
672 // complexity: range + default
673 bool found;
674 if (mCurrentType == mCodecInfos[mCurrentName].mTypes.end()) {
675 ALOGW("ignoring null type");
676 return OK;
677 }
678
679 if (name == "aspect-ratio" || name == "bitrate" || name == "block-count"
680 || name == "blocks-per-second" || name == "complexity"
681 || name == "frame-rate" || name == "quality" || name == "size"
682 || name == "measured-blocks-per-second" || name.startsWith("measured-frame-rate-")) {
683 AString min, max;
684 if (msg->findString("min", &min) && msg->findString("max", &max)) {
685 min.append("-");
686 min.append(max);
687 if (msg->contains("range") || msg->contains("value")) {
688 return limitError(name, "has 'min' and 'max' as well as 'range' or "
689 "'value' attributes");
690 }
691 msg->setString("range", min);
692 } else if (msg->contains("min") || msg->contains("max")) {
693 return limitError(name, "has only 'min' or 'max' attribute");
694 } else if (msg->findString("value", &max)) {
695 min = max;
696 min.append("-");
697 min.append(max);
698 if (msg->contains("range")) {
699 return limitError(name, "has both 'range' and 'value' attributes");
700 }
701 msg->setString("range", min);
702 }
703
704 AString range, scale = "linear", def, in_;
705 if (!msg->findString("range", &range)) {
706 return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes");
707 }
708
709 if ((name == "quality" || name == "complexity") ^
710 (found = msg->findString("default", &def))) {
711 return limitFoundMissingAttr(name, "default", found);
712 }
713 if (name != "quality" && msg->findString("scale", &scale)) {
714 return limitFoundMissingAttr(name, "scale");
715 }
716 if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) {
717 return limitFoundMissingAttr(name, "in", found);
718 }
719
720 if (name == "aspect-ratio") {
721 if (!(in_ == "pixels") && !(in_ == "blocks")) {
722 return limitInvalidAttr(name, "in", in_);
723 }
724 in_.erase(5, 1); // (pixel|block)-aspect-ratio
725 in_.append("-");
726 in_.append(name);
727 name = in_;
728 }
729 if (name == "quality") {
730 mCurrentType->mDetails["quality-scale"] = scale;
731 }
732 if (name == "quality" || name == "complexity") {
733 AString tag = name;
734 tag.append("-default");
735 mCurrentType->mDetails[tag] = def;
736 }
737 AString tag = name;
738 tag.append("-range");
739 mCurrentType->mDetails[tag] = range;
740 } else {
741 AString max, value, ranges;
742 if (msg->contains("default")) {
743 return limitFoundMissingAttr(name, "default");
744 } else if (msg->contains("in")) {
745 return limitFoundMissingAttr(name, "in");
746 } else if ((name == "channel-count" || name == "concurrent-instances") ^
747 (found = msg->findString("max", &max))) {
748 return limitFoundMissingAttr(name, "max", found);
749 } else if (msg->contains("min")) {
750 return limitFoundMissingAttr(name, "min");
751 } else if (msg->contains("range")) {
752 return limitFoundMissingAttr(name, "range");
753 } else if ((name == "sample-rate") ^
754 (found = msg->findString("ranges", &ranges))) {
755 return limitFoundMissingAttr(name, "ranges", found);
756 } else if (msg->contains("scale")) {
757 return limitFoundMissingAttr(name, "scale");
758 } else if ((name == "alignment" || name == "block-size") ^
759 (found = msg->findString("value", &value))) {
760 return limitFoundMissingAttr(name, "value", found);
761 }
762
763 if (max.size()) {
764 AString tag = "max-";
765 tag.append(name);
766 mCurrentType->mDetails[tag] = max;
767 } else if (value.size()) {
768 mCurrentType->mDetails[name] = value;
769 } else if (ranges.size()) {
770 AString tag = name;
771 tag.append("-ranges");
772 mCurrentType->mDetails[tag] = ranges;
773 } else {
774 ALOGW("Ignoring unrecognized limit '%s'", name.c_str());
775 }
776 }
777
778 return OK;
779 }
780
addFeature(const char ** attrs)781 status_t MediaCodecsXmlParser::addFeature(const char **attrs) {
782 size_t i = 0;
783 const char *name = NULL;
784 int32_t optional = -1;
785 int32_t required = -1;
786 const char *value = NULL;
787
788 while (attrs[i] != NULL) {
789 if (attrs[i + 1] == NULL) {
790 ALOGE("addFeature: feature is not given");
791 return -EINVAL;
792 }
793
794 // attributes with values
795 if (!strcmp(attrs[i], "name")) {
796 name = attrs[i + 1];
797 ++i;
798 } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) {
799 int value = (int)ParseBoolean(attrs[i + 1]);
800 if (!strcmp(attrs[i], "optional")) {
801 optional = value;
802 } else {
803 required = value;
804 }
805 ++i;
806 } else if (!strcmp(attrs[i], "value")) {
807 value = attrs[i + 1];
808 ++i;
809 } else {
810 ALOGE("addFeature: unrecognized attribute: %s", attrs[i]);
811 return -EINVAL;
812 }
813 ++i;
814 }
815 if (name == NULL) {
816 ALOGE("feature with no 'name' attribute");
817 return -EINVAL;
818 }
819
820 if (optional == required && optional != -1) {
821 ALOGE("feature '%s' is both/neither optional and required", name);
822 return -EINVAL;
823 }
824
825 if (mCurrentType == mCodecInfos[mCurrentName].mTypes.end()) {
826 ALOGW("ignoring null type");
827 return OK;
828 }
829 if (value != NULL) {
830 mCurrentType->mStringFeatures[name] = value;
831 } else {
832 mCurrentType->mBoolFeatures[name] = (required == 1) || (optional == 0);
833 }
834 return OK;
835 }
836
getGlobalSettings(std::map<AString,AString> * settings) const837 void MediaCodecsXmlParser::getGlobalSettings(
838 std::map<AString, AString> *settings) const {
839 settings->clear();
840 settings->insert(mGlobalSettings.begin(), mGlobalSettings.end());
841 }
842
getCodecInfo(const char * name,CodecInfo * info) const843 status_t MediaCodecsXmlParser::getCodecInfo(const char *name, CodecInfo *info) const {
844 if (mCodecInfos.count(name) == 0) {
845 ALOGE("Codec not found with name '%s'", name);
846 return NAME_NOT_FOUND;
847 }
848 *info = mCodecInfos.at(name);
849 return OK;
850 }
851
getQuirks(const char * name,std::vector<AString> * quirks) const852 status_t MediaCodecsXmlParser::getQuirks(const char *name, std::vector<AString> *quirks) const {
853 if (mQuirks.count(name) == 0) {
854 ALOGE("Codec not found with name '%s'", name);
855 return NAME_NOT_FOUND;
856 }
857 quirks->clear();
858 quirks->insert(quirks->end(), mQuirks.at(name).begin(), mQuirks.at(name).end());
859 return OK;
860 }
861
862 } // namespace android
863