diff --git a/Cis.Application/Cis.Application.xml b/Cis.Application/Cis.Application.xml
index 51a88ed..062d2e3 100644
--- a/Cis.Application/Cis.Application.xml
+++ b/Cis.Application/Cis.Application.xml
@@ -530,6 +530,11 @@
Onvif Api 分组排序
+
+
+ ZLMediaKit Api 分组排序
+
+
循环间隔,单位毫秒
@@ -1051,6 +1056,21 @@
{cameraId, OnvifClient}
+
+
+ 虚拟主机
+
+
+
+
+ 应用名
+
+
+
+
+ 流id
+
+
标签追踪服务
@@ -1222,6 +1242,41 @@
cbCameraId
+
+
+ zlmediakit 服务
+
+
+
+
+ 添加码流拉流代理(只支持H264/H265/aac/G711负载)
+
+ cbCameraId
+ 码流级别[0,2], default:0
+
+
+
+
+ 关闭拉流代理
+
+ cbCameraId
+ 码流级别[0,2], default:0
+
+
+
+
+ 获取流列表
+
+ cbCameraId
+ 码流级别[0,2], default:0
+
+
+
+ 判断直播流是否在线
+
+ cbCameraId
+ 码流级别[0,2], default:0
+
配置应用所需服务,在该方法中可以添加应用所需要的功能或服务
diff --git a/Cis.Application/Core/Common/CoreInfo.cs b/Cis.Application/Core/Common/CoreInfo.cs
index d39092b..22a0271 100644
--- a/Cis.Application/Core/Common/CoreInfo.cs
+++ b/Cis.Application/Core/Common/CoreInfo.cs
@@ -19,5 +19,10 @@ public class CoreInfo
///
public const int OnvifGroupOrder = 100;
+ ///
+ /// ZLMediaKit Api 分组排序
+ ///
+ public const int ZlmGroupOrder = 100;
+
#endregion Api Info
}
\ No newline at end of file
diff --git a/Cis.Application/Core/Component/ZLMediaKit/Entity/StreamConnInfo.cs b/Cis.Application/Core/Component/ZLMediaKit/Entity/StreamConnInfo.cs
new file mode 100644
index 0000000..cedcc23
--- /dev/null
+++ b/Cis.Application/Core/Component/ZLMediaKit/Entity/StreamConnInfo.cs
@@ -0,0 +1,24 @@
+namespace Cis.Application.Core.Component.ZLMediaKit;
+
+public class StreamConnInfo
+{
+ ///
+ /// 虚拟主机
+ ///
+ public string Vhost { get; set; }
+
+ ///
+ /// 应用名
+ ///
+ public string App { get; set; }
+
+ ///
+ /// 流id
+ ///
+ public string Stream { get; set; }
+
+ public static StreamConnInfo New(string vhost, string app, string stream)
+ {
+ return new() { Vhost = vhost, App = app, Stream = stream };
+ }
+}
\ No newline at end of file
diff --git a/Cis.Application/Core/Component/ZLMediaKit/Entity/ZlmException.cs b/Cis.Application/Core/Component/ZLMediaKit/Entity/ZlmException.cs
new file mode 100644
index 0000000..f23d1a0
--- /dev/null
+++ b/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, //代码抛异常
+}
\ No newline at end of file
diff --git a/Cis.Application/Core/Component/ZLMediaKit/IZlmServer.cs b/Cis.Application/Core/Component/ZLMediaKit/IZlmServer.cs
new file mode 100644
index 0000000..dc5dd69
--- /dev/null
+++ b/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 AddStreamProxy(string stream, string rtspUrl);
+
+ public Task DelStreamProxy(string stream);
+
+ public Task
+
+
+ 服务IP
+
+
+
+
+ 服务端口
+
+
+
+
+ 服务鉴权秘钥
+
+
+
+
+ 默认虚拟主机
+
+
+
+
+ 默认应用名
+
+
+
+
+ rtsp拉流时,拉流方式
+
+
+
+
+ 拉流超时时间,单位秒,float类型
+
+
+
+
+ 拉流重试次数
+
+
自定义实体过滤器接口
diff --git a/Cis.Core/Common/Option/ZLMediaKitOptions.cs b/Cis.Core/Common/Option/ZLMediaKitOptions.cs
new file mode 100644
index 0000000..88176b1
--- /dev/null
+++ b/Cis.Core/Common/Option/ZLMediaKitOptions.cs
@@ -0,0 +1,44 @@
+namespace Cis.Core;
+
+public class ZLMediaKitOptions : IConfigurableOptions
+{
+ ///
+ /// 服务IP
+ ///
+ public string Ip { get; set; }
+
+ ///
+ /// 服务端口
+ ///
+ public string Port { get; set; }
+
+ ///
+ /// 服务鉴权秘钥
+ ///
+ public string Secret { get; set; }
+
+ ///
+ /// 默认虚拟主机
+ ///
+ public string DefaultVhost { get; set; }
+
+ ///
+ /// 默认应用名
+ ///
+ public string DefaultApp { get; set; }
+
+ ///
+ /// rtsp拉流时,拉流方式
+ ///
+ public string RtpType { get; set; }
+
+ ///
+ /// 拉流超时时间,单位秒,float类型
+ ///
+ public float TimeoutSec { get; set; }
+
+ ///
+ /// 拉流重试次数
+ ///
+ public int RetryCount { get; set; }
+}
\ No newline at end of file
diff --git a/Cis.Core/CoreConfig.json b/Cis.Core/CoreConfig.json
index 485f006..afe0617 100644
--- a/Cis.Core/CoreConfig.json
+++ b/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", //rtsp拉流时,拉流方式,0:tcp,1:udp,2:组播
+ "TimeoutSec": 5, //拉流超时时间,单位秒,float类型
+ "RetryCount": 3 //拉流重试次数
+ },
"AppSettings": {
"InjectSpecificationDocument": true // 生产环境是否开启Swagger
},
diff --git a/Cis.Web.Core/ProjectOptions.cs b/Cis.Web.Core/ProjectOptions.cs
index 6fdaaee..13cf371 100644
--- a/Cis.Web.Core/ProjectOptions.cs
+++ b/Cis.Web.Core/ProjectOptions.cs
@@ -15,6 +15,7 @@ public static class ProjectOptions
services.AddConfigurableOptions();
services.AddConfigurableOptions();
services.AddConfigurableOptions();
+ services.AddConfigurableOptions();
return services;
}
diff --git a/EC.Helper/CameraSDK/Common/CameraException.cs b/EC.Helper/CameraSDK/Common/CameraException.cs
index e2a392e..2e7c9e6 100644
--- a/EC.Helper/CameraSDK/Common/CameraException.cs
+++ b/EC.Helper/CameraSDK/Common/CameraException.cs
@@ -1,4 +1,6 @@
-namespace EC.Helper.CameraSDK;
+using System.Text;
+
+namespace EC.Helper.CameraSDK;
///
/// 相机异常
@@ -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());
}
diff --git a/EC.Helper/Onvif/OnvifClient.cs b/EC.Helper/Onvif/OnvifClient.cs
index 9c5b50a..9d129ed 100644
--- a/EC.Helper/Onvif/OnvifClient.cs
+++ b/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 GetSubStreamUrl()
+ {
+ if (string.IsNullOrEmpty(SubSteamUrl))
+ {
+ MediaUri mediaUri = await Media.GetStreamUriAsync(RtspStreamSetup, SubProfileToken);
+ SubSteamUrl = mediaUri.Uri.Replace("://", $"://{Username}:{Password}@");
+ }
+ return SubSteamUrl;
+ }
+
+ public async Task GetThirdStreamUrl()
+ {
+ if (string.IsNullOrEmpty(ThirdSteamUrl))
+ {
+ MediaUri mediaUri = await Media.GetStreamUriAsync(RtspStreamSetup, SubProfileToken);
+ ThirdSteamUrl = mediaUri.Uri.Replace("://", $"://{Username}:{Password}@");
+ }
+ return ThirdSteamUrl;
+ }
+
public async Task GetSnapshotUrl()
{
if (string.IsNullOrEmpty(SnapshotUrl))