gnome-subtitles r1079 - in trunk/src/External: . GStreamerPlaybin



Author: pcastro
Date: Sun Nov 23 18:23:56 2008
New Revision: 1079
URL: http://svn.gnome.org/viewvc/gnome-subtitles?rev=1079&view=rev

Log:
Gstreamer playbin (from the fusemc project) has been incorporated into the Gnome Subtitles src dir. The CS sources are included in the GS assembly, while main.c is built into a library (SO) as it was before.
Fixed the playbin to function properly on loading a file when its state was loaded (from a previous file). Specifically, the "Found video event" and "State changed" events now work.

Added:
   trunk/src/External/
   trunk/src/External/GStreamerPlaybin/
   trunk/src/External/GStreamerPlaybin/Engine.cs
   trunk/src/External/GStreamerPlaybin/Events.cs
   trunk/src/External/GStreamerPlaybin/main.c

Added: trunk/src/External/GStreamerPlaybin/Engine.cs
==============================================================================
--- (empty file)
+++ trunk/src/External/GStreamerPlaybin/Engine.cs	Sun Nov 23 18:23:56 2008
@@ -0,0 +1,580 @@
+/*
+
+	Copyright (c)  Goran Sterjov, Pedro Castro
+
+    This file is part of the GStreamer Playbin Wrapper.
+    Derived from Fuse.
+
+    GStreamer Playbin Wrapper is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    GStreamer Playbin Wrapper is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with GStreamer Playbin Wrapper; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+
+
+using System;
+using System.Runtime.InteropServices;
+
+
+namespace GStreamer
+{
+	
+	#pragma warning disable 649		//Disables warning about fields not being assigned to
+	
+	// media engine enumerations
+	public enum MediaStatus { Playing, Paused, Loaded, Unloaded }
+	
+	// media engine event handlers
+	public delegate void ErrorEventHandler (ErrorEventArgs args);
+	public delegate void BufferEventHandler (BufferEventArgs args);
+	public delegate void EndOfStreamEventHandler ();
+	
+	public delegate void StateEventHandler (StateEventArgs args);
+	public delegate void VideoInfoEventHandler (VideoInfoEventArgs args);
+	public delegate void TagEventHandler (TagEventArgs args);
+	
+	
+	
+	/// <summary>
+	/// The GStreamer Playbin.
+	/// </summary>
+	public class Playbin
+	{
+		
+		// engine callbacks from the C wrapper
+		delegate void eosCallback ();
+		delegate void errorCallback (string error, string debug);
+		delegate void bufferCallback (int progress);
+		delegate void infoCallback (IntPtr ptr);
+		delegate void tagCallback (IntPtr ptr);
+		
+		eosCallback eos_cb;
+		errorCallback error_cb;
+		bufferCallback buffer_cb;
+		infoCallback info_cb;
+		tagCallback tag_cb;
+		
+		
+		// declarations
+		HandleRef engine;
+		MediaStatus status = MediaStatus.Unloaded;
+		
+		
+		/// <summary>Raised when an error occurs.</summary>
+		public event ErrorEventHandler Error;
+		
+		/// <summary>Raised when the buffer status has changed.</summary>
+		public event BufferEventHandler Buffer;
+		
+		/// <summary>Raised when the end of the stream is reached.</summary>
+		public event EndOfStreamEventHandler EndOfStream;
+		
+		/// <summary>Raised when the playbin state changes. ie: Playing, Paused, etc.</summary>
+		public event StateEventHandler StateChanged;
+		
+		/// <summary>Raised when video information is found.</summary>
+		public event VideoInfoEventHandler FoundVideoInfo;
+		
+		/// <summary>Raised when a media tag is found.</summary>
+		public event TagEventHandler FoundTag;
+		
+		
+		
+		/// <summary>
+		/// Load the GStreamer library and attach it
+		/// to the specified window.
+		/// </summary>
+		public bool Initiate (ulong x_window_id)
+		{
+			
+			// load the gstreamer library
+			IntPtr ptr = gst_binding_init (x_window_id);
+			
+			if(ptr == IntPtr.Zero)
+			{
+				throwError ("Failed to load the Gstreamer library", "");
+				return false;
+			}
+			else engine = new HandleRef (this, ptr);
+			
+			
+			// set up callbacks
+			eos_cb = new eosCallback (onEos);
+			error_cb = new errorCallback (onError);
+			buffer_cb = new bufferCallback (onBuffer);
+			info_cb = new infoCallback (onInfo);
+			tag_cb = new tagCallback (onTag);
+			
+			gst_binding_set_eos_cb (engine, eos_cb);
+			gst_binding_set_error_cb (engine, error_cb);
+			gst_binding_set_buffer_cb (engine, buffer_cb);
+			gst_binding_set_info_cb (engine, info_cb);
+			gst_binding_set_tag_cb (engine, tag_cb);
+			
+			
+			status = MediaStatus.Unloaded;
+			return true;
+		}
+		
+		
+		/// <summary>
+		/// Load the GStreamer library.
+		/// </summary>
+		public bool Initiate ()
+		{
+			return Initiate (0);
+		}
+		
+		
+		
+		/// <summary>
+		/// Disposes the GStreamer library.
+		/// </summary>
+		public void Dispose ()
+		{
+			Unload ();
+			gst_binding_deinit (engine);
+			changeState (MediaStatus.Unloaded);
+		}
+		
+		/// <summary>
+		/// Loads the specified path into the GStreamer library.
+		/// </summary>
+		public bool Load (string uri)
+		{
+			if (!isUnloaded)
+				Unload ();
+			
+			bool loaded = gst_binding_load (engine, uri);
+			
+            if (loaded)
+                changeState (MediaStatus.Loaded);
+            
+            return loaded;
+		}
+		
+		/// <summary>
+		/// Plays the loaded media file.
+		/// </summary>
+		public void Play ()
+		{
+			if (!isPlaying && !isUnloaded)
+			{
+				gst_binding_play (engine);
+				changeState (MediaStatus.Playing);
+			}
+		}
+		
+		/// <summary>
+		/// Pauses the loaded media file.
+		/// </summary>
+		public void Pause ()
+		{
+			if (isPlaying)
+			{
+				gst_binding_pause (engine);
+				changeState (MediaStatus.Paused);
+			}
+		}
+		
+		/// <summary>
+		/// Unloads the media file.
+		/// </summary>
+		public void Unload ()
+		{
+			if (!isUnloaded)
+			{
+				gst_binding_unload (engine);
+				changeState (MediaStatus.Unloaded);
+			}
+		}
+		
+		/// <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>
+		/// Seeks to the nearest millisecond on the media file.
+		/// </summary>
+		public void Seek (TimeSpan time)
+		{
+			if (isUnloaded)
+				return;
+			
+			gst_binding_set_position (engine, (ulong) time.TotalMilliseconds);
+		}
+		
+		
+		/// <summary>
+		/// Seeks to the nearest millisecond on the media file.
+		/// </summary>
+		public void Seek (double milliseconds)
+		{
+			TimeSpan time = TimeSpan.FromMilliseconds (milliseconds);
+			Seek (time);
+		}
+		
+		
+		/// <summary>
+		/// Seeks to the specified track number.
+		/// </summary>
+		public void SeekToTrack (int track_number)
+		{
+			if (isUnloaded)
+				return;
+			gst_binding_set_track (engine, (ulong) track_number);
+		}
+		
+		
+		
+		
+		/// <summary>
+		/// Returns the current position that the media file is on.
+		/// </summary>
+		public TimeSpan CurrentPosition
+		{
+			get
+			{
+				if (!isPlaying && !isPaused)
+					return TimeSpan.Zero;
+				
+				double pos = (double) gst_binding_get_position (engine);
+				return TimeSpan.FromMilliseconds (pos);
+			}
+		}
+		
+		
+		/// <summary>
+		/// Returns the total duration of the media file.
+		/// </summary>
+		public TimeSpan Duration
+		{
+			get{
+				if (isUnloaded)
+					return TimeSpan.Zero;
+				
+				double dur = (double) gst_binding_get_duration (engine);
+				return TimeSpan.FromMilliseconds (dur);
+			}
+		}
+		
+		
+		
+		/// <summary>
+		/// Returns the current volume of the GStreamer library.
+		/// </summary>
+		public double Volume
+		{
+			get{ return (double) gst_binding_get_volume (engine); }
+			set{ gst_binding_set_volume (engine, (int) value); }
+		}
+		
+		
+		
+		/// <summary>
+		/// Returns a value determining if the media file is a video file.
+		/// </summary>
+		public bool HasVideo
+		{
+			get{ return !isUnloaded ? gst_binding_has_video (engine) : false; }
+		}
+		
+		
+		
+		/// <summary>
+		/// Returns a string array of all the visualisations available
+		/// </summary>
+		public string[] VisualisationList
+		{
+			get
+			{
+				IntPtr ptr = gst_binding_get_visuals_list (engine);
+				GLib.List list = new GLib.List (ptr, typeof (string));
+				
+				string[] array = new string[list.Count];
+				
+				for (int i=0; i<list.Count; i++)
+					array[i] = (list[i] as string);
+				
+				list.Dispose ();
+				list = null;
+				
+				return array;
+			}
+		}
+		
+		
+		/// <summary>
+		/// Sets the visualisation
+		/// </summary>
+		public string Visualisation
+		{
+			set{ gst_binding_set_visual (engine, value); }
+		}
+
+		
+		
+		
+		/// <summary>
+		/// Returns information on the video stream, or null if it's not available
+		/// </summary>
+		public VideoInfo VideoInfo
+		{
+			get
+			{
+				IntPtr ptr = gst_binding_get_video_info (engine);
+				if (ptr != IntPtr.Zero)
+					return new VideoInfo (ptr);
+				else
+					return null;
+			}
+		}
+		
+		
+		
+		/// <summary>
+		/// Returns the tag of the current media file, or null if it's not available
+		/// </summary>
+		public Tag Tag
+		{
+			get
+			{
+				IntPtr ptr = gst_binding_get_tag (engine);
+				if (ptr != IntPtr.Zero)
+					return new Tag (ptr);
+				else
+					return null;
+			}
+		}
+		
+		
+		
+		
+		
+		/// <summary>
+		/// Returns the current status of the media engine.
+		/// </summary>
+		public MediaStatus CurrentStatus
+		{
+			get { return status; }
+		}
+		
+		
+		
+		void changeState (MediaStatus state)
+		{
+			status = state;
+			if (StateChanged != null)
+				StateChanged (new StateEventArgs (state));
+		}
+		
+		
+		
+		// throws an error to the global error handler
+		void throwError (string error, string debug)
+		{
+        	if(Error != null)
+				Error (new ErrorEventArgs (error, debug));
+		}
+		
+		
+		// an error in the gstreamer pipeline has occured
+		void onError (string error, string debug)
+		{
+			throwError (error, debug);
+		}
+		
+		
+		// the stream has ended
+		void onEos ()
+		{
+			if (EndOfStream != null)
+				EndOfStream ();
+		}
+		
+		
+		// the gstreamer pipeline is being buffered
+		void onBuffer (int progress)
+		{
+			if (Buffer != null)
+				Buffer (new BufferEventArgs (progress));
+		}
+		
+		
+		// media information is available
+		void onInfo (IntPtr ptr)
+		{
+			if (FoundVideoInfo != null)
+			{
+				VideoInfo video_info = getVideoInfo (ptr);
+				if (video_info != null)
+					FoundVideoInfo (new VideoInfoEventArgs (video_info));
+			}
+		}
+		
+		
+		// a media tag is available
+		void onTag (IntPtr ptr)
+		{
+			if (FoundTag != null)
+			{
+				Tag tag = getTag (ptr);
+				if (tag != null)
+					FoundTag (new TagEventArgs (tag));
+			}
+		}
+		
+		
+		
+		Tag getTag (IntPtr ptr)
+		{
+			if (ptr != IntPtr.Zero)
+				return new Tag (ptr);
+			else
+				return null;
+		}
+		
+		
+		VideoInfo getVideoInfo (IntPtr ptr)
+		{
+			if (ptr != IntPtr.Zero)
+				return new VideoInfo (ptr);
+			else
+				return null;
+		}
+        
+		
+		
+		// private convenience properties
+		bool isPlaying { get{ return status == MediaStatus.Playing; } }
+		bool isPaused { get{ return status == MediaStatus.Paused; } }
+		bool isUnloaded { get{ return status == MediaStatus.Unloaded; } }
+
+
+		// core engine functions
+		[DllImport("gstreamer_playbin")]
+		static extern IntPtr gst_binding_init (ulong xwin);
+		[DllImport("gstreamer_playbin")]
+		static extern void gst_binding_deinit (HandleRef play);
+		[DllImport("gstreamer_playbin")]
+		static extern bool gst_binding_load (HandleRef play, string uri);
+		[DllImport("gstreamer_playbin")]
+		static extern void gst_binding_play (HandleRef play);
+		[DllImport("gstreamer_playbin")]
+		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);
+		
+		
+		// engine property functions
+		[DllImport("gstreamer_playbin")]
+		static extern ulong gst_binding_get_duration (HandleRef play);
+		[DllImport("gstreamer_playbin")]
+		static extern ulong gst_binding_get_position (HandleRef play);
+		[DllImport("gstreamer_playbin")]
+		static extern int gst_binding_get_volume (HandleRef play);
+		[DllImport("gstreamer_playbin")]
+		static extern void gst_binding_set_position (HandleRef play, ulong pos);
+		[DllImport("gstreamer_playbin")]
+		static extern void gst_binding_set_track (HandleRef play, ulong track_number);
+		[DllImport("gstreamer_playbin")]
+		static extern void gst_binding_set_volume (HandleRef play, int vol);
+		[DllImport("gstreamer_playbin")]
+		static extern bool gst_binding_has_video (HandleRef play);
+		[DllImport("gstreamer_playbin")]
+		static extern IntPtr gst_binding_get_video_info (HandleRef play);
+		[DllImport("gstreamer_playbin")]
+		static extern IntPtr gst_binding_get_tag (HandleRef play);
+		
+		
+		// engine callbacks
+		[DllImport("gstreamer_playbin")]
+		static extern void gst_binding_set_eos_cb (HandleRef play, eosCallback cb);
+		[DllImport("gstreamer_playbin")]
+		static extern void gst_binding_set_error_cb (HandleRef play, errorCallback cb);
+		[DllImport("gstreamer_playbin")]
+		static extern void gst_binding_set_buffer_cb (HandleRef play, bufferCallback cb);
+		[DllImport("gstreamer_playbin")]
+		static extern void gst_binding_set_info_cb (HandleRef play, infoCallback cb);
+		[DllImport("gstreamer_playbin")]
+		static extern void gst_binding_set_tag_cb (HandleRef play, tagCallback cb);
+		
+		
+		[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);
+	}
+	
+	
+	
+	[StructLayout(LayoutKind.Sequential)]
+    public class VideoInfo
+    {
+    	int width;
+		int height;
+    	float frame_rate;
+		
+		public VideoInfo (IntPtr ptr)
+		{
+			if (ptr != IntPtr.Zero)
+				Marshal.PtrToStructure (ptr, this);
+		}
+    	
+    	public int Width { get{ return width; } }
+       	public int Height { get{ return height; } }
+       	public float AspectRatio { get { return (float)width/height; } }
+    	public float FrameRate { get{ return frame_rate; } }
+    	
+    	public override string ToString ()
+    	{
+    		return "width=" + width + ", height=" + height + ", frame_rate=" + frame_rate;
+    	}
+
+    }
+	
+	
+	[StructLayout(LayoutKind.Sequential)]
+    public class Tag
+    {
+    	string disc_id;
+		string music_brainz_id;
+    	int current_track;
+		int track_count;
+		int duration;
+		
+		public Tag (IntPtr ptr)
+		{
+			if (ptr != IntPtr.Zero)
+				Marshal.PtrToStructure (ptr, this);
+		}
+    	
+    	public string DiscID { get{ return disc_id; } }
+       	public string MusicBrainzID { get{ return music_brainz_id; } }
+       	public int CurrentTrack { get { return current_track; } }
+    	public int TrackCount { get{ return track_count; } }
+		public int Duration { get{ return duration; } }
+    }
+	
+	
+}
\ No newline at end of file

Added: trunk/src/External/GStreamerPlaybin/Events.cs
==============================================================================
--- (empty file)
+++ trunk/src/External/GStreamerPlaybin/Events.cs	Sun Nov 23 18:23:56 2008
@@ -0,0 +1,107 @@
+/*
+
+	Copyright (c)  Goran Sterjov, Pedro Castro
+
+    This file is part of the GStreamer Playbin Wrapper.
+    Derived from Fuse.
+
+    GStreamer Playbin Wrapper is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    GStreamer Playbin Wrapper is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with GStreamer Playbin Wrapper; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+
+
+using System;
+
+namespace GStreamer
+{
+	
+	/// <summary>
+	/// Arguments for a raised error.
+	/// </summary>
+	public sealed class ErrorEventArgs
+	{
+		string error, debug;
+		
+		internal ErrorEventArgs (string error, string debug)
+		{
+			this.error = error;
+			this.debug = debug;
+		}
+		
+        public string Error { get{ return error; } }
+		public string Debug { get{ return debug; } }
+    }
+	
+	
+	
+	/// <summary>
+	/// Arguments for a raised buffer event.
+	/// </summary>
+	public sealed class BufferEventArgs
+	{
+		int progress;
+		
+		internal BufferEventArgs (int progress)
+		{ this.progress = progress; }
+		
+        public int Progress { get{ return progress; } }
+    }
+	
+	
+	
+	/// <summary>
+	/// Arguments for a raised video info event.
+	/// </summary>
+	public sealed class VideoInfoEventArgs
+	{
+		VideoInfo video_info;
+		
+		internal VideoInfoEventArgs (VideoInfo video_info)
+		{ this.video_info = video_info; }
+		
+        public VideoInfo VideoInfo { get{ return video_info; } }
+    }
+	
+	
+	
+	/// <summary>
+	/// Arguments for a raised video info event.
+	/// </summary>
+	public sealed class TagEventArgs
+	{
+		Tag tag;
+		
+		internal TagEventArgs (Tag tag)
+		{ this.tag = tag; }
+		
+        public Tag Tag { get{ return tag; } }
+    }
+	
+    
+	
+	/// <summary>
+	/// Arguments for a raised state.
+	/// </summary>
+    public sealed class StateEventArgs
+    {
+    	MediaStatus state;
+		
+    	internal StateEventArgs (MediaStatus state)
+    	{ this.state = state; }
+		
+    	public MediaStatus State { get{ return state; } }
+    }
+	
+}
\ No newline at end of file

Added: trunk/src/External/GStreamerPlaybin/main.c
==============================================================================
--- (empty file)
+++ trunk/src/External/GStreamerPlaybin/main.c	Sun Nov 23 18:23:56 2008
@@ -0,0 +1,673 @@
+/*
+
+	Copyright (c)  Goran Sterjov, Pedro Castro
+
+    This file is part of the GStreamer Playbin Wrapper.
+    Derived from Fuse.
+
+    GStreamer Playbin Wrapper is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    GStreamer Playbin Wrapper is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with GStreamer Playbin Wrapper; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+
+
+#include <gst/gst.h>
+#include <gst/interfaces/xoverlay.h>
+#include <gst/tag/tag.h> 
+#include <string.h>
+
+
+
+typedef struct gstPlay gstPlay;
+typedef struct gstVideoInfo gstVideoInfo;
+typedef struct gstTag gstTag;
+
+// callbacks for the binding
+typedef void (* eosCallback) ();
+typedef void (* errorCallback) (const gchar *error, const gchar *debug);
+typedef void (* bufferCallback) (gint progress);
+typedef void (* infoCallback) (gstVideoInfo *video_info);
+typedef void (* tagCallback) (gstTag *tag);
+
+
+// a video info structure
+struct gstVideoInfo {
+	gint width;
+	gint height;
+	gfloat frame_rate;
+};
+
+
+// a media tag structure
+struct gstTag {
+	gchar *disc_id;
+	gchar *music_brainz_id;
+	
+	guint current_track;
+	guint track_count;
+	guint64 duration;
+};
+
+// a simple structure for the created playbin
+struct gstPlay {
+    GstElement *element;
+    gulong xid;
+	GstXOverlay *overlay;
+	
+	gchar *vis_name;
+	
+	eosCallback eos_cb;
+	errorCallback error_cb;
+	bufferCallback buffer_cb;
+	infoCallback info_cb;
+	tagCallback tag_cb;
+
+	gstVideoInfo *video_info;
+	gboolean info_loaded;
+	
+	gstTag *tag;
+};
+
+
+//Declarations
+static void setup_vis (gstPlay *play);
+gboolean gst_binding_load_video_info (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_structure_has_name (message->structure, "prepare-xwindow-id")) {
+			play->overlay = GST_X_OVERLAY (GST_MESSAGE_SRC (message));
+			gst_x_overlay_set_xwindow_id (play->overlay, play->xid);
+		}
+	}
+	return TRUE;
+}
+
+
+static gboolean
+gst_async_watch(GstBus *bus, GstMessage *message, gpointer data)
+{
+	gstPlay *play = (gstPlay *)data;
+	if (play == NULL) return FALSE;
+	
+	switch (GST_MESSAGE_TYPE (message)) {
+		
+		// the pipeline state has changed
+		case GST_MESSAGE_STATE_CHANGED:
+		{
+			GstState new_state;
+			gst_message_parse_state_changed (message, NULL, &new_state, NULL);
+			
+			if (new_state == GST_STATE_PAUSED)
+			{
+				if (play->info_loaded == FALSE)
+				{
+					if (gst_binding_load_video_info (play))
+					{
+						play->info_loaded = TRUE;
+						if(play->info_cb != NULL) {
+							play->info_cb (play->video_info);
+						}
+					}
+				}
+			}
+			
+			break;
+		}
+		
+		// and error occurred in the pipeline
+		case GST_MESSAGE_ERROR:
+		{
+			if(play->error_cb != NULL) {
+				GError *error; gchar *debug;
+				gst_message_parse_error (message, &error, &debug);
+				play->error_cb (error->message, debug);
+				g_error_free (error);
+				g_free (debug);
+			}
+			break;
+		}
+		
+		// the media file finished playing
+		case GST_MESSAGE_EOS:
+        {
+        	if(play->eos_cb != NULL)
+				play->eos_cb();
+            break;
+        }
+        
+        // the media file is being buffered
+		case GST_MESSAGE_BUFFERING:
+		{
+            const GstStructure *buffer;
+            gint prog = 0;
+            
+            buffer = gst_message_get_structure (message);
+            if(gst_structure_get_int (buffer, "buffer-percent", &prog))
+                if(play->buffer_cb != NULL)
+					play->buffer_cb(prog);
+			break;
+        }
+        
+         // the media file has a tag
+		case GST_MESSAGE_TAG:
+		{
+			play->tag = g_new0 (gstTag, 1);
+			
+            GstTagList *tags;
+            gst_message_parse_tag (message, &tags);
+            
+            guint64 duration;
+            guint current_track;
+            guint track_count;
+            char *disc_id;
+            char *music_brainz_id;
+            
+            
+            // track number
+            if (gst_tag_list_get_uint (tags, GST_TAG_TRACK_NUMBER, &current_track))
+            	play->tag->current_track = current_track;
+           	
+           	// total tracks
+           	if (gst_tag_list_get_uint (tags, GST_TAG_TRACK_COUNT, &track_count))
+            	play->tag->track_count = track_count;
+           	
+           	// track duration
+           	if (gst_tag_list_get_uint64 (tags, GST_TAG_DURATION, &duration))
+           		play->tag->duration = duration;
+            
+            // track cddb disc id
+            if (gst_tag_list_get_string (tags, GST_TAG_CDDA_CDDB_DISCID, &disc_id))
+           		play->tag->disc_id = disc_id;
+           	
+           	// track music brainz disc id
+           	if (gst_tag_list_get_string (tags, GST_TAG_CDDA_MUSICBRAINZ_DISCID, &music_brainz_id))
+           		play->tag->music_brainz_id = music_brainz_id;
+           	
+           	if(play->tag_cb != NULL)
+					play->tag_cb (play->tag);
+           	
+			break;
+        }
+
+        //By default, do nothing
+        default:
+        	break;
+	}
+	return TRUE;
+}
+
+
+gboolean isValid (gstPlay *play) {
+	if (play != NULL)
+		if (GST_IS_ELEMENT (play->element)) return TRUE;
+	return FALSE;
+}
+
+
+
+// initiates gstreamer as a playbin pipeline
+gstPlay *gst_binding_init (gulong xwin) {
+	gstPlay *play = g_new0 (gstPlay, 1);
+	
+	gst_init (NULL, NULL);
+	play->element = gst_element_factory_make ("playbin", "play");
+	if (play->element == NULL) return NULL;
+	play->xid = xwin;
+	
+	gst_bus_set_sync_handler (gst_pipeline_get_bus(GST_PIPELINE(play->element)), 
+		gst_sync_watch, play);
+	gst_bus_add_watch (gst_pipeline_get_bus(GST_PIPELINE(play->element)), 
+		gst_async_watch, play);
+	
+	return play;
+}
+
+
+
+// releases any references to gstreamer
+void gst_binding_deinit (gstPlay *play) {
+	if (isValid (play)) {
+		gst_element_set_state (play->element, GST_STATE_NULL);
+		
+		if (play->element != NULL)
+		{
+    		gst_object_unref (GST_OBJECT (play->element));
+    		play->element = NULL;
+		}
+		
+		g_free (play->vis_name);
+		play->vis_name = NULL;
+		
+		g_free (play->video_info);
+		play->video_info = NULL;
+		
+		if (play->tag != NULL)
+		{
+    		g_free (play->tag->disc_id);
+    		play->tag->disc_id = NULL;
+    		
+    		g_free (play->tag->music_brainz_id);
+    		play->tag->music_brainz_id = NULL;
+    		
+    		g_free (play->tag);
+    		play->tag = NULL;
+		}
+		
+		g_free (play);
+		play = NULL;
+	}
+}
+
+
+// loads a uri into the pipeline
+gboolean gst_binding_load (gstPlay *play, char *uri) {
+	if (isValid (play))
+	{
+		g_object_set (G_OBJECT (play->element), "uri", uri, NULL);
+		if (gst_element_set_state (play->element, GST_STATE_PAUSED) != GST_STATE_CHANGE_FAILURE)
+		    return TRUE;
+	}
+	
+	return FALSE;
+}
+
+// plays the specified uri in the pipeline
+void gst_binding_play (gstPlay *play) {
+	if (isValid (play))
+		gst_element_set_state (play->element, GST_STATE_PLAYING);
+}
+
+// pauses the specified uri in the pipeline
+void gst_binding_pause (gstPlay *play) {
+	if (isValid (play))
+		gst_element_set_state (play->element, GST_STATE_PAUSED);
+}
+
+// unloads the media in the pipeline
+void gst_binding_unload (gstPlay *play) {
+	if (isValid (play)) {
+		gst_element_set_state (play->element, GST_STATE_NULL);
+
+		g_free (play->video_info);
+		play->video_info = NULL;
+		play->info_loaded = FALSE;
+	}
+}
+
+
+// retrieves the duration of the media file
+guint64 gst_binding_get_duration (gstPlay *play) {
+	if (!isValid (play)) return 0;
+	
+	GstFormat format = GST_FORMAT_TIME;
+	gint64 duration;
+	if(gst_element_query_duration (play->element, &format, &duration))
+		return duration / GST_MSECOND;
+	return 0;
+}
+
+// retrieves the position of the media file
+guint64 gst_binding_get_position (gstPlay *play) {
+	if (!isValid (play)) return 0;
+	
+	GstFormat format = GST_FORMAT_TIME;
+    gint64 position;
+	if(gst_element_query_position (play->element, &format, &position))
+		return position / GST_MSECOND;
+	return 0;
+}
+
+// set the position of the media file
+void gst_binding_set_position (gstPlay *play, gint64 time_sec) {
+	if (!isValid (play)) return;
+	
+	gst_element_seek (play->element, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
+					  GST_SEEK_TYPE_SET, time_sec * GST_MSECOND,
+					  GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
+}
+
+
+// set the position of the media file
+void gst_binding_set_track (gstPlay *play, gint64 track_number) {
+	if (!isValid (play)) return;
+	
+	gst_element_seek (play->element, 1.0, gst_format_get_by_nick ("track"),
+	      GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, track_number - 1,
+	      GST_SEEK_TYPE_NONE, -1);
+}
+
+
+// sets the volume
+void gst_binding_set_volume (gstPlay *play, gint vol) {
+	if (!isValid (play)) return;
+	
+	gdouble volume;
+    volume = CLAMP(vol, 0, 100) / 100.0;
+	g_object_set(G_OBJECT(play->element), "volume", volume, NULL);
+}
+
+// gets the volume
+gint gst_binding_get_volume (gstPlay *play) {
+	if (!isValid (play)) return 0;
+	
+    gdouble vol = 0.0;
+    g_object_get(play->element, "volume", &vol, NULL);
+    return (gint)(vol * 100.0);
+}
+
+gboolean gst_binding_has_video (gstPlay *play) {
+	if (!isValid (play)) return FALSE;
+	
+	gint cur_video;
+	g_object_get (play->element, "current-video", &cur_video, NULL);
+	if (cur_video == -1) return FALSE;
+	else return TRUE;
+}
+
+
+
+//returns the tag information
+gstTag *gst_binding_get_tag (gstPlay *play) {
+	if (isValid (play))
+		return play->tag;
+	else return NULL;
+}
+
+
+
+//returns the video info without loading it again
+gstVideoInfo *gst_binding_get_video_info (gstPlay *play) {
+	if (isValid (play))
+		return play->video_info;
+	else return NULL;
+}
+
+
+//retrieves video information, or NULL if it's not available
+gboolean gst_binding_load_video_info (gstPlay *play) {
+	if (!isValid (play)) return FALSE;
+
+	GList *stream_info = NULL, *stream;
+	g_object_get (G_OBJECT (play->element), "stream-info", &stream_info, NULL);
+	if (!stream_info) return FALSE;
+
+	/* Iterate through the streams */
+  	for (stream = stream_info; stream; stream = g_list_next (stream)) {
+  		GObject *stream_data = G_OBJECT (stream->data);
+  		gint stream_type;
+	    g_object_get (stream_data, "type", &stream_type, NULL);
+
+  		/* Look for the video stream */
+ 		if (stream_type == 2) {
+	  		GstObject *stream_object;
+	    	g_object_get (stream_data, "object", &stream_object, NULL);
+    	
+			GstCaps *caps;
+			g_object_get(stream_object, "caps", &caps, NULL);
+			if (!GST_IS_CAPS(caps)) return FALSE;
+
+			gint caps_count = gst_caps_get_size (caps), caps_index;
+			GstStructure *caps_struct;
+			const GValue *caps_value;
+			gint caps_width = -1, caps_height = -1;
+			gfloat caps_frame_rate = -1;
+			for (caps_index = 0; caps_index < caps_count; caps_index++) {
+    			caps_struct = gst_caps_get_structure (caps, caps_index);
+    			
+    			/* Check if mime type is video */
+    			const gchar *mime_type;
+				mime_type = gst_structure_get_name (caps_struct);
+				if ((!mime_type) || (g_ascii_strncasecmp(mime_type, "video", 5)))
+					continue;
+    			    			
+    			/* Look for width */
+				caps_value = gst_structure_get_value (caps_struct, "width");
+    			if (caps_value && (G_VALUE_TYPE (caps_value) == G_TYPE_INT))
+    				caps_width = g_value_get_int(caps_value);
+
+    			/* Look for height */
+    			caps_value = gst_structure_get_value (caps_struct, "height");
+    			if (caps_value && (G_VALUE_TYPE (caps_value) == G_TYPE_INT))
+    				caps_height = g_value_get_int(caps_value);
+    			
+    			/* Look for frame rate */
+    			caps_value = gst_structure_get_value (caps_struct, "framerate");
+    			if (caps_value && (G_VALUE_TYPE (caps_value) == GST_TYPE_FRACTION)) {
+    				int num = caps_value->data[0].v_int, den = caps_value->data[1].v_int;
+		            caps_frame_rate = (float)num/den;
+				}
+			}
+			
+			if ((caps_width != -1) && (caps_height != -1) && (caps_frame_rate != -1)) {
+				play->video_info = g_new0 (gstVideoInfo, 1);
+				play->video_info->width = caps_width;
+				play->video_info->height = caps_height;
+				play->video_info->frame_rate = caps_frame_rate;
+				return TRUE;
+			}
+		}
+	}
+	return FALSE;
+}
+
+
+
+
+
+
+
+void gst_binding_set_xid (gstPlay *play, gulong xid) {
+	if (play == NULL)
+		return;
+	
+	play->xid = xid;
+	if (play->overlay != NULL && GST_IS_X_OVERLAY (play->overlay))
+		gst_x_overlay_set_xwindow_id (play->overlay, xid);
+}
+
+void gst_binding_set_eos_cb(gstPlay *play, eosCallback cb) {
+    if (play != NULL) play->eos_cb = cb;
+}
+void gst_binding_set_error_cb(gstPlay *play, errorCallback cb) {
+	if (play != NULL) play->error_cb = cb;
+}
+void gst_binding_set_buffer_cb(gstPlay *play, bufferCallback cb) {
+    if (play != NULL) play->buffer_cb = cb;
+}
+void gst_binding_set_info_cb(gstPlay *play, infoCallback cb) {
+    if (play != NULL) play->info_cb = cb;
+}
+void gst_binding_set_tag_cb(gstPlay *play, tagCallback cb) {
+    if (play != NULL) play->tag_cb = cb;
+}
+
+
+
+
+
+
+gboolean
+filter_features (GstPluginFeature *feature, gpointer data)
+{
+  GstElementFactory *f;
+
+  if (!GST_IS_ELEMENT_FACTORY (feature))
+    return FALSE;
+  f = GST_ELEMENT_FACTORY (feature);
+  if (!g_strrstr (gst_element_factory_get_klass (f), "Visualization"))
+    return FALSE;
+
+  return TRUE;
+}
+
+
+
+GList *
+get_visualization_features (void)
+{
+  return gst_registry_feature_filter (gst_registry_get_default (),
+      filter_features, FALSE, NULL);
+}
+
+
+
+
+
+//finds the visualisation factory
+GstElementFactory *
+setup_vis_find_factory (const gchar *vis_name)
+{
+  GstElementFactory *fac = NULL;
+  GList *l, *features;
+
+  features = get_visualization_features ();
+
+  /* find element factory using long name */
+  for (l = features; l != NULL; l = l->next) {
+    GstElementFactory *f = GST_ELEMENT_FACTORY (l->data);
+    
+    
+    //long name
+    if (f && strcmp (vis_name, gst_element_factory_get_longname (f)) == 0) {
+      fac = f;
+      goto done;
+    }
+    
+    //short name
+    else if (f && strcmp (vis_name, GST_PLUGIN_FEATURE_NAME (f)) == 0) {
+      fac = f;
+      goto done;
+    }
+    
+  }
+  
+
+done:
+  g_list_free (features);
+  return fac;
+}
+
+
+
+
+
+
+
+
+// setups audio visualization
+// a modified version of totem's bacon video widget
+static void
+setup_vis (gstPlay *play)
+{
+	if (play->xid == 0)
+		return;
+	
+	GstElement *vis_bin = NULL;
+	GstElement *vis_element = NULL;
+	GstElement *vis_capsfilter = NULL;
+	GstPad *pad = NULL;
+	GstElementFactory *fac = NULL;
+	
+	
+	fac = setup_vis_find_factory (play->vis_name);
+	if (fac == NULL)
+		goto beach; //cant find the visualisation
+	
+	
+	vis_element = gst_element_factory_create (fac, "vis_element");
+	if (!GST_IS_ELEMENT (vis_element))
+		goto beach; //cant create visualisation element
+	
+	
+	
+	vis_capsfilter = gst_element_factory_make ("capsfilter", "vis_capsfilter");
+	if (!GST_IS_ELEMENT (vis_capsfilter))
+	{
+		gst_object_unref (vis_element);
+		goto beach; //cant create visualisation capsfilter element
+	}
+	
+	
+	vis_bin = gst_bin_new ("vis_bin");
+	if (!GST_IS_ELEMENT (vis_bin))
+	{
+		gst_object_unref (vis_element);
+		gst_object_unref (vis_capsfilter);
+		goto beach; //cant create visualisation bin
+	}
+	
+	
+	gst_bin_add_many (GST_BIN (vis_bin), vis_element, vis_capsfilter, NULL);
+	
+	// sink ghostpad
+	pad = gst_element_get_pad (vis_element, "sink");
+	gst_element_add_pad (vis_bin, gst_ghost_pad_new ("sink", pad));
+	gst_object_unref (pad);
+	
+	
+	// source ghostpad, link with vis_element
+	pad = gst_element_get_pad (vis_capsfilter, "src");
+	gst_element_add_pad (vis_bin, gst_ghost_pad_new ("src", pad));
+	gst_element_link_pads (vis_element, "src", vis_capsfilter, "sink");
+	gst_object_unref (pad);
+	
+beach:
+	g_object_set (play->element, "vis-plugin", vis_bin, NULL);
+	
+	return;
+}
+
+
+
+
+
+
+void
+add_longname (GstElementFactory *f, GList ** to)
+{
+  *to = g_list_append (*to, (gchar *) gst_element_factory_get_longname (f));
+}
+
+
+
+
+void
+gst_binding_set_visual (gstPlay *play, const gchar *vis_name)
+{
+	play->vis_name = g_strdup (vis_name);
+	setup_vis (play);
+}
+
+
+GList *
+gst_binding_get_visuals_list (gstPlay *play)
+{
+  GList *features, *names = NULL;
+
+  if (!isValid (play)) return NULL;
+
+
+  features = get_visualization_features ();
+  g_list_foreach (features, (GFunc) add_longname, &names);
+  g_list_free (features);
+
+  return names;
+}



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