diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..a239b7b Binary files /dev/null and b/.DS_Store differ diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..90dfe8f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +[Bb]in/ +[Oo]bj/ +[Ll]og/ diff --git a/CommonFunctions/DBStructs/StreamDvrPlan.cs b/CommonFunctions/DBStructs/StreamDvrPlan.cs index 6b3ad9f..5812d20 100644 --- a/CommonFunctions/DBStructs/StreamDvrPlan.cs +++ b/CommonFunctions/DBStructs/StreamDvrPlan.cs @@ -37,6 +37,8 @@ public class StreamDvrPlan public long? LimitSpace { get; set; } public int? LimitDays { get; set; } + + [Column(MapType = typeof(string))] public OverStepPlan? OverStepPlan { get; set; } [Navigate(nameof(DvrDayTimeRange.StreamDvrPlanId))] diff --git a/CommonFunctions/MediaServerControl/MediaServerInstance.cs b/CommonFunctions/MediaServerControl/MediaServerInstance.cs index a6d2597..a82ef99 100644 --- a/CommonFunctions/MediaServerControl/MediaServerInstance.cs +++ b/CommonFunctions/MediaServerControl/MediaServerInstance.cs @@ -25,6 +25,7 @@ public class MediaServerInstance private DateTime _updateTime; //最后控制时间 private DateTime _keepAlive; private ZLMediaKitWebApiHelper _webApi; //流媒体服务操作类 + private string? _recordFilePath;//保存录制文件的目录 private ZLMediaKitConfigForResponse _config = new ZLMediaKitConfigForResponse(); //流媒体服务器配置 //private List _onlineClientSessionList = new List(); @@ -114,6 +115,12 @@ private double getUpTime() return 0; } + public string? RecordFilePath + { + get => _recordFilePath; + set => _recordFilePath = value; + } + public ZLMediaKitConfigForResponse Config { get => _config; @@ -264,7 +271,7 @@ private void checkStreamStatusNew() }*/ public MediaServerInstance(string ipaddress, ushort webApiPort, ushort mediaServerHttpPort, string secret, - string mediaServerId) + string mediaServerId,string recordFilePath) { _ipaddress = ipaddress; _webApiPort = webApiPort; @@ -273,6 +280,7 @@ public MediaServerInstance(string ipaddress, ushort webApiPort, ushort mediaServ _mediaServerId = mediaServerId; _webApi = new ZLMediaKitWebApiHelper(_ipaddress, mediaServerHttpPort, _secret); _updateTime = DateTime.Now; + _recordFilePath = recordFilePath; new Thread(new ThreadStart(delegate { diff --git a/CommonFunctions/WebApiStructs/Response/ResMediaServerWebApiReg.cs b/CommonFunctions/WebApiStructs/Response/ResMediaServerWebApiReg.cs index b3aff4d..b8b79fb 100644 --- a/CommonFunctions/WebApiStructs/Response/ResMediaServerWebApiReg.cs +++ b/CommonFunctions/WebApiStructs/Response/ResMediaServerWebApiReg.cs @@ -10,6 +10,7 @@ public class ResMediaServerWebApiReg private string? _ipaddress; private string _mediaServerId; private string _secret; + private string? _recordFilePath; public ushort MediaServerHttpPort @@ -42,5 +43,11 @@ public string Secret get => _secret; set => _secret = value; } + + public string? RecordFilePath + { + get => _recordFilePath; + set => _recordFilePath = value; + } } } \ No newline at end of file diff --git a/README.md b/README.md index 1f39d26..9f9bc26 100644 --- a/README.md +++ b/README.md @@ -315,6 +315,52 @@ NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastStreamNoneRead ~~~ +-src/Record/Recorder.cpp +~~~c++ +string Recorder::getRecordPath(Recorder::type type, const string &vhost, const string &app, const string &stream_id, const string &customized_path) { + GET_CONFIG(bool, enableVhost, General::kEnableVhost); + switch (type) { + case Recorder::type_hls: { + GET_CONFIG(string, hlsPath, Hls::kFilePath); + string m3u8FilePath; + if (enableVhost) { + m3u8FilePath = vhost + "/" + app + "/" + stream_id + "/hls.m3u8"; + } else { + m3u8FilePath = app + "/" + stream_id + "/hls.m3u8"; + } + //Here we use the customized file path. + if (!customized_path.empty()) { + m3u8FilePath = customized_path + "/hls.m3u8"; + } + return File::absolutePath(m3u8FilePath, hlsPath); + } + case Recorder::type_mp4: { + GET_CONFIG(string, recordPath, Record::kFilePath); + GET_CONFIG(string, recordAppName, Record::kAppName); + string mp4FilePath; + if (enableVhost) { + mp4FilePath = vhost + "/" + recordAppName + "/" + app + "/" + stream_id + "/"; + } else { + mp4FilePath = recordAppName + "/" + app + "/" + stream_id + "/"; + } + //Here we use the customized file path. + if (!customized_path.empty()) { + /*开始删除*/ + // mp4FilePath = customized_path + "/"; + /*删除结束*/ + /*开始添加*/ + return customized_path + "/"+mp4FilePath; + /*开始添加*/ + } + + return File::absolutePath(mp4FilePath, recordPath); + } + default: + return ""; + } +} +~~~ + # 组成部分 ## StreamNodeWebApi - 全局的流媒体管理API服务,包含了所有流媒体功能的控制,如摄像头注册,录制计划,rtp推流,ptz控制等。 @@ -367,6 +413,8 @@ NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastStreamNoneRead ~~~ + + ## StreamNodeWebApi/system.conf - StreamNodeWebApi的配置文件,参数名与参数值以::分开,每行以;结束 - 数据库方面采用CodeFirst 模式,在数据库中建立一个名为streamnode的库,数据表会自动创建 @@ -386,6 +434,7 @@ MediaServerBinPath::/root/MediaService/MediaServer;//ZLMediaKit流媒体服务 StreamNodeServerUrl::http://192.168.2.43:5800/WebHook/MediaServerRegister; //向哪个StreamNodeWebApi注册自己的服务 HttpPort::6880;//服务的WebApi端口 IpAddress::192.168.2.43;//本机ip地址 +CustomizedRecordFilePath::/home/cdtnb; //自定义存储视频的位置 ~~~ diff --git a/StreamMediaServerKeeper/Common.cs b/StreamMediaServerKeeper/Common.cs index 0bd331a..6ed6889 100644 --- a/StreamMediaServerKeeper/Common.cs +++ b/StreamMediaServerKeeper/Common.cs @@ -31,6 +31,10 @@ public static class Common public static string MediaServerId = null!; public static ProcessApis ProcessApis = new ProcessApis(); public static string MyIPAddress = ""; + /// + /// 自定义的录制文件存储位置 + /// + public static string CustomizedRecordFilePath = ""; public static LogMonitor LogMonitor= new LogMonitor(); @@ -156,6 +160,8 @@ private static void getMediaServerConfig() "http://" + streamNodeUri.Host + ":" + streamNodeUri.Port + "/WebHook/OnPublish"; //有流发布时 data["hook"]["on_record_mp4"] = "http://" + streamNodeUri.Host + ":" + streamNodeUri.Port + "/WebHook/OnRecordMp4Completed"; //当录制mp4完成时 + data["hook"]["on_record_ts"]= + "http://" + streamNodeUri.Host + ":" + streamNodeUri.Port + "/WebHook/OnRecordTsCompleted"; //当录制ts完成时 data["hook"]["on_rtsp_auth"] = ""; //rtsp鉴权,不作支持 data["hook"]["on_rtsp_realm"] = ""; //rtsp专用鉴权,不作支持 data["hook"]["on_shell_login"] = ""; //shell鉴权,不作支持 @@ -243,6 +249,34 @@ public static bool GetConfig() return false; } } + if (str.ToLower().Contains("customizedrecordfilepath")) + { + string[] tmpArr = str.Trim().Split("::", StringSplitOptions.RemoveEmptyEntries); + if (tmpArr.Length == 2) + { + CustomizedRecordFilePath = tmpArr[1].Trim().TrimEnd(';'); + if (!string.IsNullOrEmpty(CustomizedRecordFilePath)) + { + DirectoryInfo di = null; + if (!Directory.Exists(CustomizedRecordFilePath)) + { + di=Directory.CreateDirectory(CustomizedRecordFilePath); + } + else + { + di=new DirectoryInfo(CustomizedRecordFilePath); + } + + if (di != null && di.Exists) + { + //如果自定义的存储位置不为空,则使用自定义存储位置替换原有存储位置 + RecordPath = CustomizedRecordFilePath; + } + + } + } + + } } } } @@ -338,6 +372,7 @@ private static void KeepAlive() MediaServerId = MediaServerId, Secret = Secret, WebApiServerhttpPort = HttpPort, + RecordFilePath = RecordPath, }; string reqData = JsonHelper.ToJson(req); try diff --git a/StreamMediaServerKeeper/Config.conf b/StreamMediaServerKeeper/Config.conf index 2d4cb7f..129af4f 100644 --- a/StreamMediaServerKeeper/Config.conf +++ b/StreamMediaServerKeeper/Config.conf @@ -1,4 +1,5 @@ MediaServerBinPath::/Users/qiuzhouwei/Projects/StreamNode/StreamMediaServerKeeper/bin/Debug/netcoreapp3.1/api_tester_server; StreamNodeServerUrl::http://127.0.0.1:5800/WebHook/MediaServerRegister; HttpPort::6880; -IpAddress::192.168.2.45; \ No newline at end of file +IpAddress::192.168.2.45; +CustomizedRecordFilePath::/home/www; \ No newline at end of file diff --git a/StreamMediaServerKeeper/Program.cs b/StreamMediaServerKeeper/Program.cs index 9e8e5f2..3af86d4 100644 --- a/StreamMediaServerKeeper/Program.cs +++ b/StreamMediaServerKeeper/Program.cs @@ -10,6 +10,11 @@ public class Program public static void Main(string[] args) { + ///启动一下,Common对象 + if (string.IsNullOrEmpty(Common.CustomizedRecordFilePath)) + { + Common.CustomizedRecordFilePath = ""; + } CreateHostBuilder(args).Build().Run(); } diff --git a/StreamMediaServerKeeper/ReqMediaServerReg.cs b/StreamMediaServerKeeper/ReqMediaServerReg.cs index a23cbb9..4661c61 100644 --- a/StreamMediaServerKeeper/ReqMediaServerReg.cs +++ b/StreamMediaServerKeeper/ReqMediaServerReg.cs @@ -10,6 +10,7 @@ public class ReqMediaServerReg private string? _ipaddress; private string? _mediaServerId; private string? _secret; + private string? _recordFilePath; public ushort? MediaServerHttpPort { @@ -41,5 +42,11 @@ public string? Secret get => _secret; set => _secret = value; } + + public string? RecordFilePath + { + get => _recordFilePath; + set => _recordFilePath = value; + } } } \ No newline at end of file diff --git a/StreamMediaServerKeeper/Startup.cs b/StreamMediaServerKeeper/Startup.cs index 18c1e06..6c7ae01 100644 --- a/StreamMediaServerKeeper/Startup.cs +++ b/StreamMediaServerKeeper/Startup.cs @@ -4,6 +4,8 @@ using System.Linq; using System.Text.Json.Serialization; using System.Threading.Tasks; +using CommonFunctions; +using CommonFunctions.DBStructs; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -67,6 +69,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) } */ + app.UseHttpsRedirection(); // 启用Swagger中间件 app.UseSwagger(); @@ -83,6 +86,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { Directory.CreateDirectory(Common.CutOrMergePath); } + if (!Directory.Exists(Common.CutOrMergeTempPath)) { Directory.CreateDirectory(Common.CutOrMergeTempPath); @@ -100,6 +104,14 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) }; app.UseStaticFiles(staticfile); + app.UseFileServer(new FileServerOptions() + { + FileProvider = new PhysicalFileProvider + (Path.Combine(Directory.GetCurrentDirectory(), Common.RecordPath)), //实际目录地址 + RequestPath = new Microsoft.AspNetCore.Http.PathString("/CustomizedRecord"), //用户访问地址 + EnableDirectoryBrowsing = true //开启目录浏览 + }); + app.UseEndpoints( endpoints => diff --git a/StreamMediaServerKeeper/Swagger.xml b/StreamMediaServerKeeper/Swagger.xml index a1386f7..9f41dc3 100644 --- a/StreamMediaServerKeeper/Swagger.xml +++ b/StreamMediaServerKeeper/Swagger.xml @@ -9,6 +9,11 @@ + + + 自定义的录制文件存储位置 + + 正则获取内容 diff --git a/StreamNodeCtrlApis/WebHookApis/MediaServerCtrlApi.cs b/StreamNodeCtrlApis/WebHookApis/MediaServerCtrlApi.cs index 29a2db2..843d7ec 100644 --- a/StreamNodeCtrlApis/WebHookApis/MediaServerCtrlApi.cs +++ b/StreamNodeCtrlApis/WebHookApis/MediaServerCtrlApi.cs @@ -622,7 +622,7 @@ public static ResToWebHookOnStreamChange OnRecordMp4Completed(ReqForWebHookOnRec tmpDvrVideo.PushMediaServerId = record.Mediaserverid; tmpDvrVideo.UpdateTime = currentTime; tmpDvrVideo.RecordDate = st.ToString("yyyy-MM-dd"); - tmpDvrVideo.DownloadUrl = "http://" + mediaServer.Ipaddress + ":" + mediaServer.WebApiPort + "/" + + tmpDvrVideo.DownloadUrl = "http://" + mediaServer.Ipaddress + ":" + mediaServer.WebApiPort + "/CustomizedRecord/" + tmpDvrVideo.DownloadUrl; CameraSession session = null; @@ -712,7 +712,7 @@ public static ResMediaServerWebApiReg ServerReg(ResMediaServerWebApiReg req, out } MediaServerInstance msi = new MediaServerInstance(req.Ipaddress, req.WebApiServerhttpPort, - req.MediaServerHttpPort, req.Secret, req.MediaServerId); + req.MediaServerHttpPort, req.Secret, req.MediaServerId,req.RecordFilePath); msi.KeepAlive = DateTime.Now; lock (Common.MediaServerList) diff --git a/StreamNodeWebApi/AutoTasker/RecordAutoKeeper.cs b/StreamNodeWebApi/AutoTasker/RecordAutoKeeper.cs index cccda3c..6a31f76 100644 --- a/StreamNodeWebApi/AutoTasker/RecordAutoKeeper.cs +++ b/StreamNodeWebApi/AutoTasker/RecordAutoKeeper.cs @@ -373,6 +373,7 @@ private void setDvrOnorOff(StreamDvrPlan sdp, bool enable) Secret = "", Stream = obj.StreamId, Vhost = obj.Vhost, + Customized_Path = mediaServer.RecordFilePath, }, out _); } else diff --git a/StreamNodeWebApi/Controllers/WebHookController.cs b/StreamNodeWebApi/Controllers/WebHookController.cs index 8e15e12..ac14c7f 100644 --- a/StreamNodeWebApi/Controllers/WebHookController.cs +++ b/StreamNodeWebApi/Controllers/WebHookController.cs @@ -170,6 +170,7 @@ public ResToWebHookOnStreamChange OnMediaServerStart(Object req) var tmpObj = JsonHelper.FromJson(str); + ResponseStruct rs; var ret = MediaServerCtrlApi.OnMediaServerStart(tmpObj, out rs); if (rs.Code != ErrorNumber.None) @@ -201,6 +202,7 @@ public ResMediaServerWebApiReg MediaServerRegister(ResMediaServerWebApiReg req) req.Ipaddress = thisip.MapToIPv4().ToString(); } + var ret = MediaServerCtrlApi.ServerReg(req, out rs); if (rs.Code != ErrorNumber.None) {