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