toc: Make structures opaque and clean up function names and fields a bit
[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   gchar *uid;
80   GstTocEntryType type;
81   GstClockTime start, stop;
82   GList *subentries;
83   GstTagList *tags;
84
85   /*< private > */
86   gpointer _gst_reserved[GST_PADDING];
87 };
88
89 struct _GstToc
90 {
91   GstMiniObject mini_object;
92
93   GList *entries;
94   GstTagList *tags;
95
96   /*< private > */
97   gpointer _gst_reserved[GST_PADDING];
98 };
99
100 #undef gst_toc_copy
101 static GstToc *gst_toc_copy (const GstToc * toc);
102 static void gst_toc_free (GstToc * toc);
103 #undef gst_toc_entry_copy
104 static GstTocEntry *gst_toc_entry_copy (const GstTocEntry * toc);
105 static void gst_toc_entry_free (GstTocEntry * toc);
106
107 GST_DEFINE_MINI_OBJECT_TYPE (GstToc, gst_toc);
108 GST_DEFINE_MINI_OBJECT_TYPE (GstTocEntry, gst_toc_entry);
109
110 /**
111  * gst_toc_new:
112  *
113  * Create a new #GstToc structure.
114  *
115  * Returns: (transfer full): newly allocated #GstToc structure, free it
116  *     with gst_toc_unref().
117  *
118  * Since: 0.10.37
119  */
120 GstToc *
121 gst_toc_new (void)
122 {
123   GstToc *toc;
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->tags = gst_tag_list_new_empty ();
132
133   return toc;
134 }
135
136 /**
137  * gst_toc_set_tags:
138  * @toc: A #GstToc instance
139  * @tags: (allow-none) (transfer full): A #GstTagList or %NULL
140  *
141  * Set a #GstTagList with tags for the complete @toc.
142  *
143  * Since: 0.10.37
144  */
145 void
146 gst_toc_set_tags (GstToc * toc, GstTagList * tags)
147 {
148   g_return_if_fail (toc != NULL);
149   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (toc)));
150
151   if (toc->tags)
152     gst_tag_list_unref (toc->tags);
153   toc->tags = tags;
154 }
155
156 /**
157  * gst_toc_merge_tags:
158  * @toc: A #GstToc instance
159  * @tags: (allow-none): A #GstTagList or %NULL
160  * @mode: A #GstTagMergeMode
161  *
162  * Merge @tags into the existing tags of @toc using @mode.
163  *
164  * Since: 0.10.37
165  */
166 void
167 gst_toc_merge_tags (GstToc * toc, GstTagList * tags, GstTagMergeMode mode)
168 {
169   g_return_if_fail (toc != NULL);
170   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (toc)));
171
172   if (!toc->tags) {
173     toc->tags = gst_tag_list_ref (tags);
174   } else {
175     GstTagList *tmp = gst_tag_list_merge (toc->tags, tags, mode);
176     gst_tag_list_unref (toc->tags);
177     toc->tags = tmp;
178   }
179 }
180
181 /**
182  * gst_toc_get_tags:
183  * @toc: A #GstToc instance
184  *
185  * Gets the tags for @toc.
186  *
187  * Returns: (transfer none): A #GstTagList for @entry
188  *
189  * Since: 0.10.37
190  */
191 GstTagList *
192 gst_toc_get_tags (const GstToc * toc)
193 {
194   g_return_val_if_fail (toc != NULL, NULL);
195
196   return toc->tags;
197 }
198
199 /**
200  * gst_toc_append_entry:
201  * @toc: A #GstToc instance
202  * @entry: (transfer full): A #GstTocEntry
203  *
204  * Appends the #GstTocEntry @entry to @toc.
205  *
206  * Since: 0.10.37
207  */
208 void
209 gst_toc_append_entry (GstToc * toc, GstTocEntry * entry)
210 {
211   g_return_if_fail (toc != NULL);
212   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (toc)));
213
214   toc->entries = g_list_append (toc->entries, entry);
215 }
216
217 /**
218  * gst_toc_get_entries:
219  * @toc: A #GstToc instance
220  *
221  * Gets the list of #GstTocEntry of @toc.
222  *
223  * Returns: (transfer none) (element-type Gst.TocEntry): A #GList of #GstTocEntry for @entry
224  *
225  * Since: 0.10.37
226  */
227 GList *
228 gst_toc_get_entries (const GstToc * toc)
229 {
230   g_return_val_if_fail (toc != NULL, NULL);
231
232   return toc->entries;
233 }
234
235 static GstTocEntry *
236 gst_toc_entry_new_internal (GstTocEntryType type, const gchar * uid)
237 {
238   GstTocEntry *entry;
239
240   entry = g_slice_new0 (GstTocEntry);
241
242   gst_mini_object_init (GST_MINI_OBJECT_CAST (entry), 0, GST_TYPE_TOC_ENTRY,
243       (GstMiniObjectCopyFunction) gst_toc_entry_copy, NULL,
244       (GstMiniObjectFreeFunction) gst_toc_entry_free);
245
246   entry->uid = g_strdup (uid);
247   entry->type = type;
248   entry->tags = NULL;
249   entry->start = entry->stop = GST_CLOCK_TIME_NONE;
250
251   return entry;
252 }
253
254 /**
255  * gst_toc_entry_new:
256  * @type: entry type.
257  * @uid: unique ID (UID) in the whole TOC.
258  *
259  * Create new #GstTocEntry structure.
260  *
261  * Returns: newly allocated #GstTocEntry structure, free it with gst_toc_entry_unref().
262  *
263  * Since: 0.10.37
264  */
265 GstTocEntry *
266 gst_toc_entry_new (GstTocEntryType type, const gchar * uid)
267 {
268   g_return_val_if_fail (uid != NULL, NULL);
269
270   return gst_toc_entry_new_internal (type, uid);
271 }
272
273 static void
274 gst_toc_free (GstToc * toc)
275 {
276   g_list_foreach (toc->entries, (GFunc) gst_mini_object_unref, NULL);
277   g_list_free (toc->entries);
278
279   if (toc->tags != NULL)
280     gst_tag_list_unref (toc->tags);
281
282   g_slice_free (GstToc, toc);
283 }
284
285 static void
286 gst_toc_entry_free (GstTocEntry * entry)
287 {
288   g_return_if_fail (entry != NULL);
289
290   g_list_foreach (entry->subentries, (GFunc) gst_mini_object_unref, NULL);
291   g_list_free (entry->subentries);
292
293   g_free (entry->uid);
294
295   if (entry->tags != NULL)
296     gst_tag_list_unref (entry->tags);
297
298   g_slice_free (GstTocEntry, entry);
299 }
300
301 static gboolean
302 gst_toc_check_entry_for_uid (const GstTocEntry * entry, const gchar * uid)
303 {
304   GList *cur;
305
306   g_return_val_if_fail (entry != NULL, FALSE);
307   g_return_val_if_fail (uid != NULL, FALSE);
308
309   if (g_strcmp0 (entry->uid, uid) == 0)
310     return TRUE;
311
312   cur = entry->subentries;
313   while (cur != NULL) {
314     if (gst_toc_check_entry_for_uid (cur->data, uid))
315       return TRUE;
316     cur = cur->next;
317   }
318
319   return FALSE;
320 }
321
322 /**
323  * gst_toc_find_entry:
324  * @toc: #GstToc to search in.
325  * @uid: UID to find #GstTocEntry with.
326  *
327  * Find #GstTocEntry with given @uid in the @toc.
328  *
329  * Returns: #GstTocEntry with specified @uid from the @toc, or NULL if not found.
330  *
331  * Since: 0.10.37
332  */
333 GstTocEntry *
334 gst_toc_find_entry (const GstToc * toc, const gchar * uid)
335 {
336   GList *cur;
337
338   g_return_val_if_fail (toc != NULL, NULL);
339   g_return_val_if_fail (uid != NULL, NULL);
340
341   cur = toc->entries;
342   while (cur != NULL) {
343     if (gst_toc_check_entry_for_uid (cur->data, uid))
344       return cur->data;
345     cur = cur->next;
346   }
347
348   return NULL;
349 }
350
351 /**
352  * gst_toc_entry_copy:
353  * @entry: #GstTocEntry to copy.
354  *
355  * Copy #GstTocEntry with all subentries (deep copy).
356  *
357  * Returns: newly allocated #GstTocEntry in case of success, NULL otherwise;
358  * free it when done with gst_toc_entry_unref().
359  *
360  * Since: 0.10.37
361  */
362 static GstTocEntry *
363 gst_toc_entry_copy (const GstTocEntry * entry)
364 {
365   GstTocEntry *ret, *sub;
366   GstTagList *list;
367   GList *cur;
368
369   g_return_val_if_fail (entry != NULL, NULL);
370
371   ret = gst_toc_entry_new (entry->type, entry->uid);
372
373   ret->start = entry->start;
374   ret->stop = entry->stop;
375
376   if (GST_IS_TAG_LIST (entry->tags)) {
377     list = gst_tag_list_copy (entry->tags);
378     if (ret->tags)
379       gst_tag_list_unref (ret->tags);
380     ret->tags = list;
381   }
382
383   cur = entry->subentries;
384   while (cur != NULL) {
385     sub = gst_toc_entry_copy (cur->data);
386
387     if (sub != NULL)
388       ret->subentries = g_list_prepend (ret->subentries, sub);
389
390     cur = cur->next;
391   }
392   ret->subentries = g_list_reverse (ret->subentries);
393
394   return ret;
395 }
396
397 /**
398  * gst_toc_copy:
399  * @toc: #GstToc to copy.
400  *
401  * Copy #GstToc with all subentries (deep copy).
402  *
403  * Returns: newly allocated #GstToc in case of success, NULL otherwise;
404  * free it when done with gst_toc_free().
405  *
406  * Since: 0.10.37
407  */
408 static GstToc *
409 gst_toc_copy (const GstToc * toc)
410 {
411   GstToc *ret;
412   GstTocEntry *entry;
413   GList *cur;
414   GstTagList *list;
415
416   g_return_val_if_fail (toc != NULL, NULL);
417
418   ret = gst_toc_new ();
419
420   if (GST_IS_TAG_LIST (toc->tags)) {
421     list = gst_tag_list_copy (toc->tags);
422     gst_tag_list_unref (ret->tags);
423     ret->tags = list;
424   }
425
426   cur = toc->entries;
427   while (cur != NULL) {
428     entry = gst_toc_entry_copy (cur->data);
429
430     if (entry != NULL)
431       ret->entries = g_list_prepend (ret->entries, entry);
432
433     cur = cur->next;
434   }
435   ret->entries = g_list_reverse (ret->entries);
436
437   return ret;
438 }
439
440 /**
441  * gst_toc_entry_set_start_stop_times:
442  * @entry: #GstTocEntry to set values.
443  * @start: start value to set.
444  * @stop: stop value to set.
445  *
446  * Set @start and @stop values for the @entry.
447  *
448  * Since: 0.10.37
449  */
450 void
451 gst_toc_entry_set_start_stop_times (GstTocEntry * entry, gint64 start,
452     gint64 stop)
453 {
454   g_return_if_fail (entry != NULL);
455
456   entry->start = start;
457   entry->stop = stop;
458 }
459
460 /**
461  * gst_toc_entry_get_start_stop_times:
462  * @entry: #GstTocEntry to get values from.
463  * @start: (out): the storage for the start value, leave #NULL if not need.
464  * @stop: (out): the storage for the stop value, leave #NULL if not need.
465  *
466  * Get start and stop values from the @entry and write them into appropriate storages.
467  *
468  * Returns: TRUE if all non-NULL storage pointers were filled with appropriate values,
469  * FALSE otherwise.
470  *
471  * Since: 0.10.37
472  */
473 gboolean
474 gst_toc_entry_get_start_stop_times (const GstTocEntry * entry, gint64 * start,
475     gint64 * stop)
476 {
477   gboolean ret = TRUE;
478
479   g_return_val_if_fail (entry != NULL, FALSE);
480
481   if (start != NULL)
482     *start = entry->start;
483   if (stop != NULL)
484     *stop = entry->stop;
485
486   return ret;
487 }
488
489 /**
490  * gst_toc_entry_type_get_nick:
491  * @type: a #GstTocEntryType.
492  *
493  * Converts @type to a string representation.
494  *
495  * Returns: Returns a human-readable string for @type. This string is
496  *    only for debugging purpose and should not be displayed in a user
497  *    interface.
498  */
499 const gchar *
500 gst_toc_entry_type_get_nick (GstTocEntryType type)
501 {
502   switch (type) {
503     case GST_TOC_ENTRY_TYPE_ANGLE:
504       return "angle";
505     case GST_TOC_ENTRY_TYPE_VERSION:
506       return "version";
507     case GST_TOC_ENTRY_TYPE_EDITION:
508       return "edition";
509     case GST_TOC_ENTRY_TYPE_TITLE:
510       return "title";
511     case GST_TOC_ENTRY_TYPE_TRACK:
512       return "track";
513     case GST_TOC_ENTRY_TYPE_CHAPTER:
514       return "chapter";
515     default:
516       break;
517   }
518   return "invalid";
519 }
520
521 /**
522  * gst_toc_entry_get_entry_type:
523  * @entry: a #GstTocEntry
524  *
525  * Returns: @entry's entry type
526  */
527 GstTocEntryType
528 gst_toc_entry_get_entry_type (const GstTocEntry * entry)
529 {
530   g_return_val_if_fail (entry != NULL, GST_TOC_ENTRY_TYPE_INVALID);
531
532   return entry->type;
533 }
534
535 /**
536  * gst_toc_entry_is_alternative:
537  * @entry: a #GstTocEntry
538  *
539  * Returns: %TRUE if @entry's type is an alternative type, otherwise %FALSE
540  */
541 gboolean
542 gst_toc_entry_is_alternative (const GstTocEntry * entry)
543 {
544   g_return_val_if_fail (entry != NULL, FALSE);
545
546   return GST_TOC_ENTRY_TYPE_IS_ALTERNATIVE (entry->type);
547 }
548
549 /**
550  * gst_toc_entry_is_sequence:
551  * @entry: a #GstTocEntry
552  *
553  * Returns: %TRUE if @entry's type is a sequence type, otherwise %FALSE
554  */
555 gboolean
556 gst_toc_entry_is_sequence (const GstTocEntry * entry)
557 {
558   g_return_val_if_fail (entry != NULL, FALSE);
559
560   return GST_TOC_ENTRY_TYPE_IS_SEQUENCE (entry->type);
561 }
562
563 /**
564  * gst_toc_entry_get_uid:
565  * @entry: A #GstTocEntry instance
566  *
567  * Gets the UID of @entry.
568  *
569  * Returns: (transfer none): The UID of @entry
570  *
571  * Since: 0.10.37
572  */
573 const gchar *
574 gst_toc_entry_get_uid (const GstTocEntry * entry)
575 {
576   g_return_val_if_fail (entry != NULL, NULL);
577
578   return entry->uid;
579 }
580
581 /**
582  * gst_toc_entry_append_sub_entry:
583  * @entry: A #GstTocEntry instance
584  * @subentry: (transfer full): A #GstTocEntry
585  *
586  * Appends the #GstTocEntry @subentry to @entry.
587  *
588  * Since: 0.10.37
589  */
590 void
591 gst_toc_entry_append_sub_entry (GstTocEntry * entry, GstTocEntry * subentry)
592 {
593   g_return_if_fail (entry != NULL);
594   g_return_if_fail (subentry != NULL);
595   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (entry)));
596
597   entry->subentries = g_list_append (entry->subentries, subentry);
598 }
599
600 /**
601  * gst_toc_entry_get_uid:
602  * @entry: A #GstTocEntry instance
603  *
604  * Gets the sub-entries of @entry.
605  *
606  * Returns: (transfer none) (element-type Gst.TocEntry): A #GList of #GstTocEntry of @entry
607  *
608  * Since: 0.10.37
609  */
610 GList *
611 gst_toc_entry_get_sub_entries (const GstTocEntry * entry)
612 {
613   g_return_val_if_fail (entry != NULL, NULL);
614
615   return entry->subentries;
616 }
617
618 /**
619  * gst_toc_entry_set_tags:
620  * @entry: A #GstTocEntry instance
621  * @tags: (allow-none) (transfer full): A #GstTagList or %NULL
622  *
623  * Set a #GstTagList with tags for the complete @entry.
624  *
625  * Since: 0.10.37
626  */
627 void
628 gst_toc_entry_set_tags (GstTocEntry * entry, GstTagList * tags)
629 {
630   g_return_if_fail (entry != NULL);
631   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (entry)));
632
633   if (entry->tags)
634     gst_tag_list_unref (entry->tags);
635   entry->tags = tags;
636 }
637
638 /**
639  * gst_toc_entry_merge_tags:
640  * @entry: A #GstTocEntry instance
641  * @tags: (allow-none): A #GstTagList or %NULL
642  * @mode: A #GstTagMergeMode
643  *
644  * Merge @tags into the existing tags of @entry using @mode.
645  *
646  * Since: 0.10.37
647  */
648 void
649 gst_toc_entry_merge_tags (GstTocEntry * entry, GstTagList * tags,
650     GstTagMergeMode mode)
651 {
652   g_return_if_fail (entry != NULL);
653   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (entry)));
654
655   if (!entry->tags) {
656     entry->tags = gst_tag_list_ref (tags);
657   } else {
658     GstTagList *tmp = gst_tag_list_merge (entry->tags, tags, mode);
659     gst_tag_list_unref (entry->tags);
660     entry->tags = tmp;
661   }
662 }
663
664 /**
665  * gst_toc_entry_get_tags:
666  * @entry: A #GstTocEntry instance
667  *
668  * Gets the tags for @entry.
669  *
670  * Returns: (transfer none): A #GstTagList for @entry
671  *
672  * Since: 0.10.37
673  */
674 GstTagList *
675 gst_toc_entry_get_tags (const GstTocEntry * entry)
676 {
677   g_return_val_if_fail (entry != NULL, NULL);
678
679   return entry->tags;
680 }