GstSharp.Net.App 1.28.2

There is a newer version of this package available.
See the version list below for details.
dotnet add package GstSharp.Net.App --version 1.28.2
                    
NuGet\Install-Package GstSharp.Net.App -Version 1.28.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="GstSharp.Net.App" Version="1.28.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="GstSharp.Net.App" Version="1.28.2" />
                    
Directory.Packages.props
<PackageReference Include="GstSharp.Net.App" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add GstSharp.Net.App --version 1.28.2
                    
#r "nuget: GstSharp.Net.App, 1.28.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package GstSharp.Net.App@1.28.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=GstSharp.Net.App&version=1.28.2
                    
Install as a Cake Addin
#tool nuget:?package=GstSharp.Net.App&version=1.28.2
                    
Install as a Cake Tool

GstSharp.Net

NuGet CI License

Modern .NET bindings for GStreamer 1.28, designed for NativeAOT from the start.

  • .NET 10, IsAotCompatible=true on every shipping assembly.
  • [LibraryImport] everywhere: interop stubs are produced at build time by the runtime source generator, not by reflection or IL emit.
  • Zero reflection on the runtime path. Native types are mapped through a generated registry of function pointers, so trimming and AOT compilation keep the whole surface intact.
  • Generated from .gir, by a generator that lives in this repository (generator/GstSharp.Generator). The generated C# is committed, so consumers never need Python, gapi, or xsltproc.
  • Cross platform: Windows (both the MSVC and the MinGW flavor of the official GStreamer builds, plus MSYS2), macOS and Linux. The native library is located at startup by NativeLoader; no dllmap, no environment variables required.

Generated module set: Gst, GstBase, GstApp, GstVideo, GstAudio, GstPbutils, GstSdp, GstWebRTC, GstNet, GstRtsp, GES. A twelfth module, GstController, is written by hand against the public module SPI and ships alongside them.

Packages

One version for the whole set, and every identifier starts with GstSharp.Net, so a single packageSourceMapping pattern covers all of them.

Package Contents
GstSharp.Net Gst core, the hand-written runtime (native loader, marshalling, GObject/GLib layer) and the Roslyn analyzers. Every other package depends on it.
GstSharp.Net.Base GstBase.
GstSharp.Net.Controller GstController: the interpolation, LFO and trigger control sources, and the direct, ARGB and proxy control bindings that drive a property from one. Hand written against the public module SPI — see docs/modules.md.
GstSharp.Net.App GstApp: appsrc and appsink.
GstSharp.Net.Video GstVideo.
GstSharp.Net.Audio GstAudio.
GstSharp.Net.Pbutils GstPbutils.
GstSharp.Net.Sdp GstSdp: SDP session descriptions and MIKEY key management.
GstSharp.Net.WebRTC GstWebRTC: session descriptions, ICE, transports and data channels for webrtcbin.
GstSharp.Net.Net GstNet: network clocks and time providers.
GstSharp.Net.Rtsp GstRtsp: RTSP connections, messages, URLs and transports.
GstSharp.Net.GES GES: the editing services — timelines, layers, clips and the assets behind them. Initialise through GES.GstGES.Initialize(), which runs ges_init on top of the usual startup.

The analyzers ship inside GstSharp.Net rather than as a package of their own: they cannot get out of step with the binding that way, and no second package reports the same diagnostic twice. They are GST0001 (a wrapper that owns a reference and never releases it) and GST0002 (a buffer mapping that is never released); see docs/analyzers.md. They travel along the package dependency, so a project that references only a module package — GstSharp.Net.Sdp, say — gets them too. Every module clears PrivateAssets on its reference to GstSharp.Net to say so, because the default would pack a dependency that asks for the analyzer assets to be left behind.

The packages target plain net10.0 and carry managed code only. GStreamer itself is not bundled: install it separately and let NativeLoader find it.

Installation

dotnet add package GstSharp.Net
dotnet add package GstSharp.Net.App     # and one per module you use

Where the packages come from

The set is published to nuget.org. The .nupkg files are also attached to every GitHub release for offline use. The copies on GitHub Packages exist for the project's own release plumbing; that feed requires authentication and is not the intended way to consume the bindings.

The GstSharp.Net* prefix is why every package identifier shares it: in a solution that pins feeds with packageSourceMapping, one pattern covers the whole set.

The native GStreamer

The packages contain managed code only. Install GStreamer itself:

