From b127d8c2a940387defb46872aee7f907cffb5ff9 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 13 Mar 2022 20:48:01 +0800 Subject: [PATCH] =?UTF-8?q?rtsp/GB28181=E9=9A=8F=E6=9C=BA=E7=AB=AF?= =?UTF-8?q?=E5=8F=A3=EF=BC=8C=E6=94=AF=E6=8C=81=E7=AB=AF=E5=8F=A3=E8=8C=83?= =?UTF-8?q?=E5=9B=B4=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/config.ini | 3 ++ src/Common/config.cpp | 67 +++++++++----------------- src/Common/config.h | 3 ++ src/Rtsp/Rtsp.cpp | 109 ++++++++++++++++++++++++++++++++---------- 4 files changed, 113 insertions(+), 69 deletions(-) diff --git a/conf/config.ini b/conf/config.ini index 78d89406..28695fcb 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -254,6 +254,9 @@ dumpDir= port=10000 #rtp超时时间,单位秒 timeoutSec=15 +#随机端口范围,最少确保36个端口 +#该范围同时限制rtsp服务器udp端口范围 +port_range=30000-35000 [rtc] #rtc播放推流、播放超时时间 diff --git a/src/Common/config.cpp b/src/Common/config.cpp index 6b9eb233..47148054 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -82,7 +82,7 @@ const string kWaitAddTrackMS = GENERAL_FIELD"wait_add_track_ms"; const string kUnreadyFrameCache = GENERAL_FIELD"unready_frame_cache"; const string kContinuePushMS = GENERAL_FIELD"continue_push_ms"; -onceToken token([](){ +static onceToken token([](){ mINI::Instance()[kFlowThreshold] = 1024; mINI::Instance()[kStreamNoneReaderDelayMS] = 20 * 1000; mINI::Instance()[kMaxStreamWaitTimeMS] = 15 * 1000; @@ -104,33 +104,24 @@ onceToken token([](){ mINI::Instance()[kWaitAddTrackMS] = 3000; mINI::Instance()[kUnreadyFrameCache] = 100; mINI::Instance()[kContinuePushMS] = 15 * 1000; -},nullptr); +}); }//namespace General ////////////HTTP配置/////////// namespace Http { #define HTTP_FIELD "http." -//http 文件发送缓存大小 const string kSendBufSize = HTTP_FIELD"sendBufSize"; -//http 最大请求字节数 const string kMaxReqSize = HTTP_FIELD"maxReqSize"; -//http keep-alive秒数 const string kKeepAliveSecond = HTTP_FIELD"keepAliveSecond"; -//http 字符编码 const string kCharSet = HTTP_FIELD"charSet"; -//http 服务器根目录 const string kRootPath = HTTP_FIELD"rootPath"; -//http 服务器虚拟目录 const string kVirtualPath = HTTP_FIELD "virtualPath"; -//http 404错误提示内容 const string kNotFound = HTTP_FIELD"notFound"; -//是否显示文件夹菜单 const string kDirMenu = HTTP_FIELD"dirMenu"; - const string kForbidCacheSuffix = HTTP_FIELD"forbidCacheSuffix"; -onceToken token([](){ +static onceToken token([](){ mINI::Instance()[kSendBufSize] = 64 * 1024; mINI::Instance()[kMaxReqSize] = 4 * 10240; mINI::Instance()[kKeepAliveSecond] = 15; @@ -155,7 +146,7 @@ onceToken token([](){ "" << endl; mINI::Instance()[kForbidCacheSuffix] = ""; -},nullptr); +}); }//namespace Http @@ -164,9 +155,9 @@ namespace Shell { #define SHELL_FIELD "shell." const string kMaxReqSize = SHELL_FIELD"maxReqSize"; -onceToken token([](){ +static onceToken token([](){ mINI::Instance()[kMaxReqSize] = 1024; -},nullptr); +}); } //namespace Shell ////////////RTSP服务器配置/////////// @@ -177,13 +168,13 @@ const string kHandshakeSecond = RTSP_FIELD"handshakeSecond"; const string kKeepAliveSecond = RTSP_FIELD"keepAliveSecond"; const string kDirectProxy = RTSP_FIELD"directProxy"; -onceToken token([](){ +static onceToken token([](){ //默认Md5方式认证 mINI::Instance()[kAuthBasic] = 0; mINI::Instance()[kHandshakeSecond] = 15; mINI::Instance()[kKeepAliveSecond] = 15; mINI::Instance()[kDirectProxy] = 1; -},nullptr); +}); } //namespace Rtsp ////////////RTMP服务器配置/////////// @@ -193,11 +184,11 @@ const string kModifyStamp = RTMP_FIELD"modifyStamp"; const string kHandshakeSecond = RTMP_FIELD"handshakeSecond"; const string kKeepAliveSecond = RTMP_FIELD"keepAliveSecond"; -onceToken token([](){ +static onceToken token([](){ mINI::Instance()[kModifyStamp] = false; mINI::Instance()[kHandshakeSecond] = 15; mINI::Instance()[kKeepAliveSecond] = 15; -},nullptr); +}); } //namespace RTMP @@ -210,11 +201,11 @@ const string kAudioMtuSize = RTP_FIELD"audioMtuSize"; //rtp包最大长度限制,单位是KB const string kRtpMaxSize = RTP_FIELD"rtpMaxSize"; -onceToken token([](){ +static onceToken token([](){ mINI::Instance()[kVideoMtuSize] = 1400; mINI::Instance()[kAudioMtuSize] = 600; mINI::Instance()[kRtpMaxSize] = 10; -},nullptr); +}); } //namespace Rtsp ////////////组播配置/////////// @@ -227,32 +218,25 @@ const string kAddrMax = MULTI_FIELD"addrMax"; //组播TTL const string kUdpTTL = MULTI_FIELD"udpTTL"; -onceToken token([](){ +static onceToken token([](){ mINI::Instance()[kAddrMin] = "239.0.0.0"; mINI::Instance()[kAddrMax] = "239.255.255.255"; mINI::Instance()[kUdpTTL] = 64; -},nullptr); +}); } //namespace MultiCast ////////////录像配置/////////// namespace Record { #define RECORD_FIELD "record." -//查看录像的应用名称 const string kAppName = RECORD_FIELD"appName"; -//每次流化MP4文件的时长,单位毫秒 const string kSampleMS = RECORD_FIELD"sampleMS"; -//MP4文件录制大小,默认一个小时 const string kFileSecond = RECORD_FIELD"fileSecond"; -//录制文件路径 const string kFilePath = RECORD_FIELD"filePath"; -//mp4文件写缓存大小 const string kFileBufSize = RECORD_FIELD"fileBufSize"; -//mp4录制完成后是否进行二次关键帧索引写入头部 const string kFastStart = RECORD_FIELD"fastStart"; -//mp4文件是否重头循环读取 const string kFileRepeat = RECORD_FIELD"fileRepeat"; -onceToken token([](){ +static onceToken token([](){ mINI::Instance()[kAppName] = "record"; mINI::Instance()[kSampleMS] = 500; mINI::Instance()[kFileSecond] = 60*60; @@ -260,28 +244,21 @@ onceToken token([](){ mINI::Instance()[kFileBufSize] = 64 * 1024; mINI::Instance()[kFastStart] = false; mINI::Instance()[kFileRepeat] = false; -},nullptr); +}); } //namespace Record ////////////HLS相关配置/////////// namespace Hls { #define HLS_FIELD "hls." -//HLS切片时长,单位秒 const string kSegmentDuration = HLS_FIELD"segDur"; -//HLS切片个数 const string kSegmentNum = HLS_FIELD"segNum"; -//HLS切片从m3u8文件中移除后,继续保留在磁盘上的个数 const string kSegmentRetain = HLS_FIELD"segRetain"; -//HLS文件写缓存大小 const string kFileBufSize = HLS_FIELD"fileBufSize"; -//录制文件路径 const string kFilePath = HLS_FIELD"filePath"; -// 是否广播 ts 切片完成通知 const string kBroadcastRecordTs = HLS_FIELD"broadcastRecordTs"; -//hls直播文件删除延时,单位秒 const string kDeleteDelaySec = HLS_FIELD"deleteDelaySec"; -onceToken token([](){ +static onceToken token([](){ mINI::Instance()[kSegmentDuration] = 2; mINI::Instance()[kSegmentNum] = 3; mINI::Instance()[kSegmentRetain] = 5; @@ -289,22 +266,22 @@ onceToken token([](){ mINI::Instance()[kFilePath] = "./www"; mINI::Instance()[kBroadcastRecordTs] = false; mINI::Instance()[kDeleteDelaySec] = 0; -},nullptr); +}); } //namespace Hls ////////////Rtp代理相关配置/////////// namespace RtpProxy { #define RTP_PROXY_FIELD "rtp_proxy." -//rtp调试数据保存目录 const string kDumpDir = RTP_PROXY_FIELD"dumpDir"; -//rtp接收超时时间 const string kTimeoutSec = RTP_PROXY_FIELD"timeoutSec"; +const string kPortRange = RTP_PROXY_FIELD "port_range"; -onceToken token([](){ +static onceToken token([](){ mINI::Instance()[kDumpDir] = ""; mINI::Instance()[kTimeoutSec] = 15; -},nullptr); + mINI::Instance()[kPortRange] = "30000-35000"; +}); } //namespace RtpProxy diff --git a/src/Common/config.h b/src/Common/config.h index 468eb59b..bc185cd9 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -313,6 +313,9 @@ namespace RtpProxy { extern const std::string kDumpDir; //rtp接收超时时间 extern const std::string kTimeoutSec; +//随机端口范围,最少确保36个端口 +//该范围同时限制rtsp服务器udp端口范围 +extern const std::string kPortRange; } //namespace RtpProxy /** diff --git a/src/Rtsp/Rtsp.cpp b/src/Rtsp/Rtsp.cpp index 9ea1d93d..79c23a93 100644 --- a/src/Rtsp/Rtsp.cpp +++ b/src/Rtsp/Rtsp.cpp @@ -8,7 +8,8 @@ * may be found in the AUTHORS file in the root of the source tree. */ -#include +#include +#include #include "Rtsp.h" #include "Common/Parser.h" @@ -364,28 +365,88 @@ bool RtspUrl::setup(bool isSSL, const string &strUrl, const string &strUser, con return true; } +class PortManager : public std::enable_shared_from_this { +public: + PortManager() { + static auto func = [](const string &str, int index) { + uint16_t port[] = { 30000, 35000 }; + sscanf(str.data(), "%" SCNu16 "-%" SCNu16, port, port + 1); + return port[index]; + }; + GET_CONFIG_FUNC(uint16_t, s_min_port, RtpProxy::kPortRange, [](const string &str) { return func(str, 0); }); + GET_CONFIG_FUNC(uint16_t, s_max_port, RtpProxy::kPortRange, [](const string &str) { return func(str, 1); }); + assert(s_max_port > s_min_port + 36); + setRange((s_min_port + 1) / 2, s_max_port / 2); + } + + static PortManager& Instance() { + static auto instance = std::make_shared(); + return *instance; + } + + void bindUdpSock(std::pair &pair, const string &local_ip) { + auto &sock0 = pair.first; + auto &sock1 = pair.second; + auto sock_pair = getPortPair(); + if (!sock_pair) { + throw runtime_error("none reserved udp port in pool"); + } + + if (!sock0->bindUdpSock(2 * *sock_pair, local_ip.data(), false)) { + //分配端口失败 + throw runtime_error("open udp socket[0] failed"); + } + + if (!sock1->bindUdpSock(2 * *sock_pair + 1, local_ip.data(), false)) { + //分配端口失败 + throw runtime_error("open udp socket[1] failed"); + } + + auto on_cycle = [sock_pair](Socket::Ptr &, std::shared_ptr &) {}; + // udp socket没onAccept事件,设置该回调,目的是为了在销毁socket时,回收对象 + sock0->setOnAccept(on_cycle); + sock1->setOnAccept(on_cycle); + } + +private: + void setRange(uint16_t start_pos, uint16_t end_pos) { + lock_guard lck(_pool_mtx); + while (start_pos < end_pos) { + _port_pair_pool.emplace_back(start_pos++); + } + } + + std::shared_ptr getPortPair() { + lock_guard lck(_pool_mtx); + if (_port_pair_pool.empty()) { + return nullptr; + } + auto pos = _port_pair_pool.front(); + _port_pair_pool.pop_front(); + InfoL << "got port from pool:" << 2 * pos << "-" << 2 * pos + 1; + + weak_ptr weak_self = shared_from_this(); + std::shared_ptr ret(new uint16_t(pos), [weak_self, pos](uint16_t *ptr) { + delete ptr; + auto strong_self = weak_self.lock(); + if (!strong_self) { + return; + } + InfoL << "return port to pool:" << 2 * pos << "-" << 2 * pos + 1; + //回收端口号 + lock_guard lck(strong_self->_pool_mtx); + strong_self->_port_pair_pool.emplace_back(pos); + }); + return ret; + } + +private: + recursive_mutex _pool_mtx; + deque _port_pair_pool; +}; + static void makeSockPair_l(std::pair &pair, const string &local_ip) { - auto &pSockRtp = pair.first; - auto &pSockRtcp = pair.second; - - if (!pSockRtp->bindUdpSock(0, local_ip.data())) { - //分配端口失败 - throw runtime_error("open udp socket failed"); - } - - //是否是偶数 - bool even_numbers = pSockRtp->get_local_port() % 2 == 0; - if (!pSockRtcp->bindUdpSock(pSockRtp->get_local_port() + (even_numbers ? 1 : -1), local_ip.data())) { - //分配端口失败 - throw runtime_error("open udp socket failed"); - } - - if (!even_numbers) { - //如果rtp端口不是偶数,那么与rtcp端口互换,目的是兼容一些要求严格的播放器或服务器 - Socket::Ptr tmp = pSockRtp; - pSockRtp = pSockRtcp; - pSockRtcp = tmp; - } + PortManager::Instance().bindUdpSock(pair, local_ip); } void makeSockPair(std::pair &pair, const string &local_ip) { @@ -397,11 +458,11 @@ void makeSockPair(std::pair &pair, const string &local try { makeSockPair_l(pair, local_ip); break; - } catch (...) { + } catch (exception &ex) { if (++try_count == 3) { throw; } - WarnL << "open udp socket failed, retry: " << try_count; + WarnL << "open udp socket failed:" << ex.what() << ", retry: " << try_count; } } }