FreeRDP
Loading...
Searching...
No Matches
TestFreeRDPCodecDsp.c
1#include <winpr/crt.h>
2#include <winpr/stream.h>
3
4#include <freerdp/codec/audio.h>
5#include <freerdp/codec/dsp.h>
6
7#define OPUS_TEST_FRAMES 960 /* 20 ms at 48 kHz */
8
9static AUDIO_FORMAT opus_format(UINT32 samplesPerSec)
10{
11 const AUDIO_FORMAT format = { .wFormatTag = WAVE_FORMAT_OPUS,
12 .nChannels = 2,
13 .nSamplesPerSec = samplesPerSec,
14 .nAvgBytesPerSec = 16000,
15 .nBlockAlign = 4,
16 .wBitsPerSample = 16 };
17 return format;
18}
19
20static BOOL test_opus_samplerates(void)
21{
22 const AUDIO_FORMAT valid = opus_format(48000);
23 const AUDIO_FORMAT invalid = opus_format(44100);
24 FREERDP_DSP_CONTEXT* decoder = nullptr;
25 BOOL rc = FALSE;
26
27 if (!freerdp_dsp_supports_format(&valid, FALSE))
28 goto fail;
29 if (freerdp_dsp_supports_format(&invalid, FALSE))
30 goto fail;
31
32 decoder = freerdp_dsp_context_new(FALSE);
33 if (!decoder)
34 goto fail;
35 rc = !freerdp_dsp_context_reset(decoder, &invalid, 0);
36
37fail:
38 if (!rc)
39 (void)fprintf(stderr, "[%s] failed\n", __func__);
40 freerdp_dsp_context_free(decoder);
41 return rc;
42}
43
44static BOOL test_opus_roundtrip(void)
45{
46 const AUDIO_FORMAT format = opus_format(48000);
47 const AUDIO_FORMAT pcm = { .wFormatTag = WAVE_FORMAT_PCM,
48 .nChannels = 2,
49 .nSamplesPerSec = 48000,
50 .nAvgBytesPerSec = 192000,
51 .nBlockAlign = 4,
52 .wBitsPerSample = 16 };
53 INT16 samples[OPUS_TEST_FRAMES * 2] = WINPR_C_ARRAY_INIT;
54 BOOL rc = FALSE;
55
56 /* 1 kHz triangle wave */
57 for (size_t x = 0; x < OPUS_TEST_FRAMES; x++)
58 {
59 const INT32 phase = (INT32)(x % 48);
60 const INT16 v = (INT16)(((phase < 24) ? phase : 48 - phase) * 600 - 7200);
61 samples[2 * x] = v;
62 samples[2 * x + 1] = v;
63 }
64
65 FREERDP_DSP_CONTEXT* encoder = freerdp_dsp_context_new(TRUE);
66 FREERDP_DSP_CONTEXT* decoder = freerdp_dsp_context_new(FALSE);
67 wStream* encoded = Stream_New(nullptr, 4096);
68 wStream* decoded = Stream_New(nullptr, 4096);
69 if (!encoder || !decoder || !encoded || !decoded)
70 goto fail;
71
72 /* A context can be reset more than once, e.g. when the server changes the format */
73 for (size_t x = 0; x < 3; x++)
74 {
75 if (!freerdp_dsp_context_reset(encoder, &format, OPUS_TEST_FRAMES))
76 goto fail;
77 if (!freerdp_dsp_context_reset(decoder, &format, 0))
78 goto fail;
79 }
80
81 if (!freerdp_dsp_encode(encoder, &pcm, (const BYTE*)samples, sizeof(samples), encoded))
82 goto fail;
83 Stream_SealLength(encoded);
84 if (Stream_Length(encoded) == 0)
85 goto fail;
86
87 if (!freerdp_dsp_decode(decoder, &format, Stream_Buffer(encoded), Stream_Length(encoded),
88 decoded))
89 goto fail;
90 Stream_SealLength(decoded);
91 rc = (Stream_Length(decoded) == sizeof(samples));
92
93fail:
94 if (!rc)
95 (void)fprintf(stderr, "[%s] failed\n", __func__);
96 Stream_Free(encoded, TRUE);
97 Stream_Free(decoded, TRUE);
98 freerdp_dsp_context_free(encoder);
99 freerdp_dsp_context_free(decoder);
100 return rc;
101}
102
103/* Like audin: one stream reused for every packet, after Stream_SealLength() made it short, and
104 * input in whatever amounts the capture device delivers. Every packet must be a valid one. */
105static BOOL test_opus_chunks(void)
106{
107 const AUDIO_FORMAT format = opus_format(48000);
108 const AUDIO_FORMAT pcm = { .wFormatTag = WAVE_FORMAT_PCM,
109 .nChannels = 2,
110 .nSamplesPerSec = 48000,
111 .nAvgBytesPerSec = 192000,
112 .nBlockAlign = 4,
113 .wBitsPerSample = 16 };
114 const size_t chunks[] = { 333, 1000, 2047, 7, 960, 1919, 441, 2880, 100, 3000 };
115 INT16 samples[3000 * 2] = WINPR_C_ARRAY_INIT;
116 size_t in_frames = 0;
117 size_t out_frames = 0;
118 size_t packets = 0;
119 BOOL rc = FALSE;
120
121 for (size_t x = 0; x < ARRAYSIZE(samples); x++)
122 samples[x] = (INT16)((x % 96) * 300 - 14400);
123
124 FREERDP_DSP_CONTEXT* encoder = freerdp_dsp_context_new(TRUE);
125 FREERDP_DSP_CONTEXT* decoder = freerdp_dsp_context_new(FALSE);
126 wStream* encoded = Stream_New(nullptr, 16);
127 wStream* decoded = Stream_New(nullptr, 4096);
128 if (!encoder || !decoder || !encoded || !decoded)
129 goto fail;
130 if (!freerdp_dsp_context_reset(encoder, &format, OPUS_TEST_FRAMES))
131 goto fail;
132 if (!freerdp_dsp_context_reset(decoder, &format, 0))
133 goto fail;
134
135 for (size_t round = 0; round < 3; round++)
136 {
137 for (size_t x = 0; x < ARRAYSIZE(chunks); x++)
138 {
139 Stream_ResetPosition(encoded);
140 Stream_Write_UINT8(encoded, 0x06); /* a header byte, as MSG_SNDIN_DATA */
141 if (!freerdp_dsp_encode(encoder, &pcm, (const BYTE*)samples, chunks[x] * 4, encoded))
142 goto fail;
143 in_frames += chunks[x];
144 Stream_SealLength(encoded);
145 if (Stream_Length(encoded) <= 1)
146 continue;
147
148 Stream_ResetPosition(decoded);
149 if (!freerdp_dsp_decode(decoder, &format, Stream_Buffer(encoded) + 1,
150 Stream_Length(encoded) - 1, decoded))
151 goto fail;
152 /* 10, 20, 40 or 60 ms: at least the packet size asked for */
153 const size_t frames = Stream_GetPosition(decoded) / 4;
154 if ((frames != 960) && (frames != 1920) && (frames != 2880))
155 {
156 (void)fprintf(stderr, "[%s] packet of %" PRIuz " frames\n", __func__, frames);
157 goto fail;
158 }
159 out_frames += frames;
160 packets++;
161 }
162 }
163
164 /* everything but less than one packet went out */
165 rc = (packets > 0) && (out_frames <= in_frames) && (in_frames - out_frames < 2880);
166 if (!rc)
167 (void)fprintf(stderr, "[%s] %" PRIuz " frames in, %" PRIuz " out\n", __func__, in_frames,
168 out_frames);
169
170fail:
171 if (!rc)
172 (void)fprintf(stderr, "[%s] failed\n", __func__);
173 Stream_Free(encoded, TRUE);
174 Stream_Free(decoded, TRUE);
175 freerdp_dsp_context_free(encoder);
176 freerdp_dsp_context_free(decoder);
177 return rc;
178}
179
180static BOOL test_opus_other_format(void)
181{
182 const AUDIO_FORMAT opus = opus_format(48000);
183 const AUDIO_FORMAT pcm = { .wFormatTag = WAVE_FORMAT_PCM,
184 .nChannels = 2,
185 .nSamplesPerSec = 48000,
186 .nAvgBytesPerSec = 192000,
187 .nBlockAlign = 4,
188 .wBitsPerSample = 16 };
189 const BYTE data[4] = WINPR_C_ARRAY_INIT;
190 BOOL rc = FALSE;
191
192 FREERDP_DSP_CONTEXT* decoder = freerdp_dsp_context_new(FALSE);
193 wStream* decoded = Stream_New(nullptr, 4096);
194 if (!decoder || !decoded)
195 goto fail;
196
197 /* switching from Opus to PCM must not keep (or leak) the Opus decoder */
198 if (!freerdp_dsp_context_reset(decoder, &opus, 0))
199 goto fail;
200 if (!freerdp_dsp_context_reset(decoder, &pcm, 0))
201 goto fail;
202 rc = freerdp_dsp_decode(decoder, &pcm, data, sizeof(data), decoded);
203
204fail:
205 if (!rc)
206 (void)fprintf(stderr, "[%s] failed\n", __func__);
207 Stream_Free(decoded, TRUE);
208 freerdp_dsp_context_free(decoder);
209 return rc;
210}
211
212int TestFreeRDPCodecDsp(WINPR_ATTR_UNUSED int argc, WINPR_ATTR_UNUSED char* argv[])
213{
214 int rc = 0;
215
216 if (!test_opus_samplerates())
217 rc = -1;
218 if (!test_opus_roundtrip())
219 rc = -1;
220 if (!test_opus_chunks())
221 rc = -1;
222 if (!test_opus_other_format())
223 rc = -1;
224 return rc;
225}