21#include <freerdp/codec/av1.h>
22#include <freerdp/primitives.h>
23#include <freerdp/log.h>
24#include <freerdp/codec/region.h>
26#define TAG FREERDP_TAG("codec.av1")
28#if defined(WITH_LIBAOM)
31#include <aom/aom_decoder.h>
32#include <aom/aom_encoder.h>
34#include <aom/aom_image.h>
38#if defined(WITH_LIBYUV)
42struct S_FREERDP_AV1_CONTEXT
48 aom_codec_enc_cfg_t ecfg;
49 aom_codec_dec_cfg_t dcfg;
50 aom_codec_pts_t framecount;
51 aom_enc_frame_flags_t eflags;
52 aom_codec_flags_t flags;
53 aom_codec_iface_t* iface;
61static BOOL allocate_h264_metablock(UINT32 QP,
RECTANGLE_16* rectangles,
65 if (!meta || (QP > UINT8_MAX))
71 meta->regionRects = rectangles;
75 if (count > UINT32_MAX)
80 if (!meta->quantQualityVals || !meta->regionRects)
82 meta->numRegionRects = (UINT32)count;
83 for (
size_t x = 0; x < count; x++)
90 cur->qualityVal = 100 - (QP & 0x3F);
95BOOL freerdp_av1_context_set_option(FREERDP_AV1_CONTEXT* av1, FREERDP_AV1_CONTEXT_OPTION option,
101 case FREERDP_AV1_CONTEXT_OPTION_PROFILE:
105 av1->ecfg.g_profile = value;
108 case FREERDP_AV1_CONTEXT_OPTION_RATECONTROL:
113 case FREERDP_AV1_VBR:
114 case FREERDP_AV1_CBR:
119 WLog_Print(av1->log, WLOG_WARN,
120 "Unknown FREERDP_AV1_CONTEXT_OPTION_RATECONTROL value [0x%08" PRIx32
125 av1->ecfg.rc_end_usage = value;
127 case FREERDP_AV1_CONTEXT_OPTION_BITRATE:
130 av1->ecfg.rc_target_bitrate = value;
132 case FREERDP_AV1_CONTEXT_OPTION_USAGETYPE:
135 av1->ecfg.g_usage = value;
138 WLog_Print(av1->log, WLOG_ERROR,
"Unknown FREERDP_AV1_CONTEXT_OPTION[0x%08" PRIx32
"]",
142 return freerdp_av1_context_reset(av1, av1->ecfg.g_w, av1->ecfg.g_h);
145UINT32 freerdp_av1_context_get_option(FREERDP_AV1_CONTEXT* av1, FREERDP_AV1_CONTEXT_OPTION option)
149 case FREERDP_AV1_CONTEXT_OPTION_PROFILE:
152 return av1->ecfg.g_profile;
153 case FREERDP_AV1_CONTEXT_OPTION_RATECONTROL:
156 return av1->ecfg.rc_end_usage;
157 case FREERDP_AV1_CONTEXT_OPTION_BITRATE:
160 return av1->ecfg.rc_target_bitrate;
161 case FREERDP_AV1_CONTEXT_OPTION_USAGETYPE:
164 return av1->ecfg.g_usage;
166 WLog_Print(av1->log, WLOG_ERROR,
"Unknown FREERDP_AV1_CONTEXT_OPTION[0x%08" PRIx32
"]",
172INT32 freerdp_av1_compress(FREERDP_AV1_CONTEXT* av1,
const BYTE* pSrcData, DWORD SrcFormat,
173 UINT32 nSrcStep, UINT32 nSrcWidth, UINT32 nSrcHeight,
174 const RECTANGLE_16* regionRect, BYTE** ppDstData, UINT32* pDstSize,
177 *ppDstData =
nullptr;
182 WLog_Print(av1->log, WLOG_ERROR,
"av1->encoder: %d", av1->encoder);
189 WLog_Print(av1->log, WLOG_ERROR,
"primitives_get(): nullptr");
193 if (av1->yuvWidth != nSrcWidth)
195 WLog_Print(av1->log, WLOG_ERROR,
"av1->yuvWidth[%" PRIu32
"] != nSrcWidth[%" PRIu32
"]",
196 av1->yuvWidth, nSrcWidth);
200 if (av1->yuvHeight != nSrcHeight)
202 WLog_Print(av1->log, WLOG_ERROR,
"av1->yuvHeight[%" PRIu32
"] != nSrcHeight[%" PRIu32
"]",
203 av1->yuvHeight, nSrcHeight);
207 const prim_size_t roi = { .width = nSrcWidth, .height = nSrcHeight };
209 aom_image_t buffer = WINPR_C_ARRAY_INIT;
211 aom_image_t* img = &buffer;
212#if defined(WITH_LIBYUV)
214 switch (av1->ecfg.g_profile)
217 img->fmt = AOM_IMG_FMT_I420;
218 img->x_chroma_shift = 1;
219 img->y_chroma_shift = 1;
220 rec = ARGBToI420(pSrcData, nSrcStep, av1->yuvdata[0], av1->yuvStride[0],
221 av1->yuvdata[1], av1->yuvStride[1], av1->yuvdata[2],
222 WINPR_ASSERTING_INT_CAST(
int, av1->yuvStride[2]),
223 WINPR_ASSERTING_INT_CAST(
int, roi.width),
224 WINPR_ASSERTING_INT_CAST(
int, roi.height));
227 img->fmt = AOM_IMG_FMT_I444;
228 rec = ARGBToI444(pSrcData, nSrcStep, av1->yuvdata[0], av1->yuvStride[0],
229 av1->yuvdata[1], av1->yuvStride[1], av1->yuvdata[2],
230 WINPR_ASSERTING_INT_CAST(
int, av1->yuvStride[2]),
231 WINPR_ASSERTING_INT_CAST(
int, roi.width),
232 WINPR_ASSERTING_INT_CAST(
int, roi.height));
235 WLog_Print(av1->log, WLOG_ERROR,
"Unsupoorted AV1 profile %" PRIu32,
236 av1->ecfg.g_profile);
241 WLog_Print(av1->log, WLOG_ERROR,
"ARGBToI444(): %d", rec);
246 switch (av1->ecfg.g_profile)
249 img->fmt = AOM_IMG_FMT_I420;
250 img->x_chroma_shift = 1;
251 img->y_chroma_shift = 1;
252 rec = primitives->RGBToYUV420_8u_P3AC4R(pSrcData, SrcFormat, nSrcStep, av1->yuvdata,
253 av1->yuvStride, &roi);
256 img->fmt = AOM_IMG_FMT_I444;
257 rec = primitives->
RGBToI444_8u(pSrcData, SrcFormat, nSrcStep, av1->yuvdata,
258 av1->yuvStride, &roi);
261 WLog_Print(av1->log, WLOG_ERROR,
"Unsupoorted AV1 profile %" PRIu32,
262 av1->ecfg.g_profile);
267 WLog_Print(av1->log, WLOG_ERROR,
"primitives->RGBToI444_8u(): %d", rec);
272 img->d_w = img->r_w = img->w = roi.width;
273 img->d_h = img->r_h = img->h = roi.height;
274 img->stride[0] = WINPR_ASSERTING_INT_CAST(
int, av1->yuvStride[0]);
275 img->stride[1] = WINPR_ASSERTING_INT_CAST(
int, av1->yuvStride[1]);
276 img->stride[2] = WINPR_ASSERTING_INT_CAST(
int, av1->yuvStride[2]);
277 img->planes[0] = av1->yuvdata[0];
278 img->planes[1] = av1->yuvdata[1];
279 img->planes[2] = av1->yuvdata[2];
281 const aom_codec_err_t rc = aom_codec_encode(&av1->ctx, img, ++av1->framecount, 1, av1->eflags);
284 if (rc != AOM_CODEC_OK)
286 WLog_Print(av1->log, WLOG_WARN,
"aom_codec_encode: %s", aom_codec_err_to_string(rc));
290 aom_codec_iter_t iter =
nullptr;
291 const aom_codec_cx_pkt_t* pkt =
nullptr;
292 while ((pkt = aom_codec_get_cx_data(&av1->ctx, &iter)) !=
nullptr)
294 if (pkt->kind != AOM_CODEC_CX_FRAME_PKT)
297 *ppDstData = pkt->data.frame.buf;
298 *pDstSize = pkt->data.frame.sz;
306 rect->right = nSrcWidth;
307 rect->bottom = nSrcHeight;
309 return allocate_h264_metablock(10, rect, meta, 1);
312static BOOL isRectValid(UINT32 width, UINT32 height,
const RECTANGLE_16* rect)
315 if (rect->left > width)
317 if (rect->right > width)
319 if (rect->left >= rect->right)
321 if (rect->top > height)
323 if (rect->bottom > height)
325 if (rect->top >= rect->bottom)
330static BOOL areRectsValid(wLog* log, UINT32 width, UINT32 height,
const RECTANGLE_16* rects,
333 WINPR_ASSERT(rects || (count == 0));
334 for (
size_t x = 0; x < count; x++)
337 if (!isRectValid(width, height, rect))
339 char buffer[64] = WINPR_C_ARRAY_INIT;
340 WLog_Print(log, WLOG_WARN,
341 "Rectangle %" PRIuz
" %s outside of bounding frame %" PRIu32
"x%" PRIu32, x,
342 rectangle_to_string(rect, buffer,
sizeof(buffer)), width, height);
349INT32 freerdp_av1_decompress(FREERDP_AV1_CONTEXT* av1,
const BYTE* pSrcData, UINT32 SrcSize,
350 BYTE* pDstData, DWORD DstFormat, UINT32 nDstStep, UINT32 nDstWidth,
352 UINT32 numRegionRect)
357 WLog_Print(av1->log, WLOG_ERROR,
"av1->encoder: %d", av1->encoder);
361 if (!areRectsValid(av1->log, nDstWidth, nDstHeight, regionRects, numRegionRect))
364 const aom_codec_err_t rc = aom_codec_decode(&av1->ctx, pSrcData, SrcSize,
nullptr);
365 if (rc != AOM_CODEC_OK)
367 WLog_Print(av1->log, WLOG_WARN,
"aom_codec_decode: %s", aom_codec_err_to_string(rc));
371 aom_image_t* img =
nullptr;
372 aom_codec_iter_t iter =
nullptr;
373 while ((img = aom_codec_get_frame(&av1->ctx, &iter)) !=
nullptr)
378 const UINT32 nWidth = (img->d_w < nDstWidth) ? img->d_w : nDstWidth;
379 const UINT32 nHeight = (img->d_h < nDstHeight) ? img->d_h : nDstHeight;
380 const prim_size_t roi = { .width = nWidth, .height = nHeight };
382 if (!areRectsValid(av1->log, roi.width, roi.height, regionRects, numRegionRect))
385#if defined(WITH_LIBYUV)
390 case AOM_IMG_FMT_I420:
392 J420ToARGB(img->planes[0], img->stride[0], img->planes[1], img->stride[1],
393 img->planes[2], img->stride[2], pDstData, nDstStep, nWidth, nHeight);
395 case AOM_IMG_FMT_I444:
397 J444ToARGB(img->planes[0], img->stride[0], img->planes[1], img->stride[1],
398 img->planes[2], img->stride[2], pDstData, nDstStep, nWidth, nHeight);
401 WLog_Print(av1->log, WLOG_ERROR,
"img->fmt %d not supported", img->fmt);
406 WLog_Print(av1->log, WLOG_ERROR,
"I444ToABGR() %d", rec);
410 const BYTE* pSrc[] = { img->planes[0], img->planes[1], img->planes[2] };
411 const UINT32 strides[] = { img->stride[0], img->stride[1], img->stride[2] };
416 WLog_Print(av1->log, WLOG_ERROR,
"primitives_get(): nullptr");
423 case AOM_IMG_FMT_I420:
424 rec = primitives->YUV420ToRGB_8u_P3AC4R(pSrc, strides, pDstData, nDstStep,
427 case AOM_IMG_FMT_I444:
428 rec = primitives->YUV444ToRGB_8u_P3AC4R(pSrc, strides, pDstData, nDstStep,
432 WLog_Print(av1->log, WLOG_ERROR,
"img->fmt %d not supported", img->fmt);
437 WLog_Print(av1->log, WLOG_ERROR,
"I444ToABGR() %d", rec);
446BOOL freerdp_av1_context_reset(FREERDP_AV1_CONTEXT* av1, UINT32 width, UINT32 height)
450 av1->ecfg.g_w = width;
451 av1->ecfg.g_h = height;
453 if (av1->initialized)
455 const aom_codec_err_t rc = aom_codec_destroy(&av1->ctx);
456 av1->initialized =
false;
457 if (rc != AOM_CODEC_OK)
459 WLog_Print(av1->log, WLOG_WARN,
"aom_codec_destroy: %s",
460 aom_codec_err_to_string(rc));
465 const aom_codec_err_t rc =
466 aom_codec_enc_init(&av1->ctx, av1->iface, &av1->ecfg, av1->flags);
467 if (rc != AOM_CODEC_OK)
469 WLog_Print(av1->log, WLOG_WARN,
"aom_codec_enc_init: %s", aom_codec_err_to_string(rc));
479 av1->dcfg.h = height;
480 av1->dcfg.allow_lowbitdepth = 1;
481 const aom_codec_err_t rc =
482 aom_codec_dec_init(&av1->ctx, av1->iface, &av1->dcfg, av1->flags);
483 if (rc != AOM_CODEC_OK)
485 WLog_Print(av1->log, WLOG_WARN,
"aom_codec_dec_init: %s", aom_codec_err_to_string(rc));
489 av1->initialized =
true;
491 av1->yuvWidth = width;
492 av1->yuvStride[0] = width;
494 const size_t pad = av1->yuvStride[0] % 16;
496 av1->yuvStride[0] += 16 - pad;
497 av1->yuvStride[0] *= 3;
498 av1->yuvStride[1] = width;
499 av1->yuvStride[2] = width;
501 av1->yuvHeight = height;
502 winpr_aligned_free(av1->yuvdata[0]);
503 winpr_aligned_free(av1->yuvdata[1]);
504 winpr_aligned_free(av1->yuvdata[2]);
505 av1->yuvdata[0] = winpr_aligned_malloc(1ull * av1->yuvStride[0] * av1->yuvHeight, 64);
506 av1->yuvdata[1] = winpr_aligned_malloc(1ull * av1->yuvStride[1] * av1->yuvHeight, 64);
507 av1->yuvdata[2] = winpr_aligned_malloc(1ull * av1->yuvStride[2] * av1->yuvHeight, 64);
508 return av1->yuvdata[0] !=
nullptr;
511void freerdp_av1_context_free(FREERDP_AV1_CONTEXT* av1)
516 if (av1->initialized)
518 const aom_codec_err_t rc = aom_codec_destroy(&av1->ctx);
519 if (rc != AOM_CODEC_OK)
521 WLog_Print(av1->log, WLOG_WARN,
"aom_codec_destroy: %s", aom_codec_err_to_string(rc));
524 winpr_aligned_free(av1->yuvdata[0]);
525 winpr_aligned_free(av1->yuvdata[1]);
526 winpr_aligned_free(av1->yuvdata[2]);
530FREERDP_AV1_CONTEXT* freerdp_av1_context_new(BOOL Compressor)
532 FREERDP_AV1_CONTEXT* ctx = calloc(1,
sizeof(FREERDP_AV1_CONTEXT));
535 ctx->encoder = Compressor;
536 ctx->log = WLog_Get(TAG);
542 ctx->iface = aom_codec_av1_cx();
545 WLog_Print(ctx->log, WLOG_ERROR,
"aom_codec_av1_cx() nullptr");
549 const aom_codec_err_t rc =
550 aom_codec_enc_config_default(ctx->iface, &ctx->ecfg, AOM_USAGE_REALTIME);
551 if (rc != AOM_CODEC_OK)
553 WLog_Print(ctx->log, WLOG_ERROR,
"aom_codec_enc_config_default() %s",
554 aom_codec_err_to_string(rc));
560 ctx->iface = aom_codec_av1_dx();
563 WLog_Print(ctx->log, WLOG_ERROR,
"aom_codec_av1_dx() nullptr");
571 freerdp_av1_context_free(ctx);
576struct S_FREERDP_AV1_CONTEXT
582BOOL freerdp_av1_context_set_option(WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT* av1,
583 WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT_OPTION option,
584 WINPR_ATTR_UNUSED UINT32 value)
589UINT32 freerdp_av1_context_get_option(WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT* av1,
590 WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT_OPTION option)
595INT32 freerdp_av1_compress(WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT* av1,
596 WINPR_ATTR_UNUSED
const BYTE* pSrcData,
597 WINPR_ATTR_UNUSED DWORD SrcFormat, WINPR_ATTR_UNUSED UINT32 nSrcStep,
598 WINPR_ATTR_UNUSED UINT32 nSrcWidth, WINPR_ATTR_UNUSED UINT32 nSrcHeight,
600 WINPR_ATTR_UNUSED BYTE** ppDstData, WINPR_ATTR_UNUSED UINT32* pDstSize,
604 WLog_Print(av1->log, WLOG_ERROR,
605 "This build does not support AV1 codec. Recompile with '-DWITH_AOM=ON'");
609INT32 freerdp_av1_decompress(WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT* av1,
610 WINPR_ATTR_UNUSED
const BYTE* pSrcData,
611 WINPR_ATTR_UNUSED UINT32 SrcSize, WINPR_ATTR_UNUSED BYTE* pDstData,
612 WINPR_ATTR_UNUSED DWORD DstFormat, WINPR_ATTR_UNUSED UINT32 nDstStep,
613 WINPR_ATTR_UNUSED UINT32 nDstWidth,
614 WINPR_ATTR_UNUSED UINT32 nDstHeight,
616 WINPR_ATTR_UNUSED UINT32 numRegionRect)
619 WLog_Print(av1->log, WLOG_ERROR,
620 "This build does not support AV1 codec. Recompile with '-DWITH_AOM=ON'");
624BOOL freerdp_av1_context_reset(WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT* av1,
625 WINPR_ATTR_UNUSED UINT32 width, WINPR_ATTR_UNUSED UINT32 height)
628 WLog_Print(av1->log, WLOG_WARN,
629 "This build does not support AV1 codec. Recompile with '-DWITH_AOM=ON'");
633void freerdp_av1_context_free(WINPR_ATTR_UNUSED FREERDP_AV1_CONTEXT* av1)
638FREERDP_AV1_CONTEXT* freerdp_av1_context_new(BOOL Compressor)
640 FREERDP_AV1_CONTEXT* ctx = calloc(1,
sizeof(FREERDP_AV1_CONTEXT));
643 ctx->Compressor = Compressor;
644 ctx->log = WLog_Get(TAG);
WINPR_ATTR_NODISCARD fn_RGBToYUV444_8u_P3AC4R_t RGBToI444_8u