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