|
|
|
@ -7,7 +7,6 @@ using Microsoft.AspNetCore.Hosting;
|
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
|
using Microsoft.Extensions.Configuration;
|
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
|
using Microsoft.Extensions.Hosting;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
@ -25,7 +24,7 @@ namespace IoTNode.DeviceServices.Onvif
|
|
|
|
|
{
|
|
|
|
|
public class OnvifService : BaseDeviceService, IDisposable
|
|
|
|
|
{
|
|
|
|
|
private readonly ConcurrentDictionary<string, (Device camera, Process local, Process remote)> _list = new ConcurrentDictionary<string, (Device camera, Process local, Process remote)>();
|
|
|
|
|
private readonly ConcurrentDictionary<string, Process> _list = new ConcurrentDictionary<string, Process>();
|
|
|
|
|
private readonly IWebHostEnvironment _env;
|
|
|
|
|
private readonly IHttpClientFactory _httpClientFactory;
|
|
|
|
|
private readonly IOnvifDeviceManagement _onvifDeviceManagement;
|
|
|
|
@ -145,12 +144,9 @@ namespace IoTNode.DeviceServices.Onvif
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("Record", "否", DeviceDataType.String, "录像"));
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("ffmpeg.file", file, DeviceDataType.String, "ffmpeg路径", hidden: true));
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("ffmpeg.args", this._cfg["ffmpeg.args"], DeviceDataType.String, "ffmpeg.args", hidden: true));
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("mainrtmp", $"rtmp://{this._cfg["stream.rtmp"]}/live/main{ipCamera.Id}", DeviceDataType.String, "主码流rtmp"));
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("mainflv", $"http://{this._cfg["stream.flv"]}/live/main{ipCamera.Id}.flv", DeviceDataType.String, "主码流flv"));
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("mainhls", $"http://{this._cfg["stream.hls"]}/live/main{ipCamera.Id}.m3u8", DeviceDataType.String, "主码流hls"));
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("subrtmp", $"rtmp://{this._cfg["stream.rtmp"]}/live/sub{ipCamera.Id}", DeviceDataType.String, "子码流rtmp"));
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("subflv", $"http://{this._cfg["stream.flv"]}/live/sub{ipCamera.Id}.flv", DeviceDataType.String, "子码流flv"));
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("subhls", $"http://{this._cfg["stream.hls"]}/live/sub{ipCamera.Id}.m3u8", DeviceDataType.String, "子码流hls"));
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("rtmp", $"rtmp://{this._cfg["stream.rtmp"]}/live/{ipCamera.Id}", DeviceDataType.String, "rtmp"));
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("flv", $"http://{this._cfg["stream.flv"]}/live/{ipCamera.Id}.flv", DeviceDataType.String, "flv"));
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("hls", $"http://{this._cfg["stream.hls"]}/live/{ipCamera.Id}.m3u8", DeviceDataType.String, "hls"));
|
|
|
|
|
}
|
|
|
|
|
var profiles = this._onvifDeviceManagement.GetProfiles(ipCamera.DeviceUrl, ipCamera.MediaUrl);
|
|
|
|
|
var needAuth = false;
|
|
|
|
@ -183,26 +179,20 @@ namespace IoTNode.DeviceServices.Onvif
|
|
|
|
|
{
|
|
|
|
|
if (needAuth)
|
|
|
|
|
{
|
|
|
|
|
ipCamera.MainStreamUriXml = this._onvifDeviceManagement.GetStreamUri(ipCamera.DeviceUrl, ipCamera.MediaUrl, device.UserName, device.Password, ipCamera.Profiles.First().Token);
|
|
|
|
|
ipCamera.MainSnapshotUriXml = this._onvifDeviceManagement.GetSnapshotUri(ipCamera.DeviceUrl, ipCamera.MediaUrl, device.UserName, device.Password, ipCamera.Profiles.First().Token);
|
|
|
|
|
ipCamera.SubStreamUriXml = this._onvifDeviceManagement.GetStreamUri(ipCamera.DeviceUrl, ipCamera.MediaUrl, device.UserName, device.Password, ipCamera.Profiles.Last().Token);
|
|
|
|
|
ipCamera.SubSnapshotUriXml = this._onvifDeviceManagement.GetSnapshotUri(ipCamera.DeviceUrl, ipCamera.MediaUrl, device.UserName, device.Password, ipCamera.Profiles.Last().Token);
|
|
|
|
|
ipCamera.StreamUriXml = this._onvifDeviceManagement.GetStreamUri(ipCamera.DeviceUrl, ipCamera.MediaUrl, device.UserName, device.Password, ipCamera.Profiles.First().Token);
|
|
|
|
|
ipCamera.SnapshotUriXml = this._onvifDeviceManagement.GetSnapshotUri(ipCamera.DeviceUrl, ipCamera.MediaUrl, device.UserName, device.Password, ipCamera.Profiles.First().Token);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ipCamera.MainStreamUriXml = this._onvifDeviceManagement.GetStreamUri(ipCamera.DeviceUrl, ipCamera.MediaUrl, ipCamera.Profiles.First().Token);
|
|
|
|
|
ipCamera.MainSnapshotUriXml = this._onvifDeviceManagement.GetSnapshotUri(ipCamera.DeviceUrl, ipCamera.MediaUrl, ipCamera.Profiles.First().Token);
|
|
|
|
|
ipCamera.SubStreamUriXml = this._onvifDeviceManagement.GetStreamUri(ipCamera.DeviceUrl, ipCamera.MediaUrl, ipCamera.Profiles.Last().Token);
|
|
|
|
|
ipCamera.SubSnapshotUriXml = this._onvifDeviceManagement.GetSnapshotUri(ipCamera.DeviceUrl, ipCamera.MediaUrl, ipCamera.Profiles.Last().Token);
|
|
|
|
|
ipCamera.StreamUriXml = this._onvifDeviceManagement.GetStreamUri(ipCamera.DeviceUrl, ipCamera.MediaUrl, ipCamera.Profiles.First().Token);
|
|
|
|
|
ipCamera.SnapshotUriXml = this._onvifDeviceManagement.GetSnapshotUri(ipCamera.DeviceUrl, ipCamera.MediaUrl, ipCamera.Profiles.First().Token);
|
|
|
|
|
}
|
|
|
|
|
ipCamera.ParseStreamUri();
|
|
|
|
|
ipCamera.ParseSnapshotUri();
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("MainToken", ipCamera.Profiles.First().Token, DeviceDataType.String, "主码流Token"));
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("SubToken", ipCamera.Profiles.Last().Token, DeviceDataType.String, "子码流Token"));
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("MainStreamUri", ipCamera.MainStreamUri, DeviceDataType.String, "主码流地址"));
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("MainSnapshotUri", ipCamera.MainSnapshotUri, DeviceDataType.String, "主码流截图地址"));
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("SubStreamUri", ipCamera.SubStreamUri, DeviceDataType.String, "子码流地址"));
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("SubSnapshotUri", ipCamera.SubSnapshotUri, DeviceDataType.String, "子码流截图地址"));
|
|
|
|
|
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("Token", ipCamera.Profiles.First().Token, DeviceDataType.String, "Token"));
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("StreamUri", ipCamera.StreamUri, DeviceDataType.String, "流地址"));
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("SnapshotUri", ipCamera.SnapshotUri, DeviceDataType.String, "截图地址"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
@ -214,13 +204,6 @@ namespace IoTNode.DeviceServices.Onvif
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("DeviceUrl", ipCamera.DeviceUrl, DeviceDataType.String, "设备地址"));
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("PtzAddress", ipCamera.PTZAddress, DeviceDataType.String, "云台地址"));
|
|
|
|
|
this.UpdateData(deviceRepo, device, device.CreateData("Ptz3DZoomSupport", ipCamera.Ptz3DZoomSupport ? "是" : "否", DeviceDataType.String, "缩放支持"));
|
|
|
|
|
//deviceRepo.SaveChanges();
|
|
|
|
|
//var deviceDto = device.To<EditDeviceModel>();
|
|
|
|
|
//this.SendToServer(Methods.EditDevice, deviceDto);
|
|
|
|
|
//foreach (var item in device.Data.Where(o => !o.Hidden))
|
|
|
|
|
//{
|
|
|
|
|
// this.SendDataToServer(item);
|
|
|
|
|
//}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
@ -299,7 +282,7 @@ namespace IoTNode.DeviceServices.Onvif
|
|
|
|
|
{
|
|
|
|
|
var repo = scope.ServiceProvider.GetService<IRepository<Device>>();
|
|
|
|
|
var camera = repo.ReadOnlyTable().Include(o => o.Data).FirstOrDefault(o => o.Number == key);
|
|
|
|
|
if (camera != null && camera.Data.FirstOrDefault(o => o.Key == "Push").Value == "是")// && camera.Publish && (!camera.NeedAuth || (camera.NeedAuth && camera.HasAuth)))
|
|
|
|
|
if (camera != null && camera.Data.FirstOrDefault(o => o.Key == "Push").Value == "是")
|
|
|
|
|
{
|
|
|
|
|
if (camera.Data.FirstOrDefault(o => o.Key == "NeedAuth").Value == "否" || camera.Data.FirstOrDefault(o => o.Key == "HasAuth").Value == "是")
|
|
|
|
|
{
|
|
|
|
@ -317,50 +300,41 @@ namespace IoTNode.DeviceServices.Onvif
|
|
|
|
|
}
|
|
|
|
|
var needAuth = camera.Data.FirstOrDefault(o => o.Key == "NeedAuth").Value == "是";
|
|
|
|
|
var hasAuth = camera.Data.FirstOrDefault(o => o.Key == "HasAuth").Value == "是";
|
|
|
|
|
var mainStreamUri = camera.Data.FirstOrDefault(o => o.Key == "MainStreamUri").Value;
|
|
|
|
|
var mainRtspUrl = $"rtsp://{(needAuth ? $"{camera.UserName}:{camera.Password}@" : "")}{mainStreamUri.Substring(7)}";
|
|
|
|
|
var subStreamUri = camera.Data.FirstOrDefault(o => o.Key == "SubStreamUri").Value;
|
|
|
|
|
var subRtspUrl = $"rtsp://{(needAuth ? $"{camera.UserName}:{camera.Password}@" : "")}{subStreamUri.Substring(7)}";
|
|
|
|
|
var streamUri = camera.Data.FirstOrDefault(o => o.Key == "StreamUri").Value;
|
|
|
|
|
var rtspUrl = $"rtsp://{(needAuth ? $"{camera.UserName}:{camera.Password}@" : "")}{streamUri.Substring(7)}";
|
|
|
|
|
var fileName = $"ffmpeg-{Helper.Instance.GetRunTime()}{(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : "")}";
|
|
|
|
|
var file = camera.GetData("ffmpeg.file")?.Value;
|
|
|
|
|
var mainRtmp = camera.Data.FirstOrDefault(o => o.Key == "mainrtmp").Value;
|
|
|
|
|
var subRtmp = camera.Data.FirstOrDefault(o => o.Key == "subrtmp").Value;
|
|
|
|
|
var rtmp = camera.Data.FirstOrDefault(o => o.Key == "rtmp").Value;
|
|
|
|
|
Console.WriteLine(file);
|
|
|
|
|
var arguments = camera.Data.FirstOrDefault(o => o.Key == "ffmpeg.args").Value;
|
|
|
|
|
Process main = null, sub = null;
|
|
|
|
|
if (!string.IsNullOrEmpty(mainStreamUri))
|
|
|
|
|
Process process = null;
|
|
|
|
|
if (!string.IsNullOrEmpty(streamUri))
|
|
|
|
|
{
|
|
|
|
|
if (!string.IsNullOrEmpty(mainRtmp))
|
|
|
|
|
{
|
|
|
|
|
main = this.SetProcess(file, string.Format(arguments, Environment.ProcessorCount, mainRtspUrl, mainRtmp));
|
|
|
|
|
}
|
|
|
|
|
if (!string.IsNullOrEmpty(subRtmp))
|
|
|
|
|
if (!string.IsNullOrEmpty(rtmp))
|
|
|
|
|
{
|
|
|
|
|
sub = this.SetProcess(file, string.Format(arguments, Environment.ProcessorCount, subRtspUrl, subRtmp));
|
|
|
|
|
process = this.SetProcess(file, string.Format(arguments, Environment.ProcessorCount, rtspUrl, rtmp));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (this._list.TryAdd(camera.Number, (camera, main, sub)))
|
|
|
|
|
if (this._list.TryAdd(camera.Number, process))
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine($"add {camera.Number} to list");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (main != null)
|
|
|
|
|
if (process != null)
|
|
|
|
|
{
|
|
|
|
|
this.CloseProcess(main);
|
|
|
|
|
this.CloseProcess(sub);
|
|
|
|
|
this.CloseProcess(process);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void StopPushToServer(string key)
|
|
|
|
|
{
|
|
|
|
|
(Device camera, Process local, Process remote) item;
|
|
|
|
|
if (this._list.TryRemove(key, out item))
|
|
|
|
|
Process process;
|
|
|
|
|
if (this._list.TryRemove(key, out process))
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine($"OnvifService>remove {key} from list");
|
|
|
|
|
this.CloseProcess(item.local);
|
|
|
|
|
this.CloseProcess(item.remote);
|
|
|
|
|
this.CloseProcess(process);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -387,7 +361,7 @@ namespace IoTNode.DeviceServices.Onvif
|
|
|
|
|
Console.WriteLine($"ffmpeg processes list:{_list.Count},exit:{_process.ExitCode},args:{arguments}");
|
|
|
|
|
if (_process != null)
|
|
|
|
|
{
|
|
|
|
|
Thread.Sleep(100);
|
|
|
|
|
Thread.Sleep(3000);
|
|
|
|
|
_process.CancelErrorRead();
|
|
|
|
|
_process.Start();
|
|
|
|
|
_process.BeginErrorReadLine();
|
|
|
|
@ -422,20 +396,23 @@ namespace IoTNode.DeviceServices.Onvif
|
|
|
|
|
|
|
|
|
|
private void CloseProcess(Process process)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine($"close process:{process?.StartInfo?.FileName} with {process?.StartInfo?.Arguments}");
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (process != null)
|
|
|
|
|
{
|
|
|
|
|
process.EnableRaisingEvents = false;
|
|
|
|
|
process.Kill();
|
|
|
|
|
//process.WaitForExit();
|
|
|
|
|
process.Dispose();
|
|
|
|
|
process?.Kill();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
ex.PrintStack($"error when close ffmpeg process {ex.Message}");
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
process?.Dispose();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string GetOnoce(string deviceUrl)
|
|
|
|
@ -518,7 +495,7 @@ namespace IoTNode.DeviceServices.Onvif
|
|
|
|
|
if (!string.IsNullOrEmpty(ptzAddress))
|
|
|
|
|
{
|
|
|
|
|
var deviceUrl = camera.GetDataValue("DeviceUrl");
|
|
|
|
|
RequestXml(ptzAddress, MessageTemplate.StopAction, String.Format(MessageTemplate.StopMessage, camera.GetDataValue("MainToken"), true, true), camera.UserName, camera.Password, GetOnoce(deviceUrl));
|
|
|
|
|
RequestXml(ptzAddress, MessageTemplate.StopAction, String.Format(MessageTemplate.StopMessage, camera.GetDataValue("Token"), true, true), camera.UserName, camera.Password, GetOnoce(deviceUrl));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -532,31 +509,15 @@ namespace IoTNode.DeviceServices.Onvif
|
|
|
|
|
if (!string.IsNullOrEmpty(ptzAddress))
|
|
|
|
|
{
|
|
|
|
|
var deviceUrl = camera.GetDataValue("DeviceUrl");
|
|
|
|
|
RequestXml(ptzAddress, MessageTemplate.ContinuousMoveAction, String.Format(MessageTemplate.ContinuousMoveMessage, camera.GetDataValue("MainToken"), zx, px, py), camera.UserName, camera.Password, GetOnoce(deviceUrl));
|
|
|
|
|
RequestXml(ptzAddress, MessageTemplate.ContinuousMoveAction, String.Format(MessageTemplate.ContinuousMoveMessage, camera.GetDataValue("Token"), zx, px, py), camera.UserName, camera.Password, GetOnoce(deviceUrl));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] MainScreenShot(string id)
|
|
|
|
|
{
|
|
|
|
|
var camera = this.GetCamera(id);
|
|
|
|
|
var url = camera.GetDataValue("MainSubSnapshotUri");
|
|
|
|
|
if (string.IsNullOrEmpty(url))
|
|
|
|
|
{
|
|
|
|
|
return new byte[] { };
|
|
|
|
|
}
|
|
|
|
|
var hc = this._httpClientFactory.CreateClient();
|
|
|
|
|
if (camera.GetDataValue("NeedAuth") == "否")
|
|
|
|
|
{
|
|
|
|
|
return hc.GetByteArrayAsync(url).Result;
|
|
|
|
|
}
|
|
|
|
|
return hc.GetByteDigest(new Uri(url), camera.UserName, camera.Password);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] SubScreenShot(string id)
|
|
|
|
|
public byte[] ScreenShot(string id)
|
|
|
|
|
{
|
|
|
|
|
var camera = this.GetCamera(id);
|
|
|
|
|
var url = camera.GetDataValue("SubSnapshotUri");
|
|
|
|
|
var url = camera.GetDataValue("SnapshotUri");
|
|
|
|
|
if (string.IsNullOrEmpty(url))
|
|
|
|
|
{
|
|
|
|
|
return new byte[] { };
|
|
|
|
@ -607,7 +568,6 @@ namespace IoTNode.DeviceServices.Onvif
|
|
|
|
|
if (data != null)
|
|
|
|
|
{
|
|
|
|
|
data.Value = state ? "是" : "否";
|
|
|
|
|
//this.UpdateData(repo, device, data);
|
|
|
|
|
this.SendToServer(Methods.UpdateDvr, data);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -616,7 +576,7 @@ namespace IoTNode.DeviceServices.Onvif
|
|
|
|
|
|
|
|
|
|
private byte[] ScreenShot(Device camera)
|
|
|
|
|
{
|
|
|
|
|
var url = camera.GetDataValue("SubSnapshotUri");
|
|
|
|
|
var url = camera.GetDataValue("SnapshotUri");
|
|
|
|
|
if (string.IsNullOrEmpty(url))
|
|
|
|
|
{
|
|
|
|
|
return new byte[] { };
|
|
|
|
|