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

[Vala] GStreamer Video and Vala



Hi all,

yesterday night I tried to write a minimal video player for mainly two
different reasons:

1) I was asked for some simple *and* working sample code to embend a
video in a window

2) I've a dormient project that use gstreamer heavily and I want to see
the status of gstreamer api.

The writing went quite smooth but I've found some bugs when using the
XOverlay interface caused by this bug
http://bugzilla.gnome.org/show_bug.cgi?id=512744

So I've patched the compiler (valainterface.vala) to support the
'lower_case_csuffix' parameter in the CCode attribute and I modified the
bindings of gstreamer-interfaces-0.10 and then all went well.

Attached the patched to valac and the reference code of a very minimal
video player.

Have a nice day,
Andrea

P.S:
I'be attached that same fix to the bug report.


videobox: videobox.vala videobox.ui
	valac -o videobox videobox.vala --pkg gtk+-2.0 --pkg gstreamer-0.10 --pkg gstreamer-interfaces-0.10 --pkg gdk-x11-2.0

videobox.ui: videobox.glade
	gtk-builder-convert videobox.glade videobox.ui

clean:
	rm -rf videobox.c videobox.h videobox videobox.ui

Attachment: videobox.glade
Description: application/glade

using GLib;
using Gtk;
using Gst;

namespace VideoBox
{

	public static int main (string[] args)
	{
		Gtk.init (ref args);
		Gst.init (ref args);
		var vidget = new Vidget ();
		
		Gtk.main ();
		return 0;
	}

	public class Vidget : GLib.Object
	{
		private Widget _video;
		private uint32 _video_xwindow_id = 0;
		private Button _button_open;
		private Button _button_play;
		private Button _button_stop;

		private bool _is_playing = false;
		private Pipeline _pipeline = null;
		private Element _input = null;
		private Element _colorspaceconv = null;

		private string _filename = null;

		construct
		{
			try
			{
				var builder = new Builder ();
				builder.add_from_file ("videobox.ui");

				_video = (Widget) builder.get_object ("drawingarea_video");
				assert (_video != null);
				_video.expose_event += this.on_video_expose;

				_button_open = (Button) builder.get_object ("button_open");
				assert (_button_open != null);
				_button_open.clicked += this.on_button_open_clicked;

				_button_play = (Button) builder.get_object ("button_play");
				assert (_button_play != null);
				_button_play.clicked += sender => { play (); };

				_button_stop = (Button) builder.get_object ("button_stop");
				assert (_button_stop != null);
				_button_stop.clicked += sender => { stop (); };

                                var window = (Window) builder.get_object ("window_main");
                                assert (window != null);
                                window.show_all ();
				window.delete_event += this.on_window_delete_event;
			} catch (Error err) {
				critical ("error %s", err.message);
			}
		}

		private string filename
		{
			get { return _filename; }
			set
			{
				_filename = value;
				debug ("file to play %s", _filename);
				update_button_status ();
			}
		}

		private void update_button_status ()
		{
                        _button_open.set_sensitive (_filename == null || _is_playing == false);
			_button_play.set_sensitive (_filename != null && _is_playing == false);
			_button_stop.set_sensitive (_filename != null && _is_playing);
		}

		private bool on_window_delete_event (Widget sender, Gdk.Event event)
		{
			if (_is_playing)
				this.stop ();

			Gtk.main_quit ();
			return true;
		}

		private void on_button_open_clicked (Widget sender)
		{
			var dialog = new FileChooserDialog ("Add media...", null,  FileChooserAction.OPEN, 
								"gtk-cancel", Gtk.ResponseType.CANCEL,
								"gtk-open", Gtk.ResponseType.ACCEPT);
			var filter = new FileFilter ();

			filter.set_name ("All Media Files");
			filter.add_mime_type ("video/*");
			filter.add_pattern ("*.ogg");
			dialog.add_filter (filter);
			dialog.set_filter (filter);

			dialog.set_select_multiple (false);
			if (dialog.run() == Gtk.ResponseType.ACCEPT) {
				filename = ((FileChooser) dialog).get_filename();
			}
			dialog.destroy ();
		}

