[gnome-subtitles/gstreamer-sharp-bindings] Reworked to use gstreamer-sharp instead of the custom playbin bindings
- From: Pedro Castro <pcastro src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-subtitles/gstreamer-sharp-bindings] Reworked to use gstreamer-sharp instead of the custom playbin bindings
- Date: Thu, 3 Feb 2022 23:26:31 +0000 (UTC)
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]