[gnome-subtitles/gstreamer-sharp-bindings] Reworked to use gstreamer-sharp instead of the custom playbin bindings



commit c40b57b457a16d0b82cd52db711d623657cce543
Author: Pedro Castro <pedro gnomesubtitles org>
Date:   Thu Feb 3 23:21:25 2022 +0000

    Reworked to use gstreamer-sharp instead of the custom playbin bindings
    
    Also changed to GLibSharp/GtkSharp instead of the original gtk-sharp
    bindings.

 src/External/GStreamer/GstBackend.cs             | 252 +++++++++++++++++++----
 src/GnomeSubtitles/Core/Base.cs                  |  14 +-
 src/GnomeSubtitles/Execution/ExecutionContext.cs |   6 +-
 src/GnomeSubtitles/Ui/VideoPreview/Player.cs     |   6 +-
 4 files changed, 222 insertions(+), 56 deletions(-)
---
diff --git a/src/External/GStreamer/GstBackend.cs b/src/External/GStreamer/GstBackend.cs
index 5e7f6dc..ccdf649 100644
--- a/src/External/GStreamer/GstBackend.cs
+++ b/src/External/GStreamer/GstBackend.cs
@@ -17,13 +17,12 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
-using GnomeSubtitles.Core;
 using GnomeSubtitles.Ui.VideoPreview;
 using Gst;
+using Gst.PbUtils;
 using Mono.Unix;
 using SubLib.Util;
 using System;
