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(&timestampHns);
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