FreeRDP
Loading...
Searching...
No Matches
av1.c
1
21#include <freerdp/codec/av1.h>
22#include <freerdp/primitives.h>
23#include <freerdp/log.h>
24#include <freerdp/codec/region.h>
25
26#define TAG FREERDP_TAG("codec.av1")
27
28#if defined(WITH_LIBAOM)
29#include <aom/aom.h>
30
31#include <aom/aom_decoder.h>
32#include <aom/aom_encoder.h>
33
34#include <aom/aom_image.h>
35#include <aom/aomcx.h>
36#include <aom/aomdx.h>
37#endif
38
39#if defined(WITH_DAV1D)
40#include <dav1d/dav1d.h>
41#endif
42
43#if defined(WITH_LIBAOM) || defined(WITH_DAV1D)
44
45#if defined(WITH_LIBYUV)
46#include <libyuv.h>
47#endif
48
49struct S_FREERDP_AV1_CONTEXT
50{
51 wLog* log;
52 bool encoder;
53 bool initialized;
54#if defined(WITH_LIBAOM)
55 aom_codec_ctx_t ctx;
56 aom_codec_enc_cfg_t ecfg;
57 aom_codec_pts_t framecount;
58 aom_enc_frame_flags_t eflags;
59 aom_codec_flags_t flags;
60 aom_codec_iface_t* iface;
61#endif
62#if defined(WITH_LIBAOM) && !defined(WITH_DAV1D)
63 aom_codec_dec_cfg_t dcfg;
64#endif
65#if defined(WITH_DAV1D)
66 Dav1dContext* dav1d;
67#endif
68 BYTE* yuvdata[3];
69 UINT32 yuvWidth;
70 UINT32 yuvStride[3];
71 UINT32 yuvHeight;
72};
73
74#if defined(WITH_LIBAOM)
75static void av1_aom_destroy(FREERDP_AV1_CONTEXT* av1)
76{
77 const aom_codec_err_t rc = aom_codec_destroy(&av1->ctx);
78 if (rc != AOM_CODEC_OK)
79 WLog_Print(av1->log, WLOG_WARN, "aom_codec_destroy: %s", aom_codec_err_to_string(rc));
80}
81
82WINPR_ATTR_NODISCARD
83static BOOL allocate_h264_metablock(UINT32 QP, RECTANGLE_16* rectangles,
84 RDPGFX_H264_METABLOCK* meta, size_t count)
85{
86 /* [MS-RDPEGFX] 2.2.4.4.2 RDPGFX_AVC420_QUANT_QUALITY */
87 if (!meta || (QP > UINT8_MAX))
88 {
89 free(rectangles);
90 return FALSE;
91 }
92
93 meta->regionRects = rectangles;
94 if (count == 0)
95 return TRUE;
96
97 if (count > UINT32_MAX)
98 return FALSE;
99
100 meta->quantQualityVals = calloc(count, sizeof(RDPGFX_H264_QUANT_QUALITY));
101
102 if (!meta->quantQualityVals || !meta->regionRects)
103 return FALSE;
104 meta->numRegionRects = (UINT32)count;
105 for (size_t x = 0; x < count; x++)
106 {
107 RDPGFX_H264_QUANT_QUALITY* cur = &meta->quantQualityVals[x];
108 cur->qp = (UINT8)QP;
109
110 /* qpVal bit 6 and 7 are flags, so mask them out here.
111 * qualityVal is [0-100] so 100 - qpVal [0-64] is always in range */
112 cur->qualityVal = 100 - (QP & 0x3F);
113 }
114 return TRUE;
115}
116#endif
117
118BOOL freerdp_av1_context_set_option(FREERDP_AV1_CONTEXT* av1, FREERDP_AV1_CONTEXT_OPTION option,
119 UINT32 value)
120{
121#if defined(WITH_LIBAOM)
122 switch (option)
123 {
124 case FREERDP_AV1_CONTEXT_OPTION_PROFILE:
125 if (!av1->encoder)
126 return FALSE;
127
128 av1->ecfg.g_profile = value;
129 break;
130
131 case FREERDP_AV1_CONTEXT_OPTION_RATECONTROL:
132 if (!av1->encoder)
133 return FALSE;
134 switch (value)
135 {
136 case FREERDP_AV1_VBR:
137 case FREERDP_AV1_CBR:
138 case FREERDP_AV1_CQ:
139 case FREERDP_AV1_Q:
140 break;
141 default:
142 WLog_Print(av1->log, WLOG_WARN,
143 "Unknown FREERDP_AV1_CONTEXT_OPTION_RATECONTROL value [0x%08" PRIx32
144 "]",
145 value);
146 return FALSE;
147 }
148 av1->ecfg.rc_end_usage = value;
149 break;
150 case FREERDP_AV1_CONTEXT_OPTION_BITRATE:
151 if (!av1->encoder)
152 return FALSE;
153 av1->ecfg.rc_target_bitrate = value;
154 break;
155 case FREERDP_AV1_CONTEXT_OPTION_USAGETYPE:
156 if (!av1->encoder)
157 return FALSE;
158 av1->ecfg.g_usage = value;
159 break;
160 default:
161 WLog_Print(av1->log, WLOG_ERROR, "Unknown FREERDP_AV1_CONTEXT_OPTION[0x%08" PRIx32 "]",
162 option);
163 return TRUE;
164 }
165 return freerdp_av1_context_reset(av1, av1->ecfg.g_w, av1->ecfg.g_h);
166#else
167 WLog_Print(av1->log, WLOG_WARN, "AV1 encoder requires libaom, recompile with '-DWITH_AOM=ON'");
168 WINPR_UNUSED(option);
169 WINPR_UNUSED(value);
170 return FALSE;
171#endif
172}
173
174UINT32 freerdp_av1_context_get_option(FREERDP_AV1_CONTEXT* av1, FREERDP_AV1_CONTEXT_OPTION option)
175{
176#if defined(WITH_LIBAOM)
177 switch (option)
178 {
179 case FREERDP_AV1_CONTEXT_OPTION_PROFILE:
180 if (!av1->encoder)
181 return 0;
182 return av1->ecfg.g_profile;
183 case FREERDP_AV1_CONTEXT_OPTION_RATECONTROL:
184 if (!av1->encoder)
185 return 0;
186 return av1->ecfg.rc_end_usage;
187 case FREERDP_AV1_CONTEXT_OPTION_BITRATE:
188 if (!av1->encoder)
189 return 0;
190 return av1->ecfg.rc_target_bitrate;
191 case FREERDP_AV1_CONTEXT_OPTION_USAGETYPE:
192 if (!av1->encoder)
193 return 0;
194 return av1->ecfg.g_usage;
195 default:
196 WLog_Print(av1->log, WLOG_ERROR, "Unknown FREERDP_AV1_CONTEXT_OPTION[0x%08" PRIx32 "]",
197 option);
198 return 0;
199 }
200#else
201 WINPR_UNUSED(option);
202 return 0;
203#endif
204}
205
206INT32 freerdp_av1_compress(FREERDP_AV1_CONTEXT* av1, const BYTE* pSrcData, DWORD SrcFormat,
207 UINT32 nSrcStep, UINT32 nSrcWidth, UINT32 nSrcHeight,
208 const RECTANGLE_16* regionRect, BYTE** ppDstData, UINT32* pDstSize,
210{
211 *ppDstData = nullptr;
212 *pDstSize = 0;
213
214 if (!av1->encoder)
215 {
216 WLog_Print(av1->log, WLOG_ERROR, "av1->encoder: %d", av1->encoder);
217 return -1;
218 }
219
220#if defined(WITH_LIBAOM)
221 primitives_t* primitives = primitives_get();
222 if (!primitives)
223 {
224 WLog_Print(av1->log, WLOG_ERROR, "primitives_get(): nullptr");
225 return -1;
226 }
227
228 if (av1->yuvWidth != nSrcWidth)
229 {
230 WLog_Print(av1->log, WLOG_ERROR, "av1->yuvWidth[%" PRIu32 "] != nSrcWidth[%" PRIu32 "]",
231 av1->yuvWidth, nSrcWidth);
232 return -1;
233 }
234
235 if (av1->yuvHeight != nSrcHeight)
236 {
237 WLog_Print(av1->log, WLOG_ERROR, "av1->yuvHeight[%" PRIu32 "] != nSrcHeight[%" PRIu32 "]",
238 av1->yuvHeight, nSrcHeight);
239 return -1;
240 }
241
242 const prim_size_t roi = { .width = nSrcWidth, .height = nSrcHeight };
243
244 aom_image_t buffer = WINPR_C_ARRAY_INIT;
245
246 aom_image_t* img = &buffer;
247 pstatus_t rec = -1;
248#if defined(WITH_LIBYUV)
249 /* ARGBToI420()/ARGBToI444() always read pSrcData as packed BGRA8888 regardless of
250 * SrcFormat, so the fast path only applies for that layout; anything else must go
251 * through the format-aware primitives conversion below. */
252 if ((SrcFormat == PIXEL_FORMAT_BGRA32) || (SrcFormat == PIXEL_FORMAT_BGRX32))
253 {
254 switch (av1->ecfg.g_profile)
255 {
256 case 0:
257 img->fmt = AOM_IMG_FMT_I420;
258 img->x_chroma_shift = 1;
259 img->y_chroma_shift = 1;
260 rec = ARGBToI420(pSrcData, nSrcStep, av1->yuvdata[0], av1->yuvStride[0],
261 av1->yuvdata[1], av1->yuvStride[1], av1->yuvdata[2],
262 WINPR_ASSERTING_INT_CAST(int, av1->yuvStride[2]),
263 WINPR_ASSERTING_INT_CAST(int, roi.width),
264 WINPR_ASSERTING_INT_CAST(int, roi.height));
265 break;
266 case 1:
267 img->fmt = AOM_IMG_FMT_I444;
268 rec = ARGBToI444(pSrcData, nSrcStep, av1->yuvdata[0], av1->yuvStride[0],
269 av1->yuvdata[1], av1->yuvStride[1], av1->yuvdata[2],
270 WINPR_ASSERTING_INT_CAST(int, av1->yuvStride[2]),
271 WINPR_ASSERTING_INT_CAST(int, roi.width),
272 WINPR_ASSERTING_INT_CAST(int, roi.height));
273 break;
274 default:
275 WLog_Print(av1->log, WLOG_ERROR, "Unsupoorted AV1 profile %" PRIu32,
276 av1->ecfg.g_profile);
277 return -1;
278 }
279 }
280 else
281#endif
282 {
283 switch (av1->ecfg.g_profile)
284 {
285 case 0:
286 img->fmt = AOM_IMG_FMT_I420;
287 img->x_chroma_shift = 1;
288 img->y_chroma_shift = 1;
289 rec = primitives->RGBToYUV420_8u_P3AC4R(pSrcData, SrcFormat, nSrcStep, av1->yuvdata,
290 av1->yuvStride, &roi);
291 break;
292 case 1:
293 img->fmt = AOM_IMG_FMT_I444;
294 rec = primitives->RGBToI444_8u(pSrcData, SrcFormat, nSrcStep, av1->yuvdata,
295 av1->yuvStride, &roi);
296 break;
297 default:
298 WLog_Print(av1->log, WLOG_ERROR, "Unsupoorted AV1 profile %" PRIu32,
299 av1->ecfg.g_profile);
300 return -1;
301 }
302 }
303 if (rec != 0)
304 {
305 WLog_Print(av1->log, WLOG_ERROR, "RGB to YUV conversion failed: %d", rec);
306 return -1;
307 }
308 img->bit_depth = 8;
309 img->d_w = img->r_w = img->w = roi.width;
310 img->d_h = img->r_h = img->h = roi.height;
311 img->stride[0] = WINPR_ASSERTING_INT_CAST(int, av1->yuvStride[0]);
312 img->stride[1] = WINPR_ASSERTING_INT_CAST(int, av1->yuvStride[1]);
313 img->stride[2] = WINPR_ASSERTING_INT_CAST(int, av1->yuvStride[2]);
314 img->planes[0] = av1->yuvdata[0];
315 img->planes[1] = av1->yuvdata[1];
316 img->planes[2] = av1->yuvdata[2];
317
318 const aom_codec_err_t rc = aom_codec_encode(&av1->ctx, img, ++av1->framecount, 1, av1->eflags);
319 aom_img_free(img);
320
321 if (rc != AOM_CODEC_OK)
322 {
323 WLog_Print(av1->log, WLOG_WARN, "aom_codec_encode: %s", aom_codec_err_to_string(rc));
324 return -1;
325 }
326
327 aom_codec_iter_t iter = nullptr;
328 const aom_codec_cx_pkt_t* pkt = nullptr;
329 while ((pkt = aom_codec_get_cx_data(&av1->ctx, &iter)) != nullptr)
330 {
331 if (pkt->kind != AOM_CODEC_CX_FRAME_PKT)
332 continue;
333
334 *ppDstData = pkt->data.frame.buf;
335 *pDstSize = pkt->data.frame.sz;
336 };
337 if (*pDstSize == 0)
338 return 0;
339
340 RECTANGLE_16* rect = calloc(1, sizeof(RECTANGLE_16));
341 if (rect)
342 {
343 rect->right = nSrcWidth;
344 rect->bottom = nSrcHeight;
345 }
346 return allocate_h264_metablock(10, rect, meta, 1);
347#else
348 WLog_Print(av1->log, WLOG_ERROR, "AV1 encoder requires libaom, recompile with '-DWITH_AOM=ON'");
349 WINPR_UNUSED(pSrcData);
350 WINPR_UNUSED(SrcFormat);
351 WINPR_UNUSED(nSrcStep);
352 WINPR_UNUSED(nSrcWidth);
353 WINPR_UNUSED(nSrcHeight);
354 WINPR_UNUSED(regionRect);
355 WINPR_UNUSED(meta);
356 return -1;
357#endif
358}
359
360static BOOL isRectValid(UINT32 width, UINT32 height, const RECTANGLE_16* rect)
361{
362 WINPR_ASSERT(rect);
363 if (rect->left > width)
364 return FALSE;
365 if (rect->right > width)
366 return FALSE;
367 if (rect->left >= rect->right)
368 return FALSE;
369 if (rect->top > height)
370 return FALSE;
371 if (rect->bottom > height)
372 return FALSE;
373 if (rect->top >= rect->bottom)
374 return FALSE;
375 return TRUE;
376}
377
378static BOOL areRectsValid(wLog* log, UINT32 width, UINT32 height, const RECTANGLE_16* rects,
379 UINT32 count)
380{
381 WINPR_ASSERT(rects || (count == 0));
382 for (size_t x = 0; x < count; x++)
383 {
384 const RECTANGLE_16* rect = &rects[x];
385 if (!isRectValid(width, height, rect))
386 {
387 char buffer[64] = WINPR_C_ARRAY_INIT;
388 WLog_Print(log, WLOG_WARN,
389 "Rectangle %" PRIuz " %s outside of bounding frame %" PRIu32 "x%" PRIu32, x,
390 rectangle_to_string(rect, buffer, sizeof(buffer)), width, height);
391 return FALSE;
392 }
393 }
394 return TRUE;
395}
396
397#if defined(WITH_DAV1D)
398static void av1_dav1d_data_free(WINPR_ATTR_UNUSED const uint8_t* buf,
399 WINPR_ATTR_UNUSED void* cookie)
400{
401 /* pSrcData is owned by the caller of freerdp_av1_decompress(), dav1d must not free it. */
402}
403
404static INT32 av1_dav1d_convert(FREERDP_AV1_CONTEXT* av1, const Dav1dPicture* pic, BYTE* pDstData,
405 DWORD DstFormat, UINT32 nDstStep, UINT32 nDstWidth,
406 UINT32 nDstHeight, const RECTANGLE_16* regionRects,
407 UINT32 numRegionRect)
408{
409 if (pic->p.bpc != 8)
410 {
411 WLog_Print(av1->log, WLOG_ERROR, "dav1d picture bpc %d not supported", pic->p.bpc);
412 return -1;
413 }
414
415 /* The decoded frame may be smaller than the destination surface, so
416 * bound the converted region to it. Otherwise the colour conversion
417 * reads past the decoded image planes. */
418 const UINT32 dw = (UINT32)pic->p.w;
419 const UINT32 dh = (UINT32)pic->p.h;
420 const UINT32 nWidth = (dw < nDstWidth) ? dw : nDstWidth;
421 const UINT32 nHeight = (dh < nDstHeight) ? dh : nDstHeight;
422 const prim_size_t roi = { .width = nWidth, .height = nHeight };
423
424 if (!areRectsValid(av1->log, roi.width, roi.height, regionRects, numRegionRect))
425 return -2;
426
427 const UINT32 strides[] = { (UINT32)pic->stride[0], (UINT32)pic->stride[1],
428 (UINT32)pic->stride[1] };
429
430 pstatus_t rec = -1;
431#if defined(WITH_LIBYUV)
432 /* J420ToARGB()/J444ToARGB() always write pDstData as packed BGRA8888 regardless of
433 * DstFormat, so the fast path only applies for that layout; anything else must go
434 * through the format-aware primitives conversion below. */
435 if ((DstFormat == PIXEL_FORMAT_BGRA32) || (DstFormat == PIXEL_FORMAT_BGRX32))
436 {
437 const uint8_t* y = pic->data[0];
438 const uint8_t* u = pic->data[1];
439 const uint8_t* v = pic->data[2];
440
441 const int stride0 = WINPR_ASSERTING_INT_CAST(int, strides[0]);
442 const int stride1 = WINPR_ASSERTING_INT_CAST(int, strides[1]);
443 const int stride2 = WINPR_ASSERTING_INT_CAST(int, strides[2]);
444 const int iWidth = WINPR_ASSERTING_INT_CAST(int, nWidth);
445 const int iHeight = WINPR_ASSERTING_INT_CAST(int, nHeight);
446 const int iDstStep = WINPR_ASSERTING_INT_CAST(int, nDstStep);
447
448 switch (pic->p.layout)
449 {
450 case DAV1D_PIXEL_LAYOUT_I420:
451 rec = J420ToARGB(y, stride0, u, stride1, v, stride2, pDstData, iDstStep, iWidth,
452 iHeight);
453 break;
454 case DAV1D_PIXEL_LAYOUT_I444:
455 rec = J444ToARGB(y, stride0, u, stride1, v, stride2, pDstData, iDstStep, iWidth,
456 iHeight);
457 break;
458 default:
459 WLog_Print(av1->log, WLOG_ERROR, "dav1d picture layout %d not supported",
460 pic->p.layout);
461 return -1;
462 }
463 }
464 else
465#endif
466 {
467 primitives_t* primitives = primitives_get();
468 if (!primitives)
469 {
470 WLog_Print(av1->log, WLOG_ERROR, "primitives_get(): nullptr");
471 return -1;
472 }
473
474 const BYTE* pSrc[] = { pic->data[0], pic->data[1], pic->data[2] };
475 switch (pic->p.layout)
476 {
477 case DAV1D_PIXEL_LAYOUT_I420:
478 rec = primitives->YUV420ToRGB_8u_P3AC4R(pSrc, strides, pDstData, nDstStep,
479 DstFormat, &roi);
480 break;
481 case DAV1D_PIXEL_LAYOUT_I444:
482 rec = primitives->YUV444ToRGB_8u_P3AC4R(pSrc, strides, pDstData, nDstStep,
483 DstFormat, &roi);
484 break;
485 default:
486 WLog_Print(av1->log, WLOG_ERROR, "dav1d picture layout %d not supported",
487 pic->p.layout);
488 return -1;
489 }
490 }
491 if (rec != 0)
492 {
493 WLog_Print(av1->log, WLOG_ERROR, "RGB to YUV conversion failed: %d", rec);
494 return -1;
495 }
496 return TRUE;
497}
498
499/* Return: TRUE if a picture was converted, FALSE if none was ready (EAGAIN), <0 on error. */
500static INT32 av1_dav1d_drain_one(FREERDP_AV1_CONTEXT* av1, BYTE* pDstData, DWORD DstFormat,
501 UINT32 nDstStep, UINT32 nDstWidth, UINT32 nDstHeight,
502 const RECTANGLE_16* regionRects, UINT32 numRegionRect)
503{
504 Dav1dPicture pic = WINPR_C_ARRAY_INIT;
505 const int prc = dav1d_get_picture(av1->dav1d, &pic);
506 if (prc == DAV1D_ERR(EAGAIN))
507 return FALSE;
508 if (prc < 0)
509 {
510 WLog_Print(av1->log, WLOG_WARN, "dav1d_get_picture: %d", prc);
511 return -1;
512 }
513
514 const INT32 rc = av1_dav1d_convert(av1, &pic, pDstData, DstFormat, nDstStep, nDstWidth,
515 nDstHeight, regionRects, numRegionRect);
516 dav1d_picture_unref(&pic);
517 return (rc == TRUE) ? TRUE : rc;
518}
519#endif
520
521INT32 freerdp_av1_decompress(FREERDP_AV1_CONTEXT* av1, const BYTE* pSrcData, UINT32 SrcSize,
522 BYTE* pDstData, DWORD DstFormat, UINT32 nDstStep, UINT32 nDstWidth,
523 UINT32 nDstHeight, const RECTANGLE_16* regionRects,
524 UINT32 numRegionRect)
525{
526 WINPR_ASSERT(av1);
527 if (av1->encoder)
528 {
529 WLog_Print(av1->log, WLOG_ERROR, "av1->encoder: %d", av1->encoder);
530 return -1;
531 }
532
533 if (!areRectsValid(av1->log, nDstWidth, nDstHeight, regionRects, numRegionRect))
534 return -2;
535
536#if defined(WITH_DAV1D)
537 Dav1dData data = WINPR_C_ARRAY_INIT;
538 const int wrc = dav1d_data_wrap(&data, pSrcData, SrcSize, av1_dav1d_data_free, nullptr);
539 if (wrc < 0)
540 {
541 WLog_Print(av1->log, WLOG_WARN, "dav1d_data_wrap: %d", wrc);
542 return -1;
543 }
544
545 while (data.sz > 0)
546 {
547 const int src = dav1d_send_data(av1->dav1d, &data);
548 if ((src < 0) && (src != DAV1D_ERR(EAGAIN)))
549 {
550 WLog_Print(av1->log, WLOG_WARN, "dav1d_send_data: %d", src);
551 dav1d_data_unref(&data);
552 return -1;
553 }
554
555 const INT32 rc = av1_dav1d_drain_one(av1, pDstData, DstFormat, nDstStep, nDstWidth,
556 nDstHeight, regionRects, numRegionRect);
557 if ((rc != TRUE) && (rc != FALSE))
558 {
559 dav1d_data_unref(&data);
560 return rc;
561 }
562 }
563
564 /* Drain any picture the decoder buffered internally before it consumed the last input. */
565 for (;;)
566 {
567 const INT32 rc = av1_dav1d_drain_one(av1, pDstData, DstFormat, nDstStep, nDstWidth,
568 nDstHeight, regionRects, numRegionRect);
569 if (rc == FALSE)
570 break;
571 if (rc != TRUE)
572 return rc;
573 }
574
575 return TRUE;
576#elif defined(WITH_LIBAOM)
577 const aom_codec_err_t rc = aom_codec_decode(&av1->ctx, pSrcData, SrcSize, nullptr);
578 if (rc != AOM_CODEC_OK)
579 {
580 WLog_Print(av1->log, WLOG_WARN, "aom_codec_decode: %s", aom_codec_err_to_string(rc));
581 return -1;
582 }
583
584 aom_image_t* img = nullptr;
585 aom_codec_iter_t iter = nullptr;
586 while ((img = aom_codec_get_frame(&av1->ctx, &iter)) != nullptr)
587 {
588 /* The decoded frame may be smaller than the destination surface, so
589 * bound the converted region to it. Otherwise the colour conversion
590 * reads past the decoded image planes. */
591 const UINT32 nWidth = (img->d_w < nDstWidth) ? img->d_w : nDstWidth;
592 const UINT32 nHeight = (img->d_h < nDstHeight) ? img->d_h : nDstHeight;
593 const prim_size_t roi = { .width = nWidth, .height = nHeight };
594
595 if (!areRectsValid(av1->log, roi.width, roi.height, regionRects, numRegionRect))
596 return -2;
597
598 pstatus_t rec = -1;
599#if defined(WITH_LIBYUV)
600 /* J420ToARGB()/J444ToARGB() always write pDstData as packed BGRA8888 regardless of
601 * DstFormat, so the fast path only applies for that layout; anything else must go
602 * through the format-aware primitives conversion below. */
603 if ((DstFormat == PIXEL_FORMAT_BGRA32) || (DstFormat == PIXEL_FORMAT_BGRX32))
604 {
605 switch (img->fmt)
606 {
607 case AOM_IMG_FMT_I420:
608 rec = J420ToARGB(img->planes[0], img->stride[0], img->planes[1], img->stride[1],
609 img->planes[2], img->stride[2], pDstData, nDstStep, nWidth,
610 nHeight);
611 break;
612 case AOM_IMG_FMT_I444:
613 rec = J444ToARGB(img->planes[0], img->stride[0], img->planes[1], img->stride[1],
614 img->planes[2], img->stride[2], pDstData, nDstStep, nWidth,
615 nHeight);
616 break;
617 default:
618 WLog_Print(av1->log, WLOG_ERROR, "img->fmt %d not supported", img->fmt);
619 return -1;
620 }
621 }
622 else
623#endif
624 {
625 const BYTE* pSrc[] = { img->planes[0], img->planes[1], img->planes[2] };
626 const UINT32 strides[] = { img->stride[0], img->stride[1], img->stride[2] };
627
628 primitives_t* primitives = primitives_get();
629 if (!primitives)
630 {
631 WLog_Print(av1->log, WLOG_ERROR, "primitives_get(): nullptr");
632 return FALSE;
633 }
634
635 switch (img->fmt)
636 {
637 case AOM_IMG_FMT_I420:
638 rec = primitives->YUV420ToRGB_8u_P3AC4R(pSrc, strides, pDstData, nDstStep,
639 DstFormat, &roi);
640 break;
641 case AOM_IMG_FMT_I444:
642 rec = primitives->YUV444ToRGB_8u_P3AC4R(pSrc, strides, pDstData, nDstStep,
643 DstFormat, &roi);
644 break;
645 default:
646 WLog_Print(av1->log, WLOG_ERROR, "img->fmt %d not supported", img->fmt);
647 return -1;
648 }
649 }
650 if (rec != 0)
651 {
652 WLog_Print(av1->log, WLOG_ERROR, "RGB to YUV conversion failed: %d", rec);
653 return -1;
654 }
655 }
656
657 return TRUE;
658#endif
659}
660
661BOOL freerdp_av1_context_reset(FREERDP_AV1_CONTEXT* av1, UINT32 width, UINT32 height)
662{
663 if (av1->encoder)
664 {
665#if defined(WITH_LIBAOM)
666 av1->ecfg.g_w = width;
667 av1->ecfg.g_h = height;
668
669 if (av1->initialized)
670 {
671 const aom_codec_err_t rc = aom_codec_destroy(&av1->ctx);
672 av1->initialized = false;
673 if (rc != AOM_CODEC_OK)
674 {
675 WLog_Print(av1->log, WLOG_WARN, "aom_codec_destroy: %s",
676 aom_codec_err_to_string(rc));
677 return FALSE;
678 }
679 }
680
681 const aom_codec_err_t rc =
682 aom_codec_enc_init(&av1->ctx, av1->iface, &av1->ecfg, av1->flags);
683 if (rc != AOM_CODEC_OK)
684 {
685 WLog_Print(av1->log, WLOG_WARN, "aom_codec_enc_init: %s", aom_codec_err_to_string(rc));
686 return FALSE;
687 }
688
689 av1->framecount = 0;
690 av1->eflags = 0; // AOM_EFLAG_FORCE_KF;
691 av1->initialized = true;
692#else
693 WLog_Print(av1->log, WLOG_ERROR,
694 "AV1 encoder requires libaom, recompile with '-DWITH_AOM=ON'");
695 return FALSE;
696#endif
697 }
698 else
699 {
700#if defined(WITH_DAV1D)
701 if (av1->initialized)
702 {
703 dav1d_close(&av1->dav1d);
704 av1->initialized = false;
705 }
706
707 Dav1dSettings settings = WINPR_C_ARRAY_INIT;
708 dav1d_default_settings(&settings);
709 /* Single-threaded: freerdp_av1_decompress() needs its picture back synchronously,
710 * not held back by frame-parallel decoding. */
711 settings.n_threads = 1;
712 settings.max_frame_delay = 1;
713
714 const int rc = dav1d_open(&av1->dav1d, &settings);
715 if (rc < 0)
716 {
717 WLog_Print(av1->log, WLOG_WARN, "dav1d_open: %d", rc);
718 return FALSE;
719 }
720 av1->initialized = true;
721#elif defined(WITH_LIBAOM)
722 av1->dcfg.w = width;
723 av1->dcfg.h = height;
724 av1->dcfg.allow_lowbitdepth = 1;
725 const aom_codec_err_t rc =
726 aom_codec_dec_init(&av1->ctx, av1->iface, &av1->dcfg, av1->flags);
727 if (rc != AOM_CODEC_OK)
728 {
729 WLog_Print(av1->log, WLOG_WARN, "aom_codec_dec_init: %s", aom_codec_err_to_string(rc));
730 return FALSE;
731 }
732 av1->initialized = true;
733#else
734 WLog_Print(av1->log, WLOG_WARN,
735 "This build does not support AV1 decoding. Recompile with '-DWITH_DAV1D=ON' "
736 "or '-DWITH_AOM=ON'");
737 return FALSE;
738#endif
739 }
740
741 av1->yuvWidth = width;
742 av1->yuvStride[0] = width;
743
744 const size_t pad = av1->yuvStride[0] % 16;
745 if (pad != 0)
746 av1->yuvStride[0] += 16 - pad;
747 av1->yuvStride[0] *= 3;
748 av1->yuvStride[1] = width;
749 av1->yuvStride[2] = width;
750
751 av1->yuvHeight = height;
752 winpr_aligned_free(av1->yuvdata[0]);
753 winpr_aligned_free(av1->yuvdata[1]);
754 winpr_aligned_free(av1->yuvdata[2]);
755 av1->yuvdata[0] = winpr_aligned_malloc(1ull * av1->yuvStride[0] * av1->yuvHeight, 64);
756 av1->yuvdata[1] = winpr_aligned_malloc(1ull * av1->yuvStride[1] * av1->yuvHeight, 64);
757 av1->yuvdata[2] = winpr_aligned_malloc(1ull * av1->yuvStride[2] * av1->yuvHeight, 64);
758 return av1->yuvdata[0] != nullptr;
759}
760
761void freerdp_av1_context_free(FREERDP_AV1_CONTEXT* av1)
762{
763 if (!av1)
764 return;
765
766 if (av1->initialized)
767 {
768 if (av1->encoder)
769 {
770#if defined(WITH_LIBAOM)
771 av1_aom_destroy(av1);
772#endif
773 }
774 else
775 {
776#if defined(WITH_DAV1D)
777 dav1d_close(&av1->dav1d);
778#elif defined(WITH_LIBAOM)
779 av1_aom_destroy(av1);
780#endif
781 }
782 }
783 winpr_aligned_free(av1->yuvdata[0]);
784 winpr_aligned_free(av1->yuvdata[1]);
785 winpr_aligned_free(av1->yuvdata[2]);
786 free(av1);
787}
788
789FREERDP_AV1_CONTEXT* freerdp_av1_context_new(BOOL Compressor)
790{
791 FREERDP_AV1_CONTEXT* ctx = calloc(1, sizeof(FREERDP_AV1_CONTEXT));
792 if (!ctx)
793 return nullptr;
794 ctx->encoder = Compressor;
795 ctx->log = WLog_Get(TAG);
796 if (!ctx->log)
797 goto fail;
798
799 if (Compressor)
800 {
801#if defined(WITH_LIBAOM)
802 ctx->iface = aom_codec_av1_cx();
803 if (!ctx->iface)
804 {
805 WLog_Print(ctx->log, WLOG_ERROR, "aom_codec_av1_cx() nullptr");
806 goto fail;
807 }
808
809 const aom_codec_err_t rc =
810 aom_codec_enc_config_default(ctx->iface, &ctx->ecfg, AOM_USAGE_REALTIME);
811 if (rc != AOM_CODEC_OK)
812 {
813 WLog_Print(ctx->log, WLOG_ERROR, "aom_codec_enc_config_default() %s",
814 aom_codec_err_to_string(rc));
815 goto fail;
816 }
817#else
818 WLog_Print(ctx->log, WLOG_ERROR,
819 "AV1 encoder requires libaom, recompile with '-DWITH_AOM=ON'");
820 goto fail;
821#endif
822 }
823#if defined(WITH_LIBAOM) && !defined(WITH_DAV1D)
824 else
825 {
826 ctx->iface = aom_codec_av1_dx();
827 if (!ctx->iface)
828 {
829 WLog_Print(ctx->log, WLOG_ERROR, "aom_codec_av1_dx() nullptr");
830 goto fail;
831 }
832 }
833#endif
834
835 return ctx;
836
837fail:
838 freerdp_av1_context_free(ctx);
839 return nullptr;
840}
841#else
842
843struct S_FREERDP_AV1_CONTEXT
844{
845 BOOL Compressor;
846 wLog* log;
847};
848
849BOOL freerdp_av1_context_set_option(WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT* av1,
850 WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT_OPTION option,
851 WINPR_ATTR_UNUSED UINT32 value)
852{
853 return FALSE;
854}
855
856UINT32 freerdp_av1_context_get_option(WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT* av1,
857 WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT_OPTION option)
858{
859 return 0;
860}
861
862INT32 freerdp_av1_compress(WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT* av1,
863 WINPR_ATTR_UNUSED const BYTE* pSrcData,
864 WINPR_ATTR_UNUSED DWORD SrcFormat, WINPR_ATTR_UNUSED UINT32 nSrcStep,
865 WINPR_ATTR_UNUSED UINT32 nSrcWidth, WINPR_ATTR_UNUSED UINT32 nSrcHeight,
866 WINPR_ATTR_UNUSED const RECTANGLE_16* regionRect,
867 WINPR_ATTR_UNUSED BYTE** ppDstData, WINPR_ATTR_UNUSED UINT32* pDstSize,
868 WINPR_ATTR_UNUSED RDPGFX_H264_METABLOCK* meta)
869{
870 WINPR_ASSERT(av1);
871 WLog_Print(av1->log, WLOG_ERROR,
872 "This build does not support AV1 codec. Recompile with '-DWITH_AOM=ON' or "
873 "'-DWITH_DAV1D=ON'");
874 return -1;
875}
876
877INT32 freerdp_av1_decompress(WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT* av1,
878 WINPR_ATTR_UNUSED const BYTE* pSrcData,
879 WINPR_ATTR_UNUSED UINT32 SrcSize, WINPR_ATTR_UNUSED BYTE* pDstData,
880 WINPR_ATTR_UNUSED DWORD DstFormat, WINPR_ATTR_UNUSED UINT32 nDstStep,
881 WINPR_ATTR_UNUSED UINT32 nDstWidth,
882 WINPR_ATTR_UNUSED UINT32 nDstHeight,
883 WINPR_ATTR_UNUSED const RECTANGLE_16* regionRects,
884 WINPR_ATTR_UNUSED UINT32 numRegionRect)
885{
886 WINPR_ASSERT(av1);
887 WLog_Print(av1->log, WLOG_ERROR,
888 "This build does not support AV1 codec. Recompile with '-DWITH_AOM=ON' or "
889 "'-DWITH_DAV1D=ON'");
890 return -1;
891}
892
893BOOL freerdp_av1_context_reset(WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT* av1,
894 WINPR_ATTR_UNUSED UINT32 width, WINPR_ATTR_UNUSED UINT32 height)
895{
896 WINPR_ASSERT(av1);
897 WLog_Print(av1->log, WLOG_WARN,
898 "This build does not support AV1 codec. Recompile with '-DWITH_AOM=ON' or "
899 "'-DWITH_DAV1D=ON'");
900 return FALSE;
901}
902
903void freerdp_av1_context_free(WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT* av1)
904{
905 free(av1);
906}
907
908FREERDP_AV1_CONTEXT* freerdp_av1_context_new(BOOL Compressor)
909{
910 FREERDP_AV1_CONTEXT* ctx = calloc(1, sizeof(FREERDP_AV1_CONTEXT));
911 if (!ctx)
912 return nullptr;
913 ctx->Compressor = Compressor;
914 ctx->log = WLog_Get(TAG);
915 return ctx;
916}
917#endif
WINPR_ATTR_NODISCARD fn_RGBToYUV444_8u_P3AC4R_t RGBToI444_8u
Definition primitives.h:305