Platform How
Windows The official runtime installer of one flavor; msvc and mingw both work. The development installer is not needed — the binding carries its own interop and never compiles against the headers.
Windows (MSYS2) pacman -S mingw-w64-x86_64-gstreamer mingw-w64-x86_64-gst-plugins-base mingw-w64-x86_64-gst-plugins-good
Linux (Debian/Ubuntu) apt install libgstreamer1.0-0 libgstreamer-plugins-base1.0-0 gstreamer1.0-plugins-base gstreamer1.0-plugins-good, plus libgstreamer-plugins-bad1.0-0 for GstWebRTC and libges-1.0-0 for GES
macOS brew install gstreamer

Supported versions. The surface is generated from the 1.28.6 .gir files. The floor at run time is GStreamer 1.24: the struct layouts the ABI probes mirror have been stable since then. An entry point that GStreamer added after 1.24 is present in the managed surface and throws EntryPointNotFoundException against an older library — the missing export is the documented behavior, not a bug. CI runs the whole suite against four installations: Ubuntu 24.04 (1.24, the floor), the official Windows MSVC build (1.28.6), MSYS2's MinGW build, and Homebrew on macOS.

Getting started

using Gst;
using Gst.GLib;

GstSharp.Initialize();

// gst_parse_launch returns a GstElement; the cast goes through the type
// registry, which is what turns it into a Pipeline wrapper.
if (Global.ParseLaunch("playbin uri=file:///path/to/movie.mkv") is not Pipeline pipeline)
{
    Console.Error.WriteLine("that description is not a pipeline.");
    return 1;
}

// A pipeline this code built and stops is the one GObject wrapper a consumer
// disposes; see docs/ownership.md.
using (pipeline)
{
    // The bus wrapper is interned and shared with every other lookup of the
    // same bus, so it is not disposed here.
    Bus bus = pipeline.GetBus();

    pipeline.SetState(State.Playing);

    // A Message is a mini object: the wrapper owns a reference and has to be
    // released. GST0001 reports it when it is not.
    using Message? message = bus.TimedPopFiltered(
        ClockTime.None,
        MessageType.Eos | MessageType.Error);

    if (message?.Type == MessageType.Error)
    {
        (GException error, string? debug) = message.ParseError();
        Console.Error.WriteLine($"{message.SourceName}: {error.Message}");
        Console.Error.WriteLine($"debug: {debug}");
    }

    // Back to NULL before the pipeline is released: one that is still PLAYING
    // when its last reference goes away leaves its streaming threads running.
    pipeline.SetState(State.Null);
}

return 0;

GstSharp.Initialize() loads the native libraries, runs gst_init and fills the type registry. A module that is only ever named — in a cast, in a type test — never runs its own initialiser, so reach for the module entry point when that is all an application does with it: Gst.App.GstApp.Initialize(), Gst.Base.GstBase.Initialize(), and one next to every other module. The GType registry section explains the failure this avoids.

Ownership and lifetime

Read docs/ownership.md before writing anything that runs for longer than a second. The short version:

  • MiniObject and Boxed wrappers (Buffer, Caps, Sample, Message, Structure, ...) own a reference and must be disposed. GST0001 enforces it.
  • GObject wrappers (Element, Pipeline, Bus, Pad, ...) are interned and shared: normally do not dispose them. Disposal acts for every holder at once. The sanctioned exception is a pipeline this code created, after SetState(State.Null).
  • A few calls consume their argument (AppSrc.PushBuffer, Element.SendEvent, BufferPool.SetConfig, WebRTCSessionDescription.New, ...). Dispose is idempotent, so a using around the argument stays correct.
  • An application with no main loop should call GstSharp.DrainPendingReleases() periodically — once per poll of the bus is the natural place.

When the loader cannot find GStreamer

NativeLoader probes the registry, the documented environment variables, the known installation directories, PATH, and an application-bundled runtimes/<rid> tree. When none of that wins, say where to look:

GstSharp.Initialize(new GstSharpOptions
{
    NativeSearchPath = @"C:\gstreamer\1.0\msvc_x86_64\bin",
    WindowsFlavor = GstFlavor.Msvc,   // or GstFlavor.MinGW
});

And to find out what it did pick, which is the first thing to log in an application that ships to machines you do not own:

Console.WriteLine(NativeLoader.ResolvedDirectory);          // the directory that won
Console.WriteLine(NativeLoader.ResolvedFlavor);             // Msvc / MinGW, Windows only
Console.WriteLine(NativeLoader.ResolvedOrigin);             // which probe stage found it
Console.WriteLine(NativeLoader.ResolvedSourceDescription);  // that stage, in words
Console.WriteLine(NativeLoader.GetLoadedModulePath("Gst")); // the file actually mapped

