-/*
- * gstcmmldec.c - GStreamer annodex CMML decoder
- * 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-cmmldec
- * @see_also: cmmlenc, oggdemux
- *
- * Cmmldec extracts a CMML document from a CMML bitstream.<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>.
- *
- * <refsect2>
- * <title>Example pipeline</title>
- * |[
- * gst-launch -v filesrc location=annotated.ogg ! oggdemux ! cmmldec ! filesink location=annotations.cmml
- * ]|
- * </refsect2>
- */
-
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <string.h>
-#include <glib.h>
-
-#include <gst/tag/tag.h>
-#include "gstannodex.h"
-#include "gstcmmltag.h"
-#include "gstcmmldec.h"
-#include "gstcmmlutils.h"
-
-GST_DEBUG_CATEGORY_STATIC (cmmldec);
-#define GST_CAT_DEFAULT cmmldec
-
-#define CMML_IDENT_HEADER_SIZE 29
-
-enum
-{
- ARG_0,
- GST_CMML_DEC_WAIT_CLIP_END
-};
-
-enum
-{
- LAST_SIGNAL
-};
-
-static GstStaticPadTemplate gst_cmml_dec_src_factory =
-GST_STATIC_PAD_TEMPLATE ("src",
- GST_PAD_SRC,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("text/x-cmml, encoded = (boolean) false")
- );
-
-static GstStaticPadTemplate gst_cmml_dec_sink_factory =
-GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("text/x-cmml, encoded = (boolean) true")
- );
-
-/* GstCmmlDec */
-#define gst_cmml_dec_parent_class parent_class
-G_DEFINE_TYPE (GstCmmlDec, gst_cmml_dec, GST_TYPE_ELEMENT);
-
-static void gst_cmml_dec_get_property (GObject * dec, guint property_id,
- GValue * value, GParamSpec * pspec);
-static void gst_cmml_dec_set_property (GObject * dec, guint property_id,
- const GValue * value, GParamSpec * pspec);
-static gboolean gst_cmml_dec_sink_query (GstPad * pad, GstObject * parent,
- GstQuery * query);
-static gboolean gst_cmml_dec_sink_event (GstPad * pad, GstObject * parent,
- GstEvent * event);
-static gboolean gst_cmml_dec_convert (GstPad * pad, GstFormat src_fmt,
- gint64 src_val, GstFormat * dest_fmt, gint64 * dest_val);
-static GstStateChangeReturn gst_cmml_dec_change_state (GstElement * element,
- GstStateChange transition);
-static GstFlowReturn gst_cmml_dec_chain (GstPad * pad, GstObject * parent,
- GstBuffer * buffer);
-
-static GstCmmlPacketType gst_cmml_dec_parse_packet_type (GstCmmlDec * dec,
- gchar * data, gsize size);
-static void gst_cmml_dec_parse_ident_header (GstCmmlDec * dec, guint8 * data,
- gsize size);
-static void gst_cmml_dec_parse_first_header (GstCmmlDec * dec, guint8 * data,
- gsize size);
-static void gst_cmml_dec_parse_preamble (GstCmmlDec * dec, guchar * preamble,
- guchar * cmml_root_element);
-static void gst_cmml_dec_parse_xml (GstCmmlDec * dec, guchar * data,
- guint size);
-static void gst_cmml_dec_parse_head (GstCmmlDec * dec, GstCmmlTagHead * head);
-static void gst_cmml_dec_parse_clip (GstCmmlDec * dec, GstCmmlTagClip * clip);
-
-static GstFlowReturn gst_cmml_dec_new_buffer (GstCmmlDec * dec,
- guchar * data, gint size, GstBuffer ** buffer);
-static void gst_cmml_dec_push_clip (GstCmmlDec * dec, GstCmmlTagClip * clip);
-static void gst_cmml_dec_send_clip_tag (GstCmmlDec * dec,
- GstCmmlTagClip * clip);
-
-static void gst_cmml_dec_finalize (GObject * object);
-
-static void
-gst_cmml_dec_class_init (GstCmmlDecClass * klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
-
- object_class->set_property = gst_cmml_dec_set_property;
- object_class->get_property = gst_cmml_dec_get_property;
- object_class->finalize = gst_cmml_dec_finalize;
-
- g_object_class_install_property (object_class, GST_CMML_DEC_WAIT_CLIP_END,
- g_param_spec_boolean ("wait-clip-end-time",
- "Wait clip end time",
- "Send a tag for a clip when the clip ends, setting its end-time. "
- "Use when you need to know both clip's start-time and end-time.",
- FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- element_class->change_state = gst_cmml_dec_change_state;
-
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&gst_cmml_dec_sink_factory));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&gst_cmml_dec_src_factory));
- gst_element_class_set_static_metadata (element_class, "CMML stream decoder",
- "Codec/Decoder",
- "Decodes CMML streams", "Alessandro Decina <alessandro@nnva.org>");
-}
-
-static void
-gst_cmml_dec_init (GstCmmlDec * dec)
-{
- dec->sinkpad =
- gst_pad_new_from_static_template (&gst_cmml_dec_sink_factory, "sink");
- gst_pad_set_chain_function (dec->sinkpad, gst_cmml_dec_chain);
- gst_pad_set_query_function (dec->sinkpad, gst_cmml_dec_sink_query);
- gst_pad_set_event_function (dec->sinkpad, gst_cmml_dec_sink_event);
- gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad);
-
- dec->srcpad =
- gst_pad_new_from_static_template (&gst_cmml_dec_src_factory, "src");
- gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad);
-
- dec->wait_clip_end = FALSE;
-}
-
-static void
-gst_cmml_dec_get_property (GObject * object, guint property_id,
- GValue * value, GParamSpec * pspec)
-{
- GstCmmlDec *dec = GST_CMML_DEC (object);
-
- switch (property_id) {
- case GST_CMML_DEC_WAIT_CLIP_END:
- g_value_set_boolean (value, dec->wait_clip_end);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
- }
-}
-
-static void
-gst_cmml_dec_set_property (GObject * object, guint property_id,
- const GValue * value, GParamSpec * pspec)
-{
- GstCmmlDec *dec = GST_CMML_DEC (object);
-
- switch (property_id) {
- case GST_CMML_DEC_WAIT_CLIP_END:
- dec->wait_clip_end = g_value_get_boolean (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (dec, property_id, pspec);
- }
-}
-
-static void
-gst_cmml_dec_finalize (GObject * object)
-{
- GstCmmlDec *dec = GST_CMML_DEC (object);
-
- if (dec->tracks) {
- gst_cmml_track_list_destroy (dec->tracks);
- dec->tracks = NULL;
- }
-
- G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static GstStateChangeReturn
-gst_cmml_dec_change_state (GstElement * element, GstStateChange transition)
-{
- GstCmmlDec *dec = GST_CMML_DEC (element);
- GstStateChangeReturn res;
-
- switch (transition) {
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- dec->parser = gst_cmml_parser_new (GST_CMML_PARSER_DECODE);
- dec->parser->user_data = dec;
- dec->parser->preamble_callback =
- (GstCmmlParserPreambleCallback) gst_cmml_dec_parse_preamble;
- dec->parser->head_callback =
- (GstCmmlParserHeadCallback) gst_cmml_dec_parse_head;
- dec->parser->clip_callback =
- (GstCmmlParserClipCallback) gst_cmml_dec_parse_clip;
- dec->major = -1;
- dec->minor = -1;
- dec->granulerate_n = -1;
- dec->granulerate_d = -1;
- dec->granuleshift = 0;
- dec->granulepos = 0;
- dec->flow_return = GST_FLOW_OK;
- dec->sent_root = FALSE;
- dec->tracks = gst_cmml_track_list_new ();
- break;
- default:
- break;
- }
-
- res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
-
- switch (transition) {
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- gst_cmml_parser_free (dec->parser);
- gst_cmml_track_list_destroy (dec->tracks);
- dec->tracks = NULL;
- break;
- default:
- break;
- }
-
- return res;
-}
-
-static gboolean
-gst_cmml_dec_sink_query (GstPad * pad, GstObject * parent, GstQuery * query)
-{
- gboolean res = FALSE;
-
- switch (GST_QUERY_TYPE (query)) {
- case GST_QUERY_CONVERT:
- {
- GstFormat src_fmt, dest_fmt;
- gint64 src_val, dest_val;
-
- gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
- res = gst_cmml_dec_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val);
- if (res)
- gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
- break;
- }
- default:
- break;
- }
-
- return res;
-}
-
-static gboolean
-gst_cmml_dec_convert (GstPad * pad,
- GstFormat src_fmt, gint64 src_val, GstFormat * dest_fmt, gint64 * dest_val)
-{
- GstCmmlDec *dec = GST_CMML_DEC (GST_PAD_PARENT (pad));
- gboolean res = FALSE;
-
- switch (src_fmt) {
- case GST_FORMAT_DEFAULT:
- switch (*dest_fmt) {
- case GST_FORMAT_TIME:
- {
- *dest_val = gst_annodex_granule_to_time (src_val, dec->granulerate_n,
- dec->granulerate_d, dec->granuleshift);
- res = TRUE;
- break;
- }
- default:
- break;
- }
- break;
- default:
- break;
- }
-
- return res;
-}
-
-static gboolean
-gst_cmml_dec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
-{
- GstCmmlDec *dec = GST_CMML_DEC (parent);
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_EOS:
- {
- GstBuffer *buffer;
- GstCmmlTagClip *clip;
- GList *clips, *walk;
-
- GST_INFO_OBJECT (dec, "got EOS, flushing clips");
-
- /* since we output a clip when the next one in the same track is found, on
- * EOS we need to output the last clip (if any) of every track
- */
- clips = gst_cmml_track_list_get_clips (dec->tracks);
- for (walk = clips; walk; walk = g_list_next (walk)) {
- clip = GST_CMML_TAG_CLIP (walk->data);
- gst_cmml_dec_push_clip (dec, clip);
- if (dec->wait_clip_end) {
- clip->end_time = dec->timestamp;
- gst_cmml_dec_send_clip_tag (dec, clip);
- }
- }
- g_list_free (clips);
-
- /* send the cmml end tag */
- dec->flow_return = gst_cmml_dec_new_buffer (dec,
- (guchar *) "</cmml>", strlen ("</cmml>"), &buffer);
-
- if (dec->flow_return == GST_FLOW_OK)
- dec->flow_return = gst_pad_push (dec->srcpad, buffer);
- if (dec->flow_return == GST_FLOW_NOT_LINKED)
- dec->flow_return = GST_FLOW_OK; /* Ignore NOT_LINKED */
-
- break;
- }
- default:
- break;
- }
-
- return gst_pad_event_default (pad, parent, event);
-}
-
-static GstFlowReturn
-gst_cmml_dec_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
-{
- GstCmmlDec *dec = GST_CMML_DEC (parent);
- GstCmmlPacketType packet;
- GstMapInfo map;
- guint8 *data;
- gsize size;
-
- gst_buffer_map (buffer, &map, GST_MAP_READ);
- data = map.data;
- size = map.size;
-
- if (size == 0) {
- /* the EOS page could be empty */
- dec->flow_return = GST_FLOW_OK;
- goto done;
- }
-
- dec->granulepos = GST_BUFFER_OFFSET_END (buffer);
- dec->timestamp = gst_annodex_granule_to_time (dec->granulepos,
- dec->granulerate_n, dec->granulerate_d, dec->granuleshift);
-
- /* identify the packet type */
- packet = gst_cmml_dec_parse_packet_type (dec, (gchar *) data, size);
-
- /* handle the packet. the handler will set dec->flow_return */
- switch (packet) {
- case GST_CMML_PACKET_IDENT_HEADER:
- if (dec->sent_root == FALSE)
- /* don't parse the ident again in case of seeking to the beginning */
- gst_cmml_dec_parse_ident_header (dec, data, size);
- break;
- case GST_CMML_PACKET_FIRST_HEADER:
- if (dec->sent_root == FALSE)
- /* don't parse the xml preamble if it has already been parsed because it
- * would error out, so seeking to the beginning would fail */
- gst_cmml_dec_parse_first_header (dec, data, size);
- break;
- case GST_CMML_PACKET_SECOND_HEADER:
- case GST_CMML_PACKET_CLIP:
- gst_cmml_dec_parse_xml (dec, data, size);
- break;
- case GST_CMML_PACKET_UNKNOWN:
- default:
- GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("unknown packet type"));
- dec->flow_return = GST_FLOW_ERROR;
- }
-
-done:
- gst_buffer_unmap (buffer, &map);
- gst_buffer_unref (buffer);
-
- return dec->flow_return;
-}
-
-/* finds the packet type of the buffer
- */
-static GstCmmlPacketType
-gst_cmml_dec_parse_packet_type (GstCmmlDec * dec, gchar * data, gsize size)
-{
- GstCmmlPacketType packet_type = GST_CMML_PACKET_UNKNOWN;
-
- if (size >= 8 && !memcmp (data, "CMML\0\0\0\0", 8)) {
- packet_type = GST_CMML_PACKET_IDENT_HEADER;
- } else if (size >= 5) {
- if (!strncmp (data, "<?xml", 5))
- packet_type = GST_CMML_PACKET_FIRST_HEADER;
- else if (!strncmp (data, "<head", 5))
- packet_type = GST_CMML_PACKET_SECOND_HEADER;
- else if (!strncmp (data, "<clip", 5))
- packet_type = GST_CMML_PACKET_CLIP;
- }
-
- return packet_type;
-}
-
-/* creates a new buffer and sets caps and timestamp on it
- */
-static GstFlowReturn
-gst_cmml_dec_new_buffer (GstCmmlDec * dec,
- guchar * data, gint size, GstBuffer ** buffer)
-{
- GstFlowReturn res;
-
- *buffer = gst_buffer_new_allocate (NULL, size, NULL);
- if (*buffer != NULL) {
- if (data)
- gst_buffer_fill (*buffer, 0, data, size);
- GST_BUFFER_TIMESTAMP (*buffer) = dec->timestamp;
- res = GST_FLOW_OK;
- } else {
- GST_WARNING_OBJECT (dec, "could not allocate buffer");
- res = GST_FLOW_ERROR;
- }
-
- return res;
-}
-
-/* parses the first CMML packet (the ident header)
- */
-static void
-gst_cmml_dec_parse_ident_header (GstCmmlDec * dec, guint8 * data, gsize size)
-{
- /* the ident header has a fixed length */
- if (size != CMML_IDENT_HEADER_SIZE) {
- GST_ELEMENT_ERROR (dec, STREAM, DECODE,
- (NULL), ("wrong ident header size: %" G_GSIZE_FORMAT, size));
- dec->flow_return = GST_FLOW_ERROR;
-
- return;
- }
-
- data += 8;
- dec->major = GST_READ_UINT16_LE (data);
- data += 2;
- dec->minor = GST_READ_UINT16_LE (data);
- data += 2;
- dec->granulerate_n = GST_READ_UINT64_LE (data);
- data += 8;
- dec->granulerate_d = GST_READ_UINT64_LE (data);
- data += 8;
- dec->granuleshift = GST_READ_UINT8 (data);
-
- GST_INFO_OBJECT (dec, "bitstream initialized "
- "(major: %" G_GINT16_FORMAT " minor: %" G_GINT16_FORMAT
- " granulerate_n: %" G_GINT64_FORMAT " granulerate_d: %" G_GINT64_FORMAT
- " granuleshift: %d)",
- dec->major, dec->minor,
- dec->granulerate_n, dec->granulerate_d, dec->granuleshift);
-
- dec->flow_return = GST_FLOW_OK;
-}
-
-/* parses the first secondary header.
- * the first secondary header contains the xml version, the doctype and the
- * optional "cmml" processing instruction.
- */
-static void
-gst_cmml_dec_parse_first_header (GstCmmlDec * dec, guint8 * data, gsize size)
-{
- gst_cmml_dec_parse_xml (dec, data, size);
-
- /* if there is a processing instruction, gst_cmml_dec_parse_preamble
- * will be triggered. Otherwise we need to call it manually.
- */
- if (dec->flow_return == GST_FLOW_OK && !dec->sent_root) {
- guchar *preamble = (guchar *) g_strndup ((gchar *) data, size);
-
- gst_cmml_dec_parse_preamble (dec, preamble, (guchar *) "<cmml>");
- g_free (preamble);
- }
-}
-
-/* feeds data into the cmml parser.
- */
-static void
-gst_cmml_dec_parse_xml (GstCmmlDec * dec, guchar * data, guint size)
-{
- GError *err = NULL;
-
- if (!gst_cmml_parser_parse_chunk (dec->parser, (gchar *) data, size, &err)) {
- GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("%s", err->message));
- g_error_free (err);
- dec->flow_return = GST_FLOW_ERROR;
- }
-}
-
-static void
-gst_cmml_dec_parse_preamble (GstCmmlDec * dec, guchar * preamble,
- guchar * root_element)
-{
- GstBuffer *buffer;
- guchar *encoded_preamble;
-
- encoded_preamble = (guchar *) g_strconcat ((gchar *) preamble,
- (gchar *) root_element, NULL);
-
- /* send the root element to the internal parser */
- gst_cmml_dec_parse_xml (dec, root_element, strlen ((gchar *) root_element));
- dec->sent_root = TRUE;
-
- /* push the root element */
- dec->flow_return = gst_cmml_dec_new_buffer (dec,
- encoded_preamble, strlen ((gchar *) encoded_preamble), &buffer);
- if (dec->flow_return == GST_FLOW_OK) {
- dec->flow_return = gst_pad_push (dec->srcpad, buffer);
- }
-
- if (dec->flow_return == GST_FLOW_OK) {
- GST_INFO_OBJECT (dec, "preamble parsed");
- }
-
- g_free (encoded_preamble);
- return;
-}
-
-/* outputs the cmml head element and send TITLE and CMML_HEAD tags.
- * This callback is registered with dec->parser. It is called when the
- * head element is parsed.
- */
-static void
-gst_cmml_dec_parse_head (GstCmmlDec * dec, GstCmmlTagHead * head)
-{
- GstTagList *tags;
- GValue str_val = { 0 }, title_val = {
- 0};
- guchar *head_str;
- GstBuffer *buffer;
-
- GST_DEBUG_OBJECT (dec, "found CMML head (title: %s base: %s)",
- head->title, head->base);
-
- /* FIXME: what's the point of all this GValue transform crack? */
- /* create the GST_TAG_TITLE tag */
- g_value_init (&str_val, G_TYPE_STRING);
- g_value_init (&title_val, gst_tag_get_type (GST_TAG_TITLE));
- g_value_set_string (&str_val, (gchar *) head->title);
- g_value_transform (&str_val, &title_val);
-
- tags = gst_tag_list_new_empty ();
- gst_tag_list_add_values (tags, GST_TAG_MERGE_APPEND,
- GST_TAG_TITLE, &title_val, NULL);
- gst_tag_list_add (tags, GST_TAG_MERGE_APPEND, GST_TAG_CMML_HEAD, head, NULL);
- gst_pad_push_event (dec->srcpad, gst_event_new_tag (tags));
-
- g_value_unset (&str_val);
- g_value_unset (&title_val);
-
- head_str = gst_cmml_parser_tag_head_to_string (dec->parser, head);
-
- dec->flow_return = gst_cmml_dec_new_buffer (dec,
- head_str, strlen ((gchar *) head_str), &buffer);
- g_free (head_str);
- if (dec->flow_return == GST_FLOW_OK)
- dec->flow_return = gst_pad_push (dec->srcpad, buffer);
- if (dec->flow_return == GST_FLOW_NOT_LINKED)
- dec->flow_return = GST_FLOW_OK; /* Ignore NOT_LINKED */
-}
-
-/* send a TAG_MESSAGE event for a clip */
-static void
-gst_cmml_dec_send_clip_tag (GstCmmlDec * dec, GstCmmlTagClip * clip)
-{
- GstTagList *tags;
-
- GST_DEBUG_OBJECT (dec, "sending clip tag %s", clip->id);
-
- tags = gst_tag_list_new (GST_TAG_CMML_CLIP, clip, NULL);
- gst_pad_push_event (dec->srcpad, gst_event_new_tag (tags));
-}
-
-/* push the string representation of a clip */
-static void
-gst_cmml_dec_push_clip (GstCmmlDec * dec, GstCmmlTagClip * clip)
-{
- GstBuffer *buffer;
- guchar *clip_str;
-
- GST_DEBUG_OBJECT (dec, "pushing clip %s", clip->id);
-
- clip_str = gst_cmml_parser_tag_clip_to_string (dec->parser, clip);
- dec->flow_return = gst_cmml_dec_new_buffer (dec,
- clip_str, strlen ((gchar *) clip_str), &buffer);
- if (dec->flow_return == GST_FLOW_OK)
- dec->flow_return = gst_pad_push (dec->srcpad, buffer);
- if (dec->flow_return == GST_FLOW_NOT_LINKED)
- dec->flow_return = GST_FLOW_OK; /* Ignore NOT_LINKED */
-
- g_free (clip_str);
-}
-
-/* decode a clip tag
- * this callback is registered with dec->parser. It is called whenever a
- * clip is parsed.
- */
-static void
-gst_cmml_dec_parse_clip (GstCmmlDec * dec, GstCmmlTagClip * clip)
-{
- GstCmmlTagClip *prev_clip;
-
- dec->flow_return = GST_FLOW_OK;
-
- if (clip->empty)
- GST_INFO_OBJECT (dec, "parsing empty clip");
- else
- GST_INFO_OBJECT (dec, "parsing clip (id: %s)", clip->id);
-
- clip->start_time = dec->timestamp;
- if (clip->start_time == GST_CLOCK_TIME_NONE) {
- GST_ELEMENT_ERROR (dec, STREAM, DECODE,
- (NULL), ("invalid clip start time"));
-
- dec->flow_return = GST_FLOW_ERROR;
- return;
- }
-
- /* get the last clip in the current track */
- prev_clip = gst_cmml_track_list_get_track_last_clip (dec->tracks,
- (gchar *) clip->track);
- if (prev_clip) {
- /* output the previous clip */
- if (clip->empty)
- /* the current clip marks the end of the previous one */
- prev_clip->end_time = clip->start_time;
-
- gst_cmml_dec_push_clip (dec, prev_clip);
- }
-
- if (dec->wait_clip_end) {
- /* now it's time to send the tag for the previous clip */
- if (prev_clip) {
- prev_clip->end_time = clip->start_time;
- gst_cmml_dec_send_clip_tag (dec, prev_clip);
- }
- } else if (!clip->empty) {
- /* send the tag for the current clip */
- gst_cmml_dec_send_clip_tag (dec, clip);
- }
-
- if (prev_clip)
- gst_cmml_track_list_del_clip (dec->tracks, prev_clip);
-
- if (!clip->empty)
- if (!gst_cmml_track_list_has_clip (dec->tracks, clip))
- gst_cmml_track_list_add_clip (dec->tracks, clip);
-}
-
-gboolean
-gst_cmml_dec_plugin_init (GstPlugin * plugin)
-{
- if (!gst_element_register (plugin, "cmmldec", GST_RANK_PRIMARY,
- GST_TYPE_CMML_DEC))
- return FALSE;
-
- GST_DEBUG_CATEGORY_INIT (cmmldec, "cmmldec", 0,
- "annodex CMML decoding element");
-
- return TRUE;
-}