Coverage Report

Created: 2024-09-08 06:20

/src/FreeRDP/libfreerdp/codec/h264.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * H.264 Bitmap Compression
4
 *
5
 * Copyright 2014 Mike McDonald <Mike.McDonald@software.dell.com>
6
 * Copyright 2017 David Fort <contact@hardening-consulting.com>
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
#include <freerdp/config.h>
22
23
#include <winpr/crt.h>
24
#include <winpr/print.h>
25
#include <winpr/library.h>
26
#include <winpr/bitstream.h>
27
#include <winpr/synch.h>
28
29
#include <freerdp/primitives.h>
30
#include <freerdp/codec/h264.h>
31
#include <freerdp/codec/yuv.h>
32
#include <freerdp/log.h>
33
34
#include "h264.h"
35
36
0
#define TAG FREERDP_TAG("codec")
37
38
static BOOL avc444_ensure_buffer(H264_CONTEXT* h264, DWORD nDstHeight);
39
40
BOOL avc420_ensure_buffer(H264_CONTEXT* h264, UINT32 stride, UINT32 width, UINT32 height)
41
0
{
42
0
  BOOL isNull = FALSE;
43
0
  UINT32 pheight = height;
44
45
0
  if (!h264)
46
0
    return FALSE;
47
48
0
  if (stride == 0)
49
0
    stride = width;
50
51
0
  if (stride % 16 != 0)
52
0
    stride += 16 - stride % 16;
53
54
0
  if (pheight % 16 != 0)
55
0
    pheight += 16 - pheight % 16;
56
57
0
  for (size_t x = 0; x < 3; x++)
58
0
  {
59
0
    if (!h264->pYUVData[x] || !h264->pOldYUVData[x])
60
0
      isNull = TRUE;
61
0
  }
62
63
0
  if (pheight == 0)
64
0
    return FALSE;
65
0
  if (stride == 0)
66
0
    return FALSE;
67
68
0
  if (isNull || (width != h264->width) || (height != h264->height) ||
69
0
      (stride != h264->iStride[0]))
70
0
  {
71
0
    h264->iStride[0] = stride;
72
0
    h264->iStride[1] = (stride + 1) / 2;
73
0
    h264->iStride[2] = (stride + 1) / 2;
74
0
    h264->width = width;
75
0
    h264->height = height;
76
77
0
    for (size_t x = 0; x < 3; x++)
78
0
    {
79
0
      BYTE* tmp1 = winpr_aligned_recalloc(h264->pYUVData[x], h264->iStride[x], pheight, 16);
80
0
      BYTE* tmp2 =
81
0
          winpr_aligned_recalloc(h264->pOldYUVData[x], h264->iStride[x], pheight, 16);
82
0
      if (tmp1)
83
0
        h264->pYUVData[x] = tmp1;
84
0
      if (tmp2)
85
0
        h264->pOldYUVData[x] = tmp2;
86
0
      if (!tmp1 || !tmp2)
87
0
        return FALSE;
88
0
    }
89
0
  }
90
91
0
  return TRUE;
92
0
}
93
94
INT32 avc420_decompress(H264_CONTEXT* h264, const BYTE* pSrcData, UINT32 SrcSize, BYTE* pDstData,
95
                        DWORD DstFormat, UINT32 nDstStep, UINT32 nDstWidth, UINT32 nDstHeight,
96
                        const RECTANGLE_16* regionRects, UINT32 numRegionRects)
97
0
{
98
0
  int status = 0;
99
0
  const BYTE* pYUVData[3];
100
101
0
  if (!h264 || h264->Compressor)
102
0
    return -1001;
103
104
0
  status = h264->subsystem->Decompress(h264, pSrcData, SrcSize);
105
106
0
  if (status == 0)
107
0
    return 1;
108
109
0
  if (status < 0)
110
0
    return status;
111
112
0
  pYUVData[0] = h264->pYUVData[0];
113
0
  pYUVData[1] = h264->pYUVData[1];
114
0
  pYUVData[2] = h264->pYUVData[2];
115
0
  if (!yuv420_context_decode(h264->yuv, pYUVData, h264->iStride, h264->height, DstFormat,
116
0
                             pDstData, nDstStep, regionRects, numRegionRects))
117
0
    return -1002;
118
119
0
  return 1;
120
0
}
121
122
static BOOL allocate_h264_metablock(UINT32 QP, RECTANGLE_16* rectangles,
123
                                    RDPGFX_H264_METABLOCK* meta, size_t count)
