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