matroska: re-activate and update TOC support

TOC support in mastroskamux has been deactivated for a couple of years. This commit updates it to recent GstToc evolutions and introduces toc unit tests for both matroska-mux and matroska-demux.

There are two UIDs for Chapters in Matroska's specifications:
- The ChapterUID is a mandatory unsigned integer which internally refers to a given chapter. Except for title & language which use dedicated fields, this UID can also be used to add tags to the Chapter. The tags come in a separate section of the container.
- The ChapterStringUID is an optional UTF-8 string which also uniquely refers to a chapter but from an external perspective. It can act as a "WebVTT cue identifier" which "can be used to reference a specific cue, for example from script or CSS".

During muxing, the ChapterUID is generated and checked for unicity, while the ChapterStringUID receives the user defined UID. In order to be able to refer to chapters from the tags section, we maintain an internal Toc tree with the generated ChapterUID.

When demuxing, the ChapterStringUIDs (if available) are assigned to the GstTocEntries UIDs and an internal toc mimicking the toc is used to keep track of the ChapterUIDs and match the tags with the appropriate GstTocEntries.

https://bugzilla.gnome.org/show_bug.cgi?id=790686
diff --git a/gst/matroska/matroska-ids.h b/gst/matroska/matroska-ids.h
index 24e052d..181dfd8 100644
--- a/gst/matroska/matroska-ids.h
+++ b/gst/matroska/matroska-ids.h
@@ -304,6 +304,7 @@
 
 /* IDs in the ChapterAtom master */
 #define GST_MATROSKA_ID_CHAPTERUID                 0x73C4
+#define GST_MATROSKA_ID_CHAPTERSTRINGUID           0x5654
 #define GST_MATROSKA_ID_CHAPTERTIMESTART           0x91
 #define GST_MATROSKA_ID_CHAPTERTIMESTOP            0x92
 #define GST_MATROSKA_ID_CHAPTERFLAGHIDDEN          0x98
diff --git a/gst/matroska/matroska-mux.c b/gst/matroska/matroska-mux.c
index bbcbb0a..93219ba 100644
--- a/gst/matroska/matroska-mux.c
+++ b/gst/matroska/matroska-mux.c
@@ -526,6 +526,10 @@
 
   g_array_free (mux->used_uids, TRUE);
 
+  if (mux->internal_toc != NULL) {
+    gst_toc_unref (mux->internal_toc);
+  }
+
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
@@ -699,6 +703,7 @@
 
   /* reset chapters */
   gst_toc_setter_reset (GST_TOC_SETTER (mux));
+  mux->internal_toc = NULL;
 
   mux->chapters_pos = 0;
 
@@ -2609,7 +2614,6 @@
   }
 }
 
-#if 0
 static void
 gst_matroska_mux_write_chapter_title (const gchar * title, GstEbmlWrite * ebml)
 {
@@ -2625,17 +2629,20 @@
   gst_ebml_write_master_finish (ebml, title_master);
 }
 
-static void
+static GstTocEntry *
 gst_matroska_mux_write_chapter (GstMatroskaMux * mux, GstTocEntry * edition,
     GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters,
     guint64 * master_edition)
 {
-  guint64 uid, master_chapteratom;
+  guint64 master_chapteratom;
   GList *cur;
-  GstTocEntry *cur_entry;
   guint count, i;
   gchar *title;
   gint64 start, stop;
+  guint64 uid;
+  gchar s_uid[32];
+  GstTocEntry *internal_chapter, *internal_nested;
+  GstTagList *tags;
 
   if (G_UNLIKELY (master_chapters != NULL && *master_chapters == 0))
     *master_chapters =
@@ -2643,78 +2650,111 @@
 
   if (G_UNLIKELY (master_edition != NULL && *master_edition == 0)) {
     /* create uid for the parent */
-    uid = gst_matroska_mux_create_uid ();
-    g_free (edition->uid);
-    edition->uid = g_strdup_printf ("%" G_GUINT64_FORMAT, uid);
-
     *master_edition =
         gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_EDITIONENTRY);
 
-    gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONUID, uid);
+    gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONUID,
+        g_ascii_strtoull (gst_toc_entry_get_uid (edition), NULL, 10));
     gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGHIDDEN, 0);
     gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGDEFAULT, 0);
     gst_ebml_write_uint (ebml, GST_MATROSKA_ID_EDITIONFLAGORDERED, 0);
   }
 
-  uid = gst_matroska_mux_create_uid ();
   gst_toc_entry_get_start_stop_times (entry, &start, &stop);
+  tags = gst_toc_entry_get_tags (entry);
+  if (tags != NULL) {
+    tags = gst_tag_list_copy (tags);
+  }
 
+  /* build internal chapter */
+  uid = gst_matroska_mux_create_uid (mux);
+  g_snprintf (s_uid, sizeof (s_uid), "%" G_GINT64_FORMAT, uid);
+  internal_chapter = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, s_uid);
+
+  /* Write the chapter entry */
   master_chapteratom =
       gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_CHAPTERATOM);
-  g_free (entry->uid);
-  entry->uid = g_strdup_printf ("%" G_GUINT64_FORMAT, uid);
+
   gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERUID, uid);
+  /* Store the user provided UID in the ChapterStringUID */
+  gst_ebml_write_utf8 (ebml, GST_MATROSKA_ID_CHAPTERSTRINGUID,
+      gst_toc_entry_get_uid (entry));
   gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTART, start);
   gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERTIMESTOP, stop);
   gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGHIDDEN, 0);
   gst_ebml_write_uint (ebml, GST_MATROSKA_ID_CHAPTERFLAGENABLED, 1);
 
-  cur = entry->subentries;
-  while (cur != NULL) {
-    cur_entry = cur->data;
-    gst_matroska_mux_write_chapter (mux, NULL, cur_entry, ebml, NULL, NULL);
-
-    cur = cur->next;
-  }
-
-  if (G_LIKELY (entry->tags != NULL)) {
-    count = gst_tag_list_get_tag_size (entry->tags, GST_TAG_TITLE);
+  /* write current ChapterDisplays before the nested chapters */
+  if (G_LIKELY (tags != NULL)) {
+    count = gst_tag_list_get_tag_size (tags, GST_TAG_TITLE);
 
     for (i = 0; i < count; ++i) {
-      gst_tag_list_get_string_index (entry->tags, GST_TAG_TITLE, i, &title);
+      gst_tag_list_get_string_index (tags, GST_TAG_TITLE, i, &title);
+      /* FIXME: handle ChapterLanguage entries */
       gst_matroska_mux_write_chapter_title (title, ebml);
       g_free (title);
     }
 
     /* remove title tag */
     if (G_LIKELY (count > 0))
-      gst_tag_list_remove_tag (entry->tags, GST_TAG_TITLE);
+      gst_tag_list_remove_tag (tags, GST_TAG_TITLE);
+
+    gst_toc_entry_set_tags (internal_chapter, tags);
+  }
+
+  /* Write nested chapters */
+  for (cur = gst_toc_entry_get_sub_entries (entry); cur != NULL;
+      cur = cur->next) {
+    internal_nested = gst_matroska_mux_write_chapter (mux, NULL, cur->data,
+        ebml, NULL, NULL);
+
+    gst_toc_entry_append_sub_entry (internal_chapter, internal_nested);
   }
 
   gst_ebml_write_master_finish (ebml, master_chapteratom);
+
+  return internal_chapter;
 }
 
-static void
+static GstTocEntry *
 gst_matroska_mux_write_chapter_edition (GstMatroskaMux * mux,
-    GstTocEntry * entry, GstEbmlWrite * ebml, guint64 * master_chapters)
+    GstTocEntry * edition, GList * chapters, GstEbmlWrite * ebml,
+    guint64 * master_chapters)
 {
   guint64 master_edition = 0;
+  gchar s_uid[32];
   GList *cur;
-  GstTocEntry *subentry;
+  GstTocEntry *internal_edition, *internal_chapter;
+  GstTagList *tags = NULL;
 
-  cur = gst_toc_entry_get_sub_entries (entry);
-  while (cur != NULL) {
-    subentry = cur->data;
-    gst_matroska_mux_write_chapter (mux, entry, subentry, ebml, master_chapters,
-        &master_edition);
+  g_snprintf (s_uid, sizeof (s_uid), "%" G_GINT64_FORMAT,
+      gst_matroska_mux_create_uid (mux));
 
-    cur = cur->next;
+  if (edition != NULL) {
+    /* Edition entry defined, get its tags */
+    tags = gst_toc_entry_get_tags (edition);
+    if (tags != NULL) {
+      tags = gst_tag_list_copy (tags);
+    }
+  }
+
+  internal_edition = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, s_uid);
+  if (tags != NULL) {
+    gst_toc_entry_set_tags (internal_edition, tags);
+  }
+
+  for (cur = g_list_first (chapters); cur != NULL; cur = cur->next) {
+    internal_chapter = gst_matroska_mux_write_chapter (mux, internal_edition,
+        cur->data, ebml, master_chapters, &master_edition);
+
+    gst_toc_entry_append_sub_entry (internal_edition, internal_chapter);
   }
 
   if (G_LIKELY (master_edition != 0))
     gst_ebml_write_master_finish (ebml, master_edition);
+
+  return internal_edition;
 }
