? ext/annodex/.gstcmmldec.c.swp
Index: ChangeLog
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/ChangeLog,v
retrieving revision 1.2182
diff -u -w -r1.2182 ChangeLog
--- ChangeLog	25 Feb 2006 11:37:10 -0000	1.2182
+++ ChangeLog	25 Feb 2006 17:31:00 -0000
@@ -1,3 +1,21 @@
+2006-02-25  Alessandro Decina <alessandro@nnva.org>
+
+	* ext/annodex/Makefile.am:
+	* ext/annodex/gstannodex.c:
+	* ext/annodex/gstcmmldec.c:
+	* ext/annodex/gstcmmlenc.c:
+	* ext/annodex/gstcmmlparser.c:
+	* ext/annodex/gstcmmlparser.h:
+	* ext/annodex/gstcmmlutils.c:
+	* tests/check/elements/cmmldec.c:
+	* tests/check/elements/cmmlenc.c:
+	  Fix a memleak in gst_cmml_track_list_add_clip.
+	  Handle overflows in clip's start and end times.
+	  Add the "encoded" parameter to cmmldec and cmmlenc caps.
+	  Do not parse junk at the end of a CMML preamble buffer.
+	  Register a libxml error handler to not print stuff on stderr.
+	  Check for bad clip start and end times in the testsuites.
+
 2006-02-25  Julien MOUTTE  <julien@moutte.net>
 
 	* ext/annodex/gstcmmldec.c: (gst_cmml_dec_class_init),
Index: ext/annodex/Makefile.am
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/ext/annodex/Makefile.am,v
retrieving revision 1.2
diff -u -w -r1.2 Makefile.am
--- ext/annodex/Makefile.am	24 Feb 2006 23:31:08 -0000	1.2
+++ ext/annodex/Makefile.am	25 Feb 2006 17:31:00 -0000
@@ -12,8 +12,7 @@
 	gstcmmldec.c \
 	gstcmmlenc.c \
 	gstcmmltag.c \
-	gstcmmlparser.c \
-	gstskeltag.c
+	gstcmmlparser.c
 
 # flags used to compile this plugin
 # we use the GST_LIBS flags because we might be using plug-in libs
Index: ext/annodex/gstannodex.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/ext/annodex/gstannodex.c,v
retrieving revision 1.2
diff -u -w -r1.2 gstannodex.c
--- ext/annodex/gstannodex.c	24 Feb 2006 23:31:08 -0000	1.2
+++ ext/annodex/gstannodex.c	25 Feb 2006 17:31:00 -0000
@@ -25,7 +25,9 @@
 #include "config.h"
 #endif
 
+#include <math.h>
 #include <gst/tag/tag.h>
+#include "gstcmmlparser.h"
 #include "gstcmmlenc.h"
 #include "gstcmmldec.h"
 
@@ -49,9 +51,9 @@
     granulepos = keyindex + keyoffset;
   }
 
-  /* GST_SECOND / granulerate_n / granulerate_d */
+  /* GST_SECOND / (granulerate_n / granulerate_d) */
   granulerate = gst_util_uint64_scale (GST_SECOND,
-      granulerate_n, granulerate_d);
+      granulerate_d, granulerate_n);
 
   /* granulepos * granulerate */
   res = gst_util_uint64_scale (granulepos, granulerate, 1);
@@ -135,7 +137,6 @@
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
-
   gst_tag_register (GST_TAG_CMML_STREAM, GST_TAG_FLAG_META,
       GST_TYPE_CMML_TAG_STREAM, "cmml-stream", "annodex CMML stream tag", NULL);
 
@@ -145,6 +146,8 @@
   gst_tag_register (GST_TAG_CMML_CLIP, GST_TAG_FLAG_META,
       GST_TYPE_CMML_TAG_CLIP, "cmml-clip", "annodex CMML clip tag", NULL);
 
+  gst_cmml_parser_init ();
+  
   if (!gst_cmml_enc_plugin_init (plugin))
     return FALSE;
 
