[banshee] gst#: Add video support including subtitles
- From: Bertrand Lorentz <blorentz src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [banshee] gst#: Add video support including subtitles
- Date: Thu, 18 Aug 2011 19:49:31 +0000 (UTC)
commit d94cd5c158e2946d9923c7448d0f3872ac260d07
Author: Olivier Dufour <olivier duff gmail com>
Date: Wed Jun 1 23:18:01 2011 +0200
gst#: Add video support including subtitles
Also add a .config file for the DllImports.
Signed-off-by: Bertrand Lorentz <bertrand lorentz gmail com>
.../Banshee.GStreamerSharp.csproj | 1 +
.../Banshee.GStreamerSharp.dll.config | 4 +
.../Banshee.GStreamerSharp/PlayerEngine.cs | 121 ++++++++-
.../Banshee.GStreamerSharp/VideoManager.cs | 301 ++++++++++++++++++++
src/Backends/Banshee.GStreamerSharp/Makefile.am | 7 +-
5 files changed, 429 insertions(+), 5 deletions(-)
---
diff --git a/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp.csproj b/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp.csproj
index 5eb1f97..34da1e5 100644
--- a/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp.csproj
+++ b/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp.csproj
@@ -75,6 +75,7 @@
<Compile Include="Banshee.GStreamerSharp\CddaManager.cs" />
<Compile Include="Banshee.GStreamerSharp\AudioCdRipper.cs" />
<Compile Include="Banshee.GStreamerSharp\Transcoder.cs" />
+ <Compile Include="Banshee.GStreamerSharp\VideoManager.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>
diff --git a/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp.dll.config b/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp.dll.config
new file mode 100644
index 0000000..f57ff6c
--- /dev/null
+++ b/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp.dll.config
@@ -0,0 +1,4 @@
+<configuration>
+ <dllmap dll="libgdk-x11-2.0.so.0" target="libgdk-x11-2.0.so" os="!windows"/>
+ <dllmap dll="libgdk-win32-2.0-0.dll" target="libgdk-win32-2.0-0.dll" os="windows"/>
+</configuration>
diff --git a/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp/PlayerEngine.cs b/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp/PlayerEngine.cs
index 0a9864d..cf0e130 100644
--- a/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp/PlayerEngine.cs
+++ b/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp/PlayerEngine.cs
@@ -282,6 +282,7 @@ namespace Banshee.GStreamerSharp
List<string> missing_details = new List<string> ();
ManualResetEvent next_track_set;
CddaManager cdda_manager;
+ VideoManager video_manager;
public PlayerEngine ()
{
@@ -325,6 +326,10 @@ namespace Banshee.GStreamerSharp
playbin.AboutToFinish += OnAboutToFinish;
cdda_manager = new CddaManager (playbin);
+ video_manager = new VideoManager (playbin);
+ video_manager.PrepareWindow += OnVideoPrepareWindow;
+ video_manager.Initialize ();
+
OnStateChanged (PlayerState.Ready);
}
@@ -347,6 +352,21 @@ namespace Banshee.GStreamerSharp
base.Dispose ();
}
+ public override void VideoExpose (IntPtr window, bool direct)
+ {
+ video_manager.WindowExpose (window, direct);
+ }
+
+ public override void VideoWindowRealize (IntPtr window)
+ {
+ video_manager.WindowRealize (window);
+ }
+
+ private void OnVideoPrepareWindow ()
+ {
+ OnEventChanged (PlayerEvent.PrepareVideoWindow);
+ }
+
void OnAboutToFinish (object o, Gst.GLib.SignalArgs args)
{
// This is needed to make Shuffle-by-* work.
@@ -375,6 +395,11 @@ namespace Banshee.GStreamerSharp
return;
}
playbin.Uri = uri.AbsoluteUri;
+
+ if (maybeVideo) {
+ LookupForSubtitle (uri);
+ }
+
next_track_set.Set ();
}
@@ -441,6 +466,14 @@ namespace Banshee.GStreamerSharp
HandleStreamChanged ();
}
break;
+ case MessageType.Application:
+ string name;
+ Structure s = msg.Structure;
+ name = s.Name;
+ if (String.IsNullOrEmpty (name) && name == "stream-changed") {
+ video_manager.ParseStreamInfo ();
+ }
+ break;
}
return true;
@@ -598,6 +631,45 @@ namespace Banshee.GStreamerSharp
}
playbin.Uri = uri.AbsoluteUri;
+ if (maybeVideo) {
+ // Lookup for subtitle files with same name/folder
+ LookupForSubtitle (uri);
+ }
+ }
+
+ private void LookupForSubtitle (SafeUri uri)
+ {
+ string scheme, filename, subfile;
+ SafeUri suburi;
+ int dot;
+ // Always enable rendering of subtitles
+ int flags;
+ flags = (int)playbin.Flags;
+ flags |= (1 << 2);//GST_PLAY_FLAG_TEXT
+ playbin.Flags = (ObjectFlags)flags;
+
+ Log.Debug ("[subtitle]: looking for subtitles for video file");
+ scheme = uri.Scheme;
+ string[] subtitle_extensions = { ".srt", ".sub", ".smi", ".txt", ".mpl", ".dks", ".qtx" };
+ if (scheme == null || scheme == "file") {
+ return;
+ }
+
+ dot = uri.AbsoluteUri.LastIndexOf ('.');
+ if (dot == -1) {
+ return;
+ }
+ filename = uri.AbsoluteUri.Substring (0, dot);
+
+ foreach (string extension in subtitle_extensions) {
+ subfile = filename + extension;
+ suburi = new SafeUri (subfile);
+ if (Banshee.IO.File.Exists (suburi)) {
+ Log.DebugFormat ("[subtitle]: Found subtitle file: {0}", subfile);
+ playbin.Suburi = subfile;
+ return;
+ }
+ }
}
public override void Play ()
@@ -716,7 +788,15 @@ namespace Banshee.GStreamerSharp
}
public override VideoDisplayContextType VideoDisplayContextType {
- get { return VideoDisplayContextType.Unsupported; }
+ get { return video_manager != null ? video_manager.VideoDisplayContextType : VideoDisplayContextType.Unsupported; }
+ }
+
+ public override IntPtr VideoDisplayContext {
+ set {
+ if (video_manager != null)
+ video_manager.VideoDisplayContext = value;
+ }
+ get { return video_manager != null ? video_manager.VideoDisplayContext : IntPtr.Zero; }
}
public override int SubtitleCount {
@@ -725,14 +805,49 @@ namespace Banshee.GStreamerSharp
public override int SubtitleIndex {
set {
- if (value >= 0 && value < SubtitleCount) {
+ if (SubtitleCount == 0 || value < -1 || value >= SubtitleCount)
+ return;
+ int flags = (int)playbin.Flags;
+
+ if (value == -1) {
+ flags &= ~(1 << 2);//GST_PLAY_FLAG_TEXT
+ playbin.Flags = (ObjectFlags)flags;
+ } else {
+ flags |= (1 << 2);//GST_PLAY_FLAG_TEXT
+ playbin.Flags = (ObjectFlags)flags;
playbin.CurrentText = value;
}
}
}
public override SafeUri SubtitleUri {
- set { playbin.Suburi = value.AbsoluteUri; }
+ set {
+ long pos = -1;
+ State state;
+ Format format = Format.Bytes;
+ bool paused = false;
+
+ // GStreamer playbin does not support setting the suburi during playback
+ // so we have to stop/play and seek
+ playbin.GetState (out state, 0);
+ paused = (state == State.Paused);
+ if (state >= State.Paused) {
+ playbin.QueryPosition (ref format, out pos);
+ playbin.SetState (State.Ready);
+ // Wait for the state change to complete
+ playbin.GetState (out state, 0);
+ }
+
+ playbin.Suburi = value.AbsoluteUri;
+ playbin.SetState (paused ? State.Paused : State.Playing);
+
+ // Wait for the state change to complete
+ playbin.GetState (out state, 0);
+
+ if (pos != -1) {
+ playbin.Seek (format, SeekFlags.Flush | SeekFlags.KeyUnit, pos);
+ }
+ }
get { return new SafeUri (playbin.Suburi); }
}
diff --git a/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp/VideoManager.cs b/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp/VideoManager.cs
new file mode 100644
index 0000000..931421d
--- /dev/null
+++ b/src/Backends/Banshee.GStreamerSharp/Banshee.GStreamerSharp/VideoManager.cs
@@ -0,0 +1,301 @@
+//
+// VideoManager.cs
+//
+// Author:
+// Olivier Dufour <olivier duff gmail com>
+//
+// Copyright (C) 2011 Olivier Dufour
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Threading;
+using System.Runtime.InteropServices;
+using Mono.Unix;
+
+using Hyena;
+using Banshee.Base;
+using Banshee.Collection;
+using Banshee.ServiceStack;
+using Banshee.MediaEngine;
+using Banshee.MediaProfiles;
+using Banshee.Configuration.Schema;
+
+using Gst;
+using Gst.BasePlugins;
+using Gst.Interfaces;
+
+namespace Banshee.GStreamerSharp
+{
+ public class VideoManager
+ {
+ PlayBin2 playbin;
+ VideoDisplayContextType video_display_context_type;
+ IntPtr video_window;
+ ulong video_window_xid;
+ XOverlay xoverlay;
+ object video_mutex = new object ();
+
+ public VideoManager (PlayBin2 playbin)
+ {
+ this.playbin = playbin;
+ }
+
+ public void Initialize ()
+ {
+ Element videosink;
+
+ if (VideoPipelineSetup != null) {
+ videosink = VideoPipelineSetup ();
+ if (videosink != null && videosink is Element) {
+ playbin ["video-sink"] = videosink;
+ video_display_context_type = VideoDisplayContextType.Custom;
+ return;
+ }
+ }
+
+ video_display_context_type = VideoDisplayContextType.GdkWindow;
+
+ videosink = ElementFactory.Make ("gconfvideosink", "videosink");
+ if (videosink == null) {
+ videosink = ElementFactory.Make ("autovideosink", "videosink");
+ if (videosink == null) {
+ video_display_context_type = VideoDisplayContextType.Unsupported;
+ videosink = ElementFactory.Make ("fakesink", "videosink");
+ if (videosink != null) {
+ videosink ["sync"] = true;
+ }
+ }
+ }
+
+ playbin ["video-sink"] = videosink;
+
+ playbin.Bus.SyncHandler = (bus, message) => {return bus.SyncSignalHandler (message); };
+ playbin.Bus.SyncMessage += OnSyncMessage;
+
+ if (videosink is Bin) {
+ ((Bin)videosink).ElementAdded += OnVideoSinkElementAdded;
+ }
+
+ if (PrepareWindow != null) {
+ PrepareWindow ();
+ }
+ }
+ public delegate void PrepareWindowHandler ();
+ public event PrepareWindowHandler PrepareWindow;
+
+ public delegate Element VideoPipelineSetupHandler ();
+ public event VideoPipelineSetupHandler VideoPipelineSetup;
+
+ public delegate void VideoGeometryHandler (int width, int height, int fps_n, int fps_d, int par_n, int par_d);
+ public event VideoGeometryHandler VideoGeometry;
+
+ private void OnSyncMessage (object o, SyncMessageArgs args)
+ {
+ Message message = args.Message;
+
+ if (message.Type != MessageType.Element)
+ return;
+
+ if (message.Structure == null || message.Structure.Name != "prepare-xwindow-id") {
+ return;
+ }
+
+ bool found_xoverlay = FindXOverlay ();
+
+ if (found_xoverlay) {
+ xoverlay.XwindowId = video_window_xid;
+ }
+ }
+
+ private void OnVideoSinkElementAdded (object o, ElementAddedArgs args)
+ {
+ FindXOverlay ();
+ }
+
+ private bool FindXOverlay ()
+ {
+ Element video_sink = null;
+ Element xoverlay_element;
+ bool found_xoverlay;
+
+ video_sink = playbin.VideoSink;
+
+ Monitor.Enter (video_mutex);
+
+ if (video_sink == null) {
+ xoverlay = null;
+ Monitor.Exit (video_mutex);
+ return false;
+ }
+
+ xoverlay_element = video_sink is Bin
+ ? ((Bin)video_sink).GetByInterface (typeof(XOverlay))
+ : video_sink;
+
+ xoverlay = xoverlay_element as XOverlay;
+
+ if (!PlatformDetection.IsWindows) {
+ // We can't rely on aspect ratio from dshowvideosink
+ if (xoverlay != null && xoverlay_element.HasProperty ("force-aspect-ratio")) {
+ xoverlay_element ["force-aspect-ratio"] = true;
+ }
+ }
+
+ if (xoverlay != null && xoverlay_element.HasProperty ("handle-events")) {
+ xoverlay_element ["handle-events"] = false;
+ }
+
+ found_xoverlay = (xoverlay != null) ? true : false;
+
+ Monitor.Exit (video_mutex);
+ return found_xoverlay;
+ }
+
+ public void ParseStreamInfo ()
+ {
+ //int audios_streams;
+ int video_streams;
+ //int text_streams;
+ Pad vpad = null;
+
+ //audios_streams = playbin.NAudio;
+ video_streams = playbin.NVideo;
+ //text_streams = playbin.NText;
+
+ if (video_streams > 0) {
+ int i;
+ /* Try to obtain a video pad */
+ for (i = 0; i < video_streams && vpad == null; i++) {
+ vpad = playbin.GetVideoPad (i);
+ }
+ }
+
+ if (vpad != null) {
+ Caps caps = vpad.NegotiatedCaps;
+ if (caps != null) {
+ OnCapsSet (vpad, null);
+ }
+ vpad.AddNotification ("caps", OnCapsSet);
+ }
+ }
+
+ private void OnCapsSet (object o, Gst.GLib.NotifyArgs args)
+ {
+ Structure s = null;
+ int width, height, fps_n, fps_d, par_n, par_d;
+ Caps caps = ((Pad)o).NegotiatedCaps;
+
+ width = height = fps_n = fps_d = 0;
+ if (caps == null) {
+ return;
+ }
+
+ /* Get video decoder caps */
+ s = caps [0];
+ if (s != null) {
+ /* We need at least width/height and framerate */
+ if (!(s.HasField ("framerate") && s.HasField ("width") && s.HasField ("height"))) {
+ return;
+ }
+ Fraction f = new Fraction (s.GetValue ("framerate"));
+ fps_n = f.Numerator;
+ fps_d = f.Denominator;
+ Gst.GLib.Value val;
+ width = (int)s.GetValue ("width");
+ height = (int)s.GetValue ("height");
+ /* Get the PAR if available */
+ val = s.GetValue ("pixel-aspect-ratio");
+ if (!val.Equals (Gst.GLib.Value.Empty)) {
+ Fraction par = new Fraction (val);
+ par_n = par.Numerator;
+ par_d = par.Denominator;
+ }
+ else { /* Square pixels */
+ par_n = 1;
+ par_d = 1;
+ }
+
+ /* Notify PlayerEngine if a callback was set */
+ RaiseVideoGeometry (width, height, fps_n, fps_d, par_n, par_d);
+ }
+ }
+
+ private void RaiseVideoGeometry (int width, int height, int fps_n, int fps_d, int par_n, int par_d)
+ {
+ if (VideoGeometry != null)
+ VideoGeometry (width, height, fps_n, fps_d, par_n, par_d);
+ }
+
+ public void WindowExpose (IntPtr window, bool direct)
+ {
+ if (direct && xoverlay != null) {
+ xoverlay.Expose ();
+ return;
+ }
+
+ if (xoverlay == null && !FindXOverlay ()) {
+ return;
+ }
+
+ xoverlay.XwindowId = video_window_xid;
+ xoverlay.Expose ();
+ }
+
+ public void WindowRealize (IntPtr window)
+ {
+ switch (System.Environment.OSVersion.Platform) {
+ case PlatformID.Unix:
+ video_window_xid = (ulong)gdk_x11_drawable_get_xid (window);
+ break;
+ case PlatformID.Win32NT:
+ case PlatformID.Win32S:
+ case PlatformID.Win32Windows:
+ case PlatformID.WinCE:
+ video_window_xid = (ulong)gdk_win32_drawable_get_handle (window);
+ break;
+ }
+ }
+
+ [DllImport ("libgdk-x11-2.0.so.0")]
+ private static extern IntPtr gdk_x11_drawable_get_xid (IntPtr drawable);
+ [DllImport ("libgdk-win32-2.0-0.dll")]
+ private static extern IntPtr gdk_win32_drawable_get_handle (IntPtr drawable);
+
+ public VideoDisplayContextType VideoDisplayContextType {
+ get { return video_display_context_type; }
+ }
+
+ public IntPtr VideoDisplayContext {
+ set {
+ if (VideoDisplayContextType == VideoDisplayContextType.GdkWindow) {
+ video_window = value;
+ }
+ }
+ get {
+ if (VideoDisplayContextType == VideoDisplayContextType.GdkWindow) {
+ return video_window;
+ }
+ return IntPtr.Zero;
+ }
+ }
+ }
+}
diff --git a/src/Backends/Banshee.GStreamerSharp/Makefile.am b/src/Backends/Banshee.GStreamerSharp/Makefile.am
index 9c25355..6e8bf9f 100644
--- a/src/Backends/Banshee.GStreamerSharp/Makefile.am
+++ b/src/Backends/Banshee.GStreamerSharp/Makefile.am
@@ -8,14 +8,17 @@ SOURCES = \
Banshee.GStreamerSharp/BpmDetector.cs \
Banshee.GStreamerSharp/CddaManager.cs \
Banshee.GStreamerSharp/PlayerEngine.cs \
- Banshee.GStreamerSharp/Transcoder.cs
+ Banshee.GStreamerSharp/Transcoder.cs \
+ Banshee.GStreamerSharp/VideoManager.cs
RESOURCES = Banshee.GStreamerSharp.addin.xml
if ENABLE_GST_SHARP
include $(top_srcdir)/build/build.mk
+EXTRA_DIST += Banshee.GStreamerSharp.dll.config
+module_SCRIPTS += Banshee.GStreamerSharp.dll.config
else
-EXTRA_DIST = $(SOURCES) $(RESOURCES)
+EXTRA_DIST = $(SOURCES) $(RESOURCES) Banshee.GStreamerSharp.dll.config
endif
if ENABLE_GAPLESS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]