element: Enforce that elements created by gst_element_factory_create/make() are floating
[platform/upstream/gstreamer.git] / gst / gsttoc.c
index f88a69d..fd6bd66 100644 (file)
  *
  * You should have received a copy of the GNU Library General Public
  * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
  */
 
 /**
  * SECTION:gsttoc
+ * @title: GstToc
  * @short_description: Generic table of contents support
  * @see_also: #GstStructure, #GstEvent, #GstMessage, #GstQuery
  *
  *
  * Using TOC is very easy. Firstly, create #GstToc structure which represents root
  * contents of the source. You can also attach TOC-specific tags to it. Then fill
- * it with #GstTocEntry entries by appending them to #GstToc.entries #GstTocEntry.subentries
- * lists. You should use GST_TOC_ENTRY_TYPE_CHAPTER for generic TOC entry and
- * GST_TOC_ENTRY_TYPE_EDITION for the entries which are considered to be alternatives
- * (like DVD angles, Matroska editions and so on).
+ * it with #GstTocEntry entries by appending them to the #GstToc using
+ * gst_toc_append_entry(), and appending subentries to a #GstTocEntry using
+ * gst_toc_entry_append_sub_entry().
  *
  * Note that root level of the TOC can contain only either editions or chapters. You
  * should not mix them together at the same level. Otherwise you will get serialization
  * /deserialization errors. Make sure that no one of the entries has negative start and
  *  stop values.
  *
- * Please, use #GstToc.info and #GstTocEntry.info fields in that way: create a #GstStructure,
- * put all info related to your element there and put this structure into the info field under
- * the name of your element. Some fields in the info structure can be used for internal purposes,
- * so you should use it in the way described above to not to overwrite already existent fields.
- *
  * Use gst_event_new_toc() to create a new TOC #GstEvent, and gst_event_parse_toc() to
  * parse received TOC event. Use gst_event_new_toc_select() to create a new TOC select #GstEvent,
  * and gst_event_parse_toc_select() to parse received TOC select event. The same rule for
  * the #GstMessage: gst_message_new_toc() to create new TOC #GstMessage, and
- * gst_message_parse_toc() to parse received TOC message. Also you can create a new TOC query
- * with gst_query_new_toc(), set it with gst_query_set_toc() and parse it with
- * gst_query_parse_toc().
+ * gst_message_parse_toc() to parse received TOC message.
+ *
+ * TOCs can have global scope or current scope. Global scope TOCs contain
+ * all entries that can possibly be selected using a toc select event, and
+ * are what an application is usually interested in. TOCs with current scope
+ * only contain the parts of the TOC relevant to the currently selected/playing
+ * stream; the current scope TOC is used by downstream elements such as muxers
+ * to write correct TOC entries when transcoding files, for example. When
+ * playing a DVD, the global TOC would contain a hierarchy of all titles,
+ * chapters and angles, for example, while the current TOC would only contain
+ * the chapters for the currently playing title if playback of a specific
+ * title was requested.
+ *
+ * Applications and plugins should not rely on TOCs having a certain kind of
+ * structure, but should allow for different alternatives. For example, a
+ * simple CUE sheet embedded in a file may be presented as a flat list of
+ * track entries, or could have a top-level edition node (or some other
+ * alternative type entry) with track entries underneath that node; or even
+ * multiple top-level edition nodes (or some other alternative type entries)
+ * each with track entries underneath, in case the source file has extracted
+ * a track listing from different sources).
  */
 
 #ifdef HAVE_CONFIG_H
@@ -76,17 +89,23 @@ struct _GstTocEntry
 {
   GstMiniObject mini_object;
 
+  GstToc *toc;
+  GstTocEntry *parent;
+
   gchar *uid;
   GstTocEntryType type;
   GstClockTime start, stop;
   GList *subentries;
   GstTagList *tags;
+  GstTocLoopType loop_type;
+  gint repeat_count;
 };
 
 struct _GstToc
 {
   GstMiniObject mini_object;
 
+  GstTocScope scope;
   GList *entries;
   GstTagList *tags;
 };
@@ -98,11 +117,15 @@ static void gst_toc_free (GstToc * toc);
 static GstTocEntry *gst_toc_entry_copy (const GstTocEntry * toc);
 static void gst_toc_entry_free (GstTocEntry * toc);
 
