toc: add GstTocScope and require it in the constructor
[platform/upstream/gstreamer.git] / gst / gsttoc.c
1 /* GStreamer
2  * (c) 2010, 2012 Alexander Saprykin <xelfium@gmail.com>
3  *
4  * gsttoc.c: GstToc initialization and parsing/creation
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /**
23  * SECTION:gsttoc
24  * @short_description: Generic table of contents support
25  * @see_also: #GstStructure, #GstEvent, #GstMessage, #GstQuery
26  *
27  * #GstToc functions are used to create/free #GstToc and #GstTocEntry structures.
28  * Also they are used to convert #GstToc into #GstStructure and vice versa.
29  *
30  * #GstToc lets you to inform other elements in pipeline or application that playing
31  * source has some kind of table of contents (TOC). These may be chapters, editions,
32  * angles or other types. For example: DVD chapters, Matroska chapters or cue sheet
33  * TOC. Such TOC will be useful for applications to display instead of just a
34  * playlist.
35  *
36  * Using TOC is very easy. Firstly, create #GstToc structure which represents root
37  * contents of the source. You can also attach TOC-specific tags to it. Then fill
38  * it with #GstTocEntry entries by appending them to #GstToc.entries #GstTocEntry.subentries
39  * lists. You should use GST_TOC_ENTRY_TYPE_CHAPTER for generic TOC entry and
40  * GST_TOC_ENTRY_TYPE_EDITION for the entries which are considered to be alternatives
41  * (like DVD angles, Matroska editions and so on).
42  *
43  * Note that root level of the TOC can contain only either editions or chapters. You
44  * should not mix them together at the same level. Otherwise you will get serialization
45  * /deserialization errors. Make sure that no one of the entries has negative start and
46  *  stop values.
47  *
48  * Please, use #GstToc.info and #GstTocEntry.info fields in that way: create a #GstStructure,
49  * put all info related to your element there and put this structure into the info field under
50  * the name of your element. Some fields in the info structure can be used for internal purposes,
51  * so you should use it in the way described above to not to overwrite already existent fields.
52  *
53  * Use gst_event_new_toc() to create a new TOC #GstEvent, and gst_event_parse_toc() to
54  * parse received TOC event. Use gst_event_new_toc_select() to create a new TOC select #GstEvent,
55  * and gst_event_parse_toc_select() to parse received TOC select event. The same rule for
56  * the #GstMessage: gst_message_new_toc() to create new TOC #GstMessage, and
57  * gst_message_parse_toc() to parse received TOC message. Also you can create a new TOC query
58  * with gst_query_new_toc(), set it with gst_query_set_toc() and parse it with
59  * gst_query_parse_toc().
60  */
61
62 #ifdef HAVE_CONFIG_H
63 #  include "config.h"
64 #endif
65
66 #include "gst_private.h"
67 #include "gstenumtypes.h"
68 #include "gsttaglist.h"
69 #include "gststructure.h"
70 #include "gstvalue.h"
71 #include "gsttoc.h"
72 #include "gstpad.h"
73 #include "gstquark.h"
74
75 struct _GstTocEntry
76 {
77   GstMiniObject mini_object;
78
79   GstToc *toc;
80   GstTocEntry *parent;
81
82   gchar *uid;
83   GstTocEntryType type;
84   GstClockTime start, stop;
85   GList *subentries;
86   GstTagList *tags;
87 };
88
89 struct _GstToc
90 {
91   GstMiniObject mini_object;
92
93   GstTocScope scope;
94   GList *entries;
95   GstTagList *tags;
96 };
97
98 #undef gst_toc_copy
99 static GstToc *gst_toc_copy (const GstToc * toc);
100 static void gst_toc_free (GstToc * toc);
101 #undef gst_toc_entry_copy
102 static GstTocEntry *gst_toc_entry_copy (const GstTocEntry * toc);
103 static void gst_toc_entry_free (GstTocEntry * toc);
104
105 GST_DEFINE_MINI_OBJECT_TYPE (GstToc, gst_toc);
106 GST_DEFINE_MINI_OBJECT_TYPE (GstTocEntry, gst_toc_entry);
107
108 /**
109  * gst_toc_new:
110  * @scope: scope of this TOC
111  *
112  * Create a new #GstToc structure.
113  *
114  * Returns: (transfer full): newly allocated #GstToc structure, free it
115  *     with gst_toc_unref().
116  */
117 GstToc *
118 gst_toc_new (GstTocScope scope)
119 {
120   GstToc *toc;
121
122   g_return_val_if_fail (scope == GST_TOC_SCOPE_GLOBAL ||
123       scope == GST_TOC_SCOPE_CURRENT, NULL);
124
125   toc = g_slice_new0 (GstToc);
126
127   gst_mini_object_init (GST_MINI_OBJECT_CAST (toc), 0, GST_TYPE_TOC,
128       (GstMiniObjectCopyFunction) gst_toc_copy, NULL,
129       (GstMiniObjectFreeFunction) gst_toc_free);
130
131   toc->scope = scope;
132   toc->tags = gst_tag_list_new_empty ();
133
134   return toc;
135 }
136
137 /**
138  * gst_toc_get_scope:
139  * @toc: a #GstToc instance
140  *
141  * Returns: scope of @toc
142  */
143 GstTocScope
144 gst_toc_get_scope (const GstToc * toc)
145 {
146   g_return_val_if_fail (toc != NULL, GST_TOC_SCOPE_GLOBAL);
147
148   return toc->scope;
149 }
150
151 /**
152  * gst_toc_set_tags:
153  * @toc: A #GstToc instance
154  * @tags: (allow-none) (transfer full): A #GstTagList or %NULL
155  *
156  * Set a #GstTagList with tags for the complete @toc.
157  */
158 void
159 gst_toc_set_tags (GstToc * toc, GstTagList * tags)
160 {
161   g_return_if_fail (toc != NULL);
162   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (toc)));
163
164   if (toc->tags)
165     gst_tag_list_unref (toc->tags);
166   toc->tags = tags;
167 }
168
169 /**
170  * gst_toc_merge_tags:
171  * @toc: A #GstToc instance
172  * @tags: (allow-none): A #GstTagList or %NULL
173  * @mode: A #GstTagMergeMode
174  *
175  * Merge @tags into the existing tags of @toc using @mode.
176  */
177 void
178 gst_toc_merge_tags (GstToc * toc, GstTagList * tags, GstTagMergeMode mode)
179 {
180   g_return_if_fail (toc != NULL);
181   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (toc)));
182
183   if (!toc->tags) {
184     toc->tags = gst_tag_list_ref (tags);
185   } else {
186     GstTagList *tmp = gst_tag_list_merge (toc->tags, tags, mode);
187     gst_tag_list_unref (toc->tags);
188     toc->tags = tmp;
189   }
190 }
191
192 /**
193  * gst_toc_get_tags:
194  * @toc: A #GstToc instance
195  *
196  * Gets the tags for @toc.
197  *
198  * Returns: (transfer none): A #GstTagList for @entry
199  */
200 GstTagList *
201 gst_toc_get_tags (const GstToc * toc)
202 {
203   g_return_val_if_fail (toc != NULL, NULL);
204
205   return toc->tags;
206 }
207
208 /**
209  * gst_toc_append_entry:
210  * @toc: A #GstToc instance
211  * @entry: (transfer full): A #GstTocEntry
212  *
213  * Appends the #GstTocEntry @entry to @toc.
214  */
215 void
216 gst_toc_append_entry (GstToc * toc, GstTocEntry * entry)
217 {
218   g_return_if_fail (toc != NULL);
219   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (toc)));
220   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (entry)));
221   g_return_if_fail (entry->toc == NULL);
222   g_return_if_fail (entry->parent == NULL);
223
224   toc->entries = g_list_append (toc->entries, entry);
225   entry->toc = toc;
226
227   GST_LOG ("appended %s entry with uid %s to toc %p",
228       gst_toc_entry_type_get_nick (entry->type), entry->uid, toc);
229
230   gst_toc_dump (toc);
231 }
232
233 /**
234  * gst_toc_get_entries:
235  * @toc: A #GstToc instance
236  *
237  * Gets the list of #GstTocEntry of @toc.
238  *
239  * Returns: (transfer none) (element-type Gst.TocEntry): A #GList of #GstTocEntry for @entry
240  */
241 GList *
242 gst_toc_get_entries (const GstToc * toc)
243 {
244   g_return_val_if_fail (toc != NULL, NULL);
245
246   return toc->entries;
247 }
248
249 static GstTocEntry *
250 gst_toc_entry_new_internal (GstTocEntryType type, const gchar * uid)
251 {
252   GstTocEntry *entry;
253
254   entry = g_slice_new0 (GstTocEntry);
255
256   gst_mini_object_init (GST_MINI_OBJECT_CAST (entry), 0, GST_TYPE_TOC_ENTRY,
257       (GstMiniObjectCopyFunction) gst_toc_entry_copy, NULL,
258       (GstMiniObjectFreeFunction) gst_toc_entry_free);
259
260   entry->uid = g_strdup (uid);
261   entry->type = type;
262   entry->tags = NULL;
263   entry->start = entry->stop = GST_CLOCK_TIME_NONE;
264
265   return entry;
266 }
267
268 /**
269  * gst_toc_entry_new:
270  * @type: entry type.
271  * @uid: unique ID (UID) in the whole TOC.
272  *
273  * Create new #GstTocEntry structure.
274  *
275  * Returns: newly allocated #GstTocEntry structure, free it with gst_toc_entry_unref().
276  */
277 GstTocEntry *
278 gst_toc_entry_new (GstTocEntryType type, const gchar * uid)
279 {
280   g_return_val_if_fail (uid != NULL, NULL);
281
282   return gst_toc_entry_new_internal (type, uid);
283 }
284
285 static void
286 gst_toc_free (GstToc * toc)
287 {
288   g_list_foreach (toc->entries, (GFunc) gst_mini_object_unref, NULL);
289   g_list_free (toc->entries);
290
291   if (toc->tags != NULL)
292     gst_tag_list_unref (toc->tags);
293
294   g_slice_free (GstToc, toc);
295 }
296
297 static void
298 gst_toc_entry_free (GstTocEntry * entry)
299 {
300   g_return_if_fail (entry != NULL);
301
302   g_list_foreach (entry->subentries, (GFunc) gst_mini_object_unref, NULL);
303   g_list_free (entry->subentries);
304
305   g_free (entry->uid);
306
307   if (entry->tags != NULL)
308     gst_tag_list_unref (entry->tags);
309
310   g_slice_free (GstTocEntry, entry);
311 }
312
313 static GstTocEntry *
314 gst_toc_entry_find_sub_entry (const GstTocEntry * entry, const gchar * uid)
315 {
316   GList *cur;
317   GstTocEntry *subentry, *subsubentry;
318
319   g_return_val_if_fail (entry != NULL, NULL);
320   g_return_val_if_fail (uid != NULL, NULL);
321
322   cur = entry->subentries;
323   while (cur != NULL) {
324     subentry = cur->data;
325
326     if (g_strcmp0 (subentry->uid, uid) == 0)
327       return subentry;
328
329     subsubentry = gst_toc_entry_find_sub_entry (subentry, uid);
330     if (subsubentry != NULL)
331       return subsubentry;
332
333     cur = cur->next;
334   }
335
336   return NULL;
337 }
338
339 /**
340  * gst_toc_find_entry:
341  * @toc: #GstToc to search in.
342  * @uid: UID to find #GstTocEntry with.
343  *
344  * Find #GstTocEntry with given @uid in the @toc.
345  *
346  * Returns: (transfer none): #GstTocEntry with specified @uid from the @toc, or NULL if not found.
347  */
348 GstTocEntry *
349 gst_toc_find_entry (const GstToc * toc, const gchar * uid)
350 {
351   GList *cur;
352   GstTocEntry *entry, *subentry;
353
354   g_return_val_if_fail (toc != NULL, NULL);
355   g_return_val_if_fail (uid != NULL, NULL);
356
357   cur = toc->entries;
358   while (cur != NULL) {
359     entry = cur->data;
360
361     if (g_strcmp0 (entry->uid, uid) == 0)
362       return entry;
363
364     subentry = gst_toc_entry_find_sub_entry (entry, uid);
365     if (subentry != NULL)
366       return subentry;
367     cur = cur->next;
368   }
369
370   return NULL;
371 }
372
373 /**
374  * gst_toc_entry_copy:
375  * @entry: #GstTocEntry to copy.
376  *
377  * Copy #GstTocEntry with all subentries (deep copy).
378  *
379  * Returns: newly allocated #GstTocEntry in case of success, NULL otherwise;
380  * free it when done with gst_toc_entry_unref().
381  */
382 static GstTocEntry *
383 gst_toc_entry_copy (const GstTocEntry * entry)
384 {
385   GstTocEntry *ret, *sub;
386   GstTagList *list;
387   GList *cur;
388
389   g_return_val_if_fail (entry != NULL, NULL);
390
391   ret = gst_toc_entry_new (entry->type, entry->uid);
392
393   ret->start = entry->start;
394   ret->stop = entry->stop;
395
396   if (GST_IS_TAG_LIST (entry->tags)) {
397     list = gst_tag_list_copy (entry->tags);
398     if (ret->tags)
399       gst_tag_list_unref (ret->tags);
400     ret->tags = list;
401   }
402
403   cur = entry->subentries;
404   while (cur != NULL) {
405     sub = gst_toc_entry_copy (cur->data);
406
407     if (sub != NULL)
408       ret->subentries = g_list_prepend (ret->subentries, sub);
409
410     cur = cur->next;
411   }
412   ret->subentries = g_list_reverse (ret->subentries);
413
414   return ret;
415 }
416
417 /**
418  * gst_toc_copy:
419  * @toc: #GstToc to copy.
420  *
421  * Copy #GstToc with all subentries (deep copy).
422  *
423  * Returns: newly allocated #GstToc in case of success, NULL otherwise;
424  * free it when done with gst_toc_free().
425  */
426 static GstToc *
427 gst_toc_copy (const GstToc * toc)
428 {
429   GstToc *ret;
430   GstTocEntry *entry;
431   GList *cur;
432   GstTagList *list;
433
434   g_return_val_if_fail (toc != NULL, NULL);
435
436   ret = gst_toc_new (toc->scope);
437
438   if (GST_IS_TAG_LIST (toc->tags)) {
439     list = gst_tag_list_copy (toc->tags);
440     gst_tag_list_unref (ret->tags);
441     ret->tags = list;
442   }
443
444   cur = toc->entries;
445   while (cur != NULL) {
446     entry = gst_toc_entry_copy (cur->data);
447
448     if (entry != NULL)
449       ret->entries = g_list_prepend (ret->entries, entry);
450
451     cur = cur->next;
452   }
453   ret->entries = g_list_reverse (ret->entries);
454   return ret;
455 }
456
457 /**
458  * gst_toc_entry_set_start_stop_times:
459  * @entry: #GstTocEntry to set values.
460  * @start: start value to set.
461  * @stop: stop value to set.
462  *
463  * Set @start and @stop values for the @entry.
464  */
465 void
466 gst_toc_entry_set_start_stop_times (GstTocEntry * entry, gint64 start,
467     gint64 stop)
468 {
469   g_return_if_fail (entry != NULL);
470
471   entry->start = start;
472   entry->stop = stop;
473 }
474
475 /**
476  * gst_toc_entry_get_start_stop_times:
477  * @entry: #GstTocEntry to get values from.
478  * @start: (out): the storage for the start value, leave #NULL if not need.
479  * @stop: (out): the storage for the stop value, leave #NULL if not need.
480  *
481  * Get start and stop values from the @entry and write them into appropriate storages.
482  *
483  * Returns: TRUE if all non-NULL storage pointers were filled with appropriate values,
484  * FALSE otherwise.
485  */
486 gboolean
487 gst_toc_entry_get_start_stop_times (const GstTocEntry * entry, gint64 * start,
488     gint64 * stop)
489 {
490   gboolean ret = TRUE;
491
492   g_return_val_if_fail (entry != NULL, FALSE);
493
494   if (start != NULL)
495     *start = entry->start;
496   if (stop != NULL)
497     *stop = entry->stop;
498
499   return ret;
500 }
501
502 /**
503  * gst_toc_entry_type_get_nick:
504  * @type: a #GstTocEntryType.
505  *
506  * Converts @type to a string representation.
507  *
508  * Returns: Returns a human-readable string for @type. This string is
509  *    only for debugging purpose and should not be displayed in a user
510  *    interface.
511  */
512 const gchar *
513 gst_toc_entry_type_get_nick (GstTocEntryType type)
514 {
515   switch (type) {
516     case GST_TOC_ENTRY_TYPE_ANGLE:
517       return "angle";
518     case GST_TOC_ENTRY_TYPE_VERSION:
519       return "version";
520     case GST_TOC_ENTRY_TYPE_EDITION:
521       return "edition";
522     case GST_TOC_ENTRY_TYPE_TITLE:
523       return "title";
524     case GST_TOC_ENTRY_TYPE_TRACK:
525       return "track";
526     case GST_TOC_ENTRY_TYPE_CHAPTER:
527       return "chapter";
528     default:
529       break;
530   }
531   return "invalid";
532 }
533
534 /**
535  * gst_toc_entry_get_entry_type:
536  * @entry: a #GstTocEntry
537  *
538  * Returns: @entry's entry type
539  */
540 GstTocEntryType
541 gst_toc_entry_get_entry_type (const GstTocEntry * entry)
542 {
543   g_return_val_if_fail (entry != NULL, GST_TOC_ENTRY_TYPE_INVALID);
544
545   return entry->type;
546 }
547
548 /**
549  * gst_toc_entry_is_alternative:
550  * @entry: a #GstTocEntry
551  *
552  * Returns: %TRUE if @entry's type is an alternative type, otherwise %FALSE
553  */
554 gboolean
555 gst_toc_entry_is_alternative (const GstTocEntry * entry)
556 {
557   g_return_val_if_fail (entry != NULL, FALSE);
558
559   return GST_TOC_ENTRY_TYPE_IS_ALTERNATIVE (entry->type);
560 }
561
562 /**
563  * gst_toc_entry_is_sequence:
564  * @entry: a #GstTocEntry
565  *
566  * Returns: %TRUE if @entry's type is a sequence type, otherwise %FALSE
567  */
568 gboolean
569 gst_toc_entry_is_sequence (const GstTocEntry * entry)
570 {
571   g_return_val_if_fail (entry != NULL, FALSE);
572
573   return GST_TOC_ENTRY_TYPE_IS_SEQUENCE (entry->type);
574 }
575
576 /**
577  * gst_toc_entry_get_uid:
578  * @entry: A #GstTocEntry instance
579  *
580  * Gets the UID of @entry.
581  *
582  * Returns: (transfer none): The UID of @entry
583  */
584 const gchar *
585 gst_toc_entry_get_uid (const GstTocEntry * entry)
586 {
587   g_return_val_if_fail (entry != NULL, NULL);
588
589   return entry->uid;
590 }
591
592 /**
593  * gst_toc_entry_append_sub_entry:
594  * @entry: A #GstTocEntry instance
595  * @subentry: (transfer full): A #GstTocEntry
596  *
597  * Appends the #GstTocEntry @subentry to @entry.
598  */
599 void
600 gst_toc_entry_append_sub_entry (GstTocEntry * entry, GstTocEntry * subentry)
601 {
602   g_return_if_fail (entry != NULL);
603   g_return_if_fail (subentry != NULL);
604   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (entry)));
605   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST
606           (subentry)));
607   g_return_if_fail (subentry->toc == NULL);
608   g_return_if_fail (subentry->parent == NULL);
609
610   entry->subentries = g_list_append (entry->subentries, subentry);
611   subentry->toc = entry->toc;
612   subentry->parent = entry;
613
614   GST_LOG ("appended %s subentry with uid %s to entry %s",
615       gst_toc_entry_type_get_nick (subentry->type), subentry->uid, entry->uid);
616 }
617
618 /**
619  * gst_toc_entry_get_uid:
620  * @entry: A #GstTocEntry instance
621  *
622  * Gets the sub-entries of @entry.
623  *
624  * Returns: (transfer none) (element-type Gst.TocEntry): A #GList of #GstTocEntry of @entry
625  */
626 GList *
627 gst_toc_entry_get_sub_entries (const GstTocEntry * entry)
628 {
629   g_return_val_if_fail (entry != NULL, NULL);
630
631   return entry->subentries;
632 }
633
634 /**
635  * gst_toc_entry_set_tags:
636  * @entry: A #GstTocEntry instance
637  * @tags: (allow-none) (transfer full): A #GstTagList or %NULL
638  *
639  * Set a #GstTagList with tags for the complete @entry.
640  */
641 void
642 gst_toc_entry_set_tags (GstTocEntry * entry, GstTagList * tags)
643 {
644   g_return_if_fail (entry != NULL);
645   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (entry)));
646
647   if (entry->tags)
648     gst_tag_list_unref (entry->tags);
649   entry->tags = tags;
650 }
651
652 /**
653  * gst_toc_entry_merge_tags:
654  * @entry: A #GstTocEntry instance
655  * @tags: (allow-none): A #GstTagList or %NULL
656  * @mode: A #GstTagMergeMode
657  *
658  * Merge @tags into the existing tags of @entry using @mode.
659  */
660 void
661 gst_toc_entry_merge_tags (GstTocEntry * entry, GstTagList * tags,
662     GstTagMergeMode mode)
663 {
664   g_return_if_fail (entry != NULL);
665   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (entry)));
666
667   if (!entry->tags) {
668     entry->tags = gst_tag_list_ref (tags);
669   } else {
670     GstTagList *tmp = gst_tag_list_merge (entry->tags, tags, mode);
671     gst_tag_list_unref (entry->tags);
672     entry->tags = tmp;
673   }
674 }
675
676 /**
677  * gst_toc_entry_get_tags:
678  * @entry: A #GstTocEntry instance
679  *
680  * Gets the tags for @entry.
681  *
682  * Returns: (transfer none): A #GstTagList for @entry
683  */
684 GstTagList *
685 gst_toc_entry_get_tags (const GstTocEntry * entry)
686 {
687   g_return_val_if_fail (entry != NULL, NULL);
688
689   return entry->tags;
690 }
691
692 /**
693  * gst_toc_entry_get_toc:
694  * @entry: A #GstTocEntry instance
695  *
696  * Gets the parent #GstToc of @entry.
697  *
698  * Returns: (transfer none): The parent #GstToc of @entry
699  */
700 GstToc *
701 gst_toc_entry_get_toc (GstTocEntry * entry)
702 {
703   g_return_val_if_fail (entry != NULL, NULL);
704
705   return entry->toc;
706 }
707
708 /**
709  * gst_toc_entry_get_parent:
710  * @entry: A #GstTocEntry instance
711  *
712  * Gets the parent #GstTocEntry of @entry.
713  *
714  * Returns: (transfer none): The parent #GstTocEntry of @entry
715  */
716 GstTocEntry *
717 gst_toc_entry_get_parent (GstTocEntry * entry)
718 {
719   g_return_val_if_fail (entry != NULL, NULL);
720
721   return entry->parent;
722 }
723
724 #ifndef GST_DISABLE_GST_DEBUG
725 static void
726 gst_toc_dump_entries (GList * entries, guint depth)
727 {
728   GList *e;
729   gchar *indent;
730
731   indent = g_malloc0 (depth + 1);
732   memset (indent, ' ', depth);
733   for (e = entries; e != NULL; e = e->next) {
734     GstTocEntry *entry = e->data;
735
736     GST_TRACE ("%s+ %s (%s), %" GST_TIME_FORMAT " - %" GST_TIME_FORMAT ", "
737         "tags: %" GST_PTR_FORMAT, indent, entry->uid,
738         gst_toc_entry_type_get_nick (entry->type),
739         GST_TIME_ARGS (entry->start), GST_TIME_ARGS (entry->stop), entry->tags);
740
741     if (entry->subentries != NULL)
742       gst_toc_dump_entries (entry->subentries, depth + 2);
743   }
744   g_free (indent);
745 }
746 #endif
747
748 void
749 gst_toc_dump (GstToc * toc)
750 {
751 #ifndef GST_DISABLE_GST_DEBUG
752   GST_TRACE ("        Toc %p, scope: %s, tags: %" GST_PTR_FORMAT, toc,
753       (toc->scope == GST_TOC_SCOPE_GLOBAL) ? "global" : "current", toc->tags);
754   gst_toc_dump_entries (toc->entries, 2);
755 #endif
756 }