124
0
{
125
  /* [MS-RDPEGFX] 2.2.4.4.2 RDPGFX_AVC420_QUANT_QUALITY */
126
0
  if (!meta || (QP > UINT8_MAX))
127
0
  {
128
0
    free(rectangles);
129
0
    return FALSE;
130
0
  }
131
132
0
  meta->regionRects = rectangles;
133
0
  if (count == 0)
134
0
    return TRUE;
135
136
0
  if (count > UINT32_MAX)
137
0
    return FALSE;
138
139
0
  meta->quantQualityVals = calloc(count, sizeof(RDPGFX_H264_QUANT_QUALITY));
140
141
0
  if (!meta->quantQualityVals || !meta->regionRects)
142
0
    return FALSE;
143
0
  meta->numRegionRects = (UINT32)count;
144
0
  for (size_t x = 0; x < count; x++)
145
0
  {
146
0
    RDPGFX_H264_QUANT_QUALITY* cur = &meta->quantQualityVals[x];
147
0
    cur->qp = (UINT8)QP;
148
149
    /* qpVal bit 6 and 7 are flags, so mask them out here.
150
     * qualityVal is [0-100] so 100 - qpVal [0-64] is always in range */
151
0
    cur->qualityVal = 100 - (QP & 0x3F);
152
0
  }
153
0
  return TRUE;
154
0
}
155
156
static INLINE BOOL diff_tile(const RECTANGLE_16* regionRect, BYTE* pYUVData[3],
157
                             BYTE* pOldYUVData[3], UINT32 const iStride[3])
158
0
{
159
0
  size_t size = 0;
160
161
0
  if (!regionRect || !pYUVData || !pOldYUVData || !iStride)
162
0
    return FALSE;
163
0
  size = regionRect->right - regionRect->left;
164
0
  if (regionRect->right > iStride[0])
165
0
    return FALSE;
166
0
  if (regionRect->right / 2u > iStride[1])
167
0
    return FALSE;
168
0
  if (regionRect->right / 2u > iStride[2])
169
0
    return FALSE;
170
171
0
  for (size_t y = regionRect->top; y < regionRect->bottom; y++)
172
0
  {
173
0
    const BYTE* cur0 = &pYUVData[0][y * iStride[0]];
174
0
    const BYTE* cur1 = &pYUVData[1][y * iStride[1]];
175
0
    const BYTE* cur2 = &pYUVData[2][y * iStride[2]];
176
0
    const BYTE* old0 = &pOldYUVData[0][y * iStride[0]];
177
0
    const BYTE* old1 = &pOldYUVData[1][y * iStride[1]];
178
0
    const BYTE* old2 = &pOldYUVData[2][y * iStride[2]];
179
180
0
    if (memcmp(&cur0[regionRect->left], &old0[regionRect->left], size) != 0)
181
0
      return TRUE;
182
0
    if (memcmp(&cur1[regionRect->left / 2], &old1[regionRect->left / 2], size / 2) != 0)
183
0
      return TRUE;
184
0
    if (memcmp(&cur2[regionRect->left / 2], &old2[regionRect->left / 2], size / 2) != 0)
185
0
      return TRUE;
186
0
  }
187
0
  return FALSE;
188
0
}
189
190
static BOOL detect_changes(BOOL firstFrameDone, const UINT32 QP, const RECTANGLE_16* regionRect,
191
                           BYTE* pYUVData[3], BYTE* pOldYUVData[3], UINT32 const iStride[3],
192
                           RDPGFX_H264_METABLOCK* meta)
