Coverage Report

Created: 2026-10-15 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/libfreerdp/codec/yuv.c
Line
Count
Source
1
#include <winpr/sysinfo.h>
2
#include <winpr/assert.h>
3
#include <winpr/cast.h>
4
#include <winpr/pool.h>
5
6
#include <freerdp/settings.h>
7
#include <freerdp/codec/region.h>
8
#include <freerdp/primitives.h>
9
#include <freerdp/log.h>
10
#include <freerdp/codec/yuv.h>
11
12
#define TAG FREERDP_TAG("codec")
13
14
#if !defined(YUV_TILE_SIZE)
15
#error "YUV_TILE_SIZE must be defined to the size of a single YUV decoder block"
16
#endif
17
18
typedef struct
19
{
20
  YUV_CONTEXT* context;
21
  const BYTE* pYUVData[3];
22
  UINT32 iStride[3];
23
  DWORD DstFormat;
24
  BYTE* dest;
25
  UINT32 nDstStep;
26
  RECTANGLE_16 rect;
27
} YUV_PROCESS_WORK_PARAM;
28
29
typedef struct
30
{
31
  YUV_CONTEXT* context;
32
  const BYTE* pYUVData[3];
33
  UINT32 iStride[3];
34
  BYTE* pYUVDstData[3];
35
  UINT32 iDstStride[3];
36
  RECTANGLE_16 rect;
37
  avc444_frame_type type;
38
} YUV_COMBINE_WORK_PARAM;
39
40
typedef struct
41
{
42
  YUV_CONTEXT* context;
43
  const BYTE* pSrcData;
44
45
  DWORD SrcFormat;
46
  UINT32 nSrcStep;
47
  RECTANGLE_16 rect;
48
  BYTE version;
49
50
  BYTE* pYUVLumaData[3];
51
  BYTE* pYUVChromaData[3];
52
  UINT32 iStride[3];
53
} YUV_ENCODE_WORK_PARAM;
54
55
struct S_YUV_CONTEXT
56
{
57
  UINT32 width, height;
58
  BOOL useThreads;
59
  BOOL encoder;
60
  UINT32 heightStep;
61
62
  UINT32 work_object_count;
63
  PTP_WORK* work_objects;
64
  YUV_ENCODE_WORK_PARAM* work_enc_params;
65
  YUV_PROCESS_WORK_PARAM* work_dec_params;
66
  YUV_COMBINE_WORK_PARAM* work_combined_params;
67
};
68
69
WINPR_ATTR_NODISCARD
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 = WINPR_ASSERTING_INT_CAST(uint32_t, width);
94
0
  roi.height = WINPR_ASSERTING_INT_CAST(uint32_t, height);
95
96
0
  return (prims->YUV420ToRGB_8u_P3AC4R(pYUVPoint, iStride, pDstPoint, nDstStep, DstFormat,
97
0
                                       &roi) == PRIMITIVES_SUCCESS);
98
0
}
99
100
WINPR_ATTR_NODISCARD
101
static inline BOOL avc444_yuv_to_rgb(const BYTE* WINPR_RESTRICT pYUVData[3],
102
                                     const UINT32 iStride[3],
103
                                     const RECTANGLE_16* WINPR_RESTRICT rect, UINT32 nDstStep,
104
                                     BYTE* WINPR_RESTRICT pDstData, DWORD DstFormat)
105
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 = pDstData + 1ULL * rect->top * nDstStep +
118
0
                    1ULL * rect->left * FreeRDPGetBytesPerPixel(DstFormat);
119
120
0
  pYUVPoint[0] = pYUVData[0] + 1ULL * rect->top * iStride[0] + rect->left;
121
0
  pYUVPoint[1] = pYUVData[1] + 1ULL * rect->top * iStride[1] + rect->left;
122
0
  pYUVPoint[2] = pYUVData[2] + 1ULL * rect->top * iStride[2] + rect->left;
123
124
0
  roi.width = WINPR_ASSERTING_INT_CAST(uint32_t, width);
125
0
  roi.height = WINPR_ASSERTING_INT_CAST(uint32_t, height);
126
127
0
  return (prims->YUV444ToRGB_8u_P3AC4R(pYUVPoint, iStride, pDstPoint, nDstStep, DstFormat,
128
0
                                       &roi) == PRIMITIVES_SUCCESS);
129
0
}
130
131
static void CALLBACK yuv420_process_work_callback(PTP_CALLBACK_INSTANCE instance, void* context,
132
                                                  PTP_WORK work)
