21 #include <freerdp/config.h>
23 #include <winpr/crt.h>
24 #include <winpr/print.h>
25 #include <winpr/library.h>
26 #include <winpr/bitstream.h>
27 #include <winpr/synch.h>
29 #include <freerdp/primitives.h>
30 #include <freerdp/codec/h264.h>
31 #include <freerdp/codec/yuv.h>
32 #include <freerdp/log.h>
36 #define TAG FREERDP_TAG("codec")
38 static BOOL avc444_ensure_buffer(H264_CONTEXT* h264, DWORD nDstHeight);
40 BOOL avc420_ensure_buffer(H264_CONTEXT* h264, UINT32 stride, UINT32 width, UINT32 height)
43 UINT32 pheight = height;
52 stride += 16 - stride % 16;
54 if (pheight % 16 != 0)
55 pheight += 16 - pheight % 16;
57 for (
size_t x = 0; x < 3; x++)
59 if (!h264->pYUVData[x] || !h264->pOldYUVData[x])
68 if (isNull || (width != h264->width) || (height != h264->height) ||
69 (stride != h264->iStride[0]))
71 h264->iStride[0] = stride;
72 h264->iStride[1] = (stride + 1) / 2;
73 h264->iStride[2] = (stride + 1) / 2;
75 h264->height = height;
77 for (
size_t x = 0; x < 3; x++)
79 BYTE* tmp1 = winpr_aligned_recalloc(h264->pYUVData[x], h264->iStride[x], pheight, 16);
81 winpr_aligned_recalloc(h264->pOldYUVData[x], h264->iStride[x], pheight, 16);
83 h264->pYUVData[x] = tmp1;
85 h264->pOldYUVData[x] = tmp2;
94 INT32 avc420_decompress(H264_CONTEXT* h264,
const BYTE* pSrcData, UINT32 SrcSize, BYTE* pDstData,
95 DWORD DstFormat, UINT32 nDstStep, UINT32 nDstWidth, UINT32 nDstHeight,
99 const BYTE* pYUVData[3];
101 if (!h264 || h264->Compressor)
104 status = h264->subsystem->Decompress(h264, pSrcData, SrcSize);
112 pYUVData[0] = h264->pYUVData[0];
113 pYUVData[1] = h264->pYUVData[1];
114 pYUVData[2] = h264->pYUVData[2];
115 if (!yuv420_context_decode(h264->yuv, pYUVData, h264->iStride, h264->height, DstFormat,
116 pDstData, nDstStep, regionRects, numRegionRects))
122 static BOOL allocate_h264_metablock(UINT32 QP,
RECTANGLE_16* rectangles,
126 if (!meta || (QP > UINT8_MAX))
132 meta->regionRects = rectangles;
136 if (count > UINT32_MAX)
141 if (!meta->quantQualityVals || !meta->regionRects)
143 meta->numRegionRects = (UINT32)count;
144 for (
size_t x = 0; x < count; x++)
151 cur->qualityVal = 100 - (QP & 0x3F);
156 static INLINE BOOL diff_tile(
const RECTANGLE_16* regionRect, BYTE* pYUVData[3],
157 BYTE* pOldYUVData[3], UINT32
const iStride[3])
161 if (!regionRect || !pYUVData || !pOldYUVData || !iStride)
163 size = regionRect->right - regionRect->left;
164 if (regionRect->right > iStride[0])
166 if (regionRect->right / 2u > iStride[1])
168 if (regionRect->right / 2u > iStride[2])
171 for (
size_t y = regionRect->top; y < regionRect->bottom; y++)
173 const BYTE* cur0 = &pYUVData[0][y * iStride[0]];
174 const BYTE* cur1 = &pYUVData[1][y * iStride[1]];
175 const BYTE* cur2 = &pYUVData[2][y * iStride[2]];
176 const BYTE* old0 = &pOldYUVData[0][y * iStride[0]];
177 const BYTE* old1 = &pOldYUVData[1][y * iStride[1]];
178 const BYTE* old2 = &pOldYUVData[2][y * iStride[2]];
180 if (memcmp(&cur0[regionRect->left], &old0[regionRect->left], size) != 0)
182 if (memcmp(&cur1[regionRect->left / 2], &old1[regionRect->left / 2], size / 2) != 0)
184 if (memcmp(&cur2[regionRect->left / 2], &old2[regionRect->left / 2], size / 2) != 0)
190 static BOOL detect_changes(BOOL firstFrameDone,
const UINT32 QP,
const RECTANGLE_16* regionRect,
191 BYTE* pYUVData[3], BYTE* pOldYUVData[3], UINT32
const iStride[3],
199 if (!regionRect || !pYUVData || !pOldYUVData || !iStride || !meta)
202 wc = (regionRect->right - regionRect->left) / 64 + 1;
203 hc = (regionRect->bottom - regionRect->top) / 64 + 1;
209 rectangles[0] = *regionRect;
214 for (
size_t y = regionRect->top; y < regionRect->bottom; y += 64)
216 for (
size_t x = regionRect->left; x < regionRect->right; x += 64)
219 rect.left = (UINT16)MIN(UINT16_MAX, regionRect->left + x);
220 rect.top = (UINT16)MIN(UINT16_MAX, regionRect->top + y);
222 (UINT16)MIN(UINT16_MAX, MIN(regionRect->left + x + 64, regionRect->right));
224 (UINT16)MIN(UINT16_MAX, MIN(regionRect->top + y + 64, regionRect->bottom));
225 if (diff_tile(&rect, pYUVData, pOldYUVData, iStride))
226 rectangles[count++] = rect;
230 if (!allocate_h264_metablock(QP, rectangles, meta, count))
235 INT32 h264_get_yuv_buffer(H264_CONTEXT* h264, UINT32 nSrcStride, UINT32 nSrcWidth,
236 UINT32 nSrcHeight, BYTE* YUVData[3], UINT32 stride[3])
238 if (!h264 || !h264->Compressor || !h264->subsystem || !h264->subsystem->Compress)
241 if (!avc420_ensure_buffer(h264, nSrcStride, nSrcWidth, nSrcHeight))
244 for (
size_t x = 0; x < 3; x++)
246 YUVData[x] = h264->pYUVData[x];
247 stride[x] = h264->iStride[x];
253 INT32 h264_compress(H264_CONTEXT* h264, BYTE** ppDstData, UINT32* pDstSize)
255 if (!h264 || !h264->Compressor || !h264->subsystem || !h264->subsystem->Compress)
258 const BYTE* pcYUVData[3] = { h264->pYUVData[0], h264->pYUVData[1], h264->pYUVData[2] };
260 return h264->subsystem->Compress(h264, pcYUVData, h264->iStride, ppDstData, pDstSize);
263 INT32 avc420_compress(H264_CONTEXT* h264,
const BYTE* pSrcData, DWORD SrcFormat, UINT32 nSrcStep,
264 UINT32 nSrcWidth, UINT32 nSrcHeight,
const RECTANGLE_16* regionRect,
268 BYTE* pYUVData[3] = { 0 };
269 const BYTE* pcYUVData[3] = { 0 };
270 BYTE* pOldYUVData[3] = { 0 };
272 if (!h264 || !regionRect || !meta || !h264->Compressor)
275 if (!h264->subsystem->Compress)
278 if (!avc420_ensure_buffer(h264, nSrcStep, nSrcWidth, nSrcHeight))
281 if (h264->encodingBuffer)
283 for (
size_t x = 0; x < 3; x++)
285 pYUVData[x] = h264->pYUVData[x];
286 pOldYUVData[x] = h264->pOldYUVData[x];
291 for (
size_t x = 0; x < 3; x++)
293 pYUVData[x] = h264->pOldYUVData[x];
294 pOldYUVData[x] = h264->pYUVData[x];
297 h264->encodingBuffer = !h264->encodingBuffer;
299 if (!yuv420_context_encode(h264->yuv, pSrcData, nSrcStep, SrcFormat, h264->iStride, pYUVData,
303 if (!detect_changes(h264->firstLumaFrameDone, h264->QP, regionRect, pYUVData, pOldYUVData,
304 h264->iStride, meta))
307 if (meta->numRegionRects == 0)
313 for (
size_t x = 0; x < 3; x++)
314 pcYUVData[x] = pYUVData[x];
316 rc = h264->subsystem->Compress(h264, pcYUVData, h264->iStride, ppDstData, pDstSize);
318 h264->firstLumaFrameDone = TRUE;
322 free_h264_metablock(meta);
326 INT32 avc444_compress(H264_CONTEXT* h264,
const BYTE* pSrcData, DWORD SrcFormat, UINT32 nSrcStep,
327 UINT32 nSrcWidth, UINT32 nSrcHeight, BYTE version,
const RECTANGLE_16* region,
328 BYTE* op, BYTE** ppDstData, UINT32* pDstSize, BYTE** ppAuxDstData,
334 UINT32 codedSize = 0;
335 BYTE** pYUV444Data = NULL;
336 BYTE** pOldYUV444Data = NULL;
337 BYTE** pYUVData = NULL;
338 BYTE** pOldYUVData = NULL;
340 if (!h264 || !h264->Compressor)
343 if (!h264->subsystem->Compress)
346 if (!avc420_ensure_buffer(h264, nSrcStep, nSrcWidth, nSrcHeight))
349 if (!avc444_ensure_buffer(h264, nSrcHeight))
352 if (h264->encodingBuffer)
354 pYUV444Data = h264->pOldYUV444Data;
355 pOldYUV444Data = h264->pYUV444Data;
356 pYUVData = h264->pOldYUVData;
357 pOldYUVData = h264->pYUVData;
361 pYUV444Data = h264->pYUV444Data;
362 pOldYUV444Data = h264->pOldYUV444Data;
363 pYUVData = h264->pYUVData;
364 pOldYUVData = h264->pOldYUVData;
366 h264->encodingBuffer = !h264->encodingBuffer;
368 if (!yuv444_context_encode(h264->yuv, version, pSrcData, nSrcStep, SrcFormat, h264->iStride,
369 pYUV444Data, pYUVData, region, 1))
372 if (!detect_changes(h264->firstLumaFrameDone, h264->QP, region, pYUV444Data, pOldYUV444Data,
373 h264->iStride, meta))
375 if (!detect_changes(h264->firstChromaFrameDone, h264->QP, region, pYUVData, pOldYUVData,
376 h264->iStride, auxMeta))
385 if ((meta->numRegionRects > 0) && (auxMeta->numRegionRects > 0))
387 else if (meta->numRegionRects > 0)
389 else if (auxMeta->numRegionRects > 0)
393 WLog_INFO(TAG,
"no changes detected for luma or chroma frame");
398 if ((*op == 0) || (*op == 1))
400 const BYTE* pcYUV444Data[3] = { pYUV444Data[0], pYUV444Data[1], pYUV444Data[2] };
402 if (h264->subsystem->Compress(h264, pcYUV444Data, h264->iStride, &coded, &codedSize) < 0)
404 h264->firstLumaFrameDone = TRUE;
405 memcpy(h264->lumaData, coded, codedSize);
406 *ppDstData = h264->lumaData;
407 *pDstSize = codedSize;
410 if ((*op == 0) || (*op == 2))
412 const BYTE* pcYUVData[3] = { pYUVData[0], pYUVData[1], pYUVData[2] };
414 if (h264->subsystem->Compress(h264, pcYUVData, h264->iStride, &coded, &codedSize) < 0)
416 h264->firstChromaFrameDone = TRUE;
417 *ppAuxDstData = coded;
418 *pAuxDstSize = codedSize;
425 free_h264_metablock(meta);
426 free_h264_metablock(auxMeta);
431 static BOOL avc444_ensure_buffer(H264_CONTEXT* h264, DWORD nDstHeight)
435 const UINT32* piMainStride = h264->iStride;
436 UINT32* piDstSize = h264->iYUV444Size;
437 UINT32* piDstStride = h264->iYUV444Stride;
438 BYTE** ppYUVDstData = h264->pYUV444Data;
439 BYTE** ppOldYUVDstData = h264->pOldYUV444Data;
441 nDstHeight = MAX(h264->height, nDstHeight);
442 const UINT32 pad = nDstHeight % 16;
443 UINT32 padDstHeight = nDstHeight;
446 padDstHeight += 16 - pad;
448 if ((piMainStride[0] != piDstStride[0]) ||
449 (piDstSize[0] != 1ull * piMainStride[0] * padDstHeight))
451 for (UINT32 x = 0; x < 3; x++)
453 piDstStride[x] = piMainStride[0];
454 piDstSize[x] = piDstStride[x] * padDstHeight;
456 if (piDstSize[x] == 0)
459 BYTE* tmp1 = winpr_aligned_recalloc(ppYUVDstData[x], piDstSize[x], 1, 16);
462 ppYUVDstData[x] = tmp1;
463 BYTE* tmp2 = winpr_aligned_recalloc(ppOldYUVDstData[x], piDstSize[x], 1, 16);
466 ppOldYUVDstData[x] = tmp2;
470 BYTE* tmp = winpr_aligned_recalloc(h264->lumaData, piDstSize[0], 4, 16);
473 h264->lumaData = tmp;
477 for (UINT32 x = 0; x < 3; x++)
479 if (!ppOldYUVDstData[x] || !ppYUVDstData[x] || (piDstSize[x] == 0) || (piDstStride[x] == 0))
481 WLog_Print(h264->log, WLOG_ERROR,
482 "YUV buffer not initialized! check your decoder settings");
495 static BOOL avc444_process_rects(H264_CONTEXT* h264,
const BYTE* pSrcData, UINT32 SrcSize,
496 BYTE* pDstData, UINT32 DstFormat, UINT32 nDstStep,
497 UINT32 nDstWidth, UINT32 nDstHeight,
const RECTANGLE_16* rects,
498 UINT32 nrRects, avc444_frame_type type)
500 const BYTE* pYUVData[3];
501 BYTE* pYUVDstData[3];
502 UINT32* piDstStride = h264->iYUV444Stride;
503 BYTE** ppYUVDstData = h264->pYUV444Data;
504 const UINT32* piStride = h264->iStride;
506 if (h264->subsystem->Decompress(h264, pSrcData, SrcSize) < 0)
509 pYUVData[0] = h264->pYUVData[0];
510 pYUVData[1] = h264->pYUVData[1];
511 pYUVData[2] = h264->pYUVData[2];
512 if (!avc444_ensure_buffer(h264, nDstHeight))
515 pYUVDstData[0] = ppYUVDstData[0];
516 pYUVDstData[1] = ppYUVDstData[1];
517 pYUVDstData[2] = ppYUVDstData[2];
518 if (!yuv444_context_decode(h264->yuv, (BYTE)type, pYUVData, piStride, h264->height, pYUVDstData,
519 piDstStride, DstFormat, pDstData, nDstStep, rects, nrRects))
525 #if defined(AVC444_FRAME_STAT)
526 static UINT64 op1 = 0;
527 static double op1sum = 0;
528 static UINT64 op2 = 0;
529 static double op2sum = 0;
530 static UINT64 op3 = 0;
531 static double op3sum = 0;
532 static double avg(UINT64* count,
double old,
double size)
534 double tmp = size + *count * old;
541 INT32 avc444_decompress(H264_CONTEXT* h264, BYTE op,
const RECTANGLE_16* regionRects,
542 UINT32 numRegionRects,
const BYTE* pSrcData, UINT32 SrcSize,
543 const RECTANGLE_16* auxRegionRects, UINT32 numAuxRegionRect,
544 const BYTE* pAuxSrcData, UINT32 AuxSrcSize, BYTE* pDstData, DWORD DstFormat,
545 UINT32 nDstStep, UINT32 nDstWidth, UINT32 nDstHeight, UINT32 codecId)
548 avc444_frame_type chroma =
549 (codecId == RDPGFX_CODECID_AVC444) ? AVC444_CHROMAv1 : AVC444_CHROMAv2;
551 if (!h264 || !regionRects || !pSrcData || !pDstData || h264->Compressor)
558 if (!avc444_process_rects(h264, pSrcData, SrcSize, pDstData, DstFormat, nDstStep,
559 nDstWidth, nDstHeight, regionRects, numRegionRects,
562 else if (!avc444_process_rects(h264, pAuxSrcData, AuxSrcSize, pDstData, DstFormat,
563 nDstStep, nDstWidth, nDstHeight, auxRegionRects,
564 numAuxRegionRect, chroma))
572 if (!avc444_process_rects(h264, pSrcData, SrcSize, pDstData, DstFormat, nDstStep,
573 nDstWidth, nDstHeight, regionRects, numRegionRects, chroma))
581 if (!avc444_process_rects(h264, pSrcData, SrcSize, pDstData, DstFormat, nDstStep,
582 nDstWidth, nDstHeight, regionRects, numRegionRects,
594 #if defined(AVC444_FRAME_STAT)
599 op1sum = avg(&op1, op1sum, SrcSize + AuxSrcSize);
603 op2sum = avg(&op2, op2sum, SrcSize);
607 op3sum = avg(&op3, op3sum, SrcSize);
614 WLog_Print(h264->log, WLOG_INFO,
615 "luma=%" PRIu64
" [avg=%lf] chroma=%" PRIu64
" [avg=%lf] combined=%" PRIu64
617 op1, op1sum, op2, op2sum, op3, op3sum);
622 #define MAX_SUBSYSTEMS 10
623 static INIT_ONCE subsystems_once = INIT_ONCE_STATIC_INIT;
624 static const H264_CONTEXT_SUBSYSTEM* subSystems[MAX_SUBSYSTEMS] = { 0 };
626 static BOOL CALLBACK h264_register_subsystems(
PINIT_ONCE once, PVOID param, PVOID* context)
630 #ifdef WITH_MEDIACODEC
632 subSystems[i] = &g_Subsystem_mediacodec;
636 #if defined(_WIN32) && defined(WITH_MEDIA_FOUNDATION)
638 subSystems[i] = &g_Subsystem_MF;
644 subSystems[i] = &g_Subsystem_OpenH264;
648 #ifdef WITH_VIDEO_FFMPEG
650 subSystems[i] = &g_Subsystem_libavcodec;
657 static BOOL h264_context_init(H264_CONTEXT* h264)
662 h264->log = WLog_Get(TAG);
667 h264->subsystem = NULL;
668 InitOnceExecuteOnce(&subsystems_once, h264_register_subsystems, NULL, NULL);
670 for (
size_t i = 0; i < MAX_SUBSYSTEMS; i++)
672 const H264_CONTEXT_SUBSYSTEM* subsystem = subSystems[i];
674 if (!subsystem || !subsystem->Init)
677 if (subsystem->Init(h264))
679 h264->subsystem = subsystem;
687 BOOL h264_context_reset(H264_CONTEXT* h264, UINT32 width, UINT32 height)
693 h264->height = height;
694 return yuv_context_reset(h264->yuv, width, height);
697 H264_CONTEXT* h264_context_new(BOOL Compressor)
699 H264_CONTEXT* h264 = (H264_CONTEXT*)calloc(1,
sizeof(H264_CONTEXT));
703 h264->Compressor = Compressor;
708 h264->BitRate = 1000000;
709 h264->FrameRate = 30;
712 if (!h264_context_init(h264))
715 h264->yuv = yuv_context_new(Compressor, 0);
722 WINPR_PRAGMA_DIAG_PUSH
723 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
724 h264_context_free(h264);
725 WINPR_PRAGMA_DIAG_POP
729 void h264_context_free(H264_CONTEXT* h264)
734 h264->subsystem->Uninit(h264);
736 for (
size_t x = 0; x < 3; x++)
738 if (h264->Compressor)
740 winpr_aligned_free(h264->pYUVData[x]);
741 winpr_aligned_free(h264->pOldYUVData[x]);
743 winpr_aligned_free(h264->pYUV444Data[x]);
744 winpr_aligned_free(h264->pOldYUV444Data[x]);
746 winpr_aligned_free(h264->lumaData);
748 yuv_context_free(h264->yuv);
758 free(meta->quantQualityVals);
759 free(meta->regionRects);
763 BOOL h264_context_set_option(H264_CONTEXT* h264, H264_CONTEXT_OPTION option, UINT32 value)
768 case H264_CONTEXT_OPTION_BITRATE:
769 h264->BitRate = value;
771 case H264_CONTEXT_OPTION_FRAMERATE:
772 h264->FrameRate = value;
774 case H264_CONTEXT_OPTION_RATECONTROL:
775 h264->RateControlMode = value;
777 case H264_CONTEXT_OPTION_QP:
780 case H264_CONTEXT_OPTION_USAGETYPE:
781 h264->UsageType = value;
784 WLog_Print(h264->log, WLOG_WARN,
"Unknown H264_CONTEXT_OPTION[0x%08" PRIx32
"]",
790 UINT32 h264_context_get_option(H264_CONTEXT* h264, H264_CONTEXT_OPTION option)
795 case H264_CONTEXT_OPTION_BITRATE:
796 return h264->BitRate;
797 case H264_CONTEXT_OPTION_FRAMERATE:
798 return h264->FrameRate;
799 case H264_CONTEXT_OPTION_RATECONTROL:
800 return h264->RateControlMode;
801 case H264_CONTEXT_OPTION_QP:
803 case H264_CONTEXT_OPTION_USAGETYPE:
804 return h264->UsageType;
806 WLog_Print(h264->log, WLOG_WARN,
"Unknown H264_CONTEXT_OPTION[0x%08" PRIx32
"]",