| // Authors | |
| // Copyright (C) 2008 Paul Burton <paulburton89@gmail.com> | |
| // Copyright (C) 2010 Andoni Morales <ylatuya@gmail.com> | |
| // Copyright (C) 2013 Stephan Sundermann <stephansundermann@gmail.com> | |
| using System; | |
| using System; | |
| using System.Runtime.InteropServices; | |
| using Gtk; | |
| using Gst; | |
| using Gst.Video; | |
| using Gst.Base; | |
| namespace Gstreameroverlay | |
| { | |
| public class MainWindow : Gtk.Window { | |
| DrawingArea _da; | |
| IntPtr _xWindowId; | |
| Element _playbin; | |
| HScale _scale; | |
| Label _lbl; | |
| bool _updatingScale; | |
| bool _pipelineOK = false; | |
| public static void Main (string[] args) { | |
| if (System.Environment.OSVersion.Platform == PlatformID.Unix) | |
| XInitThreads (); | |
| Gtk.Application.Init (); | |
| Gst.Application.Init (); | |
| MainWindow window = new MainWindow (); | |
| window.ShowAll (); | |
| switch (System.Environment.OSVersion.Platform) { | |
| case PlatformID.Unix: | |
| window._xWindowId = gdk_x11_window_get_xid (window._da.GdkWindow.Handle); | |
| break; | |
| case PlatformID.Win32NT: | |
| case PlatformID.Win32S: | |
| case PlatformID.Win32Windows: | |
| case PlatformID.WinCE: | |
| window._xWindowId = gdk_win32_drawable_get_handle (window._da.GdkWindow.Handle); | |
| break; | |
| } | |
| Gtk.Application.Run (); | |
| } | |
| public MainWindow () | |
| : base ("Overlaytest") { | |
| VBox vBox = new VBox (); | |
| _da = new DrawingArea (); | |
| _da.ModifyBg (Gtk.StateType.Normal, new Gdk.Color (0, 0, 0)); | |
| _da.SetSizeRequest (400, 300); | |
| _da.DoubleBuffered = false; | |
| vBox.PackStart (_da, false, false, 0); | |
| _scale = new HScale (0, 1, 0.01); | |
| _scale.DrawValue = false; | |
| _scale.ValueChanged += ScaleValueChanged; | |
| vBox.PackStart (_scale, false, false, 0); | |
| HBox hBox = new HBox (); | |
| Button btnOpen = new Button (); | |
| btnOpen.Label = "Open"; | |
| btnOpen.Clicked += ButtonOpenClicked; | |
| hBox.PackStart (btnOpen, false, false, 0); | |
| Button btnPlay = new Button (); | |
| btnPlay.Label = "Play"; | |
| btnPlay.Clicked += ButtonPlayClicked; | |
| hBox.PackStart (btnPlay, false, false, 0); | |
| Button btnPause = new Button (); | |
| btnPause.Label = "Pause"; | |
| btnPause.Clicked += ButtonPauseClicked; | |
| hBox.PackStart (btnPause, false, false, 0); | |
| _lbl = new Label (); | |
| _lbl.Text = "00:00 / 00:00"; | |
| hBox.PackEnd (_lbl, false, false, 0); | |
| vBox.PackStart (hBox, false, false, 3); | |
| Add (vBox); | |
| WindowPosition = Gtk.WindowPosition.Center; | |
| DeleteEvent += OnDeleteEvent; | |
| GLib.Timeout.Add (1000, new GLib.TimeoutHandler (UpdatePos)); | |
| } | |
| void OnDeleteEvent (object sender, DeleteEventArgs args) { | |
| Gtk.Application.Quit (); | |
| if (_playbin != null) { | |
| _playbin.SetState (Gst.State.Null); | |
| _playbin.Dispose (); | |
| _playbin = null; | |
| } | |
| args.RetVal = true; | |
| } | |
| void ButtonOpenClicked (object sender, EventArgs args) { | |
| FileChooserDialog dialog = new FileChooserDialog ("Open", this, FileChooserAction.Open, new object[] { "Cancel", ResponseType.Cancel, "Open", ResponseType.Accept }); | |
| dialog.SetCurrentFolder (Environment.GetFolderPath (Environment.SpecialFolder.Personal)); | |
| if (dialog.Run () == (int) ResponseType.Accept) { | |
| _pipelineOK = false; | |
| if (_playbin != null) { | |
| _playbin.SetState (Gst.State.Null); | |
| } else { | |
| _playbin = ElementFactory.Make ("playbin", "playbin"); | |
| } | |
| _scale.Value = 0; | |
| if (_playbin == null) | |
| Console.WriteLine ("Unable to create element 'playbin'"); | |
| _playbin.Bus.EnableSyncMessageEmission (); | |
| _playbin.Bus.AddSignalWatch (); | |
| _playbin.Bus.SyncMessage += delegate (object bus, SyncMessageArgs sargs) { | |
| Gst.Message msg = sargs.Message; | |
| if (!Gst.Video.Global.IsVideoOverlayPrepareWindowHandleMessage (msg)) | |
| return; | |
| Element src = msg.Src as Element; | |
| if (src == null) | |
| return; | |
| try { | |
| src["force-aspect-ratio"] = true; | |
| } | |
| catch (PropertyNotFoundException) {} | |
| Element overlay = null; | |
| if(src is Gst.Bin) | |
| overlay = ((Gst.Bin) src).GetByInterface (VideoOverlayAdapter.GType); | |
| VideoOverlayAdapter adapter = new VideoOverlayAdapter (overlay.Handle); | |
| adapter.WindowHandle = _xWindowId; | |
| adapter.HandleEvents (true); | |
| }; | |
| _playbin.Bus.Message += delegate (object bus, MessageArgs margs) { | |
| Message message = margs.Message; | |
| switch (message.Type) { | |
| case Gst.MessageType.Error: | |
| GLib.GException err; | |
| string msg; | |
| message.ParseError (out err, out msg); | |
| Console.WriteLine (String.Format ("Error message: {0}", msg)); | |
| _pipelineOK = false; | |
| break; | |
| case Gst.MessageType.Eos: | |
| Console.WriteLine ("EOS"); | |
| break; | |
| } | |
| }; | |
| switch (System.Environment.OSVersion.Platform) { | |
| case PlatformID.Unix: | |
| _playbin["uri"] = "file://" + dialog.Filename; | |
| break; | |
| case PlatformID.Win32NT: | |
| case PlatformID.Win32S: | |
| case PlatformID.Win32Windows: | |
| case PlatformID.WinCE: | |
| _playbin["uri"] = "file:///" + dialog.Filename.Replace("\\","/"); | |
| break; | |
| } | |
| StateChangeReturn sret = _playbin.SetState (Gst.State.Playing); | |
| if (sret == StateChangeReturn.Async) { | |
| State state, pending; | |
| sret = _playbin.GetState (out state, out pending, Gst.Constants.SECOND * 5L); | |
| } | |
| if (sret == StateChangeReturn.Success) { | |
| Console.WriteLine ("State change successful"); | |
| _pipelineOK = true; | |
| } else { | |
| Console.WriteLine ("State change failed for {0} ({1})\n", dialog.Filename, sret); | |
| } | |
| } | |
| dialog.Destroy (); | |
| } | |
| void ButtonPlayClicked (object sender, EventArgs args) { | |
| if ( (_playbin != null) && _pipelineOK) | |
| _playbin.SetState (Gst.State.Playing); | |
| } | |
| void ButtonPauseClicked (object sender, EventArgs args) { | |
| if ( (_playbin != null) && _pipelineOK) | |
| _playbin.SetState (Gst.State.Paused); | |
| } | |
| void ScaleValueChanged (object sender, EventArgs args) { | |
| if (_updatingScale) | |
| return; | |
| long duration; | |
| Gst.Format fmt = Gst.Format.Time; | |
| Console.WriteLine ("Trying to seek"); | |
| if ( (_playbin != null) && _pipelineOK && _playbin.QueryDuration (fmt, out duration) && duration != -1) { | |
| long pos = (long) (duration * _scale.Value); | |
| Console.WriteLine ("Seek to {0}/{1} ({2}%)", pos, duration, _scale.Value); | |
| bool ret = _playbin.SeekSimple (Format.Time, SeekFlags.Flush | SeekFlags.KeyUnit, pos); | |
| Console.WriteLine ("Seeked {0}successfully", (ret ? "" : "not ")); | |
| } | |
| } | |
| bool UpdatePos () { | |
| Gst.Format fmt = Gst.Format.Time; | |
| long duration, pos; | |
| pos = 0; | |
| if ( (_playbin != null) && _pipelineOK && | |
| _playbin.QueryDuration (fmt, out duration) && | |
| _playbin.QueryPosition (fmt, out pos)) { | |
| _lbl.Text = string.Format ("{0} / {1}", TimeString (pos), TimeString (duration)); | |
| _updatingScale = true; | |
| _scale.Value = (double) pos / duration; | |
| _updatingScale = false; | |
| } | |
| return true; | |
| } | |
| string TimeString (long t) { | |
| long secs = t / 1000000000; | |
| int mins = (int) (secs / 60); | |
| secs = secs - (mins * 60); | |
| if (mins >= 60) { | |
| int hours = (int) (mins / 60); | |
| mins = mins - (hours * 60); | |
| return string.Format ("{0}:{1:d2}:{2:d2}", hours, mins, secs); | |
| } | |
| return string.Format ("{0}:{1:d2}", mins, secs); | |
| } | |
| [DllImport ("libgdk-3.so.0") ] | |
| static extern IntPtr gdk_x11_window_get_xid (IntPtr handle); | |
| [DllImport ("gdk-win32-3.0-0.dll") ] | |
| static extern IntPtr gdk_win32_drawable_get_handle (IntPtr handle); | |
| [DllImport ("libX11.so.6")] | |
| static extern int XInitThreads (); | |
| } | |
| } |