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