From a47d14e3953cc09f1ec824b989271f67ce0022a8 Mon Sep 17 00:00:00 2001
From: fajiao <1519100073@qq.com>
Date: Thu, 5 Jan 2023 18:12:50 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=B0=83=E7=94=A8=20?=
=?UTF-8?q?ZLMediaKit=20=E6=9C=8D=E5=8A=A1=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Cis.Application/Cis.Application.xml | 55 ++++++++
Cis.Application/Core/Common/CoreInfo.cs | 5 +
.../ZLMediaKit/Entity/StreamConnInfo.cs | 24 ++++
.../ZLMediaKit/Entity/ZlmException.cs | 66 +++++++++
.../Core/Component/ZLMediaKit/IZlmServer.cs | 18 +++
.../Core/Component/ZLMediaKit/ZlmServer.cs | 127 ++++++++++++++++++
Cis.Application/Core/Service/OnvifService.cs | 15 ++-
Cis.Application/Core/Service/ZlmService.cs | 111 +++++++++++++++
Cis.Application/Startup.cs | 2 +
Cis.Core/Cis.Core.csproj | 2 +-
Cis.Core/Cis.Core.xml | 40 ++++++
Cis.Core/Common/Option/ZLMediaKitOptions.cs | 44 ++++++
Cis.Core/CoreConfig.json | 11 ++
Cis.Web.Core/ProjectOptions.cs | 1 +
EC.Helper/CameraSDK/Common/CameraException.cs | 23 ++--
EC.Helper/Onvif/OnvifClient.cs | 38 ++++++
16 files changed, 570 insertions(+), 12 deletions(-)
create mode 100644 Cis.Application/Core/Component/ZLMediaKit/Entity/StreamConnInfo.cs
create mode 100644 Cis.Application/Core/Component/ZLMediaKit/Entity/ZlmException.cs
create mode 100644 Cis.Application/Core/Component/ZLMediaKit/IZlmServer.cs
create mode 100644 Cis.Application/Core/Component/ZLMediaKit/ZlmServer.cs
create mode 100644 Cis.Application/Core/Service/ZlmService.cs
create mode 100644 Cis.Core/Common/Option/ZLMediaKitOptions.cs
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))