1 /* 2 * Copyright 2015 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 "MediaCodecListOverrides" 19 #include <utils/Log.h> 20 21 #include "MediaCodecListOverrides.h" 22 23 #include <cutils/properties.h> 24 #include <gui/Surface.h> 25 #include <media/ICrypto.h> 26 #include <media/IMediaCodecList.h> 27 #include <media/MediaCodecInfo.h> 28 #include <media/MediaResourcePolicy.h> 29 #include <media/openmax/OMX_IVCommon.h> 30 #include <media/stagefright/foundation/AMessage.h> 31 #include <media/stagefright/MediaCodec.h> 32 #include <media/stagefright/MediaCodecList.h> 33 34 namespace android { 35 36 const char *kProfilingResults = "/data/misc/media/media_codecs_profiling_results.xml"; 37 38 AString getProfilingVersionString() { 39 char val[PROPERTY_VALUE_MAX]; 40 if (property_get("ro.build.display.id", val, NULL) && (strlen(val) > 0)) { 41 return AStringPrintf("<!-- Profiled-with: %s -->", val); 42 } 43 44 return "<!-- Profiled-with: UNKNOWN_BUILD_ID -->"; 45 } 46 47 // a limit to avoid allocating unreasonable number of codec instances in the measurement. 48 // this should be in sync with the MAX_SUPPORTED_INSTANCES defined in MediaCodecInfo.java. 49 static const int kMaxInstances = 32; 50 51 // TODO: move MediaCodecInfo to C++. Until then, some temp methods to parse out info. 52 static bool getMeasureSize(sp<MediaCodecInfo::Capabilities> caps, int32_t *width, int32_t *height) { 53 AString sizeRange; 54 if (!caps->getDetails()->findString("size-range", &sizeRange)) { 55 return false; 56 } 57 AString minSize; 58 AString maxSize; 59 if (!splitString(sizeRange, "-", &minSize, &maxSize)) { 60 return false; 61 } 62 AString sWidth; 63 AString sHeight; 64 if (!splitString(minSize, "x", &sWidth, &sHeight)) { 65 if (!splitString(minSize, "*", &sWidth, &sHeight)) { 66 return false; 67 } 68 } 69 70 *width = strtol(sWidth.c_str(), NULL, 10); 71 *height = strtol(sHeight.c_str(), NULL, 10); 72 return (*width > 0) && (*height > 0); 73 } 74 75 static void getMeasureBitrate(sp<MediaCodecInfo::Capabilities> caps, int32_t *bitrate) { 76 // Until have native MediaCodecInfo, we cannot get bitrates based on profile/levels. 77 // We use 200000 as default value for our measurement. 78 *bitrate = 200000; 79 AString bitrateRange; 80 if (!caps->getDetails()->findString("bitrate-range", &bitrateRange)) { 81 return; 82 } 83 AString minBitrate; 84 AString maxBitrate; 85 if (!splitString(bitrateRange, "-", &minBitrate, &maxBitrate)) { 86 return; 87 } 88 89 *bitrate = strtol(minBitrate.c_str(), NULL, 10); 90 } 91 92 static sp<AMessage> getMeasureFormat( 93 bool isEncoder, AString mime, sp<MediaCodecInfo::Capabilities> caps) { 94 sp<AMessage> format = new AMessage(); 95 format->setString("mime", mime); 96 97 if (isEncoder) { 98 int32_t bitrate = 0; 99 getMeasureBitrate(caps, &bitrate); 100 format->setInt32("bitrate", bitrate); 101 format->setInt32("encoder", 1); 102 } 103 104 if (mime.startsWith("video/")) { 105 int32_t width = 0; 106 int32_t height = 0; 107 if (!getMeasureSize(caps, &width, &height)) { 108 return NULL; 109 } 110 format->setInt32("width", width); 111 format->setInt32("height", height); 112 113 Vector<uint32_t> colorFormats; 114 caps->getSupportedColorFormats(&colorFormats); 115 if (colorFormats.size() == 0) { 116 return NULL; 117 } 118 format->setInt32("color-format", colorFormats[0]); 119 120 format->setFloat("frame-rate", 10.0); 121 format->setInt32("i-frame-interval", 10); 122 } else { 123 // TODO: profile hw audio 124 return NULL; 125 } 126 127 return format; 128 } 129 130 static size_t doProfileEncoderInputBuffers( 131 AString name, AString mime, sp<MediaCodecInfo::Capabilities> caps) { 132 ALOGV("doProfileEncoderInputBuffers: name %s, mime %s", name.c_str(), mime.c_str()); 133 134 sp<AMessage> format = getMeasureFormat(true /* isEncoder */, mime, caps); 135 if (format == NULL) { 136 return 0; 137 } 138 139 format->setInt32("color-format", OMX_COLOR_FormatAndroidOpaque); 140 ALOGV("doProfileEncoderInputBuffers: format %s", format->debugString().c_str()); 141 142 status_t err = OK; 143 sp<ALooper> looper = new ALooper; 144 looper->setName("MediaCodec_looper"); 145 looper->start( 146 false /* runOnCallingThread */, false /* canCallJava */, ANDROID_PRIORITY_AUDIO); 147 148 sp<MediaCodec> codec = MediaCodec::CreateByComponentName(looper, name.c_str(), &err); 149 if (err != OK) { 150 ALOGE("Failed to create codec: %s", name.c_str()); 151 return 0; 152 } 153 154 err = codec->configure(format, NULL, NULL, MediaCodec::CONFIGURE_FLAG_ENCODE); 155 if (err != OK) { 156 ALOGE("Failed to configure codec: %s with mime: %s", name.c_str(), mime.c_str()); 157 codec->release(); 158 return 0; 159 } 160 161 sp<IGraphicBufferProducer> bufferProducer; 162 err = codec->createInputSurface(&bufferProducer); 163 if (err != OK) { 164 ALOGE("Failed to create surface: %s with mime: %s", name.c_str(), mime.c_str()); 165 codec->release(); 166 return 0; 167 } 168 169 int minUndequeued = 0; 170 err = bufferProducer->query( 171 NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeued); 172 if (err != OK) { 173 ALOGE("Failed to query NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS"); 174 minUndequeued = 0; 175 } 176 177 err = codec->release(); 178 if (err != OK) { 179 ALOGW("Failed to release codec: %s with mime: %s", name.c_str(), mime.c_str()); 180 } 181 182 return minUndequeued; 183 } 184 185 static size_t doProfileCodecs( 186 bool isEncoder, AString name, AString mime, sp<MediaCodecInfo::Capabilities> caps) { 187 sp<AMessage> format = getMeasureFormat(isEncoder, mime, caps); 188 if (format == NULL) { 189 return 0; 190 } 191 ALOGV("doProfileCodecs %s %s %s %s", 192 name.c_str(), mime.c_str(), isEncoder ? "encoder" : "decoder", 193 format->debugString().c_str()); 194 195 status_t err = OK; 196 Vector<sp<MediaCodec>> codecs; 197 while (err == OK && codecs.size() < kMaxInstances) { 198 sp<ALooper> looper = new ALooper; 199 looper->setName("MediaCodec_looper"); 200 ALOGV("doProfileCodecs for codec #%zu", codecs.size()); 201 ALOGV("doProfileCodecs start looper"); 202 looper->start( 203 false /* runOnCallingThread */, false /* canCallJava */, ANDROID_PRIORITY_AUDIO); 204 ALOGV("doProfileCodecs CreateByComponentName"); 205 sp<MediaCodec> codec = MediaCodec::CreateByComponentName(looper, name.c_str(), &err); 206 if (err != OK) { 207 ALOGV("Failed to create codec: %s", name.c_str()); 208 break; 209 } 210 const sp<Surface> nativeWindow; 211 const sp<ICrypto> crypto; 212 uint32_t flags = isEncoder ? MediaCodec::CONFIGURE_FLAG_ENCODE : 0; 213 ALOGV("doProfileCodecs configure"); 214 err = codec->configure(format, nativeWindow, crypto, flags); 215 if (err != OK) { 216 ALOGV("Failed to configure codec: %s with mime: %s", name.c_str(), mime.c_str()); 217 codec->release(); 218 break; 219 } 220 ALOGV("doProfileCodecs start"); 221 err = codec->start(); 222 if (err != OK) { 223 ALOGV("Failed to start codec: %s with mime: %s", name.c_str(), mime.c_str()); 224 codec->release(); 225 break; 226 } 227 codecs.push_back(codec); 228 } 229 230 for (size_t i = 0; i < codecs.size(); ++i) { 231 ALOGV("doProfileCodecs release %s", name.c_str()); 232 err = codecs[i]->release(); 233 if (err != OK) { 234 ALOGE("Failed to release codec: %s with mime: %s", name.c_str(), mime.c_str()); 235 } 236 } 237 238 return codecs.size(); 239 } 240 241 bool splitString(const AString &s, const AString &delimiter, AString *s1, AString *s2) { 242 ssize_t pos = s.find(delimiter.c_str()); 243 if (pos < 0) { 244 return false; 245 } 246 *s1 = AString(s, 0, pos); 247 *s2 = AString(s, pos + 1, s.size() - pos - 1); 248 return true; 249 } 250 251 bool splitString( 252 const AString &s, const AString &delimiter, AString *s1, AString *s2, AString *s3) { 253 AString temp; 254 if (!splitString(s, delimiter, s1, &temp)) { 255 return false; 256 } 257 if (!splitString(temp, delimiter, s2, s3)) { 258 return false; 259 } 260 return true; 261 } 262 263 void profileCodecs(const Vector<sp<MediaCodecInfo>> &infos) { 264 CodecSettings global_results; 265 KeyedVector<AString, CodecSettings> encoder_results; 266 KeyedVector<AString, CodecSettings> decoder_results; 267 profileCodecs(infos, &global_results, &encoder_results, &decoder_results); 268 exportResultsToXML(kProfilingResults, global_results, encoder_results, decoder_results); 269 } 270 271 void profileCodecs( 272 const Vector<sp<MediaCodecInfo>> &infos, 273 CodecSettings *global_results, 274 KeyedVector<AString, CodecSettings> *encoder_results, 275 KeyedVector<AString, CodecSettings> *decoder_results, 276 bool forceToMeasure) { 277 KeyedVector<AString, sp<MediaCodecInfo::Capabilities>> codecsNeedMeasure; 278 AString supportMultipleSecureCodecs = "true"; 279 size_t maxEncoderInputBuffers = 0; 280 for (size_t i = 0; i < infos.size(); ++i) { 281 const sp<MediaCodecInfo> info = infos[i]; 282 AString name = info->getCodecName(); 283 if (name.startsWith("OMX.google.") || 284 // TODO: reenable below codecs once fixed 285 name == "OMX.Intel.VideoDecoder.VP9.hybrid") { 286 continue; 287 } 288 289 Vector<AString> mimes; 290 info->getSupportedMimes(&mimes); 291 for (size_t i = 0; i < mimes.size(); ++i) { 292 const sp<MediaCodecInfo::Capabilities> &caps = 293 info->getCapabilitiesFor(mimes[i].c_str()); 294 if (!forceToMeasure && 295 (caps->getDetails()->contains("max-supported-instances") || 296 caps->getDetails()->contains("max-concurrent-instances"))) { 297 continue; 298 } 299 300 size_t max = doProfileCodecs(info->isEncoder(), name, mimes[i], caps); 301 if (max > 0) { 302 CodecSettings settings; 303 char maxStr[32]; 304 sprintf(maxStr, "%zu", max); 305 settings.add("max-supported-instances", maxStr); 306 307 AString key = name; 308 key.append(" "); 309 key.append(mimes[i]); 310 311 if (info->isEncoder()) { 312 encoder_results->add(key, settings); 313 } else { 314 decoder_results->add(key, settings); 315 } 316 317 if (name.endsWith(".secure")) { 318 if (max <= 1) { 319 supportMultipleSecureCodecs = "false"; 320 } 321 } 322 if (info->isEncoder() && mimes[i].startsWith("video/")) { 323 size_t encoderInputBuffers = 324 doProfileEncoderInputBuffers(name, mimes[i], caps); 325 if (encoderInputBuffers > maxEncoderInputBuffers) { 326 maxEncoderInputBuffers = encoderInputBuffers; 327 } 328 } 329 } 330 } 331 } 332 if (maxEncoderInputBuffers > 0) { 333 char tmp[32]; 334 sprintf(tmp, "%zu", maxEncoderInputBuffers); 335 global_results->add(kMaxEncoderInputBuffers, tmp); 336 } 337 global_results->add(kPolicySupportsMultipleSecureCodecs, supportMultipleSecureCodecs); 338 } 339 340 static AString globalResultsToXml(const CodecSettings& results) { 341 AString ret; 342 for (size_t i = 0; i < results.size(); ++i) { 343 AString setting = AStringPrintf( 344 " <Setting name=\"%s\" value=\"%s\" />\n", 345 results.keyAt(i).c_str(), 346 results.valueAt(i).c_str()); 347 ret.append(setting); 348 } 349 return ret; 350 } 351 352 static AString codecResultsToXml(const KeyedVector<AString, CodecSettings>& results) { 353 AString ret; 354 for (size_t i = 0; i < results.size(); ++i) { 355 AString name; 356 AString mime; 357 if (!splitString(results.keyAt(i), " ", &name, &mime)) { 358 continue; 359 } 360 AString codec = 361 AStringPrintf(" <MediaCodec name=\"%s\" type=\"%s\" update=\"true\" >\n", 362 name.c_str(), 363 mime.c_str()); 364 ret.append(codec); 365 CodecSettings settings = results.valueAt(i); 366 for (size_t i = 0; i < settings.size(); ++i) { 367 // WARNING: we assume all the settings are "Limit". Currently we have only one type 368 // of setting in this case, which is "max-supported-instances". 369 AString setting = AStringPrintf( 370 " <Limit name=\"%s\" value=\"%s\" />\n", 371 settings.keyAt(i).c_str(), 372 settings.valueAt(i).c_str()); 373 ret.append(setting); 374 } 375 ret.append(" </MediaCodec>\n"); 376 } 377 return ret; 378 } 379 380 void exportResultsToXML( 381 const char *fileName, 382 const CodecSettings& global_results, 383 const KeyedVector<AString, CodecSettings>& encoder_results, 384 const KeyedVector<AString, CodecSettings>& decoder_results) { 385 if (global_results.size() == 0 && encoder_results.size() == 0 && decoder_results.size() == 0) { 386 return; 387 } 388 389 AString overrides; 390 overrides.append(getProfilingVersionString()); 391 overrides.append("\n"); 392 overrides.append("<MediaCodecs>\n"); 393 if (global_results.size() > 0) { 394 overrides.append(" <Settings>\n"); 395 overrides.append(globalResultsToXml(global_results)); 396 overrides.append(" </Settings>\n"); 397 } 398 if (encoder_results.size() > 0) { 399 overrides.append(" <Encoders>\n"); 400 overrides.append(codecResultsToXml(encoder_results)); 401 overrides.append(" </Encoders>\n"); 402 } 403 if (decoder_results.size() > 0) { 404 overrides.append(" <Decoders>\n"); 405 overrides.append(codecResultsToXml(decoder_results)); 406 overrides.append(" </Decoders>\n"); 407 } 408 overrides.append("</MediaCodecs>\n"); 409 410 FILE *f = fopen(fileName, "wb"); 411 if (f == NULL) { 412 ALOGE("Failed to open %s for writing.", fileName); 413 return; 414 } 415 if (fwrite(overrides.c_str(), 1, overrides.size(), f) != overrides.size()) { 416 ALOGE("Failed to write to %s.", fileName); 417 } 418 fclose(f); 419 } 420 421 } // namespace android 422