133
0
{
134
0
  YUV_PROCESS_WORK_PARAM* param = (YUV_PROCESS_WORK_PARAM*)context;
135
0
  WINPR_UNUSED(instance);
136
0
  WINPR_UNUSED(work);
137
0
  WINPR_ASSERT(param);
138
139
0
  if (!avc420_yuv_to_rgb(param->pYUVData, param->iStride, &param->rect, param->nDstStep,
140
0
                         param->dest, param->DstFormat))
141
0
    WLog_WARN(TAG, "avc420_yuv_to_rgb failed");
142
0
}
143
144
static void CALLBACK yuv444_process_work_callback(PTP_CALLBACK_INSTANCE instance, void* context,
145
                                                  PTP_WORK work)
146
0
{
147
0
  YUV_PROCESS_WORK_PARAM* param = (YUV_PROCESS_WORK_PARAM*)context;
148
0
  WINPR_UNUSED(instance);
149
0
  WINPR_UNUSED(work);
150
0
  WINPR_ASSERT(param);
151
152
0
  if (!avc444_yuv_to_rgb(param->pYUVData, param->iStride, &param->rect, param->nDstStep,
153
0
                         param->dest, param->DstFormat))
154
0
    WLog_WARN(TAG, "avc444_yuv_to_rgb failed");
155
0
}
156
157
BOOL yuv_context_reset(YUV_CONTEXT* WINPR_RESTRICT context, UINT32 width, UINT32 height)
158
0
{
159
0
  BOOL rc = FALSE;
160
0
  WINPR_ASSERT(context);
161
162
0
  context->width = width;
163
0
  context->height = height;
164
165
0
  context->heightStep = height;
166
167
0
  if (context->useThreads)
168
0
  {
169
0
    context->heightStep = 16;
170
    /* Preallocate workers for 16x16 tiles.
171
     * this is overallocation for most cases.
172
     *
173
     * ~2MB total for a 4k resolution, so negligible.
174
     */
175
0
    const size_t pw = (width + YUV_TILE_SIZE - width % YUV_TILE_SIZE) / 16;
176
0
    const size_t ph = (height + YUV_TILE_SIZE - height % YUV_TILE_SIZE) / 16;
177
178
0
    const size_t count = pw * ph;
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 =
211
0
        winpr_aligned_recalloc((void*)context->work_objects, count, sizeof(PTP_WORK), 32);
212
0
    if (!wtmp)
213
0
      goto fail;
214
0
    memset(wtmp, 0, count * sizeof(PTP_WORK));
215
216
0
    context->work_objects = (PTP_WORK*)wtmp;
217
0
    context->work_object_count = WINPR_ASSERTING_INT_CAST(uint32_t, count);
218
0
  }
219
0
  rc = TRUE;
220
0
fail:
221
0
  return rc;
222
0
}
223
224
YUV_CONTEXT* yuv_context_new(BOOL encoder, UINT32 ThreadingFlags)
225
0
{
226
0
  SYSTEM_INFO sysInfos = WINPR_C_ARRAY_INIT;
227
  /** do it here to avoid a race condition between threads */
228
0
  if (!primitives_get())
229
0
    return nullptr;
230
231
0
  YUV_CONTEXT* ret = winpr_aligned_calloc(1, sizeof(*ret), 32);
232
0
  if (!ret)
233
0
    return nullptr;
234
235
0
  ret->encoder = encoder;
236
0
  if (!(ThreadingFlags & THREADING_FLAGS_DISABLE_THREADS))
237
0
  {
238
0
    GetNativeSystemInfo(&sysInfos);
239
0
    ret->useThreads = (sysInfos.dwNumberOfProcessors > 1);
240
0
  }
241
242
0
  return ret;
243
0
}
244
245
void yuv_context_free(YUV_CONTEXT* context)
246
0
{
247
0
  if (!context)
248
0
    return;
249
0
  if (context->useThreads)
250
0
  {
251
0
    winpr_aligned_free((void*)context->work_objects);
252
0
    winpr_aligned_free(context->work_combined_params);
253
0
    winpr_aligned_free(context->work_enc_params);
254
0
    winpr_aligned_free(context->work_dec_params);
255
0
  }
256
0
  winpr_aligned_free(context);
257
0
}
258
259
WINPR_ATTR_NODISCARD
260
static inline YUV_PROCESS_WORK_PARAM pool_decode_param(const RECTANGLE_16* WINPR_RESTRICT rect,
261
                                                       YUV_CONTEXT* WINPR_RESTRICT context,
262
                                                       const BYTE* WINPR_RESTRICT pYUVData[3],
263
                                                       const UINT32 iStride[3], UINT32 DstFormat,
264
                                                       BYTE* WINPR_RESTRICT dest, UINT32 nDstStep)
