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