FreeRDP
h264_mf.c
1 
20 #include <winpr/winpr.h>
21 #include <freerdp/log.h>
22 #include <freerdp/codec/h264.h>
23 
24 #include <ks.h>
25 #include <codecapi.h>
26 
27 #include <mfapi.h>
28 #include <mferror.h>
29 #include <wmcodecdsp.h>
30 #include <mftransform.h>
31 
32 #include "h264.h"
33 
34 #define TAG FREERDP_TAG("codec")
35 
36 static const GUID sCLSID_CMSH264DecoderMFT = {
37  0x62CE7E72, 0x4C71, 0x4d20, { 0xB1, 0x5D, 0x45, 0x28, 0x31, 0xA8, 0x7D, 0x9D }
38 };
39 static const GUID sIID_IMFTransform = {
40  0xbf94c121, 0x5b05, 0x4e6f, { 0x80, 0x00, 0xba, 0x59, 0x89, 0x61, 0x41, 0x4d }
41 };
42 static const GUID sMF_MT_MAJOR_TYPE = {
43  0x48eba18e, 0xf8c9, 0x4687, { 0xbf, 0x11, 0x0a, 0x74, 0xc9, 0xf9, 0x6a, 0x8f }
44 };
45 static const GUID sMF_MT_FRAME_SIZE = {
46  0x1652c33d, 0xd6b2, 0x4012, { 0xb8, 0x34, 0x72, 0x03, 0x08, 0x49, 0xa3, 0x7d }
47 };
48 static const GUID sMF_MT_DEFAULT_STRIDE = {
49  0x644b4e48, 0x1e02, 0x4516, { 0xb0, 0xeb, 0xc0, 0x1c, 0xa9, 0xd4, 0x9a, 0xc6 }
50 };
51 static const GUID sMF_MT_SUBTYPE = {
52  0xf7e34c9a, 0x42e8, 0x4714, { 0xb7, 0x4b, 0xcb, 0x29, 0xd7, 0x2c, 0x35, 0xe5 }
53 };
54 static const GUID sMFMediaType_Video = {
55  0x73646976, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 }
56 };
57 static const GUID sMFVideoFormat_H264 = {
58  0x34363248, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }
59 };
60 static const GUID sMFVideoFormat_IYUV = {
61  0x56555949, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }
62 };
63 static const GUID sIID_ICodecAPI = {
64  0x901db4c7, 0x31ce, 0x41a2, { 0x85, 0xdc, 0x8f, 0xa0, 0xbf, 0x41, 0xb8, 0xda }
65 };
66 static const GUID sCODECAPI_AVLowLatencyMode = {
67  0x9c27891a, 0xed7a, 0x40e1, { 0x88, 0xe8, 0xb2, 0x27, 0x27, 0xa0, 0x24, 0xee }
68 };
69 
70 typedef HRESULT(__stdcall* pfnMFStartup)(ULONG Version, DWORD dwFlags);
71 typedef HRESULT(__stdcall* pfnMFShutdown)(void);
72 typedef HRESULT(__stdcall* pfnMFCreateSample)(IMFSample** ppIMFSample);
73 typedef HRESULT(__stdcall* pfnMFCreateMemoryBuffer)(DWORD cbMaxLength, IMFMediaBuffer** ppBuffer);
74 typedef HRESULT(__stdcall* pfnMFCreateMediaType)(IMFMediaType** ppMFType);
75 
76 typedef struct
77 {
78  ICodecAPI* codecApi;
79  IMFTransform* transform;
80  IMFMediaType* inputType;
81  IMFMediaType* outputType;
82  IMFSample* sample;
83  UINT32 frameWidth;
84  UINT32 frameHeight;
85  IMFSample* outputSample;
86  IMFMediaBuffer* outputBuffer;
87  HMODULE mfplat;
88  pfnMFStartup MFStartup;
89  pfnMFShutdown MFShutdown;
90  pfnMFCreateSample MFCreateSample;
91  pfnMFCreateMemoryBuffer MFCreateMemoryBuffer;
92  pfnMFCreateMediaType MFCreateMediaType;
93 } H264_CONTEXT_MF;
94 
95 static HRESULT mf_find_output_type(H264_CONTEXT_MF* sys, const GUID* guid,
96  IMFMediaType** ppMediaType)
97 {
98  DWORD idx = 0;
99  GUID mediaGuid;
100  HRESULT hr = S_OK;
101  IMFMediaType* pMediaType = NULL;
102 
103  while (1)
104  {
105  hr = sys->transform->lpVtbl->GetOutputAvailableType(sys->transform, 0, idx, &pMediaType);
106 
107  if (FAILED(hr))
108  break;
109 
110  pMediaType->lpVtbl->GetGUID(pMediaType, &sMF_MT_SUBTYPE, &mediaGuid);
111 
112  if (IsEqualGUID(&mediaGuid, guid))
113  {
114  *ppMediaType = pMediaType;
115  return S_OK;
116  }
117 
118  pMediaType->lpVtbl->Release(pMediaType);
119  idx++;
120  }
121 
122  return hr;
123 }
124 
125 static HRESULT mf_create_output_sample(H264_CONTEXT* h264, H264_CONTEXT_MF* sys)
126 {
127  HRESULT hr = S_OK;
128  MFT_OUTPUT_STREAM_INFO streamInfo;
129 
130  if (sys->outputSample)
131  {
132  sys->outputSample->lpVtbl->Release(sys->outputSample);
133  sys->outputSample = NULL;
134  }
135 
136  hr = sys->MFCreateSample(&sys->outputSample);
137 
138  if (FAILED(hr))
139  {
140  WLog_Print(h264->log, WLOG_ERROR, "MFCreateSample failure: 0x%08" PRIX32 "", hr);
141  goto error;
142  }
143 
144  hr = sys->transform->lpVtbl->GetOutputStreamInfo(sys->transform, 0, &streamInfo);
145 
146  if (FAILED(hr))
147  {
148  WLog_Print(h264->log, WLOG_ERROR, "GetOutputStreamInfo failure: 0x%08" PRIX32 "", hr);
149  goto error;
150  }
151 
152  hr = sys->MFCreateMemoryBuffer(streamInfo.cbSize, &sys->outputBuffer);
153 
154  if (FAILED(hr))
155  {
156  WLog_Print(h264->log, WLOG_ERROR, "MFCreateMemoryBuffer failure: 0x%08" PRIX32 "", hr);
157  goto error;
158  }
159 
160  sys->outputSample->lpVtbl->AddBuffer(sys->outputSample, sys->outputBuffer);
161 
162  if (FAILED(hr))
163  {
164  WLog_Print(h264->log, WLOG_ERROR, "AddBuffer failure: 0x%08" PRIX32 "", hr);
165  goto error;
166  }
167 
168  sys->outputBuffer->lpVtbl->Release(sys->outputBuffer);
169 error:
170  return hr;
171 }
172 
173 static int mf_decompress(H264_CONTEXT* h264, const BYTE* pSrcData, UINT32 SrcSize)
174 {
175  HRESULT hr;
176  BYTE* pbBuffer = NULL;
177  DWORD cbMaxLength = 0;
178  DWORD cbCurrentLength = 0;
179  DWORD outputStatus = 0;
180  IMFSample* inputSample = NULL;
181  IMFMediaBuffer* inputBuffer = NULL;
182  IMFMediaBuffer* outputBuffer = NULL;
183  MFT_OUTPUT_DATA_BUFFER outputDataBuffer;
184  H264_CONTEXT_MF* sys = (H264_CONTEXT_MF*)h264->pSystemData;
185  UINT32* iStride = h264->iStride;
186  BYTE** pYUVData = h264->pYUVData;
187  hr = sys->MFCreateMemoryBuffer(SrcSize, &inputBuffer);
188 
189  if (FAILED(hr))
190  {
191  WLog_Print(h264->log, WLOG_ERROR, "MFCreateMemoryBuffer failure: 0x%08" PRIX32 "", hr);
192  goto error;
193  }
194 
195  hr = inputBuffer->lpVtbl->Lock(inputBuffer, &pbBuffer, &cbMaxLength, &cbCurrentLength);
196 
197  if (FAILED(hr))
198  {
199  WLog_Print(h264->log, WLOG_ERROR, "Lock failure: 0x%08" PRIX32 "", hr);
200  goto error;
201  }
202 
203  CopyMemory(pbBuffer, pSrcData, SrcSize);
204  hr = inputBuffer->lpVtbl->SetCurrentLength(inputBuffer, SrcSize);
205 
206  if (FAILED(hr))
207  {
208  WLog_Print(h264->log, WLOG_ERROR, "SetCurrentLength failure: 0x%08" PRIX32 "", hr);
209  goto error;
210  }
211 
212  hr = inputBuffer->lpVtbl->Unlock(inputBuffer);
213 
214  if (FAILED(hr))
215  {
216  WLog_Print(h264->log, WLOG_ERROR, "Unlock failure: 0x%08" PRIX32 "", hr);
217  goto error;
218  }
219 
220  hr = sys->MFCreateSample(&inputSample);
221 
222  if (FAILED(hr))
223  {
224  WLog_Print(h264->log, WLOG_ERROR, "MFCreateSample failure: 0x%08" PRIX32 "", hr);
225  goto error;
226  }
227 
228  inputSample->lpVtbl->AddBuffer(inputSample, inputBuffer);
229 
230  if (FAILED(hr))
231  {
232  WLog_Print(h264->log, WLOG_ERROR, "AddBuffer failure: 0x%08" PRIX32 "", hr);
233  goto error;
234  }
235 
236  inputBuffer->lpVtbl->Release(inputBuffer);
237  hr = sys->transform->lpVtbl->ProcessInput(sys->transform, 0, inputSample, 0);
238 
239  if (FAILED(hr))
240  {
241  WLog_Print(h264->log, WLOG_ERROR, "ProcessInput failure: 0x%08" PRIX32 "", hr);
242  goto error;
243  }
244 
245  hr = mf_create_output_sample(h264, sys);
246 
247  if (FAILED(hr))
248  {
249  WLog_Print(h264->log, WLOG_ERROR, "mf_create_output_sample failure: 0x%08" PRIX32 "", hr);
250  goto error;
251  }
252 
253  outputDataBuffer.dwStreamID = 0;
254  outputDataBuffer.dwStatus = 0;
255  outputDataBuffer.pEvents = NULL;
256  outputDataBuffer.pSample = sys->outputSample;
257  hr = sys->transform->lpVtbl->ProcessOutput(sys->transform, 0, 1, &outputDataBuffer,
258  &outputStatus);
259 
260  if (hr == MF_E_TRANSFORM_STREAM_CHANGE)
261  {
262  UINT32 stride = 0;
263  UINT64 frameSize = 0;
264 
265  if (sys->outputType)
266  {
267  sys->outputType->lpVtbl->Release(sys->outputType);
268  sys->outputType = NULL;
269  }
270 
271  hr = mf_find_output_type(sys, &sMFVideoFormat_IYUV, &sys->outputType);
272 
273  if (FAILED(hr))
274  {
275  WLog_Print(h264->log, WLOG_ERROR, "mf_find_output_type failure: 0x%08" PRIX32 "", hr);
276  goto error;
277  }
278 
279  hr = sys->transform->lpVtbl->SetOutputType(sys->transform, 0, sys->outputType, 0);
280 
281  if (FAILED(hr))
282  {
283  WLog_Print(h264->log, WLOG_ERROR, "SetOutputType failure: 0x%08" PRIX32 "", hr);
284  goto error;
285  }
286 
287  hr = mf_create_output_sample(h264, sys);
288 
289  if (FAILED(hr))
290  {
291  WLog_Print(h264->log, WLOG_ERROR, "mf_create_output_sample failure: 0x%08" PRIX32 "",
292  hr);
293  goto error;
294  }
295 
296  hr = sys->outputType->lpVtbl->GetUINT64(sys->outputType, &sMF_MT_FRAME_SIZE, &frameSize);
297 
298  if (FAILED(hr))
299  {
300  WLog_Print(h264->log, WLOG_ERROR,
301  "GetUINT64(MF_MT_FRAME_SIZE) failure: 0x%08" PRIX32 "", hr);
302  goto error;
303  }
304 
305  sys->frameWidth = (UINT32)(frameSize >> 32);
306  sys->frameHeight = (UINT32)frameSize;
307  hr = sys->outputType->lpVtbl->GetUINT32(sys->outputType, &sMF_MT_DEFAULT_STRIDE, &stride);
308 
309  if (FAILED(hr))
310  {
311  WLog_Print(h264->log, WLOG_ERROR,
312  "GetUINT32(MF_MT_DEFAULT_STRIDE) failure: 0x%08" PRIX32 "", hr);
313  goto error;
314  }
315 
316  if (!avc420_ensure_buffer(h264, stride, sys->frameWidth, sys->frameHeight))
317  goto error;
318  }
319  else if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
320  {
321  }
322  else if (FAILED(hr))
323  {
324  WLog_Print(h264->log, WLOG_ERROR, "ProcessOutput failure: 0x%08" PRIX32 "", hr);
325  goto error;
326  }
327  else
328  {
329  int offset = 0;
330  BYTE* buffer = NULL;
331  DWORD bufferCount = 0;
332  DWORD cbMaxLength = 0;
333  DWORD cbCurrentLength = 0;
334  hr = sys->outputSample->lpVtbl->GetBufferCount(sys->outputSample, &bufferCount);
335 
336  if (FAILED(hr))
337  {
338  WLog_Print(h264->log, WLOG_ERROR, "GetBufferCount failure: 0x%08" PRIX32 "", hr);
339  goto error;
340  }
341 
342  hr = sys->outputSample->lpVtbl->GetBufferByIndex(sys->outputSample, 0, &outputBuffer);
343 
344  if (FAILED(hr))
345  {
346  WLog_Print(h264->log, WLOG_ERROR, "GetBufferByIndex failure: 0x%08" PRIX32 "", hr);
347  goto error;
348  }
349 
350  hr = outputBuffer->lpVtbl->Lock(outputBuffer, &buffer, &cbMaxLength, &cbCurrentLength);
351 
352  if (FAILED(hr))
353  {
354  WLog_Print(h264->log, WLOG_ERROR, "Lock failure: 0x%08" PRIX32 "", hr);
355  goto error;
356  }
357 
358  CopyMemory(pYUVData[0], &buffer[offset], iStride[0] * sys->frameHeight);
359  offset += iStride[0] * sys->frameHeight;
360  CopyMemory(pYUVData[1], &buffer[offset], iStride[1] * (sys->frameHeight / 2));
361  offset += iStride[1] * (sys->frameHeight / 2);
362  CopyMemory(pYUVData[2], &buffer[offset], iStride[2] * (sys->frameHeight / 2));
363  offset += iStride[2] * (sys->frameHeight / 2);
364  hr = outputBuffer->lpVtbl->Unlock(outputBuffer);
365 
366  if (FAILED(hr))
367  {
368  WLog_Print(h264->log, WLOG_ERROR, "Unlock failure: 0x%08" PRIX32 "", hr);
369  goto error;
370  }
371 
372  outputBuffer->lpVtbl->Release(outputBuffer);
373  }
374 
375  inputSample->lpVtbl->Release(inputSample);
376  return 1;
377 error:
378  (void)fprintf(stderr, "mf_decompress error\n");
379  return -1;
380 }
381 
382 static int mf_compress(H264_CONTEXT* h264, const BYTE** ppSrcYuv, const UINT32* pStride,
383  BYTE** ppDstData, UINT32* pDstSize)
384 {
385  H264_CONTEXT_MF* sys = (H264_CONTEXT_MF*)h264->pSystemData;
386  return 1;
387 }
388 
389 static BOOL mf_plat_loaded(H264_CONTEXT_MF* sys)
390 {
391  return sys->MFStartup && sys->MFShutdown && sys->MFCreateSample && sys->MFCreateMemoryBuffer &&
392  sys->MFCreateMediaType;
393 }
394 
395 static void mf_uninit(H264_CONTEXT* h264)
396 {
397  H264_CONTEXT_MF* sys = (H264_CONTEXT_MF*)h264->pSystemData;
398 
399  if (sys)
400  {
401  if (sys->transform)
402  {
403  sys->transform->lpVtbl->Release(sys->transform);
404  sys->transform = NULL;
405  }
406 
407  if (sys->codecApi)
408  {
409  sys->codecApi->lpVtbl->Release(sys->codecApi);
410  sys->codecApi = NULL;
411  }
412 
413  if (sys->inputType)
414  {
415  sys->inputType->lpVtbl->Release(sys->inputType);
416  sys->inputType = NULL;
417  }
418 
419  if (sys->outputType)
420  {
421  sys->outputType->lpVtbl->Release(sys->outputType);
422  sys->outputType = NULL;
423  }
424 
425  if (sys->outputSample)
426  {
427  sys->outputSample->lpVtbl->Release(sys->outputSample);
428  sys->outputSample = NULL;
429  }
430 
431  if (sys->mfplat)
432  {
433  if (mf_plat_loaded(sys))
434  sys->MFShutdown();
435 
436  FreeLibrary(sys->mfplat);
437  sys->mfplat = NULL;
438 
439  if (mf_plat_loaded(sys))
440  CoUninitialize();
441  }
442 
443  for (size_t x = 0; x < sizeof(h264->pYUVData) / sizeof(h264->pYUVData[0]); x++)
444  winpr_aligned_free(h264->pYUVData[x]);
445 
446  memset(h264->pYUVData, 0, sizeof(h264->pYUVData));
447  memset(h264->iStride, 0, sizeof(h264->iStride));
448 
449  free(sys);
450  h264->pSystemData = NULL;
451  }
452 }
453 
454 static BOOL mf_init(H264_CONTEXT* h264)
455 {
456  HRESULT hr;
457  H264_CONTEXT_MF* sys = (H264_CONTEXT_MF*)calloc(1, sizeof(H264_CONTEXT_MF));
458 
459  if (!sys)
460  goto error;
461 
462  h264->pSystemData = (void*)sys;
463  /* http://decklink-sdk-delphi.googlecode.com/svn/trunk/Blackmagic%20DeckLink%20SDK%209.7/Win/Samples/Streaming/StreamingPreview/DecoderMF.cpp
464  */
465  sys->mfplat = LoadLibraryA("mfplat.dll");
466 
467  if (!sys->mfplat)
468  goto error;
469 
470  sys->MFStartup = GetProcAddressAs(sys->mfplat, "MFStartup", pfnMFStartup);
471  sys->MFShutdown = GetProcAddressAs(sys->mfplat, "MFShutdown", pfnMFShutdown);
472  sys->MFCreateSample = GetProcAddressAs(sys->mfplat, "MFCreateSample", pfnMFCreateSample);
473  sys->MFCreateMemoryBuffer =
474  GetProcAddressAs(sys->mfplat, "MFCreateMemoryBuffer", pfnMFCreateMemoryBuffer);
475  sys->MFCreateMediaType =
476  GetProcAddressAs(sys->mfplat, "MFCreateMediaType", pfnMFCreateMediaType);
477 
478  if (!mf_plat_loaded(sys))
479  goto error;
480 
481  CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
482 
483  if (h264->Compressor)
484  {
485  }
486  else
487  {
488  VARIANT var = { 0 };
489  hr = sys->MFStartup(MF_VERSION, 0);
490 
491  if (FAILED(hr))
492  {
493  WLog_Print(h264->log, WLOG_ERROR, "MFStartup failure: 0x%08" PRIX32 "", hr);
494  goto error;
495  }
496 
497  hr = CoCreateInstance(&sCLSID_CMSH264DecoderMFT, NULL, CLSCTX_INPROC_SERVER,
498  &sIID_IMFTransform, (void**)&sys->transform);
499 
500  if (FAILED(hr))
501  {
502  WLog_Print(h264->log, WLOG_ERROR,
503  "CoCreateInstance(CLSID_CMSH264DecoderMFT) failure: 0x%08" PRIX32 "", hr);
504  goto error;
505  }
506 
507  hr = sys->transform->lpVtbl->QueryInterface(sys->transform, &sIID_ICodecAPI,
508  (void**)&sys->codecApi);
509 
510  if (FAILED(hr))
511  {
512  WLog_Print(h264->log, WLOG_ERROR,
513  "QueryInterface(IID_ICodecAPI) failure: 0x%08" PRIX32 "", hr);
514  goto error;
515  }
516 
517  var.vt = VT_UI4;
518  var.ulVal = 1;
519  hr = sys->codecApi->lpVtbl->SetValue(sys->codecApi, &sCODECAPI_AVLowLatencyMode, &var);
520 
521  if (FAILED(hr))
522  {
523  WLog_Print(h264->log, WLOG_ERROR,
524  "SetValue(CODECAPI_AVLowLatencyMode) failure: 0x%08" PRIX32 "", hr);
525  goto error;
526  }
527 
528  hr = sys->MFCreateMediaType(&sys->inputType);
529 
530  if (FAILED(hr))
531  {
532  WLog_Print(h264->log, WLOG_ERROR, "MFCreateMediaType failure: 0x%08" PRIX32 "", hr);
533  goto error;
534  }
535 
536  hr = sys->inputType->lpVtbl->SetGUID(sys->inputType, &sMF_MT_MAJOR_TYPE,
537  &sMFMediaType_Video);
538 
539  if (FAILED(hr))
540  {
541  WLog_Print(h264->log, WLOG_ERROR, "SetGUID(MF_MT_MAJOR_TYPE) failure: 0x%08" PRIX32 "",
542  hr);
543  goto error;
544  }
545 
546  hr = sys->inputType->lpVtbl->SetGUID(sys->inputType, &sMF_MT_SUBTYPE, &sMFVideoFormat_H264);
547 
548  if (FAILED(hr))
549  {
550  WLog_Print(h264->log, WLOG_ERROR, "SetGUID(MF_MT_SUBTYPE) failure: 0x%08" PRIX32 "",
551  hr);
552  goto error;
553  }
554 
555  hr = sys->transform->lpVtbl->SetInputType(sys->transform, 0, sys->inputType, 0);
556 
557  if (FAILED(hr))
558  {
559  WLog_Print(h264->log, WLOG_ERROR, "SetInputType failure: 0x%08" PRIX32 "", hr);
560  goto error;
561  }
562 
563  hr = mf_find_output_type(sys, &sMFVideoFormat_IYUV, &sys->outputType);
564 
565  if (FAILED(hr))
566  {
567  WLog_Print(h264->log, WLOG_ERROR, "mf_find_output_type failure: 0x%08" PRIX32 "", hr);
568  goto error;
569  }
570 
571  hr = sys->transform->lpVtbl->SetOutputType(sys->transform, 0, sys->outputType, 0);
572 
573  if (FAILED(hr))
574  {
575  WLog_Print(h264->log, WLOG_ERROR, "SetOutputType failure: 0x%08" PRIX32 "", hr);
576  goto error;
577  }
578 
579  hr = mf_create_output_sample(h264, sys);
580 
581  if (FAILED(hr))
582  {
583  WLog_Print(h264->log, WLOG_ERROR, "mf_create_output_sample failure: 0x%08" PRIX32 "",
584  hr);
585  goto error;
586  }
587  }
588 
589  return TRUE;
590 error:
591  WLog_Print(h264->log, WLOG_ERROR, "mf_init failure");
592  mf_uninit(h264);
593  return FALSE;
594 }
595 
596 const H264_CONTEXT_SUBSYSTEM g_Subsystem_MF = { "MediaFoundation", mf_init, mf_uninit,
597  mf_decompress, mf_compress };