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