193
0
{
194
0
  size_t count = 0;
195
0
  size_t wc = 0;
196
0
  size_t hc = 0;
197
0
  RECTANGLE_16* rectangles = NULL;
198
199
0
  if (!regionRect || !pYUVData || !pOldYUVData || !iStride || !meta)
200
0
    return FALSE;
201
202
0
  wc = (regionRect->right - regionRect->left) / 64 + 1;
203
0
  hc = (regionRect->bottom - regionRect->top) / 64 + 1;
204
0
  rectangles = calloc(wc * hc, sizeof(RECTANGLE_16));
205
0
  if (!rectangles)
206
0
    return FALSE;
207
0
  if (!firstFrameDone)
208
0
  {
209
0
    rectangles[0] = *regionRect;
210
0
    count = 1;
211
0
  }
212
0
  else
213
0
  {
214
0
    for (size_t y = regionRect->top; y < regionRect->bottom; y += 64)
215
0
    {
216
0
      for (size_t x = regionRect->left; x < regionRect->right; x += 64)
217
0
      {
218
0
        RECTANGLE_16 rect;
219
0
        rect.left = (UINT16)MIN(UINT16_MAX, regionRect->left + x);
220
0
        rect.top = (UINT16)MIN(UINT16_MAX, regionRect->top + y);
221
0
        rect.right =
222
0
            (UINT16)MIN(UINT16_MAX, MIN(regionRect->left + x + 64, regionRect->right));
223
0
        rect.bottom =
224
0
            (UINT16)MIN(UINT16_MAX, MIN(regionRect->top + y + 64, regionRect->bottom));
225
0
        if (diff_tile(&rect, pYUVData, pOldYUVData, iStride))
226
0
          rectangles[count++] = rect;
227
0
      }
228
0
    }
229
0
  }
230
0
  if (!allocate_h264_metablock(QP, rectangles, meta, count))
231
0
    return FALSE;
232
0
  return TRUE;
233
0
}
234
235
/**
236
 * Function description
237
 *
238
 * @return
239
 */
240
INT32 h264_get_yuv_buffer(H264_CONTEXT* h264, UINT32 nSrcStride, UINT32 nSrcWidth,
241
                          UINT32 nSrcHeight, BYTE* YUVData[3], UINT32 stride[3])
242
0
{
243
0
  if (!h264 || !h264->Compressor || !h264->subsystem || !h264->subsystem->Compress)
244
0
    return -1;
245
246
0
  if (!avc420_ensure_buffer(h264, nSrcStride, nSrcWidth, nSrcHeight))
247
0
    return -1;
248
249
0
  for (size_t x = 0; x < 3; x++)
250
0
  {
251
0
    YUVData[x] = h264->pYUVData[x];
252
0
    stride[x] = h264->iStride[x];
253
0
  }
254
255
0
  return 0;
256
0
}
257
258
/**
259
 * Function description
260
 *
261
 * @return
262
 */
263
INT32 h264_compress(H264_CONTEXT* h264, BYTE** ppDstData, UINT32* pDstSize)
264
0
{
265
0
  if (!h264 || !h264->Compressor || !h264->subsystem || !h264->subsystem->Compress)
266
0
    return -1;
267
268
0
  const BYTE* pcYUVData[3] = { h264->pYUVData[0], h264->pYUVData[1], h264->pYUVData[2] };
269
270
0
  return h264->subsystem->Compress(h264, pcYUVData, h264->iStride, ppDstData, pDstSize);
271
0
}
272
273
INT32 avc420_compress(H264_CONTEXT* h264, const BYTE* pSrcData, DWORD SrcFormat, UINT32 nSrcStep,
274
                      UINT32 nSrcWidth, UINT32 nSrcHeight, const RECTANGLE_16* regionRect,
275
                      BYTE** ppDstData, UINT32* pDstSize, RDPGFX_H264_METABLOCK* meta)
