Added gst-plugins-base-subtitles0.10-0.10.34 for Meego Harmattan 1.2
[mafwsubrenderer] / gst-plugins-base-subtitles0.10 / tests / check / pipelines / theoraenc.c
1 /* GStreamer
2  *
3  * unit test for theoraenc
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., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include <gst/check/gstcheck.h>
24 #include <gst/check/gstbufferstraw.h>
25
26 #include <theora/theora.h>
27
28 #ifndef GST_DISABLE_PARSE
29
30 #define TIMESTAMP_OFFSET G_GINT64_CONSTANT(3249870963)
31 #define FRAMERATE 10
32
33 /* I know all of these have a shift of 6 bits */
34 #define GRANULEPOS_SHIFT 6
35
36
37 #define check_buffer_is_header(buffer,is_header) \
38   fail_unless (GST_BUFFER_FLAG_IS_SET (buffer,   \
39           GST_BUFFER_FLAG_IN_CAPS) == is_header, \
40       "GST_BUFFER_IN_CAPS is set to %d but expected %d", \
41       GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_IN_CAPS), is_header)
42
43 #define check_buffer_timestamp(buffer,timestamp) \
44   fail_unless (GST_BUFFER_TIMESTAMP (buffer) == timestamp, \
45       "expected timestamp %" GST_TIME_FORMAT \
46       ", but got timestamp %" GST_TIME_FORMAT, \
47       GST_TIME_ARGS (timestamp), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)))
48
49 #define check_buffer_duration(buffer,duration) \
50   fail_unless (GST_BUFFER_DURATION (buffer) == duration, \
51       "expected duration %" GST_TIME_FORMAT \
52       ", but got duration %" GST_TIME_FORMAT, \
53       GST_TIME_ARGS (duration), GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)))
54
55 static gboolean old_libtheora;
56
57 static void
58 check_libtheora (void)
59 {
60   old_libtheora = (theora_version_number () <= 0x00030200);
61 }
62
63 static void
64 check_buffer_granulepos (GstBuffer * buffer, gint64 granulepos)
65 {
66   GstClockTime clocktime;
67   int framecount;
68
69   /* With old versions of libtheora, the granulepos represented the
70    * start time, not end time. Adapt for that. */
71   if (old_libtheora) {
72     if (granulepos >> GRANULEPOS_SHIFT)
73       granulepos -= 1 << GRANULEPOS_SHIFT;
74     else if (granulepos)
75       granulepos -= 1;
76   }
77
78   fail_unless (GST_BUFFER_OFFSET_END (buffer) == granulepos,
79       "expected granulepos %" G_GUINT64_FORMAT
80       ", but got granulepos %" G_GUINT64_FORMAT,
81       granulepos, GST_BUFFER_OFFSET_END (buffer));
82
83   /* contrary to what we record as TIMESTAMP, we can use OFFSET to check
84    * the granulepos correctly here */
85   framecount = GST_BUFFER_OFFSET_END (buffer);
86   framecount = granulepos >> GRANULEPOS_SHIFT;
87   framecount += granulepos & ((1 << GRANULEPOS_SHIFT) - 1);
88   clocktime = gst_util_uint64_scale (framecount, GST_SECOND, FRAMERATE);
89
90   fail_unless (clocktime == GST_BUFFER_OFFSET (buffer),
91       "expected OFFSET set to clocktime %" GST_TIME_FORMAT
92       ", but got %" GST_TIME_FORMAT,
93       GST_TIME_ARGS (clocktime), GST_TIME_ARGS (GST_BUFFER_OFFSET (buffer)));
94 }
95
96 /* this check is here to check that the granulepos we derive from the
97    timestamp is about correct. This is "about correct" because you can't
98    precisely go from timestamp to granulepos due to the downward-rounding
99    characteristics of gst_util_uint64_scale, so you check if granulepos is
100    equal to the number, or the number plus one. */
101 /* should be from_endtime, but theora's granulepos mapping is "special" */
102 static void
103 check_buffer_granulepos_from_starttime (GstBuffer * buffer,
104     GstClockTime starttime)
105 {
106   gint64 granulepos, expected, framecount;
107
108   granulepos = GST_BUFFER_OFFSET_END (buffer);
109   /* Now convert to 'granulepos for start time', depending on libtheora 
110    * version */
111   if (!old_libtheora) {
112     if (granulepos & ((1 << GRANULEPOS_SHIFT) - 1))
113       granulepos -= 1;
114     else if (granulepos)
115       granulepos -= 1 << GRANULEPOS_SHIFT;
116   }
117
118   framecount = granulepos >> GRANULEPOS_SHIFT;
119   framecount += granulepos & ((1 << GRANULEPOS_SHIFT) - 1);
120   expected = gst_util_uint64_scale (starttime, FRAMERATE, GST_SECOND);
121
122   fail_unless (framecount == expected || framecount == expected + 1,
123       "expected frame count %" G_GUINT64_FORMAT
124       " or %" G_GUINT64_FORMAT
125       ", but got frame count %" G_GUINT64_FORMAT,
126       expected, expected + 1, framecount);
127 }
128
129 GST_START_TEST (test_granulepos_offset)
130 {
131   GstElement *bin;
132   GstPad *pad;
133   gchar *pipe_str;
134   GstBuffer *buffer;
135   GError *error = NULL;
136
137   pipe_str = g_strdup_printf ("videotestsrc timestamp-offset=%" G_GUINT64_FORMAT
138       " num-buffers=10 ! video/x-raw-yuv,format=(fourcc)I420,framerate=10/1"
139       " ! theoraenc ! fakesink name=fs0", TIMESTAMP_OFFSET);
140
141   bin = gst_parse_launch (pipe_str, &error);
142   fail_unless (bin != NULL, "Error parsing pipeline: %s",
143       error ? error->message : "(invalid error)");
144   g_free (pipe_str);
145
146   /* get the pad */
147   {
148     GstElement *sink = gst_bin_get_by_name (GST_BIN (bin), "fs0");
149
150     fail_unless (sink != NULL, "Could not get fakesink out of bin");
151     pad = gst_element_get_static_pad (sink, "sink");
152     fail_unless (pad != NULL, "Could not get pad out of fakesink");
153     gst_object_unref (sink);
154   }
155
156   gst_buffer_straw_start_pipeline (bin, pad);
157
158   /* header packets should have timestamp == NONE, granulepos 0, IN_CAPS */
159   buffer = gst_buffer_straw_get_buffer (bin, pad);
160   check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
161   check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
162   check_buffer_granulepos (buffer, 0);
163   check_buffer_is_header (buffer, TRUE);
164   gst_buffer_unref (buffer);
165
166   buffer = gst_buffer_straw_get_buffer (bin, pad);
167   check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
168   check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
169   check_buffer_granulepos (buffer, 0);
170   check_buffer_is_header (buffer, TRUE);
171   gst_buffer_unref (buffer);
172
173   buffer = gst_buffer_straw_get_buffer (bin, pad);
174   check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
175   check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
176   check_buffer_granulepos (buffer, 0);
177   check_buffer_is_header (buffer, TRUE);
178   gst_buffer_unref (buffer);
179
180   {
181     GstClockTime next_timestamp;
182     gint64 last_granulepos;
183
184     /* first buffer should have timestamp of TIMESTAMP_OFFSET, granulepos to
185      * match the timestamp of the end of the last sample in the output buffer.
186      * Note that one cannot go timestamp->granulepos->timestamp and get the
187      * same value due to loss of precision with granulepos. theoraenc does
188      * take care to timestamp correctly based on the offset of the input data
189      * however, so it does do sub-granulepos timestamping. */
190     buffer = gst_buffer_straw_get_buffer (bin, pad);
191     last_granulepos = GST_BUFFER_OFFSET_END (buffer);
192     check_buffer_timestamp (buffer, TIMESTAMP_OFFSET);
193     /* don't really have a good way of checking duration... */
194     check_buffer_granulepos_from_starttime (buffer, TIMESTAMP_OFFSET);
195     check_buffer_is_header (buffer, FALSE);
196
197     next_timestamp = TIMESTAMP_OFFSET + GST_BUFFER_DURATION (buffer);
198
199     gst_buffer_unref (buffer);
200
201     /* check continuity with the next buffer */
202     buffer = gst_buffer_straw_get_buffer (bin, pad);
203     check_buffer_timestamp (buffer, next_timestamp);
204     check_buffer_duration (buffer,
205         gst_util_uint64_scale (GST_BUFFER_OFFSET_END (buffer), GST_SECOND,
206             FRAMERATE)
207         - gst_util_uint64_scale (last_granulepos, GST_SECOND, FRAMERATE));
208     check_buffer_granulepos_from_starttime (buffer, next_timestamp);
209     check_buffer_is_header (buffer, FALSE);
210
211     gst_buffer_unref (buffer);
212   }
213
214   gst_buffer_straw_stop_pipeline (bin, pad);
215
216   gst_object_unref (pad);
217   gst_object_unref (bin);
218 }
219
220 GST_END_TEST;
221
222 GST_START_TEST (test_continuity)
223 {
224   GstElement *bin;
225   GstPad *pad;
226   gchar *pipe_str;
227   GstBuffer *buffer;
228   GError *error = NULL;
229
230   pipe_str = g_strdup_printf ("videotestsrc num-buffers=10"
231       " ! video/x-raw-yuv,format=(fourcc)I420,framerate=10/1"
232       " ! theoraenc ! fakesink name=fs0");
233
234   bin = gst_parse_launch (pipe_str, &error);
235   fail_unless (bin != NULL, "Error parsing pipeline: %s",
236       error ? error->message : "(invalid error)");
237   g_free (pipe_str);
238
239   /* get the pad */
240   {
241     GstElement *sink = gst_bin_get_by_name (GST_BIN (bin), "fs0");
242
243     fail_unless (sink != NULL, "Could not get fakesink out of bin");
244     pad = gst_element_get_static_pad (sink, "sink");
245     fail_unless (pad != NULL, "Could not get pad out of fakesink");
246     gst_object_unref (sink);
247   }
248
249   gst_buffer_straw_start_pipeline (bin, pad);
250
251   /* header packets should have timestamp == NONE, granulepos 0 */
252   buffer = gst_buffer_straw_get_buffer (bin, pad);
253   check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
254   check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
255   check_buffer_granulepos (buffer, 0);
256   check_buffer_is_header (buffer, TRUE);
257   gst_buffer_unref (buffer);
258
259   buffer = gst_buffer_straw_get_buffer (bin, pad);
260   check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
261   check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
262   check_buffer_granulepos (buffer, 0);
263   check_buffer_is_header (buffer, TRUE);
264   gst_buffer_unref (buffer);
265
266   buffer = gst_buffer_straw_get_buffer (bin, pad);
267   check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
268   check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
269   check_buffer_granulepos (buffer, 0);
270   check_buffer_is_header (buffer, TRUE);
271   gst_buffer_unref (buffer);
272
273   {
274     GstClockTime next_timestamp;
275     gint64 last_granulepos;
276
277     /* first buffer should have timestamp of TIMESTAMP_OFFSET, granulepos to
278      * match the timestamp of the end of the last sample in the output buffer.
279      * Note that one cannot go timestamp->granulepos->timestamp and get the
280      * same value due to loss of precision with granulepos. theoraenc does
281      * take care to timestamp correctly based on the offset of the input data
282      * however, so it does do sub-granulepos timestamping. */
283     buffer = gst_buffer_straw_get_buffer (bin, pad);
284     last_granulepos = GST_BUFFER_OFFSET_END (buffer);
285     check_buffer_timestamp (buffer, 0);
286     /* plain division because I know the answer is exact */
287     check_buffer_duration (buffer, GST_SECOND / 10);
288     check_buffer_granulepos (buffer, 1 << GRANULEPOS_SHIFT);
289     check_buffer_is_header (buffer, FALSE);
290
291     next_timestamp = GST_BUFFER_DURATION (buffer);
292
293     gst_buffer_unref (buffer);
294
295     /* check continuity with the next buffer */
296     buffer = gst_buffer_straw_get_buffer (bin, pad);
297     check_buffer_timestamp (buffer, next_timestamp);
298     check_buffer_duration (buffer, GST_SECOND / 10);
299     check_buffer_granulepos (buffer, (1 << GRANULEPOS_SHIFT) | 1);
300     check_buffer_is_header (buffer, FALSE);
301
302     gst_buffer_unref (buffer);
303   }
304
305   gst_buffer_straw_stop_pipeline (bin, pad);
306
307   gst_object_unref (pad);
308   gst_object_unref (bin);
309 }
310
311 GST_END_TEST;
312
313 static gboolean
314 drop_second_data_buffer (GstPad * droppad, GstBuffer * buffer, gpointer unused)
315 {
316   return !(GST_BUFFER_OFFSET (buffer) == 1);
317 }
318
319 GST_START_TEST (test_discontinuity)
320 {
321   GstElement *bin;
322   GstPad *pad, *droppad;
323   gchar *pipe_str;
324   GstBuffer *buffer;
325   GError *error = NULL;
326   guint drop_id;
327
328   pipe_str = g_strdup_printf ("videotestsrc num-buffers=10"
329       " ! video/x-raw-yuv,format=(fourcc)I420,framerate=10/1"
330       " ! theoraenc ! fakesink name=fs0");
331
332   bin = gst_parse_launch (pipe_str, &error);
333   fail_unless (bin != NULL, "Error parsing pipeline: %s",
334       error ? error->message : "(invalid error)");
335   g_free (pipe_str);
336
337   /* the plan: same as test_continuity, but dropping a buffer and seeing if
338      theoraenc correctly notes the discontinuity */
339
340   /* get the pad to use to drop buffers */
341   {
342     GstElement *sink = gst_bin_get_by_name (GST_BIN (bin), "theoraenc0");
343
344     fail_unless (sink != NULL, "Could not get theoraenc out of bin");
345     droppad = gst_element_get_static_pad (sink, "sink");
346     fail_unless (droppad != NULL, "Could not get pad out of theoraenc");
347     gst_object_unref (sink);
348   }
349
350   /* get the pad */
351   {
352     GstElement *sink = gst_bin_get_by_name (GST_BIN (bin), "fs0");
353
354     fail_unless (sink != NULL, "Could not get fakesink out of bin");
355     pad = gst_element_get_static_pad (sink, "sink");
356     fail_unless (pad != NULL, "Could not get pad out of fakesink");
357     gst_object_unref (sink);
358   }
359
360   drop_id = gst_pad_add_buffer_probe (droppad,
361       G_CALLBACK (drop_second_data_buffer), NULL);
362   gst_buffer_straw_start_pipeline (bin, pad);
363
364   /* header packets should have timestamp == NONE, granulepos 0 */
365   buffer = gst_buffer_straw_get_buffer (bin, pad);
366   check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
367   check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
368   check_buffer_granulepos (buffer, 0);
369   check_buffer_is_header (buffer, TRUE);
370   gst_buffer_unref (buffer);
371
372   buffer = gst_buffer_straw_get_buffer (bin, pad);
373   check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
374   check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
375   check_buffer_granulepos (buffer, 0);
376   check_buffer_is_header (buffer, TRUE);
377   gst_buffer_unref (buffer);
378
379   buffer = gst_buffer_straw_get_buffer (bin, pad);
380   check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
381   check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
382   check_buffer_granulepos (buffer, 0);
383   check_buffer_is_header (buffer, TRUE);
384   gst_buffer_unref (buffer);
385
386   {
387     buffer = gst_buffer_straw_get_buffer (bin, pad);
388     check_buffer_timestamp (buffer, 0);
389     /* plain division because I know the answer is exact */
390     check_buffer_duration (buffer, GST_SECOND / 10);
391     check_buffer_granulepos (buffer, 1 << GRANULEPOS_SHIFT);
392     check_buffer_is_header (buffer, FALSE);
393     fail_if (GST_BUFFER_IS_DISCONT (buffer), "expected continuous buffer yo");
394     gst_buffer_unref (buffer);
395
396     /* check discontinuity with the next buffer */
397     buffer = gst_buffer_straw_get_buffer (bin, pad);
398     check_buffer_duration (buffer, GST_SECOND / 10);
399     /* After a discont, we'll always get a keyframe, so this one should be 
400      * 3<<GRANULEPOS_SHIFT */
401     check_buffer_granulepos (buffer, 3 << GRANULEPOS_SHIFT);
402     check_buffer_is_header (buffer, FALSE);
403     fail_unless (GST_BUFFER_IS_DISCONT (buffer),
404         "expected discontinuous buffer yo");
405     gst_buffer_unref (buffer);
406
407     /* Then the buffer after that should be continuous */
408     buffer = gst_buffer_straw_get_buffer (bin, pad);
409     fail_if (GST_BUFFER_IS_DISCONT (buffer), "expected continuous buffer yo");
410     /* plain division because I know the answer is exact */
411     check_buffer_duration (buffer, GST_SECOND / 10);
412     check_buffer_granulepos (buffer, (3 << GRANULEPOS_SHIFT) | 1);
413     check_buffer_is_header (buffer, FALSE);
414     gst_buffer_unref (buffer);
415   }
416
417   gst_buffer_straw_stop_pipeline (bin, pad);
418   gst_pad_remove_buffer_probe (droppad, drop_id);
419
420   gst_object_unref (droppad);
421   gst_object_unref (pad);
422   gst_object_unref (bin);
423 }
424
425 GST_END_TEST;
426
427 #endif /* #ifndef GST_DISABLE_PARSE */
428
429 static Suite *
430 theoraenc_suite (void)
431 {
432   Suite *s = suite_create ("theoraenc");
433   TCase *tc_chain = tcase_create ("general");
434
435   suite_add_tcase (s, tc_chain);
436
437   check_libtheora ();
438
439 #ifndef GST_DISABLE_PARSE
440   tcase_add_test (tc_chain, test_granulepos_offset);
441   tcase_add_test (tc_chain, test_continuity);
442   tcase_add_test (tc_chain, test_discontinuity);
443 #endif
444
445   return s;
446 }
447
448 int
449 main (int argc, char **argv)
450 {
451   int nf;
452
453   Suite *s = theoraenc_suite ();
454   SRunner *sr = srunner_create (s);
455
456   gst_check_init (&argc, &argv);
457
458   srunner_run_all (sr, CK_NORMAL);
459   nf = srunner_ntests_failed (sr);
460   srunner_free (sr);
461
462   return nf;
463 }