FreeRDP
Loading...
Searching...
No Matches
TestFreeRDPCodecH264.c
1
2#include <math.h>
3#include <string.h>
4#include <stdlib.h>
5#include <errno.h>
6
7#include <winpr/sysinfo.h>
8
9#include <freerdp/freerdp.h>
10#include <freerdp/codec/color.h>
11
12static const char* print_ns(UINT64 start, UINT64 end, char* buffer, size_t len)
13{
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);
20 return buffer;
21}
22
23static BOOL testContextOptions(BOOL compressor, uint32_t width, uint32_t height)
24{
25 BOOL rc = FALSE;
26 const UINT64 start = winpr_GetUnixTimeNS();
27 H264_CONTEXT* h264 = h264_context_new(FALSE);
28 if (!h264)
29 return FALSE;
30
31 struct optpair_s
32 {
33 H264_CONTEXT_OPTION opt;
34 uint32_t val;
35 };
36 const struct optpair_s optpair[] = { { H264_CONTEXT_OPTION_RATECONTROL, H264_RATECONTROL_VBR },
37 { H264_CONTEXT_OPTION_BITRATE, 2323 },
38 { H264_CONTEXT_OPTION_FRAMERATE, 23 },
39 { H264_CONTEXT_OPTION_QP, 21 },
40 { H264_CONTEXT_OPTION_USAGETYPE, 23 } };
41 for (size_t x = 0; x < ARRAYSIZE(optpair); x++)
42 {
43 const struct optpair_s* cur = &optpair[x];
44 if (!h264_context_set_option(h264, cur->opt, cur->val))
45 goto fail;
46 }
47 if (!h264_context_reset(h264, width, height))
48 goto fail;
49
50 rc = TRUE;
51fail:
52 h264_context_free(h264);
53 const UINT64 end = winpr_GetUnixTimeNS();
54
55 char buffer[64] = WINPR_C_ARRAY_INIT;
56 printf("[%s] %" PRIu32 "x%" PRIu32 " took %s\n", __func__, width, height,
57 print_ns(start, end, buffer, sizeof(buffer)));
58 return rc;
59}
60
61static void* allocRGB(uint32_t format, uint32_t width, uint32_t height, uint32_t* pstride)
62{
63 const size_t bpp = FreeRDPGetBytesPerPixel(format);
64 const size_t stride = bpp * width + 32;
65 WINPR_ASSERT(pstride);
66 *pstride = WINPR_ASSERTING_INT_CAST(uint32_t, stride);
67
68 uint8_t* rgb = calloc(stride, height);
69 if (!rgb)
70 return nullptr;
71
72 for (size_t x = 0; x < height; x++)
73 {
74 if (winpr_RAND(&rgb[x * stride], width * bpp) < 0)
75 {
76 free(rgb);
77 return nullptr;
78 }
79 }
80 return rgb;
81}
82
83static BOOL compareRGB(const uint8_t* src, const uint8_t* dst, uint32_t format, size_t width,
84 size_t stride, size_t height)
85{
86 const size_t bpp = FreeRDPGetBytesPerPixel(format);
87 for (size_t y = 0; y < height; y++)
88 {
89 const uint8_t* csrc = &src[y * stride];
90 const uint8_t* cdst = &dst[y * stride];
91 const int rc = memcmp(csrc, cdst, width * bpp);
92 // TODO: Both, AVC420 encoding and decoding are lossy.
93 // TODO: Find a proper error margin to check for
94#if 0
95 if (rc != 0)
96 return FALSE;
97#endif
98 }
99 return TRUE;
100}
101
102static BOOL testEncode(uint32_t format, uint32_t width, uint32_t height)
103{
104 BOOL rc = FALSE;
105 void* src = nullptr;
106 void* out = nullptr;
107 RDPGFX_H264_METABLOCK meta = WINPR_C_ARRAY_INIT;
108 H264_CONTEXT* h264 = h264_context_new(TRUE);
109 H264_CONTEXT* h264dec = h264_context_new(FALSE);
110 if (!h264 || !h264dec)
111 goto fail;
112
113 if (!h264_context_reset(h264, width, height))
114 goto fail;
115 if (!h264_context_reset(h264dec, width, height))
116 goto fail;
117
118 uint32_t stride = 0;
119 uint32_t ostride = 0;
120 src = allocRGB(format, width, height, &stride);
121 out = allocRGB(format, width, height, &ostride);
122 if (!src || !out || (stride < width) || (stride != ostride))
123 goto fail;
124
125 const RECTANGLE_16 rect = { .left = 0, .top = 0, .right = width, .bottom = height };
126 uint32_t dstsize = 0;
127 uint8_t* dst = nullptr;
128 if (avc420_compress(h264, src, format, stride, width, height, &rect, &dst, &dstsize, &meta) < 0)
129 goto fail;
130 if ((dstsize == 0) || !dst)
131 goto fail;
132
133 if (avc420_decompress(h264dec, dst, dstsize, out, format, stride, width, height, &rect, 1) < 0)
134 goto fail;
135
136 rc = compareRGB(src, out, format, width, stride, height);
137fail:
138 h264_context_free(h264);
139 h264_context_free(h264dec);
140 free_h264_metablock(&meta);
141 free(src);
142 free(out);
143 return rc;
144}
145
146static BOOL testEncodeOffsetRegion(void)
147{
148 BOOL rc = FALSE;
149 const UINT32 width = 192;
150 const UINT32 height = 128;
151 const UINT32 format = PIXEL_FORMAT_BGRA32;
152 const RECTANGLE_16 full = { .left = 0, .top = 0, .right = width, .bottom = height };
153 const RECTANGLE_16 region = { .left = 32, .top = 16, .right = 160, .bottom = 112 };
154 RDPGFX_H264_METABLOCK meta = WINPR_C_ARRAY_INIT;
155 H264_CONTEXT* h264 = h264_context_new(TRUE);
156 UINT32 stride = 0;
157 BYTE* src = allocRGB(format, width, height, &stride);
158 BYTE* dst = nullptr;
159 UINT32 dstSize = 0;
160
161 if (!h264 || !src || !h264_context_reset(h264, width, height))
162 goto fail;
163
164 memset(src, 0, (size_t)stride * height);
165 if (avc420_compress(h264, src, format, stride, width, height, &full, &dst, &dstSize, &meta) < 0)
166 goto fail;
167 free_h264_metablock(&meta);
168
169 for (UINT32 y = region.top; y < region.bottom; y++)
170 {
171 BYTE* row = &src[(size_t)y * stride + (size_t)region.left * 4];
172 memset(row, 0xFF, (size_t)(region.right - region.left) * 4);
173 }
174
175 dst = nullptr;
176 dstSize = 0;
177 if (avc420_compress(h264, src, format, stride, width, height, &region, &dst, &dstSize, &meta) <
178 0)
179 goto fail;
180 if ((meta.numRegionRects == 0) || (meta.regionRects[0].left != region.left) ||
181 (meta.regionRects[0].top != region.top))
182 {
183 (void)fprintf(
184 stderr, "%s failed: first changed tile does not start at (%" PRIu16 ", %" PRIu16 ")\n",
185 __func__, region.left, region.top);
186 goto fail;
187 }
188
189 rc = TRUE;
190fail:
191 h264_context_free(h264);
192 free_h264_metablock(&meta);
193 free(src);
194 return rc;
195}
196
197int TestFreeRDPCodecH264(int argc, char* argv[])
198{
199 WINPR_UNUSED(argc);
200 WINPR_UNUSED(argv);
201
202 UINT32 width = 124;
203 UINT32 height = 54;
204 const UINT32 formats[] = {
205 PIXEL_FORMAT_ABGR15, PIXEL_FORMAT_ARGB15, PIXEL_FORMAT_BGR15, PIXEL_FORMAT_BGR16,
206 PIXEL_FORMAT_BGR24, PIXEL_FORMAT_RGB15, PIXEL_FORMAT_RGB16, PIXEL_FORMAT_RGB24,
207 PIXEL_FORMAT_ABGR32, PIXEL_FORMAT_ARGB32, PIXEL_FORMAT_XBGR32, PIXEL_FORMAT_XRGB32,
208 PIXEL_FORMAT_BGRA32, PIXEL_FORMAT_RGBA32, PIXEL_FORMAT_BGRX32, PIXEL_FORMAT_RGBX32,
209 };
210
211 if (argc == 3)
212 {
213 errno = 0;
214 width = strtoul(argv[1], nullptr, 0);
215 height = strtoul(argv[2], nullptr, 0);
216 if ((errno != 0) || (width == 0) || (height == 0))
217 {
218 char buffer[128] = WINPR_C_ARRAY_INIT;
219 (void)fprintf(stderr, "%s failed: width=%" PRIu32 ", height=%" PRIu32 ", errno=%s\n",
220 __func__, width, height, winpr_strerror(errno, buffer, sizeof(buffer)));
221 return -1;
222 }
223 }
224
225#if !defined(WITH_MEDIACODEC) && !defined(WITH_MEDIA_FOUNDATION) && !defined(WITH_OPENH264) && \
226 !defined(WITH_VIDEO_FFMPEG)
227 (void)fprintf(stderr, "[%s] skipping, no H264 encoder/decoder support compiled in\n", __func__);
228 return 0;
229#endif
230
231 if (!testContextOptions(FALSE, width, height))
232 return -1;
233 if (!testContextOptions(TRUE, width, height))
234 return -1;
235 if (!testEncodeOffsetRegion())
236 return -1;
237
238 for (size_t x = 0; x < ARRAYSIZE(formats); x++)
239 {
240 const UINT32 SrcFormat = formats[x];
241 for (size_t y = 0; y < ARRAYSIZE(formats); y++)
242 {
243 if (!testEncode(SrcFormat, width, height))
244 return -1;
245 }
246 }
247
248 return 0;
249}