FreeRDP
Loading...
Searching...
No Matches
TestFreeRDPCodecYuv.c
1
2#include <string.h>
3#include <stdlib.h>
4
5#include <winpr/crt.h>
6#include <winpr/crypto.h>
7#include <winpr/wlog.h>
8
9#include <freerdp/freerdp.h>
10#include <freerdp/settings.h>
11#include <freerdp/codec/color.h>
12#include <freerdp/codec/yuv.h>
13
14/* The threaded YUV encoder splits every region into slices of a fixed number of rows that are
15 * converted by different workers. Every slice has to convert its own rows only and the last slice
16 * (which can be shorter or longer than a full slice) has to reach the bottom of the region.
17 *
18 * Convert the same random image with and without worker threads and require identical output.
19 */
20
21#define TEST_TAG "TestFreeRDPCodecYuv"
22#define TEST_WIDTH 320u
23#define TEST_HEIGHT 200u
24#define TEST_PLANES 6u
25
26/* The H264 layer pads the planes (32 more columns and rows, see yuv_ensure_buffer) so that the
27 * SIMD code can write whole vectors at the edges. Do the same here. */
28#define TEST_STRIDE (TEST_WIDTH + 32u - TEST_WIDTH % 16u)
29#define TEST_PHEIGHT (TEST_HEIGHT + 32u - TEST_HEIGHT % 16u)
30#define TEST_PLANE_SIZE (1ull * TEST_STRIDE * TEST_PHEIGHT)
31
32static void free_planes(BYTE* planes[TEST_PLANES])
33{
34 for (size_t x = 0; x < TEST_PLANES; x++)
35 {
36 free(planes[x]);
37 planes[x] = nullptr;
38 }
39}
40
41static BOOL alloc_planes(BYTE* planes[TEST_PLANES])
42{
43 for (size_t x = 0; x < TEST_PLANES; x++)
44 {
45 planes[x] = calloc(1, TEST_PLANE_SIZE);
46 if (!planes[x])
47 return FALSE;
48 }
49 return TRUE;
50}
51
52static BOOL encode(YUV_CONTEXT* context, BYTE version, const BYTE* src, UINT32 srcStep,
53 BYTE* planes[TEST_PLANES], const RECTANGLE_16* rect)
54{
55 const UINT32 stride[3] = { TEST_STRIDE, (TEST_STRIDE + 1) / 2, (TEST_STRIDE + 1) / 2 };
56 BYTE* luma[3] = { planes[0], planes[1], planes[2] };
57 BYTE* chroma[3] = { planes[3], planes[4], planes[5] };
58
59 if (version == 0)
60 return yuv420_context_encode(context, src, srcStep, PIXEL_FORMAT_BGRX32, stride, luma, rect,
61 1);
62 return yuv444_context_encode(context, version, src, srcStep, PIXEL_FORMAT_BGRX32, stride, luma,
63 chroma, rect, 1);
64}
65
66static BOOL compare_rect(YUV_CONTEXT* threaded, YUV_CONTEXT* single, BYTE version, const BYTE* src,
67 UINT32 srcStep, const RECTANGLE_16* rect)
68{
69 BOOL rc = FALSE;
70 BYTE* planesA[TEST_PLANES] = WINPR_C_ARRAY_INIT;
71 BYTE* planesB[TEST_PLANES] = WINPR_C_ARRAY_INIT;
72
73 if (!alloc_planes(planesA) || !alloc_planes(planesB))
74 goto fail;
75
76 if (!encode(threaded, version, src, srcStep, planesA, rect) ||
77 !encode(single, version, src, srcStep, planesB, rect))
78 goto fail;
79
80 for (size_t x = 0; x < TEST_PLANES; x++)
81 {
82 if (memcmp(planesA[x], planesB[x], TEST_PLANE_SIZE) != 0)
83 {
84 (void)fprintf(stderr,
85 "[%s] version %d: plane %" PRIuz " differs for rect [%" PRIu16 ",%" PRIu16
86 "-%" PRIu16 ",%" PRIu16 "]\n",
87 TEST_TAG, version, x, rect->left, rect->top, rect->right, rect->bottom);
88 goto fail;
89 }
90 }
91 rc = TRUE;
92fail:
93 free_planes(planesA);
94 free_planes(planesB);
95 return rc;
96}
97
98int TestFreeRDPCodecYuv(WINPR_ATTR_UNUSED int argc, WINPR_ATTR_UNUSED char* argv[])
99{
100 int rc = -1;
101 const UINT32 srcStep = TEST_WIDTH * 4u;
102 BYTE* src = calloc(TEST_HEIGHT, srcStep);
103 YUV_CONTEXT* threaded = yuv_context_new(TRUE, 0);
104 YUV_CONTEXT* single = yuv_context_new(TRUE, THREADING_FLAGS_DISABLE_THREADS);
105
106 /* region tops have to be even, the chroma planes are subsampled */
107 const UINT16 tops[] = { 0, 2, 16, 34 };
108 const UINT16 heights[] = { 1, 2, 3, 7, 8, 9, 15, 16, 17, 23, 24, 25, 31, 32, 33, 40, 100, 198 };
109 const UINT16 lefts[] = { 0, 16 };
110 const UINT16 rights[] = { TEST_WIDTH, TEST_WIDTH - 16 };
111 size_t tested = 0;
112
113 if (!src || !threaded || !single)
114 goto fail;
115 if (winpr_RAND_pseudo(src, 1ull * TEST_HEIGHT * srcStep) < 0)
116 goto fail;
117 if (!yuv_context_reset(threaded, TEST_WIDTH, TEST_HEIGHT) ||
118 !yuv_context_reset(single, TEST_WIDTH, TEST_HEIGHT))
119 goto fail;
120
121 /* 0 = YUV420, 1 and 2 = the two YUV444 variants used for AVC444 and AVC444v2 */
122 for (BYTE version = 0; version < 3; version++)
123 {
124 for (size_t t = 0; t < ARRAYSIZE(tops); t++)
125 {
126 for (size_t h = 0; h < ARRAYSIZE(heights); h++)
127 {
128 for (size_t s = 0; s < ARRAYSIZE(lefts); s++)
129 {
130 RECTANGLE_16 rect = { 0 };
131 rect.left = lefts[s];
132 rect.right = rights[s];
133 rect.top = tops[t];
134 rect.bottom = (UINT16)(tops[t] + heights[h]);
135 if (rect.bottom > TEST_HEIGHT)
136 continue;
137
138 if (!compare_rect(threaded, single, version, src, srcStep, &rect))
139 goto fail;
140 tested++;
141 }
142 }
143 }
144 }
145
146 /* make sure the loops above did not silently skip everything */
147 if (tested < 150)
148 {
149 (void)fprintf(stderr, "[%s] only %" PRIuz " regions tested\n", TEST_TAG, tested);
150 goto fail;
151 }
152 rc = 0;
153fail:
154 yuv_context_free(threaded);
155 yuv_context_free(single);
156 free(src);
157 return rc;
158}