1 /*
2  * Copyright 2013, Mozilla Foundation and contributors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "WMFUtils.h"
18 #include "ClearKeyUtils.h"
19 #include <versionhelpers.h>
20 
21 #include <algorithm>
22 #include <stdio.h>
23 
24 #define INITGUID
25 #include <guiddef.h>
26 
27 #pragma comment(lib, "mfuuid.lib")
28 #pragma comment(lib, "wmcodecdspuuid")
29 
LOG(const char * format,...)30 void LOG(const char* format, ...)
31 {
32 #ifdef WMF_DECODER_LOG
33   va_list args;
34   va_start(args, format);
35   vprintf(format, args);
36 #endif
37 }
38 
39 #ifdef WMF_MUST_DEFINE_AAC_MFT_CLSID
40 // Some SDK versions don't define the AAC decoder CLSID.
41 // {32D186A7-218F-4C75-8876-DD77273A8999}
42 DEFINE_GUID(CLSID_CMSAACDecMFT, 0x32D186A7, 0x218F, 0x4C75, 0x88, 0x76, 0xDD, 0x77, 0x27, 0x3A, 0x89, 0x99);
43 #endif
44 
45 DEFINE_GUID(CLSID_CMSH264DecMFT, 0x62CE7E72, 0x4C71, 0x4d20, 0xB1, 0x5D, 0x45, 0x28, 0x31, 0xA8, 0x7D, 0x9D);
46 
47 namespace wmf {
48 
49 
50 #define MFPLAT_FUNC(_func, _dllname) \
51   decltype(::_func)* _func;
52 #include "WMFSymbols.h"
53 #undef MFPLAT_FUNC
54 
55 static bool
LinkMfplat()56 LinkMfplat()
57 {
58   static bool sInitDone = false;
59   static bool sInitOk = false;
60   if (!sInitDone) {
61     sInitDone = true;
62     HMODULE handle;
63 
64 #define MFPLAT_FUNC(_func, _dllname) \
65       handle = GetModuleHandleA(_dllname); \
66       if (!(_func = (decltype(_func))(GetProcAddress(handle, #_func)))) { \
67         return false; \
68       }
69 
70 #include "WMFSymbols.h"
71 #undef MFPLAT_FUNC
72     sInitOk = true;
73   }
74   return sInitOk;
75 }
76 
77 const char*
WMFDecoderDllNameFor(CodecType aCodec)78 WMFDecoderDllNameFor(CodecType aCodec)
79 {
80   if (aCodec == H264) {
81     // For H.264 decoding, we need msmpeg2vdec.dll on Win 7 & 8,
82     // and mfh264dec.dll on Vista.
83     if (IsWindows7OrGreater()) {
84       return "msmpeg2vdec.dll";
85     } else {
86       return "mfh264dec.dll";
87     }
88   } else if (aCodec == AAC) {
89     // For AAC decoding, we need to use msauddecmft.dll on Win8,
90     // msmpeg2adec.dll on Win7, and msheaacdec.dll on Vista.
91     if (IsWindows8OrGreater()) {
92       return "msauddecmft.dll";
93     } else if (IsWindows7OrGreater()) {
94       return "msmpeg2adec.dll";
95     } else {
96       return "mfheaacdec.dll";
97     }
98   } else {
99     return "";
100   }
101 }
102 
103 
104 bool
EnsureLibs()105 EnsureLibs()
106 {
107   static bool sInitDone = false;
108   static bool sInitOk = false;
109   if (!sInitDone) {
110     sInitOk = LinkMfplat() &&
111               !!GetModuleHandleA(WMFDecoderDllNameFor(AAC)) &&
112               !!GetModuleHandleA(WMFDecoderDllNameFor(H264));
113     sInitDone = true;
114   }
115   return sInitOk;
116 }
117 
118 int32_t
MFOffsetToInt32(const MFOffset & aOffset)119 MFOffsetToInt32(const MFOffset& aOffset)
120 {
121   return int32_t(aOffset.value + (aOffset.fract / 65536.0f));
122 }
123 
124 // Gets the sub-region of the video frame that should be displayed.
125 // See: http://msdn.microsoft.com/en-us/library/windows/desktop/bb530115(v=vs.85).aspx
126 HRESULT
GetPictureRegion(IMFMediaType * aMediaType,IntRect & aOutPictureRegion)127 GetPictureRegion(IMFMediaType* aMediaType, IntRect& aOutPictureRegion)
128 {
129   // Determine if "pan and scan" is enabled for this media. If it is, we
130   // only display a region of the video frame, not the entire frame.
131   BOOL panScan = MFGetAttributeUINT32(aMediaType, MF_MT_PAN_SCAN_ENABLED, FALSE);
132 
133   // If pan and scan mode is enabled. Try to get the display region.
134   HRESULT hr = E_FAIL;
135   MFVideoArea videoArea;
136   memset(&videoArea, 0, sizeof(MFVideoArea));
137   if (panScan) {
138     hr = aMediaType->GetBlob(MF_MT_PAN_SCAN_APERTURE,
139                              (UINT8*)&videoArea,
140                              sizeof(MFVideoArea),
141                              NULL);
142   }
143 
144   // If we're not in pan-and-scan mode, or the pan-and-scan region is not set,
145   // check for a minimimum display aperture.
146   if (!panScan || hr == MF_E_ATTRIBUTENOTFOUND) {
147     hr = aMediaType->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE,
148                              (UINT8*)&videoArea,
149                              sizeof(MFVideoArea),
150                              NULL);
151   }
152 
153   if (hr == MF_E_ATTRIBUTENOTFOUND) {
154     // Minimum display aperture is not set, for "backward compatibility with
155     // some components", check for a geometric aperture.
156     hr = aMediaType->GetBlob(MF_MT_GEOMETRIC_APERTURE,
157                              (UINT8*)&videoArea,
158                              sizeof(MFVideoArea),
159                              NULL);
160   }
161 
162   if (SUCCEEDED(hr)) {
163     // The media specified a picture region, return it.
164     IntRect picture = IntRect(MFOffsetToInt32(videoArea.OffsetX),
165                               MFOffsetToInt32(videoArea.OffsetY),
166                               videoArea.Area.cx,
167                               videoArea.Area.cy);
168     ENSURE(picture.width <= mozilla::MAX_VIDEO_WIDTH, E_FAIL);
169     ENSURE(picture.height <= mozilla::MAX_VIDEO_HEIGHT, E_FAIL);
170     aOutPictureRegion = picture;
171     return S_OK;
172   }
173 
174   // No picture region defined, fall back to using the entire video area.
175   UINT32 width = 0, height = 0;
176   hr = MFGetAttributeSize(aMediaType, MF_MT_FRAME_SIZE, &width, &height);
177   ENSURE(SUCCEEDED(hr), hr);
178   ENSURE(width <= mozilla::MAX_VIDEO_WIDTH, E_FAIL);
179   ENSURE(height <= mozilla::MAX_VIDEO_HEIGHT, E_FAIL);
180   aOutPictureRegion = IntRect(0, 0, width, height);
181   return S_OK;
182 }
183 
184 
185 HRESULT
GetDefaultStride(IMFMediaType * aType,uint32_t * aOutStride)186 GetDefaultStride(IMFMediaType *aType, uint32_t* aOutStride)
187 {
188   // Try to get the default stride from the media type.
189   UINT32 stride = 0;
190   HRESULT hr = aType->GetUINT32(MF_MT_DEFAULT_STRIDE, &stride);
191   if (SUCCEEDED(hr)) {
192     ENSURE(stride <= mozilla::MAX_VIDEO_WIDTH, E_FAIL);
193     *aOutStride = stride;
194     return S_OK;
195   }
196 
197   // Stride attribute not set, calculate it.
198   GUID subtype = GUID_NULL;
199   uint32_t width = 0;
200   uint32_t height = 0;
201 
202   hr = aType->GetGUID(MF_MT_SUBTYPE, &subtype);
203   ENSURE(SUCCEEDED(hr), hr);
204 
205   hr = MFGetAttributeSize(aType, MF_MT_FRAME_SIZE, &width, &height);
206   ENSURE(SUCCEEDED(hr), hr);
207   ENSURE(width <= mozilla::MAX_VIDEO_WIDTH, E_FAIL);
208   ENSURE(height <= mozilla::MAX_VIDEO_HEIGHT, E_FAIL);
209 
210   LONG lstride = 0;
211   hr = MFGetStrideForBitmapInfoHeader(subtype.Data1, width, &lstride);
212   ENSURE(SUCCEEDED(hr), hr);
213   ENSURE(lstride <= mozilla::MAX_VIDEO_WIDTH, E_FAIL);
214   ENSURE(lstride >= 0, E_FAIL);
215   *aOutStride = lstride;
216 
217   return hr;
218 }
219 
dump(const uint8_t * data,uint32_t len,const char * filename)220 void dump(const uint8_t* data, uint32_t len, const char* filename)
221 {
222   FILE* f = 0;
223   fopen_s(&f, filename, "wb");
224   fwrite(data, len, 1, f);
225   fclose(f);
226 }
227 
228 HRESULT
CreateMFT(const CLSID & clsid,const char * aDllName,CComPtr<IMFTransform> & aOutMFT)229 CreateMFT(const CLSID& clsid,
230           const char* aDllName,
231           CComPtr<IMFTransform>& aOutMFT)
232 {
233   HMODULE module = ::GetModuleHandleA(aDllName);
234   if (!module) {
235     LOG("Failed to get %S\n", aDllName);
236     return E_FAIL;
237   }
238 
239   typedef HRESULT (WINAPI* DllGetClassObjectFnPtr)(const CLSID& clsid,
240                                                    const IID& iid,
241                                                    void** object);
242 
243   DllGetClassObjectFnPtr GetClassObjPtr =
244     reinterpret_cast<DllGetClassObjectFnPtr>(GetProcAddress(module, "DllGetClassObject"));
245   if (!GetClassObjPtr) {
246     LOG("Failed to get DllGetClassObject\n");
247     return E_FAIL;
248   }
249 
250   CComPtr<IClassFactory> classFactory;
251   HRESULT hr = GetClassObjPtr(clsid,
252                               __uuidof(IClassFactory),
253                               reinterpret_cast<void**>(static_cast<IClassFactory**>(&classFactory)));
254   if (FAILED(hr)) {
255     LOG("Failed to get H264 IClassFactory\n");
256     return E_FAIL;
257   }
258 
259   hr = classFactory->CreateInstance(NULL,
260                                     __uuidof(IMFTransform),
261                                     reinterpret_cast<void**>(static_cast<IMFTransform**>(&aOutMFT)));
262   if (FAILED(hr)) {
263     LOG("Failed to get create MFT\n");
264     return E_FAIL;
265   }
266 
267   return S_OK;
268 }
269 
270 int32_t
GetNumThreads(int32_t aCoreCount)271 GetNumThreads(int32_t aCoreCount)
272 {
273   return aCoreCount > 4 ? -1 : (std::max)(aCoreCount - 1, 1);
274 }
275 
276 } // namespace
277