Coverage Report

Created: 2024-09-08 06:20

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