tizen 2.0 init
[framework/multimedia/gst-plugins-good0.10.git] / tests / check / elements / cmmldec.c
1 /*
2  * cmmldec.c - GStreamer CMML decoder test suite
3  * Copyright (C) 2005 Alessandro Decina
4  * 
5  * Authors:
6  *   Alessandro Decina <alessandro@nnva.org>
7  *
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.
12  *
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.
17  *
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.
22  */
23
24 #include <gst/check/gstcheck.h>
25 #include <gst/tag/tag.h>
26
27 #define SINK_CAPS "text/x-cmml"
28 #define SRC_CAPS "text/x-cmml, encoded=(boolean)TRUE"
29
30 #define IDENT_HEADER \
31   "CMML\x00\x00\x00\x00"\
32   "\x03\x00\x00\x00"\
33   "\xe8\x03\x00\x00\x00\x00\x00\x00"\
34   "\x01\x00\x00\x00\x00\x00\x00\x00"\
35   "\x20"
36 #define IDENT_HEADER_SIZE 29
37
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 >"
43
44 #define HEAD_TAG \
45   "<head>"\
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\"/>"\
52   "</head>"
53
54 #define HEAD_TAG_DECODED HEAD_TAG
55
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\"/>"\
62   "</clip>"
63
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\"/>"\
70   "</clip>"
71
72 #define EMPTY_CLIP_TEMPLATE \
73   "<clip id=\"%s\" track=\"%s\" />"
74
75 #define END_TAG \
76   "</cmml>"
77
78 #define fail_unless_equals_flow_return(a, b)                            \
79 G_STMT_START {                                                          \
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)",  \
84       a_up, b_up);                                                      \
85   g_free (a_up);                                                        \
86   g_free (b_up);                                                        \
87 } G_STMT_END;
88
89 static GstElement *cmmldec;
90
91 static GstBus *bus;
92
93 static GstFlowReturn flow;
94
95 static GList *current_buf;
96
97 static gint64 granulerate;
98
99 static guint8 granuleshift;
100
101 static GstPad *srcpad, *sinkpad;
102
103 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
104     GST_PAD_SINK,
105     GST_PAD_ALWAYS,
106     GST_STATIC_CAPS (SINK_CAPS)
107     );
108
109 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
110     GST_PAD_SRC,
111     GST_PAD_ALWAYS,
112     GST_STATIC_CAPS (SRC_CAPS)
113     );
114
115 static GstBuffer *
116 buffer_new (const gchar * buffer_data, guint size)
117 {
118   GstBuffer *buffer;
119
120   GstCaps *caps;
121
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);
127
128   return buffer;
129 }
130
131 static void
132 buffer_unref (void *buffer, void *user_data)
133 {
134   ASSERT_OBJECT_REFCOUNT (buffer, "buf", 1);
135   gst_buffer_unref (GST_BUFFER (buffer));
136 }
137
138 static void
139 setup_cmmldec (void)
140 {
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);
147
148   bus = gst_bus_new ();
149   gst_element_set_bus (cmmldec, bus);
150
151   fail_unless (gst_element_set_state (cmmldec,
152           GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE,
153       "could not set to playing");
154
155   granulerate = GST_SECOND / 1000;
156   granuleshift = 32;
157   buffers = NULL;
158 }
159
160 static void
161 teardown_cmmldec (void)
162 {
163   g_list_foreach (buffers, buffer_unref, NULL);
164   g_list_free (buffers);
165   buffers = NULL;
166   current_buf = NULL;
167
168   gst_bus_set_flushing (bus, TRUE);
169   gst_object_unref (bus);
170
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);
177 }
178
179 static void
180 check_output_buffer_is_equal (const gchar * name,
181     const gchar * data, gint refcount)
182 {
183   GstBuffer *buffer;
184
185   if (current_buf == NULL)
186     current_buf = buffers;
187   else
188     current_buf = g_list_next (current_buf);
189
190   fail_unless (current_buf != NULL);
191
192   buffer = GST_BUFFER (current_buf->data);
193
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);
198 }
199
200 static GstFlowReturn
201 push_data (const gchar * name, const gchar * data, gint size, gint64 granulepos)
202 {
203   GstBuffer *buffer;
204
205   buffer = buffer_new (data, size);
206   GST_BUFFER_OFFSET_END (buffer) = granulepos;
207   return gst_pad_push (srcpad, buffer);
208 }
209
210 static GObject *
211 cmml_tag_message_pop (GstBus * bus, const gchar * tag)
212 {
213   GstMessage *message;
214
215   GstTagList *taglist;
216
217   const GValue *value;
218
219   GObject *obj;
220
221   message = gst_bus_poll (bus, GST_MESSAGE_TAG, 0);
222   if (message == NULL)
223     return NULL;
224
225   gst_message_parse_tag (message, &taglist);
226   value = gst_tag_list_get_value_index (taglist, tag, 0);
227   if (value == NULL) {
228     gst_message_unref (message);
229     gst_tag_list_free (taglist);
230     return NULL;
231   }
232
233   obj = g_value_dup_object (value);
234   gst_message_unref (message);
235   gst_tag_list_free (taglist);
236
237   return obj;
238 }
239
240 static void
241 check_headers (void)
242 {
243   GObject *head_tag;
244
245   gchar *title, *base;
246
247   GValueArray *meta;
248
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);
252
253   /* push the cmml preamble */
254   flow = push_data ("preamble", PREAMBLE, strlen (PREAMBLE), 0);
255   fail_unless_equals_flow_return (flow, GST_FLOW_OK);
256
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);
260
261   fail_unless_equals_int (g_list_length (buffers), 2);
262
263   /* check the decoded preamble */
264   check_output_buffer_is_equal ("cmml-preamble-buffer", PREAMBLE_DECODED, 1);
265
266   /* check the decoded head tag */
267   check_output_buffer_is_equal ("head-tag-buffer", HEAD_TAG_DECODED, 1);
268
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);
278
279   g_free (title);
280   g_free (base);
281   g_value_array_free (meta);
282   g_object_unref (head_tag);
283 }
284
285 static GstFlowReturn
286 push_clip_full (const gchar * name, const gchar * track, const gchar * template,
287     GstClockTime prev, GstClockTime start)
288 {
289   gchar *clip;
290
291   gint64 keyindex, keyoffset, granulepos;
292
293   GstFlowReturn res;
294
295   if (track == NULL)
296     track = "default";
297
298   if (prev == GST_CLOCK_TIME_NONE)
299     prev = 0;
300
301   keyindex = prev / granulerate << granuleshift;
302   keyoffset = (start - prev) / granulerate;
303   granulepos = keyindex + keyoffset;
304
305   clip = g_strdup_printf (template, name, track);
306   res = push_data (name, clip, strlen (clip), granulepos);
307   g_free (clip);
308
309   return res;
310 }
311
312 static GstFlowReturn
313 push_clip (const gchar * name, const gchar * track,
314     GstClockTime prev, GstClockTime start)
315 {
316   return push_clip_full (name, track, CLIP_TEMPLATE, prev, start);
317 }
318
319 static GstFlowReturn
320 push_empty_clip (const gchar * name, const gchar * track, GstClockTime start)
321 {
322   return push_clip_full (name, track,
323       EMPTY_CLIP_TEMPLATE, GST_CLOCK_TIME_NONE, start);
324 }
325
326
327 static void
328 check_output_clip (const gchar * name, const gchar * track,
329     const gchar * start, const gchar * end)
330 {
331   gchar *decoded_clip;
332
333   if (track == NULL)
334     track = "default";
335
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);
339 }
340
341 GST_START_TEST (test_dec)
342 {
343   GstClockTime clip1_start = 1 * GST_SECOND + 234 * GST_MSECOND;
344
345   GstClockTime clip2_start = clip1_start;
346
347   GstClockTime clip3_start =
348       ((100 * 3600) + (59 * 60) + 59) * GST_SECOND + 678 * GST_MSECOND;
349
350   check_headers ();
351
352   flow = push_clip ("clip-1", "default", GST_CLOCK_TIME_NONE, clip1_start);
353   fail_unless_equals_flow_return (flow, GST_FLOW_OK);
354
355   flow = push_clip ("clip-2", "othertrack", GST_CLOCK_TIME_NONE, clip2_start);
356   fail_unless_equals_flow_return (flow, GST_FLOW_OK);
357
358   flow = push_clip ("clip-3", "default", clip1_start, clip3_start);
359   fail_unless_equals_flow_return (flow, GST_FLOW_OK);
360
361   /* send EOS to flush clip-2 and clip-3 */
362   gst_pad_send_event (GST_PAD_PEER (srcpad), gst_event_new_eos ());
363
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);
368 }
369
370 GST_END_TEST;
371
372 GST_START_TEST (test_preamble_no_pi)
373 {
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);
377
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);
382
383   check_output_buffer_is_equal ("cmml-preamble-buffer",
384       PREAMBLE_NO_PI "<cmml>", 1);
385 }
386
387 GST_END_TEST;
388
389 GST_START_TEST (test_tags)
390 {
391   GObject *tag;
392
393   gboolean empty;
394
395   gchar *id, *track;
396
397   gint64 start_time, end_time;
398
399   gchar *anchor_href, *anchor_text;
400
401   gchar *img_src, *img_alt;
402
403   gchar *desc;
404
405   GValueArray *meta;
406
407   GstClockTime clip1_start;
408
409   check_headers ();
410
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);
414
415   tag = cmml_tag_message_pop (bus, GST_TAG_CMML_CLIP);
416   fail_unless (tag != NULL);
417
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);
423
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);
436
437   g_free (id);
438   g_free (track);
439   g_free (anchor_href);
440   g_free (anchor_text);
441   g_free (img_src);
442   g_free (img_alt);
443   g_free (desc);
444   g_value_array_free (meta);
445   g_object_unref (tag);
446 }
447
448 GST_END_TEST;
449
450 GST_START_TEST (test_wait_clip_end)
451 {
452   GObject *tag;
453
454   gchar *id;
455
456   GstClockTime end_time = 0;
457
458   GstClockTime clip1_start = 1 * GST_SECOND + 234 * GST_MSECOND;
459
460   GstClockTime clip2_start = 2 * GST_SECOND + 234 * GST_MSECOND;
461
462   GstClockTime clip3_start = 3 * GST_SECOND + 234 * GST_MSECOND;
463
464   GstClockTime clip3_end = 4 * GST_SECOND + 234 * GST_MSECOND;
465
466   GstClockTime clip4_start = 5 * GST_SECOND + 234 * GST_MSECOND;
467
468   g_object_set (cmmldec, "wait-clip-end-time", TRUE, NULL);
469
470   check_headers ();
471
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);
476
477   flow = push_clip ("clip-2", "default", clip1_start, clip2_start);
478   fail_unless_equals_flow_return (flow, GST_FLOW_OK);
479
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);
486   g_free (id);
487   g_object_unref (tag);
488
489   flow = push_clip ("clip-3", "default", clip2_start, clip3_start);
490   fail_unless_equals_flow_return (flow, GST_FLOW_OK);
491
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);
498   g_free (id);
499   g_object_unref (tag);
500
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);
508   g_free (id);
509   g_object_unref (tag);
510
511   flow = push_clip ("clip-4", "default", clip3_start, clip4_start);
512   fail_unless_equals_flow_return (flow, GST_FLOW_OK);
513
514   /* an empty clip just marks the end of the previous one, so no tag is posted
515    * for empty-clip */
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 ());
519
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");
524   g_free (id);
525   g_object_unref (tag);
526 }
527
528 GST_END_TEST;
529
530 GST_START_TEST (test_weird_input)
531 {
532   const gchar *bad_xml = "<?xml version=\"1.0\"?><a><b></a>";
533
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);
537
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);
541
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);
546 }
547
548 GST_END_TEST;
549
550 GST_START_TEST (test_sink_query_convert)
551 {
552   guint64 keyindex, keyoffset, granulepos;
553
554   GstClockTime index_time, offset_time;
555
556   GstFormat dstfmt = GST_FORMAT_TIME;
557
558   gint64 dstval;
559
560   /* send headers to set the granulerate */
561   check_headers ();
562
563   /* create a 1|1 granulepos */
564   index_time = 1 * GST_SECOND;
565   offset_time = 1 * GST_SECOND;
566
567   keyindex = (index_time / granulerate) << granuleshift;
568   keyoffset = offset_time / granulerate;
569   granulepos = keyindex + keyoffset;
570
571   fail_unless (gst_pad_query_convert (GST_PAD_PEER (srcpad),
572           GST_FORMAT_DEFAULT, granulepos, &dstfmt, &dstval));
573
574   fail_unless (dstfmt == GST_FORMAT_TIME);
575   /* fail unless dstval == index + offset */
576   fail_unless_equals_int (2 * GST_SECOND, dstval);
577 }
578
579 GST_END_TEST;
580
581 static Suite *
582 cmmldec_suite (void)
583 {
584   Suite *s = suite_create ("cmmldec");
585
586   TCase *tc_general = tcase_create ("general");
587
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);
596
597   return s;
598 }
599
600 GST_CHECK_MAIN (cmmldec);