[totem] backend: Add support for non-DVD chapters



commit 20800ab4f5bdd68e23629805881e3890ff785000
Author: Bastien Nocera <hadess hadess net>
Date:   Fri Mar 15 15:44:20 2013 +0100

    backend: Add support for non-DVD chapters
    
    We can now use the previous/next buttons and shortcuts to
    navigate chapters within Matroska files (and within other supported
    file types).
    
    https://bugzilla.gnome.org/show_bug.cgi?id=163546

 src/backend/bacon-video-widget.c |  132 +++++++++++++++++++++++++++++++++++++-
 1 files changed, 130 insertions(+), 2 deletions(-)
---
diff --git a/src/backend/bacon-video-widget.c b/src/backend/bacon-video-widget.c
index d6083e7..c282046 100644
--- a/src/backend/bacon-video-widget.c
+++ b/src/backend/bacon-video-widget.c
@@ -228,6 +228,7 @@ struct BaconVideoWidgetPrivate
   gdouble                      volume;
   gboolean                     is_menu;
   gboolean                     has_angles;
+  GList                       *chapters;
 
   BvwRotation                  rotation;
   
@@ -1929,6 +1930,53 @@ bvw_get_navigation_if_available (BaconVideoWidget *bvw)
 }
 
 static void
+bvw_handle_toc_message (GstMessage       *message,
+                       BaconVideoWidget *bvw)
+{
+  GstToc *toc;
+  GList *entries, *l;
+  guint i;
+
+  gst_message_parse_toc (message, &toc, NULL);
+  if (gst_toc_get_scope (toc) != GST_TOC_SCOPE_GLOBAL)
+    goto out;
+
+  entries = gst_toc_get_entries (toc);
+
+parse:
+  if (entries == NULL)
+    goto out;
+  if (gst_toc_entry_get_entry_type (entries->data) != GST_TOC_ENTRY_TYPE_CHAPTER) {
+    if (g_list_length (entries) == 1) {
+      entries = gst_toc_entry_get_sub_entries (entries->data);
+      goto parse;
+    }
+    goto out;
+  }
+
+  GST_DEBUG ("Found %d chapters", g_list_length (entries));
+
+  if (bvw->priv->chapters)
+    g_list_free_full (bvw->priv->chapters, (GDestroyNotify) gst_mini_object_unref);
+
+  for (l = entries, i = 0; l != NULL; l = l->next, i++) {
+    GstTocEntry *entry = l->data;
+    gint64 start, stop;
+
+    if (!gst_toc_entry_get_start_stop_times (entry, &start, &stop)) {
+      GST_DEBUG ("Chapter #%d (couldn't get times)", i);
+    } else {
+      GST_DEBUG ("Chapter #%d (start: %li stop: %li)", i, start, stop);
+    }
+  }
+
+  bvw->priv->chapters = g_list_copy_deep (entries, (GCopyFunc) gst_mini_object_ref, NULL);
+
+out:
+  gst_toc_unref (toc);
+}
+
+static void
 bvw_bus_message_cb (GstBus * bus, GstMessage * message, BaconVideoWidget *bvw)
 {
   GstMessageType msg_type;
@@ -2088,6 +2136,11 @@ bvw_bus_message_cb (GstBus * bus, GstMessage * message, BaconVideoWidget *bvw)
       break;
     }
 