+GType _gst_toc_type = 0;
+GType _gst_toc_entry_type = 0;
+
 GST_DEFINE_MINI_OBJECT_TYPE (GstToc, gst_toc);
 GST_DEFINE_MINI_OBJECT_TYPE (GstTocEntry, gst_toc_entry);
 
 /**
  * gst_toc_new:
+ * @scope: scope of this TOC
  *
  * Create a new #GstToc structure.
  *
@@ -110,22 +133,40 @@ GST_DEFINE_MINI_OBJECT_TYPE (GstTocEntry, gst_toc_entry);
  *     with gst_toc_unref().
  */
 GstToc *
-gst_toc_new (void)
+gst_toc_new (GstTocScope scope)
 {
   GstToc *toc;
 
+  g_return_val_if_fail (scope == GST_TOC_SCOPE_GLOBAL ||
+      scope == GST_TOC_SCOPE_CURRENT, NULL);
+
   toc = g_slice_new0 (GstToc);
 
   gst_mini_object_init (GST_MINI_OBJECT_CAST (toc), 0, GST_TYPE_TOC,
       (GstMiniObjectCopyFunction) gst_toc_copy, NULL,
       (GstMiniObjectFreeFunction) gst_toc_free);
 
+  toc->scope = scope;
   toc->tags = gst_tag_list_new_empty ();
 
   return toc;
 }
 
 /**
+ * gst_toc_get_scope:
+ * @toc: a #GstToc instance
+ *
+ * Returns: scope of @toc
+ */
+GstTocScope
+gst_toc_get_scope (const GstToc * toc)
+{
+  g_return_val_if_fail (toc != NULL, GST_TOC_SCOPE_GLOBAL);
+
+  return toc->scope;
+}
+
+/**
  * gst_toc_set_tags:
  * @toc: A #GstToc instance
  * @tags: (allow-none) (transfer full): A #GstTagList or %NULL
@@ -194,8 +235,12 @@ gst_toc_append_entry (GstToc * toc, GstTocEntry * entry)
 {
   g_return_if_fail (toc != NULL);
   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (toc)));
+  g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (entry)));
+  g_return_if_fail (entry->toc == NULL);
+  g_return_if_fail (entry->parent == NULL);
 
   toc->entries = g_list_append (toc->entries, entry);
+  entry->toc = toc;
 
   GST_LOG ("appended %s entry with uid %s to toc %p",
       gst_toc_entry_type_get_nick (entry->type), entry->uid, toc);
@@ -264,6 +309,10 @@ gst_toc_free (GstToc * toc)
   if (toc->tags != NULL)
     gst_tag_list_unref (toc->tags);
 
+#ifdef USE_POISONING
+  memset (toc, 0xff, sizeof (GstToc));
+#endif
+
   g_slice_free (GstToc, toc);
 }
 
@@ -280,6 +329,10 @@ gst_toc_entry_free (GstTocEntry * entry)
   if (entry->tags != NULL)
     gst_tag_list_unref (entry->tags);
 
+#ifdef USE_POISONING
+  memset (entry, 0xff, sizeof (GstTocEntry));
+#endif
+
   g_slice_free (GstTocEntry, entry);
 }
 
@@ -316,7 +369,8 @@ gst_toc_entry_find_sub_entry (const GstTocEntry * entry, const gchar * uid)
  *
  * Find #GstTocEntry with given @uid in the @toc.
  *
- * Returns: (transfer none): #GstTocEntry with specified @uid from the @toc, or NULL if not found.
+ * Returns: (transfer none) (nullable): #GstTocEntry with specified
+ * @uid from the @toc, or %NULL if not found.
  */
 GstTocEntry *
 gst_toc_find_entry (const GstToc * toc, const gchar * uid)
@@ -343,21 +397,33 @@ gst_toc_find_entry (const GstToc * toc, const gchar * uid)
   return NULL;
 }
 
+static GList *
+gst_toc_deep_copy_toc_entries (GList * entry_list)
+{
+  GQueue new_entries = G_QUEUE_INIT;
+  GList *l;
+
+  for (l = entry_list; l != NULL; l = l->next)
+    g_queue_push_tail (&new_entries, gst_toc_entry_copy (l->data));
+
+  return new_entries.head;
+}
+
 /**
  * gst_toc_entry_copy:
  * @entry: #GstTocEntry to copy.
  *
  * Copy #GstTocEntry with all subentries (deep copy).
  *
- * Returns: newly allocated #GstTocEntry in case of success, NULL otherwise;
- * free it when done with gst_toc_entry_unref().
+ * Returns: (nullable): newly allocated #GstTocEntry in case of
+ * success, %NULL otherwise; free it when done with
+ * gst_toc_entry_unref().
  */
 static GstTocEntry *
 gst_toc_entry_copy (const GstTocEntry * entry)
 {
-  GstTocEntry *ret, *sub;
+  GstTocEntry *ret;
   GstTagList *list;
-  GList *cur;
 
   g_return_val_if_fail (entry != NULL, NULL);
 
@@ -373,16 +439,7 @@ gst_toc_entry_copy (const GstTocEntry * entry)
     ret->tags = list;
   }
 
