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