265
0
{
266
0
  YUV_PROCESS_WORK_PARAM current = WINPR_C_ARRAY_INIT;
267
268
0
  WINPR_ASSERT(rect);
269
0
  WINPR_ASSERT(context);
270
0
  WINPR_ASSERT(pYUVData);
271
0
  WINPR_ASSERT(iStride);
272
0
  WINPR_ASSERT(dest);
273
274
0
  current.context = context;
275
0
  current.DstFormat = DstFormat;
276
0
  current.pYUVData[0] = pYUVData[0];
277
0
  current.pYUVData[1] = pYUVData[1];
278
0
  current.pYUVData[2] = pYUVData[2];
279
0
  current.iStride[0] = iStride[0];
280
0
  current.iStride[1] = iStride[1];
281
0
  current.iStride[2] = iStride[2];
282
0
  current.nDstStep = nDstStep;
283
0
  current.dest = dest;
284
0
  current.rect = *rect;
285
0
  return current;
286
0
}
287
288
WINPR_ATTR_NODISCARD
289
static BOOL submit_object(PTP_WORK* WINPR_RESTRICT work_object, PTP_WORK_CALLBACK cb,
290
                          const void* WINPR_RESTRICT param, YUV_CONTEXT* WINPR_RESTRICT context)
291
0
{
292
0
  union
293
0
  {
294
0
    const void* cpv;
295
0
    void* pv;
296
0
  } cnv;
297
298
0
  cnv.cpv = param;
299
300
0
  if (!work_object)
301
0
    return FALSE;
302
303
0
  *work_object = nullptr;
304
305
0
  if (!param || !context)
306
0
    return FALSE;
307
308
0
  *work_object = CreateThreadpoolWork(cb, cnv.pv, nullptr);
309
0
  if (!*work_object)
310
0
    return FALSE;
311
312
0
  SubmitThreadpoolWork(*work_object);
313
0
  return TRUE;
314
0
}
315
316
static void free_objects(PTP_WORK* work_objects, UINT32 waitCount)
317
0
{
318
0
  WINPR_ASSERT(work_objects || (waitCount == 0));
319
320
0
  for (UINT32 i = 0; i < waitCount; i++)
321
0
  {
322
0
    PTP_WORK cur = work_objects[i];
323
0
    work_objects[i] = nullptr;
324
325
0
    if (!cur)
326
0
      continue;
327
328
0
    WaitForThreadpoolWorkCallbacks(cur, FALSE);
329
0
    CloseThreadpoolWork(cur);
330
0
  }
331
0
}
332
333
WINPR_ATTR_NODISCARD
334
static BOOL intersects(UINT32 pos, const RECTANGLE_16* WINPR_RESTRICT regionRects,
335
                       UINT32 numRegionRects)
336
0
{
337
0
  WINPR_ASSERT(regionRects || (numRegionRects == 0));
338
339
0
  for (UINT32 x = pos + 1; x < numRegionRects; x++)
340
0
  {
341
0
    const RECTANGLE_16* what = &regionRects[pos];
342
0
    const RECTANGLE_16* rect = &regionRects[x];
343
344
0
    if (rectangles_intersects(what, rect))
345
0
    {
346
0
      WLog_WARN(TAG, "YUV decoder: intersecting rectangles, aborting");
347
0
      return TRUE;
348
0
    }
349
0
  }
350
351
0
  return FALSE;
352
0
}
353
354
WINPR_ATTR_NODISCARD
355
static RECTANGLE_16 clamp(YUV_CONTEXT* WINPR_RESTRICT context,
356
                          const RECTANGLE_16* WINPR_RESTRICT rect, UINT32 srcHeight)
357
0
{
358
0
  WINPR_ASSERT(context);
359
0
  WINPR_ASSERT(rect);
360
361
0
  RECTANGLE_16 c = *rect;
362
0
  const UINT32 height = MIN(context->height, srcHeight);
363
0
  if (c.top > height)
364
0
    c.top = WINPR_ASSERTING_INT_CAST(UINT16, height);
365
0
  if (c.bottom > height)
366
0
    c.bottom = WINPR_ASSERTING_INT_CAST(UINT16, height);
367
0
  return c;
368
0
}
369
370
WINPR_ATTR_NODISCARD
371
static BOOL pool_decode(YUV_CONTEXT* WINPR_RESTRICT context, PTP_WORK_CALLBACK cb,
372
                        const BYTE* WINPR_RESTRICT pYUVData[3], const UINT32 iStride[3],
373
                        UINT32 yuvHeight, UINT32 DstFormat, BYTE* WINPR_RESTRICT dest,
374
                        UINT32 nDstStep, const RECTANGLE_16* WINPR_RESTRICT regionRects,
375
                        UINT32 numRegionRects)