-  cur = entry->subentries;
-  while (cur != NULL) {
-    sub = gst_toc_entry_copy (cur->data);
-
-    if (sub != NULL)
-      ret->subentries = g_list_prepend (ret->subentries, sub);
-
-    cur = cur->next;
-  }
-  ret->subentries = g_list_reverse (ret->subentries);
+  ret->subentries = gst_toc_deep_copy_toc_entries (entry->subentries);
 
   return ret;
 }
@@ -393,20 +450,18 @@ gst_toc_entry_copy (const GstTocEntry * entry)
  *
  * Copy #GstToc with all subentries (deep copy).
  *
- * Returns: newly allocated #GstToc in case of success, NULL otherwise;
- * free it when done with gst_toc_free().
+ * Returns: (nullable): newly allocated #GstToc in case of success,
+ * %NULL otherwise; free it when done with gst_toc_unref().
  */
 static GstToc *
 gst_toc_copy (const GstToc * toc)
 {
   GstToc *ret;
-  GstTocEntry *entry;
-  GList *cur;
   GstTagList *list;
 
   g_return_val_if_fail (toc != NULL, NULL);
 
-  ret = gst_toc_new ();
+  ret = gst_toc_new (toc->scope);
 
   if (GST_IS_TAG_LIST (toc->tags)) {
     list = gst_tag_list_copy (toc->tags);
@@ -414,16 +469,7 @@ gst_toc_copy (const GstToc * toc)
     ret->tags = list;
   }
 
-  cur = toc->entries;
-  while (cur != NULL) {
-    entry = gst_toc_entry_copy (cur->data);
-
-    if (entry != NULL)
-      ret->entries = g_list_prepend (ret->entries, entry);
-
-    cur = cur->next;
-  }
-  ret->entries = g_list_reverse (ret->entries);
+  ret->entries = gst_toc_deep_copy_toc_entries (toc->entries);
 
   return ret;
 }
