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