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 "WMFUtils.h"
8 #include "VideoUtils.h"
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/CheckedInt.h"
11 #include "mozilla/Logging.h"
12 #include "mozilla/RefPtr.h"
13 #include "nsTArray.h"
14 #include "nsThreadUtils.h"
15 #include "nsWindowsHelpers.h"
16 #include "prenv.h"
17 #include <shlobj.h>
18 #include <shlwapi.h>
19 #include <initguid.h>
20 #include <stdint.h>
21 #include "mozilla/mscom/EnsureMTA.h"
22 #include "mozilla/WindowsVersion.h"
23
24 #ifdef WMF_MUST_DEFINE_AAC_MFT_CLSID
25 // Some SDK versions don't define the AAC decoder CLSID.
26 // {32D186A7-218F-4C75-8876-DD77273A8999}
27 DEFINE_GUID(CLSID_CMSAACDecMFT, 0x32D186A7, 0x218F, 0x4C75, 0x88, 0x76, 0xDD,
28 0x77, 0x27, 0x3A, 0x89, 0x99);
29 #endif
30
31 namespace mozilla {
32
33 using media::TimeUnit;
34
35 HRESULT
HNsToFrames(int64_t aHNs,uint32_t aRate,int64_t * aOutFrames)36 HNsToFrames(int64_t aHNs, uint32_t aRate, int64_t* aOutFrames) {
37 MOZ_ASSERT(aOutFrames);
38 const int64_t HNS_PER_S = USECS_PER_S * 10;
39 CheckedInt<int64_t> i = aHNs;
40 i *= aRate;
41 i /= HNS_PER_S;
42 NS_ENSURE_TRUE(i.isValid(), E_FAIL);
43 *aOutFrames = i.value();
44 return S_OK;
45 }
46
47 HRESULT
GetDefaultStride(IMFMediaType * aType,uint32_t aWidth,uint32_t * aOutStride)48 GetDefaultStride(IMFMediaType* aType, uint32_t aWidth, uint32_t* aOutStride) {
49 // Try to get the default stride from the media type.
50 HRESULT hr = aType->GetUINT32(MF_MT_DEFAULT_STRIDE, aOutStride);
51 if (SUCCEEDED(hr)) {
52 return S_OK;
53 }
54
55 // Stride attribute not set, calculate it.
56 GUID subtype = GUID_NULL;
57
58 hr = aType->GetGUID(MF_MT_SUBTYPE, &subtype);
59 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
60
61 hr = wmf::MFGetStrideForBitmapInfoHeader(subtype.Data1, aWidth,
62 (LONG*)(aOutStride));
63 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
64
65 return hr;
66 }
67
GetYUVColorSpace(IMFMediaType * aType)68 Maybe<gfx::YUVColorSpace> GetYUVColorSpace(IMFMediaType* aType) {
69 UINT32 yuvColorMatrix;
70 HRESULT hr = aType->GetUINT32(MF_MT_YUV_MATRIX, &yuvColorMatrix);
71 NS_ENSURE_TRUE(SUCCEEDED(hr), {});
72
73 switch (yuvColorMatrix) {
74 case MFVideoTransferMatrix_BT2020_10:
75 case MFVideoTransferMatrix_BT2020_12:
76 return Some(gfx::YUVColorSpace::BT2020);
77 case MFVideoTransferMatrix_BT709:
78 return Some(gfx::YUVColorSpace::BT709);
79 case MFVideoTransferMatrix_BT601:
80 return Some(gfx::YUVColorSpace::BT601);
81 default:
82 MOZ_ASSERT_UNREACHABLE("Unhandled MFVideoTransferMatrix_?");
83 return {};
84 }
85 }
86
MFOffsetToInt32(const MFOffset & aOffset)87 int32_t MFOffsetToInt32(const MFOffset& aOffset) {
88 return int32_t(aOffset.value + (aOffset.fract / 65536.0f));
89 }
90
GetSampleDuration(IMFSample * aSample)91 TimeUnit GetSampleDuration(IMFSample* aSample) {
92 NS_ENSURE_TRUE(aSample, TimeUnit::Invalid());
93 int64_t duration = 0;
94 HRESULT hr = aSample->GetSampleDuration(&duration);
95 NS_ENSURE_TRUE(SUCCEEDED(hr), TimeUnit::Invalid());
96 return TimeUnit::FromMicroseconds(HNsToUsecs(duration));
97 }
98
GetSampleTime(IMFSample * aSample)99 TimeUnit GetSampleTime(IMFSample* aSample) {
100 NS_ENSURE_TRUE(aSample, TimeUnit::Invalid());
101 LONGLONG timestampHns = 0;
102 HRESULT hr = aSample->GetSampleTime(×tampHns);
103 NS_ENSURE_TRUE(SUCCEEDED(hr), TimeUnit::Invalid());
104 return TimeUnit::FromMicroseconds(HNsToUsecs(timestampHns));
105 }
106
107 // Gets the sub-region of the video frame that should be displayed.
108 // See:
109 // http://msdn.microsoft.com/en-us/library/windows/desktop/bb530115(v=vs.85).aspx
110 HRESULT
GetPictureRegion(IMFMediaType * aMediaType,gfx::IntRect & aOutPictureRegion)111 GetPictureRegion(IMFMediaType* aMediaType, gfx::IntRect& aOutPictureRegion) {
112 // Determine if "pan and scan" is enabled for this media. If it is, we
113 // only display a region of the video frame, not the entire frame.
114 BOOL panScan =
115 MFGetAttributeUINT32(aMediaType, MF_MT_PAN_SCAN_ENABLED, FALSE);
116
117 // If pan and scan mode is enabled. Try to get the display region.
118 HRESULT hr = E_FAIL;
119 MFVideoArea videoArea;
120 memset(&videoArea, 0, sizeof(MFVideoArea));
121 if (panScan) {
122 hr = aMediaType->GetBlob(MF_MT_PAN_SCAN_APERTURE, (UINT8*)&videoArea,
123 sizeof(MFVideoArea), nullptr);
124 }
125
126 // If we're not in pan-and-scan mode, or the pan-and-scan region is not set,
127 // check for a minimimum display aperture.
128 if (!panScan || hr == MF_E_ATTRIBUTENOTFOUND) {
129 hr = aMediaType->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE, (UINT8*)&videoArea,
130 sizeof(MFVideoArea), nullptr);
131 }
132
133 if (hr == MF_E_ATTRIBUTENOTFOUND) {
134 // Minimum display aperture is not set, for "backward compatibility with
135 // some components", check for a geometric aperture.
136 hr = aMediaType->GetBlob(MF_MT_GEOMETRIC_APERTURE, (UINT8*)&videoArea,
137 sizeof(MFVideoArea), nullptr);
138 }
139
140 if (SUCCEEDED(hr)) {
141 // The media specified a picture region, return it.
142 aOutPictureRegion = gfx::IntRect(MFOffsetToInt32(videoArea.OffsetX),
143 MFOffsetToInt32(videoArea.OffsetY),
144 videoArea.Area.cx, videoArea.Area.cy);
145 return S_OK;
146 }
147
148 // No picture region defined, fall back to using the entire video area.
149 UINT32 width = 0, height = 0;
150 hr = MFGetAttributeSize(aMediaType, MF_MT_FRAME_SIZE, &width, &height);
151 NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
152 NS_ENSURE_TRUE(width <= MAX_VIDEO_WIDTH, E_FAIL);
153 NS_ENSURE_TRUE(height <= MAX_VIDEO_HEIGHT, E_FAIL);
154
155 aOutPictureRegion = gfx::IntRect(0, 0, width, height);
156 return S_OK;
157 }
158
GetProgramW6432Path()159 nsString GetProgramW6432Path() {
160 char* programPath = PR_GetEnvSecure("ProgramW6432");
161 if (!programPath) {
162 programPath = PR_GetEnvSecure("ProgramFiles");
163 }
164
165 if (!programPath) {
166 return u"C:\\Program Files"_ns;
167 }
168 return NS_ConvertUTF8toUTF16(programPath);
169 }
170
171 namespace wmf {
172
173 static const wchar_t* sDLLs[] = {
174 L"mfplat.dll",
175 L"mf.dll",
176 L"dxva2.dll",
177 L"evr.dll",
178 };
179
180 HRESULT
LoadDLLs()181 LoadDLLs() {
182 static bool sDLLsLoaded = false;
183 static bool sFailedToLoadDlls = false;
184
185 if (sDLLsLoaded) {
186 return S_OK;
187 }
188 if (sFailedToLoadDlls) {
189 return E_FAIL;
190 }
191
192 // Try to load all the required DLLs. If we fail to load any dll,
193 // unload the dlls we succeeded in loading.
194 nsTArray<const wchar_t*> loadedDlls;
195 for (const wchar_t* dll : sDLLs) {
196 if (!LoadLibrarySystem32(dll)) {
197 NS_WARNING("Failed to load WMF DLLs");
198 for (const wchar_t* loadedDll : loadedDlls) {
199 FreeLibrary(GetModuleHandleW(loadedDll));
200 }
201 sFailedToLoadDlls = true;
202 return E_FAIL;
203 }
204 loadedDlls.AppendElement(dll);
205 }
206 sDLLsLoaded = true;
207
208 return S_OK;
209 }
210
211 #define ENSURE_FUNCTION_PTR_HELPER(FunctionType, FunctionName, DLL) \
212 static FunctionType FunctionName##Ptr = nullptr; \
213 if (!FunctionName##Ptr) { \
214 FunctionName##Ptr = (FunctionType)GetProcAddress( \
215 GetModuleHandleW(L## #DLL), #FunctionName); \
216 if (!FunctionName##Ptr) { \
217 NS_WARNING("Failed to get GetProcAddress of " #FunctionName \
218 " from " #DLL); \
219 return E_FAIL; \
220 } \
221 }
222
223 #define ENSURE_FUNCTION_PTR(FunctionName, DLL) \
224 ENSURE_FUNCTION_PTR_HELPER(decltype(::FunctionName)*, FunctionName, DLL)
225
226 #define ENSURE_FUNCTION_PTR_(FunctionName, DLL) \
227 ENSURE_FUNCTION_PTR_HELPER(FunctionName##Ptr_t, FunctionName, DLL)
228
229 #define DECL_FUNCTION_PTR(FunctionName, ...) \
230 typedef HRESULT(STDMETHODCALLTYPE* FunctionName##Ptr_t)(__VA_ARGS__)
231
232 HRESULT
MFStartup()233 MFStartup() {
234 if (IsWin7AndPre2000Compatible()) {
235 /*
236 * Specific exclude the usage of WMF on Win 7 with compatibility mode
237 * prior to Win 2000 as we may crash while trying to startup WMF.
238 * Using GetVersionEx API which takes compatibility mode into account.
239 * See Bug 1279171.
240 */
241 return E_FAIL;
242 }
243
244 HRESULT hr = LoadDLLs();
245 if (FAILED(hr)) {
246 return hr;
247 }
248
249 const int MF_WIN7_VERSION = (0x0002 << 16 | MF_API_VERSION);
250
251 // decltype is unusable for functions having default parameters
252 DECL_FUNCTION_PTR(MFStartup, ULONG, DWORD);
253 ENSURE_FUNCTION_PTR_(MFStartup, Mfplat.dll)
254
255 hr = E_FAIL;
256 mozilla::mscom::EnsureMTA(
257 [&]() -> void { hr = MFStartupPtr(MF_WIN7_VERSION, MFSTARTUP_FULL); });
258 return hr;
259 }
260
261 HRESULT
MFShutdown()262 MFShutdown() {
263 ENSURE_FUNCTION_PTR(MFShutdown, Mfplat.dll)
264 HRESULT hr = E_FAIL;
265 mozilla::mscom::EnsureMTA([&]() -> void { hr = (MFShutdownPtr)(); });
266 return hr;
267 }
268
269 HRESULT
MFCreateMediaType(IMFMediaType ** aOutMFType)270 MFCreateMediaType(IMFMediaType** aOutMFType) {
271 ENSURE_FUNCTION_PTR(MFCreateMediaType, Mfplat.dll)
272 return (MFCreateMediaTypePtr)(aOutMFType);
273 }
274
275 HRESULT
MFGetStrideForBitmapInfoHeader(DWORD aFormat,DWORD aWidth,LONG * aOutStride)276 MFGetStrideForBitmapInfoHeader(DWORD aFormat, DWORD aWidth, LONG* aOutStride) {
277 ENSURE_FUNCTION_PTR(MFGetStrideForBitmapInfoHeader, evr.dll)
278 return (MFGetStrideForBitmapInfoHeaderPtr)(aFormat, aWidth, aOutStride);
279 }
280
MFGetService(IUnknown * punkObject,REFGUID guidService,REFIID riid,LPVOID * ppvObject)281 HRESULT MFGetService(IUnknown* punkObject, REFGUID guidService, REFIID riid,
282 LPVOID* ppvObject) {
283 ENSURE_FUNCTION_PTR(MFGetService, mf.dll)
284 return (MFGetServicePtr)(punkObject, guidService, riid, ppvObject);
285 }
286
287 HRESULT
DXVA2CreateDirect3DDeviceManager9(UINT * pResetToken,IDirect3DDeviceManager9 ** ppDXVAManager)288 DXVA2CreateDirect3DDeviceManager9(UINT* pResetToken,
289 IDirect3DDeviceManager9** ppDXVAManager) {
290 ENSURE_FUNCTION_PTR(DXVA2CreateDirect3DDeviceManager9, dxva2.dll)
291 return (DXVA2CreateDirect3DDeviceManager9Ptr)(pResetToken, ppDXVAManager);
292 }
293
294 HRESULT
MFCreateSample(IMFSample ** ppIMFSample)295 MFCreateSample(IMFSample** ppIMFSample) {
296 ENSURE_FUNCTION_PTR(MFCreateSample, mfplat.dll)
297 return (MFCreateSamplePtr)(ppIMFSample);
298 }
299
300 HRESULT
MFCreateAlignedMemoryBuffer(DWORD cbMaxLength,DWORD fAlignmentFlags,IMFMediaBuffer ** ppBuffer)301 MFCreateAlignedMemoryBuffer(DWORD cbMaxLength, DWORD fAlignmentFlags,
302 IMFMediaBuffer** ppBuffer) {
303 ENSURE_FUNCTION_PTR(MFCreateAlignedMemoryBuffer, mfplat.dll)
304 return (MFCreateAlignedMemoryBufferPtr)(cbMaxLength, fAlignmentFlags,
305 ppBuffer);
306 }
307
308 HRESULT
MFCreateDXGIDeviceManager(UINT * pResetToken,IMFDXGIDeviceManager ** ppDXVAManager)309 MFCreateDXGIDeviceManager(UINT* pResetToken,
310 IMFDXGIDeviceManager** ppDXVAManager) {
311 ENSURE_FUNCTION_PTR(MFCreateDXGIDeviceManager, mfplat.dll)
312 return (MFCreateDXGIDeviceManagerPtr)(pResetToken, ppDXVAManager);
313 }
314
315 HRESULT
MFCreateDXGISurfaceBuffer(REFIID riid,IUnknown * punkSurface,UINT uSubresourceIndex,BOOL fButtomUpWhenLinear,IMFMediaBuffer ** ppBuffer)316 MFCreateDXGISurfaceBuffer(REFIID riid, IUnknown* punkSurface,
317 UINT uSubresourceIndex, BOOL fButtomUpWhenLinear,
318 IMFMediaBuffer** ppBuffer) {
319 ENSURE_FUNCTION_PTR(MFCreateDXGISurfaceBuffer, mfplat.dll)
320 return (MFCreateDXGISurfaceBufferPtr)(riid, punkSurface, uSubresourceIndex,
321 fButtomUpWhenLinear, ppBuffer);
322 }
323
324 HRESULT
MFTEnumEx(GUID guidCategory,UINT32 Flags,const MFT_REGISTER_TYPE_INFO * pInputType,const MFT_REGISTER_TYPE_INFO * pOutputType,IMFActivate *** pppMFTActivate,UINT32 * pnumMFTActivate)325 MFTEnumEx(GUID guidCategory, UINT32 Flags,
326 const MFT_REGISTER_TYPE_INFO* pInputType,
327 const MFT_REGISTER_TYPE_INFO* pOutputType,
328 IMFActivate*** pppMFTActivate, UINT32* pnumMFTActivate) {
329 ENSURE_FUNCTION_PTR(MFTEnumEx, mfplat.dll)
330 return (MFTEnumExPtr)(guidCategory, Flags, pInputType, pOutputType,
331 pppMFTActivate, pnumMFTActivate);
332 }
333
MFTGetInfo(CLSID clsidMFT,LPWSTR * pszName,MFT_REGISTER_TYPE_INFO ** ppInputTypes,UINT32 * pcInputTypes,MFT_REGISTER_TYPE_INFO ** ppOutputTypes,UINT32 * pcOutputTypes,IMFAttributes ** ppAttributes)334 HRESULT MFTGetInfo(CLSID clsidMFT, LPWSTR* pszName,
335 MFT_REGISTER_TYPE_INFO** ppInputTypes, UINT32* pcInputTypes,
336 MFT_REGISTER_TYPE_INFO** ppOutputTypes,
337 UINT32* pcOutputTypes, IMFAttributes** ppAttributes) {
338 ENSURE_FUNCTION_PTR(MFTGetInfo, mfplat.dll)
339 return (MFTGetInfoPtr)(clsidMFT, pszName, ppInputTypes, pcInputTypes,
340 ppOutputTypes, pcOutputTypes, ppAttributes);
341 }
342
343 } // end namespace wmf
344 } // end namespace mozilla
345