MediaCodecsSupport.cpp (12556B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 #include <array> 7 8 #ifdef MOZ_AV1 9 # include "AOMDecoder.h" 10 #endif 11 #include "MP4Decoder.h" 12 #include "MediaCodecsSupport.h" 13 #include "PDMFactory.h" 14 #include "PEMFactory.h" 15 #include "PlatformDecoderModule.h" 16 #include "VPXDecoder.h" 17 #include "VideoUtils.h" 18 #include "mozilla/AppShutdown.h" 19 #include "mozilla/gfx/gfxVars.h" 20 #include "nsTHashMap.h" 21 22 using MediaCodecsSupport = mozilla::media::MediaCodecsSupport; 23 24 namespace mozilla::media { 25 26 static StaticAutoPtr<MCSInfo> sInstance; 27 static StaticMutex sInitMutex; 28 static StaticMutex sUpdateMutex; 29 30 #define CODEC_SUPPORT_LOG(msg, ...) \ 31 MOZ_LOG(sPDMLog, LogLevel::Debug, ("MediaCodecsSupport, " msg, ##__VA_ARGS__)) 32 33 /* static */ 34 MediaCodecsSupported MCSInfo::GetSupportFromFactory( 35 bool aForceRefresh /* = false */) { 36 return PDMFactory::Supported(aForceRefresh) + 37 PEMFactory::Supported(aForceRefresh); 38 } 39 40 void MCSInfo::AddSupport(const MediaCodecsSupported& aSupport) { 41 StaticMutexAutoLock lock(sUpdateMutex); 42 MCSInfo* instance = GetInstance(); 43 if (!instance) { 44 CODEC_SUPPORT_LOG("Can't add codec support without a MCSInfo instance!"); 45 return; 46 } 47 instance->mSupport += aSupport; 48 } 49 50 MediaCodecsSupported MCSInfo::GetSupport() { 51 StaticMutexAutoLock lock(sUpdateMutex); 52 MCSInfo* instance = GetInstance(); 53 if (!instance) { 54 CODEC_SUPPORT_LOG("Can't get codec support without a MCSInfo instance!"); 55 return MediaCodecsSupported{}; 56 } 57 return instance->mSupport; 58 } 59 60 void MCSInfo::ResetSupport() { 61 StaticMutexAutoLock lock(sUpdateMutex); 62 MCSInfo* instance = GetInstance(); 63 if (!instance) { 64 CODEC_SUPPORT_LOG("Can't reset codec support without a MCSInfo instance!"); 65 return; 66 } 67 instance->mSupport.clear(); 68 } 69 70 DecodeSupportSet MCSInfo::GetDecodeSupportSet( 71 const MediaCodec& aCodec, const MediaCodecsSupported& aSupported) { 72 DecodeSupportSet support; 73 const auto supportInfo = GetCodecDefinition(aCodec); 74 if (aSupported.contains(supportInfo.swDecodeSupport)) { 75 support += DecodeSupport::SoftwareDecode; 76 } 77 if (aSupported.contains(supportInfo.hwDecodeSupport)) { 78 support += DecodeSupport::HardwareDecode; 79 } 80 return support; 81 } 82 83 EncodeSupportSet MCSInfo::GetEncodeSupportSet( 84 const MediaCodec& aCodec, const MediaCodecsSupported& aSupported) { 85 EncodeSupportSet support; 86 const auto supportInfo = GetCodecDefinition(aCodec); 87 if (aSupported.contains(supportInfo.swEncodeSupport)) { 88 support += EncodeSupport::SoftwareEncode; 89 } 90 if (aSupported.contains(supportInfo.hwEncodeSupport)) { 91 support += EncodeSupport::HardwareEncode; 92 } 93 return support; 94 } 95 96 MediaCodecsSupported MCSInfo::GetDecodeMediaCodecsSupported( 97 const MediaCodec& aCodec, const DecodeSupportSet& aSupportSet) { 98 MediaCodecsSupported support; 99 const auto supportInfo = GetCodecDefinition(aCodec); 100 if (aSupportSet.contains(DecodeSupport::SoftwareDecode)) { 101 support += supportInfo.swDecodeSupport; 102 } 103 if (aSupportSet.contains(DecodeSupport::HardwareDecode)) { 104 support += supportInfo.hwDecodeSupport; 105 } 106 if (aSupportSet.contains(DecodeSupport::UnsureDueToLackOfExtension)) { 107 support += supportInfo.lackOfHWExtenstion; 108 } 109 return support; 110 } 111 112 MediaCodecsSupported MCSInfo::GetEncodeMediaCodecsSupported( 113 const MediaCodec& aCodec, const EncodeSupportSet& aSupportSet) { 114 MediaCodecsSupported support; 115 const auto supportInfo = GetCodecDefinition(aCodec); 116 if (aSupportSet.contains(EncodeSupport::SoftwareEncode)) { 117 support += supportInfo.swEncodeSupport; 118 } 119 if (aSupportSet.contains(EncodeSupport::HardwareEncode)) { 120 support += supportInfo.hwEncodeSupport; 121 } 122 if (aSupportSet.contains(EncodeSupport::UnsureDueToLackOfExtension)) { 123 support += supportInfo.lackOfHWExtenstion; 124 } 125 return support; 126 } 127 128 bool MCSInfo::SupportsSoftwareDecode( 129 const MediaCodecsSupported& aSupportedCodecs, const MediaCodec& aCodec) { 130 return ( 131 aSupportedCodecs.contains(GetCodecDefinition(aCodec).swDecodeSupport)); 132 } 133 134 bool MCSInfo::SupportsHardwareDecode( 135 const MediaCodecsSupported& aSupportedCodecs, const MediaCodec& aCodec) { 136 return ( 137 aSupportedCodecs.contains(GetCodecDefinition(aCodec).hwDecodeSupport)); 138 } 139 140 bool MCSInfo::SupportsSoftwareEncode( 141 const MediaCodecsSupported& aSupportedCodecs, const MediaCodec& aCodec) { 142 return ( 143 aSupportedCodecs.contains(GetCodecDefinition(aCodec).swEncodeSupport)); 144 } 145 146 bool MCSInfo::SupportsHardwareEncode( 147 const MediaCodecsSupported& aSupportedCodecs, const MediaCodec& aCodec) { 148 return ( 149 aSupportedCodecs.contains(GetCodecDefinition(aCodec).hwEncodeSupport)); 150 } 151 152 void MCSInfo::GetMediaCodecsSupportedString( 153 nsCString& aSupportString, const MediaCodecsSupported& aSupportedCodecs) { 154 CodecDefinition supportInfo; 155 aSupportString = ""_ns; 156 MCSInfo* instance = GetInstance(); 157 if (!instance) { 158 CODEC_SUPPORT_LOG("Can't get codec support string w/o a MCSInfo instance!"); 159 return; 160 } 161 for (const auto& it : GetAllCodecDefinitions()) { 162 if (it.codec == MediaCodec::SENTINEL) { 163 break; 164 } 165 if (!instance->mHashTableCodec->Get(it.codec, &supportInfo)) { 166 CODEC_SUPPORT_LOG("Can't find codec for MediaCodecsSupported enum: %d", 167 static_cast<int>(it.codec)); 168 continue; 169 } 170 aSupportString.Append(supportInfo.commonName); 171 bool foundSupport = false; 172 if (aSupportedCodecs.contains(it.swDecodeSupport)) { 173 aSupportString.Append(" SWDEC"_ns); 174 foundSupport = true; 175 } 176 if (aSupportedCodecs.contains(it.hwDecodeSupport)) { 177 aSupportString.Append(" HWDEC"_ns); 178 foundSupport = true; 179 } 180 if (aSupportedCodecs.contains(it.swEncodeSupport)) { 181 aSupportString.Append(" SWENC"_ns); 182 foundSupport = true; 183 } 184 if (aSupportedCodecs.contains(it.hwEncodeSupport)) { 185 aSupportString.Append(" HWENC"_ns); 186 foundSupport = true; 187 } 188 if (aSupportedCodecs.contains(it.lackOfHWExtenstion)) { 189 aSupportString.Append(" LACK_OF_EXTENSION"_ns); 190 foundSupport = true; 191 } 192 if (!foundSupport) { 193 aSupportString.Append(" NONE"_ns); 194 } 195 aSupportString.Append("\n"_ns); 196 } 197 // Remove any trailing newline characters 198 if (!aSupportString.IsEmpty()) { 199 aSupportString.Truncate(aSupportString.Length() - 1); 200 } 201 } 202 203 MCSInfo* MCSInfo::GetInstance() { 204 StaticMutexAutoLock lock(sInitMutex); 205 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) { 206 CODEC_SUPPORT_LOG("In XPCOM shutdown - not returning MCSInfo instance!"); 207 return nullptr; 208 } 209 if (!sInstance) { 210 sInstance = new MCSInfo(); 211 } 212 return sInstance.get(); 213 } 214 215 MCSInfo::MCSInfo() { 216 // Initialize hash tables 217 mHashTableMCS.reset(new nsTHashMap<MediaCodecsSupport, CodecDefinition>()); 218 mHashTableCodec.reset(new nsTHashMap<MediaCodec, CodecDefinition>()); 219 220 for (const auto& it : GetAllCodecDefinitions()) { 221 // Insert MediaCodecsSupport values as keys 222 mHashTableMCS->InsertOrUpdate(it.swDecodeSupport, it); 223 mHashTableMCS->InsertOrUpdate(it.hwDecodeSupport, it); 224 // Insert codec enum values as keys 225 mHashTableCodec->InsertOrUpdate(it.codec, it); 226 } 227 228 GetMainThreadSerialEventTarget()->Dispatch( 229 NS_NewRunnableFunction("MCSInfo::MCSInfo", [&] { 230 // Ensure hash tables freed on shutdown 231 RunOnShutdown( 232 [&] { 233 mHashTableMCS.reset(); 234 mHashTableString.reset(); 235 mHashTableCodec.reset(); 236 sInstance = nullptr; 237 }, 238 ShutdownPhase::XPCOMShutdown); 239 })); 240 } 241 242 CodecDefinition MCSInfo::GetCodecDefinition(const MediaCodec& aCodec) { 243 CodecDefinition info; 244 MCSInfo* instance = GetInstance(); 245 if (!instance) { 246 CODEC_SUPPORT_LOG("Can't get codec definition without a MCSInfo instance!"); 247 } else if (!instance->mHashTableCodec->Get(aCodec, &info)) { 248 CODEC_SUPPORT_LOG("Could not find codec definition for codec enum: %d!", 249 static_cast<int>(aCodec)); 250 } 251 return info; 252 } 253 254 MediaCodecsSupport MCSInfo::GetMediaCodecsSupportEnum( 255 const MediaCodec& aCodec, const DecodeSupport& aSupport) { 256 const CodecDefinition cd = GetCodecDefinition(aCodec); 257 if (aSupport == DecodeSupport::SoftwareDecode) { 258 return cd.swDecodeSupport; 259 } 260 if (aSupport == DecodeSupport::HardwareDecode) { 261 return cd.hwDecodeSupport; 262 } 263 return MediaCodecsSupport::SENTINEL; 264 } 265 266 MediaCodecsSupport MCSInfo::GetMediaCodecsSupportEnum( 267 const MediaCodec& aCodec, const EncodeSupport& aSupport) { 268 const CodecDefinition cd = GetCodecDefinition(aCodec); 269 if (aSupport == EncodeSupport::SoftwareEncode) { 270 return cd.swEncodeSupport; 271 } 272 if (aSupport == EncodeSupport::HardwareEncode) { 273 return cd.hwEncodeSupport; 274 } 275 return MediaCodecsSupport::SENTINEL; 276 } 277 278 MediaCodecSet MCSInfo::GetMediaCodecSetFromMimeTypes( 279 const nsTArray<nsCString>& aCodecStrings) { 280 MediaCodecSet support; 281 for (const auto& ms : aCodecStrings) { 282 const MediaCodec codec = MCSInfo::GetMediaCodecFromMimeType(ms); 283 if (codec == MediaCodec::SENTINEL) { 284 continue; 285 } 286 MOZ_ASSERT(codec < MediaCodec::SENTINEL); 287 support += codec; 288 } 289 return support; 290 } 291 292 MediaCodec MCSInfo::GetMediaCodecFromMimeType(const nsACString& aMimeType) { 293 // Video codecs 294 if (MP4Decoder::IsH264(aMimeType)) { 295 return MediaCodec::H264; 296 } 297 if (VPXDecoder::IsVP8(aMimeType)) { 298 return MediaCodec::VP8; 299 } 300 if (VPXDecoder::IsVP9(aMimeType)) { 301 return MediaCodec::VP9; 302 } 303 if (MP4Decoder::IsHEVC(aMimeType)) { 304 return MediaCodec::HEVC; 305 } 306 #ifdef MOZ_AV1 307 if (AOMDecoder::IsAV1(aMimeType)) { 308 return MediaCodec::AV1; 309 } 310 if (aMimeType.EqualsLiteral("video/av01")) { 311 return MediaCodec::AV1; 312 } 313 #endif 314 // TODO: Should this be Android only? 315 #ifdef ANDROID 316 if (aMimeType.EqualsLiteral("video/x-vnd.on2.vp8")) { 317 return MediaCodec::VP8; 318 } 319 if (aMimeType.EqualsLiteral("video/x-vnd.on2.vp9")) { 320 return MediaCodec::VP9; 321 } 322 #endif 323 // Audio codecs 324 if (MP4Decoder::IsAAC(aMimeType)) { 325 return MediaCodec::AAC; 326 } 327 if (aMimeType.EqualsLiteral("audio/vorbis")) { 328 return MediaCodec::Vorbis; 329 } 330 if (aMimeType.EqualsLiteral("audio/flac")) { 331 return MediaCodec::FLAC; 332 } 333 if (IsWaveMimetype(aMimeType)) { 334 return MediaCodec::Wave; 335 } 336 if (aMimeType.EqualsLiteral("audio/opus")) { 337 return MediaCodec::Opus; 338 } 339 if (aMimeType.EqualsLiteral("audio/mpeg")) { 340 return MediaCodec::MP3; 341 } 342 343 CODEC_SUPPORT_LOG("No specific codec enum for MIME type string: %s", 344 nsCString(aMimeType).get()); 345 return MediaCodec::SENTINEL; 346 } 347 348 #define MEDIA_CODEC_DEF_ENTRY_META(name, mimeType, lackOfExt) \ 349 {MediaCodec::name, \ 350 #name, \ 351 mimeType, \ 352 MediaCodecsSupport::name##SoftwareDecode, \ 353 MediaCodecsSupport::name##HardwareDecode, \ 354 MediaCodecsSupport::name##SoftwareEncode, \ 355 MediaCodecsSupport::name##HardwareEncode, \ 356 MediaCodecsSupport::lackOfExt} 357 358 #define MEDIA_CODEC_DEF_ENTRY(name, mimeType) \ 359 MEDIA_CODEC_DEF_ENTRY_META(name, mimeType, SENTINEL) 360 361 #define MEDIA_CODEC_DEF_ENTRY_LACKOFEXT(name, mimeType) \ 362 MEDIA_CODEC_DEF_ENTRY_META(name, mimeType, name##LackOfExtension) 363 364 std::array<CodecDefinition, 13> MCSInfo::GetAllCodecDefinitions() { 365 static constexpr std::array<CodecDefinition, 13> codecDefinitions = { 366 {MEDIA_CODEC_DEF_ENTRY(H264, "video/avc"), 367 MEDIA_CODEC_DEF_ENTRY(VP9, "video/vp9"), 368 MEDIA_CODEC_DEF_ENTRY(VP8, "video/vp8"), 369 MEDIA_CODEC_DEF_ENTRY_LACKOFEXT(AV1, "video/av1"), 370 MEDIA_CODEC_DEF_ENTRY(HEVC, "video/hevc"), 371 MEDIA_CODEC_DEF_ENTRY(AAC, "audio/mp4a-latm"), 372 MEDIA_CODEC_DEF_ENTRY(MP3, "audio/mpeg"), 373 MEDIA_CODEC_DEF_ENTRY(Opus, "audio/opus"), 374 MEDIA_CODEC_DEF_ENTRY(Vorbis, "audio/vorbis"), 375 MEDIA_CODEC_DEF_ENTRY(FLAC, "audio/flac"), 376 MEDIA_CODEC_DEF_ENTRY(Wave, "audio/x-wav")}}; 377 return codecDefinitions; 378 } 379 380 #undef MEDIA_CODEC_DEF_ENTRY_LACKOFEXT 381 #undef MEDIA_CODEC_DEF_ENTRY 382 #undef MEDIA_CODEC_DEF_ENTRY_META 383 384 } // namespace mozilla::media 385 386 #undef CODEC_SUPPORT_LOG