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