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]));
}
}
}