OnvifLib 1.1.1
dotnet add package OnvifLib --version 1.1.1
NuGet\Install-Package OnvifLib -Version 1.1.1
<PackageReference Include="OnvifLib" Version="1.1.1" />
<PackageVersion Include="OnvifLib" Version="1.1.1" />
<PackageReference Include="OnvifLib" />
paket add OnvifLib --version 1.1.1
#r "nuget: OnvifLib, 1.1.1"
#:package OnvifLib@1.1.1
#addin nuget:?package=OnvifLib&version=1.1.1
#tool nuget:?package=OnvifLib&version=1.1.1
OnvifLib
OnvifLib is a modern and lightweight .NET library for interacting with ONVIF-compliant IP cameras. It provides a simple interface to discover devices, control PTZ, retrieve media streams, and handle events over the ONVIF protocol.
๐ Source code: github.com/treealarm/OnvifLib โ issues and pull requests welcome.
๐ Features
- ๐ Device discovery and information
- ๐ฅ Media profile and RTSP URI retrieval
- ๐น๏ธ PTZ (Pan-Tilt-Zoom) camera control
- ๐ก Event handling (PullPoint or Subscription)
- ๐ง Analytics (ver20): analytics modules and rules, metadata configuration (Profile M)
- ๐๏ธ Profile G: the camera's own recordings โ search, replay and recording jobs
- ๐ Device I/O: relay outputs and digital inputs
- ๐ WS-Security (UsernameToken) support
- โ Targets .NET 10
๐ฆ Installation
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
Two runnable applications live in samples/, both referencing the library directly:
| OnvifLib.Probe | A console harness that walks the whole public API against a camera and prints OK/FAIL/SKIP per call, with a summary and an exit code. Read-only by default; --allow-writes adds writes that undo themselves. Good as a smoke test and as a way to find out what a camera actually supports. |
| OnvifLib.Gui | A cross-platform Avalonia test bench (Windows and Linux) with a tab per service: discovery, device, media and snapshots, PTZ, imaging, events, analytics, Profile G and device I/O, plus a log pane that can capture the full SOAP exchange. |
dotnet run --project samples/OnvifLib.Probe -- --discovery
dotnet run --project samples/OnvifLib.Gui
In VS Code, press F5 and pick a configuration โ the GUI, its self-test, or the probe with prompts for the address and credentials. In Visual Studio, set either sample as the startup project and pick a launch profile.
Note: do not enable trimming or NativeAOT in a consuming application.
System.ServiceModel.*builds its channels, serializers and generated proxies by reflection with no trim annotations, so a trimmed build launches and then fails on the first SOAP call.
| Product | Versions 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. |
-
net10.0
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.11)
- System.Security.Cryptography.Xml (>= 10.0.10)
- System.ServiceModel.Duplex (>= 6.0.0)
- System.ServiceModel.Federation (>= 10.0.652802)
- System.ServiceModel.Http (>= 10.0.652802)
- System.ServiceModel.NetTcp (>= 10.0.652802)
- System.ServiceModel.Primitives (>= 10.0.652802)
- System.ServiceModel.Security (>= 6.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Targets .NET 10. Previous releases targeted .NET 8; consumers on .NET 8 or 9 must stay on 1.0.10. Analytics modules and rules now expose structured parameters as OnvifElementItem (name plus raw XML) instead of a bare XML list, so a Modify can round-trip them without renaming the parameter. Adds two runnable samples: a console probe that exercises the whole public API against a camera, and a cross-platform Avalonia test bench.