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 "WMFVideoMFTManager.h"
8
9 #include "DXVA2Manager.h"
10 #include "GMPUtils.h" // For SplitAt. TODO: Move SplitAt to a central place.
11 #include "IMFYCbCrImage.h"
12 #include "ImageContainer.h"
13 #include "Layers.h"
14 #include "MP4Decoder.h"
15 #include "MediaInfo.h"
16 #include "MediaTelemetryConstants.h"
17 #include "VPXDecoder.h"
18 #include "VideoUtils.h"
19 #include "WMFUtils.h"
20 #include "gfx2DGlue.h"
21 #include "gfxPrefs.h"
22 #include "gfxWindowsPlatform.h"
23 #include "mozilla/gfx/gfxVars.h"
24 #include "mozilla/gfx/DeviceManagerDx.h"
25 #include "mozilla/AbstractThread.h"
26 #include "mozilla/ClearOnShutdown.h"
27 #include "mozilla/Logging.h"
28 #include "mozilla/SyncRunnable.h"
29 #include "mozilla/Telemetry.h"
30 #include "mozilla/WindowsVersion.h"
31 #include "mozilla/gfx/DeviceManagerDx.h"
32 #include "mozilla/layers/LayersTypes.h"
33 #include "nsPrintfCString.h"
34 #include "nsThreadUtils.h"
35 #include "nsWindowsHelpers.h"
36 #include "WMFDecoderModule.h"
37 #include <algorithm>
38 #include <psapi.h>
39 #include <winsdkver.h>
40
41 #define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
42
43 using mozilla::layers::IMFYCbCrImage;
44 using mozilla::layers::Image;
45 using mozilla::layers::LayerManager;
46 using mozilla::layers::LayersBackend;
47 using mozilla::media::TimeUnit;
48
49 // AMD
50 // Path is appended on to the %ProgramW6432% base path.
51 const wchar_t kAMDVPXDecoderDLLPath[] =
52 L"\\Common Files\\ATI Technologies\\Multimedia\\";
53
54 const wchar_t kAMDVP9DecoderDLLName[] =
55 #if defined(ARCH_CPU_X86)
56 L"amf-mft-decvp9-decoder32.dll";
57 #elif defined(ARCH_CPU_X86_64)
58 L"amf-mft-decvp9-decoder64.dll";
59 #else
60 #error Unsupported Windows CPU Architecture
61 #endif
62
63 extern const GUID CLSID_AMDWebmMfVp9Dec = {
64 0x2d2d728a,
65 0x67d6,
66 0x48ab,
67 {0x89, 0xfb, 0xa6, 0xec, 0x65, 0x55, 0x49, 0x70}};
68
69 #if WINVER_MAXVER < 0x0A00
70 // Windows 10+ SDK has VP80 and VP90 defines
71 const GUID MFVideoFormat_VP80 = {
72 0x30385056,
73 0x0000,
74 0x0010,
75 {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
76
77 const GUID MFVideoFormat_VP90 = {
78 0x30395056,
79 0x0000,
80 0x0010,
81 {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
82 #endif
83
84 // Note: CLSID_WebmMfVpxDec needs to be extern for the CanCreateWMFDecoder
85 // template in WMFDecoderModule.cpp to work.
86 extern const GUID CLSID_WebmMfVpxDec = {
87 0xe3aaf548,
88 0xc9a4,
89 0x4c6e,
90 {0x23, 0x4d, 0x5a, 0xda, 0x37, 0x4b, 0x00, 0x00}};
91
92 namespace mozilla {
93
IsWin7H264Decoder4KCapable()94 static bool IsWin7H264Decoder4KCapable() {
95 WCHAR systemPath[MAX_PATH + 1];
96 if (!ConstructSystem32Path(L"msmpeg2vdec.dll", systemPath, MAX_PATH + 1)) {
97 // Cannot build path -> Assume it's the old DLL or it's missing.
98 return false;
99 }
100
101 DWORD zero;
102 DWORD infoSize = GetFileVersionInfoSizeW(systemPath, &zero);
103 if (infoSize == 0) {
104 // Can't get file info -> Assume it's the old DLL or it's missing.
105 return false;
106 }
107 auto infoData = MakeUnique<unsigned char[]>(infoSize);
108 VS_FIXEDFILEINFO* vInfo;
109 UINT vInfoLen;
110 if (GetFileVersionInfoW(systemPath, 0, infoSize, infoData.get()) &&
111 VerQueryValueW(infoData.get(), L"\\", (LPVOID*)&vInfo, &vInfoLen)) {
112 uint64_t version = uint64_t(vInfo->dwFileVersionMS) << 32 |
113 uint64_t(vInfo->dwFileVersionLS);
114 // 12.0.9200.16426 & later allow for >1920x1088 resolutions.
115 const uint64_t minimum =
116 (uint64_t(12) << 48) | (uint64_t(9200) << 16) | uint64_t(16426);
117 return version >= minimum;
118 }
119 // Can't get file version -> Assume it's the old DLL.
120 return false;
121 }
122
123 template <class T>
124 class DeleteObjectTask : public Runnable {
125 public:
DeleteObjectTask(nsAutoPtr<T> & aObject)126 explicit DeleteObjectTask(nsAutoPtr<T>& aObject)
127 : Runnable("VideoUtils::DeleteObjectTask"), mObject(aObject) {}
Run()128 NS_IMETHOD Run() override {
129 NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
130 mObject = nullptr;
131 return NS_OK;
132 }
133
134 private:
135 nsAutoPtr<T> mObject;
136 };
137
138 template <class T>
DeleteOnMainThread(nsAutoPtr<T> & aObject)139 void DeleteOnMainThread(nsAutoPtr<T>& aObject) {
140 nsCOMPtr<nsIRunnable> r = new DeleteObjectTask<T>(aObject);
141 SystemGroup::Dispatch(TaskCategory::Other, r.forget());
142 }
143
GetCompositorBackendType(layers::KnowsCompositor * aKnowsCompositor)144 LayersBackend GetCompositorBackendType(
145 layers::KnowsCompositor* aKnowsCompositor) {
146 if (aKnowsCompositor) {
147 return aKnowsCompositor->GetCompositorBackendType();
148 }
149 return LayersBackend::LAYERS_NONE;
150 }
151
WMFVideoMFTManager(const VideoInfo & aConfig,layers::KnowsCompositor * aKnowsCompositor,layers::ImageContainer * aImageContainer,float aFramerate,bool aDXVAEnabled)152 WMFVideoMFTManager::WMFVideoMFTManager(
153 const VideoInfo& aConfig, layers::KnowsCompositor* aKnowsCompositor,
154 layers::ImageContainer* aImageContainer, float aFramerate,
155 bool aDXVAEnabled)
156 : mVideoInfo(aConfig),
157 mImageSize(aConfig.mImage),
158 mVideoStride(0),
159 mYUVColorSpace(YUVColorSpace::BT601),
160 mImageContainer(aImageContainer),
161 mKnowsCompositor(aKnowsCompositor),
162 mDXVAEnabled(aDXVAEnabled),
163 mAMDVP9InUse(false),
164 mFramerate(aFramerate)
165 // mVideoStride, mVideoWidth, mVideoHeight, mUseHwAccel are initialized in
166 // Init().
167 {
168 MOZ_COUNT_CTOR(WMFVideoMFTManager);
169
170 // Need additional checks/params to check vp8/vp9
171 if (MP4Decoder::IsH264(aConfig.mMimeType)) {
172 mStreamType = H264;
173 } else if (VPXDecoder::IsVP8(aConfig.mMimeType)) {
174 mStreamType = VP8;
175 } else if (VPXDecoder::IsVP9(aConfig.mMimeType)) {
176 mStreamType = VP9;
177 } else {
178 mStreamType = Unknown;
179 }
180 }
181
~WMFVideoMFTManager()182 WMFVideoMFTManager::~WMFVideoMFTManager() {
183 MOZ_COUNT_DTOR(WMFVideoMFTManager);
184 // Ensure DXVA/D3D9 related objects are released on the main thread.
185 if (mDXVA2Manager) {
186 DeleteOnMainThread(mDXVA2Manager);
187 }
188
189 // Record whether the video decoder successfully decoded, or output null
190 // samples but did/didn't recover.
191 uint32_t telemetry =
192 (mNullOutputCount == 0)
193 ? 0
194 : (mGotValidOutputAfterNullOutput && mGotExcessiveNullOutput)
195 ? 1
196 : mGotExcessiveNullOutput
197 ? 2
198 : mGotValidOutputAfterNullOutput ? 3 : 4;
199
200 nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
201 "WMFVideoMFTManager::~WMFVideoMFTManager", [=]() -> void {
202 LOG(nsPrintfCString(
203 "Reporting telemetry VIDEO_MFT_OUTPUT_NULL_SAMPLES=%d",
204 telemetry)
205 .get());
206 Telemetry::Accumulate(
207 Telemetry::HistogramID::VIDEO_MFT_OUTPUT_NULL_SAMPLES, telemetry);
208 });
209 SystemGroup::Dispatch(TaskCategory::Other, task.forget());
210 }
211
GetMFTGUID()212 const GUID& WMFVideoMFTManager::GetMFTGUID() {
213 MOZ_ASSERT(mStreamType != Unknown);
214 switch (mStreamType) {
215 case H264:
216 return CLSID_CMSH264DecoderMFT;
217 case VP8:
218 return CLSID_WebmMfVpxDec;
219 case VP9:
220 return CLSID_WebmMfVpxDec;
221 default:
222 return GUID_NULL;
223 };
224 }
225
GetMediaSubtypeGUID()226 const GUID& WMFVideoMFTManager::GetMediaSubtypeGUID() {
227 MOZ_ASSERT(mStreamType != Unknown);
228 switch (mStreamType) {
229 case H264:
230 return MFVideoFormat_H264;
231 case VP8:
232 return MFVideoFormat_VP80;
233 case VP9:
234 return MFVideoFormat_VP90;
235 default:
236 return GUID_NULL;
237 };
238 }
239
240 struct D3DDLLBlacklistingCache {
241 // Blacklist pref value last seen.
242 nsCString mBlacklistPref;
243 // Non-empty if a blacklisted DLL was found.
244 nsCString mBlacklistedDLL;
245 };
246 StaticAutoPtr<D3DDLLBlacklistingCache> sD3D11BlacklistingCache;
247 StaticAutoPtr<D3DDLLBlacklistingCache> sD3D9BlacklistingCache;
248
249 // If a blacklisted DLL is found, return its information, otherwise "".
FindDXVABlacklistedDLL(StaticAutoPtr<D3DDLLBlacklistingCache> & aDLLBlacklistingCache,const nsCString & aBlacklist,const char * aDLLBlacklistPrefName)250 static const nsCString& FindDXVABlacklistedDLL(
251 StaticAutoPtr<D3DDLLBlacklistingCache>& aDLLBlacklistingCache,
252 const nsCString& aBlacklist, const char* aDLLBlacklistPrefName) {
253 NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
254
255 if (!aDLLBlacklistingCache) {
256 // First time here, create persistent data that will be reused in all
257 // D3D11-blacklisting checks.
258 aDLLBlacklistingCache = new D3DDLLBlacklistingCache();
259 ClearOnShutdown(&aDLLBlacklistingCache);
260 }
261
262 if (aBlacklist.IsEmpty()) {
263 // Empty blacklist -> No blacklisting.
264 aDLLBlacklistingCache->mBlacklistPref.SetLength(0);
265 aDLLBlacklistingCache->mBlacklistedDLL.SetLength(0);
266 return aDLLBlacklistingCache->mBlacklistedDLL;
267 }
268
269 // Detect changes in pref.
270 if (aDLLBlacklistingCache->mBlacklistPref.Equals(aBlacklist)) {
271 // Same blacklist -> Return same result (i.e., don't check DLLs again).
272 return aDLLBlacklistingCache->mBlacklistedDLL;
273 }
274 // Adopt new pref now, so we don't work on it again.
275 aDLLBlacklistingCache->mBlacklistPref = aBlacklist;
276
277 HANDLE hProcess = GetCurrentProcess();
278 mozilla::UniquePtr<HMODULE[]> hMods;
279 unsigned int modulesNum = 0;
280 if (hProcess != NULL) {
281 DWORD modulesSize;
282 if (EnumProcessModules(hProcess, nullptr, 0, &modulesSize)) {
283 modulesNum = modulesSize / sizeof(HMODULE);
284 hMods = mozilla::MakeUnique<HMODULE[]>(modulesNum);
285 if (EnumProcessModules(hProcess, hMods.get(),
286 modulesNum * sizeof(HMODULE), &modulesSize)) {
287 // The list may have shrunk
288 if (modulesSize / sizeof(HMODULE) < modulesNum) {
289 modulesNum = modulesSize / sizeof(HMODULE);
290 }
291 } else {
292 modulesNum = 0;
293 }
294 }
295 }
296
297 // media.wmf.disable-d3d*-for-dlls format: (whitespace is trimmed)
298 // "dll1.dll: 1.2.3.4[, more versions...][; more dlls...]"
299 nsTArray<nsCString> dlls;
300 SplitAt(";", aBlacklist, dlls);
301 for (const auto& dll : dlls) {
302 nsTArray<nsCString> nameAndVersions;
303 SplitAt(":", dll, nameAndVersions);
304 if (nameAndVersions.Length() != 2) {
305 NS_WARNING(nsPrintfCString("Skipping incorrect '%s' dll:versions format",
306 aDLLBlacklistPrefName)
307 .get());
308 continue;
309 }
310
311 nameAndVersions[0].CompressWhitespace();
312 NS_ConvertUTF8toUTF16 name(nameAndVersions[0]);
313
314 for (unsigned int i = 0; i <= modulesNum; i++) {
315 WCHAR dllPath[MAX_PATH + 1];
316
317 if (i < modulesNum) {
318 if (!GetModuleFileNameEx(hProcess, hMods[i], dllPath,
319 sizeof(dllPath) / sizeof(WCHAR))) {
320 continue;
321 }
322
323 nsCOMPtr<nsIFile> file;
324 if (NS_WARN_IF(NS_FAILED(NS_NewLocalFile(
325 nsDependentString(dllPath), false, getter_AddRefs(file))))) {
326 continue;
327 }
328
329 nsAutoString leafName;
330 if (NS_WARN_IF(NS_FAILED(file->GetLeafName(leafName)))) {
331 continue;
332 }
333
334 if (_wcsicmp(leafName.get(), name.get())) {
335 continue;
336 }
337 } else {
338 if (!ConstructSystem32Path(name.get(), dllPath, MAX_PATH + 1)) {
339 // Cannot build path -> Assume it's not the blacklisted DLL.
340 continue;
341 }
342 }
343
344 DWORD zero;
345 DWORD infoSize = GetFileVersionInfoSizeW(dllPath, &zero);
346 if (infoSize == 0) {
347 // Can't get file info -> Assume we don't have the blacklisted DLL.
348 continue;
349 }
350 // vInfo is a pointer into infoData, that's why we keep it outside of the
351 // loop.
352 auto infoData = MakeUnique<unsigned char[]>(infoSize);
353 VS_FIXEDFILEINFO* vInfo;
354 UINT vInfoLen;
355 if (!GetFileVersionInfoW(dllPath, 0, infoSize, infoData.get()) ||
356 !VerQueryValueW(infoData.get(), L"\\", (LPVOID*)&vInfo, &vInfoLen) ||
357 !vInfo) {
358 // Can't find version -> Assume it's not blacklisted.
359 continue;
360 }
361
362 nsTArray<nsCString> versions;
363 SplitAt(",", nameAndVersions[1], versions);
364 for (const auto& version : versions) {
365 nsTArray<nsCString> numberStrings;
366 SplitAt(".", version, numberStrings);
367 if (numberStrings.Length() != 4) {
368 NS_WARNING(
369 nsPrintfCString("Skipping incorrect '%s' a.b.c.d version format",
370 aDLLBlacklistPrefName)
371 .get());
372 continue;
373 }
374 DWORD numbers[4];
375 nsresult errorCode = NS_OK;
376 for (int i = 0; i < 4; ++i) {
377 numberStrings[i].CompressWhitespace();
378 numbers[i] = DWORD(numberStrings[i].ToInteger(&errorCode));
379 if (NS_FAILED(errorCode)) {
380 break;
381 }
382 if (numbers[i] > UINT16_MAX) {
383 errorCode = NS_ERROR_FAILURE;
384 break;
385 }
386 }
387
388 if (NS_FAILED(errorCode)) {
389 NS_WARNING(
390 nsPrintfCString("Skipping incorrect '%s' a.b.c.d version format",
391 aDLLBlacklistPrefName)
392 .get());
393 continue;
394 }
395
396 if (vInfo->dwFileVersionMS == ((numbers[0] << 16) | numbers[1]) &&
397 vInfo->dwFileVersionLS == ((numbers[2] << 16) | numbers[3])) {
398 // Blacklisted! Record bad DLL.
399 aDLLBlacklistingCache->mBlacklistedDLL.SetLength(0);
400 aDLLBlacklistingCache->mBlacklistedDLL.AppendPrintf(
401 "%s (%lu.%lu.%lu.%lu)", nameAndVersions[0].get(), numbers[0],
402 numbers[1], numbers[2], numbers[3]);
403 return aDLLBlacklistingCache->mBlacklistedDLL;
404 }
405 }
406 }
407 }
408
409 // No blacklisted DLL.
410 aDLLBlacklistingCache->mBlacklistedDLL.SetLength(0);
411 return aDLLBlacklistingCache->mBlacklistedDLL;
412 }
413
FindD3D11BlacklistedDLL()414 static const nsCString& FindD3D11BlacklistedDLL() {
415 return FindDXVABlacklistedDLL(sD3D11BlacklistingCache,
416 gfx::gfxVars::PDMWMFDisableD3D11Dlls(),
417 "media.wmf.disable-d3d11-for-dlls");
418 }
419
FindD3D9BlacklistedDLL()420 static const nsCString& FindD3D9BlacklistedDLL() {
421 return FindDXVABlacklistedDLL(sD3D9BlacklistingCache,
422 gfx::gfxVars::PDMWMFDisableD3D9Dlls(),
423 "media.wmf.disable-d3d9-for-dlls");
424 }
425
GetFoundD3D11BlacklistedDLL()426 const nsCString GetFoundD3D11BlacklistedDLL() {
427 if (sD3D11BlacklistingCache) {
428 return sD3D11BlacklistingCache->mBlacklistedDLL;
429 }
430
431 return nsCString();
432 }
433
GetFoundD3D9BlacklistedDLL()434 const nsCString GetFoundD3D9BlacklistedDLL() {
435 if (sD3D9BlacklistingCache) {
436 return sD3D9BlacklistingCache->mBlacklistedDLL;
437 }
438
439 return nsCString();
440 }
441
442 class CreateDXVAManagerEvent : public Runnable {
443 public:
CreateDXVAManagerEvent(layers::KnowsCompositor * aKnowsCompositor,nsCString & aFailureReason)444 CreateDXVAManagerEvent(layers::KnowsCompositor* aKnowsCompositor,
445 nsCString& aFailureReason)
446 : Runnable("CreateDXVAManagerEvent"),
447 mBackend(LayersBackend::LAYERS_D3D11),
448 mKnowsCompositor(aKnowsCompositor),
449 mFailureReason(aFailureReason) {}
450
Run()451 NS_IMETHOD Run() override {
452 NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
453 const bool deblacklistingForTelemetry =
454 XRE_IsGPUProcess() &&
455 gfxPrefs::PDMWMFDeblacklistingForTelemetryInGPUProcess();
456 nsACString* failureReason = &mFailureReason;
457 nsCString secondFailureReason;
458 if (mBackend == LayersBackend::LAYERS_D3D11 &&
459 gfxPrefs::PDMWMFAllowD3D11() && IsWin8OrLater()) {
460 const nsCString& blacklistedDLL = FindD3D11BlacklistedDLL();
461 if (!deblacklistingForTelemetry && !blacklistedDLL.IsEmpty()) {
462 failureReason->AppendPrintf("D3D11 blacklisted with DLL %s",
463 blacklistedDLL.get());
464 } else {
465 mDXVA2Manager =
466 DXVA2Manager::CreateD3D11DXVA(mKnowsCompositor, *failureReason);
467 if (mDXVA2Manager) {
468 return NS_OK;
469 }
470 }
471 // Try again with d3d9, but record the failure reason
472 // into a new var to avoid overwriting the d3d11 failure.
473 failureReason = &secondFailureReason;
474 mFailureReason.AppendLiteral("; ");
475 }
476
477 const nsCString& blacklistedDLL = FindD3D9BlacklistedDLL();
478 if (!deblacklistingForTelemetry && !blacklistedDLL.IsEmpty()) {
479 mFailureReason.AppendPrintf("D3D9 blacklisted with DLL %s",
480 blacklistedDLL.get());
481 } else {
482 mDXVA2Manager =
483 DXVA2Manager::CreateD3D9DXVA(mKnowsCompositor, *failureReason);
484 // Make sure we include the messages from both attempts (if applicable).
485 mFailureReason.Append(secondFailureReason);
486 }
487 return NS_OK;
488 }
489 nsAutoPtr<DXVA2Manager> mDXVA2Manager;
490 layers::LayersBackend mBackend;
491 layers::KnowsCompositor* mKnowsCompositor;
492 nsACString& mFailureReason;
493 };
494
InitializeDXVA()495 bool WMFVideoMFTManager::InitializeDXVA() {
496 // If we use DXVA but aren't running with a D3D layer manager then the
497 // readback of decoded video frames from GPU to CPU memory grinds painting
498 // to a halt, and makes playback performance *worse*.
499 if (!mDXVAEnabled) {
500 mDXVAFailureReason.AssignLiteral(
501 "Hardware video decoding disabled or blacklisted");
502 return false;
503 }
504 MOZ_ASSERT(!mDXVA2Manager);
505 if (!mKnowsCompositor || !mKnowsCompositor->SupportsD3D11()) {
506 mDXVAFailureReason.AssignLiteral("Unsupported layers backend");
507 return false;
508 }
509
510 // The DXVA manager must be created on the main thread.
511 RefPtr<CreateDXVAManagerEvent> event =
512 new CreateDXVAManagerEvent(mKnowsCompositor, mDXVAFailureReason);
513
514 if (NS_IsMainThread()) {
515 event->Run();
516 } else {
517 // This logic needs to run on the main thread
518 mozilla::SyncRunnable::DispatchToThread(
519 SystemGroup::EventTargetFor(mozilla::TaskCategory::Other), event);
520 }
521 mDXVA2Manager = event->mDXVA2Manager;
522
523 return mDXVA2Manager != nullptr;
524 }
525
ValidateVideoInfo()526 MediaResult WMFVideoMFTManager::ValidateVideoInfo() {
527 if (mStreamType != H264 || gfxPrefs::PDMWMFAllowUnsupportedResolutions()) {
528 return NS_OK;
529 }
530
531 // The WMF H.264 decoder is documented to have a minimum resolution 48x48
532 // pixels for resolution, but we won't enable hw decoding for the resolution <
533 // 132 pixels. It's assumed the software decoder doesn't have this limitation,
534 // but it still might have maximum resolution limitation.
535 // https://msdn.microsoft.com/en-us/library/windows/desktop/dd797815(v=vs.85).aspx
536 const bool Is4KCapable = IsWin8OrLater() || IsWin7H264Decoder4KCapable();
537 static const int32_t MAX_H264_PIXEL_COUNT =
538 Is4KCapable ? 4096 * 2304 : 1920 * 1088;
539 const CheckedInt32 pixelCount =
540 CheckedInt32(mVideoInfo.mImage.width) * mVideoInfo.mImage.height;
541
542 if (!pixelCount.isValid() || pixelCount.value() > MAX_H264_PIXEL_COUNT) {
543 mIsValid = false;
544 return MediaResult(
545 NS_ERROR_DOM_MEDIA_FATAL_ERR,
546 RESULT_DETAIL("Can't decode H.264 stream because its "
547 "resolution is out of the maximum limitation"));
548 }
549
550 return NS_OK;
551 }
552
LoadAMDVP9Decoder()553 already_AddRefed<MFTDecoder> WMFVideoMFTManager::LoadAMDVP9Decoder() {
554 MOZ_ASSERT(mStreamType == VP9);
555
556 RefPtr<MFTDecoder> decoder = new MFTDecoder();
557
558 HRESULT hr = decoder->Create(CLSID_AMDWebmMfVp9Dec);
559 if (SUCCEEDED(hr)) {
560 return decoder.forget();
561 }
562
563 // Check if we can load the AMD VP9 decoder using the path name.
564 nsString path = GetProgramW6432Path();
565 path.Append(kAMDVPXDecoderDLLPath);
566 path.Append(kAMDVP9DecoderDLLName);
567 HMODULE decoderDLL =
568 ::LoadLibraryEx(path.get(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
569 if (!decoderDLL) {
570 return nullptr;
571 }
572 hr = decoder->Create(decoderDLL, CLSID_AMDWebmMfVp9Dec);
573 NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
574 return decoder.forget();
575 }
576
Init()577 MediaResult WMFVideoMFTManager::Init() {
578 MediaResult result = ValidateVideoInfo();
579 if (NS_FAILED(result)) {
580 return result;
581 }
582
583 result = InitInternal();
584 if (NS_FAILED(result) && mAMDVP9InUse) {
585 // Something failed with the AMD VP9 decoder; attempt again defaulting back
586 // to Microsoft MFT.
587 mCheckForAMDDecoder = false;
588 if (mDXVA2Manager) {
589 DeleteOnMainThread(mDXVA2Manager);
590 }
591 result = InitInternal();
592 }
593
594 if (NS_SUCCEEDED(result) && mDXVA2Manager) {
595 // If we had some failures but eventually made it work,
596 // make sure we preserve the messages.
597 if (mDXVA2Manager->IsD3D11()) {
598 mDXVAFailureReason.AppendLiteral("Using D3D11 API");
599 } else {
600 mDXVAFailureReason.AppendLiteral("Using D3D9 API");
601 }
602 }
603
604 return result;
605 }
606
InitInternal()607 MediaResult WMFVideoMFTManager::InitInternal() {
608 // The H264 SanityTest uses a 132x132 videos to determine if DXVA can be used.
609 // so we want to use the software decoder for videos with lower resolutions.
610 static const int MIN_H264_HW_WIDTH = 132;
611 static const int MIN_H264_HW_HEIGHT = 132;
612
613 mUseHwAccel = false; // default value; changed if D3D setup succeeds.
614 bool useDxva = (mStreamType != H264 ||
615 (mVideoInfo.ImageRect().width > MIN_H264_HW_WIDTH &&
616 mVideoInfo.ImageRect().height > MIN_H264_HW_HEIGHT)) &&
617 InitializeDXVA();
618
619 RefPtr<MFTDecoder> decoder;
620
621 HRESULT hr;
622 if (mStreamType == VP9 && useDxva && mCheckForAMDDecoder &&
623 gfxPrefs::PDMWMFAMDVP9DecoderEnabled()) {
624 if ((decoder = LoadAMDVP9Decoder())) {
625 mAMDVP9InUse = true;
626 }
627 }
628 if (!decoder) {
629 mCheckForAMDDecoder = false;
630 mAMDVP9InUse = false;
631 decoder = new MFTDecoder();
632 hr = decoder->Create(GetMFTGUID());
633 NS_ENSURE_TRUE(SUCCEEDED(hr),
634 MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
635 RESULT_DETAIL("Can't create the MFT decoder.")));
636 }
637
638 RefPtr<IMFAttributes> attr(decoder->GetAttributes());
639 UINT32 aware = 0;
640 if (attr) {
641 attr->GetUINT32(MF_SA_D3D_AWARE, &aware);
642 attr->SetUINT32(CODECAPI_AVDecNumWorkerThreads,
643 WMFDecoderModule::GetNumDecoderThreads());
644 if (gfxPrefs::PDMWMFLowLatencyEnabled()) {
645 hr = attr->SetUINT32(CODECAPI_AVLowLatencyMode, TRUE);
646 if (SUCCEEDED(hr)) {
647 LOG("Enabling Low Latency Mode");
648 } else {
649 LOG("Couldn't enable Low Latency Mode");
650 }
651 }
652 }
653
654 if (useDxva) {
655 if (aware) {
656 // TODO: Test if I need this anywhere... Maybe on Vista?
657 // hr = attr->SetUINT32(CODECAPI_AVDecVideoAcceleration_H264, TRUE);
658 // NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
659 MOZ_ASSERT(mDXVA2Manager);
660 ULONG_PTR manager = ULONG_PTR(mDXVA2Manager->GetDXVADeviceManager());
661 hr = decoder->SendMFTMessage(MFT_MESSAGE_SET_D3D_MANAGER, manager);
662 if (SUCCEEDED(hr)) {
663 mUseHwAccel = true;
664 } else {
665 mDXVAFailureReason = nsPrintfCString(
666 "MFT_MESSAGE_SET_D3D_MANAGER failed with code %X", hr);
667 }
668 } else {
669 mDXVAFailureReason.AssignLiteral(
670 "Decoder returned false for MF_SA_D3D_AWARE");
671 }
672 }
673
674 if (!mUseHwAccel) {
675 if (mDXVA2Manager) {
676 // Either mDXVAEnabled was set to false prior the second call to
677 // InitInternal() due to CanUseDXVA() returning false, or
678 // MFT_MESSAGE_SET_D3D_MANAGER failed
679 DeleteOnMainThread(mDXVA2Manager);
680 }
681 if (mStreamType == VP9 || mStreamType == VP8) {
682 return MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
683 RESULT_DETAIL("Use VP8/9 MFT only if HW acceleration "
684 "is available."));
685 }
686 Telemetry::Accumulate(Telemetry::MEDIA_DECODER_BACKEND_USED,
687 uint32_t(media::MediaDecoderBackend::WMFSoftware));
688 }
689
690 mDecoder = decoder;
691 hr = SetDecoderMediaTypes();
692 NS_ENSURE_TRUE(
693 SUCCEEDED(hr),
694 MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
695 RESULT_DETAIL("Fail to set the decoder media types.")));
696
697 RefPtr<IMFMediaType> outputType;
698 hr = mDecoder->GetOutputMediaType(outputType);
699 NS_ENSURE_TRUE(
700 SUCCEEDED(hr),
701 MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
702 RESULT_DETAIL("Fail to get the output media type.")));
703
704 if (mUseHwAccel && !CanUseDXVA(outputType, mFramerate)) {
705 mDXVAEnabled = false;
706 // DXVA initialization with current decoder actually failed,
707 // re-do initialization.
708 return InitInternal();
709 }
710
711 LOG("Video Decoder initialized, Using DXVA: %s",
712 (mUseHwAccel ? "Yes" : "No"));
713
714 if (mUseHwAccel) {
715 hr = mDXVA2Manager->ConfigureForSize(mVideoInfo.ImageRect().width,
716 mVideoInfo.ImageRect().height);
717 NS_ENSURE_TRUE(SUCCEEDED(hr),
718 MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
719 RESULT_DETAIL("Fail to configure image size for "
720 "DXVA2Manager.")));
721 } else {
722 mYUVColorSpace = GetYUVColorSpace(outputType);
723 GetDefaultStride(outputType, mVideoInfo.ImageRect().width, &mVideoStride);
724 }
725 LOG("WMFVideoMFTManager frame geometry stride=%u picture=(%d, %d, %d, %d) "
726 "display=(%d,%d)",
727 mVideoStride, mVideoInfo.ImageRect().x, mVideoInfo.ImageRect().y,
728 mVideoInfo.ImageRect().width, mVideoInfo.ImageRect().height,
729 mVideoInfo.mDisplay.width, mVideoInfo.mDisplay.height);
730
731 if (!mUseHwAccel) {
732 RefPtr<ID3D11Device> device =
733 gfx::DeviceManagerDx::Get()->GetCompositorDevice();
734 if (!device) {
735 device = gfx::DeviceManagerDx::Get()->GetContentDevice();
736 }
737 if (device) {
738 RefPtr<ID3D10Multithread> multi;
739 HRESULT hr =
740 device->QueryInterface((ID3D10Multithread**)getter_AddRefs(multi));
741 if (SUCCEEDED(hr) && multi) {
742 multi->SetMultithreadProtected(TRUE);
743 mIMFUsable = true;
744 }
745 }
746 }
747 return MediaResult(NS_OK);
748 }
749
750 HRESULT
SetDecoderMediaTypes()751 WMFVideoMFTManager::SetDecoderMediaTypes() {
752 // Setup the input/output media types.
753 RefPtr<IMFMediaType> inputType;
754 HRESULT hr = wmf::MFCreateMediaType(getter_AddRefs(inputType));
755 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
756
757 hr = inputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
758 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
759
760 hr = inputType->SetGUID(MF_MT_SUBTYPE, GetMediaSubtypeGUID());
761 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
762
763 hr = inputType->SetUINT32(MF_MT_INTERLACE_MODE,
764 MFVideoInterlace_MixedInterlaceOrProgressive);
765 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
766
767 hr = inputType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
768 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
769
770 hr = MFSetAttributeSize(inputType, MF_MT_FRAME_SIZE,
771 mVideoInfo.ImageRect().width,
772 mVideoInfo.ImageRect().height);
773 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
774
775 RefPtr<IMFMediaType> outputType;
776 hr = wmf::MFCreateMediaType(getter_AddRefs(outputType));
777 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
778
779 hr = outputType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
780 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
781
782 hr = MFSetAttributeSize(outputType, MF_MT_FRAME_SIZE,
783 mVideoInfo.ImageRect().width,
784 mVideoInfo.ImageRect().height);
785 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
786
787 GUID outputSubType = mUseHwAccel ? MFVideoFormat_NV12 : MFVideoFormat_YV12;
788 hr = outputType->SetGUID(MF_MT_SUBTYPE, outputSubType);
789 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
790
791 return mDecoder->SetMediaTypes(inputType, outputType);
792 }
793
794 HRESULT
Input(MediaRawData * aSample)795 WMFVideoMFTManager::Input(MediaRawData* aSample) {
796 if (!mIsValid) {
797 return E_FAIL;
798 }
799
800 if (!mDecoder) {
801 // This can happen during shutdown.
802 return E_FAIL;
803 }
804
805 RefPtr<IMFSample> inputSample;
806 HRESULT hr = mDecoder->CreateInputSample(
807 aSample->Data(), uint32_t(aSample->Size()),
808 aSample->mTime.ToMicroseconds(), &inputSample);
809 NS_ENSURE_TRUE(SUCCEEDED(hr) && inputSample != nullptr, hr);
810
811 mLastDuration = aSample->mDuration;
812 mLastTime = aSample->mTime;
813 mSamplesCount++;
814
815 // Forward sample data to the decoder.
816 return mDecoder->Input(inputSample);
817 }
818
819 class SupportsConfigEvent : public Runnable {
820 public:
SupportsConfigEvent(DXVA2Manager * aDXVA2Manager,IMFMediaType * aMediaType,float aFramerate)821 SupportsConfigEvent(DXVA2Manager* aDXVA2Manager, IMFMediaType* aMediaType,
822 float aFramerate)
823 : Runnable("SupportsConfigEvent"),
824 mDXVA2Manager(aDXVA2Manager),
825 mMediaType(aMediaType),
826 mFramerate(aFramerate),
827 mSupportsConfig(false) {}
828
Run()829 NS_IMETHOD Run() override {
830 MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
831 mSupportsConfig = mDXVA2Manager->SupportsConfig(mMediaType, mFramerate);
832 return NS_OK;
833 }
834 DXVA2Manager* mDXVA2Manager;
835 IMFMediaType* mMediaType;
836 const float mFramerate;
837 bool mSupportsConfig;
838 };
839
840 // The MFTransform we use for decoding h264 video will silently fall
841 // back to software decoding (even if we've negotiated DXVA) if the GPU
842 // doesn't support decoding the given resolution. It will then upload
843 // the software decoded frames into d3d textures to preserve behaviour.
844 //
845 // Unfortunately this seems to cause corruption (see bug 1193547) and is
846 // slow because the upload is done into a non-shareable texture and requires
847 // us to copy it.
848 //
849 // This code tests if the given resolution can be supported directly on the GPU,
850 // and makes sure we only ask the MFT for DXVA if it can be supported properly.
851 //
852 // Ideally we'd know the framerate during initialization and would also ensure
853 // that new decoders are created if the resolution changes. Then we could move
854 // this check into Init and consolidate the main thread blocking code.
CanUseDXVA(IMFMediaType * aType,float aFramerate)855 bool WMFVideoMFTManager::CanUseDXVA(IMFMediaType* aType, float aFramerate) {
856 MOZ_ASSERT(mDXVA2Manager);
857 // SupportsConfig only checks for valid h264 decoders currently.
858 if (mStreamType != H264) {
859 return true;
860 }
861
862 // The supports config check must be done on the main thread since we have
863 // a crash guard protecting it.
864 RefPtr<SupportsConfigEvent> event =
865 new SupportsConfigEvent(mDXVA2Manager, aType, aFramerate);
866
867 if (NS_IsMainThread()) {
868 event->Run();
869 } else {
870 // This logic needs to run on the main thread
871 mozilla::SyncRunnable::DispatchToThread(
872 SystemGroup::EventTargetFor(mozilla::TaskCategory::Other), event);
873 }
874
875 return event->mSupportsConfig;
876 }
877
878 HRESULT
CreateBasicVideoFrame(IMFSample * aSample,int64_t aStreamOffset,VideoData ** aOutVideoData)879 WMFVideoMFTManager::CreateBasicVideoFrame(IMFSample* aSample,
880 int64_t aStreamOffset,
881 VideoData** aOutVideoData) {
882 NS_ENSURE_TRUE(aSample, E_POINTER);
883 NS_ENSURE_TRUE(aOutVideoData, E_POINTER);
884
885 *aOutVideoData = nullptr;
886
887 HRESULT hr;
888 RefPtr<IMFMediaBuffer> buffer;
889
890 // Must convert to contiguous buffer to use IMD2DBuffer interface.
891 hr = aSample->ConvertToContiguousBuffer(getter_AddRefs(buffer));
892 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
893
894 // Try and use the IMF2DBuffer interface if available, otherwise fallback
895 // to the IMFMediaBuffer interface. Apparently IMF2DBuffer is more efficient,
896 // but only some systems (Windows 8?) support it.
897 BYTE* data = nullptr;
898 LONG stride = 0;
899 RefPtr<IMF2DBuffer> twoDBuffer;
900 hr = buffer->QueryInterface(
901 static_cast<IMF2DBuffer**>(getter_AddRefs(twoDBuffer)));
902 if (SUCCEEDED(hr)) {
903 hr = twoDBuffer->Lock2D(&data, &stride);
904 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
905 } else {
906 hr = buffer->Lock(&data, nullptr, nullptr);
907 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
908 stride = mVideoStride;
909 }
910
911 // YV12, planar format: [YYYY....][VVVV....][UUUU....]
912 // i.e., Y, then V, then U.
913 VideoData::YCbCrBuffer b;
914
915 uint32_t videoWidth = mImageSize.width;
916 uint32_t videoHeight = mImageSize.height;
917
918 // Y (Y') plane
919 b.mPlanes[0].mData = data;
920 b.mPlanes[0].mStride = stride;
921 b.mPlanes[0].mHeight = videoHeight;
922 b.mPlanes[0].mWidth = videoWidth;
923 b.mPlanes[0].mOffset = 0;
924 b.mPlanes[0].mSkip = 0;
925
926 // The V and U planes are stored 16-row-aligned, so we need to add padding
927 // to the row heights to ensure the Y'CbCr planes are referenced properly.
928 uint32_t padding = 0;
929 if (videoHeight % 16 != 0) {
930 padding = 16 - (videoHeight % 16);
931 }
932 uint32_t y_size = stride * (videoHeight + padding);
933 uint32_t v_size = stride * (videoHeight + padding) / 4;
934 uint32_t halfStride = (stride + 1) / 2;
935 uint32_t halfHeight = (videoHeight + 1) / 2;
936 uint32_t halfWidth = (videoWidth + 1) / 2;
937
938 // U plane (Cb)
939 b.mPlanes[1].mData = data + y_size + v_size;
940 b.mPlanes[1].mStride = halfStride;
941 b.mPlanes[1].mHeight = halfHeight;
942 b.mPlanes[1].mWidth = halfWidth;
943 b.mPlanes[1].mOffset = 0;
944 b.mPlanes[1].mSkip = 0;
945
946 // V plane (Cr)
947 b.mPlanes[2].mData = data + y_size;
948 b.mPlanes[2].mStride = halfStride;
949 b.mPlanes[2].mHeight = halfHeight;
950 b.mPlanes[2].mWidth = halfWidth;
951 b.mPlanes[2].mOffset = 0;
952 b.mPlanes[2].mSkip = 0;
953
954 // YuvColorSpace
955 b.mYUVColorSpace = mYUVColorSpace;
956
957 TimeUnit pts = GetSampleTime(aSample);
958 NS_ENSURE_TRUE(pts.IsValid(), E_FAIL);
959 TimeUnit duration = GetSampleDuration(aSample);
960 NS_ENSURE_TRUE(duration.IsValid(), E_FAIL);
961 gfx::IntRect pictureRegion =
962 mVideoInfo.ScaledImageRect(videoWidth, videoHeight);
963
964 if (!mKnowsCompositor || !mKnowsCompositor->SupportsD3D11() || !mIMFUsable) {
965 RefPtr<VideoData> v = VideoData::CreateAndCopyData(
966 mVideoInfo, mImageContainer, aStreamOffset, pts, duration, b, false,
967 TimeUnit::FromMicroseconds(-1), pictureRegion);
968 if (twoDBuffer) {
969 twoDBuffer->Unlock2D();
970 } else {
971 buffer->Unlock();
972 }
973 v.forget(aOutVideoData);
974 return S_OK;
975 }
976
977 RefPtr<layers::PlanarYCbCrImage> image =
978 new IMFYCbCrImage(buffer, twoDBuffer);
979
980 VideoData::SetVideoDataToImage(image, mVideoInfo, b, pictureRegion, false);
981
982 RefPtr<VideoData> v = VideoData::CreateFromImage(
983 mVideoInfo.mDisplay, aStreamOffset, pts, duration, image.forget(), false,
984 TimeUnit::FromMicroseconds(-1));
985
986 v.forget(aOutVideoData);
987 return S_OK;
988 }
989
990 HRESULT
CreateD3DVideoFrame(IMFSample * aSample,int64_t aStreamOffset,VideoData ** aOutVideoData)991 WMFVideoMFTManager::CreateD3DVideoFrame(IMFSample* aSample,
992 int64_t aStreamOffset,
993 VideoData** aOutVideoData) {
994 NS_ENSURE_TRUE(aSample, E_POINTER);
995 NS_ENSURE_TRUE(aOutVideoData, E_POINTER);
996 NS_ENSURE_TRUE(mDXVA2Manager, E_ABORT);
997 NS_ENSURE_TRUE(mUseHwAccel, E_ABORT);
998
999 *aOutVideoData = nullptr;
1000 HRESULT hr;
1001
1002 gfx::IntRect pictureRegion =
1003 mVideoInfo.ScaledImageRect(mImageSize.width, mImageSize.height);
1004 RefPtr<Image> image;
1005 hr =
1006 mDXVA2Manager->CopyToImage(aSample, pictureRegion, getter_AddRefs(image));
1007 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1008 NS_ENSURE_TRUE(image, E_FAIL);
1009
1010 TimeUnit pts = GetSampleTime(aSample);
1011 NS_ENSURE_TRUE(pts.IsValid(), E_FAIL);
1012 TimeUnit duration = GetSampleDuration(aSample);
1013 NS_ENSURE_TRUE(duration.IsValid(), E_FAIL);
1014 RefPtr<VideoData> v = VideoData::CreateFromImage(
1015 mVideoInfo.mDisplay, aStreamOffset, pts, duration, image.forget(), false,
1016 TimeUnit::FromMicroseconds(-1));
1017
1018 NS_ENSURE_TRUE(v, E_FAIL);
1019 v.forget(aOutVideoData);
1020
1021 return S_OK;
1022 }
1023
1024 // Blocks until decoded sample is produced by the decoder.
1025 HRESULT
Output(int64_t aStreamOffset,RefPtr<MediaData> & aOutData)1026 WMFVideoMFTManager::Output(int64_t aStreamOffset, RefPtr<MediaData>& aOutData) {
1027 RefPtr<IMFSample> sample;
1028 HRESULT hr;
1029 aOutData = nullptr;
1030 int typeChangeCount = 0;
1031 bool wasDraining = mDraining;
1032 int64_t sampleCount = mSamplesCount;
1033 if (wasDraining) {
1034 mSamplesCount = 0;
1035 mDraining = false;
1036 }
1037
1038 TimeUnit pts;
1039 TimeUnit duration;
1040
1041 // Loop until we decode a sample, or an unexpected error that we can't
1042 // handle occurs.
1043 while (true) {
1044 hr = mDecoder->Output(&sample);
1045 if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
1046 return MF_E_TRANSFORM_NEED_MORE_INPUT;
1047 }
1048
1049 if (hr == MF_E_TRANSFORM_STREAM_CHANGE) {
1050 MOZ_ASSERT(!sample);
1051 // Video stream output type change, probably geometric aperture change.
1052 // We must reconfigure the decoder output type.
1053 hr = mDecoder->SetDecoderOutputType(false /* check all attribute */,
1054 nullptr, nullptr);
1055 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1056
1057 if (!mUseHwAccel) {
1058 // The stride may have changed, recheck for it.
1059 RefPtr<IMFMediaType> outputType;
1060 hr = mDecoder->GetOutputMediaType(outputType);
1061 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1062 mYUVColorSpace = GetYUVColorSpace(outputType);
1063 hr = GetDefaultStride(outputType, mVideoInfo.ImageRect().width,
1064 &mVideoStride);
1065 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1066 }
1067 // Catch infinite loops, but some decoders perform at least 2 stream
1068 // changes on consecutive calls, so be permissive.
1069 // 100 is arbitrarily > 2.
1070 NS_ENSURE_TRUE(typeChangeCount < 100, MF_E_TRANSFORM_STREAM_CHANGE);
1071 // Loop back and try decoding again...
1072 ++typeChangeCount;
1073 continue;
1074 }
1075
1076 if (SUCCEEDED(hr)) {
1077 if (!sample) {
1078 LOG("Video MFTDecoder returned success but no output!");
1079 // On some machines/input the MFT returns success but doesn't output
1080 // a video frame. If we detect this, try again, but only up to a
1081 // point; after 250 failures, give up. Note we count all failures
1082 // over the life of the decoder, as we may end up exiting with a
1083 // NEED_MORE_INPUT and coming back to hit the same error. So just
1084 // counting with a local variable (like typeChangeCount does) may
1085 // not work in this situation.
1086 ++mNullOutputCount;
1087 if (mNullOutputCount > 250) {
1088 LOG("Excessive Video MFTDecoder returning success but no output; "
1089 "giving up");
1090 mGotExcessiveNullOutput = true;
1091 return E_FAIL;
1092 }
1093 continue;
1094 }
1095 pts = GetSampleTime(sample);
1096 duration = GetSampleDuration(sample);
1097 if (!pts.IsValid() || !duration.IsValid()) {
1098 return E_FAIL;
1099 }
1100 if (wasDraining && sampleCount == 1 && pts == TimeUnit::Zero()) {
1101 // WMF is unable to calculate a duration if only a single sample
1102 // was parsed. Additionally, the pts always comes out at 0 under those
1103 // circumstances.
1104 // Seeing that we've only fed the decoder a single frame, the pts
1105 // and duration are known, it's of the last sample.
1106 pts = mLastTime;
1107 duration = mLastDuration;
1108 }
1109 if (mSeekTargetThreshold.isSome()) {
1110 if ((pts + duration) < mSeekTargetThreshold.ref()) {
1111 LOG("Dropping video frame which pts is smaller than seek target.");
1112 // It is necessary to clear the pointer to release the previous output
1113 // buffer.
1114 sample = nullptr;
1115 continue;
1116 }
1117 mSeekTargetThreshold.reset();
1118 }
1119 break;
1120 }
1121 // Else unexpected error, assert, and bail.
1122 NS_WARNING("WMFVideoMFTManager::Output() unexpected error");
1123 return hr;
1124 }
1125
1126 RefPtr<VideoData> frame;
1127 if (mUseHwAccel) {
1128 hr = CreateD3DVideoFrame(sample, aStreamOffset, getter_AddRefs(frame));
1129 } else {
1130 hr = CreateBasicVideoFrame(sample, aStreamOffset, getter_AddRefs(frame));
1131 }
1132 // Frame should be non null only when we succeeded.
1133 MOZ_ASSERT((frame != nullptr) == SUCCEEDED(hr));
1134 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
1135 NS_ENSURE_TRUE(frame, E_FAIL);
1136
1137 aOutData = frame;
1138 // Set the potentially corrected pts and duration.
1139 aOutData->mTime = pts;
1140 // The VP9 decoder doesn't provide a valid duration. AS VP9 doesn't have a
1141 // concept of pts vs dts and have no latency. We can as such use the last
1142 // known input duration.
1143 aOutData->mDuration = (mStreamType == VP9 && duration == TimeUnit::Zero())
1144 ? mLastDuration
1145 : duration;
1146
1147 if (mNullOutputCount) {
1148 mGotValidOutputAfterNullOutput = true;
1149 }
1150
1151 return S_OK;
1152 }
1153
Shutdown()1154 void WMFVideoMFTManager::Shutdown() {
1155 mDecoder = nullptr;
1156 DeleteOnMainThread(mDXVA2Manager);
1157 }
1158
IsHardwareAccelerated(nsACString & aFailureReason) const1159 bool WMFVideoMFTManager::IsHardwareAccelerated(
1160 nsACString& aFailureReason) const {
1161 aFailureReason = mDXVAFailureReason;
1162 return mDecoder && mUseHwAccel;
1163 }
1164
GetDescriptionName() const1165 nsCString WMFVideoMFTManager::GetDescriptionName() const {
1166 nsCString failureReason;
1167 if (mAMDVP9InUse) {
1168 return NS_LITERAL_CSTRING("amd vp9 hardware video decoder");
1169 }
1170 bool hw = IsHardwareAccelerated(failureReason);
1171 return nsPrintfCString("wmf %s video decoder - %s",
1172 hw ? "hardware" : "software",
1173 hw ? gfxPrefs::PDMWMFUseNV12Format() &&
1174 gfx::DeviceManagerDx::Get()->CanUseNV12()
1175 ? "nv12"
1176 : "rgba32"
1177 : "yuv420");
1178 }
1179
1180 } // namespace mozilla
1181