Index: ext/annodex/gstcmmldec.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/ext/annodex/gstcmmldec.c,v
retrieving revision 1.3
diff -u -w -r1.3 gstcmmldec.c
--- ext/annodex/gstcmmldec.c	25 Feb 2006 11:37:10 -0000	1.3
+++ ext/annodex/gstcmmldec.c	25 Feb 2006 17:31:01 -0000
@@ -79,14 +79,14 @@
 GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
     GST_PAD_ALWAYS,
-    GST_STATIC_CAPS ("text/xml")
+    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")
+    GST_STATIC_CAPS ("text/x-cmml, encoded = (boolean) true")
     );
 
 /* GstCmmlDec */
@@ -449,6 +449,9 @@
     if (data)
       memcpy (GST_BUFFER_DATA (*buffer), data, size);
     GST_BUFFER_TIMESTAMP (*buffer) = dec->timestamp;
+  } else {
+    GST_WARNING_OBJECT (dec, "alloc function returned error %s",
+        gst_flow_get_name (res));
   }
 
   return res;
@@ -486,7 +489,8 @@
       " 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->granulerate_n, dec->granulerate_d,
+      dec->granuleshift);
 
   dec->flow_return = GST_FLOW_OK;
 }
@@ -505,8 +509,11 @@
    * will be triggered. Otherwise we need to call it manually.
    */
   if (!GST_FLOW_IS_FATAL (dec->flow_return) && !dec->sent_root) {
-    gst_cmml_dec_parse_preamble (dec, GST_BUFFER_DATA (buffer),
-        (guchar *) "<cmml>");
+    guchar *preamble = (guchar *) g_strndup ((gchar *) GST_BUFFER_DATA (buffer),
+        GST_BUFFER_SIZE (buffer));
+    
+    gst_cmml_dec_parse_preamble (dec, preamble, (guchar *) "<cmml>");
+    g_free (preamble);
   }
 }
 
@@ -536,6 +543,7 @@
 
   /* 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,
@@ -546,7 +554,6 @@
   dec->flow_return = gst_pad_push (dec->srcpad, buffer);
   if (!GST_FLOW_IS_FATAL (dec->flow_return)) {
     GST_INFO_OBJECT (dec, "preamble parsed");
-    dec->sent_root = TRUE;
   }
 
 done:
@@ -562,8 +569,7 @@
 gst_cmml_dec_parse_head (GstCmmlDec * dec, GstCmmlTagHead * head)
 {
   GstTagList *tags;
-  GValue str_val = { 0 }, title_val = {
-  0};
+  GValue str_val = { 0 }, title_val = { 0 };
   guchar *head_str;
   GstBuffer *buffer;
 
Index: ext/annodex/gstcmmlenc.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/ext/annodex/gstcmmlenc.c,v
retrieving revision 1.2
diff -u -w -r1.2 gstcmmlenc.c
--- ext/annodex/gstcmmlenc.c	25 Feb 2006 11:37:10 -0000	1.2
+++ ext/annodex/gstcmmlenc.c	25 Feb 2006 17:31:01 -0000
@@ -75,14 +75,14 @@
 GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
     GST_PAD_ALWAYS,
-    GST_STATIC_CAPS ("text/x-cmml")
+    GST_STATIC_CAPS ("text/x-cmml, encoded = (boolean) true")
     );
 
 static GstStaticPadTemplate gst_cmml_enc_sink_factory =
 GST_STATIC_PAD_TEMPLATE ("sink",
     GST_PAD_SINK,
     GST_PAD_ALWAYS,
-    GST_STATIC_CAPS ("text/xml")
+    GST_STATIC_CAPS ("text/x-cmml, encoded = (boolean) false")
     );
 
 GST_BOILERPLATE (GstCmmlEnc, gst_cmml_enc, GstElement, GST_TYPE_ELEMENT);
@@ -103,9 +103,6 @@
     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);
@@ -137,13 +134,13 @@
       g_param_spec_int64 ("granule-rate-numerator",
           "Granulerate numerator",
           "Granulerate numerator",
-          0, G_MAXINT64, 1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+          0, G_MAXINT64, 1000, 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));
+          0, G_MAXINT64, 1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 
   g_object_class_install_property (klass, GST_CMML_ENC_GRANULESHIFT,
       g_param_spec_uchar ("granule-shift",
@@ -304,15 +301,15 @@
     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)));
+    GST_WARNING_OBJECT (enc, "alloc function returned error %s",
+        gst_flow_get_name (res));
   }
 
   return res;
 }
 
 static GstCaps *
-gst_cmml_enc_set_header_on_caps (GstCaps * caps,
+gst_cmml_enc_set_header_on_caps (GstCmmlEnc * enc, GstCaps * caps,
     GstBuffer * ident, GstBuffer * preamble, GstBuffer * head)
 {
   GValue array = { 0 };
@@ -387,13 +384,6 @@
   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);
@@ -444,24 +434,23 @@
   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,
+  caps = gst_cmml_enc_set_header_on_caps (enc, caps,
       ident_buf, preamble_buf, head_buf);
 
-  for (walk = headers; walk; walk = walk->next) {
-    buffer = GST_BUFFER (walk->data);
+  while (headers) {
+    buffer = GST_BUFFER (headers->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);
+    headers = g_list_delete_link (headers, headers);
+
     if (GST_FLOW_IS_FATAL (enc->flow_return))
       goto push_error;
-
   }
 
   gst_caps_unref (caps);
-  g_list_free (headers);
 
   enc->sent_headers = TRUE;
   return;
@@ -481,22 +470,6 @@
   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
@@ -510,7 +483,7 @@
 
   /* 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 */
