Update
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
// Authors
|
||||
// Copyright (C) 2014 Stephan Sundermann <stephansundermann@gmail.com>
|
||||
|
||||
using System;
|
||||
using Gst;
|
||||
|
||||
namespace GstreamerSharp
|
||||
{
|
||||
class Playback
|
||||
{
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
// Initialize Gstreamer
|
||||
Application.Init(ref args);
|
||||
|
||||
// Build the pipeline
|
||||
var pipeline = Parse.Launch("playbin uri=http://download.blender.org/durian/trailer/sintel_trailer-1080p.mp4");
|
||||
|
||||
// Start playing
|
||||
pipeline.SetState(State.Playing);
|
||||
|
||||
// Wait until error or EOS
|
||||
var bus = pipeline.Bus;
|
||||
var msg = bus.TimedPopFiltered (Constants.CLOCK_TIME_NONE, MessageType.Eos | MessageType.Error);
|
||||
|
||||
// Free resources
|
||||
pipeline.SetState (State.Null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// Authors
|
||||
// Copyright (C) 2014 Stephan Sundermann <stephansundermann@gmail.com>
|
||||
|
||||
using System;
|
||||
using Gst;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace GstreamerSharp
|
||||
{
|
||||
class Playback
|
||||
{
|
||||
static bool IsLive;
|
||||
static Element Pipeline;
|
||||
static GLib.MainLoop MainLoop;
|
||||
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
// Initialize GStreamer
|
||||
Application.Init (ref args);
|
||||
|
||||
// Build the pipeline
|
||||
Pipeline = Parse.Launch ("playbin uri=http://download.blender.org/durian/trailer/sintel_trailer-1080p.mp4");
|
||||
var bus = Pipeline.Bus;
|
||||
|
||||
// Start playing
|
||||
var ret = Pipeline.SetState (State.Playing);
|
||||
if (ret == StateChangeReturn.Failure) {
|
||||
Console.WriteLine ("Unable to set the pipeline to the playing state.");
|
||||
return;
|
||||
} else if (ret == StateChangeReturn.NoPreroll) {
|
||||
IsLive = true;
|
||||
}
|
||||
|
||||
MainLoop = new GLib.MainLoop ();
|
||||
|
||||
bus.AddSignalWatch ();
|
||||
bus.Message += HandleMessage;
|
||||
|
||||
MainLoop.Run ();
|
||||
|
||||
// Free resources
|
||||
Pipeline.SetState (State.Null);
|
||||
}
|
||||
|
||||
static void HandleMessage (object o, MessageArgs args)
|
||||
{
|
||||
var msg = args.Message;
|
||||
switch (msg.Type) {
|
||||
case MessageType.Error: {
|
||||
GLib.GException err;
|
||||
string debug;
|
||||
|
||||
msg.ParseError (out err, out debug);
|
||||
Console.WriteLine ("Error: {0}", err.Message);
|
||||
|
||||
Pipeline.SetState (State.Ready);
|
||||
MainLoop.Quit ();
|
||||
break;
|
||||
}
|
||||
case MessageType.Eos:
|
||||
// end-of-stream
|
||||
Pipeline.SetState (State.Ready);
|
||||
MainLoop.Quit ();
|
||||
break;
|
||||
case MessageType.Buffering: {
|
||||
int percent = 0;
|
||||
|
||||
// If the stream is live, we do not care about buffering.
|
||||
if (IsLive) break;
|
||||
|
||||
percent = msg.ParseBuffering ();
|
||||
Console.WriteLine ("Buffering ({0})", percent);
|
||||
// Wait until buffering is complete before start/resume playing
|
||||
if (percent < 100)
|
||||
Pipeline.SetState (State.Paused);
|
||||
else
|
||||
Pipeline.SetState (State.Playing);
|
||||
break;
|
||||
}
|
||||
case MessageType.ClockLost:
|
||||
// Get a new clock
|
||||
Pipeline.SetState (State.Paused);
|
||||
Pipeline.SetState (State.Playing);
|
||||
break;
|
||||
default:
|
||||
// Unhandled message
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
// Authors
|
||||
// Copyright (C) 2014 Stephan Sundermann <stephansundermann@gmail.com>
|
||||
|
||||
using System;
|
||||
using Gst;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace GstreamerSharp
|
||||
{
|
||||
class Playback
|
||||
{
|
||||
static bool Playing; // Playing or Paused
|
||||
static double Rate; //Current playback rate (can be negative)
|
||||
static Element Pipeline, VideoSink;
|
||||
|
||||
// Send seek event to change rate
|
||||
static void SendSeekEvent () {
|
||||
var format = Format.Time;
|
||||
long position;
|
||||
|
||||
// Obtain the current position, needed for the seek event
|
||||
if (!Pipeline.QueryPosition (format, out position)) {
|
||||
Console.WriteLine ("Unable to retrieve current position.");
|
||||
return;
|
||||
}
|
||||
|
||||
Event seekEvent;
|
||||
// Create the seek event
|
||||
if (Rate > 0) {
|
||||
seekEvent = Event.NewSeek (Rate, Format.Time, SeekFlags.Flush | SeekFlags.Accurate, SeekType.Set, position, SeekType.None, 0);
|
||||
} else {
|
||||
seekEvent = Event.NewSeek (Rate, Format.Time, SeekFlags.Flush | SeekFlags.Accurate, SeekType.Set, 0, SeekType.Set, position);
|
||||
}
|
||||
|
||||
if (VideoSink == null) {
|
||||
// If we have not done so, obtain the sink through which we will send the seek events
|
||||
VideoSink = (Element)Pipeline ["video-sink"];
|
||||
}
|
||||
|
||||
// Send the event
|
||||
VideoSink.SendEvent (seekEvent);
|
||||
|
||||
Console.WriteLine ("Current rate: {0}", Rate);
|
||||
}
|
||||
|
||||
// Process keyboard input
|
||||
static void HandleKeyboard () {
|
||||
ConsoleKeyInfo x;
|
||||
bool terminate = false;
|
||||
while (!terminate) {
|
||||
x = Console.ReadKey ();
|
||||
switch (x.Key) {
|
||||
case ConsoleKey.P :
|
||||
Playing = !Playing;
|
||||
Pipeline.SetState (Playing ? State.Playing : State.Paused);
|
||||
Console.WriteLine ("Setting state to {0}", Playing ? "PLAYING" : "PAUSE");
|
||||
break;
|
||||
case ConsoleKey.S:
|
||||
if (x.Modifiers == ConsoleModifiers.Shift)
|
||||
Rate *= 2.0;
|
||||
else
|
||||
Rate /= 2.0;
|
||||
SendSeekEvent ();
|
||||
break;
|
||||
case ConsoleKey.D:
|
||||
Rate *= -1.0;
|
||||
SendSeekEvent ();
|
||||
break;
|
||||
case ConsoleKey.N:
|
||||
if (VideoSink == null) {
|
||||
// If we have not done so, obtain the sink through which we will send the step events
|
||||
VideoSink = (Element)Pipeline ["video-sink"];
|
||||
}
|
||||
var evnt = Event.NewStep (Format.Buffers, 1, Rate, true, false);
|
||||
VideoSink.SendEvent (evnt);
|
||||
|
||||
Console.WriteLine ("Stepping one frame");
|
||||
break;
|
||||
case ConsoleKey.Q:
|
||||
terminate = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
// Initialize GStreamer
|
||||
Application.Init (ref args);
|
||||
|
||||
// Print usage map
|
||||
Console.WriteLine ("USAGE: Choose one of the following options, then press enter:");
|
||||
Console.WriteLine (" 'P' to toggle between PAUSE and PLAY");
|
||||
Console.WriteLine (" 'S' to increase playback speed, 's' to decrease playback speed");
|
||||
Console.WriteLine (" 'D' to toggle playback direction");
|
||||
Console.WriteLine (" 'N' to move to next frame (in the current direction, better in PAUSE)");
|
||||
Console.WriteLine (" 'Q' to quit");
|
||||
|
||||
// Build the pipeline
|
||||
//Pipeline = Parse.Launch ("playbin uri=http://download.blender.org/durian/trailer/sintel_trailer-1080p.mp4");
|
||||
Pipeline = Parse.Launch ("playbin uri=file:///home/stephan/Downloads/sintel_trailer-1080p.mp4");
|
||||
|
||||
// Start playing
|
||||
var ret = Pipeline.SetState (State.Playing);
|
||||
if (ret == StateChangeReturn.Failure) {
|
||||
Console.WriteLine ("Unable to set the pipeline to the playing state.");
|
||||
return;
|
||||
}
|
||||
Playing = true;
|
||||
Rate = 1.0;
|
||||
|
||||
// Process input
|
||||
HandleKeyboard ();
|
||||
|
||||
// Free resources
|
||||
Pipeline.SetState (State.Null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
// Authors
|
||||
// Copyright (C) 2014 Stephan Sundermann <stephansundermann@gmail.com>
|
||||
|
||||
using System;
|
||||
using Gst;
|
||||
|
||||
namespace GstreamerSharp
|
||||
{
|
||||
class Playback
|
||||
{
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
// Initialize Gstreamer
|
||||
Application.Init(ref args);
|
||||
|
||||
// Build the pipeline
|
||||
var source = ElementFactory.Make ("videotestsrc", "source");
|
||||
var sink = ElementFactory.Make ("autovideosink", "sink");
|
||||
|
||||
// Create the empty pipeline
|
||||
var pipeline = new Pipeline ("test-pipeline");
|
||||
|
||||
if (pipeline == null || source == null || sink == null) {
|
||||
Console.WriteLine ("Not all elements could be created");
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the pipeline
|
||||
pipeline.Add (source, sink);
|
||||
if (!source.Link (sink)) {
|
||||
Console.WriteLine ("Elements could not be linked");
|
||||
return;
|
||||
}
|
||||
|
||||
// Modify the source's properties
|
||||
source ["pattern"] = 0;
|
||||
|
||||
// Start playing
|
||||
var ret = pipeline.SetState(State.Playing);
|
||||
if (ret == StateChangeReturn.Failure) {
|
||||
Console.WriteLine ("Unable to set the pipeline to the playing state");
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait until error or EOS
|
||||
var bus = pipeline.Bus;
|
||||
var msg = bus.TimedPopFiltered (Constants.CLOCK_TIME_NONE, MessageType.Eos | MessageType.Error);
|
||||
|
||||
// Free resources
|
||||
if (msg != null) {
|
||||
switch (msg.Type) {
|
||||
case MessageType.Error:
|
||||
GLib.GException exc;
|
||||
string debug;
|
||||
msg.ParseError (out exc, out debug);
|
||||
Console.WriteLine (String.Format ("Error received from element {0}: {1}", msg.Src.Name, exc.Message));
|
||||
Console.WriteLine (String.Format ("Debugging information {0}", debug));
|
||||
break;
|
||||
case MessageType.Eos:
|
||||
Console.WriteLine ("End-Of-Stream reached");
|
||||
break;
|
||||
default:
|
||||
// We should not reach here because we only asked for ERRORs and EOS
|
||||
Console.WriteLine ("Unexpected messag received");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pipeline.SetState (State.Null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
// Authors
|
||||
// Copyright (C) 2014 Stephan Sundermann <stephansundermann@gmail.com>
|
||||
|
||||
using System;
|
||||
using Gst;
|
||||
|
||||
namespace GstreamerSharp
|
||||
{
|
||||
class Playback
|
||||
{
|
||||
static Pipeline pipeline;
|
||||
static Element source;
|
||||
static Element convert;
|
||||
static Element sink;
|
||||
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
// Initialize Gstreamer
|
||||
Application.Init(ref args);
|
||||
|
||||
// Create the elements
|
||||
source = ElementFactory.Make ("uridecodebin", "source");
|
||||
convert = ElementFactory.Make ("audioconvert", "convert");
|
||||
sink = ElementFactory.Make ("autoaudiosink", "sink");
|
||||
|
||||
// Create the empty pipeline
|
||||
pipeline = new Pipeline ("test-pipeline");
|
||||
|
||||
if (source == null || convert == null || sink == null || pipeline == null) {
|
||||
Console.WriteLine ("Not all elements could be created");
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the pipeline. Note that we are NOT linking the source at this point.
|
||||
// We will do it later.
|
||||
pipeline.Add (source, convert, sink);
|
||||
if (!convert.Link (sink)) {
|
||||
Console.WriteLine ("Elements could not be linked");
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the URI to play
|
||||
source ["uri"] = "http://download.blender.org/durian/trailer/sintel_trailer-1080p.mp4";
|
||||
|
||||
// Connect to the pad-added signal
|
||||
source.PadAdded += HandlePadAdded;
|
||||
|
||||
// Start playing
|
||||
var ret = pipeline.SetState (State.Playing);
|
||||
if (ret == StateChangeReturn.Failure) {
|
||||
Console.WriteLine ("Unable to set the pipeline to the playing state.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Listen to the bus
|
||||
var bus = pipeline.Bus;
|
||||
bool terminated = false;
|
||||
do {
|
||||
var msg = bus.TimedPopFiltered (Constants.CLOCK_TIME_NONE, MessageType.StateChanged | MessageType.Error | MessageType.Eos);
|
||||
|
||||
if (msg != null) {
|
||||
switch (msg.Type) {
|
||||
case MessageType.Error:
|
||||
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);
|
||||
terminated = true;
|
||||
break;
|
||||
case MessageType.Eos:
|
||||
Console.WriteLine("End-Of-Stream reached.");
|
||||
terminated = true;
|
||||
break;
|
||||
case MessageType.StateChanged:
|
||||
// We are only interested in state-changed messages from the pipeline
|
||||
if (msg.Src == pipeline) {
|
||||
State oldState, newState, pendingState;
|
||||
msg.ParseStateChanged(out oldState, out newState, out pendingState);
|
||||
Console.WriteLine ("Pipeline state changed from {0} to {1}:", Element.StateGetName(oldState), Element.StateGetName(newState));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// We should not reach here
|
||||
Console.WriteLine ("Unexpected message received.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (!terminated);
|
||||
|
||||
pipeline.SetState (State.Null);
|
||||
}
|
||||
|
||||
static void HandlePadAdded (object o, PadAddedArgs args)
|
||||
{
|
||||
var src = (Element)o;
|
||||
var newPad = args.NewPad;
|
||||
var sinkPad = convert.GetStaticPad ("sink");
|
||||
|
||||
Console.WriteLine (string.Format ("Received new pad '{0}' from '{1}':", newPad.Name, src.Name));
|
||||
|
||||
// If our converter is already linked, we have nothing to do here
|
||||
if (sinkPad.IsLinked) {
|
||||
Console.WriteLine ("We are already linked. Ignoring.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the new pad's type
|
||||
var newPadCaps = newPad.Caps;
|
||||
var newPadStruct = newPadCaps.GetStructure (0);
|
||||
var newPadType = newPadStruct.Name;
|
||||
if (!newPadType.StartsWith ("audio/x-raw")) {
|
||||
Console.WriteLine ("It has type '{0}' which is not raw audio. Ignoring.", newPadType);
|
||||
return;
|
||||
}
|
||||
|
||||
// Attempt the link
|
||||
var ret = newPad.Link (sinkPad);
|
||||
if (ret != PadLinkReturn.Ok)
|
||||
Console.WriteLine ("Type is '{0} but link failed.", newPadType);
|
||||
else
|
||||
Console.WriteLine ("Link succeeded (type '{0}').", newPadType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// Authors
|
||||
// Copyright (C) 2014 Stephan Sundermann <stephansundermann@gmail.com>
|
||||
|
||||
using System;
|
||||
using Gst;
|
||||
|
||||
namespace GstreamerSharp
|
||||
{
|
||||
class Playback
|
||||
{
|
||||
static Element playbin;
|
||||
static bool playing;
|
||||
static bool terminate;
|
||||
static bool seekEnabled;
|
||||
static bool seekDone;
|
||||
static long duration;
|
||||
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
// Initialize Gstreamer
|
||||
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";
|
||||
|
||||
// Start playing
|
||||
var ret = playbin.SetState (State.Playing);
|
||||
if (ret == StateChangeReturn.Failure) {
|
||||
Console.WriteLine ("Unable to set the pipeline to the playing state.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Listen to the bus
|
||||
var bus = playbin.Bus;
|
||||
do {
|
||||
var msg = bus.TimedPopFiltered (100 * Constants.MSECOND, MessageType.StateChanged | MessageType.Error | MessageType.DurationChanged);
|
||||
|
||||
// Parse message
|
||||
if (msg != null) {
|
||||
HandleMessage (msg);
|
||||
}
|
||||
else {
|
||||
// We got no message, this means the timeout expired
|
||||
if (playing) {
|
||||
var fmt = Format.Time;
|
||||
var current = -1L;
|
||||
|
||||
// Query the current position of the stream
|
||||
if (!playbin.QueryPosition (fmt, out current)) {
|
||||
Console.WriteLine ("Could not query current position.");
|
||||
}
|
||||
|
||||
// if we didn't know it yet, query the stream position
|
||||
if (duration <= 0) {
|
||||
if (!playbin.QueryDuration (fmt, out duration)) {
|
||||
Console.WriteLine ("Could not query current duration.");
|
||||
}
|
||||
}
|
||||
|
||||
// Print current position and total duration
|
||||
Console.Write("Position {0} / {1}\r", new TimeSpan (current), new TimeSpan (duration));
|
||||
|
||||
if (seekEnabled && !seekDone && current > 10L * Constants.SECOND) {
|
||||
Console.WriteLine ("\nRead 10s, performing seek...");
|
||||
playbin.SeekSimple (fmt, SeekFlags.KeyUnit | SeekFlags.Flush, 30L * Constants.SECOND);
|
||||
seekDone = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (!terminate);
|
||||
}
|
||||
|
||||
private static void HandleMessage (Message msg) {
|
||||
switch (msg.Type) {
|
||||
case MessageType.Error:
|
||||
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);
|
||||
terminate = true;
|
||||
break;
|
||||
case MessageType.Eos:
|
||||
Console.WriteLine("End-Of-Stream reached.");
|
||||
terminate = true;
|
||||
break;
|
||||
case MessageType.DurationChanged:
|
||||
// The duration has changed, mark the current one as invalid
|
||||
duration = -1;
|
||||
break;
|
||||
case MessageType.StateChanged:
|
||||
// We are only interested in state-changed messages from the pipeline
|
||||
if (msg.Src == playbin) {
|
||||
State oldState, newState, pendingState;
|
||||
msg.ParseStateChanged(out oldState, out newState, out pendingState);
|
||||
Console.WriteLine ("Pipeline state changed from {0} to {1}:", Element.StateGetName(oldState), Element.StateGetName(newState));
|
||||
|
||||
// Remember wheather we are in the PLAYING state
|
||||
playing = newState == State.Playing;
|
||||
|
||||
if (playing) {
|
||||
// We have just moved to PLAYING. Check if seeking is possible
|
||||
var query = Query.NewSeeking (Format.Time);
|
||||
long start, end;
|
||||
Format fmt = Format.Time;
|
||||
|
||||
if (playbin.Query (query)) {
|
||||
query.ParseSeeking (out fmt, out seekEnabled, out start, out end);
|
||||
|
||||
if (seekEnabled) {
|
||||
Console.WriteLine ("Seeking is ENABLED from {0} to {1}", new TimeSpan(start), new TimeSpan(end));
|
||||
} else {
|
||||
Console.WriteLine ("Seeking DISABLED for this stream.");
|
||||
}
|
||||
} else {
|
||||
Console.WriteLine ("Seeking query failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// We should not reach here
|
||||
Console.WriteLine ("Unexpected message received.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,367 @@
|
||||
// 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 Gtk.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 = (Gtk.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;
|
||||
IntPtr windowID = IntPtr.Zero;
|
||||
|
||||
// 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 = 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 (Message.NewApplication (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";
|
||||
|
||||
// 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 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 ();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
// Authors
|
||||
// Copyright (C) 2014 Stephan Sundermann <stephansundermann@gmail.com>
|
||||
|
||||
using System;
|
||||
using Gst;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace GstreamerSharp
|
||||
{
|
||||
class Playback
|
||||
{
|
||||
// Functions below print the capabilities in a human-friendly format
|
||||
static void PrintCaps (Caps caps, string pfx) {
|
||||
|
||||
if (caps == null)
|
||||
return;
|
||||
|
||||
if (caps.IsAny) {
|
||||
Console.WriteLine ("{0}ANY", pfx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (caps.IsEmpty) {
|
||||
Console.WriteLine ("{0}EMPTY", pfx);
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < caps.Size; i++) {
|
||||
var structure = caps.GetStructure (i);
|
||||
|
||||
Console.WriteLine ("{0}{1}", pfx, structure.Name);
|
||||
structure.Foreach ((field_id, value) => {
|
||||
var ptr = g_quark_to_string (field_id);
|
||||
var quark = GLib.Marshaller.Utf8PtrToString (ptr);
|
||||
Console.WriteLine ("{0} {1}: {2}", pfx, quark, value.Val);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Prints information about a Pad Template, including its Capabilities*/
|
||||
static void PrintPadTemplateInformation (ElementFactory factory) {
|
||||
|
||||
Console.WriteLine ("Pad Templates for {0}:", factory.Name);
|
||||
if (factory.NumPadTemplates == 0) {
|
||||
Console.WriteLine (" none");
|
||||
return;
|
||||
}
|
||||
|
||||
var pads = factory.StaticPadTemplates;
|
||||
foreach (var p in pads) {
|
||||
var pad = (StaticPadTemplate) p;
|
||||
|
||||
if (pad.Direction == PadDirection.Src)
|
||||
Console.WriteLine (" SRC template: '{0}'", pad.NameTemplate);
|
||||
else if (pad.Direction == PadDirection.Sink)
|
||||
Console.WriteLine (" SINK template: '{0}'", pad.NameTemplate);
|
||||
else
|
||||
Console.WriteLine (" UNKNOWN!!! template: '{0}'", pad.NameTemplate);
|
||||
|
||||
if (pad.Presence == PadPresence.Always)
|
||||
Console.WriteLine (" Availability: Always");
|
||||
else if (pad.Presence == PadPresence.Sometimes)
|
||||
Console.WriteLine (" Availability: Sometimes");
|
||||
else if (pad.Presence == PadPresence.Request) {
|
||||
Console.WriteLine (" Availability: On request");
|
||||
} else
|
||||
Console.WriteLine (" Availability: UNKNOWN!!!");
|
||||
|
||||
if (pad.StaticCaps.String != null) {
|
||||
Console.WriteLine (" Capabilities:");
|
||||
PrintCaps (pad.StaticCaps.Get (), " ");
|
||||
}
|
||||
|
||||
Console.WriteLine ();
|
||||
}
|
||||
}
|
||||
|
||||
// Shows the CURRENT capabilities of the requested pad in the given element */
|
||||
static void PrintPadCapabilities (Element element, string padName) {
|
||||
|
||||
// Retrieve pad
|
||||
var pad = element.GetStaticPad (padName);
|
||||
if (pad == null) {
|
||||
Console.WriteLine ("Could not retrieve pad '{0}'", padName);
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve negotiated caps (or acceptable caps if negotiation is not finished yet)
|
||||
var caps = pad.CurrentCaps;
|
||||
if (caps == null)
|
||||
caps = pad.Caps;
|
||||
|
||||
/* Print and free */
|
||||
Console.WriteLine ("Caps for the {0} pad:", padName);
|
||||
PrintCaps (caps, " ");
|
||||
}
|
||||
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
// Initialize Gstreamer
|
||||
Gst.Application.Init(ref args);
|
||||
|
||||
// Create the element factories
|
||||
var sourceFactory = ElementFactory.Find ("audiotestsrc");
|
||||
var sinkFactory = ElementFactory.Find ("autoaudiosink");
|
||||
|
||||
|
||||
|
||||
if (sourceFactory == null || sinkFactory == null) {
|
||||
Console.WriteLine ("Not all element factories could be created.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Print information about the pad templates of these factories
|
||||
PrintPadTemplateInformation (sourceFactory);
|
||||
PrintPadTemplateInformation (sinkFactory);
|
||||
|
||||
// Ask the factories to instantiate actual elements
|
||||
var source = sourceFactory.Create ("source");
|
||||
var sink = sinkFactory.Create ("sink");
|
||||
|
||||
// Create the empty pipeline
|
||||
var pipeline = new Pipeline ("test-pipeline");
|
||||
|
||||
if (pipeline == null || source == null || sink == null) {
|
||||
Console.WriteLine ("Not all elements could be created.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the pipeline
|
||||
pipeline.Add (source, sink);
|
||||
if (!source.Link (sink)) {
|
||||
Console.WriteLine ("Elements could not be linked.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Print initial negotiated caps (in NULL state)
|
||||
Console.WriteLine ("In NULL state:");
|
||||
PrintPadCapabilities (sink, "sink");
|
||||
|
||||
// Start playing
|
||||
var ret = pipeline.SetState (State.Playing);
|
||||
if (ret == StateChangeReturn.Failure) {
|
||||
Console.WriteLine ("Unable to set the pipeline to the playing state (check the bus for error messages).");
|
||||
}
|
||||
|
||||
// Wait until error, EOS or State Change
|
||||
var bus = pipeline.Bus;
|
||||
var terminate = false;
|
||||
|
||||
do {
|
||||
var msg = bus.TimedPopFiltered (Constants.CLOCK_TIME_NONE, MessageType.Error | MessageType.Eos | MessageType.StateChanged);
|
||||
|
||||
// Parse message
|
||||
if (msg != null) {
|
||||
switch (msg.Type) {
|
||||
case MessageType.Error:
|
||||
string debug;
|
||||
GLib.GException exc;
|
||||
msg.ParseError (out exc, out debug);
|
||||
Console.WriteLine ("Error received from element {0}: {1}", msg.Src.Name, exc.Message);
|
||||
Console.WriteLine ("Debugging information: {0}", debug != null ? debug : "none");
|
||||
terminate = true;
|
||||
break;
|
||||
case MessageType.Eos:
|
||||
Console.WriteLine ("End-Of-Stream reached.\n");
|
||||
terminate = true;
|
||||
break;
|
||||
case MessageType.StateChanged:
|
||||
// We are only interested in state-changed messages from the pipeline
|
||||
if (msg.Src == pipeline) {
|
||||
State oldState, newState, pendingState;
|
||||
msg.ParseStateChanged (out oldState, out newState, out pendingState);
|
||||
Console.WriteLine ("Pipeline state changed from {0} to {1}:",
|
||||
Element.StateGetName (oldState), Element.StateGetName (newState));
|
||||
// Print the current capabilities of the sink element
|
||||
PrintPadCapabilities (sink, "sink");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// We should not reach here because we only asked for ERRORs, EOS and STATE_CHANGED
|
||||
Console.WriteLine ("Unexpected message received.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (!terminate);
|
||||
|
||||
// Free resources
|
||||
pipeline.SetState (State.Null);
|
||||
}
|
||||
|
||||
[DllImport ("glib-2.0.dll")]
|
||||
static extern IntPtr g_quark_to_string (uint quark);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
// Authors
|
||||
// Copyright (C) 2014 Stephan Sundermann <stephansundermann@gmail.com>
|
||||
|
||||
using System;
|
||||
using Gst;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace GstreamerSharp
|
||||
{
|
||||
class Playback
|
||||
{
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
// Initialize Gstreamer
|
||||
Gst.Application.Init(ref args);
|
||||
|
||||
// Create the elements
|
||||
var audioSource = ElementFactory.Make ("audiotestsrc", "audio_source");
|
||||
var tee = ElementFactory.Make ("tee", "tee");
|
||||
var audioQueue = ElementFactory.Make ("queue", "audio_queue");
|
||||
var audioConvert = ElementFactory.Make ("audioconvert", "audio_convert");
|
||||
var audioResample = ElementFactory.Make ("audioresample", "audio_resample");
|
||||
var audioSink = ElementFactory.Make ("autoaudiosink", "audio_sink");
|
||||
var videoQueue = ElementFactory.Make ("queue", "video_queue");
|
||||
var visual = ElementFactory.Make ("wavescope", "visual");
|
||||
var videoConvert = ElementFactory.Make ("videoconvert", "csp");
|
||||
var videoSink = ElementFactory.Make ("autovideosink", "video_sink");
|
||||
|
||||
// Create the empty pipeline
|
||||
var pipeline = new Pipeline ("test-pipeline");
|
||||
|
||||
if (audioSource == null || tee == null || audioQueue == null || audioConvert == null || audioResample == null ||
|
||||
audioSink == null || videoQueue == null || visual == null || videoConvert == null || videoSink == null || pipeline == null) {
|
||||
Console.WriteLine ("Not all elements could be created.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Link all elements that can be automatically linked because they have "Always" pads
|
||||
pipeline.Add (audioSource, tee, audioQueue, audioConvert, audioResample, audioSink,
|
||||
videoQueue, visual, videoConvert, videoSink);
|
||||
if (!audioSource.Link (tee) ||
|
||||
!Element.Link (audioQueue, audioConvert, audioResample, audioSink) ||
|
||||
!Element.Link (videoQueue, visual, videoConvert, videoSink)) {
|
||||
Console.WriteLine ("Elements could not be linked.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Manually link the Tee, which has "Request" pads
|
||||
var teeSrcPadTemplate = tee.GetPadTemplate ("src_%u");
|
||||
var teeAudioPad = tee.RequestPad (teeSrcPadTemplate, null, null);
|
||||
Console.WriteLine ("Obtained request pad {0} for audio branch.", teeAudioPad.Name);
|
||||
var queueAudioPad = audioQueue.GetStaticPad ("sink");
|
||||
var teeVideoPad = tee.RequestPad (teeSrcPadTemplate, null, null);
|
||||
Console.WriteLine ("Obtained request pad {0} for video branch.", teeVideoPad.Name);
|
||||
var queueVideoPad = videoQueue.GetStaticPad ("sink");
|
||||
if (teeAudioPad.Link (queueAudioPad) != PadLinkReturn.Ok ||
|
||||
teeVideoPad.Link(queueVideoPad) != PadLinkReturn.Ok) {
|
||||
Console.WriteLine ("Tee could not be linked.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Start playing
|
||||
var ret = pipeline.SetState (State.Playing);
|
||||
if (ret == StateChangeReturn.Failure) {
|
||||
Console.WriteLine ("Unable to set the pipeline to the playing state (check the bus for error messages).");
|
||||
}
|
||||
|
||||
// Wait until error or EOS
|
||||
pipeline.Bus.TimedPopFiltered (Constants.CLOCK_TIME_NONE, MessageType.Error | MessageType.Eos);
|
||||
|
||||
// Release the request pads from the Tee, and unref them
|
||||
tee.ReleaseRequestPad (teeAudioPad);
|
||||
tee.ReleaseRequestPad (teeVideoPad);
|
||||
|
||||
// Free resources
|
||||
pipeline.SetState (State.Null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
// Authors
|
||||
// Copyright (C) 2014 Stephan Sundermann <stephansundermann@gmail.com>
|
||||
|
||||
using System;
|
||||
using Gst;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace GstreamerSharp
|
||||
{
|
||||
class Playback
|
||||
{
|
||||
const int ChunkSize = 1024;
|
||||
const int SampleRate = 44100;
|
||||
|
||||
static Gst.App.AppSink AppSink;
|
||||
static Gst.App.AppSrc AppSource;
|
||||
static Element Pipeline, Tee, AudioQueue, AudioConvert1, AudioResample, AudioSink;
|
||||
static Element VideoQueue, AudioConvert2, Visual, VideoConvert, VideoSink;
|
||||
static Element AppQueue;
|
||||
|
||||
static long NumSamples; // Number of samples generated so far (for timestamp generation)
|
||||
static float a, b, c, d; // For waveform generation
|
||||
|
||||
static uint Sourceid; // To control the GSource
|
||||
|
||||
static GLib.MainLoop MainLoop; // GLib's Main Loop
|
||||
|
||||
// This method is called by the idle GSource in the mainloop, to feed CHUNK_SIZE bytes into appsrc.
|
||||
// The idle handler is added to the mainloop when appsrc requests us to start sending data (need-data signal)
|
||||
// and is removed when appsrc has enough data (enough-data signal).
|
||||
|
||||
static bool PushData () {
|
||||
var numSamples = ChunkSize / 2; // Because each sample is 16 bits
|
||||
MapInfo map;
|
||||
|
||||
// Create a new empty buffer
|
||||
var buffer = new Gst.Buffer (null, ChunkSize, AllocationParams.Zero);
|
||||
|
||||
// Set its timestamp and duration
|
||||
buffer.Pts = Util.Uint64Scale ((ulong)NumSamples, (ulong)Constants.SECOND, (ulong)SampleRate);
|
||||
buffer.Dts = Util.Uint64Scale ((ulong)NumSamples, (ulong)Constants.SECOND, (ulong)SampleRate);
|
||||
buffer.Duration = Util.Uint64Scale ((ulong)NumSamples, (ulong)Constants.SECOND, (ulong)SampleRate);
|
||||
|
||||
// Generate some psychodelic waveforms
|
||||
buffer.Map (out map, MapFlags.Write);
|
||||
c += d;
|
||||
d -= c / 1000f;
|
||||
var freq = 1100f + 1000f * d;
|
||||
short[] data = new short[numSamples];
|
||||
for (int i = 0; i < numSamples; i++) {
|
||||
a += b;
|
||||
b -= a / freq;
|
||||
data[i] = (short)(500f * a);
|
||||
}
|
||||
// convert the short[] to a byte[] by marshalling
|
||||
var native = Marshal.AllocHGlobal (data.Length * sizeof(short));
|
||||
Marshal.Copy (data, 0, native, data.Length);
|
||||
byte[] bytedata = new byte[2 * data.Length];
|
||||
Marshal.Copy (native, bytedata, 0, data.Length * sizeof(short));
|
||||
|
||||
map.Data = bytedata;
|
||||
buffer.Unmap (map);
|
||||
NumSamples += numSamples;
|
||||
|
||||
// Push the buffer into the appsrc
|
||||
var ret = AppSource.PushBuffer (buffer);
|
||||
|
||||
// Free the buffer now that we are done with it
|
||||
buffer.Dispose ();
|
||||
|
||||
if (ret != FlowReturn.Ok) {
|
||||
// We got some error, stop sending data
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// This signal callback triggers when appsrc needs Here, we add an idle handler
|
||||
// to the mainloop to start pushing data into the appsrc
|
||||
static void StartFeed (object sender, Gst.App.NeedDataArgs args) {
|
||||
if (Sourceid == 0) {
|
||||
Console.WriteLine ("Start feeding");
|
||||
Sourceid = GLib.Idle.Add (PushData);
|
||||
}
|
||||
}
|
||||
|
||||
// This callback triggers when appsrc has enough data and we can stop sending.
|
||||
// We remove the idle handler from the mainloop
|
||||
static void StopFeed (object sender, EventArgs args) {
|
||||
if (Sourceid != 0) {
|
||||
Console.WriteLine ("Stop feeding");
|
||||
GLib.Source.Remove (Sourceid);
|
||||
Sourceid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// The appsink has received a buffer
|
||||
static void NewSample (object sender, GLib.SignalArgs args) {
|
||||
var sink = (Gst.App.AppSink)sender;
|
||||
|
||||
// Retrieve the buffer
|
||||
var sample = sink.PullSample ();
|
||||
if (sample != null) {
|
||||
// The only thing we do in this example is print a * to indicate a received buffer
|
||||
Console.Write ("*");
|
||||
sample.Dispose ();
|
||||
}
|
||||
}
|
||||
|
||||
// This function is called when an error message is posted on the bus
|
||||
static void HandleError (object sender, GLib.SignalArgs args) {
|
||||
GLib.GException err;
|
||||
string debug;
|
||||
var msg = (Message) args.Args[0];
|
||||
|
||||
// Print error details on the screen
|
||||
msg.ParseError (out err, out debug);
|
||||
Console.WriteLine ("Error received from element {0}: {1}", msg.Src.Name, err.Message);
|
||||
Console.WriteLine ("Debugging information: {0}", debug != null ? debug : "none");
|
||||
|
||||
MainLoop.Quit ();
|
||||
}
|
||||
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
b = 1;
|
||||
d = 1;
|
||||
Gst.Audio.AudioInfo info = new Gst.Audio.AudioInfo();
|
||||
|
||||
// Initialize Gstreamer
|
||||
Gst.Application.Init(ref args);
|
||||
|
||||
// Create the elements
|
||||
AppSource = new Gst.App.AppSrc ("app_src");
|
||||
Tee = ElementFactory.Make ("tee", "tee");
|
||||
AudioQueue = ElementFactory.Make ("queue", "audio_queue");
|
||||
AudioConvert1 = ElementFactory.Make ("audioconvert", "audio_convert1");
|
||||
AudioResample = ElementFactory.Make ("audioresample", "audio_resample");
|
||||
AudioSink = ElementFactory.Make ("autoaudiosink", "audio_sink");
|
||||
VideoQueue = ElementFactory.Make ("queue", "video_queue");
|
||||
AudioConvert2 = ElementFactory.Make ("audioconvert", "audio_convert2");
|
||||
Visual = ElementFactory.Make ("wavescope", "visual");
|
||||
VideoConvert = ElementFactory.Make ("videoconvert", "video_convert");
|
||||
VideoSink = ElementFactory.Make ("autovideosink", "video_sink");
|
||||
AppQueue = ElementFactory.Make ("queue", "app_queue");
|
||||
AppSink = new Gst.App.AppSink ("app_sink");
|
||||
|
||||
// Create the empty pipeline
|
||||
var pipeline = new Pipeline ("test-pipeline");
|
||||
|
||||
if (AppSource == null || Tee == null || AudioQueue == null || AudioConvert1 == null || AudioResample == null ||
|
||||
AudioSink == null || VideoQueue == null || AudioConvert2 == null || Visual == null || VideoConvert == null ||
|
||||
AppQueue == null || AppSink == null ||pipeline == null) {
|
||||
Console.WriteLine ("Not all elements could be created.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Configure wavescope
|
||||
Visual ["shader"] = 0;
|
||||
Visual ["style"] = 0;
|
||||
|
||||
// Configure appsrc
|
||||
Gst.Audio.AudioChannelPosition[] position = {};
|
||||
info.SetFormat (Gst.Audio.AudioFormat.S16, SampleRate, 1, position);
|
||||
var audioCaps = info.ToCaps ();
|
||||
AppSource ["caps"] = audioCaps;
|
||||
AppSource ["format"] = Format.Time;
|
||||
|
||||
AppSource.NeedData += StartFeed;
|
||||
AppSource.EnoughData += StopFeed;
|
||||
|
||||
// Configure appsink
|
||||
AppSink ["emit-signals"] = true;
|
||||
AppSink ["caps"] = audioCaps;
|
||||
AppSink.NewSample += NewSample;
|
||||
|
||||
// Link all elements that can be automatically linked because they have "Always" pads
|
||||
pipeline.Add (AppSource, Tee, AudioQueue, AudioConvert1, AudioResample,
|
||||
AudioSink, VideoQueue, AudioConvert2, Visual, VideoConvert, VideoSink, AppQueue, AppSink);
|
||||
if (!Element.Link (AppSource, Tee) ||
|
||||
!Element.Link (AudioQueue, AudioConvert1, AudioResample, AudioSink) ||
|
||||
!Element.Link (VideoQueue, AudioConvert2, Visual, VideoConvert, VideoSink) ||
|
||||
!Element.Link (AppQueue, AppSink)) {
|
||||
Console.WriteLine ("Elements could not be linked.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Manually link the Tee, which has "Request" pads
|
||||
var teeSrcPadTemplate = Tee.GetPadTemplate ("src_%u");
|
||||
var teeAudioPad = Tee.RequestPad (teeSrcPadTemplate);
|
||||
Console.WriteLine ("Obtained request pad {0} for audio branch.", teeAudioPad.Name);
|
||||
var queueAudioPad = AudioQueue.GetStaticPad ("sink");
|
||||
var teeVideoPad = Tee.RequestPad (teeSrcPadTemplate);
|
||||
Console.WriteLine ("Obtained request pad {0} for video branch.", teeVideoPad.Name);
|
||||
var queueVideoPad = VideoQueue.GetStaticPad ("sink");
|
||||
var teeAppPad = Tee.RequestPad (teeSrcPadTemplate);
|
||||
Console.WriteLine ("Obtained request pad {0} for app branch.", teeAppPad.Name);
|
||||
var queueAppPad = AppQueue.GetStaticPad ("sink");
|
||||
if (teeAudioPad.Link (queueAudioPad) != PadLinkReturn.Ok ||
|
||||
teeVideoPad.Link (queueVideoPad) != PadLinkReturn.Ok ||
|
||||
teeAppPad.Link (queueAppPad) != PadLinkReturn.Ok) {
|
||||
Console.WriteLine ("Tee could not be linked");
|
||||
return;
|
||||
}
|
||||
|
||||
// Instruct the bus to emit signals for each received message, and connect to the interesting signals
|
||||
var bus = pipeline.Bus;
|
||||
bus.AddSignalWatch ();
|
||||
bus.Connect ("message::error", HandleError);
|
||||
|
||||
// Start playing the pipeline
|
||||
pipeline.SetState (State.Playing);
|
||||
|
||||
// Create a GLib Main Loop and set it to run
|
||||
MainLoop = new GLib.MainLoop ();
|
||||
MainLoop.Run ();
|
||||
|
||||
// Release the request pads from the Tee, and unref them
|
||||
Tee.ReleaseRequestPad(teeAudioPad);
|
||||
Tee.ReleaseRequestPad(teeVideoPad);
|
||||
Tee.ReleaseRequestPad(teeAppPad);
|
||||
|
||||
// Free resources
|
||||
pipeline.SetState (State.Null);
|
||||
|
||||
Gst.Global.Deinit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
// Authors
|
||||
// Copyright (C) 2014 Stephan Sundermann <stephansundermann@gmail.com>
|
||||
|
||||
using System;
|
||||
using Gst;
|
||||
using Gst.PbUtils;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace GstreamerSharp
|
||||
{
|
||||
class Playback
|
||||
{
|
||||
static Discoverer Discoverer;
|
||||
static GLib.MainLoop MainLoop;
|
||||
|
||||
// Print a tag in a human-readable format (name: value)
|
||||
static void PrintTagForeach (TagList tags, string tag, int depth) {
|
||||
var val = GLib.Value.Empty;
|
||||
|
||||
TagList.CopyValue (ref val, tags, tag);
|
||||
|
||||
string str;
|
||||
if (val.Val is string)
|
||||
str = (string)val.Val;
|
||||
else
|
||||
str = Value.Serialize (val);
|
||||
|
||||
Console.WriteLine ("{0}{1}: {2}", new string(' ', 2 * depth), Tag.GetNick (tag), str);
|
||||
}
|
||||
|
||||
// Print information regarding a stream
|
||||
static void PrintStreamInfo (DiscovererStreamInfo info, int depth) {
|
||||
|
||||
var caps = info.Caps;
|
||||
|
||||
string desc = null;
|
||||
if (caps != null) {
|
||||
if (caps.IsFixed)
|
||||
desc = Gst.PbUtils.Global.PbUtilsGetCodecDescription (caps);
|
||||
else
|
||||
desc = caps.ToString ();
|
||||
}
|
||||
|
||||
Console.WriteLine ("{0}{1}: {2}", new string (' ', 2 * depth), info.StreamTypeNick, (desc != null ? desc : ""));
|
||||
|
||||
var tags = info.Tags;
|
||||
if (tags != null) {
|
||||
Console.WriteLine ("{0}Tags:", new string (' ', 2 * (depth + 1)));
|
||||
tags.Foreach ((TagForeachFunc)delegate (TagList list, string tag) {
|
||||
PrintTagForeach (list, tag, depth + 2);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Print information regarding a stream and its substreams, if any
|
||||
static void PrintTopology (DiscovererStreamInfo info, int depth) {
|
||||
|
||||
if (info == null)
|
||||
return;
|
||||
|
||||
PrintStreamInfo (info, depth);
|
||||
|
||||
var next = info.Next;
|
||||
if (next != null) {
|
||||
PrintTopology (next, depth + 1);
|
||||
} else if (info is DiscovererContainerInfo) {
|
||||
var streams = ((DiscovererContainerInfo)info).Streams;
|
||||
foreach (var stream in streams) {
|
||||
PrintTopology (stream, depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//This function is called every time the discoverer has information regarding one of the URIs we provided.
|
||||
static void HandleDiscovered (object disc, DiscoveredArgs args) {
|
||||
var info = args.Info;
|
||||
var uri = info.Uri;
|
||||
var result = info.Result;
|
||||
var discoverer = (Discoverer)disc;
|
||||
|
||||
switch (result) {
|
||||
case DiscovererResult.UriInvalid:
|
||||
Console.WriteLine ("Invalid URI '{0}'", uri);
|
||||
break;
|
||||
case DiscovererResult.Error:
|
||||
var err = new GLib.GException (args.Error);
|
||||
Console.WriteLine ("Discoverer error: {0}", err.Message);
|
||||
break;
|
||||
case DiscovererResult.Timeout:
|
||||
Console.WriteLine ("Timeout");
|
||||
break;
|
||||
case DiscovererResult.Busy:
|
||||
Console.WriteLine ("Busy");
|
||||
break;
|
||||
case DiscovererResult.MissingPlugins:{
|
||||
var s = info.Misc;
|
||||
|
||||
if (s != null) {
|
||||
Console.WriteLine ("Missing plugins: {0}", s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DiscovererResult.Ok:
|
||||
Console.WriteLine ("Discovered '{0}'", uri);
|
||||
break;
|
||||
}
|
||||
|
||||
if (result != DiscovererResult.Ok) {
|
||||
Console.WriteLine ("This URI cannot be played");
|
||||
return;
|
||||
}
|
||||
|
||||
// If we got no error, show the retrieved information
|
||||
Console.WriteLine ("\nDuration: {0}", new TimeSpan((long)info.Duration));
|
||||
|
||||
var tags = info.Tags;
|
||||
if (tags != null) {
|
||||
Console.WriteLine ("Tags:");
|
||||
tags.Foreach ((TagForeachFunc)delegate (TagList list, string tag) {
|
||||
PrintTagForeach (list, tag, 1);
|
||||
});
|
||||
}
|
||||
|
||||
Console.WriteLine ("Seekable: {0}", (info.Seekable ? "yes" : "no"));
|
||||
|
||||
Console.WriteLine ();
|
||||
|
||||
var sinfo = info.StreamInfo;
|
||||
if (sinfo == null)
|
||||
return;
|
||||
|
||||
Console.WriteLine ("Stream information:");
|
||||
|
||||
PrintTopology (sinfo, 1);
|
||||
|
||||
Console.WriteLine ();
|
||||
}
|
||||
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
var uri = "http://download.blender.org/durian/trailer/sintel_trailer-1080p.mp4";
|
||||
|
||||
// if a URI was provided, use it instead of the default one
|
||||
if (args.Length > 1) {
|
||||
uri = args[0];
|
||||
}
|
||||
|
||||
// Initialize GStreamer
|
||||
Gst.Application.Init (ref args);
|
||||
|
||||
Console.WriteLine ("Discovering '{0}'", uri);
|
||||
|
||||
// Instantiate the Discoverer
|
||||
Discoverer = new Discoverer (5L * Gst.Constants.SECOND);
|
||||
|
||||
// Connect to the interesting signals
|
||||
Discoverer.Discovered += HandleDiscovered;
|
||||
Discoverer.Finished += (sender, e) => {
|
||||
Console.WriteLine ("Finished discovering");
|
||||
MainLoop.Quit ();
|
||||
};
|
||||
|
||||
// Start the discoverer process (nothing to do yet)
|
||||
Discoverer.Start ();
|
||||
|
||||
// Add a request to process asynchronously the URI passed through the command line
|
||||
if (!Discoverer.DiscoverUriAsync (uri)) {
|
||||
Console.WriteLine ("Failed to start discovering URI '{0}'", uri);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a GLib Main Loop and set it to run, so we can wait for the signals
|
||||
MainLoop = new GLib.MainLoop ();
|
||||
MainLoop.Run ();
|
||||
|
||||
// Stop the discoverer process
|
||||
Discoverer.Stop ();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
using GLib;
|
||||
using Gst;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace GstreamerSharp
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Gst.Application.Init(ref args);
|
||||
|
||||
Element src = ElementFactory.Make("audiotestsrc");
|
||||
Element convert = ElementFactory.Make("audioconvert");
|
||||
Element volume = new ExampleVolume();
|
||||
Element sink = ElementFactory.Make("autoaudiosink");
|
||||
|
||||
Pipeline pipeline = new Pipeline();
|
||||
pipeline.Add(src, convert, volume, sink);
|
||||
Element.Link(src, convert, volume, sink);
|
||||
|
||||
pipeline.SetState(State.Playing);
|
||||
|
||||
MainLoop loop = new MainLoop();
|
||||
loop.Run();
|
||||
|
||||
pipeline.SetState(State.Null);
|
||||
|
||||
Console.ReadLine();
|
||||
}
|
||||
}
|
||||
class ExampleVolume : Element
|
||||
{
|
||||
public ExampleVolume()
|
||||
{
|
||||
Volume = 0.5;
|
||||
|
||||
_sink = new Pad(__sinkTemplate, "sink");
|
||||
_sink.ChainFunctionFull = Chain;
|
||||
_sink.Flags |= PadFlags.ProxyCaps;
|
||||
AddPad(_sink);
|
||||
|
||||
_src = new Pad(__srcTemplate, "src");
|
||||
_src.Flags |= PadFlags.ProxyCaps;
|
||||
AddPad(_src);
|
||||
}
|
||||
|
||||
public double Volume { get; set; }
|
||||
|
||||
FlowReturn Chain(Pad pad, Gst.Object parent, Gst.Buffer buffer)
|
||||
{
|
||||
if (Volume == 1.0)
|
||||
{
|
||||
return _src.Push(buffer);
|
||||
}
|
||||
|
||||
buffer.MakeWritable();
|
||||
|
||||
MapInfo mapInfo;
|
||||
buffer.Map(out mapInfo, MapFlags.Read | MapFlags.Write);
|
||||
|
||||
ScaleInt16(mapInfo.DataPtr, mapInfo.Size / 2, Volume);
|
||||
|
||||
buffer.Unmap(mapInfo);
|
||||
|
||||
return _src.Push(buffer);
|
||||
}
|
||||
|
||||
private unsafe void ScaleInt16(IntPtr data, ulong size, double volume)
|
||||
{
|
||||
Int16* sample = (Int16*)data;
|
||||
for (ulong i = 0; i < size; i++)
|
||||
{
|
||||
*sample = ClampInt16(*sample * volume);
|
||||
sample++;
|
||||
}
|
||||
}
|
||||
|
||||
private Int16 ClampInt16(double d)
|
||||
{
|
||||
int i = (int)Math.Round(d);
|
||||
|
||||
if (i > Int16.MaxValue)
|
||||
{
|
||||
return Int16.MaxValue;
|
||||
}
|
||||
else if (i < Int16.MinValue)
|
||||
{
|
||||
return Int16.MinValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (Int16)i;
|
||||
}
|
||||
}
|
||||
|
||||
Pad _src;
|
||||
Pad _sink;
|
||||
|
||||
static ExampleVolume()
|
||||
{
|
||||
Caps audioCaps = Caps.FromString("audio/x-raw, format=(string) S16LE, rate=(int) [1, MAX], channels=(int) 2, layout=(string) interleaved");
|
||||
__srcTemplate = new PadTemplate("src", PadDirection.Src, PadPresence.Always, audioCaps);
|
||||
__sinkTemplate = new PadTemplate("sink", PadDirection.Sink, PadPresence.Always, audioCaps);
|
||||
}
|
||||
|
||||
static PadTemplate __srcTemplate;
|
||||
static PadTemplate __sinkTemplate;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// Authors
|
||||
// Copyright (C) 2017 Thibault Saunier <thibault.saunier@osg-samsung.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Lesser General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2.1 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
// 02110-1301 USA
|
||||
|
||||
|
||||
using System;
|
||||
using Gst;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace GESSharp
|
||||
{
|
||||
class GESExample
|
||||
{
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
// Initialize Gstreamer
|
||||
Gst.Application.Init();
|
||||
|
||||
// Build the pipeline
|
||||
GES.Global.Init();
|
||||
var pipeline = new GES.Pipeline();
|
||||
var timeline = GES.Timeline.NewAudioVideo();
|
||||
var layer = timeline.AppendLayer();
|
||||
|
||||
pipeline["timeline"] = timeline;
|
||||
|
||||
var clip = new GES.TitleClip();
|
||||
clip.Duration = Constants.SECOND * 5;
|
||||
layer.AddClip(clip);
|
||||
clip.SetChildProperty("text", new GLib.Value("Clip 1"));
|
||||
|
||||
var clip1 = new GES.TitleClip();
|
||||
clip1.Start = Constants.SECOND * 5;
|
||||
clip1.Duration = Constants.SECOND * 5;
|
||||
layer.AddClip(clip1);
|
||||
clip1.SetChildProperty("text", new GLib.Value("Clip 2"));
|
||||
|
||||
timeline.Commit();
|
||||
|
||||
pipeline.SetState(State.Playing);
|
||||
//// Wait until error or EOS
|
||||
var bus = pipeline.Bus;
|
||||
Message msg = null;
|
||||
while (msg == null) {
|
||||
var format = Format.Time;
|
||||
long position;
|
||||
msg = bus.TimedPopFiltered (Gst.Constants.SECOND, MessageType.Eos | MessageType.Error);
|
||||
|
||||
pipeline.QueryPosition (format, out position);
|
||||
Console.WriteLine("position: " + Global.TimeFormat(position)
|
||||
+ " / " + Global.TimeFormat(timeline.Duration));
|
||||
}
|
||||
pipeline.SetState(State.Null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright (C) 2013 Stephan Sundermann <stephansundermann@gmail.com>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using Gst;
|
||||
|
||||
namespace GstreamerSharp
|
||||
{
|
||||
class Playback
|
||||
{
|
||||
static GLib.MainLoop Loop;
|
||||
static Element element;
|
||||
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
Loop = new GLib.MainLoop();
|
||||
|
||||
Application.Init(ref args);
|
||||
element = Gst.Parse.Launch("playbin uri=http://ftp.nluug.nl/ftp/graphics/blender/apricot/trailer/Sintel_Trailer1.1080p.DivX_Plus_HD.mkv");
|
||||
|
||||
element.Bus.AddSignalWatch();
|
||||
element.Bus.Message += Handle;
|
||||
element.SetState(State.Playing);
|
||||
Loop.Run();
|
||||
}
|
||||
|
||||
static void Handle (object e, MessageArgs args)
|
||||
{
|
||||
|
||||
switch (args.Message.Type) {
|
||||
case MessageType.StateChanged:
|
||||
State oldstate, newstate, pendingstate;
|
||||
args.Message.ParseStateChanged (out oldstate, out newstate, out pendingstate);
|
||||
System.Console.WriteLine ("[StateChange] From " + oldstate + " to " + newstate + " pending at " + pendingstate);
|
||||
break;
|
||||
case MessageType.StreamStatus:
|
||||
Element owner;
|
||||
StreamStatusType type;
|
||||
args.Message.ParseStreamStatus (out type, out owner);
|
||||
System.Console.WriteLine ("[StreamStatus] Type" + type + " from " + owner);
|
||||
break;
|
||||
case MessageType.DurationChanged:
|
||||
long duration;
|
||||
element.QueryDuration (Format.Time, out duration);
|
||||
System.Console.WriteLine ("[DurationChanged] New duration is " + (duration / Constants.SECOND) + " seconds");
|
||||
break;
|
||||
case MessageType.ResetTime:
|
||||
ulong runningtime = args.Message.ParseResetTime ();
|
||||
System.Console.WriteLine ("[ResetTime] Running time is " + runningtime);
|
||||
break;
|
||||
case MessageType.AsyncDone:
|
||||
ulong desiredrunningtime = args.Message.ParseAsyncDone ();
|
||||
System.Console.WriteLine ("[AsyncDone] Running time is " + desiredrunningtime);
|
||||
break;
|
||||
case MessageType.NewClock:
|
||||
Clock clock = args.Message.ParseNewClock ();
|
||||
System.Console.WriteLine ("[NewClock] " + clock);
|
||||
break;
|
||||
case MessageType.Buffering:
|
||||
int percent = args.Message.ParseBuffering ();
|
||||
System.Console.WriteLine ("[Buffering] " + percent + " % done");
|
||||
break;
|
||||
case MessageType.Tag:
|
||||
TagList list = args.Message.ParseTag ();
|
||||
System.Console.WriteLine ("[Tag] Information in scope " + list.Scope + " is " + list.ToString());
|
||||
break;
|
||||
case MessageType.Error:
|
||||
GLib.GException gerror;
|
||||
string debug;
|
||||
args.Message.ParseError (out gerror, out debug);
|
||||
System.Console.WriteLine ("[Error] " + gerror.Message + " debug information " + debug + ". Exiting! ");
|
||||
Loop.Quit ();
|
||||
break;
|
||||
case MessageType.Eos:
|
||||
System.Console.WriteLine ("[Eos] Playback has ended. Exiting!");
|
||||
Loop.Quit ();
|
||||
break;
|
||||
default:
|
||||
System.Console.WriteLine ("[Recv] " + args.Message.Type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
// Authors
|
||||
// Copyright (C) 2014 Stephan Sundermann <stephansundermann@gmail.com>
|
||||
|
||||
using System;
|
||||
using Gst;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace GstreamerSharp
|
||||
{
|
||||
class Playback
|
||||
{
|
||||
// playbin flags
|
||||
static uint Video = (1 << 0); // We want video output
|
||||
static uint Audio = (1 << 1); // We want audio output
|
||||
static uint Text = (1 << 2); // We want subtitle output
|
||||
|
||||
static Element Playbin;
|
||||
static int NAudio, NVideo, NText;
|
||||
static int CurrentVideo, CurrentAudio, CurrentText;
|
||||
static GLib.MainLoop MainLoop;
|
||||
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
// Initialize GStreamer
|
||||
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://freedesktop.org/software/gstreamer-sdk/data/media/sintel_cropped_multilingual.webm";
|
||||
|
||||
// Set flags to show Audio and Video but ignore Subtitles
|
||||
var flags = (uint)Playbin ["flags"];
|
||||
flags |= Audio | Video;
|
||||
flags &= ~Text;
|
||||
Playbin ["flags"] = flags;
|
||||
|
||||
// Set connection speed. This will affect some internal decisions of playbin2
|
||||
Playbin ["connection-speed"] = 56;
|
||||
|
||||
// Add a bus watch, so we get notified when a message arrives
|
||||
var bus = Playbin.Bus;
|
||||
bus.AddSignalWatch ();
|
||||
bus.Message += HandleMessage;
|
||||
|
||||
// Start playing
|
||||
var ret = Playbin.SetState (State.Playing);
|
||||
if (ret == StateChangeReturn.Failure) {
|
||||
Console.WriteLine ("Unable to set the pipeline to the playing state.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Add a keyboard watch so we get notified of keystrokes
|
||||
GLib.Idle.Add (HandleKeyboard);
|
||||
MainLoop = new GLib.MainLoop ();
|
||||
MainLoop.Run ();
|
||||
|
||||
// Free resources
|
||||
Playbin.SetState (State.Null);
|
||||
}
|
||||
|
||||
static void HandleMessage (object o, MessageArgs args)
|
||||
{
|
||||
var msg = args.Message;
|
||||
switch (msg.Type) {
|
||||
case MessageType.Error:
|
||||
GLib.GException err;
|
||||
string debug;
|
||||
msg.ParseError (out err, out debug);
|
||||
Console.WriteLine ("Error received from element {0}: {1}", msg.Src, err.Message);
|
||||
Console.WriteLine ("Debugging information: {0}", debug != null ? debug : "none");
|
||||
MainLoop.Quit ();
|
||||
break;
|
||||
case MessageType.Eos:
|
||||
Console.WriteLine ("End-Of-Stream reached.");
|
||||
MainLoop.Quit ();
|
||||
break;
|
||||
case MessageType.StateChanged: {
|
||||
State oldState, newState, pendingState;
|
||||
msg.ParseStateChanged (out oldState, out newState, out pendingState);
|
||||
if (msg.Src == Playbin) {
|
||||
if (newState == State.Playing) {
|
||||
// Once we are in the playing state, analyze the streams
|
||||
AnalyzeStreams ();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// We want to keep receiving messages
|
||||
args.RetVal = true;
|
||||
}
|
||||
|
||||
// Extract some metadata from the streams and print it on the screen
|
||||
static void AnalyzeStreams () {
|
||||
// Read some properties
|
||||
NVideo = (int)Playbin ["n-video"];
|
||||
NAudio = (int)Playbin ["n-audio"];
|
||||
NText = (int)Playbin ["n-text"];
|
||||
|
||||
Console.WriteLine ("{0} video stream(s), {1} audio stream(s), {2} text stream(s)", NVideo, NAudio, NText);
|
||||
|
||||
Console.WriteLine ();
|
||||
for (int i = 0; i < NVideo; i++) {
|
||||
// Retrieve the stream's video tags
|
||||
var tags = (TagList)Playbin.Emit ("get-video-tags", new object [] { i });
|
||||
if (tags != null) {
|
||||
Console.WriteLine ("video stream {0}", i);
|
||||
string str;
|
||||
tags.GetString (Constants.TAG_VIDEO_CODEC, out str);
|
||||
Console.WriteLine (" codec: {0}", str != null ? str : "unknown");
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine ();
|
||||
for (int i = 0; i < NAudio; i++) {
|
||||
// Retrieve the stream's audio tags
|
||||
var tags = (TagList)Playbin.Emit ("get-audio-tags", new object [] { i });
|
||||
if (tags != null) {
|
||||
Console.WriteLine ("audio stream {0}", i);
|
||||
string str;
|
||||
if (tags.GetString (Constants.TAG_AUDIO_CODEC, out str)) {
|
||||
Console.WriteLine (" codec: {0}", str);
|
||||
}
|
||||
if (tags.GetString (Constants.TAG_LANGUAGE_CODE, out str)) {
|
||||
Console.WriteLine (" language: {0}", str);
|
||||
}
|
||||
uint rate;
|
||||
if (tags.GetUint (Constants.TAG_BITRATE, out rate)) {
|
||||
Console.WriteLine (" bitrate: {0}", rate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine ();
|
||||
for (int i = 0; i < NText; i++) {
|
||||
// Retrieve the stream's subtitle tags
|
||||
var tags = (TagList)Playbin.Emit ("get-text-tags", new object [] { i });
|
||||
if (tags != null) {
|
||||
Console.WriteLine ("subtitle stream {0}", i);
|
||||
string str;
|
||||
if (tags.GetString (Constants.TAG_LANGUAGE_CODE, out str)) {
|
||||
Console.WriteLine (" language: {0}", str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CurrentAudio = (int)Playbin ["current-audio"];
|
||||
CurrentVideo = (int)Playbin ["current-video"];
|
||||
CurrentText = (int)Playbin ["current-text"];
|
||||
|
||||
Console.WriteLine ();
|
||||
Console.WriteLine ("Currently playing video stream {0}, audio stream {1} and text stream {2}", CurrentVideo, CurrentAudio, CurrentText);
|
||||
Console.WriteLine ("Type any number to select a different audio stream");
|
||||
}
|
||||
|
||||
// Process keyboard input
|
||||
static bool HandleKeyboard () {
|
||||
if (Console.KeyAvailable) {
|
||||
var key = Console.ReadKey (false);
|
||||
|
||||
if (char.IsDigit (key.KeyChar)) {
|
||||
var digit = int.Parse (key.KeyChar.ToString ());
|
||||
|
||||
if (digit < 0 || digit > NAudio) {
|
||||
Console.WriteLine ("Index out of bounds");
|
||||
} else {
|
||||
// If the input was a valid audio stream index, set the current audio stream
|
||||
Console.WriteLine ("Setting current audio stream to {0}", digit);
|
||||
Playbin ["current-audio"] = digit;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
// Authors
|
||||
// Copyright (C) 2014 Stephan Sundermann <stephansundermann@gmail.com>
|
||||
|
||||
using System;
|
||||
using Gst;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace GstreamerSharp
|
||||
{
|
||||
class Playback
|
||||
{
|
||||
// playbin flags
|
||||
static uint Video = (1 << 0); // We want video output
|
||||
static uint Audio = (1 << 1); // We want audio output
|
||||
static uint Text = (1 << 2); // We want subtitle output
|
||||
|
||||
static Element Playbin;
|
||||
static int NAudio, NVideo, NText;
|
||||
static int CurrentVideo, CurrentAudio, CurrentText;
|
||||
static GLib.MainLoop MainLoop;
|
||||
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
// Initialize GStreamer
|
||||
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://freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.ogv";
|
||||
|
||||
// Set the subtitle URI to play and some font description
|
||||
Playbin ["suburi"] = "http://freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer_gr.srt";
|
||||
Playbin ["subtitle-font-desc"] = "Sans, 18";
|
||||
|
||||
// Set flags to show Audio and Video and Subtitles
|
||||
var flags = (uint)Playbin ["flags"];
|
||||
flags |= Audio | Video | Text;
|
||||
Playbin ["flags"] = flags;
|
||||
|
||||
// Add a bus watch, so we get notified when a message arrives
|
||||
var bus = Playbin.Bus;
|
||||
bus.AddSignalWatch ();
|
||||
bus.Message += HandleMessage;
|
||||
|
||||
// Start playing
|
||||
var ret = Playbin.SetState (State.Playing);
|
||||
if (ret == StateChangeReturn.Failure) {
|
||||
Console.WriteLine ("Unable to set the pipeline to the playing state.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Add a keyboard watch so we get notified of keystrokes
|
||||
GLib.Idle.Add (HandleKeyboard);
|
||||
MainLoop = new GLib.MainLoop ();
|
||||
MainLoop.Run ();
|
||||
|
||||
// Free resources
|
||||
Playbin.SetState (State.Null);
|
||||
}
|
||||
|
||||
static void HandleMessage (object o, MessageArgs args)
|
||||
{
|
||||
var msg = args.Message;
|
||||
switch (msg.Type) {
|
||||
case MessageType.Error:
|
||||
GLib.GException err;
|
||||
string debug;
|
||||
msg.ParseError (out err, out debug);
|
||||
Console.WriteLine ("Error received from element {0}: {1}", msg.Src, err.Message);
|
||||
Console.WriteLine ("Debugging information: {0}", debug != null ? debug : "none");
|
||||
MainLoop.Quit ();
|
||||
break;
|
||||
case MessageType.Eos:
|
||||
Console.WriteLine ("End-Of-Stream reached.");
|
||||
MainLoop.Quit ();
|
||||
break;
|
||||
case MessageType.StateChanged: {
|
||||
State oldState, newState, pendingState;
|
||||
msg.ParseStateChanged (out oldState, out newState, out pendingState);
|
||||
if (msg.Src == Playbin) {
|
||||
if (newState == State.Playing) {
|
||||
// Once we are in the playing state, analyze the streams
|
||||
AnalyzeStreams ();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// We want to keep receiving messages
|
||||
args.RetVal = true;
|
||||
}
|
||||
|
||||
// Extract some metadata from the streams and print it on the screen
|
||||
static void AnalyzeStreams () {
|
||||
// Read some properties
|
||||
NVideo = (int)Playbin ["n-video"];
|
||||
NAudio = (int)Playbin ["n-audio"];
|
||||
NText = (int)Playbin ["n-text"];
|
||||
|
||||
Console.WriteLine ("{0} video stream(s), {1} audio stream(s), {2} text stream(s)", NVideo, NAudio, NText);
|
||||
|
||||
Console.WriteLine ();
|
||||
for (int i = 0; i < NVideo; i++) {
|
||||
// Retrieve the stream's video tags
|
||||
var tags = (TagList)Playbin.Emit ("get-video-tags", new object [] { i });
|
||||
if (tags != null) {
|
||||
Console.WriteLine ("video stream {0}", i);
|
||||
string str;
|
||||
tags.GetString (Constants.TAG_VIDEO_CODEC, out str);
|
||||
Console.WriteLine (" codec: {0}", str != null ? str : "unknown");
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine ();
|
||||
for (int i = 0; i < NAudio; i++) {
|
||||
// Retrieve the stream's audio tags
|
||||
var tags = (TagList)Playbin.Emit ("get-audio-tags", new object [] { i });
|
||||
if (tags != null) {
|
||||
Console.WriteLine ("audio stream {0}", i);
|
||||
string str;
|
||||
if (tags.GetString (Constants.TAG_AUDIO_CODEC, out str)) {
|
||||
Console.WriteLine (" codec: {0}", str);
|
||||
}
|
||||
if (tags.GetString (Constants.TAG_LANGUAGE_CODE, out str)) {
|
||||
Console.WriteLine (" language: {0}", str);
|
||||
}
|
||||
uint rate;
|
||||
if (tags.GetUint (Constants.TAG_BITRATE, out rate)) {
|
||||
Console.WriteLine (" bitrate: {0}", rate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine ();
|
||||
for (int i = 0; i < NText; i++) {
|
||||
// Retrieve the stream's subtitle tags
|
||||
var tags = (TagList)Playbin.Emit ("get-text-tags", new object [] { i });
|
||||
if (tags != null) {
|
||||
Console.WriteLine ("subtitle stream {0}", i);
|
||||
string str;
|
||||
if (tags.GetString (Constants.TAG_LANGUAGE_CODE, out str)) {
|
||||
Console.WriteLine (" language: {0}", str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CurrentAudio = (int)Playbin ["current-audio"];
|
||||
CurrentVideo = (int)Playbin ["current-video"];
|
||||
CurrentText = (int)Playbin ["current-text"];
|
||||
|
||||
Console.WriteLine ();
|
||||
Console.WriteLine ("Currently playing video stream {0}, audio stream {1} and text stream {2}", CurrentVideo, CurrentAudio, CurrentText);
|
||||
Console.WriteLine ("Type any number to select a different subtitle stream");
|
||||
}
|
||||
|
||||
// Process keyboard input
|
||||
static bool HandleKeyboard () {
|
||||
if (Console.KeyAvailable) {
|
||||
var key = Console.ReadKey (false);
|
||||
|
||||
if (char.IsDigit (key.KeyChar)) {
|
||||
var digit = int.Parse (key.KeyChar.ToString ());
|
||||
|
||||
if (digit < 0 || digit > NText) {
|
||||
Console.WriteLine ("Index out of bounds");
|
||||
} else {
|
||||
// If the input was a valid subtitle stream index, set the current subtitle stream
|
||||
Console.WriteLine ("Setting current subtitle stream to {0}", digit);
|
||||
Playbin ["current-text"] = digit;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
// Authors
|
||||
// Copyright (C) 2014 Stephan Sundermann <stephansundermann@gmail.com>
|
||||
|
||||
using System;
|
||||
using Gst;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace GstreamerSharp
|
||||
{
|
||||
class Playback
|
||||
{
|
||||
const int ChunkSize = 1024;
|
||||
const int SampleRate = 44100;
|
||||
|
||||
static Gst.App.AppSrc AppSource;
|
||||
static Element Pipeline;
|
||||
|
||||
static long NumSamples; // Number of samples generated so far (for timestamp generation)
|
||||
static float a, b, c, d; // For waveform generation
|
||||
|
||||
static uint Sourceid; // To control the GSource
|
||||
|
||||
static GLib.MainLoop MainLoop; // GLib's Main Loop
|
||||
|
||||
// This method is called by the idle GSource in the mainloop, to feed CHUNK_SIZE bytes into appsrc.
|
||||
// The idle handler is added to the mainloop when appsrc requests us to start sending data (need-data signal)
|
||||
// and is removed when appsrc has enough data (enough-data signal).
|
||||
|
||||
static bool PushData () {
|
||||
var numSamples = ChunkSize / 2; // Because each sample is 16 bits
|
||||
MapInfo map;
|
||||
|
||||
// Create a new empty buffer
|
||||
var buffer = new Gst.Buffer (null, ChunkSize, AllocationParams.Zero);
|
||||
|
||||
// Set its timestamp and duration
|
||||
buffer.Pts = Util.Uint64Scale ((ulong)NumSamples, (ulong)Constants.SECOND, (ulong)SampleRate);
|
||||
buffer.Dts = Util.Uint64Scale ((ulong)NumSamples, (ulong)Constants.SECOND, (ulong)SampleRate);
|
||||
buffer.Duration = Util.Uint64Scale ((ulong)NumSamples, (ulong)Constants.SECOND, (ulong)SampleRate);
|
||||
|
||||
// Generate some psychodelic waveforms
|
||||
buffer.Map (out map, MapFlags.Write);
|
||||
c += d;
|
||||
d -= c / 1000f;
|
||||
var freq = 1100f + 1000f * d;
|
||||
short[] data = new short[numSamples];
|
||||
for (int i = 0; i < numSamples; i++) {
|
||||
a += b;
|
||||
b -= a / freq;
|
||||
data[i] = (short)(500f * a);
|
||||
}
|
||||
// convert the short[] to a byte[] by marshalling
|
||||
var native = Marshal.AllocHGlobal (data.Length * sizeof(short));
|
||||
Marshal.Copy (data, 0, native, data.Length);
|
||||
byte[] bytedata = new byte[2 * data.Length];
|
||||
Marshal.Copy (native, bytedata, 0, data.Length * sizeof(short));
|
||||
|
||||
map.Data = bytedata;
|
||||
buffer.Unmap (map);
|
||||
NumSamples += numSamples;
|
||||
|
||||
// Push the buffer into the appsrc
|
||||
var ret = AppSource.PushBuffer (buffer);
|
||||
|
||||
// Free the buffer now that we are done with it
|
||||
buffer.Dispose ();
|
||||
|
||||
if (ret != FlowReturn.Ok) {
|
||||
// We got some error, stop sending data
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// This signal callback triggers when appsrc needs Here, we add an idle handler
|
||||
// to the mainloop to start pushing data into the appsrc
|
||||
static void StartFeed (object sender, Gst.App.NeedDataArgs args) {
|
||||
if (Sourceid == 0) {
|
||||
Console.WriteLine ("Start feeding");
|
||||
Sourceid = GLib.Idle.Add (PushData);
|
||||
}
|
||||
}
|
||||
|
||||
// This callback triggers when appsrc has enough data and we can stop sending.
|
||||
// We remove the idle handler from the mainloop
|
||||
static void StopFeed (object sender, EventArgs args) {
|
||||
if (Sourceid != 0) {
|
||||
Console.WriteLine ("Stop feeding");
|
||||
GLib.Source.Remove (Sourceid);
|
||||
Sourceid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// This function is called when playbin has created the appsrc element, so we have a chance to configure it.
|
||||
static void SourceSetup (object sender, GLib.SignalArgs args) {
|
||||
var info = new Gst.Audio.AudioInfo ();
|
||||
var source = new Gst.App.AppSrc(((Element)args.Args [0]).Handle);
|
||||
Console.WriteLine ("Source has been created. Configuring.");
|
||||
AppSource = source;
|
||||
|
||||
// Configure appsrc
|
||||
Gst.Audio.AudioChannelPosition[] position = {};
|
||||
info.SetFormat (Gst.Audio.AudioFormat.S16, SampleRate, 1, position);
|
||||
var audioCaps = info.ToCaps ();
|
||||
source ["caps"] = audioCaps;
|
||||
source ["format"] = Format.Time;
|
||||
source.NeedData += StartFeed;
|
||||
source.EnoughData += StopFeed;
|
||||
}
|
||||
|
||||
// This function is called when an error message is posted on the bus
|
||||
static void HandleError (object sender, GLib.SignalArgs args) {
|
||||
GLib.GException err;
|
||||
string debug;
|
||||
var msg = (Message) args.Args[0];
|
||||
|
||||
// Print error details on the screen
|
||||
msg.ParseError (out err, out debug);
|
||||
Console.WriteLine ("Error received from element {0}: {1}", msg.Src.Name, err.Message);
|
||||
Console.WriteLine ("Debugging information: {0}", debug != null ? debug : "none");
|
||||
|
||||
MainLoop.Quit ();
|
||||
}
|
||||
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
b = 1;
|
||||
d = 1;
|
||||
|
||||
// Initialize Gstreamer
|
||||
Gst.Application.Init(ref args);
|
||||
|
||||
// Create the playbin element
|
||||
Pipeline = Parse.Launch ("playbin uri=appsrc://");
|
||||
Pipeline.Connect ("source-setup", SourceSetup);
|
||||
|
||||
// Instruct the bus to emit signals for each received message, and connect to the interesting signals
|
||||
var bus = Pipeline.Bus;
|
||||
bus.AddSignalWatch ();
|
||||
bus.Connect ("message::error", HandleError);
|
||||
|
||||
// Start playing the pipeline
|
||||
Pipeline.SetState (State.Playing);
|
||||
|
||||
// Create a GLib Main Loop and set it to run
|
||||
MainLoop = new GLib.MainLoop ();
|
||||
MainLoop.Run ();
|
||||
|
||||
// Free resources
|
||||
Pipeline.SetState (State.Null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
// Authors
|
||||
// Copyright (C) 2014 Stephan Sundermann <stephansundermann@gmail.com>
|
||||
|
||||
using System;
|
||||
using Gst;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace GstreamerSharp
|
||||
{
|
||||
class Playback
|
||||
{
|
||||
const int GraphLength = 78;
|
||||
static uint PlayFlagDownload = (1 << 7); // Enable progressive download (on selected formats)
|
||||
|
||||
static bool IsLive;
|
||||
static Element Pipeline;
|
||||
static GLib.MainLoop MainLoop;
|
||||
static int BufferingLevel;
|
||||
|
||||
static void GotLocation (object sender, GLib.SignalArgs args) {
|
||||
var propObject = (Gst.Object)args.Args [0];
|
||||
var location = (string) propObject["temp-location"];
|
||||
Console.WriteLine ("Temporary file: {0}", location);
|
||||
// Uncomment this line to keep the temporary file after the program exits
|
||||
// g_object_set (G_OBJECT (prop_object), "temp-remove", FALSE, NULL);
|
||||
}
|
||||
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
// Initialize GStreamer
|
||||
Application.Init (ref args);
|
||||
BufferingLevel = 100;
|
||||
|
||||
// Build the pipeline
|
||||
Pipeline = Parse.Launch ("playbin uri=http://freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm");
|
||||
var bus = Pipeline.Bus;
|
||||
|
||||
// Set the download flag
|
||||
var flags = (uint)Pipeline ["flags"];
|
||||
flags |= PlayFlagDownload;
|
||||
Pipeline ["flags"] = flags;
|
||||
|
||||
// Uncomment this line to limit the amount of downloaded data
|
||||
// g_object_set (pipeline, "ring-buffer-max-size", (guint64)4000000, NULL);
|
||||
|
||||
// Start playing
|
||||
var ret = Pipeline.SetState (State.Playing);
|
||||
if (ret == StateChangeReturn.Failure) {
|
||||
Console.WriteLine ("Unable to set the pipeline to the playing state.");
|
||||
return;
|
||||
} else if (ret == StateChangeReturn.NoPreroll) {
|
||||
IsLive = true;
|
||||
}
|
||||
|
||||
MainLoop = new GLib.MainLoop ();
|
||||
|
||||
bus.AddSignalWatch ();
|
||||
bus.Message += HandleMessage;
|
||||
Pipeline.Connect ("deep-notify::temp-location", GotLocation);
|
||||
|
||||
// Register a function that GLib will call every second
|
||||
GLib.Timeout.AddSeconds (1, RefreshUI);
|
||||
|
||||
MainLoop.Run ();
|
||||
|
||||
// Free resources
|
||||
Pipeline.SetState (State.Null);
|
||||
}
|
||||
|
||||
static void HandleMessage (object o, MessageArgs args)
|
||||
{
|
||||
var msg = args.Message;
|
||||
switch (msg.Type) {
|
||||
case MessageType.Error: {
|
||||
GLib.GException err;
|
||||
string debug;
|
||||
|
||||
msg.ParseError (out err, out debug);
|
||||
Console.WriteLine ("Error: {0}", err.Message);
|
||||
|
||||
Pipeline.SetState (State.Ready);
|
||||
MainLoop.Quit ();
|
||||
break;
|
||||
}
|
||||
case MessageType.Eos:
|
||||
// end-of-stream
|
||||
Pipeline.SetState (State.Ready);
|
||||
MainLoop.Quit ();
|
||||
break;
|
||||
case MessageType.Buffering: {
|
||||
int percent = 0;
|
||||
|
||||
// If the stream is live, we do not care about buffering.
|
||||
if (IsLive) break;
|
||||
|
||||
percent = msg.ParseBuffering ();
|
||||
// Wait until buffering is complete before start/resume playing
|
||||
if (percent < 100)
|
||||
Pipeline.SetState (State.Paused);
|
||||
else
|
||||
Pipeline.SetState (State.Playing);
|
||||
break;
|
||||
}
|
||||
case MessageType.ClockLost:
|
||||
// Get a new clock
|
||||
Pipeline.SetState (State.Paused);
|
||||
Pipeline.SetState (State.Playing);
|
||||
break;
|
||||
default:
|
||||
// Unhandled message
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool RefreshUI () {
|
||||
var query = new Query (Format.Percent);
|
||||
if (Pipeline.Query (query)) {
|
||||
var graph = new StringBuilder (GraphLength);
|
||||
long position = 0, duration = 0;
|
||||
|
||||
var nRanges = query.NBufferingRanges;
|
||||
for (uint range = 0; range < nRanges; range++) {
|
||||
long start, stop;
|
||||
query.ParseNthBufferingRange (range, out start, out stop);
|
||||
start = start * GraphLength / (stop - start);
|
||||
stop = stop * GraphLength / (stop - start);
|
||||
for (int i = (int)start; i < stop; i++)
|
||||
graph.Insert (i, '-');
|
||||
}
|
||||
if (Pipeline.QueryPosition (Format.Time, out position) && position >= 0
|
||||
&& Pipeline.QueryDuration (Format.Time, out duration) && duration >= 0) {
|
||||
var i = (int)(GraphLength * (double)position / (double)(duration + 1));
|
||||
graph [i] = BufferingLevel < 100 ? 'X' : '>';
|
||||
}
|
||||
Console.WriteLine ("[{0}]", graph);
|
||||
if (BufferingLevel < 100) {
|
||||
Console.WriteLine (" Buffering: {0}", BufferingLevel);
|
||||
} else {
|
||||
Console.WriteLine (" ");
|
||||
}
|
||||
Console.WriteLine ();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
// Authors
|
||||
// Copyright (C) 2014 Stephan Sundermann <stephansundermann@gmail.com>
|
||||
|
||||
using System;
|
||||
using Gst;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace GstreamerSharp
|
||||
{
|
||||
class Playback
|
||||
{
|
||||
static Element Pipeline;
|
||||
static GLib.MainLoop MainLoop;
|
||||
|
||||
// Process a color balance command
|
||||
static void UpdateColorChannel (string channelName, bool increase, Gst.Video.IColorBalance cb) {
|
||||
|
||||
// Retrieve the list of channels and locate the requested one
|
||||
var channels = cb.ListChannels ();
|
||||
Gst.Video.ColorBalanceChannel channel = null;
|
||||
foreach (var ch in channels) {
|
||||
var label = ch.Label;
|
||||
|
||||
if (label.Contains (channelName)) {
|
||||
channel = ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (channel == null)
|
||||
return;
|
||||
|
||||
// Change the channel's value
|
||||
var step = 0.1 * (channel.MaxValue - channel.MinValue);
|
||||
var value = cb.GetValue (channel);
|
||||
if (increase) {
|
||||
value = (int)(value + step);
|
||||
if (value > channel.MaxValue)
|
||||
value = channel.MaxValue;
|
||||
} else {
|
||||
value = (int)(value - step);
|
||||
if (value < channel.MinValue)
|
||||
value = channel.MinValue;
|
||||
}
|
||||
cb.SetValue (channel, value);
|
||||
}
|
||||
|
||||
// Process keyboard input
|
||||
static bool HandleKeyboard () {
|
||||
if (Console.KeyAvailable) {
|
||||
var key = Console.ReadKey (false);
|
||||
var cb = new Gst.Video.ColorBalanceAdapter (Pipeline.Handle);
|
||||
|
||||
switch (key.Key) {
|
||||
case ConsoleKey.C:
|
||||
UpdateColorChannel ("CONTRAST", key.Modifiers == ConsoleModifiers.Shift, cb);
|
||||
break;
|
||||
case ConsoleKey.B:
|
||||
UpdateColorChannel ("BRIGHTNESS", key.Modifiers == ConsoleModifiers.Shift, cb);
|
||||
break;
|
||||
case ConsoleKey.H:
|
||||
UpdateColorChannel ("HUE", key.Modifiers == ConsoleModifiers.Shift, cb);
|
||||
break;
|
||||
case ConsoleKey.S:
|
||||
UpdateColorChannel ("SATURATION", key.Modifiers == ConsoleModifiers.Shift, cb);
|
||||
break;
|
||||
case ConsoleKey.Q:
|
||||
MainLoop.Quit ();
|
||||
break;
|
||||
}
|
||||
PrintCurrentValues ();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Output the current values of all Color Balance channels
|
||||
static void PrintCurrentValues () {
|
||||
// Output Color Balance value
|
||||
var cb = new Gst.Video.ColorBalanceAdapter (Pipeline.Handle);
|
||||
var channels = cb.ListChannels ();
|
||||
|
||||
foreach (var ch in channels) {
|
||||
var value = cb.GetValue (ch);
|
||||
Console.WriteLine ("{0}: {1}", ch.Label, 100 * (value - ch.MinValue) / (ch.MaxValue - ch.MinValue));
|
||||
}
|
||||
Console.WriteLine ();
|
||||
}
|
||||
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
// Initialize GStreamer
|
||||
Application.Init (ref args);
|
||||
GtkSharp.GstreamerSharp.ObjectManager.Initialize ();
|
||||
|
||||
// Print usage map
|
||||
Console.WriteLine ("USAGE: Choose one of the following options, then press enter:");
|
||||
Console.WriteLine (" 'C' to increase contrast, 'c' to decrease contrast");
|
||||
Console.WriteLine (" 'B' to increase brightness, 'b' to decrease brightness");
|
||||
Console.WriteLine (" 'H' to increase hue, 'h' to decrease hue");
|
||||
Console.WriteLine (" 'S' to increase saturation, 's' to decrease saturation");
|
||||
Console.WriteLine (" 'Q' to quit");
|
||||
|
||||
// Build the pipeline
|
||||
Pipeline = Parse.Launch ("playbin uri=http://freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm");
|
||||
|
||||
// Add a keyboard watch so we get notified of keystrokes
|
||||
|
||||
// Start playing
|
||||
var ret = Pipeline.SetState (State.Playing);
|
||||
if (ret == StateChangeReturn.Failure) {
|
||||
Console.WriteLine ("Unable to set the pipeline to the playing state.");
|
||||
return;
|
||||
}
|
||||
PrintCurrentValues ();
|
||||
|
||||
// Create a GLib Main Loop and set it to run
|
||||
MainLoop = new GLib.MainLoop ();
|
||||
GLib.Timeout.Add (50, HandleKeyboard);
|
||||
MainLoop.Run ();
|
||||
|
||||
// Free resources
|
||||
Pipeline.SetState (State.Null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// Authors
|
||||
// Copyright (C) 2014 Stephan Sundermann <stephansundermann@gmail.com>
|
||||
|
||||
using System;
|
||||
using Gst;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace GstreamerSharp
|
||||
{
|
||||
class Playback
|
||||
{
|
||||
const uint PlayFlagsVis = (1 << 3);
|
||||
|
||||
static Element Pipeline;
|
||||
|
||||
// Return TRUE if this is a Visualization element
|
||||
static bool FilterVisFeatures (PluginFeature feature) {
|
||||
|
||||
if (!(feature is ElementFactory))
|
||||
return false;
|
||||
var factory = (ElementFactory)feature;
|
||||
if (!factory.GetMetadata (Gst.Constants.ELEMENT_METADATA_KLASS).Contains ("Visualization"))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
ElementFactory selectedFactory = null;
|
||||
|
||||
// Initialize GStreamer
|
||||
Application.Init (ref args);
|
||||
|
||||
// Get a list of all visualization plugins
|
||||
var list = Registry.Get().FeatureFilter (FilterVisFeatures, false);
|
||||
|
||||
// Print their names
|
||||
Console.WriteLine ("Available visualization plugins:");
|
||||
foreach (var walk in list) {
|
||||
var factory = (ElementFactory)walk;
|
||||
var name = factory.Name;
|
||||
Console.WriteLine(" {0}", name);
|
||||
|
||||
if (selectedFactory == null && name.StartsWith ("goom")) {
|
||||
selectedFactory = factory;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't use the factory if it's still empty
|
||||
// e.g. no visualization plugins found
|
||||
if (selectedFactory == null) {
|
||||
Console.WriteLine ("No visualization plugins found!");
|
||||
return;
|
||||
}
|
||||
|
||||
// We have now selected a factory for the visualization element
|
||||
Console.WriteLine ("Selected '{0}'", selectedFactory.Name);
|
||||
var visPlugin = selectedFactory.Create ();
|
||||
if (visPlugin == null)
|
||||
return;
|
||||
|
||||
|
||||
// Build the pipeline
|
||||
Pipeline = Parse.Launch ("playbin uri=http://1live.akacast.akamaistream.net/7/706/119434/v1/gnl.akacast.akamaistream.net/1live");
|
||||
|
||||
// Set the visualization flag
|
||||
var flags = (uint)Pipeline ["flags"];
|
||||
flags |= PlayFlagsVis;
|
||||
Pipeline ["flags"] = flags;
|
||||
|
||||
// set vis plugin for playbin
|
||||
Pipeline ["vis-plugin"] = visPlugin;
|
||||
|
||||
// Start playing
|
||||
var ret = Pipeline.SetState (State.Playing);
|
||||
if (ret == StateChangeReturn.Failure) {
|
||||
Console.WriteLine ("Unable to set the pipeline to the playing state.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait until error or EOS
|
||||
var bus = Pipeline.Bus;
|
||||
var msg = bus.TimedPopFiltered (Constants.CLOCK_TIME_NONE, MessageType.Error | MessageType.Eos);
|
||||
|
||||
// Free resources
|
||||
Pipeline.SetState (State.Null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// Authors
|
||||
// Copyright (C) 2014 Stephan Sundermann <stephansundermann@gmail.com>
|
||||
|
||||
using System;
|
||||
using Gst;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace GstreamerSharp
|
||||
{
|
||||
class Playback
|
||||
{
|
||||
public static void Main (string[] args)
|
||||
{
|
||||
// Initialize GStreamer
|
||||
Application.Init (ref args);
|
||||
|
||||
// Build the pipeline
|
||||
var pipeline = Parse.Launch ("playbin uri=http://freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm");
|
||||
|
||||
// Create the elements inside the sink bin
|
||||
var equalizer = ElementFactory.Make ("equalizer-3bands", "equalizer");
|
||||
var convert = ElementFactory.Make ("audioconvert", "convert");
|
||||
var sink = ElementFactory.Make ("autoaudiosink", "audio_sink");
|
||||
if (equalizer == null || convert == null || sink == null) {
|
||||
Console.WriteLine ("Not all elements could be created.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the sink bin, add the elements and link them
|
||||
var bin = new Bin ("audio_sink_bin");
|
||||
bin.Add (equalizer, convert, sink);
|
||||
Element.Link (equalizer, convert, sink);
|
||||
var pad = equalizer.GetStaticPad ("sink");
|
||||
var ghostPad = new GhostPad ("sink", pad);
|
||||
ghostPad.SetActive (true);
|
||||
bin.AddPad (ghostPad);
|
||||
|
||||
// Configure the equalizer
|
||||
equalizer["band1"] = (double)-24.0;
|
||||
equalizer["band2"] = (double)-24.0;
|
||||
|
||||
// Set playbin's audio sink to be our sink bin
|
||||
pipeline["audio-sink"] = bin;
|
||||
|
||||
// Start playing
|
||||
var ret = pipeline.SetState (State.Playing);
|
||||
if (ret == StateChangeReturn.Failure) {
|
||||
Console.WriteLine ("Unable to set the pipeline to the playing state.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait until error or EOS
|
||||
var bus = pipeline.Bus;
|
||||
var msg = bus.TimedPopFiltered (Constants.CLOCK_TIME_NONE, MessageType.Error | MessageType.Eos);
|
||||
|
||||
// Free resources
|
||||
pipeline.SetState (State.Null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
// 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 ();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
examples = [
|
||||
['playback', 'Playback.cs'],
|
||||
['video-overlay' , 'VideoOverlay.cs', has_gtk, gtk_sharp_dep],
|
||||
['basic-tutorial-1' , 'BasicTutorial1.cs',],
|
||||
['basic-tutorial-2' , 'BasicTutorial2.cs',],
|
||||
['basic-tutorial-3' , 'BasicTutorial3.cs',],
|
||||
['basic-tutorial-4' , 'BasicTutorial4.cs',],
|
||||
['basic-tutorial-5' , 'BasicTutorial5.cs', has_gtk, gtk_sharp_dep],
|
||||
['basic-tutorial-6' , 'BasicTutorial6.cs',],
|
||||
['basic-tutorial-7' , 'BasicTutorial7.cs',],
|
||||
['basic-tutorial-8' , 'BasicTutorial8.cs',],
|
||||
['basic-tutorial-9' , 'BasicTutorial9.cs',],
|
||||
['basic-tutorial-12' , 'BasicTutorial12.cs',],
|
||||
['basic-tutorial-13' , 'BasicTutorial13.cs',],
|
||||
['example-volume' , 'ExampleVolume.cs',],
|
||||
['playback-tutorial-1' , 'PlaybackTutorial1.cs',],
|
||||
['playback-tutorial-2' , 'PlaybackTutorial2.cs',],
|
||||
['playback-tutorial-3' , 'PlaybackTutorial3.cs',],
|
||||
['playback-tutorial-4' , 'PlaybackTutorial4.cs',],
|
||||
['playback-tutorial-5' , 'PlaybackTutorial5.cs',],
|
||||
['playback-tutorial-6' , 'PlaybackTutorial6.cs',],
|
||||
['playback-tutorial-7' , 'PlaybackTutorial7.cs',],
|
||||
]
|
||||
|
||||
foreach example: examples
|
||||
deps = [gst_sharp_dep]
|
||||
if example.length() == 2 or example.get(2)
|
||||
if example.length() > 2
|
||||
deps += example.get(3)
|
||||
endif
|
||||
|
||||
executable(example.get(0), example.get(1),
|
||||
cs_args: ['-unsafe'], dependencies: deps)
|
||||
endif
|
||||
endforeach
|
||||
|
||||
if ges_dep.found()
|
||||
executable('ges-example', 'GESExample.cs',
|
||||
cs_args: ['-unsafe'],
|
||||
dependencies: [gst_sharp_dep],
|
||||
link_with: ges_sharp)
|
||||
endif
|
||||
Reference in New Issue
Block a user