SharpOnvifClient.AdvancedSecurity
0.1.2
dotnet add package SharpOnvifClient.AdvancedSecurity --version 0.1.2
NuGet\Install-Package SharpOnvifClient.AdvancedSecurity -Version 0.1.2
<PackageReference Include="SharpOnvifClient.AdvancedSecurity" Version="0.1.2" />
paket add SharpOnvifClient.AdvancedSecurity --version 0.1.2
#r "nuget: SharpOnvifClient.AdvancedSecurity, 0.1.2"
// Install SharpOnvifClient.AdvancedSecurity as a Cake Addin #addin nuget:?package=SharpOnvifClient.AdvancedSecurity&version=0.1.2 // Install SharpOnvifClient.AdvancedSecurity as a Cake Tool #tool nuget:?package=SharpOnvifClient.AdvancedSecurity&version=0.1.2
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 and NET8.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 OnvifDiscoveryClient.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
). Add the usings:
using SharpOnvifClient;
Create the Onvif client and set the authentication behavior using SetOnvifAuthentication
extension method from SharpOnvifClient.OnvifAuthenticationExtensions
before you use it:
System.Net.NetworkCredential credentials = new System.Net.NetworkCredential(userName, password);
IEndpointBehavior legacyAuth = new WsUsernameTokenBehavior(credentials);
using (var deviceClient = new DeviceClient(
OnvifBindingFactory.CreateBinding(),
new System.ServiceModel.EndpointAddress("http://192.168.1.10/onvif/device_service")))
{
deviceClient.SetOnvifAuthentication(OnvifAuthentication.WsUsernameToken | OnvifAuthentication.HttpDigest, credentials, legacyAuth);
// use the client
}
Call any method on the client, e.g.:
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 | Versions 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. |
-
.NETStandard 2.0
- Microsoft.XmlSerializer.Generator (>= 8.0.0)
- System.Formats.Asn1 (>= 8.0.1)
- System.Security.Cryptography.Pkcs (>= 8.0.1)
- System.ServiceModel.Duplex (>= 4.10.3)
- System.ServiceModel.Http (>= 4.10.3)
- System.ServiceModel.NetTcp (>= 4.10.3)
- System.ServiceModel.Primitives (>= 4.10.3)
- System.ServiceModel.Security (>= 4.10.3)
-
net8.0
- Microsoft.XmlSerializer.Generator (>= 8.0.0)
- System.Formats.Asn1 (>= 8.0.1)
- System.Security.Cryptography.Pkcs (>= 8.0.1)
- System.ServiceModel.Http (>= 8.0.0)
- System.ServiceModel.NetTcp (>= 8.0.0)
- System.ServiceModel.Primitives (>= 8.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.