-#endif
 
 /**
  * gst_matroska_mux_start:
@@ -2745,9 +2785,7 @@
   guint32 segment_uid[4];
   GTimeVal time = { 0, 0 };
   gchar s_id[32];
-#if 0
   GstToc *toc;
-#endif
 
   /* if not streaming, check if downstream is seekable */
   if (!mux->ebml_write->streamable) {
@@ -2930,77 +2968,66 @@
   }
   gst_ebml_write_master_finish (ebml, master);
 
-  /* FIXME: Check if we get a TOC that is supported by Matroska
-   * and clean up the code below */
-#if 0
   /* chapters */
   toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
   if (toc != NULL && !mux->ebml_write->streamable) {
     guint64 master_chapters = 0;
-    GstTocEntry *toc_entry;
-    GList *cur, *to_write = NULL;
-    gint64 start, stop;
+    GstTocEntry *internal_edition;
+    GList *cur, *chapters;
 
     GST_DEBUG ("Writing chapters");
 
-    /* check whether we have editions or chapters at the root level */
-    toc_entry = toc->entries->data;
+    /* There are two UIDs for Chapters:
+     * - The ChapterUID is a mandatory unsigned integer which internally
+     * refers to a given chapter. Except for the title & language which use
+     * dedicated fields, this UID can also be used to add tags to the Chapter.
+     * The tags come in a separate section of the container.
+     * - The ChapterStringUID is an optional UTF-8 string which also uniquely
+     * refers to a chapter but from an external perspective. It can act as a
+     * "WebVTT cue identifier" which "can be used to reference a specific cue,
+     * for example from script or CSS".
+     *
+     * The ChapterUID will be generated and checked for unicity, while the
+     * ChapterStringUID will receive the user defined UID.
+     *
+     * In order to be able to refer to chapters from the tags section,
+     * we must maintain an internal Toc tree with the generated ChapterUID
+     * (see gst_matroska_mux_write_toc_entry_tags) */
 
-    if (toc_entry->type != GST_TOC_ENTRY_TYPE_EDITION) {
-      toc_entry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, "");
-      gst_toc_entry_set_start_stop_times (toc_entry, -1, -1);
+    /* Check whether we have editions or chapters at the root level. */
+    cur = gst_toc_get_entries (toc);
+    if (cur != NULL) {
+      mux->chapters_pos = ebml->pos;
 
-      /* aggregate all chapters without root edition */
-      cur = gst_toc_get_entries (toc);
-      while (cur != NULL) {
-        toc_entry->subentries =
-            g_list_prepend (toc_entry->subentries, cur->data);
-        cur = cur->next;
+      mux->internal_toc = gst_toc_new (GST_TOC_SCOPE_GLOBAL);
+
+      if (gst_toc_entry_get_entry_type (cur->data) ==
+          GST_TOC_ENTRY_TYPE_EDITION) {
+        /* Editions at the root level */
+        for (; cur != NULL; cur = cur->next) {
+          chapters = gst_toc_entry_get_sub_entries (cur->data);
+          internal_edition = gst_matroska_mux_write_chapter_edition (mux,
+              cur->data, chapters, ebml, &master_chapters);
+          gst_toc_append_entry (mux->internal_toc, internal_edition);
+        }
+      } else {
+        /* Chapters at the root level */
+        internal_edition = gst_matroska_mux_write_chapter_edition (mux,
+            NULL, cur, ebml, &master_chapters);
+        gst_toc_append_entry (mux->internal_toc, internal_edition);
       }
 
-      gst_toc_entry_get_start_stop_times (((GstTocEntry *)
-              toc_entry->subentries->data), &start, NULL);
-      toc_entry->subentries = g_list_reverse (toc_entry->subentries);
-      gst_toc_entry_get_start_stop_times (((GstTocEntry *)
-              toc_entry->subentries->data), NULL, &stop);
-      gst_toc_entry_set_start_stop_times (toc_entry, start, stop);
-
-      to_write = g_list_append (to_write, toc_entry);
-    } else {
-      toc_entry = NULL;
-      to_write = toc->entries;
-    }
-
-    /* finally write chapters */
-    mux->chapters_pos = ebml->pos;
-
-    cur = to_write;
-    while (cur != NULL) {
-      gst_matroska_mux_write_chapter_edition (mux, cur->data, ebml,
-          &master_chapters);
-      cur = cur->next;
-    }
-
-    /* close master element if any edition was written */
-    if (G_LIKELY (master_chapters != 0))
-      gst_ebml_write_master_finish (ebml, master_chapters);
-
-    if (toc_entry != NULL) {
-      g_list_free (toc_entry->subentries);
-      toc_entry->subentries = NULL;
-      gst_toc_entry_unref (toc_entry);
-      g_list_free (to_write);
+      /* close master element if any edition was written */
+      if (G_LIKELY (master_chapters != 0))
+        gst_ebml_write_master_finish (ebml, master_chapters);
     }
   }
-#endif
 
   /* lastly, flush the cache */
   gst_ebml_write_flush_cache (ebml, FALSE, 0);
 
-#if 0
   if (toc != NULL)
     gst_toc_unref (toc);
-#endif
 }
 
 /* TODO: more sensible tag mappings */
@@ -3146,19 +3173,21 @@
   return FALSE;
 }
 
-#if 0
 static void
 gst_matroska_mux_write_toc_entry_tags (GstMatroskaMux * mux,
-    const GstTocEntry * entry, guint64 * master_tags)
+    const GstTocEntry * entry, guint64 * master_tags, gboolean * has_tags)
 {
   guint64 master_tag, master_targets;
   GstEbmlWrite *ebml;
   GList *cur;
+  const GstTagList *tags;
 
   ebml = mux->ebml_write;
 
-  if (G_UNLIKELY (entry->tags != NULL
-          && !gst_matroska_mux_tag_list_is_empty (entry->tags))) {
+  tags = gst_toc_entry_get_tags (entry);
+  if (G_UNLIKELY (tags != NULL && !gst_matroska_mux_tag_list_is_empty (tags))) {
+    *has_tags = TRUE;
+
     if (*master_tags == 0) {
       mux->tags_pos = ebml->pos;
       *master_tags = gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TAGS);
@@ -3168,25 +3197,24 @@
     master_targets =
         gst_ebml_write_master_start (ebml, GST_MATROSKA_ID_TARGETS);
 
-    if (entry->type == GST_TOC_ENTRY_TYPE_EDITION)
+    if (gst_toc_entry_get_entry_type (entry) == GST_TOC_ENTRY_TYPE_EDITION)
       gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETEDITIONUID,
-          g_ascii_strtoull (entry->uid, NULL, 10));
+          g_ascii_strtoull (gst_toc_entry_get_uid (entry), NULL, 10));
     else
       gst_ebml_write_uint (ebml, GST_MATROSKA_ID_TARGETCHAPTERUID,
-          g_ascii_strtoull (entry->uid, NULL, 10));
+          g_ascii_strtoull (gst_toc_entry_get_uid (entry), NULL, 10));
 
     gst_ebml_write_master_finish (ebml, master_targets);
-    gst_tag_list_foreach (entry->tags, gst_matroska_mux_write_simple_tag, ebml);
+    gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
     gst_ebml_write_master_finish (ebml, master_tag);
   }
 
-  cur = entry->subentries;
-  while (cur != NULL) {
-    gst_matroska_mux_write_toc_entry_tags (mux, cur->data, master_tags);
-    cur = cur->next;
+  for (cur = gst_toc_entry_get_sub_entries (entry); cur != NULL;
+      cur = cur->next) {
+    gst_matroska_mux_write_toc_entry_tags (mux, cur->data, master_tags,
+        has_tags);
   }
 }
