[英]Onvif Event Subscription in C#
我正在用 C# 實現一個 ipCamera/編碼器管理系統。 該系統將管理來自多個供應商的多個 ipCameras 和/或編碼器。 使用 Onvif 而不是關閉每個 ipcamera 或編碼器 sdk 將是一個好處。
管理系統的關鍵概念之一是偵聽來自攝像機的事件,例如運動檢測事件。 Onvif 通過使用 ws-basenotification 或拉式支持來支持這一點。 我不喜歡拉,所以我將在 Onvif(Onvif 規范 9.1)中使用 ws-basenotification 支持。
我已成功訂閱 Sony SNC-RH164、Bosh VIP X1 XF IVA 和 Acti TCD2100。
我的問題是:我沒有從任何設備收到任何通知。 任何人都可以看到我做錯了什么或給我一些關於如何從設備獲取通知的指示。 我的電腦與設備在同一個子網上。 我的防火牆已關閉以進行測試。
我的測試控制台應用程序啟動 OnvifManager 類。
using (var manager = new OnvifManager())
{
//manager.ScanForDevices();
var sonyDevice = new OnvifClassLib.OnvifDevice
{
OnvifDeviceServiceUri = new Uri(@"http://192.168.0.101/onvif/device_service"),
};
manager.AddDevice(sonyDevice);
manager.AddEventSubscription(sonyDevice, "PT1H");
var boshDevice = new OnvifClassLib.OnvifDevice
{
OnvifDeviceServiceUri = new Uri(@"http://192.168.0.102/onvif/device_service"),
};
manager.AddDevice(boshDevice);
manager.AddEventSubscription(boshDevice, string.Empty);
var actiDevice = new OnvifClassLib.OnvifDevice
{
OnvifDeviceServiceUri = new Uri(@"http://192.168.0.103/onvif/device_service"),
UserName = "uid",
Password = "pwd"
};
manager.AddDevice(actiDevice);
manager.AddEventSubscription(actiDevice);
Console.WriteLine("Waiting...");
Console.Read();
}
我的 managerClass 將在構造函數中初始化我的 NotificationConsumer 接口。
private void InitializeNotificationConsumerService()
{
_notificationConsumerService = new NotificationConsumerService();
_notificationConsumerService.NewNotification += NotificationConsumerService_OnNewNotification;
_notificationConsumerServiceHost = new ServiceHost(_notificationConsumerService);
_notificationConsumerServiceHost.Open();
}
我的 NotificationConsumer 接口實現。
/// <summary>
/// The client reciever service for WS-BaseNotification
/// </summary>
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class NotificationConsumerService : NotificationConsumer
{
public event EventHandler<EventArgs<Notify1>> NewNotification;
/// <summary>
/// Notifies the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <remarks>A </remarks>
public void Notify(Notify1 request)
{
var threadSafeEventHandler = NewNotification;
if (threadSafeEventHandler != null)
threadSafeEventHandler.Invoke(this, new EventArgs<Notify1>(request));
}
}
public class EventArgs<T> : EventArgs
{
public EventArgs(T data)
{
Data = data;
}
public T Data { get; set; }
}
NotificationConsumerService 的配置
<services>
<service name="OnvifClassLib.NotificationConsumerService">
<endpoint address="" binding="customBinding" bindingConfiguration="CustomBasicHttpBinding"
name="CustomHttpBinding" contract="EventService.NotificationConsumer" />
<host>
<baseAddresses>
<add baseAddress="http://192.168.0.10:8080/NotificationConsumerService" />
</baseAddresses>
</host>
</service>
</services>
<bindings>
<customBinding>
<binding name="CustomBasicHttpBinding">
<textMessageEncoding messageVersion="Soap12">
<readerQuotas maxStringContentLength="80000" />
</textMessageEncoding>
<httpTransport maxReceivedMessageSize="800000" maxBufferSize="800000" />
</binding>
</customBinding>
</bindings>
添加設備方法
public void AddDevice(OnvifDevice device)
{
LoadCapabilities(device);
OnvifDevices.Add(device);
}
internal void LoadCapabilities(OnvifDevice onvifDevice)
{
if (onvifDevice.OnvifDeviceServiceUri == null)
return;
if (onvifDevice.DeviceClient == null)
LoadDeviceClient(onvifDevice);
try
{
onvifDevice.Capabilities = onvifDevice.DeviceClient.GetCapabilities(new[] { OnvifClassLib.DeviceManagement.CapabilityCategory.All });
}
catch (Exception ex)
{
Console.Write(ex.ToString());
}
}
private void LoadDeviceClient(OnvifDevice onvifDevice)
{
if (onvifDevice.OnvifDeviceServiceUri == null)
return;
var serviceAddress = new EndpointAddress(onvifDevice.OnvifDeviceServiceUri.ToString());
var binding = GetBindingFactory(onvifDevice);
onvifDevice.DeviceClient = new OnvifClassLib.DeviceManagement.DeviceClient(binding, serviceAddress);
if (!string.IsNullOrWhiteSpace(onvifDevice.UserName))
{
onvifDevice.DeviceClient.ClientCredentials.UserName.UserName = onvifDevice.UserName;
onvifDevice.DeviceClient.ClientCredentials.UserName.Password = onvifDevice.Password;
}
}
AddEventSubscription 方法
public void AddEventSubscription(OnvifDevice onvifDevice, string initialTerminationTime = "PT2H")
{
if (onvifDevice.Capabilities.Events == null)
throw new ApplicationException("The streamer info does not support event");
try
{
if (onvifDevice.NotificationProducerClient == null)
LoadNotificationProducerClient(onvifDevice);
XmlElement[] filterXml = null;
var subScribe = new Subscribe()
{
ConsumerReference = new EndpointReferenceType
{
Address = new AttributedURIType { Value = _notificationConsumerServiceHost.BaseAddresses.First().ToString() },
}
};
if (!string.IsNullOrWhiteSpace(initialTerminationTime))
subScribe.InitialTerminationTime = initialTerminationTime;
onvifDevice.SubscribeResponse = onvifDevice.NotificationProducerClient.Subscribe(subScribe);
Console.WriteLine("Listening on event from {0}", onvifDevice.NotificationProducerClient.Endpoint.Address.Uri.ToString());
}
catch (FaultException ex)
{
Console.Write(ex.ToString());
}
catch (Exception ex)
{
Console.Write(ex.ToString());
}
}
private void LoadNotificationProducerClient(OnvifDevice onvifDevice)
{
var serviceAddress = new EndpointAddress(onvifDevice.Capabilities.Events.XAddr.ToString());
var binding = GetBindingFactory(onvifDevice);
onvifDevice.NotificationProducerClient = new OnvifClassLib.EventService.NotificationProducerClient(binding, serviceAddress);
if (!string.IsNullOrWhiteSpace(onvifDevice.UserName))
{
onvifDevice.NotificationProducerClient.ClientCredentials.UserName.UserName = onvifDevice.UserName;
onvifDevice.NotificationProducerClient.ClientCredentials.UserName.Password = onvifDevice.Password;
}
}
Soap12 的綁定
private Binding GetBindingFactory(OnvifDevice onvifDevice)
{
return GetCustomBinding(onvifDevice);
}
private Binding GetCustomBinding(OnvifDevice onvifDevice)
{
HttpTransportBindingElement transportElement = new HttpTransportBindingElement();
if (!string.IsNullOrWhiteSpace(onvifDevice.UserName))
transportElement.AuthenticationScheme = AuthenticationSchemes.Basic;
var messegeElement = new TextMessageEncodingBindingElement();
messegeElement.MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.None);
var binding = new CustomBinding(messegeElement, transportElement);
binding.SendTimeout = new TimeSpan(0, 10, 0);
return binding;
}
我認為問題在於,您的通知使用者使用AddressingVersion.None
,而來自 ONVIF 設備的通知根據 WS-Addressing 1.0 進行格式化。 嘗試更改 GetCustomBinding 方法的以下行:
messegeElement.MessageVersion = MessageVersion.CreateVersion(
EnvelopeVersion.Soap12, AddressingVersion.None);
到
messegeElement.MessageVersion = MessageVersion.CreateVersion(
EnvelopeVersion.Soap12, AddressingVersion.WSAddressing10);
我在 GrandStream 相機上遇到了這個問題。 我不得不使用攝像頭網絡用戶界面為其添加一個或多個檢測區域,以使其檢測運動。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.