		private bool on_video_expose(Widget sender, Gdk.EventExpose event)
		{
			_video_xwindow_id = Gdk.x11_drawable_get_xid (sender.window);
			_video.expose_event -= this.on_video_expose;
			debug ("drawinf area xwindow id %d", _video_xwindow_id);
			return true;
		}

		private void on_pad_added(Gst.Element element, Gst.Pad new_pad)
		{
			string mimetype = new_pad.get_caps().to_string();
			Pipeline pipeline = (Pipeline) element.get_parent();
			Element output = null;
			if (mimetype.has_prefix("video/"))
			{
				debug ("new video pad");
				output = _colorspaceconv;
			}
			else if (mimetype.has_prefix("audio/"))
			{
				debug ("new audio pad");
				output = ElementFactory.make("fakesink",null);
				pipeline.add(output);
			}
			output.set_state(State.PAUSED);

			Gst.Pad pad = null;
			pad = output.get_compatible_pad(new_pad, new_pad.get_caps());
			return_if_fail(pad != null);

			PadLinkReturn ret = new_pad.link(pad);
			if ( ret != PadLinkReturn.OK)
			{
				warning ("Error linking pads %d req caps %s my caps %s", ret, new_pad.get_caps().to_string(), pad.get_caps().to_string());
			}
		}

		private void create_pipeline()
		{
			_input = ElementFactory.make("filesrc","input");
			_input.set ("location", _filename);
			return_if_fail(_input != null);

                        Element videoscale = ElementFactory.make("decodebin","decoder");
			return_if_fail(videoscale != null);
			videoscale.pad_added += this.on_pad_added;

			_colorspaceconv = ElementFactory.make("ffmpegcolorspace","colorspaceconv");
			return_if_fail(_colorspaceconv != null);

			var output = ElementFactory.make("xvimagesink","output");
			return_if_fail(output != null);

			_pipeline = (Pipeline) new Pipeline("videobox");
			return_if_fail(_pipeline != null);
                        
			_pipeline.add (_input);
			_pipeline.add (videoscale);
			_pipeline.add (_colorspaceconv);
			_pipeline.add (output);
			
			_input.link (videoscale);
			videoscale.link (_colorspaceconv);
			_colorspaceconv.link (output);
                        
			Bus bus = _pipeline.get_bus ();
			bus.set_sync_handler ((BusSyncHandler) this.sync_bus_message_callback, this);
			_pipeline.set_state (State.PAUSED);
		}

		private void destroy_pipeline ()
		{
			_input = null;
			_colorspaceconv = null;
			_pipeline = null;
		}

		private void play ()
		{
			create_pipeline ();
			_pipeline.set_state(State.PLAYING);
			_is_playing = true; //HACK: this should listent to bus messages
			update_button_status ();
		}

		private void stop ()
		{
			_pipeline.set_state(State.NULL);
			_is_playing = false;
			update_button_status ();
			destroy_pipeline ();
		}

