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