+   * overflows in GstCmmlParser */
   if (clip->start_time == GST_CLOCK_TIME_NONE) {
     GST_ELEMENT_ERROR (enc, STREAM, ENCODE,
         (NULL), ("invalid start time for clip (%s)", clip->id));
@@ -519,10 +492,12 @@
     return;
   }
 
+  /* get the previous clip's start time to encode the current granulepos */
   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;
+    /* we don't need the prev clip anymore */
     gst_cmml_track_list_del_clip (enc->tracks, prev_clip);
   }
 
@@ -543,10 +518,8 @@
 
   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;
+    GST_WARNING_OBJECT (enc, 
+        "previous clip start time > current clip (%s) start time", clip->id);
   }
 
   /* encode the clip */
@@ -566,6 +539,9 @@
   /* set the granulepos */
   granulepos = gst_cmml_clock_time_to_granule (prev_clip_time, clip->start_time,
       enc->granulerate_n, enc->granulerate_d, enc->granuleshift);
+  if (granulepos == -1)
+    goto granule_overflow;
+  
   GST_BUFFER_OFFSET_END (buffer) = granulepos;
   GST_BUFFER_TIMESTAMP (buffer) = clip->start_time;
 
@@ -574,8 +550,7 @@
     goto done;
 
   if (clip->end_time != GST_CLOCK_TIME_NONE) {
-    /* create a new empty clip for the same cmml "track" starting at
-     * "end_time"
+    /* 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);
@@ -587,6 +562,11 @@
   }
 done:
   return res;
+
+granule_overflow:
+  GST_ELEMENT_ERROR (enc, STREAM, ENCODE,
+      (NULL), ("granulepos overflow"));
+  return GST_FLOW_ERROR;
 }
 
 static GstFlowReturn
@@ -600,7 +580,7 @@
   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)));
+        (NULL), ("could not push buffer: %s", gst_flow_get_name (res)));
 
   return res;
 }
Index: ext/annodex/gstcmmlparser.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/ext/annodex/gstcmmlparser.c,v
retrieving revision 1.1
diff -u -w -r1.1 gstcmmlparser.c
--- ext/annodex/gstcmmlparser.c	24 Feb 2006 19:07:09 -0000	1.1
+++ ext/annodex/gstcmmlparser.c	25 Feb 2006 17:31:02 -0000
@@ -22,12 +22,18 @@
  */
 
 #include <string.h>
+#include <stdarg.h>
 #include <gst/gst.h>
 
 #include "gstcmmlparser.h"
 #include "gstannodex.h"
 #include "gstcmmlutils.h"
 
+GST_DEBUG_CATEGORY (cmmlparser);
+#define GST_CAT_DEFAULT cmmlparser
+
+static void gst_cmml_parser_generic_error (void * ctx,
+    const char * msg, ...);
 static xmlNodePtr gst_cmml_parser_new_node (GstCmmlParser * parser,
     const gchar * name, ...);
 static void
@@ -42,6 +48,16 @@
 static void gst_cmml_parser_meta_to_string (GstCmmlParser * parser,
     xmlNodePtr parent, GValueArray * meta);
 
+/* initialize the parser */
+void
+gst_cmml_parser_init (void)
+{
+  GST_DEBUG_CATEGORY_INIT (cmmlparser, "cmmlparser", 0,
+      "annodex CMML parser");
+
+  xmlGenericError = gst_cmml_parser_generic_error;
+}
+
 /* create a new CMML parser
  */
 GstCmmlParser *
@@ -58,8 +74,8 @@
       (startElementNsSAX2Func) gst_cmml_parser_parse_start_element_ns;
   parser->context->sax->endElementNs =
       (endElementNsSAX2Func) gst_cmml_parser_parse_end_element_ns;
-  parser->context->sax->processingInstruction = (processingInstructionSAXFunc)
-      gst_cmml_parser_parse_processing_instruction;
+  parser->context->sax->processingInstruction =
+    (processingInstructionSAXFunc) gst_cmml_parser_parse_processing_instruction;
   parser->preamble_callback = NULL;
   parser->cmml_end_callback = NULL;
   parser->stream_callback = NULL;
@@ -97,7 +113,9 @@
     xmlErrorPtr xml_error = xmlCtxtGetLastError (parser->context);
 
     g_set_error (err,
-        GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED, xml_error->message);
+        GST_LIBRARY_ERROR,
+        GST_LIBRARY_ERROR_FAILED,
+        xml_error->message);
     return FALSE;
   }
 