276
0
{
277
0
  INT32 rc = -1;
278
0
  BYTE* pYUVData[3] = { 0 };
279
0
  const BYTE* pcYUVData[3] = { 0 };
280
0
  BYTE* pOldYUVData[3] = { 0 };
281
282
0
  if (!h264 || !regionRect || !meta || !h264->Compressor)
283
0
    return -1;
284
285
0
  if (!h264->subsystem->Compress)
286
0
    return -1;
287
288
0
  if (!avc420_ensure_buffer(h264, nSrcStep, nSrcWidth, nSrcHeight))
289
0
    return -1;
290
291
0
  if (h264->encodingBuffer)
292
0
  {
293
0
    for (size_t x = 0; x < 3; x++)
294
0
    {
295
0
      pYUVData[x] = h264->pYUVData[x];
296
0
      pOldYUVData[x] = h264->pOldYUVData[x];
297
0
    }
298
0
  }
299
0
  else
300
0
  {
301
0
    for (size_t x = 0; x < 3; x++)
302
0
    {
303
0
      pYUVData[x] = h264->pOldYUVData[x];
304
0
      pOldYUVData[x] = h264->pYUVData[x];
305
0
    }
306
0
  }
307
0
  h264->encodingBuffer = !h264->encodingBuffer;
308
309
0
  if (!yuv420_context_encode(h264->yuv, pSrcData, nSrcStep, SrcFormat, h264->iStride, pYUVData,
310
0
                             regionRect, 1))
311
0
    goto fail;
312
313
0
  if (!detect_changes(h264->firstLumaFrameDone, h264->QP, regionRect, pYUVData, pOldYUVData,
314
0
                      h264->iStride, meta))
315
0
    goto fail;
316
317
0
  if (meta->numRegionRects == 0)
318
0
  {
319
0
    rc = 0;
320
0
    goto fail;
321
0
  }
322
323
0
  for (size_t x = 0; x < 3; x++)
324
0
    pcYUVData[x] = pYUVData[x];
325
326
0
  rc = h264->subsystem->Compress(h264, pcYUVData, h264->iStride, ppDstData, pDstSize);
327
0
  if (rc >= 0)
328
0
    h264->firstLumaFrameDone = TRUE;
329
330
0
fail:
331
0
  if (rc < 0)
332
0
    free_h264_metablock(meta);
333
0
  return rc;
334
0
}
335
336
INT32 avc444_compress(H264_CONTEXT* h264, const BYTE* pSrcData, DWORD SrcFormat, UINT32 nSrcStep,
337
                      UINT32 nSrcWidth, UINT32 nSrcHeight, BYTE version, const RECTANGLE_16* region,
338
                      BYTE* op, BYTE** ppDstData, UINT32* pDstSize, BYTE** ppAuxDstData,
339
                      UINT32* pAuxDstSize, RDPGFX_H264_METABLOCK* meta,
340
                      RDPGFX_H264_METABLOCK* auxMeta)
