Skip to content

First public release

Choose a tag to compare

@yartat yartat released this 17 Sep 13:31
· 8 commits to main since this release

MediaToolkit.NET 0.1.1

First usable release: low-level wrappers over the native media stacks for
.NET 10, plus one cross-platform API on top of them.

There is no 0.1.0. Two packages of that version reached nuget.org before
the rest of the set was rejected. nuget.org refuses an id that matches an
existing one once . and - are stripped, so MediaToolkitNet.Core collided
with MediaToolkit.NetCore and the facade MediaToolkitNet collided with
MediaToolkit.NET. They are now MediaToolkitNet.Abstractions and
MediaToolkitNet.All. MediaToolkitNet.Interop 0.1.0 and
MediaToolkitNet.FFmpeg 0.1.0 depend on an id that never shipped, so they are
unlisted and must not be used.

All 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, which keeps the assemblies AOT- and trim-safe and keeps
the exact native signature visible at every call site.

Install

dotnet add package MediaToolkitNet.All

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

dotnet add package MediaToolkitNet.Windows
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: demux, decode, encode, mux
MediaToolkitNet.Mpv libmpv client API
MediaToolkitNet.Windows Media Foundation, WASAPI, DirectShow
MediaToolkitNet.Linux V4L2, ALSA, PulseAudio
MediaToolkitNet.MacOS AVFoundation, CoreAudio

What is in it

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 —

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
  • A 64-bit process. Every mapped struct layout assumes a 64-bit pointer.
  • MediaToolkitNet.FFmpeg needs FFmpeg 7.x present at runtime
    (avutil 59 / avcodec 61 / avformat 61). No native binary ships in any package.
  • MediaToolkitNet.Mpv needs libmpv present at runtime.
  • The platform backends only call libraries that are already part of the
    operating system.

Verified by running

Exercised against real hardware on Windows 11:

  • device enumeration through Media Foundation, WASAPI and DirectShow, including
    default-endpoint flags;
  • video capture from a USB camera, 640x480 NV12 at 30 fps, sustained ~23 fps
    delivered to the frame handler;
  • audio capture through WASAPI at 48 kHz stereo float;
  • recording live audio to AAC/MP4 through IMFSinkWriter, producing a
    structurally complete MP4 — ftyp / mdat / moov, brand mp42 — which
    confirms the writer was finalised rather than merely fed.

The Windows COM vtable slot tables were corrected twice during development by
running them, not by reading them: a wrong slot number compiles, raises no
exception, and silently calls the neighbouring method.

Not exercised yet

Please read this before depending on it.

  • The Linux and macOS backends have never been run. They compile and are
    guarded at runtime, but the V4L2 struct sizes and the Objective-C runtime
    layout have not been confirmed on real hardware.
  • The FFmpeg and mpv backends have never been run, because those native
    libraries were not installed on the development machine. The FFmpeg ABI
    self-check refuses to proceed on a layout it cannot confirm, so a mismatch
    should produce a clear error rather than memory corruption, but that path is
    untested too.
  • Media Foundation video recording is untested: the development machine has
    no video encoder MFT registered at all. MediaFoundationRecorder detects that
    case and reports it plainly. Audio recording on that path does work.

This is why the release is 0.1.1. The public API may still change.

Known limitations

  • Media Foundation video recording needs a video encoder MFT registered on the
    machine. Editions such as Windows N without the Media Feature Pack have none;
    the encoders sample command reports what a machine actually has.
  • MacOSBackend cannot write files — AVAssetWriter is not wrapped. Use FFmpeg.
  • FFmpegPlayer only decodes: no renderer, no audio output.
  • Only the mpv client API is wrapped; the render API is not.
  • DirectShow is wrapped for device enumeration only; no filter graph.

Design notes

Frames are borrowed. VideoFrame and AudioFrame are ref structs over
memory the backend owns, valid only inside the handler that received them. The
compiler refuses to let you store one. Zero copies and zero allocations per
frame; copy the data out if you need to keep it.

FFmpeg is pinned to 7.x, and checks itself. FFmpeg keeps no stable ABI for
its public structs across majors, so every direct field access lives in a single
file, the loader gates on the major versions, and the layout is validated at
startup against documented post-allocation defaults. The AVCodecParameters
offsets and the AVFrame audio fields are discovered by runtime probing rather
than hard-coded, and codecs are resolved by name instead of by the unstable
AVCodecID values.

Trying it

The sample CLI covers every capability and is how the library was validated:

dotnet run --project samples/MediaToolkitNet.Sample.Cli -- backends
dotnet run --project samples/MediaToolkitNet.Sample.Cli -- devices
dotnet run --project samples/MediaToolkitNet.Sample.Cli -- rec-video 2
dotnet run --project samples/MediaToolkitNet.Sample.Cli -- rec-audio-file 3 out.m4a

Reports from Linux and macOS are especially welcome.

Full documentation: README