@@ -463,13 +481,18 @@
   GstClockTime start_time = GST_CLOCK_TIME_NONE;
   GstClockTime end_time = GST_CLOCK_TIME_NONE;
 
-  g_value_init (&str_val, G_TYPE_STRING);
+  start = xmlGetProp (clip, (xmlChar *) "start");
+  if (parser->mode == GST_CMML_PARSER_ENCODE && start == NULL)
+    /* XXX: validate the document */
+    return;
 
   id = xmlGetProp (clip, (xmlChar *) "id");
   track = xmlGetProp (clip, (xmlChar *) "track");
-  start = xmlGetProp (clip, (xmlChar *) "start");
   end = xmlGetProp (clip, (xmlChar *) "end");
 
+  if (track == NULL)
+    track = (guchar *) g_strdup ("default"); 
+   
   if (start) {
     if (!strncmp ((gchar *) start, "smpte", 5))
       start_time = gst_cmml_clock_time_from_smpte ((gchar *) start);
@@ -484,11 +507,7 @@
       end_time = gst_cmml_clock_time_from_npt ((gchar *) end);
   }
 
-  if (track == NULL)
-    track = (guchar *) g_strdup ("default");
-
-  clip_tag = g_object_new (GST_TYPE_CMML_TAG_CLIP,
-      "id", id,
+  clip_tag = g_object_new (GST_TYPE_CMML_TAG_CLIP, "id", id,
       "track", track, "start-time", start_time, "end-time", end_time, NULL);
 
   g_free (id);
@@ -496,6 +515,8 @@
   g_free (start);
   g_free (end);
 
+  g_value_init (&str_val, G_TYPE_STRING);
+  
   /* parse the children */
   for (walk = clip->children; walk; walk = walk->next) {
     /* the clip is not empty */
@@ -545,6 +566,18 @@
   }
 }
 
+static void
+gst_cmml_parser_generic_error (void * ctx,
+    const char * msg, ...)
+{
+  va_list varargs;
+
+  va_start (varargs, msg);
+  gst_debug_log_valist (GST_CAT_DEFAULT, GST_LEVEL_WARNING,
+      "", "", 0, NULL, msg, varargs);
+  va_end (varargs);
+}
+
 /* sax handler called when an element start tag is found
  * this is used to parse the cmml start tag
  */
