FreeRDP
Loading...
Searching...
No Matches
yuv.c
1#include <winpr/sysinfo.h>
2#include <winpr/assert.h>
3#include <winpr/cast.h>
4#include <winpr/pool.h>
5
6#include <freerdp/settings.h>
7#include <freerdp/codec/region.h>
8#include <freerdp/primitives.h>
9#include <freerdp/log.h>
10#include <freerdp/codec/yuv.h>
11
12#define TAG FREERDP_TAG("codec")
13
14#define TILE_SIZE 64
15
16typedef struct
17{
18 YUV_CONTEXT* context;
19 const BYTE* pYUVData[3];
20 UINT32 iStride[3];
21 DWORD DstFormat;
22 BYTE* dest;
23 UINT32 nDstStep;
24 RECTANGLE_16 rect;
25} YUV_PROCESS_WORK_PARAM;
26
27typedef struct
28{
29 YUV_CONTEXT* context;
30 const BYTE* pYUVData[3];
31 UINT32 iStride[3];
32 BYTE* pYUVDstData[3];
33 UINT32 iDstStride[3];
34 RECTANGLE_16 rect;
35 BYTE type;
36} YUV_COMBINE_WORK_PARAM;
37
38typedef struct
39{
40 YUV_CONTEXT* context;
41 const BYTE* pSrcData;
42
43 DWORD SrcFormat;
44 UINT32 nSrcStep;
45 RECTANGLE_16 rect;
46 BYTE version;
47
48 BYTE* pYUVLumaData[3];
49 BYTE* pYUVChromaData[3];
50 UINT32 iStride[3];
51} YUV_ENCODE_WORK_PARAM;
52
53struct S_YUV_CONTEXT
54{
55 UINT32 width, height;
56 BOOL useThreads;
57 BOOL encoder;
58 UINT32 nthreads;
59 UINT32 heightStep;
60
61 PTP_POOL threadPool;
62 TP_CALLBACK_ENVIRON ThreadPoolEnv;
63
64 UINT32 work_object_count;
65 PTP_WORK* work_objects;
66 YUV_ENCODE_WORK_PARAM* work_enc_params;
67 YUV_PROCESS_WORK_PARAM* work_dec_params;
68 YUV_COMBINE_WORK_PARAM* work_combined_params;
69};
70
71static INLINE BOOL avc420_yuv_to_rgb(const BYTE* WINPR_RESTRICT pYUVData[3],
72 const UINT32 iStride[3],
73 const RECTANGLE_16* WINPR_RESTRICT rect, UINT32 nDstStep,
74 BYTE* WINPR_RESTRICT pDstData, DWORD DstFormat)
75{
76 primitives_t* prims = primitives_get();
77 prim_size_t roi;
78 const BYTE* pYUVPoint[3];
79
80 WINPR_ASSERT(pYUVData);
81 WINPR_ASSERT(iStride);
82 WINPR_ASSERT(rect);
83 WINPR_ASSERT(pDstData);
84
85 const INT32 width = rect->right - rect->left;
86 const INT32 height = rect->bottom - rect->top;
87 BYTE* pDstPoint = pDstData + 1ULL * rect->top * nDstStep +
88 1ULL * rect->left * FreeRDPGetBytesPerPixel(DstFormat);
89
90 pYUVPoint[0] = pYUVData[0] + 1ULL * rect->top * iStride[0] + rect->left;
91 pYUVPoint[1] = pYUVData[1] + 1ULL * rect->top / 2 * iStride[1] + rect->left / 2;
92 pYUVPoint[2] = pYUVData[2] + 1ULL * rect->top / 2 * iStride[2] + rect->left / 2;
93
94 roi.width = WINPR_ASSERTING_INT_CAST(uint32_t, width);
95 roi.height = WINPR_ASSERTING_INT_CAST(uint32_t, height);
96
97 if (prims->YUV420ToRGB_8u_P3AC4R(pYUVPoint, iStride, pDstPoint, nDstStep, DstFormat, &roi) !=
98 PRIMITIVES_SUCCESS)
99 return FALSE;
100
101 return TRUE;
102}
103
104static INLINE BOOL avc444_yuv_to_rgb(const BYTE* WINPR_RESTRICT pYUVData[3],
105 const UINT32 iStride[3],
106 const RECTANGLE_16* WINPR_RESTRICT rect, UINT32 nDstStep,
107 BYTE* WINPR_RESTRICT pDstData, DWORD DstFormat)
108{
109 primitives_t* prims = primitives_get();
110 prim_size_t roi;
111 const BYTE* pYUVPoint[3];
112
113 WINPR_ASSERT(pYUVData);
114 WINPR_ASSERT(iStride);
115 WINPR_ASSERT(rect);
116 WINPR_ASSERT(pDstData);
117
118 const INT32 width = rect->right - rect->left;
119 const INT32 height = rect->bottom - rect->top;
120 BYTE* pDstPoint = pDstData + 1ULL * rect->top * nDstStep +
121 1ULL * rect->left * FreeRDPGetBytesPerPixel(DstFormat);
122
123 pYUVPoint[0] = pYUVData[0] + 1ULL * rect->top * iStride[0] + rect->left;
124 pYUVPoint[1] = pYUVData[1] + 1ULL * rect->top * iStride[1] + rect->left;
125 pYUVPoint[2] = pYUVData[2] + 1ULL * rect->top * iStride[2] + rect->left;
126
127 roi.width = WINPR_ASSERTING_INT_CAST(uint32_t, width);
128 roi.height = WINPR_ASSERTING_INT_CAST(uint32_t, height);
129
130 if (prims->YUV444ToRGB_8u_P3AC4R(pYUVPoint, iStride, pDstPoint, nDstStep, DstFormat, &roi) !=
131 PRIMITIVES_SUCCESS)
132 return FALSE;
133
134 return TRUE;
135}
136
137static void CALLBACK yuv420_process_work_callback(PTP_CALLBACK_INSTANCE instance, void* context,
138 PTP_WORK work)
139{
140 YUV_PROCESS_WORK_PARAM* param = (YUV_PROCESS_WORK_PARAM*)context;
141 WINPR_UNUSED(instance);
142 WINPR_UNUSED(work);
143 WINPR_ASSERT(param);
144
145 if (!avc420_yuv_to_rgb(param->pYUVData, param->iStride, &param->rect, param->nDstStep,
146 param->dest, param->DstFormat))
147 WLog_WARN(TAG, "avc420_yuv_to_rgb failed");
148}
149
150static void CALLBACK yuv444_process_work_callback(PTP_CALLBACK_INSTANCE instance, void* context,
151 PTP_WORK work)
152{
153 YUV_PROCESS_WORK_PARAM* param = (YUV_PROCESS_WORK_PARAM*)context;
154 WINPR_UNUSED(instance);
155 WINPR_UNUSED(work);
156 WINPR_ASSERT(param);
157
158 if (!avc444_yuv_to_rgb(param->pYUVData, param->iStride, &param->rect, param->nDstStep,
159 param->dest, param->DstFormat))
160 WLog_WARN(TAG, "avc444_yuv_to_rgb failed");
161}
162
163BOOL yuv_context_reset(YUV_CONTEXT* WINPR_RESTRICT context, UINT32 width, UINT32 height)
164{
165 BOOL rc = FALSE;
166 WINPR_ASSERT(context);
167
168 context->width = width;
169 context->height = height;
170
171 context->heightStep = height > context->nthreads ? (height / context->nthreads) : 1;
172
173 if (context->useThreads)
174 {
175 /* Preallocate workers for 16x16 tiles.
176 * this is overallocation for most cases.
177 *
178 * ~2MB total for a 4k resolution, so negligible.
179 */
180 const size_t pw = (width + TILE_SIZE - width % TILE_SIZE) / 16;
181 const size_t ph = (height + TILE_SIZE - height % TILE_SIZE) / 16;
182
183 const size_t count = pw * ph;
184
185 context->work_object_count = 0;
186 if (context->encoder)
187 {
188 void* tmp = winpr_aligned_recalloc(context->work_enc_params, count,
189 sizeof(YUV_ENCODE_WORK_PARAM), 32);
190 if (!tmp)
191 goto fail;
192 memset(tmp, 0, count * sizeof(YUV_ENCODE_WORK_PARAM));
193
194 context->work_enc_params = tmp;
195 }
196 else
197 {
198 void* tmp = winpr_aligned_recalloc(context->work_dec_params, count,
199 sizeof(YUV_PROCESS_WORK_PARAM), 32);
200 if (!tmp)
201 goto fail;
202 memset(tmp, 0, count * sizeof(YUV_PROCESS_WORK_PARAM));
203
204 context->work_dec_params = tmp;
205
206 void* ctmp = winpr_aligned_recalloc(context->work_combined_params, count,
207 sizeof(YUV_COMBINE_WORK_PARAM), 32);
208 if (!ctmp)
209 goto fail;
210 memset(ctmp, 0, count * sizeof(YUV_COMBINE_WORK_PARAM));
211
212 context->work_combined_params = ctmp;
213 }
214
215 void* wtmp =
216 winpr_aligned_recalloc((void*)context->work_objects, count, sizeof(PTP_WORK), 32);
217 if (!wtmp)
218 goto fail;
219 memset(wtmp, 0, count * sizeof(PTP_WORK));
220
221 context->work_objects = (PTP_WORK*)wtmp;
222 context->work_object_count = WINPR_ASSERTING_INT_CAST(uint32_t, count);
223 }
224 rc = TRUE;
225fail:
226 return rc;
227}
228
229YUV_CONTEXT* yuv_context_new(BOOL encoder, UINT32 ThreadingFlags)
230{
231 SYSTEM_INFO sysInfos;
232 YUV_CONTEXT* ret = winpr_aligned_calloc(1, sizeof(*ret), 32);
233 if (!ret)
234 return NULL;
235
237 primitives_get();
238
239 ret->encoder = encoder;
240 ret->nthreads = 1;
241 if (!(ThreadingFlags & THREADING_FLAGS_DISABLE_THREADS))
242 {
243 GetNativeSystemInfo(&sysInfos);
244 ret->useThreads = (sysInfos.dwNumberOfProcessors > 1);
245 if (ret->useThreads)
246 {
247 ret->nthreads = sysInfos.dwNumberOfProcessors;
248 ret->threadPool = CreateThreadpool(NULL);
249 if (!ret->threadPool)
250 {
251 goto error_threadpool;
252 }
253
254 InitializeThreadpoolEnvironment(&ret->ThreadPoolEnv);
255 SetThreadpoolCallbackPool(&ret->ThreadPoolEnv, ret->threadPool);
256 }
257 }
258
259 return ret;
260
261error_threadpool:
262 WINPR_PRAGMA_DIAG_PUSH
263 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
264 yuv_context_free(ret);
265 WINPR_PRAGMA_DIAG_POP
266 return NULL;
267}
268
269void yuv_context_free(YUV_CONTEXT* context)
270{
271 if (!context)
272 return;
273 if (context->useThreads)
274 {
275 if (context->threadPool)
276 CloseThreadpool(context->threadPool);
277 DestroyThreadpoolEnvironment(&context->ThreadPoolEnv);
278 winpr_aligned_free((void*)context->work_objects);
279 winpr_aligned_free(context->work_combined_params);
280 winpr_aligned_free(context->work_enc_params);
281 winpr_aligned_free(context->work_dec_params);
282 }
283 winpr_aligned_free(context);
284}
285
286static INLINE YUV_PROCESS_WORK_PARAM pool_decode_param(const RECTANGLE_16* WINPR_RESTRICT rect,
287 YUV_CONTEXT* WINPR_RESTRICT context,
288 const BYTE* WINPR_RESTRICT pYUVData[3],
289 const UINT32 iStride[3], UINT32 DstFormat,
290 BYTE* WINPR_RESTRICT dest, UINT32 nDstStep)
291{
292 YUV_PROCESS_WORK_PARAM current = { 0 };
293
294 WINPR_ASSERT(rect);
295 WINPR_ASSERT(context);
296 WINPR_ASSERT(pYUVData);
297 WINPR_ASSERT(iStride);
298 WINPR_ASSERT(dest);
299
300 current.context = context;
301 current.DstFormat = DstFormat;
302 current.pYUVData[0] = pYUVData[0];
303 current.pYUVData[1] = pYUVData[1];
304 current.pYUVData[2] = pYUVData[2];
305 current.iStride[0] = iStride[0];
306 current.iStride[1] = iStride[1];
307 current.iStride[2] = iStride[2];
308 current.nDstStep = nDstStep;
309 current.dest = dest;
310 current.rect = *rect;
311 return current;
312}
313
314static BOOL submit_object(PTP_WORK* WINPR_RESTRICT work_object, PTP_WORK_CALLBACK cb,
315 const void* WINPR_RESTRICT param, YUV_CONTEXT* WINPR_RESTRICT context)
316{
317 union
318 {
319 const void* cpv;
320 void* pv;
321 } cnv;
322
323 cnv.cpv = param;
324
325 if (!work_object)
326 return FALSE;
327
328 *work_object = NULL;
329
330 if (!param || !context)
331 return FALSE;
332
333 *work_object = CreateThreadpoolWork(cb, cnv.pv, &context->ThreadPoolEnv);
334 if (!*work_object)
335 return FALSE;
336
337 SubmitThreadpoolWork(*work_object);
338 return TRUE;
339}
340
341static void free_objects(PTP_WORK* work_objects, UINT32 waitCount)
342{
343 WINPR_ASSERT(work_objects || (waitCount == 0));
344
345 for (UINT32 i = 0; i < waitCount; i++)
346 {
347 PTP_WORK cur = work_objects[i];
348 work_objects[i] = NULL;
349
350 if (!cur)
351 continue;
352
353 WaitForThreadpoolWorkCallbacks(cur, FALSE);
354 CloseThreadpoolWork(cur);
355 }
356}
357
358static BOOL intersects(UINT32 pos, const RECTANGLE_16* WINPR_RESTRICT regionRects,
359 UINT32 numRegionRects)
360{
361 WINPR_ASSERT(regionRects || (numRegionRects == 0));
362
363 for (UINT32 x = pos + 1; x < numRegionRects; x++)
364 {
365 const RECTANGLE_16* what = &regionRects[pos];
366 const RECTANGLE_16* rect = &regionRects[x];
367
368 if (rectangles_intersects(what, rect))
369 {
370 WLog_WARN(TAG, "YUV decoder: intersecting rectangles, aborting");
371 return TRUE;
372 }
373 }
374
375 return FALSE;
376}
377
378static RECTANGLE_16 clamp(YUV_CONTEXT* WINPR_RESTRICT context,
379 const RECTANGLE_16* WINPR_RESTRICT rect, UINT32 srcHeight)
380{
381 WINPR_ASSERT(context);
382 WINPR_ASSERT(rect);
383
384 RECTANGLE_16 c = *rect;
385 const UINT32 height = MIN(context->height, srcHeight);
386 if (c.top > height)
387 c.top = WINPR_ASSERTING_INT_CAST(UINT16, height);
388 if (c.bottom > height)
389 c.bottom = WINPR_ASSERTING_INT_CAST(UINT16, height);
390 return c;
391}
392
393static BOOL pool_decode(YUV_CONTEXT* WINPR_RESTRICT context, PTP_WORK_CALLBACK cb,
394 const BYTE* WINPR_RESTRICT pYUVData[3], const UINT32 iStride[3],
395 UINT32 yuvHeight, UINT32 DstFormat, BYTE* WINPR_RESTRICT dest,
396 UINT32 nDstStep, const RECTANGLE_16* WINPR_RESTRICT regionRects,
397 UINT32 numRegionRects)
398{
399 BOOL rc = FALSE;
400 UINT32 waitCount = 0;
401 primitives_t* prims = primitives_get();
402
403 WINPR_ASSERT(context);
404 WINPR_ASSERT(cb);
405 WINPR_ASSERT(pYUVData);
406 WINPR_ASSERT(iStride);
407 WINPR_ASSERT(dest);
408 WINPR_ASSERT(regionRects || (numRegionRects == 0));
409
410 if (context->encoder)
411 {
412 WLog_ERR(TAG, "YUV context set up for encoding, can not decode with it, aborting");
413 return FALSE;
414 }
415
416 if (!context->useThreads || (primitives_flags(prims) & PRIM_FLAGS_HAVE_EXTGPU))
417 {
418 for (UINT32 y = 0; y < numRegionRects; y++)
419 {
420 const RECTANGLE_16 rect = clamp(context, &regionRects[y], yuvHeight);
421 YUV_PROCESS_WORK_PARAM current =
422 pool_decode_param(&rect, context, pYUVData, iStride, DstFormat, dest, nDstStep);
423 cb(NULL, &current, NULL);
424 }
425 return TRUE;
426 }
427
428 /* case where we use threads */
429 for (UINT32 x = 0; x < numRegionRects; x++)
430 {
431 RECTANGLE_16 r = clamp(context, &regionRects[x], yuvHeight);
432
433 if (intersects(x, regionRects, numRegionRects))
434 continue;
435
436 while (r.left < r.right)
437 {
438 RECTANGLE_16 y = r;
439 y.right = MIN(r.right, r.left + TILE_SIZE);
440
441 while (y.top < y.bottom)
442 {
443 RECTANGLE_16 z = y;
444
445 if (context->work_object_count <= waitCount)
446 {
447 free_objects(context->work_objects, context->work_object_count);
448 waitCount = 0;
449 }
450
451 YUV_PROCESS_WORK_PARAM* cur = &context->work_dec_params[waitCount];
452 z.bottom = MIN(z.bottom, z.top + TILE_SIZE);
453 if (rectangle_is_empty(&z))
454 continue;
455 *cur = pool_decode_param(&z, context, pYUVData, iStride, DstFormat, dest, nDstStep);
456 if (!submit_object(&context->work_objects[waitCount], cb, cur, context))
457 goto fail;
458 waitCount++;
459 y.top += TILE_SIZE;
460 }
461
462 r.left += TILE_SIZE;
463 }
464 }
465 rc = TRUE;
466fail:
467 free_objects(context->work_objects, context->work_object_count);
468 return rc;
469}
470
471static INLINE BOOL check_rect(const YUV_CONTEXT* WINPR_RESTRICT yuv,
472 const RECTANGLE_16* WINPR_RESTRICT rect, UINT32 nDstWidth,
473 UINT32 nDstHeight)
474{
475 WINPR_ASSERT(yuv);
476 WINPR_ASSERT(rect);
477
478 /* Check, if the output rectangle is valid in decoded h264 frame. */
479 if ((rect->right > yuv->width) || (rect->left > yuv->width))
480 return FALSE;
481
482 if ((rect->top > yuv->height) || (rect->bottom > yuv->height))
483 return FALSE;
484
485 /* Check, if the output rectangle is valid in destination buffer. */
486 if ((rect->right > nDstWidth) || (rect->left > nDstWidth))
487 return FALSE;
488
489 if ((rect->bottom > nDstHeight) || (rect->top > nDstHeight))
490 return FALSE;
491
492 return TRUE;
493}
494
495static void CALLBACK yuv444_combine_work_callback(PTP_CALLBACK_INSTANCE instance, void* context,
496 PTP_WORK work)
497{
498 YUV_COMBINE_WORK_PARAM* param = (YUV_COMBINE_WORK_PARAM*)context;
499 primitives_t* prims = primitives_get();
500
501 WINPR_ASSERT(param);
502 YUV_CONTEXT* yuv = param->context;
503 WINPR_ASSERT(yuv);
504
505 const RECTANGLE_16* rect = &param->rect;
506 WINPR_ASSERT(rect);
507
508 const UINT32 alignedWidth = yuv->width + ((yuv->width % 16 != 0) ? 16 - yuv->width % 16 : 0);
509 const UINT32 alignedHeight =
510 yuv->height + ((yuv->height % 16 != 0) ? 16 - yuv->height % 16 : 0);
511
512 WINPR_UNUSED(instance);
513 WINPR_UNUSED(work);
514
515 if (!check_rect(param->context, rect, yuv->width, yuv->height))
516 return;
517
518 if (prims->YUV420CombineToYUV444(param->type, param->pYUVData, param->iStride, alignedWidth,
519 alignedHeight, param->pYUVDstData, param->iDstStride,
520 rect) != PRIMITIVES_SUCCESS)
521 WLog_WARN(TAG, "YUV420CombineToYUV444 failed");
522}
523
524static INLINE YUV_COMBINE_WORK_PARAM
525pool_decode_rect_param(const RECTANGLE_16* WINPR_RESTRICT rect, YUV_CONTEXT* WINPR_RESTRICT context,
526 BYTE type, const BYTE* WINPR_RESTRICT pYUVData[3], const UINT32 iStride[3],
527 BYTE* WINPR_RESTRICT pYUVDstData[3], const UINT32 iDstStride[3])
528{
529 YUV_COMBINE_WORK_PARAM current = { 0 };
530
531 WINPR_ASSERT(rect);
532 WINPR_ASSERT(context);
533 WINPR_ASSERT(pYUVData);
534 WINPR_ASSERT(iStride);
535 WINPR_ASSERT(pYUVDstData);
536 WINPR_ASSERT(iDstStride);
537
538 current.context = context;
539 current.pYUVData[0] = pYUVData[0];
540 current.pYUVData[1] = pYUVData[1];
541 current.pYUVData[2] = pYUVData[2];
542 current.pYUVDstData[0] = pYUVDstData[0];
543 current.pYUVDstData[1] = pYUVDstData[1];
544 current.pYUVDstData[2] = pYUVDstData[2];
545 current.iStride[0] = iStride[0];
546 current.iStride[1] = iStride[1];
547 current.iStride[2] = iStride[2];
548 current.iDstStride[0] = iDstStride[0];
549 current.iDstStride[1] = iDstStride[1];
550 current.iDstStride[2] = iDstStride[2];
551 current.type = type;
552 current.rect = *rect;
553 return current;
554}
555
556static BOOL pool_decode_rect(YUV_CONTEXT* WINPR_RESTRICT context, BYTE type,
557 const BYTE* WINPR_RESTRICT pYUVData[3], const UINT32 iStride[3],
558 BYTE* WINPR_RESTRICT pYUVDstData[3], const UINT32 iDstStride[3],
559 const RECTANGLE_16* WINPR_RESTRICT regionRects, UINT32 numRegionRects)
560{
561 BOOL rc = FALSE;
562 UINT32 waitCount = 0;
563 PTP_WORK_CALLBACK cb = yuv444_combine_work_callback;
564 primitives_t* prims = primitives_get();
565
566 WINPR_ASSERT(context);
567 WINPR_ASSERT(pYUVData);
568 WINPR_ASSERT(iStride);
569 WINPR_ASSERT(pYUVDstData);
570 WINPR_ASSERT(iDstStride);
571 WINPR_ASSERT(regionRects || (numRegionRects == 0));
572
573 if (!context->useThreads || (primitives_flags(prims) & PRIM_FLAGS_HAVE_EXTGPU))
574 {
575 for (UINT32 y = 0; y < numRegionRects; y++)
576 {
577 YUV_COMBINE_WORK_PARAM current = pool_decode_rect_param(
578 &regionRects[y], context, type, pYUVData, iStride, pYUVDstData, iDstStride);
579 cb(NULL, &current, NULL);
580 }
581 return TRUE;
582 }
583
584 /* case where we use threads */
585 for (waitCount = 0; waitCount < numRegionRects; waitCount++)
586 {
587 YUV_COMBINE_WORK_PARAM* current = NULL;
588
589 if (context->work_object_count <= waitCount)
590 {
591 free_objects(context->work_objects, context->work_object_count);
592 waitCount = 0;
593 }
594 current = &context->work_combined_params[waitCount];
595 *current = pool_decode_rect_param(&regionRects[waitCount], context, type, pYUVData, iStride,
596 pYUVDstData, iDstStride);
597
598 if (!submit_object(&context->work_objects[waitCount], cb, current, context))
599 goto fail;
600 }
601
602 rc = TRUE;
603fail:
604 free_objects(context->work_objects, context->work_object_count);
605 return rc;
606}
607
608BOOL yuv444_context_decode(YUV_CONTEXT* WINPR_RESTRICT context, BYTE type,
609 const BYTE* WINPR_RESTRICT pYUVData[3], const UINT32 iStride[3],
610 UINT32 srcYuvHeight, BYTE* WINPR_RESTRICT pYUVDstData[3],
611 const UINT32 iDstStride[3], DWORD DstFormat, BYTE* WINPR_RESTRICT dest,
612 UINT32 nDstStep, const RECTANGLE_16* WINPR_RESTRICT regionRects,
613 UINT32 numRegionRects)
614{
615 const BYTE* pYUVCDstData[3];
616
617 WINPR_ASSERT(context);
618 WINPR_ASSERT(pYUVData);
619 WINPR_ASSERT(iStride);
620 WINPR_ASSERT(pYUVDstData);
621 WINPR_ASSERT(iDstStride);
622 WINPR_ASSERT(dest);
623 WINPR_ASSERT(regionRects || (numRegionRects == 0));
624
625 if (context->encoder)
626 {
627 WLog_ERR(TAG, "YUV context set up for encoding, can not decode with it, aborting");
628 return FALSE;
629 }
630 if (!pool_decode_rect(context, type, pYUVData, iStride, pYUVDstData, iDstStride, regionRects,
631 numRegionRects))
632 return FALSE;
633
634 pYUVCDstData[0] = pYUVDstData[0];
635 pYUVCDstData[1] = pYUVDstData[1];
636 pYUVCDstData[2] = pYUVDstData[2];
637 return pool_decode(context, yuv444_process_work_callback, pYUVCDstData, iDstStride,
638 srcYuvHeight, DstFormat, dest, nDstStep, regionRects, numRegionRects);
639}
640
641BOOL yuv420_context_decode(YUV_CONTEXT* WINPR_RESTRICT context,
642 const BYTE* WINPR_RESTRICT pYUVData[3], const UINT32 iStride[3],
643 UINT32 yuvHeight, DWORD DstFormat, BYTE* WINPR_RESTRICT dest,
644 UINT32 nDstStep, const RECTANGLE_16* WINPR_RESTRICT regionRects,
645 UINT32 numRegionRects)
646{
647 return pool_decode(context, yuv420_process_work_callback, pYUVData, iStride, yuvHeight,
648 DstFormat, dest, nDstStep, regionRects, numRegionRects);
649}
650
651static void CALLBACK yuv420_encode_work_callback(PTP_CALLBACK_INSTANCE instance, void* context,
652 PTP_WORK work)
653{
654 prim_size_t roi;
655 YUV_ENCODE_WORK_PARAM* param = (YUV_ENCODE_WORK_PARAM*)context;
656 primitives_t* prims = primitives_get();
657 BYTE* pYUVData[3];
658 const BYTE* src = NULL;
659
660 WINPR_UNUSED(instance);
661 WINPR_UNUSED(work);
662 WINPR_ASSERT(param);
663
664 roi.width = param->rect.right - param->rect.left;
665 roi.height = param->rect.bottom - param->rect.top;
666 src = param->pSrcData + 1ULL * param->nSrcStep * param->rect.top +
667 1ULL * param->rect.left * FreeRDPGetBytesPerPixel(param->SrcFormat);
668 pYUVData[0] =
669 param->pYUVLumaData[0] + 1ULL * param->rect.top * param->iStride[0] + param->rect.left;
670 pYUVData[1] = param->pYUVLumaData[1] + 1ULL * param->rect.top / 2 * param->iStride[1] +
671 param->rect.left / 2;
672 pYUVData[2] = param->pYUVLumaData[2] + 1ULL * param->rect.top / 2 * param->iStride[2] +
673 param->rect.left / 2;
674
675 if (prims->RGBToYUV420_8u_P3AC4R(src, param->SrcFormat, param->nSrcStep, pYUVData,
676 param->iStride, &roi) != PRIMITIVES_SUCCESS)
677 {
678 WLog_ERR(TAG, "error when decoding lines");
679 }
680}
681
682static void CALLBACK yuv444v1_encode_work_callback(PTP_CALLBACK_INSTANCE instance, void* context,
683 PTP_WORK work)
684{
685 prim_size_t roi;
686 YUV_ENCODE_WORK_PARAM* param = (YUV_ENCODE_WORK_PARAM*)context;
687 primitives_t* prims = primitives_get();
688 BYTE* pYUVLumaData[3];
689 BYTE* pYUVChromaData[3];
690 const BYTE* src = NULL;
691
692 WINPR_UNUSED(instance);
693 WINPR_UNUSED(work);
694 WINPR_ASSERT(param);
695
696 roi.width = param->rect.right - param->rect.left;
697 roi.height = param->rect.bottom - param->rect.top;
698 src = param->pSrcData + 1ULL * param->nSrcStep * param->rect.top +
699 1ULL * param->rect.left * FreeRDPGetBytesPerPixel(param->SrcFormat);
700 pYUVLumaData[0] =
701 param->pYUVLumaData[0] + 1ULL * param->rect.top * param->iStride[0] + param->rect.left;
702 pYUVLumaData[1] = param->pYUVLumaData[1] + 1ULL * param->rect.top / 2 * param->iStride[1] +
703 param->rect.left / 2;
704 pYUVLumaData[2] = param->pYUVLumaData[2] + 1ULL * param->rect.top / 2 * param->iStride[2] +
705 param->rect.left / 2;
706 pYUVChromaData[0] =
707 param->pYUVChromaData[0] + 1ULL * param->rect.top * param->iStride[0] + param->rect.left;
708 pYUVChromaData[1] = param->pYUVChromaData[1] + 1ULL * param->rect.top / 2 * param->iStride[1] +
709 param->rect.left / 2;
710 pYUVChromaData[2] = param->pYUVChromaData[2] + 1ULL * param->rect.top / 2 * param->iStride[2] +
711 param->rect.left / 2;
712 if (prims->RGBToAVC444YUV(src, param->SrcFormat, param->nSrcStep, pYUVLumaData, param->iStride,
713 pYUVChromaData, param->iStride, &roi) != PRIMITIVES_SUCCESS)
714 {
715 WLog_ERR(TAG, "error when decoding lines");
716 }
717}
718
719static void CALLBACK yuv444v2_encode_work_callback(PTP_CALLBACK_INSTANCE instance, void* context,
720 PTP_WORK work)
721{
722 prim_size_t roi;
723 YUV_ENCODE_WORK_PARAM* param = (YUV_ENCODE_WORK_PARAM*)context;
724 primitives_t* prims = primitives_get();
725 BYTE* pYUVLumaData[3];
726 BYTE* pYUVChromaData[3];
727 const BYTE* src = NULL;
728
729 WINPR_UNUSED(instance);
730 WINPR_UNUSED(work);
731 WINPR_ASSERT(param);
732
733 roi.width = param->rect.right - param->rect.left;
734 roi.height = param->rect.bottom - param->rect.top;
735 src = param->pSrcData + 1ULL * param->nSrcStep * param->rect.top +
736 1ULL * param->rect.left * FreeRDPGetBytesPerPixel(param->SrcFormat);
737 pYUVLumaData[0] =
738 param->pYUVLumaData[0] + 1ULL * param->rect.top * param->iStride[0] + param->rect.left;
739 pYUVLumaData[1] = param->pYUVLumaData[1] + 1ULL * param->rect.top / 2 * param->iStride[1] +
740 param->rect.left / 2;
741 pYUVLumaData[2] = param->pYUVLumaData[2] + 1ULL * param->rect.top / 2 * param->iStride[2] +
742 param->rect.left / 2;
743 pYUVChromaData[0] =
744 param->pYUVChromaData[0] + 1ULL * param->rect.top * param->iStride[0] + param->rect.left;
745 pYUVChromaData[1] = param->pYUVChromaData[1] + 1ULL * param->rect.top / 2 * param->iStride[1] +
746 param->rect.left / 2;
747 pYUVChromaData[2] = param->pYUVChromaData[2] + 1ULL * param->rect.top / 2 * param->iStride[2] +
748 param->rect.left / 2;
749 if (prims->RGBToAVC444YUVv2(src, param->SrcFormat, param->nSrcStep, pYUVLumaData,
750 param->iStride, pYUVChromaData, param->iStride,
751 &roi) != PRIMITIVES_SUCCESS)
752 {
753 WLog_ERR(TAG, "error when decoding lines");
754 }
755}
756
757static INLINE YUV_ENCODE_WORK_PARAM pool_encode_fill(
758 const RECTANGLE_16* WINPR_RESTRICT rect, YUV_CONTEXT* WINPR_RESTRICT context,
759 const BYTE* WINPR_RESTRICT pSrcData, UINT32 nSrcStep, UINT32 SrcFormat, const UINT32 iStride[],
760 BYTE* WINPR_RESTRICT pYUVLumaData[], BYTE* WINPR_RESTRICT pYUVChromaData[])
761{
762 YUV_ENCODE_WORK_PARAM current = { 0 };
763
764 WINPR_ASSERT(rect);
765 WINPR_ASSERT(context);
766 WINPR_ASSERT(pSrcData);
767 WINPR_ASSERT(iStride);
768 WINPR_ASSERT(pYUVLumaData);
769
770 current.context = context;
771 current.pSrcData = pSrcData;
772 current.SrcFormat = SrcFormat;
773 current.nSrcStep = nSrcStep;
774 current.pYUVLumaData[0] = pYUVLumaData[0];
775 current.pYUVLumaData[1] = pYUVLumaData[1];
776 current.pYUVLumaData[2] = pYUVLumaData[2];
777 if (pYUVChromaData)
778 {
779 current.pYUVChromaData[0] = pYUVChromaData[0];
780 current.pYUVChromaData[1] = pYUVChromaData[1];
781 current.pYUVChromaData[2] = pYUVChromaData[2];
782 }
783 current.iStride[0] = iStride[0];
784 current.iStride[1] = iStride[1];
785 current.iStride[2] = iStride[2];
786
787 current.rect = *rect;
788
789 return current;
790}
791
792static BOOL pool_encode(YUV_CONTEXT* WINPR_RESTRICT context, PTP_WORK_CALLBACK cb,
793 const BYTE* WINPR_RESTRICT pSrcData, UINT32 nSrcStep, UINT32 SrcFormat,
794 const UINT32 iStride[], BYTE* WINPR_RESTRICT pYUVLumaData[],
795 BYTE* WINPR_RESTRICT pYUVChromaData[],
796 const RECTANGLE_16* WINPR_RESTRICT regionRects, UINT32 numRegionRects)
797{
798 BOOL rc = FALSE;
799 primitives_t* prims = primitives_get();
800 UINT32 waitCount = 0;
801
802 WINPR_ASSERT(context);
803 WINPR_ASSERT(cb);
804 WINPR_ASSERT(pSrcData);
805 WINPR_ASSERT(iStride);
806 WINPR_ASSERT(regionRects || (numRegionRects == 0));
807
808 if (!context->encoder)
809 {
810
811 WLog_ERR(TAG, "YUV context set up for decoding, can not encode with it, aborting");
812 return FALSE;
813 }
814
815 if (!context->useThreads || (primitives_flags(prims) & PRIM_FLAGS_HAVE_EXTGPU))
816 {
817 for (UINT32 x = 0; x < numRegionRects; x++)
818 {
819 YUV_ENCODE_WORK_PARAM current =
820 pool_encode_fill(&regionRects[x], context, pSrcData, nSrcStep, SrcFormat, iStride,
821 pYUVLumaData, pYUVChromaData);
822 cb(NULL, &current, NULL);
823 }
824 return TRUE;
825 }
826
827 /* case where we use threads */
828 for (UINT32 x = 0; x < numRegionRects; x++)
829 {
830 const RECTANGLE_16* rect = &regionRects[x];
831 const UINT32 height = rect->bottom - rect->top;
832 const UINT32 steps = (height + context->heightStep / 2) / context->heightStep;
833
834 waitCount += steps;
835 }
836
837 for (UINT32 x = 0; x < numRegionRects; x++)
838 {
839 const RECTANGLE_16* rect = &regionRects[x];
840 const UINT32 height = rect->bottom - rect->top;
841 const UINT32 steps = (height + context->heightStep / 2) / context->heightStep;
842
843 for (UINT32 y = 0; y < steps; y++)
844 {
845 RECTANGLE_16 r = *rect;
846 YUV_ENCODE_WORK_PARAM* current = NULL;
847
848 if (context->work_object_count <= waitCount)
849 {
850 free_objects(context->work_objects, context->work_object_count);
851 waitCount = 0;
852 }
853
854 current = &context->work_enc_params[waitCount];
855 r.top += y * context->heightStep;
856 *current = pool_encode_fill(&r, context, pSrcData, nSrcStep, SrcFormat, iStride,
857 pYUVLumaData, pYUVChromaData);
858 if (!submit_object(&context->work_objects[waitCount], cb, current, context))
859 goto fail;
860 waitCount++;
861 }
862 }
863
864 rc = TRUE;
865fail:
866 free_objects(context->work_objects, context->work_object_count);
867 return rc;
868}
869
870BOOL yuv420_context_encode(YUV_CONTEXT* WINPR_RESTRICT context, const BYTE* WINPR_RESTRICT pSrcData,
871 UINT32 nSrcStep, UINT32 SrcFormat, const UINT32 iStride[3],
872 BYTE* WINPR_RESTRICT pYUVData[3],
873 const RECTANGLE_16* WINPR_RESTRICT regionRects, UINT32 numRegionRects)
874{
875 if (!context || !pSrcData || !iStride || !pYUVData || !regionRects)
876 return FALSE;
877
878 return pool_encode(context, yuv420_encode_work_callback, pSrcData, nSrcStep, SrcFormat, iStride,
879 pYUVData, NULL, regionRects, numRegionRects);
880}
881
882BOOL yuv444_context_encode(YUV_CONTEXT* WINPR_RESTRICT context, BYTE version,
883 const BYTE* WINPR_RESTRICT pSrcData, UINT32 nSrcStep, UINT32 SrcFormat,
884 const UINT32 iStride[3], BYTE* WINPR_RESTRICT pYUVLumaData[3],
885 BYTE* WINPR_RESTRICT pYUVChromaData[3],
886 const RECTANGLE_16* WINPR_RESTRICT regionRects, UINT32 numRegionRects)
887{
888 PTP_WORK_CALLBACK cb = NULL;
889 switch (version)
890 {
891 case 1:
892 cb = yuv444v1_encode_work_callback;
893 break;
894 case 2:
895 cb = yuv444v2_encode_work_callback;
896 break;
897 default:
898 return FALSE;
899 }
900
901 return pool_encode(context, cb, pSrcData, nSrcStep, SrcFormat, iStride, pYUVLumaData,
902 pYUVChromaData, regionRects, numRegionRects);
903}