		public static BusSyncReply sync_bus_message_callback (Bus bus, Message message, pointer data)
		{
			var self = (Vidget) data;

			if (message.structure != null && message.structure.has_name("prepare-xwindow-id")) {
				/* set xwindow id */
				debug ("prepare-xwindow-id message received");
				((XOverlay)message.src).set_xwindow_id (self._video_xwindow_id);
				return BusSyncReply.DROP;
			}
			return BusSyncReply.PASS;
		}

	}
}
diff --git a/vala/valainterface.vala b/vala/valainterface.vala
index e3615c1..d726841 100644
--- a/vala/valainterface.vala
+++ b/vala/valainterface.vala
@@ -263,20 +263,32 @@ public class Vala.Interface : Typesymbol {
 	 */
 	public string! get_lower_case_csuffix () {
 		if (lower_case_csuffix == null) {
-			lower_case_csuffix = camel_case_to_lower_case (name);
-
-			// remove underscores in some cases to avoid conflicts of type macros
-			if (lower_case_csuffix.has_prefix ("type_")) {
-				lower_case_csuffix = "type" + lower_case_csuffix.offset ("type_".len ());
-			} else if (lower_case_csuffix.has_prefix ("is_")) {
-				lower_case_csuffix = "is" + lower_case_csuffix.offset ("is_".len ());
-			}
-			if (lower_case_csuffix.has_suffix ("_class")) {
-				lower_case_csuffix = lower_case_csuffix.substring (0, lower_case_csuffix.len () - "_class".len ()) + "class";
-			}
+			lower_case_csuffix = get_default_lower_case_csuffix ();
 		}
 		return lower_case_csuffix;
 	}
+
+	/**
+	 * Returns default string to be prepended to the name of members of this
+	 * interface when used in C code.
+	 *
+	 * @return the suffix to be used in C code
+	 */
+	public string! get_default_lower_case_csuffix () {
+		string result = camel_case_to_lower_case (name);
+
+		// remove underscores in some cases to avoid conflicts of type macros
+		if (result.has_prefix ("type_")) {
+			result = "type" + result.offset ("type_".len ());
+		} else if (result.has_prefix ("is_")) {
+			result = "is" + result.offset ("is_".len ());
+		}
+		if (result.has_suffix ("_class")) {
+			result = result.substring (0, result.len () - "_class".len ()) + "class";
+		}
+
+		return result;
+	}
 	
 	/**
 	 * Sets the string to be prepended to the name of members of this
@@ -389,6 +401,9 @@ public class Vala.Interface : Typesymbol {
 				add_cheader_filename (filename);
 			}
 		}
+		if (a.has_argument ("lower_case_csuffix")) {
+			lower_case_csuffix = a.get_string ("lower_case_csuffix");
+		}
 	}
 
 	private void process_dbus_interface_attribute (Attribute! a) {
diff --git a/vala/valainterfacewriter.vala b/vala/valainterfacewriter.vala
index 10c8e93..c7d7ddf 100644
--- a/vala/valainterfacewriter.vala
+++ b/vala/valainterfacewriter.vala
@@ -240,7 +240,11 @@ public class Vala.InterfaceWriter : CodeVisitor {
 				cheaders = "%s,%s".printf (cheaders, cheader);
 			}
 		}
-		write_string ("[CCode (cheader_filename = \"%s\")]".printf (cheaders));
+		write_string ("[CCode (cheader_filename = \"%s\"".printf (cheaders));
+		if (iface.get_lower_case_csuffix () != iface.get_default_lower_case_csuffix ())
+			write_string (", lower_case_csuffix = \"%s\"".printf (iface.get_lower_case_csuffix ()));
+
+		write_string (")]");
 		write_newline ();
 
 		write_indent ();
diff --git a/vapi/packages/gstreamer-interfaces-0.10/gstreamer-interfaces-0.10.metadata b/vapi/packages/gstreamer-interfaces-0.10/gstreamer-interfaces-0.10.metadata
index 4602013..4abc3ea 100644
--- a/vapi/packages/gstreamer-interfaces-0.10/gstreamer-interfaces-0.10.metadata
+++ b/vapi/packages/gstreamer-interfaces-0.10/gstreamer-interfaces-0.10.metadata
@@ -23,4 +23,4 @@ GstNavigation cheader_filename="gst/interfaces/navigation.h"
 GstPropertyProbe cheader_filename="gst/interfaces/propertyprobe.h"
 GstTuner cheader_filename="gst/interfaces/tunerchannel.h"
 GstVideoOrientation cheader_filename="gst/interfaces/videoorientation.h"
-GstXOverlay cheader_filename="gst/interfaces/xoverlay.h"
+GstXOverlay cheader_filename="gst/interfaces/xoverlay.h" lower_case_csuffix="x_overlay"
diff --git a/vapigen/valagidlparser.vala b/vapigen/valagidlparser.vala
index 1b9cae7..4fbd575 100644
--- a/vapigen/valagidlparser.vala
+++ b/vapigen/valagidlparser.vala
@@ -917,6 +917,8 @@ public class Vala.GIdlParser : CodeVisitor {
 					var nv = attr.split ("=", 2);
 					if (nv[0] == "cheader_filename") {
 						iface.add_cheader_filename (eval (nv[1]));
+					} else if (nv[0] == "lower_case_csuffix") {
+						iface.set_lower_case_csuffix (eval (nv[1]));
 					}
 				}
 			}


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