Browse Source

feat: 添加调用 ZLMediaKit 服务功能

master
fajiao 2 years ago
parent
commit
a47d14e395
  1. 55
      Cis.Application/Cis.Application.xml
  2. 5
      Cis.Application/Core/Common/CoreInfo.cs
  3. 24
      Cis.Application/Core/Component/ZLMediaKit/Entity/StreamConnInfo.cs
  4. 66
      Cis.Application/Core/Component/ZLMediaKit/Entity/ZlmException.cs
  5. 18
      Cis.Application/Core/Component/ZLMediaKit/IZlmServer.cs
  6. 127
      Cis.Application/Core/Component/ZLMediaKit/ZlmServer.cs
  7. 15
      Cis.Application/Core/Service/OnvifService.cs
  8. 111
      Cis.Application/Core/Service/ZlmService.cs
  9. 2
      Cis.Application/Startup.cs
  10. 2
      Cis.Core/Cis.Core.csproj
  11. 40
      Cis.Core/Cis.Core.xml
  12. 44
      Cis.Core/Common/Option/ZLMediaKitOptions.cs
  13. 11
      Cis.Core/CoreConfig.json
  14. 1
      Cis.Web.Core/ProjectOptions.cs
  15. 23
      EC.Helper/CameraSDK/Common/CameraException.cs
  16. 38
      EC.Helper/Onvif/OnvifClient.cs

55
Cis.Application/Cis.Application.xml

@ -530,6 +530,11 @@
Onvif Api 分组排序
</summary>
</member>
<member name="F:Cis.Application.Core.CoreInfo.ZlmGroupOrder">
<summary>
ZLMediaKit Api 分组排序
</summary>
</member>
<member name="P:Cis.Application.Core.CameraDataBase.LoopInterval">
<summary>
循环间隔,单位毫秒
@ -1051,6 +1056,21 @@
{cameraId, OnvifClient}
</summary>
</member>
<member name="P:Cis.Application.Core.Component.ZLMediaKit.StreamConnInfo.Vhost">
<summary>
虚拟主机
</summary>
</member>
<member name="P:Cis.Application.Core.Component.ZLMediaKit.StreamConnInfo.App">
<summary>
应用名
</summary>
</member>
<member name="P:Cis.Application.Core.Component.ZLMediaKit.StreamConnInfo.Stream">
<summary>
流id
</summary>
</member>
<member name="T:Cis.Application.Core.MarkSearchService">
<summary>
标签追踪服务
@ -1222,6 +1242,41 @@
</summary>
<param name="cameraId">cbCameraId</param>
</member>
<member name="T:Cis.Application.Core.Service.ZlmService">
<summary>
zlmediakit 服务
</summary>
</member>
<member name="M:Cis.Application.Core.Service.ZlmService.AddStreamProxy(System.Int64,System.Int32)">
<summary>
添加码流拉流代理(只支持H264/H265/aac/G711负载)
</summary>
<param name="cameraId">cbCameraId</param>
<param name="streamLevel">码流级别[0,2], default:0</param>
<returns></returns>
</member>
<member name="M:Cis.Application.Core.Service.ZlmService.DelStreamProxy(System.Int64,System.Int32)">
<summary>
关闭拉流代理
</summary>
<param name="cameraId">cbCameraId</param>
<param name="streamLevel">码流级别[0,2], default:0</param>
<returns></returns>
</member>
<member name="M:Cis.Application.Core.Service.ZlmService.GetMediaList(System.Int64,System.Int32)">
<summary>
获取流列表
</summary>
<param name="cameraId">cbCameraId</param>
<param name="streamLevel">码流级别[0,2], default:0</param>
</member>
<member name="M:Cis.Application.Core.Service.ZlmService.IsMediaOnline(System.Int64,System.Int32)">
<summary>
判断直播流是否在线
</summary>
<param name="cameraId">cbCameraId</param>
<param name="streamLevel">码流级别[0,2], default:0</param>
</member>
<member name="M:Cis.Application.Startup.ConfigureServices(Microsoft.Extensions.DependencyInjection.IServiceCollection)">
<summary>
配置应用所需服务,在该方法中可以添加应用所需要的功能或服务

