summaryrefslogtreecommitdiff
path: root/samples
diff options
context:
space:
mode:
authorStephan Sundermann <ssundermann@gnome.org>2014-08-03 17:41:31 +0200
committerStephan Sundermann <ssundermann@gnome.org>2014-08-03 17:41:31 +0200
commit6c5ab2a474d29d5cf1f2b181c7797f9fb5886e89 (patch)
tree4ac778a8b77f796968cf4160991df47d3e609ee4 /samples
parent54812a36ed34b1be7425f7d274fe6d03d026a91a (diff)
sample: Add basic tutorial 5 sample
Diffstat (limited to 'samples')
-rw-r--r--samples/BasicTutorial5.cs368
-rw-r--r--samples/Makefile.am6
2 files changed, 373 insertions, 1 deletions
diff --git a/samples/BasicTutorial5.cs b/samples/BasicTutorial5.cs
new file mode 100644
index 0000000..1a4d7f5
--- /dev/null
+++ b/samples/BasicTutorial5.cs
@@ -0,0 +1,368 @@
+// Authors
+// Copyright (C) 2014 Stephan Sundermann <stephansundermann@gmail.com>
+
+using System;
+using Gst;
+using Gtk;
+using System.Runtime.InteropServices;
+using Gst.Video;
+
+namespace GstreamerSharp
+{
+ class Playback
+ {
+ static Element Playbin;
+ static Range Slider;
+ static TextView StreamsList;
+ static ulong silderUpdateSignalID;
+
+ static State State;
+ static long Duration = -1;
+ static int ignoreCount = 0;
+
+ static void HandleValueChanged (object sender, EventArgs e)
+ {
+ var range = (Range)sender;
+ var value = range.Value;
+ Playbin.SeekSimple (Format.Time, SeekFlags.Flush | SeekFlags.KeyUnit, (long)(value * Gst.Constants.SECOND));
+ }
+
+ // This method is called when the STOP button is clicked
+ static void HandleStop (object sender, EventArgs e)
+ {
+ Playbin.SetState (State.Ready);
+ }
+
+ // This method is called when the PAUSE button is clicked
+ static void HandlePause (object sender, EventArgs e)
+ {
+ Playbin.SetState (State.Paused);
+ }
+
+ // This method is called when the PLAY button is clicked
+ static void HandlePlay (object sender, EventArgs e)
+ {
+ Playbin.SetState (State.Playing);
+
+ }
+
+ static void HandleRealized (object sender, EventArgs e)
+ {
+ var widget = (Widget)sender;
+ var window = widget.Window;
+ ulong windowID = 0;
+
+ // Retrieve window handler from GDK
+ switch (System.Environment.OSVersion.Platform) {
+ case PlatformID.Unix:
+ windowID = gdk_x11_window_get_xid (window.Handle);
+ break;
+ case PlatformID.Win32NT:
+ case PlatformID.Win32S:
+ case PlatformID.Win32Windows:
+ case PlatformID.WinCE:
+ windowID = (ulong) gdk_win32_drawable_get_handle (window.Handle);
+ break;
+ }
+
+ Element overlay = null;
+ if(Playbin is Gst.Bin)
+ overlay = ((Gst.Bin) Playbin).GetByInterface (VideoOverlayAdapter.GType);
+
+ VideoOverlayAdapter adapter = new VideoOverlayAdapter (overlay.Handle);
+ adapter.WindowHandle = windowID;
+ adapter.HandleEvents (true);
+ }
+
+ // This function is called when the main window is closed
+ static void HandleDelete (object o, DeleteEventArgs args)
+ {
+ HandleStop (null, null);
+ Gtk.Application.Quit ();
+ }
+
+ //This function is called everytime the video window needs to be redrawn (due to damage/exposure, rescaling, etc). GStreamer takes care of this in the PAUSED and PLAYING states, otherwise, we simply draw a black rectangle to avoid garbage showing up. */
+ static void HandleDamage (object o, DamageEventArgs args)
+ {
+ var widget = (Widget)o;
+
+ if (State != State.Paused && State != State.Playing) {
+ var window = widget.Window;
+ var allocation = widget.Allocation;
+
+ var cr = Gdk.CairoHelper.Create (window);
+ cr.SetSourceRGB (0, 0, 0);
+ cr.Rectangle (0, 0, allocation.Width, allocation.Height);
+ cr.Fill ();
+ cr.Dispose ();
+ }
+
+ args.RetVal = false;
+ }
+
+ static void CreateUI () {
+ var mainWindow = new Window (WindowType.Toplevel);
+ mainWindow.DeleteEvent += HandleDelete;
+
+ var videoWindow = new DrawingArea ();
+ videoWindow.DoubleBuffered = false;
+ videoWindow.Realized += HandleRealized;
+ videoWindow.DamageEvent += HandleDamage;
+
+ var playButton = new Button (Stock.MediaPlay);
+ playButton.Clicked += HandlePlay;
+
+ var pauseButton = new Button (Stock.MediaPause);
+ pauseButton.Clicked += HandlePause;
+
+ var stopButton = new Button (Stock.MediaStop);
+ stopButton.Clicked += HandleStop;
+
+ Slider = new HScale (0, 100, 1);
+ ((Scale)Slider).DrawValue = false;
+ Slider.ValueChanged += HandleValueChanged;
+
+ StreamsList = new TextView ();
+ StreamsList.Editable = false;
+
+ var controls = new HBox (false, 0);
+ controls.PackStart (playButton, false, false, 2);
+ controls.PackStart (pauseButton, false, false, 2);
+ controls.PackStart (stopButton, false, false, 2);
+ controls.PackStart (Slider, true, true, 2);
+
+ var mainHBox = new HBox (false, 0);
+ mainHBox.PackStart (videoWindow, true, true, 0);
+ mainHBox.PackStart (StreamsList, false, false, 2);
+
+ var mainBox = new VBox (false, 0);
+ mainBox.PackStart (mainHBox, true, true, 0);
+ mainBox.PackStart (controls, false, false, 0);
+ mainWindow.Add (mainBox);
+ mainWindow.SetDefaultSize (640, 480);
+
+ mainWindow.ShowAll ();
+ }
+
+ // This function is called periodically to refresh the GUI
+ static bool RefreshUI () {
+ var fmt = Format.Time;
+ long current = 0;
+
+ // We do not want to update anything nless we are in the PAUSED or PLAYING states
+ if (State != State.Playing && State != State.Paused)
+ return true;
+
+ // If we didn't know it yet, query the stream duration
+ if (Duration < 0) {
+ if (!Playbin.QueryDuration (fmt, out Duration))
+ Console.WriteLine ("Could not query the current duration.");
+ else {
+ // Set the range of the silder to the clip duration, in SECONDS
+ Slider.SetRange (0, Duration / (double)Gst.Constants.SECOND);
+ }
+ }
+
+ if (Playbin.QueryPosition (fmt, out current)) {
+ // Block the "value-changed" signal, so the HandleSlider function is not called (which would trigger a seek the user has not requested)
+ ignoreCount++;
+ Slider.ValueChanged -= HandleValueChanged;
+ // Set the position of the slider to the current pipeline position, in SECONDS
+ Slider.Value = current / (double)Gst.Constants.SECOND;
+ Slider.ValueChanged += HandleValueChanged;
+
+ }
+ return true;
+ }
+
+
+
+ // This function is called when an error message is posted on the bus
+ static void HandleTags (object sender, GLib.SignalArgs args) {
+ // We are possibly in the Gstreamer working thread, so we notify the main thread of this event through a message in the bus
+ var s = new Structure ("tags-changed");
+ Playbin.PostMessage (new Message (Playbin, s));
+ }
+
+ // This function is called when an error message is posted on the bus
+ static void HandleError (object sender, GLib.SignalArgs args) {
+ var msg = (Message)args.Args [0];
+ string debug;
+ GLib.GException exc;
+ msg.ParseError (out exc, out debug);
+ Console.WriteLine (string.Format ("Error received from element {0}: {1}", msg.Src.Name, exc.Message));
+ Console.WriteLine ("Debugging information: {0}", debug);
+ // Set the pipeline to READY (which stops playback)
+ Playbin.SetState (State.Ready);
+ }
+
+ // This function is called when an End-Of-Stream message is posted on the bus. We just set the pipelien to READY (which stops playback)
+ static void HandleEos (object sender, GLib.SignalArgs args) {
+ Console.WriteLine ("End-Of-Stream reached.");
+ Playbin.SetState (State.Ready);
+ }
+
+ // This function is called when the pipeline changes states. We use it to keep track of the current state.
+ static void HandleStateChanged (object sender, GLib.SignalArgs args) {
+ var msg = (Message) args.Args [0];
+ State oldState, newState, pendingState;
+ msg.ParseStateChanged (out oldState, out newState, out pendingState);
+ if (msg.Src == Playbin) {
+ State = newState;
+ Console.WriteLine ("State set to {0}", Element.StateGetName (newState));
+ if (oldState == State.Ready && newState == State.Paused) {
+ // For extra responsiveness, we refresh the GUI as soon as we reach the PAUSED state
+ RefreshUI ();
+ }
+ }
+
+ }
+
+ // Extract metadata from all the streams and write it to the text widget in the GUI
+ static void AnalyzeStreams () {
+ TagList tags;
+ String str, totalStr;
+ uint rate;
+
+ // Clean current contents of the widget
+ var text = StreamsList.Buffer;
+ text.Text = String.Empty;
+
+ // Read some properties
+ var nVideo = (int) Playbin ["n-video"];
+ var nAudio = (int) Playbin ["n-audio"];
+ var nText = (int) Playbin ["n-text"];
+
+ for (int i = 0; i < nVideo; i++) {
+ // Retrieve the stream's video tags
+ tags = (TagList)Playbin.Emit ("get-video-tags", i);
+
+ if (tags != null) {
+ totalStr = string.Format ("video stream {0}:\n", i);
+ text.InsertAtCursor (totalStr);
+ tags.GetString (Gst.Constants.TAG_VIDEO_CODEC, out str);
+ totalStr = string.Format (" codec: {0}\n", str != null ? str : "unknown");
+ text.InsertAtCursor (totalStr);
+ }
+ }
+
+ for (int i = 0; i < nAudio; i++) {
+ // Retrieve the stream's audio tags
+ tags = (TagList)Playbin.Emit ("get-audio-tags", i);
+
+ if (tags != null) {
+ totalStr = string.Format ("audio stream {0}:\n", i);
+ text.InsertAtCursor (totalStr);
+
+ str = String.Empty;
+ if (tags.GetString (Gst.Constants.TAG_AUDIO_CODEC, out str)) {
+ totalStr = string.Format (" codec: {0}\n", str);
+ text.InsertAtCursor (totalStr);
+ }
+ str = String.Empty;
+
+ if (tags.GetString (Gst.Constants.TAG_LANGUAGE_CODE+"dr", out str)) {
+ totalStr = string.Format (" language: {0}\n", str);
+ text.InsertAtCursor (totalStr);
+ }
+ str = String.Empty;
+
+ if (tags.GetUint (Gst.Constants.TAG_BITRATE, out rate)) {
+ totalStr = string.Format (" bitrate: {0}\n", rate);
+ text.InsertAtCursor (totalStr);
+ }
+ }
+ }
+
+ for (int i = 0; i < nText; i++) {
+ // Retrieve the stream's text tags
+ tags = (TagList)Playbin.Emit ("get-text-tags", i);
+
+ if (tags != null) {
+ totalStr = string.Format ("subtitle stream {0}:\n", i);
+ text.InsertAtCursor (totalStr);
+
+ if (tags.GetString (Gst.Constants.TAG_LANGUAGE_CODE, out str)) {
+ totalStr = string.Format (" language: {0}\n", str);
+ text.InsertAtCursor (totalStr);
+ }
+ }
+ }
+ }
+
+ // This function is called when an "application" message is posted on the bus. Here we retrieve the message posted by the HandleTags callback
+ static void HandleApplication (object sender, GLib.SignalArgs args) {
+ var msg = (Message)args.Args [0];
+
+ if (msg.Structure.Name.Equals ("tags-changed")) {
+ // If the message is the "tags-changed" (only one we are currently issuing), update the stream info GUI
+ AnalyzeStreams ();
+ }
+ }
+
+ public static void Main (string[] args)
+ {
+ // Initialize GTK
+ Gtk.Application.Init ();
+
+ // Initialize Gstreamer
+ Gst.Application.Init(ref args);
+
+ // Create the elements
+ Playbin = ElementFactory.Make ("playbin", "playbin");
+
+ if (Playbin == null) {
+ Console.WriteLine ("Not all elements could be created");
+ return;
+ }
+
+ // Set the URI to play.
+ //Playbin ["uri"] = "http://download.blender.org/durian/trailer/sintel_trailer-1080p.mp4";
+ Playbin ["uri"] = "file:///home/stephan/Downloads/sintel_trailer-1080p.mp4";
+
+ // Connect to interesting signals in playbin
+ Playbin.Connect ("video-tags-changed", HandleTags);
+ Playbin.Connect ("audio-tags-changed", HandleTags);
+ Playbin.Connect ("text-tags-changed", HandleTags);
+
+
+ // Create the GUI
+ CreateUI ();
+
+ // Instruct the bus to emit signals for each received message, and connect to the interesting signals
+ var bus = Playbin.Bus;
+ bus.AddSignalWatch ();
+ bus.Connect ("message::error", HandleError);
+ bus.Connect ("message::eos", HandleEos);
+ bus.Connect ("message::state-changed", HandleStateChanged);
+ bus.Connect ("message::application", HandleApplication);
+
+
+ // Start playing
+ var ret = Playbin.SetState (State.Playing);
+ if (ret == StateChangeReturn.Failure) {
+ Console.WriteLine ("Unable to set the pipeline to the playing state.");
+ return;
+ }
+
+ // Register a function that GLib will call every second
+ GLib.Timeout.Add (1, RefreshUI);
+
+ // Start the GTK main loop- We will not regain control until gtk_main_quit is called
+ Gtk.Application.Run ();
+
+ // Free resources
+ Playbin.SetState (State.Null);
+
+ }
+
+ [DllImport ("libgdk-3.so.0") ]
+ static extern uint gdk_x11_window_get_xid (IntPtr handle);
+
+ [DllImport ("libgdk-win32-3.0-0.dll") ]
+ static extern IntPtr gdk_win32_drawable_get_handle (IntPtr handle);
+
+ [DllImport ("libX11.so.6")]
+ static extern int XInitThreads ();
+ }
+} \ No newline at end of file
diff --git a/samples/Makefile.am b/samples/Makefile.am
index b161e74..06df1e8 100644
--- a/samples/Makefile.am
+++ b/samples/Makefile.am
@@ -1,4 +1,4 @@
-TARGETS = playback.exe video-overlay.exe basic-tutorial-1.exe basic-tutorial-2.exe basic-tutorial-3.exe basic-tutorial-4.exe
+TARGETS = playback.exe video-overlay.exe basic-tutorial-1.exe basic-tutorial-2.exe basic-tutorial-3.exe basic-tutorial-4.exe basic-tutorial-5.exe
DEBUGS = $(addsuffix .mdb, $(TARGETS))
assemblies = \
@@ -27,10 +27,14 @@ basic-tutorial-3.exe: $(srcdir)/BasicTutorial3.cs $(assemblies)
basic-tutorial-4.exe: $(srcdir)/BasicTutorial4.cs $(assemblies)
$(CSC) $(CSFLAGS) -out:basic-tutorial-4.exe $(references) $(GLIB_SHARP_LIBS) $(srcdir)/BasicTutorial4.cs
+basic-tutorial-5.exe: $(srcdir)/BasicTutorial5.cs $(assemblies)
+ $(CSC) $(CSFLAGS) -out:basic-tutorial-5.exe $(references) $(GTK_SHARP_LIBS) $(srcdir)/BasicTutorial5.cs
+
EXTRA_DIST = \
Playback.cs \
VideoOverlay.cs \
BasicTutorial1.cs \
BasicTutorial2.cs \
BasicTutorial3.cs \
+ BasicTutorial4.cs \
BasicTutorial4.cs