-#endif
 
 /**
  * gst_matroska_mux_finish:
@@ -3201,8 +3229,9 @@
   guint64 pos;
   guint64 duration = 0;
   GSList *collected;
-  const GstTagList *tags;
-  gboolean has_main_tags;
+  const GstTagList *tags, *toc_tags;
+  gboolean has_main_tags, toc_has_tags = FALSE;
+  GList *cur;
 
   /* finish last cluster */
   if (mux->cluster) {
@@ -3245,16 +3274,9 @@
   if (has_main_tags || gst_matroska_mux_streams_have_tags (mux)
       || gst_toc_setter_get_toc (GST_TOC_SETTER (mux)) != NULL) {
     guint64 master_tags = 0, master_tag;
-#if 0
-    const GstToc *toc;
-#endif
 
     GST_DEBUG_OBJECT (mux, "Writing tags");
 
-#if 0
-    toc = gst_toc_setter_get_toc (GST_TOC_SETTER (mux));
-#endif
-
     if (has_main_tags) {
       /* TODO: maybe limit via the TARGETS id by looking at the source pad */
       mux->tags_pos = ebml->pos;
@@ -3263,23 +3285,23 @@
 
       if (tags != NULL)
         gst_tag_list_foreach (tags, gst_matroska_mux_write_simple_tag, ebml);
-#if 0
-      if (toc != NULL)
-        gst_tag_list_foreach (toc->tags, gst_matroska_mux_write_simple_tag,
+      if (mux->internal_toc != NULL) {
+        toc_tags = gst_toc_get_tags (mux->internal_toc);
+        toc_has_tags = (toc_tags != NULL);
+        gst_tag_list_foreach (toc_tags, gst_matroska_mux_write_simple_tag,
             ebml);
-#endif
+      }
 
       gst_ebml_write_master_finish (ebml, master_tag);
     }
-#if 0
-    if (toc != NULL) {
-      cur = toc->entries;
-      while (cur != NULL) {
-        gst_matroska_mux_write_toc_entry_tags (mux, cur->data, &master_tags);
-        cur = cur->next;
+
+    if (mux->internal_toc != NULL) {
+      for (cur = gst_toc_get_entries (mux->internal_toc); cur != NULL;
+          cur = cur->next) {
+        gst_matroska_mux_write_toc_entry_tags (mux, cur->data, &master_tags,
+            &toc_has_tags);
       }
     }
-#endif
 
     if (master_tags == 0 && gst_matroska_mux_streams_have_tags (mux)) {
       mux->tags_pos = ebml->pos;
@@ -3329,7 +3351,7 @@
     gst_ebml_write_seek (ebml, my_pos);
   }
 
-  if (tags != NULL) {
+  if (tags != NULL || toc_has_tags) {
     gst_ebml_replace_uint (ebml, mux->seekhead_pos + 144,
         mux->tags_pos - mux->segment_master);
   } else {
diff --git a/gst/matroska/matroska-mux.h b/gst/matroska/matroska-mux.h
index aaeeecd..7283840 100644
--- a/gst/matroska/matroska-mux.h
+++ b/gst/matroska/matroska-mux.h
@@ -135,6 +135,9 @@
   /* GstForceKeyUnit event */
   GstEvent       *force_key_unit_event;
 
+  /* Internal Toc (adjusted UIDs and title tags removed when processed) */
+  GstToc         *internal_toc;
+
   /* Flag to ease handling of WebM specifics */
   gboolean is_webm;
 
diff --git a/gst/matroska/matroska-read-common.c b/gst/matroska/matroska-read-common.c
index 39bc2db..fcce782 100644
--- a/gst/matroska/matroska-read-common.c
+++ b/gst/matroska/matroska-read-common.c
@@ -731,18 +731,19 @@
 
 static void
 gst_matroska_read_common_parse_toc_tag (GstTocEntry * entry,
-    GArray * edition_targets, GArray * chapter_targtes, GstTagList * tags)
+    GstTocEntry * internal_entry, GArray * edition_targets,
+    GArray * chapter_targets, GstTagList * tags)
 {
   gchar *uid;
   guint i;
   guint64 tgt;
   GArray *targets;
-  GList *cur;
+  GList *cur, *internal_cur;
   GstTagList *etags;
 
   targets =
       (gst_toc_entry_get_entry_type (entry) ==
-      GST_TOC_ENTRY_TYPE_EDITION) ? edition_targets : chapter_targtes;
+      GST_TOC_ENTRY_TYPE_EDITION) ? edition_targets : chapter_targets;
 
   etags = gst_tag_list_new_empty ();
 
@@ -753,7 +754,7 @@
       gst_tag_list_insert (etags, tags, GST_TAG_MERGE_APPEND);
     else {
       uid = g_strdup_printf ("%" G_GUINT64_FORMAT, tgt);
-      if (g_strcmp0 (gst_toc_entry_get_uid (entry), uid) == 0)
+      if (g_strcmp0 (gst_toc_entry_get_uid (internal_entry), uid) == 0)
         gst_tag_list_insert (etags, tags, GST_TAG_MERGE_APPEND);
       g_free (uid);
     }
@@ -763,10 +764,12 @@
   gst_tag_list_unref (etags);
 
   cur = gst_toc_entry_get_sub_entries (entry);
-  while (cur != NULL) {
-    gst_matroska_read_common_parse_toc_tag (cur->data, edition_targets,
-        chapter_targtes, tags);
+  internal_cur = gst_toc_entry_get_sub_entries (internal_entry);
+  while (cur != NULL && internal_cur != NULL) {
+    gst_matroska_read_common_parse_toc_tag (cur->data, internal_cur->data,
+        edition_targets, chapter_targets, tags);
     cur = cur->next;
+    internal_cur = internal_cur->next;
   }
 }
 
@@ -954,16 +957,16 @@
 
 static GstFlowReturn
 gst_matroska_read_common_parse_chapter_element (GstMatroskaReadCommon * common,
-    GstEbmlRead * ebml, GList ** subentries)
+    GstEbmlRead * ebml, GList ** subentries, GList ** internal_subentries)
 {
   guint32 id;
   guint64 start_time = -1, stop_time = -1;
   guint64 is_hidden = 0, is_enabled = 1, uid = 0;
   GstFlowReturn ret = GST_FLOW_OK;
-  GstTocEntry *chapter_info;
+  GstTocEntry *chapter_info, *internal_chapter_info;
   GstTagList *tags;
-  gchar *uid_str;
-  GList *subsubentries = NULL, *l;
+  gchar *uid_str, *string_uid = NULL;
+  GList *subsubentries = NULL, *internal_subsubentries = NULL, *l, *il;
 
   DEBUG_ELEMENT_START (common, ebml, "ChaptersElement");
 
@@ -983,6 +986,10 @@
         ret = gst_ebml_read_uint (ebml, &id, &uid);
         break;
 
+      case GST_MATROSKA_ID_CHAPTERSTRINGUID:
+        ret = gst_ebml_read_utf8 (ebml, &id, &string_uid);
+        break;
+
       case GST_MATROSKA_ID_CHAPTERTIMESTART:
         ret = gst_ebml_read_uint (ebml, &id, &start_time);
         break;
@@ -992,9 +999,8 @@
         break;
 
       case GST_MATROSKA_ID_CHAPTERATOM:
-        ret =
-            gst_matroska_read_common_parse_chapter_element (common, ebml,
-            &subsubentries);
+        ret = gst_matroska_read_common_parse_chapter_element (common, ebml,
+            &subsubentries, &internal_subsubentries);
         break;
 
       case GST_MATROSKA_ID_CHAPTERDISPLAY:
@@ -1021,15 +1027,29 @@
   if (uid == 0)
     uid = (((guint64) g_random_int ()) << 32) | g_random_int ();
   uid_str = g_strdup_printf ("%" G_GUINT64_FORMAT, uid);
-  chapter_info = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, uid_str);
+  if (string_uid != NULL) {
+    /* init toc with provided String UID */
+    chapter_info = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, string_uid);
+    g_free (string_uid);
+  } else {
+    /* No String UID provided => use the internal UID instead */
+    chapter_info = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, uid_str);
+  }
+  /* init internal toc with internal UID */
+  internal_chapter_info = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER,
+      uid_str);
   g_free (uid_str);
 
   gst_toc_entry_set_tags (chapter_info, tags);
   gst_toc_entry_set_start_stop_times (chapter_info, start_time, stop_time);
 
-  for (l = subsubentries; l; l = l->next)
+  for (l = subsubentries, il = internal_subsubentries;
+      l && il; l = l->next, il = il->next) {
     gst_toc_entry_append_sub_entry (chapter_info, l->data);
+    gst_toc_entry_append_sub_entry (internal_chapter_info, il->data);
+  }
   g_list_free (subsubentries);
+  g_list_free (internal_subsubentries);
 
   DEBUG_ELEMENT_STOP (common, ebml, "ChaptersElement", ret);
 
@@ -1038,21 +1058,25 @@
   if (is_hidden == 0 && is_enabled > 0 &&
       start_time != -1 && ret == GST_FLOW_OK) {
     *subentries = g_list_append (*subentries, chapter_info);
-  } else
+    *internal_subentries = g_list_append (*internal_subentries,
+        internal_chapter_info);
+  } else {
     gst_toc_entry_unref (chapter_info);
+    gst_toc_entry_unref (internal_chapter_info);
+  }
 
   return ret;
 }
 
 static GstFlowReturn
 gst_matroska_read_common_parse_chapter_edition (GstMatroskaReadCommon * common,
-    GstEbmlRead * ebml, GstToc * toc)
+    GstEbmlRead * ebml, GstToc * toc, GstToc * internal_toc)
 {
   guint32 id;
   guint64 is_hidden = 0, uid = 0;
   GstFlowReturn ret = GST_FLOW_OK;
-  GstTocEntry *edition_info;
-  GList *subentries = NULL, *l;
+  GstTocEntry *edition_info, *internal_edition_info;
+  GList *subentries = NULL, *internal_subentries = NULL, *l, *il;
   gchar *uid_str;
 
   DEBUG_ELEMENT_START (common, ebml, "ChaptersEdition");
@@ -1072,9 +1096,8 @@
         break;
 
       case GST_MATROSKA_ID_CHAPTERATOM:
-        ret =
-            gst_matroska_read_common_parse_chapter_element (common, ebml,
-            &subentries);
+        ret = gst_matroska_read_common_parse_chapter_element (common, ebml,
+            &subentries, &internal_subentries);
         break;
 
       case GST_MATROSKA_ID_EDITIONFLAGHIDDEN:
@@ -1096,18 +1119,26 @@
   uid_str = g_strdup_printf ("%" G_GUINT64_FORMAT, uid);
   edition_info = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, uid_str);
   gst_toc_entry_set_start_stop_times (edition_info, -1, -1);
+  internal_edition_info = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION,
+      uid_str);
   g_free (uid_str);
 
