summaryrefslogtreecommitdiff
path: root/ext/annodex/gstcmmlenc.c
diff options
context:
space:
mode:
authorMichael Smith <msmith@xiph.org>2006-02-24 19:07:10 +0000
committerMichael Smith <msmith@xiph.org>2006-02-24 19:07:10 +0000
commit73ce8c5b9e9a9cb27c1307dbfee300939265650c (patch)
tree207f645f3849f86026894e7411d9335353a12e53 /ext/annodex/gstcmmlenc.c
parentdd8f37faac9ea109550d630f4d7dcbf295cd5fc1 (diff)
Add Annodex elements from Alessendro Decina: skeleton and CMML.
Original commit message from CVS: * configure.ac: * docs/plugins/gst-plugins-good-plugins-sections.txt: * ext/Makefile.am: * ext/annodex/Makefile.am: * ext/annodex/gstannodex.c: * ext/annodex/gstannodex.h: * ext/annodex/gstcmmldec.c: * ext/annodex/gstcmmldec.h: * ext/annodex/gstcmmlenc.c: * ext/annodex/gstcmmlenc.h: * ext/annodex/gstcmmlparser.c: * ext/annodex/gstcmmlparser.h: * ext/annodex/gstcmmltag.c: * ext/annodex/gstcmmltag.h: * ext/annodex/gstcmmlutils.c: * ext/annodex/gstcmmlutils.h: * ext/annodex/gstskeldec.c: * ext/annodex/gstskeldec.h: * ext/annodex/gstskeltag.c: * ext/annodex/gstskeltag.h: * tests/check/Makefile.am: * tests/check/elements/cmmldec.c: * tests/check/elements/cmmlenc.c: * tests/check/elements/skeldec.c: Add Annodex elements from Alessendro Decina: skeleton and CMML. Includes tests & docs, oh my! Passes Thomas's -good checklist entirely. Wow.
Diffstat (limited to 'ext/annodex/gstcmmlenc.c')
-rw-r--r--ext/annodex/gstcmmlenc.c624
1 files changed, 624 insertions, 0 deletions
diff --git a/ext/annodex/gstcmmlenc.c b/ext/annodex/gstcmmlenc.c
new file mode 100644
index 000000000..7bc98bc87
--- /dev/null
+++ b/ext/annodex/gstcmmlenc.c
@@ -0,0 +1,624 @@
+/*
+ * gstcmmlenc.c - GStreamer CMML encoder
+ * Copyright (C) 2005 Alessandro Decina
+ *
+ * Authors:
+ * Alessandro Decina <alessandro@nnva.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:element-cmmlenc
+ * @see_also: cmmldec, oggmux
+ *
+ * <refsect2>
+ * <para> Cmmlenc encodes a CMML document into a CMML stream. <ulink
+ * url="http://www.annodex.net/TR/draft-pfeiffer-cmml-02.html">CMML</ulink> is
+ * an XML markup language for time-continuous data maintained by the <ulink
+ * url="http:/www.annodex.org/">Annodex Foundation</ulink>.
+ * </para>
+ * <title>Example pipeline</title>
+ * <programlisting>
+ * gst-launch -v filesrc location=annotations.cmml ! cmmlenc ! oggmux name=mux ! filesink location=annotated.ogg
+ * </programlisting>
+ * </refsect2>
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include "gstcmmlenc.h"
+#include "gstannodex.h"
+
+GST_DEBUG_CATEGORY (cmmlenc);
+#define GST_CAT_DEFAULT cmmlenc
+
+#define CMML_IDENT_HEADER_SIZE 29
+
+enum
+{
+ ARG_0,
+ GST_CMML_ENC_GRANULERATE_N,
+ GST_CMML_ENC_GRANULERATE_D,
+ GST_CMML_ENC_GRANULESHIFT
+};
+
+enum
+{
+ LAST_SIGNAL
+};
+
+static GstElementDetails gst_cmml_enc_details = {
+ "cmmlenc: Encodes CMML streams", "Codec/Encoder",
+ "Encodes CMML streams",
+ "Alessandro Decina <alessandro@nnva.org>",
+};
+
+static GstStaticPadTemplate gst_cmml_enc_src_factory =
+GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("text/x-cmml")
+ );
+
+static GstStaticPadTemplate gst_cmml_enc_sink_factory =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("text/xml")
+ );
+
+GST_BOILERPLATE (GstCmmlEnc, gst_cmml_enc, GstElement, GST_TYPE_ELEMENT);
+static void gst_cmml_enc_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec);
+static void gst_cmml_enc_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec);
+static gboolean gst_cmml_enc_sink_event (GstPad * pad, GstEvent * event);
+static GstStateChangeReturn gst_cmml_enc_change_state (GstElement * element,
+ GstStateChange transition);
+static GstFlowReturn gst_cmml_enc_chain (GstPad * pad, GstBuffer * buffer);
+static void gst_cmml_enc_parse_preamble (GstCmmlEnc * enc,
+ guchar * preamble, guchar * processing_instruction);
+static void gst_cmml_enc_parse_end_tag (GstCmmlEnc * enc);
+static void gst_cmml_enc_parse_tag_head (GstCmmlEnc * enc,
+ GstCmmlTagHead * head);
+static void gst_cmml_enc_parse_tag_clip (GstCmmlEnc * enc,
+ GstCmmlTagClip * tag);
+static GstFlowReturn gst_cmml_enc_new_buffer (GstCmmlEnc * enc,
+ guchar * data, gint size, GstBuffer ** buffer);
+#if 0
+static void gst_cmml_enc_flush_clips (GstCmmlEnc * enc);
+#endif
+static GstFlowReturn gst_cmml_enc_push_clip (GstCmmlEnc * enc,
+ GstCmmlTagClip * clip, GstClockTime prev_clip_time);
+static GstFlowReturn gst_cmml_enc_push (GstCmmlEnc * enc, GstBuffer * buffer);
+
+static void
+gst_cmml_enc_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&gst_cmml_enc_sink_factory));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&gst_cmml_enc_src_factory));
+ gst_element_class_set_details (element_class, &gst_cmml_enc_details);
+}
+
+static void
+gst_cmml_enc_class_init (GstCmmlEncClass * enc_class)
+{
+ GObjectClass *klass = G_OBJECT_CLASS (enc_class);
+
+ klass->get_property = gst_cmml_enc_get_property;
+ klass->set_property = gst_cmml_enc_set_property;
+
+ g_object_class_install_property (klass, GST_CMML_ENC_GRANULERATE_N,
+ g_param_spec_int64 ("granule-rate-numerator",
+ "Granulerate numerator",
+ "Granulerate numerator",
+ 0, G_MAXINT64, 1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (klass, GST_CMML_ENC_GRANULERATE_D,
+ g_param_spec_int64 ("granule-rate-denominator",
+ "Granulerate denominator",
+ "Granulerate denominator",
+ 0, G_MAXINT64, 1000, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ g_object_class_install_property (klass, GST_CMML_ENC_GRANULESHIFT,
+ g_param_spec_uchar ("granule-shift",
+ "Granuleshift",
+ "The number of lower bits to use for partitioning a granule position",
+ 0, G_MAXUINT8, 32, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+ GST_ELEMENT_CLASS (klass)->change_state = gst_cmml_enc_change_state;
+}
+
+static void
+gst_cmml_enc_init (GstCmmlEnc * enc, GstCmmlEncClass * klass)
+{
+ enc->sinkpad =
+ gst_pad_new_from_static_template (&gst_cmml_enc_sink_factory, "sink");
+ gst_pad_set_chain_function (enc->sinkpad, gst_cmml_enc_chain);
+ gst_pad_set_event_function (enc->sinkpad, gst_cmml_enc_sink_event);
+ gst_element_add_pad (GST_ELEMENT (enc), enc->sinkpad);
+
+ enc->srcpad =
+ gst_pad_new_from_static_template (&gst_cmml_enc_src_factory, "src");
+ gst_element_add_pad (GST_ELEMENT (enc), enc->srcpad);
+
+ enc->major = 3;
+ enc->minor = 0;
+}
+
+static void
+gst_cmml_enc_set_property (GObject * object, guint property_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstCmmlEnc *enc = GST_CMML_ENC (object);
+
+ switch (property_id) {
+ case GST_CMML_ENC_GRANULERATE_N:
+ /* XXX: may need to flush clips */
+ enc->granulerate_n = g_value_get_int64 (value);
+ break;
+ case GST_CMML_ENC_GRANULERATE_D:
+ enc->granulerate_d = g_value_get_int64 (value);
+ break;
+ case GST_CMML_ENC_GRANULESHIFT:
+ enc->granuleshift = g_value_get_uchar (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+gst_cmml_enc_get_property (GObject * object, guint property_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstCmmlEnc *enc = GST_CMML_ENC (object);
+
+ switch (property_id) {
+ case GST_CMML_ENC_GRANULERATE_N:
+ g_value_set_int64 (value, enc->granulerate_n);
+ break;
+ case GST_CMML_ENC_GRANULERATE_D:
+ g_value_set_int64 (value, enc->granulerate_d);
+ break;
+ case GST_CMML_ENC_GRANULESHIFT:
+ g_value_set_uchar (value, enc->granuleshift);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static GstStateChangeReturn
+gst_cmml_enc_change_state (GstElement * element, GstStateChange transition)
+{
+ GstCmmlEnc *enc = GST_CMML_ENC (element);
+ GstStateChangeReturn res;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_READY_TO_PAUSED:
+ enc->parser = gst_cmml_parser_new (GST_CMML_PARSER_ENCODE);
+ enc->parser->user_data = enc;
+ enc->parser->preamble_callback =
+ (GstCmmlParserPreambleCallback) gst_cmml_enc_parse_preamble;
+ enc->parser->head_callback =
+ (GstCmmlParserHeadCallback) gst_cmml_enc_parse_tag_head;
+ enc->parser->clip_callback =
+ (GstCmmlParserClipCallback) gst_cmml_enc_parse_tag_clip;
+ enc->parser->cmml_end_callback =
+ (GstCmmlParserCmmlEndCallback) gst_cmml_enc_parse_end_tag;
+ enc->tracks = gst_cmml_track_list_new ();
+ enc->sent_headers = FALSE;
+ enc->sent_eos = FALSE;
+ enc->flow_return = GST_FLOW_OK;
+ break;
+ default:
+ break;
+ }
+
+ res = parent_class->change_state (element, transition);
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ {
+ gst_cmml_track_list_destroy (enc->tracks);
+ g_free (enc->preamble);
+ gst_cmml_parser_free (enc->parser);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return res;
+}
+
+static gboolean
+gst_cmml_enc_sink_event (GstPad * pad, GstEvent * event)
+{
+ GstCmmlEnc *enc = GST_CMML_ENC (GST_PAD_PARENT (pad));
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_EOS:
+ {
+ if (!enc->sent_eos)
+ gst_cmml_enc_parse_end_tag (enc);
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ return gst_pad_event_default (pad, event);
+}
+
+static GstFlowReturn
+gst_cmml_enc_new_buffer (GstCmmlEnc * enc,
+ guchar * data, gint size, GstBuffer ** buffer)
+{
+ GstFlowReturn res;
+
+ res = gst_pad_alloc_buffer (enc->srcpad, GST_BUFFER_OFFSET_NONE, size,
+ NULL, buffer);
+ if (res == GST_FLOW_OK) {
+ if (data)
+ memcpy (GST_BUFFER_DATA (*buffer), data, size);
+ } else {
+ GST_ELEMENT_ERROR (enc, STREAM, ENCODE,
+ (NULL), ("alloc function returned error %s", gst_flow_get_name (res)));
+ }
+
+ return res;
+}
+
+static GstCaps *
+gst_cmml_enc_set_header_on_caps (GstCaps * caps,
+ GstBuffer * ident, GstBuffer * preamble, GstBuffer * head)
+{
+ GValue array = { 0 };
+ GValue value = { 0 };
+ GstStructure *structure;
+
+ caps = gst_caps_make_writable (caps);
+ structure = gst_caps_get_structure (caps, 0);
+
+ g_value_init (&array, GST_TYPE_ARRAY);
+ g_value_init (&value, GST_TYPE_BUFFER);
+
+ gst_value_set_buffer (&value, ident);
+ gst_value_array_append_value (&array, &value);
+ gst_value_set_buffer (&value, preamble);
+ gst_value_array_append_value (&array, &value);
+ gst_value_set_buffer (&value, head);
+ gst_value_array_append_value (&array, &value);
+
+ GST_BUFFER_FLAG_SET (ident, GST_BUFFER_FLAG_IN_CAPS);
+ GST_BUFFER_FLAG_SET (preamble, GST_BUFFER_FLAG_IN_CAPS);
+ GST_BUFFER_FLAG_SET (head, GST_BUFFER_FLAG_IN_CAPS);
+
+ gst_structure_set_value (structure, "streamheader", &array);
+
+ g_value_unset (&value);
+ g_value_unset (&array);
+
+ return caps;
+}
+
+/* create a CMML ident header buffer
+ */
+static GstFlowReturn
+gst_cmml_enc_new_ident_header (GstCmmlEnc * enc, GstBuffer ** buffer)
+{
+ guint8 ident_header[CMML_IDENT_HEADER_SIZE];
+ guint8 *wptr = ident_header;
+
+ memcpy (wptr, "CMML\0\0\0\0", 8);
+ wptr += 8;
+ GST_WRITE_UINT16_LE (wptr, enc->major);
+ wptr += 2;
+ GST_WRITE_UINT16_LE (wptr, enc->minor);
+ wptr += 2;
+ GST_WRITE_UINT64_LE (wptr, enc->granulerate_n);
+ wptr += 8;
+ GST_WRITE_UINT64_LE (wptr, enc->granulerate_d);
+ wptr += 8;
+ *wptr = enc->granuleshift;
+
+ return gst_cmml_enc_new_buffer (enc,
+ (guchar *) & ident_header, CMML_IDENT_HEADER_SIZE, buffer);
+}
+
+/* parse the CMML preamble */
+static void
+gst_cmml_enc_parse_preamble (GstCmmlEnc * enc,
+ guchar * preamble, guchar * processing_instruction)
+{
+ GST_INFO_OBJECT (enc, "parsing preamble");
+
+ /* save the preamble: it will be pushed when the head tag is found */
+ enc->preamble = (guchar *) g_strconcat ((gchar *) preamble,
+ (gchar *) processing_instruction, NULL);
+}
+
+/* parse the CMML end tag */
+static void
+gst_cmml_enc_parse_end_tag (GstCmmlEnc * enc)
+{
+ GstBuffer *buffer;
+
+ GST_INFO_OBJECT (enc, "parsing end tag");
+#if 0
+ if (!enc->streaming)
+ gst_cmml_enc_flush_clips (enc);
+
+ if (GST_FLOW_IS_FATAL (enc->flow_return))
+ goto done;
+#endif
+
+ /* push an empty buffer to signal EOS */
+ enc->flow_return = gst_cmml_enc_new_buffer (enc, NULL, 0, &buffer);
+ if (enc->flow_return == GST_FLOW_OK) {
+ /* set granulepos 0 on EOS */
+ GST_BUFFER_OFFSET_END (buffer) = 0;
+ enc->flow_return = gst_cmml_enc_push (enc, buffer);
+ enc->sent_eos = TRUE;
+ }
+
+ return;
+}
+
+/* encode the CMML head tag and push the CMML headers
+ */
+static void
+gst_cmml_enc_parse_tag_head (GstCmmlEnc * enc, GstCmmlTagHead * head)
+{
+ GList *headers = NULL;
+ GList *walk;
+ guchar *head_string;
+ GstCaps *caps;
+ GstBuffer *ident_buf, *preamble_buf, *head_buf;
+ GstBuffer *buffer;
+
+ if (enc->preamble == NULL)
+ goto flow_unexpected;
+
+ GST_INFO_OBJECT (enc, "parsing head tag");
+
+ enc->flow_return = gst_cmml_enc_new_ident_header (enc, &ident_buf);
+ if (enc->flow_return != GST_FLOW_OK)
+ goto alloc_error;
+ headers = g_list_append (headers, ident_buf);
+
+ enc->flow_return = gst_cmml_enc_new_buffer (enc,
+ enc->preamble, strlen ((gchar *) enc->preamble), &preamble_buf);
+ if (enc->flow_return != GST_FLOW_OK)
+ goto alloc_error;
+ headers = g_list_append (headers, preamble_buf);
+
+ head_string = gst_cmml_parser_tag_head_to_string (enc->parser, head);
+ enc->flow_return = gst_cmml_enc_new_buffer (enc,
+ head_string, strlen ((gchar *) head_string), &head_buf);
+ g_free (head_string);
+ if (enc->flow_return != GST_FLOW_OK)
+ goto alloc_error;
+ headers = g_list_append (headers, head_buf);
+
+ caps = gst_pad_get_caps (enc->srcpad);
+ /* this call unrefs caps and creates new caps */
+ caps = gst_cmml_enc_set_header_on_caps (caps,
+ ident_buf, preamble_buf, head_buf);
+
+ for (walk = headers; walk; walk = walk->next) {
+ buffer = GST_BUFFER (walk->data);
+ /* set granulepos 0 on headers */
+ GST_BUFFER_OFFSET_END (buffer) = 0;
+ gst_buffer_set_caps (buffer, caps);
+
+ enc->flow_return = gst_cmml_enc_push (enc, buffer);
+ if (GST_FLOW_IS_FATAL (enc->flow_return))
+ goto push_error;
+
+ }
+
+ gst_caps_unref (caps);
+ g_list_free (headers);
+
+ enc->sent_headers = TRUE;
+ return;
+
+flow_unexpected:
+ GST_ELEMENT_ERROR (enc, STREAM, ENCODE,
+ (NULL), ("got head tag before preamble"));
+ enc->flow_return = GST_FLOW_UNEXPECTED;
+ return;
+push_error:
+ gst_caps_unref (caps);
+ /* fallthrough */
+alloc_error:
+ for (walk = headers; walk; walk = walk->next)
+ gst_buffer_unref (GST_BUFFER (walk->data));
+ g_list_free (headers);
+ return;
+}
+
+#if 0
+static void
+gst_cmml_enc_flush_clips (GstCmmlEnc * enc)
+{
+ GList *clips, *walk;
+
+ clips = gst_cmml_track_list_get_clips (enc->tracks);
+ for (walk = clips; walk; walk = g_list_next (walk)) {
+ enc->flow_return = gst_cmml_enc_push_clip (enc,
+ GST_CMML_TAG_CLIP (walk->data));
+ if (!GST_FLOW_IS_FATAL (enc->flow_return))
+ break;
+ }
+}
+#endif
+
+/* encode a CMML clip tag
+ * remove the start and end attributes (GstCmmlParser does this itself) and
+ * push the tag with the timestamp of its start attribute. If the tag has the
+ * end attribute, create a new empty clip and encode it.
+ */
+static void
+gst_cmml_enc_parse_tag_clip (GstCmmlEnc * enc, GstCmmlTagClip * clip)
+{
+ GstCmmlTagClip *prev_clip;
+ GstClockTime prev_clip_time = GST_CLOCK_TIME_NONE;
+
+ /* this can happen if there's a programming error (eg user forgets to set
+ * the start-time property) or if one of the gst_cmml_clock_time_from_*
+ * overflows in the GstCmmlParser code */
+ if (clip->start_time == GST_CLOCK_TIME_NONE) {
+ GST_ELEMENT_ERROR (enc, STREAM, ENCODE,
+ (NULL), ("invalid start time for clip (%s)", clip->id));
+ enc->flow_return = GST_FLOW_ERROR;
+
+ return;
+ }
+
+ prev_clip = gst_cmml_track_list_get_track_last_clip (enc->tracks,
+ (gchar *) clip->track);
+ if (prev_clip) {
+ prev_clip_time = prev_clip->start_time;
+ gst_cmml_track_list_del_clip (enc->tracks, prev_clip);
+ }
+
+ /* add the current clip to the tracklist */
+ gst_cmml_track_list_add_clip (enc->tracks, clip);
+
+ enc->flow_return = gst_cmml_enc_push_clip (enc, clip, prev_clip_time);
+}
+
+static GstFlowReturn
+gst_cmml_enc_push_clip (GstCmmlEnc * enc, GstCmmlTagClip * clip,
+ GstClockTime prev_clip_time)
+{
+ GstFlowReturn res;
+ GstBuffer *buffer;
+ gchar *clip_string;
+ gint64 granulepos;
+
+ if (prev_clip_time != GST_CLOCK_TIME_NONE &&
+ prev_clip_time > clip->start_time) {
+ GST_ELEMENT_ERROR (enc, STREAM, ENCODE,
+ (NULL), ("previous clip start time > current clip (%s) start time",
+ clip->id));
+ return GST_FLOW_ERROR;
+ }
+
+ /* encode the clip */
+ clip_string =
+ (gchar *) gst_cmml_parser_tag_clip_to_string (enc->parser, clip);
+
+ res = gst_cmml_enc_new_buffer (enc,
+ (guchar *) clip_string, strlen (clip_string), &buffer);
+ g_free (clip_string);
+ if (res != GST_FLOW_OK)
+ goto done;
+
+ GST_INFO_OBJECT (enc, "encoding clip"
+ "(start-time: %" GST_TIME_FORMAT " end-time: %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (clip->start_time), GST_TIME_ARGS (clip->end_time));
+
+ /* set the granulepos */
+ granulepos = gst_cmml_clock_time_to_granule (prev_clip_time, clip->start_time,
+ enc->granulerate_n, enc->granulerate_d, enc->granuleshift);
+ GST_BUFFER_OFFSET_END (buffer) = granulepos;
+ GST_BUFFER_TIMESTAMP (buffer) = clip->start_time;
+
+ res = gst_cmml_enc_push (enc, buffer);
+ if (GST_FLOW_IS_FATAL (res))
+ goto done;
+
+ if (clip->end_time != GST_CLOCK_TIME_NONE) {
+ /* create a new empty clip for the same cmml "track" starting at
+ * "end_time"
+ */
+ GObject *end_clip = g_object_new (GST_TYPE_CMML_TAG_CLIP,
+ "start-time", clip->end_time, "track", clip->track, NULL);
+
+ /* encode the empty end clip */
+ gst_cmml_enc_push_clip (enc, GST_CMML_TAG_CLIP (end_clip),
+ clip->start_time);
+ g_object_unref (end_clip);
+ }
+done:
+ return res;
+}
+
+static GstFlowReturn
+gst_cmml_enc_push (GstCmmlEnc * enc, GstBuffer * buffer)
+{
+ GstFlowReturn res;
+
+ /* FIXME: hack to make oggmux flush every cmml tag in its own page */
+ GST_BUFFER_DURATION (buffer) = G_MAXINT64;
+
+ res = gst_pad_push (enc->srcpad, buffer);
+ if (GST_FLOW_IS_FATAL (res))
+ GST_ELEMENT_ERROR (enc, STREAM, ENCODE,
+ (NULL), ("could not push the buffer: %s", gst_flow_get_name (res)));
+
+ return res;
+}
+
+static GstFlowReturn
+gst_cmml_enc_chain (GstPad * pad, GstBuffer * buffer)
+{
+ GError *err = NULL;
+ GstCmmlEnc *enc = GST_CMML_ENC (GST_PAD_PARENT (pad));
+
+ /* the CMML handlers registered with enc->parser will override this when
+ * encoding/pushing the buffers downstream
+ */
+ enc->flow_return = GST_FLOW_OK;
+
+ if (!gst_cmml_parser_parse_chunk (enc->parser,
+ (gchar *) GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), &err)) {
+ GST_ELEMENT_ERROR (enc, STREAM, ENCODE, (NULL), (err->message));
+ g_error_free (err);
+ enc->flow_return = GST_FLOW_ERROR;
+ }
+
+ gst_buffer_unref (buffer);
+ return enc->flow_return;
+}
+
+gboolean
+gst_cmml_enc_plugin_init (GstPlugin * plugin)
+{
+ if (!gst_element_register (plugin, "cmmlenc", GST_RANK_NONE,
+ GST_TYPE_CMML_ENC))
+ return FALSE;
+
+ GST_DEBUG_CATEGORY_INIT (cmmlenc, "cmmlenc", 0,
+ "annodex cmml decoding element");
+
+ return TRUE;
+}