7#include <winpr/sysinfo.h>
9#include <freerdp/freerdp.h>
10#include <freerdp/codec/color.h>
13static const char* print_ns(UINT64 start, UINT64 end,
char* buffer,
size_t len)
17 const UINT64 diff = end - start;
18 const unsigned ns = diff % 1000;
19 const unsigned us = (diff / 1000) % 1000;
20 const unsigned ms = (diff / 1000000) % 1000;
21 const unsigned s = (diff / 1000000000ULL) % 1000;
22 (void)_snprintf(buffer, len,
"%03u %03u %03u %03uns", s, ms, us, ns);
27static BOOL testContextOptions(BOOL compressor, uint32_t width, uint32_t height)
30 const UINT64 start = winpr_GetUnixTimeNS();
32 H264_CONTEXT* h264 = h264_context_new(compressor);
35 (void)fprintf(stderr,
"[%s] h264_context_new failed\n", __func__);
41 H264_CONTEXT_OPTION opt;
44 const struct optpair_s optpair[] = { { H264_CONTEXT_OPTION_RATECONTROL, H264_RATECONTROL_VBR },
45 { H264_CONTEXT_OPTION_BITRATE, 2323 },
46 { H264_CONTEXT_OPTION_FRAMERATE, 23 },
47 { H264_CONTEXT_OPTION_QP, 21 },
48 { H264_CONTEXT_OPTION_USAGETYPE, 23 } };
49 for (
size_t x = 0; x < ARRAYSIZE(optpair); x++)
51 const struct optpair_s* cur = &optpair[x];
52 if (!h264_context_set_option(h264, cur->opt, cur->val))
55 "[%s] h264_context_set_option %" PRIuz
"{ %d, %" PRIu32
" } failed\n",
56 __func__, x, cur->opt, cur->val);
60 if (!h264_context_reset(h264, width, height))
62 (void)fprintf(stderr,
"[%s] h264_context_reset\n", __func__);
68 h264_context_free(h264);
69 const UINT64 end = winpr_GetUnixTimeNS();
71 char buffer[64] = WINPR_C_ARRAY_INIT;
72 printf(
"[%s] %" PRIu32
"x%" PRIu32
" took %s\n", __func__, width, height,
73 print_ns(start, end, buffer,
sizeof(buffer)));
77WINPR_ATTR_MALLOC(free, 1)
78static
void* allocRGB(uint32_t format, uint32_t width, uint32_t height, uint32_t* pstride)
80 const size_t bpp = FreeRDPGetBytesPerPixel(format);
81 const size_t stride = bpp * width + 32;
82 WINPR_ASSERT(pstride);
83 *pstride = WINPR_ASSERTING_INT_CAST(uint32_t, stride);
85 uint8_t* rgb = calloc(stride, height);
89 for (
size_t x = 0; x < height; x++)
91 if (winpr_RAND(&rgb[x * stride], width * bpp) < 0)
101static BOOL compareRGB(
const uint8_t* src,
const uint8_t* dst, uint32_t format,
size_t width,
102 size_t stride,
size_t height)
107 const size_t bpp = FreeRDPGetBytesPerPixel(format);
108 for (
size_t y = 0; y < height; y++)
110 const uint8_t* csrc = &src[y * stride];
111 const uint8_t* cdst = &dst[y * stride];
112 const int rc = memcmp(csrc, cdst, width * bpp);
124static BOOL testEncode(uint32_t format, uint32_t width, uint32_t height)
130 H264_CONTEXT* h264 = h264_context_new(TRUE);
131 H264_CONTEXT* h264dec = h264_context_new(FALSE);
132 if (!h264 || !h264dec)
134 (void)fprintf(stderr,
"[%s] encoder=%p, decoder=%p\n", __func__, (
void*)h264,
139 if (!h264_context_reset(h264, width, height))
141 (void)fprintf(stderr,
"[%s] h264_context_reset(encoder) failed\n", __func__);
144 if (!h264_context_reset(h264dec, width, height))
146 (void)fprintf(stderr,
"[%s] h264_context_reset(decoder) failed\n", __func__);
151 uint32_t ostride = 0;
152 src = allocRGB(format, width, height, &stride);
153 out = allocRGB(format, width, height, &ostride);
154 if (!src || !out || (stride < width) || (stride != ostride))
156 (void)fprintf(stderr,
157 "[%s] src=%p, out=%p, stride(%" PRIu32
") < width(%" PRIu32
158 "), stride != ostride(%" PRIu32
")\n",
159 __func__, src, out, stride, width, ostride);
163 const RECTANGLE_16 rect = { .left = 0, .top = 0, .right = width, .bottom = height };
164 uint32_t dstsize = 0;
165 uint8_t* dst =
nullptr;
167 avc420_compress(h264, src, format, stride, width, height, &rect, &dst, &dstsize, &meta);
170 (void)fprintf(stderr,
"[%s] avc420_compress failed: %d\n", __func__, res1);
173 if ((dstsize == 0) || !dst)
175 (void)fprintf(stderr,
"[%s] dstsize=%" PRIu32
", dst=%p\n", __func__, dstsize, (
void*)dst);
180 avc420_decompress(h264dec, dst, dstsize, out, format, stride, width, height, &rect, 1);
183 (void)fprintf(stderr,
"[%s] avc420_decompress failed: %d\n", __func__, res2);
187 rc = compareRGB(src, out, format, width, stride, height);
190 (void)fprintf(stderr,
"[%s] run failed\n", __func__);
191 h264_context_free(h264);
192 h264_context_free(h264dec);
193 free_h264_metablock(&meta);
200static BOOL testEncodeOffsetRegion(
void)
203 const UINT32 width = 192;
204 const UINT32 height = 128;
205 const UINT32 format = PIXEL_FORMAT_BGRA32;
206 const RECTANGLE_16 full = { .left = 0, .top = 0, .right = width, .bottom = height };
207 const RECTANGLE_16 region = { .left = 32, .top = 16, .right = 160, .bottom = 112 };
209 H264_CONTEXT* h264 = h264_context_new(TRUE);
211 BYTE* src = allocRGB(format, width, height, &stride);
215 if (!h264 || !src || !h264_context_reset(h264, width, height))
218 memset(src, 0, (
size_t)stride * height);
220 avc420_compress(h264, src, format, stride, width, height, &full, &dst, &dstSize, &meta);
224 (void)fprintf(stderr,
"[%s] first avc420_compress failed: %d\n", __func__, res);
227 free_h264_metablock(&meta);
229 for (UINT32 y = region.top; y < region.bottom; y++)
231 BYTE* row = &src[(size_t)y * stride + (
size_t)region.left * 4];
232 memset(row, 0xFF, (
size_t)(region.right - region.left) * 4);
238 avc420_compress(h264, src, format, stride, width, height, ®ion, &dst, &dstSize, &meta);
241 (void)fprintf(stderr,
"[%s] second avc420_compress failed: %d\n", __func__, res2);
244 if ((meta.numRegionRects == 0) || (meta.regionRects[0].left != region.left) ||
245 (meta.regionRects[0].top != region.top))
248 stderr,
"%s failed: first changed tile does not start at (%" PRIu16
", %" PRIu16
")\n",
249 __func__, region.left, region.top);
256 (void)fprintf(stderr,
"[%s] run failed\n", __func__);
257 h264_context_free(h264);
258 free_h264_metablock(&meta);
266static const BYTE solid_idr_64x64[] = {
267 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x0a, 0xd9, 0x04, 0x26, 0xc0, 0x44, 0x00,
268 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0xc8, 0x3c, 0x48, 0x99, 0x20, 0x00,
269 0x00, 0x00, 0x01, 0x68, 0xcb, 0x83, 0xcb, 0x20, 0x00, 0x00, 0x01, 0x65, 0x88, 0x84,
270 0x0a, 0xf1, 0x18, 0xa0, 0x00, 0x22, 0x4b, 0x1c, 0x00, 0x04, 0x53, 0xa3, 0x80, 0x00,
271 0x85, 0x8c, 0x9c, 0x9c, 0x9d, 0x75, 0xd7, 0x5d, 0x75, 0xd7, 0x5d, 0x75, 0xd7, 0x5e
275static BOOL testDecode(BOOL separateParameters)
278 const UINT32 width = 64;
279 const UINT32 height = 64;
280 const UINT32 format = PIXEL_FORMAT_BGRX32;
281 const UINT32 stride = width * 4;
282 const RECTANGLE_16 rect = { .left = 0, .top = 0, .right = width, .bottom = height };
283 H264_CONTEXT* h264 = h264_context_new(FALSE);
284 BYTE* dst = calloc(stride, height);
286 if (!h264 || !dst || !h264_context_reset(h264, width, height))
290 const UINT32 offset = separateParameters ? 36 : 0;
291 if (separateParameters && (avc420_decompress(h264, solid_idr_64x64, offset, dst, format, stride,
292 width, height, &rect, 1) < 0))
297 avc420_decompress(h264, solid_idr_64x64 + offset,
sizeof(solid_idr_64x64) - offset, dst,
298 format, stride, width, height, &rect, 1);
301 (void)fprintf(stderr,
"[%s] avc420_decompress failed: %" PRId32
"\n", __func__, res);
305 for (UINT32 y = 0; y < height; y++)
307 for (UINT32 x = 0; x < width; x++)
312 const UINT32 color = FreeRDPReadColor(&dst[y * stride + x * 4], format);
313 FreeRDPSplitColor(color, format, &r, &g, &b,
nullptr,
nullptr);
314 if ((abs(r - 0x33) > 16) || (abs(g - 0x66) > 16) || (abs(b - 0xCC) > 16))
317 stderr,
"[%s] pixel %" PRIu32
",%" PRIu32
" is %02X%02X%02X, expected 3366CC\n",
318 __func__, x, y, r, g, b);
326 h264_context_free(h264);
334static const BYTE avc444_idr[] = {
335 0x00, 0x00, 0x00, 0x01, 0x67, 0x64, 0x00, 0x0A, 0xAC, 0xB2, 0x08, 0x4D, 0x08, 0x00, 0x00, 0x03,
336 0x00, 0x08, 0x00, 0x00, 0x03, 0x01, 0xE0, 0x78, 0x91, 0x32, 0x40, 0x00, 0x00, 0x00, 0x01, 0x68,
337 0xEB, 0xC0, 0x8C, 0xB2, 0x2C, 0x00, 0x00, 0x00, 0x01, 0x09, 0x10, 0x00, 0x00, 0x01, 0x65, 0x88,
338 0x84, 0x3A, 0xFE, 0xC7, 0x6F, 0xE6, 0x59, 0x2A, 0x80, 0xC4, 0x0B, 0xD4, 0xF7, 0xD5, 0xB5, 0x93,
339 0x2C, 0x2D, 0x6E, 0xCB, 0x72, 0xFB, 0xC9, 0x1D, 0x47, 0x81, 0x00, 0x00, 0x01, 0x65, 0x12, 0x22,
340 0x10, 0xEB, 0xFE, 0xC7, 0x6F, 0xE6, 0x59, 0x2A, 0x80, 0xC4, 0x0B, 0xD4, 0xF7, 0xD5, 0xB5, 0x93,
341 0x2C, 0x2D, 0x6E, 0xCB, 0x72, 0xFB, 0xC9, 0x1D, 0x47, 0x81,
344static const BYTE avc444_p[] = {
345 0x00, 0x00, 0x00, 0x01, 0x09, 0x30, 0x00, 0x00, 0x01, 0x41, 0x9A, 0x3B, 0x1F, 0xF9,
346 0x59, 0xCD, 0xEF, 0xFF, 0xFC, 0xF8, 0xAA, 0xD6, 0x9F, 0x73, 0x05, 0x6A, 0x32, 0x45,
347 0xA5, 0x7B, 0x00, 0x00, 0x01, 0x41, 0x12, 0x68, 0xEC, 0x7F, 0xFA, 0xE1, 0x80,
351static BOOL testDecodeAvc444(UINT32 codec)
356 const UINT32 stride = 64 * 4;
357 const UINT32 format = PIXEL_FORMAT_BGRX32;
358 const BYTE untouched[4] = { 0xA5, 0xA5, 0xA5, 0xA5 };
359 const size_t sampleOffset = partial.top * stride + partial.left * 4;
360 BYTE luma[4] = WINPR_C_ARRAY_INIT;
361 BYTE combined[64 * 64 * 4] = WINPR_C_ARRAY_INIT;
362 BYTE separate[64 * 64 * 4] = WINPR_C_ARRAY_INIT;
363 H264_CONTEXT* combinedContext = h264_context_new(FALSE);
364 H264_CONTEXT* separateContext = h264_context_new(FALSE);
366 if (!combinedContext || !separateContext || !h264_context_reset(combinedContext, 64, 64) ||
367 !h264_context_reset(separateContext, 64, 64))
369 memset(separate, 0xA5,
sizeof(separate));
372 if (avc444_decompress(combinedContext, 0, &full, 1, avc444_idr,
sizeof(avc444_idr), &full, 1,
373 avc444_p,
sizeof(avc444_p), combined, format, stride, 64, 64, codec) != 0)
377 if (avc444_decompress(separateContext, 1, &partial, 1, avc444_idr,
sizeof(avc444_idr),
nullptr,
378 0,
nullptr, 0, separate, format, stride, 64, 64, codec) != 0)
380 memcpy(luma, &separate[sampleOffset],
sizeof(luma));
381 if (avc444_decompress(separateContext, 2, &partial, 1, avc444_p,
sizeof(avc444_p),
nullptr, 0,
382 nullptr, 0, separate, format, stride, 64, 64, codec) != 0)
386 if ((memcmp(luma, untouched,
sizeof(luma)) == 0) ||
387 (memcmp(luma, &separate[sampleOffset],
sizeof(luma)) == 0))
389 (void)fprintf(stderr,
"[%s] missing main or auxiliary output\n", __func__);
394 for (UINT32 y = 0; y < 64; y++)
396 for (UINT32 x = 0; x < 64; x++)
398 const size_t offset = y * stride + x * 4;
399 const BOOL inside = (x >= partial.left) && (x < partial.right) && (y >= partial.top) &&
400 (y < partial.bottom);
401 const BYTE* expected = inside ? &combined[offset] : untouched;
402 if (memcmp(&separate[offset], expected, inside ? 3 : 4) != 0)
404 (void)fprintf(stderr,
"[%s] pixel %" PRIu32
",%" PRIu32
" differs\n", __func__, x,
414 (void)fprintf(stderr,
"[%s] codec 0x%04" PRIX32
" failed\n", __func__, codec);
415 h264_context_free(combinedContext);
416 h264_context_free(separateContext);
421int TestFreeRDPCodecH264(
int argc,
char* argv[])
430 const UINT32 formats[] = {
431 PIXEL_FORMAT_ABGR15, PIXEL_FORMAT_ARGB15, PIXEL_FORMAT_BGR15, PIXEL_FORMAT_BGR16,
432 PIXEL_FORMAT_BGR24, PIXEL_FORMAT_RGB15, PIXEL_FORMAT_RGB16, PIXEL_FORMAT_RGB24,
433 PIXEL_FORMAT_ABGR32, PIXEL_FORMAT_ARGB32, PIXEL_FORMAT_XBGR32, PIXEL_FORMAT_XRGB32,
434 PIXEL_FORMAT_BGRA32, PIXEL_FORMAT_RGBA32, PIXEL_FORMAT_BGRX32, PIXEL_FORMAT_RGBX32,
440 width = strtoul(argv[1],
nullptr, 0);
441 height = strtoul(argv[2],
nullptr, 0);
442 if ((errno != 0) || (width == 0) || (height == 0))
444 char buffer[128] = WINPR_C_ARRAY_INIT;
445 (void)fprintf(stderr,
"%s failed: width=%" PRIu32
", height=%" PRIu32
", errno=%s\n",
446 __func__, width, height, winpr_strerror(errno, buffer,
sizeof(buffer)));
452#if !defined(WITH_GFX_H264)
453 (void)fprintf(stderr,
"[%s] skipping, no H264 encoder/decoder support compiled in\n", __func__);
458 if (!testContextOptions(FALSE, width, height))
460 if (!testDecode(FALSE))
462#ifdef WITH_APPLE_VIDEOTOOLBOX
463 if (!testDecode(TRUE))
467 if (!testDecodeAvc444(RDPGFX_CODECID_AVC444) || !testDecodeAvc444(RDPGFX_CODECID_AVC444v2))
470#if !defined(WITH_MEDIA_FOUNDATION) && \
471 (defined(WITH_OPENH264) || defined(WITH_VIDEO_FFMPEG) || defined(WITH_MEDIACODEC))
473 if (!testContextOptions(TRUE, width, height))
475 if (!testEncodeOffsetRegion())
478 for (
size_t x = 0; x < ARRAYSIZE(formats); x++)
480 const UINT32 SrcFormat = formats[x];
481 for (
size_t y = 0; y < ARRAYSIZE(formats); y++)
483 if (!testEncode(SrcFormat, width, height))
487#elif defined(WITH_MEDIA_FOUNDATION)
488 (void)fprintf(stderr,
489 "TODO: WITH_MEDIA_FOUNDATION: compression tests skipped, not implemented\n");
491 (void)fprintf(stderr,
"H.264 compression tests skipped: no encoder configured\n");