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