341
0
{
342
0
  int rc = -1;
343
0
  BYTE* coded = NULL;
344
0
  UINT32 codedSize = 0;
345
0
  BYTE** pYUV444Data = NULL;
346
0
  BYTE** pOldYUV444Data = NULL;
347
0
  BYTE** pYUVData = NULL;
348
0
  BYTE** pOldYUVData = NULL;
349
350
0
  if (!h264 || !h264->Compressor)
351
0
    return -1;
352
353
0
  if (!h264->subsystem->Compress)
354
0
    return -1;
355
356
0
  if (!avc420_ensure_buffer(h264, nSrcStep, nSrcWidth, nSrcHeight))
357
0
    return -1;
358
359
0
  if (!avc444_ensure_buffer(h264, nSrcHeight))
360
0
    return -1;
361
362
0
  if (h264->encodingBuffer)
363
0
  {
364
0
    pYUV444Data = h264->pOldYUV444Data;
365
0
    pOldYUV444Data = h264->pYUV444Data;
366
0
    pYUVData = h264->pOldYUVData;
367
0
    pOldYUVData = h264->pYUVData;
368
0
  }
369
0
  else
370
0
  {
371
0
    pYUV444Data = h264->pYUV444Data;
372
0
    pOldYUV444Data = h264->pOldYUV444Data;
373
0
    pYUVData = h264->pYUVData;
374
0
    pOldYUVData = h264->pOldYUVData;
375
0
  }
376
0
  h264->encodingBuffer = !h264->encodingBuffer;
377
378
0
  if (!yuv444_context_encode(h264->yuv, version, pSrcData, nSrcStep, SrcFormat, h264->iStride,
379
0
                             pYUV444Data, pYUVData, region, 1))
380
0
    goto fail;
381
382
0
  if (!detect_changes(h264->firstLumaFrameDone, h264->QP, region, pYUV444Data, pOldYUV444Data,
383
0
                      h264->iStride, meta))
384
0
    goto fail;
385
0
  if (!detect_changes(h264->firstChromaFrameDone, h264->QP, region, pYUVData, pOldYUVData,
386
0
                      h264->iStride, auxMeta))
387
0
    goto fail;
388
389
  /* [MS-RDPEGFX] 2.2.4.5 RFX_AVC444_BITMAP_STREAM
390
   * LC:
391
   * 0 ... Luma & Chroma
392
   * 1 ... Luma
393
   * 2 ... Chroma
394
   */
395
0
  if ((meta->numRegionRects > 0) && (auxMeta->numRegionRects > 0))
396
0
    *op = 0;
397
0
  else if (meta->numRegionRects > 0)
398
0
    *op = 1;
399
0
  else if (auxMeta->numRegionRects > 0)
400
0
    *op = 2;
401
0
  else
402
0
  {
403
0
    WLog_INFO(TAG, "no changes detected for luma or chroma frame");
404
0
    rc = 0;
405
0
    goto fail;
406
0
  }
407
408
0
  if ((*op == 0) || (*op == 1))
409
0
  {
410
0
    const BYTE* pcYUV444Data[3] = { pYUV444Data[0], pYUV444Data[1], pYUV444Data[2] };
411
412
0
    if (h264->subsystem->Compress(h264, pcYUV444Data, h264->iStride, &coded, &codedSize) < 0)
413
0
      goto fail;
414
0
    h264->firstLumaFrameDone = TRUE;
415
0
    memcpy(h264->lumaData, coded, codedSize);
416
0
    *ppDstData = h264->lumaData;
417
0
    *pDstSize = codedSize;
418
0
  }
419
420
0
  if ((*op == 0) || (*op == 2))
421
0
  {
422
0
    const BYTE* pcYUVData[3] = { pYUVData[0], pYUVData[1], pYUVData[2] };
423
424
0
    if (h264->subsystem->Compress(h264, pcYUVData, h264->iStride, &coded, &codedSize) < 0)
425
0
      goto fail;
426
0
    h264->firstChromaFrameDone = TRUE;
427
0
    *ppAuxDstData = coded;
428
0
    *pAuxDstSize = codedSize;
429
0
  }
430
431
0
  rc = 1;
432
0
fail:
433
0
  if (rc < 0)
434
0
  {
435
0
    free_h264_metablock(meta);
436
0
    free_h264_metablock(auxMeta);
437
0
  }
438
0
  return rc;
439
0
}
440
441
static BOOL avc444_ensure_buffer(H264_CONTEXT* h264, DWORD nDstHeight)
442
0
{
443
0
  WINPR_ASSERT(h264);
444
445
0
  const UINT32* piMainStride = h264->iStride;
446
0
  UINT32* piDstSize = h264->iYUV444Size;
447
0
  UINT32* piDstStride = h264->iYUV444Stride;
448
0
  BYTE** ppYUVDstData = h264->pYUV444Data;
449
0
  BYTE** ppOldYUVDstData = h264->pOldYUV444Data;
450
451
0
  nDstHeight = MAX(h264->height, nDstHeight);
452
0
  const UINT32 pad = nDstHeight % 16;
453
0
  UINT32 padDstHeight = nDstHeight; /* Need alignment to 16x16 blocks */
454
455
0
  if (pad != 0)
456
0
    padDstHeight += 16 - pad;
457
458
0
  if ((piMainStride[0] != piDstStride[0]) ||
459
0
      (piDstSize[0] != 1ull * piMainStride[0] * padDstHeight))
460
0
  {
461
0
    for (UINT32 x = 0; x < 3; x++)
462
0
    {
463
0
      piDstStride[x] = piMainStride[0];
464
0
      piDstSize[x] = piDstStride[x] * padDstHeight;
465
466
0
      if (piDstSize[x] == 0)
467
0
        return FALSE;
468
469
0
      BYTE* tmp1 = winpr_aligned_recalloc(ppYUVDstData[x], piDstSize[x], 1, 16);
470
0
      if (!tmp1)
471
0
        return FALSE;
472
0
      ppYUVDstData[x] = tmp1;
473
0
      BYTE* tmp2 = winpr_aligned_recalloc(ppOldYUVDstData[x], piDstSize[x], 1, 16);
474
0
      if (!tmp2)
475
0
        return FALSE;
476
0
      ppOldYUVDstData[x] = tmp2;
477
0
    }
478
479
0
    {
480
0
      BYTE* tmp = winpr_aligned_recalloc(h264->lumaData, piDstSize[0], 4, 16);
481
0
      if (!tmp)
482
0
        goto fail;
483
0
      h264->lumaData = tmp;
484
0
    }
485
0
  }
486
487
0
  for (UINT32 x = 0; x < 3; x++)
488
0
  {
489
0
    if (!ppOldYUVDstData[x] || !ppYUVDstData[x] || (piDstSize[x] == 0) || (piDstStride[x] == 0))
490
0
    {
491
0
      WLog_Print(h264->log, WLOG_ERROR,
492
0
                 "YUV buffer not initialized! check your decoder settings");
493
0
      goto fail;
494
0
    }
495
0
  }
496
497
0
  if (!h264->lumaData)
498
0
    goto fail;
499
500
0
  return TRUE;
501
0
fail:
502
0
  return FALSE;
503
0
}
504
505
static BOOL avc444_process_rects(H264_CONTEXT* h264, const BYTE* pSrcData, UINT32 SrcSize,
506
                                 BYTE* pDstData, UINT32 DstFormat, UINT32 nDstStep,
507
                                 UINT32 nDstWidth, UINT32 nDstHeight, const RECTANGLE_16* rects,
508
                                 UINT32 nrRects, avc444_frame_type type)