376
0
{
377
0
  BOOL rc = FALSE;
378
0
  UINT32 waitCount = 0;
379
0
  primitives_t* prims = primitives_get();
380
381
0
  WINPR_ASSERT(context);
382
0
  WINPR_ASSERT(cb);
383
0
  WINPR_ASSERT(pYUVData);
384
0
  WINPR_ASSERT(iStride);
385
0
  WINPR_ASSERT(dest);
386
0
  WINPR_ASSERT(regionRects || (numRegionRects == 0));
387
388
0
  if (context->encoder)
389
0
  {
390
0
    WLog_ERR(TAG, "YUV context set up for encoding, can not decode with it, aborting");
391
0
    return FALSE;
392
0
  }
393
394
0
  if (!context->useThreads || (primitives_flags(prims) & PRIM_FLAGS_HAVE_EXTGPU))
395
0
  {
396
0
    for (UINT32 y = 0; y < numRegionRects; y++)
397
0
    {
398
0
      const RECTANGLE_16 rect = clamp(context, &regionRects[y], yuvHeight);
399
0
      YUV_PROCESS_WORK_PARAM current =
400
0
          pool_decode_param(&rect, context, pYUVData, iStride, DstFormat, dest, nDstStep);
401
0
      cb(nullptr, &current, nullptr);
402
0
    }
403
0
    return TRUE;
404
0
  }
405
406
  /* case where we use threads */
407
0
  for (UINT32 x = 0; x < numRegionRects; x++)
408
0
  {
409
0
    RECTANGLE_16 r = clamp(context, &regionRects[x], yuvHeight);
410
411
0
    if (intersects(x, regionRects, numRegionRects))
412
0
      continue;
413
414
0
    while (r.left < r.right)
415
0
    {
416
0
      RECTANGLE_16 y = r;
417
0
      y.right = MIN(r.right, r.left + YUV_TILE_SIZE);
418
419
0
      while (y.top < y.bottom)
420
0
      {
421
0
        RECTANGLE_16 z = y;
422
423
0
        if (context->work_object_count <= waitCount)
424
0
        {
425
0
          free_objects(context->work_objects, context->work_object_count);
426
0
          waitCount = 0;
427
0
        }
428
429
0
        YUV_PROCESS_WORK_PARAM* cur = &context->work_dec_params[waitCount];
430
0
        z.bottom = MIN(z.bottom, z.top + YUV_TILE_SIZE);
431
0
        if (rectangle_is_empty(&z))
432
0
          continue;
433
0
        *cur = pool_decode_param(&z, context, pYUVData, iStride, DstFormat, dest, nDstStep);
434
0
        if (!submit_object(&context->work_objects[waitCount], cb, cur, context))
435
0
          goto fail;
436
0
        waitCount++;
437
0
        y.top += YUV_TILE_SIZE;
438
0
      }
439
440
0
      r.left += YUV_TILE_SIZE;
441
0
    }
442
0
  }
443
0
  rc = TRUE;
444
0
fail:
445
0
  free_objects(context->work_objects, context->work_object_count);
446
0
  return rc;
447
0
}
448
449
WINPR_ATTR_NODISCARD
450
static inline BOOL check_rect(const YUV_CONTEXT* WINPR_RESTRICT yuv,
451
                              const RECTANGLE_16* WINPR_RESTRICT rect, UINT32 nDstWidth,
452
                              UINT32 nDstHeight)
453
0
{
454
0
  WINPR_ASSERT(yuv);
455
0
  WINPR_ASSERT(rect);
456
457
  /* Check, if the output rectangle is valid in decoded h264 frame. */
458
0
  if ((rect->right > yuv->width) || (rect->left > yuv->width))
459
0
    return FALSE;
460
461
0
  if ((rect->top > yuv->height) || (rect->bottom > yuv->height))
462
0
    return FALSE;
463
464
  /* Check, if the output rectangle is valid in destination buffer. */
465
0
  if ((rect->right > nDstWidth) || (rect->left > nDstWidth))
466
0
    return FALSE;
467
468
0
  if ((rect->bottom > nDstHeight) || (rect->top > nDstHeight))
469
0
    return FALSE;
470
471
0
  return TRUE;
472
0
}
473
474
static void CALLBACK yuv444_combine_work_callback(PTP_CALLBACK_INSTANCE instance, void* context,
475
                                                  PTP_WORK work)
