upload tizen1.0 source
[framework/multimedia/gst-plugins-good0.10.git] / ext / annodex / gstcmmlenc.c
1 /*
2  * gstcmmlenc.c - GStreamer CMML encoder
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-cmmlenc
26  * @see_also: cmmldec, oggmux
27  *
28  * Cmmlenc encodes a CMML document into a CMML stream.  <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=annotations.cmml ! cmmlenc ! oggmux name=mux ! filesink location=annotated.ogg
37  * ]|
38  * </refsect2>
39  */
40
41
42 #ifdef HAVE_CONFIG_H
43 #include "config.h"
44 #endif
45
46 #include <string.h>
47 #include "gstcmmlenc.h"
48 #include "gstannodex.h"
49
50 GST_DEBUG_CATEGORY_STATIC (cmmlenc);
51 #define GST_CAT_DEFAULT cmmlenc
52
53 #define CMML_IDENT_HEADER_SIZE 29
54
55 enum
56 {
57   ARG_0,
58   GST_CMML_ENC_GRANULERATE_N,
59   GST_CMML_ENC_GRANULERATE_D,
60   GST_CMML_ENC_GRANULESHIFT
61 };
62
63 enum
64 {
65   LAST_SIGNAL
66 };
67
68 static GstStaticPadTemplate gst_cmml_enc_src_factory =
69 GST_STATIC_PAD_TEMPLATE ("src",
70     GST_PAD_SRC,
71     GST_PAD_ALWAYS,
72     GST_STATIC_CAPS ("text/x-cmml, encoded = (boolean) true")
73     );
74
75 static GstStaticPadTemplate gst_cmml_enc_sink_factory =
76 GST_STATIC_PAD_TEMPLATE ("sink",
77     GST_PAD_SINK,
78     GST_PAD_ALWAYS,
79     GST_STATIC_CAPS ("text/x-cmml, encoded = (boolean) false")
80     );
81
82 GST_BOILERPLATE (GstCmmlEnc, gst_cmml_enc, GstElement, GST_TYPE_ELEMENT);
83 static void gst_cmml_enc_get_property (GObject * object, guint property_id,
84     GValue * value, GParamSpec * pspec);
85 static void gst_cmml_enc_set_property (GObject * object, guint property_id,
86     const GValue * value, GParamSpec * pspec);
87 static gboolean gst_cmml_enc_sink_event (GstPad * pad, GstEvent * event);
88 static GstStateChangeReturn gst_cmml_enc_change_state (GstElement * element,
89     GstStateChange transition);
90 static GstFlowReturn gst_cmml_enc_chain (GstPad * pad, GstBuffer * buffer);
91 static void gst_cmml_enc_parse_preamble (GstCmmlEnc * enc,
92     guchar * preamble, guchar * processing_instruction);
93 static void gst_cmml_enc_parse_end_tag (GstCmmlEnc * enc);
94 static void gst_cmml_enc_parse_tag_head (GstCmmlEnc * enc,
95     GstCmmlTagHead * head);
96 static void gst_cmml_enc_parse_tag_clip (GstCmmlEnc * enc,
97     GstCmmlTagClip * tag);
98 static GstFlowReturn gst_cmml_enc_new_buffer (GstCmmlEnc * enc,
99     guchar * data, gint size, GstBuffer ** buffer);
100 static GstFlowReturn gst_cmml_enc_push_clip (GstCmmlEnc * enc,
101     GstCmmlTagClip * clip, GstClockTime prev_clip_time);
102 static GstFlowReturn gst_cmml_enc_push (GstCmmlEnc * enc, GstBuffer * buffer);
103
104 static void gst_cmml_enc_finalize (GObject * object);
105
106 static void
107 gst_cmml_enc_base_init (gpointer g_class)
108 {
109   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
110
111   gst_element_class_add_pad_template (element_class,
112       gst_static_pad_template_get (&gst_cmml_enc_sink_factory));
113   gst_element_class_add_pad_template (element_class,
114       gst_static_pad_template_get (&gst_cmml_enc_src_factory));
115   gst_element_class_set_details_simple (element_class, "CMML streams encoder",
116       "Codec/Encoder",
117       "Encodes CMML streams", "Alessandro Decina <alessandro@nnva.org>");
118 }
119
120 static void
121 gst_cmml_enc_class_init (GstCmmlEncClass * enc_class)
122 {
123   GObjectClass *klass = G_OBJECT_CLASS (enc_class);
124
125   klass->get_property = gst_cmml_enc_get_property;
126   klass->set_property = gst_cmml_enc_set_property;
127   klass->finalize = gst_cmml_enc_finalize;
128
129   g_object_class_install_property (klass, GST_CMML_ENC_GRANULERATE_N,
130       g_param_spec_int64 ("granule-rate-numerator",
131           "Granulerate numerator",
132           "Granulerate numerator",
133           0, G_MAXINT64, 1000,
134           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
135
136   g_object_class_install_property (klass, GST_CMML_ENC_GRANULERATE_D,
137       g_param_spec_int64 ("granule-rate-denominator",
138           "Granulerate denominator",
139           "Granulerate denominator",
140           0, G_MAXINT64, 1,
141           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
142
143   g_object_class_install_property (klass, GST_CMML_ENC_GRANULESHIFT,
144       g_param_spec_uchar ("granule-shift",
145           "Granuleshift",
146           "The number of lower bits to use for partitioning a granule position",
147           0, 64, 32,
148           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
149
150   GST_ELEMENT_CLASS (klass)->change_state = gst_cmml_enc_change_state;
151 }
152
153 static void
154 gst_cmml_enc_init (GstCmmlEnc * enc, GstCmmlEncClass * klass)
155 {
156   enc->sinkpad =
157       gst_pad_new_from_static_template (&gst_cmml_enc_sink_factory, "sink");
158   gst_pad_set_chain_function (enc->sinkpad, gst_cmml_enc_chain);
159   gst_pad_set_event_function (enc->sinkpad, gst_cmml_enc_sink_event);
160   gst_element_add_pad (GST_ELEMENT (enc), enc->sinkpad);
161
162   enc->srcpad =
163       gst_pad_new_from_static_template (&gst_cmml_enc_src_factory, "src");
164   gst_element_add_pad (GST_ELEMENT (enc), enc->srcpad);
165
166   enc->major = 3;
167   enc->minor = 0;
168 }
169
170 static void
171 gst_cmml_enc_set_property (GObject * object, guint property_id,
172     const GValue * value, GParamSpec * pspec)
173 {
174   GstCmmlEnc *enc = GST_CMML_ENC (object);
175
176   switch (property_id) {
177     case GST_CMML_ENC_GRANULERATE_N:
178       /* XXX: may need to flush clips */
179       enc->granulerate_n = g_value_get_int64 (value);
180       break;
181     case GST_CMML_ENC_GRANULERATE_D:
182       enc->granulerate_d = g_value_get_int64 (value);
183       break;
184     case GST_CMML_ENC_GRANULESHIFT:
185       enc->granuleshift = g_value_get_uchar (value);
186       break;
187     default:
188       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
189   }
190 }
191
192 static void
193 gst_cmml_enc_get_property (GObject * object, guint property_id,
194     GValue * value, GParamSpec * pspec)
195 {
196   GstCmmlEnc *enc = GST_CMML_ENC (object);
197
198   switch (property_id) {
199     case GST_CMML_ENC_GRANULERATE_N:
200       g_value_set_int64 (value, enc->granulerate_n);
201       break;
202     case GST_CMML_ENC_GRANULERATE_D:
203       g_value_set_int64 (value, enc->granulerate_d);
204       break;
205     case GST_CMML_ENC_GRANULESHIFT:
206       g_value_set_uchar (value, enc->granuleshift);
207       break;
208     default:
209       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
210   }
211 }
212
213 static void
214 gst_cmml_enc_finalize (GObject * object)
215 {
216   GstCmmlEnc *enc = GST_CMML_ENC (object);
217
218   if (enc->tracks) {
219     gst_cmml_track_list_destroy (enc->tracks);
220     enc->tracks = NULL;
221   }
222
223   G_OBJECT_CLASS (parent_class)->finalize (object);
224 }
225
226 static GstStateChangeReturn
227 gst_cmml_enc_change_state (GstElement * element, GstStateChange transition)
228 {
229   GstCmmlEnc *enc = GST_CMML_ENC (element);
230   GstStateChangeReturn res;
231
232   switch (transition) {
233     case GST_STATE_CHANGE_READY_TO_PAUSED:
234       enc->parser = gst_cmml_parser_new (GST_CMML_PARSER_ENCODE);
235       enc->parser->user_data = enc;
236       enc->parser->preamble_callback =
237           (GstCmmlParserPreambleCallback) gst_cmml_enc_parse_preamble;
238       enc->parser->head_callback =
239           (GstCmmlParserHeadCallback) gst_cmml_enc_parse_tag_head;
240       enc->parser->clip_callback =
241           (GstCmmlParserClipCallback) gst_cmml_enc_parse_tag_clip;
242       enc->parser->cmml_end_callback =
243           (GstCmmlParserCmmlEndCallback) gst_cmml_enc_parse_end_tag;
244       enc->tracks = gst_cmml_track_list_new ();
245       enc->sent_headers = FALSE;
246       enc->sent_eos = FALSE;
247       enc->flow_return = GST_FLOW_OK;
248       break;
249     default:
250       break;
251   }
252
253   res = parent_class->change_state (element, transition);
254
255   switch (transition) {
256     case GST_STATE_CHANGE_PAUSED_TO_READY:
257     {
258       gst_cmml_track_list_destroy (enc->tracks);
259       enc->tracks = NULL;
260       g_free (enc->preamble);
261       enc->preamble = NULL;
262       gst_cmml_parser_free (enc->parser);
263       break;
264     }
265     default:
266       break;
267   }
268
269   return res;
270 }
271
272 static gboolean
273 gst_cmml_enc_sink_event (GstPad * pad, GstEvent * event)
274 {
275   GstCmmlEnc *enc = GST_CMML_ENC (GST_PAD_PARENT (pad));
276
277   switch (GST_EVENT_TYPE (event)) {
278     case GST_EVENT_EOS:
279     {
280       if (!enc->sent_eos)
281         gst_cmml_enc_parse_end_tag (enc);
282
283       break;
284     }
285     default:
286       break;
287   }
288
289   return gst_pad_event_default (pad, event);
290 }
291
292 static GstFlowReturn
293 gst_cmml_enc_new_buffer (GstCmmlEnc * enc,
294     guchar * data, gint size, GstBuffer ** buffer)
295 {
296   GstFlowReturn res;
297
298   res = gst_pad_alloc_buffer (enc->srcpad, GST_BUFFER_OFFSET_NONE, size,
299       NULL, buffer);
300   if (res == GST_FLOW_OK) {
301     if (data)
302       memcpy (GST_BUFFER_DATA (*buffer), data, size);
303   } else {
304     GST_WARNING_OBJECT (enc, "alloc function returned error %s",
305         gst_flow_get_name (res));
306   }
307
308   return res;
309 }
310
311 static GstCaps *
312 gst_cmml_enc_set_header_on_caps (GstCmmlEnc * enc, GstCaps * caps,
313     GstBuffer * ident, GstBuffer * preamble, GstBuffer * head)
314 {
315   GValue array = { 0 };
316   GValue value = { 0 };
317   GstStructure *structure;
318   GstBuffer *buffer;
319
320   caps = gst_caps_make_writable (caps);
321   structure = gst_caps_get_structure (caps, 0);
322
323   g_value_init (&array, GST_TYPE_ARRAY);
324   g_value_init (&value, GST_TYPE_BUFFER);
325
326   /* Make copies of header buffers to avoid circular references */
327   buffer = gst_buffer_copy (ident);
328   gst_value_set_buffer (&value, buffer);
329   gst_value_array_append_value (&array, &value);
330   gst_buffer_unref (buffer);
331
332   buffer = gst_buffer_copy (preamble);
333   gst_value_set_buffer (&value, buffer);
334   gst_value_array_append_value (&array, &value);
335   gst_buffer_unref (buffer);
336
337   buffer = gst_buffer_copy (head);
338   gst_value_set_buffer (&value, buffer);
339   gst_value_array_append_value (&array, &value);
340   gst_buffer_unref (buffer);
341
342   GST_BUFFER_FLAG_SET (ident, GST_BUFFER_FLAG_IN_CAPS);
343   GST_BUFFER_FLAG_SET (preamble, GST_BUFFER_FLAG_IN_CAPS);
344   GST_BUFFER_FLAG_SET (head, GST_BUFFER_FLAG_IN_CAPS);
345
346   gst_structure_set_value (structure, "streamheader", &array);
347
348   g_value_unset (&value);
349   g_value_unset (&array);
350
351   return caps;
352 }
353
354 /* create a CMML ident header buffer
355  */
356 static GstFlowReturn
357 gst_cmml_enc_new_ident_header (GstCmmlEnc * enc, GstBuffer ** buffer)
358 {
359   guint8 ident_header[CMML_IDENT_HEADER_SIZE];
360   guint8 *wptr = ident_header;
361
362   memcpy (wptr, "CMML\0\0\0\0", 8);
363   wptr += 8;
364   GST_WRITE_UINT16_LE (wptr, enc->major);
365   wptr += 2;
366   GST_WRITE_UINT16_LE (wptr, enc->minor);
367   wptr += 2;
368   GST_WRITE_UINT64_LE (wptr, enc->granulerate_n);
369   wptr += 8;
370   GST_WRITE_UINT64_LE (wptr, enc->granulerate_d);
371   wptr += 8;
372   *wptr = enc->granuleshift;
373
374   return gst_cmml_enc_new_buffer (enc,
375       (guchar *) & ident_header, CMML_IDENT_HEADER_SIZE, buffer);
376 }
377
378 /* parse the CMML preamble */
379 static void
380 gst_cmml_enc_parse_preamble (GstCmmlEnc * enc,
381     guchar * preamble, guchar * processing_instruction)
382 {
383   GST_INFO_OBJECT (enc, "parsing preamble");
384
385   /* save the preamble: it will be pushed when the head tag is found */
386   enc->preamble = (guchar *) g_strconcat ((gchar *) preamble,
387       (gchar *) processing_instruction, NULL);
388 }
389
390 /* parse the CMML end tag */
391 static void
392 gst_cmml_enc_parse_end_tag (GstCmmlEnc * enc)
393 {
394   GstBuffer *buffer;
395
396   GST_INFO_OBJECT (enc, "parsing end tag");
397
398   /* push an empty buffer to signal EOS */
399   enc->flow_return = gst_cmml_enc_new_buffer (enc, NULL, 0, &buffer);
400   if (enc->flow_return == GST_FLOW_OK) {
401     /* set granulepos 0 on EOS */
402     GST_BUFFER_OFFSET_END (buffer) = 0;
403     enc->flow_return = gst_cmml_enc_push (enc, buffer);
404     enc->sent_eos = TRUE;
405   }
406
407   return;
408 }
409
410 /* encode the CMML head tag and push the CMML headers
411  */
412 static void
413 gst_cmml_enc_parse_tag_head (GstCmmlEnc * enc, GstCmmlTagHead * head)
414 {
415   GList *headers = NULL;
416   GList *walk;
417   guchar *head_string;
418   GstCaps *caps;
419   GstBuffer *ident_buf, *preamble_buf, *head_buf;
420   GstBuffer *buffer;
421
422   if (enc->preamble == NULL)
423     goto flow_unexpected;
424
425   GST_INFO_OBJECT (enc, "parsing head tag");
426
427   enc->flow_return = gst_cmml_enc_new_ident_header (enc, &ident_buf);
428   if (enc->flow_return != GST_FLOW_OK)
429     goto alloc_error;
430   headers = g_list_append (headers, ident_buf);
431
432   enc->flow_return = gst_cmml_enc_new_buffer (enc,
433       enc->preamble, strlen ((gchar *) enc->preamble), &preamble_buf);
434   if (enc->flow_return != GST_FLOW_OK)
435     goto alloc_error;
436   headers = g_list_append (headers, preamble_buf);
437
438   head_string = gst_cmml_parser_tag_head_to_string (enc->parser, head);
439   enc->flow_return = gst_cmml_enc_new_buffer (enc,
440       head_string, strlen ((gchar *) head_string), &head_buf);
441   g_free (head_string);
442   if (enc->flow_return != GST_FLOW_OK)
443     goto alloc_error;
444   headers = g_list_append (headers, head_buf);
445
446   caps = gst_pad_get_caps (enc->srcpad);
447   caps = gst_cmml_enc_set_header_on_caps (enc, caps,
448       ident_buf, preamble_buf, head_buf);
449
450   while (headers) {
451     buffer = GST_BUFFER (headers->data);
452     /* set granulepos 0 on headers */
453     GST_BUFFER_OFFSET_END (buffer) = 0;
454     gst_buffer_set_caps (buffer, caps);
455
456     enc->flow_return = gst_cmml_enc_push (enc, buffer);
457     headers = g_list_delete_link (headers, headers);
458
459     if (enc->flow_return != GST_FLOW_OK)
460       goto push_error;
461   }
462
463   gst_caps_unref (caps);
464
465   enc->sent_headers = TRUE;
466   return;
467
468 flow_unexpected:
469   GST_ELEMENT_ERROR (enc, STREAM, ENCODE,
470       (NULL), ("got head tag before preamble"));
471   enc->flow_return = GST_FLOW_ERROR;
472   return;
473 push_error:
474   gst_caps_unref (caps);
475   /* fallthrough */
476 alloc_error:
477   for (walk = headers; walk; walk = walk->next)
478     gst_buffer_unref (GST_BUFFER (walk->data));
479   g_list_free (headers);
480   return;
481 }
482
483 /* encode a CMML clip tag
484  * remove the start and end attributes (GstCmmlParser does this itself) and
485  * push the tag with the timestamp of its start attribute. If the tag has the
486  * end attribute, create a new empty clip and encode it.
487  */
488 static void
489 gst_cmml_enc_parse_tag_clip (GstCmmlEnc * enc, GstCmmlTagClip * clip)
490 {
491   GstCmmlTagClip *prev_clip;
492   GstClockTime prev_clip_time = GST_CLOCK_TIME_NONE;
493
494   /* this can happen if there's a programming error (eg user forgets to set
495    * the start-time property) or if one of the gst_cmml_clock_time_from_*
496    * overflows in GstCmmlParser */
497   if (clip->start_time == GST_CLOCK_TIME_NONE) {
498     GST_ELEMENT_ERROR (enc, STREAM, ENCODE,
499         (NULL), ("invalid start time for clip (%s)", clip->id));
500     enc->flow_return = GST_FLOW_ERROR;
501
502     return;
503   }
504
505   /* get the previous clip's start time to encode the current granulepos */
506   prev_clip = gst_cmml_track_list_get_track_last_clip (enc->tracks,
507       (gchar *) clip->track);
508   if (prev_clip) {
509     prev_clip_time = prev_clip->start_time;
510     if (prev_clip_time > clip->start_time) {
511       GST_ELEMENT_ERROR (enc, STREAM, ENCODE,
512           (NULL), ("previous clip start time > current clip (%s) start time",
513               clip->id));
514       enc->flow_return = GST_FLOW_ERROR;
515       return;
516     }
517
518     /* we don't need the prev clip anymore */
519     gst_cmml_track_list_del_clip (enc->tracks, prev_clip);
520   }
521
522   /* add the current clip to the tracklist */
523   gst_cmml_track_list_add_clip (enc->tracks, clip);
524
525   enc->flow_return = gst_cmml_enc_push_clip (enc, clip, prev_clip_time);
526 }
527
528 static GstFlowReturn
529 gst_cmml_enc_push_clip (GstCmmlEnc * enc, GstCmmlTagClip * clip,
530     GstClockTime prev_clip_time)
531 {
532   GstFlowReturn res;
533   GstBuffer *buffer;
534   gchar *clip_string;
535   gint64 granulepos;
536
537   /* encode the clip */
538   clip_string =
539       (gchar *) gst_cmml_parser_tag_clip_to_string (enc->parser, clip);
540
541   res = gst_cmml_enc_new_buffer (enc,
542       (guchar *) clip_string, strlen (clip_string), &buffer);
543   g_free (clip_string);
544   if (res != GST_FLOW_OK)
545     goto done;
546
547   GST_INFO_OBJECT (enc, "encoding clip"
548       "(start-time: %" GST_TIME_FORMAT " end-time: %" GST_TIME_FORMAT,
549       GST_TIME_ARGS (clip->start_time), GST_TIME_ARGS (clip->end_time));
550
551   /* set the granulepos */
552   granulepos = gst_cmml_clock_time_to_granule (prev_clip_time, clip->start_time,
553       enc->granulerate_n, enc->granulerate_d, enc->granuleshift);
554   if (granulepos == -1) {
555     gst_buffer_unref (buffer);
556     goto granule_overflow;
557   }
558
559   GST_BUFFER_OFFSET (buffer) = clip->start_time;
560   GST_BUFFER_OFFSET_END (buffer) = granulepos;
561   GST_BUFFER_TIMESTAMP (buffer) = clip->start_time;
562
563   res = gst_cmml_enc_push (enc, buffer);
564   if (res != GST_FLOW_OK)
565     goto done;
566
567   if (clip->end_time != GST_CLOCK_TIME_NONE) {
568     /* create a new empty clip for the same cmml track starting at end_time
569      */
570     GObject *end_clip = g_object_new (GST_TYPE_CMML_TAG_CLIP,
571         "start-time", clip->end_time, "track", clip->track, NULL);
572
573     /* encode the empty end clip */
574     gst_cmml_enc_push_clip (enc, GST_CMML_TAG_CLIP (end_clip),
575         clip->start_time);
576     g_object_unref (end_clip);
577   }
578 done:
579   return res;
580
581 granule_overflow:
582   GST_ELEMENT_ERROR (enc, STREAM, ENCODE, (NULL), ("granulepos overflow"));
583   return GST_FLOW_ERROR;
584 }
585
586 static GstFlowReturn
587 gst_cmml_enc_push (GstCmmlEnc * enc, GstBuffer * buffer)
588 {
589   GstFlowReturn res;
590
591   res = gst_pad_push (enc->srcpad, buffer);
592   if (res != GST_FLOW_OK)
593     GST_WARNING_OBJECT (enc, "push returned: %s", gst_flow_get_name (res));
594
595   return res;
596 }
597
598 static GstFlowReturn
599 gst_cmml_enc_chain (GstPad * pad, GstBuffer * buffer)
600 {
601   GError *err = NULL;
602   GstCmmlEnc *enc = GST_CMML_ENC (GST_PAD_PARENT (pad));
603
604   /* the CMML handlers registered with enc->parser will override this when
605    * encoding/pushing the buffers downstream
606    */
607   enc->flow_return = GST_FLOW_OK;
608
609   if (!gst_cmml_parser_parse_chunk (enc->parser,
610           (gchar *) GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), &err)) {
611     GST_ELEMENT_ERROR (enc, STREAM, ENCODE, (NULL), ("%s", err->message));
612     g_error_free (err);
613     enc->flow_return = GST_FLOW_ERROR;
614   }
615
616   gst_buffer_unref (buffer);
617   return enc->flow_return;
618 }
619
620 gboolean
621 gst_cmml_enc_plugin_init (GstPlugin * plugin)
622 {
623   if (!gst_element_register (plugin, "cmmlenc", GST_RANK_NONE,
624           GST_TYPE_CMML_ENC))
625     return FALSE;
626
627   GST_DEBUG_CATEGORY_INIT (cmmlenc, "cmmlenc", 0,
628       "annodex cmml decoding element");
629
630   return TRUE;
631 }