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