From 2ae97a66ead93ce927a4e23b8c9730cdb5c5afed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Tue, 22 Sep 2020 16:07:11 +0800 Subject: [PATCH 1/3] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index bfb01cdb..d5bcf43c 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,8 @@ ## 功能清单 +### 功能一览 +图片 - RTSP[S] - RTSP[S] 服务器,支持RTMP/MP4/HLS转RTSP[S],支持亚马逊echo show这样的设备 From 4f42f508d5e763f018a3fe0ef5861a8097950106 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 26 Sep 2020 09:39:38 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E5=A4=8D=E7=94=A8=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- src/Http/HttpFileManager.cpp | 8 +------- src/Player/PlayerBase.cpp | 8 +------- src/Rtsp/RtspSession.cpp | 8 +------- 4 files changed, 4 insertions(+), 22 deletions(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 8611e88c..f564c7ed 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 8611e88c2eda178973662dcfe180691ff1d8ba35 +Subproject commit f564c7ed27f141a3b5f08d239970b24d700a1244 diff --git a/src/Http/HttpFileManager.cpp b/src/Http/HttpFileManager.cpp index dd2647a1..0e603e81 100644 --- a/src/Http/HttpFileManager.cpp +++ b/src/Http/HttpFileManager.cpp @@ -298,12 +298,6 @@ static bool makeFolderMenu(const string &httpPath, const string &strFullPath, st return true; } -//字符串是否以xx结尾 -static bool end_of(const string &str, const string &substr){ - auto pos = str.rfind(substr); - return pos != string::npos && pos == str.size() - substr.size(); -}; - //拦截hls的播放请求 static bool emitHlsPlayed(const Parser &parser, const MediaInfo &mediaInfo, const HttpSession::HttpAccessPathInvoker &invoker,TcpSession &sender){ //访问的hls.m3u8结尾,我们转换成kBroadcastMediaPlayed事件 @@ -488,7 +482,7 @@ static string pathCat(const string &a, const string &b){ * @param cb 回调对象 */ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo &mediaInfo, const string &strFile, const HttpFileManager::invoker &cb) { - bool is_hls = end_of(strFile, kHlsSuffix); + bool is_hls = end_with(strFile, kHlsSuffix); bool file_exist = File::is_file(strFile.data()); if (!is_hls && !file_exist) { //文件不存在且不是hls,那么直接返回404 diff --git a/src/Player/PlayerBase.cpp b/src/Player/PlayerBase.cpp index 775f38d5..c29c4c27 100644 --- a/src/Player/PlayerBase.cpp +++ b/src/Player/PlayerBase.cpp @@ -17,12 +17,6 @@ using namespace toolkit; namespace mediakit { -//字符串是否以xx结尾 -static bool end_of(const string &str, const string &substr){ - auto pos = str.rfind(substr); - return pos != string::npos && pos == str.size() - substr.size(); -} - PlayerBase::Ptr PlayerBase::createPlayer(const EventPoller::Ptr &poller,const string &url_in) { static auto releasePlayer = [](PlayerBase *ptr){ onceToken token(nullptr,[&](){ @@ -54,7 +48,7 @@ PlayerBase::Ptr PlayerBase::createPlayer(const EventPoller::Ptr &poller,const st return PlayerBase::Ptr(new RtmpPlayerImp(poller),releasePlayer); } - if ((strcasecmp("http",prefix.data()) == 0 || strcasecmp("https",prefix.data()) == 0) && end_of(url, ".m3u8")) { + if ((strcasecmp("http",prefix.data()) == 0 || strcasecmp("https",prefix.data()) == 0) && end_with(url, ".m3u8")) { return PlayerBase::Ptr(new HlsPlayerImp(poller),releasePlayer); } diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index 6f7a6a65..cdc44293 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -126,12 +126,6 @@ void RtspSession::onRecv(const Buffer::Ptr &buf) { } } -//字符串是否以xx结尾 -static inline bool end_of(const string &str, const string &substr){ - auto pos = str.rfind(substr); - return pos != string::npos && pos == str.size() - substr.size(); -} - void RtspSession::onWholeRtspPacket(Parser &parser) { string method = parser.Method(); //提取出请求命令字 _cseq = atoi(parser["CSeq"].data()); @@ -224,7 +218,7 @@ void RtspSession::handleReq_ANNOUNCE(const Parser &parser) { } auto full_url = parser.FullUrl(); - if(end_of(full_url,".sdp")){ + if(end_with(full_url,".sdp")){ //去除.sdp后缀,防止EasyDarwin推流器强制添加.sdp后缀 full_url = full_url.substr(0,full_url.length() - 4); _media_info.parse(full_url); From 2cf66594e8228d266be5b62e6d400b6442811a3c Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 26 Sep 2020 10:07:22 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E6=88=AA=E5=9B=BE?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 52 ++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index be3db75b..8a81deff 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -940,50 +940,60 @@ void installWebApi() { CHECK_ARGS("url", "timeout_sec", "expire_sec"); GET_CONFIG(string, snap_root, API::kSnapRoot); + bool have_old_snap = false, res_old_snap = false; int expire_sec = allArgs["expire_sec"]; auto scan_path = File::absolutePath(MD5(allArgs["url"]).hexdigest(), snap_root) + "/"; - string snap_path; + string new_snap = StrPrinter << scan_path << time(NULL) << ".jpeg"; + File::scanDir(scan_path, [&](const string &path, bool isDir) { - if (isDir) { - //忽略文件夹 + if (isDir || !end_with(path, ".jpeg")) { + //忽略文件夹或其他类型的文件 return true; } //找到截图 auto tm = FindField(path.data() + scan_path.size(), nullptr, ".jpeg"); if (atoll(tm.data()) + expire_sec < time(NULL)) { - //截图已经过期,删除之,后面重新生成 - File::delete_file(path.data()); + //截图已经过期,改名,以便再次请求时,可以返回老截图 + rename(path.data(), new_snap.data()); + have_old_snap = true; return true; } - //截图未过期,中断遍历,返回上次生成的截图 - snap_path = path; + //截图存在,且未过期,那么返回之 + res_old_snap = true; + responseSnap(path, headerIn, invoker); + //中断遍历 return false; }); - if(!snap_path.empty()){ - responseSnap(snap_path, headerIn, invoker); + if (res_old_snap) { + //已经回复了旧的截图 return; } //无截图或者截图已经过期 - snap_path = StrPrinter << scan_path << time(NULL) << ".jpeg"; - - //生成一个空文件,目的是顺便创建文件夹路径, - //同时防止在FFmpeg生成截图途中不停的尝试调用该api启动FFmpeg生成相同的截图 - auto file = File::create_file(snap_path.data(), "wb"); - if (file) { - fclose(file); + if (!have_old_snap) { + //无过期截图,生成一个空文件,目的是顺便创建文件夹路径 + //同时防止在FFmpeg生成截图途中不停的尝试调用该api多次启动FFmpeg进程 + auto file = File::create_file(new_snap.data(), "wb"); + if (file) { + fclose(file); + } } - //启动FFmpeg进程,开始截图 - FFmpegSnap::makeSnap(allArgs["url"],snap_path,allArgs["timeout_sec"],[invoker,headerIn,snap_path](bool success){ - if(!success){ + //启动FFmpeg进程,开始截图,生成临时文件,截图成功后替换为正式文件 + auto new_snap_tmp = new_snap + ".tmp"; + FFmpegSnap::makeSnap(allArgs["url"], new_snap_tmp, allArgs["timeout_sec"], [invoker, headerIn, new_snap, new_snap_tmp](bool success) { + if (!success) { //生成截图失败,可能残留空文件 - File::delete_file(snap_path.data()); + File::delete_file(new_snap_tmp.data()); + } else { + //临时文件改成正式文件 + File::delete_file(new_snap.data()); + rename(new_snap_tmp.data(), new_snap.data()); } - responseSnap(snap_path, headerIn, invoker); + responseSnap(new_snap, headerIn, invoker); }); });