remove query types
[platform/upstream/gst-plugins-good.git] / ext / annodex / gstcmmldec.c
1 /*
2  * gstcmmldec.c - GStreamer annodex CMML decoder
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 /**
25  * SECTION:element-cmmldec
26  * @see_also: cmmlenc, oggdemux
27  *
28  * Cmmldec extracts a CMML document from a CMML bitstream.<ulink
29  * url="http://www.annodex.net/TR/draft-pfeiffer-cmml-02.html">CMML</ulink> is
30  * an XML markup language for time-continuous data maintained by the <ulink
31  * url="http:/www.annodex.org/">Annodex Foundation</ulink>.
32  *
33  * <refsect2>
34  * <title>Example pipeline</title>
35  * |[
36  * gst-launch -v filesrc location=annotated.ogg ! oggdemux ! cmmldec ! filesink location=annotations.cmml
37  * ]|
38  * </refsect2>
39  */
40
41
42 #ifdef HAVE_CONFIG_H
43 #include "config.h"
44 #endif
45
46 #include <string.h>
47 #include <glib.h>
48
49 #include <gst/tag/tag.h>
50 #include "gstannodex.h"
51 #include "gstcmmltag.h"
52 #include "gstcmmldec.h"
53 #include "gstcmmlutils.h"
54
55 GST_DEBUG_CATEGORY_STATIC (cmmldec);
56 #define GST_CAT_DEFAULT cmmldec
57
58 #define CMML_IDENT_HEADER_SIZE 29
59
60 enum
61 {
62   ARG_0,
63   GST_CMML_DEC_WAIT_CLIP_END
64 };
65
66 enum
67 {
68   LAST_SIGNAL
69 };
70
71 static GstStaticPadTemplate gst_cmml_dec_src_factory =
72 GST_STATIC_PAD_TEMPLATE ("src",
73     GST_PAD_SRC,
74     GST_PAD_ALWAYS,
75     GST_STATIC_CAPS ("text/x-cmml, encoded = (boolean) false")
76     );
77
78 static GstStaticPadTemplate gst_cmml_dec_sink_factory =
79 GST_STATIC_PAD_TEMPLATE ("sink",
80     GST_PAD_SINK,
81     GST_PAD_ALWAYS,
82     GST_STATIC_CAPS ("text/x-cmml, encoded = (boolean) true")
83     );
84
85 /* GstCmmlDec */
86 #define gst_cmml_dec_parent_class parent_class
87 G_DEFINE_TYPE (GstCmmlDec, gst_cmml_dec, GST_TYPE_ELEMENT);
88
89 static void gst_cmml_dec_get_property (GObject * dec, guint property_id,
90     GValue * value, GParamSpec * pspec);
91 static void gst_cmml_dec_set_property (GObject * dec, guint property_id,
92     const GValue * value, GParamSpec * pspec);
93 static gboolean gst_cmml_dec_sink_query (GstPad * pad, GstQuery * query);
94 static gboolean gst_cmml_dec_sink_event (GstPad * pad, GstEvent * event);
95 static gboolean gst_cmml_dec_convert (GstPad * pad, GstFormat src_fmt,
96     gint64 src_val, GstFormat * dest_fmt, gint64 * dest_val);
97 static GstStateChangeReturn gst_cmml_dec_change_state (GstElement * element,
98     GstStateChange transition);
99 static GstFlowReturn gst_cmml_dec_chain (GstPad * pad, GstBuffer * buffer);
100
101 static GstCmmlPacketType gst_cmml_dec_parse_packet_type (GstCmmlDec * dec,
102     gchar * data, gsize size);
103 static void gst_cmml_dec_parse_ident_header (GstCmmlDec * dec, guint8 * data,
104     gsize size);
105 static void gst_cmml_dec_parse_first_header (GstCmmlDec * dec, guint8 * data,
106     gsize size);
107 static void gst_cmml_dec_parse_preamble (GstCmmlDec * dec, guchar * preamble,
108     guchar * cmml_root_element);
109 static void gst_cmml_dec_parse_xml (GstCmmlDec * dec, guchar * data,
110     guint size);
111 static void gst_cmml_dec_parse_head (GstCmmlDec * dec, GstCmmlTagHead * head);
112 static void gst_cmml_dec_parse_clip (GstCmmlDec * dec, GstCmmlTagClip * clip);
113
114 static GstFlowReturn gst_cmml_dec_new_buffer (GstCmmlDec * dec,
115     guchar * data, gint size, GstBuffer ** buffer);
116 static void gst_cmml_dec_push_clip (GstCmmlDec * dec, GstCmmlTagClip * clip);
117 static void gst_cmml_dec_send_clip_tag (GstCmmlDec * dec,
118     GstCmmlTagClip * clip);
119
120 static void gst_cmml_dec_finalize (GObject * object);
121
122 static void
123 gst_cmml_dec_class_init (GstCmmlDecClass * klass)
124 {
125   GObjectClass *object_class = G_OBJECT_CLASS (klass);
126   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
127
128   object_class->set_property = gst_cmml_dec_set_property;
129   object_class->get_property = gst_cmml_dec_get_property;
130   object_class->finalize = gst_cmml_dec_finalize;
131
132   g_object_class_install_property (object_class, GST_CMML_DEC_WAIT_CLIP_END,
133       g_param_spec_boolean ("wait-clip-end-time",
134           "Wait clip end time",
135           "Send a tag for a clip when the clip ends, setting its end-time. "
136           "Use when you need to know both clip's start-time and end-time.",
137           FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
138
139   element_class->change_state = gst_cmml_dec_change_state;
140
141   gst_element_class_add_pad_template (element_class,
142       gst_static_pad_template_get (&gst_cmml_dec_sink_factory));
143   gst_element_class_add_pad_template (element_class,
144       gst_static_pad_template_get (&gst_cmml_dec_src_factory));
145   gst_element_class_set_details_simple (element_class, "CMML stream decoder",
146       "Codec/Decoder",
147       "Decodes CMML streams", "Alessandro Decina <alessandro@nnva.org>");
148 }
149
150 static void
151 gst_cmml_dec_init (GstCmmlDec * dec)
152 {
153   dec->sinkpad =
154       gst_pad_new_from_static_template (&gst_cmml_dec_sink_factory, "sink");
155   gst_pad_set_chain_function (dec->sinkpad, gst_cmml_dec_chain);
156   gst_pad_set_query_function (dec->sinkpad, gst_cmml_dec_sink_query);
157   gst_pad_set_event_function (dec->sinkpad, gst_cmml_dec_sink_event);
158   gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad);
159
160   dec->srcpad =
161       gst_pad_new_from_static_template (&gst_cmml_dec_src_factory, "src");
162   gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad);
163
164   dec->wait_clip_end = FALSE;
165 }
166
167 static void
168 gst_cmml_dec_get_property (GObject * object, guint property_id,
169     GValue * value, GParamSpec * pspec)
170 {
171   GstCmmlDec *dec = GST_CMML_DEC (object);
172
173   switch (property_id) {
174     case GST_CMML_DEC_WAIT_CLIP_END:
175       g_value_set_boolean (value, dec->wait_clip_end);
176       break;
177     default:
178       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
179   }
180 }
181
182 static void
183 gst_cmml_dec_set_property (GObject * object, guint property_id,
184     const GValue * value, GParamSpec * pspec)
185 {
186   GstCmmlDec *dec = GST_CMML_DEC (object);
187
188   switch (property_id) {
189     case GST_CMML_DEC_WAIT_CLIP_END:
190       dec->wait_clip_end = g_value_get_boolean (value);
191       break;
192     default:
193       G_OBJECT_WARN_INVALID_PROPERTY_ID (dec, property_id, pspec);
194   }
195 }
196
197 static void
198 gst_cmml_dec_finalize (GObject * object)
199 {
200   GstCmmlDec *dec = GST_CMML_DEC (object);
201
202   if (dec->tracks) {
203     gst_cmml_track_list_destroy (dec->tracks);
204     dec->tracks = NULL;
205   }
206
207   G_OBJECT_CLASS (parent_class)->finalize (object);
208 }
209
210 static GstStateChangeReturn
211 gst_cmml_dec_change_state (GstElement * element, GstStateChange transition)
212 {
213   GstCmmlDec *dec = GST_CMML_DEC (element);
214   GstStateChangeReturn res;
215
216   switch (transition) {
217     case GST_STATE_CHANGE_READY_TO_PAUSED:
218       dec->parser = gst_cmml_parser_new (GST_CMML_PARSER_DECODE);
219       dec->parser->user_data = dec;
220       dec->parser->preamble_callback =
221           (GstCmmlParserPreambleCallback) gst_cmml_dec_parse_preamble;
222       dec->parser->head_callback =
223           (GstCmmlParserHeadCallback) gst_cmml_dec_parse_head;
224       dec->parser->clip_callback =
225           (GstCmmlParserClipCallback) gst_cmml_dec_parse_clip;
226       dec->major = -1;
227       dec->minor = -1;
228       dec->granulerate_n = -1;
229       dec->granulerate_d = -1;
230       dec->granuleshift = 0;
231       dec->granulepos = 0;
232       dec->flow_return = GST_FLOW_OK;
233       dec->sent_root = FALSE;
234       dec->tracks = gst_cmml_track_list_new ();
235       break;
236     default:
237       break;
238   }
239
240   res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
241
242   switch (transition) {
243     case GST_STATE_CHANGE_PAUSED_TO_READY:
244       gst_cmml_parser_free (dec->parser);
245       gst_cmml_track_list_destroy (dec->tracks);
246       dec->tracks = NULL;
247       break;
248     default:
249       break;
250   }
251
252   return res;
253 }
254
255 static gboolean
256 gst_cmml_dec_sink_query (GstPad * pad, GstQuery * query)
257 {
258   gboolean res = FALSE;
259
260   switch (GST_QUERY_TYPE (query)) {
261     case GST_QUERY_CONVERT:
262     {
263       GstFormat src_fmt, dest_fmt;
264       gint64 src_val, dest_val;
265
266       gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
267       res = gst_cmml_dec_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val);
268       if (res)
269         gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
270       break;
271     }
272     default:
273       break;
274   }
275
276   return res;
277 }
278
279 static gboolean
280 gst_cmml_dec_convert (GstPad * pad,
281     GstFormat src_fmt, gint64 src_val, GstFormat * dest_fmt, gint64 * dest_val)
282 {
283   GstCmmlDec *dec = GST_CMML_DEC (GST_PAD_PARENT (pad));
284   gboolean res = FALSE;
285
286   switch (src_fmt) {
287     case GST_FORMAT_DEFAULT:
288       switch (*dest_fmt) {
289         case GST_FORMAT_TIME:
290         {
291           *dest_val = gst_annodex_granule_to_time (src_val, dec->granulerate_n,
292               dec->granulerate_d, dec->granuleshift);
293           res = TRUE;
294           break;
295         }
296         default:
297           break;
298       }
299       break;
300     default:
301       break;
302   }
303
304   return res;
305 }
306
307 static gboolean
308 gst_cmml_dec_sink_event (GstPad * pad, GstEvent * event)
309 {
310   GstCmmlDec *dec = GST_CMML_DEC (GST_PAD_PARENT (pad));
311
312   switch (GST_EVENT_TYPE (event)) {
313     case GST_EVENT_EOS:
314     {
315       GstBuffer *buffer;
316       GstCmmlTagClip *clip;
317       GList *clips, *walk;
318
319       GST_INFO_OBJECT (dec, "got EOS, flushing clips");
320
321       /* since we output a clip when the next one in the same track is found, on
322        * EOS we need to output the last clip (if any) of every track
323        */
324       clips = gst_cmml_track_list_get_clips (dec->tracks);
325       for (walk = clips; walk; walk = g_list_next (walk)) {
326         clip = GST_CMML_TAG_CLIP (walk->data);
327         gst_cmml_dec_push_clip (dec, clip);
328         if (dec->wait_clip_end) {
329           clip->end_time = dec->timestamp;
330           gst_cmml_dec_send_clip_tag (dec, clip);
331         }
332       }
333       g_list_free (clips);
334
335       /* send the cmml end tag */
336       dec->flow_return = gst_cmml_dec_new_buffer (dec,
337           (guchar *) "</cmml>", strlen ("</cmml>"), &buffer);
338
339       if (dec->flow_return == GST_FLOW_OK)
340         dec->flow_return = gst_pad_push (dec->srcpad, buffer);
341       if (dec->flow_return == GST_FLOW_NOT_LINKED)
342         dec->flow_return = GST_FLOW_OK; /* Ignore NOT_LINKED */
343
344       break;
345     }
346     default:
347       break;
348   }
349
350   return gst_pad_event_default (pad, event);
351 }
352
353 static GstFlowReturn
354 gst_cmml_dec_chain (GstPad * pad, GstBuffer * buffer)
355 {
356   GstCmmlDec *dec = GST_CMML_DEC (GST_PAD_PARENT (pad));
357   GstCmmlPacketType packet;
358   guint8 *data;
359   gsize size;
360
361   data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
362
363   if (size == 0) {
364     /* the EOS page could be empty */
365     dec->flow_return = GST_FLOW_OK;
366     goto done;
367   }
368
369   dec->granulepos = GST_BUFFER_OFFSET_END (buffer);
370   dec->timestamp = gst_annodex_granule_to_time (dec->granulepos,
371       dec->granulerate_n, dec->granulerate_d, dec->granuleshift);
372
373   /* identify the packet type */
374   packet = gst_cmml_dec_parse_packet_type (dec, (gchar *) data, size);
375
376   /* handle the packet. the handler will set dec->flow_return */
377   switch (packet) {
378     case GST_CMML_PACKET_IDENT_HEADER:
379       if (dec->sent_root == FALSE)
380         /* don't parse the ident again in case of seeking to the beginning */
381         gst_cmml_dec_parse_ident_header (dec, data, size);
382       break;
383     case GST_CMML_PACKET_FIRST_HEADER:
384       if (dec->sent_root == FALSE)
385         /* don't parse the xml preamble if it has already been parsed because it
386          * would error out, so seeking to the beginning would fail */
387         gst_cmml_dec_parse_first_header (dec, data, size);
388       break;
389     case GST_CMML_PACKET_SECOND_HEADER:
390     case GST_CMML_PACKET_CLIP:
391       gst_cmml_dec_parse_xml (dec, data, size);
392       break;
393     case GST_CMML_PACKET_UNKNOWN:
394     default:
395       GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("unknown packet type"));
396       dec->flow_return = GST_FLOW_ERROR;
397   }
398
399 done:
400   gst_buffer_unmap (buffer, data, size);
401   gst_buffer_unref (buffer);
402
403   return dec->flow_return;
404 }
405
406 /* finds the packet type of the buffer
407  */
408 static GstCmmlPacketType
409 gst_cmml_dec_parse_packet_type (GstCmmlDec * dec, gchar * data, gsize size)
410 {
411   GstCmmlPacketType packet_type = GST_CMML_PACKET_UNKNOWN;
412
413   if (size >= 8 && !memcmp (data, "CMML\0\0\0\0", 8)) {
414     packet_type = GST_CMML_PACKET_IDENT_HEADER;
415   } else if (size >= 5) {
416     if (!strncmp (data, "<?xml", 5))
417       packet_type = GST_CMML_PACKET_FIRST_HEADER;
418     else if (!strncmp (data, "<head", 5))
419       packet_type = GST_CMML_PACKET_SECOND_HEADER;
420     else if (!strncmp (data, "<clip", 5))
421       packet_type = GST_CMML_PACKET_CLIP;
422   }
423
424   return packet_type;
425 }
426
427 /* creates a new buffer and sets caps and timestamp on it
428  */
429 static GstFlowReturn
430 gst_cmml_dec_new_buffer (GstCmmlDec * dec,
431     guchar * data, gint size, GstBuffer ** buffer)
432 {
433   GstFlowReturn res;
434
435   *buffer = gst_buffer_new_allocate (NULL, size, 0);
436   if (*buffer != NULL) {
437     if (data)
438       gst_buffer_fill (*buffer, 0, data, size);
439     GST_BUFFER_TIMESTAMP (*buffer) = dec->timestamp;
440     res = GST_FLOW_OK;
441   } else {
442     GST_WARNING_OBJECT (dec, "could not allocate buffer");
443     res = GST_FLOW_ERROR;
444   }
445
446   return res;
447 }
448
449 /* parses the first CMML packet (the ident header)
450  */
451 static void
452 gst_cmml_dec_parse_ident_header (GstCmmlDec * dec, guint8 * data, gsize size)
453 {
454   /* the ident header has a fixed length */
455   if (size != CMML_IDENT_HEADER_SIZE) {
456     GST_ELEMENT_ERROR (dec, STREAM, DECODE,
457         (NULL), ("wrong ident header size: %d", size));
458     dec->flow_return = GST_FLOW_ERROR;
459
460     return;
461   }
462
463   data += 8;
464   dec->major = GST_READ_UINT16_LE (data);
465   data += 2;
466   dec->minor = GST_READ_UINT16_LE (data);
467   data += 2;
468   dec->granulerate_n = GST_READ_UINT64_LE (data);
469   data += 8;
470   dec->granulerate_d = GST_READ_UINT64_LE (data);
471   data += 8;
472   dec->granuleshift = GST_READ_UINT8 (data);
473
474   GST_INFO_OBJECT (dec, "bitstream initialized "
475       "(major: %" G_GINT16_FORMAT " minor: %" G_GINT16_FORMAT
476       " granulerate_n: %" G_GINT64_FORMAT " granulerate_d: %" G_GINT64_FORMAT
477       " granuleshift: %d)",
478       dec->major, dec->minor,
479       dec->granulerate_n, dec->granulerate_d, dec->granuleshift);
480
481   dec->flow_return = GST_FLOW_OK;
482 }
483
484 /* parses the first secondary header.
485  * the first secondary header contains the xml version, the doctype and the
486  * optional "cmml" processing instruction.
487  */
488 static void
489 gst_cmml_dec_parse_first_header (GstCmmlDec * dec, guint8 * data, gsize size)
490 {
491   gst_cmml_dec_parse_xml (dec, data, size);
492
493   /* if there is a processing instruction, gst_cmml_dec_parse_preamble
494    * will be triggered. Otherwise we need to call it manually.
495    */
496   if (dec->flow_return == GST_FLOW_OK && !dec->sent_root) {
497     guchar *preamble = (guchar *) g_strndup ((gchar *) data, size);
498
499     gst_cmml_dec_parse_preamble (dec, preamble, (guchar *) "<cmml>");
500     g_free (preamble);
501   }
502 }
503
504 /* feeds data into the cmml parser.
505  */
506 static void
507 gst_cmml_dec_parse_xml (GstCmmlDec * dec, guchar * data, guint size)
508 {
509   GError *err = NULL;
510
511   if (!gst_cmml_parser_parse_chunk (dec->parser, (gchar *) data, size, &err)) {
512     GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("%s", err->message));
513     g_error_free (err);
514     dec->flow_return = GST_FLOW_ERROR;
515   }
516 }
517
518 static void
519 gst_cmml_dec_parse_preamble (GstCmmlDec * dec, guchar * preamble,
520     guchar * root_element)
521 {
522   GstBuffer *buffer;
523   guchar *encoded_preamble;
524
525   encoded_preamble = (guchar *) g_strconcat ((gchar *) preamble,
526       (gchar *) root_element, NULL);
527
528   /* send the root element to the internal parser */
529   gst_cmml_dec_parse_xml (dec, root_element, strlen ((gchar *) root_element));
530   dec->sent_root = TRUE;
531
532   /* push the root element */
533   dec->flow_return = gst_cmml_dec_new_buffer (dec,
534       encoded_preamble, strlen ((gchar *) encoded_preamble), &buffer);
535   if (dec->flow_return == GST_FLOW_OK) {
536     dec->flow_return = gst_pad_push (dec->srcpad, buffer);
537   }
538
539   if (dec->flow_return == GST_FLOW_OK) {
540     GST_INFO_OBJECT (dec, "preamble parsed");
541   }
542
543   g_free (encoded_preamble);
544   return;
545 }
546
547 /* outputs the cmml head element and send TITLE and CMML_HEAD tags.
548  * This callback is registered with dec->parser. It is called when the
549  * head element is parsed.
550  */
551 static void
552 gst_cmml_dec_parse_head (GstCmmlDec * dec, GstCmmlTagHead * head)
553 {
554   GstTagList *tags;
555   GValue str_val = { 0 }, title_val = {
556   0};
557   guchar *head_str;
558   GstBuffer *buffer;
559
560   GST_DEBUG_OBJECT (dec, "found CMML head (title: %s base: %s)",
561       head->title, head->base);
562
563   /* FIXME: what's the point of all this GValue transform crack? */
564   /* create the GST_TAG_TITLE tag */
565   g_value_init (&str_val, G_TYPE_STRING);
566   g_value_init (&title_val, gst_tag_get_type (GST_TAG_TITLE));
567   g_value_set_string (&str_val, (gchar *) head->title);
568   g_value_transform (&str_val, &title_val);
569
570   tags = gst_tag_list_new_empty ();
571   gst_tag_list_add_values (tags, GST_TAG_MERGE_APPEND,
572       GST_TAG_TITLE, &title_val, NULL);
573   gst_tag_list_add (tags, GST_TAG_MERGE_APPEND, GST_TAG_CMML_HEAD, head, NULL);
574   gst_pad_push_event (dec->srcpad, gst_event_new_tag (tags));
575
576   g_value_unset (&str_val);
577   g_value_unset (&title_val);
578
579   head_str = gst_cmml_parser_tag_head_to_string (dec->parser, head);
580
581   dec->flow_return = gst_cmml_dec_new_buffer (dec,
582       head_str, strlen ((gchar *) head_str), &buffer);
583   g_free (head_str);
584   if (dec->flow_return == GST_FLOW_OK)
585     dec->flow_return = gst_pad_push (dec->srcpad, buffer);
586   if (dec->flow_return == GST_FLOW_NOT_LINKED)
587     dec->flow_return = GST_FLOW_OK;     /* Ignore NOT_LINKED */
588 }
589
590 /* send a TAG_MESSAGE event for a clip */
591 static void
592 gst_cmml_dec_send_clip_tag (GstCmmlDec * dec, GstCmmlTagClip * clip)
593 {
594   GstTagList *tags;
595
596   GST_DEBUG_OBJECT (dec, "sending clip tag %s", clip->id);
597
598   tags = gst_tag_list_new (GST_TAG_CMML_CLIP, clip, NULL);
599   gst_pad_push_event (dec->srcpad, gst_event_new_tag (tags));
600 }
601
602 /* push the string representation of a clip */
603 static void
604 gst_cmml_dec_push_clip (GstCmmlDec * dec, GstCmmlTagClip * clip)
605 {
606   GstBuffer *buffer;
607   guchar *clip_str;
608
609   GST_DEBUG_OBJECT (dec, "pushing clip %s", clip->id);
610
611   clip_str = gst_cmml_parser_tag_clip_to_string (dec->parser, clip);
612   dec->flow_return = gst_cmml_dec_new_buffer (dec,
613       clip_str, strlen ((gchar *) clip_str), &buffer);
614   if (dec->flow_return == GST_FLOW_OK)
615     dec->flow_return = gst_pad_push (dec->srcpad, buffer);
616   if (dec->flow_return == GST_FLOW_NOT_LINKED)
617     dec->flow_return = GST_FLOW_OK;     /* Ignore NOT_LINKED */
618
619   g_free (clip_str);
620 }
621
622 /* decode a clip tag
623  * this callback is registered with dec->parser. It is called whenever a
624  * clip is parsed.
625  */
626 static void
627 gst_cmml_dec_parse_clip (GstCmmlDec * dec, GstCmmlTagClip * clip)
628 {
629   GstCmmlTagClip *prev_clip;
630
631   dec->flow_return = GST_FLOW_OK;
632
633   if (clip->empty)
634     GST_INFO_OBJECT (dec, "parsing empty clip");
635   else
636     GST_INFO_OBJECT (dec, "parsing clip (id: %s)", clip->id);
637
638   clip->start_time = dec->timestamp;
639   if (clip->start_time == GST_CLOCK_TIME_NONE) {
640     GST_ELEMENT_ERROR (dec, STREAM, DECODE,
641         (NULL), ("invalid clip start time"));
642
643     dec->flow_return = GST_FLOW_ERROR;
644     return;
645   }
646
647   /* get the last clip in the current track */
648   prev_clip = gst_cmml_track_list_get_track_last_clip (dec->tracks,
649       (gchar *) clip->track);
650   if (prev_clip) {
651     /* output the previous clip */
652     if (clip->empty)
653       /* the current clip marks the end of the previous one */
654       prev_clip->end_time = clip->start_time;
655
656     gst_cmml_dec_push_clip (dec, prev_clip);
657   }
658
659   if (dec->wait_clip_end) {
660     /* now it's time to send the tag for the previous clip */
661     if (prev_clip) {
662       prev_clip->end_time = clip->start_time;
663       gst_cmml_dec_send_clip_tag (dec, prev_clip);
664     }
665   } else if (!clip->empty) {
666     /* send the tag for the current clip */
667     gst_cmml_dec_send_clip_tag (dec, clip);
668   }
669
670   if (prev_clip)
671     gst_cmml_track_list_del_clip (dec->tracks, prev_clip);
672
673   if (!clip->empty)
674     if (!gst_cmml_track_list_has_clip (dec->tracks, clip))
675       gst_cmml_track_list_add_clip (dec->tracks, clip);
676 }
677
678 gboolean
679 gst_cmml_dec_plugin_init (GstPlugin * plugin)
680 {
681   if (!gst_element_register (plugin, "cmmldec", GST_RANK_PRIMARY,
682           GST_TYPE_CMML_DEC))
683     return FALSE;
684
685   GST_DEBUG_CATEGORY_INIT (cmmldec, "cmmldec", 0,
686       "annodex CMML decoding element");
687
688   return TRUE;
689 }