@@ -449,20 +495,21 @@ gst_toc_entry_set_start_stop_times (GstTocEntry * entry, gint64 start,
 /**
  * gst_toc_entry_get_start_stop_times:
  * @entry: #GstTocEntry to get values from.
- * @start: (out): the storage for the start value, leave #NULL if not need.
- * @stop: (out): the storage for the stop value, leave #NULL if not need.
+ * @start: (out) (allow-none): the storage for the start value, leave
+ *   %NULL if not need.
+ * @stop: (out) (allow-none): the storage for the stop value, leave
+ *   %NULL if not need.
  *
- * Get start and stop values from the @entry and write them into appropriate storages.
+ * Get @start and @stop values from the @entry and write them into appropriate
+ * storages.
  *
- * Returns: TRUE if all non-NULL storage pointers were filled with appropriate values,
- * FALSE otherwise.
+ * Returns: %TRUE if all non-%NULL storage pointers were filled with appropriate
+ * values, %FALSE otherwise.
  */
 gboolean
 gst_toc_entry_get_start_stop_times (const GstTocEntry * entry, gint64 * start,
     gint64 * stop)
 {
-  gboolean ret = TRUE;
-
   g_return_val_if_fail (entry != NULL, FALSE);
 
   if (start != NULL)
@@ -470,10 +517,63 @@ gst_toc_entry_get_start_stop_times (const GstTocEntry * entry, gint64 * start,
   if (stop != NULL)
     *stop = entry->stop;
 
-  return ret;
+  return TRUE;
 }
 
 /**
+ * gst_toc_entry_set_loop:
+ * @entry: #GstTocEntry to set values.
+ * @loop_type: loop_type value to set.
+ * @repeat_count: repeat_count value to set.
+ *
+ * Set @loop_type and @repeat_count values for the @entry.
+ *
+ * Since: 1.4
+ */
+void
+gst_toc_entry_set_loop (GstTocEntry * entry, GstTocLoopType loop_type,
+    gint repeat_count)
+{
+  g_return_if_fail (entry != NULL);
+
+  entry->loop_type = loop_type;
+  entry->repeat_count = repeat_count;
+}
+
+/**
+ * gst_toc_entry_get_loop:
+ * @entry: #GstTocEntry to get values from.
+ * @loop_type: (out) (allow-none): the storage for the loop_type
+ *             value, leave %NULL if not need.
+ * @repeat_count: (out) (allow-none): the storage for the repeat_count
+ *                value, leave %NULL if not need.
+ *
+ * Get @loop_type and @repeat_count values from the @entry and write them into
+ * appropriate storages. Loops are e.g. used by sampled instruments. GStreamer
+ * is not automatically applying the loop. The application can process this
+ * meta data and use it e.g. to send a seek-event to loop a section.
+ *
+ * Returns: %TRUE if all non-%NULL storage pointers were filled with appropriate
+ * values, %FALSE otherwise.
+ *
+ * Since: 1.4
+ */
+gboolean
+gst_toc_entry_get_loop (const GstTocEntry * entry, GstTocLoopType * loop_type,
+    gint * repeat_count)
+{
+  g_return_val_if_fail (entry != NULL, FALSE);
+
+  if (loop_type != NULL)
+    *loop_type = entry->loop_type;
+  if (repeat_count != NULL)
+    *repeat_count = entry->repeat_count;
+
+  return TRUE;
+}
+
+
+/**
  * gst_toc_entry_type_get_nick:
  * @type: a #GstTocEntryType.
  *
@@ -576,15 +676,21 @@ gst_toc_entry_append_sub_entry (GstTocEntry * entry, GstTocEntry * subentry)
   g_return_if_fail (entry != NULL);
   g_return_if_fail (subentry != NULL);
   g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST (entry)));
+  g_return_if_fail (gst_mini_object_is_writable (GST_MINI_OBJECT_CAST
+          (subentry)));
+  g_return_if_fail (subentry->toc == NULL);
+  g_return_if_fail (subentry->parent == NULL);
 
   entry->subentries = g_list_append (entry->subentries, subentry);
+  subentry->toc = entry->toc;
+  subentry->parent = entry;
 
   GST_LOG ("appended %s subentry with uid %s to entry %s",
       gst_toc_entry_type_get_nick (subentry->type), subentry->uid, entry->uid);
 }
 
 /**
- * gst_toc_entry_get_uid:
+ * gst_toc_entry_get_sub_entries:
  * @entry: A #GstTocEntry instance
  *
  * Gets the sub-entries of @entry.
@@ -657,6 +763,38 @@ gst_toc_entry_get_tags (const GstTocEntry * entry)
   return entry->tags;
 }
 
+/**
+ * gst_toc_entry_get_toc:
+ * @entry: A #GstTocEntry instance
+ *
+ * Gets the parent #GstToc of @entry.
+ *
+ * Returns: (transfer none): The parent #GstToc of @entry
+ */
+GstToc *
+gst_toc_entry_get_toc (GstTocEntry * entry)
+{
+  g_return_val_if_fail (entry != NULL, NULL);
+
+  return entry->toc;
+}
+
+/**
+ * gst_toc_entry_get_parent:
+ * @entry: A #GstTocEntry instance
+ *
+ * Gets the parent #GstTocEntry of @entry.
+ *
+ * Returns: (transfer none) (nullable): The parent #GstTocEntry of @entry
+ */
+GstTocEntry *
+gst_toc_entry_get_parent (GstTocEntry * entry)
+{
+  g_return_val_if_fail (entry != NULL, NULL);
+
+  return entry->parent;
+}
+
 #ifndef GST_DISABLE_GST_DEBUG
 static void
 gst_toc_dump_entries (GList * entries, guint depth)
@@ -685,7 +823,15 @@ void
 gst_toc_dump (GstToc * toc)
 {
 #ifndef GST_DISABLE_GST_DEBUG
-  GST_TRACE ("        Toc %p, tags: %" GST_PTR_FORMAT, toc, toc->tags);
+  GST_TRACE ("        Toc %p, scope: %s, tags: %" GST_PTR_FORMAT, toc,
+      (toc->scope == GST_TOC_SCOPE_GLOBAL) ? "global" : "current", toc->tags);
   gst_toc_dump_entries (toc->entries, 2);
 #endif
 }
+
+void
+_priv_gst_toc_initialize (void)
+{
+  _gst_toc_type = gst_toc_get_type ();
+  _gst_toc_entry_type = gst_toc_entry_get_type ();
+}