MediaToolkitNet.Windows 0.2.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package MediaToolkitNet.Windows --version 0.2.0
                    
NuGet\Install-Package MediaToolkitNet.Windows -Version 0.2.0
                    
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="MediaToolkitNet.Windows" Version="0.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MediaToolkitNet.Windows" Version="0.2.0" />
                    
Directory.Packages.props
<PackageReference Include="MediaToolkitNet.Windows" />
                    
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 MediaToolkitNet.Windows --version 0.2.0
                    
#r "nuget: MediaToolkitNet.Windows, 0.2.0"
                    
#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 MediaToolkitNet.Windows@0.2.0
                    
#: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=MediaToolkitNet.Windows&version=0.2.0
                    
Install as a Cake Addin
#tool nuget:?package=MediaToolkitNet.Windows&version=0.2.0
                    
Install as a Cake Tool

MediaToolkit.NET

Low-level wrappers over the native media stacks for .NET 10, plus one cross-platform API on top of them: device enumeration, camera and microphone capture, audio playback, decoding and recording.

Interop goes through [LibraryImport] and unmanaged function pointers. COM on Windows is called directly through vtable slots, without [ComImport] or the built-in COM marshaller, so the assemblies stay AOT- and trim-safe.

Install

dotnet add package MediaToolkitNet.All

MediaToolkitNet.All pulls in every backend and selects one at runtime. Take a single backend instead when you do not want the rest:

Package Contents
MediaToolkitNet.All Facade: backend registry and per-platform selection
MediaToolkitNet.Abstractions Abstractions only; no native code, no dependencies
MediaToolkitNet.Interop Native loading, UTF-8 marshalling, COM vtable access
MediaToolkitNet.FFmpeg FFmpeg 7.x, 8.x or 9.x: demux, decode, encode, mux
MediaToolkitNet.Mpv libmpv client API
MediaToolkitNet.Windows Media Foundation, WASAPI, DirectShow (devices, graph, Sample Grabber)
MediaToolkitNet.Linux V4L2, ALSA, PulseAudio
MediaToolkitNet.GStreamer GStreamer 1.x pipelines on Linux
MediaToolkitNet.MacOS AVFoundation, CoreAudio

Quick start

using MediaToolkitNet;
using MediaToolkitNet.Abstractions.Capture;
using MediaToolkitNet.Abstractions.Frames;

// Whichever backend this machine actually has.
using var capture = MediaToolkitNetBackends.CreateVideoCapture(VideoCaptureSettings.Default);

capture.FrameCaptured += (in VideoFrame frame) =>
{
    // The frame is borrowed: it is valid only inside this handler.
    // Copy the data out if you need to keep it.
    ReadOnlySpan<byte> luma = frame.GetPlane(0);
};

capture.Start();

List what a machine can do:

foreach (var backend in MediaToolkitNetBackends.Registered)
{
    Console.WriteLine($"{backend.Name}: {backend.IsAvailable}, {backend.Capabilities}");
}

foreach (var device in MediaToolkitNetBackends.EnumerateDevices())
{
    Console.WriteLine($"{device.Kind} {device.Name}");
}

Assemble a processing graph. Each stack is wrapped in its own terms, because the shapes are genuinely different:

// libavfilter: the same chain the ffmpeg command line takes after -vf.
var input = new VideoFormat(1920, 1080, PixelFormat.Bgr24, 30);
using var chain = FFmpegFilterGraph.ForVideo(input, "scale=640:-1,hflip");

chain.Push(sourceFrame);
while (chain.TryReceive((in VideoFrame frame) => Save(frame)))
{
}
// DirectShow: filters and pins, with frames handed back by the Sample Grabber.
using var graph = DirectShowGraph.Create();
var grabber = DirectShowSampleGrabber.AddTo(graph);
grabber.AcceptVideo(PixelFormat.Bgr24);
grabber.VideoGrabbed += (in VideoFrame frame) => Save(frame);
// GStreamer: a whole pipeline from one gst-launch string.
using var pipeline = GStreamerPipeline.Parse(
    "filesrc location=clip.mp4 ! decodebin ! videoconvert ! appsink name=sink");
var sink = pipeline.GetSink("sink");

What each backend provides

