2 // Copyright (C) 2014 Stephan Sundermann <stephansundermann@gmail.com>
6 using System.Runtime.InteropServices;
8 namespace GstreamerSharp
12 const int ChunkSize = 1024;
13 const int SampleRate = 44100;
15 static Gst.App.AppSrc AppSource;
16 static Element Pipeline;
18 static long NumSamples; // Number of samples generated so far (for timestamp generation)
19 static float a, b, c, d; // For waveform generation
21 static uint Sourceid; // To control the GSource
23 static GLib.MainLoop MainLoop; // GLib's Main Loop
25 // This method is called by the idle GSource in the mainloop, to feed CHUNK_SIZE bytes into appsrc.
26 // The idle handler is added to the mainloop when appsrc requests us to start sending data (need-data signal)
27 // and is removed when appsrc has enough data (enough-data signal).
29 static bool PushData () {
30 var numSamples = ChunkSize / 2; // Because each sample is 16 bits
33 // Create a new empty buffer
34 var buffer = new Gst.Buffer (null, ChunkSize, AllocationParams.Zero);
36 // Set its timestamp and duration
37 buffer.Pts = Util.Uint64Scale ((ulong)NumSamples, (ulong)Constants.SECOND, (ulong)SampleRate);
38 buffer.Dts = Util.Uint64Scale ((ulong)NumSamples, (ulong)Constants.SECOND, (ulong)SampleRate);
39 buffer.Duration = Util.Uint64Scale ((ulong)NumSamples, (ulong)Constants.SECOND, (ulong)SampleRate);
41 // Generate some psychodelic waveforms
42 buffer.Map (out map, MapFlags.Write);
45 var freq = 1100f + 1000f * d;
46 short[] data = new short[numSamples];
47 for (int i = 0; i < numSamples; i++) {
50 data[i] = (short)(500f * a);
52 // convert the short[] to a byte[] by marshalling
53 var native = Marshal.AllocHGlobal (data.Length * sizeof(short));
54 Marshal.Copy (data, 0, native, data.Length);
55 byte[] bytedata = new byte[2 * data.Length];
56 Marshal.Copy (native, bytedata, 0, data.Length * sizeof(short));
60 NumSamples += numSamples;
62 // Push the buffer into the appsrc
63 var ret = AppSource.PushBuffer (buffer);
65 // Free the buffer now that we are done with it
68 if (ret != FlowReturn.Ok) {
69 // We got some error, stop sending data
75 // This signal callback triggers when appsrc needs Here, we add an idle handler
76 // to the mainloop to start pushing data into the appsrc
77 static void StartFeed (object sender, Gst.App.NeedDataArgs args) {
79 Console.WriteLine ("Start feeding");
80 Sourceid = GLib.Idle.Add (PushData);
84 // This callback triggers when appsrc has enough data and we can stop sending.
85 // We remove the idle handler from the mainloop
86 static void StopFeed (object sender, EventArgs args) {
88 Console.WriteLine ("Stop feeding");
89 GLib.Source.Remove (Sourceid);
94 // This function is called when playbin has created the appsrc element, so we have a chance to configure it.
95 static void SourceSetup (object sender, GLib.SignalArgs args) {
96 var info = new Gst.Audio.AudioInfo ();
97 var source = new Gst.App.AppSrc(((Element)args.Args [0]).Handle);
98 Console.WriteLine ("Source has been created. Configuring.");
102 Gst.Audio.AudioChannelPosition[] position = {};
103 info.SetFormat (Gst.Audio.AudioFormat.S16, SampleRate, 1, position);
104 var audioCaps = info.ToCaps ();
105 source ["caps"] = audioCaps;
106 source ["format"] = Format.Time;
107 source.NeedData += StartFeed;
108 source.EnoughData += StopFeed;
111 // This function is called when an error message is posted on the bus
112 static void HandleError (object sender, GLib.SignalArgs args) {
115 var msg = (Message) args.Args[0];
117 // Print error details on the screen
118 msg.ParseError (out err, out debug);
119 Console.WriteLine ("Error received from element {0}: {1}", msg.Src.Name, err.Message);
120 Console.WriteLine ("Debugging information: {0}", debug != null ? debug : "none");
125 public static void Main (string[] args)
130 // Initialize Gstreamer
131 Gst.Application.Init(ref args);
133 // Create the playbin element
134 Pipeline = Parse.Launch ("playbin uri=appsrc://");
135 Pipeline.Connect ("source-setup", SourceSetup);
137 // Instruct the bus to emit signals for each received message, and connect to the interesting signals
138 var bus = Pipeline.Bus;
139 bus.AddSignalWatch ();
140 bus.Connect ("message::error", HandleError);
142 // Start playing the pipeline
143 Pipeline.SetState (State.Playing);
145 // Create a GLib Main Loop and set it to run
146 MainLoop = new GLib.MainLoop ();
150 Pipeline.SetState (State.Null);