One flavor and one directory are pinned for every module, so a process can never end up with half an MSVC and half a MinGW GStreamer.

Samples

Sample What it shows Run it
samples/PlaybinPlayer A pipeline from a description, driven by a polled bus. No main loop, no signal handler. dotnet run --project samples/PlaybinPlayer
samples/AppSinkSpans Raw video out of an appsink, read through a Span<byte> over the mapped GStreamer memory. Pull mode and signal mode produce the same checksum. dotnet run --project samples/AppSinkSpans -- --mode pull
samples/GstLaunch A port of gst-launch-1.0: the whole bus loop, the preroll/buffering/progress state machine, -t -c -v -q -m -e -X -f, --gst-* passthrough and the exit codes of the C tool. One binary with per-OS behavior — Ctrl+C through a GstLaunchInterrupt application message everywhere, SIGHUP and SIGQUIT on POSIX, the multimedia timer on Windows. Its header comment lists what it cannot match. dotnet run --project samples/GstLaunch -- videotestsrc num-buffers=100 ! fakesink
samples/GstTypefind A port of gst-typefind-1.0: filesrc ! typefind ! fakesink per file, PAUSED and a blocking GetState, directory recursion, and the <file> - <caps> line of the C tool. It is the sample that connects a signal by namehave-type on a plugin element no .gir describes — and its header comment records what that emission can and cannot hand over. dotnet run --project samples/GstTypefind -- <file-or-directory>
samples/GstDeviceMonitor A port of gst-device-monitor-1.0: DeviceMonitor with the DEVICE_CLASSES[:FILTER_CAPS] filters, the device listing with caps and properties, and --follow for hotplug — all of it as messages on the monitor's bus, polled rather than watched from a main loop. Its header comment lists the shell-quoting and property-enumeration parts of the C tool that the binding cannot reach yet. dotnet run --project samples/GstDeviceMonitor
samples/GstDiscoverer A port of gst-discoverer-1.0, synchronous path: DiscoverUri per URI, the result and duration, the topology walk with its container recursion, the per-stream blocks for audio, video and subtitles, --verbose tags and --toc. Its output is byte for byte the C tool's on generated media; its header comment says why -a is absent and what a failed discovery cannot report. dotnet run --project samples/GstDiscoverer -- <file-or-uri>
samples/GstInspect A partial port of gst-inspect-1.0: the registry census, and the element page as far as the bound surface reaches — factory and plugin details, the type hierarchy, pad templates with their caps, URI handling and the property listing. Every page ends with a note naming the sections it does not print, and its header says what each of them would need. dotnet run --project samples/GstInspect -- fakesink
samples/AotSmoke The NativeAOT gate: initialise, make an element, release it, with zero trimming warnings. dotnet publish samples/AotSmoke -r win-x64 -c Release /p:PublishAot=true

PlaybinPlayer and AppSinkSpans also take --native-path <directory>, --flavor msvc\|mingw and --timeout <seconds>; GstLaunch, GstTypefind, GstDeviceMonitor, GstDiscoverer and GstInspect take the first two. Each of the five ports adds one option of its own that the C tool does not have, so that a path which normally needs a console signal or a person can be run unattended, or so that what the port cannot do stays visible: GstLaunch --interrupt-after <seconds> drives its Ctrl+C path, GstDeviceMonitor --follow-for <seconds> bounds a hotplug run, GstTypefind --fail-on-unknown turns a file whose type was not found into a non-zero exit code, GstDiscoverer --fail-on-error does the same for a URI that could not be discovered, and GstInspect --no-coverage-note takes the closing note off a page that is being diffed against the C tool's.

The official tutorials

samples/tutorials/ holds the GStreamer basic tutorials ported onto this binding, one runnable project per tutorial, with the upstream numbering kept:

Project Upstream page What it teaches
BasicTutorial01 Hello world ParseLaunch, the states, the bus
BasicTutorial02 GStreamer concepts factories, a pipeline, a link, a property, a parsed error
BasicTutorial03 Dynamic pipelines pad-added, linking pads, reading caps
BasicTutorial04 Time management position, duration, the seeking query, SeekSimple
BasicTutorial06 Media formats and pad capabilities caps, structures, fields, pad templates
BasicTutorial08 Short-cutting the pipeline appsrc, appsink, a tee and its request pads
BasicTutorial13 Playback speed seek events with a rate, reverse playback, step events