5
Cis.Application/Core/Common/CoreInfo.cs

@ -19,5 +19,10 @@ public class CoreInfo
/// </summary>
public const int OnvifGroupOrder = 100;
/// <summary>
/// ZLMediaKit Api 分组排序
/// </summary>
public const int ZlmGroupOrder = 100;
#endregion Api Info
}

24
Cis.Application/Core/Component/ZLMediaKit/Entity/StreamConnInfo.cs

@ -0,0 +1,24 @@
namespace Cis.Application.Core.Component.ZLMediaKit;
public class StreamConnInfo
{
/// <summary>
/// 虚拟主机
/// </summary>
public string Vhost { get; set; }
/// <summary>
/// 应用名
/// </summary>
public string App { get; set; }
/// <summary>
/// 流id
/// </summary>
public string Stream { get; set; }
public static StreamConnInfo New(string vhost, string app, string stream)
{
return new() { Vhost = vhost, App = app, Stream = stream };
}
}

66
Cis.Application/Core/Component/ZLMediaKit/Entity/ZlmException.cs

@ -0,0 +1,66 @@
using System.Text;
namespace Cis.Application.Core.Component.ZLMediaKit;
public class ZlmException : Exception
{
public ZlmException() : base()
{
}
public ZlmException(string message) : base(message)
{
}
public ZlmException(string message, Exception innerException) : base(message, innerException)
{
}
protected class ZlmExceptionObj
{
public ZlmCode Code { get; set; }
public string Msg { get; set; }
public int Result { get; set; }
public override string ToString()
{
StringBuilder builder = new();
builder.Append($"Code:{Code.ToInt()}({Code}), Msg:{Msg}");
if (Result < 0) builder.Append($", Result:{Result}");
return builder.ToString();
}
}
public static ZlmException New(ZlmCode code, string msg)
{
ZlmExceptionObj obj = new()
{
Code = code,
Msg = msg
};
return new ZlmException(obj.ToString());
}
public static ZlmException New(ZlmCode code, string msg, int result)
{
ZlmExceptionObj obj = new()
{
Code = code,
Msg = msg,
Result = result
};
return new ZlmException(obj.ToString());
}
}
public enum ZlmCode
{
Success = 0, //执行成功
OtherFailed = -1, //业务代码执行失败
AuthFailed = -100, //鉴权失败
SqlFailed = -200, //sql执行失败
InvalidArgs = -300, //参数不合法
Exception = -400, //代码抛异常
}

18
Cis.Application/Core/Component/ZLMediaKit/IZlmServer.cs

@ -0,0 +1,18 @@
namespace Cis.Application.Core.Component.ZLMediaKit;
public interface IZlmServer
{
#region Base Method
public Task<StreamConnInfo> AddStreamProxy(string stream, string rtspUrl);
public Task<bool> DelStreamProxy(string stream);
public Task<object> GetMediaList(string stream);
public Task<bool> IsMediaOnline(string stream);
public StreamConnInfo GetStreamConnInfo(string stream);
#endregion Base Method
}

127
Cis.Application/Core/Component/ZLMediaKit/ZlmServer.cs

