FreeRDP
Loading...
Searching...
No Matches
h264.c
1
21#include <freerdp/config.h>
22
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>
28
29#include <freerdp/primitives.h>
30#include <freerdp/codec/h264.h>
31#include <freerdp/codec/yuv.h>
32#include <freerdp/log.h>
33#include <freerdp/codec/region.h>
34
35#include "h264.h"
36
37#define TAG FREERDP_TAG("codec")
38
39WINPR_ATTR_NODISCARD
40static BOOL avc444_ensure_buffer(H264_CONTEXT* h264, DWORD nDstHeight);
41
42WINPR_ATTR_NODISCARD
43static BOOL yuv_ensure_buffer(H264_CONTEXT* h264, UINT32 stride, UINT32 width, UINT32 height)
44{
45 BOOL isNull = FALSE;
46 UINT32 pheight = height;
47
48 if (!h264)
49 return FALSE;
50
51 if (stride == 0)
52 stride = width;
53
54 /* Add padding lines. Allows relaxing bounds checks in decoder functions */
55 stride += 32 - stride % 16;
56 pheight += 32 - pheight % 16;
57
58 const size_t nPlanes = h264->hwAccel ? 2 : 3;
59
60 for (size_t x = 0; x < nPlanes; x++)
61 {
62 if (!h264->pYUVData[x] || !h264->pOldYUVData[x])
63 isNull = TRUE;
64 }
65
66 if (pheight == 0)
67 return FALSE;
68 if (stride == 0)
69 return FALSE;
70
71 if (isNull || (width != h264->width) || (height != h264->height) ||
72 (stride != h264->iStride[0]))
73 {
74 if (h264->hwAccel) /* NV12 */
75 {
76 h264->iStride[0] = stride;
77 h264->iStride[1] = stride;
78 h264->iStride[2] = 0;
79 }
80 else /* I420 */
81 {
82 h264->iStride[0] = stride;
83 h264->iStride[1] = (stride + 1) / 2;
84 h264->iStride[2] = (stride + 1) / 2;
85 }
86
87 for (size_t x = 0; x < nPlanes; x++)
88 {
89 BYTE* tmp1 = winpr_aligned_recalloc(h264->pYUVData[x], h264->iStride[x], pheight, 16);
90 BYTE* tmp2 =
91 winpr_aligned_recalloc(h264->pOldYUVData[x], h264->iStride[x], pheight, 16);
92 if (tmp1)
93 h264->pYUVData[x] = tmp1;
94 if (tmp2)
95 h264->pOldYUVData[x] = tmp2;
96 if (!tmp1 || !tmp2)
97 return FALSE;
98 }
99 h264->width = width;
100 h264->height = height;
101 }
102
103 return TRUE;
104}
105
106BOOL avc420_ensure_buffer(H264_CONTEXT* h264, UINT32 stride, UINT32 width, UINT32 height)
107{
108 return yuv_ensure_buffer(h264, stride, width, height);
109}
110
111static BOOL isRectValid(UINT32 width, UINT32 height, const RECTANGLE_16* rect)
112{
113 WINPR_ASSERT(rect);
114 if (rect->left > width)
115 return FALSE;
116 if (rect->right > width)
117 return FALSE;
118 if (rect->left >= rect->right)
119 return FALSE;
120 if (rect->top > height)
121 return FALSE;
122 if (rect->bottom > height)
123 return FALSE;
124 if (rect->top >= rect->bottom)
125 return FALSE;
126 return TRUE;
127}
128
129static BOOL areRectsValid(wLog* log, UINT32 width, UINT32 height, const RECTANGLE_16* rects,
130 UINT32 count)
131{
132 WINPR_ASSERT(rects || (count == 0));
133 for (size_t x = 0; x < count; x++)
134 {
135 const RECTANGLE_16* rect = &rects[x];
136 if (!isRectValid(width, height, rect))
137 {
138 char buffer[64] = WINPR_C_ARRAY_INIT;
139 WLog_Print(log, WLOG_WARN,
140 "Rectangle %" PRIuz " %s outside of bounding frame %" PRIu32 "x%" PRIu32, x,
141 rectangle_to_string(rect, buffer, sizeof(buffer)), width, height);
142 return FALSE;
143 }
144 }
145 return TRUE;
146}
147
148static int log_decompress(H264_CONTEXT* h264, const BYTE* pSrcData, UINT32 SrcSize,
149 const RECTANGLE_16* rects, UINT32 nrRects)
150{
151 WINPR_ASSERT(h264);
152 WINPR_ASSERT(h264->subsystem);
153 WINPR_ASSERT(h264->subsystem->Decompress);
154
155 const int status = h264->subsystem->Decompress(h264, pSrcData, SrcSize);
156 if (status < 0)
157 {
158 WLog_Print(h264->log, WLOG_WARN, "H264 decompress failed with %d", status);
159 return status;
160 }
161
162 /* some server implementations (krdc) use H264 frames smaller than the surface sizes,
163 * validate the regions against this size as well */
164 if (!areRectsValid(h264->log, h264->YUVWidth, h264->YUVHeight, rects, nrRects))
165 return -1015;
166 if (!areRectsValid(h264->log, h264->width, h264->height, rects, nrRects))
167 return -1016;
168 return status;
169}
170
171INT32 avc420_decompress(H264_CONTEXT* h264, const BYTE* pSrcData, UINT32 SrcSize, BYTE* pDstData,
172 DWORD DstFormat, UINT32 nDstStep, WINPR_ATTR_UNUSED UINT32 nDstWidth,
173 WINPR_ATTR_UNUSED UINT32 nDstHeight, const RECTANGLE_16* regionRects,
174 UINT32 numRegionRects)
175{
176 int status = 0;
177 const BYTE* pYUVData[3];
178
179 if (!h264 || h264->Compressor)
180 return -1001;
181
182 if (!areRectsValid(h264->log, nDstWidth, nDstHeight, regionRects, numRegionRects))
183 return -1013;
184
185 status = log_decompress(h264, pSrcData, SrcSize, regionRects, numRegionRects);
186
187 if (status == 0)
188 return 1;
189
190 if (status < 0)
191 return status;
192
193 pYUVData[0] = h264->pYUVData[0];
194 pYUVData[1] = h264->pYUVData[1];
195 pYUVData[2] = h264->pYUVData[2];
196 if (!yuv420_context_decode(h264->yuv, pYUVData, h264->iStride, h264->YUVHeight, DstFormat,
197 pDstData, nDstStep, regionRects, numRegionRects))
198 return -1002;
199
200 return 1;
201}
202
203static BOOL allocate_h264_metablock(UINT32 QP, RECTANGLE_16* rectangles,
204 RDPGFX_H264_METABLOCK* meta, size_t count)
205{
206 /* [MS-RDPEGFX] 2.2.4.4.2 RDPGFX_AVC420_QUANT_QUALITY */
207 if (!meta || (QP > UINT8_MAX))
208 {
209 free(rectangles);
210 return FALSE;
211 }
212
213 meta->regionRects = rectangles;
214 if (count == 0)
215 return TRUE;
216
217 if (count > UINT32_MAX)
218 return FALSE;
219
220 meta->quantQualityVals = calloc(count, sizeof(RDPGFX_H264_QUANT_QUALITY));
221
222 if (!meta->quantQualityVals || !meta->regionRects)
223 return FALSE;
224 meta->numRegionRects = (UINT32)count;
225 for (size_t x = 0; x < count; x++)
226 {
227 RDPGFX_H264_QUANT_QUALITY* cur = &meta->quantQualityVals[x];
228 cur->qp = (UINT8)QP;
229
230 /* qpVal bit 6 and 7 are flags, so mask them out here.
231 * qualityVal is [0-100] so 100 - qpVal [0-64] is always in range */
232 cur->qualityVal = 100 - (QP & 0x3F);
233 }
234 return TRUE;
235}
236
237static inline BOOL diff_tile(const RECTANGLE_16* regionRect, BYTE* pYUVData[3],
238 BYTE* pOldYUVData[3], UINT32 const iStride[3])
239{
240 size_t size = 0;
241
242 if (!regionRect || !pYUVData || !pOldYUVData || !iStride)
243 return FALSE;
244 size = regionRect->right - regionRect->left;
245 if (regionRect->right > iStride[0])
246 return FALSE;
247 if (regionRect->right / 2u > iStride[1])
248 return FALSE;
249 if (regionRect->right / 2u > iStride[2])
250 return FALSE;
251
252 for (size_t y = regionRect->top; y < regionRect->bottom; y++)
253 {
254 const BYTE* cur0 = &pYUVData[0][y * iStride[0]];
255 const BYTE* cur1 = &pYUVData[1][y * iStride[1]];
256 const BYTE* cur2 = &pYUVData[2][y * iStride[2]];
257 const BYTE* old0 = &pOldYUVData[0][y * iStride[0]];
258 const BYTE* old1 = &pOldYUVData[1][y * iStride[1]];
259 const BYTE* old2 = &pOldYUVData[2][y * iStride[2]];
260
261 if (memcmp(&cur0[regionRect->left], &old0[regionRect->left], size) != 0)
262 return TRUE;
263 if (memcmp(&cur1[regionRect->left / 2], &old1[regionRect->left / 2], size / 2) != 0)
264 return TRUE;
265 if (memcmp(&cur2[regionRect->left / 2], &old2[regionRect->left / 2], size / 2) != 0)
266 return TRUE;
267 }
268 return FALSE;
269}
270
271static BOOL detect_changes(BOOL firstFrameDone, const UINT32 QP, const RECTANGLE_16* regionRect,
272 BYTE* pYUVData[3], BYTE* pOldYUVData[3], UINT32 const iStride[3],
274{
275 size_t count = 0;
276 size_t wc = 0;
277 size_t hc = 0;
278 RECTANGLE_16* rectangles = nullptr;
279
280 if (!regionRect || !pYUVData || !pOldYUVData || !iStride || !meta)
281 return FALSE;
282
283 wc = (regionRect->right - regionRect->left) / 64 + 1;
284 hc = (regionRect->bottom - regionRect->top) / 64 + 1;
285 rectangles = calloc(wc * hc, sizeof(RECTANGLE_16));
286 if (!rectangles)
287 return FALSE;
288 if (!firstFrameDone)
289 {
290 rectangles[0] = *regionRect;
291 count = 1;
292 }
293 else
294 {
295 for (size_t y = 0; y < regionRect->bottom - regionRect->top; y += 64)
296 {
297 for (size_t x = 0; x < regionRect->right - regionRect->left; x += 64)
298 {
299 RECTANGLE_16 rect;
300 rect.left = (UINT16)MIN(UINT16_MAX, regionRect->left + x);
301 rect.top = (UINT16)MIN(UINT16_MAX, regionRect->top + y);
302 rect.right =
303 (UINT16)MIN(UINT16_MAX, MIN(regionRect->left + x + 64, regionRect->right));
304 rect.bottom =
305 (UINT16)MIN(UINT16_MAX, MIN(regionRect->top + y + 64, regionRect->bottom));
306 if (diff_tile(&rect, pYUVData, pOldYUVData, iStride))
307 rectangles[count++] = rect;
308 }
309 }
310 }
311 if (!allocate_h264_metablock(QP, rectangles, meta, count))
312 return FALSE;
313 return TRUE;
314}
315
316INT32 h264_get_yuv_buffer(H264_CONTEXT* h264, UINT32 nSrcStride, UINT32 nSrcWidth,
317 UINT32 nSrcHeight, BYTE* YUVData[3], UINT32 stride[3])
318{
319 if (!h264 || !h264->Compressor || !h264->subsystem || !h264->subsystem->Compress)
320 return -1;
321
322 if (!yuv_ensure_buffer(h264, nSrcStride, nSrcWidth, nSrcHeight))
323 return -1;
324
325 for (size_t x = 0; x < 3; x++)
326 {
327 YUVData[x] = h264->pYUVData[x];
328 stride[x] = h264->iStride[x];
329 }
330
331 return 0;
332}
333
334INT32 h264_compress(H264_CONTEXT* h264, BYTE** ppDstData, UINT32* pDstSize)
335{
336 if (!h264 || !h264->Compressor || !h264->subsystem || !h264->subsystem->Compress)
337 return -1;
338
339 const BYTE* pcYUVData[3] = { h264->pYUVData[0], h264->pYUVData[1], h264->pYUVData[2] };
340
341 return h264->subsystem->Compress(h264, pcYUVData, h264->iStride, ppDstData, pDstSize);
342}
343
344INT32 avc420_compress(H264_CONTEXT* h264, const BYTE* pSrcData, DWORD SrcFormat, UINT32 nSrcStep,
345 UINT32 nSrcWidth, UINT32 nSrcHeight, const RECTANGLE_16* regionRect,
346 BYTE** ppDstData, UINT32* pDstSize, RDPGFX_H264_METABLOCK* meta)
347{
348 INT32 rc = -1;
349 BYTE* pYUVData[3] = WINPR_C_ARRAY_INIT;
350 const BYTE* pcYUVData[3] = WINPR_C_ARRAY_INIT;
351 BYTE* pOldYUVData[3] = WINPR_C_ARRAY_INIT;
352
353 if (!h264 || !regionRect || !meta || !h264->Compressor)
354 return -1;
355
356 if (!h264->subsystem->Compress)
357 return -1;
358
359 if (!avc420_ensure_buffer(h264, nSrcStep, nSrcWidth, nSrcHeight))
360 return -1;
361
362 if (h264->encodingBuffer)
363 {
364 for (size_t x = 0; x < 3; x++)
365 {
366 pYUVData[x] = h264->pYUVData[x];
367 pOldYUVData[x] = h264->pOldYUVData[x];
368 }
369 }
370 else
371 {
372 for (size_t x = 0; x < 3; x++)
373 {
374 pYUVData[x] = h264->pOldYUVData[x];
375 pOldYUVData[x] = h264->pYUVData[x];
376 }
377 }
378 h264->encodingBuffer = !h264->encodingBuffer;
379
380 if (!yuv420_context_encode(h264->yuv, pSrcData, nSrcStep, SrcFormat, h264->iStride, pYUVData,
381 regionRect, 1))
382 goto fail;
383
384 if (!detect_changes(h264->firstLumaFrameDone, h264->QP, regionRect, pYUVData, pOldYUVData,
385 h264->iStride, meta))
386 goto fail;
387
388 if (meta->numRegionRects == 0)
389 {
390 rc = 0;
391 goto fail;
392 }
393
394 for (size_t x = 0; x < 3; x++)
395 pcYUVData[x] = pYUVData[x];
396
397 rc = h264->subsystem->Compress(h264, pcYUVData, h264->iStride, ppDstData, pDstSize);
398 if (rc >= 0)
399 h264->firstLumaFrameDone = TRUE;
400
401fail:
402 if (rc < 0)
403 free_h264_metablock(meta);
404 return rc;
405}
406
407INT32 avc444_compress(H264_CONTEXT* h264, const BYTE* pSrcData, DWORD SrcFormat, UINT32 nSrcStep,
408 UINT32 nSrcWidth, UINT32 nSrcHeight, BYTE version, const RECTANGLE_16* region,
409 BYTE* op, BYTE** ppDstData, UINT32* pDstSize, BYTE** ppAuxDstData,
410 UINT32* pAuxDstSize, RDPGFX_H264_METABLOCK* meta,
411 RDPGFX_H264_METABLOCK* auxMeta)
412{
413 int rc = -1;
414 BYTE* coded = nullptr;
415 UINT32 codedSize = 0;
416 BYTE** pYUV444Data = nullptr;
417 BYTE** pOldYUV444Data = nullptr;
418 BYTE** pYUVData = nullptr;
419 BYTE** pOldYUVData = nullptr;
420
421 if (!h264 || !h264->Compressor)
422 return -1;
423
424 if (!h264->subsystem->Compress)
425 return -1;
426
427 if (!avc420_ensure_buffer(h264, nSrcStep, nSrcWidth, nSrcHeight))
428 return -1;
429
430 if (!avc444_ensure_buffer(h264, nSrcHeight))
431 return -1;
432
433 if (h264->encodingBuffer)
434 {
435 pYUV444Data = h264->pOldYUV444Data;
436 pOldYUV444Data = h264->pYUV444Data;
437 pYUVData = h264->pOldYUVData;
438 pOldYUVData = h264->pYUVData;
439 }
440 else
441 {
442 pYUV444Data = h264->pYUV444Data;
443 pOldYUV444Data = h264->pOldYUV444Data;
444 pYUVData = h264->pYUVData;
445 pOldYUVData = h264->pOldYUVData;
446 }
447 h264->encodingBuffer = !h264->encodingBuffer;
448
449 if (!yuv444_context_encode(h264->yuv, version, pSrcData, nSrcStep, SrcFormat, h264->iStride,
450 pYUV444Data, pYUVData, region, 1))
451 goto fail;
452
453 if (!detect_changes(h264->firstLumaFrameDone, h264->QP, region, pYUV444Data, pOldYUV444Data,
454 h264->iStride, meta))
455 goto fail;
456 if (!detect_changes(h264->firstChromaFrameDone, h264->QP, region, pYUVData, pOldYUVData,
457 h264->iStride, auxMeta))
458 goto fail;
459
460 /* [MS-RDPEGFX] 2.2.4.5 RFX_AVC444_BITMAP_STREAM
461 * LC:
462 * 0 ... Luma & Chroma
463 * 1 ... Luma
464 * 2 ... Chroma
465 */
466 if ((meta->numRegionRects > 0) && (auxMeta->numRegionRects > 0))
467 *op = 0;
468 else if (meta->numRegionRects > 0)
469 *op = 1;
470 else if (auxMeta->numRegionRects > 0)
471 *op = 2;
472 else
473 {
474 WLog_Print(h264->log, WLOG_TRACE, "no changes detected for luma or chroma frame");
475 rc = 0;
476 goto fail;
477 }
478
479 if ((*op == 0) || (*op == 1))
480 {
481 const BYTE* pcYUV444Data[3] = { pYUV444Data[0], pYUV444Data[1], pYUV444Data[2] };
482
483 if (h264->subsystem->Compress(h264, pcYUV444Data, h264->iStride, &coded, &codedSize) < 0)
484 goto fail;
485 h264->firstLumaFrameDone = TRUE;
486 memcpy(h264->lumaData, coded, codedSize);
487 *ppDstData = h264->lumaData;
488 *pDstSize = codedSize;
489 }
490
491 if ((*op == 0) || (*op == 2))
492 {
493 const BYTE* pcYUVData[3] = { pYUVData[0], pYUVData[1], pYUVData[2] };
494
495 if (h264->subsystem->Compress(h264, pcYUVData, h264->iStride, &coded, &codedSize) < 0)
496 goto fail;
497 h264->firstChromaFrameDone = TRUE;
498 *ppAuxDstData = coded;
499 *pAuxDstSize = codedSize;
500 }
501
502 rc = 1;
503fail:
504 if (rc < 0)
505 {
506 free_h264_metablock(meta);
507 free_h264_metablock(auxMeta);
508 }
509 return rc;
510}
511
512BOOL avc444_ensure_buffer(H264_CONTEXT* h264, DWORD nDstHeight)
513{
514 WINPR_ASSERT(h264);
515
516 const UINT32* piMainStride = h264->iStride;
517 UINT32* piDstSize = h264->iYUV444Size;
518 UINT32* piDstStride = h264->iYUV444Stride;
519 BYTE** ppYUVDstData = h264->pYUV444Data;
520 BYTE** ppOldYUVDstData = h264->pOldYUV444Data;
521
522 nDstHeight = MAX(h264->height, nDstHeight);
523 const UINT32 pad = nDstHeight % 16;
524 UINT32 padDstHeight = nDstHeight; /* Need alignment to 16x16 blocks */
525
526 if (pad != 0)
527 padDstHeight += 16 - pad;
528
529 /* Add padding lines. Allows relaxing bounds checks in decoder functions */
530 padDstHeight += 16;
531
532 if ((piMainStride[0] == 0) || (padDstHeight == 0))
533 return FALSE;
534
535 const uint64_t dstsize = 1ull * piMainStride[0] * padDstHeight;
536 if (dstsize > UINT32_MAX)
537 return FALSE;
538
539 if ((piMainStride[0] != piDstStride[0]) || (piDstSize[0] != dstsize))
540 {
541 for (UINT32 x = 0; x < 3; x++)
542 {
543 piDstStride[x] = piMainStride[0];
544
545 const uint64_t dstride = 1ull * piDstStride[x] * padDstHeight;
546 if (dstride > UINT32_MAX)
547 return FALSE;
548
549 piDstSize[x] = WINPR_ASSERTING_INT_CAST(UINT32, dstride);
550 if (piDstSize[x] == 0)
551 return FALSE;
552
553 BYTE* tmp1 = winpr_aligned_recalloc(ppYUVDstData[x], piDstSize[x], 1, 16);
554 if (!tmp1)
555 return FALSE;
556 ppYUVDstData[x] = tmp1;
557 BYTE* tmp2 = winpr_aligned_recalloc(ppOldYUVDstData[x], piDstSize[x], 1, 16);
558 if (!tmp2)
559 return FALSE;
560 ppOldYUVDstData[x] = tmp2;
561 }
562
563 {
564 BYTE* tmp = winpr_aligned_recalloc(h264->lumaData, piDstSize[0], 4, 16);
565 if (!tmp)
566 goto fail;
567 h264->lumaData = tmp;
568 }
569 }
570
571 for (UINT32 x = 0; x < 3; x++)
572 {
573 if (!ppOldYUVDstData[x] || !ppYUVDstData[x] || (piDstSize[x] == 0) || (piDstStride[x] == 0))
574 {
575 WLog_Print(h264->log, WLOG_ERROR,
576 "YUV buffer not initialized! check your decoder settings");
577 goto fail;
578 }
579 }
580
581 if (!h264->lumaData)
582 goto fail;
583
584 return TRUE;
585fail:
586 return FALSE;
587}
588
589static BOOL avc444_process_rects(H264_CONTEXT* h264, const BYTE* pSrcData, UINT32 SrcSize,
590 BYTE* pDstData, UINT32 DstFormat, UINT32 nDstStep,
591 WINPR_ATTR_UNUSED UINT32 nDstWidth, UINT32 nDstHeight,
592 const RECTANGLE_16* rects, UINT32 nrRects, avc444_frame_type type)
593{
594 const BYTE* pYUVData[3] = WINPR_C_ARRAY_INIT;
595 BYTE* pYUVDstData[3] = WINPR_C_ARRAY_INIT;
596 UINT32* piDstStride = h264->iYUV444Stride;
597 BYTE** ppYUVDstData = h264->pYUV444Data;
598 const UINT32* piStride = h264->iStride;
599
600 if (log_decompress(h264, pSrcData, SrcSize, rects, nrRects) < 0)
601 return FALSE;
602
603 pYUVData[0] = h264->pYUVData[0];
604 pYUVData[1] = h264->pYUVData[1];
605 pYUVData[2] = h264->pYUVData[2];
606 if (!avc444_ensure_buffer(h264, nDstHeight))
607 return FALSE;
608
609 pYUVDstData[0] = ppYUVDstData[0];
610 pYUVDstData[1] = ppYUVDstData[1];
611 pYUVDstData[2] = ppYUVDstData[2];
612 return (yuv444_context_decode(h264->yuv, (BYTE)type, pYUVData, piStride, h264->YUVHeight,
613 pYUVDstData, piDstStride, DstFormat, pDstData, nDstStep, rects,
614 nrRects));
615}
616
617#if defined(AVC444_FRAME_STAT)
618static UINT64 op1 = 0;
619static double op1sum = 0;
620static UINT64 op2 = 0;
621static double op2sum = 0;
622static UINT64 op3 = 0;
623static double op3sum = 0;
624static double avg(UINT64* count, double old, double size)
625{
626 double tmp = size + *count * old;
627 (*count)++;
628 tmp = tmp / *count;
629 return tmp;
630}
631#endif
632
633INT32 avc444_decompress(H264_CONTEXT* h264, BYTE op, const RECTANGLE_16* regionRects,
634 UINT32 numRegionRects, const BYTE* pSrcData, UINT32 SrcSize,
635 const RECTANGLE_16* auxRegionRects, UINT32 numAuxRegionRect,
636 const BYTE* pAuxSrcData, UINT32 AuxSrcSize, BYTE* pDstData, DWORD DstFormat,
637 UINT32 nDstStep, UINT32 nDstWidth, UINT32 nDstHeight, UINT32 codecId)
638{
639 INT32 status = -1;
640 avc444_frame_type chroma =
641 (codecId == RDPGFX_CODECID_AVC444) ? AVC444_CHROMAv1 : AVC444_CHROMAv2;
642
643 if (!h264 || !regionRects || !pSrcData || !pDstData || h264->Compressor)
644 return -1001;
645
646 if (!areRectsValid(h264->log, nDstWidth, nDstHeight, regionRects, numRegionRects))
647 return -1013;
648 if (!areRectsValid(h264->log, nDstWidth, nDstHeight, auxRegionRects, numAuxRegionRect))
649 return -1014;
650
651 switch (op)
652 {
653 case 0: /* YUV420 in stream 1
654 * Chroma420 in stream 2 */
655 if (!avc444_process_rects(h264, pSrcData, SrcSize, pDstData, DstFormat, nDstStep,
656 nDstWidth, nDstHeight, regionRects, numRegionRects,
657 AVC444_LUMA))
658 status = -1;
659 else if (!avc444_process_rects(h264, pAuxSrcData, AuxSrcSize, pDstData, DstFormat,
660 nDstStep, nDstWidth, nDstHeight, auxRegionRects,
661 numAuxRegionRect, chroma))
662 status = -1;
663 else
664 status = 0;
665
666 break;
667
668 case 2: /* Chroma420 in stream 1 */
669 if (!avc444_process_rects(h264, pSrcData, SrcSize, pDstData, DstFormat, nDstStep,
670 nDstWidth, nDstHeight, regionRects, numRegionRects, chroma))
671 status = -1;
672 else
673 status = 0;
674
675 break;
676
677 case 1: /* YUV420 in stream 1 */
678 if (!avc444_process_rects(h264, pSrcData, SrcSize, pDstData, DstFormat, nDstStep,
679 nDstWidth, nDstHeight, regionRects, numRegionRects,
680 AVC444_LUMA))
681 status = -1;
682 else
683 status = 0;
684
685 break;
686
687 default: /* WTF? */
688 break;
689 }
690
691#if defined(AVC444_FRAME_STAT)
692
693 switch (op)
694 {
695 case 0:
696 op1sum = avg(&op1, op1sum, SrcSize + AuxSrcSize);
697 break;
698
699 case 1:
700 op2sum = avg(&op2, op2sum, SrcSize);
701 break;
702
703 case 2:
704 op3sum = avg(&op3, op3sum, SrcSize);
705 break;
706
707 default:
708 break;
709 }
710
711 WLog_Print(h264->log, WLOG_INFO,
712 "luma=%" PRIu64 " [avg=%lf] chroma=%" PRIu64 " [avg=%lf] combined=%" PRIu64
713 " [avg=%lf]",
714 op1, op1sum, op2, op2sum, op3, op3sum);
715#endif
716 return status;
717}
718
719#define MAX_SUBSYSTEMS 10
720static INIT_ONCE subsystems_once = INIT_ONCE_STATIC_INIT;
721static const H264_CONTEXT_SUBSYSTEM* subSystems[MAX_SUBSYSTEMS] = WINPR_C_ARRAY_INIT;
722
723static BOOL CALLBACK h264_register_subsystems(WINPR_ATTR_UNUSED PINIT_ONCE once,
724 WINPR_ATTR_UNUSED PVOID param,
725 WINPR_ATTR_UNUSED PVOID* context)
726{
727 int i = 0;
728
729#ifdef WITH_MEDIACODEC
730 {
731 subSystems[i] = &g_Subsystem_mediacodec;
732 i++;
733 }
734#endif
735#if defined(_WIN32) && defined(WITH_MEDIA_FOUNDATION)
736 {
737 subSystems[i] = &g_Subsystem_MF;
738 i++;
739 }
740#endif
741#ifdef WITH_OPENH264
742 {
743 subSystems[i] = &g_Subsystem_OpenH264;
744 i++;
745 }
746#endif
747#ifdef WITH_VIDEO_FFMPEG
748 {
749 subSystems[i] = &g_Subsystem_libavcodec;
750 i++;
751 }
752#endif
753 return i > 0;
754}
755
756static BOOL h264_context_init(H264_CONTEXT* h264)
757{
758 if (!h264)
759 return FALSE;
760
761 h264->subsystem = nullptr;
762 if (!InitOnceExecuteOnce(&subsystems_once, h264_register_subsystems, nullptr, nullptr))
763 return FALSE;
764
765 for (size_t i = 0; i < MAX_SUBSYSTEMS; i++)
766 {
767 const H264_CONTEXT_SUBSYSTEM* subsystem = subSystems[i];
768
769 if (!subsystem || !subsystem->Init)
770 break;
771
772 if (subsystem->Init(h264))
773 {
774 h264->subsystem = subsystem;
775 return TRUE;
776 }
777 }
778
779 return FALSE;
780}
781
782BOOL h264_context_reset(H264_CONTEXT* h264, UINT32 width, UINT32 height)
783{
784 if (!h264)
785 return FALSE;
786
787 h264->width = width;
788 h264->height = height;
789
790 if (h264->subsystem && h264->subsystem->Uninit)
791 h264->subsystem->Uninit(h264);
792 if (!h264_context_init(h264))
793 return FALSE;
794
795 return yuv_context_reset(h264->yuv, width, height);
796}
797
798H264_CONTEXT* h264_context_new(BOOL Compressor)
799{
800 H264_CONTEXT* h264 = (H264_CONTEXT*)calloc(1, sizeof(H264_CONTEXT));
801 if (!h264)
802 return nullptr;
803
804 h264->log = WLog_Get(TAG);
805
806 if (!h264->log)
807 goto fail;
808
809 h264->Compressor = Compressor;
810 if (Compressor)
811 {
812 /* Default compressor settings, may be changed by caller */
813 h264->BitRate = 1000000;
814 h264->FrameRate = 30;
815 }
816
817 if (!h264_context_init(h264))
818 goto fail;
819
820 h264->yuv = yuv_context_new(Compressor, 0);
821 if (!h264->yuv)
822 goto fail;
823
824 return h264;
825
826fail:
827 WINPR_PRAGMA_DIAG_PUSH
828 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
829 h264_context_free(h264);
830 WINPR_PRAGMA_DIAG_POP
831 return nullptr;
832}
833
834void h264_context_free(H264_CONTEXT* h264)
835{
836 if (h264)
837 {
838 if (h264->subsystem)
839 {
840 WINPR_ASSERT(h264->subsystem->Uninit);
841 h264->subsystem->Uninit(h264);
842 }
843
844 for (size_t x = 0; x < 3; x++)
845 {
846 if (h264->Compressor)
847 {
848 winpr_aligned_free(h264->pYUVData[x]);
849 winpr_aligned_free(h264->pOldYUVData[x]);
850 }
851 winpr_aligned_free(h264->pYUV444Data[x]);
852 winpr_aligned_free(h264->pOldYUV444Data[x]);
853 }
854 winpr_aligned_free(h264->lumaData);
855
856 yuv_context_free(h264->yuv);
857 free(h264);
858 }
859}
860
861void free_h264_metablock(RDPGFX_H264_METABLOCK* meta)
862{
863 RDPGFX_H264_METABLOCK m = WINPR_C_ARRAY_INIT;
864 if (!meta)
865 return;
866 free(meta->quantQualityVals);
867 free(meta->regionRects);
868 *meta = m;
869}
870
871BOOL h264_context_set_option(H264_CONTEXT* h264, H264_CONTEXT_OPTION option, UINT32 value)
872{
873 WINPR_ASSERT(h264);
874 switch (option)
875 {
876 case H264_CONTEXT_OPTION_BITRATE:
877 h264->BitRate = value;
878 return TRUE;
879 case H264_CONTEXT_OPTION_FRAMERATE:
880 h264->FrameRate = value;
881 return TRUE;
882 case H264_CONTEXT_OPTION_RATECONTROL:
883 {
884 switch (value)
885 {
886 case H264_RATECONTROL_VBR:
887 h264->RateControlMode = H264_RATECONTROL_VBR;
888 return TRUE;
889 case H264_RATECONTROL_CQP:
890 h264->RateControlMode = H264_RATECONTROL_CQP;
891 return TRUE;
892 default:
893 WLog_Print(h264->log, WLOG_WARN,
894 "Unknown H264_CONTEXT_OPTION_RATECONTROL value [0x%08" PRIx32 "]",
895 value);
896 return FALSE;
897 }
898 return TRUE;
899 }
900 case H264_CONTEXT_OPTION_QP:
901 h264->QP = value;
902 return TRUE;
903 case H264_CONTEXT_OPTION_USAGETYPE:
904 h264->UsageType = value;
905 return TRUE;
906 case H264_CONTEXT_OPTION_HW_ACCEL:
907 h264->hwAccel = (value);
908 return TRUE;
909 default:
910 WLog_Print(h264->log, WLOG_WARN, "Unknown H264_CONTEXT_OPTION[0x%08" PRIx32 "]",
911 option);
912 return FALSE;
913 }
914}
915
916UINT32 h264_context_get_option(H264_CONTEXT* h264, H264_CONTEXT_OPTION option)
917{
918 WINPR_ASSERT(h264);
919 switch (option)
920 {
921 case H264_CONTEXT_OPTION_BITRATE:
922 return h264->BitRate;
923 case H264_CONTEXT_OPTION_FRAMERATE:
924 return h264->FrameRate;
925 case H264_CONTEXT_OPTION_RATECONTROL:
926 return h264->RateControlMode;
927 case H264_CONTEXT_OPTION_QP:
928 return h264->QP;
929 case H264_CONTEXT_OPTION_USAGETYPE:
930 return h264->UsageType;
931 case H264_CONTEXT_OPTION_HW_ACCEL:
932 return h264->hwAccel;
933 default:
934 WLog_Print(h264->log, WLOG_WARN, "Unknown H264_CONTEXT_OPTION[0x%08" PRIx32 "]",
935 option);
936 return 0;
937 }
938}