+    case GST_MESSAGE_TOC: {
+       bvw_handle_toc_message (message, bvw);
+       break;
+    }
+
     /* FIXME: at some point we might want to handle CLOCK_LOST and set the
      * pipeline back to PAUSED and then PLAYING again to select a different
      * clock (this seems to trip up rtspsrc though so has to wait until
@@ -2577,6 +2630,11 @@ bacon_video_widget_finalize (GObject * object)
     bvw->priv->update_id = 0;
   }
 
+  if (bvw->priv->chapters) {
+    g_list_free_full (bvw->priv->chapters, (GDestroyNotify) gst_mini_object_unref);
+    bvw->priv->chapters = NULL;
+  }
+
   g_clear_pointer (&bvw->priv->tagcache, gst_tag_list_unref);
   g_clear_pointer (&bvw->priv->audiotags, gst_tag_list_unref);
   g_clear_pointer (&bvw->priv->videotags, gst_tag_list_unref);
@@ -2831,6 +2889,28 @@ bacon_video_widget_set_next_subtitle (BaconVideoWidget *bvw)
   bacon_video_widget_set_subtitle (bvw, current_text);
 }
 
+static gboolean
+bvw_chapter_compare_func (GstTocEntry      *entry,
+                         BaconVideoWidget *bvw)
+{
+  gint64 start, stop;
+
+  if (!gst_toc_entry_get_start_stop_times (entry, &start, &stop))
+    return -1;
+
+  if (bvw->priv->current_time >= start / GST_MSECOND &&
+      bvw->priv->current_time < stop / GST_MSECOND)
+    return 0;
+
+  return -1;
+}
+
+static GList *
+bvw_get_current_chapter (BaconVideoWidget *bvw)
+{
+  return g_list_find_custom (bvw->priv->chapters, bvw, (GCompareFunc) bvw_chapter_compare_func);
+}
+
 /**
  * bacon_video_widget_has_next_track:
  * @bvw: a #BaconVideoWidget
@@ -2843,6 +2923,8 @@ bacon_video_widget_set_next_subtitle (BaconVideoWidget *bvw)
 gboolean
 bacon_video_widget_has_next_track (BaconVideoWidget *bvw)
 {
+  GList *l;
+
   g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), FALSE);
 
   if (bvw->priv->mrl == NULL)
@@ -2851,6 +2933,10 @@ bacon_video_widget_has_next_track (BaconVideoWidget *bvw)
   if (g_str_has_prefix (bvw->priv->mrl, "dvd:/"))
     return TRUE;
 
+  l = bvw_get_current_chapter (bvw);
+  if (l != NULL && l->next != NULL)
+    return TRUE;
+
   return FALSE;
 }
 
@@ -2868,6 +2954,7 @@ bacon_video_widget_has_previous_track (BaconVideoWidget *bvw)
 {
   GstFormat fmt;
   gint64 val;
+  GList *l;
 
   g_return_val_if_fail (BACON_IS_VIDEO_WIDGET (bvw), FALSE);
 
@@ -2877,6 +2964,11 @@ bacon_video_widget_has_previous_track (BaconVideoWidget *bvw)
   if (g_str_has_prefix (bvw->priv->mrl, "dvd:/"))
     return TRUE;
 
+  /* Look in the chapters first */
+  l = bvw_get_current_chapter (bvw);
+  if (l != NULL && l->prev != NULL)
+    return TRUE;
+
   fmt = gst_format_get_by_nick ("chapter");
   /* If chapter isn't registered, then there's no chapters support */
   if (fmt == GST_FORMAT_UNDEFINED)
@@ -3854,6 +3946,11 @@ bacon_video_widget_close (BaconVideoWidget * bvw)
   if (bvw->priv->eos_id != 0)
     g_source_remove (bvw->priv->eos_id);
 
+  if (bvw->priv->chapters) {
+    g_list_free_full (bvw->priv->chapters, (GDestroyNotify) gst_mini_object_unref);
+    bvw->priv->chapters = NULL;
+  }
+
   g_clear_pointer (&bvw->priv->tagcache, gst_tag_list_unref);
   g_clear_pointer (&bvw->priv->audiotags, gst_tag_list_unref);
   g_clear_pointer (&bvw->priv->videotags, gst_tag_list_unref);
@@ -3948,6 +4045,35 @@ handle_dvd_seek (BaconVideoWidget *bvw,
   }
 }
 
+static gboolean
+handle_chapters_seek (BaconVideoWidget *bvw,
+                     gboolean          forward)
+{
+  GList *l;
+  GstTocEntry *entry;
+  gint64 start;
+
+  l = bvw_get_current_chapter (bvw);
+  if (!l)
+    return FALSE;
+
+  entry = NULL;
+  if (forward && l->next)
+    entry = l->next->data;
+  else if (!forward && l->prev)
+    entry = l->prev->data;
+
+  if (!entry)
+    return FALSE;
+
+  if (!gst_toc_entry_get_start_stop_times (entry, &start, NULL))
+    return FALSE;
+
+  GST_DEBUG ("Found chapter and seeking to %" G_GINT64_FORMAT, start / GST_MSECOND);
+
+  return bacon_video_widget_seek_time (bvw, start / GST_MSECOND, FALSE, NULL);
+}
+
 /**
  * bacon_video_widget_dvd_event:
  * @bvw: a #BaconVideoWidget
@@ -4002,10 +4128,12 @@ bacon_video_widget_dvd_event (BaconVideoWidget * bvw,
       bvw_do_navigation_command (bvw, GST_NAVIGATION_COMMAND_ACTIVATE);
       break;
     case BVW_DVD_NEXT_CHAPTER:
-      handle_dvd_seek (bvw, 1, "chapter");
+      if (!handle_chapters_seek (bvw, TRUE))
+       handle_dvd_seek (bvw, 1, "chapter");
       break;
     case BVW_DVD_PREV_CHAPTER:
-      handle_dvd_seek (bvw, -1, "chapter");
+      if (!handle_chapters_seek (bvw, FALSE))
+       handle_dvd_seek (bvw, -1, "chapter");
       break;
     case BVW_DVD_NEXT_TITLE:
       handle_dvd_seek (bvw, 1, "title");


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]