5fabd5ea5073b48c4f904b5b8ea3f7a3aaf655bd
[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, #GstPad
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 /**
76  * gst_toc_new:
77  *
78  * Create new #GstToc structure.
79  *
80  * Returns: newly allocated #GstToc structure, free it with gst_toc_free().
81  *
82  * Since: 0.10.37
83  */
84 GstToc *
85 gst_toc_new (void)
86 {
87   GstToc *toc;
88
89   toc = g_slice_new0 (GstToc);
90   toc->tags = gst_tag_list_new_empty ();
91   toc->info = gst_structure_new_id_empty (GST_QUARK (INFO_STRUCTURE));
92
93   return toc;
94 }
95
96 /**
97  * gst_toc_entry_new:
98  * @type: entry type.
99  * @uid: unique ID (UID) in the whole TOC.
100  *
101  * Create new #GstTocEntry structure.
102  *
103  * Returns: newly allocated #GstTocEntry structure, free it with gst_toc_entry_free().
104  *
105  * Since: 0.10.37
106  */
107 GstTocEntry *
108 gst_toc_entry_new (GstTocEntryType type, const gchar * uid)
109 {
110   GstTocEntry *entry;
111
112   g_return_val_if_fail (uid != NULL, NULL);
113
114   entry = g_slice_new0 (GstTocEntry);
115   entry->uid = g_strdup (uid);
116   entry->type = type;
117   entry->tags = gst_tag_list_new_empty ();
118   entry->info = gst_structure_new_id_empty (GST_QUARK (INFO_STRUCTURE));
119
120   return entry;
121 }
122
123 /**
124  * gst_toc_entry_new_with_pad:
125  * @type: entry type.
126  * @uid: unique ID (UID) in the whole TOC.
127  * @pad: #GstPad related to this entry.
128  *
129  * Create new #GstTocEntry structure with #GstPad related.
130  *
131  * Returns: newly allocated #GstTocEntry structure, free it with gst_toc_entry_free()
132  * when done.
133  *
134  * Since: 0.10.37
135  */
136 GstTocEntry *
137 gst_toc_entry_new_with_pad (GstTocEntryType type, const gchar * uid,
138     gpointer pad)
139 {
140   GstTocEntry *entry;
141
142   g_return_val_if_fail (uid != NULL, NULL);
143
144   entry = g_slice_new0 (GstTocEntry);
145   entry->uid = g_strdup (uid);
146   entry->type = type;
147   entry->tags = gst_tag_list_new_empty ();
148   entry->info = gst_structure_new_id_empty (GST_QUARK (INFO_STRUCTURE));
149
150   if (pad != NULL && GST_IS_PAD (pad))
151     entry->pads = g_list_append (entry->pads, gst_object_ref (pad));
152
153   return entry;
154 }
155
156 /**
157  * gst_toc_free:
158  * @toc: #GstToc structure to free.
159  *
160  * Free unused #GstToc structure.
161  *
162  * Since: 0.10.37
163  */
164 void
165 gst_toc_free (GstToc * toc)
166 {
167   g_return_if_fail (toc != NULL);
168
169   g_list_foreach (toc->entries, (GFunc) gst_toc_entry_free, NULL);
170   g_list_free (toc->entries);
171
172   if (toc->tags != NULL)
173     gst_tag_list_free (toc->tags);
174
175   if (toc->info != NULL)
176     gst_structure_free (toc->info);
177
178   g_slice_free (GstToc, toc);
179 }
180
181 /**
182  * gst_toc_entry_free:
183  * @entry: #GstTocEntry structure to free.
184  *
185  * Free unused #GstTocEntry structure. Note that #GstTocEntry.uid will
186  * be freed with g_free() and all #GstPad objects in the #GstTocEntry.pads
187  * list will be unrefed with gst_object_unref().
188  *
189  * Since: 0.10.37
190  */
191 void
192 gst_toc_entry_free (GstTocEntry * entry)
193 {
194   GList *cur;
195
196   g_return_if_fail (entry != NULL);
197
198   g_list_foreach (entry->subentries, (GFunc) gst_toc_entry_free, NULL);
199   g_list_free (entry->subentries);
200
201   g_free (entry->uid);
202
203   if (entry->tags != NULL)
204     gst_tag_list_free (entry->tags);
205
206   if (entry->info != NULL)
207     gst_structure_free (entry->info);
208
209   cur = entry->pads;
210   while (cur != NULL) {
211     if (GST_IS_PAD (cur->data))
212       gst_object_unref (cur->data);
213     cur = cur->next;
214   }
215
216   g_list_free (entry->pads);
217
218   g_slice_free (GstTocEntry, entry);
219 }
220
221 static GstStructure *
222 gst_toc_structure_new (GstTagList * tags, GstStructure * info)
223 {
224   GstStructure *ret;
225   GValue val = { 0 };
226
227   ret = gst_structure_new_id_empty (GST_QUARK (TOC));
228
229   if (tags != NULL) {
230     g_value_init (&val, GST_TYPE_STRUCTURE);
231     gst_value_set_structure (&val, GST_STRUCTURE (tags));
232     gst_structure_id_set_value (ret, GST_QUARK (TAGS), &val);
233     g_value_unset (&val);
234   }
235
236   if (info != NULL) {
237     g_value_init (&val, GST_TYPE_STRUCTURE);
238     gst_value_set_structure (&val, info);
239     gst_structure_id_set_value (ret, GST_QUARK (INFO), &val);
240     g_value_unset (&val);
241   }
242
243   return ret;
244 }
245
246 static GstStructure *
247 gst_toc_entry_structure_new (GstTocEntryType type, const gchar * uid,
248     GstTagList * tags, GstStructure * info)
249 {
250   GValue val = { 0 };
251   GstStructure *ret;
252
253   ret = gst_structure_new_id_empty (GST_QUARK (TOC_ENTRY));
254
255   gst_structure_id_set (ret, GST_QUARK (TYPE), GST_TYPE_TOC_ENTRY_TYPE, type,
256       NULL);
257
258   g_value_init (&val, G_TYPE_STRING);
259   g_value_set_string (&val, uid);
260   gst_structure_id_set_value (ret, GST_QUARK (UID), &val);
261   g_value_unset (&val);
262
263   if (tags != NULL) {
264     g_value_init (&val, GST_TYPE_STRUCTURE);
265     gst_value_set_structure (&val, GST_STRUCTURE (tags));
266     gst_structure_id_set_value (ret, GST_QUARK (TAGS), &val);
267     g_value_unset (&val);
268   }
269
270   if (info != NULL) {
271     g_value_init (&val, GST_TYPE_STRUCTURE);
272     gst_value_set_structure (&val, info);
273     gst_structure_id_set_value (ret, GST_QUARK (INFO), &val);
274     g_value_unset (&val);
275   }
276
277   return ret;
278 }
279
280 static guint
281 gst_toc_entry_structure_n_subentries (const GstStructure * entry)
282 {
283   if (G_UNLIKELY (!gst_structure_id_has_field_typed (entry,
284               GST_QUARK (SUB_ENTRIES), GST_TYPE_ARRAY)))
285     return 0;
286   else
287     return gst_value_array_get_size ((gst_structure_id_get_value (entry,
288                 GST_QUARK (SUB_ENTRIES))));
289 }
290
291 static const GstStructure *
292 gst_toc_entry_structure_nth_subentry (const GstStructure * entry, guint nth)
293 {
294   guint count;
295   const GValue *array;
296
297   count = gst_toc_entry_structure_n_subentries (entry);
298
299   if (count < nth)
300     return NULL;
301
302   if (G_UNLIKELY (!gst_structure_id_has_field_typed (entry,
303               GST_QUARK (SUB_ENTRIES), GST_TYPE_ARRAY)))
304     return NULL;
305   else {
306     array =
307         gst_value_array_get_value (gst_structure_id_get_value (entry,
308             GST_QUARK (SUB_ENTRIES)), nth);
309     return gst_value_get_structure (array);
310   }
311 }
312
313 static GstTocEntry *
314 gst_toc_entry_from_structure (const GstStructure * entry, guint level)
315 {
316   GstTocEntry *ret, *subentry;
317   const GValue *val;
318   const GstStructure *subentry_struct;
319   GstTagList *list;
320   GstStructure *st;
321   gint count, i;
322   const gchar *uid;
323   guint chapters_count = 0, editions_count = 0;
324
325   g_return_val_if_fail (entry != NULL, NULL);
326   g_return_val_if_fail (gst_structure_id_has_field_typed (entry,
327           GST_QUARK (UID), G_TYPE_STRING), NULL);
328   g_return_val_if_fail (gst_structure_id_has_field_typed (entry,
329           GST_QUARK (TYPE), GST_TYPE_TOC_ENTRY_TYPE), NULL);
330
331   val = gst_structure_id_get_value (entry, GST_QUARK (UID));
332   uid = g_value_get_string (val);
333
334   ret = gst_toc_entry_new (GST_TOC_ENTRY_TYPE_CHAPTER, uid);
335
336   gst_structure_get_enum (entry, g_quark_to_string (GST_QUARK (TYPE)),
337       GST_TYPE_TOC_ENTRY_TYPE, (gint *) & (ret->type));
338
339   if (gst_structure_id_has_field_typed (entry,
340           GST_QUARK (SUB_ENTRIES), GST_TYPE_ARRAY)) {
341     count = gst_toc_entry_structure_n_subentries (entry);
342
343     for (i = 0; i < count; ++i) {
344       subentry_struct = gst_toc_entry_structure_nth_subentry (entry, i);
345       subentry = gst_toc_entry_from_structure (subentry_struct, level + 1);
346
347       /* skip empty editions */
348       if (G_UNLIKELY (subentry->type == GST_TOC_ENTRY_TYPE_EDITION
349               && subentry->subentries == NULL)) {
350         g_warning
351             ("Empty edition found while deserializing TOC from GstStructure, skipping");
352         continue;
353       }
354
355       if (subentry->type == GST_TOC_ENTRY_TYPE_EDITION)
356         ++editions_count;
357       else
358         ++chapters_count;
359
360       /* check for mixed content */
361       if (G_UNLIKELY (chapters_count > 0 && editions_count > 0)) {
362         g_critical
363             ("Mixed editions and chapters in the TOC contents, the TOC is broken");
364         gst_toc_entry_free (subentry);
365         gst_toc_entry_free (ret);
366         return NULL;
367       }
368
369       if (G_UNLIKELY (subentry == NULL)) {
370         gst_toc_entry_free (ret);
371         return NULL;
372       }
373
374       ret->subentries = g_list_prepend (ret->subentries, subentry);
375     }
376
377     ret->subentries = g_list_reverse (ret->subentries);
378   }
379
380   if (gst_structure_id_has_field_typed (entry,
381           GST_QUARK (TAGS), GST_TYPE_STRUCTURE)) {
382     val = gst_structure_id_get_value (entry, GST_QUARK (TAGS));
383
384     if (G_LIKELY (GST_IS_TAG_LIST (gst_value_get_structure (val)))) {
385       list = gst_tag_list_copy (GST_TAG_LIST (gst_value_get_structure (val)));
386       gst_tag_list_free (ret->tags);
387       ret->tags = list;
388     }
389   }
390
391   if (gst_structure_id_has_field_typed (entry,
392           GST_QUARK (INFO), GST_TYPE_STRUCTURE)) {
393     val = gst_structure_id_get_value (entry, GST_QUARK (INFO));
394
395     if (G_LIKELY (GST_IS_STRUCTURE (gst_value_get_structure (val)))) {
396       st = gst_structure_copy (gst_value_get_structure (val));
397       gst_structure_free (ret->info);
398       ret->info = st;
399     }
400   }
401
402   return ret;
403 }
404
405 GstToc *
406 __gst_toc_from_structure (const GstStructure * toc)
407 {
408   GstToc *ret;
409   GstTocEntry *subentry;
410   const GstStructure *subentry_struct;
411   const GValue *val;
412   GstTagList *list;
413   GstStructure *st;
414   guint count, i;
415   guint editions_count = 0, chapters_count = 0;
416
417   g_return_val_if_fail (toc != NULL, NULL);
418
419   ret = gst_toc_new ();
420
421   if (gst_structure_id_has_field_typed (toc,
422           GST_QUARK (SUB_ENTRIES), GST_TYPE_ARRAY)) {
423     count = gst_toc_entry_structure_n_subentries (toc);
424
425     for (i = 0; i < count; ++i) {
426       subentry_struct = gst_toc_entry_structure_nth_subentry (toc, i);
427       subentry = gst_toc_entry_from_structure (subentry_struct, 0);
428
429       /* skip empty editions */
430       if (G_UNLIKELY (subentry->type == GST_TOC_ENTRY_TYPE_EDITION
431               && subentry->subentries == NULL)) {
432         g_warning
433             ("Empty edition found while deserializing TOC from GstStructure, skipping");
434         continue;
435       }
436
437       /* check for success */
438       if (G_UNLIKELY (subentry == NULL)) {
439         g_critical ("Couldn't serialize deserializing TOC from GstStructure");
440         gst_toc_free (ret);
441         return NULL;
442       }
443
444       if (subentry->type == GST_TOC_ENTRY_TYPE_EDITION)
445         ++editions_count;
446       else
447         ++chapters_count;
448
449       /* check for mixed content */
450       if (G_UNLIKELY (chapters_count > 0 && editions_count > 0)) {
451         g_critical
452             ("Mixed editions and chapters in the TOC contents, the TOC is broken");
453         gst_toc_entry_free (subentry);
454         gst_toc_free (ret);
455         return NULL;
456       }
457
458       ret->entries = g_list_prepend (ret->entries, subentry);
459     }
460
461     ret->entries = g_list_reverse (ret->entries);
462   }
463
464   if (gst_structure_id_has_field_typed (toc,
465           GST_QUARK (TAGS), GST_TYPE_STRUCTURE)) {
466     val = gst_structure_id_get_value (toc, GST_QUARK (TAGS));
467
468     if (G_LIKELY (GST_IS_TAG_LIST (gst_value_get_structure (val)))) {
469       list = gst_tag_list_copy (GST_TAG_LIST (gst_value_get_structure (val)));
470       gst_tag_list_free (ret->tags);
471       ret->tags = list;
472     }
473   }
474
475   if (gst_structure_id_has_field_typed (toc,
476           GST_QUARK (INFO), GST_TYPE_STRUCTURE)) {
477     val = gst_structure_id_get_value (toc, GST_QUARK (INFO));
478
479     if (G_LIKELY (GST_IS_STRUCTURE (gst_value_get_structure (val)))) {
480       st = gst_structure_copy (gst_value_get_structure (val));
481       gst_structure_free (ret->info);
482       ret->info = st;
483     }
484   }
485
486   if (G_UNLIKELY (ret->entries == NULL)) {
487     gst_toc_free (ret);
488     return NULL;
489   }
490
491   return ret;
492 }
493
494 static GstStructure *
495 gst_toc_entry_to_structure (const GstTocEntry * entry, guint level)
496 {
497   GstStructure *ret, *subentry_struct;
498   GstTocEntry *subentry;
499   GList *cur;
500   GValue subentries_val = { 0 };
501   GValue entry_val = { 0 };
502   guint chapters_count = 0, editions_count = 0;
503
504   g_return_val_if_fail (entry != NULL, NULL);
505
506   ret =
507       gst_toc_entry_structure_new (entry->type, entry->uid, entry->tags,
508       entry->info);
509
510   g_value_init (&subentries_val, GST_TYPE_ARRAY);
511   g_value_init (&entry_val, GST_TYPE_STRUCTURE);
512
513   cur = entry->subentries;
514   while (cur != NULL) {
515     subentry = cur->data;
516
517     if (subentry->type == GST_TOC_ENTRY_TYPE_EDITION)
518       ++editions_count;
519     else
520       ++chapters_count;
521
522     /* check for mixed content */
523     if (G_UNLIKELY (chapters_count > 0 && editions_count > 0)) {
524       g_critical
525           ("Mixed editions and chapters in the TOC contents, the TOC is broken");
526       gst_structure_free (ret);
527       g_value_unset (&entry_val);
528       g_value_unset (&subentries_val);
529       return NULL;
530     }
531
532     /* skip empty editions */
533     if (G_UNLIKELY (subentry->type == GST_TOC_ENTRY_TYPE_EDITION
534             && subentry->subentries == NULL)) {
535       g_warning
536           ("Empty edition found while serializing TOC to GstStructure, skipping");
537       cur = cur->next;
538       continue;
539     }
540
541     subentry_struct = gst_toc_entry_to_structure (subentry, level + 1);
542
543     /* check for success */
544     if (G_UNLIKELY (subentry_struct == NULL)) {
545       gst_structure_free (ret);
546       g_value_unset (&subentries_val);
547       g_value_unset (&entry_val);
548       return NULL;
549     }
550
551     /* skip empty editions */
552     if (G_UNLIKELY (subentry->type == GST_TOC_ENTRY_TYPE_EDITION
553             && subentry->subentries == NULL)) {
554       g_warning
555           ("Empty edition found while serializing TOC to GstStructure, skipping");
556       cur = cur->next;
557       continue;
558     }
559
560     gst_value_set_structure (&entry_val, subentry_struct);
561     gst_value_array_append_value (&subentries_val, &entry_val);
562     gst_structure_free (subentry_struct);
563
564     cur = cur->next;
565   }
566
567   gst_structure_id_set_value (ret, GST_QUARK (SUB_ENTRIES), &subentries_val);
568
569   g_value_unset (&subentries_val);
570   g_value_unset (&entry_val);
571   return ret;
572 }
573
574 GstStructure *
575 __gst_toc_to_structure (const GstToc * toc)
576 {
577   GValue val = { 0 };
578   GValue subentries_val = { 0 };
579   GstStructure *ret, *subentry_struct;
580   GstTocEntry *subentry;
581   GList *cur;
582   guint editions_count = 0, chapters_count = 0;
583
584   g_return_val_if_fail (toc != NULL, NULL);
585   g_return_val_if_fail (toc->entries != NULL, NULL);
586
587   ret = gst_toc_structure_new (toc->tags, toc->info);
588
589   g_value_init (&val, GST_TYPE_STRUCTURE);
590   g_value_init (&subentries_val, GST_TYPE_ARRAY);
591   cur = toc->entries;
592
593   while (cur != NULL) {
594     subentry = cur->data;
595
596     if (subentry->type == GST_TOC_ENTRY_TYPE_EDITION)
597       ++editions_count;
598     else
599       ++chapters_count;
600
601     /* check for mixed content */
602     if (G_UNLIKELY (chapters_count > 0 && editions_count > 0)) {
603       g_critical
604           ("Mixed editions and chapters in the TOC contents, the TOC is broken");
605       gst_structure_free (ret);
606       g_value_unset (&val);
607       g_value_unset (&subentries_val);
608       return NULL;
609     }
610
611     /* skip empty editions */
612     if (G_UNLIKELY (subentry->type == GST_TOC_ENTRY_TYPE_EDITION
613             && subentry->subentries == NULL)) {
614       g_warning
615           ("Empty edition found while serializing TOC to GstStructure, skipping");
616       cur = cur->next;
617       continue;
618     }
619
620     subentry_struct = gst_toc_entry_to_structure (subentry, 0);
621
622     /* check for success */
623     if (G_UNLIKELY (subentry_struct == NULL)) {
624       g_critical ("Couldn't serialize TOC to GstStructure");
625       gst_structure_free (ret);
626       g_value_unset (&val);
627       g_value_unset (&subentries_val);
628       return NULL;
629     }
630
631     gst_value_set_structure (&val, subentry_struct);
632     gst_value_array_append_value (&subentries_val, &val);
633     gst_structure_free (subentry_struct);
634
635     cur = cur->next;
636   }
637
638   gst_structure_id_set_value (ret, GST_QUARK (SUB_ENTRIES), &subentries_val);
639
640   g_value_unset (&val);
641   g_value_unset (&subentries_val);
642   return ret;
643 }
644
645 static gboolean
646 gst_toc_check_entry_for_uid (const GstTocEntry * entry, const gchar * uid)
647 {
648   GList *cur;
649
650   g_return_val_if_fail (entry != NULL, FALSE);
651   g_return_val_if_fail (uid != NULL, FALSE);
652
653   if (g_strcmp0 (entry->uid, uid) == 0)
654     return TRUE;
655
656   cur = entry->subentries;
657   while (cur != NULL) {
658     if (gst_toc_check_entry_for_uid (cur->data, uid))
659       return TRUE;
660     cur = cur->next;
661   }
662
663   return FALSE;
664 }
665
666 /**
667  * gst_toc_find_entry:
668  * @toc: #GstToc to search in.
669  * @uid: UID to find #GstTocEntry with.
670  *
671  * Find #GstTocEntry with given @uid in the @toc.
672  *
673  * Returns: #GstTocEntry with specified @uid from the @toc, or NULL if not found.
674  *
675  * Since: 0.10.37
676  */
677 GstTocEntry *
678 gst_toc_find_entry (const GstToc * toc, const gchar * uid)
679 {
680   GList *cur;
681
682   g_return_val_if_fail (toc != NULL, NULL);
683   g_return_val_if_fail (uid != NULL, NULL);
684
685   cur = toc->entries;
686   while (cur != NULL) {
687     if (gst_toc_check_entry_for_uid (cur->data, uid))
688       return cur->data;
689     cur = cur->next;
690   }
691
692   return NULL;
693 }
694
695 /**
696  * gst_toc_entry_copy:
697  * @entry: #GstTocEntry to copy.
698  *
699  * Copy #GstTocEntry with all subentries (deep copy).
700  *
701  * Returns: newly allocated #GstTocEntry in case of success, NULL otherwise;
702  * free it when done with gst_toc_entry_free().
703  *
704  * Since: 0.10.37
705  */
706 GstTocEntry *
707 gst_toc_entry_copy (const GstTocEntry * entry)
708 {
709   GstTocEntry *ret, *sub;
710   GList *cur;
711   GstTagList *list;
712   GstStructure *st;
713
714   g_return_val_if_fail (entry != NULL, NULL);
715
716   ret = gst_toc_entry_new (entry->type, entry->uid);
717
718   if (GST_IS_STRUCTURE (entry->info)) {
719     st = gst_structure_copy (entry->info);
720     gst_structure_free (ret->info);
721     ret->info = st;
722   }
723
724   if (GST_IS_TAG_LIST (entry->tags)) {
725     list = gst_tag_list_copy (entry->tags);
726     gst_tag_list_free (ret->tags);
727     ret->tags = list;
728   }
729
730   cur = entry->pads;
731   while (cur != NULL) {
732     if (GST_IS_PAD (cur->data))
733       ret->pads = g_list_prepend (ret->pads, gst_object_ref (cur->data));
734     cur = cur->next;
735   }
736   ret->pads = g_list_reverse (ret->pads);
737
738   cur = entry->subentries;
739   while (cur != NULL) {
740     sub = gst_toc_entry_copy (cur->data);
741
742     if (sub != NULL)
743       ret->subentries = g_list_prepend (ret->subentries, sub);
744
745     cur = cur->next;
746   }
747   ret->subentries = g_list_reverse (ret->subentries);
748
749   return ret;
750 }
751
752 /**
753  * gst_toc_copy:
754  * @toc: #GstToc to copy.
755  *
756  * Copy #GstToc with all subentries (deep copy).
757  *
758  * Returns: newly allocated #GstToc in case of success, NULL otherwise;
759  * free it when done with gst_toc_free().
760  *
761  * Since: 0.10.37
762  */
763 GstToc *
764 gst_toc_copy (const GstToc * toc)
765 {
766   GstToc *ret;
767   GstTocEntry *entry;
768   GList *cur;
769   GstTagList *list;
770   GstStructure *st;
771
772   g_return_val_if_fail (toc != NULL, NULL);
773
774   ret = gst_toc_new ();
775
776   if (GST_IS_STRUCTURE (toc->info)) {
777     st = gst_structure_copy (toc->info);
778     gst_structure_free (ret->info);
779     ret->info = st;
780   }
781
782   if (GST_IS_TAG_LIST (toc->tags)) {
783     list = gst_tag_list_copy (toc->tags);
784     gst_tag_list_free (ret->tags);
785     ret->tags = list;
786   }
787
788   cur = toc->entries;
789   while (cur != NULL) {
790     entry = gst_toc_entry_copy (cur->data);
791
792     if (entry != NULL)
793       ret->entries = g_list_prepend (ret->entries, entry);
794
795     cur = cur->next;
796   }
797   ret->entries = g_list_reverse (ret->entries);
798
799   return ret;
800 }
801
802 /**
803  * gst_toc_entry_set_start_stop:
804  * @entry: #GstTocEntry to set values.
805  * @start: start value to set.
806  * @stop: stop value to set.
807  *
808  * Set @start and @stop values for the @entry.
809  *
810  * Since: 0.10.37
811  */
812 void
813 gst_toc_entry_set_start_stop (GstTocEntry * entry, gint64 start, gint64 stop)
814 {
815   const GValue *val;
816   GstStructure *structure = NULL;
817
818   g_return_if_fail (entry != NULL);
819   g_return_if_fail (GST_IS_STRUCTURE (entry->info));
820
821   if (gst_structure_id_has_field_typed (entry->info, GST_QUARK (TIME),
822           GST_TYPE_STRUCTURE)) {
823     val = gst_structure_id_get_value (entry->info, GST_QUARK (TIME));
824     structure = gst_structure_copy (gst_value_get_structure (val));
825   }
826
827   if (structure == NULL)
828     structure = gst_structure_new_id_empty (GST_QUARK (TIME_STRUCTURE));
829
830   gst_structure_id_set (structure, GST_QUARK (START),
831       G_TYPE_INT64, start, GST_QUARK (STOP), G_TYPE_INT64, stop, NULL);
832
833   gst_structure_id_set (entry->info, GST_QUARK (TIME),
834       GST_TYPE_STRUCTURE, structure, NULL);
835
836   gst_structure_free (structure);
837 }
838
839 /**
840  * gst_toc_entry_get_start_stop:
841  * @entry: #GstTocEntry to get values from.
842  * @start: (out): the storage for the start value, leave #NULL if not need.
843  * @stop: (out): the storage for the stop value, leave #NULL if not need.
844  *
845  * Get start and stop values from the @entry and write them into appropriate storages.
846  *
847  * Returns: TRUE if all non-NULL storage pointers were filled with appropriate values,
848  * FALSE otherwise.
849  *
850  * Since: 0.10.37
851  */
852 gboolean
853 gst_toc_entry_get_start_stop (const GstTocEntry * entry, gint64 * start,
854     gint64 * stop)
855 {
856   gboolean ret = TRUE;
857   const GValue *val;
858   const GstStructure *structure;
859
860   g_return_val_if_fail (entry != NULL, FALSE);
861   g_return_val_if_fail (GST_IS_STRUCTURE (entry->info), FALSE);
862
863   if (!gst_structure_id_has_field_typed (entry->info,
864           GST_QUARK (TIME), GST_TYPE_STRUCTURE))
865     return FALSE;
866
867   val = gst_structure_id_get_value (entry->info, GST_QUARK (TIME));
868   structure = gst_value_get_structure (val);
869
870   if (start != NULL) {
871     if (gst_structure_id_has_field_typed (structure,
872             GST_QUARK (START), G_TYPE_INT64))
873       *start =
874           g_value_get_int64 (gst_structure_id_get_value (structure,
875               GST_QUARK (START)));
876     else
877       ret = FALSE;
878   }
879
880   if (stop != NULL) {
881     if (gst_structure_id_has_field_typed (structure,
882             GST_QUARK (STOP), G_TYPE_INT64))
883       *stop =
884           g_value_get_int64 (gst_structure_id_get_value (structure,
885               GST_QUARK (STOP)));
886     else
887       ret = FALSE;
888   }
889
890   return ret;
891 }
892
893 /**
894  * gst_toc_entry_type_get_nick:
895  * @type: a #GstTocEntryType.
896  *
897  * Converts @type to a string representation.
898  *
899  * Returns: Returns the human-readable @type. Can be NULL if an error occurred.
900  * Since: 0.11.92
901  */
902 const gchar *
903 gst_toc_entry_type_get_nick (GstTocEntryType type)
904 {
905   const gchar *entry_types[] = { "chapter", "edition" };
906
907   g_return_val_if_fail ((gint) type >= 0
908       && (gint) type < G_N_ELEMENTS (entry_types), NULL);
909   return entry_types[type];
910 }
911
912 gboolean
913 __gst_toc_structure_get_updated (const GstStructure * toc)
914 {
915   const GValue *val;
916
917   g_return_val_if_fail (GST_IS_STRUCTURE (toc), FALSE);
918
919   if (G_LIKELY (gst_structure_id_has_field_typed (toc,
920               GST_QUARK (UPDATED), G_TYPE_BOOLEAN))) {
921     val = gst_structure_id_get_value (toc, GST_QUARK (UPDATED));
922     return g_value_get_boolean (val);
923   }
924
925   return FALSE;
926 }
927
928 void
929 __gst_toc_structure_set_updated (GstStructure * toc, gboolean updated)
930 {
931   GValue val = { 0 };
932
933   g_return_if_fail (toc != NULL);
934
935   g_value_init (&val, G_TYPE_BOOLEAN);
936   g_value_set_boolean (&val, updated);
937   gst_structure_id_set_value (toc, GST_QUARK (UPDATED), &val);
938   g_value_unset (&val);
939 }
940
941 gchar *
942 __gst_toc_structure_get_extend_uid (const GstStructure * toc)
943 {
944   const GValue *val;
945
946   g_return_val_if_fail (GST_IS_STRUCTURE (toc), NULL);
947
948   if (G_LIKELY (gst_structure_id_has_field_typed (toc,
949               GST_QUARK (EXTEND_UID), G_TYPE_STRING))) {
950     val = gst_structure_id_get_value (toc, GST_QUARK (EXTEND_UID));
951     return g_strdup (g_value_get_string (val));
952   }
953
954   return NULL;
955 }
956
957 void
958 __gst_toc_structure_set_extend_uid (GstStructure * toc,
959     const gchar * extend_uid)
960 {
961   GValue val = { 0 };
962
963   g_return_if_fail (toc != NULL);
964   g_return_if_fail (extend_uid != NULL);
965
966   g_value_init (&val, G_TYPE_STRING);
967   g_value_set_string (&val, extend_uid);
968   gst_structure_id_set_value (toc, GST_QUARK (EXTEND_UID), &val);
969   g_value_unset (&val);
970 }