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