2 * cmmldec.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>
25 #include <gst/tag/tag.h>
27 #define SINK_CAPS "text/x-cmml"
28 #define SRC_CAPS "text/x-cmml, encoded=(boolean)TRUE"
30 #define IDENT_HEADER \
31 "CMML\x00\x00\x00\x00"\
33 "\xe8\x03\x00\x00\x00\x00\x00\x00"\
34 "\x01\x00\x00\x00\x00\x00\x00\x00"\
36 #define IDENT_HEADER_SIZE 29
38 #define PREAMBLE_NO_PI \
39 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"\
40 "<!DOCTYPE cmml SYSTEM \"cmml.dtd\">\n"
41 #define PREAMBLE PREAMBLE_NO_PI "<?cmml?>"
42 #define PREAMBLE_DECODED PREAMBLE_NO_PI "<cmml >"
46 "<title>The Research Hunter</title>"\
47 "<meta name=\"DC.audience\" content=\"General\"/>"\
48 "<meta name=\"DC.author\" content=\"CSIRO Publishing\"/>"\
49 "<meta name=\"DC.format\" content=\"video\"/>"\
50 "<meta name=\"DC.language\" content=\"English\"/>"\
51 "<meta name=\"DC.publisher\" content=\"CSIRO Australia\"/>"\
54 #define HEAD_TAG_DECODED HEAD_TAG
56 #define CLIP_TEMPLATE \
57 "<clip id=\"%s\" track=\"%s\">"\
58 "<a href=\"http://www.csiro.au/\">http://www.csiro.au</a>"\
59 "<img src=\"images/index1.jpg\"/>"\
60 "<desc>Welcome to CSIRO</desc>"\
61 "<meta name=\"test\" content=\"test content\"/>"\
64 #define CLIP_TEMPLATE_DECODED \
65 "<clip id=\"%s\" track=\"%s\" start=\"%s\">"\
66 "<a href=\"http://www.csiro.au/\">http://www.csiro.au</a>"\
67 "<img src=\"images/index1.jpg\"/>"\
68 "<desc>Welcome to CSIRO</desc>"\
69 "<meta name=\"test\" content=\"test content\"/>"\
72 #define EMPTY_CLIP_TEMPLATE \
73 "<clip id=\"%s\" track=\"%s\" />"
78 #define fail_unless_equals_flow_return(a, b) \
80 gchar *a_up = g_ascii_strup (gst_flow_get_name (a), -1); \
81 gchar *b_up = g_ascii_strup (gst_flow_get_name (b), -1); \
82 fail_unless (a == b, \
83 "'" #a "' (GST_FLOW_%s) is not equal to '" #b "' (GST_FLOW_%s)", \
89 static GstElement *cmmldec;
93 static GstFlowReturn flow;
95 static GList *current_buf;
97 static gint64 granulerate;
99 static guint8 granuleshift;
101 static GstPad *srcpad, *sinkpad;
103 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
106 GST_STATIC_CAPS (SINK_CAPS)
109 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
112 GST_STATIC_CAPS (SRC_CAPS)
116 buffer_new (const gchar * buffer_data, guint size)
122 buffer = gst_buffer_new_and_alloc (size);
123 memcpy (GST_BUFFER_DATA (buffer), buffer_data, size);
124 caps = gst_caps_from_string (SRC_CAPS);
125 gst_buffer_set_caps (buffer, caps);
126 gst_caps_unref (caps);
132 buffer_unref (void *buffer, void *user_data)
134 ASSERT_OBJECT_REFCOUNT (buffer, "buf", 1);
135 gst_buffer_unref (GST_BUFFER (buffer));
141 GST_DEBUG ("setup_cmmldec");
142 cmmldec = gst_check_setup_element ("cmmldec");
143 srcpad = gst_check_setup_src_pad (cmmldec, &srctemplate, NULL);
144 sinkpad = gst_check_setup_sink_pad (cmmldec, &sinktemplate, NULL);
145 gst_pad_set_active (srcpad, TRUE);
146 gst_pad_set_active (sinkpad, TRUE);
148 bus = gst_bus_new ();
149 gst_element_set_bus (cmmldec, bus);
151 fail_unless (gst_element_set_state (cmmldec,
152 GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE,
153 "could not set to playing");
155 granulerate = GST_SECOND / 1000;
161 teardown_cmmldec (void)
163 g_list_foreach (buffers, buffer_unref, NULL);
164 g_list_free (buffers);
168 gst_bus_set_flushing (bus, TRUE);
169 gst_object_unref (bus);
171 GST_DEBUG ("teardown_cmmldec");
172 gst_pad_set_active (srcpad, FALSE);
173 gst_pad_set_active (sinkpad, FALSE);
174 gst_check_teardown_src_pad (cmmldec);
175 gst_check_teardown_sink_pad (cmmldec);
176 gst_check_teardown_element (cmmldec);
180 check_output_buffer_is_equal (const gchar * name,
181 const gchar * data, gint refcount)
185 if (current_buf == NULL)
186 current_buf = buffers;
188 current_buf = g_list_next (current_buf);
190 fail_unless (current_buf != NULL);
192 buffer = GST_BUFFER (current_buf->data);
194 ASSERT_OBJECT_REFCOUNT (buffer, name, refcount);
195 fail_unless (memcmp (GST_BUFFER_DATA (buffer), data,
196 GST_BUFFER_SIZE (buffer)) == 0,
197 "'%s' (%s) is not equal to (%s)", name, GST_BUFFER_DATA (buffer), data);
201 push_data (const gchar * name, const gchar * data, gint size, gint64 granulepos)
205 buffer = buffer_new (data, size);
206 GST_BUFFER_OFFSET_END (buffer) = granulepos;
207 return gst_pad_push (srcpad, buffer);
211 cmml_tag_message_pop (GstBus * bus, const gchar * tag)
221 message = gst_bus_poll (bus, GST_MESSAGE_TAG, 0);
225 gst_message_parse_tag (message, &taglist);
226 value = gst_tag_list_get_value_index (taglist, tag, 0);
228 gst_message_unref (message);
229 gst_tag_list_free (taglist);
233 obj = g_value_dup_object (value);
234 gst_message_unref (message);
235 gst_tag_list_free (taglist);
249 /* push the ident header */
250 flow = push_data ("ident-header", IDENT_HEADER, IDENT_HEADER_SIZE, 0);
251 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
253 /* push the cmml preamble */
254 flow = push_data ("preamble", PREAMBLE, strlen (PREAMBLE), 0);
255 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
257 /* push the head tag */
258 flow = push_data ("head", HEAD_TAG, strlen (HEAD_TAG), 0);
259 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
261 fail_unless_equals_int (g_list_length (buffers), 2);
263 /* check the decoded preamble */
264 check_output_buffer_is_equal ("cmml-preamble-buffer", PREAMBLE_DECODED, 1);
266 /* check the decoded head tag */
267 check_output_buffer_is_equal ("head-tag-buffer", HEAD_TAG_DECODED, 1);
269 /* check the GstCmmlTagHead tag object */
270 head_tag = cmml_tag_message_pop (bus, GST_TAG_CMML_HEAD);
271 fail_unless (head_tag != NULL);
272 g_object_get (head_tag,
273 "title", &title, "base-uri", &base, "meta", &meta, NULL);
274 fail_unless_equals_string ("The Research Hunter", title);
275 fail_unless (base == NULL);
276 fail_unless (meta != NULL);
277 fail_unless_equals_int (meta->n_values, 10);
281 g_value_array_free (meta);
282 g_object_unref (head_tag);
286 push_clip_full (const gchar * name, const gchar * track, const gchar * template,
287 GstClockTime prev, GstClockTime start)
291 gint64 keyindex, keyoffset, granulepos;
298 if (prev == GST_CLOCK_TIME_NONE)
301 keyindex = prev / granulerate << granuleshift;
302 keyoffset = (start - prev) / granulerate;
303 granulepos = keyindex + keyoffset;
305 clip = g_strdup_printf (template, name, track);
306 res = push_data (name, clip, strlen (clip), granulepos);
313 push_clip (const gchar * name, const gchar * track,
314 GstClockTime prev, GstClockTime start)
316 return push_clip_full (name, track, CLIP_TEMPLATE, prev, start);
320 push_empty_clip (const gchar * name, const gchar * track, GstClockTime start)
322 return push_clip_full (name, track,
323 EMPTY_CLIP_TEMPLATE, GST_CLOCK_TIME_NONE, start);
328 check_output_clip (const gchar * name, const gchar * track,
329 const gchar * start, const gchar * end)
336 decoded_clip = g_strdup_printf (CLIP_TEMPLATE_DECODED, name, track, start);
337 check_output_buffer_is_equal (name, decoded_clip, 1);
338 g_free (decoded_clip);
341 GST_START_TEST (test_dec)
343 GstClockTime clip1_start = 1 * GST_SECOND + 234 * GST_MSECOND;
345 GstClockTime clip2_start = clip1_start;
347 GstClockTime clip3_start =
348 ((100 * 3600) + (59 * 60) + 59) * GST_SECOND + 678 * GST_MSECOND;
352 flow = push_clip ("clip-1", "default", GST_CLOCK_TIME_NONE, clip1_start);
353 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
355 flow = push_clip ("clip-2", "othertrack", GST_CLOCK_TIME_NONE, clip2_start);
356 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
358 flow = push_clip ("clip-3", "default", clip1_start, clip3_start);
359 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
361 /* send EOS to flush clip-2 and clip-3 */
362 gst_pad_send_event (GST_PAD_PEER (srcpad), gst_event_new_eos ());
364 check_output_clip ("clip-1", "default", "0:00:01.234", NULL);
365 check_output_clip ("clip-2", "othertrack", "0:00:01.234", NULL);
366 check_output_clip ("clip-3", "default", "100:59:59.678", NULL);
367 check_output_buffer_is_equal ("cmml-end-tag", END_TAG, 1);
372 GST_START_TEST (test_preamble_no_pi)
374 flow = push_data ("ident-header", IDENT_HEADER, IDENT_HEADER_SIZE, 0);
375 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
376 fail_unless_equals_int (g_list_length (buffers), 0);
378 flow = push_data ("preamble-no-pi",
379 PREAMBLE_NO_PI, strlen (PREAMBLE_NO_PI), 0);
380 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
381 fail_unless_equals_int (g_list_length (buffers), 1);
383 check_output_buffer_is_equal ("cmml-preamble-buffer",
384 PREAMBLE_NO_PI "<cmml>", 1);
389 GST_START_TEST (test_tags)
397 gint64 start_time, end_time;
399 gchar *anchor_href, *anchor_text;
401 gchar *img_src, *img_alt;
407 GstClockTime clip1_start;
411 clip1_start = 1 * GST_SECOND + 234 * GST_MSECOND;
412 flow = push_clip ("clip-1", "default", 0, clip1_start);
413 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
415 tag = cmml_tag_message_pop (bus, GST_TAG_CMML_CLIP);
416 fail_unless (tag != NULL);
418 g_object_get (tag, "id", &id, "empty", &empty, "track", &track,
419 "start-time", &start_time, "end-time", &end_time,
420 "anchor-uri", &anchor_href, "anchor-text", &anchor_text,
421 "img-uri", &img_src, "img-alt", &img_alt,
422 "description", &desc, "meta", &meta, NULL);
424 fail_unless (empty == FALSE);
425 fail_unless_equals_string (id, "clip-1");
426 fail_unless_equals_string (track, "default");
427 fail_unless_equals_int (start_time, 1 * GST_SECOND + 234 * GST_MSECOND);
428 fail_unless_equals_uint64 (end_time, GST_CLOCK_TIME_NONE);
429 fail_unless_equals_string (anchor_href, "http://www.csiro.au/");
430 fail_unless_equals_string (anchor_text, "http://www.csiro.au");
431 fail_unless_equals_string (img_src, "images/index1.jpg");
432 fail_unless (img_alt == NULL);
433 fail_unless_equals_string (desc, "Welcome to CSIRO");
434 fail_unless (meta != NULL);
435 fail_unless_equals_int (meta->n_values, 2);
439 g_free (anchor_href);
440 g_free (anchor_text);
444 g_value_array_free (meta);
445 g_object_unref (tag);
450 GST_START_TEST (test_wait_clip_end)
456 GstClockTime end_time = 0;
458 GstClockTime clip1_start = 1 * GST_SECOND + 234 * GST_MSECOND;
460 GstClockTime clip2_start = 2 * GST_SECOND + 234 * GST_MSECOND;
462 GstClockTime clip3_start = 3 * GST_SECOND + 234 * GST_MSECOND;
464 GstClockTime clip3_end = 4 * GST_SECOND + 234 * GST_MSECOND;
466 GstClockTime clip4_start = 5 * GST_SECOND + 234 * GST_MSECOND;
468 g_object_set (cmmldec, "wait-clip-end-time", TRUE, NULL);
472 flow = push_clip ("clip-1", "default", 0, clip1_start);
473 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
474 /* no tag has been posted yet */
475 fail_unless (cmml_tag_message_pop (bus, GST_TAG_CMML_CLIP) == NULL);
477 flow = push_clip ("clip-2", "default", clip1_start, clip2_start);
478 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
480 tag = cmml_tag_message_pop (bus, GST_TAG_CMML_CLIP);
481 fail_unless (tag != NULL);
482 g_object_get (tag, "id", &id, "end-time", &end_time, NULL);
483 /* clip-1 is posted when clip-2 is decoded. clip-1 ends when clip-2 starts */
484 fail_unless_equals_string (id, "clip-1");
485 fail_unless_equals_int (end_time, clip2_start);
487 g_object_unref (tag);
489 flow = push_clip ("clip-3", "default", clip2_start, clip3_start);
490 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
492 tag = cmml_tag_message_pop (bus, GST_TAG_CMML_CLIP);
493 fail_unless (tag != NULL);
494 g_object_get (tag, "id", &id, "end-time", &end_time, NULL);
495 /* clip-2 is posted when clip-3 is decoded. It ends when clip-3 starts */
496 fail_unless_equals_string (id, "clip-2");
497 fail_unless_equals_int (end_time, clip3_start);
499 g_object_unref (tag);
501 flow = push_empty_clip ("empty-clip", "default", clip3_end);
502 tag = cmml_tag_message_pop (bus, GST_TAG_CMML_CLIP);
503 fail_unless (tag != NULL);
504 g_object_get (tag, "id", &id, "end-time", &end_time, NULL);
505 /* clip-3 ends when empty-clip is decoded */
506 fail_unless_equals_string (id, "clip-3");
507 fail_unless_equals_int (end_time, clip3_end);
509 g_object_unref (tag);
511 flow = push_clip ("clip-4", "default", clip3_start, clip4_start);
512 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
514 /* an empty clip just marks the end of the previous one, so no tag is posted
516 fail_unless (cmml_tag_message_pop (bus, GST_TAG_CMML_CLIP) == NULL);
517 /* send EOS to flush clip-4 */
518 gst_pad_send_event (GST_PAD_PEER (srcpad), gst_event_new_eos ());
520 tag = cmml_tag_message_pop (bus, GST_TAG_CMML_CLIP);
521 fail_unless (tag != NULL);
522 g_object_get (tag, "id", &id, NULL);
523 fail_unless_equals_string (id, "clip-4");
525 g_object_unref (tag);
530 GST_START_TEST (test_weird_input)
532 const gchar *bad_xml = "<?xml version=\"1.0\"?><a><b></a>";
534 /* malformed ident header */
535 flow = push_data ("bad-ident-header", "CMML\0\0\0\0garbage", 15, 0);
536 fail_unless_equals_flow_return (flow, GST_FLOW_ERROR);
538 /* push invalid xml */
539 flow = push_data ("bad-xml", bad_xml, strlen (bad_xml), 0);
540 fail_unless_equals_flow_return (flow, GST_FLOW_ERROR);
542 /* and now for something completely different: an empty buffer. This is valid
543 * as 'NIL' EOS pages are allowed */
544 flow = push_data ("empty-eos", NULL, 0, 0);
545 fail_unless_equals_flow_return (flow, GST_FLOW_OK);
550 GST_START_TEST (test_sink_query_convert)
552 guint64 keyindex, keyoffset, granulepos;
554 GstClockTime index_time, offset_time;
556 GstFormat dstfmt = GST_FORMAT_TIME;
560 /* send headers to set the granulerate */
563 /* create a 1|1 granulepos */
564 index_time = 1 * GST_SECOND;
565 offset_time = 1 * GST_SECOND;
567 keyindex = (index_time / granulerate) << granuleshift;
568 keyoffset = offset_time / granulerate;
569 granulepos = keyindex + keyoffset;
571 fail_unless (gst_pad_query_convert (GST_PAD_PEER (srcpad),
572 GST_FORMAT_DEFAULT, granulepos, &dstfmt, &dstval));
574 fail_unless (dstfmt == GST_FORMAT_TIME);
575 /* fail unless dstval == index + offset */
576 fail_unless_equals_int (2 * GST_SECOND, dstval);
584 Suite *s = suite_create ("cmmldec");
586 TCase *tc_general = tcase_create ("general");
588 suite_add_tcase (s, tc_general);
589 tcase_add_checked_fixture (tc_general, setup_cmmldec, teardown_cmmldec);
590 tcase_add_test (tc_general, test_dec);
591 tcase_add_test (tc_general, test_tags);
592 tcase_add_test (tc_general, test_preamble_no_pi);
593 tcase_add_test (tc_general, test_wait_clip_end);
594 tcase_add_test (tc_general, test_sink_query_convert);
595 tcase_add_test (tc_general, test_weird_input);
600 GST_CHECK_MAIN (cmmldec);