The walkthrough text stays upstream; each file carries a header comment saying where the port differs from the C original and why — a polled bus instead of a GMainLoop, using instead of gst_*_unref, a typed event instead of g_signal_connect. samples/tutorials/README.md is the index and explains the two options the tutorials do not have (--headless and BasicTutorial13 --keys), which exist so that a tutorial can be run unattended.

dotnet run --project samples/tutorials/BasicTutorial02
dotnet run --project samples/tutorials/BasicTutorial03 -- <file-or-uri>

Properties and signals without a generated binding

Not everything is in the .gir files. Plugins add properties and signals that introspection never sees, and GStreamer publishes a good deal of its functionality as action signals, which are calls dressed up as signals. The by-name surface on Gst.GObject.Object reaches all of it, without reflection:

using Gst.GObject;

// A property no binding exposes, read and written by name.
using Value swapchain = sink.GetProperty("swapchain-handle");
nint handle = swapchain.GetPointer();

using Value sync = Value.CreateFor(false, GType.Boolean);
sink.SetProperty("sync", sync);

// An action signal is a call dressed up as a signal.
sink.EmitSignal("resize", 1920, 1080);

// A signal the .gir never mentioned, connected and disconnected by name.
ulong id = playbin.ConnectSignal("about-to-finish", (sender, args) => null);
playbin.RemoveHandler(id);

Arguments are validated against the signature the object declares before anything is emitted, and a mismatch names the expectation.

What the generator did not bind, and why, is listed per module and per reason in girs/skip-report.md. The gaps worth naming here:

  • Subclassing is limited to a closed set of base classes. A C# type can derive from Gst.Element, Gst.Bin, Gst.Base.BaseSrc, Gst.Base.PushSrc, Gst.Base.BaseSink or Gst.Base.BaseTransform, override a curated set of vfuncs and be called back through the native vtable. What is not there yet: the rest of the vfuncs, properties and signals on managed types, and construction from native code — an element registered this way cannot be built by gst_element_factory_make or named in a pipeline description. See docs/subclassing.md.
  • Writing GValue-typed structures is incomplete. Reading is covered — Value.GetBoxed<T>() for a boxed value and Value.GetMiniObject<T>() for a caps, a tag list or a sample — and building a GValue for every fundamental type is in progress. A boxed value that is not a mini object still comes back from EmitSignal as an opaque handle rather than as a typed wrapper.
  • scope="async" callbacks are not generated. The Gio asynchronous pattern is exposed as Task-returning methods instead, hand written per operation; see docs/gio-async.md.
  • The byte and bit cursors are out of scope. GstByteReader, GstByteWriter, GstBitReader and GstBitWriter walk a block of memory the caller already owns, and .NET has that surface built in: Span<byte>, BinaryPrimitives (every width and endianness as a single inlined instruction), ArrayBufferWriter<byte> for the growing writer. The memory is already reached as a span — Gst.Buffer.Map and Gst.Base.Adapter.Map hand one out, Gst.Buffer.NewMemdup takes one back — and the masked 32 bit start code scan is bound on Gst.Base.Adapter.MaskedScanUint32. The C cursors add a bit level position with fixed width reads only — no Exp-Golomb, nothing codec aware. The 148 dropped methods are listed under GstBase in girs/skip-report.md. This is a decision, not a gap, and it does not change within 1.28.x.

Namespaces

The C# namespace of a module is its gir namespace under Gst: GstBase becomes Gst.Base, GstApp becomes Gst.App, GstWebRTC becomes Gst.WebRTC, and so on. GES is the exception: its gir namespace is already top level, so its C# namespace is GES, mirroring the gir rather than inventing a prefix the C library does not have.

Gst.GLib, Gst.GObject and Gst.Interop are the hand-written runtime, not generated from the GLib girs. Gst.Gio is a deliberately small hand-written subset of Gio — cancellables, sockets, TLS — covering what the GStreamer surface hands out and nothing else.

Status and versioning

Stable. From 1.28.1 on the public surface only grows: new members appear as the binding closes gaps, existing signatures stay. A behavioral bug — ownership, lifetime, a wrong contract — is fixed in a patch release and called out in the release notes. A change that would break compilation waits for the next GStreamer series.

1.28.1 itself is the one deliberate exception, taken in the first week of the series while nothing depended on the surface: it re-projected the types a value copy could not represent — the buffer metas, the static caps and pad templates — and repaired the shipped members that discarded or overran what the native call wrote. That window is closed. The compatibility promise above counts from 1.28.1, and 1.28.0 is unlisted for it.