476
0
{
477
0
  YUV_COMBINE_WORK_PARAM* param = (YUV_COMBINE_WORK_PARAM*)context;
478
0
  primitives_t* prims = primitives_get();
479
480
0
  WINPR_ASSERT(param);
481
0
  YUV_CONTEXT* yuv = param->context;
482
0
  WINPR_ASSERT(yuv);
483
484
0
  const RECTANGLE_16* rect = &param->rect;
485
0
  WINPR_ASSERT(rect);
486
487
  /* For AVC444v2 the combine reads the V (and second half U) samples from the
488
   * decoded auxiliary plane at a fixed nTotalWidth offset. A server may send a
489
   * frame smaller than the surface, so cap the aligned width at the Y plane
490
   * stride to keep that offset inside the decoded plane. */
491
0
  const UINT32 alignedWidth =
492
0
      MIN(yuv->width + ((yuv->width % 32 != 0) ? 32 - yuv->width % 32 : 0), param->iStride[0]);
493
0
  const UINT32 alignedHeight =
494
0
      yuv->height + ((yuv->height % 16 != 0) ? 16 - yuv->height % 16 : 0);
495
496
0
  WINPR_UNUSED(instance);
497
0
  WINPR_UNUSED(work);
498
499
0
  if (!check_rect(param->context, rect, yuv->width, yuv->height))
500
0
    return;
501
502
0
  if (prims->YUV420CombineToYUV444(param->type, param->pYUVData, param->iStride, alignedWidth,
503
0
                                   alignedHeight, param->pYUVDstData, param->iDstStride,
504
0
                                   rect) != PRIMITIVES_SUCCESS)
505
0
    WLog_WARN(TAG, "YUV420CombineToYUV444 failed");
506
0
}
507
508
WINPR_ATTR_NODISCARD
509
static inline YUV_COMBINE_WORK_PARAM
510
pool_decode_rect_param(const RECTANGLE_16* WINPR_RESTRICT rect, YUV_CONTEXT* WINPR_RESTRICT context,
511
                       BYTE type, const BYTE* WINPR_RESTRICT pYUVData[3], const UINT32 iStride[3],
512
                       BYTE* WINPR_RESTRICT pYUVDstData[3], const UINT32 iDstStride[3])
513
0
{
514
0
  YUV_COMBINE_WORK_PARAM current = WINPR_C_ARRAY_INIT;
515
516
0
  WINPR_ASSERT(rect);
517
0
  WINPR_ASSERT(context);
518
0
  WINPR_ASSERT(pYUVData);
519
0
  WINPR_ASSERT(iStride);
520
0
  WINPR_ASSERT(pYUVDstData);
521
0
  WINPR_ASSERT(iDstStride);
522
523
0
  current.context = context;
524
0
  current.pYUVData[0] = pYUVData[0];
525
0
  current.pYUVData[1] = pYUVData[1];
526
0
  current.pYUVData[2] = pYUVData[2];
527
0
  current.pYUVDstData[0] = pYUVDstData[0];
528
0
  current.pYUVDstData[1] = pYUVDstData[1];
529
0
  current.pYUVDstData[2] = pYUVDstData[2];
530
0
  current.iStride[0] = iStride[0];
531
0
  current.iStride[1] = iStride[1];
532
0
  current.iStride[2] = iStride[2];
533
0
  current.iDstStride[0] = iDstStride[0];
534
0
  current.iDstStride[1] = iDstStride[1];
535
0
  current.iDstStride[2] = iDstStride[2];
536
0
  current.type = WINPR_ASSERTING_INT_CAST(avc444_frame_type, type);
537
0
  current.rect = *rect;
538
0
  return current;
539
0
}
540
541
WINPR_ATTR_NODISCARD
542
static BOOL pool_decode_rect(YUV_CONTEXT* WINPR_RESTRICT context, BYTE type,
543
                             const BYTE* WINPR_RESTRICT pYUVData[3], const UINT32 iStride[3],
544
                             BYTE* WINPR_RESTRICT pYUVDstData[3], const UINT32 iDstStride[3],
545
                             const RECTANGLE_16* WINPR_RESTRICT regionRects, UINT32 numRegionRects)
