FFmpegDecoderModule.h (11821B)
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 7 #ifndef __FFmpegDecoderModule_h__ 8 #define __FFmpegDecoderModule_h__ 9 10 #include "FFmpegAudioDecoder.h" 11 #include "FFmpegLibWrapper.h" 12 #include "FFmpegUtils.h" 13 #include "FFmpegVideoDecoder.h" 14 #include "MP4Decoder.h" 15 #include "PlatformDecoderModule.h" 16 #include "VPXDecoder.h" 17 #include "VideoUtils.h" 18 #include "mozilla/DataMutex.h" 19 #include "mozilla/StaticPrefs_media.h" 20 #include "mozilla/gfx/gfxVars.h" 21 #include "prenv.h" 22 23 #ifdef DEBUG 24 # include "mozilla/AppShutdown.h" 25 #endif 26 27 namespace mozilla { 28 29 template <int V> 30 class FFmpegDecoderModule : public PlatformDecoderModule { 31 public: 32 const char* Name() const override { 33 #ifdef FFVPX_VERSION 34 return "FFmpeg(FFVPX)"; 35 #else 36 return "FFmpeg(OS library)"; 37 #endif 38 } 39 static void Init(const FFmpegLibWrapper* aLib) { 40 #if (defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || \ 41 defined(MOZ_WIDGET_ANDROID)) && \ 42 defined(MOZ_USE_HWDECODE) && !defined(MOZ_FFVPX_AUDIOONLY) 43 # ifdef XP_WIN 44 if (!XRE_IsGPUProcess()) { 45 return; 46 } 47 # else 48 if (!XRE_IsRDDProcess() && !XRE_IsUtilityProcess() && 49 !(XRE_IsParentProcess() && PR_GetEnv("MOZ_RUN_GTEST"))) { 50 return; 51 } 52 # endif 53 54 if (!gfx::gfxVars::IsInitialized()) { 55 MOZ_ASSERT(AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdown)); 56 return; 57 } 58 59 const AVHWDeviceType kDeviceTypes[] = { 60 # ifdef XP_WIN 61 AV_HWDEVICE_TYPE_D3D11VA, 62 # endif 63 # ifdef MOZ_WIDGET_GTK 64 AV_HWDEVICE_TYPE_VAAPI, 65 AV_HWDEVICE_TYPE_NONE, // Placeholder for V4L2. 66 # endif 67 # ifdef MOZ_WIDGET_ANDROID 68 AV_HWDEVICE_TYPE_MEDIACODEC, 69 AV_HWDEVICE_TYPE_NONE, // Placeholder for audio. 70 # endif 71 }; 72 73 struct CodecEntry { 74 AVCodecID mId; 75 bool mHwAllowed; 76 }; 77 78 const CodecEntry kCodecIDs[] = { 79 // The following open video codecs can be decoded via hardware by using the 80 // system ffmpeg or ffvpx. 81 # if LIBAVCODEC_VERSION_MAJOR >= 59 82 {AV_CODEC_ID_AV1, gfx::gfxVars::UseAV1HwDecode()}, 83 # endif 84 # if LIBAVCODEC_VERSION_MAJOR >= 55 85 {AV_CODEC_ID_VP9, gfx::gfxVars::UseVP9HwDecode()}, 86 # endif 87 # if (defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_ANDROID)) && \ 88 LIBAVCODEC_VERSION_MAJOR >= 54 89 {AV_CODEC_ID_VP8, gfx::gfxVars::UseVP8HwDecode()}, 90 # endif 91 92 # if defined(MOZ_WIDGET_GTK) && !defined(FFVPX_VERSION) 93 // These proprietary video codecs can only be decoded via hardware by using 94 // the system ffmpeg, not supported by ffvpx. 95 # if LIBAVCODEC_VERSION_MAJOR >= 55 96 {AV_CODEC_ID_HEVC, gfx::gfxVars::UseHEVCHwDecode()}, 97 # endif 98 {AV_CODEC_ID_H264, gfx::gfxVars::UseH264HwDecode()}, 99 # endif 100 # ifdef MOZ_WIDGET_ANDROID 101 // These proprietary codecs can only be decoded via MediaCodec decoders, 102 // but the underlying implementation may be software or hardware. 103 {AV_CODEC_ID_HEVC, true}, 104 {AV_CODEC_ID_H264, true}, 105 {AV_CODEC_ID_AAC, true}, 106 # endif 107 }; 108 109 { 110 // Reset the list of supported hardware codecs and reevaluate them. 111 auto hwCodecs = sSupportedHWCodecs.Lock(); 112 hwCodecs->Clear(); 113 for (const auto& entry : kCodecIDs) { 114 if (!entry.mHwAllowed) { 115 MOZ_LOG(sPDMLog, LogLevel::Debug, 116 ("Hw codec disabled by gfxVars for %s", 117 AVCodecToString(entry.mId))); 118 continue; 119 } 120 121 const AVCodec* codec = nullptr; 122 for (const auto& deviceType : kDeviceTypes) { 123 codec = FFmpegVideoDecoder<V>::FindVideoHardwareAVCodec( 124 aLib, entry.mId, deviceType); 125 if (codec) { 126 break; 127 } 128 } 129 130 if (!codec) { 131 MOZ_LOG( 132 sPDMLog, LogLevel::Debug, 133 ("No hw codec or decoder for %s", AVCodecToString(entry.mId))); 134 continue; 135 } 136 137 hwCodecs->AppendElement(entry.mId); 138 MOZ_LOG(sPDMLog, LogLevel::Debug, 139 ("Support %s for hw decoding", AVCodecToString(entry.mId))); 140 } 141 } 142 #endif // (XP_WIN || MOZ_WIDGET_GTK || MOZ_WIDGET_ANDROID) && MOZ_USE_HWDECODE 143 // && !MOZ_FFVPX_AUDIOONLY 144 } 145 146 static already_AddRefed<PlatformDecoderModule> Create( 147 const FFmpegLibWrapper* aLib) { 148 RefPtr<PlatformDecoderModule> pdm = new FFmpegDecoderModule(aLib); 149 150 return pdm.forget(); 151 } 152 153 explicit FFmpegDecoderModule(const FFmpegLibWrapper* aLib) : mLib(aLib) {} 154 virtual ~FFmpegDecoderModule() = default; 155 156 already_AddRefed<MediaDataDecoder> CreateVideoDecoder( 157 const CreateDecoderParams& aParams) override { 158 if (Supports(SupportDecoderParams(aParams), nullptr).isEmpty()) { 159 return nullptr; 160 } 161 auto decoder = MakeRefPtr<FFmpegVideoDecoder<V>>( 162 mLib, aParams.VideoConfig(), aParams.mKnowsCompositor, 163 aParams.mImageContainer, 164 aParams.mOptions.contains(CreateDecoderParams::Option::LowLatency), 165 aParams.mOptions.contains( 166 CreateDecoderParams::Option::HardwareDecoderNotAllowed), 167 aParams.mOptions.contains( 168 CreateDecoderParams::Option::Output8BitPerChannel), 169 aParams.mTrackingId, aParams.mCDM); 170 171 // Ensure that decoding is exclusively performed using HW decoding in 172 // the GPU process. If FFmpeg does not support HW decoding, reset the 173 // decoder to allow PDMFactory to select an alternative HW-capable decoder 174 // module if available. In contrast, in the RDD process, it is acceptable 175 // to fallback to SW decoding when HW decoding is not available. 176 if (XRE_IsGPUProcess()) { 177 AVCodecID videoCodec = 178 FFmpegVideoDecoder<V>::GetCodecId(aParams.mConfig.mMimeType); 179 if (IsHWDecodingSupported(videoCodec) && 180 !decoder->IsHardwareAccelerated()) { 181 MOZ_LOG(sPDMLog, LogLevel::Debug, 182 ("FFmpeg video decoder can't perform hw decoding, abort!")); 183 (void)decoder->Shutdown(); 184 decoder = nullptr; 185 } 186 } 187 return decoder.forget(); 188 } 189 190 already_AddRefed<MediaDataDecoder> CreateAudioDecoder( 191 const CreateDecoderParams& aParams) override { 192 if (Supports(SupportDecoderParams(aParams), nullptr).isEmpty()) { 193 return nullptr; 194 } 195 RefPtr<MediaDataDecoder> decoder = new FFmpegAudioDecoder<V>(mLib, aParams); 196 return decoder.forget(); 197 } 198 199 media::DecodeSupportSet SupportsMimeType( 200 const nsACString& aMimeType, 201 DecoderDoctorDiagnostics* aDiagnostics) const override { 202 UniquePtr<TrackInfo> trackInfo = CreateTrackInfoWithMIMEType(aMimeType); 203 if (!trackInfo) { 204 return media::DecodeSupportSet{}; 205 } 206 return Supports(SupportDecoderParams(*trackInfo), aDiagnostics); 207 } 208 209 media::DecodeSupportSet Supports( 210 const SupportDecoderParams& aParams, 211 DecoderDoctorDiagnostics* aDiagnostics) const override { 212 // This should only be supported by MFMediaEngineDecoderModule. 213 if (aParams.mMediaEngineId) { 214 return media::DecodeSupportSet{}; 215 } 216 217 // Temporary - forces use of VPXDecoder when alpha is present. 218 // Bug 1263836 will handle alpha scenario once implemented. It will shift 219 // the check for alpha to PDMFactory but not itself remove the need for a 220 // check. 221 const auto& trackInfo = aParams.mConfig; 222 const nsACString& mimeType = trackInfo.mMimeType; 223 if (VPXDecoder::IsVPX(mimeType) && trackInfo.GetAsVideoInfo()->HasAlpha()) { 224 MOZ_LOG(sPDMLog, LogLevel::Debug, 225 ("FFmpeg decoder rejects requested type '%s'", 226 mimeType.BeginReading())); 227 return media::DecodeSupportSet{}; 228 } 229 230 if (VPXDecoder::IsVP9(mimeType) && 231 aParams.mOptions.contains(CreateDecoderParams::Option::LowLatency)) { 232 // SVC layers are unsupported, and may be used in low latency use cases 233 // (WebRTC). 234 MOZ_LOG(sPDMLog, LogLevel::Debug, 235 ("FFmpeg decoder rejects requested type '%s' due to low latency", 236 mimeType.BeginReading())); 237 return media::DecodeSupportSet{}; 238 } 239 240 if (MP4Decoder::IsHEVC(mimeType) && !StaticPrefs::media_hevc_enabled()) { 241 MOZ_LOG( 242 sPDMLog, LogLevel::Debug, 243 ("FFmpeg decoder rejects requested type '%s' due to being disabled " 244 "by the pref", 245 mimeType.BeginReading())); 246 return media::DecodeSupportSet{}; 247 } 248 249 AVCodecID videoCodec = FFmpegVideoDecoder<V>::GetCodecId(mimeType); 250 AVCodecID audioCodec = FFmpegAudioDecoder<V>::GetCodecId( 251 mimeType, 252 trackInfo.GetAsAudioInfo() ? *trackInfo.GetAsAudioInfo() : AudioInfo()); 253 if (audioCodec == AV_CODEC_ID_NONE && videoCodec == AV_CODEC_ID_NONE) { 254 MOZ_LOG(sPDMLog, LogLevel::Debug, 255 ("FFmpeg decoder rejects requested type '%s'", 256 mimeType.BeginReading())); 257 return media::DecodeSupportSet{}; 258 } 259 AVCodecID codecId = 260 audioCodec != AV_CODEC_ID_NONE ? audioCodec : videoCodec; 261 262 media::DecodeSupportSet supports; 263 if (IsSWDecodingSupported(codecId)) { 264 supports += media::DecodeSupport::SoftwareDecode; 265 } 266 if (IsHWDecodingSupported(codecId)) { 267 #ifdef MOZ_WIDGET_ANDROID 268 // Because we don't provide software implementations of H264 or HEVC on 269 // Android, we must use the platform software decoders even if true 270 // hardware decoding support is missing. 271 switch (codecId) { 272 case AV_CODEC_ID_H264: 273 supports += gfx::gfxVars::UseH264HwDecode() 274 ? media::DecodeSupport::HardwareDecode 275 : media::DecodeSupport::SoftwareDecode; 276 break; 277 case AV_CODEC_ID_HEVC: 278 supports += gfx::gfxVars::UseHEVCHwDecode() 279 ? media::DecodeSupport::HardwareDecode 280 : media::DecodeSupport::SoftwareDecode; 281 break; 282 default: 283 supports += media::DecodeSupport::HardwareDecode; 284 break; 285 } 286 #else 287 supports += media::DecodeSupport::HardwareDecode; 288 #endif 289 } 290 291 #ifdef XP_WIN 292 // TODO : add this for Android as well in bug 1974849. 293 MOZ_ASSERT_IF(XRE_IsGPUProcess() && IsVideoCodec(codecId), 294 !supports.contains(media::DecodeSupport::SoftwareDecode)); 295 #endif 296 297 MOZ_LOG( 298 sPDMLog, LogLevel::Debug, 299 ("FFmpeg decoder %s requested type '%s'", 300 supports.isEmpty() ? "rejects" : "supports", mimeType.BeginReading())); 301 return supports; 302 } 303 304 protected: 305 bool SupportsColorDepth( 306 gfx::ColorDepth aColorDepth, 307 DecoderDoctorDiagnostics* aDiagnostics) const override { 308 #if defined(MOZ_WIDGET_ANDROID) 309 return aColorDepth == gfx::ColorDepth::COLOR_8; 310 #endif 311 return true; 312 } 313 314 bool IsSWDecodingSupported(const AVCodecID& aCodec) const { 315 #ifdef XP_WIN 316 // SW video decoding is not allowed in the GPU process. 317 if (IsVideoCodec(aCodec) && XRE_IsGPUProcess()) { 318 return false; 319 } 320 #endif 321 return FFmpegDataDecoder<V>::FindSoftwareAVCodec(mLib, aCodec); 322 } 323 324 bool IsHWDecodingSupported(AVCodecID aCodec) const { 325 #ifdef FFVPX_VERSION 326 if (!StaticPrefs::media_ffvpx_hw_enabled()) { 327 return false; 328 } 329 #endif 330 // We don't need to check the gfxVars again because we check them when we 331 // populated sSupportedHWCodecs. 332 auto hwCodecs = sSupportedHWCodecs.Lock(); 333 return hwCodecs->Contains(aCodec); 334 } 335 336 private: 337 const FFmpegLibWrapper* mLib; 338 MOZ_RUNINIT static inline StaticDataMutex<nsTArray<AVCodecID>> 339 sSupportedHWCodecs{"sSupportedHWCodecs"}; 340 }; 341 342 } // namespace mozilla 343 344 #endif // __FFmpegDecoderModule_h__