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