546
0
{
547
0
  BOOL rc = FALSE;
548
0
  UINT32 waitCount = 0;
549
0
  PTP_WORK_CALLBACK cb = yuv444_combine_work_callback;
550
0
  primitives_t* prims = primitives_get();
551
552
0
  WINPR_ASSERT(context);
553
0
  WINPR_ASSERT(pYUVData);
554
0
  WINPR_ASSERT(iStride);
555
0
  WINPR_ASSERT(pYUVDstData);
556
0
  WINPR_ASSERT(iDstStride);
557
0
  WINPR_ASSERT(regionRects || (numRegionRects == 0));
558
559
0
  if (!context->useThreads || (primitives_flags(prims) & PRIM_FLAGS_HAVE_EXTGPU))
560
0
  {
561
0
    for (UINT32 y = 0; y < numRegionRects; y++)
562
0
    {
563
0
      YUV_COMBINE_WORK_PARAM current = pool_decode_rect_param(
564
0
          &regionRects[y], context, type, pYUVData, iStride, pYUVDstData, iDstStride);
565
0
      cb(nullptr, &current, nullptr);
566
0
    }
567
0
    return TRUE;
568
0
  }
569
570
  /* case where we use threads */
571
0
  for (UINT32 x = 0; x < numRegionRects; x++)
572
0
  {
573
0
    YUV_COMBINE_WORK_PARAM* current = nullptr;
574
575
0
    if (context->work_object_count <= waitCount)
576
0
    {
577
0
      free_objects(context->work_objects, context->work_object_count);
578
0
      waitCount = 0;
579
0
    }
580
0
    current = &context->work_combined_params[waitCount];
581
0
    *current = pool_decode_rect_param(&regionRects[x], context, type, pYUVData, iStride,
582
0
                                      pYUVDstData, iDstStride);
583
584
0
    if (!submit_object(&context->work_objects[waitCount], cb, current, context))
585
0
      goto fail;
586
0
    waitCount++;
587
0
  }
588
589
0
  rc = TRUE;
590
0
fail:
591
0
  free_objects(context->work_objects, context->work_object_count);
592
0
  return rc;
593
0
}
594
595
BOOL yuv444_context_decode(YUV_CONTEXT* WINPR_RESTRICT context, BYTE type,
596
                           const BYTE* WINPR_RESTRICT pYUVData[3], const UINT32 iStride[3],
597
                           UINT32 srcYuvHeight, BYTE* WINPR_RESTRICT pYUVDstData[3],
598
                           const UINT32 iDstStride[3], DWORD DstFormat, BYTE* WINPR_RESTRICT dest,
599
                           UINT32 nDstStep, const RECTANGLE_16* WINPR_RESTRICT regionRects,
600
                           UINT32 numRegionRects)
601
0
{
602
0
  const BYTE* pYUVCDstData[3];
603
604
0
  WINPR_ASSERT(context);
605
0
  WINPR_ASSERT(pYUVData);
606
0
  WINPR_ASSERT(iStride);
607
0
  WINPR_ASSERT(pYUVDstData);
608
0
  WINPR_ASSERT(iDstStride);
609
0
  WINPR_ASSERT(dest);
610
0
  WINPR_ASSERT(regionRects || (numRegionRects == 0));
611
612
0
  if (context->encoder)
613
0
  {
614
0
    WLog_ERR(TAG, "YUV context set up for encoding, can not decode with it, aborting");
615
0
    return FALSE;
616
0
  }
617
0
  if (!pool_decode_rect(context, type, pYUVData, iStride, pYUVDstData, iDstStride, regionRects,
618
0
                        numRegionRects))
619
0
    return FALSE;
620
621
0
  pYUVCDstData[0] = pYUVDstData[0];
622
0
  pYUVCDstData[1] = pYUVDstData[1];
623
0
  pYUVCDstData[2] = pYUVDstData[2];
624
0
  return pool_decode(context, yuv444_process_work_callback, pYUVCDstData, iDstStride,
625
0
                     srcYuvHeight, DstFormat, dest, nDstStep, regionRects, numRegionRects);
626
0
}
627
628
BOOL yuv420_context_decode(YUV_CONTEXT* WINPR_RESTRICT context,
629
                           const BYTE* WINPR_RESTRICT pYUVData[3], const UINT32 iStride[3],
630
                           UINT32 yuvHeight, DWORD DstFormat, BYTE* WINPR_RESTRICT dest,
631
                           UINT32 nDstStep, const RECTANGLE_16* WINPR_RESTRICT regionRects,
632
                           UINT32 numRegionRects)
633
0
{
634
0
  return pool_decode(context, yuv420_process_work_callback, pYUVData, iStride, yuvHeight,
635
0
                     DstFormat, dest, nDstStep, regionRects, numRegionRects);
636
0
}
637
638
static void CALLBACK yuv420_encode_work_callback(PTP_CALLBACK_INSTANCE instance, void* context,
639
                                                 PTP_WORK work)
