Coverage Report

Created: 2025-07-01 06:46

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