[gnome-subtitles] Switched to gtksink to display gstreamer video



commit 18cb1460594b558f0f70dce1c6102ad716d8d4e3
Author: Pedro Castro <pedro gnomesubtitles org>
Date:   Sun Jul 1 18:48:26 2018 +0100

    Switched to gtksink to display gstreamer video
    
    Gstreamer was previously embedded by using gst-video-overlay
    and using the Xid of the UI's socket widget. This presented
    a number of flows. The implementation now uses gtksink, together
    with the gst-playbin element that was already in use.

 src/External/GStreamerPlaybin/Engine.cs            | 71 ++++++++++--------
 src/External/GStreamerPlaybin/main.c               | 87 +++++++++++++---------
 .../PlayerCouldNotInitiateEngineException.cs       |  5 +-
 src/GnomeSubtitles/Ui/VideoPreview/Player.cs       | 55 ++++++++------
 4 files changed, 132 insertions(+), 86 deletions(-)
---
diff --git a/src/External/GStreamerPlaybin/Engine.cs b/src/External/GStreamerPlaybin/Engine.cs
index c7c1691..d1ef937 100644
--- a/src/External/GStreamerPlaybin/Engine.cs
+++ b/src/External/GStreamerPlaybin/Engine.cs
@@ -1,7 +1,7 @@
 /*
 
        Copyright (C) 2007 Goran Sterjov, Pedro Castro
-       Copyright (C) 2011 Pedro Castro
+       Copyright (C) 2011-2018 Pedro Castro
 
     This file is part of the GStreamer Playbin Wrapper.
     Derived from Fuse.
@@ -21,11 +21,27 @@
     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
 
-
-
+using Gtk;
 using System;
 using System.Runtime.InteropServices;
 
+/*
+ * There are a number of ways to integrate gstreamer into an application. This implementation uses a 
gstreamer
+ * playbin (which is a gstreamer pipeline with a number of additional features) with a video sink set to a 
+ * gtksink (instead of the default "auto" video sink). Once initialized, this video sink allows returning
+ * its GtkWidget, which is then added to the application UI. Most of the hard work is done in the auxiliary
+ * gstreamer_playbin C library, whereas here we use bindings to that library.
+ * 
+ * Previously, this implementation relied on gst_video_overlay_set_window_handle which received the X11 id
+ * of the Gdk.Window to which we wanted gstreamer to attach to, however this implementation presented many
+ * glitches which weren't simple to solve (ex: screen went black when switching between apps, and also when
+ * updating the current subtitle while the video was paused).
+ *
+ * Reference to other ways to implement gstreamer into a GTK application:
+ *   - https://github.com/GNOME/clutter-gst (does a lot more than a simple integration; used in Totem)
+ *   - https://developer.gnome.org/totem/stable/BaconVideoWidget.html (Totem bacon video widget)
+ */
+
 
 namespace GStreamer
 {
@@ -95,11 +111,11 @@ namespace GStreamer
                /// Load the GStreamer library and attach it
                /// to the specified window.
                /// </summary>
-               public bool Initiate (ulong x_window_id)
+               public bool Initiate ()
                {
 
                        // load the gstreamer library
-                       IntPtr ptr = gst_binding_init (x_window_id);
+                       IntPtr ptr = gst_binding_init ();
 
                        if(ptr == IntPtr.Zero)
                        {
@@ -124,19 +140,10 @@ namespace GStreamer
 
 
                        status = MediaStatus.Unloaded;
+                       
                        return true;
                }
-
-
-               /// <summary>
-               /// Load the GStreamer library.
-               /// </summary>
-               public bool Initiate ()
-               {
-                       return Initiate (0);
-               }
-
-
+               
 
                /// <summary>
                /// Disposes the GStreamer library.
@@ -200,13 +207,13 @@ namespace GStreamer
                        }
                }
 
