You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
181 lines
8.6 KiB
181 lines
8.6 KiB
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Net.Http;
|
|
using System.Net.NetworkInformation;
|
|
using System.Net.Sockets;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Infrastructure.Extensions;
|
|
|
|
namespace ONVIFService
|
|
{
|
|
public class OnvifDeviceManagement : IOnvifDeviceManagement
|
|
{
|
|
private readonly IHttpClientFactory _httpClientFactory;
|
|
private readonly object thisLock = new object();
|
|
|
|
public OnvifDeviceManagement(IHttpClientFactory httpClientFactory)
|
|
{
|
|
this._httpClientFactory = httpClientFactory;
|
|
}
|
|
|
|
public List<IPCamera> Discovery()
|
|
{
|
|
Console.WriteLine("search onvif devices...");
|
|
var list = new ConcurrentBag<string>();
|
|
var ips = NetworkInterface.GetAllNetworkInterfaces()
|
|
.Where(nic => nic.OperationalStatus == OperationalStatus.Up && nic.NetworkInterfaceType != NetworkInterfaceType.Loopback)
|
|
.Select(nic => nic.GetIPProperties().UnicastAddresses)
|
|
.SelectMany(o => o.ToList())
|
|
.Select(o => o.Address)
|
|
.Where(o => o.AddressFamily == AddressFamily.InterNetwork)
|
|
.ToList();
|
|
foreach (var ip in ips)
|
|
{
|
|
Console.WriteLine($"search:{ip}");
|
|
lock (thisLock)
|
|
{
|
|
try
|
|
{
|
|
var tokenSource = new CancellationTokenSource();
|
|
using (var client = new UdpClient(new IPEndPoint(ip, 0)))
|
|
{
|
|
var probeMessage = string.Format(Template.GetServiceRequestTemplage, Guid.NewGuid());
|
|
var message = Encoding.UTF8.GetBytes(probeMessage);
|
|
client.Send(message, message.Length, new IPEndPoint(IPAddress.Parse("239.255.255.250"), 3702));
|
|
Task.Run(() =>
|
|
{
|
|
try
|
|
{
|
|
var ep = new IPEndPoint(IPAddress.Any, 3702);
|
|
while (!tokenSource.IsCancellationRequested)
|
|
{
|
|
if (client.Available > 0)
|
|
{
|
|
var buffer = client.Receive(ref ep);
|
|
var result = Encoding.UTF8.GetString(buffer);
|
|
if (!list.Any(o => o == result))
|
|
{
|
|
list.Add(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine(ex.Message);
|
|
}
|
|
}, tokenSource.Token);
|
|
Thread.Sleep(1000);
|
|
tokenSource.Cancel();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ex.PrintStack();
|
|
}
|
|
}
|
|
}
|
|
var cameras = new List<IPCamera>();
|
|
foreach (var item in list)
|
|
{
|
|
var camera = new IPCamera { DiscoveryXml = item };
|
|
try
|
|
{
|
|
camera.ParseDiscovery();
|
|
camera.GetCapabilitiesXml = GetCapabilities(camera.DeviceUrl);
|
|
camera.ParseCapabilities();
|
|
cameras.Add(camera);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ex.PrintStack();
|
|
}
|
|
}
|
|
return cameras;
|
|
}
|
|
|
|
public string GetCapabilities(string deviceUrl)
|
|
{
|
|
return SoapRequest(deviceUrl, Template.GetCapabilitiesAction, Template.GetCapabilitiesMessage);
|
|
}
|
|
|
|
public string GetProfiles(string deviceUrl, string mediaUrl)
|
|
{
|
|
return SoapRequest(mediaUrl, Template.GetProfilesAction, string.Format(Template.Templage, Template.GetProfilesMessageBody));
|
|
}
|
|
|
|
public string GetProfiles(string deviceUrl, string mediaUrl, string userName, string password)
|
|
{
|
|
return RequestXml(mediaUrl, Template.GetProfilesAction, Template.GetProfilesMessageBody, userName, password, GetOnoce(deviceUrl));
|
|
}
|
|
|
|
public string GetStreamUri(string deviceUrl, string mediaUrl, string token)
|
|
{
|
|
return SoapRequest(mediaUrl, Template.GetStreamUriAction, string.Format(Template.Templage, String.Format(Template.GetStreamUriMessage, token)));
|
|
}
|
|
|
|
public string GetStreamUri(string deviceUrl, string mediaUrl, string userName, string password, string token)
|
|
{
|
|
return RequestXml(mediaUrl, Template.GetStreamUriAction, String.Format(Template.GetStreamUriMessage, token), userName, password, GetOnoce(deviceUrl));
|
|
}
|
|
|
|
public string GetSnapshotUri(string deviceUrl, string mediaUrl, string token)
|
|
{
|
|
return SoapRequest(mediaUrl, Template.GetSnapshotUriAction, string.Format(Template.Templage, String.Format(Template.GetSnapshotUriMessage, token)));
|
|
}
|
|
|
|
public string GetSnapshotUri(string deviceUrl, string mediaUrl, string userName, string password, string token)
|
|
{
|
|
return RequestXml(mediaUrl, Template.GetSnapshotUriAction, String.Format(Template.GetSnapshotUriMessage, token), userName, password, GetOnoce(deviceUrl));
|
|
}
|
|
|
|
private string SoapRequest(string url, string action, string message)
|
|
{
|
|
var hc = this._httpClientFactory.CreateClient();
|
|
hc.DefaultRequestHeaders.Add("ContentType", $"application/soap+xml; charset=utf-8; action=\"{action}\"");
|
|
var task = hc.PostAsync(url, new StringContent(message));
|
|
var result = task.Result;
|
|
var content = result.Content.ReadAsStringAsync().Result;
|
|
return result.StatusCode == HttpStatusCode.OK ? content : "";
|
|
}
|
|
|
|
private string RequestXml(string url, string action, string body, string userName, string password, string onoce)
|
|
{
|
|
var nonce_b = Convert.FromBase64String(onoce);
|
|
var now = DateTime.Now.ToUniversalTime().ToString("yyyy-MM-ddThh:mm:ss.fffZ");
|
|
var creationtime_b = Encoding.ASCII.GetBytes(now);
|
|
var password_b = Encoding.ASCII.GetBytes(password);
|
|
var concatenation_b = new byte[nonce_b.Length + creationtime_b.Length + password_b.Length];
|
|
Buffer.BlockCopy(nonce_b, 0, concatenation_b, 0, nonce_b.Length);
|
|
Buffer.BlockCopy(creationtime_b, 0, concatenation_b, nonce_b.Length, creationtime_b.Length);
|
|
Buffer.BlockCopy(password_b, 0, concatenation_b, nonce_b.Length + creationtime_b.Length, password_b.Length);
|
|
var sha = new SHA1CryptoServiceProvider();
|
|
var pdresult = sha.ComputeHash(concatenation_b);
|
|
var passworddigest = Convert.ToBase64String(pdresult);
|
|
var message = string.Format(Template.AuthTemplate, userName, passworddigest, Convert.ToBase64String(nonce_b), now, body);
|
|
var result = SoapRequest(url, action, message);
|
|
return result;
|
|
}
|
|
|
|
private string GetOnoce(string deviceUrl)
|
|
{
|
|
var message = @"<s:Envelope xmlns:s=""http://www.w3.org/2003/05/soap-envelope"">
|
|
<s:Body xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
|
|
<GetDeviceInformation xmlns=""http://www.onvif.org/ver10/device/wsdl""></GetDeviceInformation>
|
|
</s:Body>
|
|
</s:Envelope>";
|
|
var hc = this._httpClientFactory.CreateClient();
|
|
hc.DefaultRequestHeaders.Add("ContentType", $"application/soap+xml; charset=utf-8; action=\"{Template.GetDeviceInformationAction}\"");
|
|
var task = hc.PostAsync(deviceUrl, new StringContent(message));
|
|
var result = task.Result.Headers.WwwAuthenticate;
|
|
return Regex.Match(result.ToString(), "nonce=\"([^\"]+)\"").Groups[1].Value;
|
|
}
|
|
}
|
|
} |