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
25#define TAG FREERDP_TAG("codec.av1")
26
27#if defined(WITH_LIBAOM)
28#include <aom/aom.h>
29
30#include <aom/aom_decoder.h>
31#include <aom/aom_encoder.h>
32
33#include <aom/aom_image.h>
34#include <aom/aomcx.h>
35#include <aom/aomdx.h>
36
37#if defined(WITH_LIBYUV)
38#include <libyuv.h>
39#endif
40
41struct S_FREERDP_AV1_CONTEXT
42{
43 aom_codec_ctx_t ctx;
44 wLog* log;
45 bool encoder;
46 bool initialized;
47 aom_codec_enc_cfg_t ecfg;
48 aom_codec_dec_cfg_t dcfg;
49 aom_codec_pts_t framecount;
50 aom_enc_frame_flags_t eflags;
51 aom_codec_flags_t flags;
52 aom_codec_iface_t* iface;
53 BYTE* yuvdata[3];
54 UINT32 yuvWidth;
55 UINT32 yuvStride[3];
56 UINT32 yuvHeight;
57};
58
59WINPR_ATTR_NODISCARD
60static BOOL allocate_h264_metablock(UINT32 QP, RECTANGLE_16* rectangles,
61 RDPGFX_H264_METABLOCK* meta, size_t count)
62{
63 /* [MS-RDPEGFX] 2.2.4.4.2 RDPGFX_AVC420_QUANT_QUALITY */
64 if (!meta || (QP > UINT8_MAX))
65 {
66 free(rectangles);
67 return FALSE;
68 }
69
70 meta->regionRects = rectangles;
71 if (count == 0)
72 return TRUE;
73
74 if (count > UINT32_MAX)
75 return FALSE;
76
77 meta->quantQualityVals = calloc(count, sizeof(RDPGFX_H264_QUANT_QUALITY));
78
79 if (!meta->quantQualityVals || !meta->regionRects)
80 return FALSE;
81 meta->numRegionRects = (UINT32)count;
82 for (size_t x = 0; x < count; x++)
83 {
84 RDPGFX_H264_QUANT_QUALITY* cur = &meta->quantQualityVals[x];
85 cur->qp = (UINT8)QP;
86
87 /* qpVal bit 6 and 7 are flags, so mask them out here.
88 * qualityVal is [0-100] so 100 - qpVal [0-64] is always in range */
89 cur->qualityVal = 100 - (QP & 0x3F);
90 }
91 return TRUE;
92}
93
94BOOL freerdp_av1_context_set_option(FREERDP_AV1_CONTEXT* av1, FREERDP_AV1_CONTEXT_OPTION option,
95 UINT32 value)
96{
97
98 switch (option)
99 {
100 case FREERDP_AV1_CONTEXT_OPTION_PROFILE:
101 if (!av1->encoder)
102 return FALSE;
103
104 av1->ecfg.g_profile = value;
105 break;
106
107 case FREERDP_AV1_CONTEXT_OPTION_RATECONTROL:
108 if (!av1->encoder)
109 return FALSE;
110 switch (value)
111 {
112 case FREERDP_AV1_VBR:
113 case FREERDP_AV1_CBR:
114 case FREERDP_AV1_CQ:
115 case FREERDP_AV1_Q:
116 break;
117 default:
118 WLog_Print(av1->log, WLOG_WARN,
119 "Unknown FREERDP_AV1_CONTEXT_OPTION_RATECONTROL value [0x%08" PRIx32
120 "]",
121 value);
122 return FALSE;
123 }
124 av1->ecfg.rc_end_usage = value;
125 break;
126 case FREERDP_AV1_CONTEXT_OPTION_BITRATE:
127 if (!av1->encoder)
128 return FALSE;
129 av1->ecfg.rc_target_bitrate = value;
130 break;
131 case FREERDP_AV1_CONTEXT_OPTION_USAGETYPE:
132 if (!av1->encoder)
133 return FALSE;
134 av1->ecfg.g_usage = value;
135 break;
136 default:
137 WLog_Print(av1->log, WLOG_ERROR, "Unknown FREERDP_AV1_CONTEXT_OPTION[0x%08" PRIx32 "]",
138 option);
139 return TRUE;
140 }
141 return freerdp_av1_context_reset(av1, av1->ecfg.g_w, av1->ecfg.g_h);
142}
143
144UINT32 freerdp_av1_context_get_option(FREERDP_AV1_CONTEXT* av1, FREERDP_AV1_CONTEXT_OPTION option)
145{
146 switch (option)
147 {
148 case FREERDP_AV1_CONTEXT_OPTION_PROFILE:
149 if (!av1->encoder)
150 return 0;
151 return av1->ecfg.g_profile;
152 case FREERDP_AV1_CONTEXT_OPTION_RATECONTROL:
153 if (!av1->encoder)
154 return 0;
155 return av1->ecfg.rc_end_usage;
156 case FREERDP_AV1_CONTEXT_OPTION_BITRATE:
157 if (!av1->encoder)
158 return 0;
159 return av1->ecfg.rc_target_bitrate;
160 case FREERDP_AV1_CONTEXT_OPTION_USAGETYPE:
161 if (!av1->encoder)
162 return 0;
163 return av1->ecfg.g_usage;
164 default:
165 WLog_Print(av1->log, WLOG_ERROR, "Unknown FREERDP_AV1_CONTEXT_OPTION[0x%08" PRIx32 "]",
166 option);
167 return 0;
168 }
169}
170
171INT32 freerdp_av1_compress(FREERDP_AV1_CONTEXT* av1, const BYTE* pSrcData, DWORD SrcFormat,
172 UINT32 nSrcStep, UINT32 nSrcWidth, UINT32 nSrcHeight,
173 const RECTANGLE_16* regionRect, BYTE** ppDstData, UINT32* pDstSize,
175{
176 *ppDstData = nullptr;
177 *pDstSize = 0;
178
179 if (!av1->encoder)
180 {
181 WLog_Print(av1->log, WLOG_ERROR, "av1->encoder: %d", av1->encoder);
182 return -1;
183 }
184
185 primitives_t* primitives = primitives_get();
186 if (!primitives)
187 {
188 WLog_Print(av1->log, WLOG_ERROR, "primitives_get(): nullptr");
189 return -1;
190 }
191
192 if (av1->yuvWidth != nSrcWidth)
193 {
194 WLog_Print(av1->log, WLOG_ERROR, "av1->yuvWidth[%" PRIu32 "] != nSrcWidth[%" PRIu32 "]",
195 av1->yuvWidth, nSrcWidth);
196 return -1;
197 }
198
199 if (av1->yuvHeight != nSrcHeight)
200 {
201 WLog_Print(av1->log, WLOG_ERROR, "av1->yuvHeight[%" PRIu32 "] != nSrcHeight[%" PRIu32 "]",
202 av1->yuvHeight, nSrcHeight);
203 return -1;
204 }
205
206 const prim_size_t roi = { .width = nSrcWidth, .height = nSrcHeight };
207
208 aom_image_t buffer = { 0 };
209
210 aom_image_t* img = &buffer;
211#if defined(WITH_LIBYUV)
212 int rec = -1;
213 switch (av1->ecfg.g_profile)
214 {
215 case 0:
216 img->fmt = AOM_IMG_FMT_I420;
217 img->x_chroma_shift = 1;
218 img->y_chroma_shift = 1;
219 rec = ARGBToI420(pSrcData, nSrcStep, av1->yuvdata[0], av1->yuvStride[0],
220 av1->yuvdata[1], av1->yuvStride[1], av1->yuvdata[2],
221 WINPR_ASSERTING_INT_CAST(int, av1->yuvStride[2]),
222 WINPR_ASSERTING_INT_CAST(int, roi.width),
223 WINPR_ASSERTING_INT_CAST(int, roi.height));
224 break;
225 case 1:
226 img->fmt = AOM_IMG_FMT_I444;
227 rec = ARGBToI444(pSrcData, nSrcStep, av1->yuvdata[0], av1->yuvStride[0],
228 av1->yuvdata[1], av1->yuvStride[1], av1->yuvdata[2],
229 WINPR_ASSERTING_INT_CAST(int, av1->yuvStride[2]),
230 WINPR_ASSERTING_INT_CAST(int, roi.width),
231 WINPR_ASSERTING_INT_CAST(int, roi.height));
232 break;
233 default:
234 WLog_Print(av1->log, WLOG_ERROR, "Unsupoorted AV1 profile %" PRIu32,
235 av1->ecfg.g_profile);
236 return -1;
237 }
238 if (rec != 0)
239 {
240 WLog_Print(av1->log, WLOG_ERROR, "ARGBToI444(): %d", rec);
241 return -1;
242 }
243#else
244 pstatus_t rec = -1;
245 switch (av1->ecfg.g_profile)
246 {
247 case 0:
248 img->fmt = AOM_IMG_FMT_I420;
249 img->x_chroma_shift = 1;
250 img->y_chroma_shift = 1;
251 rec = primitives->RGBToYUV420_8u_P3AC4R(pSrcData, SrcFormat, nSrcStep, av1->yuvdata,
252 av1->yuvStride, &roi);
253 break;
254 case 1:
255 img->fmt = AOM_IMG_FMT_I444;
256 rec = primitives->RGBToI444_8u(pSrcData, SrcFormat, nSrcStep, av1->yuvdata,
257 av1->yuvStride, &roi);
258 break;
259 default:
260 WLog_Print(av1->log, WLOG_ERROR, "Unsupoorted AV1 profile %" PRIu32,
261 av1->ecfg.g_profile);
262 return -1;
263 }
264 if (rec != 0)
265 {
266 WLog_Print(av1->log, WLOG_ERROR, "primitives->RGBToI444_8u(): %d", rec);
267 return -1;
268 }
269#endif
270 img->bit_depth = 8;
271 img->d_w = img->r_w = img->w = roi.width;
272 img->d_h = img->r_h = img->h = roi.height;
273 img->stride[0] = WINPR_ASSERTING_INT_CAST(int, av1->yuvStride[0]);
274 img->stride[1] = WINPR_ASSERTING_INT_CAST(int, av1->yuvStride[1]);
275 img->stride[2] = WINPR_ASSERTING_INT_CAST(int, av1->yuvStride[2]);
276 img->planes[0] = av1->yuvdata[0];
277 img->planes[1] = av1->yuvdata[1];
278 img->planes[2] = av1->yuvdata[2];
279
280 const aom_codec_err_t rc = aom_codec_encode(&av1->ctx, img, ++av1->framecount, 1, av1->eflags);
281 aom_img_free(img);
282
283 if (rc != AOM_CODEC_OK)
284 {
285 WLog_Print(av1->log, WLOG_WARN, "aom_codec_encode: %s", aom_codec_err_to_string(rc));
286 return -1;
287 }
288
289 aom_codec_iter_t iter = nullptr;
290 const aom_codec_cx_pkt_t* pkt = nullptr;
291 while ((pkt = aom_codec_get_cx_data(&av1->ctx, &iter)) != nullptr)
292 {
293 if (pkt->kind != AOM_CODEC_CX_FRAME_PKT)
294 continue;
295
296 *ppDstData = pkt->data.frame.buf;
297 *pDstSize = pkt->data.frame.sz;
298 };
299 if (*pDstSize == 0)
300 return 0;
301
302 RECTANGLE_16* rect = calloc(1, sizeof(RECTANGLE_16));
303 if (rect)
304 {
305 rect->right = nSrcWidth;
306 rect->bottom = nSrcHeight;
307 }
308 return allocate_h264_metablock(10, rect, meta, 1);
309}
310
311INT32 freerdp_av1_decompress(FREERDP_AV1_CONTEXT* av1, const BYTE* pSrcData, UINT32 SrcSize,
312 BYTE* pDstData, DWORD DstFormat, UINT32 nDstStep, UINT32 nDstWidth,
313 UINT32 nDstHeight, const RECTANGLE_16* regionRects,
314 UINT32 numRegionRect)
315{
316 WINPR_ASSERT(av1);
317 if (av1->encoder)
318 {
319 WLog_Print(av1->log, WLOG_ERROR, "av1->encoder: %d", av1->encoder);
320 return -1;
321 }
322
323 const aom_codec_err_t rc = aom_codec_decode(&av1->ctx, pSrcData, SrcSize, nullptr);
324 if (rc != AOM_CODEC_OK)
325 {
326 WLog_Print(av1->log, WLOG_WARN, "aom_codec_decode: %s", aom_codec_err_to_string(rc));
327 return -1;
328 }
329
330 aom_image_t* img = nullptr;
331 aom_codec_iter_t iter = nullptr;
332 while ((img = aom_codec_get_frame(&av1->ctx, &iter)) != nullptr)
333 {
334 const prim_size_t roi = { .width = nDstWidth, .height = nDstHeight };
335
336#if defined(WITH_LIBYUV)
337 int rec = -1;
338
339 switch (img->fmt)
340 {
341 case AOM_IMG_FMT_I420:
342 rec = J420ToARGB(img->planes[0], img->stride[0], img->planes[1], img->stride[1],
343 img->planes[2], img->stride[2], pDstData, nDstStep, nDstWidth,
344 nDstHeight);
345 break;
346 case AOM_IMG_FMT_I444:
347 rec = J444ToARGB(img->planes[0], img->stride[0], img->planes[1], img->stride[1],
348 img->planes[2], img->stride[2], pDstData, nDstStep, nDstWidth,
349 nDstHeight);
350 break;
351 default:
352 WLog_Print(av1->log, WLOG_ERROR, "img->fmt %d not supported", img->fmt);
353 return -1;
354 }
355 if (rec != 0)
356 {
357 WLog_Print(av1->log, WLOG_ERROR, "I444ToABGR() %d", rec);
358 return -1;
359 }
360#else
361 const BYTE* pSrc[] = { img->planes[0], img->planes[1], img->planes[2] };
362 const UINT32 strides[] = { img->stride[0], img->stride[1], img->stride[2] };
363
364 primitives_t* primitives = primitives_get();
365 if (!primitives)
366 {
367 WLog_Print(av1->log, WLOG_ERROR, "primitives_get(): nullptr");
368 return FALSE;
369 }
370
371 pstatus_t rec = -1;
372 switch (img->fmt)
373 {
374 case AOM_IMG_FMT_I420:
375 rec = primitives->YUV420ToRGB_8u_P3AC4R(pSrc, strides, pDstData, nDstStep,
376 DstFormat, &roi);
377 break;
378 case AOM_IMG_FMT_I444:
379 rec = primitives->YUV444ToRGB_8u_P3AC4R(pSrc, strides, pDstData, nDstStep,
380 DstFormat, &roi);
381 break;
382 default:
383 WLog_Print(av1->log, WLOG_ERROR, "img->fmt %d not supported", img->fmt);
384 return -1;
385 }
386 if (rec != 0)
387 {
388 WLog_Print(av1->log, WLOG_ERROR, "I444ToABGR() %d", rec);
389 return -1;
390 }
391#endif
392 }
393
394 return TRUE;
395}
396
397BOOL freerdp_av1_context_reset(FREERDP_AV1_CONTEXT* av1, UINT32 width, UINT32 height)
398{
399 if (av1->encoder)
400 {
401 av1->ecfg.g_w = width;
402 av1->ecfg.g_h = height;
403
404 if (av1->initialized)
405 {
406 const aom_codec_err_t rc = aom_codec_destroy(&av1->ctx);
407 av1->initialized = false;
408 if (rc != AOM_CODEC_OK)
409 {
410 WLog_Print(av1->log, WLOG_WARN, "aom_codec_destroy: %s",
411 aom_codec_err_to_string(rc));
412 return FALSE;
413 }
414 }
415
416 const aom_codec_err_t rc =
417 aom_codec_enc_init(&av1->ctx, av1->iface, &av1->ecfg, av1->flags);
418 if (rc != AOM_CODEC_OK)
419 {
420 WLog_Print(av1->log, WLOG_WARN, "aom_codec_enc_init: %s", aom_codec_err_to_string(rc));
421 return FALSE;
422 }
423
424 av1->framecount = 0;
425 av1->eflags = 0; // AOM_EFLAG_FORCE_KF;
426 }
427 else
428 {
429 av1->dcfg.w = width;
430 av1->dcfg.h = height;
431 av1->dcfg.allow_lowbitdepth = 1;
432 const aom_codec_err_t rc =
433 aom_codec_dec_init(&av1->ctx, av1->iface, &av1->dcfg, av1->flags);
434 if (rc != AOM_CODEC_OK)
435 {
436 WLog_Print(av1->log, WLOG_WARN, "aom_codec_dec_init: %s", aom_codec_err_to_string(rc));
437 return FALSE;
438 }
439 }
440 av1->initialized = true;
441
442 av1->yuvWidth = width;
443 av1->yuvStride[0] = width;
444
445 const size_t pad = av1->yuvStride[0] % 16;
446 if (pad != 0)
447 av1->yuvStride[0] += 16 - pad;
448 av1->yuvStride[0] *= 3;
449 av1->yuvStride[1] = width;
450 av1->yuvStride[2] = width;
451
452 av1->yuvHeight = height;
453 winpr_aligned_free(av1->yuvdata[0]);
454 winpr_aligned_free(av1->yuvdata[1]);
455 winpr_aligned_free(av1->yuvdata[2]);
456 av1->yuvdata[0] = winpr_aligned_malloc(1ull * av1->yuvStride[0] * av1->yuvHeight, 64);
457 av1->yuvdata[1] = winpr_aligned_malloc(1ull * av1->yuvStride[1] * av1->yuvHeight, 64);
458 av1->yuvdata[2] = winpr_aligned_malloc(1ull * av1->yuvStride[2] * av1->yuvHeight, 64);
459 return av1->yuvdata[0] != nullptr;
460}
461
462void freerdp_av1_context_free(FREERDP_AV1_CONTEXT* av1)
463{
464 if (!av1)
465 return;
466
467 if (av1->initialized)
468 {
469 const aom_codec_err_t rc = aom_codec_destroy(&av1->ctx);
470 if (rc != AOM_CODEC_OK)
471 {
472 WLog_Print(av1->log, WLOG_WARN, "aom_codec_destroy: %s", aom_codec_err_to_string(rc));
473 }
474 }
475 winpr_aligned_free(av1->yuvdata[0]);
476 winpr_aligned_free(av1->yuvdata[1]);
477 winpr_aligned_free(av1->yuvdata[2]);
478 free(av1);
479}
480
481FREERDP_AV1_CONTEXT* freerdp_av1_context_new(BOOL Compressor)
482{
483 FREERDP_AV1_CONTEXT* ctx = calloc(1, sizeof(FREERDP_AV1_CONTEXT));
484 if (!ctx)
485 return nullptr;
486 ctx->encoder = Compressor;
487 ctx->log = WLog_Get(TAG);
488 if (!ctx->log)
489 goto fail;
490
491 if (Compressor)
492 {
493 ctx->iface = aom_codec_av1_cx();
494 if (!ctx->iface)
495 {
496 WLog_Print(ctx->log, WLOG_ERROR, "aom_codec_av1_cx() nullptr");
497 goto fail;
498 }
499
500 const aom_codec_err_t rc =
501 aom_codec_enc_config_default(ctx->iface, &ctx->ecfg, AOM_USAGE_REALTIME);
502 if (rc != AOM_CODEC_OK)
503 {
504 WLog_Print(ctx->log, WLOG_ERROR, "aom_codec_enc_config_default() %s",
505 aom_codec_err_to_string(rc));
506 goto fail;
507 }
508 }
509 else
510 {
511 ctx->iface = aom_codec_av1_dx();
512 if (!ctx->iface)
513 {
514 WLog_Print(ctx->log, WLOG_ERROR, "aom_codec_av1_dx() nullptr");
515 goto fail;
516 }
517 }
518
519 return ctx;
520
521fail:
522 freerdp_av1_context_free(ctx);
523 return nullptr;
524}
525#else
526
527struct S_FREERDP_AV1_CONTEXT
528{
529 BOOL Compressor;
530 wLog* log;
531};
532
533BOOL freerdp_av1_context_set_option(WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT* av1,
534 WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT_OPTION option,
535 WINPR_ATTR_UNUSED UINT32 value)
536{
537 return FALSE;
538}
539
540UINT32 freerdp_av1_context_get_option(WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT* av1,
541 WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT_OPTION option)
542{
543 return 0;
544}
545
546INT32 freerdp_av1_compress(WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT* av1,
547 WINPR_ATTR_UNUSED const BYTE* pSrcData,
548 WINPR_ATTR_UNUSED DWORD SrcFormat, WINPR_ATTR_UNUSED UINT32 nSrcStep,
549 WINPR_ATTR_UNUSED UINT32 nSrcWidth, WINPR_ATTR_UNUSED UINT32 nSrcHeight,
550 WINPR_ATTR_UNUSED const RECTANGLE_16* regionRect,
551 WINPR_ATTR_UNUSED BYTE** ppDstData, WINPR_ATTR_UNUSED UINT32* pDstSize,
552 WINPR_ATTR_UNUSED RDPGFX_H264_METABLOCK* meta)
553{
554 WINPR_ASSERT(av1);
555 WLog_Print(av1->log, WLOG_ERROR,
556 "This build does not support AV1 codec. Recompile with '-DWITH_AOM=ON'");
557 return -1;
558}
559
560INT32 freerdp_av1_decompress(WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT* av1,
561 WINPR_ATTR_UNUSED const BYTE* pSrcData,
562 WINPR_ATTR_UNUSED UINT32 SrcSize, WINPR_ATTR_UNUSED BYTE* pDstData,
563 WINPR_ATTR_UNUSED DWORD DstFormat, WINPR_ATTR_UNUSED UINT32 nDstStep,
564 WINPR_ATTR_UNUSED UINT32 nDstWidth,
565 WINPR_ATTR_UNUSED UINT32 nDstHeight,
566 WINPR_ATTR_UNUSED const RECTANGLE_16* regionRects,
567 WINPR_ATTR_UNUSED UINT32 numRegionRect)
568{
569 WINPR_ASSERT(av1);
570 WLog_Print(av1->log, WLOG_ERROR,
571 "This build does not support AV1 codec. Recompile with '-DWITH_AOM=ON'");
572 return -1;
573}
574
575BOOL freerdp_av1_context_reset(WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT* av1,
576 WINPR_ATTR_UNUSED UINT32 width, WINPR_ATTR_UNUSED UINT32 height)
577{
578 WINPR_ASSERT(av1);
579 WLog_Print(av1->log, WLOG_WARN,
580 "This build does not support AV1 codec. Recompile with '-DWITH_AOM=ON'");
581 return FALSE;
582}
583
584void freerdp_av1_context_free(WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT* av1)
585{
586 free(av1);
587}
588
589FREERDP_AV1_CONTEXT* freerdp_av1_context_new(BOOL Compressor)
590{
591 FREERDP_AV1_CONTEXT* ctx = calloc(1, sizeof(FREERDP_AV1_CONTEXT));
592 if (!ctx)
593 return nullptr;
594 ctx->Compressor = Compressor;
595 ctx->log = WLog_Get(TAG);
596 return ctx;
597}
598#endif
WINPR_ATTR_NODISCARD fn_RGBToYUV444_8u_P3AC4R_t RGBToI444_8u
Definition primitives.h:305