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