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