SharpOnvifClient.PTZ 0.0.6

dotnet add package SharpOnvifClient.PTZ --version 0.0.6                
NuGet\Install-Package SharpOnvifClient.PTZ -Version 0.0.6                
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="SharpOnvifClient.PTZ" Version="0.0.6" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add SharpOnvifClient.PTZ --version 0.0.6                
#r "nuget: SharpOnvifClient.PTZ, 0.0.6"                
#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.
// Install SharpOnvifClient.PTZ as a Cake Addin
#addin nuget:?package=SharpOnvifClient.PTZ&version=0.0.6

// Install SharpOnvifClient.PTZ as a Cake Tool
#tool nuget:?package=SharpOnvifClient.PTZ&version=0.0.6                

SharpOnvif

A C# implementation of the ONVIF interface - client as well as the server. All profiles are supported.

SharpOnvifServer

ONVIF server provides NET8 CoreWCF bindings generated using svcutil.exe. It makes it easy to implement only parts of the ONVIF specification needed for your project.

Start with creating a new CoreWCF service:

var builder = WebApplication.CreateBuilder();
builder.Services.AddServiceModelServices();
builder.Services.AddServiceModelMetadata();
builder.Services.AddSingleton<IServiceBehavior, UseRequestHeadersForMetadataAddressBehavior>();

Add Digest authentication for ONVIF:

builder.Services.AddSingleton<IUserRepository, UserRepository>();
builder.Services.AddOnvifDigestAuthentication();

Implement IUserRepository to provide user verification and configure your user:

public class UserRepository : IUserRepository
{
    public string UserName { get; set; } = "admin";
    public string Password { get; set; } = "password";

    public Task<UserInfo> GetUser(string userName)
    {
        if (string.Compare(userName, UserName, true) == 0)
        {
            return Task.FromResult(new UserInfo() { UserName = userName, Password = Password });
        }

        return Task.FromResult((UserInfo)null);
    }
}

Optionally, add ONVIF discovery to make your service discoverable on the network:

builder.Services.AddOnvifDiscovery();

Simple DeviceImpl just extends SharpOnvifServer.DeviceMgmt.DeviceBase and overrides a method you want to implement - for instance GetDeviceInformation:

public class DeviceImpl : DeviceBase
{
    public override GetDeviceInformationResponse GetDeviceInformation(GetDeviceInformationRequest request)
    {
        return new GetDeviceInformationResponse()
        {
            FirmwareVersion = "1.0",
            HardwareId = "1.0",
            Manufacturer = "Manufacturer",
            Model = "1",
            SerialNumber = "1"
        };
    }
}

Add authentication:

var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();

And make sure to call app.UseOnvif() to handle SOAP requests with action in the SOAP message instead of the Content-Type header:

app.UseOnvif();

Add the CoreWCF service endpoint:

((IApplicationBuilder)app).UseServiceModel(serviceBuilder =>
{
    var serviceMetadataBehavior = app.Services.GetRequiredService<ServiceMetadataBehavior>();
    serviceMetadataBehavior.HttpGetEnabled = true;

    serviceBuilder.AddService<DeviceImpl>();
    serviceBuilder.AddServiceEndpoint<DeviceImpl, SharpOnvifServer.DeviceMgmt.Device>(OnvifBindingFactory.CreateBinding(), "/onvif/device_service");
});

Finally call app.Run():

app.Run();

Your ONVIF service should now be discoverable on the network and you should be able to use ONVIF Device Manager or similar tool to call your endpoint.

SharpOnvifClient

ONVIF client provides netstandard2.0 WCF bindings generated using dotnet-svcutil. SimpleOnvifClient wraps common API calls to get basic information from the camera and includes both Pull Point as well as Basic event subscriptions.

To discover ONVIF devices on your network, use:

string[] onvifDeviceUris = await DiscoveryClient.DiscoverAsync();

To create the SimpleOnvifClient, use:

var client = new SimpleOnvifClient(onvifDeviceUri, "admin", "password");

Call GetDeviceInformationAsync to retrieve information about the device:

var deviceInfo = await client.GetDeviceInformationAsync();

Call GetServicesAsync to retrieve a list of all services supported by the device:

var services = await client.GetServicesAsync();

Some operations require the device to support a service. For instance, to retrieve the stream URI the device must support the media service. To check whether the ONVIF service is supported by the device, call:

if (services.Service.FirstOrDefault(x => x.Namespace == OnvifServices.MEDIA) != null)
{
    // operation only available when the service is supported
}

Full list of services that can be supported by the device is available in SharpOnvifCommon.OnvifServices.

Pull Point event subscription

Pull point event subscription does not require any special networking configuration and it should work in most networks. To create a new Pull Point subscription, call:

var subscription = await client.PullPointSubscribeAsync();

To retrive the current notifications from the Pull Point subscription, call:

var notifications = await client.PullPointPullMessagesAsync(subscription);
foreach (var notification in notifications)
{
    // handle the notification message
    bool? isMotion = SharpOnvifClient.OnvifEvents.IsMotionDetected(notification);
}

Basic event subscription

Basic event subscription utilizes a callback from the camera when an event occurs. This requires the camera to be able to reach your machine through a firewall/NAT. To listen for incoming notifications, you must run SimpleOnvifEventListener:

// ID 1 will identify this camera in the callback
const int CAMERA1 = 1;

var eventListener = new SimpleOnvifEventListener();
eventListener.Start((int cameraID, string ev) =>
{
    bool? isTamper = SharpOnvifClient.OnvifEvents.IsTamperDetected(notification);
    if(cameraID == CAMERA1)
    {
        // handle the notification message for CAMERA1
    }
});

var subscriptionResponse = await client.BasicSubscribeAsync(eventListener.GetOnvifEventListenerUri(CAMERA1));

Using the generated WCF clients

First add a reference to the DLL that implements the clients (e.g. SharpOnvifClient.DeviceMgmt). Create the authentication behavior:

var auth = new DigestBehavior("admin", "password");

Create the ONVIF client and set the authentication behavior before you use it:

 using (var deviceClient = new DeviceClient(
     OnvifBindingFactory.CreateBinding(),
     new System.ServiceModel.EndpointAddress("http://192.168.1.10/onvif/device_service")))
 {
     deviceClient.Endpoint.EndpointBehaviors.Add(auth);
     
     // use the client
 }

Call any method on the client like:

var deviceInfo = await deviceClient.GetDeviceInformationAsync(new GetDeviceInformationRequest());

Testing

Only the DeviceMgmt, Media and Events were tested with Hikvision cameras. Server implementation was tested using ONVIF Device Manager.

Credits

Special thanks to Piotr Stapp for figuring out the SOAP security headers in NET8: https://stapp.space/using-soap-security-in-dotnet-core/.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos 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
0.0.6 75 11/30/2024
0.0.5 87 11/25/2024
0.0.4 108 11/10/2024
0.0.3 132 9/29/2024
0.0.2 150 9/5/2024
0.0.1 151 5/27/2024