-  for (l = subentries; l; l = l->next)
+  for (l = subentries, il = internal_subentries; l && il;
+      l = l->next, il = il->next) {
     gst_toc_entry_append_sub_entry (edition_info, l->data);
+    gst_toc_entry_append_sub_entry (internal_edition_info, il->data);
+  }
   g_list_free (subentries);
+  g_list_free (internal_subentries);
 
-  if (is_hidden == 0 && subentries != NULL && ret == GST_FLOW_OK)
+  if (is_hidden == 0 && subentries != NULL && ret == GST_FLOW_OK) {
     gst_toc_append_entry (toc, edition_info);
-  else {
+    gst_toc_append_entry (internal_toc, internal_edition_info);
+  } else {
     GST_DEBUG_OBJECT (common->sinkpad,
         "Skipping empty or hidden edition in the chapters TOC");
     gst_toc_entry_unref (edition_info);
+    gst_toc_entry_unref (internal_edition_info);
   }
 
   return ret;
@@ -1119,7 +1150,7 @@
 {
   guint32 id;
   GstFlowReturn ret = GST_FLOW_OK;
-  GstToc *toc;
+  GstToc *toc, *internal_toc;
 
   DEBUG_ELEMENT_START (common, ebml, "Chapters");
 
@@ -1130,6 +1161,7 @@
 
   /* FIXME: create CURRENT toc as well */
   toc = gst_toc_new (GST_TOC_SCOPE_GLOBAL);
+  internal_toc = gst_toc_new (GST_TOC_SCOPE_GLOBAL);
 
   while (ret == GST_FLOW_OK && gst_ebml_read_has_remaining (ebml, 1, TRUE)) {
     if ((ret = gst_ebml_peek_id (ebml, &id)) != GST_FLOW_OK)
@@ -1137,8 +1169,8 @@
 
     switch (id) {
       case GST_MATROSKA_ID_EDITIONENTRY:
-        ret =
-            gst_matroska_read_common_parse_chapter_edition (common, ebml, toc);
+        ret = gst_matroska_read_common_parse_chapter_edition (common, ebml,
+            toc, internal_toc);
         break;
 
       default:
@@ -1151,10 +1183,15 @@
   if (gst_toc_get_entries (toc) != NULL) {
     gst_matroska_read_common_postprocess_toc_entries (gst_toc_get_entries (toc),
         common->segment.duration, "");
+    /* no need to postprocess internal_toc as we don't need to keep track
+     * of start / end and tags (only UIDs) */
 
     common->toc = toc;
-  } else
+    common->internal_toc = internal_toc;
+  } else {
     gst_toc_unref (toc);
+    gst_toc_unref (internal_toc);
+  }
 
   common->chapters_parsed = TRUE;
 
@@ -2228,7 +2265,7 @@
         continue;
       } else if (ctx->target_type_value >= 50) {
         gst_tag_list_add_value (ctx->result, GST_TAG_MERGE_APPEND,
-            GST_TAG_ALBUM, val_ref);
+            GST_TAG_TITLE, val_ref);
         continue;
       }
     } else if (strcmp (tag, GST_TAG_TITLE_SORTNAME) == 0) {
@@ -2238,19 +2275,19 @@
         continue;
       } else if (ctx->target_type_value >= 50) {
         gst_tag_list_add_value (ctx->result, GST_TAG_MERGE_APPEND,
-            GST_TAG_ALBUM_SORTNAME, val_ref);
+            GST_TAG_TITLE_SORTNAME, val_ref);
         continue;
       }
     } else if (strcmp (tag, GST_TAG_ARTIST) == 0) {
       if (ctx->target_type_value >= 50) {
         gst_tag_list_add_value (ctx->result, GST_TAG_MERGE_APPEND,
-            GST_TAG_ALBUM_ARTIST, val_ref);
+            GST_TAG_ARTIST, val_ref);
         continue;
       }
     } else if (strcmp (tag, GST_TAG_ARTIST_SORTNAME) == 0) {
       if (ctx->target_type_value >= 50) {
         gst_tag_list_add_value (ctx->result, GST_TAG_MERGE_APPEND,
-            GST_TAG_ALBUM_ARTIST_SORTNAME, val_ref);
+            GST_TAG_ARTIST_SORTNAME, val_ref);
         continue;
       }
     } else if (strcmp (tag, GST_TAG_TRACK_COUNT) == 0) {
@@ -2311,7 +2348,7 @@
   GstFlowReturn ret;
   GArray *chapter_targets, *edition_targets, *track_targets;
   GstTagList *taglist;
-  GList *cur;
+  GList *cur, *internal_cur;
   guint64 target_type_value = 50;
   gchar *target_type = NULL;
 
@@ -2371,10 +2408,12 @@
             "Found chapter/edition specific tag, but TOC is not present");
       else {
         cur = gst_toc_get_entries (common->toc);
-        while (cur != NULL) {
-          gst_matroska_read_common_parse_toc_tag (cur->data, edition_targets,
-              chapter_targets, taglist);
+        internal_cur = gst_toc_get_entries (common->internal_toc);
+        while (cur != NULL && internal_cur != NULL) {
+          gst_matroska_read_common_parse_toc_tag (cur->data, internal_cur->data,
+              edition_targets, chapter_targets, taglist);
           cur = cur->next;
+          internal_cur = internal_cur->next;
         }
         common->toc_updated = TRUE;
       }
@@ -2967,6 +3006,10 @@
     gst_toc_unref (ctx->toc);
     ctx->toc = NULL;
   }
+  if (ctx->internal_toc) {
+    gst_toc_unref (ctx->internal_toc);
+    ctx->internal_toc = NULL;
+  }
 }
 
 /* call with object lock held */
diff --git a/gst/matroska/matroska-read-common.h b/gst/matroska/matroska-read-common.h
index 717323a..a6282d6 100644
--- a/gst/matroska/matroska-read-common.h
+++ b/gst/matroska/matroska-read-common.h
@@ -73,7 +73,11 @@
   GList                   *tags_parsed;
 
   /* chapters stuff */
+  /* Internal toc is used to keep track of the internal UID
+   * which are different from the external StringUID used
+   * in the user toc */
   GstToc                  *toc;
+  GstToc                  *internal_toc;
   gboolean                toc_updated;
 
   /* start-of-segment and length */
diff --git a/tests/check/elements/matroskademux.c b/tests/check/elements/matroskademux.c
index 93c139a..71bc181 100644
--- a/tests/check/elements/matroskademux.c
+++ b/tests/check/elements/matroskademux.c
@@ -32,6 +32,33 @@
     "AA2bggfQoYeBF3AAYmF6oAEAAAAAAAAOm4IH0KGIgScQAGbDtgCgAQAAAAAAABWbggfQoY+BMsgA"
     "PGk+YmFyPC9pPgCgAQAAAAAAAA6bggfQoYiBPoAAYuR6ABJUw2cBAAAAAAAACnNzAQAAAAAAAAA=";
 