@ -0,0 +1,127 @@
using Furion.RemoteRequest.Extensions;
using Newtonsoft.Json.Linq;
namespace Cis.Application.Core.Component.ZLMediaKit;
public class ZlmServer : IZlmServer, ISingleton
{
#region Attr
private readonly ZLMediaKitOptions _options;
#region Url
private string AddStreamProxyUrl { get; set; }
private string DelStreamProxyUrl { get; set; }
private string GetMediaListUrl { get; set; }
private string IsMediaOnlineUrl { get; set; }
#endregion Url
#endregion Attr
public ZlmServer()
{
_options = App.GetOptions<ZLMediaKitOptions>();
InitUrl();
}
#region Util Method
private void InitUrl()
{
string baseUrl = $"http://{_options.Ip}:{_options.Port}";
AddStreamProxyUrl = $"{baseUrl}/index/api/addStreamProxy";
DelStreamProxyUrl = $"{baseUrl}/index/api/delStreamProxy";
GetMediaListUrl = $"{baseUrl}/index/api/getMediaList";
IsMediaOnlineUrl = $"{baseUrl}/index/api/isMediaOnline";
}
private bool JudgeResult(JObject data)
{
ZlmCode code = (ZlmCode)data["code"].ToInt();
if (code == ZlmCode.Success) return false;
string msg = data["msg"].ToString();
int result = data["result"].ToInt();
throw ZlmException.New(code, msg, result);
}
#endregion Util Method
#region Base Method
public async Task<StreamConnInfo> AddStreamProxy(string stream, string rtspUrl)
{
string resp = await AddStreamProxyUrl.SetBody(new
{
secret = _options.Secret,
vhost = _options.DefaultVhost,
app = _options.DefaultApp,
stream = stream,
url = rtspUrl,
rtp_type = _options.RtpType,
timeout_sec = _options.TimeoutSec,
retry_count = _options.RetryCount,
enable_rtmp = 1,
enable_hls = 0,
enable_mp4 = 0,
enable_ts = 0,
enable_fmp4 = 0,
}).PostAsStringAsync();
JObject respJObj = resp.ToJObject();
JudgeResult(respJObj);
return StreamConnInfo.New(_options.DefaultVhost, _options.DefaultApp, stream);
}
public async Task<bool> DelStreamProxy(string stream)
{
string resp = await DelStreamProxyUrl.SetBody(new
{
secret = _options.Secret,
key = $"{_options.DefaultVhost}/{_options.DefaultApp}/{stream}",
}).PostAsStringAsync();
JObject respJObj = resp.ToJObject();
JudgeResult(respJObj);
return respJObj["data"]["flag"].ToBoolean();
}
public async Task<object> GetMediaList(string stream)
{
string resp = await GetMediaListUrl.SetBody(new
{
secret = _options.Secret,
vhost = _options.DefaultVhost,
app = _options.DefaultApp,
stream = stream,
schema = "rtsp",
}).PostAsStringAsync();
JObject respJObj = resp.ToJObject();
JudgeResult(respJObj);
return respJObj["data"]?.ToObject<object>();
}
public async Task<bool> IsMediaOnline(string stream)
{
string resp = await IsMediaOnlineUrl.SetBody(new
{
secret = _options.Secret,
vhost = _options.DefaultVhost,
app = _options.DefaultApp,
stream = stream,
schema = "rtsp",
}).PostAsStringAsync();
JObject respJObj = resp.ToJObject();
JudgeResult(respJObj);
return respJObj["online"].ToBoolean();
}
public StreamConnInfo GetStreamConnInfo(string stream)
{
return StreamConnInfo.New(_options.DefaultVhost, _options.DefaultApp, stream);
}
#endregion Base Method
}

15
Cis.Application/Core/Service/OnvifService.cs

