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