-               /// <summary>
-               /// Changes the window to which video playback is attached to.
-               /// </summary>
-               public void SetWindow (ulong window_id)
-               {
-                       gst_binding_set_xid (engine, window_id);
-               }
+               ///// <summary>
+               ///// Changes the window to which video playback is attached to.
+               ///// </summary>
+               //public void SetWindow (ulong window_id)
+               //{
+               //      gst_binding_set_xid (engine, window_id);
+               //}
 
 
 
@@ -374,6 +381,11 @@ namespace GStreamer
                {
                        get { return status; }
                }
+               
+               public Widget GetVideoWidget ()
+               {
+                       return new Widget(gst_binding_get_video_widget(engine));
+               }
 
 
 
@@ -458,8 +470,7 @@ namespace GStreamer
                        else
                                return null;
                }
-
-
+               
 
                // private convenience properties
                bool isPlaying { get{ return status == MediaStatus.Playing; } }
@@ -468,7 +479,7 @@ namespace GStreamer
 
                // core engine functions
                [DllImport("gstreamer_playbin")]
-               static extern IntPtr gst_binding_init (ulong xwin);
+               static extern IntPtr gst_binding_init ();
                [DllImport("gstreamer_playbin")]
                static extern void gst_binding_deinit (HandleRef play);
                [DllImport("gstreamer_playbin")]
@@ -479,8 +490,8 @@ namespace GStreamer
                static extern void gst_binding_pause (HandleRef play);
                [DllImport("gstreamer_playbin")]
                static extern void gst_binding_unload (HandleRef play);
-               [DllImport("gstreamer_playbin")]
-               static extern void gst_binding_set_xid (HandleRef play, ulong xid);
+               //[DllImport("gstreamer_playbin")]
+               //static extern void gst_binding_set_xid (HandleRef play, ulong xid);
 
 
                // engine property functions
@@ -520,9 +531,11 @@ namespace GStreamer
                [DllImport("gstreamer_playbin")]
                static extern void gst_binding_set_visual (HandleRef play, string vis_name);
 
-
                [DllImport("gstreamer_playbin")]
                static extern IntPtr gst_binding_get_visuals_list (HandleRef play);
+               
+               [DllImport("gstreamer_playbin")]
+               static extern IntPtr gst_binding_get_video_widget (HandleRef play);
        }
 
 
diff --git a/src/External/GStreamerPlaybin/main.c b/src/External/GStreamerPlaybin/main.c
index dce6bf9..5f91415 100644
--- a/src/External/GStreamerPlaybin/main.c
+++ b/src/External/GStreamerPlaybin/main.c
@@ -22,7 +22,7 @@
 */
 
 #include <gst/gst.h>
-#include <gst/video/videooverlay.h>
+//#include <gst/video/videooverlay.h>
 #include <gst/tag/tag.h>
 #include <string.h>
 
@@ -63,8 +63,9 @@ struct gstTag
 struct gstPlay
 {
        GstElement *element;
-       gulong xid;
-       GstVideoOverlay *overlay;
+       GstElement *video_element;
+//     gulong xid;
+//     GstVideoOverlay *overlay;
 
        gchar *vis_name;
 
@@ -86,23 +87,23 @@ gboolean gst_binding_load_video_info(gstPlay *play);
 gboolean gst_binding_has_video(gstPlay *play);
 gboolean gst_binding_has_audio(gstPlay *play);
 
-static GstBusSyncReply
-gst_sync_watch(GstBus *bus, GstMessage *message, gpointer data)
-{
-       gstPlay *play = (gstPlay *)data;
-       if (play == NULL)
-               return FALSE;
-
-       if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_ELEMENT)
-       {
-               if (gst_is_video_overlay_prepare_window_handle_message(message))
-               {
-                       play->overlay = GST_VIDEO_OVERLAY(GST_MESSAGE_SRC(message));
-                       gst_video_overlay_set_window_handle(play->overlay, play->xid);
-               }
-       }
-       return TRUE;
-}
+// static GstBusSyncReply
+// gst_sync_watch(GstBus *bus, GstMessage *message, gpointer data)
+// {
+//     gstPlay *play = (gstPlay *)data;
+//     if (play == NULL)
+//             return FALSE;
+
+//     if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_ELEMENT)
+//     {
+//             if (gst_is_video_overlay_prepare_window_handle_message(message))
+//             {
+//                     play->overlay = GST_VIDEO_OVERLAY(GST_MESSAGE_SRC(message));
+//                     //gst_video_overlay_set_window_handle(play->overlay, play->xid);
+//             }
+//     }
+//     return TRUE;
+// }
 
 static gboolean
 gst_async_watch(GstBus *bus, GstMessage *message, gpointer data)
