1 /* GStreamer
2 *
3 * unit test for rawaudioparse
4 *
5 * Copyright (C) <2016> Carlos Rafael Giani <dv at pseudoterminal dot org>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 /* FIXME: GValueArray is deprecated, but there is currently no viable alternatives
27 * See https://bugzilla.gnome.org/show_bug.cgi?id=667228 */
28 #define GLIB_DISABLE_DEPRECATION_WARNINGS
29
30 #include <gst/check/gstcheck.h>
31 #include <gst/audio/audio.h>
32
33 /* Checks are hardcoded to expect stereo 16-bit data. The sample rate
34 * however varies from the default of 40 kHz in some tests to see the
35 * differences in calculated buffer durations. */
36 #define NUM_TEST_SAMPLES 512
37 #define NUM_TEST_CHANNELS 2
38 #define TEST_SAMPLE_RATE 40000
39 #define TEST_SAMPLE_FORMAT GST_AUDIO_FORMAT_S16
40
41 /* For ease of programming we use globals to keep refs for our floating
42 * src and sink pads we create; otherwise we always have to do get_pad,
43 * get_peer, and then remove references in every test function */
44 static GstPad *mysrcpad, *mysinkpad;
45
46 typedef struct
47 {
48 GstElement *rawaudioparse;
49 GstAdapter *test_data_adapter;
50 }
51 RawAudParseTestCtx;
52
53 /* Sets up a rawaudioparse element and a GstAdapter that contains 512 test
54 * audio samples. The samples a monotonically increasing set from the values
55 * 0 to 511 for the left and 512 to 1023 for the right channel. The result
56 * is a GstAdapter that contains the interleaved 16-bit integer values:
57 * 0,512,1,513,2,514, ... 511,1023 . This set is used in the checks to see
58 * if rawaudioparse's output buffers contain valid data. */
59 static void
setup_rawaudioparse(RawAudParseTestCtx * testctx,gboolean use_sink_caps,gboolean set_properties,GstCaps * incaps,GstFormat format)60 setup_rawaudioparse (RawAudParseTestCtx * testctx, gboolean use_sink_caps,
61 gboolean set_properties, GstCaps * incaps, GstFormat format)
62 {
63 GstElement *rawaudioparse;
64 GstAdapter *test_data_adapter;
65 GstBuffer *buffer;
66 guint i;
67 guint16 samples[NUM_TEST_SAMPLES * NUM_TEST_CHANNELS];
68
69
70 /* Setup the rawaudioparse element and the pads */
71
72 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
73 GST_PAD_SINK,
74 GST_PAD_ALWAYS,
75 GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL))
76 );
77 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
78 GST_PAD_SRC,
79 GST_PAD_ALWAYS,
80 GST_STATIC_CAPS_ANY);
81
82 rawaudioparse = gst_check_setup_element ("rawaudioparse");
83
84 g_object_set (G_OBJECT (rawaudioparse), "use-sink-caps", use_sink_caps, NULL);
85 if (set_properties)
86 g_object_set (G_OBJECT (rawaudioparse), "sample-rate", TEST_SAMPLE_RATE,
87 "num-channels", NUM_TEST_CHANNELS, "pcm-format", TEST_SAMPLE_FORMAT,
88 NULL);
89
90 fail_unless (gst_element_set_state (rawaudioparse,
91 GST_STATE_PAUSED) == GST_STATE_CHANGE_SUCCESS,
92 "could not set to paused");
93
94 mysrcpad = gst_check_setup_src_pad (rawaudioparse, &srctemplate);
95 mysinkpad = gst_check_setup_sink_pad (rawaudioparse, &sinktemplate);
96
97 gst_pad_set_active (mysrcpad, TRUE);
98 gst_pad_set_active (mysinkpad, TRUE);
99
100 gst_check_setup_events (mysrcpad, rawaudioparse, incaps, format);
101 if (incaps)
102 gst_caps_unref (incaps);
103
104
105 /* Fill the adapter with the interleaved 0..511 and
106 * 512..1023 samples */
107 for (i = 0; i < NUM_TEST_SAMPLES; ++i) {
108 guint c;
109 for (c = 0; c < NUM_TEST_CHANNELS; ++c)
110 samples[i * NUM_TEST_CHANNELS + c] = c * NUM_TEST_SAMPLES + i;
111 }
112
113 test_data_adapter = gst_adapter_new ();
114 buffer = gst_buffer_new_allocate (NULL, sizeof (samples), NULL);
115 gst_buffer_fill (buffer, 0, samples, sizeof (samples));
116 gst_adapter_push (test_data_adapter, buffer);
117
118
119 testctx->rawaudioparse = rawaudioparse;
120 testctx->test_data_adapter = test_data_adapter;
121 }
122
123 static void
cleanup_rawaudioparse(RawAudParseTestCtx * testctx)124 cleanup_rawaudioparse (RawAudParseTestCtx * testctx)
125 {
126 int num_buffers, i;
127
128 gst_pad_set_active (mysrcpad, FALSE);
129 gst_pad_set_active (mysinkpad, FALSE);
130 gst_check_teardown_src_pad (testctx->rawaudioparse);
131 gst_check_teardown_sink_pad (testctx->rawaudioparse);
132 gst_check_teardown_element (testctx->rawaudioparse);
133
134 g_object_unref (G_OBJECT (testctx->test_data_adapter));
135
136 if (buffers != NULL) {
137 num_buffers = g_list_length (buffers);
138 for (i = 0; i < num_buffers; ++i) {
139 GstBuffer *buf = GST_BUFFER (buffers->data);
140 buffers = g_list_remove (buffers, buf);
141 gst_buffer_unref (buf);
142 }
143
144 g_list_free (buffers);
145 buffers = NULL;
146 }
147 }
148
149
150 static void
push_data_and_check_output(RawAudParseTestCtx * testctx,gsize num_in_bytes,gsize expected_num_out_bytes,gint64 expected_pts,gint64 expected_dur,guint expected_num_buffers_in_list,guint bpf,guint16 channel0_start,guint16 channel1_start)151 push_data_and_check_output (RawAudParseTestCtx * testctx, gsize num_in_bytes,
152 gsize expected_num_out_bytes, gint64 expected_pts, gint64 expected_dur,
153 guint expected_num_buffers_in_list, guint bpf, guint16 channel0_start,
154 guint16 channel1_start)
155 {
156 GstBuffer *inbuf, *outbuf;
157 guint num_buffers;
158
159 /* Simulate upstream input by taking num_in_bytes bytes from the adapter */
160 inbuf = gst_adapter_take_buffer (testctx->test_data_adapter, num_in_bytes);
161 fail_unless (inbuf != NULL);
162
163 /* Push the input data and check that the output buffers list grew as
164 * expected */
165 fail_unless (gst_pad_push (mysrcpad, inbuf) == GST_FLOW_OK);
166 num_buffers = g_list_length (buffers);
167 fail_unless_equals_int (num_buffers, expected_num_buffers_in_list);
168
169 /* Take the latest output buffer */
170 outbuf = g_list_nth_data (buffers, num_buffers - 1);
171 fail_unless (outbuf != NULL);
172
173 /* Verify size, PTS, duration of the output buffer */
174 fail_unless_equals_uint64 (expected_num_out_bytes,
175 gst_buffer_get_size (outbuf));
176 fail_unless_equals_uint64 (expected_pts, GST_BUFFER_PTS (outbuf));
177 fail_unless_equals_uint64 (expected_dur, GST_BUFFER_DURATION (outbuf));
178
179 /* Go through all of the samples in the output buffer and check that they are
180 * valid. The samples are interleaved. The offsets specified by channel0_start
181 * and channel1_start are the expected values of the first sample for each
182 * channel in the buffer. So, if channel0_start is 512, then sample #0 in the
183 * buffer must have value 512, and if channel1_start is 700, then sample #1
184 * in the buffer must have value 700 etc. */
185 {
186 guint i, num_frames;
187 guint16 *s;
188 GstMapInfo map_info;
189 guint channel_starts[2] = { channel0_start, channel1_start };
190
191 gst_buffer_map (outbuf, &map_info, GST_MAP_READ);
192 num_frames = map_info.size / bpf;
193 s = (guint16 *) (map_info.data);
194
195 for (i = 0; i < num_frames; ++i) {
196 guint c;
197
198 for (c = 0; i < NUM_TEST_CHANNELS; ++i) {
199 guint16 expected = channel_starts[c] + i;
200 guint16 actual = s[i * NUM_TEST_CHANNELS + c];
201
202 fail_unless_equals_int (expected, actual);
203 }
204 }
205
206 gst_buffer_unmap (outbuf, &map_info);
207 }
208 }
209
210
GST_START_TEST(test_push_unaligned_data_properties_config)211 GST_START_TEST (test_push_unaligned_data_properties_config)
212 {
213 RawAudParseTestCtx testctx;
214
215 setup_rawaudioparse (&testctx, FALSE, TRUE, NULL, GST_FORMAT_BYTES);
216
217 /* Send in data buffers that are not aligned to multiples of the
218 * frame size (= sample size * num_channels). This tests if rawaudioparse
219 * aligns output data properly.
220 *
221 * The second line sends in 99 bytes, and expects 100 bytes in the
222 * output buffer. This is because the first buffer contains 45 bytes,
223 * and rawaudioparse is expected to output 44 bytes (which is an integer
224 * multiple of the frame size). The leftover 1 byte then gets prepended
225 * to the input buffer with 99 bytes, resulting in 100 bytes, which is
226 * an integer multiple of the frame size.
227 */
228
229 push_data_and_check_output (&testctx, 45, 44, GST_USECOND * 0,
230 GST_USECOND * 275, 1, 4, 0, 512);
231 push_data_and_check_output (&testctx, 99, 100, GST_USECOND * 275,
232 GST_USECOND * 625, 2, 4, 11, 523);
233 push_data_and_check_output (&testctx, 18, 16, GST_USECOND * 900,
234 GST_USECOND * 100, 3, 4, 36, 548);
235
236 cleanup_rawaudioparse (&testctx);
237 }
238
239 GST_END_TEST;
240
GST_START_TEST(test_push_unaligned_data_sink_caps_config)241 GST_START_TEST (test_push_unaligned_data_sink_caps_config)
242 {
243 RawAudParseTestCtx testctx;
244 GstAudioInfo ainfo;
245 GstCaps *caps;
246
247 /* This test is essentially the same as test_push_unaligned_data_properties_config,
248 * except that rawaudioparse uses the sink caps config instead of the property config. */
249
250 gst_audio_info_set_format (&ainfo, TEST_SAMPLE_FORMAT, TEST_SAMPLE_RATE,
251 NUM_TEST_CHANNELS, NULL);
252 caps = gst_audio_info_to_caps (&ainfo);
253
254 setup_rawaudioparse (&testctx, TRUE, FALSE, caps, GST_FORMAT_BYTES);
255
256 push_data_and_check_output (&testctx, 45, 44, GST_USECOND * 0,
257 GST_USECOND * 275, 1, 4, 0, 512);
258 push_data_and_check_output (&testctx, 99, 100, GST_USECOND * 275,
259 GST_USECOND * 625, 2, 4, 11, 523);
260 push_data_and_check_output (&testctx, 18, 16, GST_USECOND * 900,
261 GST_USECOND * 100, 3, 4, 36, 548);
262
263 cleanup_rawaudioparse (&testctx);
264 }
265
266 GST_END_TEST;
267
GST_START_TEST(test_push_swapped_channels)268 GST_START_TEST (test_push_swapped_channels)
269 {
270 RawAudParseTestCtx testctx;
271 GValueArray *valarray;
272 GValue val = G_VALUE_INIT;
273
274 /* Send in 40 bytes and use a nonstandard channel order (left and right channels
275 * swapped). Expected behavior is for rawaudioparse to reorder the samples inside
276 * output buffers to conform to the GStreamer channel order. For this reason,
277 * channel0 offset is 512 and channel1 offset is 0 in the check below. */
278
279 setup_rawaudioparse (&testctx, FALSE, TRUE, NULL, GST_FORMAT_BYTES);
280
281 valarray = g_value_array_new (2);
282 g_value_init (&val, GST_TYPE_AUDIO_CHANNEL_POSITION);
283 g_value_set_enum (&val, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT);
284 g_value_array_insert (valarray, 0, &val);
285 g_value_set_enum (&val, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT);
286 g_value_array_insert (valarray, 1, &val);
287 g_object_set (G_OBJECT (testctx.rawaudioparse), "channel-positions",
288 valarray, NULL);
289 g_value_array_free (valarray);
290 g_value_unset (&val);
291
292 push_data_and_check_output (&testctx, 40, 40, GST_USECOND * 0,
293 GST_USECOND * 250, 1, 4, 512, 0);
294
295 cleanup_rawaudioparse (&testctx);
296 }
297
298 GST_END_TEST;
299
GST_START_TEST(test_config_switch)300 GST_START_TEST (test_config_switch)
301 {
302 RawAudParseTestCtx testctx;
303 GstAudioInfo ainfo;
304 GstCaps *caps;
305
306 /* Start processing with the properties config active, then mid-stream switch to
307 * the sink caps config. The properties config is altered to have a different
308 * sample rate than the sink caps to be able to detect the switch. The net effect
309 * is that output buffer durations are altered. For example, 40 bytes equal
310 * 10 samples, and this equals 500 us with 20 kHz or 250 us with 40 kHz. */
311
312 gst_audio_info_set_format (&ainfo, TEST_SAMPLE_FORMAT, TEST_SAMPLE_RATE,
313 NUM_TEST_CHANNELS, NULL);
314 caps = gst_audio_info_to_caps (&ainfo);
315
316 setup_rawaudioparse (&testctx, FALSE, TRUE, caps, GST_FORMAT_BYTES);
317
318 g_object_set (G_OBJECT (testctx.rawaudioparse), "sample-rate", 20000, NULL);
319
320 /* Push in data with properties config active, expecting duration calculations
321 * to be based on the 20 kHz sample rate */
322 push_data_and_check_output (&testctx, 40, 40, GST_USECOND * 0,
323 GST_USECOND * 500, 1, 4, 0, 512);
324 push_data_and_check_output (&testctx, 20, 20, GST_USECOND * 500,
325 GST_USECOND * 250, 2, 4, 10, 522);
326
327 /* Perform the switch */
328 g_object_set (G_OBJECT (testctx.rawaudioparse), "use-sink-caps", TRUE, NULL);
329
330 /* Push in data with sink caps config active, expecting duration calculations
331 * to be based on the 40 kHz sample rate */
332 push_data_and_check_output (&testctx, 40, 40, GST_USECOND * 750,
333 GST_USECOND * 250, 3, 4, 15, 527);
334
335 cleanup_rawaudioparse (&testctx);
336 }
337
338 GST_END_TEST;
339
GST_START_TEST(test_change_caps)340 GST_START_TEST (test_change_caps)
341 {
342 RawAudParseTestCtx testctx;
343 GstAudioInfo ainfo;
344 GstCaps *caps;
345
346 /* Start processing with the sink caps config active, using the
347 * default channel count and sample format and 20 kHz sample rate
348 * for the caps. Push some data, then change caps (20 kHz -> 40 kHz).
349 * Check that the changed caps are handled properly. */
350
351 gst_audio_info_set_format (&ainfo, TEST_SAMPLE_FORMAT, 20000,
352 NUM_TEST_CHANNELS, NULL);
353 caps = gst_audio_info_to_caps (&ainfo);
354
355 setup_rawaudioparse (&testctx, TRUE, FALSE, caps, GST_FORMAT_BYTES);
356
357 /* Push in data with caps sink config active, expecting duration calculations
358 * to be based on the 20 kHz sample rate */
359 push_data_and_check_output (&testctx, 40, 40, GST_USECOND * 0,
360 GST_USECOND * 500, 1, 4, 0, 512);
361 push_data_and_check_output (&testctx, 20, 20, GST_USECOND * 500,
362 GST_USECOND * 250, 2, 4, 10, 522);
363
364 /* Change caps */
365 gst_audio_info_set_format (&ainfo, TEST_SAMPLE_FORMAT, 40000,
366 NUM_TEST_CHANNELS, NULL);
367 caps = gst_audio_info_to_caps (&ainfo);
368 fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_caps (caps)));
369 gst_caps_unref (caps);
370
371 /* Push in data with the new caps, expecting duration calculations
372 * to be based on the 40 kHz sample rate */
373 push_data_and_check_output (&testctx, 40, 40, GST_USECOND * 750,
374 GST_USECOND * 250, 3, 4, 15, 527);
375
376 cleanup_rawaudioparse (&testctx);
377 }
378
379 GST_END_TEST;
380
381
382 static Suite *
rawaudioparse_suite(void)383 rawaudioparse_suite (void)
384 {
385 Suite *s = suite_create ("rawaudioparse");
386 TCase *tc_chain = tcase_create ("general");
387
388 suite_add_tcase (s, tc_chain);
389 tcase_add_test (tc_chain, test_push_unaligned_data_properties_config);
390 tcase_add_test (tc_chain, test_push_unaligned_data_sink_caps_config);
391 tcase_add_test (tc_chain, test_push_swapped_channels);
392 tcase_add_test (tc_chain, test_config_switch);
393 tcase_add_test (tc_chain, test_change_caps);
394
395 return s;
396 }
397
398 GST_CHECK_MAIN (rawaudioparse);
399