640
0
{
641
0
  prim_size_t roi;
642
0
  YUV_ENCODE_WORK_PARAM* param = (YUV_ENCODE_WORK_PARAM*)context;
643
0
  primitives_t* prims = primitives_get();
644
0
  BYTE* pYUVData[3];
645
0
  const BYTE* src = nullptr;
646
647
0
  WINPR_UNUSED(instance);
648
0
  WINPR_UNUSED(work);
649
0
  WINPR_ASSERT(param);
650
651
0
  roi.width = param->rect.right - param->rect.left;
652
0
  roi.height = param->rect.bottom - param->rect.top;
653
0
  src = param->pSrcData + 1ULL * param->nSrcStep * param->rect.top +
654
0
        1ULL * param->rect.left * FreeRDPGetBytesPerPixel(param->SrcFormat);
655
0
  pYUVData[0] =
656
0
      param->pYUVLumaData[0] + 1ULL * param->rect.top * param->iStride[0] + param->rect.left;
657
0
  pYUVData[1] = param->pYUVLumaData[1] + 1ULL * param->rect.top / 2 * param->iStride[1] +
658
0
                param->rect.left / 2;
659
0
  pYUVData[2] = param->pYUVLumaData[2] + 1ULL * param->rect.top / 2 * param->iStride[2] +
660
0
                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 = nullptr;
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 + 1ULL * param->nSrcStep * param->rect.top +
686
0
        1ULL * param->rect.left * FreeRDPGetBytesPerPixel(param->SrcFormat);
687
0
  pYUVLumaData[0] =
688
0
      param->pYUVLumaData[0] + 1ULL * param->rect.top * param->iStride[0] + param->rect.left;
689
0
  pYUVLumaData[1] = param->pYUVLumaData[1] + 1ULL * param->rect.top / 2 * param->iStride[1] +
690
0
                    param->rect.left / 2;
691
0
  pYUVLumaData[2] = param->pYUVLumaData[2] + 1ULL * param->rect.top / 2 * param->iStride[2] +
692
0
                    param->rect.left / 2;
693
0
  pYUVChromaData[0] =
694
0
      param->pYUVChromaData[0] + 1ULL * param->rect.top * param->iStride[0] + param->rect.left;
695
0
  pYUVChromaData[1] = param->pYUVChromaData[1] + 1ULL * param->rect.top / 2 * param->iStride[1] +
696
0
                      param->rect.left / 2;
697
0
  pYUVChromaData[2] = param->pYUVChromaData[2] + 1ULL * param->rect.top / 2 * param->iStride[2] +
698
0
                      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 = nullptr;
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 + 1ULL * param->nSrcStep * param->rect.top +
723
0
        1ULL * param->rect.left * FreeRDPGetBytesPerPixel(param->SrcFormat);
724
0
  pYUVLumaData[0] =
725
0
      param->pYUVLumaData[0] + 1ULL * param->rect.top * param->iStride[0] + param->rect.left;
726
0
  pYUVLumaData[1] = param->pYUVLumaData[1] + 1ULL * param->rect.top / 2 * param->iStride[1] +
727
0
                    param->rect.left / 2;
728
0
  pYUVLumaData[2] = param->pYUVLumaData[2] + 1ULL * param->rect.top / 2 * param->iStride[2] +
729
0
                    param->rect.left / 2;
730
0
  pYUVChromaData[0] =
731
0
      param->pYUVChromaData[0] + 1ULL * param->rect.top * param->iStride[0] + param->rect.left;
732
0
  pYUVChromaData[1] = param->pYUVChromaData[1] + 1ULL * param->rect.top / 2 * param->iStride[1] +
733
0
                      param->rect.left / 2;
734
0
  pYUVChromaData[2] = param->pYUVChromaData[2] + 1ULL * param->rect.top / 2 * param->iStride[2] +
735
0
                      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
WINPR_ATTR_NODISCARD
745
static inline YUV_ENCODE_WORK_PARAM
746
pool_encode_fill(const RECTANGLE_16* WINPR_RESTRICT rect, YUV_CONTEXT* WINPR_RESTRICT context,
747
                 const BYTE* WINPR_RESTRICT pSrcData, UINT32 nSrcStep, UINT32 SrcFormat,
748
                 const UINT32 iStride[], BYTE* WINPR_RESTRICT pYUVLumaData[],
749
                 BYTE* WINPR_RESTRICT pYUVChromaData[])
750
0
{
751
0
  YUV_ENCODE_WORK_PARAM current = WINPR_C_ARRAY_INIT;
752
753
0
  WINPR_ASSERT(rect);
754
0
  WINPR_ASSERT(context);
755
0
  WINPR_ASSERT(pSrcData);
756
0
  WINPR_ASSERT(iStride);
757
0
  WINPR_ASSERT(pYUVLumaData);
758
759
0
  current.context = context;
760
0
  current.pSrcData = pSrcData;
761
0
  current.SrcFormat = SrcFormat;
762
0
  current.nSrcStep = nSrcStep;
763
0
  current.pYUVLumaData[0] = pYUVLumaData[0];
764
0
  current.pYUVLumaData[1] = pYUVLumaData[1];
765
0
  current.pYUVLumaData[2] = pYUVLumaData[2];
766
0
  if (pYUVChromaData)
767
0
  {
768
0
    current.pYUVChromaData[0] = pYUVChromaData[0];
769
0
    current.pYUVChromaData[1] = pYUVChromaData[1];
770
0
    current.pYUVChromaData[2] = pYUVChromaData[2];
771
0
  }
772
0
  current.iStride[0] = iStride[0];
773
0
  current.iStride[1] = iStride[1];
774
0
  current.iStride[2] = iStride[2];
775
776
0
  current.rect = *rect;
777
778
0
  return current;
779
0
}
780
781
WINPR_ATTR_NODISCARD
782
static uint32_t getSteps(uint32_t height, uint32_t step)
783
0
{
784
0
  const uint32_t steps = (height + step / 2 + 1) / step;
785
0
  if (steps < 1)
786
0
    return 1;
787
0
  return steps;
788
0
}
789
790
WINPR_ATTR_NODISCARD
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(nullptr, &current, nullptr);
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 = getSteps(height, context->heightStep);
832
833
0
    for (UINT32 y = 0; y < steps; y++)
834
0
    {
835
0
      RECTANGLE_16 r = *rect;
836
0
      YUV_ENCODE_WORK_PARAM* current = nullptr;
837
838
0
      if (context->work_object_count <= waitCount)
839
0
      {
840
0
        free_objects(context->work_objects, context->work_object_count);
841
0
        waitCount = 0;
842
0
      }
843
844
0
      current = &context->work_enc_params[waitCount];
845
0
      r.top += y * context->heightStep;
846
      /* Limit the slice to its own rows. Without this every slice converts everything from
847
       * its first row down to the bottom of the region, so the same rows are converted over
848
       * and over (about 34 times for a 1080p frame and 16 row slices). getSteps rounds to the
849
       * nearest slice count, so the last slice has to reach the bottom of the region whatever
850
       * its height is. */
851
0
      if ((y + 1) < steps)
852
0
        r.bottom = (UINT16)MIN((UINT32)rect->bottom, (UINT32)r.top + context->heightStep);
853
0
      *current = pool_encode_fill(&r, context, pSrcData, nSrcStep, SrcFormat, iStride,
854
0
                                  pYUVLumaData, pYUVChromaData);
855
0
      if (!submit_object(&context->work_objects[waitCount], cb, current, context))
856
0
        goto fail;
857
0
      waitCount++;
858
0
    }
859
0
  }