@@ -230,7 +231,8 @@ gboolean isValid(gstPlay *play)
 }
 
 // initiates gstreamer as a playbin pipeline
-gstPlay *gst_binding_init(gulong xwin)
+//gstPlay *gst_binding_init(gulong xwin)
+gstPlay *gst_binding_init()
 {
        gstPlay *play = g_new0(gstPlay, 1);
 
@@ -238,16 +240,33 @@ gstPlay *gst_binding_init(gulong xwin)
        play->element = gst_element_factory_make("playbin", "play");
        if (play->element == NULL)
                return NULL;
-       play->xid = xwin;
+       
+//     play->xid = xwin;
+
+       play->video_element = gst_element_factory_make ("gtksink", "gtksink");
+       if (play->video_element == NULL) {
+               return NULL;
+       }
+
+       g_object_set(G_OBJECT(play->element), "video-sink", play->video_element, NULL);
 
-       gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(play->element)),
-                                                        gst_sync_watch, play, NULL);
+//     gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(play->element)),
+//                                                      gst_sync_watch, play, NULL);
        gst_bus_add_watch(gst_pipeline_get_bus(GST_PIPELINE(play->element)),
                                          gst_async_watch, play);
 
        return play;
 }
 
+/* Gets the GtkWidget inside the gtksink video element */
+void *gst_binding_get_video_widget(gstPlay *play)
+{
+       void *widget;
+       g_object_get (play->video_element, "widget", &widget, NULL);
+       return widget;
+}
+
+
 // releases any references to gstreamer
 void gst_binding_deinit(gstPlay *play)
 {
@@ -520,15 +539,15 @@ gboolean gst_binding_load_video_info(gstPlay *play)
        return FALSE;
 }
 
