7#include <winpr/sysinfo.h>
9#include <freerdp/freerdp.h>
10#include <freerdp/codec/color.h>
12static const char* print_ns(UINT64 start, UINT64 end,
char* buffer,
size_t len)
14 const UINT64 diff = end - start;
15 const unsigned ns = diff % 1000;
16 const unsigned us = (diff / 1000) % 1000;
17 const unsigned ms = (diff / 1000000) % 1000;
18 const unsigned s = (diff / 1000000000ULL) % 1000;
19 (void)_snprintf(buffer, len,
"%03u %03u %03u %03uns", s, ms, us, ns);
23static BOOL testContextOptions(BOOL compressor, uint32_t width, uint32_t height)
26 const UINT64 start = winpr_GetUnixTimeNS();
27 H264_CONTEXT* h264 = h264_context_new(FALSE);
30 (void)fprintf(stderr,
"[%s] h264_context_new failed\n", __func__);
36 H264_CONTEXT_OPTION opt;
39 const struct optpair_s optpair[] = { { H264_CONTEXT_OPTION_RATECONTROL, H264_RATECONTROL_VBR },
40 { H264_CONTEXT_OPTION_BITRATE, 2323 },
41 { H264_CONTEXT_OPTION_FRAMERATE, 23 },
42 { H264_CONTEXT_OPTION_QP, 21 },
43 { H264_CONTEXT_OPTION_USAGETYPE, 23 } };
44 for (
size_t x = 0; x < ARRAYSIZE(optpair); x++)
46 const struct optpair_s* cur = &optpair[x];
47 if (!h264_context_set_option(h264, cur->opt, cur->val))
50 "[%s] h264_context_set_option %" PRIuz
"{ %d, %" PRIu32
" } failed\n",
51 __func__, x, cur->opt, cur->val);
55 if (!h264_context_reset(h264, width, height))
57 (void)fprintf(stderr,
"[%s] h264_context_reset\n", __func__);
63 h264_context_free(h264);
64 const UINT64 end = winpr_GetUnixTimeNS();
66 char buffer[64] = WINPR_C_ARRAY_INIT;
67 printf(
"[%s] %" PRIu32
"x%" PRIu32
" took %s\n", __func__, width, height,
68 print_ns(start, end, buffer,
sizeof(buffer)));
72static void* allocRGB(uint32_t format, uint32_t width, uint32_t height, uint32_t* pstride)
74 const size_t bpp = FreeRDPGetBytesPerPixel(format);
75 const size_t stride = bpp * width + 32;
76 WINPR_ASSERT(pstride);
77 *pstride = WINPR_ASSERTING_INT_CAST(uint32_t, stride);
79 uint8_t* rgb = calloc(stride, height);
83 for (
size_t x = 0; x < height; x++)
85 if (winpr_RAND(&rgb[x * stride], width * bpp) < 0)
94static BOOL compareRGB(
const uint8_t* src,
const uint8_t* dst, uint32_t format,
size_t width,
95 size_t stride,
size_t height)
97 const size_t bpp = FreeRDPGetBytesPerPixel(format);
98 for (
size_t y = 0; y < height; y++)
100 const uint8_t* csrc = &src[y * stride];
101 const uint8_t* cdst = &dst[y * stride];
102 const int rc = memcmp(csrc, cdst, width * bpp);
113static BOOL testEncode(uint32_t format, uint32_t width, uint32_t height)
119 H264_CONTEXT* h264 = h264_context_new(TRUE);
120 H264_CONTEXT* h264dec = h264_context_new(FALSE);
121 if (!h264 || !h264dec)
123 (void)fprintf(stderr,
"[%s] encoder=%p, decoder=%p\n", __func__, (
void*)h264,
128 if (!h264_context_reset(h264, width, height))
130 (void)fprintf(stderr,
"[%s] h264_context_reset(encoder) failed\n", __func__);
133 if (!h264_context_reset(h264dec, width, height))
135 (void)fprintf(stderr,
"[%s] h264_context_reset(decoder) failed\n", __func__);
140 uint32_t ostride = 0;
141 src = allocRGB(format, width, height, &stride);
142 out = allocRGB(format, width, height, &ostride);
143 if (!src || !out || (stride < width) || (stride != ostride))
145 (void)fprintf(stderr,
146 "[%s] src=%p, out=%p, stride(%" PRIu32
") < width(%" PRIu32
147 "), stride != ostride(%" PRIu32
")\n",
148 __func__, src, out, stride, width, ostride);
152 const RECTANGLE_16 rect = { .left = 0, .top = 0, .right = width, .bottom = height };
153 uint32_t dstsize = 0;
154 uint8_t* dst =
nullptr;
156 avc420_compress(h264, src, format, stride, width, height, &rect, &dst, &dstsize, &meta);
159 (void)fprintf(stderr,
"[%s] avc420_compress failed: %d\n", __func__, res1);
162 if ((dstsize == 0) || !dst)
164 (void)fprintf(stderr,
"[%s] dstsize=%" PRIu32
", dst=%p\n", __func__, dstsize, (
void*)dst);
169 avc420_decompress(h264dec, dst, dstsize, out, format, stride, width, height, &rect, 1);
172 (void)fprintf(stderr,
"[%s] avc420_decompress failed: %d\n", __func__, res2);
176 rc = compareRGB(src, out, format, width, stride, height);
179 (void)fprintf(stderr,
"[%s] run failed\n", __func__);
180 h264_context_free(h264);
181 h264_context_free(h264dec);
182 free_h264_metablock(&meta);
188static BOOL testEncodeOffsetRegion(
void)
191 const UINT32 width = 192;
192 const UINT32 height = 128;
193 const UINT32 format = PIXEL_FORMAT_BGRA32;
194 const RECTANGLE_16 full = { .left = 0, .top = 0, .right = width, .bottom = height };
195 const RECTANGLE_16 region = { .left = 32, .top = 16, .right = 160, .bottom = 112 };
197 H264_CONTEXT* h264 = h264_context_new(TRUE);
199 BYTE* src = allocRGB(format, width, height, &stride);
203 if (!h264 || !src || !h264_context_reset(h264, width, height))
206 memset(src, 0, (
size_t)stride * height);
208 avc420_compress(h264, src, format, stride, width, height, &full, &dst, &dstSize, &meta);
212 (void)fprintf(stderr,
"[%s] first avc420_compress failed: %d\n", __func__, res);
215 free_h264_metablock(&meta);
217 for (UINT32 y = region.top; y < region.bottom; y++)
219 BYTE* row = &src[(size_t)y * stride + (
size_t)region.left * 4];
220 memset(row, 0xFF, (
size_t)(region.right - region.left) * 4);
226 avc420_compress(h264, src, format, stride, width, height, ®ion, &dst, &dstSize, &meta);
229 (void)fprintf(stderr,
"[%s] second avc420_compress failed: %d\n", __func__, res2);
232 if ((meta.numRegionRects == 0) || (meta.regionRects[0].left != region.left) ||
233 (meta.regionRects[0].top != region.top))
236 stderr,
"%s failed: first changed tile does not start at (%" PRIu16
", %" PRIu16
")\n",
237 __func__, region.left, region.top);
244 (void)fprintf(stderr,
"[%s] run failed\n", __func__);
245 h264_context_free(h264);
246 free_h264_metablock(&meta);
254static const BYTE solid_idr_64x64[] = {
255 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x0a, 0xd9, 0x04, 0x26, 0xc0, 0x44, 0x00,
256 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0xc8, 0x3c, 0x48, 0x99, 0x20, 0x00,
257 0x00, 0x00, 0x01, 0x68, 0xcb, 0x83, 0xcb, 0x20, 0x00, 0x00, 0x01, 0x65, 0x88, 0x84,
258 0x0a, 0xf1, 0x18, 0xa0, 0x00, 0x22, 0x4b, 0x1c, 0x00, 0x04, 0x53, 0xa3, 0x80, 0x00,
259 0x85, 0x8c, 0x9c, 0x9c, 0x9d, 0x75, 0xd7, 0x5d, 0x75, 0xd7, 0x5d, 0x75, 0xd7, 0x5e
262static BOOL testDecode(
void)
265 const UINT32 width = 64;
266 const UINT32 height = 64;
267 const UINT32 format = PIXEL_FORMAT_BGRX32;
268 const UINT32 stride = width * 4;
269 const RECTANGLE_16 rect = { .left = 0, .top = 0, .right = width, .bottom = height };
270 H264_CONTEXT* h264 = h264_context_new(FALSE);
271 BYTE* dst = calloc(stride, height);
273 if (!h264 || !dst || !h264_context_reset(h264, width, height))
277 const INT32 res = avc420_decompress(h264, solid_idr_64x64,
sizeof(solid_idr_64x64), dst, format,
278 stride, width, height, &rect, 1);
281 (void)fprintf(stderr,
"[%s] avc420_decompress failed: %" PRId32
"\n", __func__, res);
285 for (UINT32 y = 0; y < height; y++)
287 for (UINT32 x = 0; x < width; x++)
292 const UINT32 color = FreeRDPReadColor(&dst[y * stride + x * 4], format);
293 FreeRDPSplitColor(color, format, &r, &g, &b,
nullptr,
nullptr);
294 if ((abs(r - 0x33) > 16) || (abs(g - 0x66) > 16) || (abs(b - 0xCC) > 16))
297 stderr,
"[%s] pixel %" PRIu32
",%" PRIu32
" is %02X%02X%02X, expected 3366CC\n",
298 __func__, x, y, r, g, b);
306 h264_context_free(h264);
311int TestFreeRDPCodecH264(
int argc,
char* argv[])
318 const UINT32 formats[] = {
319 PIXEL_FORMAT_ABGR15, PIXEL_FORMAT_ARGB15, PIXEL_FORMAT_BGR15, PIXEL_FORMAT_BGR16,
320 PIXEL_FORMAT_BGR24, PIXEL_FORMAT_RGB15, PIXEL_FORMAT_RGB16, PIXEL_FORMAT_RGB24,
321 PIXEL_FORMAT_ABGR32, PIXEL_FORMAT_ARGB32, PIXEL_FORMAT_XBGR32, PIXEL_FORMAT_XRGB32,
322 PIXEL_FORMAT_BGRA32, PIXEL_FORMAT_RGBA32, PIXEL_FORMAT_BGRX32, PIXEL_FORMAT_RGBX32,
328 width = strtoul(argv[1],
nullptr, 0);
329 height = strtoul(argv[2],
nullptr, 0);
330 if ((errno != 0) || (width == 0) || (height == 0))
332 char buffer[128] = WINPR_C_ARRAY_INIT;
333 (void)fprintf(stderr,
"%s failed: width=%" PRIu32
", height=%" PRIu32
", errno=%s\n",
334 __func__, width, height, winpr_strerror(errno, buffer,
sizeof(buffer)));
339#if !defined(WITH_MEDIACODEC) && !defined(WITH_MEDIA_FOUNDATION) && !defined(WITH_OPENH264) && \
340 !defined(WITH_VIDEO_FFMPEG)
341 (void)fprintf(stderr,
"[%s] skipping, no H264 encoder/decoder support compiled in\n", __func__);
345 if (!testContextOptions(FALSE, width, height))
347 if (!testContextOptions(TRUE, width, height))
351#if !defined(WITH_MEDIA_FOUNDATION)
352 if (!testEncodeOffsetRegion())
355 for (
size_t x = 0; x < ARRAYSIZE(formats); x++)
357 const UINT32 SrcFormat = formats[x];
358 for (
size_t y = 0; y < ARRAYSIZE(formats); y++)
360 if (!testEncode(SrcFormat, width, height))
365 (void)fprintf(stderr,
366 "TODO: WITH_MEDIA_FOUNDATION: compression tests skipped, not implemented\n");