+const gchar mkv_toc_base64[] =
+    "GkXfowEAAAAAAAAUQoKJbWF0cm9za2EAQoeBAUKFgQEYU4BnAQAAAAAABUoRTZt0AQAAAAAAAIxN"
+    "uwEAAAAAAAASU6uEFUmpZlOsiAAAAAAAAACYTbsBAAAAAAAAElOrhBZUrmtTrIgAAAAAAAABGk27"
+    "AQAAAAAAABJTq4QQQ6dwU6yIAAAAAAAAAWFNuwEAAAAAAAASU6uEHFO7a1OsiAAAAAAAAANrTbsB"
+    "AAAAAAAAElOrhBJUw2dTrIgAAAAAAAADkxVJqWYBAAAAAAAAdnOkkFdJrZAH7YY5MCvJGPwl5E4q"
+    "17GDD0JARImIP/AAAAAAAABNgKdHU3RyZWFtZXIgbWF0cm9za2FtdXggdmVyc2lvbiAxLjEzLjAu"
+    "MQBXQZlHU3RyZWFtZXIgTWF0cm9za2EgbXV4ZXIARGGIB2iH12N5DgAWVK5rAQAAAAAAADuuAQAA"
+    "AAAAADLXgQGDgQJzxYgJixQa+ZhvPSPjg4MPQkBTboZBdWRpbwDhAQAAAAAAAACGhkFfQUMzABBD"
+    "p3ABAAAAAAAB30W5AQAAAAAAAdVFvIi3DuS4TWeFXUW9gQBF24EARd2BALYBAAAAAAAA1HPEiOV0"
+    "L8eev+wgVlSGdWlkLjEAkYEAkoMehICYgQBFmIEBgAEAAAAAAAAQhYdjaGFwLjEAQ3yEdW5kALYB"
+    "AAAAAAAAQnPEiCW5ajpHRzyzVlSIdWlkLjEuMQCRgQCSgw9CQJiBAEWYgQGAAQAAAAAAABSFi25l"
+    "c3RlZC4xLjEAQ3yEdW5kALYBAAAAAAAARHPEiA9klFqtGkBoVlSIdWlkLjEuMgCRgw9CQJKDHoSA"
+    "mIEARZiBAYABAAAAAAAAFIWLbmVzdGVkLzEuMgBDfIR1bmQAtgEAAAAAAADYc8SIeu4QRrjscdtW"
+    "VIZ1aWQuMgCRgx6EgJKDPQkAmIEARZiBAYABAAAAAAAAEIWHY2hhcC4yAEN8hHVuZAC2AQAAAAAA"
+    "AERzxIik77DMKqRyzFZUiHVpZC4yLjEAkYMehICSgy3GwJiBAEWYgQGAAQAAAAAAABSFi25lc3Rl"
+    "ZC4yLjEAQ3yEdW5kALYBAAAAAAAARHPEiDvwt+5+V1ktVlSIdWlkLjIuMgCRgy3GwJKDPQkAmIEA"
+    "RZiBAYABAAAAAAAAFIWLbmVzdGVkLzIuMgBDfIR1bmQAH0O2dQEAAAAAAAAT54EAoAEAAAAAAAAH"
+    "oYWBAAAAABxTu2sBAAAAAAAAHLsBAAAAAAAAE7OBALcBAAAAAAAAB/eBAfGCA0wSVMNnAQAAAAAA"
+    "AatzcwEAAAAAAAAxY8ABAAAAAAAAC2PJiLcO5LhNZ4VdZ8gBAAAAAAAAEkWjiUNPTU1FTlRTAESH"
+    "g0VkAHNzAQAAAAAAADJjwAEAAAAAAAALY8SI5XQvx56/7CBnyAEAAAAAAAATRaOHQVJUSVNUAESH"
+    "hmFydC4xAHNzAQAAAAAAADRjwAEAAAAAAAALY8SIJblqOkdHPLNnyAEAAAAAAAAVRaOHQVJUSVNU"
+    "AESHiGFydC4xLjEAc3MBAAAAAAAANGPAAQAAAAAAAAtjxIgPZJRarRpAaGfIAQAAAAAAABVFo4dB"
+    "UlRJU1QARIeIYXJ0LjEuMgBzcwEAAAAAAAAyY8ABAAAAAAAAC2PEiHruEEa47HHbZ8gBAAAAAAAA"
+    "E0Wjh0FSVElTVABEh4ZhcnQuMgBzcwEAAAAAAAA0Y8ABAAAAAAAAC2PEiKTvsMwqpHLMZ8gBAAAA"
+    "AAAAFUWjh0FSVElTVABEh4hhcnQuMi4xAHNzAQAAAAAAADRjwAEAAAAAAAALY8SIO/C37n5XWS1n"
+    "yAEAAAAAAAAVRaOHQVJUSVNUAESHiGFydC4yLjIA";
+
 static void
 pad_added_cb (GstElement * matroskademux, GstPad * pad, gpointer user_data)
 {
@@ -109,6 +136,175 @@
 
 GST_END_TEST;
 
+/* Recusively compare 2 toc entries */
+static void
+check_toc_entries (const GstTocEntry * original, const GstTocEntry * other)
+{
+  gint64 start, stop, other_start, other_stop;
+  GstTocEntryType original_type, other_type;
+  const gchar *original_string_uid = NULL, *other_string_uid = NULL;
+  GstTagList *original_tags, *other_tags;
+  GList *cur, *other_cur;
+
+  original_type = gst_toc_entry_get_entry_type (original);
+  other_type = gst_toc_entry_get_entry_type (other);
+  fail_unless (original_type == other_type);
+
+  if (original_type != GST_TOC_ENTRY_TYPE_EDITION) {
+    original_string_uid = gst_toc_entry_get_uid (original);
+    other_string_uid = gst_toc_entry_get_uid (other);
+    fail_unless (g_strcmp0 (original_string_uid, other_string_uid) == 0);
+  }
+
+  if (original_type != GST_TOC_ENTRY_TYPE_EDITION) {
+    gst_toc_entry_get_start_stop_times (original, &start, &stop);
+    gst_toc_entry_get_start_stop_times (other, &other_start, &other_stop);
+
+    fail_unless (start == other_start && stop == other_stop);
+  }
+
+  /* tags */
+  original_tags = gst_toc_entry_get_tags (original);
+  other_tags = gst_toc_entry_get_tags (other);
+  fail_unless (gst_tag_list_is_equal (original_tags, other_tags));
+
+  other_cur = gst_toc_entry_get_sub_entries (other);
+  for (cur = gst_toc_entry_get_sub_entries (original); cur != NULL;
+      cur = cur->next) {
+    fail_unless (other_cur != NULL);
+
+    check_toc_entries (cur->data, other_cur->data);
+
+    other_cur = other_cur->next;
+  }
+}
+
+/* Create a new chapter */
+static GstTocEntry *
+new_chapter (const guint chapter_nb, const gint64 start, const gint64 stop)
+{
+  GstTocEntry *toc_entry, *toc_sub_entry;
+  GstTagList *tags;
+  gchar title[32];
+  gchar artist[32];
+  gchar str_uid[32];
+
+  g_snprintf (str_uid, sizeof (str_uid), "uid.%d", chapter_nb);
+  toc_entry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, str_uid);
+  gst_toc_entry_set_start_stop_times (toc_entry, start, stop);
+
+  g_snprintf (title, sizeof (title), "chap.%d", chapter_nb);
+  g_snprintf (artist, sizeof (artist), "art.%d", chapter_nb);
+  tags = gst_tag_list_new (GST_TAG_TITLE, title, GST_TAG_ARTIST, artist, NULL);
+  gst_toc_entry_set_tags (toc_entry, tags);
+
+  g_snprintf (str_uid, sizeof (str_uid), "uid.%d.1", chapter_nb);
+  toc_sub_entry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, str_uid);
+  gst_toc_entry_set_start_stop_times (toc_sub_entry, start, (start + stop) / 2);
+
+  g_snprintf (title, sizeof (title), "nested.%d.1", chapter_nb);
+  g_snprintf (artist, sizeof (artist), "art.%d.1", chapter_nb);
+  tags = gst_tag_list_new (GST_TAG_TITLE, title, GST_TAG_ARTIST, artist, NULL);
+  gst_toc_entry_set_tags (toc_sub_entry, tags);
+
+  gst_toc_entry_append_sub_entry (toc_entry, toc_sub_entry);
+
+  g_snprintf (str_uid, sizeof (str_uid), "uid.%d.2", chapter_nb);
+  toc_sub_entry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, str_uid);
+  gst_toc_entry_set_start_stop_times (toc_sub_entry, (start + stop) / 2, stop);
+
+  g_snprintf (title, sizeof (title), "nested/%d.2", chapter_nb);
+  g_snprintf (artist, sizeof (artist), "art.%d.2", chapter_nb);
+  tags = gst_tag_list_new (GST_TAG_TITLE, title, GST_TAG_ARTIST, artist, NULL);
+  gst_toc_entry_set_tags (toc_sub_entry, tags);
+
+  gst_toc_entry_append_sub_entry (toc_entry, toc_sub_entry);
+
+  return toc_entry;
+}
+
+/* Create a reference toc which matches what is expected in mkv_toc_base64 */
+static GstToc *
+new_reference_toc (void)
+{
+  GstToc *ref_toc;
+  GstTocEntry *toc_edition_entry, *toc_entry;
+  GstTagList *tags;
+
+  ref_toc = gst_toc_new (GST_TOC_SCOPE_GLOBAL);
+
+  toc_edition_entry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, "00");
+  tags = gst_tag_list_new (GST_TAG_COMMENT, "Ed", NULL);
+  gst_toc_entry_set_tags (toc_edition_entry, tags);
+
+  toc_entry = new_chapter (1, 0 * GST_MSECOND, 2 * GST_MSECOND);
+  gst_toc_entry_append_sub_entry (toc_edition_entry, toc_entry);
+
+  toc_entry = new_chapter (2, 2 * GST_MSECOND, 4 * GST_MSECOND);
+  gst_toc_entry_append_sub_entry (toc_edition_entry, toc_entry);
+
+  gst_toc_append_entry (ref_toc, toc_edition_entry);
+
+  return ref_toc;
+}
+
+GST_START_TEST (test_toc_demux)
+{
+  GstHarness *h;
+  GstBuffer *buf;
+  guchar *mkv_data;
+  gsize mkv_size;
+  GstEvent *event;
+  gboolean update;
+  GstToc *ref_toc, *demuxed_toc = NULL;
+  GList *ref_cur, *demuxed_cur;
+
+  h = gst_harness_new_with_padnames ("matroskademux", "sink", NULL);
+
+  g_signal_connect (h->element, "pad-added", G_CALLBACK (pad_added_cb), h);
+
+  mkv_data = g_base64_decode (mkv_toc_base64, &mkv_size);
+  fail_unless (mkv_data != NULL);
+
+  gst_harness_set_src_caps_str (h, "audio/x-matroska");
+
+  buf = gst_buffer_new_wrapped (mkv_data, mkv_size);
+  GST_BUFFER_OFFSET (buf) = 0;
+
+  fail_unless_equals_int (gst_harness_push (h, buf), GST_FLOW_OK);
+  gst_harness_push_event (h, gst_event_new_eos ());
+
+  event = gst_harness_try_pull_event (h);
+  fail_unless (event != NULL);
+
+  while (event != NULL) {
+    if (event->type == GST_EVENT_TOC) {
+      gst_event_parse_toc (event, &demuxed_toc, &update);
+      break;
+    }
+    event = gst_harness_try_pull_event (h);
+  }
+
+  fail_unless (demuxed_toc != NULL);
+  ref_toc = new_reference_toc ();
+
+  demuxed_cur = gst_toc_get_entries (demuxed_toc);
+  for (ref_cur = gst_toc_get_entries (ref_toc); ref_cur != NULL;
+      ref_cur = ref_cur->next) {
+    fail_unless (demuxed_cur != NULL);
+
+    check_toc_entries (ref_cur->data, demuxed_cur->data);
+    demuxed_cur = demuxed_cur->next;
+  }
+
+  gst_toc_unref (ref_toc);
+  gst_toc_unref (demuxed_toc);
+
+  gst_harness_teardown (h);
+}
+
+GST_END_TEST;
+
 static Suite *
 matroskademux_suite (void)
 {
@@ -117,6 +313,7 @@
 
   suite_add_tcase (s, tc_chain);
   tcase_add_test (tc_chain, test_sub_terminator);
+  tcase_add_test (tc_chain, test_toc_demux);
 
   return s;
 }
