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 #include "WMFDecoderModule.h"
8 
9 #include <algorithm>
10 #include <vector>
11 
12 #include "DriverCrashGuard.h"
13 #include "GfxDriverInfo.h"
14 #include "MFTDecoder.h"
15 #include "MP4Decoder.h"
16 #include "MediaInfo.h"
17 #include "PDMFactory.h"
18 #include "VPXDecoder.h"
19 #include "WMF.h"
20 #include "WMFAudioMFTManager.h"
21 #include "WMFMediaDataDecoder.h"
22 #include "WMFVideoMFTManager.h"
23 #include "mozilla/DebugOnly.h"
24 #include "mozilla/Maybe.h"
25 #include "mozilla/StaticMutex.h"
26 #include "mozilla/StaticPrefs_media.h"
27 #include "mozilla/WindowsVersion.h"
28 #include "mozilla/gfx/gfxVars.h"
29 #include "mozilla/mscom/EnsureMTA.h"
30 #include "mozilla/ProfilerMarkers.h"
31 #include "nsComponentManagerUtils.h"
32 #include "nsIXULRuntime.h"
33 #include "nsIXULRuntime.h"  // for BrowserTabsRemoteAutostart
34 #include "nsServiceManagerUtils.h"
35 #include "nsWindowsHelpers.h"
36 #include "prsystem.h"
37 
38 #define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
39 
40 extern const GUID CLSID_WebmMfVpxDec;
41 
42 namespace mozilla {
43 
44 // Helper function to add a profile marker and log at the same time.
45 static void MOZ_FORMAT_PRINTF(2, 3)
WmfDecoderModuleMarkerAndLog(const ProfilerString8View & aMarkerTag,const char * aFormat,...)46     WmfDecoderModuleMarkerAndLog(const ProfilerString8View& aMarkerTag,
47                                  const char* aFormat, ...) {
48   va_list ap;
49   va_start(ap, aFormat);
50   const nsVprintfCString markerString(aFormat, ap);
51   va_end(ap);
52   PROFILER_MARKER_TEXT(aMarkerTag, MEDIA_PLAYBACK, {}, markerString);
53   LOG("%s", markerString.get());
54 }
55 
56 static Atomic<bool> sDXVAEnabled(false);
57 static Atomic<bool> sUsableVPXMFT(false);
58 
59 /* static */
Create()60 already_AddRefed<PlatformDecoderModule> WMFDecoderModule::Create() {
61   RefPtr<WMFDecoderModule> wmf = new WMFDecoderModule();
62   return wmf.forget();
63 }
64 
~WMFDecoderModule()65 WMFDecoderModule::~WMFDecoderModule() {
66   if (mWMFInitialized) {
67     DebugOnly<HRESULT> hr = wmf::MFShutdown();
68     NS_ASSERTION(SUCCEEDED(hr), "MFShutdown failed");
69   }
70 }
71 
IsRemoteAcceleratedCompositor(layers::KnowsCompositor * aKnowsCompositor)72 static bool IsRemoteAcceleratedCompositor(
73     layers::KnowsCompositor* aKnowsCompositor) {
74   if (!aKnowsCompositor) {
75     return false;
76   }
77 
78   if (aKnowsCompositor->UsingSoftwareWebRenderD3D11()) {
79     return true;
80   }
81 
82   TextureFactoryIdentifier ident =
83       aKnowsCompositor->GetTextureFactoryIdentifier();
84   return !aKnowsCompositor->UsingSoftwareWebRender() &&
85          ident.mParentProcessType == GeckoProcessType_GPU;
86 }
87 
CanCreateMFTDecoder(const GUID & aGuid)88 static bool CanCreateMFTDecoder(const GUID& aGuid) {
89   // The IMFTransform interface used by MFTDecoder is documented to require to
90   // run on an MTA thread.
91   // https://msdn.microsoft.com/en-us/library/windows/desktop/ee892371(v=vs.85).aspx#components
92   // Note: our normal SharedThreadPool task queues are initialized to MTA, but
93   // the main thread (which calls in here from our CanPlayType implementation)
94   // is not.
95   bool canCreateDecoder = false;
96   mozilla::mscom::EnsureMTA([&]() -> void {
97     if (FAILED(wmf::MFStartup())) {
98       return;
99     }
100     RefPtr<MFTDecoder> decoder(new MFTDecoder());
101     canCreateDecoder = SUCCEEDED(decoder->Create(aGuid));
102     wmf::MFShutdown();
103   });
104   return canCreateDecoder;
105 }
106 
107 /* static */
Init()108 void WMFDecoderModule::Init() {
109   MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
110   if (XRE_IsContentProcess()) {
111     // If we're in the content process and the UseGPUDecoder pref is set, it
112     // means that we've given up on the GPU process (it's been crashing) so we
113     // should disable DXVA
114     sDXVAEnabled = !StaticPrefs::media_gpu_process_decoder();
115   } else if (XRE_IsGPUProcess()) {
116     // Always allow DXVA in the GPU process.
117     sDXVAEnabled = true;
118   } else if (XRE_IsRDDProcess()) {
119     // Only allows DXVA if we have an image device. We may have explicitly
120     // disabled its creation following an earlier RDD process crash.
121     sDXVAEnabled = !!DeviceManagerDx::Get()->GetImageDevice();
122   } else {
123     // Only allow DXVA in the UI process if we aren't in e10s Firefox
124     sDXVAEnabled = !mozilla::BrowserTabsRemoteAutostart();
125   }
126 
127   // We have heavy logging below to help diagnose issue around hardware decoding
128   // failures. Due to these failures often relating to driver level problems
129   // they're hard to nail down, so we want lots of info. We may be able to relax
130   // this in future if we're not seeing such problems (see bug 1673007 for
131   // references to the bugs motivating this).
132   sDXVAEnabled = sDXVAEnabled && gfx::gfxVars::CanUseHardwareVideoDecoding();
133   bool testForVPx = gfx::gfxVars::CanUseHardwareVideoDecoding();
134   if (testForVPx && StaticPrefs::media_wmf_vp9_enabled_AtStartup()) {
135     gfx::WMFVPXVideoCrashGuard guard;
136     if (!guard.Crashed()) {
137       WmfDecoderModuleMarkerAndLog("WMFInit VPx Pending",
138                                    "Attempting to create MFT decoder for VPx");
139 
140       sUsableVPXMFT = CanCreateMFTDecoder(CLSID_WebmMfVpxDec);
141 
142       WmfDecoderModuleMarkerAndLog("WMFInit VPx Initialized",
143                                    "CanCreateMFTDecoder returned %s for VPx",
144                                    sUsableVPXMFT ? "true" : "false");
145     } else {
146       WmfDecoderModuleMarkerAndLog(
147           "WMFInit VPx Failure",
148           "Will not use MFT VPx due to crash guard reporting a crash");
149     }
150   }
151 
152   WmfDecoderModuleMarkerAndLog(
153       "WMFInit Result",
154       "WMFDecoderModule::Init finishing with sDXVAEnabled=%s testForVPx=%s "
155       "sUsableVPXMFT=%s",
156       sDXVAEnabled ? "true" : "false", testForVPx ? "true" : "false",
157       sUsableVPXMFT ? "true" : "false");
158 }
159 
160 /* static */
GetNumDecoderThreads()161 int WMFDecoderModule::GetNumDecoderThreads() {
162   int32_t numCores = PR_GetNumberOfProcessors();
163 
164   // If we have more than 4 cores, let the decoder decide how many threads.
165   // On an 8 core machine, WMF chooses 4 decoder threads.
166   static const int WMF_DECODER_DEFAULT = -1;
167   if (numCores > 4) {
168     return WMF_DECODER_DEFAULT;
169   }
170   return std::max(numCores - 1, 1);
171 }
172 
Startup()173 nsresult WMFDecoderModule::Startup() {
174   mWMFInitialized = SUCCEEDED(wmf::MFStartup());
175   return mWMFInitialized ? NS_OK : NS_ERROR_FAILURE;
176 }
177 
CreateVideoDecoder(const CreateDecoderParams & aParams)178 already_AddRefed<MediaDataDecoder> WMFDecoderModule::CreateVideoDecoder(
179     const CreateDecoderParams& aParams) {
180   // In GPU process, only support decoding if an accelerated compositor is
181   // known.
182   if (XRE_IsGPUProcess() &&
183       !IsRemoteAcceleratedCompositor(aParams.mKnowsCompositor)) {
184     return nullptr;
185   }
186 
187   UniquePtr<WMFVideoMFTManager> manager(new WMFVideoMFTManager(
188       aParams.VideoConfig(), aParams.mKnowsCompositor, aParams.mImageContainer,
189       aParams.mRate.mValue, aParams.mOptions, sDXVAEnabled));
190 
191   MediaResult result = manager->Init();
192   if (NS_FAILED(result)) {
193     if (aParams.mError) {
194       *aParams.mError = result;
195     }
196     WmfDecoderModuleMarkerAndLog(
197         "WMFVDecoderCreation Failure",
198         "WMFDecoderModule::CreateVideoDecoder failed for manager with "
199         "description %s with result: %s",
200         manager->GetDescriptionName().get(), result.Description().get());
201     return nullptr;
202   }
203 
204   WmfDecoderModuleMarkerAndLog(
205       "WMFVDecoderCreation Success",
206       "WMFDecoderModule::CreateVideoDecoder success for manager with "
207       "description %s",
208       manager->GetDescriptionName().get());
209 
210   RefPtr<MediaDataDecoder> decoder = new WMFMediaDataDecoder(manager.release());
211   return decoder.forget();
212 }
213 
CreateAudioDecoder(const CreateDecoderParams & aParams)214 already_AddRefed<MediaDataDecoder> WMFDecoderModule::CreateAudioDecoder(
215     const CreateDecoderParams& aParams) {
216   if (XRE_IsGPUProcess()) {
217     // Only allow video in the GPU process.
218     return nullptr;
219   }
220 
221   UniquePtr<WMFAudioMFTManager> manager(
222       new WMFAudioMFTManager(aParams.AudioConfig()));
223 
224   if (!manager->Init()) {
225     WmfDecoderModuleMarkerAndLog(
226         "WMFADecoderCreation Failure",
227         "WMFDecoderModule::CreateAudioDecoder failed for manager with "
228         "description %s",
229         manager->GetDescriptionName().get());
230     return nullptr;
231   }
232 
233   WmfDecoderModuleMarkerAndLog(
234       "WMFADecoderCreation Success",
235       "WMFDecoderModule::CreateAudioDecoder success for manager with "
236       "description %s",
237       manager->GetDescriptionName().get());
238 
239   RefPtr<MediaDataDecoder> decoder = new WMFMediaDataDecoder(manager.release());
240   return decoder.forget();
241 }
242 
243 template <const GUID& aGuid>
CanCreateWMFDecoder()244 static bool CanCreateWMFDecoder() {
245   static StaticMutex sMutex;
246   StaticMutexAutoLock lock(sMutex);
247   static Maybe<bool> result;
248   if (result.isNothing()) {
249     result.emplace(CanCreateMFTDecoder(aGuid));
250   }
251   return result.value();
252 }
253 
254 /* static */
HasH264()255 bool WMFDecoderModule::HasH264() {
256   return CanCreateWMFDecoder<CLSID_CMSH264DecoderMFT>();
257 }
258 
259 /* static */
HasVP8()260 bool WMFDecoderModule::HasVP8() {
261   return sUsableVPXMFT && CanCreateWMFDecoder<CLSID_WebmMfVpxDec>();
262 }
263 
264 /* static */
HasVP9()265 bool WMFDecoderModule::HasVP9() {
266   return sUsableVPXMFT && CanCreateWMFDecoder<CLSID_WebmMfVpxDec>();
267 }
268 
269 /* static */
HasAAC()270 bool WMFDecoderModule::HasAAC() {
271   return CanCreateWMFDecoder<CLSID_CMSAACDecMFT>();
272 }
273 
274 /* static */
HasMP3()275 bool WMFDecoderModule::HasMP3() {
276   return CanCreateWMFDecoder<CLSID_CMP3DecMediaObject>();
277 }
278 
SupportsMimeType(const nsACString & aMimeType,DecoderDoctorDiagnostics * aDiagnostics) const279 bool WMFDecoderModule::SupportsMimeType(
280     const nsACString& aMimeType, DecoderDoctorDiagnostics* aDiagnostics) const {
281   UniquePtr<TrackInfo> trackInfo = CreateTrackInfoWithMIMEType(aMimeType);
282   if (!trackInfo) {
283     return false;
284   }
285   return Supports(SupportDecoderParams(*trackInfo), aDiagnostics);
286 }
287 
Supports(const SupportDecoderParams & aParams,DecoderDoctorDiagnostics * aDiagnostics) const288 bool WMFDecoderModule::Supports(const SupportDecoderParams& aParams,
289                                 DecoderDoctorDiagnostics* aDiagnostics) const {
290   // In GPU process, only support decoding if video. This only gives a hint of
291   // what the GPU decoder *may* support. The actual check will occur in
292   // CreateVideoDecoder.
293   const auto& trackInfo = aParams.mConfig;
294   if (XRE_IsGPUProcess() && !trackInfo.GetAsVideoInfo()) {
295     return false;
296   }
297 
298   const auto* videoInfo = trackInfo.GetAsVideoInfo();
299   // Temporary - forces use of VPXDecoder when alpha is present.
300   // Bug 1263836 will handle alpha scenario once implemented. It will shift
301   // the check for alpha to PDMFactory but not itself remove the need for a
302   // check.
303   if (videoInfo && (!SupportsColorDepth(videoInfo->mColorDepth, aDiagnostics) ||
304                     videoInfo->HasAlpha())) {
305     return false;
306   }
307 
308   if ((trackInfo.mMimeType.EqualsLiteral("audio/mp4a-latm") ||
309        trackInfo.mMimeType.EqualsLiteral("audio/mp4")) &&
310       WMFDecoderModule::HasAAC()) {
311     return true;
312   }
313   if (MP4Decoder::IsH264(trackInfo.mMimeType) && WMFDecoderModule::HasH264()) {
314     return true;
315   }
316   if (trackInfo.mMimeType.EqualsLiteral("audio/mpeg") &&
317       !StaticPrefs::media_ffvpx_mp3_enabled() && WMFDecoderModule::HasMP3()) {
318     return true;
319   }
320   static const uint32_t VP8_USABLE_BUILD = 16287;
321   if (VPXDecoder::IsVP8(trackInfo.mMimeType) &&
322       IsWindowsBuildOrLater(VP8_USABLE_BUILD) && WMFDecoderModule::HasVP8()) {
323     return true;
324   }
325   if (VPXDecoder::IsVP9(trackInfo.mMimeType) && WMFDecoderModule::HasVP9()) {
326     return true;
327   }
328 
329   // Some unsupported codec.
330   return false;
331 }
332 
333 }  // namespace mozilla
334 
335 #undef WFM_DECODER_MODULE_STATUS_MARKER
336 #undef LOG
337