20#include <winpr/winpr.h>
21#include <winpr/library.h>
23#include <freerdp/log.h>
24#include <freerdp/codec/h264.h>
31#include <wmcodecdsp.h>
32#include <mftransform.h>
36#define TAG FREERDP_TAG("codec")
38static const GUID sCLSID_CMSH264DecoderMFT = {
39 0x62CE7E72, 0x4C71, 0x4d20, { 0xB1, 0x5D, 0x45, 0x28, 0x31, 0xA8, 0x7D, 0x9D }
41static const GUID sIID_IMFTransform = {
42 0xbf94c121, 0x5b05, 0x4e6f, { 0x80, 0x00, 0xba, 0x59, 0x89, 0x61, 0x41, 0x4d }
44static const GUID sMF_MT_MAJOR_TYPE = {
45 0x48eba18e, 0xf8c9, 0x4687, { 0xbf, 0x11, 0x0a, 0x74, 0xc9, 0xf9, 0x6a, 0x8f }
47static const GUID sMF_MT_FRAME_SIZE = {
48 0x1652c33d, 0xd6b2, 0x4012, { 0xb8, 0x34, 0x72, 0x03, 0x08, 0x49, 0xa3, 0x7d }
50static const GUID sMF_MT_DEFAULT_STRIDE = {
51 0x644b4e48, 0x1e02, 0x4516, { 0xb0, 0xeb, 0xc0, 0x1c, 0xa9, 0xd4, 0x9a, 0xc6 }
53static const GUID sMF_MT_SUBTYPE = {
54 0xf7e34c9a, 0x42e8, 0x4714, { 0xb7, 0x4b, 0xcb, 0x29, 0xd7, 0x2c, 0x35, 0xe5 }
56static const GUID sMFMediaType_Video = {
57 0x73646976, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 }
59static const GUID sMFVideoFormat_H264 = {
60 0x34363248, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }
62static const GUID sMFVideoFormat_IYUV = {
63 0x56555949, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }
65static const GUID sIID_ICodecAPI = {
66 0x901db4c7, 0x31ce, 0x41a2, { 0x85, 0xdc, 0x8f, 0xa0, 0xbf, 0x41, 0xb8, 0xda }
68static const GUID sCODECAPI_AVLowLatencyMode = {
69 0x9c27891a, 0xed7a, 0x40e1, { 0x88, 0xe8, 0xb2, 0x27, 0x27, 0xa0, 0x24, 0xee }
73static const GUID sCLSID_CMSH264EncoderMFT = {
74 0x6ca50344, 0x051a, 0x4ded, { 0x97, 0x79, 0xa4, 0x33, 0x05, 0x16, 0x5e, 0x35 }
76static const GUID sMFVideoFormat_NV12 = {
77 0x3231564e, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 }
79static const GUID sMF_MT_FRAME_RATE = {
80 0xc459a2e8, 0x3d2c, 0x4e44, { 0xb1, 0x32, 0xfe, 0xe5, 0x15, 0x6c, 0x7b, 0xb0 }
82static const GUID sMF_MT_PIXEL_ASPECT_RATIO = {
83 0xc6376a1e, 0x8d0a, 0x4027, { 0xbe, 0x45, 0x6d, 0x9a, 0x0a, 0xd3, 0x9b, 0xb6 }
85static const GUID sMF_MT_INTERLACE_MODE = {
86 0xe2724bb8, 0xe676, 0x4806, { 0xb4, 0xb2, 0xa8, 0xd6, 0xef, 0xb4, 0x4c, 0xcd }
88static const GUID sMF_MT_AVG_BITRATE = {
89 0x20332624, 0xfb0d, 0x4d9e, { 0xbd, 0x0d, 0xcb, 0xf6, 0x78, 0x6c, 0x10, 0x2e }
91static const GUID sMF_MT_MPEG2_PROFILE = {
92 0xad76a80b, 0x2d5c, 0x4e0b, { 0xb3, 0x75, 0x64, 0xe5, 0x20, 0x13, 0x70, 0x36 }
94static const GUID sCODECAPI_AVEncCommonRateControlMode = {
95 0x1c0608e9, 0x370c, 0x4710, { 0x8a, 0x58, 0xcb, 0x61, 0x81, 0xc4, 0x24, 0x23 }
97static const GUID sCODECAPI_AVEncCommonMeanBitRate = {
98 0xf7222374, 0x2144, 0x4815, { 0xb5, 0x50, 0xa3, 0x7f, 0x8e, 0x12, 0xee, 0x52 }
100static const GUID sCODECAPI_AVEncCommonQuality = {
101 0xfcbf57a3, 0x7ea5, 0x4b0c, { 0x96, 0x44, 0x69, 0xb4, 0x0c, 0x39, 0xc3, 0x91 }
103static const GUID sCODECAPI_AVEncVideoEncodeQP = {
104 0x2cb5696b, 0x23fb, 0x4ce1, { 0xa0, 0xf9, 0xef, 0x5b, 0x90, 0xfd, 0x55, 0xca }
106static const GUID sCODECAPI_AVEncMPVGOPSize = {
107 0x95f31b26, 0x95a4, 0x41aa, { 0x93, 0x03, 0x24, 0x6a, 0x7f, 0xc6, 0xee, 0xf1 }
111#define FREERDP_MF_RC_CBR 0
112#define FREERDP_MF_RC_QUALITY 3
113#define FREERDP_MF_PROFILE_BASELINE 66
114#define FREERDP_MF_INTERLACE_PROGRESSIVE 2
116typedef HRESULT(__stdcall* pfnMFStartup)(ULONG Version, DWORD dwFlags);
117typedef HRESULT(__stdcall* pfnMFShutdown)(void);
118typedef HRESULT(__stdcall* pfnMFCreateSample)(IMFSample** ppIMFSample);
119typedef HRESULT(__stdcall* pfnMFCreateMemoryBuffer)(DWORD cbMaxLength, IMFMediaBuffer** ppBuffer);
120typedef HRESULT(__stdcall* pfnMFCreateMediaType)(IMFMediaType** ppMFType);
125 IMFTransform* transform;
126 IMFMediaType* inputType;
127 IMFMediaType* outputType;
131 IMFSample* outputSample;
132 IMFMediaBuffer* outputBuffer;
134 pfnMFStartup MFStartup;
135 pfnMFShutdown MFShutdown;
136 pfnMFCreateSample MFCreateSample;
137 pfnMFCreateMemoryBuffer MFCreateMemoryBuffer;
138 pfnMFCreateMediaType MFCreateMediaType;
149 H264_RATECONTROL_MODE encRateControl;
150 DWORD encOutputFlags;
152 LONGLONG encSampleTime;
155 size_t encDataLength;
161static HRESULT mf_find_output_type(H264_CONTEXT_MF* sys,
const GUID* guid,
162 IMFMediaType** ppMediaType)
165 GUID mediaGuid = WINPR_C_ARRAY_INIT;
167 IMFMediaType* pMediaType =
nullptr;
170 WINPR_ASSERT(sys->transform);
172 WINPR_ASSERT(ppMediaType);
176 hr = sys->transform->lpVtbl->GetOutputAvailableType(sys->transform, 0, idx, &pMediaType);
182 if (SUCCEEDED(pMediaType->lpVtbl->GetGUID(pMediaType, &sMF_MT_SUBTYPE, &mediaGuid)) &&
183 IsEqualGUID(&mediaGuid, guid))
185 *ppMediaType = pMediaType;
189 pMediaType->lpVtbl->Release(pMediaType);
197static HRESULT mf_create_output_sample(H264_CONTEXT* h264, H264_CONTEXT_MF* sys)
200 MFT_OUTPUT_STREAM_INFO streamInfo;
204 WINPR_ASSERT(sys->transform);
206 if (sys->outputSample)
208 sys->outputSample->lpVtbl->Release(sys->outputSample);
209 sys->outputSample =
nullptr;
212 hr = sys->MFCreateSample(&sys->outputSample);
216 WLog_Print(h264->log, WLOG_ERROR,
"MFCreateSample failure: 0x%08" PRIX32
"", hr);
220 hr = sys->transform->lpVtbl->GetOutputStreamInfo(sys->transform, 0, &streamInfo);
224 WLog_Print(h264->log, WLOG_ERROR,
"GetOutputStreamInfo failure: 0x%08" PRIX32
"", hr);
228 hr = sys->MFCreateMemoryBuffer(streamInfo.cbSize, &sys->outputBuffer);
232 WLog_Print(h264->log, WLOG_ERROR,
"MFCreateMemoryBuffer failure: 0x%08" PRIX32
"", hr);
236 hr = sys->outputSample->lpVtbl->AddBuffer(sys->outputSample, sys->outputBuffer);
240 WLog_Print(h264->log, WLOG_ERROR,
"AddBuffer failure: 0x%08" PRIX32
"", hr);
244 sys->outputBuffer->lpVtbl->Release(sys->outputBuffer);
251static size_t getSrcOffset(
const H264_CONTEXT_MF* sys, UINT32 line,
size_t plane)
255 const size_t stride = (plane == 0) ? sys->stride : sys->stride / 2u;
256 const size_t height = (plane == 0) ? sys->frameHeight : sys->frameHeight / 2u;
259 offset += (size_t)sys->stride * sys->frameHeight;
261 offset += stride * height;
263 WINPR_ASSERT(line < height);
264 const size_t row = sys->bottomUp ? height - line - 1u : line;
265 return offset + row * stride;
269static HRESULT mf_process_output(H264_CONTEXT_MF* sys)
272 MFT_OUTPUT_DATA_BUFFER buffer = WINPR_C_ARRAY_INIT;
275 WINPR_ASSERT(sys->transform);
277 buffer.pSample = sys->outputSample;
279 sys->transform->lpVtbl->ProcessOutput(sys->transform, 0, 1, &buffer, &status);
281 buffer.pEvents->lpVtbl->Release(buffer.pEvents);
286static int mf_decompress(H264_CONTEXT* WINPR_RESTRICT h264,
const BYTE* WINPR_RESTRICT pSrcData,
290 BYTE* pbBuffer =
nullptr;
291 DWORD cbMaxLength = 0;
292 DWORD cbCurrentLength = 0;
293 IMFSample* inputSample =
nullptr;
294 IMFMediaBuffer* inputBuffer =
nullptr;
295 IMFMediaBuffer* outputBuffer =
nullptr;
298 WINPR_ASSERT(pSrcData);
300 H264_CONTEXT_MF* sys = (H264_CONTEXT_MF*)h264->pSystemData;
302 UINT32* iStride = h264->iStride;
303 BYTE** pYUVData = h264->pYUVData;
304 HRESULT hr = sys->MFCreateMemoryBuffer(SrcSize, &inputBuffer);
308 WLog_Print(h264->log, WLOG_ERROR,
"MFCreateMemoryBuffer failure: 0x%08" PRIX32
"", hr);
312 hr = inputBuffer->lpVtbl->Lock(inputBuffer, &pbBuffer, &cbMaxLength, &cbCurrentLength);
316 WLog_Print(h264->log, WLOG_ERROR,
"Lock failure: 0x%08" PRIX32
"", hr);
320 CopyMemory(pbBuffer, pSrcData, SrcSize);
321 hr = inputBuffer->lpVtbl->SetCurrentLength(inputBuffer, SrcSize);
325 WLog_Print(h264->log, WLOG_ERROR,
"SetCurrentLength failure: 0x%08" PRIX32
"", hr);
329 hr = inputBuffer->lpVtbl->Unlock(inputBuffer);
333 WLog_Print(h264->log, WLOG_ERROR,
"Unlock failure: 0x%08" PRIX32
"", hr);
337 hr = sys->MFCreateSample(&inputSample);
341 WLog_Print(h264->log, WLOG_ERROR,
"MFCreateSample failure: 0x%08" PRIX32
"", hr);
345 hr = inputSample->lpVtbl->AddBuffer(inputSample, inputBuffer);
349 WLog_Print(h264->log, WLOG_ERROR,
"AddBuffer failure: 0x%08" PRIX32
"", hr);
353 hr = sys->transform->lpVtbl->ProcessInput(sys->transform, 0, inputSample, 0);
357 WLog_Print(h264->log, WLOG_ERROR,
"ProcessInput failure: 0x%08" PRIX32
"", hr);
361 hr = mf_create_output_sample(h264, sys);
365 WLog_Print(h264->log, WLOG_ERROR,
"mf_create_output_sample failure: 0x%08" PRIX32
"", hr);
369 hr = mf_process_output(sys);
371 if (hr == MF_E_TRANSFORM_STREAM_CHANGE)
373 UINT64 frameSize = 0;
377 sys->outputType->lpVtbl->Release(sys->outputType);
378 sys->outputType =
nullptr;
381 hr = mf_find_output_type(sys, &sMFVideoFormat_IYUV, &sys->outputType);
385 WLog_Print(h264->log, WLOG_ERROR,
"mf_find_output_type failure: 0x%08" PRIX32
"", hr);
389 hr = sys->transform->lpVtbl->SetOutputType(sys->transform, 0, sys->outputType, 0);
393 WLog_Print(h264->log, WLOG_ERROR,
"SetOutputType failure: 0x%08" PRIX32
"", hr);
397 hr = mf_create_output_sample(h264, sys);
401 WLog_Print(h264->log, WLOG_ERROR,
"mf_create_output_sample failure: 0x%08" PRIX32
"",
406 hr = sys->outputType->lpVtbl->GetUINT64(sys->outputType, &sMF_MT_FRAME_SIZE, &frameSize);
410 WLog_Print(h264->log, WLOG_ERROR,
411 "GetUINT64(MF_MT_FRAME_SIZE) failure: 0x%08" PRIX32
"", hr);
415 sys->frameWidth = (UINT32)(frameSize >> 32);
416 sys->frameHeight = (UINT32)frameSize;
417 hr = sys->outputType->lpVtbl->GetUINT32(sys->outputType, &sMF_MT_DEFAULT_STRIDE,
422 WLog_Print(h264->log, WLOG_ERROR,
423 "GetUINT32(MF_MT_DEFAULT_STRIDE) failure: 0x%08" PRIX32
"", hr);
427 const INT32 istride = (INT32)sys->stride;
428 sys->bottomUp = (istride < 0);
430 sys->stride = istride * -1;
432 if (!avc420_ensure_buffer(h264, sys->stride, sys->frameWidth, sys->frameHeight))
436 hr = mf_process_output(sys);
439 if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
443 WLog_Print(h264->log, WLOG_ERROR,
"ProcessOutput failure: 0x%08" PRIX32
"", hr);
448 BYTE* buffer =
nullptr;
449 DWORD bufferCount = 0;
450 DWORD cbMaxLength = 0;
451 DWORD cbCurrentLength = 0;
452 hr = sys->outputSample->lpVtbl->GetBufferCount(sys->outputSample, &bufferCount);
456 WLog_Print(h264->log, WLOG_ERROR,
"GetBufferCount failure: 0x%08" PRIX32
"", hr);
460 hr = sys->outputSample->lpVtbl->GetBufferByIndex(sys->outputSample, 0, &outputBuffer);
464 WLog_Print(h264->log, WLOG_ERROR,
"GetBufferByIndex failure: 0x%08" PRIX32
"", hr);
468 hr = outputBuffer->lpVtbl->Lock(outputBuffer, &buffer, &cbMaxLength, &cbCurrentLength);
472 WLog_Print(h264->log, WLOG_ERROR,
"Lock failure: 0x%08" PRIX32
"", hr);
476 if ((sys->stride == 0) || (cbCurrentLength / 3u / sys->stride < sys->frameHeight / 2u))
478 WLog_Print(h264->log, WLOG_ERROR,
"output buffer too small: %" PRIu32
"",
480 (void)outputBuffer->lpVtbl->Unlock(outputBuffer);
488 for (UINT32 x = 0; x < sys->frameHeight; x++)
490 const size_t srcOffset = getSrcOffset(sys, x, 0);
491 const UINT32 dstOffset = (UINT32)iStride[0] * x;
492 CopyMemory(&pYUVData[0][dstOffset], &buffer[srcOffset], sys->stride);
494 for (UINT32 x = 0; x < sys->frameHeight / 2; x++)
496 const size_t srcUOffset = getSrcOffset(sys, x, 1);
497 const size_t srcVOffset = getSrcOffset(sys, x, 2);
498 const UINT32 dstUOffset = (UINT32)iStride[1] * x;
499 const UINT32 dstVOffset = (UINT32)iStride[2] * x;
500 CopyMemory(&pYUVData[1][dstUOffset], &buffer[srcUOffset], sys->stride / 2u);
501 CopyMemory(&pYUVData[2][dstVOffset], &buffer[srcVOffset], sys->stride / 2u);
504 hr = outputBuffer->lpVtbl->Unlock(outputBuffer);
508 WLog_Print(h264->log, WLOG_ERROR,
"Unlock failure: 0x%08" PRIX32
"", hr);
512 h264->YUVWidth = sys->frameWidth;
513 h264->YUVHeight = sys->frameHeight;
519 outputBuffer->lpVtbl->Release(outputBuffer);
521 inputSample->lpVtbl->Release(inputSample);
523 inputBuffer->lpVtbl->Release(inputBuffer);
525 WLog_Print(h264->log, WLOG_ERROR,
"decompression failed");
530static HRESULT mf_encoder_set_value(H264_CONTEXT* h264, H264_CONTEXT_MF* sys,
const GUID* guid,
531 const char* name, VARTYPE vt, UINT64 value)
533 VARIANT var = WINPR_C_ARRAY_INIT;
541 return E_NOINTERFACE;
545 var.n1.n2.n3.ullVal = value;
546 else if (vt == VT_BOOL)
547 var.n1.n2.n3.boolVal = value ? VARIANT_TRUE : VARIANT_FALSE;
549 var.n1.n2.n3.ulVal = (ULONG)value;
551 const HRESULT hr = sys->codecApi->lpVtbl->SetValue(sys->codecApi, guid, &var);
553 WLog_Print(h264->log, WLOG_DEBUG,
"ICodecAPI::SetValue(%s) failure: 0x%08" PRIX32
"", name,
564static BOOL mf_encoder_check_value(H264_CONTEXT* h264, H264_CONTEXT_MF* sys,
const GUID* guid,
565 const char* name, HRESULT hr)
575 if ((hr != E_NOTIMPL) && (hr != E_NOINTERFACE) && sys->codecApi &&
576 (sys->codecApi->lpVtbl->IsSupported(sys->codecApi, guid) == S_OK))
578 WLog_Print(h264->log, WLOG_ERROR,
579 "H264 encoder refuses ICodecAPI property %s: 0x%08" PRIX32
"", name, (UINT32)hr);
583 WLog_Print(h264->log, WLOG_DEBUG,
"H264 encoder does not support %s, using its default", name);
588static BOOL mf_encoder_set_optional(H264_CONTEXT* h264, H264_CONTEXT_MF* sys,
const GUID* guid,
589 const char* name, VARTYPE vt, UINT64 value)
591 const HRESULT hr = mf_encoder_set_value(h264, sys, guid, name, vt, value);
592 return mf_encoder_check_value(h264, sys, guid, name, hr);
595static void mf_encoder_release(H264_CONTEXT_MF* sys)
599 if (sys->outputSample)
601 sys->outputSample->lpVtbl->Release(sys->outputSample);
602 sys->outputSample =
nullptr;
606 sys->inputType->lpVtbl->Release(sys->inputType);
607 sys->inputType =
nullptr;
611 sys->outputType->lpVtbl->Release(sys->outputType);
612 sys->outputType =
nullptr;
616 sys->codecApi->lpVtbl->Release(sys->codecApi);
617 sys->codecApi =
nullptr;
621 sys->transform->lpVtbl->Release(sys->transform);
622 sys->transform =
nullptr;
624 sys->encStreaming = FALSE;
628static BOOL mf_encoder_create(H264_CONTEXT* h264, H264_CONTEXT_MF* sys)
633 HRESULT hr = CoCreateInstance(&sCLSID_CMSH264EncoderMFT,
nullptr, CLSCTX_INPROC_SERVER,
634 &sIID_IMFTransform, (
void**)&sys->transform);
638 WLog_Print(h264->log, WLOG_DEBUG,
639 "CoCreateInstance(CLSID_CMSH264EncoderMFT) failure: 0x%08" PRIX32
"",
645 hr = sys->transform->lpVtbl->QueryInterface(sys->transform, &sIID_ICodecAPI,
646 (
void**)&sys->codecApi);
649 WLog_Print(h264->log, WLOG_WARN,
650 "H264 encoder has no ICodecAPI (0x%08" PRIX32
"), using its defaults",
652 sys->codecApi =
nullptr;
659static HRESULT mf_encoder_media_type(H264_CONTEXT_MF* sys, IMFMediaType** ppType,
660 const GUID* subtype, UINT32 width, UINT32 height,
661 UINT32 frameRate, UINT32 bitRate)
663 IMFMediaType* type =
nullptr;
666 WINPR_ASSERT(sys->MFCreateMediaType);
667 WINPR_ASSERT(ppType);
668 WINPR_ASSERT(subtype);
670 HRESULT hr = sys->MFCreateMediaType(&type);
675 hr = type->lpVtbl->SetGUID(type, &sMF_MT_MAJOR_TYPE, &sMFMediaType_Video);
677 hr = type->lpVtbl->SetGUID(type, &sMF_MT_SUBTYPE, subtype);
679 hr = type->lpVtbl->SetUINT64(type, &sMF_MT_FRAME_SIZE, ((UINT64)width << 32) | height);
681 hr = type->lpVtbl->SetUINT64(type, &sMF_MT_FRAME_RATE, ((UINT64)frameRate << 32) | 1);
683 hr = type->lpVtbl->SetUINT64(type, &sMF_MT_PIXEL_ASPECT_RATIO, ((UINT64)1 << 32) | 1);
686 type->lpVtbl->SetUINT32(type, &sMF_MT_INTERLACE_MODE, FREERDP_MF_INTERLACE_PROGRESSIVE);
688 if (SUCCEEDED(hr) && IsEqualGUID(subtype, &sMFVideoFormat_H264))
690 hr = type->lpVtbl->SetUINT32(type, &sMF_MT_AVG_BITRATE, bitRate);
693 hr = type->lpVtbl->SetUINT32(type, &sMF_MT_MPEG2_PROFILE, FREERDP_MF_PROFILE_BASELINE);
698 type->lpVtbl->Release(type);
707static BOOL mf_encoder_rate_control(H264_CONTEXT* h264, H264_CONTEXT_MF* sys, UINT32 bitRate)
715 HRESULT hr = mf_encoder_set_value(h264, sys, &sCODECAPI_AVLowLatencyMode,
"AVLowLatencyMode",
718 hr = mf_encoder_set_value(h264, sys, &sCODECAPI_AVLowLatencyMode,
"AVLowLatencyMode",
720 if (!mf_encoder_check_value(h264, sys, &sCODECAPI_AVLowLatencyMode,
"AVLowLatencyMode", hr))
727 if (!mf_encoder_set_optional(h264, sys, &sCODECAPI_AVEncMPVGOPSize,
"AVEncMPVGOPSize", VT_UI4,
731 switch (h264->RateControlMode)
733 case H264_RATECONTROL_CQP:
735 if (!mf_encoder_set_optional(h264, sys, &sCODECAPI_AVEncCommonRateControlMode,
736 "AVEncCommonRateControlMode", VT_UI4,
737 FREERDP_MF_RC_QUALITY))
742 if (FAILED(mf_encoder_set_value(h264, sys, &sCODECAPI_AVEncVideoEncodeQP,
743 "AVEncVideoEncodeQP", VT_UI8, h264->QP & 0xFFFF)))
745 const UINT32 qp = MIN(h264->QP, 51);
746 if (!mf_encoder_set_optional(h264, sys, &sCODECAPI_AVEncCommonQuality,
747 "AVEncCommonQuality", VT_UI4, 100 - (qp * 100 / 51)))
753 case H264_RATECONTROL_VBR:
757 if (!mf_encoder_set_optional(h264, sys, &sCODECAPI_AVEncCommonRateControlMode,
758 "AVEncCommonRateControlMode", VT_UI4, FREERDP_MF_RC_CBR))
760 if (!mf_encoder_set_optional(h264, sys, &sCODECAPI_AVEncCommonMeanBitRate,
761 "AVEncCommonMeanBitRate", VT_UI4, bitRate))
770static BOOL mf_encoder_configure(H264_CONTEXT* h264, H264_CONTEXT_MF* sys, UINT32 width,
771 UINT32 height, UINT32 frameRate, UINT32 bitRate)
773 MFT_OUTPUT_STREAM_INFO streamInfo = WINPR_C_ARRAY_INIT;
779 if (sys->encStreaming || !sys->transform)
781 mf_encoder_release(sys);
782 if (!mf_encoder_create(h264, sys))
787 if (!mf_encoder_rate_control(h264, sys, bitRate))
791 HRESULT hr = mf_encoder_media_type(sys, &sys->outputType, &sMFVideoFormat_H264, width, height,
795 WLog_Print(h264->log, WLOG_ERROR,
"encoder output type failure: 0x%08" PRIX32
"",
800 hr = sys->transform->lpVtbl->SetOutputType(sys->transform, 0, sys->outputType, 0);
803 WLog_Print(h264->log, WLOG_ERROR,
"encoder SetOutputType failure: 0x%08" PRIX32
"",
808 hr = mf_encoder_media_type(sys, &sys->inputType, &sMFVideoFormat_NV12, width, height, frameRate,
812 WLog_Print(h264->log, WLOG_ERROR,
"encoder input type failure: 0x%08" PRIX32
"",
817 hr = sys->transform->lpVtbl->SetInputType(sys->transform, 0, sys->inputType, 0);
820 WLog_Print(h264->log, WLOG_ERROR,
"encoder SetInputType failure: 0x%08" PRIX32
"",
825 hr = sys->transform->lpVtbl->GetOutputStreamInfo(sys->transform, 0, &streamInfo);
828 WLog_Print(h264->log, WLOG_ERROR,
"encoder GetOutputStreamInfo failure: 0x%08" PRIX32
"",
833 sys->encOutputFlags = streamInfo.dwFlags;
834 sys->encOutputSize = streamInfo.cbSize;
835 if (sys->encOutputSize == 0)
836 sys->encOutputSize = (DWORD)(3ull * width * height / 2);
838 if ((sys->encOutputFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) == 0)
840 IMFMediaBuffer* buffer =
nullptr;
842 hr = sys->MFCreateSample(&sys->outputSample);
844 hr = sys->MFCreateMemoryBuffer(sys->encOutputSize, &buffer);
846 hr = sys->outputSample->lpVtbl->AddBuffer(sys->outputSample, buffer);
848 buffer->lpVtbl->Release(buffer);
851 WLog_Print(h264->log, WLOG_ERROR,
"encoder output sample failure: 0x%08" PRIX32
"",
857 hr = sys->transform->lpVtbl->ProcessMessage(sys->transform, MFT_MESSAGE_NOTIFY_BEGIN_STREAMING,
860 hr = sys->transform->lpVtbl->ProcessMessage(sys->transform,
861 MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
864 WLog_Print(h264->log, WLOG_ERROR,
"encoder ProcessMessage failure: 0x%08" PRIX32
"",
869 sys->encStreaming = TRUE;
870 sys->encWidth = width;
871 sys->encHeight = height;
872 sys->encFrameRate = frameRate;
873 sys->encBitRate = bitRate;
874 sys->encQP = h264->QP;
875 sys->encRateControl = h264->RateControlMode;
876 sys->encSampleTime = 0;
878 WLog_Print(h264->log, WLOG_DEBUG,
879 "MediaFoundation H264 encoder: %" PRIu32
"x%" PRIu32
" %" PRIu32
880 " fps, output flags 0x%08" PRIx32
"",
881 width, height, frameRate, (UINT32)sys->encOutputFlags);
885 mf_encoder_release(sys);
890static BOOL mf_encoder_append(H264_CONTEXT* h264, H264_CONTEXT_MF* sys, IMFSample* sample)
892 IMFMediaBuffer* buffer =
nullptr;
893 BYTE* data =
nullptr;
900 WINPR_ASSERT(sample);
902 HRESULT hr = sample->lpVtbl->ConvertToContiguousBuffer(sample, &buffer);
905 WLog_Print(h264->log, WLOG_ERROR,
"ConvertToContiguousBuffer failure: 0x%08" PRIX32
"",
910 hr = buffer->lpVtbl->Lock(buffer, &data, &maxLength, &length);
913 WLog_Print(h264->log, WLOG_ERROR,
"Lock failure: 0x%08" PRIX32
"", (UINT32)hr);
918 const size_t needed = sys->encDataLength + length;
919 if (needed > sys->encDataSize)
921 BYTE* tmp = realloc(sys->encData, needed);
925 sys->encDataSize = needed;
928 if (needed <= sys->encDataSize)
931 memcpy(&sys->encData[sys->encDataLength], data, length);
932 sys->encDataLength = needed;
937 hr = buffer->lpVtbl->Unlock(buffer);
939 WLog_Print(h264->log, WLOG_ERROR,
"Unlock failure: 0x%08" PRIX32
"", (UINT32)hr);
941 WLog_Print(h264->log, WLOG_ERROR,
"encoder output: out of memory (%" PRIuz
" bytes)",
947 buffer->lpVtbl->Release(buffer);
953static HRESULT mf_encoder_drain(H264_CONTEXT* h264, H264_CONTEXT_MF* sys)
957 WINPR_ASSERT(sys->transform);
959 const BOOL provides = (sys->encOutputFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) != 0;
961 for (
size_t i = 0; i < 16; i++)
964 MFT_OUTPUT_DATA_BUFFER output = WINPR_C_ARRAY_INIT;
969 IMFMediaBuffer* buffer =
nullptr;
971 WINPR_ASSERT(sys->outputSample);
972 HRESULT hr = sys->outputSample->lpVtbl->GetBufferByIndex(sys->outputSample, 0, &buffer);
975 hr = buffer->lpVtbl->SetCurrentLength(buffer, 0);
976 buffer->lpVtbl->Release(buffer);
980 WLog_Print(h264->log, WLOG_ERROR,
981 "encoder output buffer reset failure: 0x%08" PRIX32
"", (UINT32)hr);
984 output.pSample = sys->outputSample;
988 sys->transform->lpVtbl->ProcessOutput(sys->transform, 0, 1, &output, &status);
990 output.pEvents->lpVtbl->Release(output.pEvents);
992 if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
997 if (provides && output.pSample)
998 output.pSample->lpVtbl->Release(output.pSample);
999 WLog_Print(h264->log, WLOG_ERROR,
"encoder ProcessOutput failure: 0x%08" PRIX32
"",
1004 const BOOL ok = output.pSample ? mf_encoder_append(h264, sys, output.pSample) : TRUE;
1005 if (provides && output.pSample)
1006 output.pSample->lpVtbl->Release(output.pSample);
1015static BOOL mf_encoder_update(H264_CONTEXT* h264, H264_CONTEXT_MF* sys, UINT32 width, UINT32 height,
1016 UINT32 frameRate, UINT32 bitRate)
1021 if (!sys->encStreaming || (sys->encWidth != width) || (sys->encHeight != height) ||
1022 (sys->encFrameRate != frameRate) || (sys->encRateControl != h264->RateControlMode))
1023 return mf_encoder_configure(h264, sys, width, height, frameRate, bitRate);
1026 if ((h264->RateControlMode == H264_RATECONTROL_VBR) && (sys->encBitRate != bitRate))
1028 if (FAILED(mf_encoder_set_value(h264, sys, &sCODECAPI_AVEncCommonMeanBitRate,
1029 "AVEncCommonMeanBitRate", VT_UI4, bitRate)))
1030 return mf_encoder_configure(h264, sys, width, height, frameRate, bitRate);
1031 sys->encBitRate = bitRate;
1034 if ((h264->RateControlMode == H264_RATECONTROL_CQP) && (sys->encQP != h264->QP))
1036 if (FAILED(mf_encoder_set_value(h264, sys, &sCODECAPI_AVEncVideoEncodeQP,
1037 "AVEncVideoEncodeQP", VT_UI8, h264->QP & 0xFFFF)))
1038 return mf_encoder_configure(h264, sys, width, height, frameRate, bitRate);
1039 sys->encQP = h264->QP;
1046static void mf_encoder_copy_nv12(BYTE* WINPR_RESTRICT dst,
const BYTE** WINPR_RESTRICT pSrcYuv,
1047 const UINT32* WINPR_RESTRICT pStride, UINT32 width, UINT32 height)
1050 WINPR_ASSERT(pSrcYuv);
1051 WINPR_ASSERT(pSrcYuv[0]);
1052 WINPR_ASSERT(pSrcYuv[1]);
1053 WINPR_ASSERT(pSrcYuv[2]);
1054 WINPR_ASSERT(pStride);
1056 for (UINT32 y = 0; y < height; y++)
1057 memcpy(&dst[1ull * y * width], &pSrcYuv[0][1ull * y * pStride[0]], width);
1059 BYTE* uv = &dst[1ull * width * height];
1060 for (UINT32 y = 0; y < height / 2; y++)
1062 const BYTE* u = &pSrcYuv[1][1ull * y * pStride[1]];
1063 const BYTE* v = &pSrcYuv[2][1ull * y * pStride[2]];
1064 BYTE* line = &uv[1ull * y * width];
1066 for (UINT32 x = 0; x < width / 2; x++)
1069 line[2 * x + 1] = v[x];
1075static int mf_compress(H264_CONTEXT* WINPR_RESTRICT h264,
const BYTE** WINPR_RESTRICT ppSrcYuv,
1076 const UINT32* WINPR_RESTRICT pStride, BYTE** WINPR_RESTRICT ppDstData,
1077 UINT32* WINPR_RESTRICT pDstSize)
1080 BYTE* data =
nullptr;
1081 DWORD maxLength = 0;
1082 DWORD curLength = 0;
1083 IMFSample* sample =
nullptr;
1084 IMFMediaBuffer* buffer =
nullptr;
1087 WINPR_ASSERT(ppSrcYuv);
1088 WINPR_ASSERT(pStride);
1089 WINPR_ASSERT(ppDstData);
1090 WINPR_ASSERT(pDstSize);
1092 H264_CONTEXT_MF* sys = (H264_CONTEXT_MF*)h264->pSystemData;
1093 if (!sys || !ppSrcYuv[0] || !ppSrcYuv[1] || !ppSrcYuv[2])
1096 if ((h264->width == 0) || (h264->height == 0) || (h264->width > UINT16_MAX) ||
1097 (h264->height > UINT16_MAX))
1102 const UINT32 width = (h264->width + 1) & ~1u;
1103 const UINT32 height = (h264->height + 1) & ~1u;
1104 const UINT32 frameRate = (h264->FrameRate > 0) ? h264->FrameRate : 30;
1105 const UINT32 bitRate = (h264->BitRate > 0) ? h264->BitRate : 1000000;
1108 const UINT64 size64 = 3ull * width * height / 2;
1109 if (size64 > UINT32_MAX)
1111 const DWORD size = (DWORD)size64;
1113 if (!mf_encoder_update(h264, sys, width, height, frameRate, bitRate))
1116 HRESULT hr = sys->MFCreateMemoryBuffer(size, &buffer);
1119 WLog_Print(h264->log, WLOG_ERROR,
"MFCreateMemoryBuffer failure: 0x%08" PRIX32
"",
1124 hr = buffer->lpVtbl->Lock(buffer, &data, &maxLength, &curLength);
1127 WLog_Print(h264->log, WLOG_ERROR,
"Lock failure: 0x%08" PRIX32
"", (UINT32)hr);
1131 const BOOL fits = (maxLength >= size);
1133 mf_encoder_copy_nv12(data, ppSrcYuv, pStride, width, height);
1136 hr = buffer->lpVtbl->Unlock(buffer);
1139 WLog_Print(h264->log, WLOG_ERROR,
"Unlock failure: 0x%08" PRIX32
"", (UINT32)hr);
1144 WLog_Print(h264->log, WLOG_ERROR,
1145 "encoder input buffer too small: %" PRIu32
" < %" PRIu32
" bytes",
1146 (UINT32)maxLength, (UINT32)size);
1150 hr = buffer->lpVtbl->SetCurrentLength(buffer, size);
1152 hr = sys->MFCreateSample(&sample);
1154 hr = sample->lpVtbl->AddBuffer(sample, buffer);
1157 const LONGLONG duration = 10000000LL / frameRate;
1159 hr = sample->lpVtbl->SetSampleTime(sample, sys->encSampleTime);
1161 hr = sample->lpVtbl->SetSampleDuration(sample, duration);
1164 WLog_Print(h264->log, WLOG_ERROR,
"encoder input sample failure: 0x%08" PRIX32
"",
1168 sys->encSampleTime += duration;
1170 sys->encDataLength = 0;
1171 hr = sys->transform->lpVtbl->ProcessInput(sys->transform, 0, sample, 0);
1172 if (hr == MF_E_NOTACCEPTING)
1175 hr = mf_encoder_drain(h264, sys);
1177 hr = sys->transform->lpVtbl->ProcessInput(sys->transform, 0, sample, 0);
1181 WLog_Print(h264->log, WLOG_ERROR,
"encoder ProcessInput failure: 0x%08" PRIX32
"",
1186 hr = mf_encoder_drain(h264, sys);
1190 if (sys->encDataLength > UINT32_MAX)
1193 if (sys->encDataLength == 0)
1199 if ((sys->encHeld == 4) && !sys->encWarned)
1201 WLog_Print(h264->log, WLOG_WARN,
1202 "MediaFoundation H264 encoder holds frames back (low latency mode not "
1203 "honoured), the client will see changes late");
1204 sys->encWarned = TRUE;
1211 *ppDstData = sys->encData;
1212 *pDstSize = (UINT32)sys->encDataLength;
1217 sample->lpVtbl->Release(sample);
1219 buffer->lpVtbl->Release(buffer);
1224static BOOL mf_plat_loaded(H264_CONTEXT_MF* sys)
1227 return sys->MFStartup && sys->MFShutdown && sys->MFCreateSample && sys->MFCreateMemoryBuffer &&
1228 sys->MFCreateMediaType;
1231static void mf_uninit(H264_CONTEXT* h264)
1235 H264_CONTEXT_MF* sys = (H264_CONTEXT_MF*)h264->pSystemData;
1241 sys->transform->lpVtbl->Release(sys->transform);
1242 sys->transform =
nullptr;
1247 sys->codecApi->lpVtbl->Release(sys->codecApi);
1248 sys->codecApi =
nullptr;
1253 sys->inputType->lpVtbl->Release(sys->inputType);
1254 sys->inputType =
nullptr;
1257 if (sys->outputType)
1259 sys->outputType->lpVtbl->Release(sys->outputType);
1260 sys->outputType =
nullptr;
1263 if (sys->outputSample)
1265 sys->outputSample->lpVtbl->Release(sys->outputSample);
1266 sys->outputSample =
nullptr;
1271 if (mf_plat_loaded(sys))
1274 const HRESULT hr = sys->MFShutdown();
1276 WLog_Print(h264->log, WLOG_WARN,
"MFShutdown failure: 0x%08" PRIX32
"",
1280 FreeLibrary(sys->mfplat);
1281 sys->mfplat =
nullptr;
1283 if (mf_plat_loaded(sys))
1287 for (
size_t x = 0; x <
sizeof(h264->pYUVData) /
sizeof(h264->pYUVData[0]); x++)
1288 winpr_aligned_free(h264->pYUVData[x]);
1290 memset(h264->pYUVData, 0,
sizeof(h264->pYUVData));
1291 memset(h264->iStride, 0,
sizeof(h264->iStride));
1295 h264->pSystemData =
nullptr;
1300static BOOL mf_init(H264_CONTEXT* h264)
1306 H264_CONTEXT_MF* sys = (H264_CONTEXT_MF*)calloc(1,
sizeof(H264_CONTEXT_MF));
1311 h264->pSystemData = (
void*)sys;
1314 sys->mfplat = LoadLibraryA(
"mfplat.dll");
1319 sys->MFStartup = GetProcAddressAs(sys->mfplat,
"MFStartup", pfnMFStartup);
1320 sys->MFShutdown = GetProcAddressAs(sys->mfplat,
"MFShutdown", pfnMFShutdown);
1321 sys->MFCreateSample = GetProcAddressAs(sys->mfplat,
"MFCreateSample", pfnMFCreateSample);
1322 sys->MFCreateMemoryBuffer =
1323 GetProcAddressAs(sys->mfplat,
"MFCreateMemoryBuffer", pfnMFCreateMemoryBuffer);
1324 sys->MFCreateMediaType =
1325 GetProcAddressAs(sys->mfplat,
"MFCreateMediaType", pfnMFCreateMediaType);
1327 if (!mf_plat_loaded(sys))
1330 CoInitializeEx(
nullptr, COINIT_APARTMENTTHREADED);
1332 if (h264->Compressor)
1334 hr = sys->MFStartup(MF_VERSION, 0);
1338 WLog_Print(h264->log, WLOG_ERROR,
"MFStartup failure: 0x%08" PRIX32
"", hr);
1345 if (!mf_encoder_create(h264, sys))
1347 WLog_Print(h264->log, WLOG_WARN,
"MediaFoundation H264 encoder not available");
1353 VARIANT var = WINPR_C_ARRAY_INIT;
1354 hr = sys->MFStartup(MF_VERSION, 0);
1358 WLog_Print(h264->log, WLOG_ERROR,
"MFStartup failure: 0x%08" PRIX32
"", hr);
1362 hr = CoCreateInstance(&sCLSID_CMSH264DecoderMFT,
nullptr, CLSCTX_INPROC_SERVER,
1363 &sIID_IMFTransform, (
void**)&sys->transform);
1367 WLog_Print(h264->log, WLOG_ERROR,
1368 "CoCreateInstance(CLSID_CMSH264DecoderMFT) failure: 0x%08" PRIX32
"", hr);
1372 hr = sys->transform->lpVtbl->QueryInterface(sys->transform, &sIID_ICodecAPI,
1373 (
void**)&sys->codecApi);
1377 WLog_Print(h264->log, WLOG_ERROR,
1378 "QueryInterface(IID_ICodecAPI) failure: 0x%08" PRIX32
"", hr);
1382 var.n1.n2.vt = VT_UI4;
1383 var.n1.n2.n3.ulVal = 1;
1384 hr = sys->codecApi->lpVtbl->SetValue(sys->codecApi, &sCODECAPI_AVLowLatencyMode, &var);
1388 WLog_Print(h264->log, WLOG_ERROR,
1389 "SetValue(CODECAPI_AVLowLatencyMode) failure: 0x%08" PRIX32
"", hr);
1393 hr = sys->MFCreateMediaType(&sys->inputType);
1397 WLog_Print(h264->log, WLOG_ERROR,
"MFCreateMediaType failure: 0x%08" PRIX32
"", hr);
1401 hr = sys->inputType->lpVtbl->SetGUID(sys->inputType, &sMF_MT_MAJOR_TYPE,
1402 &sMFMediaType_Video);
1406 WLog_Print(h264->log, WLOG_ERROR,
"SetGUID(MF_MT_MAJOR_TYPE) failure: 0x%08" PRIX32
"",
1411 hr = sys->inputType->lpVtbl->SetGUID(sys->inputType, &sMF_MT_SUBTYPE, &sMFVideoFormat_H264);
1415 WLog_Print(h264->log, WLOG_ERROR,
"SetGUID(MF_MT_SUBTYPE) failure: 0x%08" PRIX32
"",
1420 hr = sys->transform->lpVtbl->SetInputType(sys->transform, 0, sys->inputType, 0);
1424 WLog_Print(h264->log, WLOG_ERROR,
"SetInputType failure: 0x%08" PRIX32
"", hr);
1428 hr = mf_find_output_type(sys, &sMFVideoFormat_IYUV, &sys->outputType);
1432 WLog_Print(h264->log, WLOG_ERROR,
"mf_find_output_type failure: 0x%08" PRIX32
"", hr);
1436 hr = sys->transform->lpVtbl->SetOutputType(sys->transform, 0, sys->outputType, 0);
1440 WLog_Print(h264->log, WLOG_ERROR,
"SetOutputType failure: 0x%08" PRIX32
"", hr);
1444 hr = mf_create_output_sample(h264, sys);
1448 WLog_Print(h264->log, WLOG_ERROR,
"mf_create_output_sample failure: 0x%08" PRIX32
"",
1456 WLog_Print(h264->log, WLOG_ERROR,
"mf_init failure");
1461const H264_CONTEXT_SUBSYSTEM g_Subsystem_MF = {
"MediaFoundation", mf_init, mf_uninit,
1462 mf_decompress, mf_compress };