summaryrefslogtreecommitdiff
path: root/samples/BasicTutorial8.cs
diff options
context:
space:
mode:
Diffstat (limited to 'samples/BasicTutorial8.cs')
-rw-r--r--samples/BasicTutorial8.cs226
1 files changed, 226 insertions, 0 deletions
diff --git a/samples/BasicTutorial8.cs b/samples/BasicTutorial8.cs
new file mode 100644
index 0000000..02acfbd
--- /dev/null
+++ b/samples/BasicTutorial8.cs
@@ -0,0 +1,226 @@
+// 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 = Gst.Audio.AudioInfo.Zero;
+
+ // 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
+ info.SetFormat (Gst.Audio.AudioFormat.S16, SampleRate, 1, (Gst.Audio.AudioChannelPosition) 0);
+ 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.Playing);
+ }
+ }
+} \ No newline at end of file