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