From e2170cbdc7d6f0b6dd0693f683a1ada76ffda2bf Mon Sep 17 00:00:00 2001
From: Rafostar <rafostar.github@gmail.com>
Date: Tue, 12 Jan 2021 13:23:19 +0100
Subject: [PATCH] player: add TOC support

Retrive and store TOC data as part of global MediaInfo object.
This is useful for obtaining various video info stored in TOC (e.g. chapters).
---
 .../gst/player/gstplayer-media-info-private.h |  1 +
 gst-libs/gst/player/gstplayer-media-info.c    | 19 ++++++++
 gst-libs/gst/player/gstplayer-media-info.h    |  3 ++
 gst-libs/gst/player/gstplayer.c               | 46 +++++++++++++++++++
 4 files changed, 69 insertions(+)

diff --git a/gst-libs/gst/player/gstplayer-media-info-private.h b/gst-libs/gst/player/gstplayer-media-info-private.h
index 70aaea925..5c9902287 100644
--- a/gst-libs/gst/player/gstplayer-media-info-private.h
+++ b/gst-libs/gst/player/gstplayer-media-info-private.h
@@ -99,6 +99,7 @@ struct _GstPlayerMediaInfo
   gchar *container;
   gboolean seekable, is_live;
   GstTagList *tags;
+  GstToc *toc;
   GstSample *image_sample;
 
   GList *stream_list;
diff --git a/gst-libs/gst/player/gstplayer-media-info.c b/gst-libs/gst/player/gstplayer-media-info.c
index 6c9a4a887..4b31e8a3e 100644
--- a/gst-libs/gst/player/gstplayer-media-info.c
+++ b/gst-libs/gst/player/gstplayer-media-info.c
@@ -430,6 +430,9 @@ gst_player_media_info_finalize (GObject * object)
 
   g_free (info->container);
 
+  if (info->toc)
+    gst_toc_unref (info->toc);
+
   if (info->image_sample)
     gst_sample_unref (info->image_sample);
 
@@ -566,6 +569,8 @@ gst_player_media_info_copy (GstPlayerMediaInfo * ref)
   info->is_live = ref->is_live;
   if (ref->tags)
     info->tags = gst_tag_list_ref (ref->tags);
+  if (ref->toc)
+    info->toc = gst_toc_ref (ref->toc);
   if (ref->title)
     info->title = g_strdup (ref->title);
   if (ref->container)
@@ -751,6 +756,20 @@ gst_player_media_info_get_tags (const GstPlayerMediaInfo * info)
   return info->tags;
 }
 
