|
|
|
using EC.Helper.Onvif.Common;
|
|
|
|
using EC.Helper.Onvif.Device;
|
|
|
|
using EC.Helper.Onvif.Imaging;
|
|
|
|
using EC.Helper.Onvif.Media;
|
|
|
|
using EC.Helper.Onvif.PTZ;
|
|
|
|
using System.Net;
|
|
|
|
using Capabilities = EC.Helper.Onvif.Common.Capabilities;
|
|
|
|
|
|
|
|
namespace EC.Helper.Onvif;
|
|
|
|
|
|
|
|
public class OnvifClient
|
|
|
|
{
|
|
|
|
#region Attr
|
|
|
|
|
|
|
|
public string Hostname { get; private set; }
|
|
|
|
|
|
|
|
public string Username { get; private set; }
|
|
|
|
|
|
|
|
public string Password { get; private set; }
|
|
|
|
|
|
|
|
#endregion Attr
|
|
|
|
|
|
|
|
#region Client Attr
|
|
|
|
|
|
|
|
protected DeviceClient Device { get; set; }
|
|
|
|
|
|
|
|
protected MediaClient Media { get; set; }
|
|
|
|
|
|
|
|
protected PTZClient PTZ { get; set; }
|
|
|
|
|
|
|
|
protected ImagingClient Imaging { get; set; }
|
|
|
|
|
|
|
|
protected Capabilities Caps { get; set; }
|
|
|
|
|
|
|
|
#endregion Client Attr
|
|
|
|
|
|
|
|
#region Cache Attr
|
|
|
|
|
|
|
|
protected string ProfileToken { get; set; }
|
|
|
|
|
|
|
|
protected string SubProfileToken { get; set; }
|
|
|
|
|
|
|
|
protected string ThirdProfileToken { get; set; }
|
|
|
|
|
|
|
|
protected string VideoSourceToken { get; set; }
|
|
|
|
|
|
|
|
protected string SteamUrl { get; set; }
|
|
|
|
|
|
|
|
protected string SubSteamUrl { get; set; }
|
|
|
|
|
|
|
|
protected string ThirdSteamUrl { get; set; }
|
|
|
|
|
|
|
|
protected string SnapshotUrl { get; set; }
|
|
|
|
|
|
|
|
#endregion Cache Attr
|
|
|
|
|
|
|
|
public OnvifClient(string hostname, string username, string password)
|
|
|
|
{
|
|
|
|
Hostname = hostname;
|
|
|
|
Username = username;
|
|
|
|
Password = password;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Task<bool> Init()
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
Device = await OnvifClientFactory.CreateDeviceClientAsync(Hostname, Username, Password);
|
|
|
|
Media = await OnvifClientFactory.CreateMediaClientAsync(Hostname, Username, Password);
|
|
|
|
PTZ = await OnvifClientFactory.CreatePTZClientAsync(Hostname, Username, Password);
|
|
|
|
Imaging = await OnvifClientFactory.CreateImagingClientAsync(Hostname, Username, Password);
|
|
|
|
var profiles = await Media.GetProfilesAsync();
|
|
|
|
var videoSources = await Media.GetVideoSourcesAsync();
|
|
|
|
|
|
|
|
foreach (var profile in profiles.Profiles)
|
|
|
|
{
|
|
|
|
if (string.IsNullOrEmpty(ProfileToken))
|
|
|
|
{
|
|
|
|
ProfileToken = profile.token;
|
|
|
|
}
|
|
|
|
else if (string.IsNullOrEmpty(SubProfileToken))
|
|
|
|
{
|
|
|
|
SubProfileToken = profile.token;
|
|
|
|
}
|
|
|
|
else if (string.IsNullOrEmpty(ThirdProfileToken))
|
|
|
|
{
|
|
|
|
ThirdProfileToken = profile.token;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(SubProfileToken)) SubProfileToken = ProfileToken;
|
|
|
|
if (string.IsNullOrEmpty(ThirdProfileToken)) ThirdProfileToken = SubProfileToken;
|
|
|
|
|
|
|
|
foreach (var source in videoSources.VideoSources)
|
|
|
|
{
|
|
|
|
if (string.IsNullOrEmpty(VideoSourceToken))
|
|
|
|
{
|
|
|
|
VideoSourceToken = source.token;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Caps = (await Device.GetCapabilitiesAsync(new CapabilityCategory[] { CapabilityCategory.All })).Capabilities;
|
|
|
|
}
|
|
|
|
catch (Exception)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#region DeviceClient
|
|
|
|
|
|
|
|
public bool IsDeviceContected()
|
|
|
|
{
|
|
|
|
return Device != null;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion DeviceClient
|
|
|
|
|
|
|
|
#region MediaClient
|
|
|
|
|
|
|
|
public bool IsMediaContected()
|
|
|
|
{
|
|
|
|
bool ret = IsDeviceContected();
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected StreamSetup RtspStreamSetup { get; } = new()
|
|
|
|
{
|
|
|
|
Stream = StreamType.RTPUnicast,
|
|
|
|
Transport = new() { Protocol = TransportProtocol.RTSP }
|
|
|
|
};
|
|
|
|
|
|
|
|
public async Task<string> GetStreamUrl()
|
|
|
|
{
|
|
|
|
if (string.IsNullOrEmpty(SteamUrl))
|
|
|
|
{
|
|
|
|
MediaUri mediaUri = await Media.GetStreamUriAsync(RtspStreamSetup, ProfileToken);
|
|
|
|
SteamUrl = mediaUri.Uri.Replace("://", $"://{Username}:{Password}@");
|
|
|
|
}
|
|
|
|
return SteamUrl;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Task<string> GetSubStreamUrl()
|
|
|
|
{
|
|
|
|
if (string.IsNullOrEmpty(SubSteamUrl))
|
|
|
|
{
|
|
|
|
MediaUri mediaUri = await Media.GetStreamUriAsync(RtspStreamSetup, SubProfileToken);
|
|
|
|
SubSteamUrl = mediaUri.Uri.Replace("://", $"://{Username}:{Password}@");
|
|
|
|
}
|
|
|
|
return SubSteamUrl;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Task<string> GetThirdStreamUrl()
|
|
|
|
{
|
|
|
|
if (string.IsNullOrEmpty(ThirdSteamUrl))
|
|
|
|
{
|
|
|
|
MediaUri mediaUri = await Media.GetStreamUriAsync(RtspStreamSetup, SubProfileToken);
|
|
|
|
ThirdSteamUrl = mediaUri.Uri.Replace("://", $"://{Username}:{Password}@");
|
|
|
|
}
|
|
|
|
return ThirdSteamUrl;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Task<string> GetSnapshotUrl()
|
|
|
|
{
|
|
|
|
if (string.IsNullOrEmpty(SnapshotUrl))
|
|
|
|
{
|
|
|
|
MediaUri mediaUri = await Media.GetSnapshotUriAsync(ProfileToken);
|
|
|
|
SnapshotUrl = mediaUri.Uri.Replace("://", $"://{Username}:{Password}@");
|
|
|
|
}
|
|
|
|
return SnapshotUrl;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Task<string> GetSnapshot()
|
|
|
|
{
|
|
|
|
string url = await GetSnapshotUrl();
|
|
|
|
WebRequest request = WebRequest.Create(url);
|
|
|
|
request.Method = "GET";
|
|
|
|
request.PreAuthenticate = true;
|
|
|
|
request.Credentials = new NetworkCredential(Username, Password);
|
|
|
|
using HttpWebResponse response = (HttpWebResponse)request.GetResponse();
|
|
|
|
using Stream stream = response.GetResponseStream();
|
|
|
|
using MemoryStream mStream = new();
|
|
|
|
byte[] buffer = new byte[1024];
|
|
|
|
int byteCount;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
byteCount = stream.Read(buffer, 0, buffer.Length);
|
|
|
|
mStream.Write(buffer, 0, byteCount);
|
|
|
|
} while (byteCount > 0);
|
|
|
|
mStream.Position = 0;
|
|
|
|
return Convert.ToBase64String(mStream.ToArray());
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion MediaClient
|
|
|
|
|
|
|
|
#region PTZClient
|
|
|
|
|
|
|
|
public bool IsPTZContected()
|
|
|
|
{
|
|
|
|
bool ret = IsDeviceContected() && (PTZ != null) && !string.IsNullOrEmpty(ProfileToken);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 绝对移动
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="pan"></param>
|
|
|
|
/// <param name="tilt"></param>
|
|
|
|
/// <param name="zoom"></param>
|
|
|
|
/// <param name="atomDist"></param>
|
|
|
|
/// <returns></returns>
|
|
|
|
public async Task AbsoluteMove(float pan, float tilt, float zoom, float atomDist = 0.1f)
|
|
|
|
{
|
|
|
|
await PTZ.AbsoluteMoveAsync(ProfileToken, new PTZVector
|
|
|
|
{
|
|
|
|
PanTilt = new Vector2D { x = pan, y = tilt },
|
|
|
|
Zoom = new Vector1D { x = zoom }
|
|
|
|
}, new PTZSpeed
|
|
|
|
{
|
|
|
|
PanTilt = new Vector2D { x = atomDist, y = atomDist },
|
|
|
|
Zoom = new Vector1D { x = atomDist }
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 相对移动
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="pan">[-1,1]</param>
|
|
|
|
/// <param name="tilt">[-1,1]</param>
|
|
|
|
/// <param name="zoom">[-1,1]</param>
|
|
|
|
/// <param name="atomSpeed">[0,1]</param>
|
|
|
|
public async Task RelativeMove(float pan, float tilt, float zoom, float atomSpeed = 0.1f)
|
|
|
|
{
|
|
|
|
await PTZ.RelativeMoveAsync(ProfileToken, new PTZVector
|
|
|
|
{
|
|
|
|
PanTilt = new Vector2D { x = pan, y = tilt },
|
|
|
|
Zoom = new Vector1D { x = zoom }
|
|
|
|
}, new PTZSpeed
|
|
|
|
{
|
|
|
|
PanTilt = new Vector2D { x = atomSpeed, y = atomSpeed },
|
|
|
|
Zoom = new Vector1D { x = atomSpeed }
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 持续移动
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="pan">[-1,1]</param>
|
|
|
|
/// <param name="tilt">[-1,1]</param>
|
|
|
|
/// <param name="zoom">[-1,1]</param>
|
|
|
|
/// <param name="timeout">ms</param>
|
|
|
|
public async Task ContinuousMove(float pan, float tilt, float zoom, string timeout = "")
|
|
|
|
{
|
|
|
|
await PTZ.ContinuousMoveAsync(ProfileToken, new PTZSpeed
|
|
|
|
{
|
|
|
|
PanTilt = new Vector2D { x = pan, y = tilt },
|
|
|
|
Zoom = new Vector1D { x = zoom }
|
|
|
|
}, timeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Task StopMove()
|
|
|
|
{
|
|
|
|
await PTZ.StopAsync(ProfileToken, true, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Task<PTZStatus> GetStatus()
|
|
|
|
{
|
|
|
|
return await PTZ.GetStatusAsync(ProfileToken);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion PTZClient
|
|
|
|
|
|
|
|
#region ImagingClient
|
|
|
|
|
|
|
|
public bool IsImagingContected()
|
|
|
|
{
|
|
|
|
bool ret = IsDeviceContected();
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Task FocusAbsoluteMove(float position)
|
|
|
|
{
|
|
|
|
await Imaging.MoveAsync(VideoSourceToken, new FocusMove
|
|
|
|
{
|
|
|
|
Absolute = new AbsoluteFocus
|
|
|
|
{
|
|
|
|
Position = position,
|
|
|
|
//Speed = 1f,
|
|
|
|
//SpeedSpecified = true
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Task FocusRelativeMove(float distance)
|
|
|
|
{
|
|
|
|
await Imaging.MoveAsync(VideoSourceToken, new FocusMove
|
|
|
|
{
|
|
|
|
Relative = new RelativeFocus
|
|
|
|
{
|
|
|
|
Distance = distance,
|
|
|
|
//Speed = 1f,
|
|
|
|
//SpeedSpecified = true
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Task FocusContinuousMove(float speed)
|
|
|
|
{
|
|
|
|
await Imaging.MoveAsync(VideoSourceToken, new FocusMove
|
|
|
|
{
|
|
|
|
Continuous = new ContinuousFocus { Speed = speed }
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public async Task FocusStopMove()
|
|
|
|
{
|
|
|
|
await Imaging.StopAsync(VideoSourceToken);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion ImagingClient
|
|
|
|
}
|