summaryrefslogtreecommitdiff
path: root/samples/PlaybackTutorial3.cs
blob: bd0027a2848109d51cc92dfcc01ac85a4ac5acc7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// 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 = Gst.Audio.AudioInfo.Zero;
			var source = new Gst.App.AppSrc(((Element)args.Args [0]).Handle);
			Console.WriteLine ("Source has been created. Configuring.");
			AppSource = source;

			// Configure appsrc
			info.SetFormat (Gst.Audio.AudioFormat.S16, SampleRate, 1, (Gst.Audio.AudioChannelPosition)0);
			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);
		}
	}
}