mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-17 03:35:21 +00:00
sample: Add basic tutorial 8 sample
This commit is contained in:
parent
8391bb849a
commit
d5e5a1030b
2 changed files with 232 additions and 2 deletions
226
samples/BasicTutorial8.cs
Normal file
226
samples/BasicTutorial8.cs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
TARGETS = playback.exe video-overlay.exe basic-tutorial-1.exe basic-tutorial-2.exe basic-tutorial-3.exe basic-tutorial-4.exe basic-tutorial-5.exe basic-tutorial-6.exe basic-tutorial-7.exe
|
||||
TARGETS = playback.exe video-overlay.exe basic-tutorial-1.exe basic-tutorial-2.exe basic-tutorial-3.exe basic-tutorial-4.exe basic-tutorial-5.exe basic-tutorial-6.exe basic-tutorial-7.exe basic-tutorial-8.exe
|
||||
|
||||
DEBUGS = $(addsuffix .mdb, $(TARGETS))
|
||||
assemblies = \
|
||||
|
@ -36,6 +36,9 @@ basic-tutorial-6.exe: $(srcdir)/BasicTutorial6.cs $(assemblies)
|
|||
basic-tutorial-7.exe: $(srcdir)/BasicTutorial7.cs $(assemblies)
|
||||
$(CSC) $(CSFLAGS) -out:basic-tutorial-7.exe $(references) $(GLIB_SHARP_LIBS) $(srcdir)/BasicTutorial7.cs
|
||||
|
||||
basic-tutorial-8.exe: $(srcdir)/BasicTutorial8.cs $(assemblies)
|
||||
$(CSC) $(CSFLAGS) -out:basic-tutorial-8.exe $(references) $(GLIB_SHARP_LIBS) $(srcdir)/BasicTutorial8.cs
|
||||
|
||||
EXTRA_DIST = \
|
||||
Playback.cs \
|
||||
VideoOverlay.cs \
|
||||
|
@ -45,4 +48,5 @@ EXTRA_DIST = \
|
|||
BasicTutorial4.cs \
|
||||
BasicTutorial5.cs \
|
||||
BasicTutorial6.cs \
|
||||
BasicTutorial7.cs
|
||||
BasicTutorial7.cs \
|
||||
BasicTutorial8.cs
|
||||
|
|
Loading…
Reference in a new issue