1 /*
2 * Copyright 2012, 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 "MediaCodecList"
19 #include <utils/Log.h>
20
21 #include "MediaCodecListOverrides.h"
22
23 #include <binder/IServiceManager.h>
24
25 #include <media/IMediaCodecList.h>
26 #include <media/IMediaPlayerService.h>
27 #include <media/IResourceManagerService.h>
28 #include <media/MediaCodecInfo.h>
29 #include <media/MediaResourcePolicy.h>
30
31 #include <media/stagefright/foundation/ADebug.h>
32 #include <media/stagefright/foundation/AMessage.h>
33 #include <media/stagefright/MediaCodecList.h>
34 #include <media/stagefright/MediaErrors.h>
35 #include <media/stagefright/OMXClient.h>
36 #include <media/stagefright/OMXCodec.h>
37
38 #include <sys/stat.h>
39 #include <utils/threads.h>
40
41 #include <cutils/properties.h>
42 #include <libexpat/expat.h>
43
44 namespace android {
45
46 const char *kMaxEncoderInputBuffers = "max-video-encoder-input-buffers";
47
48 static Mutex sInitMutex;
49
parseBoolean(const char * s)50 static bool parseBoolean(const char *s) {
51 if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) {
52 return true;
53 }
54 char *end;
55 unsigned long res = strtoul(s, &end, 10);
56 return *s != '\0' && *end == '\0' && res > 0;
57 }
58
isProfilingNeeded()59 static bool isProfilingNeeded() {
60 int8_t value = property_get_bool("debug.stagefright.profilecodec", 0);
61 if (value == 0) {
62 return false;
63 }
64
65 bool profilingNeeded = true;
66 FILE *resultsFile = fopen(kProfilingResults, "r");
67 if (resultsFile) {
68 AString currentVersion = getProfilingVersionString();
69 size_t currentVersionSize = currentVersion.size();
70 char *versionString = new char[currentVersionSize + 1];
71 fgets(versionString, currentVersionSize + 1, resultsFile);
72 if (strcmp(versionString, currentVersion.c_str()) == 0) {
73 // profiling result up to date
74 profilingNeeded = false;
75 }
76 fclose(resultsFile);
77 delete[] versionString;
78 }
79 return profilingNeeded;
80 }
81
82 // static
83 sp<IMediaCodecList> MediaCodecList::sCodecList;
84
85 // static
profilerThreadWrapper(void *)86 void *MediaCodecList::profilerThreadWrapper(void * /*arg*/) {
87 ALOGV("Enter profilerThreadWrapper.");
88 remove(kProfilingResults); // remove previous result so that it won't be loaded to
89 // the new MediaCodecList
90 MediaCodecList *codecList = new MediaCodecList();
91 if (codecList->initCheck() != OK) {
92 ALOGW("Failed to create a new MediaCodecList, skipping codec profiling.");
93 delete codecList;
94 return NULL;
95 }
96
97 Vector<sp<MediaCodecInfo>> infos;
98 for (size_t i = 0; i < codecList->countCodecs(); ++i) {
99 infos.push_back(codecList->getCodecInfo(i));
100 }
101 ALOGV("Codec profiling started.");
102 profileCodecs(infos);
103 ALOGV("Codec profiling completed.");
104 codecList->parseTopLevelXMLFile(kProfilingResults, true /* ignore_errors */);
105
106 {
107 Mutex::Autolock autoLock(sInitMutex);
108 sCodecList = codecList;
109 }
110 return NULL;
111 }
112
113 // static
getLocalInstance()114 sp<IMediaCodecList> MediaCodecList::getLocalInstance() {
115 Mutex::Autolock autoLock(sInitMutex);
116
117 if (sCodecList == NULL) {
118 MediaCodecList *codecList = new MediaCodecList;
119 if (codecList->initCheck() == OK) {
120 sCodecList = codecList;
121
122 if (isProfilingNeeded()) {
123 ALOGV("Codec profiling needed, will be run in separated thread.");
124 pthread_t profiler;
125 if (pthread_create(&profiler, NULL, profilerThreadWrapper, NULL) != 0) {
126 ALOGW("Failed to create thread for codec profiling.");
127 }
128 }
129 } else {
130 // failure to initialize may be temporary. retry on next call.
131 delete codecList;
132 }
133 }
134
135 return sCodecList;
136 }
137
138 static Mutex sRemoteInitMutex;
139
140 sp<IMediaCodecList> MediaCodecList::sRemoteList;
141
142 sp<MediaCodecList::BinderDeathObserver> MediaCodecList::sBinderDeathObserver;
143
binderDied(const wp<IBinder> & who __unused)144 void MediaCodecList::BinderDeathObserver::binderDied(const wp<IBinder> &who __unused) {
145 Mutex::Autolock _l(sRemoteInitMutex);
146 sRemoteList.clear();
147 sBinderDeathObserver.clear();
148 }
149
150 // static
getInstance()151 sp<IMediaCodecList> MediaCodecList::getInstance() {
152 Mutex::Autolock _l(sRemoteInitMutex);
153 if (sRemoteList == NULL) {
154 sp<IBinder> binder =
155 defaultServiceManager()->getService(String16("media.player"));
156 sp<IMediaPlayerService> service =
157 interface_cast<IMediaPlayerService>(binder);
158 if (service.get() != NULL) {
159 sRemoteList = service->getCodecList();
160 if (sRemoteList != NULL) {
161 sBinderDeathObserver = new BinderDeathObserver();
162 binder->linkToDeath(sBinderDeathObserver.get());
163 }
164 }
165 if (sRemoteList == NULL) {
166 // if failed to get remote list, create local list
167 sRemoteList = getLocalInstance();
168 }
169 }
170 return sRemoteList;
171 }
172
MediaCodecList()173 MediaCodecList::MediaCodecList()
174 : mInitCheck(NO_INIT),
175 mUpdate(false),
176 mGlobalSettings(new AMessage()) {
177 parseTopLevelXMLFile("/etc/media_codecs.xml");
178 parseTopLevelXMLFile("/etc/media_codecs_performance.xml", true/* ignore_errors */);
179 parseTopLevelXMLFile(kProfilingResults, true/* ignore_errors */);
180 }
181
parseTopLevelXMLFile(const char * codecs_xml,bool ignore_errors)182 void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml, bool ignore_errors) {
183 // get href_base
184 char *href_base_end = strrchr(codecs_xml, '/');
185 if (href_base_end != NULL) {
186 mHrefBase = AString(codecs_xml, href_base_end - codecs_xml + 1);
187 }
188
189 mInitCheck = OK; // keeping this here for safety
190 mCurrentSection = SECTION_TOPLEVEL;
191 mDepth = 0;
192
193 OMXClient client;
194 mInitCheck = client.connect();
195 if (mInitCheck != OK) {
196 return; // this may fail if IMediaPlayerService is not available.
197 }
198 mOMX = client.interface();
199 parseXMLFile(codecs_xml);
200 mOMX.clear();
201
202 if (mInitCheck != OK) {
203 if (ignore_errors) {
204 mInitCheck = OK;
205 return;
206 }
207 mCodecInfos.clear();
208 return;
209 }
210
211 Vector<MediaResourcePolicy> policies;
212 AString value;
213 if (mGlobalSettings->findString(kPolicySupportsMultipleSecureCodecs, &value)) {
214 policies.push_back(
215 MediaResourcePolicy(
216 String8(kPolicySupportsMultipleSecureCodecs),
217 String8(value.c_str())));
218 }
219 if (mGlobalSettings->findString(kPolicySupportsSecureWithNonSecureCodec, &value)) {
220 policies.push_back(
221 MediaResourcePolicy(
222 String8(kPolicySupportsSecureWithNonSecureCodec),
223 String8(value.c_str())));
224 }
225 if (policies.size() > 0) {
226 sp<IServiceManager> sm = defaultServiceManager();
227 sp<IBinder> binder = sm->getService(String16("media.resource_manager"));
228 sp<IResourceManagerService> service = interface_cast<IResourceManagerService>(binder);
229 if (service == NULL) {
230 ALOGE("MediaCodecList: failed to get ResourceManagerService");
231 } else {
232 service->config(policies);
233 }
234 }
235
236 for (size_t i = mCodecInfos.size(); i > 0;) {
237 i--;
238 const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get();
239 if (info.mCaps.size() == 0) {
240 // No types supported by this component???
241 ALOGW("Component %s does not support any type of media?",
242 info.mName.c_str());
243
244 mCodecInfos.removeAt(i);
245 #if LOG_NDEBUG == 0
246 } else {
247 for (size_t type_ix = 0; type_ix < info.mCaps.size(); ++type_ix) {
248 AString mime = info.mCaps.keyAt(type_ix);
249 const sp<MediaCodecInfo::Capabilities> &caps = info.mCaps.valueAt(type_ix);
250
251 ALOGV("%s codec info for %s: %s", info.mName.c_str(), mime.c_str(),
252 caps->getDetails()->debugString().c_str());
253 ALOGV(" flags=%d", caps->getFlags());
254 {
255 Vector<uint32_t> colorFormats;
256 caps->getSupportedColorFormats(&colorFormats);
257 AString nice;
258 for (size_t ix = 0; ix < colorFormats.size(); ix++) {
259 if (ix > 0) {
260 nice.append(", ");
261 }
262 nice.append(colorFormats.itemAt(ix));
263 }
264 ALOGV(" colors=[%s]", nice.c_str());
265 }
266 {
267 Vector<MediaCodecInfo::ProfileLevel> profileLevels;
268 caps->getSupportedProfileLevels(&profileLevels);
269 AString nice;
270 for (size_t ix = 0; ix < profileLevels.size(); ix++) {
271 if (ix > 0) {
272 nice.append(", ");
273 }
274 const MediaCodecInfo::ProfileLevel &pl =
275 profileLevels.itemAt(ix);
276 nice.append(pl.mProfile);
277 nice.append("/");
278 nice.append(pl.mLevel);
279 }
280 ALOGV(" levels=[%s]", nice.c_str());
281 }
282 {
283 AString quirks;
284 for (size_t ix = 0; ix < info.mQuirks.size(); ix++) {
285 if (ix > 0) {
286 quirks.append(", ");
287 }
288 quirks.append(info.mQuirks[ix]);
289 }
290 ALOGV(" quirks=[%s]", quirks.c_str());
291 }
292 }
293 #endif
294 }
295 }
296
297 #if 0
298 for (size_t i = 0; i < mCodecInfos.size(); ++i) {
299 const CodecInfo &info = mCodecInfos.itemAt(i);
300
301 AString line = info.mName;
302 line.append(" supports ");
303 for (size_t j = 0; j < mTypes.size(); ++j) {
304 uint32_t value = mTypes.valueAt(j);
305
306 if (info.mTypes & (1ul << value)) {
307 line.append(mTypes.keyAt(j));
308 line.append(" ");
309 }
310 }
311
312 ALOGI("%s", line.c_str());
313 }
314 #endif
315 }
316
~MediaCodecList()317 MediaCodecList::~MediaCodecList() {
318 }
319
initCheck() const320 status_t MediaCodecList::initCheck() const {
321 return mInitCheck;
322 }
323
parseXMLFile(const char * path)324 void MediaCodecList::parseXMLFile(const char *path) {
325 FILE *file = fopen(path, "r");
326
327 if (file == NULL) {
328 ALOGW("unable to open media codecs configuration xml file: %s", path);
329 mInitCheck = NAME_NOT_FOUND;
330 return;
331 }
332
333 XML_Parser parser = ::XML_ParserCreate(NULL);
334 CHECK(parser != NULL);
335
336 ::XML_SetUserData(parser, this);
337 ::XML_SetElementHandler(
338 parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
339
340 const int BUFF_SIZE = 512;
341 while (mInitCheck == OK) {
342 void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
343 if (buff == NULL) {
344 ALOGE("failed in call to XML_GetBuffer()");
345 mInitCheck = UNKNOWN_ERROR;
346 break;
347 }
348
349 int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
350 if (bytes_read < 0) {
351 ALOGE("failed in call to read");
352 mInitCheck = ERROR_IO;
353 break;
354 }
355
356 XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0);
357 if (status != XML_STATUS_OK) {
358 ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser)));
359 mInitCheck = ERROR_MALFORMED;
360 break;
361 }
362
363 if (bytes_read == 0) {
364 break;
365 }
366 }
367
368 ::XML_ParserFree(parser);
369
370 fclose(file);
371 file = NULL;
372 }
373
374 // static
StartElementHandlerWrapper(void * me,const char * name,const char ** attrs)375 void MediaCodecList::StartElementHandlerWrapper(
376 void *me, const char *name, const char **attrs) {
377 static_cast<MediaCodecList *>(me)->startElementHandler(name, attrs);
378 }
379
380 // static
EndElementHandlerWrapper(void * me,const char * name)381 void MediaCodecList::EndElementHandlerWrapper(void *me, const char *name) {
382 static_cast<MediaCodecList *>(me)->endElementHandler(name);
383 }
384
includeXMLFile(const char ** attrs)385 status_t MediaCodecList::includeXMLFile(const char **attrs) {
386 const char *href = NULL;
387 size_t i = 0;
388 while (attrs[i] != NULL) {
389 if (!strcmp(attrs[i], "href")) {
390 if (attrs[i + 1] == NULL) {
391 return -EINVAL;
392 }
393 href = attrs[i + 1];
394 ++i;
395 } else {
396 return -EINVAL;
397 }
398 ++i;
399 }
400
401 // For security reasons and for simplicity, file names can only contain
402 // [a-zA-Z0-9_.] and must start with media_codecs_ and end with .xml
403 for (i = 0; href[i] != '\0'; i++) {
404 if (href[i] == '.' || href[i] == '_' ||
405 (href[i] >= '0' && href[i] <= '9') ||
406 (href[i] >= 'A' && href[i] <= 'Z') ||
407 (href[i] >= 'a' && href[i] <= 'z')) {
408 continue;
409 }
410 ALOGE("invalid include file name: %s", href);
411 return -EINVAL;
412 }
413
414 AString filename = href;
415 if (!filename.startsWith("media_codecs_") ||
416 !filename.endsWith(".xml")) {
417 ALOGE("invalid include file name: %s", href);
418 return -EINVAL;
419 }
420 filename.insert(mHrefBase, 0);
421
422 parseXMLFile(filename.c_str());
423 return mInitCheck;
424 }
425
startElementHandler(const char * name,const char ** attrs)426 void MediaCodecList::startElementHandler(
427 const char *name, const char **attrs) {
428 if (mInitCheck != OK) {
429 return;
430 }
431
432 bool inType = true;
433
434 if (!strcmp(name, "Include")) {
435 mInitCheck = includeXMLFile(attrs);
436 if (mInitCheck == OK) {
437 mPastSections.push(mCurrentSection);
438 mCurrentSection = SECTION_INCLUDE;
439 }
440 ++mDepth;
441 return;
442 }
443
444 switch (mCurrentSection) {
445 case SECTION_TOPLEVEL:
446 {
447 if (!strcmp(name, "Decoders")) {
448 mCurrentSection = SECTION_DECODERS;
449 } else if (!strcmp(name, "Encoders")) {
450 mCurrentSection = SECTION_ENCODERS;
451 } else if (!strcmp(name, "Settings")) {
452 mCurrentSection = SECTION_SETTINGS;
453 }
454 break;
455 }
456
457 case SECTION_SETTINGS:
458 {
459 if (!strcmp(name, "Setting")) {
460 mInitCheck = addSettingFromAttributes(attrs);
461 }
462 break;
463 }
464
465 case SECTION_DECODERS:
466 {
467 if (!strcmp(name, "MediaCodec")) {
468 mInitCheck =
469 addMediaCodecFromAttributes(false /* encoder */, attrs);
470
471 mCurrentSection = SECTION_DECODER;
472 }
473 break;
474 }
475
476 case SECTION_ENCODERS:
477 {
478 if (!strcmp(name, "MediaCodec")) {
479 mInitCheck =
480 addMediaCodecFromAttributes(true /* encoder */, attrs);
481
482 mCurrentSection = SECTION_ENCODER;
483 }
484 break;
485 }
486
487 case SECTION_DECODER:
488 case SECTION_ENCODER:
489 {
490 if (!strcmp(name, "Quirk")) {
491 mInitCheck = addQuirk(attrs);
492 } else if (!strcmp(name, "Type")) {
493 mInitCheck = addTypeFromAttributes(attrs);
494 mCurrentSection =
495 (mCurrentSection == SECTION_DECODER
496 ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
497 }
498 }
499 inType = false;
500 // fall through
501
502 case SECTION_DECODER_TYPE:
503 case SECTION_ENCODER_TYPE:
504 {
505 // ignore limits and features specified outside of type
506 bool outside = !inType && !mCurrentInfo->mHasSoleMime;
507 if (outside && (!strcmp(name, "Limit") || !strcmp(name, "Feature"))) {
508 ALOGW("ignoring %s specified outside of a Type", name);
509 } else if (!strcmp(name, "Limit")) {
510 mInitCheck = addLimit(attrs);
511 } else if (!strcmp(name, "Feature")) {
512 mInitCheck = addFeature(attrs);
513 }
514 break;
515 }
516
517 default:
518 break;
519 }
520
521 ++mDepth;
522 }
523
endElementHandler(const char * name)524 void MediaCodecList::endElementHandler(const char *name) {
525 if (mInitCheck != OK) {
526 return;
527 }
528
529 switch (mCurrentSection) {
530 case SECTION_SETTINGS:
531 {
532 if (!strcmp(name, "Settings")) {
533 mCurrentSection = SECTION_TOPLEVEL;
534 }
535 break;
536 }
537
538 case SECTION_DECODERS:
539 {
540 if (!strcmp(name, "Decoders")) {
541 mCurrentSection = SECTION_TOPLEVEL;
542 }
543 break;
544 }
545
546 case SECTION_ENCODERS:
547 {
548 if (!strcmp(name, "Encoders")) {
549 mCurrentSection = SECTION_TOPLEVEL;
550 }
551 break;
552 }
553
554 case SECTION_DECODER_TYPE:
555 case SECTION_ENCODER_TYPE:
556 {
557 if (!strcmp(name, "Type")) {
558 mCurrentSection =
559 (mCurrentSection == SECTION_DECODER_TYPE
560 ? SECTION_DECODER : SECTION_ENCODER);
561
562 mCurrentInfo->complete();
563 }
564 break;
565 }
566
567 case SECTION_DECODER:
568 {
569 if (!strcmp(name, "MediaCodec")) {
570 mCurrentSection = SECTION_DECODERS;
571 mCurrentInfo->complete();
572 mCurrentInfo = NULL;
573 }
574 break;
575 }
576
577 case SECTION_ENCODER:
578 {
579 if (!strcmp(name, "MediaCodec")) {
580 mCurrentSection = SECTION_ENCODERS;
581 mCurrentInfo->complete();;
582 mCurrentInfo = NULL;
583 }
584 break;
585 }
586
587 case SECTION_INCLUDE:
588 {
589 if (!strcmp(name, "Include") && mPastSections.size() > 0) {
590 mCurrentSection = mPastSections.top();
591 mPastSections.pop();
592 }
593 break;
594 }
595
596 default:
597 break;
598 }
599
600 --mDepth;
601 }
602
addSettingFromAttributes(const char ** attrs)603 status_t MediaCodecList::addSettingFromAttributes(const char **attrs) {
604 const char *name = NULL;
605 const char *value = NULL;
606 const char *update = NULL;
607
608 size_t i = 0;
609 while (attrs[i] != NULL) {
610 if (!strcmp(attrs[i], "name")) {
611 if (attrs[i + 1] == NULL) {
612 return -EINVAL;
613 }
614 name = attrs[i + 1];
615 ++i;
616 } else if (!strcmp(attrs[i], "value")) {
617 if (attrs[i + 1] == NULL) {
618 return -EINVAL;
619 }
620 value = attrs[i + 1];
621 ++i;
622 } else if (!strcmp(attrs[i], "update")) {
623 if (attrs[i + 1] == NULL) {
624 return -EINVAL;
625 }
626 update = attrs[i + 1];
627 ++i;
628 } else {
629 return -EINVAL;
630 }
631
632 ++i;
633 }
634
635 if (name == NULL || value == NULL) {
636 return -EINVAL;
637 }
638
639 mUpdate = (update != NULL) && parseBoolean(update);
640 if (mUpdate != mGlobalSettings->contains(name)) {
641 return -EINVAL;
642 }
643
644 mGlobalSettings->setString(name, value);
645 return OK;
646 }
647
setCurrentCodecInfo(bool encoder,const char * name,const char * type)648 void MediaCodecList::setCurrentCodecInfo(bool encoder, const char *name, const char *type) {
649 for (size_t i = 0; i < mCodecInfos.size(); ++i) {
650 if (AString(name) == mCodecInfos[i]->getCodecName()) {
651 if (mCodecInfos[i]->getCapabilitiesFor(type) == NULL) {
652 ALOGW("Overrides with an unexpected mime %s", type);
653 // Create a new MediaCodecInfo (but don't add it to mCodecInfos) to hold the
654 // overrides we don't want.
655 mCurrentInfo = new MediaCodecInfo(name, encoder, type);
656 } else {
657 mCurrentInfo = mCodecInfos.editItemAt(i);
658 mCurrentInfo->updateMime(type); // to set the current cap
659 }
660 return;
661 }
662 }
663 mCurrentInfo = new MediaCodecInfo(name, encoder, type);
664 // The next step involves trying to load the codec, which may
665 // fail. Only list the codec if this succeeds.
666 // However, keep mCurrentInfo object around until parsing
667 // of full codec info is completed.
668 if (initializeCapabilities(type) == OK) {
669 mCodecInfos.push_back(mCurrentInfo);
670 }
671 }
672
addMediaCodecFromAttributes(bool encoder,const char ** attrs)673 status_t MediaCodecList::addMediaCodecFromAttributes(
674 bool encoder, const char **attrs) {
675 const char *name = NULL;
676 const char *type = NULL;
677 const char *update = NULL;
678
679 size_t i = 0;
680 while (attrs[i] != NULL) {
681 if (!strcmp(attrs[i], "name")) {
682 if (attrs[i + 1] == NULL) {
683 return -EINVAL;
684 }
685 name = attrs[i + 1];
686 ++i;
687 } else if (!strcmp(attrs[i], "type")) {
688 if (attrs[i + 1] == NULL) {
689 return -EINVAL;
690 }
691 type = attrs[i + 1];
692 ++i;
693 } else if (!strcmp(attrs[i], "update")) {
694 if (attrs[i + 1] == NULL) {
695 return -EINVAL;
696 }
697 update = attrs[i + 1];
698 ++i;
699 } else {
700 return -EINVAL;
701 }
702
703 ++i;
704 }
705
706 if (name == NULL) {
707 return -EINVAL;
708 }
709
710 mUpdate = (update != NULL) && parseBoolean(update);
711 ssize_t index = -1;
712 for (size_t i = 0; i < mCodecInfos.size(); ++i) {
713 if (AString(name) == mCodecInfos[i]->getCodecName()) {
714 index = i;
715 }
716 }
717 if (mUpdate != (index >= 0)) {
718 return -EINVAL;
719 }
720
721 if (index >= 0) {
722 // existing codec
723 mCurrentInfo = mCodecInfos.editItemAt(index);
724 if (type != NULL) {
725 // existing type
726 if (mCodecInfos[index]->getCapabilitiesFor(type) == NULL) {
727 return -EINVAL;
728 }
729 mCurrentInfo->updateMime(type);
730 }
731 } else {
732 // new codec
733 mCurrentInfo = new MediaCodecInfo(name, encoder, type);
734 // The next step involves trying to load the codec, which may
735 // fail. Only list the codec if this succeeds.
736 // However, keep mCurrentInfo object around until parsing
737 // of full codec info is completed.
738 if (initializeCapabilities(type) == OK) {
739 mCodecInfos.push_back(mCurrentInfo);
740 }
741 }
742
743 return OK;
744 }
745
initializeCapabilities(const char * type)746 status_t MediaCodecList::initializeCapabilities(const char *type) {
747 if (type == NULL) {
748 return OK;
749 }
750
751 ALOGV("initializeCapabilities %s:%s",
752 mCurrentInfo->mName.c_str(), type);
753
754 CodecCapabilities caps;
755 status_t err = QueryCodec(
756 mOMX,
757 mCurrentInfo->mName.c_str(),
758 type,
759 mCurrentInfo->mIsEncoder,
760 &caps);
761 if (err != OK) {
762 return err;
763 }
764
765 return mCurrentInfo->initializeCapabilities(caps);
766 }
767
addQuirk(const char ** attrs)768 status_t MediaCodecList::addQuirk(const char **attrs) {
769 const char *name = NULL;
770
771 size_t i = 0;
772 while (attrs[i] != NULL) {
773 if (!strcmp(attrs[i], "name")) {
774 if (attrs[i + 1] == NULL) {
775 return -EINVAL;
776 }
777 name = attrs[i + 1];
778 ++i;
779 } else {
780 return -EINVAL;
781 }
782
783 ++i;
784 }
785
786 if (name == NULL) {
787 return -EINVAL;
788 }
789
790 mCurrentInfo->addQuirk(name);
791 return OK;
792 }
793
addTypeFromAttributes(const char ** attrs)794 status_t MediaCodecList::addTypeFromAttributes(const char **attrs) {
795 const char *name = NULL;
796 const char *update = NULL;
797
798 size_t i = 0;
799 while (attrs[i] != NULL) {
800 if (!strcmp(attrs[i], "name")) {
801 if (attrs[i + 1] == NULL) {
802 return -EINVAL;
803 }
804 name = attrs[i + 1];
805 ++i;
806 } else if (!strcmp(attrs[i], "update")) {
807 if (attrs[i + 1] == NULL) {
808 return -EINVAL;
809 }
810 update = attrs[i + 1];
811 ++i;
812 } else {
813 return -EINVAL;
814 }
815
816 ++i;
817 }
818
819 if (name == NULL) {
820 return -EINVAL;
821 }
822
823 bool isExistingType = (mCurrentInfo->getCapabilitiesFor(name) != NULL);
824 if (mUpdate != isExistingType) {
825 return -EINVAL;
826 }
827
828 status_t ret;
829 if (mUpdate) {
830 ret = mCurrentInfo->updateMime(name);
831 } else {
832 ret = mCurrentInfo->addMime(name);
833 }
834
835 if (ret != OK) {
836 return ret;
837 }
838
839 // The next step involves trying to load the codec, which may
840 // fail. Handle this gracefully (by not reporting such mime).
841 if (!mUpdate && initializeCapabilities(name) != OK) {
842 mCurrentInfo->removeMime(name);
843 }
844 return OK;
845 }
846
847 // legacy method for non-advanced codecs
findCodecByType(const char * type,bool encoder,size_t startIndex) const848 ssize_t MediaCodecList::findCodecByType(
849 const char *type, bool encoder, size_t startIndex) const {
850 static const char *advancedFeatures[] = {
851 "feature-secure-playback",
852 "feature-tunneled-playback",
853 };
854
855 size_t numCodecs = mCodecInfos.size();
856 for (; startIndex < numCodecs; ++startIndex) {
857 const MediaCodecInfo &info = *mCodecInfos.itemAt(startIndex).get();
858
859 if (info.isEncoder() != encoder) {
860 continue;
861 }
862 sp<MediaCodecInfo::Capabilities> capabilities = info.getCapabilitiesFor(type);
863 if (capabilities == NULL) {
864 continue;
865 }
866 const sp<AMessage> &details = capabilities->getDetails();
867
868 int32_t required;
869 bool isAdvanced = false;
870 for (size_t ix = 0; ix < ARRAY_SIZE(advancedFeatures); ix++) {
871 if (details->findInt32(advancedFeatures[ix], &required) &&
872 required != 0) {
873 isAdvanced = true;
874 break;
875 }
876 }
877
878 if (!isAdvanced) {
879 return startIndex;
880 }
881 }
882
883 return -ENOENT;
884 }
885
limitFoundMissingAttr(AString name,const char * attr,bool found=true)886 static status_t limitFoundMissingAttr(AString name, const char *attr, bool found = true) {
887 ALOGE("limit '%s' with %s'%s' attribute", name.c_str(),
888 (found ? "" : "no "), attr);
889 return -EINVAL;
890 }
891
limitError(AString name,const char * msg)892 static status_t limitError(AString name, const char *msg) {
893 ALOGE("limit '%s' %s", name.c_str(), msg);
894 return -EINVAL;
895 }
896
limitInvalidAttr(AString name,const char * attr,AString value)897 static status_t limitInvalidAttr(AString name, const char *attr, AString value) {
898 ALOGE("limit '%s' with invalid '%s' attribute (%s)", name.c_str(),
899 attr, value.c_str());
900 return -EINVAL;
901 }
902
addLimit(const char ** attrs)903 status_t MediaCodecList::addLimit(const char **attrs) {
904 sp<AMessage> msg = new AMessage();
905
906 size_t i = 0;
907 while (attrs[i] != NULL) {
908 if (attrs[i + 1] == NULL) {
909 return -EINVAL;
910 }
911
912 // attributes with values
913 if (!strcmp(attrs[i], "name")
914 || !strcmp(attrs[i], "default")
915 || !strcmp(attrs[i], "in")
916 || !strcmp(attrs[i], "max")
917 || !strcmp(attrs[i], "min")
918 || !strcmp(attrs[i], "range")
919 || !strcmp(attrs[i], "ranges")
920 || !strcmp(attrs[i], "scale")
921 || !strcmp(attrs[i], "value")) {
922 msg->setString(attrs[i], attrs[i + 1]);
923 ++i;
924 } else {
925 return -EINVAL;
926 }
927 ++i;
928 }
929
930 AString name;
931 if (!msg->findString("name", &name)) {
932 ALOGE("limit with no 'name' attribute");
933 return -EINVAL;
934 }
935
936 // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio,
937 // measured-frame-rate, measured-blocks-per-second: range
938 // quality: range + default + [scale]
939 // complexity: range + default
940 bool found;
941
942 if (name == "aspect-ratio" || name == "bitrate" || name == "block-count"
943 || name == "blocks-per-second" || name == "complexity"
944 || name == "frame-rate" || name == "quality" || name == "size"
945 || name == "measured-blocks-per-second" || name.startsWith("measured-frame-rate-")) {
946 AString min, max;
947 if (msg->findString("min", &min) && msg->findString("max", &max)) {
948 min.append("-");
949 min.append(max);
950 if (msg->contains("range") || msg->contains("value")) {
951 return limitError(name, "has 'min' and 'max' as well as 'range' or "
952 "'value' attributes");
953 }
954 msg->setString("range", min);
955 } else if (msg->contains("min") || msg->contains("max")) {
956 return limitError(name, "has only 'min' or 'max' attribute");
957 } else if (msg->findString("value", &max)) {
958 min = max;
959 min.append("-");
960 min.append(max);
961 if (msg->contains("range")) {
962 return limitError(name, "has both 'range' and 'value' attributes");
963 }
964 msg->setString("range", min);
965 }
966
967 AString range, scale = "linear", def, in_;
968 if (!msg->findString("range", &range)) {
969 return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes");
970 }
971
972 if ((name == "quality" || name == "complexity") ^
973 (found = msg->findString("default", &def))) {
974 return limitFoundMissingAttr(name, "default", found);
975 }
976 if (name != "quality" && msg->findString("scale", &scale)) {
977 return limitFoundMissingAttr(name, "scale");
978 }
979 if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) {
980 return limitFoundMissingAttr(name, "in", found);
981 }
982
983 if (name == "aspect-ratio") {
984 if (!(in_ == "pixels") && !(in_ == "blocks")) {
985 return limitInvalidAttr(name, "in", in_);
986 }
987 in_.erase(5, 1); // (pixel|block)-aspect-ratio
988 in_.append("-");
989 in_.append(name);
990 name = in_;
991 }
992 if (name == "quality") {
993 mCurrentInfo->addDetail("quality-scale", scale);
994 }
995 if (name == "quality" || name == "complexity") {
996 AString tag = name;
997 tag.append("-default");
998 mCurrentInfo->addDetail(tag, def);
999 }
1000 AString tag = name;
1001 tag.append("-range");
1002 mCurrentInfo->addDetail(tag, range);
1003 } else {
1004 AString max, value, ranges;
1005 if (msg->contains("default")) {
1006 return limitFoundMissingAttr(name, "default");
1007 } else if (msg->contains("in")) {
1008 return limitFoundMissingAttr(name, "in");
1009 } else if ((name == "channel-count" || name == "concurrent-instances") ^
1010 (found = msg->findString("max", &max))) {
1011 return limitFoundMissingAttr(name, "max", found);
1012 } else if (msg->contains("min")) {
1013 return limitFoundMissingAttr(name, "min");
1014 } else if (msg->contains("range")) {
1015 return limitFoundMissingAttr(name, "range");
1016 } else if ((name == "sample-rate") ^
1017 (found = msg->findString("ranges", &ranges))) {
1018 return limitFoundMissingAttr(name, "ranges", found);
1019 } else if (msg->contains("scale")) {
1020 return limitFoundMissingAttr(name, "scale");
1021 } else if ((name == "alignment" || name == "block-size") ^
1022 (found = msg->findString("value", &value))) {
1023 return limitFoundMissingAttr(name, "value", found);
1024 }
1025
1026 if (max.size()) {
1027 AString tag = "max-";
1028 tag.append(name);
1029 mCurrentInfo->addDetail(tag, max);
1030 } else if (value.size()) {
1031 mCurrentInfo->addDetail(name, value);
1032 } else if (ranges.size()) {
1033 AString tag = name;
1034 tag.append("-ranges");
1035 mCurrentInfo->addDetail(tag, ranges);
1036 } else {
1037 ALOGW("Ignoring unrecognized limit '%s'", name.c_str());
1038 }
1039 }
1040 return OK;
1041 }
1042
addFeature(const char ** attrs)1043 status_t MediaCodecList::addFeature(const char **attrs) {
1044 size_t i = 0;
1045 const char *name = NULL;
1046 int32_t optional = -1;
1047 int32_t required = -1;
1048 const char *value = NULL;
1049
1050 while (attrs[i] != NULL) {
1051 if (attrs[i + 1] == NULL) {
1052 return -EINVAL;
1053 }
1054
1055 // attributes with values
1056 if (!strcmp(attrs[i], "name")) {
1057 name = attrs[i + 1];
1058 ++i;
1059 } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) {
1060 int value = (int)parseBoolean(attrs[i + 1]);
1061 if (!strcmp(attrs[i], "optional")) {
1062 optional = value;
1063 } else {
1064 required = value;
1065 }
1066 ++i;
1067 } else if (!strcmp(attrs[i], "value")) {
1068 value = attrs[i + 1];
1069 ++i;
1070 } else {
1071 return -EINVAL;
1072 }
1073 ++i;
1074 }
1075 if (name == NULL) {
1076 ALOGE("feature with no 'name' attribute");
1077 return -EINVAL;
1078 }
1079
1080 if (optional == required && optional != -1) {
1081 ALOGE("feature '%s' is both/neither optional and required", name);
1082 return -EINVAL;
1083 }
1084
1085 if ((optional != -1 || required != -1) && (value != NULL)) {
1086 ALOGE("feature '%s' has both a value and optional/required attribute", name);
1087 return -EINVAL;
1088 }
1089
1090 if (value != NULL) {
1091 mCurrentInfo->addFeature(name, value);
1092 } else {
1093 mCurrentInfo->addFeature(name, (required == 1) || (optional == 0));
1094 }
1095 return OK;
1096 }
1097
findCodecByName(const char * name) const1098 ssize_t MediaCodecList::findCodecByName(const char *name) const {
1099 for (size_t i = 0; i < mCodecInfos.size(); ++i) {
1100 const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get();
1101
1102 if (info.mName == name) {
1103 return i;
1104 }
1105 }
1106
1107 return -ENOENT;
1108 }
1109
countCodecs() const1110 size_t MediaCodecList::countCodecs() const {
1111 return mCodecInfos.size();
1112 }
1113
getGlobalSettings() const1114 const sp<AMessage> MediaCodecList::getGlobalSettings() const {
1115 return mGlobalSettings;
1116 }
1117
1118 } // namespace android
1119