-void gst_binding_set_xid(gstPlay *play, gulong xid)
-{
-       if (play == NULL)
-               return;
+// void gst_binding_set_xid(gstPlay *play, gulong xid)
+// {
+//     if (play == NULL)
+//             return;
 
-       play->xid = xid;
-       if (play->overlay != NULL && GST_IS_VIDEO_OVERLAY(play->overlay))
-               gst_video_overlay_set_window_handle(play->overlay, xid);
-}
+//     play->xid = xid;
+//     if (play->overlay != NULL && GST_IS_VIDEO_OVERLAY(play->overlay))
+//             gst_video_overlay_set_window_handle(play->overlay, xid);
+// }
 
 void gst_binding_set_eos_cb(gstPlay *play, eosCallback cb)
 {
@@ -616,8 +635,8 @@ done:
 static void
 setup_vis(gstPlay *play)
 {
-       if (play->xid == 0)
-               return;
+//     if (play->xid == 0)
+//             return;
 
        GstElement *vis_bin = NULL;
        GstElement *vis_element = NULL;
diff --git a/src/GnomeSubtitles/Ui/VideoPreview/Exceptions/PlayerCouldNotInitiateEngineException.cs 
b/src/GnomeSubtitles/Ui/VideoPreview/Exceptions/PlayerCouldNotInitiateEngineException.cs
index 3cb5b43..8e5bd87 100644
--- a/src/GnomeSubtitles/Ui/VideoPreview/Exceptions/PlayerCouldNotInitiateEngineException.cs
+++ b/src/GnomeSubtitles/Ui/VideoPreview/Exceptions/PlayerCouldNotInitiateEngineException.cs
@@ -1,6 +1,6 @@
 /*
  * This file is part of Gnome Subtitles.
- * Copyright (C) 2008 Pedro Castro
+ * Copyright (C) 2008-2018 Pedro Castro
  *
  * Gnome Subtitles is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -23,6 +23,9 @@ namespace GnomeSubtitles.Ui.VideoPreview.Exceptions {
 
 public class PlayerCouldNotInitiateEngineException : ApplicationException {
 
+       public PlayerCouldNotInitiateEngineException (string message) : base(message) {
+       }
+
 }
 
 }
diff --git a/src/GnomeSubtitles/Ui/VideoPreview/Player.cs b/src/GnomeSubtitles/Ui/VideoPreview/Player.cs
index 35a0dee..73a6874 100644
--- a/src/GnomeSubtitles/Ui/VideoPreview/Player.cs
+++ b/src/GnomeSubtitles/Ui/VideoPreview/Player.cs
@@ -17,7 +17,6 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
-using Gdk;
 using GnomeSubtitles.Ui.VideoPreview.Exceptions;
 using GStreamer;
 using Gtk;
@@ -34,7 +33,7 @@ public delegate void VideoDurationEventHandler (TimeSpan duration);
 public class Player {
 
        private AspectFrame frame = null;
-       private Socket socket = null;
+       //private Socket socket = null;
        private Playbin playbin = null;
        private PlayerPositionWatcher position = null;
        private bool hasFoundDuration = false;
@@ -51,11 +50,11 @@ public class Player {
        public Player (AspectFrame aspectFrame) {
                this.frame = aspectFrame;
 
-               InitializeSocket();
+               //InitializeSocket();
                InitializePositionWatcher();
                InitializePlaybin();
        }
-
+       
 
        /* Events */
        public event PlayerErrorEventHandler Error;
@@ -180,31 +179,43 @@ public class Player {
 
        /* Private members */
 
-       private void InitializeSocket () {
-               socket = new Socket();
 
-               /* Set the socket background color. If we don't do this, the socket/video
-                * area will show a copy of the UI elements (menu, labels, other
-                * components) when the video isn't loaded.
-                */
-               RGBA black = new RGBA();
-               black.Red = 0;
-               black.Green = 0;
-               black.Blue = 0;
-               black.Alpha = 1;
-               socket.OverrideBackgroundColor(StateFlags.Normal, black);
+       //private void InitializeSocket () {
+       //      socket = new Socket();
 
-               frame.Child = socket;
+       //      /* Set the socket background color. If we don't do this, the socket/video
+       //       * area will show a copy of the UI elements (menu, labels, other
+       //       * components) when the video isn't loaded.
+       //       */
+       //      RGBA black = new RGBA();
+       //      black.Red = 0;
+       //      black.Green = 0;
+       //      black.Blue = 0;
+       //      black.Alpha = 1;
+       //      socket.OverrideBackgroundColor(StateFlags.Normal, black);
+       //      socket.DoubleBuffered = false;
 
-               socket.Realize();
-               socket.Show();
-       }
+       //      frame.Child = socket;
+
+       //      socket.Realize();
+       //      socket.Show();
+       //}
 
        private void InitializePlaybin () {
                playbin = new Playbin();
 
-               if (!playbin.Initiate(socket.Id))
-                       throw new PlayerCouldNotInitiateEngineException();
+               if (!playbin.Initiate()) {
+                       throw new PlayerCouldNotInitiateEngineException("Unable to initiate the playbin 
engine");
+               }
+               
+               Widget videoWidget = playbin.GetVideoWidget();
+               if (videoWidget == null) {
+                       throw new PlayerCouldNotInitiateEngineException("Unable to get the video widget from 
the playbin engine");
+               }
+
+               frame.Child = videoWidget;
+               videoWidget.Realize();
+               videoWidget.Show();
 
                playbin.Error += OnPlaybinError;
                playbin.EndOfStream += OnPlaybinEndOfStream;


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