Index: ext/annodex/gstcmmlparser.h
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/ext/annodex/gstcmmlparser.h,v
retrieving revision 1.1
diff -u -w -r1.1 gstcmmlparser.h
--- ext/annodex/gstcmmlparser.h	24 Feb 2006 19:07:09 -0000	1.1
+++ ext/annodex/gstcmmlparser.h	25 Feb 2006 17:31:02 -0000
@@ -69,6 +69,8 @@
   GstCmmlParserClipCallback clip_callback;
 };
 
+void gst_cmml_parser_init (void);
+
 GstCmmlParser *gst_cmml_parser_new (GstCmmlParserMode mode);
 void gst_cmml_parser_free (GstCmmlParser * parser);
 
Index: ext/annodex/gstcmmlutils.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/ext/annodex/gstcmmlutils.c,v
retrieving revision 1.2
diff -u -w -r1.2 gstcmmlutils.c
--- ext/annodex/gstcmmlutils.c	25 Feb 2006 11:37:10 -0000	1.2
+++ ext/annodex/gstcmmlutils.c	25 Feb 2006 17:31:03 -0000
@@ -186,13 +186,28 @@
   if (prev_time > current_time)
     return -1;
 
+  /* GST_SECOND / (granulerate_n / granulerate_d) */
   granulerate = gst_util_uint64_scale (GST_SECOND,
-      granulerate_n, granulerate_d);
-  keyindex = prev_time / granulerate << granuleshift;
-  keyoffset = (current_time - prev_time) / granulerate;
+      granulerate_d, granulerate_n);
+  
+  prev_time = prev_time / granulerate;
+  if (prev_time > (((guint64)1 << (64 - granuleshift)) - 1))
+    /* we need more than 64 - granuleshift bits to encode prev_time */
+    goto overflow;
+  
+  keyindex = prev_time << granuleshift;
+
+  keyoffset = (current_time / granulerate) - prev_time;
+  if (keyoffset > ((guint64)1 << granuleshift) - 1)
+    /* we need more than granuleshift bits to encode prev_time - current_time */
+    goto overflow;
+  
   granulepos = keyindex + keyoffset;
 
   return granulepos;
+
+overflow:
+  return -1;
 }
 
 /* track list */