-using System.Runtime.InteropServices;
 
 namespace External.GStreamer {
 
@@ -32,33 +31,45 @@ namespace External.GStreamer {
                private Element playbin = null;
                private Element gtkSink = null;
                private GstMediaInfo mediaInfo = null;
+               private float speed = 1f; //Keeping it here as we need to pass it to every 'seek' call
 
-               /* Enums */
-               private enum ErrorType { GStreamer, NoMediaInfo, NoVideoOrAudio };
-
-               /* Error messages */
-               private readonly string ErrorMessageNoMediaInfo = Catalog.GetString("Unable to obtain the 
complete media information.");
-
-               public override string Name => throw new NotImplementedException();
-
-               public override long CurrentPosition => throw new NotImplementedException();
-
-               public override long Duration => throw new NotImplementedException();
-
-               public override bool HasVideo => throw new NotImplementedException();
-
-               public override float AspectRatio => throw new NotImplementedException();
+               public override string Name {
+                       get { return "GStreamer"; }
+               }
+               
+               public override long CurrentPosition {
+                       get { return GetCurrentPosition(); }
+               }
+               
+               public override long Duration {
+                       get { return mediaInfo.Duration; }
+               }
+               
+               public override bool HasVideo {
+                       get { return mediaInfo.HasVideo; }
+               }
+               
+               public override float AspectRatio {
+                       get { return mediaInfo.AspectRatio; }
+               }
 
-               public override float FrameRate => throw new NotImplementedException();
+               public override float FrameRate {
+                       get { return mediaInfo.FrameRate; }
+               }
 
-               public override bool HasAudio => throw new NotImplementedException();
+               public override bool HasAudio {
+                       get { return mediaInfo.HasAudio; }
+               }
 
                public override Gtk.Widget CreateVideoWidget () {
-                       throw new NotImplementedException();
+                       return gtkSink["widget"] as Gtk.Widget;
                }
+               
+               
+               /* Public methods */
 
                public override void Dispose () {
-                       throw new NotImplementedException();
+                       //Do nothing
                }
 
                public override void Initialize () {
@@ -73,36 +84,64 @@ namespace External.GStreamer {
                        playbin = ElementFactory.Make("playbin", "playbin");
                        gtkSink = ElementFactory.Make("gtksink", "gtksink");
 
-                       playbin.Data ["video-sink"] = gtkSink; //FIXME funciona?
-                                                                                                  
//g_object_set(G_OBJECT(backend->element), "video-sink", backend->video_element, NULL);
+                       playbin["video-sink"] = gtkSink;
 
                        playbin.Bus.AddSignalWatch();
                        playbin.Bus.Message += OnGstMessage;
                }
 
                public override bool Load (string uri) {
-                       throw new NotImplementedException();
+                       SetStatus(MediaStatus.Loading);
+                       
+                       playbin["uri"]  = uri;
+                       return playbin.SetState(State.Paused) != StateChangeReturn.Failure;
                }
 
                public override void Pause () {
-                       throw new NotImplementedException();
+                       playbin.SetState(State.Paused);
+                       SetStatus(MediaStatus.Paused);
                }
 
                public override void Play () {
-                       throw new NotImplementedException();
+                       playbin.SetState(State.Playing);
+                       SetStatus(MediaStatus.Playing);
                }
 
                public override void Seek (long time, bool isAbsolute) {
-                       throw new NotImplementedException();
+                       if ((Status == MediaStatus.Unloaded) || (Status == MediaStatus.Loading)) {
+                               return;
+                       }
+                       
+                       long newPosition = isAbsolute ? time : GetCurrentPosition() + time;
+
+                       /* Note: gst_element_seek_simple can't be used here because it always resets speed to 
1, 
+                        * and we need to keep the current speed.
+                        */
+                       playbin.Seek(speed, Format.Time, SeekFlags.Flush, SeekType.Set, newPosition * 
Gst.Constants.MSECOND, SeekType.None, 0);
                }
 
                public override void SetSpeed (float speed) {
-                       throw new NotImplementedException();
+                       if ((Status == MediaStatus.Unloaded) || (Status == MediaStatus.Loading)) {
+                               return;
+                       }
+                       
+                       this.speed = speed;
+                       playbin.Seek(speed, Format.Time, SeekFlags.Flush, SeekType.Set, GetCurrentPosition() 
* Gst.Constants.MSECOND, SeekType.None, 0);
                }
 
                public override void Unload () {
-                       throw new NotImplementedException();
+                       if (playbin != null) {
+                               playbin.SetState(State.Null);
+                               playbin.Unref();
+                               playbin = null;
+                       }
+
+                       SetStatus(MediaStatus.Unloaded);
+                                               
+                       mediaInfo = null;
+                       speed = 1f;
                }
+                               
 
                /* Private members */
 
@@ -156,30 +195,167 @@ namespace External.GStreamer {
                        }
 
                        if (mediaInfo.HasVideo) {
-                               LoadVideoInfo();
-                               //load_media_info_video(backend);
+                               if (!LoadVideoInfo(mediaInfo)) {
+                                       TriggerNoMediaInfoError();
+                                       return;
+                               }
                        }
 
                        LoadMediaDuration();
-                       //load_media_info_duration(backend);
                }
                
                private bool CheckIfMediaHasVideo() {
-                       throw new NotImplementedException();
+                       return (int)playbin["current-video"] != -1;
                }
                
                private bool CheckIfMediaHasAudio() {
-                       throw new NotImplementedException();
+                       return (int)playbin["current-audio"] != -1;
+               }
+               
+               private bool LoadVideoInfo(GstMediaInfo info) {
+                       Pad videoPad = gtkSink.GetStaticPad("sink");
+                       if (videoPad == null) {
+                               Logger.Error("[GstBackend] Unable to obtain the video pad");
+                               return false;
+                       }
+                       
+                       Caps caps = videoPad.CurrentCaps;
+                       if (caps == null) {
+                               Logger.Error("[GstBackend] Unable to obtain the video caps");
+                               return false;
+                       }
+                       
+                       bool gotWidth = false, gotHeight = false, gotFrameRate = false;
+                       foreach (Structure structure in caps) {
+                               string name = structure.Name;
+                               if (String.IsNullOrEmpty(name) || !name.StartsWith("video", 
StringComparison.Ordinal)) {
+                                       continue;
+                               }
+                               
+                               /* Get the values */
+                               
+                               if (!gotWidth) {
+                                       int width = -1;
+                                       if (structure.GetInt("width", out width)) {
+                                               info.Width = width;
+                                               gotWidth = true;
+                                       }
+                               }
+                               
+                               if (!gotHeight) {
+                                       int height = -1;
+                                       if (structure.GetInt("height", out height)) {
+                                               info.Height = height;
+                                               gotHeight = true;
+                                       }
+                               }
+                               
+                               if (!gotFrameRate) {
+                                       int numerator = -1, denominator = -1;
+                                       if (structure.GetFraction("framerate", out numerator, out 
denominator)) {
+                                               info.FrameRate = (float)numerator / (float)denominator;
+                                               gotFrameRate = true;
+                                       }
+                               }
+                               
+                               if (gotWidth && gotHeight && gotFrameRate) {
+                                       break;
+                               }
+                       }
+
+
+                       /* Check if we got everything we needed */
+                       
+                       if (!gotWidth || !gotHeight || !gotFrameRate) {
+                               Logger.Error("[GstBackend] Unable to obtain the video width, height or frame 
rate. GotWidth={0}, GotHeight={1}, GotFrameRate={2}", gotWidth, gotHeight, gotFrameRate);
+                               return false;
+                       }
+                       
+                       info.AspectRatio = (float)mediaInfo.Width / (float)mediaInfo.Height;
+                       
+                       return true;
+               }
+               
+               /**
+                * Performs an async query when the default method for obtaining the duration fails.
+                */
+               private void LoadMediaDuration() {
+                       long duration = -1;     
+                       if (playbin.QueryDuration(Format.Time, out duration)) {
+                               mediaInfo.Duration = duration / Gst.Constants.MSECOND;
+                               TriggerLoadComplete();
+                               return;
+                       }
+       
+                       /* Usually we can query the duration above. However, there are
+                        * some files where this doesn't happen (for example, some audio files
+                        * where the duration is only computed when starting to play the file).
+                        * In those cases, the GstDiscoverer is used below.
+                        */
+                       Logger.Info("[GstBackend] Unable to query the media duration. Using the discoverer.");
+                       
+                       
+                       string uri = playbin["current-uri"] as String;
+                       Discoverer discoverer = new Discoverer(Gst.Constants.SECOND);
+                       if (discoverer == null) {
+                               Logger.Error("[GstBackend] Unable to get the gstreamer discoverer");
+                               TriggerNoMediaInfoError();
+                               return;
+                       }
+
+                       discoverer.Discovered += OnDiscovered;
+                       discoverer.Start();
+                       
+                       if (!discoverer.DiscoverUriAsync(uri)) {
+                               Logger.Error("[GstBackend] Failed to start the discoverer");
+                               TriggerNoMediaInfoError();
+                               return;
+                       }
                }
                
-               private bool LoadVideoInfo() {
-                       throw new NotImplementedException();
+               private long GetCurrentPosition() {
+                       long position = -1;
+                       
+                       /* Try to query the position */
+                       if (playbin.QueryPosition(Format.Time, out position)) {
+                               return position / Gst.Constants.MSECOND;
+                       }
+                       
+                       /* Query position wasn't successful. This is usually due to a pending state change,
+                        * which happens for example after seeking, so we wait for the state change to 
complete
+                        * and try again.
+                        */
+                       WaitForStateChangeToComplete();
+                       if (playbin.QueryPosition(Format.Time, out position)) {
+                               return position / Gst.Constants.MSECOND;
+                       }
+                       
+                       /* If we're here, we're still unable to return the position after waiting for the 
state
+                        * change to complete. Return -1. */
+                       Logger.Error("[GstBackend] Unable to query the current position");
+                       return -1;
+               }
+
+               private void WaitForStateChangeToComplete () {
+                       State state;
+                       State pending;
+                       playbin.GetState(out state, out pending, Gst.Constants.CLOCK_TIME_NONE);
+               }
+
+               private void TriggerLoadComplete () {
+                       SetStatus(MediaStatus.Loaded);
                }
                
-               private bool LoadMediaDuration() {
-                       throw new NotImplementedException();
+               private void OnDiscovered (object o, DiscoveredArgs args) {
+                       mediaInfo.Duration = (long)args.Info.Duration / Gst.Constants.MSECOND;
+                       TriggerLoadComplete();                  
                }
 
+               private void TriggerNoMediaInfoError() {
+                       TriggerErrorFound(Catalog.GetString("Unable to obtain the complete media 
information."));
+               }
+               
+
        }
 
 }
\ No newline at end of file
diff --git a/src/GnomeSubtitles/Core/Base.cs b/src/GnomeSubtitles/Core/Base.cs
index eb9874d..6ecfd2f 100644
--- a/src/GnomeSubtitles/Core/Base.cs
+++ b/src/GnomeSubtitles/Core/Base.cs
@@ -1,6 +1,6 @@
 /*
  * This file is part of Gnome Subtitles.
- * Copyright (C) 2006-2019 Pedro Castro
+ * Copyright (C) 2006-2022 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
@@ -224,19 +224,9 @@ public static class Base {
 
        public static void Open (string path, Encoding encoding, Uri videoUri) {
                OpenDocument(path, encoding);
-               
-               OpenVideo(videoUri); //FIXME
-               //videoUriToOpenWithTimeout = videoUri; //FIXME
-               //GLib.Timeout.Add(1000, OpenVideoWithTimeout); //FIXME
+               OpenVideo(videoUri);
        }
        
-       /* Nasty hack while there isn't a proper fix for #184 */
-       private static Uri videoUriToOpenWithTimeout = null;
-       private static bool OpenVideoWithTimeout() {
-               OpenVideo(videoUriToOpenWithTimeout);
-               return false;
-       }
-
        public static void OpenTranslation (string path, Encoding encoding) {
                if (document.IsTranslationLoaded)
                        CloseTranslation();
diff --git a/src/GnomeSubtitles/Execution/ExecutionContext.cs 
b/src/GnomeSubtitles/Execution/ExecutionContext.cs
index f199ab6..eab5aff 100644
--- a/src/GnomeSubtitles/Execution/ExecutionContext.cs
+++ b/src/GnomeSubtitles/Execution/ExecutionContext.cs
@@ -1,6 +1,6 @@
 /*
  * This file is part of Gnome Subtitles.
- * Copyright (C) 2006-2019 Pedro Castro
+ * Copyright (C) 2006-2022 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
@@ -96,8 +96,8 @@ public class ExecutionContext {
                app.Activated += (sender, e) => {
                        methodToExecute();
                };
-
-               app.Run(0, "");
+               
+               app.Run(applicationID, new string[]{ });
        }
        
 
diff --git a/src/GnomeSubtitles/Ui/VideoPreview/Player.cs b/src/GnomeSubtitles/Ui/VideoPreview/Player.cs
index 86e0d61..37270ff 100644
--- a/src/GnomeSubtitles/Ui/VideoPreview/Player.cs
+++ b/src/GnomeSubtitles/Ui/VideoPreview/Player.cs
@@ -1,6 +1,6 @@
 /*
  * This file is part of Gnome Subtitles.
- * Copyright (C) 2007-2021 Pedro Castro
+ * Copyright (C) 2007-2022 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
@@ -17,7 +17,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
-using External.GstBackendOld;
+using External.GStreamer;
 using GnomeSubtitles.Core;
 using Gtk;
 using SubLib.Util;
@@ -174,7 +174,7 @@ public class Player {
        /* Private members */
 
        private MediaBackend InitializeBackend() {
-               MediaBackend backend = new GstBackendOld();
+               MediaBackend backend = new GstBackend();
                backend.Initialize();
                
                Widget videoWidget = backend.CreateVideoWidget();


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