FreeRDP
Loading...
Searching...
No Matches
av2.c
1
18#include <freerdp/codec/av2.h>
19#include <freerdp/primitives.h>
20#include <freerdp/log.h>
21#include <freerdp/codec/region.h>
22
23#define TAG FREERDP_TAG("codec.av2")
24
25#if defined(WITH_LIBAVM)
26#include <avm/avm.h>
27
28#include <avm/avm_decoder.h>
29#include <avm/avm_image.h>
30#include <avm/avmdx.h>
31#endif
32
33/* The encoder: libavm's, unless it was built without one (CONFIG_AV2_ENCODER=0,
34 * WITH_AVM_ENCODER=OFF), which leaves out its headers and functions; decoding is the same */
35#if defined(WITH_LIBAVM) && defined(WITH_LIBAVM_ENCODER)
36#define AV2_ENCODER_LIBAVM
37#include <avm/avm_encoder.h>
38#include <avm/avmcx.h>
39#endif
40
41/* The decoder: libavm, the reference, when there is one. dav2d 0.1.0 decodes the intra frames
42 * of libavm v1.0.0 bit-exact, but not its inter frames, and rejects its 4:4:4 streams. */
43#if defined(WITH_DAV2D) && !defined(WITH_LIBAVM)
44#define AV2_DECODER_DAV2D
45#include <dav2d/dav2d.h>
46#endif
47
48#if defined(WITH_LIBAVM) || defined(WITH_DAV2D)
49
50#if defined(WITH_LIBYUV)
51#include <libyuv.h>
52#endif
53
54/* AV2 bitstream profiles, [AV2] Annex A, Table A.1 */
55#define AV2_PROFILE_MAIN_420_10_IP0 0
56#define AV2_PROFILE_MAIN_444_10_IP1 4
57
58/* The realtime usage is newer than libavm v1.0.0 */
59#if defined(WITH_LIBAVM) && !defined(AVM_USAGE_REALTIME)
60#define AVM_USAGE_REALTIME AVM_USAGE_GOOD_QUALITY
61#endif
62
63/* The planes of a decoded picture */
64typedef struct
65{
66 const BYTE* data[3];
67 UINT32 stride[3]; /* in bytes */
68 UINT32 width;
69 UINT32 height;
70 UINT32 bpc; /* significant bits per sample */
71 BOOL highbd; /* samples stored in 16 bits instead of 8 */
72 BOOL i444;
73} av2_planes;
74
75struct S_FREERDP_AV2_CONTEXT
76{
77 wLog* log;
78 bool encoder;
79 bool initialized;
80#if defined(WITH_LIBAVM)
81 avm_codec_ctx_t ctx;
82#if defined(AV2_ENCODER_LIBAVM)
83 avm_codec_enc_cfg_t ecfg;
84 avm_codec_pts_t framecount;
85 avm_enc_frame_flags_t eflags;
86#endif
87 avm_codec_flags_t flags;
88 avm_codec_iface_t* iface;
89 UINT32 profile; /* FREERDP_AV2_CONTEXT_OPTION_PROFILE: 0 4:2:0, 1 4:4:4 */
90#endif
91#if defined(WITH_LIBAVM)
92 avm_codec_dec_cfg_t dcfg;
93#endif
94#if defined(AV2_DECODER_DAV2D)
95 Dav2dContext* dav2d;
96#endif
97 BYTE* yuvdata[3];
98 UINT32 yuvWidth;
99 UINT32 yuvStride[3];
100 UINT32 yuvHeight;
101};
102
103#if defined(WITH_LIBAVM)
104static void av2_avm_destroy(FREERDP_AV2_CONTEXT* av2)
105{
106 const avm_codec_err_t rc = avm_codec_destroy(&av2->ctx);
107 if (rc != AVM_CODEC_OK)
108 WLog_Print(av2->log, WLOG_WARN, "avm_codec_destroy: %s", avm_codec_err_to_string(rc));
109}
110#endif
111
112#if defined(AV2_ENCODER_LIBAVM)
113
114WINPR_ATTR_NODISCARD
115static BOOL allocate_h264_metablock(UINT32 QP, RECTANGLE_16* rectangles,
116 RDPGFX_H264_METABLOCK* meta, size_t count)
117{
118 /* [MS-RDPEGFX] 2.2.4.4.2 RDPGFX_AVC420_QUANT_QUALITY */
119 if (!meta || (QP > UINT8_MAX))
120 {
121 free(rectangles);
122 return FALSE;
123 }
124
125 meta->regionRects = rectangles;
126 if (count == 0)
127 return TRUE;
128
129 if (count > UINT32_MAX)
130 return FALSE;
131
132 meta->quantQualityVals = calloc(count, sizeof(RDPGFX_H264_QUANT_QUALITY));
133
134 if (!meta->quantQualityVals || !meta->regionRects)
135 return FALSE;
136 meta->numRegionRects = (UINT32)count;
137 for (size_t x = 0; x < count; x++)
138 {
139 RDPGFX_H264_QUANT_QUALITY* cur = &meta->quantQualityVals[x];
140 cur->qp = (UINT8)QP;
141
142 /* qpVal bit 6 and 7 are flags, so mask them out here.
143 * qualityVal is [0-100] so 100 - qpVal [0-64] is always in range */
144 cur->qualityVal = 100 - (QP & 0x3F);
145 }
146 return TRUE;
147}
148
149WINPR_ATTR_NODISCARD
150static BOOL av2_avm_control(FREERDP_AV2_CONTEXT* av2, int id, const char* name, int value)
151{
152 const avm_codec_err_t rc = avm_codec_control(&av2->ctx, id, value);
153 if (rc != AVM_CODEC_OK)
154 {
155 WLog_Print(av2->log, WLOG_WARN, "avm_codec_control(%s, %d): %s", name, value,
156 avm_codec_err_to_string(rc));
157 return FALSE;
158 }
159 return TRUE;
160}
161#endif
162
163BOOL freerdp_av2_context_set_option(FREERDP_AV2_CONTEXT* av2, FREERDP_AV2_CONTEXT_OPTION option,
164 UINT32 value)
165{
166#if defined(AV2_ENCODER_LIBAVM)
167 switch (option)
168 {
169 case FREERDP_AV2_CONTEXT_OPTION_PROFILE:
170 if (!av2->encoder)
171 return FALSE;
172 if (value > 1)
173 {
174 WLog_Print(av2->log, WLOG_WARN,
175 "Unknown FREERDP_AV2_CONTEXT_OPTION_PROFILE value %" PRIu32, value);
176 return FALSE;
177 }
178
179 av2->profile = value;
180 av2->ecfg.g_profile =
181 (value == 1) ? AV2_PROFILE_MAIN_444_10_IP1 : AV2_PROFILE_MAIN_420_10_IP0;
182 break;
183
184 case FREERDP_AV2_CONTEXT_OPTION_RATECONTROL:
185 if (!av2->encoder)
186 return FALSE;
187 switch (value)
188 {
189 case FREERDP_AV2_VBR:
190 av2->ecfg.rc_end_usage = AVM_VBR;
191 break;
192 case FREERDP_AV2_CBR:
193 av2->ecfg.rc_end_usage = AVM_CBR;
194 break;
195 case FREERDP_AV2_CQ:
196 av2->ecfg.rc_end_usage = AVM_CQ;
197 break;
198 case FREERDP_AV2_Q:
199 av2->ecfg.rc_end_usage = AVM_Q;
200 break;
201 default:
202 WLog_Print(av2->log, WLOG_WARN,
203 "Unknown FREERDP_AV2_CONTEXT_OPTION_RATECONTROL value [0x%08" PRIx32
204 "]",
205 value);
206 return FALSE;
207 }
208 break;
209 case FREERDP_AV2_CONTEXT_OPTION_BITRATE:
210 if (!av2->encoder)
211 return FALSE;
212 av2->ecfg.rc_target_bitrate = value;
213 break;
214 case FREERDP_AV2_CONTEXT_OPTION_USAGETYPE:
215 if (!av2->encoder)
216 return FALSE;
217 av2->ecfg.g_usage = value;
218 break;
219 default:
220 WLog_Print(av2->log, WLOG_ERROR, "Unknown FREERDP_AV2_CONTEXT_OPTION[0x%08" PRIx32 "]",
221 option);
222 return TRUE;
223 }
224 return freerdp_av2_context_reset(av2, av2->ecfg.g_w, av2->ecfg.g_h);
225#else
226 WLog_Print(av2->log, WLOG_WARN,
227 "AV2 encoder requires libavm with its encoder, recompile with '-DWITH_AVM=ON'");
228 WINPR_UNUSED(option);
229 WINPR_UNUSED(value);
230 return FALSE;
231#endif
232}
233
234UINT32 freerdp_av2_context_get_option(FREERDP_AV2_CONTEXT* av2, FREERDP_AV2_CONTEXT_OPTION option)
235{
236#if defined(AV2_ENCODER_LIBAVM)
237 switch (option)
238 {
239 case FREERDP_AV2_CONTEXT_OPTION_PROFILE:
240 if (!av2->encoder)
241 return 0;
242 return av2->profile;
243 case FREERDP_AV2_CONTEXT_OPTION_RATECONTROL:
244 if (!av2->encoder)
245 return 0;
246 switch (av2->ecfg.rc_end_usage)
247 {
248 case AVM_VBR:
249 return FREERDP_AV2_VBR;
250 case AVM_CBR:
251 return FREERDP_AV2_CBR;
252 case AVM_CQ:
253 return FREERDP_AV2_CQ;
254 case AVM_Q:
255 default:
256 return FREERDP_AV2_Q;
257 }
258 case FREERDP_AV2_CONTEXT_OPTION_BITRATE:
259 if (!av2->encoder)
260 return 0;
261 return av2->ecfg.rc_target_bitrate;
262 case FREERDP_AV2_CONTEXT_OPTION_USAGETYPE:
263 if (!av2->encoder)
264 return 0;
265 return av2->ecfg.g_usage;
266 default:
267 WLog_Print(av2->log, WLOG_ERROR, "Unknown FREERDP_AV2_CONTEXT_OPTION[0x%08" PRIx32 "]",
268 option);
269 return 0;
270 }
271#else
272 WINPR_UNUSED(av2);
273 WINPR_UNUSED(option);
274 return 0;
275#endif
276}
277
278INT32 freerdp_av2_compress(FREERDP_AV2_CONTEXT* av2, const BYTE* pSrcData, DWORD SrcFormat,
279 UINT32 nSrcStep, UINT32 nSrcWidth, UINT32 nSrcHeight,
280 const RECTANGLE_16* regionRect, BYTE** ppDstData, UINT32* pDstSize,
282{
283 *ppDstData = nullptr;
284 *pDstSize = 0;
285
286 if (!av2->encoder)
287 {
288 WLog_Print(av2->log, WLOG_ERROR, "av2->encoder: %d", av2->encoder);
289 return -1;
290 }
291
292#if defined(AV2_ENCODER_LIBAVM)
293 primitives_t* primitives = primitives_get();
294 if (!primitives)
295 {
296 WLog_Print(av2->log, WLOG_ERROR, "primitives_get(): nullptr");
297 return -1;
298 }
299
300 if (av2->yuvWidth != nSrcWidth)
301 {
302 WLog_Print(av2->log, WLOG_ERROR, "av2->yuvWidth[%" PRIu32 "] != nSrcWidth[%" PRIu32 "]",
303 av2->yuvWidth, nSrcWidth);
304 return -1;
305 }
306
307 if (av2->yuvHeight != nSrcHeight)
308 {
309 WLog_Print(av2->log, WLOG_ERROR, "av2->yuvHeight[%" PRIu32 "] != nSrcHeight[%" PRIu32 "]",
310 av2->yuvHeight, nSrcHeight);
311 return -1;
312 }
313
314 const prim_size_t roi = { .width = nSrcWidth, .height = nSrcHeight };
315
316 avm_image_t buffer = WINPR_C_ARRAY_INIT;
317
318 avm_image_t* img = &buffer;
319 pstatus_t rec = -1;
320#if defined(WITH_LIBYUV)
321 /* ARGBToI420()/ARGBToI444() always read pSrcData as packed BGRA8888 regardless of
322 * SrcFormat, so the fast path only applies for that layout; anything else must go
323 * through the format-aware primitives conversion below. */
324 if ((SrcFormat == PIXEL_FORMAT_BGRA32) || (SrcFormat == PIXEL_FORMAT_BGRX32))
325 {
326 switch (av2->profile)
327 {
328 case 0:
329 img->fmt = AVM_IMG_FMT_I420;
330 img->x_chroma_shift = 1;
331 img->y_chroma_shift = 1;
332 rec = ARGBToI420(pSrcData, nSrcStep, av2->yuvdata[0], av2->yuvStride[0],
333 av2->yuvdata[1], av2->yuvStride[1], av2->yuvdata[2],
334 WINPR_ASSERTING_INT_CAST(int, av2->yuvStride[2]),
335 WINPR_ASSERTING_INT_CAST(int, roi.width),
336 WINPR_ASSERTING_INT_CAST(int, roi.height));
337 break;
338 case 1:
339 img->fmt = AVM_IMG_FMT_I444;
340 rec = ARGBToI444(pSrcData, nSrcStep, av2->yuvdata[0], av2->yuvStride[0],
341 av2->yuvdata[1], av2->yuvStride[1], av2->yuvdata[2],
342 WINPR_ASSERTING_INT_CAST(int, av2->yuvStride[2]),
343 WINPR_ASSERTING_INT_CAST(int, roi.width),
344 WINPR_ASSERTING_INT_CAST(int, roi.height));
345 break;
346 default:
347 WLog_Print(av2->log, WLOG_ERROR, "Unsupported AV2 profile %" PRIu32, av2->profile);
348 return -1;
349 }
350 }
351 else
352#endif
353 {
354 switch (av2->profile)
355 {
356 case 0:
357 img->fmt = AVM_IMG_FMT_I420;
358 img->x_chroma_shift = 1;
359 img->y_chroma_shift = 1;
360 rec = primitives->RGBToYUV420_8u_P3AC4R(pSrcData, SrcFormat, nSrcStep, av2->yuvdata,
361 av2->yuvStride, &roi);
362 break;
363 case 1:
364 img->fmt = AVM_IMG_FMT_I444;
365 rec = primitives->RGBToI444_8u(pSrcData, SrcFormat, nSrcStep, av2->yuvdata,
366 av2->yuvStride, &roi);
367 break;
368 default:
369 WLog_Print(av2->log, WLOG_ERROR, "Unsupported AV2 profile %" PRIu32, av2->profile);
370 return -1;
371 }
372 }
373 if (rec != 0)
374 {
375 WLog_Print(av2->log, WLOG_ERROR, "RGB to YUV conversion failed: %d", rec);
376 return -1;
377 }
378 img->bit_depth = 8;
379 img->d_w = img->r_w = img->w = roi.width;
380 img->d_h = img->r_h = img->h = roi.height;
381 img->stride[0] = WINPR_ASSERTING_INT_CAST(int, av2->yuvStride[0]);
382 img->stride[1] = WINPR_ASSERTING_INT_CAST(int, av2->yuvStride[1]);
383 img->stride[2] = WINPR_ASSERTING_INT_CAST(int, av2->yuvStride[2]);
384 img->planes[0] = av2->yuvdata[0];
385 img->planes[1] = av2->yuvdata[1];
386 img->planes[2] = av2->yuvdata[2];
387
388 const avm_codec_err_t rc = avm_codec_encode(&av2->ctx, img, ++av2->framecount, 1, av2->eflags);
389 av2->eflags = 0;
390
391 if (rc != AVM_CODEC_OK)
392 {
393 WLog_Print(av2->log, WLOG_WARN, "avm_codec_encode: %s", avm_codec_err_to_string(rc));
394 return -1;
395 }
396
397 avm_codec_iter_t iter = nullptr;
398 const avm_codec_cx_pkt_t* pkt = nullptr;
399 while ((pkt = avm_codec_get_cx_data(&av2->ctx, &iter)) != nullptr)
400 {
401 if (pkt->kind != AVM_CODEC_CX_FRAME_PKT)
402 continue;
403
404 *ppDstData = pkt->data.frame.buf;
405 *pDstSize = WINPR_ASSERTING_INT_CAST(UINT32, pkt->data.frame.sz);
406 }
407 if (*pDstSize == 0)
408 return 0;
409
410 RECTANGLE_16* rect = calloc(1, sizeof(RECTANGLE_16));
411 if (rect)
412 {
413 rect->right = WINPR_ASSERTING_INT_CAST(UINT16, nSrcWidth);
414 rect->bottom = WINPR_ASSERTING_INT_CAST(UINT16, nSrcHeight);
415 }
416 return allocate_h264_metablock(10, rect, meta, 1);
417#else
418 WLog_Print(av2->log, WLOG_ERROR,
419 "AV2 encoder requires libavm with its encoder, recompile with '-DWITH_AVM=ON'");
420 WINPR_UNUSED(pSrcData);
421 WINPR_UNUSED(SrcFormat);
422 WINPR_UNUSED(nSrcStep);
423 WINPR_UNUSED(nSrcWidth);
424 WINPR_UNUSED(nSrcHeight);
425 WINPR_UNUSED(regionRect);
426 WINPR_UNUSED(meta);
427 return -1;
428#endif
429}
430
431static BOOL isRectValid(UINT32 width, UINT32 height, const RECTANGLE_16* rect)
432{
433 WINPR_ASSERT(rect);
434 if (rect->left > width)
435 return FALSE;
436 if (rect->right > width)
437 return FALSE;
438 if (rect->left >= rect->right)
439 return FALSE;
440 if (rect->top > height)
441 return FALSE;
442 if (rect->bottom > height)
443 return FALSE;
444 if (rect->top >= rect->bottom)
445 return FALSE;
446 return TRUE;
447}
448
449static BOOL areRectsValid(wLog* log, UINT32 width, UINT32 height, const RECTANGLE_16* rects,
450 UINT32 count)
451{
452 WINPR_ASSERT(rects || (count == 0));
453 for (size_t x = 0; x < count; x++)
454 {
455 const RECTANGLE_16* rect = &rects[x];
456 if (!isRectValid(width, height, rect))
457 {
458 char buffer[64] = WINPR_C_ARRAY_INIT;
459 WLog_Print(log, WLOG_WARN,
460 "Rectangle %" PRIuz " %s outside of bounding frame %" PRIu32 "x%" PRIu32, x,
461 rectangle_to_string(rect, buffer, sizeof(buffer)), width, height);
462 return FALSE;
463 }
464 }
465 return TRUE;
466}
467
468/* AV2's main profiles are 10 bit: a decoder may hand out 16 bit samples, even for an 8 bit
469 * stream. Bring them down to 8 bit into the context's own planes, so that the colour
470 * conversion below always reads 8 bit samples. */
471WINPR_ATTR_NODISCARD
472static BOOL av2_planes_to_8bit(FREERDP_AV2_CONTEXT* av2, av2_planes* planes)
473{
474 WINPR_ASSERT(av2);
475 WINPR_ASSERT(planes);
476
477 if (!planes->highbd)
478 return (planes->bpc == 8);
479 if ((planes->bpc < 8) || (planes->bpc > 16))
480 {
481 WLog_Print(av2->log, WLOG_ERROR, "AV2 picture bpc %" PRIu32 " not supported", planes->bpc);
482 return FALSE;
483 }
484
485 if ((planes->width > av2->yuvWidth) || (planes->height > av2->yuvHeight))
486 {
487 WLog_Print(av2->log, WLOG_ERROR,
488 "AV2 picture %" PRIu32 "x%" PRIu32 " larger than the surface %" PRIu32
489 "x%" PRIu32,
490 planes->width, planes->height, av2->yuvWidth, av2->yuvHeight);
491 return FALSE;
492 }
493
494 const UINT32 shift = planes->bpc - 8;
495 for (size_t p = 0; p < 3; p++)
496 {
497 const UINT32 w = ((p == 0) || planes->i444) ? planes->width : (planes->width + 1) / 2;
498 const UINT32 h = ((p == 0) || planes->i444) ? planes->height : (planes->height + 1) / 2;
499 for (UINT32 y = 0; y < h; y++)
500 {
501 const UINT16* src = WINPR_PACKED_ALIGN_CAST(
502 const UINT16*, &planes->data[p][1ull * y * planes->stride[p]]);
503 BYTE* dst = &av2->yuvdata[p][1ull * y * av2->yuvStride[p]];
504 for (UINT32 x = 0; x < w; x++)
505 {
506 const UINT32 v = src[x] >> shift;
507 dst[x] = (BYTE)MIN(v, UINT8_MAX);
508 }
509 }
510 planes->data[p] = av2->yuvdata[p];
511 planes->stride[p] = av2->yuvStride[p];
512 }
513 planes->bpc = 8;
514 planes->highbd = FALSE;
515 return TRUE;
516}
517
518WINPR_ATTR_NODISCARD
519static INT32 av2_convert(FREERDP_AV2_CONTEXT* av2, av2_planes* planes, BYTE* pDstData,
520 DWORD DstFormat, UINT32 nDstStep, UINT32 nDstWidth, UINT32 nDstHeight,
521 const RECTANGLE_16* regionRects, UINT32 numRegionRect)
522{
523 if (!av2_planes_to_8bit(av2, planes))
524 {
525 WLog_Print(av2->log, WLOG_ERROR, "AV2 picture with %" PRIu32 " bit samples not supported",
526 planes->bpc);
527 return -1;
528 }
529
530 /* The decoded frame may be smaller than the destination surface, so
531 * bound the converted region to it. Otherwise the colour conversion
532 * reads past the decoded image planes. */
533 const UINT32 nWidth = (planes->width < nDstWidth) ? planes->width : nDstWidth;
534 const UINT32 nHeight = (planes->height < nDstHeight) ? planes->height : nDstHeight;
535 const prim_size_t roi = { .width = nWidth, .height = nHeight };
536
537 if (!areRectsValid(av2->log, roi.width, roi.height, regionRects, numRegionRect))
538 return -2;
539
540 pstatus_t rec = -1;
541#if defined(WITH_LIBYUV)
542 /* J420ToARGB()/J444ToARGB() always write pDstData as packed BGRA8888 regardless of
543 * DstFormat, so the fast path only applies for that layout; anything else must go
544 * through the format-aware primitives conversion below. */
545 if ((DstFormat == PIXEL_FORMAT_BGRA32) || (DstFormat == PIXEL_FORMAT_BGRX32))
546 {
547 const int stride0 = WINPR_ASSERTING_INT_CAST(int, planes->stride[0]);
548 const int stride1 = WINPR_ASSERTING_INT_CAST(int, planes->stride[1]);
549 const int stride2 = WINPR_ASSERTING_INT_CAST(int, planes->stride[2]);
550 const int iWidth = WINPR_ASSERTING_INT_CAST(int, nWidth);
551 const int iHeight = WINPR_ASSERTING_INT_CAST(int, nHeight);
552 const int iDstStep = WINPR_ASSERTING_INT_CAST(int, nDstStep);
553
554 if (planes->i444)
555 rec = J444ToARGB(planes->data[0], stride0, planes->data[1], stride1, planes->data[2],
556 stride2, pDstData, iDstStep, iWidth, iHeight);
557 else
558 rec = J420ToARGB(planes->data[0], stride0, planes->data[1], stride1, planes->data[2],
559 stride2, pDstData, iDstStep, iWidth, iHeight);
560 }
561 else
562#endif
563 {
564 primitives_t* primitives = primitives_get();
565 if (!primitives)
566 {
567 WLog_Print(av2->log, WLOG_ERROR, "primitives_get(): nullptr");
568 return -1;
569 }
570
571 if (planes->i444)
572 rec = primitives->I444ToRGB_8u(planes->data, planes->stride, pDstData, nDstStep,
573 DstFormat, &roi);
574 else
575 rec = primitives->YUV420ToRGB_8u_P3AC4R(planes->data, planes->stride, pDstData,
576 nDstStep, DstFormat, &roi);
577 }
578 if (rec != 0)
579 {
580 WLog_Print(av2->log, WLOG_ERROR, "YUV to RGB conversion failed: %d", rec);
581 return -1;
582 }
583 return TRUE;
584}
585
586#if defined(AV2_DECODER_DAV2D)
587static void av2_dav2d_data_free(WINPR_ATTR_UNUSED const uint8_t* buf,
588 WINPR_ATTR_UNUSED void* cookie)
589{
590 /* pSrcData is owned by the caller of freerdp_av2_decompress(), dav2d must not free it. */
591}
592
593/* Return: TRUE if a picture was converted, FALSE if none was ready (EAGAIN), <0 on error. */
594static INT32 av2_dav2d_drain_one(FREERDP_AV2_CONTEXT* av2, BYTE* pDstData, DWORD DstFormat,
595 UINT32 nDstStep, UINT32 nDstWidth, UINT32 nDstHeight,
596 const RECTANGLE_16* regionRects, UINT32 numRegionRect)
597{
598 Dav2dPicture pic = WINPR_C_ARRAY_INIT;
599 const int prc = dav2d_get_picture(av2->dav2d, &pic);
600 if (prc == DAV2D_ERR(EAGAIN))
601 return FALSE;
602 if (prc < 0)
603 {
604 WLog_Print(av2->log, WLOG_WARN, "dav2d_get_picture: %d", prc);
605 return -1;
606 }
607
608 INT32 rc = -1;
609 switch (pic.p.layout)
610 {
611 case DAV2D_PIXEL_LAYOUT_I420:
612 case DAV2D_PIXEL_LAYOUT_I444:
613 {
614 av2_planes planes = { .data = { pic.data[0], pic.data[1], pic.data[2] },
615 .stride = { (UINT32)pic.stride[0], (UINT32)pic.stride[1],
616 (UINT32)pic.stride[1] },
617 .width = (UINT32)pic.p.w,
618 .height = (UINT32)pic.p.h,
619 .bpc = (UINT32)pic.p.bpc,
620 .highbd = (pic.p.bpc > 8),
621 .i444 = (pic.p.layout == DAV2D_PIXEL_LAYOUT_I444) };
622 rc = av2_convert(av2, &planes, pDstData, DstFormat, nDstStep, nDstWidth, nDstHeight,
623 regionRects, numRegionRect);
624 }
625 break;
626 default:
627 WLog_Print(av2->log, WLOG_ERROR, "dav2d picture layout %u not supported", pic.p.layout);
628 break;
629 }
630 dav2d_picture_unref(&pic);
631 return (rc == TRUE) ? TRUE : rc;
632}
633#endif
634
635INT32 freerdp_av2_decompress(FREERDP_AV2_CONTEXT* av2, const BYTE* pSrcData, UINT32 SrcSize,
636 BYTE* pDstData, DWORD DstFormat, UINT32 nDstStep, UINT32 nDstWidth,
637 UINT32 nDstHeight, const RECTANGLE_16* regionRects,
638 UINT32 numRegionRect)
639{
640 WINPR_ASSERT(av2);
641 if (av2->encoder)
642 {
643 WLog_Print(av2->log, WLOG_ERROR, "av2->encoder: %d", av2->encoder);
644 return -1;
645 }
646
647 if (!areRectsValid(av2->log, nDstWidth, nDstHeight, regionRects, numRegionRect))
648 return -2;
649
650#if defined(AV2_DECODER_DAV2D)
651 Dav2dData data = WINPR_C_ARRAY_INIT;
652 const int wrc = dav2d_data_wrap(&data, pSrcData, SrcSize, av2_dav2d_data_free, nullptr);
653 if (wrc < 0)
654 {
655 WLog_Print(av2->log, WLOG_WARN, "dav2d_data_wrap: %d", wrc);
656 return -1;
657 }
658
659 while (data.sz > 0)
660 {
661 const int src = dav2d_send_data(av2->dav2d, &data);
662 if ((src < 0) && (src != DAV2D_ERR(EAGAIN)))
663 {
664 WLog_Print(av2->log, WLOG_WARN, "dav2d_send_data: %d", src);
665 dav2d_data_unref(&data);
666 return -1;
667 }
668
669 const INT32 rc = av2_dav2d_drain_one(av2, pDstData, DstFormat, nDstStep, nDstWidth,
670 nDstHeight, regionRects, numRegionRect);
671 if ((rc != TRUE) && (rc != FALSE))
672 {
673 dav2d_data_unref(&data);
674 return rc;
675 }
676 }
677
678 /* Drain any picture the decoder buffered internally before it consumed the last input. */
679 for (;;)
680 {
681 const INT32 rc = av2_dav2d_drain_one(av2, pDstData, DstFormat, nDstStep, nDstWidth,
682 nDstHeight, regionRects, numRegionRect);
683 if (rc == FALSE)
684 break;
685 if (rc != TRUE)
686 return rc;
687 }
688
689 return TRUE;
690#elif defined(WITH_LIBAVM)
691 const avm_codec_err_t rc = avm_codec_decode(&av2->ctx, pSrcData, SrcSize, nullptr);
692 if (rc != AVM_CODEC_OK)
693 {
694 WLog_Print(av2->log, WLOG_WARN, "avm_codec_decode: %s", avm_codec_err_to_string(rc));
695 return -1;
696 }
697
698 avm_image_t* img = nullptr;
699 avm_codec_iter_t iter = nullptr;
700 while ((img = avm_codec_get_frame(&av2->ctx, &iter)) != nullptr)
701 {
702 const avm_img_fmt_t fmt = img->fmt & ~AVM_IMG_FMT_HIGHBITDEPTH;
703 if ((fmt != AVM_IMG_FMT_I420) && (fmt != AVM_IMG_FMT_I444))
704 {
705 WLog_Print(av2->log, WLOG_ERROR, "img->fmt %d not supported", img->fmt);
706 return -1;
707 }
708
709 /* bit_depth is the coded depth; a high bit depth buffer may still carry 8 bit samples */
710 av2_planes planes = { .data = { img->planes[0], img->planes[1], img->planes[2] },
711 .stride = { (UINT32)img->stride[0], (UINT32)img->stride[1],
712 (UINT32)img->stride[2] },
713 .width = img->d_w,
714 .height = img->d_h,
715 .bpc = img->bit_depth,
716 .highbd = (img->fmt & AVM_IMG_FMT_HIGHBITDEPTH) != 0,
717 .i444 = (fmt == AVM_IMG_FMT_I444) };
718
719 const INT32 crc = av2_convert(av2, &planes, pDstData, DstFormat, nDstStep, nDstWidth,
720 nDstHeight, regionRects, numRegionRect);
721 if (crc != TRUE)
722 return crc;
723 }
724
725 return TRUE;
726#endif
727}
728
729BOOL freerdp_av2_context_reset(FREERDP_AV2_CONTEXT* av2, UINT32 width, UINT32 height)
730{
731 if (av2->encoder)
732 {
733#if defined(AV2_ENCODER_LIBAVM)
734 av2->ecfg.g_w = width;
735 av2->ecfg.g_h = height;
736
737 if (av2->initialized)
738 {
739 const avm_codec_err_t rc = avm_codec_destroy(&av2->ctx);
740 av2->initialized = false;
741 if (rc != AVM_CODEC_OK)
742 {
743 WLog_Print(av2->log, WLOG_WARN, "avm_codec_destroy: %s",
744 avm_codec_err_to_string(rc));
745 return FALSE;
746 }
747 }
748
749 const avm_codec_err_t rc =
750 avm_codec_enc_init(&av2->ctx, av2->iface, &av2->ecfg, av2->flags);
751 if (rc != AVM_CODEC_OK)
752 {
753 WLog_Print(av2->log, WLOG_WARN, "avm_codec_enc_init: %s", avm_codec_err_to_string(rc));
754 return FALSE;
755 }
756 av2->initialized = true;
757
758 /* A remote desktop: screen content, the fastest preset of the usage */
759 const int cpuUsed = (av2->ecfg.g_usage == AVM_USAGE_REALTIME) ? 9 : 6;
760 if (!av2_avm_control(av2, AVME_SET_CPUUSED, "AVME_SET_CPUUSED", cpuUsed) ||
761 !av2_avm_control(av2, AV2E_SET_TUNE_CONTENT, "AV2E_SET_TUNE_CONTENT",
762 AVM_CONTENT_SCREEN) ||
763 !av2_avm_control(av2, AV2E_SET_ROW_MT, "AV2E_SET_ROW_MT", 1))
764 return FALSE;
765
766 av2->framecount = 0;
767 av2->eflags = AVM_EFLAG_FORCE_KF;
768#else
769 WLog_Print(av2->log, WLOG_ERROR,
770 "AV2 encoder requires libavm with its encoder, recompile with '-DWITH_AVM=ON'");
771 return FALSE;
772#endif
773 }
774 else
775 {
776#if defined(AV2_DECODER_DAV2D)
777 if (av2->initialized)
778 {
779 dav2d_close(&av2->dav2d);
780 av2->initialized = false;
781 }
782
783 Dav2dSettings settings = WINPR_C_ARRAY_INIT;
784 dav2d_default_settings(&settings);
785 /* Single-threaded: freerdp_av2_decompress() needs its picture back synchronously,
786 * not held back by frame-parallel decoding. */
787 settings.n_threads = 1;
788 settings.max_frame_delay = 1;
789
790 const int rc = dav2d_open(&av2->dav2d, &settings);
791 if (rc < 0)
792 {
793 WLog_Print(av2->log, WLOG_WARN, "dav2d_open: %d", rc);
794 return FALSE;
795 }
796 av2->initialized = true;
797#elif defined(WITH_LIBAVM)
798 if (av2->initialized)
799 {
800 av2_avm_destroy(av2);
801 av2->initialized = false;
802 }
803
804 av2->dcfg.w = width;
805 av2->dcfg.h = height;
806 const avm_codec_err_t rc =
807 avm_codec_dec_init(&av2->ctx, av2->iface, &av2->dcfg, av2->flags);
808 if (rc != AVM_CODEC_OK)
809 {
810 WLog_Print(av2->log, WLOG_WARN, "avm_codec_dec_init: %s", avm_codec_err_to_string(rc));
811 return FALSE;
812 }
813 av2->initialized = true;
814#else
815 WLog_Print(av2->log, WLOG_WARN,
816 "This build does not support AV2 decoding. Recompile with '-DWITH_DAV2D=ON' "
817 "or '-DWITH_AVM=ON'");
818 return FALSE;
819#endif
820 }
821
822 av2->yuvWidth = width;
823 av2->yuvStride[0] = width;
824
825 const size_t pad = av2->yuvStride[0] % 16;
826 if (pad != 0)
827 av2->yuvStride[0] += 16 - pad;
828 av2->yuvStride[1] = av2->yuvStride[0];
829 av2->yuvStride[2] = av2->yuvStride[0];
830
831 av2->yuvHeight = height;
832 winpr_aligned_free(av2->yuvdata[0]);
833 winpr_aligned_free(av2->yuvdata[1]);
834 winpr_aligned_free(av2->yuvdata[2]);
835 av2->yuvdata[0] = winpr_aligned_malloc(1ull * av2->yuvStride[0] * av2->yuvHeight, 64);
836 av2->yuvdata[1] = winpr_aligned_malloc(1ull * av2->yuvStride[1] * av2->yuvHeight, 64);
837 av2->yuvdata[2] = winpr_aligned_malloc(1ull * av2->yuvStride[2] * av2->yuvHeight, 64);
838 return (av2->yuvdata[0] != nullptr) && (av2->yuvdata[1] != nullptr) &&
839 (av2->yuvdata[2] != nullptr);
840}
841
842void freerdp_av2_context_free(FREERDP_AV2_CONTEXT* av2)
843{
844 if (!av2)
845 return;
846
847 if (av2->initialized)
848 {
849 if (av2->encoder)
850 {
851#if defined(WITH_LIBAVM)
852 av2_avm_destroy(av2);
853#endif
854 }
855 else
856 {
857#if defined(AV2_DECODER_DAV2D)
858 dav2d_close(&av2->dav2d);
859#elif defined(WITH_LIBAVM)
860 av2_avm_destroy(av2);
861#endif
862 }
863 }
864 winpr_aligned_free(av2->yuvdata[0]);
865 winpr_aligned_free(av2->yuvdata[1]);
866 winpr_aligned_free(av2->yuvdata[2]);
867 free(av2);
868}
869
870FREERDP_AV2_CONTEXT* freerdp_av2_context_new(BOOL Compressor)
871{
872 FREERDP_AV2_CONTEXT* ctx = calloc(1, sizeof(FREERDP_AV2_CONTEXT));
873 if (!ctx)
874 return nullptr;
875 ctx->encoder = Compressor;
876 ctx->log = WLog_Get(TAG);
877 if (!ctx->log)
878 goto fail;
879
880 if (Compressor)
881 {
882#if defined(AV2_ENCODER_LIBAVM)
883 ctx->iface = avm_codec_av2_cx();
884 if (!ctx->iface)
885 {
886 WLog_Print(ctx->log, WLOG_ERROR, "avm_codec_av2_cx() nullptr");
887 goto fail;
888 }
889
890 const avm_codec_err_t rc =
891 avm_codec_enc_config_default(ctx->iface, &ctx->ecfg, AVM_USAGE_REALTIME);
892 if (rc != AVM_CODEC_OK)
893 {
894 WLog_Print(ctx->log, WLOG_ERROR, "avm_codec_enc_config_default() %s",
895 avm_codec_err_to_string(rc));
896 goto fail;
897 }
898 /* A frame out for each frame in, and no key frames but the first */
899 ctx->ecfg.g_lag_in_frames = 0;
900 ctx->ecfg.kf_mode = AVM_KF_DISABLED;
901 ctx->ecfg.g_profile = AV2_PROFILE_MAIN_420_10_IP0;
902 ctx->ecfg.g_bit_depth = AVM_BITS_8;
903 ctx->ecfg.g_input_bit_depth = 8;
904#else
905 WLog_Print(ctx->log, WLOG_ERROR,
906 "AV2 encoder requires libavm with its encoder, recompile with '-DWITH_AVM=ON'");
907 goto fail;
908#endif
909 }
910#if defined(WITH_LIBAVM)
911 else
912 {
913 ctx->iface = avm_codec_av2_dx();
914 if (!ctx->iface)
915 {
916 WLog_Print(ctx->log, WLOG_ERROR, "avm_codec_av2_dx() nullptr");
917 goto fail;
918 }
919 }
920#endif
921
922 return ctx;
923
924fail:
925 freerdp_av2_context_free(ctx);
926 return nullptr;
927}
928#else
929
930struct S_FREERDP_AV2_CONTEXT
931{
932 BOOL Compressor;
933 wLog* log;
934};
935
936BOOL freerdp_av2_context_set_option(WINPR_ATTR_UNUSED FREERDP_AV2_CONTEXT* av2,
937 WINPR_ATTR_UNUSED FREERDP_AV2_CONTEXT_OPTION option,
938 WINPR_ATTR_UNUSED UINT32 value)
939{
940 return FALSE;
941}
942
943UINT32 freerdp_av2_context_get_option(WINPR_ATTR_UNUSED FREERDP_AV2_CONTEXT* av2,
944 WINPR_ATTR_UNUSED FREERDP_AV2_CONTEXT_OPTION option)
945{
946 return 0;
947}
948
949INT32 freerdp_av2_compress(WINPR_ATTR_UNUSED FREERDP_AV2_CONTEXT* av2,
950 WINPR_ATTR_UNUSED const BYTE* pSrcData,
951 WINPR_ATTR_UNUSED DWORD SrcFormat, WINPR_ATTR_UNUSED UINT32 nSrcStep,
952 WINPR_ATTR_UNUSED UINT32 nSrcWidth, WINPR_ATTR_UNUSED UINT32 nSrcHeight,
953 WINPR_ATTR_UNUSED const RECTANGLE_16* regionRect,
954 WINPR_ATTR_UNUSED BYTE** ppDstData, WINPR_ATTR_UNUSED UINT32* pDstSize,
955 WINPR_ATTR_UNUSED RDPGFX_H264_METABLOCK* meta)
956{
957 WINPR_ASSERT(av2);
958 WLog_Print(av2->log, WLOG_ERROR,
959 "This build does not support AV2 codec. Recompile with '-DWITH_AVM=ON' or "
960 "'-DWITH_DAV2D=ON'");
961 return -1;
962}
963
964INT32 freerdp_av2_decompress(WINPR_ATTR_UNUSED FREERDP_AV2_CONTEXT* av2,
965 WINPR_ATTR_UNUSED const BYTE* pSrcData,
966 WINPR_ATTR_UNUSED UINT32 SrcSize, WINPR_ATTR_UNUSED BYTE* pDstData,
967 WINPR_ATTR_UNUSED DWORD DstFormat, WINPR_ATTR_UNUSED UINT32 nDstStep,
968 WINPR_ATTR_UNUSED UINT32 nDstWidth,
969 WINPR_ATTR_UNUSED UINT32 nDstHeight,
970 WINPR_ATTR_UNUSED const RECTANGLE_16* regionRects,
971 WINPR_ATTR_UNUSED UINT32 numRegionRect)
972{
973 WINPR_ASSERT(av2);
974 WLog_Print(av2->log, WLOG_ERROR,
975 "This build does not support AV2 codec. Recompile with '-DWITH_AVM=ON' or "
976 "'-DWITH_DAV2D=ON'");
977 return -1;
978}
979
980BOOL freerdp_av2_context_reset(WINPR_ATTR_UNUSED FREERDP_AV2_CONTEXT* av2,
981 WINPR_ATTR_UNUSED UINT32 width, WINPR_ATTR_UNUSED UINT32 height)
982{
983 WINPR_ASSERT(av2);
984 WLog_Print(av2->log, WLOG_WARN,
985 "This build does not support AV2 codec. Recompile with '-DWITH_AVM=ON' or "
986 "'-DWITH_DAV2D=ON'");
987 return FALSE;
988}
989
990void freerdp_av2_context_free(WINPR_ATTR_UNUSED FREERDP_AV2_CONTEXT* av2)
991{
992 free(av2);
993}
994
995FREERDP_AV2_CONTEXT* freerdp_av2_context_new(BOOL Compressor)
996{
997 FREERDP_AV2_CONTEXT* ctx = calloc(1, sizeof(FREERDP_AV2_CONTEXT));
998 if (!ctx)
999 return nullptr;
1000 ctx->Compressor = Compressor;
1001 ctx->log = WLog_Get(TAG);
1002 return ctx;
1003}
1004#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