@ -14,11 +14,11 @@ public class OnvifService : IDynamicApiController, ITransient
private readonly SqlSugarRepository<CbCamera> _cbCameraRep;
private readonly OnvifServer _onvifServer;
private readonly IOnvifServer _onvifServer;
#endregion
public OnvifService(SqlSugarRepository<CbCamera> cbCameraRep, OnvifServer onvifServer)
public OnvifService(SqlSugarRepository<CbCamera> cbCameraRep, IOnvifServer onvifServer)
{
_cbCameraRep = cbCameraRep;
_onvifServer = onvifServer;
@ -75,6 +75,7 @@ public class OnvifService : IDynamicApiController, ITransient
/// <param name="position">变焦移动绝对点:[-1,1]</param>
/// <returns></returns>
[HttpPost]
[ApiDescriptionSettings(Description = "cameraId:cbCameraId<br/>position:变焦移动绝对点:[-1,1]")]
public async Task<bool> FocusAbsoluteMove([Required][FromForm] long cameraId, [Required][FromForm] float position)
{
bool ret = _onvifServer.TryGet(cameraId, out OnvifClient client);
@ -90,6 +91,7 @@ public class OnvifService : IDynamicApiController, ITransient
/// <param name="distance">变焦移动相对点:[-1,1]</param>
/// <returns></returns>
[HttpPost]
[ApiDescriptionSettings(Description = "cameraId:cbCameraId<br/>distance:变焦移动相对点:[-1,1]")]
public async Task<bool> FocusRelativeMove([Required][FromForm] long cameraId, [Required][FromForm] float distance)
{
bool ret = _onvifServer.TryGet(cameraId, out OnvifClient client);
@ -105,6 +107,7 @@ public class OnvifService : IDynamicApiController, ITransient
/// <param name="speed">持续移动方向:[-1,1]</param>
/// <returns></returns>
[HttpPost]
[ApiDescriptionSettings(Description = "cameraId:cbCameraId<br/>speed:持续移动方向:[-1,1]")]
public async Task<bool> FocusContinuousMove([Required][FromForm] long cameraId, [Required][FromForm] float speed)
{
bool ret = _onvifServer.TryGet(cameraId, out OnvifClient client);
@ -118,6 +121,7 @@ public class OnvifService : IDynamicApiController, ITransient
/// </summary>
/// <param name="cameraId">cbCameraId</param>
[HttpPost]
[ApiDescriptionSettings(Description = "cameraId:cbCameraId")]
public async Task<bool> FocusStopMove([Required][FromForm] long cameraId)
{
bool ret = _onvifServer.TryGet(cameraId, out OnvifClient client);
@ -186,6 +190,8 @@ public class OnvifService : IDynamicApiController, ITransient
/// <param name="atomDist">可以理解为移动速度:[0,1],默认 0.1</param>
/// <returns></returns>
[HttpPost]
[ApiDescriptionSettings(Description = "cameraId:cbCameraId<br/>pan:水平方向移动绝对点:[-1,1]<br/>tilt:[-1,1]<br/>" +
"zoom:变倍绝对点:[-1,1]<br/>atomDist:可以理解为移动速度:[0,1],默认 0.1")]
public async Task<bool> AbsoluteMove([Required][FromForm] long cameraId,
[Required][FromForm] float pan, [Required][FromForm] float tilt, [Required][FromForm] float zoom, [FromForm] float atomDist = 0.1f)
{
@ -204,6 +210,8 @@ public class OnvifService : IDynamicApiController, ITransient
/// <param name="zoom">变倍相对点:[-1,1]</param>
/// <param name="atomSpeed">移动速度:[0,1],默认 0.1</param>
[HttpPost]
[ApiDescriptionSettings(Description = "cameraId:cbCameraId<br/>pan:水平方向移动相对点:[-1,1]<br/> tilt:[-1,1]<br/>" +
"zoom:变倍相对点:[-1,1]<br/>atomSpeed:移动速度:[0,1],默认 0.1")]
public async Task<bool> RelativeMove([Required][FromForm] long cameraId,
[Required][FromForm] float pan, [Required][FromForm] float tilt, [Required][FromForm] float zoom, [FromForm] float atomSpeed = 0.1f)
{
@ -222,6 +230,8 @@ public class OnvifService : IDynamicApiController, ITransient
/// <param name="zoom">变倍移动方向:[-1,1]</param>
/// <param name="timeout">超时时间,ms</param>
[HttpPost]
[ApiDescriptionSettings(Description = "cameraId:cbCameraId<br/>pan:水平方向移动方向:[-1,1]<br/>tilt:[-1,1]<br/>" +
"zoom:变倍移动方向:[-1,1]<br/>timeout:超时时间,ms")]
public async Task<bool> ContinuousMove([Required][FromForm] long cameraId,
[Required][FromForm] float pan, [Required][FromForm] float tilt, [Required][FromForm] float zoom, [FromForm] string timeout = "")
{
@ -236,6 +246,7 @@ public class OnvifService : IDynamicApiController, ITransient
/// </summary>
/// <param name="cameraId">cbCameraId</param>
[HttpPost]
[ApiDescriptionSettings(Description = "cameraId:cbCameraId")]
public async Task<bool> StopMove([Required][FromForm] long cameraId)
{
bool ret = _onvifServer.TryGet(cameraId, out OnvifClient client);

111
Cis.Application/Core/Service/ZlmService.cs

@ -0,0 +1,111 @@
using Cis.Application.Cb;
using Cis.Application.Core.Component.Onvif;
using Cis.Application.Core.Component.ZLMediaKit;
using EC.Helper.Onvif;
using Furion.DataEncryption;
namespace Cis.Application.Core.Service;
/// <summary>
/// zlmediakit 服务
/// </summary>
[ApiDescriptionSettings(CoreInfo.GroupName, Order = CoreInfo.ZlmGroupOrder)]
public class ZlmService : IDynamicApiController, ITransient
{
#region Attr
private readonly SqlSugarRepository<CbCamera> _cbCameraRep;
private readonly IZlmServer _zlmServer;
private readonly IOnvifServer _onvifServer;
#endregion Attr
public ZlmService(SqlSugarRepository<CbCamera> cbCameraRep, IZlmServer zlmServer, IOnvifServer onvifServer)
{
_cbCameraRep = cbCameraRep;
_zlmServer = zlmServer;
_onvifServer = onvifServer;
}
#region Base Method
/// <summary>
/// 添加码流拉流代理(只支持H264/H265/aac/G711负载)
/// </summary>
/// <param name="cameraId">cbCameraId</param>
/// <param name="streamLevel">码流级别[0,2], default:0</param>
/// <returns></returns>
[HttpPost]
[ApiDescriptionSettings(Description = "cameraId:cbCameraId<br/>streamLevel:码流级别[0,2],default:0")]
public async Task<StreamConnInfo> AddStreamProxy([Required][FromForm] long cameraId, [FromForm][Range(0, 2)] int streamLevel = 0)
{
CbCamera camera = await _cbCameraRep.GetByIdAsync(cameraId);
if (camera == null) return default;
string stream = MD5Encryption.Encrypt($"{camera.Ip}:{streamLevel}");
bool isOnline = await _zlmServer.IsMediaOnline(stream);
if (isOnline) return _zlmServer.GetStreamConnInfo(stream);
bool ret = _onvifServer.IsExists(camera.Id);
if (!ret) ret = await _onvifServer.RegisterAsync(camera);
if (!ret) return default;
ret = _onvifServer.TryGet(cameraId, out OnvifClient client);
if (!ret) return default;
string rtspUrl = streamLevel switch
{
0 => await client.GetStreamUrl(),
1 => await client.GetSubStreamUrl(),
2 => await client.GetThirdStreamUrl(),
_ => await client.GetStreamUrl()
};
return await _zlmServer.AddStreamProxy(stream, rtspUrl);
}
/// <summary>
/// 关闭拉流代理
/// </summary>
/// <param name="cameraId">cbCameraId</param>
/// <param name="streamLevel">码流级别[0,2], default:0</param>
/// <returns></returns>
[HttpPost]
[ApiDescriptionSettings(Description = "cameraId:cbCameraId<br/>streamLevel:码流级别[0,2],default:0")]
public async Task<bool> DelStreamProxy([Required][FromForm] long cameraId, [FromForm][Range(0, 2)] int streamLevel = 0)
{
CbCamera camera = await _cbCameraRep.GetByIdAsync(cameraId);
if (camera == null) return false;
string stream = MD5Encryption.Encrypt($"{camera.Ip}:{streamLevel}");
return await _zlmServer.DelStreamProxy(stream);
}
/// <summary>
/// 获取流列表
/// </summary>
/// <param name="cameraId">cbCameraId</param>
/// <param name="streamLevel">码流级别[0,2], default:0</param>
[HttpPost]
[ApiDescriptionSettings(Description = "cameraId:cbCameraId<br/>streamLevel:码流级别[0,2],default:0")]
public async Task<object> GetMediaList([Required][FromForm] long cameraId, [FromForm][Range(0, 2)] int streamLevel = 0)
{
CbCamera camera = await _cbCameraRep.GetByIdAsync(cameraId);
if (camera == null) return default;
string stream = MD5Encryption.Encrypt($"{camera.Ip}:{streamLevel}");
return await _zlmServer.GetMediaList(stream);
}
/// <summary>
/// 判断直播流是否在线
/// </summary>
/// <param name="cameraId">cbCameraId</param>
/// <param name="streamLevel">码流级别[0,2], default:0</param>
[HttpPost]
[ApiDescriptionSettings(Description = "cameraId:cbCameraId<br/>streamLevel:码流级别[0,2],default:0")]
public async Task<bool> IsMediaOnline([Required][FromForm] long cameraId, [FromForm][Range(0, 2)] int streamLevel = 0)
{
CbCamera camera = await _cbCameraRep.GetByIdAsync(cameraId);
if (camera == null) return false;
string stream = MD5Encryption.Encrypt($"{camera.Ip}:{streamLevel}");
return await _zlmServer.IsMediaOnline(stream);
}
#endregion Base Method
}

2
Cis.Application/Startup.cs

@ -3,6 +3,7 @@ using Cis.Application.Core.Component.CameraSDK;
using Cis.Application.Core.Component.MarkSeacher;
using Cis.Application.Core.Component.Onvif;
using Cis.Application.Core.Component.PtzServer;
using Cis.Application.Core.Component.ZLMediaKit;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
@ -22,6 +23,7 @@ public class Startup : AppStartup
services.AddSingleton<ICameraSdkServer, CameraSdkServer>();
services.AddSingleton<IOnvifServer, OnvifServer>();
services.AddSingleton<IZlmServer, ZlmServer>();
services.AddSingleton<IMarkSearcherServer, MarkSearcherServer>();
services.AddSingleton(typeof(CameraDataCenter));
}

2
Cis.Core/Cis.Core.csproj

@ -34,6 +34,6 @@
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
</ItemGroup>
<ProjectExtensions><VisualStudio><UserProperties coreconfig_1json__JsonSchema="https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json" /></VisualStudio></ProjectExtensions>
<ProjectExtensions><VisualStudio><UserProperties coreconfig_1json__JsonSchema="" /></VisualStudio></ProjectExtensions>
</Project>

40
Cis.Core/Cis.Core.xml

@ -403,6 +403,46 @@
机器码
</summary>
</member>
<member name="P:Cis.Core.ZLMediaKitOptions.Ip">
<summary>
服务IP
</summary>
</member>
<member name="P:Cis.Core.ZLMediaKitOptions.Port">
<summary>
服务端口
</summary>
</member>
<member name="P:Cis.Core.ZLMediaKitOptions.Secret">
<summary>
服务鉴权秘钥
</summary>
</member>
<member name="P:Cis.Core.ZLMediaKitOptions.DefaultVhost">
<summary>
默认虚拟主机
</summary>
</member>
<member name="P:Cis.Core.ZLMediaKitOptions.DefaultApp">
<summary>
默认应用名
</summary>
</member>
<member name="P:Cis.Core.ZLMediaKitOptions.RtpType">
<summary>
rtsp拉流时,拉流方式
</summary>
</member>
<member name="P:Cis.Core.ZLMediaKitOptions.TimeoutSec">
<summary>
拉流超时时间,单位秒,float类型
</summary>
</member>
<member name="P:Cis.Core.ZLMediaKitOptions.RetryCount">
<summary>
拉流重试次数
</summary>
</member>
<member name="T:Cis.Core.IEntityFilter">
<summary>
自定义实体过滤器接口

44
Cis.Core/Common/Option/ZLMediaKitOptions.cs

@ -0,0 +1,44 @@
namespace Cis.Core;
public class ZLMediaKitOptions : IConfigurableOptions
{
/// <summary>
/// 服务IP
/// </summary>
public string Ip { get; set; }
/// <summary>
/// 服务端口
/// </summary>
public string Port { get; set; }
/// <summary>
/// 服务鉴权秘钥
/// </summary>
public string Secret { get; set; }
/// <summary>
/// 默认虚拟主机
/// </summary>
public string DefaultVhost { get; set; }
/// <summary>
/// 默认应用名
/// </summary>
public string DefaultApp { get; set; }
/// <summary>
/// rtsp拉流时,拉流方式
/// </summary>
public string RtpType { get; set; }
/// <summary>
/// 拉流超时时间,单位秒,float类型
/// </summary>
public float TimeoutSec { get; set; }
/// <summary>
/// 拉流重试次数
/// </summary>
public int RetryCount { get; set; }
}

11
Cis.Core/CoreConfig.json

@ -1,4 +1,5 @@
{
"$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
"DbConnection": {
"ConnectionConfigs": [
{
@ -16,6 +17,16 @@
"Redis": {
"ConnectionString": "192.168.1.119:6379,password=123456,defaultDatabase=2"
},
"ZLMediaKit": {
"Ip": "192.168.1.119", //ip
"Port": "8080", //
"Secret": "035c73f7-bb6b-4889-a715-d9eb2d1925cc", //
"DefaultVhost": "__defaultVhost__", //
"DefaultApp": "live", //
"RtpType": "0", //rtsp0tcp1udp2
"TimeoutSec": 5, //float
"RetryCount": 3 //
},
"AppSettings": {
"InjectSpecificationDocument": true // Swagger
},

1
Cis.Web.Core/ProjectOptions.cs

@ -15,6 +15,7 @@ public static class ProjectOptions
services.AddConfigurableOptions<DbConnectionOptions>();
services.AddConfigurableOptions<RedisOptions>();
services.AddConfigurableOptions<SnowIdOptions>();
services.AddConfigurableOptions<ZLMediaKitOptions>();
return services;
}

23
EC.Helper/CameraSDK/Common/CameraException.cs

@ -1,4 +1,6 @@
namespace EC.Helper.CameraSDK;
using System.Text;
namespace EC.Helper.CameraSDK;
/// <summary>
/// 相机异常
@ -21,33 +23,36 @@ public class CameraException : Exception
{
public CameraManufactor Manufactor { get; set; }
public int ErrCode { get; set; }
public int Code { get; set; }
public string? ErrMsg { get; set; }
public string? Msg { get; set; }
public override string? ToString()
{
return $"Manufactor:{Manufactor}, ErrCode:{ErrCode}, ErrMsg:{ErrMsg}";
StringBuilder builder = new();
builder.Append($"Manufactor:{Manufactor}, Code:{Code}");
if (!string.IsNullOrEmpty(Msg)) builder.Append($", Msg:{Msg}");
return builder.ToString();
}
}
public static CameraException New(CameraManufactor manufactor, int errCode)
public static CameraException New(CameraManufactor manufactor, int code)
{
CameraExceptionObj obj = new()
{
Manufactor = manufactor,
ErrCode = errCode
Code = code
};
return new CameraException(obj.ToString());
}
public static CameraException New(CameraManufactor manufactor, int errCode, string errMsg)
public static CameraException New(CameraManufactor manufactor, int code, string msg)
{
CameraExceptionObj obj = new()
{
Manufactor = manufactor,
ErrCode = errCode,
ErrMsg = errMsg
Code = code,
Msg = msg
};
return new CameraException(obj.ToString());
}

38
EC.Helper/Onvif/OnvifClient.cs

@ -38,10 +38,18 @@ public class OnvifClient
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
@ -69,9 +77,19 @@ public class OnvifClient
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)
{
@ -125,6 +143,26 @@ public class OnvifClient
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))

Loading…
Cancel
Save