860
861
0
  rc = TRUE;
862
0
fail:
863
0
  free_objects(context->work_objects, context->work_object_count);
864
0
  return rc;
865
0
}
866
867
BOOL yuv420_context_encode(YUV_CONTEXT* WINPR_RESTRICT context, const BYTE* WINPR_RESTRICT pSrcData,
868
                           UINT32 nSrcStep, UINT32 SrcFormat, const UINT32 iStride[3],
869
                           BYTE* WINPR_RESTRICT pYUVData[3],
870
                           const RECTANGLE_16* WINPR_RESTRICT regionRects, UINT32 numRegionRects)
871
0
{
872
0
  if (!context || !pSrcData || !iStride || !pYUVData || !regionRects)
873
0
    return FALSE;
874
875
0
  return pool_encode(context, yuv420_encode_work_callback, pSrcData, nSrcStep, SrcFormat, iStride,
876
0
                     pYUVData, nullptr, regionRects, numRegionRects);
877
0
}
878
879
BOOL yuv444_context_encode(YUV_CONTEXT* WINPR_RESTRICT context, BYTE version,
880
                           const BYTE* WINPR_RESTRICT pSrcData, UINT32 nSrcStep, UINT32 SrcFormat,
881
                           const UINT32 iStride[3], BYTE* WINPR_RESTRICT pYUVLumaData[3],
882
                           BYTE* WINPR_RESTRICT pYUVChromaData[3],
883
                           const RECTANGLE_16* WINPR_RESTRICT regionRects, UINT32 numRegionRects)
884
0
{
885
0
  PTP_WORK_CALLBACK cb = nullptr;
886
0
  switch (version)
887
0
  {
888
0
    case 1:
889
0
      cb = yuv444v1_encode_work_callback;
890
0
      break;
891
0
    case 2:
892
0
      cb = yuv444v2_encode_work_callback;
893
0
      break;
894
0
    default:
895
0
      return FALSE;
896
0
  }
897
898
0
  return pool_encode(context, cb, pSrcData, nSrcStep, SrcFormat, iStride, pYUVLumaData,
899
0
                     pYUVChromaData, regionRects, numRegionRects);
900
0
}