gnome-subtitles r1079 - in trunk/src/External: . GStreamerPlaybin
- From: pcastro svn gnome org
- To: svn-commits-list gnome org
- Subject: gnome-subtitles r1079 - in trunk/src/External: . GStreamerPlaybin
- Date: Sun, 23 Nov 2008 18:23:56 +0000 (UTC)
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, ¤t_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]