The version is <gstreamer-major>.<gstreamer-minor>.<binding-patch>:

  • major and minor track the GStreamer series the bindings are generated from. 1.28.x of this package is generated from GStreamer 1.28, and a move to GStreamer 1.30 makes the package 1.30.0.
  • patch is the binding's own counter. It has nothing to do with the GStreamer patch release; 1.28.3 does not imply GStreamer 1.28.3, and the runtime floor stays 1.24 regardless.

Extending the binding

A hand-written binding module can live outside this repository. An assembly that references the GstSharp.Net package can register its own native library with the loader, hand its GType-to-wrapper table to TypeRegistry, and derive its wrappers through the protected constructors of Gst.GObject.Object, Gst.GObject.InitiallyUnowned, Gst.MiniObject and Gst.GObject.Boxed — with no InternalsVisibleTo from here. GstSharp.Net.Controller is that module written out: it binds all of libgstcontroller-1.0, it ships, and nothing grants it the internals of anything.

It attaches to the generated hierarchy too. Every generated wrapper class — Gst.Element, Gst.Object, Gst.ControlSource and the rest — carries the same protected constructor, so a module's classes derive from the wrapper of the nearest native ancestor and are shaped like the C types they stand for. That means the members of the generated ancestors are inherited and that generated API taking one of them takes your wrapper: Gst.Controller.InterpolationControlSource really is a Gst.ControlSource, and GES.TrackElement.SetControlSource accepts it, across three assemblies and still with no grant of internals anywhere.

Generator-backed modules are not supported. The generator is a tool of this repository, not a product, and the code it emits uses internals; a module is written by hand.

docs/modules.md is the guide: the three registration calls, the obligations that come with each wrapper base, how to attach to the generated hierarchy and what is still closed — everything about a generated class except its constructor — and the worked example, file by file.

Documentation

Page Contents
docs/ownership.md Who owns a wrapper, who disposes it, and the GType registry. Start here.
docs/analyzers.md GST0001 and GST0002, what they catch and how to satisfy them.
docs/gio-async.md How Gio's *_async / *_finish pairs become Task-returning methods.
docs/subclassing.md Deriving from Element, Bin and the GstBase classes in C#: the guide is §11, the design is the rest.
docs/modules.md Writing a binding module for a library this repository does not cover, from your own assembly.
girs/skip-report.md Every gir symbol the generator did not bind, grouped by reason.
eng/ci-notes.md Why the workflows look the way they do, and how to run each gate by hand.
CONTRIBUTING.md Build, test, regenerate, and the quality gates a change has to pass.

Repository layout

Path Contents
girs/ Vendored .gir inputs and overlay files. See girs/README.md.
generator/ The .gir to C# generator (console application, no NuGet dependencies).
src/ Shipping libraries: the bindings, the hand-written runtime under src/GstSharp.Net/Core/, and the Roslyn analyzers.
samples/ Runnable samples, including the NativeAOT smoke test and, under samples/tutorials/, the official GStreamer tutorials ported onto the binding.
tests/ Generator unit tests, analyzer tests, and integration tests that need a native GStreamer.

Building

dotnet build
dotnet test

A native GStreamer installation is only needed for the integration tests and the samples. See CONTRIBUTING.md for the generator commands and the quality gates.

License

LGPL-2.1-or-later. See LICENSE.

The bindings are generated from GStreamer's .gir files and embed their documentation text, which is LGPL licensed; the same license therefore applies to the generated sources.

What that means in practice — not legal advice, and not a substitute for reading the license or asking a lawyer:

  • GStreamer is loaded dynamically, through [LibraryImport] against the shared libraries an installation provides. Nothing in these packages links GStreamer statically, including under NativeAOT: an AOT-published application is one native executable of managed origin that still resolves libgstreamer-1.0 at run time.
  • A closed-source application may use these packages. The LGPL condition that matters is that the user can replace the LGPL parts, which dynamic loading satisfies: point the application at a different GStreamer installation and it uses that one.
  • Changes to GstSharp.Net itself are LGPL and belong back here, because the generated sources carry GStreamer's own documentation text.
  • The GStreamer installation is a separate question. Its plugins carry their own licenses — several of the "ugly" and "bad" sets are GPL or carry patent conditions — and shipping a runtime alongside an application means auditing what is in it.
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.28.10 42 9/12/2026
1.28.9 127 9/7/2026
1.28.8 114 9/6/2026
1.28.7 105 9/5/2026
1.28.6 144 9/3/2026
1.28.5 111 9/2/2026
1.28.4 151 8/31/2026
1.28.3 124 8/23/2026
1.28.2 139 8/18/2026
1.28.1 150 8/18/2026