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