FreeRDP
Loading...
Searching...
No Matches
shadow_encoder.c
1
19#include <freerdp/config.h>
20
21#include <errno.h>
22#include <stdlib.h>
23
24#include <winpr/assert.h>
25#include <winpr/sysinfo.h>
26
27#include "shadow.h"
28
29#include "shadow_encoder.h"
30
31#include <freerdp/log.h>
32#define TAG CLIENT_TAG("shadow")
33
34UINT32 shadow_encoder_preferred_fps(rdpShadowEncoder* encoder)
35{
36 /* Return preferred fps calculated according to the last
37 * sent frame id and last client-acknowledged frame id.
38 */
39 return encoder->fps;
40}
41
42UINT32 shadow_encoder_inflight_frames(rdpShadowEncoder* encoder)
43{
44 /* Return in-flight frame count.
45 * If queueDepth is SUSPEND_FRAME_ACKNOWLEDGEMENT, count = 0
46 * Otherwise, calculate count =
47 * <last sent frame id> - <last client-acknowledged frame id>
48 * Note: This function is exported so that subsystem could
49 * implement its own strategy to tune fps.
50 */
51 if (encoder->queueDepth == SUSPEND_FRAME_ACKNOWLEDGEMENT)
52 return 0;
53 if (encoder->lastAckframeId > encoder->frameId)
54 return 1;
55 return encoder->frameId - encoder->lastAckframeId;
56}
57
58UINT32 shadow_encoder_create_frame_id(rdpShadowEncoder* encoder)
59{
60 const UINT64 inFlightFrames = shadow_encoder_inflight_frames(encoder);
61
62 /*
63 * Calculate preferred fps according to how much frames are
64 * in-progress. Note that it only works when subsystem implementation
65 * calls shadow_encoder_preferred_fps and takes the suggestion.
66 */
67 if (inFlightFrames > 1)
68 {
69 UINT64 fps = (100 / (inFlightFrames + 1) * encoder->maxFps) / 100;
70 if (fps > encoder->maxFps)
71 fps = encoder->maxFps;
72 encoder->fps = WINPR_ASSERTING_INT_CAST(UINT32, fps);
73 }
74 else
75 {
76 encoder->fps += 2;
77
78 if (encoder->fps > encoder->maxFps)
79 encoder->fps = encoder->maxFps;
80 }
81
82 if (encoder->fps < 1)
83 encoder->fps = 1;
84
85 const UINT32 frameId = ++encoder->frameId;
86 return frameId;
87}
88
89WINPR_ATTR_NODISCARD
90static int shadow_encoder_init_grid(rdpShadowEncoder* encoder)
91{
92 UINT32 tileSize = 0;
93 UINT32 tileCount = 0;
94 encoder->gridWidth = ((encoder->width + (encoder->maxTileWidth - 1)) / encoder->maxTileWidth);
95 encoder->gridHeight =
96 ((encoder->height + (encoder->maxTileHeight - 1)) / encoder->maxTileHeight);
97 tileSize = encoder->maxTileWidth * encoder->maxTileHeight * 4;
98 tileCount = encoder->gridWidth * encoder->gridHeight;
99 encoder->gridBuffer = (BYTE*)calloc(tileSize, tileCount);
100
101 if (!encoder->gridBuffer)
102 return -1;
103
104 encoder->grid = (BYTE**)calloc(tileCount, sizeof(BYTE*));
105
106 if (!encoder->grid)
107 return -1;
108
109 for (UINT32 i = 0; i < encoder->gridHeight; i++)
110 {
111 for (UINT32 j = 0; j < encoder->gridWidth; j++)
112 {
113 const size_t k = (1ULL * i * encoder->gridWidth) + j;
114 encoder->grid[k] = &(encoder->gridBuffer[k * tileSize]);
115 }
116 }
117
118 return 0;
119}
120
121static int shadow_encoder_uninit_grid(rdpShadowEncoder* encoder)
122{
123 if (encoder->gridBuffer)
124 {
125 free(encoder->gridBuffer);
126 encoder->gridBuffer = nullptr;
127 }
128
129 if (encoder->grid)
130 {
131 free((void*)encoder->grid);
132 encoder->grid = nullptr;
133 }
134
135 encoder->gridWidth = 0;
136 encoder->gridHeight = 0;
137 return 0;
138}
139
140WINPR_ATTR_NODISCARD
141static int shadow_encoder_init_rfx(rdpShadowEncoder* encoder)
142{
143 if (!encoder->rfx)
144 encoder->rfx = rfx_context_new_ex(
145 TRUE, freerdp_settings_get_uint32(encoder->server->settings, FreeRDP_ThreadingFlags));
146
147 if (!encoder->rfx)
148 goto fail;
149
150 if (!rfx_context_reset(encoder->rfx, encoder->width, encoder->height))
151 goto fail;
152
153 {
154 const UINT32 mode =
155 freerdp_settings_get_uint32(encoder->server->settings, FreeRDP_RemoteFxRlgrMode);
156 if (!rfx_context_set_mode(encoder->rfx, WINPR_ASSERTING_INT_CAST(RLGR_MODE, mode)))
157 goto fail;
158 }
159 rfx_context_set_pixel_format(encoder->rfx, PIXEL_FORMAT_BGRX32);
160 encoder->codecs |= FREERDP_CODEC_REMOTEFX;
161 return 1;
162fail:
163 rfx_context_free(encoder->rfx);
164 encoder->rfx = nullptr;
165 return -1;
166}
167
168WINPR_ATTR_NODISCARD
169static int shadow_encoder_init_nsc(rdpShadowEncoder* encoder)
170{
171 rdpContext* context = (rdpContext*)encoder->client;
172 rdpSettings* settings = context->settings;
173
174 if (!encoder->nsc)
175 encoder->nsc = nsc_context_new();
176
177 if (!encoder->nsc)
178 goto fail;
179
180 if (!nsc_context_reset(encoder->nsc, encoder->width, encoder->height))
181 goto fail;
182
183 if (!nsc_context_set_parameters(
184 encoder->nsc, NSC_COLOR_LOSS_LEVEL,
185 freerdp_settings_get_uint32(settings, FreeRDP_NSCodecColorLossLevel)))
186 goto fail;
187 if (!nsc_context_set_parameters(
188 encoder->nsc, NSC_ALLOW_SUBSAMPLING,
189 freerdp_settings_get_bool(settings, FreeRDP_NSCodecAllowSubsampling) ? 1 : 0))
190 goto fail;
191 if (!nsc_context_set_parameters(
192 encoder->nsc, NSC_DYNAMIC_COLOR_FIDELITY,
193 !freerdp_settings_get_bool(settings, FreeRDP_NSCodecAllowDynamicColorFidelity)))
194 goto fail;
195 if (!nsc_context_set_parameters(encoder->nsc, NSC_COLOR_FORMAT, PIXEL_FORMAT_BGRX32))
196 goto fail;
197 encoder->codecs |= FREERDP_CODEC_NSCODEC;
198 return 1;
199fail:
200 nsc_context_free(encoder->nsc);
201 return -1;
202}
203
204WINPR_ATTR_NODISCARD
205static int shadow_encoder_init_planar(rdpShadowEncoder* encoder)
206{
207 DWORD planarFlags = 0;
208 rdpContext* context = (rdpContext*)encoder->client;
209 rdpSettings* settings = context->settings;
210
211 if (freerdp_settings_get_bool(settings, FreeRDP_DrawAllowSkipAlpha))
212 planarFlags |= PLANAR_FORMAT_HEADER_NA;
213
214 planarFlags |= PLANAR_FORMAT_HEADER_RLE;
215
216 if (!encoder->planar)
217 {
218 encoder->planar = freerdp_bitmap_planar_context_new(planarFlags, encoder->maxTileWidth,
219 encoder->maxTileHeight);
220 }
221
222 if (!encoder->planar)
223 goto fail;
224
225 if (!freerdp_bitmap_planar_context_reset(encoder->planar, encoder->maxTileWidth,
226 encoder->maxTileHeight))
227 goto fail;
228
229 encoder->codecs |= FREERDP_CODEC_PLANAR;
230 return 1;
231fail:
232 freerdp_bitmap_planar_context_free(encoder->planar);
233 return -1;
234}
235
236WINPR_ATTR_NODISCARD
237static int shadow_encoder_init_interleaved(rdpShadowEncoder* encoder)
238{
239 if (!encoder->interleaved)
240 encoder->interleaved = bitmap_interleaved_context_new(TRUE);
241
242 if (!encoder->interleaved)
243 goto fail;
244
245 if (!bitmap_interleaved_context_reset(encoder->interleaved))
246 goto fail;
247
248 encoder->codecs |= FREERDP_CODEC_INTERLEAVED;
249 return 1;
250fail:
251 bitmap_interleaved_context_free(encoder->interleaved);
252 return -1;
253}
254
255WINPR_ATTR_NODISCARD
256static int shadow_encoder_init_h264(rdpShadowEncoder* encoder)
257{
258 if (!encoder->h264)
259 encoder->h264 = h264_context_new(TRUE);
260
261 if (!encoder->h264)
262 goto fail;
263
264 if (!h264_context_reset(encoder->h264, encoder->width, encoder->height))
265 goto fail;
266
267 if (!h264_context_set_option(encoder->h264, H264_CONTEXT_OPTION_RATECONTROL,
268 encoder->server->h264RateControlMode))
269 goto fail;
270 if (!h264_context_set_option(encoder->h264, H264_CONTEXT_OPTION_BITRATE,
271 encoder->server->h264BitRate))
272 goto fail;
273 if (!h264_context_set_option(encoder->h264, H264_CONTEXT_OPTION_FRAMERATE,
274 encoder->server->h264FrameRate))
275 goto fail;
276 if (!h264_context_set_option(encoder->h264, H264_CONTEXT_OPTION_QP, encoder->server->h264QP))
277 goto fail;
278 if (!h264_context_set_option(encoder->h264, H264_CONTEXT_OPTION_HW_ACCEL,
279 (UINT32)encoder->server->h264HwAccel))
280 goto fail;
281
282 encoder->codecs |= FREERDP_CODEC_AVC420 | FREERDP_CODEC_AVC444;
283 return 1;
284fail:
285 h264_context_free(encoder->h264);
286 return -1;
287}
288
289#if defined(WITH_GFX_AV1)
290WINPR_ATTR_NODISCARD
291static int shadow_encoder_init_av1(rdpShadowEncoder* encoder, UINT32 codecs)
292{
293 WINPR_ASSERT(encoder);
294
295 UINT32 profile = 0;
296 if ((codecs & FREERDP_CODEC_AV1_I444) != 0)
297 profile = 1;
298
299 if (!encoder->av1)
300 encoder->av1 = freerdp_av1_context_new(TRUE);
301
302 if (!encoder->av1)
303 goto fail;
304
305 if (!freerdp_av1_context_reset(encoder->av1, encoder->width, encoder->height))
306 goto fail;
307
308 if (!freerdp_av1_context_set_option(encoder->av1, FREERDP_AV1_CONTEXT_OPTION_PROFILE, profile))
309 goto fail;
310
311 if (!freerdp_av1_context_set_option(encoder->av1, FREERDP_AV1_CONTEXT_OPTION_RATECONTROL,
312 encoder->server->AV1RateControlMode))
313 goto fail;
314 if (!freerdp_av1_context_set_option(encoder->av1, FREERDP_AV1_CONTEXT_OPTION_BITRATE,
315 encoder->server->AV1BitRate))
316 goto fail;
317
318 encoder->codecs &= ~(FREERDP_CODEC_AV1_I420 | FREERDP_CODEC_AV1_I444);
319 encoder->codecs |= codecs & (FREERDP_CODEC_AV1_I420 | FREERDP_CODEC_AV1_I444);
320 return 1;
321fail:
322 freerdp_av1_context_free(encoder->av1);
323 encoder->av1 = nullptr;
324 return -1;
325}
326#endif
327
328#if defined(WITH_GFX_AV2)
329WINPR_ATTR_NODISCARD
330static int shadow_encoder_init_av2(rdpShadowEncoder* encoder, UINT32 codecs)
331{
332 WINPR_ASSERT(encoder);
333
334 UINT32 profile = 0;
335 if ((codecs & FREERDP_CODEC_AV2_I444) != 0)
336 profile = 1;
337
338 if (!encoder->av2)
339 encoder->av2 = freerdp_av2_context_new(TRUE);
340
341 if (!encoder->av2)
342 goto fail;
343
344 if (!freerdp_av2_context_reset(encoder->av2, encoder->width, encoder->height))
345 goto fail;
346
347 if (!freerdp_av2_context_set_option(encoder->av2, FREERDP_AV2_CONTEXT_OPTION_PROFILE, profile))
348 goto fail;
349
350 if (!freerdp_av2_context_set_option(encoder->av2, FREERDP_AV2_CONTEXT_OPTION_RATECONTROL,
351 encoder->server->AV2RateControlMode))
352 goto fail;
353 if (!freerdp_av2_context_set_option(encoder->av2, FREERDP_AV2_CONTEXT_OPTION_BITRATE,
354 encoder->server->AV2BitRate))
355 goto fail;
356
357 encoder->codecs &= ~(FREERDP_CODEC_AV2_I420 | FREERDP_CODEC_AV2_I444);
358 encoder->codecs |= codecs & (FREERDP_CODEC_AV2_I420 | FREERDP_CODEC_AV2_I444);
359 return 1;
360fail:
361 freerdp_av2_context_free(encoder->av2);
362 encoder->av2 = nullptr;
363 return -1;
364}
365#endif
366
367WINPR_ATTR_NODISCARD
368static int shadow_encoder_init_progressive(rdpShadowEncoder* encoder)
369{
370 WINPR_ASSERT(encoder);
371 if (!encoder->progressive)
372 encoder->progressive = progressive_context_new(TRUE);
373
374 if (!encoder->progressive)
375 goto fail;
376
377 if (!progressive_context_reset(encoder->progressive))
378 goto fail;
379
380 encoder->codecs |= FREERDP_CODEC_PROGRESSIVE;
381 return 1;
382fail:
383 progressive_context_free(encoder->progressive);
384 return -1;
385}
386
387WINPR_ATTR_NODISCARD
388static int shadow_encoder_init(rdpShadowEncoder* encoder)
389{
390 encoder->width = encoder->server->screen->width;
391 encoder->height = encoder->server->screen->height;
392 encoder->maxTileWidth = 64;
393 encoder->maxTileHeight = 64;
394 if (shadow_encoder_init_grid(encoder) < 0)
395 return -1;
396
397 if (!encoder->bs)
398 encoder->bs = Stream_New(nullptr, 4ULL * encoder->maxTileWidth * encoder->maxTileHeight);
399
400 if (!encoder->bs)
401 return -1;
402
403 return 1;
404}
405
406static int shadow_encoder_uninit_rfx(rdpShadowEncoder* encoder)
407{
408 if (encoder->rfx)
409 {
410 rfx_context_free(encoder->rfx);
411 encoder->rfx = nullptr;
412 }
413
414 encoder->codecs &= (UINT32)~FREERDP_CODEC_REMOTEFX;
415 return 1;
416}
417
418static int shadow_encoder_uninit_nsc(rdpShadowEncoder* encoder)
419{
420 if (encoder->nsc)
421 {
422 nsc_context_free(encoder->nsc);
423 encoder->nsc = nullptr;
424 }
425
426 encoder->codecs &= (UINT32)~FREERDP_CODEC_NSCODEC;
427 return 1;
428}
429
430static int shadow_encoder_uninit_planar(rdpShadowEncoder* encoder)
431{
432 if (encoder->planar)
433 {
434 freerdp_bitmap_planar_context_free(encoder->planar);
435 encoder->planar = nullptr;
436 }
437
438 encoder->codecs &= (UINT32)~FREERDP_CODEC_PLANAR;
439 return 1;
440}
441
442static int shadow_encoder_uninit_interleaved(rdpShadowEncoder* encoder)
443{
444 if (encoder->interleaved)
445 {
446 bitmap_interleaved_context_free(encoder->interleaved);
447 encoder->interleaved = nullptr;
448 }
449
450 encoder->codecs &= (UINT32)~FREERDP_CODEC_INTERLEAVED;
451 return 1;
452}
453
454static int shadow_encoder_uninit_h264(rdpShadowEncoder* encoder)
455{
456 if (encoder->h264)
457 {
458 h264_context_free(encoder->h264);
459 encoder->h264 = nullptr;
460 }
461
462 encoder->codecs &= (UINT32) ~(FREERDP_CODEC_AVC420 | FREERDP_CODEC_AVC444);
463 return 1;
464}
465
466#if defined(WITH_GFX_AV1)
467static int shadow_encoder_uninit_av1(rdpShadowEncoder* encoder)
468{
469 WINPR_ASSERT(encoder);
470
471 freerdp_av1_context_free(encoder->av1);
472 encoder->av1 = nullptr;
473
474 encoder->codecs &= (UINT32) ~(FREERDP_CODEC_AV1_I420 | FREERDP_CODEC_AV1_I444);
475 return 1;
476}
477#endif
478
479#if defined(WITH_GFX_AV2)
480static int shadow_encoder_uninit_av2(rdpShadowEncoder* encoder)
481{
482 WINPR_ASSERT(encoder);
483
484 freerdp_av2_context_free(encoder->av2);
485 encoder->av2 = nullptr;
486
487 encoder->codecs &= (UINT32) ~(FREERDP_CODEC_AV2_I420 | FREERDP_CODEC_AV2_I444);
488 return 1;
489}
490#endif
491
492static int shadow_encoder_uninit_progressive(rdpShadowEncoder* encoder)
493{
494 WINPR_ASSERT(encoder);
495 if (encoder->progressive)
496 {
497 progressive_context_free(encoder->progressive);
498 encoder->progressive = nullptr;
499 }
500
501 encoder->codecs &= (UINT32)~FREERDP_CODEC_PROGRESSIVE;
502 return 1;
503}
504
505static int shadow_encoder_uninit(rdpShadowEncoder* encoder)
506{
507 shadow_encoder_uninit_grid(encoder);
508
509 if (encoder->bs)
510 {
511 Stream_Free(encoder->bs, TRUE);
512 encoder->bs = nullptr;
513 }
514
515 shadow_encoder_uninit_rfx(encoder);
516
517 shadow_encoder_uninit_nsc(encoder);
518
519 shadow_encoder_uninit_planar(encoder);
520
521 shadow_encoder_uninit_interleaved(encoder);
522 shadow_encoder_uninit_h264(encoder);
523#if defined(WITH_GFX_AV1)
524 shadow_encoder_uninit_av1(encoder);
525#endif
526#if defined(WITH_GFX_AV2)
527 shadow_encoder_uninit_av2(encoder);
528#endif
529
530 shadow_encoder_uninit_progressive(encoder);
531
532 /* Release the RFX dirty tile skip snapshot. shadow_encoder_reset also
533 * routes through here, so after a reset/free the next frame is always
534 * encoded in full. */
535 free(encoder->gfxSnapshot);
536 encoder->gfxSnapshot = nullptr;
537 free(encoder->gfxTileEncoded);
538 encoder->gfxTileEncoded = nullptr;
539 encoder->gfxSnapshotValid = FALSE;
540 encoder->gfxSnapshotWidth = 0;
541 encoder->gfxSnapshotHeight = 0;
542 encoder->gfxSnapshotStep = 0;
543
544 return 1;
545}
546
547int shadow_encoder_reset(rdpShadowEncoder* encoder)
548{
549 int status = 0;
550 UINT32 codecs = encoder->codecs;
551 rdpContext* context = (rdpContext*)encoder->client;
552 rdpSettings* settings = context->settings;
553 status = shadow_encoder_uninit(encoder);
554
555 if (status < 0)
556 return -1;
557
558 status = shadow_encoder_init(encoder);
559
560 if (status < 0)
561 return -1;
562
563 status = shadow_encoder_prepare(encoder, codecs);
564
565 if (status < 0)
566 return -1;
567
568 encoder->fps = 16;
569 encoder->maxFps = 32;
570 encoder->frameId = 0;
571 encoder->lastAckframeId = 0;
572 encoder->lastAckTick = GetTickCount64();
573 encoder->gfxHeld = FALSE;
574 encoder->frameAck = freerdp_settings_get_bool(settings, FreeRDP_SurfaceFrameMarkerEnabled);
575 return 1;
576}
577
578int shadow_encoder_prepare(rdpShadowEncoder* encoder, UINT32 codecs)
579{
580 int status = 0;
581
582 if ((codecs & FREERDP_CODEC_REMOTEFX) && !(encoder->codecs & FREERDP_CODEC_REMOTEFX))
583 {
584 WLog_DBG(TAG, "initializing RemoteFX encoder");
585 status = shadow_encoder_init_rfx(encoder);
586
587 if (status < 0)
588 return -1;
589 }
590
591 if ((codecs & FREERDP_CODEC_NSCODEC) && !(encoder->codecs & FREERDP_CODEC_NSCODEC))
592 {
593 WLog_DBG(TAG, "initializing NSCodec encoder");
594 status = shadow_encoder_init_nsc(encoder);
595
596 if (status < 0)
597 return -1;
598 }
599
600 if ((codecs & FREERDP_CODEC_PLANAR) && !(encoder->codecs & FREERDP_CODEC_PLANAR))
601 {
602 WLog_DBG(TAG, "initializing planar bitmap encoder");
603 status = shadow_encoder_init_planar(encoder);
604
605 if (status < 0)
606 return -1;
607 }
608
609 if ((codecs & FREERDP_CODEC_INTERLEAVED) && !(encoder->codecs & FREERDP_CODEC_INTERLEAVED))
610 {
611 WLog_DBG(TAG, "initializing interleaved bitmap encoder");
612 status = shadow_encoder_init_interleaved(encoder);
613
614 if (status < 0)
615 return -1;
616 }
617
618 if ((codecs & (FREERDP_CODEC_AVC420 | FREERDP_CODEC_AVC444)) &&
619 !(encoder->codecs & (FREERDP_CODEC_AVC420 | FREERDP_CODEC_AVC444)))
620 {
621 WLog_DBG(TAG, "initializing H.264 encoder");
622 status = shadow_encoder_init_h264(encoder);
623
624 if (status < 0)
625 return -1;
626 }
627
628 if ((codecs & FREERDP_CODEC_PROGRESSIVE) && !(encoder->codecs & FREERDP_CODEC_PROGRESSIVE))
629 {
630 WLog_DBG(TAG, "initializing progressive encoder");
631 status = shadow_encoder_init_progressive(encoder);
632
633 if (status < 0)
634 return -1;
635 }
636
637#if defined(WITH_GFX_AV1)
638 const UINT32 cmask = codecs & (FREERDP_CODEC_AV1_I420 | FREERDP_CODEC_AV1_I444);
639 const UINT32 emask = encoder->codecs & (FREERDP_CODEC_AV1_I420 | FREERDP_CODEC_AV1_I444);
640 /* only when AV1 is asked for: preparing another codec must not touch it */
641 if ((cmask != 0) && (cmask != emask))
642 {
643 WLog_DBG(TAG, "initializing AV1 encoder");
644 status = shadow_encoder_init_av1(encoder, codecs);
645
646 if (status < 0)
647 return -1;
648 }
649#endif
650
651#if defined(WITH_GFX_AV2)
652 const UINT32 cmask2 = codecs & (FREERDP_CODEC_AV2_I420 | FREERDP_CODEC_AV2_I444);
653 const UINT32 emask2 = encoder->codecs & (FREERDP_CODEC_AV2_I420 | FREERDP_CODEC_AV2_I444);
654 if ((cmask2 != 0) && (cmask2 != emask2))
655 {
656 WLog_DBG(TAG, "initializing AV2 encoder");
657 status = shadow_encoder_init_av2(encoder, codecs);
658
659 if (status < 0)
660 return -1;
661 }
662#endif
663
664 return 1;
665}
666
667rdpShadowEncoder* shadow_encoder_new(rdpShadowClient* client)
668{
669 rdpShadowEncoder* encoder = nullptr;
670 rdpShadowServer* server = client->server;
671 encoder = (rdpShadowEncoder*)calloc(1, sizeof(rdpShadowEncoder));
672
673 if (!encoder)
674 return nullptr;
675
676 encoder->client = client;
677 encoder->server = server;
678 encoder->fps = 16;
679 encoder->maxFps = 32;
680 encoder->lastAckTick = GetTickCount64();
681 {
682 /* like xrdp's XRDP_GFX_FRAMES_IN_FLIGHT; 0 turns the limit off */
683 // NOLINTNEXTLINE(concurrency-mt-unsafe)
684 const char* env = getenv("FREERDP_SHADOW_GFX_FRAMES_IN_FLIGHT");
685 encoder->maxInFlight = SHADOW_GFX_FRAMES_IN_FLIGHT;
686 if (env && *env)
687 {
688 char* end = nullptr;
689 errno = 0;
690 const unsigned long value = strtoul(env, &end, 10);
691 if ((errno == 0) && end && (*end == '\0') && (value <= 1000))
692 encoder->maxInFlight = (UINT32)value;
693 else
694 WLog_WARN(TAG, "FREERDP_SHADOW_GFX_FRAMES_IN_FLIGHT=%s ignored: 0 to 1000", env);
695 }
696 }
697
698 if (shadow_encoder_init(encoder) < 0)
699 {
700 shadow_encoder_free(encoder);
701 return nullptr;
702 }
703
704 return encoder;
705}
706
707void shadow_encoder_free(rdpShadowEncoder* encoder)
708{
709 if (!encoder)
710 return;
711
712 shadow_encoder_uninit(encoder);
713 free(encoder);
714}
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.