Capability Windows Linux macOS FFmpeg mpv
Device enumeration MF + WASAPI + DShow ALSA + V4L2 CoreAudio + AVF avdevice —
Video capture IMFSourceReader V4L2 mmap AVCaptureSession — —
Audio capture WASAPI (+ loopback) ALSA / PulseAudio AudioQueue — —
Audio render WASAPI ALSA / PulseAudio AudioQueue — —
Playback — — AVPlayer decode to frames full
Recording to file IMFSinkWriter — — encode + mux —
Filter graph DirectShow + Sample Grabber GStreamer pipeline — libavfilter vf / af chains

A dash means the backend does not take that role: FFmpeg owns no audio output, mpv never hands frames back, and on Linux and macOS writing files is the FFmpeg backend's job.

Requirements

  • .NET 10, and a 64-bit process: every mapped struct layout assumes a 64-bit pointer.
  • No native binary ships in any package. The platform backends only call libraries that are already part of the operating system, but:
    • MediaToolkitNet.FFmpeg needs FFmpeg 7.x, 8.x or 9.x at runtime. The loader reads the versions it actually got and refuses a mixture belonging to no single series. libavfilter is optional: without it only filter graphs are unavailable;
    • MediaToolkitNet.Mpv needs libmpv at runtime;
    • MediaToolkitNet.GStreamer needs GStreamer 1.x and its plugins, and binds the Linux library names, so it reports itself unavailable elsewhere.

A backend whose libraries are missing reports IsAvailable == false instead of throwing, so probing is always safe.

Status

This is an early release and the public API may still change.

Exercised against real hardware: on Windows, device enumeration, camera capture, WASAPI audio capture, audio recording to AAC/MP4, and the DirectShow graph with the Sample Grabber; the FFmpeg backend on Debian 13 against FFmpeg 7.1.5 and on Windows 11 against FFmpeg 9.0.1, with every struct offset checked against the library actually installed.

Not run yet: libavfilter, the mpv backend, the whole of the GStreamer backend, the macOS backend, and the V4L2, ALSA and PulseAudio paths on Linux. They compile and are guarded at runtime — a layout that cannot be confirmed disables the feature rather than proceeding — but that is not the same as having been run. See the release notes for the full picture before depending on it.

MIT licensed.

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 (1)

Showing the top 1 NuGet packages that depend on MediaToolkitNet.Windows:

Package Downloads
MediaToolkitNet.All

MediaToolkit.NET: one API over the native media stacks. This package pulls in every backend and selects the right one for the current operating system. Install a single backend package instead if you only need one.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.3.1 126 9/26/2026
0.3.0 106 9/26/2026
0.2.0 116 9/19/2026
0.1.2 114 9/17/2026
0.1.1 121 9/17/2026

MediaToolkit.NET 0.2.0 - the filter graph release.

Four native stacks can assemble a processing graph, and all four are now wrapped:
the DirectShow filter graph on Windows, libavfilter through FFmpeg, mpv's vf and
af chains, and GStreamer pipelines on Linux. Each is wrapped in its own terms
rather than behind a common abstraction, because the shapes are genuinely
different and hiding that would cost more than it saves.

The README on this page covers what the library does and what it needs at
runtime. These notes cover only what is specific to this release.

NEW PACKAGE

MediaToolkitNet.GStreamer, for GStreamer 1.x on Linux: a pipeline built from a
gst-launch description, its state, bus and seeking, the element-factory registry,
and video and audio frames taken out of an appsink. It implements no capture or
playback interface of its own, because the pipeline covers those roles in
GStreamer's own terms. MediaToolkitNet.All pulls it in.

ADDED

- DirectShow filter graph (MediaToolkitNet.Windows). Filters, pins, media types,
 intelligent connect, RenderFile, transport control, seeking, bus events and a
 text dump of the graph. A catalogue of thirty built-in filters - the splitters,
 the parsers, the ACM and MPEG decoders, the DVD Navigator, the tees, the
 renderers and the muxers - is addressed by name through the DsFilter enum
 rather than by class id.
- DirectShow Sample Grabber, which is frame access from inside the graph.
 VideoGrabbed and AudioGrabbed deliver borrowed frames on the streaming thread;
 AcceptVideo and AcceptAudio pin the format the graph has to negotiate to, so
 the builder inserts a decoder and a colour converter as needed. Bottom-up DIBs
 arrive with a negative stride, which VideoFrame already understands. A buffered
 snapshot path, SetOneShot with BufferSamples and CopyCurrentBuffer, covers
 grabbing a single frame out of a file.
