Source code

Revision control

Other Tools

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