@@ -221,8 +236,6 @@
 void
 gst_cmml_track_list_destroy (GHashTable * tracks)
 {
-  g_return_if_fail (tracks != NULL);
-
   g_hash_table_foreach_remove (tracks,
       (GHRFunc) gst_cmml_track_list_destroy_track, NULL);
   g_hash_table_destroy (tracks);
@@ -240,28 +253,24 @@
 void
 gst_cmml_track_list_add_clip (GHashTable * tracks, GstCmmlTagClip * clip)
 {
-  GstCmmlTrack *track = NULL;
-  gchar *track_name = NULL;
-  void *key = NULL, *value = NULL;
+  gpointer key, value;
+  GstCmmlTrack *track;
+  gchar *track_name;
 
-  /* find clip's track */
-  g_hash_table_lookup_extended (tracks, clip->track, &key, &value);
-  track_name = (gchar *) key;
-  track = (GstCmmlTrack *) track;
+  g_return_if_fail (clip->track != NULL);
 
-  if (track_name == NULL)
-    /* it doesn't exist yet: create its key */
+  if (g_hash_table_lookup_extended (tracks, clip->track, &key, &value)) {
+    track_name = (gchar *)key;
+    track = (GstCmmlTrack *)value;
+  } else {
     track_name = g_strdup ((gchar *) clip->track);
-
-  if (track == NULL)
     track = g_new0 (GstCmmlTrack, 1);
+    g_hash_table_insert (tracks, track_name, track);
+  }
 
   /* add clip to the tracklist */
   track->clips = g_list_insert_sorted (track->clips, g_object_ref (clip),
       (GCompareFunc) gst_cmml_track_list_compare_clips);
-
-  /* reset the head every time as it could change */
-  g_hash_table_insert (tracks, track_name, track);
 }
 
 gboolean
@@ -271,6 +280,8 @@
   GList *link;
   gboolean res = FALSE;
 
+  g_return_val_if_fail (clip->track != NULL, FALSE);
+  
   track = g_hash_table_lookup (tracks, clip->track);
   if (track) {
     link = g_list_find (track->clips, clip);
@@ -293,6 +304,8 @@
   gchar *clip_id = (gchar *) clip->id;
   gboolean res = FALSE;
 
+  g_return_val_if_fail (clip_id != NULL, FALSE);
+
   track = g_hash_table_lookup (tracks, clip_id);
   if (track) {
     for (walk = track->clips; walk; walk = g_list_next (walk)) {
@@ -329,6 +342,8 @@
 {
   GstCmmlTrack *track;
 
+  g_return_val_if_fail (track_name != NULL, NULL);
+  
   track = g_hash_table_lookup (tracks, track_name);
   return track ? track->clips : NULL;
 }
@@ -350,6 +365,8 @@
   GstCmmlTrack *track;
   GList *res = NULL;
 
+  g_return_val_if_fail (track_name != NULL, NULL);
+  
   track = g_hash_table_lookup (tracks, track_name);
   if (track && track->clips)
     res = g_list_last (track->clips);
@@ -363,6 +380,8 @@
 {
   GstCmmlTrack *track;
 
+  g_return_if_fail (track_name != NULL);
+  
   track = g_hash_table_lookup (tracks, track_name);
   if (track)
     track->user_data = data;
@@ -373,6 +392,9 @@
 {
   GstCmmlTrack *track;
 
+  g_return_val_if_fail (track_name != NULL, NULL);
+  
   track = g_hash_table_lookup (tracks, track_name);
   return track ? track->user_data : NULL;
 }
+
Index: tests/check/elements/cmmldec.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/tests/check/elements/cmmldec.c,v
retrieving revision 1.2
diff -u -w -r1.2 cmmldec.c
--- tests/check/elements/cmmldec.c	24 Feb 2006 23:52:28 -0000	1.2
+++ tests/check/elements/cmmldec.c	25 Feb 2006 17:31:03 -0000
@@ -25,14 +25,14 @@
 
 #include <gst/tag/tag.h>
 
-#define SINK_CAPS "text/xml"
+#define SINK_CAPS "text/x-cmml"
 #define SRC_CAPS "text/x-cmml"
 
 #define IDENT_HEADER \
   "CMML\x00\x00\x00\x00"\
   "\x03\x00\x00\x00"\
-  "\x01\x00\x00\x00\x00\x00\x00\x00"\
   "\xe8\x03\x00\x00\x00\x00\x00\x00"\
+  "\x01\x00\x00\x00\x00\x00\x00\x00"\
   "\x20"
 
 #define XML_PREAMBLE \
@@ -165,7 +165,6 @@
     const gchar * data, gint refcount)
 {
   GstBuffer *buffer = GST_BUFFER (current_buf->data);
-
   ASSERT_OBJECT_REFCOUNT (buffer, name, refcount);
   fail_unless (memcmp (GST_BUFFER_DATA (buffer), data,
           GST_BUFFER_SIZE (buffer)) == 0,
@@ -269,17 +268,13 @@
   /* send EOS to flush clip-2 and clip-3 */
   gst_pad_send_event (GST_PAD_PEER (srcpad), gst_event_new_eos ());
 
-  printf ("Check1\n");
   check_clip ("clip-1", "default", "0:00:01.234", NULL);
-  printf ("Check2\n");
   check_clip ("clip-2", "othertrack", "0:00:04.321", NULL);
-  printf ("Check3\n");
   check_clip ("clip-3", "default", "100:59:59.678", NULL);
   check_end ();
 
   cleanup_cmmldec (cmmldec);
 }
-
 GST_END_TEST;
 
 GST_START_TEST (test_tags)
@@ -377,7 +372,6 @@
   gst_object_unref (bus);
   cleanup_cmmldec (cmmldec);
 }
-
 GST_END_TEST;
 
 Suite *
Index: tests/check/elements/cmmlenc.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/tests/check/elements/cmmlenc.c,v
retrieving revision 1.2
diff -u -w -r1.2 cmmlenc.c
--- tests/check/elements/cmmlenc.c	24 Feb 2006 23:52:28 -0000	1.2
+++ tests/check/elements/cmmlenc.c	25 Feb 2006 17:31:03 -0000
@@ -26,13 +26,13 @@
 #include <gst/tag/tag.h>
 
 #define SINK_CAPS "text/x-cmml"
-#define SRC_CAPS "text/xml"
+#define SRC_CAPS "text/x-cmml"
 
 #define IDENT_HEADER \
   "CMML\x00\x00\x00\x00"\
   "\x03\x00\x00\x00"\
-  "\x01\x00\x00\x00\x00\x00\x00\x00"\
   "\xe8\x03\x00\x00\x00\x00\x00\x00"\
+  "\x01\x00\x00\x00\x00\x00\x00\x00"\
   "\x20"
 
 #define XML_PREAMBLE \
@@ -154,7 +154,7 @@
       "granule-rate-denominator", &granulerate_d,
       "granule-shift", &granuleshift, NULL);
 
-  granulerate = GST_SECOND * granulerate_n / granulerate_d;
+  granulerate = GST_SECOND * granulerate_d / granulerate_n;
   buffers = NULL;
   return cmmlenc;
 }
@@ -240,7 +240,7 @@
   if (track == NULL)
     track = "default";
 
-  clip = g_strdup_printf (CLIP_TEMPLATE, name, track, start, end);
+  clip = g_strdup_printf (CLIP_TEMPLATE, name, track, start);
   push_data (name, clip, strlen (clip), expected_return);
   g_free (clip);
 }
@@ -306,7 +306,6 @@
 
   cleanup_cmmlenc (cmmlenc);
 }
