The use of AppSink in C#.

Hi.
※I am using deepl.
I am building a windows form app with C# (Visual Studio 2019) + Gstreamer (Ver 1.24.1) + GstSharp (1.18.0).
I need some help on how to use AppSink.

About “appSink = pipeline.GetByName(“sink”) as AppSink;” in the following program,
When I run it, appSink is null.
I guess the cast must be failing, but I can’t figure out why.
The plugin seems to be loaded.
Environment variables have also been set.
Path:C:\gstreamer\1.0\msvc_x86_64\bin
GST_PLUGIN_PATH:C:\gstreamer\1.0\msvc_x86_64\lib\gstreamer-1.0

If you know anything about it, could you please let me know?
I look forward to hearing from you.

public partial class Form1 : Form
{
private Pipeline pipeline;
private Gst.App.AppSink appSink;
private VideoOverlayAdapter overlayAdapter;
private bool pipelineInitialized = false;

public Form1()
{
    InitializeComponent();
    InitGStreamer();
    InitializePipeline();
}

private void InitGStreamer()
{
    Gst.Application.Init();
}

private void InitializePipeline()
{
    if (!Gst.Application.InitCheck())
    {
        MessageBox.Show("Failed to initialize GStreamer.");
        return;
    }

    pipeline = new Pipeline();
    var source = ElementFactory.Make("udpsrc", "source");
    if (source == null)
    {
        MessageBox.Show("Failed to create source.");
        return;
    }

    var queue = ElementFactory.Make("queue", "queue");
    var jitterBuffer = ElementFactory.Make("rtpjitterbuffer", "jitter-buffer");
    var depayloader = ElementFactory.Make("rtph264depay", "depayloader");
    var decoder = ElementFactory.Make("avdec_h264", "decoder");
    var converter = ElementFactory.Make("videoconvert", "converter");
    var sink = ElementFactory.Make("appsink", "sink");

    if (queue == null || jitterBuffer == null || depayloader == null || decoder == null || converter == null || sink == null)
    {
        MessageBox.Show("Failed to create one or more elements.");
        return;
    }

    pipeline.Add(source, queue, jitterBuffer, depayloader, decoder, converter, sink);
    if (!Element.Link(source, queue, jitterBuffer, depayloader, decoder, converter, sink))
    {
        MessageBox.Show("Failed to link elements.");
        return;
    }

    appSink = pipeline.GetByName("sink") as AppSink;

    var caps = Caps.FromString("application/x-rtp,media=(string)video,clock-rate=(int)90000,encoding-name=(string)H264,payload=(int)96");
    source["caps"] = caps;
    source["port"] = 50007;

    //appSink.EmitSignals = true;
    //appSink.Sync = false;
    //appSink.NewSample += AppSink_NewSample;

    pipelineInitialized = true;
}

}

C:\Users\KY2017>gst-inspect-1.0 app
Plugin Details:
Name app
Description Elements used to communicate with applications
Filename C:\gstreamer\1.0\msvc_x86_64\lib\gstreamer-1.0\gstapp.dll
Version 1.24.1
License LGPL
Source module gst-plugins-base
Documentation app
Source release date 2024-03-21
Binary package GStreamer Base Plug-ins source release
Origin URL Unknown package origin

appsink: AppSink
appsrc: AppSrc

2 features:
±- 2 elements

C:\Users\KY2017>

You still can use AppSink as Element type.

Set properties with

appSink["emit-signals"] = true;
appSink["sync"] = false;

And connect your signal with

appSink.AddSignalHandler("new-sample", AppSink_NewSample);

Another way is to instantiate AppSink and add it to pipeline by yourself

var appSink = new AppSink("sink");
pipeline.Add(appSink);
converter.Link(appSink);

Hi, toxicmouse7
Thanks for the reply. I tried the method you taught me.
I have an additional question.
We are still investigating, but if you know anything about it,
could you please let us know.


“sink.AddSignalHandler(“new-sample”, AppSink_NewSample);”, but it will be
“CS1503 Argument 2: cannot convert from ‘method group’ to ‘Delegate’”

private void AppSink_NewSample(object sender, NewSampleArgs args)
{
var sample = appSink.PullSample();
if (sample == null) return;

using (var buffer = sample.Buffer)
{
    MapInfo map;
    if (buffer.Map(out map, MapFlags.Read))
    {
        try
        {
            var data = map.Data;
            Invoke((MethodInvoker)(() =>
            {
                using (MemoryStream ms = new MemoryStream(data.ToArray()))
                {
                    pictureBox1.Image?.Dispose(); // Dispose old image before setting new one
                    pictureBox1.Image = Image.FromStream(ms);
                }
            }));
        }
        finally
        {
            buffer.Unmap(map);
        }
    }
}

}


「var appSink = new AppSink(“sink”);」 causes an error(↓) at runtime.

GLib-GObject:ERROR:…/gobject/gobject.c:3598:toggle_refs_notify: assertion failed: (tstack.n_toggle_refs == 1)
Bail out! GLib-GObject:ERROR:…/gobject/gobject.c:3598:toggle_refs_notify: assertion failed: (tstack.n_toggle_refs == 1)


Also, I would like to get frames from the received data, and I am aware that PullSample() in AppSink is required for this.
But,
var sink = ElementFactory.Make(“appsink”, “sink”);
appSink = pipeline.GetByName(“sink”) as AppSink;
“appsink” will be null. Then PullSample() is not available.

Which version of GStreamer you use? You should use GStreamer built with MinGW (not the MSVC). That should propably solve you problem with GLib exception

I’m using version 1.24.1.(MSVC).
I believe MinGW(1.24.1) had an error “gstreamer-1.0-0.dll does not exist” at build time(When “Gst.Application.Init()” is executed).
When MinGW is installed, “complete” is selected.

You should add path to bin and lib folder to PATH env variable
image

Thanks for the reply.
I have to add 「lib」 as well.
I had only added 「bin」.
I will give it a try.

I have not yet tried the procedure you gave me, but I have confirmed that AppSink_NewSample is called each time a new sample is retrieved in the following process.

sink = ElementFactory.Make(“appsink”, “sink”);
sink.Connect(“new-sample”, AppSink_NewSample);
sink[“emit-signals”] = true;
sink[“sync”] = false;
※It seems that sink can be left as Element type.

I will continue to try the steps you gave me.
Thanks a lot.