1 /*
2  * This file is part of FFmpeg.
3  *
4  * FFmpeg is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * FFmpeg is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with FFmpeg; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #define COBJMACROS
20 #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0602
21 #undef _WIN32_WINNT
22 #define _WIN32_WINNT 0x0602
23 #endif
24 
25 #include "mf_utils.h"
26 #include "libavutil/pixdesc.h"
27 
ff_MFGetAttributeSize(IMFAttributes * pattr,REFGUID guid,UINT32 * pw,UINT32 * ph)28 HRESULT ff_MFGetAttributeSize(IMFAttributes *pattr, REFGUID guid,
29                               UINT32 *pw, UINT32 *ph)
30 {
31     UINT64 t;
32     HRESULT hr = IMFAttributes_GetUINT64(pattr, guid, &t);
33     if (!FAILED(hr)) {
34         *pw = t >> 32;
35         *ph = (UINT32)t;
36     }
37     return hr;
38 }
39 
ff_MFSetAttributeSize(IMFAttributes * pattr,REFGUID guid,UINT32 uw,UINT32 uh)40 HRESULT ff_MFSetAttributeSize(IMFAttributes *pattr, REFGUID guid,
41                               UINT32 uw, UINT32 uh)
42 {
43     UINT64 t = (((UINT64)uw) << 32) | uh;
44     return IMFAttributes_SetUINT64(pattr, guid, t);
45 }
46 
47 #define ff_MFSetAttributeRatio ff_MFSetAttributeSize
48 #define ff_MFGetAttributeRatio ff_MFGetAttributeSize
49 
50 // MFTEnumEx was missing from mingw-w64's mfplat import library until
51 // mingw-w64 v6.0.0, thus wrap it and load it using GetProcAddress.
52 // It's also missing in Windows Vista's mfplat.dll.
ff_MFTEnumEx(GUID guidCategory,UINT32 Flags,const MFT_REGISTER_TYPE_INFO * pInputType,const MFT_REGISTER_TYPE_INFO * pOutputType,IMFActivate *** pppMFTActivate,UINT32 * pnumMFTActivate)53 HRESULT ff_MFTEnumEx(GUID guidCategory, UINT32 Flags,
54                      const MFT_REGISTER_TYPE_INFO *pInputType,
55                      const MFT_REGISTER_TYPE_INFO *pOutputType,
56                      IMFActivate ***pppMFTActivate, UINT32 *pnumMFTActivate)
57 {
58     HRESULT (WINAPI *MFTEnumEx_ptr)(GUID guidCategory, UINT32 Flags,
59                                     const MFT_REGISTER_TYPE_INFO *pInputType,
60                                     const MFT_REGISTER_TYPE_INFO *pOutputType,
61                                     IMFActivate ***pppMFTActivate,
62                                     UINT32 *pnumMFTActivate) = NULL;
63 #if !HAVE_UWP
64     HANDLE lib = GetModuleHandleW(L"mfplat.dll");
65     if (lib)
66         MFTEnumEx_ptr = (void *)GetProcAddress(lib, "MFTEnumEx");
67 #else
68     // In UWP (which lacks GetModuleHandle), just link directly against
69     // the functions - this requires building with new/complete enough
70     // import libraries.
71     MFTEnumEx_ptr = MFTEnumEx;
72 #endif
73     if (!MFTEnumEx_ptr)
74         return E_FAIL;
75     return MFTEnumEx_ptr(guidCategory,
76                          Flags,
77                          pInputType,
78                          pOutputType,
79                          pppMFTActivate,
80                          pnumMFTActivate);
81 }
82 
ff_hr_str_buf(char * buf,size_t size,HRESULT hr)83 char *ff_hr_str_buf(char *buf, size_t size, HRESULT hr)
84 {
85 #define HR(x) case x: return (char *) # x;
86     switch (hr) {
87     HR(S_OK)
88     HR(E_UNEXPECTED)
89     HR(MF_E_INVALIDMEDIATYPE)
90     HR(MF_E_INVALIDSTREAMNUMBER)
91     HR(MF_E_INVALIDTYPE)
92     HR(MF_E_TRANSFORM_CANNOT_CHANGE_MEDIATYPE_WHILE_PROCESSING)
93     HR(MF_E_TRANSFORM_TYPE_NOT_SET)
94     HR(MF_E_UNSUPPORTED_D3D_TYPE)
95     HR(MF_E_TRANSFORM_NEED_MORE_INPUT)
96     HR(MF_E_TRANSFORM_STREAM_CHANGE)
97     HR(MF_E_NOTACCEPTING)
98     HR(MF_E_NO_SAMPLE_TIMESTAMP)
99     HR(MF_E_NO_SAMPLE_DURATION)
100 #undef HR
101     }
102     snprintf(buf, size, "%x", (unsigned)hr);
103     return buf;
104 }
105 
106 // If fill_data!=NULL, initialize the buffer and set the length. (This is a
107 // subtle but important difference: some decoders want CurrentLength==0 on
108 // provided output buffers.)
ff_create_memory_sample(void * fill_data,size_t size,size_t align)109 IMFSample *ff_create_memory_sample(void *fill_data, size_t size, size_t align)
110 {
111     HRESULT hr;
112     IMFSample *sample;
113     IMFMediaBuffer *buffer;
114 
115     hr = MFCreateSample(&sample);
116     if (FAILED(hr))
117         return NULL;
118 
119     align = FFMAX(align, 16); // 16 is "recommended", even if not required
120 
121     hr = MFCreateAlignedMemoryBuffer(size, align - 1, &buffer);
122     if (FAILED(hr))
123         return NULL;
124 
125     if (fill_data) {
126         BYTE *tmp;
127 
128         hr = IMFMediaBuffer_Lock(buffer, &tmp, NULL, NULL);
129         if (FAILED(hr)) {
130             IMFMediaBuffer_Release(buffer);
131             IMFSample_Release(sample);
132             return NULL;
133         }
134         memcpy(tmp, fill_data, size);
135 
136         IMFMediaBuffer_SetCurrentLength(buffer, size);
137         IMFMediaBuffer_Unlock(buffer);
138     }
139 
140     IMFSample_AddBuffer(sample, buffer);
141     IMFMediaBuffer_Release(buffer);
142 
143     return sample;
144 }
145 
ff_media_type_to_sample_fmt(IMFAttributes * type)146 enum AVSampleFormat ff_media_type_to_sample_fmt(IMFAttributes *type)
147 {
148     HRESULT hr;
149     UINT32 bits;
150     GUID subtype;
151 
152     hr = IMFAttributes_GetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &bits);
153     if (FAILED(hr))
154         return AV_SAMPLE_FMT_NONE;
155 
156     hr = IMFAttributes_GetGUID(type, &MF_MT_SUBTYPE, &subtype);
157     if (FAILED(hr))
158         return AV_SAMPLE_FMT_NONE;
159 
160     if (IsEqualGUID(&subtype, &MFAudioFormat_PCM)) {
161         switch (bits) {
162         case 8:  return AV_SAMPLE_FMT_U8;
163         case 16: return AV_SAMPLE_FMT_S16;
164         case 32: return AV_SAMPLE_FMT_S32;
165         }
166     } else if (IsEqualGUID(&subtype, &MFAudioFormat_Float)) {
167         switch (bits) {
168         case 32: return AV_SAMPLE_FMT_FLT;
169         case 64: return AV_SAMPLE_FMT_DBL;
170         }
171     }
172 
173     return AV_SAMPLE_FMT_NONE;
174 }
175 
176 struct mf_pix_fmt_entry {
177     const GUID *guid;
178     enum AVPixelFormat pix_fmt;
179 };
180 
181 static const struct mf_pix_fmt_entry mf_pix_fmts[] = {
182     {&MFVideoFormat_IYUV, AV_PIX_FMT_YUV420P},
183     {&MFVideoFormat_I420, AV_PIX_FMT_YUV420P},
184     {&MFVideoFormat_NV12, AV_PIX_FMT_NV12},
185     {&MFVideoFormat_P010, AV_PIX_FMT_P010},
186     {&MFVideoFormat_P016, AV_PIX_FMT_P010}, // not equal, but compatible
187     {&MFVideoFormat_YUY2, AV_PIX_FMT_YUYV422},
188 };
189 
ff_media_type_to_pix_fmt(IMFAttributes * type)190 enum AVPixelFormat ff_media_type_to_pix_fmt(IMFAttributes *type)
191 {
192     HRESULT hr;
193     GUID subtype;
194     int i;
195 
196     hr = IMFAttributes_GetGUID(type, &MF_MT_SUBTYPE, &subtype);
197     if (FAILED(hr))
198         return AV_PIX_FMT_NONE;
199 
200     for (i = 0; i < FF_ARRAY_ELEMS(mf_pix_fmts); i++) {
201         if (IsEqualGUID(&subtype, mf_pix_fmts[i].guid))
202             return mf_pix_fmts[i].pix_fmt;
203     }
204 
205     return AV_PIX_FMT_NONE;
206 }
207 
ff_pix_fmt_to_guid(enum AVPixelFormat pix_fmt)208 const GUID *ff_pix_fmt_to_guid(enum AVPixelFormat pix_fmt)
209 {
210     int i;
211 
212     for (i = 0; i < FF_ARRAY_ELEMS(mf_pix_fmts); i++) {
213         if (mf_pix_fmts[i].pix_fmt == pix_fmt)
214             return mf_pix_fmts[i].guid;
215     }
216 
217     return NULL;
218 }
219 
220 // If this GUID is of the form XXXXXXXX-0000-0010-8000-00AA00389B71, then
221 // extract the XXXXXXXX prefix as FourCC (oh the pain).
ff_fourcc_from_guid(const GUID * guid,uint32_t * out_fourcc)222 int ff_fourcc_from_guid(const GUID *guid, uint32_t *out_fourcc)
223 {
224     if (guid->Data2 == 0 && guid->Data3 == 0x0010 &&
225         guid->Data4[0] == 0x80 &&
226         guid->Data4[1] == 0x00 &&
227         guid->Data4[2] == 0x00 &&
228         guid->Data4[3] == 0xAA &&
229         guid->Data4[4] == 0x00 &&
230         guid->Data4[5] == 0x38 &&
231         guid->Data4[6] == 0x9B &&
232         guid->Data4[7] == 0x71) {
233         *out_fourcc = guid->Data1;
234         return 0;
235     }
236 
237     *out_fourcc = 0;
238     return AVERROR_UNKNOWN;
239 }
240 
241 struct GUID_Entry {
242     const GUID *guid;
243     const char *name;
244 };
245 
246 #define GUID_ENTRY(var) {&(var), # var}
247 
248 static struct GUID_Entry guid_names[] = {
249     GUID_ENTRY(MFT_FRIENDLY_NAME_Attribute),
250     GUID_ENTRY(MFT_TRANSFORM_CLSID_Attribute),
251     GUID_ENTRY(MFT_ENUM_HARDWARE_URL_Attribute),
252     GUID_ENTRY(MFT_CONNECTED_STREAM_ATTRIBUTE),
253     GUID_ENTRY(MFT_CONNECTED_TO_HW_STREAM),
254     GUID_ENTRY(MF_SA_D3D_AWARE),
255     GUID_ENTRY(ff_MF_SA_MINIMUM_OUTPUT_SAMPLE_COUNT),
256     GUID_ENTRY(ff_MF_SA_MINIMUM_OUTPUT_SAMPLE_COUNT_PROGRESSIVE),
257     GUID_ENTRY(ff_MF_SA_D3D11_BINDFLAGS),
258     GUID_ENTRY(ff_MF_SA_D3D11_USAGE),
259     GUID_ENTRY(ff_MF_SA_D3D11_AWARE),
260     GUID_ENTRY(ff_MF_SA_D3D11_SHARED),
261     GUID_ENTRY(ff_MF_SA_D3D11_SHARED_WITHOUT_MUTEX),
262     GUID_ENTRY(MF_MT_SUBTYPE),
263     GUID_ENTRY(MF_MT_MAJOR_TYPE),
264     GUID_ENTRY(MF_MT_AUDIO_SAMPLES_PER_SECOND),
265     GUID_ENTRY(MF_MT_AUDIO_NUM_CHANNELS),
266     GUID_ENTRY(MF_MT_AUDIO_CHANNEL_MASK),
267     GUID_ENTRY(MF_MT_FRAME_SIZE),
268     GUID_ENTRY(MF_MT_INTERLACE_MODE),
269     GUID_ENTRY(MF_MT_USER_DATA),
270     GUID_ENTRY(MF_MT_PIXEL_ASPECT_RATIO),
271     GUID_ENTRY(MFMediaType_Audio),
272     GUID_ENTRY(MFMediaType_Video),
273     GUID_ENTRY(MFAudioFormat_PCM),
274     GUID_ENTRY(MFAudioFormat_Float),
275     GUID_ENTRY(MFVideoFormat_H264),
276     GUID_ENTRY(MFVideoFormat_H264_ES),
277     GUID_ENTRY(ff_MFVideoFormat_HEVC),
278     GUID_ENTRY(ff_MFVideoFormat_HEVC_ES),
279     GUID_ENTRY(MFVideoFormat_MPEG2),
280     GUID_ENTRY(MFVideoFormat_MP43),
281     GUID_ENTRY(MFVideoFormat_MP4V),
282     GUID_ENTRY(MFVideoFormat_WMV1),
283     GUID_ENTRY(MFVideoFormat_WMV2),
284     GUID_ENTRY(MFVideoFormat_WMV3),
285     GUID_ENTRY(MFVideoFormat_WVC1),
286     GUID_ENTRY(MFAudioFormat_Dolby_AC3),
287     GUID_ENTRY(MFAudioFormat_Dolby_DDPlus),
288     GUID_ENTRY(MFAudioFormat_AAC),
289     GUID_ENTRY(MFAudioFormat_MP3),
290     GUID_ENTRY(MFAudioFormat_MSP1),
291     GUID_ENTRY(MFAudioFormat_WMAudioV8),
292     GUID_ENTRY(MFAudioFormat_WMAudioV9),
293     GUID_ENTRY(MFAudioFormat_WMAudio_Lossless),
294     GUID_ENTRY(MF_MT_ALL_SAMPLES_INDEPENDENT),
295     GUID_ENTRY(MF_MT_COMPRESSED),
296     GUID_ENTRY(MF_MT_FIXED_SIZE_SAMPLES),
297     GUID_ENTRY(MF_MT_SAMPLE_SIZE),
298     GUID_ENTRY(MF_MT_WRAPPED_TYPE),
299     GUID_ENTRY(MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION),
300     GUID_ENTRY(MF_MT_AAC_PAYLOAD_TYPE),
301     GUID_ENTRY(MF_MT_AUDIO_AVG_BYTES_PER_SECOND),
302     GUID_ENTRY(MF_MT_AUDIO_BITS_PER_SAMPLE),
303     GUID_ENTRY(MF_MT_AUDIO_BLOCK_ALIGNMENT),
304     GUID_ENTRY(MF_MT_AUDIO_CHANNEL_MASK),
305     GUID_ENTRY(MF_MT_AUDIO_FLOAT_SAMPLES_PER_SECOND),
306     GUID_ENTRY(MF_MT_AUDIO_FOLDDOWN_MATRIX),
307     GUID_ENTRY(MF_MT_AUDIO_NUM_CHANNELS),
308     GUID_ENTRY(MF_MT_AUDIO_PREFER_WAVEFORMATEX),
309     GUID_ENTRY(MF_MT_AUDIO_SAMPLES_PER_BLOCK),
310     GUID_ENTRY(MF_MT_AUDIO_SAMPLES_PER_SECOND),
311     GUID_ENTRY(MF_MT_AUDIO_VALID_BITS_PER_SAMPLE),
312     GUID_ENTRY(MF_MT_AUDIO_WMADRC_AVGREF),
313     GUID_ENTRY(MF_MT_AUDIO_WMADRC_AVGTARGET),
314     GUID_ENTRY(MF_MT_AUDIO_WMADRC_PEAKREF),
315     GUID_ENTRY(MF_MT_AUDIO_WMADRC_PEAKTARGET),
316     GUID_ENTRY(MF_MT_AVG_BIT_ERROR_RATE),
317     GUID_ENTRY(MF_MT_AVG_BITRATE),
318     GUID_ENTRY(MF_MT_DEFAULT_STRIDE),
319     GUID_ENTRY(MF_MT_DRM_FLAGS),
320     GUID_ENTRY(MF_MT_FRAME_RATE),
321     GUID_ENTRY(MF_MT_FRAME_RATE_RANGE_MAX),
322     GUID_ENTRY(MF_MT_FRAME_RATE_RANGE_MIN),
323     GUID_ENTRY(MF_MT_FRAME_SIZE),
324     GUID_ENTRY(MF_MT_GEOMETRIC_APERTURE),
325     GUID_ENTRY(MF_MT_INTERLACE_MODE),
326     GUID_ENTRY(MF_MT_MAX_KEYFRAME_SPACING),
327     GUID_ENTRY(MF_MT_MINIMUM_DISPLAY_APERTURE),
328     GUID_ENTRY(MF_MT_MPEG_SEQUENCE_HEADER),
329     GUID_ENTRY(MF_MT_MPEG_START_TIME_CODE),
330     GUID_ENTRY(MF_MT_MPEG2_FLAGS),
331     GUID_ENTRY(MF_MT_MPEG2_LEVEL),
332     GUID_ENTRY(MF_MT_MPEG2_PROFILE),
333     GUID_ENTRY(MF_MT_PAD_CONTROL_FLAGS),
334     GUID_ENTRY(MF_MT_PALETTE),
335     GUID_ENTRY(MF_MT_PAN_SCAN_APERTURE),
336     GUID_ENTRY(MF_MT_PAN_SCAN_ENABLED),
337     GUID_ENTRY(MF_MT_PIXEL_ASPECT_RATIO),
338     GUID_ENTRY(MF_MT_SOURCE_CONTENT_HINT),
339     GUID_ENTRY(MF_MT_TRANSFER_FUNCTION),
340     GUID_ENTRY(MF_MT_VIDEO_CHROMA_SITING),
341     GUID_ENTRY(MF_MT_VIDEO_LIGHTING),
342     GUID_ENTRY(MF_MT_VIDEO_NOMINAL_RANGE),
343     GUID_ENTRY(MF_MT_VIDEO_PRIMARIES),
344     GUID_ENTRY(MF_MT_VIDEO_ROTATION),
345     GUID_ENTRY(MF_MT_YUV_MATRIX),
346     GUID_ENTRY(ff_CODECAPI_AVDecVideoThumbnailGenerationMode),
347     GUID_ENTRY(ff_CODECAPI_AVDecVideoDropPicWithMissingRef),
348     GUID_ENTRY(ff_CODECAPI_AVDecVideoSoftwareDeinterlaceMode),
349     GUID_ENTRY(ff_CODECAPI_AVDecVideoFastDecodeMode),
350     GUID_ENTRY(ff_CODECAPI_AVLowLatencyMode),
351     GUID_ENTRY(ff_CODECAPI_AVDecVideoH264ErrorConcealment),
352     GUID_ENTRY(ff_CODECAPI_AVDecVideoMPEG2ErrorConcealment),
353     GUID_ENTRY(ff_CODECAPI_AVDecVideoCodecType),
354     GUID_ENTRY(ff_CODECAPI_AVDecVideoDXVAMode),
355     GUID_ENTRY(ff_CODECAPI_AVDecVideoDXVABusEncryption),
356     GUID_ENTRY(ff_CODECAPI_AVDecVideoSWPowerLevel),
357     GUID_ENTRY(ff_CODECAPI_AVDecVideoMaxCodedWidth),
358     GUID_ENTRY(ff_CODECAPI_AVDecVideoMaxCodedHeight),
359     GUID_ENTRY(ff_CODECAPI_AVDecNumWorkerThreads),
360     GUID_ENTRY(ff_CODECAPI_AVDecSoftwareDynamicFormatChange),
361     GUID_ENTRY(ff_CODECAPI_AVDecDisableVideoPostProcessing),
362 };
363 
ff_guid_str_buf(char * buf,size_t buf_size,const GUID * guid)364 char *ff_guid_str_buf(char *buf, size_t buf_size, const GUID *guid)
365 {
366     uint32_t fourcc;
367     int n;
368     for (n = 0; n < FF_ARRAY_ELEMS(guid_names); n++) {
369         if (IsEqualGUID(guid, guid_names[n].guid)) {
370             snprintf(buf, buf_size, "%s", guid_names[n].name);
371             return buf;
372         }
373     }
374 
375     if (ff_fourcc_from_guid(guid, &fourcc) >= 0) {
376         snprintf(buf, buf_size, "<FourCC %s>", av_fourcc2str(fourcc));
377         return buf;
378     }
379 
380     snprintf(buf, buf_size,
381              "{%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}",
382              (unsigned) guid->Data1, guid->Data2, guid->Data3,
383              guid->Data4[0], guid->Data4[1],
384              guid->Data4[2], guid->Data4[3],
385              guid->Data4[4], guid->Data4[5],
386              guid->Data4[6], guid->Data4[7]);
387     return buf;
388 }
389 
ff_attributes_dump(void * log,IMFAttributes * attrs)390 void ff_attributes_dump(void *log, IMFAttributes *attrs)
391 {
392     HRESULT hr;
393     UINT32 count;
394     int n;
395 
396     hr = IMFAttributes_GetCount(attrs, &count);
397     if (FAILED(hr))
398         return;
399 
400     for (n = 0; n < count; n++) {
401         GUID key;
402         MF_ATTRIBUTE_TYPE type;
403         char extra[80] = {0};
404         const char *name = NULL;
405 
406         hr = IMFAttributes_GetItemByIndex(attrs, n, &key, NULL);
407         if (FAILED(hr))
408             goto err;
409 
410         name = ff_guid_str(&key);
411 
412         if (IsEqualGUID(&key, &MF_MT_AUDIO_CHANNEL_MASK)) {
413             UINT32 v;
414             hr = IMFAttributes_GetUINT32(attrs, &key, &v);
415             if (FAILED(hr))
416                 goto err;
417             snprintf(extra, sizeof(extra), " (0x%x)", (unsigned)v);
418         } else if (IsEqualGUID(&key, &MF_MT_FRAME_SIZE)) {
419             UINT32 w, h;
420 
421             hr = ff_MFGetAttributeSize(attrs, &MF_MT_FRAME_SIZE, &w, &h);
422             if (FAILED(hr))
423                 goto err;
424             snprintf(extra, sizeof(extra), " (%dx%d)", (int)w, (int)h);
425         } else if (IsEqualGUID(&key, &MF_MT_PIXEL_ASPECT_RATIO) ||
426                    IsEqualGUID(&key, &MF_MT_FRAME_RATE)) {
427             UINT32 num, den;
428 
429             hr = ff_MFGetAttributeRatio(attrs, &key, &num, &den);
430             if (FAILED(hr))
431                 goto err;
432             snprintf(extra, sizeof(extra), " (%d:%d)", (int)num, (int)den);
433         }
434 
435         hr = IMFAttributes_GetItemType(attrs, &key, &type);
436         if (FAILED(hr))
437             goto err;
438 
439         switch (type) {
440         case MF_ATTRIBUTE_UINT32: {
441             UINT32 v;
442             hr = IMFAttributes_GetUINT32(attrs, &key, &v);
443             if (FAILED(hr))
444                 goto err;
445             av_log(log, AV_LOG_VERBOSE, "   %s=%d%s\n", name, (int)v, extra);
446             break;
447         case MF_ATTRIBUTE_UINT64: {
448             UINT64 v;
449             hr = IMFAttributes_GetUINT64(attrs, &key, &v);
450             if (FAILED(hr))
451                 goto err;
452             av_log(log, AV_LOG_VERBOSE, "   %s=%lld%s\n", name, (long long)v, extra);
453             break;
454         }
455         case MF_ATTRIBUTE_DOUBLE: {
456             DOUBLE v;
457             hr = IMFAttributes_GetDouble(attrs, &key, &v);
458             if (FAILED(hr))
459                 goto err;
460             av_log(log, AV_LOG_VERBOSE, "   %s=%f%s\n", name, (double)v, extra);
461             break;
462         }
463         case MF_ATTRIBUTE_STRING: {
464             wchar_t s[512]; // being lazy here
465             hr = IMFAttributes_GetString(attrs, &key, s, sizeof(s), NULL);
466             if (FAILED(hr))
467                 goto err;
468             av_log(log, AV_LOG_VERBOSE, "   %s='%ls'%s\n", name, s, extra);
469             break;
470         }
471         case MF_ATTRIBUTE_GUID: {
472             GUID v;
473             hr = IMFAttributes_GetGUID(attrs, &key, &v);
474             if (FAILED(hr))
475                 goto err;
476             av_log(log, AV_LOG_VERBOSE, "   %s=%s%s\n", name, ff_guid_str(&v), extra);
477             break;
478         }
479         case MF_ATTRIBUTE_BLOB: {
480             UINT32 sz;
481             UINT8 buffer[100];
482             hr = IMFAttributes_GetBlobSize(attrs, &key, &sz);
483             if (FAILED(hr))
484                 goto err;
485             if (sz <= sizeof(buffer)) {
486                 // hex-dump it
487                 char str[512] = {0};
488                 size_t pos = 0;
489                 hr = IMFAttributes_GetBlob(attrs, &key, buffer, sizeof(buffer), &sz);
490                 if (FAILED(hr))
491                     goto err;
492                 for (pos = 0; pos < sz; pos++) {
493                     const char *hex = "0123456789ABCDEF";
494                     if (pos * 3 + 3 > sizeof(str))
495                         break;
496                     str[pos * 3 + 0] = hex[buffer[pos] >> 4];
497                     str[pos * 3 + 1] = hex[buffer[pos] & 15];
498                     str[pos * 3 + 2] = ' ';
499                 }
500                 str[pos * 3 + 0] = 0;
501                 av_log(log, AV_LOG_VERBOSE, "   %s=<blob size %d: %s>%s\n", name, (int)sz, str, extra);
502             } else {
503                 av_log(log, AV_LOG_VERBOSE, "   %s=<blob size %d>%s\n", name, (int)sz, extra);
504             }
505             break;
506         }
507         case MF_ATTRIBUTE_IUNKNOWN: {
508             av_log(log, AV_LOG_VERBOSE, "   %s=<IUnknown>%s\n", name, extra);
509             break;
510         }
511         default:
512             av_log(log, AV_LOG_VERBOSE, "   %s=<unknown type>%s\n", name, extra);
513             break;
514         }
515         }
516 
517         if (IsEqualGUID(&key, &MF_MT_SUBTYPE)) {
518             const char *fmt;
519             fmt = av_get_sample_fmt_name(ff_media_type_to_sample_fmt(attrs));
520             if (fmt)
521                 av_log(log, AV_LOG_VERBOSE, "   FF-sample-format=%s\n", fmt);
522 
523             fmt = av_get_pix_fmt_name(ff_media_type_to_pix_fmt(attrs));
524             if (fmt)
525                 av_log(log, AV_LOG_VERBOSE, "   FF-pixel-format=%s\n", fmt);
526         }
527 
528         continue;
529     err:
530         av_log(log, AV_LOG_VERBOSE, "   %s=<failed to get value>\n", name ? name : "?");
531     }
532 }
533 
ff_media_type_dump(void * log,IMFMediaType * type)534 void ff_media_type_dump(void *log, IMFMediaType *type)
535 {
536     ff_attributes_dump(log, (IMFAttributes *)type);
537 }
538 
ff_codec_to_mf_subtype(enum AVCodecID codec)539 const CLSID *ff_codec_to_mf_subtype(enum AVCodecID codec)
540 {
541     switch (codec) {
542     case AV_CODEC_ID_H264:              return &MFVideoFormat_H264;
543     case AV_CODEC_ID_HEVC:              return &ff_MFVideoFormat_HEVC;
544     case AV_CODEC_ID_AC3:               return &MFAudioFormat_Dolby_AC3;
545     case AV_CODEC_ID_AAC:               return &MFAudioFormat_AAC;
546     case AV_CODEC_ID_MP3:               return &MFAudioFormat_MP3;
547     default:                            return NULL;
548     }
549 }
550 
init_com_mf(void * log)551 static int init_com_mf(void *log)
552 {
553     HRESULT hr;
554 
555     hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
556     if (hr == RPC_E_CHANGED_MODE) {
557         av_log(log, AV_LOG_ERROR, "COM must not be in STA mode\n");
558         return AVERROR(EINVAL);
559     } else if (FAILED(hr)) {
560         av_log(log, AV_LOG_ERROR, "could not initialize COM\n");
561         return AVERROR(ENOSYS);
562     }
563 
564     hr = MFStartup(MF_VERSION, MFSTARTUP_FULL);
565     if (FAILED(hr)) {
566         av_log(log, AV_LOG_ERROR, "could not initialize MediaFoundation\n");
567         CoUninitialize();
568         return AVERROR(ENOSYS);
569     }
570 
571     return 0;
572 }
573 
uninit_com_mf(void)574 static void uninit_com_mf(void)
575 {
576     MFShutdown();
577     CoUninitialize();
578 }
579 
580 // Find and create a IMFTransform with the given input/output types. When done,
581 // you should use ff_free_mf() to destroy it, which will also uninit COM.
ff_instantiate_mf(void * log,GUID category,MFT_REGISTER_TYPE_INFO * in_type,MFT_REGISTER_TYPE_INFO * out_type,int use_hw,IMFTransform ** res)582 int ff_instantiate_mf(void *log,
583                       GUID category,
584                       MFT_REGISTER_TYPE_INFO *in_type,
585                       MFT_REGISTER_TYPE_INFO *out_type,
586                       int use_hw,
587                       IMFTransform **res)
588 {
589     HRESULT hr;
590     int n;
591     int ret;
592     IMFActivate **activate;
593     UINT32 num_activate;
594     IMFActivate *winner = 0;
595     UINT32 flags;
596 
597     ret = init_com_mf(log);
598     if (ret < 0)
599         return ret;
600 
601     flags = MFT_ENUM_FLAG_SORTANDFILTER;
602 
603     if (use_hw) {
604         flags |= MFT_ENUM_FLAG_HARDWARE;
605     } else {
606         flags |= MFT_ENUM_FLAG_SYNCMFT;
607     }
608 
609     hr = ff_MFTEnumEx(category, flags, in_type, out_type, &activate,
610                       &num_activate);
611     if (FAILED(hr))
612         goto error_uninit_mf;
613 
614     if (log) {
615         if (!num_activate)
616             av_log(log, AV_LOG_ERROR, "could not find any MFT for the given media type\n");
617 
618         for (n = 0; n < num_activate; n++) {
619             av_log(log, AV_LOG_VERBOSE, "MF %d attributes:\n", n);
620             ff_attributes_dump(log, (IMFAttributes *)activate[n]);
621         }
622     }
623 
624     *res = NULL;
625     for (n = 0; n < num_activate; n++) {
626         if (log)
627             av_log(log, AV_LOG_VERBOSE, "activate MFT %d\n", n);
628         hr = IMFActivate_ActivateObject(activate[n], &IID_IMFTransform,
629                                         (void **)res);
630         if (*res) {
631             winner = activate[n];
632             IMFActivate_AddRef(winner);
633             break;
634         }
635     }
636 
637     for (n = 0; n < num_activate; n++)
638        IMFActivate_Release(activate[n]);
639     CoTaskMemFree(activate);
640 
641     if (!*res) {
642         if (log)
643             av_log(log, AV_LOG_ERROR, "could not create MFT\n");
644         goto error_uninit_mf;
645     }
646 
647     if (log) {
648         wchar_t s[512]; // being lazy here
649         IMFAttributes *attrs;
650         hr = IMFTransform_GetAttributes(*res, &attrs);
651         if (!FAILED(hr) && attrs) {
652 
653             av_log(log, AV_LOG_VERBOSE, "MFT attributes\n");
654             ff_attributes_dump(log, attrs);
655             IMFAttributes_Release(attrs);
656         }
657 
658         hr = IMFActivate_GetString(winner, &MFT_FRIENDLY_NAME_Attribute, s,
659                                    sizeof(s), NULL);
660         if (!FAILED(hr))
661             av_log(log, AV_LOG_INFO, "MFT name: '%ls'\n", s);
662 
663     }
664 
665     IMFActivate_Release(winner);
666 
667     return 0;
668 
669 error_uninit_mf:
670     uninit_com_mf();
671     return AVERROR(ENOSYS);
672 }
673 
ff_free_mf(IMFTransform ** mft)674 void ff_free_mf(IMFTransform **mft)
675 {
676     if (*mft)
677         IMFTransform_Release(*mft);
678     *mft = NULL;
679     uninit_com_mf();
680 }
681