FreeRDP
Loading...
Searching...
No Matches
TestFreeRDPCodecAV2.c
1
2#include <stdlib.h>
3
4#include <freerdp/freerdp.h>
5#include <freerdp/codec/color.h>
6#include <freerdp/codec/av2.h>
7
8WINPR_ATTR_MALLOC(free, 1)
9static void* allocRGB(uint32_t format, uint32_t width, uint32_t height, uint32_t* pstride)
10{
11 const size_t bpp = FreeRDPGetBytesPerPixel(format);
12 const size_t stride = bpp * width + 32;
13 WINPR_ASSERT(pstride);
14 *pstride = WINPR_ASSERTING_INT_CAST(uint32_t, stride);
15
16 if ((stride == 0) || (height == 0))
17 return nullptr;
18
19 uint8_t* rgb = calloc(stride, height);
20 if (!rgb)
21 return nullptr;
22
23 for (size_t x = 0; x < height; x++)
24 {
25 if (winpr_RAND(&rgb[x * stride], width * bpp) < 0)
26 {
27 free(rgb);
28 return nullptr;
29 }
30 }
31 return rgb;
32}
33
34WINPR_ATTR_NODISCARD
35static BOOL testEncodeDecode(uint32_t format, uint32_t width, uint32_t height)
36{
37 BOOL rc = FALSE;
38 void* src = nullptr;
39 void* out = nullptr;
40 RDPGFX_H264_METABLOCK meta = WINPR_C_ARRAY_INIT;
41 FREERDP_AV2_CONTEXT* enc = freerdp_av2_context_new(TRUE);
42 FREERDP_AV2_CONTEXT* dec = freerdp_av2_context_new(FALSE);
43 if (!enc || !dec)
44 goto fail;
45
46 if (!freerdp_av2_context_reset(enc, width, height))
47 goto fail;
48 if (!freerdp_av2_context_reset(dec, width, height))
49 goto fail;
50
51 uint32_t stride = 0;
52 uint32_t ostride = 0;
53 src = allocRGB(format, width, height, &stride);
54 out = allocRGB(format, width, height, &ostride);
55 if (!src || !out || (stride < width) || (stride != ostride))
56 goto fail;
57
58 const RECTANGLE_16 rect = { .left = 0, .top = 0, .right = width, .bottom = height };
59 uint8_t* dst = nullptr;
60 uint32_t dstsize = 0;
61 if (freerdp_av2_compress(enc, src, format, stride, width, height, &rect, &dst, &dstsize,
62 &meta) < 0)
63 goto fail;
64 if ((dstsize == 0) || !dst)
65 goto fail;
66
67 /* AV2 is a lossy codec, so this only checks that the bitstream produced by the
68 * encoder (always libavm) round-trips through whichever decoder backend is active
69 * (dav2d or libavm) without error. It does not compare decoded pixels. */
70 rc = freerdp_av2_decompress(dec, dst, dstsize, out, format, stride, width, height, &rect, 1) >=
71 0;
72
73fail:
74 freerdp_av2_context_free(enc);
75 freerdp_av2_context_free(dec);
76 free_h264_metablock(&meta);
77 free(src);
78 free(out);
79 return rc;
80}
81
82/* Encodes a picture and checks the colours of the decoded picture.
83 * YUV444 (profile option 1): the chroma differs within each 2x2 block. A YUV444 conversion,
84 * that treats the first chroma sample of a 2x2 block as the average of the block (as done for
85 * AVC444), corrupts the first pixel of each 2x2 block.
86 * YUV420 (profile option 0): the colour changes per 8x8 block, which 4:2:0 keeps. */
87WINPR_ATTR_NODISCARD
88static BOOL testColours(uint32_t profile, uint32_t width, uint32_t height)
89{
90 const uint32_t format = PIXEL_FORMAT_BGRX32;
91 const uint32_t bpp = FreeRDPGetBytesPerPixel(format);
92 const uint32_t stride = width * bpp;
93 BOOL rc = FALSE;
94 uint8_t* src = calloc(stride, height);
95 uint8_t* out = calloc(stride, height);
96 RDPGFX_H264_METABLOCK meta = WINPR_C_ARRAY_INIT;
97 FREERDP_AV2_CONTEXT* enc = freerdp_av2_context_new(TRUE);
98 FREERDP_AV2_CONTEXT* dec = freerdp_av2_context_new(FALSE);
99 if (!src || !out || !enc || !dec)
100 goto fail;
101
102 if (!freerdp_av2_context_reset(enc, width, height) ||
103 !freerdp_av2_context_reset(dec, width, height))
104 goto fail;
105 if (!freerdp_av2_context_set_option(enc, FREERDP_AV2_CONTEXT_OPTION_PROFILE, profile) ||
106 !freerdp_av2_context_set_option(enc, FREERDP_AV2_CONTEXT_OPTION_RATECONTROL,
107 FREERDP_AV2_VBR) ||
108 !freerdp_av2_context_set_option(enc, FREERDP_AV2_CONTEXT_OPTION_BITRATE, 100000))
109 goto fail;
110
111 for (uint32_t y = 0; y < height; y++)
112 {
113 for (uint32_t x = 0; x < width; x++)
114 {
115 const BOOL first =
116 (profile == 1) ? ((x % 2) == 0) && ((y % 2) == 0) : (((x / 8) + (y / 8)) % 2) == 0;
117 const uint32_t color = first ? FreeRDPGetColor(format, 0xA0, 0x60, 0x60, 0xFF)
118 : FreeRDPGetColor(format, 0x60, 0x60, 0xA0, 0xFF);
119 if (!FreeRDPWriteColor(&src[y * stride + x * bpp], format, color))
120 goto fail;
121 }
122 }
123
124 const RECTANGLE_16 rect = { .left = 0, .top = 0, .right = width, .bottom = height };
125 uint8_t* dst = nullptr;
126 uint32_t dstsize = 0;
127 if (freerdp_av2_compress(enc, src, format, stride, width, height, &rect, &dst, &dstsize,
128 &meta) < 0)
129 goto fail;
130 if (freerdp_av2_decompress(dec, dst, dstsize, out, format, stride, width, height, &rect, 1) < 0)
131 goto fail;
132
133 int maxDiff = 0;
134 for (uint32_t y = 0; y < height; y++)
135 {
136 for (uint32_t x = 0; x < width; x++)
137 {
138 BYTE r1 = 0;
139 BYTE g1 = 0;
140 BYTE b1 = 0;
141 BYTE r2 = 0;
142 BYTE g2 = 0;
143 BYTE b2 = 0;
144 FreeRDPSplitColor(FreeRDPReadColor(&src[y * stride + x * bpp], format), format, &r1,
145 &g1, &b1, nullptr, nullptr);
146 FreeRDPSplitColor(FreeRDPReadColor(&out[y * stride + x * bpp], format), format, &r2,
147 &g2, &b2, nullptr, nullptr);
148 maxDiff = MAX(maxDiff, abs(r1 - r2));
149 maxDiff = MAX(maxDiff, abs(g1 - g2));
150 maxDiff = MAX(maxDiff, abs(b1 - b2));
151 }
152 }
153
154 (void)fprintf(stderr, "[%s] %s %" PRIu32 "x%" PRIu32 ": maximum colour difference %d\n",
155 __func__, (profile == 1) ? "YUV444" : "YUV420", width, height, maxDiff);
156 rc = maxDiff <= 32;
157
158fail:
159 freerdp_av2_context_free(enc);
160 freerdp_av2_context_free(dec);
161 free_h264_metablock(&meta);
162 free(src);
163 free(out);
164 return rc;
165}
166
167/* Encodes a few frames of a moving picture into one stream, so that all but the first are inter
168 * frames, and checks the colours of each decoded frame. */
169WINPR_ATTR_NODISCARD
170static BOOL testSequence(uint32_t width, uint32_t height, uint32_t frames)
171{
172 const uint32_t format = PIXEL_FORMAT_BGRX32;
173 const uint32_t bpp = FreeRDPGetBytesPerPixel(format);
174 const uint32_t stride = width * bpp;
175 BOOL rc = FALSE;
176 uint8_t* src = calloc(stride, height);
177 uint8_t* out = calloc(stride, height);
178 FREERDP_AV2_CONTEXT* enc = freerdp_av2_context_new(TRUE);
179 FREERDP_AV2_CONTEXT* dec = freerdp_av2_context_new(FALSE);
180 if (!src || !out || !enc || !dec)
181 goto fail;
182
183 if (!freerdp_av2_context_reset(enc, width, height) ||
184 !freerdp_av2_context_reset(dec, width, height))
185 goto fail;
186 if (!freerdp_av2_context_set_option(enc, FREERDP_AV2_CONTEXT_OPTION_PROFILE, 0) ||
187 !freerdp_av2_context_set_option(enc, FREERDP_AV2_CONTEXT_OPTION_RATECONTROL,
188 FREERDP_AV2_VBR) ||
189 !freerdp_av2_context_set_option(enc, FREERDP_AV2_CONTEXT_OPTION_BITRATE, 100000))
190 goto fail;
191
192 for (uint32_t f = 0; f < frames; f++)
193 {
194 /* 8x8 blocks, moving 4 pixels to the right per frame */
195 for (uint32_t y = 0; y < height; y++)
196 {
197 for (uint32_t x = 0; x < width; x++)
198 {
199 const BOOL first = ((((x + 4 * f) / 8) + (y / 8)) % 2) == 0;
200 const uint32_t color = first ? FreeRDPGetColor(format, 0xA0, 0x60, 0x60, 0xFF)
201 : FreeRDPGetColor(format, 0x60, 0x60, 0xA0, 0xFF);
202 if (!FreeRDPWriteColor(&src[y * stride + x * bpp], format, color))
203 goto fail;
204 }
205 }
206
207 RDPGFX_H264_METABLOCK meta = WINPR_C_ARRAY_INIT;
208 const RECTANGLE_16 rect = { .left = 0, .top = 0, .right = width, .bottom = height };
209 uint8_t* dst = nullptr;
210 uint32_t dstsize = 0;
211 const INT32 crc = freerdp_av2_compress(enc, src, format, stride, width, height, &rect, &dst,
212 &dstsize, &meta);
213 free_h264_metablock(&meta);
214 if ((crc <= 0) || !dst || (dstsize == 0))
215 goto fail;
216 if (freerdp_av2_decompress(dec, dst, dstsize, out, format, stride, width, height, &rect,
217 1) < 0)
218 goto fail;
219
220 int maxDiff = 0;
221 for (uint32_t y = 0; y < height; y++)
222 {
223 for (uint32_t x = 0; x < width; x++)
224 {
225 BYTE r1 = 0;
226 BYTE g1 = 0;
227 BYTE b1 = 0;
228 BYTE r2 = 0;
229 BYTE g2 = 0;
230 BYTE b2 = 0;
231 FreeRDPSplitColor(FreeRDPReadColor(&src[y * stride + x * bpp], format), format, &r1,
232 &g1, &b1, nullptr, nullptr);
233 FreeRDPSplitColor(FreeRDPReadColor(&out[y * stride + x * bpp], format), format, &r2,
234 &g2, &b2, nullptr, nullptr);
235 maxDiff = MAX(maxDiff, abs(r1 - r2));
236 maxDiff = MAX(maxDiff, abs(g1 - g2));
237 maxDiff = MAX(maxDiff, abs(b1 - b2));
238 }
239 }
240
241 (void)fprintf(stderr,
242 "[%s] frame %" PRIu32 " %" PRIu32 "x%" PRIu32 " (%" PRIu32
243 " bytes): maximum colour difference %d\n",
244 __func__, f, width, height, dstsize, maxDiff);
245 if (maxDiff > 32)
246 goto fail;
247 }
248 rc = TRUE;
249
250fail:
251 freerdp_av2_context_free(enc);
252 freerdp_av2_context_free(dec);
253 free(src);
254 free(out);
255 return rc;
256}
257
258int TestFreeRDPCodecAV2(int argc, char* argv[])
259{
260 WINPR_UNUSED(argc);
261 WINPR_UNUSED(argv);
262
263#if !defined(WITH_LIBAVM)
264 (void)fprintf(stderr, "[%s] skipping, no AV2 encoder support compiled in\n", __func__);
265 return 0;
266#else
267 const UINT32 width = 124;
268 const UINT32 height = 54;
269 const UINT32 formats[] = { PIXEL_FORMAT_BGRA32, PIXEL_FORMAT_RGB16 };
270
271 for (size_t x = 0; x < ARRAYSIZE(formats); x++)
272 {
273 if (!testEncodeDecode(formats[x], width, height))
274 return -1;
275 }
276
277 if (!testColours(0, 64, 64))
278 return -1;
279 if (!testColours(1, 64, 64))
280 return -1;
281 if (!testSequence(64, 64, 4))
282 return -1;
283
284 return 0;
285#endif
286}