1 /* GStreamer
2 *
3 * unit test for vorbisenc
4 *
5 * Copyright (C) 2006 Andy Wingo <wingo at pobox.com>
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 #include <gst/check/gstcheck.h>
27 #include <gst/check/gstbufferstraw.h>
28
29 #ifndef GST_DISABLE_PARSE
30
31 #define TIMESTAMP_OFFSET G_GINT64_CONSTANT(3249870963)
32
33 static void
check_buffer_timestamp(GstBuffer * buffer,GstClockTime timestamp)34 check_buffer_timestamp (GstBuffer * buffer, GstClockTime timestamp)
35 {
36 fail_unless (GST_BUFFER_TIMESTAMP (buffer) == timestamp,
37 "expected timestamp %" GST_TIME_FORMAT
38 ", but got timestamp %" GST_TIME_FORMAT,
39 GST_TIME_ARGS (timestamp), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
40 }
41
42 static void
check_buffer_duration(GstBuffer * buffer,GstClockTime duration)43 check_buffer_duration (GstBuffer * buffer, GstClockTime duration)
44 {
45 fail_unless (GST_BUFFER_DURATION (buffer) == duration,
46 "expected duration %" GST_TIME_FORMAT
47 ", but got duration %" GST_TIME_FORMAT,
48 GST_TIME_ARGS (duration), GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
49 }
50
51 static void
check_buffer_granulepos(GstBuffer * buffer,gint64 granulepos)52 check_buffer_granulepos (GstBuffer * buffer, gint64 granulepos)
53 {
54 GstClockTime clocktime;
55
56 fail_unless (GST_BUFFER_OFFSET_END (buffer) == granulepos,
57 "expected granulepos %" G_GUINT64_FORMAT
58 ", but got granulepos %" G_GUINT64_FORMAT,
59 granulepos, GST_BUFFER_OFFSET_END (buffer));
60
61 /* contrary to what we record as TIMESTAMP, we can use OFFSET to check
62 * the granulepos correctly here */
63 clocktime = gst_util_uint64_scale (GST_BUFFER_OFFSET_END (buffer), 44100,
64 GST_SECOND);
65
66 fail_unless (clocktime == GST_BUFFER_OFFSET (buffer),
67 "expected OFFSET set to clocktime %" GST_TIME_FORMAT
68 ", but got %" GST_TIME_FORMAT,
69 GST_TIME_ARGS (clocktime), GST_TIME_ARGS (GST_BUFFER_OFFSET (buffer)));
70 }
71
72 /* this check is here to check that the granulepos we derive from the timestamp
73 is about correct. This is "about correct" because you can't precisely go from
74 timestamp to granulepos due to the downward-rounding characteristics of
75 gst_util_uint64_scale, so you check if granulepos is equal to the number, or
76 the number plus one. */
77 static void
check_buffer_granulepos_from_endtime(GstBuffer * buffer,GstClockTime endtime)78 check_buffer_granulepos_from_endtime (GstBuffer * buffer, GstClockTime endtime)
79 {
80 gint64 granulepos, expected;
81
82 granulepos = GST_BUFFER_OFFSET_END (buffer);
83 expected = gst_util_uint64_scale (endtime, 44100, GST_SECOND);
84
85 fail_unless (granulepos == expected || granulepos == expected + 1,
86 "expected granulepos %" G_GUINT64_FORMAT
87 " or %" G_GUINT64_FORMAT
88 ", but got granulepos %" G_GUINT64_FORMAT,
89 expected, expected + 1, granulepos);
90 }
91
GST_START_TEST(test_granulepos_offset)92 GST_START_TEST (test_granulepos_offset)
93 {
94 GstElement *bin;
95 GstPad *pad;
96 gchar *pipe_str;
97 GstBuffer *buffer;
98 GError *error = NULL;
99
100 pipe_str = g_strdup_printf ("audiotestsrc timestamp-offset=%" G_GUINT64_FORMAT
101 " ! audio/x-raw,rate=44100"
102 " ! audioconvert ! vorbisenc ! fakesink name=sink", TIMESTAMP_OFFSET);
103
104 bin = gst_parse_launch (pipe_str, &error);
105 fail_unless (bin != NULL, "Error parsing pipeline: %s",
106 error ? error->message : "(invalid error)");
107 g_free (pipe_str);
108
109 /* get the pad */
110 {
111 GstElement *sink = gst_bin_get_by_name (GST_BIN (bin), "sink");
112
113 fail_unless (sink != NULL, "Could not get fakesink out of bin");
114 pad = gst_element_get_static_pad (sink, "sink");
115 fail_unless (pad != NULL, "Could not get pad out of fakesink");
116 gst_object_unref (sink);
117 }
118
119 gst_buffer_straw_start_pipeline (bin, pad);
120
121 /* header packets should have timestamp == NONE, granulepos 0 */
122 buffer = gst_buffer_straw_get_buffer (bin, pad);
123 GST_DEBUG ("Got buffer in test");
124 check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
125 check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
126 check_buffer_granulepos (buffer, 0);
127 gst_buffer_unref (buffer);
128 GST_DEBUG ("Unreffed buffer in test");
129
130 buffer = gst_buffer_straw_get_buffer (bin, pad);
131 check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
132 check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
133 check_buffer_granulepos (buffer, 0);
134 gst_buffer_unref (buffer);
135
136 buffer = gst_buffer_straw_get_buffer (bin, pad);
137 check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
138 check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
139 check_buffer_granulepos (buffer, 0);
140 gst_buffer_unref (buffer);
141
142 {
143 GstClockTime next_timestamp;
144 gint64 last_granulepos = 0;
145
146 /* first buffer should have timestamp of TIMESTAMP_OFFSET, granulepos to
147 * match the timestamp of the end of the last sample in the output buffer.
148 * Note that one cannot go timestamp->granulepos->timestamp and get the same
149 * value due to loss of precision with granulepos. vorbisenc does take care
150 * to timestamp correctly based on the offset of the input data however, so
151 * it does do sub-granulepos timestamping. */
152 buffer = gst_buffer_straw_get_buffer (bin, pad);
153 last_granulepos = GST_BUFFER_OFFSET_END (buffer);
154 check_buffer_timestamp (buffer, TIMESTAMP_OFFSET);
155 /* don't really have a good way of checking duration... */
156 check_buffer_granulepos_from_endtime (buffer,
157 TIMESTAMP_OFFSET + GST_BUFFER_DURATION (buffer));
158
159 next_timestamp = TIMESTAMP_OFFSET + GST_BUFFER_DURATION (buffer);
160
161 gst_buffer_unref (buffer);
162
163 /* check continuity with the next buffer */
164 buffer = gst_buffer_straw_get_buffer (bin, pad);
165 check_buffer_timestamp (buffer, next_timestamp);
166 check_buffer_duration (buffer,
167 gst_util_uint64_scale (GST_BUFFER_OFFSET_END (buffer), GST_SECOND,
168 44100)
169 - gst_util_uint64_scale (last_granulepos, GST_SECOND, 44100));
170 check_buffer_granulepos_from_endtime (buffer,
171 next_timestamp + GST_BUFFER_DURATION (buffer));
172
173 gst_buffer_unref (buffer);
174 }
175
176 gst_buffer_straw_stop_pipeline (bin, pad);
177
178 gst_object_unref (pad);
179 gst_object_unref (bin);
180 }
181
182 GST_END_TEST;
183
GST_START_TEST(test_timestamps)184 GST_START_TEST (test_timestamps)
185 {
186 GstElement *bin;
187 GstPad *pad;
188 gchar *pipe_str;
189 GstBuffer *buffer;
190 GError *error = NULL;
191
192 pipe_str = g_strdup_printf ("audiotestsrc"
193 " ! audio/x-raw,rate=44100 ! audioconvert ! vorbisenc "
194 " ! fakesink name=sink");
195
196 bin = gst_parse_launch (pipe_str, &error);
197 fail_unless (bin != NULL, "Error parsing pipeline: %s",
198 error ? error->message : "(invalid error)");
199 g_free (pipe_str);
200
201 /* get the pad */
202 {
203 GstElement *sink = gst_bin_get_by_name (GST_BIN (bin), "sink");
204
205 fail_unless (sink != NULL, "Could not get fakesink out of bin");
206 pad = gst_element_get_static_pad (sink, "sink");
207 fail_unless (pad != NULL, "Could not get pad out of fakesink");
208 gst_object_unref (sink);
209 }
210
211 gst_buffer_straw_start_pipeline (bin, pad);
212
213 /* check header packets */
214 buffer = gst_buffer_straw_get_buffer (bin, pad);
215 check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
216 check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
217 check_buffer_granulepos (buffer, 0);
218 gst_buffer_unref (buffer);
219
220 buffer = gst_buffer_straw_get_buffer (bin, pad);
221 check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
222 check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
223 check_buffer_granulepos (buffer, 0);
224 gst_buffer_unref (buffer);
225
226 buffer = gst_buffer_straw_get_buffer (bin, pad);
227 check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
228 check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
229 check_buffer_granulepos (buffer, 0);
230 gst_buffer_unref (buffer);
231
232 {
233 GstClockTime next_timestamp;
234 gint64 last_granulepos;
235
236 /* first buffer has timestamp 0 */
237 buffer = gst_buffer_straw_get_buffer (bin, pad);
238 last_granulepos = GST_BUFFER_OFFSET_END (buffer);
239 check_buffer_timestamp (buffer, 0);
240 /* don't really have a good way of checking duration... */
241 check_buffer_granulepos_from_endtime (buffer, GST_BUFFER_DURATION (buffer));
242
243 next_timestamp = GST_BUFFER_DURATION (buffer);
244
245 gst_buffer_unref (buffer);
246
247 /* check continuity with the next buffer */
248 buffer = gst_buffer_straw_get_buffer (bin, pad);
249 check_buffer_timestamp (buffer, next_timestamp);
250 check_buffer_duration (buffer,
251 gst_util_uint64_scale (GST_BUFFER_OFFSET_END (buffer), GST_SECOND,
252 44100)
253 - gst_util_uint64_scale (last_granulepos, GST_SECOND, 44100));
254 check_buffer_granulepos_from_endtime (buffer,
255 next_timestamp + GST_BUFFER_DURATION (buffer));
256
257 gst_buffer_unref (buffer);
258 }
259
260 gst_buffer_straw_stop_pipeline (bin, pad);
261
262 gst_object_unref (pad);
263 gst_object_unref (bin);
264 }
265
266 GST_END_TEST;
267
268 static GstPadProbeReturn
drop_second_data_buffer(GstPad * droppad,GstPadProbeInfo * info,gpointer unused)269 drop_second_data_buffer (GstPad * droppad, GstPadProbeInfo * info,
270 gpointer unused)
271 {
272 GstBuffer *buffer = GST_PAD_PROBE_INFO_BUFFER (info);
273 GstPadProbeReturn res = GST_PAD_PROBE_OK;
274
275 if (GST_BUFFER_OFFSET (buffer) == 4096)
276 res = GST_PAD_PROBE_DROP;
277
278 GST_DEBUG ("dropping %d", res);
279
280 return res;
281 }
282
GST_START_TEST(test_discontinuity)283 GST_START_TEST (test_discontinuity)
284 {
285 GstElement *bin;
286 GstPad *pad, *droppad;
287 gchar *pipe_str;
288 GstBuffer *buffer;
289 GError *error = NULL;
290 guint drop_id;
291
292 /* make audioencoder act sufficiently pedantic */
293 pipe_str = g_strdup_printf ("audiotestsrc samplesperbuffer=1024"
294 " ! audio/x-raw,rate=44100 ! audioconvert "
295 " ! vorbisenc tolerance=10000000 name=enc ! fakesink name=sink");
296
297 bin = gst_parse_launch (pipe_str, &error);
298 fail_unless (bin != NULL, "Error parsing pipeline: %s",
299 error ? error->message : "(invalid error)");
300 g_free (pipe_str);
301
302 /* the plan: same as test_timestamps, but dropping a buffer and seeing if
303 vorbisenc correctly notes the discontinuity */
304
305 /* get the pad to use to drop buffers */
306 {
307 GstElement *sink = gst_bin_get_by_name (GST_BIN (bin), "enc");
308
309 fail_unless (sink != NULL, "Could not get vorbisenc out of bin");
310 droppad = gst_element_get_static_pad (sink, "sink");
311 fail_unless (droppad != NULL, "Could not get pad out of vorbisenc");
312 gst_object_unref (sink);
313 }
314
315 /* get the pad */
316 {
317 GstElement *sink = gst_bin_get_by_name (GST_BIN (bin), "sink");
318
319 fail_unless (sink != NULL, "Could not get fakesink out of bin");
320 pad = gst_element_get_static_pad (sink, "sink");
321 fail_unless (pad != NULL, "Could not get pad out of fakesink");
322 gst_object_unref (sink);
323 }
324
325 drop_id = gst_pad_add_probe (droppad, GST_PAD_PROBE_TYPE_BUFFER,
326 (GstPadProbeCallback) drop_second_data_buffer, NULL, NULL);
327 gst_buffer_straw_start_pipeline (bin, pad);
328
329 /* check header packets */
330 buffer = gst_buffer_straw_get_buffer (bin, pad);
331 check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
332 check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
333 check_buffer_granulepos (buffer, 0);
334 gst_buffer_unref (buffer);
335
336 buffer = gst_buffer_straw_get_buffer (bin, pad);
337 check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
338 check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
339 check_buffer_granulepos (buffer, 0);
340 gst_buffer_unref (buffer);
341
342 buffer = gst_buffer_straw_get_buffer (bin, pad);
343 check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
344 check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
345 check_buffer_granulepos (buffer, 0);
346 gst_buffer_unref (buffer);
347
348 {
349 GstClockTime next_timestamp = 0;
350 gint64 last_granulepos = 0, granulepos;
351 gint i;
352
353 for (i = 0; i < 10; i++) {
354 buffer = gst_buffer_straw_get_buffer (bin, pad);
355 granulepos = GST_BUFFER_OFFSET_END (buffer);
356 /* discont is either at start, or following gap */
357 if (GST_BUFFER_IS_DISCONT (buffer)) {
358 if (next_timestamp) {
359 fail_unless (granulepos - last_granulepos > 1024,
360 "expected discont of at least 1024 samples");
361 next_timestamp = GST_BUFFER_TIMESTAMP (buffer);
362 }
363 }
364 check_buffer_timestamp (buffer, next_timestamp);
365 next_timestamp += GST_BUFFER_DURATION (buffer);
366 last_granulepos = granulepos;
367 gst_buffer_unref (buffer);
368 }
369 }
370
371 gst_buffer_straw_stop_pipeline (bin, pad);
372 gst_pad_remove_probe (droppad, drop_id);
373
374 gst_object_unref (droppad);
375 gst_object_unref (pad);
376 gst_object_unref (bin);
377 }
378
379 GST_END_TEST;
380
381 #endif /* #ifndef GST_DISABLE_PARSE */
382
383 static Suite *
vorbisenc_suite(void)384 vorbisenc_suite (void)
385 {
386 Suite *s = suite_create ("vorbisenc");
387 TCase *tc_chain = tcase_create ("general");
388
389 suite_add_tcase (s, tc_chain);
390 #ifndef GST_DISABLE_PARSE
391 tcase_add_test (tc_chain, test_granulepos_offset);
392 tcase_add_test (tc_chain, test_timestamps);
393 tcase_add_test (tc_chain, test_discontinuity);
394 #endif
395
396 return s;
397 }
398
399 GST_CHECK_MAIN (vorbisenc);
400