Coverage Report

Created: 2024-05-20 06:11

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