509
0
{
510
0
  const BYTE* pYUVData[3];
511
0
  BYTE* pYUVDstData[3];
512
0
  UINT32* piDstStride = h264->iYUV444Stride;
513
0
  BYTE** ppYUVDstData = h264->pYUV444Data;
514
0
  const UINT32* piStride = h264->iStride;
515
516
0
  if (h264->subsystem->Decompress(h264, pSrcData, SrcSize) < 0)
517
0
    return FALSE;
518
519
0
  pYUVData[0] = h264->pYUVData[0];
520
0
  pYUVData[1] = h264->pYUVData[1];
521
0
  pYUVData[2] = h264->pYUVData[2];
522
0
  if (!avc444_ensure_buffer(h264, nDstHeight))
523
0
    return FALSE;
524
525
0
  pYUVDstData[0] = ppYUVDstData[0];
526
0
  pYUVDstData[1] = ppYUVDstData[1];
527
0
  pYUVDstData[2] = ppYUVDstData[2];
528
0
  if (!yuv444_context_decode(h264->yuv, (BYTE)type, pYUVData, piStride, h264->height, pYUVDstData,
529
0
                             piDstStride, DstFormat, pDstData, nDstStep, rects, nrRects))
530
0
    return FALSE;
531
532
0
  return TRUE;
533
0
}
534
535
#if defined(AVC444_FRAME_STAT)
536
static UINT64 op1 = 0;
537
static double op1sum = 0;
538
static UINT64 op2 = 0;
539
static double op2sum = 0;
540
static UINT64 op3 = 0;
541
static double op3sum = 0;
542
static double avg(UINT64* count, double old, double size)
543
{
544
  double tmp = size + *count * old;
545
  (*count)++;
546
  tmp = tmp / *count;
547
  return tmp;
548
}
549
#endif
550
551
INT32 avc444_decompress(H264_CONTEXT* h264, BYTE op, const RECTANGLE_16* regionRects,
552
                        UINT32 numRegionRects, const BYTE* pSrcData, UINT32 SrcSize,
553
                        const RECTANGLE_16* auxRegionRects, UINT32 numAuxRegionRect,
554
                        const BYTE* pAuxSrcData, UINT32 AuxSrcSize, BYTE* pDstData, DWORD DstFormat,
555
                        UINT32 nDstStep, UINT32 nDstWidth, UINT32 nDstHeight, UINT32 codecId)