- libavfilter (MediaToolkitNet.FFmpeg). FFmpegFilterGraph.ForVideo and ForAudio
 take the chain in FFmpeg's own syntax, the same string the command line takes
 after -vf or -af; the buffer source and sink at either end are added for you.
 Frames go in as an AVFrame or as a borrowed VideoFrame or AudioFrame and come
 out borrowed. FFmpegFilters lists what the loaded library was built with, which
 is not a fixed list the way the DirectShow catalogue is.
- mpv filter chains (MediaToolkitNet.Mpv). MpvPlayer.VideoFilters and
 AudioFilters wrap the vf and af properties: Set, Add, Remove, Toggle and Clear,
 plus MpvFilterChain.Lavfi to hand a libavfilter chain to mpv.
- Seven sample commands: ds-filters, ds-graph, ds-grab, av-filters, av-filter,
 gst-elements and gst-run.

CHANGED

- FFmpegGeneration takes an AvFilter major version, as its seventh positional
 parameter. Code that constructs one has to pass it; nothing else moves.
- libavfilter is optional, like libavdevice, and is looked for only in the major
 version belonging to the series that was already loaded. When it is missing,
 FFmpegFilterGraph.IsAvailable is false and nothing else changes.

VERIFIED BY RUNNING (Windows 11)

- All thirty catalogued DirectShow class ids were instantiated. Twenty-eight
 resolve; Line21Decoder and WavDest are not registered on current Windows, which
 is the operating system's doing and not a bad GUID.
- A WAVE file through the Sample Grabber: 23 buffers, 122868 samples at 22050 Hz,
 which is 5.5722 seconds and exactly the duration the graph reported.
- An H.264 file through decoder, colour converter and Sample Grabber to BGR24 at
 1728x1080: thirty frames per second sustained, a negative stride as a bottom-up
 DIB should have, and pixels read end to end of every plane rather than at the
 corners, so a wrong pointer or stride could not pass as a plausible frame count.

NOT EXERCISED YET - PLEASE READ BEFORE DEPENDING ON THIS

- libavfilter has not been run. The development machine has no FFmpeg installed.
 The two struct layouts it needs are found at startup by parsing a graph and
 reading the labels back, and the graph refuses to build when that probe does
 not pass, so a mismatch should produce a clear error rather than corruption -
 but that path is untested too.
- The mpv filter chains have not been run; libmpv was not installed either.
- Nothing in MediaToolkitNet.GStreamer has run at all. It binds the Linux library
 names, and the development machine is Windows. The four things GStreamer
 exposes only as macros - the message type, the buffer timestamp, GstMapInfo and
 the links of a GList - are each found by probing an object built for the
 purpose rather than hard-coded, and a probe that fails disables the backend.
 The parts most worth checking first on real hardware are those probes and the
 floating-reference handling.

Everything 0.1.2 verified still stands: the FFmpeg backend was run on Debian 13
against FFmpeg 7.1.5 and on Windows 11 against FFmpeg 9.0.1.

That is why this is 0.2.0 rather than 1.0. The public API may still change.

KNOWN LIMITATIONS

- The GStreamer backend reports itself unavailable on Windows and macOS even
 where GStreamer is installed, because it binds the Linux library names. Lifting
 that is a matter of the file names and nothing else.
- The Sample Grabber is registered for SampleCB only. BufferCB returns E_NOTIMPL:
 SampleCB carries the sample itself, and therefore its timestamps and any
 mid-stream format change.
- A filter graph is not a player. None of the four renders audio or video on its
 own - they hand frames back, and what happens next is the caller's.
- Media Foundation video recording still needs a video encoder MFT registered on
 the machine.
- MacOSBackend cannot write files, because AVAssetWriter is not wrapped.
- FFmpegPlayer only decodes: no renderer and no audio output.
- Only the mpv client API is wrapped; the render API is not.

Reports from Linux and macOS are especially welcome.

Full notes: https://github.com/yartat/MediaToolkit.NET/releases/tag/v0.2.0
Issues: https://github.com/yartat/MediaToolkit.NET/issues