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