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))
723 if (!mf_encoder_set_optional(h264, sys, &sCODECAPI_AVEncMPVGOPSize,
"AVEncMPVGOPSize", VT_UI4,
727 switch (h264->RateControlMode)
729 case H264_RATECONTROL_CQP:
731 if (!mf_encoder_set_optional(h264, sys, &sCODECAPI_AVEncCommonRateControlMode,
732 "AVEncCommonRateControlMode", VT_UI4,
733 FREERDP_MF_RC_QUALITY))
738 if (FAILED(mf_encoder_set_value(h264, sys, &sCODECAPI_AVEncVideoEncodeQP,
739 "AVEncVideoEncodeQP", VT_UI8, h264->QP & 0xFFFF)))
741 const UINT32 qp = MIN(h264->QP, 51);
742 if (!mf_encoder_set_optional(h264, sys, &sCODECAPI_AVEncCommonQuality,
743 "AVEncCommonQuality", VT_UI4, 100 - (qp * 100 / 51)))
749 case H264_RATECONTROL_VBR:
753 if (!mf_encoder_set_optional(h264, sys, &sCODECAPI_AVEncCommonRateControlMode,
754 "AVEncCommonRateControlMode", VT_UI4, FREERDP_MF_RC_CBR))
756 if (!mf_encoder_set_optional(h264, sys, &sCODECAPI_AVEncCommonMeanBitRate,
757 "AVEncCommonMeanBitRate", VT_UI4, bitRate))
766static BOOL mf_encoder_configure(H264_CONTEXT* h264, H264_CONTEXT_MF* sys, UINT32 width,
767 UINT32 height, UINT32 frameRate, UINT32 bitRate)
769 MFT_OUTPUT_STREAM_INFO streamInfo = WINPR_C_ARRAY_INIT;
775 if (sys->encStreaming || !sys->transform)
777 mf_encoder_release(sys);
778 if (!mf_encoder_create(h264, sys))
783 if (!mf_encoder_rate_control(h264, sys, bitRate))
787 HRESULT hr = mf_encoder_media_type(sys, &sys->outputType, &sMFVideoFormat_H264, width, height,
791 WLog_Print(h264->log, WLOG_ERROR,
"encoder output type failure: 0x%08" PRIX32
"",
796 hr = sys->transform->lpVtbl->SetOutputType(sys->transform, 0, sys->outputType, 0);
799 WLog_Print(h264->log, WLOG_ERROR,
"encoder SetOutputType failure: 0x%08" PRIX32
"",
804 hr = mf_encoder_media_type(sys, &sys->inputType, &sMFVideoFormat_NV12, width, height, frameRate,
808 WLog_Print(h264->log, WLOG_ERROR,
"encoder input type failure: 0x%08" PRIX32
"",
813 hr = sys->transform->lpVtbl->SetInputType(sys->transform, 0, sys->inputType, 0);
816 WLog_Print(h264->log, WLOG_ERROR,
"encoder SetInputType failure: 0x%08" PRIX32
"",
821 hr = sys->transform->lpVtbl->GetOutputStreamInfo(sys->transform, 0, &streamInfo);
824 WLog_Print(h264->log, WLOG_ERROR,
"encoder GetOutputStreamInfo failure: 0x%08" PRIX32
"",
829 sys->encOutputFlags = streamInfo.dwFlags;
830 sys->encOutputSize = streamInfo.cbSize;
831 if (sys->encOutputSize == 0)
832 sys->encOutputSize = (DWORD)(3ull * width * height / 2);
834 if ((sys->encOutputFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) == 0)
836 IMFMediaBuffer* buffer =
nullptr;
838 hr = sys->MFCreateSample(&sys->outputSample);
840 hr = sys->MFCreateMemoryBuffer(sys->encOutputSize, &buffer);
842 hr = sys->outputSample->lpVtbl->AddBuffer(sys->outputSample, buffer);
844 buffer->lpVtbl->Release(buffer);
847 WLog_Print(h264->log, WLOG_ERROR,
"encoder output sample failure: 0x%08" PRIX32
"",
853 hr = sys->transform->lpVtbl->ProcessMessage(sys->transform, MFT_MESSAGE_NOTIFY_BEGIN_STREAMING,
856 hr = sys->transform->lpVtbl->ProcessMessage(sys->transform,
857 MFT_MESSAGE_NOTIFY_START_OF_STREAM, 0);
860 WLog_Print(h264->log, WLOG_ERROR,
"encoder ProcessMessage failure: 0x%08" PRIX32
"",
865 sys->encStreaming = TRUE;
866 sys->encWidth = width;
867 sys->encHeight = height;
868 sys->encFrameRate = frameRate;
869 sys->encBitRate = bitRate;
870 sys->encQP = h264->QP;
871 sys->encRateControl = h264->RateControlMode;
872 sys->encSampleTime = 0;
874 WLog_Print(h264->log, WLOG_DEBUG,
875 "MediaFoundation H264 encoder: %" PRIu32
"x%" PRIu32
" %" PRIu32
876 " fps, output flags 0x%08" PRIx32
"",
877 width, height, frameRate, (UINT32)sys->encOutputFlags);
881 mf_encoder_release(sys);
886static BOOL mf_encoder_append(H264_CONTEXT* h264, H264_CONTEXT_MF* sys, IMFSample* sample)
888 IMFMediaBuffer* buffer =
nullptr;
889 BYTE* data =
nullptr;
896 WINPR_ASSERT(sample);
898 HRESULT hr = sample->lpVtbl->ConvertToContiguousBuffer(sample, &buffer);
901 WLog_Print(h264->log, WLOG_ERROR,
"ConvertToContiguousBuffer failure: 0x%08" PRIX32
"",
906 hr = buffer->lpVtbl->Lock(buffer, &data, &maxLength, &length);
909 WLog_Print(h264->log, WLOG_ERROR,
"Lock failure: 0x%08" PRIX32
"", (UINT32)hr);
914 const size_t needed = sys->encDataLength + length;
915 if (needed > sys->encDataSize)
917 BYTE* tmp = realloc(sys->encData, needed);
921 sys->encDataSize = needed;
924 if (needed <= sys->encDataSize)
927 memcpy(&sys->encData[sys->encDataLength], data, length);
928 sys->encDataLength = needed;
933 hr = buffer->lpVtbl->Unlock(buffer);
935 WLog_Print(h264->log, WLOG_ERROR,
"Unlock failure: 0x%08" PRIX32
"", (UINT32)hr);
937 WLog_Print(h264->log, WLOG_ERROR,
"encoder output: out of memory (%" PRIuz
" bytes)",
943 buffer->lpVtbl->Release(buffer);
949static HRESULT mf_encoder_drain(H264_CONTEXT* h264, H264_CONTEXT_MF* sys)
953 WINPR_ASSERT(sys->transform);
955 const BOOL provides = (sys->encOutputFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES) != 0;
957 for (
size_t i = 0; i < 16; i++)
960 MFT_OUTPUT_DATA_BUFFER output = WINPR_C_ARRAY_INIT;
965 IMFMediaBuffer* buffer =
nullptr;
967 WINPR_ASSERT(sys->outputSample);
968 HRESULT hr = sys->outputSample->lpVtbl->GetBufferByIndex(sys->outputSample, 0, &buffer);
971 hr = buffer->lpVtbl->SetCurrentLength(buffer, 0);
972 buffer->lpVtbl->Release(buffer);
976 WLog_Print(h264->log, WLOG_ERROR,
977 "encoder output buffer reset failure: 0x%08" PRIX32
"", (UINT32)hr);
980 output.pSample = sys->outputSample;
984 sys->transform->lpVtbl->ProcessOutput(sys->transform, 0, 1, &output, &status);
986 output.pEvents->lpVtbl->Release(output.pEvents);
988 if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
993 if (provides && output.pSample)
994 output.pSample->lpVtbl->Release(output.pSample);
995 WLog_Print(h264->log, WLOG_ERROR,
"encoder ProcessOutput failure: 0x%08" PRIX32
"",
1000 const BOOL ok = output.pSample ? mf_encoder_append(h264, sys, output.pSample) : TRUE;
1001 if (provides && output.pSample)
1002 output.pSample->lpVtbl->Release(output.pSample);
1011static BOOL mf_encoder_update(H264_CONTEXT* h264, H264_CONTEXT_MF* sys, UINT32 width, UINT32 height,
1012 UINT32 frameRate, UINT32 bitRate)
1017 if (!sys->encStreaming || (sys->encWidth != width) || (sys->encHeight != height) ||
1018 (sys->encFrameRate != frameRate) || (sys->encRateControl != h264->RateControlMode))
1019 return mf_encoder_configure(h264, sys, width, height, frameRate, bitRate);
1022 if ((h264->RateControlMode == H264_RATECONTROL_VBR) && (sys->encBitRate != bitRate))
1024 if (FAILED(mf_encoder_set_value(h264, sys, &sCODECAPI_AVEncCommonMeanBitRate,
1025 "AVEncCommonMeanBitRate", VT_UI4, bitRate)))
1026 return mf_encoder_configure(h264, sys, width, height, frameRate, bitRate);
1027 sys->encBitRate = bitRate;
1030 if ((h264->RateControlMode == H264_RATECONTROL_CQP) && (sys->encQP != h264->QP))
1032 if (FAILED(mf_encoder_set_value(h264, sys, &sCODECAPI_AVEncVideoEncodeQP,
1033 "AVEncVideoEncodeQP", VT_UI8, h264->QP & 0xFFFF)))
1034 return mf_encoder_configure(h264, sys, width, height, frameRate, bitRate);
1035 sys->encQP = h264->QP;
1042static void mf_encoder_copy_nv12(BYTE* WINPR_RESTRICT dst,
const BYTE** WINPR_RESTRICT pSrcYuv,
1043 const UINT32* WINPR_RESTRICT pStride, UINT32 width, UINT32 height)
1046 WINPR_ASSERT(pSrcYuv);
1047 WINPR_ASSERT(pSrcYuv[0]);
1048 WINPR_ASSERT(pSrcYuv[1]);
1049 WINPR_ASSERT(pSrcYuv[2]);
1050 WINPR_ASSERT(pStride);
1052 for (UINT32 y = 0; y < height; y++)
1053 memcpy(&dst[1ull * y * width], &pSrcYuv[0][1ull * y * pStride[0]], width);
1055 BYTE* uv = &dst[1ull * width * height];
1056 for (UINT32 y = 0; y < height / 2; y++)
1058 const BYTE* u = &pSrcYuv[1][1ull * y * pStride[1]];
1059 const BYTE* v = &pSrcYuv[2][1ull * y * pStride[2]];
1060 BYTE* line = &uv[1ull * y * width];
1062 for (UINT32 x = 0; x < width / 2; x++)
1065 line[2 * x + 1] = v[x];
1071static int mf_compress(H264_CONTEXT* WINPR_RESTRICT h264,
const BYTE** WINPR_RESTRICT ppSrcYuv,
1072 const UINT32* WINPR_RESTRICT pStride, BYTE** WINPR_RESTRICT ppDstData,
1073 UINT32* WINPR_RESTRICT pDstSize)
1076 BYTE* data =
nullptr;
1077 DWORD maxLength = 0;
1078 DWORD curLength = 0;
1079 IMFSample* sample =
nullptr;
1080 IMFMediaBuffer* buffer =
nullptr;
1083 WINPR_ASSERT(ppSrcYuv);
1084 WINPR_ASSERT(pStride);
1085 WINPR_ASSERT(ppDstData);
1086 WINPR_ASSERT(pDstSize);
1088 H264_CONTEXT_MF* sys = (H264_CONTEXT_MF*)h264->pSystemData;
1089 if (!sys || !ppSrcYuv[0] || !ppSrcYuv[1] || !ppSrcYuv[2])
1092 if ((h264->width == 0) || (h264->height == 0) || (h264->width > UINT16_MAX) ||
1093 (h264->height > UINT16_MAX))
1098 const UINT32 width = (h264->width + 1) & ~1u;
1099 const UINT32 height = (h264->height + 1) & ~1u;
1100 const UINT32 frameRate = (h264->FrameRate > 0) ? h264->FrameRate : 30;
1101 const UINT32 bitRate = (h264->BitRate > 0) ? h264->BitRate : 1000000;
1104 const UINT64 size64 = 3ull * width * height / 2;
1105 if (size64 > UINT32_MAX)
1107 const DWORD size = (DWORD)size64;
1109 if (!mf_encoder_update(h264, sys, width, height, frameRate, bitRate))
1112 HRESULT hr = sys->MFCreateMemoryBuffer(size, &buffer);
1115 WLog_Print(h264->log, WLOG_ERROR,
"MFCreateMemoryBuffer failure: 0x%08" PRIX32
"",
1120 hr = buffer->lpVtbl->Lock(buffer, &data, &maxLength, &curLength);
1123 WLog_Print(h264->log, WLOG_ERROR,
"Lock failure: 0x%08" PRIX32
"", (UINT32)hr);
1127 const BOOL fits = (maxLength >= size);
1129 mf_encoder_copy_nv12(data, ppSrcYuv, pStride, width, height);
1132 hr = buffer->lpVtbl->Unlock(buffer);
1135 WLog_Print(h264->log, WLOG_ERROR,
"Unlock failure: 0x%08" PRIX32
"", (UINT32)hr);
1140 WLog_Print(h264->log, WLOG_ERROR,
1141 "encoder input buffer too small: %" PRIu32
" < %" PRIu32
" bytes",
1142 (UINT32)maxLength, (UINT32)size);
1146 hr = buffer->lpVtbl->SetCurrentLength(buffer, size);
1148 hr = sys->MFCreateSample(&sample);
1150 hr = sample->lpVtbl->AddBuffer(sample, buffer);
1153 const LONGLONG duration = 10000000LL / frameRate;
1155 hr = sample->lpVtbl->SetSampleTime(sample, sys->encSampleTime);
1157 hr = sample->lpVtbl->SetSampleDuration(sample, duration);
1160 WLog_Print(h264->log, WLOG_ERROR,
"encoder input sample failure: 0x%08" PRIX32
"",
1164 sys->encSampleTime += duration;
1166 sys->encDataLength = 0;
1167 hr = sys->transform->lpVtbl->ProcessInput(sys->transform, 0, sample, 0);
1168 if (hr == MF_E_NOTACCEPTING)
1171 hr = mf_encoder_drain(h264, sys);
1173 hr = sys->transform->lpVtbl->ProcessInput(sys->transform, 0, sample, 0);
1177 WLog_Print(h264->log, WLOG_ERROR,
"encoder ProcessInput failure: 0x%08" PRIX32
"",
1182 hr = mf_encoder_drain(h264, sys);
1186 if (sys->encDataLength > UINT32_MAX)
1189 if (sys->encDataLength == 0)
1195 if ((sys->encHeld == 4) && !sys->encWarned)
1197 WLog_Print(h264->log, WLOG_WARN,
1198 "MediaFoundation H264 encoder holds frames back (low latency mode not "
1199 "honoured), the client will see changes late");
1200 sys->encWarned = TRUE;
1207 *ppDstData = sys->encData;
1208 *pDstSize = (UINT32)sys->encDataLength;
1213 sample->lpVtbl->Release(sample);
1215 buffer->lpVtbl->Release(buffer);
1220static BOOL mf_plat_loaded(H264_CONTEXT_MF* sys)
1223 return sys->MFStartup && sys->MFShutdown && sys->MFCreateSample && sys->MFCreateMemoryBuffer &&
1224 sys->MFCreateMediaType;
1227static void mf_uninit(H264_CONTEXT* h264)
1231 H264_CONTEXT_MF* sys = (H264_CONTEXT_MF*)h264->pSystemData;
1237 sys->transform->lpVtbl->Release(sys->transform);
1238 sys->transform =
nullptr;
1243 sys->codecApi->lpVtbl->Release(sys->codecApi);
1244 sys->codecApi =
nullptr;
1249 sys->inputType->lpVtbl->Release(sys->inputType);
1250 sys->inputType =
nullptr;
1253 if (sys->outputType)
1255 sys->outputType->lpVtbl->Release(sys->outputType);
1256 sys->outputType =
nullptr;
1259 if (sys->outputSample)
1261 sys->outputSample->lpVtbl->Release(sys->outputSample);
1262 sys->outputSample =
nullptr;
1267 if (mf_plat_loaded(sys))
1270 const HRESULT hr = sys->MFShutdown();
1272 WLog_Print(h264->log, WLOG_WARN,
"MFShutdown failure: 0x%08" PRIX32
"",
1276 FreeLibrary(sys->mfplat);
1277 sys->mfplat =
nullptr;
1279 if (mf_plat_loaded(sys))
1283 for (
size_t x = 0; x <
sizeof(h264->pYUVData) /
sizeof(h264->pYUVData[0]); x++)
1284 winpr_aligned_free(h264->pYUVData[x]);
1286 memset(h264->pYUVData, 0,
sizeof(h264->pYUVData));
1287 memset(h264->iStride, 0,
sizeof(h264->iStride));
1291 h264->pSystemData =
nullptr;
1296static BOOL mf_init(H264_CONTEXT* h264)
1302 H264_CONTEXT_MF* sys = (H264_CONTEXT_MF*)calloc(1,
sizeof(H264_CONTEXT_MF));
1307 h264->pSystemData = (
void*)sys;
1310 sys->mfplat = LoadLibraryA(
"mfplat.dll");
1315 sys->MFStartup = GetProcAddressAs(sys->mfplat,
"MFStartup", pfnMFStartup);
1316 sys->MFShutdown = GetProcAddressAs(sys->mfplat,
"MFShutdown", pfnMFShutdown);
1317 sys->MFCreateSample = GetProcAddressAs(sys->mfplat,
"MFCreateSample", pfnMFCreateSample);
1318 sys->MFCreateMemoryBuffer =
1319 GetProcAddressAs(sys->mfplat,
"MFCreateMemoryBuffer", pfnMFCreateMemoryBuffer);
1320 sys->MFCreateMediaType =
1321 GetProcAddressAs(sys->mfplat,
"MFCreateMediaType", pfnMFCreateMediaType);
1323 if (!mf_plat_loaded(sys))
1326 CoInitializeEx(
nullptr, COINIT_APARTMENTTHREADED);
1328 if (h264->Compressor)
1330 hr = sys->MFStartup(MF_VERSION, 0);
1334 WLog_Print(h264->log, WLOG_ERROR,
"MFStartup failure: 0x%08" PRIX32
"", hr);
1341 if (!mf_encoder_create(h264, sys))
1343 WLog_Print(h264->log, WLOG_WARN,
"MediaFoundation H264 encoder not available");
1349 VARIANT var = WINPR_C_ARRAY_INIT;
1350 hr = sys->MFStartup(MF_VERSION, 0);
1354 WLog_Print(h264->log, WLOG_ERROR,
"MFStartup failure: 0x%08" PRIX32
"", hr);
1358 hr = CoCreateInstance(&sCLSID_CMSH264DecoderMFT,
nullptr, CLSCTX_INPROC_SERVER,
1359 &sIID_IMFTransform, (
void**)&sys->transform);
1363 WLog_Print(h264->log, WLOG_ERROR,
1364 "CoCreateInstance(CLSID_CMSH264DecoderMFT) failure: 0x%08" PRIX32
"", hr);
1368 hr = sys->transform->lpVtbl->QueryInterface(sys->transform, &sIID_ICodecAPI,
1369 (
void**)&sys->codecApi);
1373 WLog_Print(h264->log, WLOG_ERROR,
1374 "QueryInterface(IID_ICodecAPI) failure: 0x%08" PRIX32
"", hr);
1378 var.n1.n2.vt = VT_UI4;
1379 var.n1.n2.n3.ulVal = 1;
1380 hr = sys->codecApi->lpVtbl->SetValue(sys->codecApi, &sCODECAPI_AVLowLatencyMode, &var);
1384 WLog_Print(h264->log, WLOG_ERROR,
1385 "SetValue(CODECAPI_AVLowLatencyMode) failure: 0x%08" PRIX32
"", hr);
1389 hr = sys->MFCreateMediaType(&sys->inputType);
1393 WLog_Print(h264->log, WLOG_ERROR,
"MFCreateMediaType failure: 0x%08" PRIX32
"", hr);
1397 hr = sys->inputType->lpVtbl->SetGUID(sys->inputType, &sMF_MT_MAJOR_TYPE,
1398 &sMFMediaType_Video);
1402 WLog_Print(h264->log, WLOG_ERROR,
"SetGUID(MF_MT_MAJOR_TYPE) failure: 0x%08" PRIX32
"",
1407 hr = sys->inputType->lpVtbl->SetGUID(sys->inputType, &sMF_MT_SUBTYPE, &sMFVideoFormat_H264);
1411 WLog_Print(h264->log, WLOG_ERROR,
"SetGUID(MF_MT_SUBTYPE) failure: 0x%08" PRIX32
"",
1416 hr = sys->transform->lpVtbl->SetInputType(sys->transform, 0, sys->inputType, 0);
1420 WLog_Print(h264->log, WLOG_ERROR,
"SetInputType failure: 0x%08" PRIX32
"", hr);
1424 hr = mf_find_output_type(sys, &sMFVideoFormat_IYUV, &sys->outputType);
1428 WLog_Print(h264->log, WLOG_ERROR,
"mf_find_output_type failure: 0x%08" PRIX32
"", hr);
1432 hr = sys->transform->lpVtbl->SetOutputType(sys->transform, 0, sys->outputType, 0);
1436 WLog_Print(h264->log, WLOG_ERROR,
"SetOutputType failure: 0x%08" PRIX32
"", hr);
1440 hr = mf_create_output_sample(h264, sys);
1444 WLog_Print(h264->log, WLOG_ERROR,
"mf_create_output_sample failure: 0x%08" PRIX32
"",
1452 WLog_Print(h264->log, WLOG_ERROR,
"mf_init failure");
1457const H264_CONTEXT_SUBSYSTEM g_Subsystem_MF = {
"MediaFoundation", mf_init, mf_uninit,
1458 mf_decompress, mf_compress };