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 YUVColorSpace GetYUVColorSpace(IMFMediaType* aType) {
69   UINT32 yuvColorMatrix;
70   HRESULT hr = aType->GetUINT32(MF_MT_YUV_MATRIX, &yuvColorMatrix);
71   NS_ENSURE_TRUE(SUCCEEDED(hr), YUVColorSpace::BT601);
72 
73   switch (yuvColorMatrix) {
74     case MFVideoTransferMatrix_BT709:
75       return YUVColorSpace::BT709;
76     case MFVideoTransferMatrix_BT601:
77     default:
78       return YUVColorSpace::BT601;
79   }
80 }
81 
MFOffsetToInt32(const MFOffset & aOffset)82 int32_t MFOffsetToInt32(const MFOffset& aOffset) {
83   return int32_t(aOffset.value + (aOffset.fract / 65536.0f));
84 }
85 
GetSampleDuration(IMFSample * aSample)86 TimeUnit GetSampleDuration(IMFSample* aSample) {
87   NS_ENSURE_TRUE(aSample, TimeUnit::Invalid());
88   int64_t duration = 0;
89   aSample->GetSampleDuration(&duration);
90   return TimeUnit::FromMicroseconds(HNsToUsecs(duration));
91 }
92 
GetSampleTime(IMFSample * aSample)93 TimeUnit GetSampleTime(IMFSample* aSample) {
94   NS_ENSURE_TRUE(aSample, TimeUnit::Invalid());
95   LONGLONG timestampHns = 0;
96   HRESULT hr = aSample->GetSampleTime(&timestampHns);
97   NS_ENSURE_TRUE(SUCCEEDED(hr), TimeUnit::Invalid());
98   return TimeUnit::FromMicroseconds(HNsToUsecs(timestampHns));
99 }
100 
101 // Gets the sub-region of the video frame that should be displayed.
102 // See:
103 // http://msdn.microsoft.com/en-us/library/windows/desktop/bb530115(v=vs.85).aspx
104 HRESULT
GetPictureRegion(IMFMediaType * aMediaType,gfx::IntRect & aOutPictureRegion)105 GetPictureRegion(IMFMediaType* aMediaType, gfx::IntRect& aOutPictureRegion) {
106   // Determine if "pan and scan" is enabled for this media. If it is, we
107   // only display a region of the video frame, not the entire frame.
108   BOOL panScan =
109       MFGetAttributeUINT32(aMediaType, MF_MT_PAN_SCAN_ENABLED, FALSE);
110 
111   // If pan and scan mode is enabled. Try to get the display region.
112   HRESULT hr = E_FAIL;
113   MFVideoArea videoArea;
114   memset(&videoArea, 0, sizeof(MFVideoArea));
115   if (panScan) {
116     hr = aMediaType->GetBlob(MF_MT_PAN_SCAN_APERTURE, (UINT8*)&videoArea,
117                              sizeof(MFVideoArea), nullptr);
118   }
119 
120   // If we're not in pan-and-scan mode, or the pan-and-scan region is not set,
121   // check for a minimimum display aperture.
122   if (!panScan || hr == MF_E_ATTRIBUTENOTFOUND) {
123     hr = aMediaType->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE, (UINT8*)&videoArea,
124                              sizeof(MFVideoArea), nullptr);
125   }
126 
127   if (hr == MF_E_ATTRIBUTENOTFOUND) {
128     // Minimum display aperture is not set, for "backward compatibility with
129     // some components", check for a geometric aperture.
130     hr = aMediaType->GetBlob(MF_MT_GEOMETRIC_APERTURE, (UINT8*)&videoArea,
131                              sizeof(MFVideoArea), nullptr);
132   }
133 
134   if (SUCCEEDED(hr)) {
135     // The media specified a picture region, return it.
136     aOutPictureRegion = gfx::IntRect(MFOffsetToInt32(videoArea.OffsetX),
137                                      MFOffsetToInt32(videoArea.OffsetY),
138                                      videoArea.Area.cx, videoArea.Area.cy);
139     return S_OK;
140   }
141 
142   // No picture region defined, fall back to using the entire video area.
143   UINT32 width = 0, height = 0;
144   hr = MFGetAttributeSize(aMediaType, MF_MT_FRAME_SIZE, &width, &height);
145   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
146   NS_ENSURE_TRUE(width <= MAX_VIDEO_WIDTH, E_FAIL);
147   NS_ENSURE_TRUE(height <= MAX_VIDEO_HEIGHT, E_FAIL);
148 
149   aOutPictureRegion = gfx::IntRect(0, 0, width, height);
150   return S_OK;
151 }
152 
GetProgramW6432Path()153 nsString GetProgramW6432Path() {
154   char* programPath = PR_GetEnvSecure("ProgramW6432");
155   if (!programPath) {
156     programPath = PR_GetEnvSecure("ProgramFiles");
157   }
158 
159   if (!programPath) {
160     return NS_LITERAL_STRING("C:\\Program Files");
161   }
162   return NS_ConvertUTF8toUTF16(programPath);
163 }
164 
165 namespace wmf {
166 
167 static const wchar_t* sDLLs[] = {
168     L"mfplat.dll",
169     L"mf.dll",
170     L"dxva2.dll",
171     L"evr.dll",
172 };
173 
174 HRESULT
LoadDLLs()175 LoadDLLs() {
176   static bool sDLLsLoaded = false;
177   static bool sFailedToLoadDlls = false;
178 
179   if (sDLLsLoaded) {
180     return S_OK;
181   }
182   if (sFailedToLoadDlls) {
183     return E_FAIL;
184   }
185 
186   // Try to load all the required DLLs. If we fail to load any dll,
187   // unload the dlls we succeeded in loading.
188   nsTArray<const wchar_t*> loadedDlls;
189   for (const wchar_t* dll : sDLLs) {
190     if (!LoadLibrarySystem32(dll)) {
191       NS_WARNING("Failed to load WMF DLLs");
192       for (const wchar_t* loadedDll : loadedDlls) {
193         FreeLibrary(GetModuleHandleW(loadedDll));
194       }
195       sFailedToLoadDlls = true;
196       return E_FAIL;
197     }
198     loadedDlls.AppendElement(dll);
199   }
200   sDLLsLoaded = true;
201 
202   return S_OK;
203 }
204 
205 #define ENSURE_FUNCTION_PTR_HELPER(FunctionType, FunctionName, DLL) \
206   static FunctionType FunctionName##Ptr = nullptr;                  \
207   if (!FunctionName##Ptr) {                                         \
208     FunctionName##Ptr = (FunctionType)GetProcAddress(               \
209         GetModuleHandleW(L## #DLL), #FunctionName);                 \
210     if (!FunctionName##Ptr) {                                       \
211       NS_WARNING("Failed to get GetProcAddress of " #FunctionName   \
212                  " from " #DLL);                                    \
213       return E_FAIL;                                                \
214     }                                                               \
215   }
216 
217 #define ENSURE_FUNCTION_PTR(FunctionName, DLL) \
218   ENSURE_FUNCTION_PTR_HELPER(decltype(::FunctionName)*, FunctionName, DLL)
219 
220 #define ENSURE_FUNCTION_PTR_(FunctionName, DLL) \
221   ENSURE_FUNCTION_PTR_HELPER(FunctionName##Ptr_t, FunctionName, DLL)
222 
223 #define DECL_FUNCTION_PTR(FunctionName, ...) \
224   typedef HRESULT(STDMETHODCALLTYPE* FunctionName##Ptr_t)(__VA_ARGS__)
225 
226 HRESULT
MFStartup()227 MFStartup() {
228   if (IsWin7AndPre2000Compatible()) {
229     /*
230      * Specific exclude the usage of WMF on Win 7 with compatibility mode
231      * prior to Win 2000 as we may crash while trying to startup WMF.
232      * Using GetVersionEx API which takes compatibility mode into account.
233      * See Bug 1279171.
234      */
235     return E_FAIL;
236   }
237 
238   HRESULT hr = LoadDLLs();
239   if (FAILED(hr)) {
240     return hr;
241   }
242 
243   const int MF_WIN7_VERSION = (0x0002 << 16 | MF_API_VERSION);
244 
245   // decltype is unusable for functions having default parameters
246   DECL_FUNCTION_PTR(MFStartup, ULONG, DWORD);
247   ENSURE_FUNCTION_PTR_(MFStartup, Mfplat.dll)
248 
249   hr = E_FAIL;
250   mozilla::mscom::EnsureMTA(
251       [&]() -> void { hr = MFStartupPtr(MF_WIN7_VERSION, MFSTARTUP_FULL); });
252   return hr;
253 }
254 
255 HRESULT
MFShutdown()256 MFShutdown() {
257   ENSURE_FUNCTION_PTR(MFShutdown, Mfplat.dll)
258   HRESULT hr = E_FAIL;
259   mozilla::mscom::EnsureMTA([&]() -> void { hr = (MFShutdownPtr)(); });
260   return hr;
261 }
262 
263 HRESULT
MFCreateMediaType(IMFMediaType ** aOutMFType)264 MFCreateMediaType(IMFMediaType** aOutMFType) {
265   ENSURE_FUNCTION_PTR(MFCreateMediaType, Mfplat.dll)
266   return (MFCreateMediaTypePtr)(aOutMFType);
267 }
268 
269 HRESULT
MFGetStrideForBitmapInfoHeader(DWORD aFormat,DWORD aWidth,LONG * aOutStride)270 MFGetStrideForBitmapInfoHeader(DWORD aFormat, DWORD aWidth, LONG* aOutStride) {
271   ENSURE_FUNCTION_PTR(MFGetStrideForBitmapInfoHeader, evr.dll)
272   return (MFGetStrideForBitmapInfoHeaderPtr)(aFormat, aWidth, aOutStride);
273 }
274 
MFGetService(IUnknown * punkObject,REFGUID guidService,REFIID riid,LPVOID * ppvObject)275 HRESULT MFGetService(IUnknown* punkObject, REFGUID guidService, REFIID riid,
276                      LPVOID* ppvObject) {
277   ENSURE_FUNCTION_PTR(MFGetService, mf.dll)
278   return (MFGetServicePtr)(punkObject, guidService, riid, ppvObject);
279 }
280 
281 HRESULT
DXVA2CreateDirect3DDeviceManager9(UINT * pResetToken,IDirect3DDeviceManager9 ** ppDXVAManager)282 DXVA2CreateDirect3DDeviceManager9(UINT* pResetToken,
283                                   IDirect3DDeviceManager9** ppDXVAManager) {
284   ENSURE_FUNCTION_PTR(DXVA2CreateDirect3DDeviceManager9, dxva2.dll)
285   return (DXVA2CreateDirect3DDeviceManager9Ptr)(pResetToken, ppDXVAManager);
286 }
287 
288 HRESULT
MFCreateSample(IMFSample ** ppIMFSample)289 MFCreateSample(IMFSample** ppIMFSample) {
290   ENSURE_FUNCTION_PTR(MFCreateSample, mfplat.dll)
291   return (MFCreateSamplePtr)(ppIMFSample);
292 }
293 
294 HRESULT
MFCreateAlignedMemoryBuffer(DWORD cbMaxLength,DWORD fAlignmentFlags,IMFMediaBuffer ** ppBuffer)295 MFCreateAlignedMemoryBuffer(DWORD cbMaxLength, DWORD fAlignmentFlags,
296                             IMFMediaBuffer** ppBuffer) {
297   ENSURE_FUNCTION_PTR(MFCreateAlignedMemoryBuffer, mfplat.dll)
298   return (MFCreateAlignedMemoryBufferPtr)(cbMaxLength, fAlignmentFlags,
299                                           ppBuffer);
300 }
301 
302 HRESULT
MFCreateDXGIDeviceManager(UINT * pResetToken,IMFDXGIDeviceManager ** ppDXVAManager)303 MFCreateDXGIDeviceManager(UINT* pResetToken,
304                           IMFDXGIDeviceManager** ppDXVAManager) {
305   ENSURE_FUNCTION_PTR(MFCreateDXGIDeviceManager, mfplat.dll)
306   return (MFCreateDXGIDeviceManagerPtr)(pResetToken, ppDXVAManager);
307 }
308 
309 HRESULT
MFCreateDXGISurfaceBuffer(REFIID riid,IUnknown * punkSurface,UINT uSubresourceIndex,BOOL fButtomUpWhenLinear,IMFMediaBuffer ** ppBuffer)310 MFCreateDXGISurfaceBuffer(REFIID riid, IUnknown* punkSurface,
311                           UINT uSubresourceIndex, BOOL fButtomUpWhenLinear,
312                           IMFMediaBuffer** ppBuffer) {
313   ENSURE_FUNCTION_PTR(MFCreateDXGISurfaceBuffer, mfplat.dll)
314   return (MFCreateDXGISurfaceBufferPtr)(riid, punkSurface, uSubresourceIndex,
315                                         fButtomUpWhenLinear, ppBuffer);
316 }
317 
318 }  // end namespace wmf
319 }  // end namespace mozilla
320