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