diff --git a/tests/check/elements/matroskamux.c b/tests/check/elements/matroskamux.c
index bffd440..97a033f 100644
--- a/tests/check/elements/matroskamux.c
+++ b/tests/check/elements/matroskamux.c
@@ -155,6 +155,21 @@
 }
 
 
+gboolean downstream_is_seekable;
+static gboolean
+matroskamux_sinkpad_query (GstPad * pad, GstObject * parent, GstQuery * query)
+{
+  gboolean ret = FALSE;
+
+  if (GST_QUERY_TYPE (query) == GST_QUERY_SEEKING) {
+    gst_query_set_seeking (query, GST_FORMAT_BYTES, downstream_is_seekable, 0,
+        -1);
+    ret = TRUE;
+  }
+
+  return ret;
+}
+
 static GstElement *
 setup_matroskamux (GstStaticPadTemplate * srctemplate)
 {
@@ -463,6 +478,654 @@
 
 GST_END_TEST;
 
+/* Create a new chapter */
+static GstTocEntry *
+new_chapter (const guint chapter_nb, const gint64 start, const gint64 stop)
+{
+  GstTocEntry *toc_entry, *toc_sub_entry;
+  GstTagList *tags;
+  gchar title[32];
+  gchar artist[32];
+  gchar str_uid[32];
+
+  g_snprintf (str_uid, sizeof (str_uid), "uid.%d", chapter_nb);
+  toc_entry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, str_uid);
+  gst_toc_entry_set_start_stop_times (toc_entry, start, stop);
+
+  g_snprintf (title, sizeof (title), "chap.%d", chapter_nb);
+  g_snprintf (artist, sizeof (artist), "art.%d", chapter_nb);
+  tags = gst_tag_list_new (GST_TAG_TITLE, title, GST_TAG_ARTIST, artist, NULL);
+  gst_toc_entry_set_tags (toc_entry, tags);
+
+  g_snprintf (str_uid, sizeof (str_uid), "uid.%d.1", chapter_nb);
+  toc_sub_entry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, str_uid);
+  gst_toc_entry_set_start_stop_times (toc_sub_entry, start, (start + stop) / 2);
+
+  g_snprintf (title, sizeof (title), "nested.%d.1", chapter_nb);
+  g_snprintf (artist, sizeof (artist), "art.%d.1", chapter_nb);
+  tags = gst_tag_list_new (GST_TAG_TITLE, title, GST_TAG_ARTIST, artist, NULL);
+  gst_toc_entry_set_tags (toc_sub_entry, tags);
+
+  gst_toc_entry_append_sub_entry (toc_entry, toc_sub_entry);
+
+  g_snprintf (str_uid, sizeof (str_uid), "uid.%d.2", chapter_nb);
+  toc_sub_entry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, str_uid);
+  gst_toc_entry_set_start_stop_times (toc_sub_entry, (start + stop) / 2, stop);
+
+  g_snprintf (title, sizeof (title), "nested/%d.2", chapter_nb);
+  g_snprintf (artist, sizeof (artist), "art.%d.2", chapter_nb);
+  tags = gst_tag_list_new (GST_TAG_TITLE, title, GST_TAG_ARTIST, artist, NULL);
+  gst_toc_entry_set_tags (toc_sub_entry, tags);
+
+  gst_toc_entry_append_sub_entry (toc_entry, toc_sub_entry);
+
+  return toc_entry;
+}
+
+/* Create a reference toc which includes a master edition entry */
+static GstToc *
+new_reference_toc (void)
+{
+  GstToc *ref_toc;
+  GstTocEntry *toc_edition_entry, *toc_entry;
+  GstTagList *tags;
+
+  ref_toc = gst_toc_new (GST_TOC_SCOPE_GLOBAL);
+
+  toc_edition_entry = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, "00");
+  tags = gst_tag_list_new (GST_TAG_COMMENT, "Ed", NULL);
+  gst_toc_entry_set_tags (toc_edition_entry, tags);
+
+  toc_entry = new_chapter (1, 0 * GST_MSECOND, 2 * GST_MSECOND);
+  gst_toc_entry_append_sub_entry (toc_edition_entry, toc_entry);
+
+  toc_entry = new_chapter (2, 2 * GST_MSECOND, 4 * GST_MSECOND);
+  gst_toc_entry_append_sub_entry (toc_edition_entry, toc_entry);
+
+  gst_toc_append_entry (ref_toc, toc_edition_entry);
+
+  return ref_toc;
+}
+
+/* Create a toc which includes chapters without edition entry */
+static GstToc *
+new_no_edition_toc (void)
+{
+  GstToc *ref_toc;
+  GstTocEntry *toc_entry;
+
+  ref_toc = gst_toc_new (GST_TOC_SCOPE_GLOBAL);
+
+  toc_entry = new_chapter (1, 0 * GST_MSECOND, 2 * GST_MSECOND);
+  gst_toc_append_entry (ref_toc, toc_entry);
+
+  toc_entry = new_chapter (2, 2 * GST_MSECOND, 4 * GST_MSECOND);
+  gst_toc_append_entry (ref_toc, toc_entry);
+
+  return ref_toc;
+}
+
+static guint64
+read_integer (GstMapInfo * info, gsize * index, guint64 len)
+{
+  guint64 total = 0;
+
+  for (; len > 0; --len) {
+    total = (total << 8) | GST_READ_UINT8 (info->data + *index);
+    ++(*index);
+  }
+
+  return total;
+}
+
+static guint64
+read_length (GstMapInfo * info, gsize * index)
+{
+  gint len_mask = 0x80, read = 1;
+  guint64 total;
+  guint8 b;
+
+  b = GST_READ_UINT8 (info->data + *index);
+  ++(*index);
+  total = (guint64) b;
+  while (read <= 8 && !(total & len_mask)) {
+    read++;
+    len_mask >>= 1;
+  }
+  total &= (len_mask - 1);
+
+  for (; read > 1; --read) {
+    total = (total << 8) | GST_READ_UINT8 (info->data + *index);
+    ++(*index);
+  }
+
+  return total;
+}
+
+static gboolean
+check_id (GstMapInfo * info, gsize * index,
+    guint8 * tag, gint tag_len, guint64 * len)
+{
+  if (memcmp (info->data + *index, tag, tag_len) == 0) {
+    *index += tag_len;
+    *len = read_length (info, index);
+    return TRUE;
+  } else {
+    return FALSE;
+  }
+}
+
+static gboolean
+check_id_read_int (GstMapInfo * info, gsize * index,
+    guint8 * tag, gint tag_len, guint64 * value)
+{
+  guint64 len;
+
+  if (check_id (info, index, tag, tag_len, &len)) {
+    *value = read_integer (info, index, len);
+    return TRUE;
+  } else {
+    return FALSE;
+  }
+}
+
+/* Check the toc entry against the muxed buffer
+ * Returns the internal UID */
+static void
+check_chapter (GstTocEntry * toc_entry, GstTocEntry * internal_toc_entry,
+    GstMapInfo * info, gsize * index, gint last_offset)
+{
+  guint64 len, value, uid;
+  gint64 start_ref, end_ref;
+  gchar s_uid[32];
+  const gchar *str_uid;
+  GstTocEntry *internal_chapter;
+  GList *cur_sub_chap;
+  GstTagList *tags;
+  gchar *title;
+
+  guint8 chapter_atom[1] = { 0xb6 };
+  guint8 chapter_uid[2] = { 0x73, 0xc4 };
+  guint8 chapter_str_uid[2] = { 0x56, 0x54 };
+  guint8 chapter_start[1] = { 0x91 };
+  guint8 chapter_end[1] = { 0x92 };
+  guint8 chapter_flag_hidden[1] = { 0x98 };
+  guint8 chapter_flag_enabled[2] = { 0x45, 0x98 };
+  guint8 chapter_segment_uid[2] = { 0x6e, 0x67 };
+  guint8 chapter_segment_edition_uid[2] = { 0x6e, 0xbc };
+  guint8 chapter_physical_equiv[2] = { 0x63, 0xc3 };
+  guint8 chapter_track[1] = { 0x8f };
+  guint8 chapter_track_nb[1] = { 0x89 };
+  guint8 chapter_display[1] = { 0x80 };
+  guint8 chapter_string[1] = { 0x85 };
+  guint8 chapter_language[2] = { 0x43, 0x7c };
+
+  fail_unless (check_id (info, index, chapter_atom,
+          sizeof (chapter_atom), &len));
+
+  fail_unless (check_id_read_int (info, index, chapter_uid,
+          sizeof (chapter_uid), &uid));
+
+  /* optional StringUID */
+  if (check_id (info, index, chapter_str_uid, sizeof (chapter_str_uid), &len)) {
+    str_uid = gst_toc_entry_get_uid (toc_entry);
+    fail_unless (memcmp (info->data + *index, str_uid, strlen (str_uid)) == 0);
+    *index += len;
+  }
+
+  gst_toc_entry_get_start_stop_times (toc_entry, &start_ref, &end_ref);
+
+  fail_unless (check_id_read_int (info, index, chapter_start,
+          sizeof (chapter_start), &value));
+  fail_unless ((gint64) value == start_ref);
+
+  /* optional chapter end */
+  if (check_id_read_int (info, index, chapter_end,
+          sizeof (chapter_end), &value)) {
+    fail_unless ((gint64) value == end_ref);
+  }
+
+  fail_unless (check_id_read_int (info, index, chapter_flag_hidden,
+          sizeof (chapter_flag_hidden), &value));
+
+  fail_unless (check_id_read_int (info, index, chapter_flag_enabled,
+          sizeof (chapter_flag_enabled), &value));
+
+  /* optional segment UID */
+  check_id_read_int (info, index, chapter_segment_uid,
+      sizeof (chapter_segment_uid), &value);
+
+  /* optional segment edition UID */
+  check_id_read_int (info, index, chapter_segment_edition_uid,
+      sizeof (chapter_segment_edition_uid), &value);
+
+  /* optional physical equiv */
+  check_id_read_int (info, index, chapter_physical_equiv,
+      sizeof (chapter_physical_equiv), &value);
+
+  /* optional chapter track */
+  if (check_id (info, index, chapter_track, sizeof (chapter_track), &len)) {
+    fail_unless (check_id_read_int (info, index, chapter_track_nb,
+            sizeof (chapter_track_nb), &value));
+  }
+
+  /* FIXME: there can be several chapter displays */
+  if (check_id (info, index, chapter_display, sizeof (chapter_display), &len)) {
+    /* chapter display */
+    fail_unless (check_id (info, index, chapter_string,
+            sizeof (chapter_string), &len));
+
+    tags = gst_toc_entry_get_tags (toc_entry);
+    if (gst_tag_list_get_tag_size (tags, GST_TAG_TITLE) > 0) {
+      gst_tag_list_get_string_index (tags, GST_TAG_TITLE, 0, &title);
+      fail_unless (memcmp (info->data + *index, title, strlen (title)) == 0);
+    }
+    *index += len;
+
+    fail_unless (check_id (info, index, chapter_language,
+            sizeof (chapter_language), &len));
+    /* TODO: define language - always "und" ATM */
+    *index += len;
+  }
+
+  /* TODO: add remaining fields (not used in current matroska-mux) */
+
+  g_snprintf (s_uid, sizeof (s_uid), "%" G_GINT64_FORMAT, uid);
+  internal_chapter = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, s_uid);
+  gst_toc_entry_append_sub_entry (internal_toc_entry, internal_chapter);
+
+  cur_sub_chap = gst_toc_entry_get_sub_entries (toc_entry);
+  while (cur_sub_chap != NULL && *index < last_offset) {
+    check_chapter (cur_sub_chap->data, internal_chapter, info,
+        index, last_offset);
+    cur_sub_chap = cur_sub_chap->next;
+  }
+
+  fail_unless (cur_sub_chap == NULL);
+}
+
+/* Check the reference toc against the muxed buffer */
+static void
+check_toc (GstToc * ref_toc, GstToc * internal_toc,
+    GstMapInfo * info, gsize * index)
+{
+  guint64 len, value, uid;
+  gchar s_uid[32];
+  gint last_offset;
+  GList *cur_entry, *cur_chapter;
+  GstTocEntry *internal_edition;
+
+  guint8 edition_entry[2] = { 0x45, 0xb9 };
+  guint8 edition_uid[2] = { 0x45, 0xbc };
+  guint8 edition_flag_hidden[2] = { 0x45, 0xbd };
+  guint8 edition_flag_default[2] = { 0x45, 0xdb };
+  guint8 edition_flag_ordered[2] = { 0x45, 0xdd };
+
+  /* edition entry */
+  fail_unless (check_id (info, index, edition_entry,
+          sizeof (edition_entry), &len));
+  last_offset = *index + (gint) len;
+
+  cur_entry = gst_toc_get_entries (ref_toc);
+  while (cur_entry != NULL && *index < last_offset) {
+    uid = 0;
+    check_id_read_int (info, index, edition_uid, sizeof (edition_uid), &uid);
+    g_snprintf (s_uid, sizeof (s_uid), "%" G_GINT64_FORMAT, uid);
+    internal_edition = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_EDITION, s_uid);
+    gst_toc_append_entry (internal_toc, internal_edition);
+
+    fail_unless (check_id_read_int (info, index, edition_flag_hidden,
+            sizeof (edition_flag_hidden), &value));
+
+    fail_unless (check_id_read_int (info, index, edition_flag_default,
+            sizeof (edition_flag_default), &value));
+
+    /* optional */
+    check_id_read_int (info, index, edition_flag_ordered,
+        sizeof (edition_flag_ordered), &value);
+
+    cur_chapter = gst_toc_entry_get_sub_entries (cur_entry->data);
+    while (cur_chapter != NULL && *index < last_offset) {
+      check_chapter (cur_chapter->data, internal_edition, info,
+          index, last_offset);
+      cur_chapter = cur_chapter->next;
+    }
+    fail_unless (cur_chapter == NULL);
+
+    cur_entry = cur_entry->next;
+  }
+
+  fail_unless (cur_entry == NULL && *index == last_offset);
+}
+
+static GstTocEntry *
+find_toc_entry (GstTocEntry * ref_toc_entry, GstTocEntry * internal_toc_entry,
+    guint64 uid)
+{
+  GList *cur_ref_entry, *cur_internal_entry;
+  guint64 internal_uid;
+  GstTocEntry *result = NULL;
+
+  internal_uid = g_ascii_strtoull (gst_toc_entry_get_uid (internal_toc_entry),
+      NULL, 10);
+  if (uid == internal_uid) {
+    result = ref_toc_entry;
+  } else {
+    cur_ref_entry = gst_toc_entry_get_sub_entries (ref_toc_entry);
+    cur_internal_entry = gst_toc_entry_get_sub_entries (internal_toc_entry);
+    while (cur_ref_entry != NULL && cur_internal_entry != NULL) {
+      result = find_toc_entry (cur_ref_entry->data, cur_internal_entry->data,
+          uid);
+
+      if (result != NULL) {
+        break;
+      }
+
+      cur_ref_entry = cur_ref_entry->next;
+      cur_internal_entry = cur_internal_entry->next;
+    }
+  }
+
+  return result;
+}
+
+static void
+find_and_check_tags (GstToc * ref_toc, GstToc * internal_toc, GstMapInfo * info,
+    guint64 uid, gchar * tag_name, gchar * tag_string)
+{
+  GList *cur_ref_entry, *cur_internal_entry;
+  GstTocEntry *ref_toc_entry = NULL;
+  GstTagList *tags;
+  const gchar *tag_type;
+  gchar *cur_tag_string;
+
+  /* find the reference toc entry matching the UID */
+  cur_ref_entry = gst_toc_get_entries (ref_toc);
+  cur_internal_entry = gst_toc_get_entries (internal_toc);
+  while (cur_ref_entry != NULL && cur_internal_entry != NULL) {
+    ref_toc_entry = find_toc_entry (cur_ref_entry->data,
+        cur_internal_entry->data, uid);
+
+    if (ref_toc_entry != NULL) {
+      break;
+    }
+
+    cur_ref_entry = cur_ref_entry->next;
+    cur_internal_entry = cur_internal_entry->next;
+  }
+
+  fail_unless (ref_toc_entry != NULL);
+
+  if (g_strcmp0 (tag_name, "ARTIST") == 0) {
+    tag_type = GST_TAG_ARTIST;
+  } else if (g_strcmp0 (tag_name, "COMMENTS") == 0) {
+    tag_type = GST_TAG_COMMENT;
+  } else {
+    tag_type = NULL;
+  }
+
+  fail_unless (tag_type != NULL);
+
+  tags = gst_toc_entry_get_tags (ref_toc_entry);
+  fail_unless (gst_tag_list_get_tag_size (tags, tag_type) > 0);
+  gst_tag_list_get_string_index (tags, tag_type, 0, &cur_tag_string);
+  fail_unless (g_strcmp0 (cur_tag_string, tag_string) == 0);
+}
+
+static void
+check_tags (GstToc * ref_toc, GstToc * internal_toc,
+    GstMapInfo * info, gsize * index)
+{
+  gboolean found_tags = FALSE, must_check_tag = FALSE;
+  guint64 len, value, uid;
+  gsize last_offset, next_tag;
+  gchar *tag_name_str, *tag_string_str;
+  guint8 tags[4] = { 0x12, 0x54, 0xc3, 0x67 };
+  guint8 tag[2] = { 0x73, 0x73 };
+  guint8 tag_targets[2] = { 0x63, 0xc0 };
+  guint8 tag_target_type_value[2] = { 0x68, 0xca };
+  guint8 tag_target_type[2] = { 0x63, 0xca };
+  guint8 tag_edition_uid[2] = { 0x63, 0xc9 };
+  guint8 tag_chapter_uid[2] = { 0x63, 0xc4 };
+  guint8 simple_tag[2] = { 0x67, 0xc8 };
+  guint8 tag_name[2] = { 0x45, 0xa3 };
+  guint8 tag_string[2] = { 0x44, 0x87 };
+
+  if (info->size > *index + sizeof (tags)) {
+    for (; *index < info->size - sizeof (tags); ++(*index)) {
+      if (memcmp (info->data + *index, tags, sizeof (tags)) == 0) {
+        *index += sizeof (tags);
+
+        len = read_length (info, index);
+        last_offset = *index + len;
+
+        found_tags = TRUE;
+        break;
+      }
+    }
+  }
+
+  fail_unless (found_tags);
+
+  while (*index < last_offset) {
+    fail_unless (check_id (info, index, tag, sizeof (tag), &len));
+    next_tag = *index + len;
+
+    fail_unless (check_id (info, index, tag_targets,
+            sizeof (tag_targets), &len));
+
+    must_check_tag = FALSE;
+    check_id_read_int (info, index, tag_target_type_value,
+        sizeof (tag_target_type_value), &value);
+
+    if (check_id (info, index, tag_target_type, sizeof (tag_target_type), &len)) {
+      *index += len;
+    }
+
+    if (check_id_read_int (info, index, tag_chapter_uid,
+            sizeof (tag_chapter_uid), &uid)) {
+      must_check_tag = TRUE;
+    } else if (check_id_read_int (info, index, tag_edition_uid,
+            sizeof (tag_edition_uid), &uid)) {
+      must_check_tag = TRUE;
+    }
+
+    if (must_check_tag) {
+      fail_unless (check_id (info, index, simple_tag,
+              sizeof (simple_tag), &len));
+
+      fail_unless (check_id (info, index, tag_name, sizeof (tag_name), &len));
+      tag_name_str = g_strndup ((gchar *) info->data + *index, len);
+      *index += len;
+
+      fail_unless (check_id (info, index, tag_string, sizeof (tag_string),
+              &len));
+      tag_string_str = g_strndup ((gchar *) info->data + *index, len);
+      *index += len;
+
+      find_and_check_tags (ref_toc, internal_toc, info, uid,
+          tag_name_str, tag_string_str);
+
+      g_free (tag_name_str);
+      g_free (tag_string_str);
+    }
+
+    *index = next_tag;
+  }
+}
+
+static void
+check_segment (GstToc * ref_toc, GstToc * internal_toc,
+    GstMapInfo * info, gsize * index)
+{
+  guint8 matroska_segment[4] = { 0x18, 0x53, 0x80, 0x67 };
+  guint8 matroska_seek_id_chapters[7] = { 0x53, 0xab, 0x84,
+    0x10, 0x43, 0xA7, 0x70
+  };
+  guint8 matroska_seek_id_tags[7] = { 0x53, 0xab, 0x84,
+    0x12, 0x54, 0xc3, 0x67
+  };
+  guint8 matroska_seek_pos[2] = { 0x53, 0xac };
+  guint8 matroska_chapters[4] = { 0x10, 0x43, 0xA7, 0x70 };
+
+  guint64 len, value, segment_offset, chapters_offset, tags_offset;
+  gboolean found_chapters_declaration = FALSE, found_tags_declaration = FALSE;
+
+  /* Segment */
+  fail_unless (info->size > sizeof (matroska_segment));
+  fail_unless (check_id (info, index, matroska_segment,
+          sizeof (matroska_segment), &len));
+
+  segment_offset = *index;
+
+  /* Search chapter declaration in seek head */
+  for (; *index < len - sizeof (matroska_seek_id_chapters); ++(*index)) {
+    if (memcmp (info->data + *index, matroska_seek_id_chapters,
+            sizeof (matroska_seek_id_chapters)) == 0) {
+      *index += sizeof (matroska_seek_id_chapters);
+
+      if (check_id_read_int (info, index, matroska_seek_pos,
+              sizeof (matroska_seek_pos), &value)) {
+        /* found chapter declaration */
+        found_chapters_declaration = TRUE;
+        chapters_offset = segment_offset + value;
+        break;
+      }
+    }
+  }
+
+  fail_unless (found_chapters_declaration);
+
+  *index = chapters_offset;
+  if (check_id (info, index, matroska_chapters,
+          sizeof (matroska_chapters), &len)) {
+    check_toc (ref_toc, internal_toc, info, index);
+  }
+
+  /* Search tags declaration in seek head */
+  for (*index = segment_offset; *index < len - sizeof (matroska_seek_id_tags);
+      ++(*index)) {
+    if (memcmp (info->data + *index, matroska_seek_id_tags,
+            sizeof (matroska_seek_id_tags)) == 0) {
+      *index += sizeof (matroska_seek_id_tags);
+
+      if (check_id_read_int (info, index, matroska_seek_pos,
+              sizeof (matroska_seek_pos), &value)) {
+        /* found tags declaration */
+        found_tags_declaration = TRUE;
+        tags_offset = segment_offset + value;
+        break;
+      }
+    }
+  }
+
+  fail_unless (found_tags_declaration);
+
+  *index = tags_offset;
+  check_tags (ref_toc, internal_toc, info, index);
+}
+
+static void
+test_toc (gboolean with_edition)
+{
+  GstElement *matroskamux;
+  GstBuffer *inbuffer, *outbuffer, *merged_buffer;
+  GstMapInfo info;
+  GstCaps *caps;
+  int num_buffers, i;
+  guint64 len;
+  gsize index;
+  GstTocSetter *toc_setter;
+  GstToc *test_toc, *ref_toc, *internal_toc;
+
+  guint8 ebml_header[4] = { 0x1a, 0x45, 0xdf, 0xa3 };
+
+  matroskamux = setup_matroskamux (&srcac3template);
+  downstream_is_seekable = TRUE;
+  gst_pad_set_query_function (mysinkpad, matroskamux_sinkpad_query);
+
+  toc_setter = GST_TOC_SETTER (matroskamux);
+  fail_unless (toc_setter != NULL);
+
+  if (with_edition) {
+    test_toc = new_reference_toc ();
+  } else {
+    test_toc = new_no_edition_toc ();
+  }
+  gst_toc_setter_set_toc (toc_setter, test_toc);
+  gst_toc_unref (test_toc);
+
+  caps = gst_caps_from_string (srcac3template.static_caps.string);
+  gst_check_setup_events (mysrcpad, matroskamux, caps, GST_FORMAT_TIME);
+  gst_caps_unref (caps);
+
+  inbuffer = gst_buffer_new_and_alloc (1);
+  gst_buffer_memset (inbuffer, 0, 0, 1);
+  GST_BUFFER_TIMESTAMP (inbuffer) = 0;
+  GST_BUFFER_DURATION (inbuffer) = 1 * GST_MSECOND;
+  ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
+  fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
+
+  /* send eos to ensure everything is written */
+  fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()));
+
+  /* Merge buffers */
+  num_buffers = g_list_length (buffers);
+  merged_buffer = gst_buffer_new ();
+  for (i = 0; i < num_buffers; ++i) {
+    outbuffer = GST_BUFFER (buffers->data);
+    fail_if (outbuffer == NULL);
+    buffers = g_list_remove (buffers, outbuffer);
+
+    if (outbuffer->offset == gst_buffer_get_size (merged_buffer)) {
+      gst_buffer_append_memory (merged_buffer,
+          gst_buffer_get_all_memory (outbuffer));
+    } else {
+      fail_unless (gst_buffer_map (outbuffer, &info, GST_MAP_READ));
+      gst_buffer_fill (merged_buffer, outbuffer->offset, info.data, info.size);
+      gst_buffer_unmap (outbuffer, &info);
+    }
+
+    ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
+    gst_buffer_unref (outbuffer);
+    outbuffer = NULL;
+  }
+
+  fail_unless (gst_buffer_map (merged_buffer, &info, GST_MAP_READ));
+  index = 0;
+
+  fail_unless (check_id (&info, &index, ebml_header,
+          sizeof (ebml_header), &len));
+  /* skip header */
+  index += len;
+
+  ref_toc = new_reference_toc ();
+  internal_toc = gst_toc_new (GST_TOC_SCOPE_GLOBAL);
+  check_segment (ref_toc, internal_toc, &info, &index);
+  gst_toc_unref (internal_toc);
+  gst_toc_unref (ref_toc);
+
+  gst_buffer_unmap (merged_buffer, &info);
+
+  cleanup_matroskamux (matroskamux);
+  g_list_free (buffers);
+  buffers = NULL;
+}
+
+GST_START_TEST (test_toc_with_edition)
+{
+  test_toc (TRUE);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_toc_without_edition)
+{
+  test_toc (FALSE);
+}
+
+GST_END_TEST;
+
 static Suite *
 matroskamux_suite (void)
 {
@@ -475,7 +1138,8 @@
   tcase_add_test (tc_chain, test_block_group);
   tcase_add_test (tc_chain, test_reset);
   tcase_add_test (tc_chain, test_link_webmmux_webm_sink);
-
+  tcase_add_test (tc_chain, test_toc_with_edition);
+  tcase_add_test (tc_chain, test_toc_without_edition);
   return s;
 }