OnvifLib
OnvifLib is a modern .NET 10 library for ONVIF IP cameras — discovery, PTZ, media URIs, events, analytics, Profile G, and Device I/O.
OnvifLib.Gui is the desktop companion: a cross-platform ONVIF Device Manager alternative for Windows and Linux, with live H.264 / HEVC playback (bundled ffmpeg), multi-camera tabs, and a full service test bench.
Download GUI
·
GUI docs
·
NuGet
Why OnvifLib.Gui
Classic ONVIF Device Manager is Windows-only and often stalls on modern codecs. This stack is different:
| ODM | OnvifLib.Gui |
|---|
| Platforms | Windows | Windows + Linux |
| Live video | Limited (HEVC often missing) | H.264 / HEVC via ffmpeg |
| Install | Aging installer | WiX MSI + portable zip |
| Profiles | Weak G/M on many builds | Profile G archive + Profile M / analytics |
| Stack | Legacy player era | .NET 10 + Avalonia |
Windows: prefer the MSI from Releases (Program Files, no Mark-of-the-Web). Linux: zip. After winget merges: winget install treealarm.OnvifLib.Gui.
Library features
- Device discovery (WS-Discovery) and device information
- Media profiles and RTSP / snapshot URIs
- PTZ continuous, relative, absolute, presets
- Events (PullPoint / subscription)
- Analytics (ver20) and metadata configuration (Profile M)
- Profile G: search, replay, recording jobs
- Device I/O: relays and digital inputs
- WS-Security UsernameToken
- Target: .NET 10
Install (library)
dotnet add package OnvifLib
Quick start
using OnvifLib;
// Create returns immediately and discovers the camera's services in the background.
var camera = Camera.Create("192.168.1.64", 80, "admin", "secret");
await camera.InitTask;
// A null service list means unreachable or unauthorized — Create itself never throws.
if (await camera.GetServicesAsync() is null)
throw new InvalidOperationException("could not reach the camera");
var info = await camera.GetDeviceInformationAsync();
Console.WriteLine($"{info.Manufacturer} {info.Model}, firmware {info.FirmwareVersion}");
// Every Get*Service() returns null when the camera does not offer that service.
if (await camera.GetMediaService() is { } media)
{
foreach (var profile in media.GetProfiles())
{
var uri = await media.GetStreamUri(profile.Token);
Console.WriteLine($"{profile.Name} {profile.Width}x{profile.Height} {profile.Encoding} → {uri}");
}
// Snapshot bytes, or null (the failure is logged rather than thrown).
if (await media.GetImage() is { } image)
await File.WriteAllBytesAsync("snapshot.jpg", image.Data);
}
if (await camera.GetPtzService() is { } ptz)
{
var profile = (await camera.GetProfiles())!.First();
await ptz.ContinuousMoveAsync(profile.Token, panTiltX: 0.5f, panTiltY: 0f);
await Task.Delay(500);
await ptz.StopAsync(profile.Token);
}
Stream URIs come back exactly as the camera reported them, which is usually without
credentials — a player needs user:password spliced in, percent-encoded.
To find cameras rather than address one directly:
var result = await WsDiscovery.ProbeAsync(TimeSpan.FromSeconds(4), CancellationToken.None);
// ScanOk distinguishes "scanned, found nothing" from "could not scan at all".
if (!result.ScanOk)
Console.WriteLine("no interface could join the multicast group — check the firewall");
foreach (var device in result.Devices)
Console.WriteLine($"{device.Name} at {device.Ip}:{device.Port}");
Samples
| |
|---|
| OnvifLib.Probe | Console harness over the public API — OK/FAIL/SKIP, exit code, optional --allow-writes. |
| OnvifLib.Gui | Desktop ODM alternative: discovery, live video, PTZ, imaging, events, analytics, Profile G, Device I/O, SOAP log. Releases · WiX setup. |
dotnet run --project samples/OnvifLib.Probe -- --discovery
dotnet run --project samples/OnvifLib.Gui
In VS Code, press F5 and pick a configuration. In Visual Studio, set either sample as startup.
> Note: do not enable trimming or NativeAOT in a consuming application.
> System.ServiceModel.* builds channels, serializers and proxies by reflection with no trim
> annotations — a trimmed build launches and then fails on the first SOAP call.