556
0
{
557
0
  INT32 status = -1;
558
0
  avc444_frame_type chroma =
559
0
      (codecId == RDPGFX_CODECID_AVC444) ? AVC444_CHROMAv1 : AVC444_CHROMAv2;
560
561
0
  if (!h264 || !regionRects || !pSrcData || !pDstData || h264->Compressor)
562
0
    return -1001;
563
564
0
  switch (op)
565
0
  {
566
0
    case 0: /* YUV420 in stream 1
567
             * Chroma420 in stream 2 */
568
0
      if (!avc444_process_rects(h264, pSrcData, SrcSize, pDstData, DstFormat, nDstStep,
569
0
                                nDstWidth, nDstHeight, regionRects, numRegionRects,
570
0
                                AVC444_LUMA))
571
0
        status = -1;
572
0
      else if (!avc444_process_rects(h264, pAuxSrcData, AuxSrcSize, pDstData, DstFormat,
573
0
                                     nDstStep, nDstWidth, nDstHeight, auxRegionRects,
574
0
                                     numAuxRegionRect, chroma))
575
0
        status = -1;
576
0
      else
577
0
        status = 0;
578
579
0
      break;
580
581
0
    case 2: /* Chroma420 in stream 1 */
582
0
      if (!avc444_process_rects(h264, pSrcData, SrcSize, pDstData, DstFormat, nDstStep,
583
0
                                nDstWidth, nDstHeight, regionRects, numRegionRects, chroma))
584
0
        status = -1;
585
0
      else
586
0
        status = 0;
587
588
0
      break;
589
590
0
    case 1: /* YUV420 in stream 1 */
591
0
      if (!avc444_process_rects(h264, pSrcData, SrcSize, pDstData, DstFormat, nDstStep,
592
0
                                nDstWidth, nDstHeight, regionRects, numRegionRects,
593
0
                                AVC444_LUMA))
594
0
        status = -1;
595
0
      else
596
0
        status = 0;
597
598
0
      break;
599
600
0
    default: /* WTF? */
601
0
      break;
602
0
  }
603
604
#if defined(AVC444_FRAME_STAT)
605
606
  switch (op)
607
  {
608
    case 0:
609
      op1sum = avg(&op1, op1sum, SrcSize + AuxSrcSize);
610
      break;
611
612
    case 1:
613
      op2sum = avg(&op2, op2sum, SrcSize);
614
      break;
615
616
    case 2:
617
      op3sum = avg(&op3, op3sum, SrcSize);
618
      break;
619
620
    default:
621
      break;
622
  }
623
624
  WLog_Print(h264->log, WLOG_INFO,
625
             "luma=%" PRIu64 " [avg=%lf] chroma=%" PRIu64 " [avg=%lf] combined=%" PRIu64
626
             " [avg=%lf]",
627
             op1, op1sum, op2, op2sum, op3, op3sum);
628
#endif
629
0
  return status;
630
0
}
631
632
0
#define MAX_SUBSYSTEMS 10
633
static INIT_ONCE subsystems_once = INIT_ONCE_STATIC_INIT;
634
static const H264_CONTEXT_SUBSYSTEM* subSystems[MAX_SUBSYSTEMS] = { 0 };
635
636
static BOOL CALLBACK h264_register_subsystems(PINIT_ONCE once, PVOID param, PVOID* context)
637
0
{
638
0
  int i = 0;
639
640
#ifdef WITH_MEDIACODEC
641
  {
642
    subSystems[i] = &g_Subsystem_mediacodec;
643
    i++;
644
  }
645
#endif
646
#if defined(_WIN32) && defined(WITH_MEDIA_FOUNDATION)
647
  {
648
    subSystems[i] = &g_Subsystem_MF;
649
    i++;
650
  }
651
#endif
652
#ifdef WITH_OPENH264
653
  {
654
    subSystems[i] = &g_Subsystem_OpenH264;
655
    i++;
656
  }
657
#endif
658
#ifdef WITH_VIDEO_FFMPEG
659
  {
660
    subSystems[i] = &g_Subsystem_libavcodec;
661
    i++;
662
  }
663
#endif
664
0
  return i > 0;
665
0
}
666
667
static BOOL h264_context_init(H264_CONTEXT* h264)
668
0
{
669
0
  if (!h264)
670
0
    return FALSE;
671
672
0
  h264->log = WLog_Get(TAG);
673
674
0
  if (!h264->log)
675
0
    return FALSE;
676
677
0
  h264->subsystem = NULL;
678
0
  InitOnceExecuteOnce(&subsystems_once, h264_register_subsystems, NULL, NULL);
679
680
0
  for (size_t i = 0; i < MAX_SUBSYSTEMS; i++)
681
0
  {
682
0
    const H264_CONTEXT_SUBSYSTEM* subsystem = subSystems[i];
683
684
0
    if (!subsystem || !subsystem->Init)
685
0
      break;
686
687
0
    if (subsystem->Init(h264))
688
0
    {
689
0
      h264->subsystem = subsystem;
690
0
      return TRUE;
691
0
    }
692
0
  }
693
694
0
  return FALSE;
695
0
}
696
697
BOOL h264_context_reset(H264_CONTEXT* h264, UINT32 width, UINT32 height)
698
0
{
699
0
  if (!h264)
700
0
    return FALSE;
701
702
0
  h264->width = width;
703
0
  h264->height = height;
704
0
  return yuv_context_reset(h264->yuv, width, height);
705
0
}
706
707
H264_CONTEXT* h264_context_new(BOOL Compressor)
708
0
{
709
0
  H264_CONTEXT* h264 = (H264_CONTEXT*)calloc(1, sizeof(H264_CONTEXT));
710
0
  if (!h264)
711
0
    return NULL;
712
713
0
  h264->Compressor = Compressor;
714
0
  if (Compressor)
715
716
0
  {
717
    /* Default compressor settings, may be changed by caller */
718
0
    h264->BitRate = 1000000;
719
0
    h264->FrameRate = 30;
720
0
  }
721
722
0
  if (!h264_context_init(h264))
723
0
    goto fail;
724
725
0
  h264->yuv = yuv_context_new(Compressor, 0);
726
0
  if (!h264->yuv)
727
0
    goto fail;
728
729
0
  return h264;
730
731
0
fail:
732
0
  WINPR_PRAGMA_DIAG_PUSH
733
0
  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
734
0
  h264_context_free(h264);
735
0
  WINPR_PRAGMA_DIAG_POP
736
0
  return NULL;
737
0
}
738
739
void h264_context_free(H264_CONTEXT* h264)
740
0
{
741
0
  if (h264)
742
0
  {
743
0
    if (h264->subsystem)
744
0
      h264->subsystem->Uninit(h264);
745
746
0
    for (size_t x = 0; x < 3; x++)
747
0
    {
748
0
      if (h264->Compressor)
749
0
      {
750
0
        winpr_aligned_free(h264->pYUVData[x]);
751
0
        winpr_aligned_free(h264->pOldYUVData[x]);
752
0
      }
753
0
      winpr_aligned_free(h264->pYUV444Data[x]);
754
0
      winpr_aligned_free(h264->pOldYUV444Data[x]);
755
0
    }
756
0
    winpr_aligned_free(h264->lumaData);
757
758
0
    yuv_context_free(h264->yuv);
759
0
    free(h264);
760
0
  }
761
0
}
762
763
void free_h264_metablock(RDPGFX_H264_METABLOCK* meta)
764
0
{
765
0
  RDPGFX_H264_METABLOCK m = { 0 };
766
0
  if (!meta)
767
0
    return;
768
0
  free(meta->quantQualityVals);
769
0
  free(meta->regionRects);
770
0
  *meta = m;
771
0
}
772
773
BOOL h264_context_set_option(H264_CONTEXT* h264, H264_CONTEXT_OPTION option, UINT32 value)
774
0
{
775
0
  WINPR_ASSERT(h264);
776
0
  switch (option)
777
0
  {
778
0
    case H264_CONTEXT_OPTION_BITRATE:
779
0
      h264->BitRate = value;
780
0
      return TRUE;
781
0
    case H264_CONTEXT_OPTION_FRAMERATE:
782
0
      h264->FrameRate = value;
783
0
      return TRUE;
784
0
    case H264_CONTEXT_OPTION_RATECONTROL:
785
0
      h264->RateControlMode = value;
786
0
      return TRUE;
787
0
    case H264_CONTEXT_OPTION_QP:
788
0
      h264->QP = value;
789
0
      return TRUE;
790
0
    case H264_CONTEXT_OPTION_USAGETYPE:
791
0
      h264->UsageType = value;
792
0
      return TRUE;
793
0
    default:
794
0
      WLog_Print(h264->log, WLOG_WARN, "Unknown H264_CONTEXT_OPTION[0x%08" PRIx32 "]",
795
0
                 option);
796
0
      return FALSE;
797
0
  }
798
0
}
799
800
UINT32 h264_context_get_option(H264_CONTEXT* h264, H264_CONTEXT_OPTION option)
801
0
{
802
0
  WINPR_ASSERT(h264);
803
0
  switch (option)
804
0
  {
805
0
    case H264_CONTEXT_OPTION_BITRATE:
806
0
      return h264->BitRate;
807
0
    case H264_CONTEXT_OPTION_FRAMERATE:
808
0
      return h264->FrameRate;
809
0
    case H264_CONTEXT_OPTION_RATECONTROL:
810
0
      return h264->RateControlMode;
811
0
    case H264_CONTEXT_OPTION_QP:
812
0
      return h264->QP;
813
0
    case H264_CONTEXT_OPTION_USAGETYPE:
814
0
      return h264->UsageType;
815
0
    default:
816
0
      WLog_Print(h264->log, WLOG_WARN, "Unknown H264_CONTEXT_OPTION[0x%08" PRIx32 "]",
817
0
                 option);
818
0
      return 0;
819
0
  }
820
0
}