FreeRDP
Loading...
Searching...
No Matches
TestFreeRDPCodecAV1.c
1
2#include <stdlib.h>
3
4#include <freerdp/freerdp.h>
5#include <freerdp/codec/color.h>
6#include <freerdp/codec/av1.h>
7
8static void* allocRGB(uint32_t format, uint32_t width, uint32_t height, uint32_t* pstride)
9{
10 const size_t bpp = FreeRDPGetBytesPerPixel(format);
11 const size_t stride = bpp * width + 32;
12 WINPR_ASSERT(pstride);
13 *pstride = WINPR_ASSERTING_INT_CAST(uint32_t, stride);
14
15 uint8_t* rgb = calloc(stride, height);
16 if (!rgb)
17 return nullptr;
18
19 for (size_t x = 0; x < height; x++)
20 {
21 if (winpr_RAND(&rgb[x * stride], width * bpp) < 0)
22 {
23 free(rgb);
24 return nullptr;
25 }
26 }
27 return rgb;
28}
29
30static BOOL testEncodeDecode(uint32_t format, uint32_t width, uint32_t height)
31{
32 BOOL rc = FALSE;
33 void* src = nullptr;
34 void* out = nullptr;
35 RDPGFX_H264_METABLOCK meta = WINPR_C_ARRAY_INIT;
36 FREERDP_AV1_CONTEXT* enc = freerdp_av1_context_new(TRUE);
37 FREERDP_AV1_CONTEXT* dec = freerdp_av1_context_new(FALSE);
38 if (!enc || !dec)
39 goto fail;
40
41 if (!freerdp_av1_context_reset(enc, width, height))
42 goto fail;
43 if (!freerdp_av1_context_reset(dec, width, height))
44 goto fail;
45
46 uint32_t stride = 0;
47 uint32_t ostride = 0;
48 src = allocRGB(format, width, height, &stride);
49 out = allocRGB(format, width, height, &ostride);
50 if (!src || !out || (stride < width) || (stride != ostride))
51 goto fail;
52
53 const RECTANGLE_16 rect = { .left = 0, .top = 0, .right = width, .bottom = height };
54 uint8_t* dst = nullptr;
55 uint32_t dstsize = 0;
56 if (freerdp_av1_compress(enc, src, format, stride, width, height, &rect, &dst, &dstsize,
57 &meta) < 0)
58 goto fail;
59 if ((dstsize == 0) || !dst)
60 goto fail;
61
62 /* AV1 is a lossy codec, so this only checks that the bitstream produced by the
63 * encoder (always libaom) round-trips through whichever decoder backend is active
64 * (dav1d or libaom) without error. It does not compare decoded pixels. */
65 rc = freerdp_av1_decompress(dec, dst, dstsize, out, format, stride, width, height, &rect, 1) >=
66 0;
67
68fail:
69 freerdp_av1_context_free(enc);
70 freerdp_av1_context_free(dec);
71 free_h264_metablock(&meta);
72 free(src);
73 free(out);
74 return rc;
75}
76
77/* Encodes a picture, whose chroma differs within each 2x2 block, in YUV444 (AV1 profile 1)
78 * and checks the colours of the decoded picture. A YUV444 conversion, that treats the first
79 * chroma sample of a 2x2 block as the average of the block (as done for AVC444), corrupts
80 * the first pixel of each 2x2 block. */
81static BOOL testYUV444Colours(uint32_t width, uint32_t height)
82{
83 const uint32_t format = PIXEL_FORMAT_BGRX32;
84 const uint32_t bpp = FreeRDPGetBytesPerPixel(format);
85 const uint32_t stride = width * bpp;
86 BOOL rc = FALSE;
87 uint8_t* src = calloc(stride, height);
88 uint8_t* out = calloc(stride, height);
89 RDPGFX_H264_METABLOCK meta = WINPR_C_ARRAY_INIT;
90 FREERDP_AV1_CONTEXT* enc = freerdp_av1_context_new(TRUE);
91 FREERDP_AV1_CONTEXT* dec = freerdp_av1_context_new(FALSE);
92 if (!src || !out || !enc || !dec)
93 goto fail;
94
95 if (!freerdp_av1_context_reset(enc, width, height) ||
96 !freerdp_av1_context_reset(dec, width, height))
97 goto fail;
98 if (!freerdp_av1_context_set_option(enc, FREERDP_AV1_CONTEXT_OPTION_PROFILE, 1) ||
99 !freerdp_av1_context_set_option(enc, FREERDP_AV1_CONTEXT_OPTION_RATECONTROL,
100 FREERDP_AV1_VBR) ||
101 !freerdp_av1_context_set_option(enc, FREERDP_AV1_CONTEXT_OPTION_BITRATE, 100000))
102 goto fail;
103
104 for (uint32_t y = 0; y < height; y++)
105 {
106 for (uint32_t x = 0; x < width; x++)
107 {
108 const BOOL first = ((x % 2) == 0) && ((y % 2) == 0);
109 const uint32_t color = first ? FreeRDPGetColor(format, 0xA0, 0x60, 0x60, 0xFF)
110 : FreeRDPGetColor(format, 0x60, 0x60, 0xA0, 0xFF);
111 if (!FreeRDPWriteColor(&src[y * stride + x * bpp], format, color))
112 goto fail;
113 }
114 }
115
116 const RECTANGLE_16 rect = { .left = 0, .top = 0, .right = width, .bottom = height };
117 uint8_t* dst = nullptr;
118 uint32_t dstsize = 0;
119 if (freerdp_av1_compress(enc, src, format, stride, width, height, &rect, &dst, &dstsize,
120 &meta) < 0)
121 goto fail;
122 if (freerdp_av1_decompress(dec, dst, dstsize, out, format, stride, width, height, &rect, 1) < 0)
123 goto fail;
124
125 int maxDiff = 0;
126 for (uint32_t y = 0; y < height; y++)
127 {
128 for (uint32_t x = 0; x < width; x++)
129 {
130 BYTE r1 = 0;
131 BYTE g1 = 0;
132 BYTE b1 = 0;
133 BYTE r2 = 0;
134 BYTE g2 = 0;
135 BYTE b2 = 0;
136 FreeRDPSplitColor(FreeRDPReadColor(&src[y * stride + x * bpp], format), format, &r1,
137 &g1, &b1, nullptr, nullptr);
138 FreeRDPSplitColor(FreeRDPReadColor(&out[y * stride + x * bpp], format), format, &r2,
139 &g2, &b2, nullptr, nullptr);
140 maxDiff = MAX(maxDiff, abs(r1 - r2));
141 maxDiff = MAX(maxDiff, abs(g1 - g2));
142 maxDiff = MAX(maxDiff, abs(b1 - b2));
143 }
144 }
145
146 (void)fprintf(stderr, "[%s] %" PRIu32 "x%" PRIu32 ": maximum colour difference %d\n", __func__,
147 width, height, maxDiff);
148 rc = maxDiff <= 32;
149
150fail:
151 freerdp_av1_context_free(enc);
152 freerdp_av1_context_free(dec);
153 free_h264_metablock(&meta);
154 free(src);
155 free(out);
156 return rc;
157}
158
159int TestFreeRDPCodecAV1(int argc, char* argv[])
160{
161 WINPR_UNUSED(argc);
162 WINPR_UNUSED(argv);
163
164#if !defined(WITH_LIBAOM)
165 (void)fprintf(stderr, "[%s] skipping, no AV1 encoder support compiled in\n", __func__);
166 return 0;
167#else
168 const UINT32 width = 124;
169 const UINT32 height = 54;
170 const UINT32 formats[] = { PIXEL_FORMAT_BGRA32, PIXEL_FORMAT_RGB16 };
171
172 for (size_t x = 0; x < ARRAYSIZE(formats); x++)
173 {
174 if (!testEncodeDecode(formats[x], width, height))
175 return -1;
176 }
177
178 if (!testYUV444Colours(64, 64))
179 return -1;
180
181 return 0;
182#endif
183}