2 * cmmlenc.c - GStreamer CMML decoder test suite
3 * Copyright (C) 2005 Alessandro Decina
6 * Alessandro Decina <alessandro@nnva.org>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
24 #include <gst/check/gstcheck.h>
26 #include <gst/tag/tag.h>
28 #define SINK_CAPS "text/x-cmml"
29 #define SRC_CAPS "text/x-cmml"
31 #define IDENT_HEADER \
32 "CMML\x00\x00\x00\x00"\
34 "\xe8\x03\x00\x00\x00\x00\x00\x00"\
35 "\x01\x00\x00\x00\x00\x00\x00\x00"\
38 #define XML_PREAMBLE \
39 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"\
40 "<!DOCTYPE cmml SYSTEM \"cmml.dtd\">\n"
45 #define PROCESSING_INSTRUCTION \
49 XML_PREAMBLE START_TAG
51 #define PREAMBLE_ENCODED \
52 XML_PREAMBLE PROCESSING_INSTRUCTION
55 "<stream timebase=\"10\">"\
56 "<import src=\"test.ogg\"/>"\
57 "<import src=\"test1.ogg\"/>"\
60 #define STREAM_TAG_ENCODED STREAM_TAG
64 "<title>The Research Hunter</title>"\
65 "<meta name=\"DC.audience\" content=\"General\"/>"\
66 "<meta name=\"DC.author\" content=\"CSIRO Publishing\"/>"\
67 "<meta name=\"DC.format\" content=\"video\"/>"\
68 "<meta name=\"DC.language\" content=\"English\"/>"\
69 "<meta name=\"DC.publisher\" content=\"CSIRO Australia\"/>"\
72 #define HEAD_TAG_ENCODED HEAD_TAG
74 #define CLIP_TEMPLATE \
75 "<clip id=\"%s\" track=\"%s\" start=\"%s\">"\
76 "<a href=\"http://www.annodex.org/\">http://www.annodex.org</a>"\
77 "<img src=\"images/index.jpg\"/>"\
78 "<desc>Annodex Foundation</desc>"\
79 "<meta name=\"test\" content=\"test content\"/>"\
82 #define ENDED_CLIP_TEMPLATE \
83 "<clip id=\"%s\" track=\"%s\" start=\"%s\" end=\"%s\">"\
84 "<a href=\"http://www.annodex.org/\">http://www.annodex.org</a>"\
85 "<img src=\"images/index.jpg\"/>"\
86 "<desc>Annodex Foundation</desc>"\
87 "<meta name=\"test\" content=\"test content\"/>"\
90 #define CLIP_TEMPLATE_ENCODED \
91 "<clip id=\"%s\" track=\"%s\">"\
92 "<a href=\"http://www.annodex.org/\">http://www.annodex.org</a>"\
93 "<img src=\"images/index.jpg\"/>"\
94 "<desc>Annodex Foundation</desc>"\
95 "<meta name=\"test\" content=\"test content\"/>"\
98 #define EMPTY_CLIP_TEMPLATE_ENCODED \
99 "<clip track=\"%s\"/>"
101 #define fail_unless_equals_flow_return(a, b) \
103 gchar *a_up = g_ascii_strup (gst_flow_get_name (a), -1); \
104 gchar *b_up = g_ascii_strup (gst_flow_get_name (b), -1); \
105 fail_unless (a == b, \
106 "'" #a "' (GST_FLOW_%s) is not equal to '" #b "' (GST_FLOW_%s)", \
112 static GList *current_buf;
113 static guint64 granulerate;
114 static guint8 granuleshift;
115 static GstElement *cmmlenc;
117 static GstFlowReturn flow;
118 static GstPad *srcpad, *sinkpad;
120 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
123 GST_STATIC_CAPS (SINK_CAPS)
126 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
129 GST_STATIC_CAPS (SRC_CAPS)
133 buffer_new (const gchar * buffer_data, guint size)
138 buffer = gst_buffer_new_and_alloc (size);
139 memcpy (GST_BUFFER_DATA (buffer), buffer_data, size);
140 caps = gst_caps_from_string (SRC_CAPS);
141 gst_buffer_set_caps (buffer, caps);
142 gst_caps_unref (caps);
148 buffer_unref (void *buffer, void *user_data)
150 gst_buffer_unref (GST_BUFFER (buffer));
156 guint64 granulerate_n, granulerate_d;
158 GST_DEBUG ("setup_cmmlenc");
160 cmmlenc = gst_check_setup_element ("cmmlenc");
161 srcpad = gst_check_setup_src_pad (cmmlenc, &srctemplate, NULL);
162 sinkpad = gst_check_setup_sink_pad (cmmlenc, &sinktemplate, NULL);
163 gst_pad_set_active (srcpad, TRUE);
164 gst_pad_set_active (sinkpad, TRUE);
166 bus = gst_bus_new ();
167 gst_element_set_bus (cmmlenc, bus);
169 fail_unless (gst_element_set_state (cmmlenc,
170 GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE,
171 "could not set to playing");
173 g_object_get (cmmlenc, "granule-rate-numerator", &granulerate_n,
174 "granule-rate-denominator", &granulerate_d,
175 "granule-shift", &granuleshift, NULL);
177 granulerate = GST_SECOND * granulerate_d / granulerate_n;
181 teardown_cmmlenc (void)
183 /* free encoded buffers */
184 g_list_foreach (buffers, buffer_unref, NULL);
185 g_list_free (buffers);
189 gst_bus_set_flushing (bus, TRUE);
190 gst_object_unref (bus);
192 GST_DEBUG ("teardown_cmmlenc");
193 gst_pad_set_active (srcpad, FALSE);
194 gst_pad_set_active (sinkpad, FALSE);
195 gst_check_teardown_src_pad (cmmlenc);
196 gst_check_teardown_sink_pad (cmmlenc);
197 gst_check_teardown_element (cmmlenc);
201 check_output_buffer_is_equal (const gchar * name,
202 const gchar * data, gint refcount)
206 if (current_buf == NULL)
207 current_buf = buffers;
209 current_buf = g_list_next (current_buf);
211 fail_unless (current_buf != NULL);
212 buffer = GST_BUFFER (current_buf->data);
214 ASSERT_OBJECT_REFCOUNT (buffer, name, refcount);
215 fail_unless (memcmp (GST_BUFFER_DATA (buffer), data,
216 GST_BUFFER_SIZE (buffer)) == 0,
217 "'%s' (%s) is not equal to (%s)", name, GST_BUFFER_DATA (buffer), data);
221 push_data (const gchar * name, const gchar * data, gint size)
226 buffer = buffer_new (data, size);
227 res = gst_pad_push (srcpad, buffer);
235 /* push the cmml start tag */
236 flow = push_data ("preamble", PREAMBLE, strlen (PREAMBLE));
237 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
239 /* push the stream tag */
240 flow = push_data ("stream", STREAM_TAG, strlen (STREAM_TAG));
241 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
242 /* push the head tag */
243 flow = push_data ("head", HEAD_TAG, strlen (HEAD_TAG));
244 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
246 /* should output 3 buffers: the ident, preamble and head headers */
247 fail_unless_equals_int (g_list_length (buffers), 3);
249 /* check the ident header */
250 check_output_buffer_is_equal ("cmml-ident-buffer", IDENT_HEADER, 1);
252 /* check the cmml processing instruction */
253 check_output_buffer_is_equal ("cmml-preamble-buffer", PREAMBLE_ENCODED, 1);
255 /* check the encoded head tag */
256 check_output_buffer_is_equal ("head-tag-buffer", HEAD_TAG_ENCODED, 1);
260 push_clip (const gchar * name, const gchar * track,
261 const gchar * start, const gchar * end)
267 clip = g_strdup_printf (ENDED_CLIP_TEMPLATE, name, track, start, end);
269 clip = g_strdup_printf (CLIP_TEMPLATE, name, track, start);
270 res = push_data (name, clip, strlen (clip));
277 check_clip_times (GstBuffer * buffer, GstClockTime start, GstClockTime prev)
279 guint64 keyindex, keyoffset, granulepos;
281 granulepos = GST_BUFFER_OFFSET_END (buffer);
282 if (granuleshift == 0 || granuleshift == 64)
285 keyindex = granulepos >> granuleshift;
286 keyoffset = granulepos - (keyindex << granuleshift);
287 fail_unless_equals_uint64 (keyindex * granulerate, prev);
288 fail_unless_equals_uint64 ((keyindex + keyoffset) * granulerate, start);
292 check_clip (const gchar * name, const gchar * track,
293 GstClockTime start, GstClockTime prev)
298 encoded_clip = g_strdup_printf (CLIP_TEMPLATE_ENCODED, name, track);
299 check_output_buffer_is_equal (name, encoded_clip, 1);
300 g_free (encoded_clip);
301 buffer = GST_BUFFER (current_buf->data);
302 check_clip_times (buffer, start, prev);
306 check_empty_clip (const gchar * name, const gchar * track,
307 GstClockTime start, GstClockTime prev)
312 encoded_clip = g_strdup_printf (EMPTY_CLIP_TEMPLATE_ENCODED, track);
313 check_output_buffer_is_equal (name, encoded_clip, 1);
314 g_free (encoded_clip);
315 buffer = GST_BUFFER (current_buf->data);
316 check_clip_times (buffer, start, prev);
319 GST_START_TEST (test_enc)
323 flow = push_clip ("clip-1", "default", "1.234", NULL);
324 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
325 check_clip ("clip-1", "default", 1 * GST_SECOND + 234 * GST_MSECOND, 0);
327 flow = push_clip ("clip-2", "default", "5.678", NULL);
328 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
329 check_clip ("clip-2", "default",
330 5 * GST_SECOND + 678 * GST_MSECOND, 1 * GST_SECOND + 234 * GST_MSECOND);
332 flow = push_clip ("clip-3", "othertrack", "9.123", NULL);
333 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
334 check_clip ("clip-3", "othertrack", 9 * GST_SECOND + 123 * GST_MSECOND, 0);
336 flow = push_data ("end-tag", "</cmml>", strlen ("</cmml>"));
337 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
338 check_output_buffer_is_equal ("cmml-eos", NULL, 1);
343 GST_START_TEST (test_clip_end_time)
347 /* push a clip that starts at 1.234 an ends at 2.234 */
348 flow = push_clip ("clip-1", "default", "1.234", "2.234");
349 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
350 check_clip ("clip-1", "default", 1 * GST_SECOND + 234 * GST_MSECOND, 0);
352 /* now check that the encoder created an empty clip starting at 2.234 to mark
353 * the end of clip-1 */
354 check_empty_clip ("clip-1-end", "default",
355 2 * GST_SECOND + 234 * GST_MSECOND, 1 * GST_SECOND + 234 * GST_MSECOND);
357 /* now push another clip on the same track and check that the keyindex part of
358 * the granulepos points to clip-1 and not to the empty clip */
359 flow = push_clip ("clip-2", "default", "5", NULL);
360 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
361 check_clip ("clip-2", "default",
362 5 * GST_SECOND, 1 * GST_SECOND + 234 * GST_MSECOND);
367 GST_START_TEST (test_time_order)
371 /* clips belonging to the same track must have start times in non decreasing
373 flow = push_clip ("clip-1", "default", "1000:00:00.000", NULL);
374 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
375 check_clip ("clip-1", "default", 3600 * 1000 * GST_SECOND, 0);
377 /* this will make the encoder throw an error message */
378 flow = push_clip ("clip-2", "default", "5.678", NULL);
379 fail_unless_equals_flow_return (flow, GST_FLOW_ERROR);
381 flow = push_clip ("clip-3", "default", "1000:00:00.001", NULL);
382 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
383 check_clip ("clip-3", "default",
384 3600 * 1000 * GST_SECOND + 1 * GST_MSECOND, 3600 * 1000 * GST_SECOND);
386 /* tracks don't interfere with each other */
387 flow = push_clip ("clip-4", "othertrack", "9.123", NULL);
388 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
389 check_clip ("clip-4", "othertrack", 9 * GST_SECOND + 123 * GST_MSECOND, 0);
394 GST_START_TEST (test_time_parsing)
398 flow = push_clip ("bad-msecs", "default", "0.1000", NULL);
399 fail_unless_equals_flow_return (flow, GST_FLOW_ERROR);
401 flow = push_clip ("bad-secs", "default", "00:00:60.123", NULL);
402 fail_unless_equals_flow_return (flow, GST_FLOW_ERROR);
404 flow = push_clip ("bad-minutes", "default", "00:60:12.345", NULL);
405 fail_unless_equals_flow_return (flow, GST_FLOW_ERROR);
407 /* this fails since we can't store 5124096 * 3600 * GST_SECOND in a
409 flow = push_clip ("bad-hours", "default", "5124096:00:00.000", NULL);
410 fail_unless_equals_flow_return (flow, GST_FLOW_ERROR);
415 GST_START_TEST (test_time_limits)
419 /* ugly hack to make sure that the following checks actually overflow parsing
420 * the times in gst_cmml_clock_time_from_npt rather than converting them to
421 * granulepos in gst_cmml_clock_time_to_granule */
423 g_object_set (cmmlenc, "granule-shift", granuleshift, NULL);
425 /* 5124095:34:33.709 is the max npt-hhmmss time representable with
427 flow = push_clip ("max-npt-hhmmss", "foo", "5124095:34:33.709", NULL);
428 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
429 check_clip ("max-npt-hhmmss", "foo",
430 (GstClockTime) 5124095 * 3600 * GST_SECOND + 34 * 60 * GST_SECOND +
431 33 * GST_SECOND + 709 * GST_MSECOND, 0);
433 flow = push_clip ("overflow-max-npt-hhmmss", "overflows",
434 "5124095:34:33.710", NULL);
435 fail_unless_equals_flow_return (flow, GST_FLOW_ERROR);
437 /* 18446744073.709 is the max ntp-sec time */
438 flow = push_clip ("max-npt-secs", "bar", "18446744073.709", NULL);
439 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
440 check_clip ("max-npt-secs", "bar",
441 (GstClockTime) 5124095 * 3600 * GST_SECOND + 34 * 60 * GST_SECOND +
442 33 * GST_SECOND + 709 * GST_MSECOND, 0);
444 /* overflow doing 18446744074 * GST_SECOND */
445 flow = push_clip ("overflow-max-npt-secs", "overflows",
446 "18446744074.000", NULL);
447 fail_unless_equals_flow_return (flow, GST_FLOW_ERROR);
449 /* overflow doing seconds + milliseconds */
450 flow = push_clip ("overflow-max-npt-secs-msecs", "overflows",
451 "18446744073.710", NULL);
452 fail_unless_equals_flow_return (flow, GST_FLOW_ERROR);
454 /* reset granuleshift to 32 to check keyoffset overflows in
455 * gst_cmml_clock_time_to_granule */
457 g_object_set (cmmlenc, "granule-shift", granuleshift, NULL);
459 /* 1193:02:47.295 is the max time we can encode in the keyoffset part of a
460 * granulepos given a granuleshift of 32 */
461 flow = push_clip ("max-granule-keyoffset", "baz", "1193:02:47.295", NULL);
462 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
463 check_clip ("max-granule-keyoffset", "baz",
464 1193 * 3600 * GST_SECOND + 2 * 60 * GST_SECOND +
465 47 * GST_SECOND + 295 * GST_MSECOND, 0);
467 flow = push_clip ("overflow-max-granule-keyoffset", "overflows",
468 "1193:02:47.296", NULL);
469 fail_unless_equals_flow_return (flow, GST_FLOW_ERROR);
477 Suite *s = suite_create ("cmmlenc");
478 TCase *tc_general = tcase_create ("general");
480 suite_add_tcase (s, tc_general);
481 tcase_add_checked_fixture (tc_general, setup_cmmlenc, teardown_cmmlenc);
482 tcase_add_test (tc_general, test_enc);
483 tcase_add_test (tc_general, test_clip_end_time);
484 tcase_add_test (tc_general, test_time_order);
485 tcase_add_test (tc_general, test_time_parsing);
486 tcase_add_test (tc_general, test_time_limits);
491 GST_CHECK_MAIN (cmmlenc);