+/**
+ * gst_player_media_info_get_toc:
+ * @info: a #GstPlayerMediaInfo
+ *
+ * Returns: (transfer none): the toc contained in media info.
+ */
+GstToc *
+gst_player_media_info_get_toc (const GstPlayerMediaInfo * info)
+{
+  g_return_val_if_fail (GST_IS_PLAYER_MEDIA_INFO (info), NULL);
+
+  return info->toc;
+}
+
 /**
  * gst_player_media_info_get_title:
  * @info: a #GstPlayerMediaInfo
diff --git a/gst-libs/gst/player/gstplayer-media-info.h b/gst-libs/gst/player/gstplayer-media-info.h
index ab034b158..8f6220ad9 100644
--- a/gst-libs/gst/player/gstplayer-media-info.h
+++ b/gst-libs/gst/player/gstplayer-media-info.h
@@ -232,6 +232,9 @@ guint         gst_player_media_info_get_number_of_subtitle_streams (const GstPla
 GST_PLAYER_API
 GstTagList*   gst_player_media_info_get_tags (const GstPlayerMediaInfo *info);
 
+GST_PLAYER_API
+GstToc*   gst_player_media_info_get_toc (const GstPlayerMediaInfo *info);
+
 GST_PLAYER_API
 const gchar*  gst_player_media_info_get_title (const GstPlayerMediaInfo *info);
 
diff --git a/gst-libs/gst/player/gstplayer.c b/gst-libs/gst/player/gstplayer.c
index 62d5cb487..76c2341ba 100644
--- a/gst-libs/gst/player/gstplayer.c
+++ b/gst-libs/gst/player/gstplayer.c
@@ -182,6 +182,7 @@ struct _GstPlayer
   gint buffering;
 
   GstTagList *global_tags;
+  GstToc *global_toc;
   GstPlayerMediaInfo *media_info;
 
   GstElement *current_vis_element;
@@ -542,6 +543,8 @@ gst_player_finalize (GObject * object)
   g_free (self->subtitle_sid);
   if (self->global_tags)
     gst_tag_list_unref (self->global_tags);
+  if (self->global_toc)
+    gst_toc_unref (self->global_toc);
   if (self->video_renderer)
     g_object_unref (self->video_renderer);
   if (self->signal_dispatcher)
@@ -1120,6 +1123,11 @@ emit_error (GstPlayer * self, GError * err)
     self->global_tags = NULL;
   }
 
+  if (self->global_toc) {
+    gst_toc_unref (self->global_toc);
+    self->global_toc = NULL;
+  }
+
   self->seek_pending = FALSE;
   remove_seek_source (self);
   self->seek_position = GST_CLOCK_TIME_NONE;
@@ -1819,6 +1827,37 @@ tags_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
   gst_tag_list_unref (tags);
 }
 
+static void
+toc_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
+{
+  GstPlayer *self = GST_PLAYER (user_data);
+  GstToc *toc = NULL;
+
+  gst_message_parse_toc (msg, &toc, NULL);
+
+  GST_DEBUG_OBJECT (self, "received %s toc",
+      gst_toc_get_scope (toc) == GST_TOC_SCOPE_GLOBAL ? "global" : "stream");
+
+  if (gst_toc_get_scope (toc) == GST_TOC_SCOPE_GLOBAL) {
+    g_mutex_lock (&self->lock);
+    if (self->media_info) {
+      if (self->media_info->toc)
+        gst_toc_unref (self->media_info->toc);
+      self->media_info->toc = gst_toc_ref (toc);
+      media_info_update (self, self->media_info);
+      g_mutex_unlock (&self->lock);
+      emit_media_info_updated_signal (self);
+    } else {
+      if (self->global_toc)
+        gst_toc_unref (self->global_toc);
+      self->global_toc = gst_toc_ref (toc);
+      g_mutex_unlock (&self->lock);
+    }
+  }
+
+  gst_toc_unref (toc);
+}
+
 static void
 element_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
 {
@@ -2743,8 +2782,10 @@ gst_player_media_info_create (GstPlayer * self)
   media_info = gst_player_media_info_new (self->uri);
   media_info->duration = gst_player_get_duration (self);
   media_info->tags = self->global_tags;
+  media_info->toc = self->global_toc;
   media_info->is_live = self->is_live;
   self->global_tags = NULL;
+  self->global_toc = NULL;
 
   query = gst_query_new_seeking (GST_FORMAT_TIME);
   if (gst_element_query (self->playbin, query))
@@ -2969,6 +3010,7 @@ gst_player_main (gpointer data)
   g_signal_connect (G_OBJECT (bus), "message::element",
       G_CALLBACK (element_cb), self);
   g_signal_connect (G_OBJECT (bus), "message::tag", G_CALLBACK (tags_cb), self);
+  g_signal_connect (G_OBJECT (bus), "message::toc", G_CALLBACK (toc_cb), self);
 
   if (self->use_playbin3) {
     g_signal_connect (G_OBJECT (bus), "message::stream-collection",
@@ -3273,6 +3315,10 @@ gst_player_stop_internal (GstPlayer * self, gboolean transient)
     gst_tag_list_unref (self->global_tags);
     self->global_tags = NULL;
   }
+  if (self->global_toc) {
+    gst_toc_unref (self->global_toc);
+    self->global_toc = NULL;
+  }
   self->seek_pending = FALSE;
   remove_seek_source (self);
   self->seek_position = GST_CLOCK_TIME_NONE;
-- 
2.28.0