-
 GST_END_TEST;
 
 GST_START_TEST (test_bad_start_time)
@@ -317,23 +316,38 @@
 
   check_headers ();
 
-  push_clip ("clip-1", "default", "1.234", NULL, GST_FLOW_OK);
-  check_clip ("clip-1", "default", 1234 * granulerate, 0);
-
-  push_clip ("clip-bad", "default", "1.1000", NULL, GST_FLOW_ERROR);
+  push_clip ("clip-1", "default", "1000:00:00.000", NULL, GST_FLOW_OK);
+  check_clip ("clip-1", "default", (guint64)3600000 * 1000  * granulerate, 0);
 
-  push_clip ("clip-2", NULL, "5.678", NULL, GST_FLOW_OK);
-  check_clip ("clip-2", "default", 5678 * granulerate, 1234 * granulerate);
+  /* keyindex overflow: npt:1000:00:00.000 doesn't fit in 32 bits */
+  push_clip ("clip-2", NULL, "5.678", NULL, GST_FLOW_ERROR);
 
+  /* other tracks should work */
   push_clip ("clip-3", "othertrack", "9.123", NULL, GST_FLOW_OK);
   check_clip ("clip-3", "othertrack", 9123 * granulerate, 0);
 
+  /* bad msecs */
+  push_clip ("clip-bad-msecs", "default", "0.1000", NULL, GST_FLOW_ERROR);
+ 
+  /* bad secs */
+  push_clip ("clip-bad-secs", "default", "00:00:60.123", NULL, GST_FLOW_ERROR);
+
+  /* bad minutes */
+  push_clip ("clip-bad-minutes", "default", "00:60:12.345",
+      NULL, GST_FLOW_ERROR);
+
+  /* bad hours */
+  push_clip ("clip-bad-hours", "default", "10000:12:34.567",
+      NULL, GST_FLOW_ERROR);
+  
   push_end ();
   check_end ();
 
   cleanup_cmmlenc (cmmlenc);
 }
-GST_END_TEST static Suite *
+GST_END_TEST
+
+static Suite *
 cmmlenc_suite ()
 {
   Suite *s = suite_create ("cmmlenc");
