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