From a30a83d665ec981462b8d4c804721d532591197c Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 1 Nov 2019 15:40:21 +0800 Subject: [PATCH 001/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0H265=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/Device.cpp | 24 ++++++++++++++++++++++++ src/Common/Device.h | 15 +++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/Common/Device.cpp b/src/Common/Device.cpp index d98683e1..a04dba72 100644 --- a/src/Common/Device.cpp +++ b/src/Common/Device.cpp @@ -32,6 +32,7 @@ #include "Util/TimeTicker.h" #include "Extension/AAC.h" #include "Extension/H264.h" +#include "Extension/H265.h" using namespace toolkit; @@ -106,6 +107,24 @@ void DevChannel::inputH264(const char* pcData, int iDataLen, uint32_t dts,uint32 inputFrame(std::make_shared((char *)pcData,iDataLen,dts,pts,prefixeSize)); } +void DevChannel::inputH265(const char* pcData, int iDataLen, uint32_t dts,uint32_t pts) { + if(dts == 0){ + dts = (uint32_t)_aTicker[0].elapsedTime(); + } + if(pts == 0){ + pts = dts; + } + int prefixeSize; + if (memcmp("\x00\x00\x00\x01", pcData, 4) == 0) { + prefixeSize = 4; + } else if (memcmp("\x00\x00\x01", pcData, 3) == 0) { + prefixeSize = 3; + } else { + prefixeSize = 0; + } + inputFrame(std::make_shared((char *)pcData,iDataLen,dts,pts,prefixeSize)); +} + void DevChannel::inputAAC(const char* pcData, int iDataLen, uint32_t uiStamp,bool withAdtsHeader) { if(withAdtsHeader){ inputAAC(pcData+7,iDataLen-7,uiStamp,pcData); @@ -135,6 +154,11 @@ void DevChannel::initVideo(const VideoInfo& info) { addTrack(std::make_shared()); } +void DevChannel::initH265Video(const VideoInfo &info){ + _video = std::make_shared(info); + addTrack(std::make_shared()); +} + void DevChannel::initAudio(const AudioInfo& info) { _audio = std::make_shared(info); addTrack(std::make_shared()); diff --git a/src/Common/Device.h b/src/Common/Device.h index 7e5e7684..a8c63daf 100644 --- a/src/Common/Device.h +++ b/src/Common/Device.h @@ -88,6 +88,12 @@ public: */ void initVideo(const VideoInfo &info); + /** + * 初始化h265视频Track + * @param info + */ + void initH265Video(const VideoInfo &info); + /** * 初始化aac音频Track * 相当于MultiMediaSourceMuxer::addTrack(AACTrack::Ptr ); @@ -104,6 +110,15 @@ public: */ void inputH264(const char *pcData, int iDataLen, uint32_t dts,uint32_t pts = 0); + /** + * 输入265帧 + * @param pcData 265单帧数据指针 + * @param iDataLen 数据指针长度 + * @param dts 解码时间戳,单位毫秒;等于0时内部会自动生成时间戳 + * @param pts 播放时间戳,单位毫秒;等于0时内部会赋值为dts + */ + void inputH265(const char *pcData, int iDataLen, uint32_t dts,uint32_t pts = 0); + /** * 输入可能带adts头的aac帧 * @param pcDataWithAdts 可能带adts头的aac帧 From 35c4c05e7b343a046aa8b826e220dd66f8b35ca9 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 4 Nov 2019 09:21:11 +0800 Subject: [PATCH 002/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8DsetServerConfig?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E4=BF=9D=E5=AD=98=E9=85=8D=E7=BD=AE=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E7=9B=B8=E5=85=B3bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 2 +- server/WebApi.h | 2 ++ server/main.cpp | 8 +++++--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 9a4fcdb7..453db5aa 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -347,7 +347,7 @@ void installWebApi() { } if (changed > 0) { NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastReloadConfig); - ini.dumpFile(); + ini.dumpFile(g_ini_file); } val["changed"] = changed; }); diff --git a/server/WebApi.h b/server/WebApi.h index c3ffecf1..2a917db3 100644 --- a/server/WebApi.h +++ b/server/WebApi.h @@ -47,5 +47,7 @@ extern const string kPort; void installWebApi(); void unInstallWebApi(); +//配置文件路径 +extern string g_ini_file; #endif //ZLMEDIAKIT_WEBAPI_H diff --git a/server/main.cpp b/server/main.cpp index fe583513..734baabe 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -205,6 +205,8 @@ static void inline listen_shell_input(){ } #endif//!defined(_WIN32) +//全局变量,在WebApi中用于保存配置文件用 +string g_ini_file; int start_main(int argc,char *argv[]) { { @@ -219,7 +221,7 @@ int start_main(int argc,char *argv[]) { bool bDaemon = cmd_main.hasKey("daemon"); LogLevel logLevel = (LogLevel) cmd_main["level"].as(); logLevel = MIN(MAX(logLevel, LTrace), LError); - static string ini_file = cmd_main["config"]; + g_ini_file = cmd_main["config"]; string ssl_file = cmd_main["ssl"]; int threads = cmd_main["threads"]; @@ -244,7 +246,7 @@ int start_main(int argc,char *argv[]) { //启动异步日志线程 Logger::Instance().setWriter(std::make_shared()); //加载配置文件,如果配置文件不存在就创建一个 - loadIniConfig(ini_file.data()); + loadIniConfig(g_ini_file.data()); //加载证书,证书包含公钥和私钥 SSL_Initor::Instance().loadCertificate(ssl_file.data()); @@ -306,7 +308,7 @@ int start_main(int argc,char *argv[]) { });// 设置退出信号 #if !defined(_WIN32) - signal(SIGHUP, [](int) { mediakit::loadIniConfig(ini_file.data()); }); + signal(SIGHUP, [](int) { mediakit::loadIniConfig(g_ini_file.data()); }); #endif sem.wait(); } From ccbd1d4a831433d88c31757067793b1c714c17a2 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 5 Nov 2019 10:27:28 +0800 Subject: [PATCH 003/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0hls.segRetain?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E9=A1=B9=EF=BC=8C=E4=BF=AE=E5=A4=8Dhls?= =?UTF-8?q?=E5=88=87=E7=89=87=E7=AD=89=E4=BA=8EsegNum+1=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/config.ini | 4 +- src/Common/config.cpp | 130 ++++++++++++------------------------- src/Common/config.h | 4 +- src/MediaFile/HlsMaker.cpp | 9 +-- 4 files changed, 51 insertions(+), 96 deletions(-) diff --git a/conf/config.ini b/conf/config.ini index cbbcb96d..048971f2 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -44,10 +44,12 @@ fileBufSize=65536 #可以为相对(相对于本可执行程序目录)或绝对路径 filePath=./httpRoot #hls最大切片时间 -segDur=3 +segDur=2 #m3u8索引中,hls保留切片个数(实际保留切片个数大2~3个) #如果设置为0,则不删除切片,而是保存为点播 segNum=3 +#HLS切片从m3u8文件中移除后,继续保留在磁盘上的个数 +segRetain=5 [hook] #在推流时,如果url参数匹对admin_params,那么可以不经过hook鉴权直接推流成功,播放时亦然 diff --git a/src/Common/config.cpp b/src/Common/config.cpp index 15540bcd..8991d768 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -95,57 +95,44 @@ onceToken token([](){ ////////////HTTP配置/////////// namespace Http { #define HTTP_FIELD "http." - //http 文件发送缓存大小 -#define HTTP_SEND_BUF_SIZE (64 * 1024) const string kSendBufSize = HTTP_FIELD"sendBufSize"; - //http 最大请求字节数 -#define HTTP_MAX_REQ_SIZE (4*1024) const string kMaxReqSize = HTTP_FIELD"maxReqSize"; - //http keep-alive秒数 -#define HTTP_KEEP_ALIVE_SECOND 10 const string kKeepAliveSecond = HTTP_FIELD"keepAliveSecond"; - //http keep-alive最大请求数 -#define HTTP_MAX_REQ_CNT 100 const string kMaxReqCount = HTTP_FIELD"maxReqCount"; - - //http 字符编码 -#if defined(_WIN32) -#define HTTP_CHAR_SET "gb2312" -#else -#define HTTP_CHAR_SET "utf-8" -#endif const string kCharSet = HTTP_FIELD"charSet"; - //http 服务器根目录 -#define HTTP_ROOT_PATH "./httpRoot" const string kRootPath = HTTP_FIELD"rootPath"; - //http 404错误提示内容 -#define HTTP_NOT_FOUND ""\ - "404 Not Found"\ - ""\ - "

您访问的资源不存在!

"\ - "
"\ - SERVER_NAME\ - "
"\ - ""\ - "" const string kNotFound = HTTP_FIELD"notFound"; - onceToken token([](){ - mINI::Instance()[kSendBufSize] = HTTP_SEND_BUF_SIZE; - mINI::Instance()[kMaxReqSize] = HTTP_MAX_REQ_SIZE; - mINI::Instance()[kKeepAliveSecond] = HTTP_KEEP_ALIVE_SECOND; - mINI::Instance()[kMaxReqCount] = HTTP_MAX_REQ_CNT; - mINI::Instance()[kCharSet] = HTTP_CHAR_SET; - mINI::Instance()[kRootPath] = HTTP_ROOT_PATH; - mINI::Instance()[kNotFound] = HTTP_NOT_FOUND; + mINI::Instance()[kSendBufSize] = 64 * 1024; + mINI::Instance()[kMaxReqSize] = 4*1024; + mINI::Instance()[kKeepAliveSecond] = 15; + mINI::Instance()[kMaxReqCount] = 100; + +#if defined(_WIN32) + mINI::Instance()[kCharSet] = "gb2312"; +#else + mINI::Instance()[kCharSet] ="utf-8"; +#endif + + mINI::Instance()[kRootPath] = "./httpRoot"; + mINI::Instance()[kNotFound] = + "" + "404 Not Found" + "" + "

您访问的资源不存在!

" + "
" + SERVER_NAME + "
" + "" + ""; },nullptr); }//namespace Http @@ -153,12 +140,10 @@ onceToken token([](){ ////////////SHELL配置/////////// namespace Shell { #define SHELL_FIELD "shell." - -#define SHELL_MAX_REQ_SIZE 1024 const string kMaxReqSize = SHELL_FIELD"maxReqSize"; onceToken token([](){ - mINI::Instance()[kMaxReqSize] = SHELL_MAX_REQ_SIZE; + mINI::Instance()[kMaxReqSize] = 1024; },nullptr); } //namespace Shell @@ -179,7 +164,6 @@ onceToken token([](){ mINI::Instance()[kDirectProxy] = 1; mINI::Instance()[kModifyStamp] = false; },nullptr); - } //namespace Rtsp ////////////RTMP服务器配置/////////// @@ -194,40 +178,28 @@ onceToken token([](){ mINI::Instance()[kHandshakeSecond] = 15; mINI::Instance()[kKeepAliveSecond] = 15; },nullptr); - } //namespace RTMP ////////////RTP配置/////////// namespace Rtp { #define RTP_FIELD "rtp." - //RTP打包最大MTU,公网情况下更小 -#define RTP_VIDOE_MTU_SIZE 1400 const string kVideoMtuSize = RTP_FIELD"videoMtuSize"; - -#define RTP_Audio_MTU_SIZE 600 const string kAudioMtuSize = RTP_FIELD"audioMtuSize"; - //RTP排序缓存最大个数 -#define RTP_MAX_RTP_COUNT 50 const string kMaxRtpCount = RTP_FIELD"maxRtpCount"; - //如果RTP序列正确次数累计达到该数字就启动清空排序缓存 -#define RTP_CLEAR_COUNT 10 const string kClearCount = RTP_FIELD"clearCount"; - //最大RTP时间为13个小时,每13小时回环一次 -#define RTP_CYCLE_MS (13*60*60*1000) const string kCycleMS = RTP_FIELD"cycleMS"; - onceToken token([](){ - mINI::Instance()[kVideoMtuSize] = RTP_VIDOE_MTU_SIZE; - mINI::Instance()[kAudioMtuSize] = RTP_Audio_MTU_SIZE; - mINI::Instance()[kMaxRtpCount] = RTP_MAX_RTP_COUNT; - mINI::Instance()[kClearCount] = RTP_CLEAR_COUNT; - mINI::Instance()[kCycleMS] = RTP_CYCLE_MS; + mINI::Instance()[kVideoMtuSize] = 1400; + mINI::Instance()[kAudioMtuSize] = 600; + mINI::Instance()[kMaxRtpCount] = 50; + mINI::Instance()[kClearCount] = 10; + mINI::Instance()[kCycleMS] = 13*60*60*1000; },nullptr); } //namespace Rtsp @@ -239,88 +211,67 @@ const string kAddrMin = MULTI_FIELD"addrMin"; //组播分配截止地址 const string kAddrMax = MULTI_FIELD"addrMax"; //组播TTL -#define MULTI_UDP_TTL 64 const string kUdpTTL = MULTI_FIELD"udpTTL"; onceToken token([](){ mINI::Instance()[kAddrMin] = "239.0.0.0"; mINI::Instance()[kAddrMax] = "239.255.255.255"; - mINI::Instance()[kUdpTTL] = MULTI_UDP_TTL; + mINI::Instance()[kUdpTTL] = 64; },nullptr); - } //namespace MultiCast ////////////录像配置/////////// namespace Record { #define RECORD_FIELD "record." - //查看录像的应用名称 -#define RECORD_APP_NAME "record" const string kAppName = RECORD_FIELD"appName"; - //每次流化MP4文件的时长,单位毫秒 -#define RECORD_SAMPLE_MS 500 const string kSampleMS = RECORD_FIELD"sampleMS"; - //MP4文件录制大小,默认一个小时 -#define RECORD_FILE_SECOND (60*60) const string kFileSecond = RECORD_FIELD"fileSecond"; - //录制文件路径 -#define RECORD_FILE_PATH HTTP_ROOT_PATH 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([](){ - mINI::Instance()[kAppName] = RECORD_APP_NAME; - mINI::Instance()[kSampleMS] = RECORD_SAMPLE_MS; - mINI::Instance()[kFileSecond] = RECORD_FILE_SECOND; - mINI::Instance()[kFilePath] = RECORD_FILE_PATH; + mINI::Instance()[kAppName] = "record"; + mINI::Instance()[kSampleMS] = 500; + mINI::Instance()[kFileSecond] = 60*60; + mINI::Instance()[kFilePath] = "./httpRoot"; mINI::Instance()[kFileBufSize] = 64 * 1024; mINI::Instance()[kFastStart] = false; mINI::Instance()[kFileRepeat] = false; },nullptr); - } //namespace Record ////////////HLS相关配置/////////// namespace Hls { #define HLS_FIELD "hls." - //HLS切片时长,单位秒 -#define HLS_SEGMENT_DURATION 3 const string kSegmentDuration = HLS_FIELD"segDur"; - //HLS切片个数 -#define HLS_SEGMENT_NUM 3 const string kSegmentNum = HLS_FIELD"segNum"; - +//HLS切片从m3u8文件中移除后,继续保留在磁盘上的个数 +const string kSegmentRetain = HLS_FIELD"segRetain"; //HLS文件写缓存大小 -#define HLS_FILE_BUF_SIZE (64 * 1024) const string kFileBufSize = HLS_FIELD"fileBufSize"; - //录制文件路径 -#define HLS_FILE_PATH (HTTP_ROOT_PATH) const string kFilePath = HLS_FIELD"filePath"; onceToken token([](){ - mINI::Instance()[kSegmentDuration] = HLS_SEGMENT_DURATION; - mINI::Instance()[kSegmentNum] = HLS_SEGMENT_NUM; - mINI::Instance()[kFileBufSize] = HLS_FILE_BUF_SIZE; - mINI::Instance()[kFilePath] = HLS_FILE_PATH; + mINI::Instance()[kSegmentDuration] = 2; + mINI::Instance()[kSegmentNum] = 3; + mINI::Instance()[kSegmentRetain] = 5; + mINI::Instance()[kFileBufSize] = 64 * 1024; + mINI::Instance()[kFilePath] = "./httpRoot"; },nullptr); - } //namespace Hls - namespace Client { const string kNetAdapter = "net_adapter"; const string kRtpType = "rtp_type"; @@ -331,7 +282,6 @@ const string kTimeoutMS = "protocol_timeout_ms"; const string kMediaTimeoutMS = "media_timeout_ms"; const string kBeatIntervalMS = "beat_interval_ms"; const string kMaxAnalysisMS = "max_analysis_ms"; - } } // namespace mediakit diff --git a/src/Common/config.h b/src/Common/config.h index a4557ea1..5658bd53 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -283,8 +283,10 @@ extern const string kFileRepeat; namespace Hls { //HLS切片时长,单位秒 extern const string kSegmentDuration; -//HLS切片个数,如果设置为0,则不删除切片,而是保存为点播 +//m3u8文件中HLS切片个数,如果设置为0,则不删除切片,而是保存为点播 extern const string kSegmentNum; +//HLS切片从m3u8文件中移除后,继续保留在磁盘上的个数 +extern const string kSegmentRetain; //HLS文件写缓存大小 extern const string kFileBufSize; //录制文件路径 diff --git a/src/MediaFile/HlsMaker.cpp b/src/MediaFile/HlsMaker.cpp index 959fcc36..3788f139 100644 --- a/src/MediaFile/HlsMaker.cpp +++ b/src/MediaFile/HlsMaker.cpp @@ -93,13 +93,14 @@ void HlsMaker::delOldSegment() { return; } //在hls m3u8索引文件中,我们保存的切片个数跟_seg_number相关设置一致 - if (_file_index >= _seg_number + 2) { + if (_file_index > _seg_number) { _seg_dur_list.pop_front(); } - //但是实际保存的切片个数比m3u8所述多两个,这样做的目的是防止播放器在切片删除前能下载完毕 - if (_file_index >= _seg_number + 4) { - onDelSegment(_file_index - _seg_number - 4); + GET_CONFIG(uint32_t,segRetain,Hls::kSegmentRetain); + //但是实际保存的切片个数比m3u8所述多若干个,这样做的目的是防止播放器在切片删除前能下载完毕 + if (_file_index > _seg_number + segRetain) { + onDelSegment(_file_index - _seg_number - segRetain - 1); } } From 9d7b2821b7eb705a28919ce5a303ada20a699cfe Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 8 Nov 2019 15:20:54 +0800 Subject: [PATCH 004/272] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E6=94=AF=E6=8C=81ssl?= =?UTF-8?q?=E5=A4=9A=E8=AF=81=E4=B9=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- server/main.cpp | 20 +++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 8d1681b5..95ad7aad 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 8d1681b5bb247e7f47ae0f8c414f6eeb376b742b +Subproject commit 95ad7aadfd63e63f5b21fd3ae4cd9935d905dd55 diff --git a/server/main.cpp b/server/main.cpp index 734baabe..d38fc835 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -148,7 +148,7 @@ public: Option::ArgRequired,/*该选项后面必须跟值*/ (exeDir() + "ssl.p12").data(),/*该选项默认值*/ false,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/ - "ssl证书路径,支持p12/pem类型",/*该选项说明文字*/ + "ssl证书文件或文件夹,支持p12/pem类型",/*该选项说明文字*/ nullptr); (*_parser) << Option('t',/*该选项简称,如果是\x00则说明无简称*/ @@ -248,12 +248,18 @@ int start_main(int argc,char *argv[]) { //加载配置文件,如果配置文件不存在就创建一个 loadIniConfig(g_ini_file.data()); - //加载证书,证书包含公钥和私钥 - SSL_Initor::Instance().loadCertificate(ssl_file.data()); - //信任某个自签名证书 - SSL_Initor::Instance().trustCertificate(ssl_file.data()); - //不忽略无效证书证书(例如自签名或过期证书) - SSL_Initor::Instance().ignoreInvalidCertificate(true); + if(!File::is_dir(ssl_file.data())){ + //不是文件夹,加载证书,证书包含公钥和私钥 + SSL_Initor::Instance().loadCertificate(ssl_file.data()); + }else{ + //加载文件夹下的所有证书 + File::scanDir(ssl_file,[](const string &path, bool isDir){ + if(!isDir){ + SSL_Initor::Instance().loadCertificate(path.data()); + } + return true; + }); + } uint16_t shellPort = mINI::Instance()[Shell::kPort]; uint16_t rtspPort = mINI::Instance()[Rtsp::kPort]; From 9b7d62347e95e24b6b1f2eeeab799aaea5cae472 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 8 Nov 2019 15:26:46 +0800 Subject: [PATCH 005/272] =?UTF-8?q?=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/server/main.cpp b/server/main.cpp index d38fc835..82fbd0b9 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -255,6 +255,7 @@ int start_main(int argc,char *argv[]) { //加载文件夹下的所有证书 File::scanDir(ssl_file,[](const string &path, bool isDir){ if(!isDir){ + //最后的一个证书会当做默认证书(客户端ssl握手时未指定主机) SSL_Initor::Instance().loadCertificate(path.data()); } return true; From a8a4f267f78a2c277ab3e113ef976aadbc259225 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 8 Nov 2019 15:54:13 +0800 Subject: [PATCH 006/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E9=A1=B9=EF=BC=8C=E6=8E=A7=E5=88=B6=E5=9C=A8=E5=85=B3=E9=97=AD?= =?UTF-8?q?hook=E6=97=B6=EF=BC=8C=E6=8E=A8=E6=B5=81=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E8=BD=AC=E5=8D=8F=E8=AE=AE=E6=88=96=E5=BD=95=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/config.ini | 6 ++++++ server/WebHook.cpp | 5 ++++- src/Common/config.cpp | 6 ++++++ src/Common/config.h | 6 ++++++ src/Rtmp/RtmpSession.cpp | 5 ++++- src/Rtsp/RtspSession.cpp | 7 +++++-- 6 files changed, 31 insertions(+), 4 deletions(-) diff --git a/conf/config.ini b/conf/config.ini index 048971f2..937ff6d2 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -36,6 +36,12 @@ addMuteAudio=1 #拉流代理时如果断流再重连成功是否删除前一次的媒体流数据,如果删除将重新开始, #如果不删除将会接着上一次的数据继续写(录制hls/mp4时会继续在前一个文件后面写) resetWhenRePlay=1 +#是否默认推流时转换成rtsp或rtmp,hook接口(on_publish中可以覆盖该设置) +publishToRtxp=1 +#是否默认推流时转换成hls,hook接口(on_publish中可以覆盖该设置) +publishToHls=1 +#是否默认推流时mp4录像,hook接口(on_publish中可以覆盖该设置) +publishToMP4=0 [hls] #hls写文件的buf大小,调整参数可以提高文件io性能 diff --git a/server/WebHook.cpp b/server/WebHook.cpp index e4c8945e..1391d10a 100644 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -195,7 +195,10 @@ void installWebHook(){ NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastMediaPublish,[](BroadcastMediaPublishArgs){ if(!hook_enable || args._param_strs == hook_adminparams || hook_publish.empty() || sender.get_peer_ip() == "127.0.0.1"){ - invoker("",true, true,false); + GET_CONFIG(bool,toRtxp,General::kPublishToRtxp); + GET_CONFIG(bool,toHls,General::kPublishToHls); + GET_CONFIG(bool,toMP4,General::kPublishToMP4); + invoker("",toRtxp,toHls,toMP4); return; } //异步执行该hook api,防止阻塞NoticeCenter diff --git a/src/Common/config.cpp b/src/Common/config.cpp index 8991d768..39336e69 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -79,6 +79,9 @@ const string kEnableVhost = GENERAL_FIELD"enableVhost"; const string kUltraLowDelay = GENERAL_FIELD"ultraLowDelay"; const string kAddMuteAudio = GENERAL_FIELD"addMuteAudio"; const string kResetWhenRePlay = GENERAL_FIELD"resetWhenRePlay"; +const string kPublishToRtxp = GENERAL_FIELD"publishToRtxp"; +const string kPublishToHls = GENERAL_FIELD"publishToHls"; +const string kPublishToMP4 = GENERAL_FIELD"publishToMP4"; onceToken token([](){ mINI::Instance()[kFlowThreshold] = 1024; @@ -88,6 +91,9 @@ onceToken token([](){ mINI::Instance()[kUltraLowDelay] = 1; mINI::Instance()[kAddMuteAudio] = 1; mINI::Instance()[kResetWhenRePlay] = 1; + mINI::Instance()[kPublishToRtxp] = 1; + mINI::Instance()[kPublishToHls] = 1; + mINI::Instance()[kPublishToMP4] = 0; },nullptr); }//namespace General diff --git a/src/Common/config.h b/src/Common/config.h index 5658bd53..13abe95c 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -182,6 +182,12 @@ extern const string kAddMuteAudio; //拉流代理时如果断流再重连成功是否删除前一次的媒体流数据,如果删除将重新开始, //如果不删除将会接着上一次的数据继续写(录制hls/mp4时会继续在前一个文件后面写) extern const string kResetWhenRePlay; +//是否默认推流时转换成rtsp或rtmp,hook接口(on_publish中可以覆盖该设置) +extern const string kPublishToRtxp ; +//是否默认推流时转换成hls,hook接口(on_publish中可以覆盖该设置) +extern const string kPublishToHls ; +//是否默认推流时mp4录像,hook接口(on_publish中可以覆盖该设置) +extern const string kPublishToMP4 ; }//namespace General diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index 62787b1d..e946d275 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -198,7 +198,10 @@ void RtmpSession::onCmd_publish(AMFDecoder &dec) { *this); if(!flag){ //该事件无人监听,默认鉴权成功 - onRes("",true,true,false); + GET_CONFIG(bool,toRtxp,General::kPublishToRtxp); + GET_CONFIG(bool,toHls,General::kPublishToHls); + GET_CONFIG(bool,toMP4,General::kPublishToMP4); + onRes("",toRtxp,toHls,toMP4); } } diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index 3985ebec..14e2b89a 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -324,8 +324,11 @@ void RtspSession::handleReq_RECORD(const Parser &parser){ auto flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPublish,_mediaInfo,invoker,*this); if(!flag){ //该事件无人监听,默认不鉴权 - onRes("",true,true,false); - } + GET_CONFIG(bool,toRtxp,General::kPublishToRtxp); + GET_CONFIG(bool,toHls,General::kPublishToHls); + GET_CONFIG(bool,toMP4,General::kPublishToMP4); + onRes("",toRtxp,toHls,toMP4); + } } void RtspSession::handleReq_Describe(const Parser &parser) { From 5a1503008fc8aa981faaa9f3c80f5b22a849d1a7 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 8 Nov 2019 15:55:26 +0800 Subject: [PATCH 007/272] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/config.ini | 6 +++--- src/Common/config.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/conf/config.ini b/conf/config.ini index 937ff6d2..830acbb3 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -36,11 +36,11 @@ addMuteAudio=1 #拉流代理时如果断流再重连成功是否删除前一次的媒体流数据,如果删除将重新开始, #如果不删除将会接着上一次的数据继续写(录制hls/mp4时会继续在前一个文件后面写) resetWhenRePlay=1 -#是否默认推流时转换成rtsp或rtmp,hook接口(on_publish中可以覆盖该设置) +#是否默认推流时转换成rtsp或rtmp,hook接口(on_publish)中可以覆盖该设置 publishToRtxp=1 -#是否默认推流时转换成hls,hook接口(on_publish中可以覆盖该设置) +#是否默认推流时转换成hls,hook接口(on_publish)中可以覆盖该设置 publishToHls=1 -#是否默认推流时mp4录像,hook接口(on_publish中可以覆盖该设置) +#是否默认推流时mp4录像,hook接口(on_publish)中可以覆盖该设置 publishToMP4=0 [hls] diff --git a/src/Common/config.h b/src/Common/config.h index 13abe95c..ba171c99 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -182,11 +182,11 @@ extern const string kAddMuteAudio; //拉流代理时如果断流再重连成功是否删除前一次的媒体流数据,如果删除将重新开始, //如果不删除将会接着上一次的数据继续写(录制hls/mp4时会继续在前一个文件后面写) extern const string kResetWhenRePlay; -//是否默认推流时转换成rtsp或rtmp,hook接口(on_publish中可以覆盖该设置) +//是否默认推流时转换成rtsp或rtmp,hook接口(on_publish)中可以覆盖该设置 extern const string kPublishToRtxp ; -//是否默认推流时转换成hls,hook接口(on_publish中可以覆盖该设置) +//是否默认推流时转换成hls,hook接口(on_publish)中可以覆盖该设置 extern const string kPublishToHls ; -//是否默认推流时mp4录像,hook接口(on_publish中可以覆盖该设置) +//是否默认推流时mp4录像,hook接口(on_publish)中可以覆盖该设置 extern const string kPublishToMP4 ; }//namespace General From 814e94b6b0605e4a3bc6ff56f148d8155ad9ba50 Mon Sep 17 00:00:00 2001 From: Gemfield Date: Fri, 8 Nov 2019 16:50:23 +0800 Subject: [PATCH 008/272] Add Docker support --- README.md | 11 +++++++++++ docker/Dockerfile | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 docker/Dockerfile diff --git a/README.md b/README.md index 5dcbcb68..becaaf31 100644 --- a/README.md +++ b/README.md @@ -306,6 +306,17 @@ It is recommended to compile on Ubuntu or MacOS,compiling on windows is cumber }); ``` +## Docker Image +You can pull a pre-built docker image from Docker Hub and run with +```bash +docker run -id -p 1935:1935 -p 8080:80 gemfield/zlmediakit +``` + +Dockerfile is also supplied to build images on Ubuntu 16.04 +```bash +cd docker +docker build -t zlmediakit . +``` ## Mirrors diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 00000000..3f427538 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,36 @@ +FROM ubuntu:16.04 +#shell,rtmp,rtsp,rtsps,http,https +EXPOSE 9000/tcp +EXPOSE 1935/tcp +EXPOSE 554/tcp +EXPOSE 322/tcp +EXPOSE 80/tcp +EXPOSE 443/tcp + +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + cmake \ + git \ + curl \ + ca-certificates \ + libssl-dev \ + libmysqlclient-dev \ + libx264-dev \ + libfaac-dev \ + libmp4v2-dev && \ + rm -rf /var/lib/apt/lists/* + +RUN mkdir -p /opt/media + +WORKDIR /opt/media +RUN git clone --depth=1 https://github.com/xiongziliang/ZLMediaKit && \ + cd ZLMediaKit && git submodule update --init --recursive && \ + mkdir -p build release/linux/Release/ + +WORKDIR /opt/media/ZLMediaKit/build +RUN cmake -DCMAKE_BUILD_TYPE=Release .. && \ + make -j4 + +ENV PATH /opt/media/ZLMediaKit/release/linux/Release/:$PATH + +CMD MediaServer From d6b319b9939008941bfe54f113c8b74debe8cb41 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 11 Nov 2019 16:22:12 +0800 Subject: [PATCH 009/272] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E4=BF=AE=E5=A4=8D=20?= =?UTF-8?q?#125?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 95ad7aad..0ac7b3b1 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 95ad7aadfd63e63f5b21fd3ae4cd9935d905dd55 +Subproject commit 0ac7b3b1dc9817de1b1bb8644e2799ca0e4e9472 From 1b6e61c4130a976c5f77de59dc398ff59102fdf3 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 11 Nov 2019 19:03:03 +0800 Subject: [PATCH 010/272] =?UTF-8?q?1=E3=80=81=E4=BF=AE=E5=A4=8D#125=202?= =?UTF-8?q?=E3=80=81=E4=BF=AE=E5=A4=8D=E9=9D=9E=E6=B3=95rtsp=20url?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E5=BC=82=E5=B8=B8=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- src/Rtsp/RtspPlayer.cpp | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 0ac7b3b1..442fb18d 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 0ac7b3b1dc9817de1b1bb8644e2799ca0e4e9472 +Subproject commit 442fb18d2c13de4d5ac005c6d286e3e2b73309eb diff --git a/src/Rtsp/RtspPlayer.cpp b/src/Rtsp/RtspPlayer.cpp index bd3b83b9..be1424d6 100644 --- a/src/Rtsp/RtspPlayer.cpp +++ b/src/Rtsp/RtspPlayer.cpp @@ -105,7 +105,7 @@ void RtspPlayer::play(const string &strUrl){ } void RtspPlayer::play(bool isSSL,const string &strUrl, const string &strUser, const string &strPwd, Rtsp::eRtpType eType ) { - DebugL << strUrl << " " + DebugL << strUrl << " " << (strUser.size() ? strUser : "null") << " " << (strPwd.size() ? strPwd:"null") << " " << eType; @@ -122,7 +122,7 @@ void RtspPlayer::play(bool isSSL,const string &strUrl, const string &strUser, co _eType = eType; auto ip = FindField(strUrl.data(), "://", "/"); - if (!ip.size()) { + if (ip.empty()) { ip = split(FindField(strUrl.data(), "://", NULL),"?")[0]; } auto port = atoi(FindField(ip.data(), ":", NULL).data()); @@ -134,6 +134,11 @@ void RtspPlayer::play(bool isSSL,const string &strUrl, const string &strUser, co ip = FindField(ip.data(), NULL, ":"); } + if(ip.empty()){ + onPlayResult_l(SockException(Err_other,StrPrinter << "illegal rtsp url:" << strUrl)); + return; + } + _strUrl = strUrl; weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); From 8c0596bfb2147a9dc7395d474283cc2d2ec3409f Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 12 Nov 2019 09:52:34 +0800 Subject: [PATCH 011/272] =?UTF-8?q?=E6=9B=B4=E6=96=B0ZLToolKit(=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E5=8F=91=E9=80=81=E7=BC=93=E5=AD=98=E8=A6=86=E7=9B=96?= =?UTF-8?q?=E5=AF=BC=E8=87=B4ssl=E9=94=99=E8=AF=AF=E7=9A=84bug)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 442fb18d..988de87d 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 442fb18d2c13de4d5ac005c6d286e3e2b73309eb +Subproject commit 988de87df8a8f2bc4fd6fa1187b671e795f985f0 From 3554fe920fb9595af722fe59f3b7b5e8a96d0570 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 12 Nov 2019 10:00:55 +0800 Subject: [PATCH 012/272] =?UTF-8?q?=E6=9B=B4=E6=96=B0ZLToolKit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 988de87d..73eb9077 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 988de87df8a8f2bc4fd6fa1187b671e795f985f0 +Subproject commit 73eb9077e19e473c643708cf586517bacaa16302 From d1fd674c36e57dc087438cbe48b91ced7fbfe248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Wed, 13 Nov 2019 10:43:46 +0800 Subject: [PATCH 013/272] Update README.md --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index becaaf31..d02ad8e3 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,12 @@ - Apple OSX(Darwin), both 32 and 64bits. - All hardware with x86/x86_64/arm/mips cpu. - Windows. + +## How to build + +It is recommended to compile on Ubuntu or MacOS,compiling on windows is cumbersome, and some features are not compiled by default. + +### Before build - **You must use git to clone the complete code. Do not download the source code by downloading zip package. Otherwise, the sub-module code will not be downloaded by default.You can do it like this:** ``` git clone https://github.com/zlmediakit/ZLMediaKit.git @@ -124,12 +130,6 @@ cd ZLMediaKit git submodule update --init ``` - - -## How to build - -It is recommended to compile on Ubuntu or MacOS,compiling on windows is cumbersome, and some features are not compiled by default. - ### Build on linux - My environment From 94eebdadf1709cd9df52f85defb396e6b12473c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Wed, 13 Nov 2019 10:45:52 +0800 Subject: [PATCH 014/272] Update README_CN.md --- README_CN.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README_CN.md b/README_CN.md index 54199461..a2eba340 100644 --- a/README_CN.md +++ b/README_CN.md @@ -128,6 +128,9 @@ ## 编译要求 - 编译器支持C++11,GCC4.8/Clang3.3/VC2015或以上 - cmake3.2或以上 + +## 编译前必看!!! + - **必须使用git下载完整的代码,不要使用下载zip包的方式下载源码,否则子模块代码默认不下载!你可以像以下这样操作:** ``` git clone https://github.com/zlmediakit/ZLMediaKit.git From 88a5de00b2d1498b16237088d0ca9ee6c4510f82 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 13 Nov 2019 14:05:15 +0800 Subject: [PATCH 015/272] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=8B=BC=E5=86=99?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 453db5aa..a33d9bb0 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -554,7 +554,7 @@ void installWebApi() { }); #if !defined(_WIN32) - static auto addFFmepgSource = [](const string &src_url, + static auto addFFmpegSource = [](const string &src_url, const string &dst_url, int timeout_ms, const function &cb){ @@ -591,7 +591,7 @@ void installWebApi() { auto dst_url = allArgs["dst_url"]; int timeout_ms = allArgs["timeout_ms"]; - addFFmepgSource(src_url,dst_url,timeout_ms,[invoker,val,headerOut](const SockException &ex,const string &key){ + addFFmpegSource(src_url,dst_url,timeout_ms,[invoker,val,headerOut](const SockException &ex,const string &key){ if(ex){ const_cast(val)["code"] = API::OtherFailed; const_cast(val)["msg"] = ex.what(); @@ -604,7 +604,7 @@ void installWebApi() { //关闭拉流代理 //测试url http://127.0.0.1/index/api/delFFmepgSource?key=key - API_REGIST(api,delFFmepgSource,{ + API_REGIST(api,delFFmpegSource,{ CHECK_SECRET(); CHECK_ARGS("key"); lock_guard lck(s_ffmpegMapMtx); @@ -677,7 +677,7 @@ void installWebApi() { << allArgs["stream"] << "?vhost=" << allArgs["vhost"]; - addFFmepgSource("http://live.hkstv.hk.lxdns.com/live/hks2/playlist.m3u8",/** ffmpeg拉流支持任意编码格式任意协议 **/ + addFFmpegSource("http://live.hkstv.hk.lxdns.com/live/hks2/playlist.m3u8",/** ffmpeg拉流支持任意编码格式任意协议 **/ dst_url, (1000 * timeout_sec) - 500, [invoker,val,headerOut](const SockException &ex,const string &key){ From 9665d557ae89a902cafad62d5a2417da858b0705 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 13 Nov 2019 14:11:49 +0800 Subject: [PATCH 016/272] =?UTF-8?q?=E5=85=BC=E5=AE=B9=E6=8B=BC=E5=86=99?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index a33d9bb0..587697a8 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -72,6 +72,8 @@ typedef map ApiArgsType; invoker("200 OK", headerOut, val.toStyledString()); \ }); +#define API_ARGS_VALUE sender,headerIn,headerOut,allArgs,val,invoker + #define API_REGIST_INVOKER(field, name, ...) \ s_map_api.emplace("/index/"#field"/"#name,[](API_ARGS,const HttpSession::HttpResponseInvoker &invoker) __VA_ARGS__); @@ -602,13 +604,23 @@ void installWebApi() { }); }); - //关闭拉流代理 - //测试url http://127.0.0.1/index/api/delFFmepgSource?key=key - API_REGIST(api,delFFmpegSource,{ + + static auto api_delFFmpegSource = [](API_ARGS,const HttpSession::HttpResponseInvoker &invoker){ CHECK_SECRET(); CHECK_ARGS("key"); lock_guard lck(s_ffmpegMapMtx); val["data"]["flag"] = s_ffmpegMap.erase(allArgs["key"]) == 1; + }; + + //关闭拉流代理 + //测试url http://127.0.0.1/index/api/delFFmepgSource?key=key + API_REGIST(api,delFFmpegSource,{ + api_delFFmpegSource(API_ARGS_VALUE); + }); + + //此处为了兼容之前的拼写错误 + API_REGIST(api,delFFmepgSource,{ + api_delFFmpegSource(API_ARGS_VALUE); }); #endif From aef8fb8849d1416395761ebf2bec28254f1c32c7 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 13 Nov 2019 14:26:12 +0800 Subject: [PATCH 017/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0isMediaOnline?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=88=A4=E6=96=AD=E6=98=AF=E5=90=A6=E5=AA=92?= =?UTF-8?q?=E4=BD=93=E5=9C=A8=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 587697a8..0f53cd7c 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -418,6 +418,13 @@ void installWebApi() { }); }); + //测试url http://127.0.0.1/index/api/isMediaOnline?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs + API_REGIST(api,isMediaOnline,{ + CHECK_SECRET(); + CHECK_ARGS("schema","vhost","app","stream"); + val["online"] = (bool) (MediaSource::find(allArgs["schema"],allArgs["vhost"],allArgs["app"],allArgs["stream"],false)); + }); + //主动关断流,包括关断拉流、推流 //测试url http://127.0.0.1/index/api/close_stream?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1 API_REGIST(api,close_stream,{ From d440c424c4273050ffa7cdf1475ab4a4729615ff Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 13 Nov 2019 14:33:19 +0800 Subject: [PATCH 018/272] =?UTF-8?q?=E8=8E=B7=E5=8F=96=E7=BA=BF=E7=A8=8B?= =?UTF-8?q?=E8=B4=9F=E8=BD=BD=E6=8E=A5=E5=8F=A3=E8=BF=94=E5=9B=9Ecode?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=20=E7=AE=80=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 0f53cd7c..1c7eab29 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -296,6 +296,7 @@ void installWebApi() { obj["delay"] = vecDelay[i++]; val["data"].append(obj); } + val["code"] = API::Success; invoker("200 OK", headerOut, val.toStyledString()); }); }); @@ -313,6 +314,7 @@ void installWebApi() { obj["delay"] = vecDelay[i++]; val["data"].append(obj); } + val["code"] = API::Success; invoker("200 OK", headerOut, val.toStyledString()); }); }); @@ -393,8 +395,6 @@ void installWebApi() { API_REGIST(api,getMediaList,{ CHECK_SECRET(); //获取所有MediaSource列表 - val["code"] = API::Success; - val["msg"] = "success"; MediaSource::for_each_media([&](const string &schema, const string &vhost, const string &app, @@ -479,13 +479,9 @@ void installWebApi() { //踢掉tcp会话 auto session = SessionMap::Instance().get(allArgs["id"]); if(!session){ - val["code"] = API::OtherFailed; - val["msg"] = "can not find the target"; - return; + throw ApiRetException("can not find the target",API::OtherFailed); } session->safeShutdown(); - val["code"] = API::Success; - val["msg"] = "success"; }); static auto addStreamProxy = [](const string &vhost, From c1f3b8ffbc9b393687c5da00176f891b76f96eef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Thu, 14 Nov 2019 14:45:12 +0800 Subject: [PATCH 019/272] Create ccpp.yml --- .github/workflows/ccpp.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/workflows/ccpp.yml diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml new file mode 100644 index 00000000..89cea2fd --- /dev/null +++ b/.github/workflows/ccpp.yml @@ -0,0 +1,16 @@ +name: C/C++ CI + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: build + run: ./build_for_linux.sh + - name: run + run: cd ZLMediaKit/release/linux/Debug && ./MediaServer -d + From c8ba6fd73fedf15cd218185a2dd317d969a7a721 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 14 Nov 2019 14:48:27 +0800 Subject: [PATCH 020/272] =?UTF-8?q?=E6=9B=B4=E6=96=B0ZLToolKit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 73eb9077..628d3b25 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 73eb9077e19e473c643708cf586517bacaa16302 +Subproject commit 628d3b2527f63b54a5eb38b9e9973254d4a2192b From a3e67a13064c88452aee107575284cbecddb8c22 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 14 Nov 2019 15:14:25 +0800 Subject: [PATCH 021/272] =?UTF-8?q?=E5=AE=8C=E6=88=90workflows=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ccpp.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 89cea2fd..17674cc2 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -9,8 +9,16 @@ jobs: steps: - uses: actions/checkout@v1 - - name: build - run: ./build_for_linux.sh - - name: run - run: cd ZLMediaKit/release/linux/Debug && ./MediaServer -d + + - name: 下载submodule源码 + run: git submodule update --init + + - name: apt-get安装依赖库(非必选) + run: sudo apt-get install -y cmake libmysqlclient-dev libssl-dev libx264-dev libfaac-dev libmp4v2-dev libsdl-dev libavcodec-dev libavutil-dev + + - name: 编译 + run: mkdir -p linux_build && cd linux_build && cmake .. && make -j4 + + - name: 运行MediaServer + run: pwd && cd release/linux/Debug && ./MediaServer -h From 2543ee01dc25217f0539934ae5c2df6a0ee92f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Thu, 14 Nov 2019 15:22:15 +0800 Subject: [PATCH 022/272] Update ccpp.yml --- .github/workflows/ccpp.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 17674cc2..fc130fbf 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -20,5 +20,8 @@ jobs: run: mkdir -p linux_build && cd linux_build && cmake .. && make -j4 - name: 运行MediaServer - run: pwd && cd release/linux/Debug && ./MediaServer -h + run: pwd && cd release/linux/Debug && sudo ./MediaServer -d & + + - name: 关闭MediaServer + run: sleep 3 && killall -2 MediaServer From 08ad39d0056aad18893db33091e63df9d8f58fc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Thu, 14 Nov 2019 15:26:35 +0800 Subject: [PATCH 023/272] Update ccpp.yml --- .github/workflows/ccpp.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index fc130fbf..dc2660e2 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -22,6 +22,4 @@ jobs: - name: 运行MediaServer run: pwd && cd release/linux/Debug && sudo ./MediaServer -d & - - name: 关闭MediaServer - run: sleep 3 && killall -2 MediaServer From a993132a595485031aa1cfccdca4f301403e6b68 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 15 Nov 2019 13:50:42 +0800 Subject: [PATCH 024/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8DFFmpeg=E6=8B=89?= =?UTF-8?q?=E6=B5=81=E4=BB=A3=E7=90=86=E5=8D=A1=E9=A1=BF=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/config.ini | 2 +- server/FFmpegSource.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/config.ini b/conf/config.ini index 830acbb3..1ef7c145 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -9,7 +9,7 @@ secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc #FFmpeg可执行程序绝对路径 bin=/usr/local/bin/ffmpeg #FFmpeg拉流再推流的命令模板,通过该模板可以设置再编码的一些参数 -cmd=%s -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s +cmd=%s -re -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s #FFmpeg日志的路径,如果置空则不生成FFmpeg日志 #可以为相对(相对于本可执行程序目录)或绝对路径 log=./ffmpeg/ffmpeg.log diff --git a/server/FFmpegSource.cpp b/server/FFmpegSource.cpp index e55e6dbb..359ed2be 100644 --- a/server/FFmpegSource.cpp +++ b/server/FFmpegSource.cpp @@ -38,7 +38,7 @@ const char kLog[] = FFmpeg_FIELD"log"; onceToken token([]() { mINI::Instance()[kBin] = trim(System::execute("which ffmpeg")); - mINI::Instance()[kCmd] = "%s -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s"; + mINI::Instance()[kCmd] = "%s -re -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s"; mINI::Instance()[kLog] = "./ffmpeg/ffmpeg.log"; }); } From ddf351d35008899896075b72e6542c9d1d3a8c43 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 18 Nov 2019 10:46:59 +0800 Subject: [PATCH 025/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0close=5Fstreams?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 1c7eab29..f4a998ca 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -445,6 +445,39 @@ void installWebApi() { } }); + //批量主动关断流,包括关断拉流、推流 + //测试url http://127.0.0.1/index/api/close_streams?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1 + API_REGIST(api,close_streams,{ + CHECK_SECRET(); + //筛选命中个数 + int count_hit = 0; + int count_closed = 0; + MediaSource::for_each_media([&](const string &schema, + const string &vhost, + const string &app, + const string &stream, + const MediaSource::Ptr &media){ + if(!allArgs["schema"].empty() && allArgs["schema"] != schema){ + return; + } + if(!allArgs["vhost"].empty() && allArgs["vhost"] != vhost){ + return; + } + if(!allArgs["app"].empty() && allArgs["app"] != app){ + return; + } + if(!allArgs["stream"].empty() && allArgs["stream"] != stream){ + return; + } + ++count_hit; + if(media->close(allArgs["force"].as())){ + ++count_closed; + } + }); + val["count_hit"] = count_hit; + val["count_closed"] = count_closed; + }); + //获取所有TcpSession列表信息 //可以根据本地端口和远端ip来筛选 //测试url(筛选某端口下的tcp会话) http://127.0.0.1/index/api/getAllSession?local_port=1935 From 0d42aab9e7370f14b798548eb10355303d067445 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 18 Nov 2019 12:07:11 +0800 Subject: [PATCH 026/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8DFFmpeg=E6=8B=89?= =?UTF-8?q?=E6=B5=81=E4=BB=A3=E7=90=86=E6=97=A0=E6=B3=95=E9=80=9A=E8=BF=87?= =?UTF-8?q?close=5Fstream=E6=8E=A5=E5=8F=A3=E5=85=B3=E9=97=AD=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/FFmpegSource.cpp | 55 ++++++++++++++++++++++------------------ server/FFmpegSource.h | 7 ++++- src/Common/MediaSource.h | 5 ++++ 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/server/FFmpegSource.cpp b/server/FFmpegSource.cpp index 359ed2be..81d98bd5 100644 --- a/server/FFmpegSource.cpp +++ b/server/FFmpegSource.cpp @@ -48,7 +48,6 @@ FFmpegSource::FFmpegSource() { } FFmpegSource::~FFmpegSource() { - NoticeCenter::Instance().delListener(this, Broadcast::kBroadcastStreamNoneReader); DebugL; } @@ -83,6 +82,7 @@ void FFmpegSource::play(const string &src_url,const string &dst_url,int timeout_ if(src){ //推流给自己成功 cb(SockException()); + strongSelf->onGetMediaSource(src); strongSelf->startTimer(timeout_ms); return; } @@ -192,8 +192,7 @@ void FFmpegSource::startTimer(int timeout_ms) { //同步查找流 if (!src) { //流不在线,重新拉流 - strongSelf->play(strongSelf->_src_url, strongSelf->_dst_url, timeout_ms, - [](const SockException &) {}); + strongSelf->play(strongSelf->_src_url, strongSelf->_dst_url, timeout_ms, [](const SockException &) {}); } }); } else { @@ -205,29 +204,35 @@ void FFmpegSource::startTimer(int timeout_ms) { } return true; }, _poller); - - NoticeCenter::Instance().delListener(this, Broadcast::kBroadcastStreamNoneReader); - NoticeCenter::Instance().addListener(this, Broadcast::kBroadcastStreamNoneReader,[weakSelf](BroadcastStreamNoneReaderArgs) { - auto strongSelf = weakSelf.lock(); - if (!strongSelf) { - //自身已经销毁 - return; - } - - if(sender.getVhost() != strongSelf->_media_info._vhost || - sender.getApp() != strongSelf->_media_info._app || - sender.getId() != strongSelf->_media_info._streamid){ - //不是自己感兴趣的事件,忽略之 - return; - } - - //该流无人观看,我们停止吧 - if(strongSelf->_onClose){ - strongSelf->_onClose(); - } - }); } void FFmpegSource::setOnClose(const function &cb){ _onClose = cb; -} \ No newline at end of file +} + +bool FFmpegSource::close(MediaSource &sender, bool force) { + auto listener = _listener.lock(); + if(listener && !listener->close(sender,force)){ + //关闭失败 + return false; + } + //该流无人观看,我们停止吧 + if(_onClose){ + _onClose(); + } + return true; +} + +void FFmpegSource::onNoneReader(MediaSource &sender) { + auto listener = _listener.lock(); + if(listener){ + listener->onNoneReader(sender); + }else{ + MediaSourceEvent::onNoneReader(sender); + } +} + +void FFmpegSource::onGetMediaSource(const MediaSource::Ptr &src) { + _listener = src->getListener(); + src->setListener(shared_from_this()); +} diff --git a/server/FFmpegSource.h b/server/FFmpegSource.h index 6be92fc3..5cf2c4f7 100644 --- a/server/FFmpegSource.h +++ b/server/FFmpegSource.h @@ -39,7 +39,7 @@ using namespace std; using namespace toolkit; using namespace mediakit; -class FFmpegSource : public std::enable_shared_from_this{ +class FFmpegSource : public std::enable_shared_from_this , public MediaSourceEvent{ public: typedef shared_ptr Ptr; typedef function onPlay; @@ -55,6 +55,10 @@ public: private: void findAsync(int maxWaitMS ,const function &cb); void startTimer(int timeout_ms); + void onGetMediaSource(const MediaSource::Ptr &src); + + bool close(MediaSource &sender,bool force) override; + void onNoneReader(MediaSource &sender) override ; private: Process _process; Timer::Ptr _timer; @@ -63,6 +67,7 @@ private: string _src_url; string _dst_url; function _onClose; + std::weak_ptr _listener; }; diff --git a/src/Common/MediaSource.h b/src/Common/MediaSource.h index a54bc8d8..5cfe597d 100644 --- a/src/Common/MediaSource.h +++ b/src/Common/MediaSource.h @@ -167,10 +167,15 @@ public: } listener->onNoneReader(*this); } + virtual void setListener(const std::weak_ptr &listener){ _listener = listener; } + std::weak_ptr getListener(){ + return _listener; + } + template static void for_each_media(FUN && fun){ lock_guard lock(g_mtxMediaSrc); From f628985037fbab822b986e9456fd666bd36f5a62 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 18 Nov 2019 16:16:56 +0800 Subject: [PATCH 027/272] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index f4a998ca..417a4d0d 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -488,7 +488,7 @@ void installWebApi() { string &peer_ip = allArgs["peer_ip"]; SessionMap::Instance().for_each_session([&](const string &id,const TcpSession::Ptr &session){ - if(local_port != API::Success && local_port != session->get_local_port()){ + if(local_port != 0 && local_port != session->get_local_port()){ return; } if(!peer_ip.empty() && peer_ip != session->get_peer_ip()){ From bcc5c839a583627231639c11002af64ca84d18e0 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 18 Nov 2019 16:28:56 +0800 Subject: [PATCH 028/272] =?UTF-8?q?close=5Fstream=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E8=BF=94=E5=9B=9E=E5=AD=97=E6=AE=B5=E6=94=B9?= =?UTF-8?q?=E6=88=90result?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 417a4d0d..e1278c9c 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -437,10 +437,10 @@ void installWebApi() { allArgs["stream"]); if(src){ bool flag = src->close(allArgs["force"].as()); - val["code"] = flag ? 0 : -1; + val["result"] = flag ? 0 : -1; val["msg"] = flag ? "success" : "close failed"; }else{ - val["code"] = -2; + val["result"] = -2; val["msg"] = "can not find the stream"; } }); From 517f3c229c151ef3a4cfed98c7c17db19afaa34b Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 18 Nov 2019 16:34:39 +0800 Subject: [PATCH 029/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0kick=5Fsessions?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index e1278c9c..f1263e70 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -517,6 +517,28 @@ void installWebApi() { session->safeShutdown(); }); + + //批量断开tcp连接,比如说可以断开rtsp、rtmp播放器等 + //测试url http://127.0.0.1/index/api/kick_sessions?local_port=1935 + API_REGIST(api,kick_sessions,{ + CHECK_SECRET(); + uint16_t local_port = allArgs["local_port"].as(); + string &peer_ip = allArgs["peer_ip"]; + uint64_t count_hit = 0; + + SessionMap::Instance().for_each_session([&](const string &id,const TcpSession::Ptr &session){ + if(local_port != 0 && local_port != session->get_local_port()){ + return; + } + if(!peer_ip.empty() && peer_ip != session->get_peer_ip()){ + return; + } + session->safeShutdown(); + ++count_hit; + }); + val["count_hit"] = (Json::UInt64)count_hit; + }); + static auto addStreamProxy = [](const string &vhost, const string &app, const string &stream, From 2eb64d2c39cde19ebca8d45426e46c7d8c6124dd Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 19 Nov 2019 10:20:57 +0800 Subject: [PATCH 030/272] =?UTF-8?q?=E7=A1=AE=E4=BF=9Drtp=E7=AB=AF=E5=8F=A3?= =?UTF-8?q?=E4=B8=BA=E5=81=B6=E6=95=B0=EF=BC=8Crtcp=E7=AB=AF=E5=8F=A3?= =?UTF-8?q?=E4=B8=BA=E5=A5=87=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtsp/RtspPlayer.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Rtsp/RtspPlayer.cpp b/src/Rtsp/RtspPlayer.cpp index be1424d6..aa7dfce3 100644 --- a/src/Rtsp/RtspPlayer.cpp +++ b/src/Rtsp/RtspPlayer.cpp @@ -277,6 +277,13 @@ void RtspPlayer::createUdpSockIfNecessary(int track_idx){ throw std::runtime_error("open rtcp sock failed"); } } + + if(rtpSockRef->get_local_port() % 2 != 0){ + //如果rtp端口不是偶数,那么与rtcp端口互换,目的是兼容一些要求严格的服务器 + Socket::Ptr tmp = rtpSockRef; + rtpSockRef = rtcpSockRef; + rtcpSockRef = tmp; + } } From 85cd4a7d0234869faa4fc66c2129b5d54f71f413 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 19 Nov 2019 10:55:44 +0800 Subject: [PATCH 031/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=81=8D=E5=8E=86?= =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=8F=AF=E8=83=BD=E5=B4=A9=E6=BA=83=E7=9A=84?= =?UTF-8?q?bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index f1263e70..9a23da09 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -452,6 +452,7 @@ void installWebApi() { //筛选命中个数 int count_hit = 0; int count_closed = 0; + list media_list; MediaSource::for_each_media([&](const string &schema, const string &vhost, const string &app, @@ -470,10 +471,15 @@ void installWebApi() { return; } ++count_hit; - if(media->close(allArgs["force"].as())){ + media_list.emplace_back(media); + }); + + bool force = allArgs["force"].as(); + for(auto &media : media_list){ + if(media->close(force)){ ++count_closed; } - }); + } val["count_hit"] = count_hit; val["count_closed"] = count_closed; }); @@ -526,6 +532,7 @@ void installWebApi() { string &peer_ip = allArgs["peer_ip"]; uint64_t count_hit = 0; + list session_list; SessionMap::Instance().for_each_session([&](const string &id,const TcpSession::Ptr &session){ if(local_port != 0 && local_port != session->get_local_port()){ return; @@ -533,9 +540,13 @@ void installWebApi() { if(!peer_ip.empty() && peer_ip != session->get_peer_ip()){ return; } - session->safeShutdown(); + session_list.emplace_back(session); ++count_hit; }); + + for(auto &session : session_list){ + session->safeShutdown(); + } val["count_hit"] = (Json::UInt64)count_hit; }); From e6d511cc9e1f3aba97d6157c28b7dcb2eb85ad31 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 19 Nov 2019 15:52:02 +0800 Subject: [PATCH 032/272] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E6=92=AD=E6=94=BE?= =?UTF-8?q?=E6=88=90=E5=8A=9F=E4=B8=8E=E4=B8=AD=E9=80=94=E6=96=AD=E5=BC=80?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E8=A7=A6=E5=8F=91=E7=B4=8A=E4=B9=B1=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98:#143?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/media-server | 2 +- src/Rtmp/RtmpPlayer.cpp | 108 +++++++++++++++--------------- src/Rtmp/RtmpPlayer.h | 5 +- src/Rtsp/RtspPlayer.cpp | 145 ++++++++++++++++++++++------------------ src/Rtsp/RtspPlayer.h | 7 +- 5 files changed, 140 insertions(+), 127 deletions(-) diff --git a/3rdpart/media-server b/3rdpart/media-server index 40edf624..678678b2 160000 --- a/3rdpart/media-server +++ b/3rdpart/media-server @@ -1 +1 @@ -Subproject commit 40edf6243d9d99676062062efdec203b24a178aa +Subproject commit 678678b2ee7118d21d33a90a303698e0da02790c diff --git a/src/Rtmp/RtmpPlayer.cpp b/src/Rtmp/RtmpPlayer.cpp index cb5c4900..5251ca42 100644 --- a/src/Rtmp/RtmpPlayer.cpp +++ b/src/Rtmp/RtmpPlayer.cpp @@ -34,44 +34,35 @@ using namespace mediakit::Client; namespace mediakit { -unordered_map RtmpPlayer::g_mapCmd; RtmpPlayer::RtmpPlayer(const EventPoller::Ptr &poller) : TcpClient(poller) { - static onceToken token([]() { - g_mapCmd.emplace("_error",&RtmpPlayer::onCmd_result); - g_mapCmd.emplace("_result",&RtmpPlayer::onCmd_result); - g_mapCmd.emplace("onStatus",&RtmpPlayer::onCmd_onStatus); - g_mapCmd.emplace("onMetaData",&RtmpPlayer::onCmd_onMetaData); - }, []() {}); - } RtmpPlayer::~RtmpPlayer() { DebugL << endl; } + void RtmpPlayer::teardown() { if (alive()) { - _strApp.clear(); - _strStream.clear(); - _strTcUrl.clear(); - - { - lock_guard lck(_mtxOnResultCB); - _mapOnResultCB.clear(); - } - { - lock_guard lck(_mtxOnStatusCB); - _dqOnStatusCB.clear(); - } - _pBeatTimer.reset(); - _pPlayTimer.reset(); - _pMediaTimer.reset(); - _iSeekTo = 0; - CLEAR_ARR(_aiFistStamp); - CLEAR_ARR(_aiNowStamp); - reset(); - shutdown(SockException(Err_shutdown,"teardown")); + shutdown(SockException(Err_shutdown,"teardown")); } + _strApp.clear(); + _strStream.clear(); + _strTcUrl.clear(); + _pBeatTimer.reset(); + _pPlayTimer.reset(); + _pMediaTimer.reset(); + _iSeekTo = 0; + RtmpProtocol::reset(); + + CLEAR_ARR(_aiFistStamp); + CLEAR_ARR(_aiNowStamp); + + lock_guard lck(_mtxOnResultCB); + _mapOnResultCB.clear(); + lock_guard lck2(_mtxOnStatusCB); + _dqOnStatusCB.clear(); } + void RtmpPlayer::play(const string &strUrl) { teardown(); string strHost = FindField(strUrl.data(), "://", "/"); @@ -80,7 +71,7 @@ void RtmpPlayer::play(const string &strUrl) { _strTcUrl = string("rtmp://") + strHost + "/" + _strApp; if (!_strApp.size() || !_strStream.size()) { - onPlayResult_l(SockException(Err_other,"rtmp url非法")); + onPlayResult_l(SockException(Err_other,"rtmp url非法"),false); return; } DebugL << strHost << " " << _strApp << " " << _strStream; @@ -104,7 +95,7 @@ void RtmpPlayer::play(const string &strUrl) { if(!strongSelf) { return false; } - strongSelf->onPlayResult_l(SockException(Err_timeout,"play rtmp timeout")); + strongSelf->onPlayResult_l(SockException(Err_timeout,"play rtmp timeout"),false); return false; },getPoller())); @@ -112,53 +103,52 @@ void RtmpPlayer::play(const string &strUrl) { startConnect(strHost, iPort , playTimeOutSec); } void RtmpPlayer::onErr(const SockException &ex){ - onPlayResult_l(ex); + //定时器_pPlayTimer为空后表明握手结束了 + onPlayResult_l(ex, !_pPlayTimer); } -void RtmpPlayer::onPlayResult_l(const SockException &ex) { +void RtmpPlayer::onPlayResult_l(const SockException &ex , bool handshakeCompleted) { WarnL << ex.getErrCode() << " " << ex.what(); if(!ex){ - //恢复rtmp接收超时定时器 + //播放成功,恢复rtmp接收超时定时器 _mediaTicker.resetTime(); weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); int timeoutMS = (*this)[kMediaTimeoutMS].as(); + //创建rtmp数据接收超时检测定时器 _pMediaTimer.reset( new Timer(timeoutMS / 2000.0, [weakSelf,timeoutMS]() { auto strongSelf=weakSelf.lock(); if(!strongSelf) { return false; } if(strongSelf->_mediaTicker.elapsedTime()> timeoutMS) { - //recv media timeout! - strongSelf->onPlayResult_l(SockException(Err_timeout,"recv rtmp timeout")); + //接收rtmp媒体数据超时 + strongSelf->onPlayResult_l(SockException(Err_timeout,"receive rtmp timeout"),true); return false; } return true; },getPoller())); } - if (_pPlayTimer) { + if (!handshakeCompleted) { //开始播放阶段 _pPlayTimer.reset(); onPlayResult(ex); - }else { - //播放中途阶段 - if (ex) { - //播放成功后异常断开回调 - onShutdown(ex); - }else{ - //恢复播放 - onResume(); - } - } + } else if (ex) { + //播放成功后异常断开回调 + onShutdown(ex); + } else { + //恢复播放 + onResume(); + } if(ex){ teardown(); } } void RtmpPlayer::onConnect(const SockException &err){ - if(err.getErrCode()!=Err_success) { - onPlayResult_l(err); + if(err.getErrCode() != Err_success) { + onPlayResult_l(err, false); return; } weak_ptr weakSelf= dynamic_pointer_cast(shared_from_this()); @@ -175,7 +165,8 @@ void RtmpPlayer::onRecv(const Buffer::Ptr &pBuf){ onParseRtmp(pBuf->data(), pBuf->size()); } catch (exception &e) { SockException ex(Err_other, e.what()); - onPlayResult_l(ex); + //定时器_pPlayTimer为空后表明握手结束了 + onPlayResult_l(ex, !_pPlayTimer); } } @@ -253,7 +244,7 @@ inline void RtmpPlayer::send_pause(bool bPause) { }else{ _bPaused = bPause; if(!bPause){ - onPlayResult_l(SockException(Err_success, "rtmp resum success")); + onPlayResult_l(SockException(Err_success, "resum rtmp success"), true); }else{ //暂停播放 _pMediaTimer.reset(); @@ -327,7 +318,7 @@ void RtmpPlayer::onCmd_onMetaData(AMFDecoder &dec) { void RtmpPlayer::onStreamDry(uint32_t ui32StreamId) { //TraceL << ui32StreamId; - onPlayResult_l(SockException(Err_other,"rtmp stream dry")); + onPlayResult_l(SockException(Err_other,"rtmp stream dry"), true); } void RtmpPlayer::onMediaData_l(const RtmpPacket::Ptr &packet) { @@ -343,7 +334,7 @@ void RtmpPlayer::onMediaData_l(const RtmpPacket::Ptr &packet) { onMediaData(packet); }else{ //先触发onPlayResult事件,这个时候解码器才能初始化完毕 - onPlayResult_l(SockException(Err_success,"play rtmp success")); + onPlayResult_l(SockException(Err_success,"play rtmp success"), false); //触发onPlayResult事件后,再把帧数据输入到解码器 onMediaData(packet); } @@ -351,6 +342,15 @@ void RtmpPlayer::onMediaData_l(const RtmpPacket::Ptr &packet) { void RtmpPlayer::onRtmpChunk(RtmpPacket &chunkData) { + typedef void (RtmpPlayer::*rtmp_func_ptr)(AMFDecoder &dec); + static unordered_map s_func_map; + static onceToken token([]() { + s_func_map.emplace("_error",&RtmpPlayer::onCmd_result); + s_func_map.emplace("_result",&RtmpPlayer::onCmd_result); + s_func_map.emplace("onStatus",&RtmpPlayer::onCmd_onStatus); + s_func_map.emplace("onMetaData",&RtmpPlayer::onCmd_onMetaData); + }, []() {}); + switch (chunkData.typeId) { case MSG_CMD: case MSG_CMD3: @@ -358,8 +358,8 @@ void RtmpPlayer::onRtmpChunk(RtmpPacket &chunkData) { case MSG_DATA3: { AMFDecoder dec(chunkData.strBuf, 0); std::string type = dec.load(); - auto it = g_mapCmd.find(type); - if(it != g_mapCmd.end()){ + auto it = s_func_map.find(type); + if(it != s_func_map.end()){ auto fun = it->second; (this->*fun)(dec); }else{ diff --git a/src/Rtmp/RtmpPlayer.h b/src/Rtmp/RtmpPlayer.h index e63ee668..e06e8191 100644 --- a/src/Rtmp/RtmpPlayer.h +++ b/src/Rtmp/RtmpPlayer.h @@ -61,7 +61,7 @@ protected: void seekToMilliSecond(uint32_t ms); protected: void onMediaData_l(const RtmpPacket::Ptr &chunkData); - void onPlayResult_l(const SockException &ex); + void onPlayResult_l(const SockException &ex, bool handshakeCompleted); //form Tcpclient void onRecv(const Buffer::Ptr &pBuf) override; @@ -104,9 +104,6 @@ private: deque > _dqOnStatusCB; recursive_mutex _mtxOnStatusCB; - typedef void (RtmpPlayer::*rtmpCMDHandle)(AMFDecoder &dec); - static unordered_map g_mapCmd; - //超时功能实现 Ticker _mediaTicker; std::shared_ptr _pMediaTimer; diff --git a/src/Rtsp/RtspPlayer.cpp b/src/Rtsp/RtspPlayer.cpp index aa7dfce3..7c9c74ad 100644 --- a/src/Rtsp/RtspPlayer.cpp +++ b/src/Rtsp/RtspPlayer.cpp @@ -42,11 +42,17 @@ using namespace mediakit::Client; namespace mediakit { +enum PlayType { + type_play = 0, + type_pause, + type_seek +}; + RtspPlayer::RtspPlayer(const EventPoller::Ptr &poller) : TcpClient(poller){ RtpReceiver::setPoolSize(64); } RtspPlayer::~RtspPlayer(void) { - DebugL<onPlayResult_l(SockException(Err_timeout,"play rtsp timeout")); + strongSelf->onPlayResult_l(SockException(Err_timeout,"play rtsp timeout"),false); return false; },getPoller())); @@ -158,8 +163,8 @@ void RtspPlayer::play(bool isSSL,const string &strUrl, const string &strUser, co startConnect(ip, port , playTimeOutSec); } void RtspPlayer::onConnect(const SockException &err){ - if(err.getErrCode()!=Err_success) { - onPlayResult_l(err); + if(err.getErrCode() != Err_success) { + onPlayResult_l(err,false); return; } @@ -170,7 +175,8 @@ void RtspPlayer::onRecv(const Buffer::Ptr& pBuf) { input(pBuf->data(),pBuf->size()); } void RtspPlayer::onErr(const SockException &ex) { - onPlayResult_l(ex); + //定时器_pPlayTimer为空后表明握手结束了 + onPlayResult_l(ex,!_pPlayTimer); } // from live555 bool RtspPlayer::handleAuthenticationFailure(const string ¶msStr) { @@ -403,8 +409,7 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex) WarnL << "收到其他地址的rtcp数据:" << inet_ntoa(((struct sockaddr_in *) addr)->sin_addr); return; } - strongSelf->onRtcpPacket(uiTrackIndex, strongSelf->_aTrackInfo[uiTrackIndex], - (unsigned char *) buf->data(), buf->size()); + strongSelf->onRtcpPacket(uiTrackIndex, strongSelf->_aTrackInfo[uiTrackIndex], (unsigned char *) buf->data(), buf->size()); }); } } @@ -416,14 +421,7 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex) } //所有setup命令发送完毕 //发送play命令 - sendPause(false, 0,false); -} - -void RtspPlayer::sendOptions() { - _onHandshake = [](const Parser& parser){ -// DebugL << "options response"; - }; - sendRtspRequest("OPTIONS",_strContentBase); + sendPause(type_play, 0); } void RtspPlayer::sendDescribe() { @@ -432,46 +430,67 @@ void RtspPlayer::sendDescribe() { sendRtspRequest("DESCRIBE",_strUrl,{"Accept","application/sdp"}); } - -void RtspPlayer::sendPause(bool bPause,uint32_t seekMS,bool range){ +void RtspPlayer::sendPause(int type , uint32_t seekMS){ + _onHandshake = std::bind(&RtspPlayer::handleResPAUSE,this, placeholders::_1,type); //开启或暂停rtsp - _onHandshake = std::bind(&RtspPlayer::handleResPAUSE,this, placeholders::_1,bPause); - if(!bPause && range){ - sendRtspRequest(bPause ? "PAUSE" : "PLAY", _strContentBase, - {"Range",StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << seekMS / 1000.0 << "-"}); - } else{ - sendRtspRequest(bPause ? "PAUSE" : "PLAY", _strContentBase); - } - + switch (type){ + case type_pause: + sendRtspRequest("PAUSE", _strContentBase); + break; + case type_play: + sendRtspRequest("PLAY", _strContentBase); + break; + case type_seek: + sendRtspRequest("PLAY", _strContentBase, {"Range",StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << seekMS / 1000.0 << "-"}); + break; + default: + WarnL << "unknown type : " << type; + _onHandshake = nullptr; + break; + } } void RtspPlayer::pause(bool bPause) { - sendPause(bPause, getProgressMilliSecond(),false); + sendPause(bPause ? type_pause : type_seek, getProgressMilliSecond()); } -void RtspPlayer::handleResPAUSE(const Parser& parser, bool bPause) { +void RtspPlayer::handleResPAUSE(const Parser& parser,int type) { if (parser.Url() != "200") { - WarnL <<(bPause ? "Pause" : "Play") << " failed:" << parser.Url() << " " << parser.Tail() << endl; + switch (type) { + case type_pause: + WarnL << "Pause failed:" << parser.Url() << " " << parser.Tail() << endl; + break; + case type_play: + WarnL << "Play failed:" << parser.Url() << " " << parser.Tail() << endl; + break; + case type_seek: + WarnL << "Seek failed:" << parser.Url() << " " << parser.Tail() << endl; + break; + } return; } - if (!bPause) { - uint32_t iSeekTo = 0; - //修正时间轴 - auto strRange = parser["Range"]; - if (strRange.size()) { - auto strStart = FindField(strRange.data(), "npt=", "-"); - if (strStart == "now") { - strStart = "0"; - } - iSeekTo = 1000 * atof(strStart.data()); - DebugL << "seekTo(ms):" << iSeekTo ; - } - //设置相对时间戳 - _stamp[0].setRelativeStamp(iSeekTo); - _stamp[1].setRelativeStamp(iSeekTo); - onPlayResult_l(SockException(Err_success, "rtsp play success")); - } else { + + if (type == type_pause) { + //暂停成功! _pRtpTimer.reset(); + return; } + + //play或seek成功 + uint32_t iSeekTo = 0; + //修正时间轴 + auto strRange = parser["Range"]; + if (strRange.size()) { + auto strStart = FindField(strRange.data(), "npt=", "-"); + if (strStart == "now") { + strStart = "0"; + } + iSeekTo = 1000 * atof(strStart.data()); + DebugL << "seekTo(ms):" << iSeekTo; + } + //设置相对时间戳 + _stamp[0].setRelativeStamp(iSeekTo); + _stamp[1].setRelativeStamp(iSeekTo); + onPlayResult_l(SockException(Err_success, type == type_seek ? "resum rtsp success" : "rtsp play success"), type == type_seek); } void RtspPlayer::onWholeRtspPacket(Parser &parser) { @@ -483,8 +502,8 @@ void RtspPlayer::onWholeRtspPacket(Parser &parser) { } parser.Clear(); } catch (std::exception &err) { - SockException ex(Err_other, err.what()); - onPlayResult_l(ex); + //定时器_pPlayTimer为空后表明握手结束了 + onPlayResult_l(SockException(Err_other, err.what()),!_pPlayTimer); } } @@ -674,7 +693,7 @@ uint32_t RtspPlayer::getProgressMilliSecond() const{ return MAX(_stamp[0].getRelativeStamp(),_stamp[1].getRelativeStamp()); } void RtspPlayer::seekToMilliSecond(uint32_t ms) { - sendPause(false,ms, true); + sendPause(type_seek,ms); } void RtspPlayer::sendRtspRequest(const string &cmd, const string &url, const std::initializer_list &header) { @@ -764,7 +783,7 @@ void RtspPlayer::onRecvRTP_l(const RtpPacket::Ptr &pkt, const SdpTrack::Ptr &tra } -void RtspPlayer::onPlayResult_l(const SockException &ex) { +void RtspPlayer::onPlayResult_l(const SockException &ex , bool handshakeCompleted) { WarnL << ex.getErrCode() << " " << ex.what(); if(!ex){ @@ -772,33 +791,31 @@ void RtspPlayer::onPlayResult_l(const SockException &ex) { _rtpTicker.resetTime(); weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); int timeoutMS = (*this)[kMediaTimeoutMS].as(); - _pRtpTimer.reset( new Timer(timeoutMS / 2000.0, [weakSelf,timeoutMS]() { + //创建rtp数据接收超时检测定时器 + _pRtpTimer.reset( new Timer(timeoutMS / 2000.0, [weakSelf,timeoutMS]() { auto strongSelf=weakSelf.lock(); if(!strongSelf) { return false; } if(strongSelf->_rtpTicker.elapsedTime()> timeoutMS) { - //recv rtp timeout! - strongSelf->onPlayResult_l(SockException(Err_timeout,"recv rtp timeout")); + //接收rtp媒体数据包超时 + strongSelf->onPlayResult_l(SockException(Err_timeout,"receive rtp timeout"), true); return false; } return true; },getPoller())); } - if (_pPlayTimer) { + if (!handshakeCompleted) { //开始播放阶段 _pPlayTimer.reset(); onPlayResult(ex); - }else { - //播放中途阶段 - if (ex) { - //播放成功后异常断开回调 - onShutdown(ex); - }else{ - //恢复播放 - onResume(); - } + } else if (ex) { + //播放成功后异常断开回调 + onShutdown(ex); + } else { + //恢复播放 + onResume(); } if(ex){ diff --git a/src/Rtsp/RtspPlayer.h b/src/Rtsp/RtspPlayer.h index be170953..a633ab91 100644 --- a/src/Rtsp/RtspPlayer.h +++ b/src/Rtsp/RtspPlayer.h @@ -101,7 +101,7 @@ protected: void onErr(const SockException &ex) override; private: void onRecvRTP_l(const RtpPacket::Ptr &pRtppt, const SdpTrack::Ptr &track); - void onPlayResult_l(const SockException &ex); + void onPlayResult_l(const SockException &ex , bool handshakeCompleted); int getTrackIndexByControlSuffix(const string &controlSuffix) const; int getTrackIndexByInterleaved(int interleaved) const; @@ -111,12 +111,11 @@ private: void handleResSETUP(const Parser &parser, unsigned int uiTrackIndex); void handleResDESCRIBE(const Parser &parser); bool handleAuthenticationFailure(const string &wwwAuthenticateParamsStr); - void handleResPAUSE(const Parser &parser, bool bPause); + void handleResPAUSE(const Parser &parser, int type); //发送SETUP命令 void sendSetup(unsigned int uiTrackIndex); - void sendPause(bool bPause,uint32_t ms, bool range); - void sendOptions(); + void sendPause(int type , uint32_t ms); void sendDescribe(); void sendRtspRequest(const string &cmd, const string &url ,const StrCaseMap &header = StrCaseMap()); From 083933cbad92ea696752d4617fa52a5272328c51 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 21 Nov 2019 13:46:42 +0800 Subject: [PATCH 033/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AE=BF=E9=97=AE?= =?UTF-8?q?=E9=87=8E=E6=8C=87=E9=92=88=E7=9A=84=E9=97=AE=E9=A2=98=EF=BC=9A?= =?UTF-8?q?#146?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/HttpSession.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp index 10d6b08d..a6ccb2b9 100644 --- a/src/Http/HttpSession.cpp +++ b/src/Http/HttpSession.cpp @@ -433,8 +433,9 @@ static string findIndexFile(const string &dir){ while ((pDirent = readdir(pDir)) != NULL) { static set indexSet = {"index.html","index.htm","index"}; if(indexSet.find(pDirent->d_name) != indexSet.end()){ + string ret = pDirent->d_name; closedir(pDir); - return pDirent->d_name; + return ret; } } closedir(pDir); From e7bd5a41749b346d59db9f5d4ddecbeb3ee3384b Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 21 Nov 2019 15:11:43 +0800 Subject: [PATCH 034/272] =?UTF-8?q?=E5=85=BC=E5=AE=B9=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=A5=87=E6=80=AA=E7=9A=84sdp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Extension/Factory.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Extension/Factory.cpp b/src/Extension/Factory.cpp index d65dbb56..053dd5a1 100644 --- a/src/Extension/Factory.cpp +++ b/src/Extension/Factory.cpp @@ -63,13 +63,14 @@ Track::Ptr Factory::getTrackBySdp(const SdpTrack::Ptr &track) { //a=fmtp:96 packetization-mode=1;profile-level-id=42C01F;sprop-parameter-sets=Z0LAH9oBQBboQAAAAwBAAAAPI8YMqA==,aM48gA== auto map = Parser::parseArgs(FindField(track->_fmtp.data()," ", nullptr),";","="); auto sps_pps = map["sprop-parameter-sets"]; - if(sps_pps.empty()){ - return std::make_shared(); - } string base64_SPS = FindField(sps_pps.data(), NULL, ","); string base64_PPS = FindField(sps_pps.data(), ",", NULL); auto sps = decodeBase64(base64_SPS); auto pps = decodeBase64(base64_PPS); + if(sps.empty() || pps.empty()){ + return std::make_shared(); + } + return std::make_shared(sps,pps,0,0); } From 54f2c0b53cd1c7d86b5d659db480fbaa7fbcffb9 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 21 Nov 2019 16:31:50 +0800 Subject: [PATCH 035/272] =?UTF-8?q?=E5=85=BC=E5=AE=B9=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E5=A5=87=E6=80=AAsdp=E7=9A=84rtsp=20url?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Extension/Factory.cpp | 5 +++++ src/Player/PlayerBase.cpp | 16 +++++++++++----- src/Player/PlayerBase.h | 10 +--------- src/Rtmp/RtmpPlayer.h | 1 + src/Rtsp/RtspPlayerImp.h | 26 ++++++++++++++++++-------- 5 files changed, 36 insertions(+), 22 deletions(-) diff --git a/src/Extension/Factory.cpp b/src/Extension/Factory.cpp index 053dd5a1..4aad5e9f 100644 --- a/src/Extension/Factory.cpp +++ b/src/Extension/Factory.cpp @@ -68,6 +68,7 @@ Track::Ptr Factory::getTrackBySdp(const SdpTrack::Ptr &track) { auto sps = decodeBase64(base64_SPS); auto pps = decodeBase64(base64_PPS); if(sps.empty() || pps.empty()){ + //如果sdp里面没有sps/pps,那么可能在后续的rtp里面恢复出sps/pps return std::make_shared(); } @@ -80,6 +81,10 @@ Track::Ptr Factory::getTrackBySdp(const SdpTrack::Ptr &track) { auto vps = decodeBase64(map["sprop-vps"]); auto sps = decodeBase64(map["sprop-sps"]); auto pps = decodeBase64(map["sprop-pps"]); + if(sps.empty() || pps.empty()){ + //如果sdp里面没有sps/pps,那么可能在后续的rtp里面恢复出sps/pps + return std::make_shared(); + } return std::make_shared(vps,sps,pps,0,0,0); } diff --git a/src/Player/PlayerBase.cpp b/src/Player/PlayerBase.cpp index 4de82c30..a48879a1 100644 --- a/src/Player/PlayerBase.cpp +++ b/src/Player/PlayerBase.cpp @@ -69,12 +69,18 @@ PlayerBase::PlayerBase() { ///////////////////////////Demuxer////////////////////////////// bool Demuxer::isInited(int analysisMs) { - if(_ticker.createdTime() < analysisMs){ - //analysisMs毫秒内判断条件 - //如果音视频都准备好了 ,说明Track全部就绪 - return (_videoTrack && _videoTrack->ready() && _audioTrack && _audioTrack->ready()); + if(analysisMs && _ticker.createdTime() > analysisMs){ + //analysisMs毫秒后强制初始化完毕 + return true; + } + if (_videoTrack && !_videoTrack->ready()) { + //视频未准备好 + return false; + } + if (_audioTrack && !_audioTrack->ready()) { + //音频未准备好 + return false; } - //analysisMs毫秒后强制初始化完毕 return true; } diff --git a/src/Player/PlayerBase.h b/src/Player/PlayerBase.h index 50330b06..84fd6795 100644 --- a/src/Player/PlayerBase.h +++ b/src/Player/PlayerBase.h @@ -236,18 +236,10 @@ protected: } void onPlayResult(const SockException &ex) override { - if(!_playResultCB){ - return; - } - if(ex){ - //播放失败,则立即回调 + if(_playResultCB) { _playResultCB(ex); _playResultCB = nullptr; - return; } - //播放成功 - _playResultCB(ex); - _playResultCB = nullptr; } void onResume() override{ diff --git a/src/Rtmp/RtmpPlayer.h b/src/Rtmp/RtmpPlayer.h index e06e8191..10bc8ff1 100644 --- a/src/Rtmp/RtmpPlayer.h +++ b/src/Rtmp/RtmpPlayer.h @@ -61,6 +61,7 @@ protected: void seekToMilliSecond(uint32_t ms); protected: void onMediaData_l(const RtmpPacket::Ptr &chunkData); + //在获取config帧后才触发onPlayResult_l(而不是收到play命令回复),所以此时所有track都初始化完毕了 void onPlayResult_l(const SockException &ex, bool handshakeCompleted); //form Tcpclient diff --git a/src/Rtsp/RtspPlayerImp.h b/src/Rtsp/RtspPlayerImp.h index effa7328..c9702b43 100644 --- a/src/Rtsp/RtspPlayerImp.h +++ b/src/Rtsp/RtspPlayerImp.h @@ -75,19 +75,29 @@ private: } _parser->inputRtp(rtp); - //由于我们重载isInited方法强制认为一旦获取sdp那么就初始化Track成功, - //所以我们不需要在后续检验是否初始化成功 - //checkInited(0); + if(_maxAnalysisMS && _parser->isInited(_maxAnalysisMS)){ + PlayerImp::onPlayResult(SockException(Err_success,"play rtsp success")); + _maxAnalysisMS = 0; + } } - bool isInited(int analysisMs) override{ - //rtsp是通过sdp来完成track的初始化的,所以我们强制返回true, - //认为已经初始化完毕,这样可以提高rtsp打开速度 - return true; + //在RtspPlayer中触发onPlayResult事件只是代表收到play回复了, + //并不代表所有track初始化成功了(这跟rtmp播放器不一样) + //如果sdp里面信息不完整,只能尝试延后从rtp中恢复关键信息并初始化track + //如果超过这个时间还未获取成功,那么会强制触发onPlayResult事件(虽然此时有些track还未初始化成功) + void onPlayResult(const SockException &ex) override { + //isInited判断条件:无超时 + if(_parser->isInited(0)){ + //已经初始化成功,说明sdp里面有完善的信息 + PlayerImp::onPlayResult(ex); + }else{ + //还没初始化成功,说明sdp里面信息不完善,还有一些track未初始化成功 + _maxAnalysisMS = (*this)[Client::kMaxAnalysisMS]; + } } private: RtspMediaSource::Ptr _pRtspMediaSrc; - + int _maxAnalysisMS = 0; }; } /* namespace mediakit */ From ab97486200f21fed6b5d2f2b632b39b7e7ad68b1 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 21 Nov 2019 16:34:18 +0800 Subject: [PATCH 036/272] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=88=A4=E6=96=AD?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtsp/RtspPlayerImp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Rtsp/RtspPlayerImp.h b/src/Rtsp/RtspPlayerImp.h index c9702b43..0bf77c34 100644 --- a/src/Rtsp/RtspPlayerImp.h +++ b/src/Rtsp/RtspPlayerImp.h @@ -87,7 +87,7 @@ private: //如果超过这个时间还未获取成功,那么会强制触发onPlayResult事件(虽然此时有些track还未初始化成功) void onPlayResult(const SockException &ex) override { //isInited判断条件:无超时 - if(_parser->isInited(0)){ + if(ex || _parser->isInited(0)){ //已经初始化成功,说明sdp里面有完善的信息 PlayerImp::onPlayResult(ex); }else{ From 21cf3bcee0e2011cbd43e9edc7853e1fb645ff3b Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 21 Nov 2019 18:07:40 +0800 Subject: [PATCH 037/272] =?UTF-8?q?=E6=92=AD=E6=94=BE=E5=99=A8track?= =?UTF-8?q?=E6=9C=AA=E5=88=9D=E5=A7=8B=E5=8C=96=E6=88=90=E5=8A=9F=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E6=94=B9=E6=88=90=E6=9C=80=E5=A4=A7=E4=B8=BA5?= =?UTF-8?q?=E7=A7=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Player/PlayerBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Player/PlayerBase.cpp b/src/Player/PlayerBase.cpp index a48879a1..eab05ce7 100644 --- a/src/Player/PlayerBase.cpp +++ b/src/Player/PlayerBase.cpp @@ -64,7 +64,7 @@ PlayerBase::PlayerBase() { this->mINI::operator[](kTimeoutMS) = 10000; this->mINI::operator[](kMediaTimeoutMS) = 5000; this->mINI::operator[](kBeatIntervalMS) = 5000; - this->mINI::operator[](kMaxAnalysisMS) = 2000; + this->mINI::operator[](kMaxAnalysisMS) = 5000; } ///////////////////////////Demuxer////////////////////////////// From bf70a01f6adc67b5fac477ec1572282d2b00b450 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 25 Nov 2019 17:59:04 +0800 Subject: [PATCH 038/272] =?UTF-8?q?=E6=8F=90=E5=8D=87rtsp=E7=9A=84?= =?UTF-8?q?=E5=85=BC=E5=AE=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtsp/RtspPlayer.cpp | 15 ++++++--------- src/Rtsp/RtspPlayer.h | 1 - src/Rtsp/RtspPusher.cpp | 3 +++ src/Rtsp/RtspSession.cpp | 6 ++++++ 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Rtsp/RtspPlayer.cpp b/src/Rtsp/RtspPlayer.cpp index 7c9c74ad..10a1f7e3 100644 --- a/src/Rtsp/RtspPlayer.cpp +++ b/src/Rtsp/RtspPlayer.cpp @@ -823,21 +823,15 @@ void RtspPlayer::onPlayResult_l(const SockException &ex , bool handshakeComplete } } -int RtspPlayer::getTrackIndexByControlSuffix(const string &controlSuffix) const{ - for (unsigned int i = 0; i < _aTrackInfo.size(); i++) { - auto pos = _aTrackInfo[i]->_control_surffix.find(controlSuffix); - if (pos == 0) { - return i; - } - } - return -1; -} int RtspPlayer::getTrackIndexByInterleaved(int interleaved) const{ for (unsigned int i = 0; i < _aTrackInfo.size(); i++) { if (_aTrackInfo[i]->_interleaved == interleaved) { return i; } } + if(_aTrackInfo.size() == 1){ + return 0; + } return -1; } @@ -847,6 +841,9 @@ int RtspPlayer::getTrackIndexByTrackType(TrackType trackType) const { return i; } } + if(_aTrackInfo.size() == 1){ + return 0; + } return -1; } diff --git a/src/Rtsp/RtspPlayer.h b/src/Rtsp/RtspPlayer.h index a633ab91..ee7afba0 100644 --- a/src/Rtsp/RtspPlayer.h +++ b/src/Rtsp/RtspPlayer.h @@ -103,7 +103,6 @@ private: void onRecvRTP_l(const RtpPacket::Ptr &pRtppt, const SdpTrack::Ptr &track); void onPlayResult_l(const SockException &ex , bool handshakeCompleted); - int getTrackIndexByControlSuffix(const string &controlSuffix) const; int getTrackIndexByInterleaved(int interleaved) const; int getTrackIndexByTrackType(TrackType trackType) const; diff --git a/src/Rtsp/RtspPusher.cpp b/src/Rtsp/RtspPusher.cpp index 7e2ca8c2..e40a185b 100644 --- a/src/Rtsp/RtspPusher.cpp +++ b/src/Rtsp/RtspPusher.cpp @@ -351,6 +351,9 @@ inline int RtspPusher::getTrackIndexByTrackType(TrackType type) { return i; } } + if(_aTrackInfo.size() == 1){ + return 0; + } return -1; } diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index 14e2b89a..c06e204a 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -1105,6 +1105,9 @@ inline int RtspSession::getTrackIndexByTrackType(TrackType type) { return i; } } + if(_aTrackInfo.size() == 1){ + return 0; + } return -1; } inline int RtspSession::getTrackIndexByControlSuffix(const string &controlSuffix) { @@ -1125,6 +1128,9 @@ inline int RtspSession::getTrackIndexByInterleaved(int interleaved){ return i; } } + if(_aTrackInfo.size() == 1){ + return 0; + } return -1; } From e84af6a9e255f545b25d4a180e1bfda8aec97de0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Tue, 26 Nov 2019 09:52:20 +0800 Subject: [PATCH 039/272] Update ccpp.yml --- .github/workflows/ccpp.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index dc2660e2..55bb0021 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -14,7 +14,7 @@ jobs: run: git submodule update --init - name: apt-get安装依赖库(非必选) - run: sudo apt-get install -y cmake libmysqlclient-dev libssl-dev libx264-dev libfaac-dev libmp4v2-dev libsdl-dev libavcodec-dev libavutil-dev + run: sudo apt-get install -y cmake libssl-dev libmp4v2-dev libsdl-dev libavcodec-dev libavutil-dev - name: 编译 run: mkdir -p linux_build && cd linux_build && cmake .. && make -j4 From c42ee1dc5d4b2aa1db638f5b4a23f80e949892f1 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 29 Nov 2019 09:54:48 +0800 Subject: [PATCH 040/272] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E6=88=B3=E8=A6=86=E7=9B=96=E9=80=BB=E8=BE=91:#148?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MediaFile/Stamp.cpp | 7 +++++-- src/MediaFile/Stamp.h | 1 + src/Rtmp/RtmpSession.cpp | 2 +- src/Rtsp/RtspSession.cpp | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/MediaFile/Stamp.cpp b/src/MediaFile/Stamp.cpp index ab77a1bf..4474fe45 100644 --- a/src/MediaFile/Stamp.cpp +++ b/src/MediaFile/Stamp.cpp @@ -60,8 +60,11 @@ void Stamp::revise(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out, //pts和dts的差值 int pts_dts_diff = pts - dts; - //相对时间戳 - _relativeStamp += deltaStamp(modifyStamp ? _ticker.elapsedTime() : dts); + if(_last_dts != dts){ + //时间戳发生变更 + _relativeStamp += deltaStamp(modifyStamp ? _ticker.elapsedTime() : dts); + _last_dts = dts; + } dts_out = _relativeStamp; //////////////以下是播放时间戳的计算////////////////// diff --git a/src/MediaFile/Stamp.h b/src/MediaFile/Stamp.h index 82458fda..09e77338 100644 --- a/src/MediaFile/Stamp.h +++ b/src/MediaFile/Stamp.h @@ -84,6 +84,7 @@ public: int64_t getRelativeStamp() const ; private: int64_t _relativeStamp = 0; + int64_t _last_dts = -1; SmoothTicker _ticker; }; diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index e946d275..671ff101 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -493,7 +493,7 @@ void RtmpSession::onRtmpChunk(RtmpPacket &chunkData) { GET_CONFIG(bool,rtmp_modify_stamp,Rtmp::kModifyStamp); if(rtmp_modify_stamp){ int64_t dts_out; - _stamp[chunkData.typeId % 2].revise(0, 0, dts_out, dts_out, true); + _stamp[chunkData.typeId % 2].revise(chunkData.timeStamp, chunkData.timeStamp, dts_out, dts_out, true); chunkData.timeStamp = dts_out; } if(!_metadata_got && !chunkData.isCfgFrame()){ diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index c06e204a..cb138308 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -940,7 +940,7 @@ void RtspSession::onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx) { GET_CONFIG(bool,modify_stamp,Rtsp::kModifyStamp); if(modify_stamp){ int64_t dts_out; - _stamp[trackidx].revise(0, 0, dts_out, dts_out, true); + _stamp[trackidx].revise(rtppt->timeStamp, rtppt->timeStamp, dts_out, dts_out, true); rtppt->timeStamp = dts_out; } _pushSrc->onWrite(rtppt, false); From d587ddf7efeb5dc639cb4464d46b3992f5c01e3f Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 29 Nov 2019 10:12:20 +0800 Subject: [PATCH 041/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8=E5=90=AF=E5=8A=A8=E6=8A=A5=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 6 ++++++ server/WebHook.cpp | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 9a23da09..d1cb621c 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -840,6 +840,12 @@ void installWebApi() { }); + API_REGIST(hook,on_server_started,{ + //服务器重启报告 + throw SuccessException(); + }); + + } void unInstallWebApi(){ diff --git a/server/WebHook.cpp b/server/WebHook.cpp index 1391d10a..85c63d84 100644 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -71,6 +71,7 @@ const string kOnRecordMp4 = HOOK_FIELD"on_record_mp4"; const string kOnShellLogin = HOOK_FIELD"on_shell_login"; const string kOnStreamNoneReader = HOOK_FIELD"on_stream_none_reader"; const string kOnHttpAccess = HOOK_FIELD"on_http_access"; +const string kOnServerStarted = HOOK_FIELD"on_server_started"; const string kAdminParams = HOOK_FIELD"admin_params"; onceToken token([](){ @@ -87,6 +88,7 @@ onceToken token([](){ mINI::Instance()[kOnShellLogin] = "https://127.0.0.1/index/hook/on_shell_login"; mINI::Instance()[kOnStreamNoneReader] = "https://127.0.0.1/index/hook/on_stream_none_reader"; mINI::Instance()[kOnHttpAccess] = "https://127.0.0.1/index/hook/on_http_access"; + mINI::Instance()[kOnServerStarted] = "https://127.0.0.1/index/hook/on_server_started"; mINI::Instance()[kAdminParams] = "secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc"; },nullptr); }//namespace Hook @@ -177,6 +179,20 @@ static ArgsType make_json(const MediaInfo &args){ return std::move(body); } +static void reportServerStarted(){ + GET_CONFIG(bool,hook_enable,Hook::kEnable); + GET_CONFIG(string,hook_server_started,Hook::kOnServerStarted); + if(!hook_enable || hook_server_started.empty()){ + return; + } + + ArgsType body; + for (auto &pr : mINI::Instance()) { + body[pr.first] = (string &) pr.second; + } + //执行hook + do_http_hook(hook_server_started,body, nullptr); +} void installWebHook(){ GET_CONFIG(bool,hook_enable,Hook::kEnable); @@ -459,6 +475,9 @@ void installWebHook(){ invoker(obj["err"].asString(),obj["path"].asString(),obj["second"].asInt()); }); }); + + //汇报服务器重新启动 + reportServerStarted(); } void unInstallWebHook(){ From 22c01d0a03fd20037617ff5372c1cdf0e0831853 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 29 Nov 2019 10:14:08 +0800 Subject: [PATCH 042/272] =?UTF-8?q?=E5=AE=8C=E5=96=84=E8=8C=83=E4=BE=8B?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/config.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conf/config.ini b/conf/config.ini index 1ef7c145..c6bebc7d 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -86,6 +86,8 @@ on_stream_changed=https://127.0.0.1/index/hook/on_stream_changed on_stream_none_reader=https://127.0.0.1/index/hook/on_stream_none_reader #播放时,未找到流事件,通过配合hook.on_stream_none_reader事件可以完成按需拉流 on_stream_not_found=https://127.0.0.1/index/hook/on_stream_not_found +#服务器启动报告,可以用于服务器的崩溃重启事件监听 +on_server_started=https://127.0.0.1/index/hook/on_server_started #hook api最大等待回复时间,单位秒 timeoutSec=10 From 5e9291e03574bec5447229996b1b6cceefd5c106 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 29 Nov 2019 11:27:54 +0800 Subject: [PATCH 043/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/main.cpp | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/server/main.cpp b/server/main.cpp index 82fbd0b9..763de656 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -235,6 +235,7 @@ int start_main(int argc,char *argv[]) { #endif// #if !defined(_WIN32) + pid_t pid = getpid(); if (bDaemon) { //启动守护进程 System::startDaemon(); @@ -278,21 +279,35 @@ int start_main(int argc,char *argv[]) { TcpServer::Ptr rtspSrv(new TcpServer()); TcpServer::Ptr rtmpSrv(new TcpServer()); TcpServer::Ptr httpSrv(new TcpServer()); - - shellSrv->start(shellPort); - rtspSrv->start(rtspPort);//默认554 - rtmpSrv->start(rtmpPort);//默认1935 - //http服务器 - httpSrv->start(httpPort);//默认80 - //如果支持ssl,还可以开启https服务器 TcpServer::Ptr httpsSrv(new TcpServer()); - //https服务器,支持websocket - httpsSrv->start(httpsPort);//默认443 - //支持ssl加密的rtsp服务器,可用于诸如亚马逊echo show这样的设备访问 TcpServer::Ptr rtspSSLSrv(new TcpServer()); - rtspSSLSrv->start(rtspsPort);//默认322 + + try { + //rtsp服务器,端口默认554 + rtspSrv->start(rtspPort);//默认554 + //rtsps服务器,端口默认322 + rtspSSLSrv->start(rtspsPort); + //rtmp服务器,端口默认1935 + rtmpSrv->start(rtmpPort); + //http服务器,端口默认80 + httpSrv->start(httpPort); + //https服务器,端口默认443 + httpsSrv->start(httpsPort); + //telnet远程调试服务器 + shellSrv->start(shellPort); + }catch (std::exception &ex){ + WarnL << "端口占用或无权限:" << ex.what() << endl; + ErrorL << "程序启动失败,请修改配置文件中端口号后重试!" << endl; + sleep(1); +#if !defined(_WIN32) + if(pid != getpid()){ + kill(pid,SIGINT); + } +#endif + return -1; + } installWebApi(); InfoL << "已启动http api 接口"; From 0c79af024c8fe970a02bff166b4344991cf3f85d Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 29 Nov 2019 11:32:17 +0800 Subject: [PATCH 044/272] =?UTF-8?q?=E5=88=87=E6=8D=A2submodule=E8=87=B3git?= =?UTF-8?q?ee?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitmodules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index e9de36ce..54226a7f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "ZLToolKit"] path = 3rdpart/ZLToolKit - url = https://github.com/xiongziliang/ZLToolKit.git + url = https://gitee.com/xiahcu/ZLToolKit [submodule "3rdpart/media-server"] path = 3rdpart/media-server - url = https://github.com/xiongziliang/media-server + url = https://gitee.com/xiahcu/media-server From 64a1255b76bd043f6c5956fedfc7be86d927c54a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Fri, 29 Nov 2019 11:36:26 +0800 Subject: [PATCH 045/272] Update README_CN.md --- README_CN.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README_CN.md b/README_CN.md index a2eba340..c117907d 100644 --- a/README_CN.md +++ b/README_CN.md @@ -2,6 +2,13 @@ # 一个基于C++11的高性能运营级流媒体服务框架 [![Build Status](https://travis-ci.org/xiongziliang/ZLMediaKit.svg?branch=master)](https://travis-ci.org/xiongziliang/ZLMediaKit) + +## 国内用户请使用gitee镜像下载 +``` +git clone --depth 1 https://gitee.com/xiahcu/ZLMediaKit +cd ZLMediaKit +git submodule update --init +``` ## 项目特点 - 基于C++11开发,避免使用裸指针,代码稳定可靠;同时跨平台移植简单方便,代码清晰简洁。 - 打包多种流媒体协议(RTSP/RTMP/HLS/HTTP-FLV/Websocket-FLV),支持协议间的互相转换,提供一站式的服务。 From 9b6ee8131baeb10d7d860c54fb0fcb1cf1962d62 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 29 Nov 2019 11:59:39 +0800 Subject: [PATCH 046/272] =?UTF-8?q?hls=E7=94=A8=E4=BD=9C=E7=82=B9=E6=92=AD?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E9=82=A3=E4=B9=88m3u8=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=AE=9E=E6=97=B6=E8=AE=B0=E5=BD=95=E4=B8=BA=E7=82=B9=E6=92=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MediaFile/HlsMaker.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MediaFile/HlsMaker.cpp b/src/MediaFile/HlsMaker.cpp index 3788f139..8c5b2994 100644 --- a/src/MediaFile/HlsMaker.cpp +++ b/src/MediaFile/HlsMaker.cpp @@ -110,8 +110,8 @@ void HlsMaker::addNewSegment(uint32_t) { return; } - //关闭并保存上一个切片 - flushLastSegment(); + //关闭并保存上一个切片,如果_seg_number==0,那么是点播。 + flushLastSegment(_seg_number == 0); //新增切片 _last_file_name = onOpenSegment(_file_index++); //重置切片计时器 From a44ab25d461f7657f4dae8552d60bbe1458a511f Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 30 Nov 2019 11:38:00 +0800 Subject: [PATCH 047/272] =?UTF-8?q?=E6=95=B4=E7=90=86http=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=9C=8D=E5=8A=A1=E5=99=A8=E7=9B=B8=E5=85=B3=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/HttpFileManager.cpp | 501 +++++++++++++++++++++++++++++++++ src/Http/HttpFileManager.h | 80 ++++++ src/Http/HttpSession.cpp | 520 ++--------------------------------- src/Http/HttpSession.h | 51 +--- 4 files changed, 610 insertions(+), 542 deletions(-) create mode 100644 src/Http/HttpFileManager.cpp create mode 100644 src/Http/HttpFileManager.h diff --git a/src/Http/HttpFileManager.cpp b/src/Http/HttpFileManager.cpp new file mode 100644 index 00000000..800c0a6e --- /dev/null +++ b/src/Http/HttpFileManager.cpp @@ -0,0 +1,501 @@ +/* + * MIT License + * + * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#if !defined(_WIN32) +#include +#endif //!defined(_WIN32) +#include +#include "HttpFileManager.h" +#include "Util/File.h" +#include "HttpSession.h" + +namespace mediakit { + +static int kHlsCookieSecond = 10 * 60; +static const string kCookieName = "ZL_COOKIE"; +static const string kCookiePathKey = "kCookiePathKey"; +static const string kAccessErrKey = "kAccessErrKey"; + +static const string &getMimeType(const char *name) { + const char *dot; + dot = strrchr(name, '.'); + static StrCaseMap mapType; + static onceToken token([&]() { + mapType.emplace(".html", "text/html"); + mapType.emplace(".htm", "text/html"); + mapType.emplace(".mp4", "video/mp4"); + mapType.emplace(".mkv", "video/x-matroska"); + mapType.emplace(".rmvb", "application/vnd.rn-realmedia"); + mapType.emplace(".rm", "application/vnd.rn-realmedia"); + mapType.emplace(".m3u8", "application/vnd.apple.mpegurl"); + mapType.emplace(".jpg", "image/jpeg"); + mapType.emplace(".jpeg", "image/jpeg"); + mapType.emplace(".gif", "image/gif"); + mapType.emplace(".png", "image/png"); + mapType.emplace(".ico", "image/x-icon"); + mapType.emplace(".css", "text/css"); + mapType.emplace(".js", "application/javascript"); + mapType.emplace(".au", "audio/basic"); + mapType.emplace(".wav", "audio/wav"); + mapType.emplace(".avi", "video/x-msvideo"); + mapType.emplace(".mov", "video/quicktime"); + mapType.emplace(".qt", "video/quicktime"); + mapType.emplace(".mpeg", "video/mpeg"); + mapType.emplace(".mpe", "video/mpeg"); + mapType.emplace(".vrml", "model/vrml"); + mapType.emplace(".wrl", "model/vrml"); + mapType.emplace(".midi", "audio/midi"); + mapType.emplace(".mid", "audio/midi"); + mapType.emplace(".mp3", "audio/mpeg"); + mapType.emplace(".ogg", "application/ogg"); + mapType.emplace(".pac", "application/x-ns-proxy-autoconfig"); + mapType.emplace(".flv", "video/x-flv"); + }); + static string defaultType = "text/plain"; + if (!dot) { + return defaultType; + } + auto it = mapType.find(dot); + if (it == mapType.end()) { + return defaultType; + } + return it->second; +} + +static string searchIndexFile(const string &dir){ + DIR *pDir; + dirent *pDirent; + if ((pDir = opendir(dir.data())) == NULL) { + return ""; + } + set setFile; + while ((pDirent = readdir(pDir)) != NULL) { + static set indexSet = {"index.html","index.htm","index"}; + if(indexSet.find(pDirent->d_name) != indexSet.end()){ + string ret = pDirent->d_name; + closedir(pDir); + return ret; + } + } + closedir(pDir); + return ""; +} + +static bool makeFolderMenu(const string &httpPath, const string &strFullPath, string &strRet) { + string strPathPrefix(strFullPath); + string last_dir_name; + if(strPathPrefix.back() == '/'){ + strPathPrefix.pop_back(); + }else{ + last_dir_name = split(strPathPrefix,"/").back(); + } + + if (!File::is_dir(strPathPrefix.data())) { + return false; + } + stringstream ss; + ss << "\r\n" + "\r\n" + "文件索引\r\n" + "\r\n" + "\r\n" + "

文件索引:"; + + ss << httpPath; + ss << "

\r\n"; + if (httpPath != "/") { + ss << "
  • "; + ss << "根目录"; + ss << "
  • \r\n"; + + ss << "
  • "; + ss << "上级目录"; + ss << "
  • \r\n"; + } + + DIR *pDir; + dirent *pDirent; + if ((pDir = opendir(strPathPrefix.data())) == NULL) { + return false; + } + set setFile; + while ((pDirent = readdir(pDir)) != NULL) { + if (File::is_special_dir(pDirent->d_name)) { + continue; + } + if(pDirent->d_name[0] == '.'){ + continue; + } + setFile.emplace(pDirent->d_name); + } + int i = 0; + for(auto &strFile :setFile ){ + string strAbsolutePath = strPathPrefix + "/" + strFile; + bool isDir = File::is_dir(strAbsolutePath.data()); + ss << "
  • " << i++ << "\t"; + ss << ""; + ss << strFile; + if (isDir) { + ss << "/
  • \r\n"; + continue; + } + //是文件 + struct stat fileData; + if (0 == stat(strAbsolutePath.data(), &fileData)) { + auto &fileSize = fileData.st_size; + if (fileSize < 1024) { + ss << " (" << fileData.st_size << "B)" << endl; + } else if (fileSize < 1024 * 1024) { + ss << fixed << setprecision(2) << " (" << fileData.st_size / 1024.0 << "KB)"; + } else if (fileSize < 1024 * 1024 * 1024) { + ss << fixed << setprecision(2) << " (" << fileData.st_size / 1024 / 1024.0 << "MB)"; + } else { + ss << fixed << setprecision(2) << " (" << fileData.st_size / 1024 / 1024 / 1024.0 << "GB)"; + } + } + ss << "\r\n"; + } + closedir(pDir); + ss << "
      \r\n"; + ss << "
    \r\n"; + ss.str().swap(strRet); + 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 checkHls(BroadcastHttpAccessArgs){ + if(!end_of(args._streamid,("/hls.m3u8"))) { + //不是hls + return false; + } + //访问的hls.m3u8结尾,我们转换成kBroadcastMediaPlayed事件 + Broadcast::AuthInvoker mediaAuthInvoker = [invoker,path](const string &err){ + //cookie有效期为kHlsCookieSecond + invoker(err,"",kHlsCookieSecond); + }; + + auto args_copy = args; + replace(args_copy._streamid,"/hls.m3u8",""); + return NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed,args_copy,mediaAuthInvoker,sender); +} + + +/** + * 判断http客户端是否有权限访问文件的逻辑步骤 + * 1、根据http请求头查找cookie,找到进入步骤3 + * 2、根据http url参数查找cookie,如果还是未找到cookie则进入步骤5 + * 3、cookie标记是否有权限访问文件,如果有权限,直接返回文件 + * 4、cookie中记录的url参数是否跟本次url参数一致,如果一致直接返回客户端错误码 + * 5、触发kBroadcastHttpAccess事件 + */ +static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaInfo &mediaInfo, bool is_dir, + const function &callback) { + //获取用户唯一id + auto uid = parser.Params(); + auto path = parser.Url(); + + //先根据http头中的cookie字段获取cookie + HttpServerCookie::Ptr cookie = HttpCookieManager::Instance().getCookie(kCookieName, parser.getValues()); + //如果不是从http头中找到的cookie,我们让http客户端设置下cookie + bool cookie_from_header = true; + if (!cookie && !uid.empty()) { + //客户端请求中无cookie,再根据该用户的用户id获取cookie + cookie = HttpCookieManager::Instance().getCookieByUid(kCookieName, uid); + cookie_from_header = false; + } + + if (cookie) { + //找到了cookie,对cookie上锁先 + auto lck = cookie->getLock(); + auto accessErr = (*cookie)[kAccessErrKey].get(); + auto cookiePath = (*cookie)[kCookiePathKey].get(); + if (path.find(cookiePath) == 0) { + //上次cookie是限定本目录 + if (accessErr.empty()) { + //上次鉴权成功 + callback("", cookie_from_header ? nullptr : cookie); + return; + } + //上次鉴权失败,但是如果url参数发生变更,那么也重新鉴权下 + if (parser.Params().empty() || parser.Params() == cookie->getUid()) { + //url参数未变,或者本来就没有url参数,那么判断本次请求为重复请求,无访问权限 + callback(accessErr, cookie_from_header ? nullptr : cookie); + return; + } + } + //如果url参数变了或者不是限定本目录,那么旧cookie失效,重新鉴权 + HttpCookieManager::Instance().delCookie(cookie); + } + + //该用户从来未获取过cookie,这个时候我们广播是否允许该用户访问该http目录 + HttpSession::HttpAccessPathInvoker accessPathInvoker = [callback, uid, path, is_dir](const string &errMsg, + const string &cookie_path_in, + int cookieLifeSecond) { + HttpServerCookie::Ptr cookie; + if (cookieLifeSecond) { + //本次鉴权设置了有效期,我们把鉴权结果缓存在cookie中 + string cookie_path = cookie_path_in; + if (cookie_path.empty()) { + //如果未设置鉴权目录,那么我们采用当前目录 + cookie_path = is_dir ? path : path.substr(0, path.rfind("/") + 1); + } + + cookie = HttpCookieManager::Instance().addCookie(kCookieName, uid, cookieLifeSecond); + //对cookie上锁 + auto lck = cookie->getLock(); + //记录用户能访问的路径 + (*cookie)[kCookiePathKey].set(cookie_path); + //记录能否访问 + (*cookie)[kAccessErrKey].set(errMsg); + } + callback(errMsg, cookie); + }; + + if (checkHls(parser, mediaInfo, path, is_dir, accessPathInvoker, sender)) { + //是hls的播放鉴权,拦截之 + return; + } + + //事件未被拦截,则认为是http下载请求 + bool flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastHttpAccess, parser, mediaInfo, path, is_dir, accessPathInvoker, sender); + if (!flag) { + //此事件无人监听,我们默认都有权限访问 + callback("", nullptr); + } +} + +/** + * 发送404 Not Found + */ +static void sendNotFound(const HttpFileManager::invoker &cb) { + GET_CONFIG(string,notFound,Http::kNotFound); + cb("404 Not Found","text/html",StrCaseMap(),std::make_shared(notFound)); +} + +/** + * 拼接文件路径 + */ +static string pathCat(const string &a, const string &b){ + if(a.back() == '/'){ + return a + b; + } + return a + '/' + b; +} + +/** + * 访问文件 + * @param sender 事件触发者 + * @param parser http请求 + * @param mediaInfo http url信息 + * @param strFile 文件绝对路径 + * @param cb 回调对象 + */ +static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo &mediaInfo, const string &strFile, const HttpFileManager::invoker &cb) { + if (!File::is_file(strFile.data())) { + sendNotFound(cb); + return; + } + //判断是否有权限访问该文件 + canAccessPath(sender, parser, mediaInfo, false, [cb, strFile, parser](const string &errMsg, const HttpServerCookie::Ptr &cookie) { + if (!errMsg.empty()) { + StrCaseMap headerOut; + if (cookie) { + headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); + } + cb("401 Unauthorized", "", headerOut, std::make_shared(errMsg)); + return; + } + + StrCaseMap httpHeader; + if (cookie) { + httpHeader["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); + } + HttpSession::HttpResponseInvoker invoker = [&](const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body) { + cb(codeOut.data(), getMimeType(strFile.data()), headerOut, body); + }; + invoker.responseFile(parser.getValues(), httpHeader, strFile); + }); +} + +/** + * 访问文件或文件夹 + * @param sender 事件触发者 + * @param parser http请求 + * @param cb 回调对象 + */ +void HttpFileManager::onAccessPath(TcpSession &sender, Parser &parser, const HttpFileManager::invoker &cb) { + auto fullUrl = string(HTTP_SCHEMA) + "://" + parser["Host"] + parser.FullUrl(); + MediaInfo mediaInfo(fullUrl); + + GET_CONFIG(bool, enableVhost, General::kEnableVhost); + GET_CONFIG(string, rootPath, Http::kRootPath); + auto strFile = File::absolutePath(enableVhost ? mediaInfo._vhost + parser.Url() : parser.Url(), rootPath); + + //访问的是文件夹 + if (strFile.back() == '/' || File::is_dir(strFile.data())) { + auto indexFile = searchIndexFile(strFile); + if (!indexFile.empty()) { + //发现该文件夹下有index文件 + strFile = pathCat(strFile, indexFile); + parser.setUrl(pathCat(parser.Url(), indexFile)); + accessFile(sender, parser, mediaInfo, strFile, cb); + return; + } + string strMenu; + //生成文件夹菜单索引 + if (!makeFolderMenu(parser.Url(), strFile, strMenu)) { + //文件夹不存在 + sendNotFound(cb); + return; + } + //判断是否有权限访问该目录 + canAccessPath(sender, parser, mediaInfo, true, [strMenu, cb](const string &errMsg, const HttpServerCookie::Ptr &cookie) { + if (!errMsg.empty()) { + const_cast(strMenu) = errMsg; + } + StrCaseMap headerOut; + if (cookie) { + headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); + } + cb(errMsg.empty() ? "200 OK" : "401 Unauthorized", "text/html", headerOut, std::make_shared(strMenu)); + }); + return; + } + + //访问的是文件 + accessFile(sender, parser, mediaInfo, strFile, cb); +}; + + +////////////////////////////////////HttpResponseInvokerImp////////////////////////////////////// + +void HttpResponseInvokerImp::operator()(const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body) const{ + if(_lambad){ + _lambad(codeOut,headerOut,body); + } +} + +void HttpResponseInvokerImp::operator()(const string &codeOut, const StrCaseMap &headerOut, const string &body) const{ + this->operator()(codeOut,headerOut,std::make_shared(body)); +} + +HttpResponseInvokerImp::HttpResponseInvokerImp(const HttpResponseInvokerImp::HttpResponseInvokerLambda0 &lambda){ + _lambad = lambda; +} + +HttpResponseInvokerImp::HttpResponseInvokerImp(const HttpResponseInvokerImp::HttpResponseInvokerLambda1 &lambda){ + if(!lambda){ + _lambad = nullptr; + return; + } + _lambad = [lambda](const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body){ + string str; + if(body && body->remainSize()){ + str = body->readData(body->remainSize())->toString(); + } + lambda(codeOut,headerOut,str); + }; +} + +void HttpResponseInvokerImp::responseFile(const StrCaseMap &requestHeader, + const StrCaseMap &responseHeader, + const string &filePath) const { + StrCaseMap &httpHeader = const_cast(responseHeader); + std::shared_ptr fp(fopen(filePath.data(), "rb"), [](FILE *fp) { + if (fp) { + fclose(fp); + } + }); + + if (!fp) { + //打开文件失败 + GET_CONFIG(string,notFound,Http::kNotFound); + GET_CONFIG(string,charSet,Http::kCharSet); + + auto strContentType = StrPrinter << "text/html; charset=" << charSet << endl; + httpHeader["Content-Type"] = strContentType; + (*this)("404 Not Found", httpHeader, notFound); + return; + } + + auto &strRange = const_cast(requestHeader)["Range"]; + int64_t iRangeStart = 0; + int64_t iRangeEnd = 0 ; + int64_t fileSize = HttpMultiFormBody::fileSize(fp.get()); + + const char *pcHttpResult = NULL; + if (strRange.size() == 0) { + //全部下载 + pcHttpResult = "200 OK"; + iRangeEnd = fileSize - 1; + } else { + //分节下载 + pcHttpResult = "206 Partial Content"; + iRangeStart = atoll(FindField(strRange.data(), "bytes=", "-").data()); + iRangeEnd = atoll(FindField(strRange.data(), "-", "\r\n").data()); + if (iRangeEnd == 0) { + iRangeEnd = fileSize - 1; + } + //分节下载返回Content-Range头 + httpHeader.emplace("Content-Range", StrPrinter << "bytes " << iRangeStart << "-" << iRangeEnd << "/" << fileSize << endl); + } + + //回复文件 + HttpBody::Ptr fileBody = std::make_shared(fp, iRangeStart, iRangeEnd - iRangeStart + 1); + (*this)(pcHttpResult, httpHeader, fileBody); +} + +HttpResponseInvokerImp::operator bool(){ + return _lambad.operator bool(); +} + + +}//namespace mediakit \ No newline at end of file diff --git a/src/Http/HttpFileManager.h b/src/Http/HttpFileManager.h new file mode 100644 index 00000000..4421431a --- /dev/null +++ b/src/Http/HttpFileManager.h @@ -0,0 +1,80 @@ +/* + * MIT License + * + * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef ZLMEDIAKIT_HTTPFILEMANAGER_H +#define ZLMEDIAKIT_HTTPFILEMANAGER_H + +#include "HttpBody.h" +#include "HttpCookie.h" +#include "Common/Parser.h" +#include "Network/TcpSession.h" +#include "Util/function_traits.h" + +namespace mediakit { + +class HttpResponseInvokerImp{ +public: + typedef std::function HttpResponseInvokerLambda0; + typedef std::function HttpResponseInvokerLambda1; + + HttpResponseInvokerImp(){} + ~HttpResponseInvokerImp(){} + template + HttpResponseInvokerImp(const C &c):HttpResponseInvokerImp(typename function_traits::stl_function_type(c)) {} + HttpResponseInvokerImp(const HttpResponseInvokerLambda0 &lambda); + HttpResponseInvokerImp(const HttpResponseInvokerLambda1 &lambda); + + void operator()(const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body) const; + void operator()(const string &codeOut, const StrCaseMap &headerOut, const string &body) const; + void responseFile(const StrCaseMap &requestHeader,const StrCaseMap &responseHeader,const string &filePath) const; + operator bool(); +private: + HttpResponseInvokerLambda0 _lambad; +}; + +/** + * 该对象用于控制http静态文件夹服务器的访问权限 + */ +class HttpFileManager { +public: + typedef function invoker; + + /** + * 访问文件或文件夹 + * @param sender 事件触发者 + * @param parser http请求 + * @param cb 回调对象 + */ + static void onAccessPath(TcpSession &sender, Parser &parser, const invoker &cb); +private: + HttpFileManager() = delete; + ~HttpFileManager() = delete; +}; + +} + + +#endif //ZLMEDIAKIT_HTTPFILEMANAGER_H diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp index a6ccb2b9..3f01f567 100644 --- a/src/Http/HttpSession.cpp +++ b/src/Http/HttpSession.cpp @@ -35,163 +35,12 @@ #include "Common/config.h" #include "strCoding.h" #include "HttpSession.h" -#include "Util/File.h" -#include "Util/util.h" -#include "Util/TimeTicker.h" -#include "Util/onceToken.h" -#include "Util/mini.h" -#include "Util/NoticeCenter.h" #include "Util/base64.h" #include "Util/SHA1.h" -#include "Rtmp/utils.h" using namespace toolkit; namespace mediakit { -static int kHlsCookieSecond = 10 * 60; -static const string kCookieName = "ZL_COOKIE"; -static const string kCookiePathKey = "kCookiePathKey"; -static const string kAccessErrKey = "kAccessErrKey"; - -string dateStr() { - char buf[64]; - time_t tt = time(NULL); - strftime(buf, sizeof buf, "%a, %b %d %Y %H:%M:%S GMT", gmtime(&tt)); - return buf; -} - -const char *HttpSession::get_mime_type(const char *name) { - const char *dot; - dot = strrchr(name, '.'); - static HttpSession::KeyValue mapType; - static onceToken token([&]() { - mapType.emplace(".html", "text/html"); - mapType.emplace(".htm", "text/html"); - mapType.emplace(".mp4", "video/mp4"); - mapType.emplace(".mkv", "video/x-matroska"); - mapType.emplace(".rmvb", "application/vnd.rn-realmedia"); - mapType.emplace(".rm", "application/vnd.rn-realmedia"); - mapType.emplace(".m3u8", "application/vnd.apple.mpegurl"); - mapType.emplace(".jpg", "image/jpeg"); - mapType.emplace(".jpeg", "image/jpeg"); - mapType.emplace(".gif", "image/gif"); - mapType.emplace(".png", "image/png"); - mapType.emplace(".ico", "image/x-icon"); - mapType.emplace(".css", "text/css"); - mapType.emplace(".js", "application/javascript"); - mapType.emplace(".au", "audio/basic"); - mapType.emplace(".wav", "audio/wav"); - mapType.emplace(".avi", "video/x-msvideo"); - mapType.emplace(".mov", "video/quicktime"); - mapType.emplace(".qt", "video/quicktime"); - mapType.emplace(".mpeg", "video/mpeg"); - mapType.emplace(".mpe", "video/mpeg"); - mapType.emplace(".vrml", "model/vrml"); - mapType.emplace(".wrl", "model/vrml"); - mapType.emplace(".midi", "audio/midi"); - mapType.emplace(".mid", "audio/midi"); - mapType.emplace(".mp3", "audio/mpeg"); - mapType.emplace(".ogg", "application/ogg"); - mapType.emplace(".pac", "application/x-ns-proxy-autoconfig"); - mapType.emplace(".flv", "video/x-flv"); - }, nullptr); - if (!dot) { - return "text/plain"; - } - auto it = mapType.find(dot); - if (it == mapType.end()) { - return "text/plain"; - } - return it->second.data(); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -void HttpResponseInvokerImp::operator()(const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body) const{ - if(_lambad){ - _lambad(codeOut,headerOut,body); - } -} - -void HttpResponseInvokerImp::operator()(const string &codeOut, const StrCaseMap &headerOut, const string &body) const{ - this->operator()(codeOut,headerOut,std::make_shared(body)); -} - -HttpResponseInvokerImp::HttpResponseInvokerImp(const HttpResponseInvokerImp::HttpResponseInvokerLambda0 &lambda){ - _lambad = lambda; -} - -HttpResponseInvokerImp::HttpResponseInvokerImp(const HttpResponseInvokerImp::HttpResponseInvokerLambda1 &lambda){ - if(!lambda){ - _lambad = nullptr; - return; - } - _lambad = [lambda](const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body){ - string str; - if(body && body->remainSize()){ - str = body->readData(body->remainSize())->toString(); - } - lambda(codeOut,headerOut,str); - }; -} - -void HttpResponseInvokerImp::responseFile(const StrCaseMap &requestHeader, - const StrCaseMap &responseHeader, - const string &filePath) const { - StrCaseMap &httpHeader = const_cast(responseHeader); - do { - std::shared_ptr fp(fopen(filePath.data(), "rb"), [](FILE *fp) { - if (fp) { - fclose(fp); - } - }); - if (!fp) { - //打开文件失败 - break; - } - - auto &strRange = const_cast(requestHeader)["Range"]; - int64_t iRangeStart = 0; - int64_t iRangeEnd = 0 ; - int64_t fileSize = HttpMultiFormBody::fileSize(fp.get()); - - const char *pcHttpResult = NULL; - if (strRange.size() == 0) { - //全部下载 - pcHttpResult = "200 OK"; - iRangeEnd = fileSize - 1; - } else { - //分节下载 - pcHttpResult = "206 Partial Content"; - iRangeStart = atoll(FindField(strRange.data(), "bytes=", "-").data()); - iRangeEnd = atoll(FindField(strRange.data(), "-", "\r\n").data()); - if (iRangeEnd == 0) { - iRangeEnd = fileSize - 1; - } - //分节下载返回Content-Range头 - httpHeader.emplace("Content-Range", StrPrinter << "bytes " << iRangeStart << "-" << iRangeEnd << "/" << fileSize << endl); - } - - //回复文件 - HttpBody::Ptr fileBody = std::make_shared(fp, iRangeStart, iRangeEnd - iRangeStart + 1); - (*this)(pcHttpResult, httpHeader, fileBody); - return; - }while(false); - - GET_CONFIG(string,notFound,Http::kNotFound); - GET_CONFIG(string,charSet,Http::kCharSet); - - auto strContentType = StrPrinter << "text/html; charset=" << charSet << endl; - httpHeader["Content-Type"] = strContentType; - (*this)("404 Not Found", httpHeader, notFound); -} - -HttpResponseInvokerImp::operator bool(){ - return _lambad.operator bool(); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// - HttpSession::HttpSession(const Socket::Ptr &pSock) : TcpSession(pSock) { TraceP(this); GET_CONFIG(uint32_t,keep_alive_sec,Http::kKeepAliveSecond); @@ -206,19 +55,18 @@ HttpSession::~HttpSession() { int64_t HttpSession::onRecvHeader(const char *header,uint64_t len) { typedef void (HttpSession::*HttpCMDHandle)(int64_t &); - static unordered_map g_mapCmdIndex; + static unordered_map s_func_map; static onceToken token([]() { - g_mapCmdIndex.emplace("GET",&HttpSession::Handle_Req_GET); - g_mapCmdIndex.emplace("POST",&HttpSession::Handle_Req_POST); + s_func_map.emplace("GET",&HttpSession::Handle_Req_GET); + s_func_map.emplace("POST",&HttpSession::Handle_Req_POST); }, nullptr); _parser.Parse(header); urlDecode(_parser); string cmd = _parser.Method(); - auto it = g_mapCmdIndex.find(cmd); - if (it == g_mapCmdIndex.end()) { + auto it = s_func_map.find(cmd); + if (it == s_func_map.end()) { sendResponse("403 Forbidden", true); - shutdown(SockException(Err_shutdown,StrPrinter << "403 Forbidden:" << cmd)); return 0; } @@ -230,10 +78,6 @@ int64_t HttpSession::onRecvHeader(const char *header,uint64_t len) { auto &fun = it->second; try { (this->*fun)(content_len); - }catch (SockException &ex){ - if(ex){ - shutdown(ex); - } }catch (exception &ex){ shutdown(SockException(Err_shutdown,ex.what())); } @@ -324,12 +168,12 @@ bool HttpSession::checkWebSocket(){ //如果checkLiveFlvStream返回false,则代表不是websocket-flv,而是普通的websocket连接 if(!onWebSocketConnect(_parser)){ sendResponse("501 Not Implemented",true, nullptr, headerOut); - shutdown(SockException(Err_shutdown,"WebSocket server not implemented")); return true; } sendResponse("101 Switching Protocols",false, nullptr,headerOut); return true; } + //http-flv 链接格式:http://vhost-url:port/app/streamid.flv?key1=value1&key2=value2 //如果url(除去?以及后面的参数)后缀是.flv,那么表明该url是一个http-flv直播。 bool HttpSession::checkLiveFlvStream(const function &cb){ @@ -365,9 +209,6 @@ bool HttpSession::checkLiveFlvStream(const function &cb){ if(!rtmp_src){ //未找到该流 sendNotFound(bClose); - if(bClose){ - shutdown(SockException(Err_shutdown,"flv stream not found")); - } return; } //找到流了 @@ -375,7 +216,6 @@ bool HttpSession::checkLiveFlvStream(const function &cb){ bool authSuccess = err.empty(); if(!authSuccess){ sendResponse("401 Unauthorized", true, nullptr, KeyValue(), std::make_shared(err)); - shutdown(SockException(Err_shutdown,StrPrinter << "401 Unauthorized:" << err)); return ; } @@ -421,159 +261,6 @@ bool HttpSession::checkLiveFlvStream(const function &cb){ return true; } -bool makeMeun(const string &httpPath,const string &strFullPath, string &strRet) ; - -static string findIndexFile(const string &dir){ - DIR *pDir; - dirent *pDirent; - if ((pDir = opendir(dir.data())) == NULL) { - return ""; - } - set setFile; - while ((pDirent = readdir(pDir)) != NULL) { - static set indexSet = {"index.html","index.htm","index"}; - if(indexSet.find(pDirent->d_name) != indexSet.end()){ - string ret = pDirent->d_name; - closedir(pDir); - return ret; - } - } - closedir(pDir); - return ""; -} - -string HttpSession::getClientUid(){ - //如果http客户端不支持cookie,那么我们可以通过url参数来追踪用户 - //如果url参数也没有,那么只能通过ip+端口号来追踪用户 - //追踪用户的目的是为了减少http短链接情况的重复鉴权验证,通过缓存记录鉴权结果,提高性能 - string uid = _parser.Params(); - if(uid.empty()){ - uid = StrPrinter << get_peer_ip() << ":" << get_peer_port(); - } - return uid; -} - - -//字符串是否以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 checkHls(BroadcastHttpAccessArgs){ - if(!end_of(args._streamid,("/hls.m3u8"))) { - //不是hls - return false; - } - //访问的hls.m3u8结尾,我们转换成kBroadcastMediaPlayed事件 - Broadcast::AuthInvoker mediaAuthInvoker = [invoker,path](const string &err){ - //cookie有效期为kHlsCookieSecond - invoker(err,"",kHlsCookieSecond); - }; - - auto args_copy = args; - replace(args_copy._streamid,"/hls.m3u8",""); - return NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed,args_copy,mediaAuthInvoker,sender); -} - -void HttpSession::canAccessPath(const string &path,bool is_dir,const function &callback_in){ - auto callback = [callback_in,this](const string &errMsg,const HttpServerCookie::Ptr &cookie){ - try { - callback_in(errMsg,cookie); - }catch (SockException &ex){ - if(ex){ - shutdown(ex); - } - }catch (exception &ex){ - shutdown(SockException(Err_shutdown,ex.what())); - } - }; - - //获取用户唯一id - auto uid = getClientUid(); - //先根据http头中的cookie字段获取cookie - HttpServerCookie::Ptr cookie = HttpCookieManager::Instance().getCookie(kCookieName, _parser.getValues()); - //如果不是从http头中找到的cookie,我们让http客户端设置下cookie - bool cookie_from_header = true; - if(!cookie){ - //客户端请求中无cookie,再根据该用户的用户id获取cookie - cookie = HttpCookieManager::Instance().getCookieByUid(kCookieName, uid); - cookie_from_header = false; - } - - if(cookie){ - //找到了cookie,对cookie上锁先 - auto lck = cookie->getLock(); - auto accessErr = (*cookie)[kAccessErrKey].get(); - auto cookiePath = (*cookie)[kCookiePathKey].get(); - if(path.find(cookiePath) == 0){ - //上次cookie是限定本目录 - if(accessErr.empty()){ - //上次鉴权成功 - callback("", cookie_from_header ? nullptr : cookie); - return; - } - //上次鉴权失败,但是如果url参数发生变更,那么也重新鉴权下 - if (_parser.Params().empty() || _parser.Params() == cookie->getUid()) { - //url参数未变,或者本来就没有url参数,那么判断本次请求为重复请求,无访问权限 - callback(accessErr, cookie_from_header ? nullptr : cookie); - return; - } - } - //如果url参数变了或者不是限定本目录,那么旧cookie失效,重新鉴权 - HttpCookieManager::Instance().delCookie(cookie); - } - - //该用户从来未获取过cookie,这个时候我们广播是否允许该用户访问该http目录 - weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - HttpAccessPathInvoker accessPathInvoker = [weakSelf,callback,uid,path,is_dir] (const string &errMsg,const string &cookie_path_in, int cookieLifeSecond) { - HttpServerCookie::Ptr cookie ; - if(cookieLifeSecond) { - //本次鉴权设置了有效期,我们把鉴权结果缓存在cookie中 - string cookie_path = cookie_path_in; - if(cookie_path.empty()){ - //如果未设置鉴权目录,那么我们采用当前目录 - cookie_path = is_dir ? path : path.substr(0,path.rfind("/") + 1); - } - - cookie = HttpCookieManager::Instance().addCookie(kCookieName, uid, cookieLifeSecond); - //对cookie上锁 - auto lck = cookie->getLock(); - //记录用户能访问的路径 - (*cookie)[kCookiePathKey].set(cookie_path); - //记录能否访问 - (*cookie)[kAccessErrKey].set(errMsg); - } - - auto strongSelf = weakSelf.lock(); - if (!strongSelf) { - //自己已经销毁 - return; - } - strongSelf->async([weakSelf,callback,cookie,errMsg]() { - //切换到自己线程 - auto strongSelf = weakSelf.lock(); - if (!strongSelf) { - //自己已经销毁 - return; - } - callback(errMsg, cookie); - }); - }; - - if(checkHls(_parser,_mediaInfo,path,is_dir,accessPathInvoker,*this)){ - //是hls的播放鉴权,拦截之 - return; - } - - bool flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastHttpAccess,_parser,_mediaInfo,path,is_dir,accessPathInvoker,*this); - if(!flag){ - //此事件无人监听,我们默认都有权限访问 - callback("", nullptr); - } - -} void HttpSession::Handle_Req_GET(int64_t &content_len) { //先看看是否为WebSocket请求 @@ -597,182 +284,41 @@ void HttpSession::Handle_Req_GET(int64_t &content_len) { return; } - //事件未被拦截,则认为是http下载请求 - auto fullUrl = string(HTTP_SCHEMA) + "://" + _parser["Host"] + _parser.FullUrl(); - _mediaInfo.parse(fullUrl); - - /////////////HTTP连接是否需要被关闭//////////////// GET_CONFIG(uint32_t,reqCnt,Http::kMaxReqCount); - GET_CONFIG(bool,enableVhost,General::kEnableVhost); - GET_CONFIG(string,rootPath,Http::kRootPath); - auto strFile = File::absolutePath(enableVhost ? _mediaInfo._vhost + _parser.Url() : _parser.Url(),rootPath); bool bClose = (strcasecmp(_parser["Connection"].data(),"close") == 0) || ( ++_iReqCnt > reqCnt); - do{ - //访问的是文件夹 - if (strFile.back() == '/' || File::is_dir(strFile.data())) { - auto indexFile = findIndexFile(strFile); - if(!indexFile.empty()){ - //发现该文件夹下有index文件 - strFile = strFile + "/" + indexFile; - _parser.setUrl(_parser.Url() + "/" + indexFile); - break; - } - string strMeun; - //生成文件夹菜单索引 - if (!makeMeun(_parser.Url(),strFile,strMeun)) { - //文件夹不存在 - sendNotFound(bClose); - throw SockException(bClose ? Err_shutdown : Err_success,"close connection after send 404 not found on folder"); - } - - //判断是否有权限访问该目录 - canAccessPath(_parser.Url(),true,[this,bClose,strFile,strMeun](const string &errMsg,const HttpServerCookie::Ptr &cookie){ - if(!errMsg.empty()){ - const_cast(strMeun) = errMsg; - } - KeyValue headerOut; - if(cookie){ - headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); - } - sendResponse(errMsg.empty() ? "200 OK" : "401 Unauthorized" ,bClose, "text/html", headerOut, std::make_shared(strMeun)); - throw SockException(bClose ? Err_shutdown : Err_success,"close connection after access folder"); - }); + weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); + HttpFileManager::onAccessPath(*this, _parser, [weakSelf, bClose](const string &status_code, const string &content_type, + const StrCaseMap &responseHeader, const HttpBody::Ptr &body) { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { return; } - }while(0); - - auto parser = _parser; - //判断是否有权限访问该文件 - canAccessPath(_parser.Url(),false,[this,parser,bClose,strFile](const string &errMsg,const HttpServerCookie::Ptr &cookie){ - if(!errMsg.empty()){ - KeyValue headerOut; - if(cookie){ - headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); + strongSelf->async([weakSelf, bClose, status_code, content_type, responseHeader, body]() { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { + return; } - sendResponse("401 Unauthorized" ,bClose, nullptr, headerOut, std::make_shared(errMsg)); - throw SockException(bClose ? Err_shutdown : Err_success,"close connection after access file failed"); - } - - KeyValue httpHeader; - if(cookie){ - httpHeader["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); - } - - HttpResponseInvoker invoker = [this,bClose,&strFile](const string &codeOut, const KeyValue &headerOut, const HttpBody::Ptr &body){ - sendResponse(codeOut.data(), bClose, get_mime_type(strFile.data()), headerOut, body); - }; - invoker.responseFile(parser.getValues(),httpHeader,strFile); + strongSelf->sendResponse(status_code.data(), bClose, + content_type.empty() ? nullptr : content_type.data(), + responseHeader, body); + }); }); } -bool makeMeun(const string &httpPath,const string &strFullPath, string &strRet) { - string strPathPrefix(strFullPath); - string last_dir_name; - if(strPathPrefix.back() == '/'){ - strPathPrefix.pop_back(); - }else{ - last_dir_name = split(strPathPrefix,"/").back(); - } - - if (!File::is_dir(strPathPrefix.data())) { - return false; - } - stringstream ss; - ss << "\r\n" - "\r\n" - "文件索引\r\n" - "\r\n" - "\r\n" - "

    文件索引:"; - - ss << httpPath; - ss << "

    \r\n"; - if (httpPath != "/") { - ss << "
  • "; - ss << "根目录"; - ss << "
  • \r\n"; - - ss << "
  • "; - ss << "上级目录"; - ss << "
  • \r\n"; - } - - DIR *pDir; - dirent *pDirent; - if ((pDir = opendir(strPathPrefix.data())) == NULL) { - return false; - } - set setFile; - while ((pDirent = readdir(pDir)) != NULL) { - if (File::is_special_dir(pDirent->d_name)) { - continue; - } - if(pDirent->d_name[0] == '.'){ - continue; - } - setFile.emplace(pDirent->d_name); - } - int i = 0; - for(auto &strFile :setFile ){ - string strAbsolutePath = strPathPrefix + "/" + strFile; - bool isDir = File::is_dir(strAbsolutePath.data()); - ss << "
  • " << i++ << "\t"; - ss << ""; - ss << strFile; - if (isDir) { - ss << "/
  • \r\n"; - continue; - } - //是文件 - struct stat fileData; - if (0 == stat(strAbsolutePath.data(), &fileData)) { - auto &fileSize = fileData.st_size; - if (fileSize < 1024) { - ss << " (" << fileData.st_size << "B)" << endl; - } else if (fileSize < 1024 * 1024) { - ss << fixed << setprecision(2) << " (" << fileData.st_size / 1024.0 << "KB)"; - } else if (fileSize < 1024 * 1024 * 1024) { - ss << fixed << setprecision(2) << " (" << fileData.st_size / 1024 / 1024.0 << "MB)"; - } else { - ss << fixed << setprecision(2) << " (" << fileData.st_size / 1024 / 1024 / 1024.0 << "GB)"; - } - } - ss << "\r\n"; - } - closedir(pDir); - ss << "
      \r\n"; - ss << "
    \r\n"; - ss.str().swap(strRet); - return true; +static string dateStr() { + char buf[64]; + time_t tt = time(NULL); + strftime(buf, sizeof buf, "%a, %b %d %Y %H:%M:%S GMT", gmtime(&tt)); + return buf; } - void HttpSession::sendResponse(const char *pcStatus, bool bClose, const char *pcContentType, const HttpSession::KeyValue &header, const HttpBody::Ptr &body, bool set_content_len ){ - GET_CONFIG(string,charSet,Http::kCharSet); GET_CONFIG(uint32_t,keepAliveSec,Http::kKeepAliveSecond); GET_CONFIG(uint32_t,reqCnt,Http::kMaxReqCount); @@ -838,7 +384,7 @@ void HttpSession::sendResponse(const char *pcStatus, if(!size){ //没有body if(bClose){ - shutdown(SockException(Err_shutdown,"close connection after send http header completed")); + shutdown(SockException(Err_shutdown,StrPrinter << "close connection after send http header completed with status code:" << pcStatus)); } return; } @@ -847,7 +393,8 @@ void HttpSession::sendResponse(const char *pcStatus, GET_CONFIG(uint32_t,sendBufSize,Http::kSendBufSize); weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - auto onFlush = [body,bClose,weakSelf]() { + string status_code = pcStatus; + auto onFlush = [body,bClose,weakSelf,status_code]() { auto strongSelf = weakSelf.lock(); if(!strongSelf){ //本对象已经销毁 @@ -884,7 +431,7 @@ void HttpSession::sendResponse(const char *pcStatus, if(bClose) { //最后一次flush事件,文件也发送完毕了,可以关闭socket了 - strongSelf->shutdown(SockException(Err_shutdown,"close connection after send http body completed")); + strongSelf->shutdown(SockException(Err_shutdown, StrPrinter << "close connection after send http body completed with status code:" << status_code)); } //不再监听onFlush事件 return false; @@ -935,13 +482,6 @@ bool HttpSession::emitHttpEvent(bool doInvoke){ //本对象已经销毁 return; } - - if(codeOut.empty()){ - //回调提供的参数异常 - strongSelf->sendNotFound(bClose); - return; - } - strongSelf->sendResponse(codeOut.data(), bClose, nullptr, headerOut, body); }); }; @@ -951,10 +491,6 @@ bool HttpSession::emitHttpEvent(bool doInvoke){ if(!consumed && doInvoke){ //该事件无人消费,所以返回404 invoker("404 Not Found",KeyValue(), HttpBody::Ptr()); - if(bClose){ - //close类型,回复完毕,关闭连接 - shutdown(SockException(Err_shutdown,"404 Not Found")); - } } return consumed; } diff --git a/src/Http/HttpSession.h b/src/Http/HttpSession.h index 98f234c3..1d9ead78 100644 --- a/src/Http/HttpSession.h +++ b/src/Http/HttpSession.h @@ -27,46 +27,19 @@ #define SRC_HTTP_HTTPSESSION_H_ #include -#include "Common/config.h" -#include "Common/Parser.h" #include "Network/TcpSession.h" -#include "Network/TcpServer.h" #include "Rtmp/RtmpMediaSource.h" #include "Rtmp/FlvMuxer.h" #include "HttpRequestSplitter.h" #include "WebSocketSplitter.h" #include "HttpCookieManager.h" -#include "HttpBody.h" -#include "Util/function_traits.h" +#include "HttpFileManager.h" using namespace std; using namespace toolkit; namespace mediakit { -/** - * 该类实现与老代码的兼容适配 - */ -class HttpResponseInvokerImp{ -public: - typedef std::function HttpResponseInvokerLambda0; - typedef std::function HttpResponseInvokerLambda1; - - HttpResponseInvokerImp(){} - ~HttpResponseInvokerImp(){} - template - HttpResponseInvokerImp(const C &c):HttpResponseInvokerImp(typename function_traits::stl_function_type(c)) {} - HttpResponseInvokerImp(const HttpResponseInvokerLambda0 &lambda); - HttpResponseInvokerImp(const HttpResponseInvokerLambda1 &lambda); - - void operator()(const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body) const; - void operator()(const string &codeOut, const StrCaseMap &headerOut, const string &body) const; - void responseFile(const StrCaseMap &requestHeader,const StrCaseMap &responseHeader,const string &filePath) const; - operator bool(); -private: - HttpResponseInvokerLambda0 _lambad; -}; - class HttpSession: public TcpSession, public FlvMuxer, public HttpRequestSplitter, @@ -88,9 +61,7 @@ public: virtual void onRecv(const Buffer::Ptr &) override; virtual void onError(const SockException &err) override; virtual void onManager() override; - static string urlDecode(const string &str); - static const char* get_mime_type(const char* name); protected: //FlvMuxer override void onWrite(const Buffer::Ptr &data) override ; @@ -145,26 +116,6 @@ private: void sendResponse(const char *pcStatus, bool bClose, const char *pcContentType = nullptr, const HttpSession::KeyValue &header = HttpSession::KeyValue(), const HttpBody::Ptr &body = nullptr,bool set_content_len = true); - /** - * 判断http客户端是否有权限访问文件的逻辑步骤 - * - * 1、根据http请求头查找cookie,找到进入步骤3 - * 2、根据http url参数(如果没有根据ip+端口号)查找cookie,如果还是未找到cookie则进入步骤5 - * 3、cookie标记是否有权限访问文件,如果有权限,直接返回文件 - * 4、cookie中记录的url参数是否跟本次url参数一致,如果一致直接返回客户端错误码 - * 5、触发kBroadcastHttpAccess事件 - * @param path 文件或目录 - * @param is_dir path是否为目录 - * @param callback 有权限或无权限的回调 - */ - void canAccessPath(const string &path,bool is_dir,const function &callback); - - /** - * 获取用户唯一识别id - * 有url参数返回参数,无参数返回ip+端口号 - * @return - */ - string getClientUid(); //设置socket标志 void setSocketFlags(); From 12346f77a4c4ce2f545132802e0df580a1090b84 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 30 Nov 2019 11:44:05 +0800 Subject: [PATCH 048/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebHook.cpp | 2 +- src/Http/WebSocketSession.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/server/WebHook.cpp b/server/WebHook.cpp index 85c63d84..e251f24b 100644 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -427,7 +427,7 @@ void installWebHook(){ /** * kBroadcastHttpAccess事件触发机制 * 1、根据http请求头查找cookie,找到进入步骤3 - * 2、根据http url参数(如果没有根据ip+端口号)查找cookie,如果还是未找到cookie则进入步骤5 + * 2、根据http url参数查找cookie,如果还是未找到cookie则进入步骤5 * 3、cookie标记是否有权限访问文件,如果有权限,直接返回文件 * 4、cookie中记录的url参数是否跟本次url参数一致,如果一致直接返回客户端错误码 * 5、触发kBroadcastHttpAccess事件 diff --git a/src/Http/WebSocketSession.h b/src/Http/WebSocketSession.h index fe4d443a..682a2559 100644 --- a/src/Http/WebSocketSession.h +++ b/src/Http/WebSocketSession.h @@ -28,6 +28,7 @@ #define ZLMEDIAKIT_WEBSOCKETSESSION_H #include "HttpSession.h" +#include "Network/TcpServer.h" /** * 通过该模板类可以透明化WebSocket协议, From d46b67a5cf450c743a1c7087aadb5771539a302b Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 30 Nov 2019 11:56:40 +0800 Subject: [PATCH 049/272] =?UTF-8?q?=E7=B2=BE=E7=AE=80=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/HttpFileManager.cpp | 4 ++-- src/Http/HttpSession.cpp | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Http/HttpFileManager.cpp b/src/Http/HttpFileManager.cpp index 800c0a6e..e4aebcf6 100644 --- a/src/Http/HttpFileManager.cpp +++ b/src/Http/HttpFileManager.cpp @@ -350,7 +350,7 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo if (cookie) { headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); } - cb("401 Unauthorized", "", headerOut, std::make_shared(errMsg)); + cb("401 Unauthorized", "text/html", headerOut, std::make_shared(errMsg)); return; } @@ -380,7 +380,7 @@ void HttpFileManager::onAccessPath(TcpSession &sender, Parser &parser, const Htt auto strFile = File::absolutePath(enableVhost ? mediaInfo._vhost + parser.Url() : parser.Url(), rootPath); //访问的是文件夹 - if (strFile.back() == '/' || File::is_dir(strFile.data())) { + if (File::is_dir(strFile.data())) { auto indexFile = searchIndexFile(strFile); if (!indexFile.empty()) { //发现该文件夹下有index文件 diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp index 3f01f567..bb2b3b5a 100644 --- a/src/Http/HttpSession.cpp +++ b/src/Http/HttpSession.cpp @@ -299,9 +299,7 @@ void HttpSession::Handle_Req_GET(int64_t &content_len) { if (!strongSelf) { return; } - strongSelf->sendResponse(status_code.data(), bClose, - content_type.empty() ? nullptr : content_type.data(), - responseHeader, body); + strongSelf->sendResponse(status_code.data(), bClose, content_type.data(), responseHeader, body); }); }); } From cadff93d4d9257d60a6f878e13a9cc7bad48f215 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 30 Nov 2019 14:29:44 +0800 Subject: [PATCH 050/272] =?UTF-8?q?1=E3=80=81hls=20cookie=E6=94=B9?= =?UTF-8?q?=E6=88=9060=E7=A7=92=E6=9C=89=E6=95=88=E6=9C=9F=E5=B9=B6?= =?UTF-8?q?=E4=B8=94=E8=AE=BF=E9=97=AE=E6=97=B6=E5=88=B7=E6=96=B0cookie=20?= =?UTF-8?q?2=E3=80=81=E5=8E=BB=E9=99=A4keep-alive=E4=B8=8B=E6=9C=80?= =?UTF-8?q?=E5=A4=A7=E8=AF=B7=E6=B1=82=E6=AC=A1=E6=95=B0=E9=99=90=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/config.ini | 2 -- src/Common/config.cpp | 4 ---- src/Common/config.h | 2 -- src/Http/HttpFileManager.cpp | 36 +++++++++++++++++++++++------------- src/Http/HttpSession.cpp | 18 +++++------------- src/Http/HttpSession.h | 1 - 6 files changed, 28 insertions(+), 35 deletions(-) diff --git a/conf/config.ini b/conf/config.ini index c6bebc7d..8df1dd3b 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -96,8 +96,6 @@ timeoutSec=10 charSet=utf-8 #http链接超时时间 keepAliveSecond=10 -#keep-alive类型的链接最多复用次数 -maxReqCount=100 #http请求体最大字节数,如果post的body太大,则不适合缓存body在内存 maxReqSize=4096 #404网页内容,用户可以自定义404网页 diff --git a/src/Common/config.cpp b/src/Common/config.cpp index 39336e69..37597b96 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -107,8 +107,6 @@ const string kSendBufSize = HTTP_FIELD"sendBufSize"; const string kMaxReqSize = HTTP_FIELD"maxReqSize"; //http keep-alive秒数 const string kKeepAliveSecond = HTTP_FIELD"keepAliveSecond"; -//http keep-alive最大请求数 -const string kMaxReqCount = HTTP_FIELD"maxReqCount"; //http 字符编码 const string kCharSet = HTTP_FIELD"charSet"; //http 服务器根目录 @@ -120,8 +118,6 @@ onceToken token([](){ mINI::Instance()[kSendBufSize] = 64 * 1024; mINI::Instance()[kMaxReqSize] = 4*1024; mINI::Instance()[kKeepAliveSecond] = 15; - mINI::Instance()[kMaxReqCount] = 100; - #if defined(_WIN32) mINI::Instance()[kCharSet] = "gb2312"; #else diff --git a/src/Common/config.h b/src/Common/config.h index ba171c99..3f597c5a 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -199,8 +199,6 @@ extern const string kSendBufSize; extern const string kMaxReqSize; //http keep-alive秒数 extern const string kKeepAliveSecond; -//http keep-alive最大请求数 -extern const string kMaxReqCount; //http 字符编码 extern const string kCharSet; //http 服务器根目录 diff --git a/src/Http/HttpFileManager.cpp b/src/Http/HttpFileManager.cpp index e4aebcf6..21097021 100644 --- a/src/Http/HttpFileManager.cpp +++ b/src/Http/HttpFileManager.cpp @@ -35,12 +35,17 @@ namespace mediakit { -static int kHlsCookieSecond = 10 * 60; +// hls的播放cookie缓存时间默认60秒, +// 每次访问一次该cookie,那么将重新刷新cookie有效期 +// 假如播放器在60秒内都未访问该cookie,那么将重新触发hls播放鉴权 +static int kHlsCookieSecond = 60; static const string kCookieName = "ZL_COOKIE"; static const string kCookiePathKey = "kCookiePathKey"; static const string kAccessErrKey = "kAccessErrKey"; +static const string kAccessHls = "kAccessHls"; +static const string kHlsSuffix = "/hls.m3u8"; -static const string &getMimeType(const char *name) { +static const string &getContentType(const char *name) { const char *dot; dot = strrchr(name, '.'); static StrCaseMap mapType; @@ -211,11 +216,7 @@ static bool end_of(const string &str, const string &substr){ }; //拦截hls的播放请求 -static bool checkHls(BroadcastHttpAccessArgs){ - if(!end_of(args._streamid,("/hls.m3u8"))) { - //不是hls - return false; - } +static bool emitHlsPlayed(BroadcastHttpAccessArgs){ //访问的hls.m3u8结尾,我们转换成kBroadcastMediaPlayed事件 Broadcast::AuthInvoker mediaAuthInvoker = [invoker,path](const string &err){ //cookie有效期为kHlsCookieSecond @@ -223,7 +224,7 @@ static bool checkHls(BroadcastHttpAccessArgs){ }; auto args_copy = args; - replace(args_copy._streamid,"/hls.m3u8",""); + replace(args_copy._streamid,kHlsSuffix,""); return NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed,args_copy,mediaAuthInvoker,sender); } @@ -257,10 +258,16 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI auto lck = cookie->getLock(); auto accessErr = (*cookie)[kAccessErrKey].get(); auto cookiePath = (*cookie)[kCookiePathKey].get(); + auto is_hls = (*cookie)[kAccessHls].get(); if (path.find(cookiePath) == 0) { //上次cookie是限定本目录 if (accessErr.empty()) { //上次鉴权成功 + if(is_hls){ + //如果播放的是hls,那么刷新hls的cookie + cookie->updateTime(); + cookie_from_header = false; + } callback("", cookie_from_header ? nullptr : cookie); return; } @@ -275,10 +282,11 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI HttpCookieManager::Instance().delCookie(cookie); } + bool is_hls = end_of(path,kHlsSuffix); //该用户从来未获取过cookie,这个时候我们广播是否允许该用户访问该http目录 - HttpSession::HttpAccessPathInvoker accessPathInvoker = [callback, uid, path, is_dir](const string &errMsg, - const string &cookie_path_in, - int cookieLifeSecond) { + auto accessPathInvoker = [callback, uid, path, is_dir, is_hls](const string &errMsg, + const string &cookie_path_in, + int cookieLifeSecond) { HttpServerCookie::Ptr cookie; if (cookieLifeSecond) { //本次鉴权设置了有效期,我们把鉴权结果缓存在cookie中 @@ -295,11 +303,13 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI (*cookie)[kCookiePathKey].set(cookie_path); //记录能否访问 (*cookie)[kAccessErrKey].set(errMsg); + //记录访问的是否为hls + (*cookie)[kAccessHls].set(is_hls); } callback(errMsg, cookie); }; - if (checkHls(parser, mediaInfo, path, is_dir, accessPathInvoker, sender)) { + if (is_hls && emitHlsPlayed(parser, mediaInfo, path, is_dir, accessPathInvoker, sender)) { //是hls的播放鉴权,拦截之 return; } @@ -359,7 +369,7 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo httpHeader["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); } HttpSession::HttpResponseInvoker invoker = [&](const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body) { - cb(codeOut.data(), getMimeType(strFile.data()), headerOut, body); + cb(codeOut.data(), getContentType(strFile.data()), headerOut, body); }; invoker.responseFile(parser.getValues(), httpHeader, strFile); }); diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp index bb2b3b5a..1c36e5cc 100644 --- a/src/Http/HttpSession.cpp +++ b/src/Http/HttpSession.cpp @@ -194,9 +194,7 @@ bool HttpSession::checkLiveFlvStream(const function &cb){ return false; } _mediaInfo._streamid.erase(_mediaInfo._streamid.size() - 4);//去除.flv后缀 - - GET_CONFIG(uint32_t,reqCnt,Http::kMaxReqCount); - bool bClose = (strcasecmp(_parser["Connection"].data(),"close") == 0) || ( ++_iReqCnt > reqCnt); + bool bClose = !strcasecmp(_parser["Connection"].data(),"close"); weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); MediaSource::findAsync(_mediaInfo,weakSelf.lock(), true,[weakSelf,bClose,this,cb](const MediaSource::Ptr &src){ @@ -284,8 +282,7 @@ void HttpSession::Handle_Req_GET(int64_t &content_len) { return; } - GET_CONFIG(uint32_t,reqCnt,Http::kMaxReqCount); - bool bClose = (strcasecmp(_parser["Connection"].data(),"close") == 0) || ( ++_iReqCnt > reqCnt); + bool bClose = !strcasecmp(_parser["Connection"].data(),"close"); weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); HttpFileManager::onAccessPath(*this, _parser, [weakSelf, bClose](const string &status_code, const string &content_type, @@ -319,7 +316,6 @@ void HttpSession::sendResponse(const char *pcStatus, bool set_content_len ){ GET_CONFIG(string,charSet,Http::kCharSet); GET_CONFIG(uint32_t,keepAliveSec,Http::kKeepAliveSecond); - GET_CONFIG(uint32_t,reqCnt,Http::kMaxReqCount); //body默认为空 int64_t size = 0; @@ -343,7 +339,7 @@ void HttpSession::sendResponse(const char *pcStatus, headerOut.emplace("Server", SERVER_NAME); headerOut.emplace("Connection", bClose ? "close" : "keep-alive"); if(!bClose){ - headerOut.emplace("Keep-Alive",StrPrinter << "timeout=" << keepAliveSec << ", max=" << reqCnt << endl); + headerOut.emplace("Keep-Alive",StrPrinter << "timeout=" << keepAliveSec << ", max=100" << endl); } if(!_origin.empty()){ @@ -463,10 +459,7 @@ void HttpSession::urlDecode(Parser &parser){ } bool HttpSession::emitHttpEvent(bool doInvoke){ - ///////////////////是否断开本链接/////////////////////// - GET_CONFIG(uint32_t,reqCnt,Http::kMaxReqCount); - - bool bClose = (strcasecmp(_parser["Connection"].data(),"close") == 0) || ( ++_iReqCnt > reqCnt); + bool bClose = !strcasecmp(_parser["Connection"].data(),"close"); /////////////////////异步回复Invoker/////////////////////////////// weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); HttpResponseInvoker invoker = [weakSelf,bClose](const string &codeOut, const KeyValue &headerOut, const HttpBody::Ptr &body){ @@ -495,7 +488,6 @@ bool HttpSession::emitHttpEvent(bool doInvoke){ void HttpSession::Handle_Req_POST(int64_t &content_len) { GET_CONFIG(uint64_t,maxReqSize,Http::kMaxReqSize); - GET_CONFIG(int,maxReqCnt,Http::kMaxReqCount); int64_t totalContentLen = _parser["Content-Length"].empty() ? -1 : atoll(_parser["Content-Length"].data()); @@ -535,7 +527,7 @@ void HttpSession::Handle_Req_POST(int64_t &content_len) { content_len = -1; auto parserCopy = _parser; std::shared_ptr recvedContentLen = std::make_shared(0); - bool bClose = (strcasecmp(_parser["Connection"].data(),"close") == 0) || ( ++_iReqCnt > maxReqCnt); + bool bClose = !strcasecmp(_parser["Connection"].data(),"close"); _contentCallBack = [this,parserCopy,totalContentLen,recvedContentLen,bClose](const char *data,uint64_t len){ *(recvedContentLen) += len; diff --git a/src/Http/HttpSession.h b/src/Http/HttpSession.h index 1d9ead78..207b061b 100644 --- a/src/Http/HttpSession.h +++ b/src/Http/HttpSession.h @@ -123,7 +123,6 @@ private: string _origin; Parser _parser; Ticker _ticker; - uint32_t _iReqCnt = 0; //消耗的总流量 uint64_t _ui64TotalBytes = 0; //flv over http From c1ab73f758ad20b76c75455949b0123079c7e6ef Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 30 Nov 2019 22:14:22 +0800 Subject: [PATCH 051/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2=E5=A4=B1=E8=B4=A5=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/HttpFileManager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Http/HttpFileManager.cpp b/src/Http/HttpFileManager.cpp index 21097021..d44f736d 100644 --- a/src/Http/HttpFileManager.cpp +++ b/src/Http/HttpFileManager.cpp @@ -284,9 +284,9 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI bool is_hls = end_of(path,kHlsSuffix); //该用户从来未获取过cookie,这个时候我们广播是否允许该用户访问该http目录 - auto accessPathInvoker = [callback, uid, path, is_dir, is_hls](const string &errMsg, - const string &cookie_path_in, - int cookieLifeSecond) { + HttpSession::HttpAccessPathInvoker accessPathInvoker = [callback, uid, path, is_dir, is_hls]( const string &errMsg, + const string &cookie_path_in, + int cookieLifeSecond) { HttpServerCookie::Ptr cookie; if (cookieLifeSecond) { //本次鉴权设置了有效期,我们把鉴权结果缓存在cookie中 From c55f26fba70bd631ae81101cb25783d9530e8f27 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 3 Dec 2019 12:32:57 +0800 Subject: [PATCH 052/272] =?UTF-8?q?=E6=95=B4=E7=90=86MediaSink?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/MediaSink.cpp | 17 +++---- src/Common/MediaSink.h | 36 +++++++++------ src/Common/MediaSource.h | 10 +--- src/Common/MultiMediaSourceMuxer.h | 74 +++++++++++++++++------------- src/Extension/Frame.h | 2 +- src/Extension/Track.h | 31 +++++++++++++ src/MediaFile/MP4Muxer.cpp | 22 +++++++-- src/MediaFile/MP4Muxer.h | 26 ++++++----- src/MediaFile/MP4Recorder.cpp | 5 +- src/MediaFile/MP4Recorder.h | 15 +++--- src/MediaFile/MediaRecorder.h | 2 +- src/MediaFile/TsMuxer.h | 2 +- src/Player/PlayerBase.h | 31 ++----------- src/Rtmp/RtmpMediaSourceMuxer.h | 5 +- src/Rtmp/RtmpMuxer.cpp | 44 +++++++++++------- src/Rtmp/RtmpMuxer.h | 24 +++++++--- src/Rtsp/RtspMediaSourceMuxer.h | 6 ++- src/Rtsp/RtspMuxer.cpp | 32 +++++++++---- src/Rtsp/RtspMuxer.h | 26 +++++++---- 19 files changed, 243 insertions(+), 167 deletions(-) diff --git a/src/Common/MediaSink.cpp b/src/Common/MediaSink.cpp index 4d74e2c9..c757b57c 100644 --- a/src/Common/MediaSink.cpp +++ b/src/Common/MediaSink.cpp @@ -111,21 +111,16 @@ void MediaSink::inputFrame(const Frame::Ptr &frame) { } } -bool MediaSink::isAllTrackReady() const { - return _allTrackReady; -} - -Track::Ptr MediaSink::getTrack(TrackType type,bool trackReady) const { +vector MediaSink::getTracks(bool trackReady) const{ + vector ret; lock_guard lck(_mtx); for (auto &pr : _track_map){ - if(pr.second->getTrackType() == type){ - if(!trackReady){ - return pr.second; - } - return pr.second->ready() ? pr.second : nullptr; + if(trackReady && !pr.second->ready()){ + continue; } + ret.emplace_back(pr.second); } - return nullptr; + return std::move(ret); } diff --git a/src/Common/MediaSink.h b/src/Common/MediaSink.h index 8798590d..d117aae5 100644 --- a/src/Common/MediaSink.h +++ b/src/Common/MediaSink.h @@ -38,11 +38,29 @@ using namespace toolkit; namespace mediakit{ +class MediaSinkInterface : public FrameWriterInterface { +public: + MediaSinkInterface(){}; + virtual ~MediaSinkInterface(){}; + + /** + * 添加track,内部会调用Track的clone方法 + * 只会克隆sps pps这些信息 ,而不会克隆Delegate相关关系 + * @param track + */ + virtual void addTrack(const Track::Ptr & track) = 0; + + /** + * 重置track + */ + virtual void resetTracks() = 0; +}; + /** * 该类的作用是等待Track ready()返回true也就是就绪后再通知派生类进行下一步的操作 * 目的是输入Frame前由Track截取处理下,以便获取有效的信息(譬如sps pps aa_cfg) */ -class MediaSink : public FrameWriterInterface , public std::enable_shared_from_this{ +class MediaSink : public MediaSinkInterface , public TrackSource , public std::enable_shared_from_this{ public: typedef std::shared_ptr Ptr; MediaSink(){} @@ -59,26 +77,18 @@ public: * 只会克隆sps pps这些信息 ,而不会克隆Delegate相关关系 * @param track */ - virtual void addTrack(const Track::Ptr & track); + void addTrack(const Track::Ptr & track) override; /** * 重置track */ - virtual void resetTracks(); + void resetTracks() override; /** - * 全部Track是否都准备好了 - * @return - */ - bool isAllTrackReady() const; - - /** - * 获取特定类型的Track - * @param type track类型 + * 获取所有Track * @param trackReady 是否获取已经准备好的Track - * @return */ - Track::Ptr getTrack(TrackType type,bool trackReady = true) const; + vector getTracks(bool trackReady = true) const override ; protected: /** * 某track已经准备好,其ready()状态返回true, diff --git a/src/Common/MediaSource.h b/src/Common/MediaSource.h index 5cfe597d..1e31b57b 100644 --- a/src/Common/MediaSource.h +++ b/src/Common/MediaSource.h @@ -92,7 +92,7 @@ public: string _param_strs; }; -class MediaSource: public enable_shared_from_this { +class MediaSource: public TrackSource, public enable_shared_from_this { public: typedef std::shared_ptr Ptr; typedef unordered_map > StreamMap; @@ -191,14 +191,6 @@ public: } virtual int readerCount() = 0; - - /** - * 获取track - * @return - */ - virtual vector getTracks(bool trackReady) const{ - return vector(0); - } protected: void regist() ; bool unregist() ; diff --git a/src/Common/MultiMediaSourceMuxer.h b/src/Common/MultiMediaSourceMuxer.h index b466ffec..4bf40c85 100644 --- a/src/Common/MultiMediaSourceMuxer.h +++ b/src/Common/MultiMediaSourceMuxer.h @@ -31,7 +31,7 @@ #include "Rtmp/RtmpMediaSourceMuxer.h" #include "MediaFile/MediaRecorder.h" -class MultiMediaSourceMuxer : public FrameWriterInterface{ +class MultiMediaSourceMuxer : public MediaSink{ public: typedef std::shared_ptr Ptr; @@ -54,25 +54,10 @@ public: } virtual ~MultiMediaSourceMuxer(){} - - /** - * 添加音视频媒体 - * @param track 媒体描述 - */ - void addTrack(const Track::Ptr & track) { - if(_rtmp){ - _rtmp->addTrack(track); - } - if(_rtsp){ - _rtsp->addTrack(track); - } - _record->addTrack(track); - } - /** * 重置音视频媒体 */ - void resetTracks() { + void resetTracks() override{ if(_rtmp){ _rtmp->resetTracks(); } @@ -82,20 +67,6 @@ public: _record->resetTracks(); } - /** - * 写入帧数据然后打包rtmp - * @param frame 帧数据 - */ - void inputFrame(const Frame::Ptr &frame) override { - if(_rtmp) { - _rtmp->inputFrame(frame); - } - if(_rtsp) { - _rtsp->inputFrame(frame); - } - _record->inputFrame(frame); - } - /** * 设置事件监听器 * @param listener @@ -122,6 +93,47 @@ public: _rtsp->setTimeStamp(stamp); } } + +protected: + /** + * 添加音视频媒体 + * @param track 媒体描述 + */ + void onTrackReady(const Track::Ptr & track) override { + if(_rtmp){ + _rtmp->addTrack(track); + } + if(_rtsp){ + _rtsp->addTrack(track); + } + _record->addTrack(track); + } + + /** + * 写入帧数据然后打包rtmp + * @param frame 帧数据 + */ + void onTrackFrame(const Frame::Ptr &frame) override { + if(_rtmp) { + _rtmp->inputFrame(frame); + } + if(_rtsp) { + _rtsp->inputFrame(frame); + } + _record->inputFrame(frame); + } + + /** + * 所有Track都准备就绪,触发媒体注册事件 + */ + void onAllTrackReady() override{ + if(_rtmp) { + _rtmp->onAllTrackReady(); + } + if(_rtsp) { + _rtsp->onAllTrackReady(); + } + } private: RtmpMediaSourceMuxer::Ptr _rtmp; RtspMediaSourceMuxer::Ptr _rtsp; diff --git a/src/Extension/Frame.h b/src/Extension/Frame.h index 79b5d02a..5e4234f7 100644 --- a/src/Extension/Frame.h +++ b/src/Extension/Frame.h @@ -50,7 +50,7 @@ typedef enum { TrackVideo = 0, TrackAudio, TrackTitle, - TrackMax = 0x7FFF + TrackMax = 3 } TrackType; /** diff --git a/src/Extension/Track.h b/src/Extension/Track.h index 5209c74e..b4771027 100644 --- a/src/Extension/Track.h +++ b/src/Extension/Track.h @@ -131,6 +131,37 @@ public: }; +class TrackSource{ +public: + TrackSource(){} + virtual ~TrackSource(){} + + /** + * 获取全部的Track + * @param trackReady 是否获取全部已经准备好的Track + * @return + */ + virtual vector getTracks(bool trackReady = true) const { + return vector(); + } + + /** + * 获取特定Track + * @param type track类型 + * @param trackReady 是否获取全部已经准备好的Track + * @return + */ + Track::Ptr getTrack(TrackType type , bool trackReady = true) const { + auto tracks = getTracks(trackReady); + for(auto &track : tracks){ + if(track->getTrackType() == type){ + return track; + } + } + return nullptr; + } +}; + }//namespace mediakit #endif //ZLMEDIAKIT_TRACK_H diff --git a/src/MediaFile/MP4Muxer.cpp b/src/MediaFile/MP4Muxer.cpp index c1b5bad3..70d6e38c 100644 --- a/src/MediaFile/MP4Muxer.cpp +++ b/src/MediaFile/MP4Muxer.cpp @@ -67,8 +67,12 @@ void MP4MuxerBase::init(int flags) { } /////////////////////////////////// +void MP4Muxer::resetTracks() { + _codec_to_trackid.clear(); + _started = false; +} -void MP4Muxer::onTrackFrame(const Frame::Ptr &frame) { +void MP4Muxer::inputFrame(const Frame::Ptr &frame) { if(frame->configFrame()){ //忽略配置帧 return; @@ -117,7 +121,7 @@ void MP4Muxer::onTrackFrame(const Frame::Ptr &frame) { with_nalu_size); } -void MP4Muxer::onTrackReady(const Track::Ptr &track) { +void MP4Muxer::addTrack(const Track::Ptr &track) { switch (track->getCodecId()) { case CodecAAC: { auto aac_track = dynamic_pointer_cast(track); @@ -210,7 +214,12 @@ void MP4Muxer::onTrackReady(const Track::Ptr &track) { } } -MP4MuxerFile::MP4MuxerFile(const char *file) { +MP4MuxerFile::MP4MuxerFile(const char *file){ + _file_name = file; + openFile(file); +} + +void MP4MuxerFile::openFile(const char *file) { //创建文件 auto fp = File::createfile_file(file,"wb+"); if(!fp){ @@ -237,7 +246,6 @@ MP4MuxerFile::MP4MuxerFile(const char *file) { }); GET_CONFIG(bool, mp4FastStart, Record::kFastStart); - init(mp4FastStart ? MOV_FLAG_FASTSTART : 0); } @@ -264,6 +272,12 @@ uint64_t MP4MuxerFile::onTell() { return ftell64(_file.get()); } + +void MP4MuxerFile::resetTracks(){ + MP4Muxer::resetTracks(); + openFile(_file_name.data()); +} + }//namespace mediakit #endif//#ifdef ENABLE_MP4RECORD diff --git a/src/MediaFile/MP4Muxer.h b/src/MediaFile/MP4Muxer.h index de15d4cc..1532d87e 100644 --- a/src/MediaFile/MP4Muxer.h +++ b/src/MediaFile/MP4Muxer.h @@ -57,23 +57,24 @@ protected: std::shared_ptr _mov_writter; }; -class MP4Muxer : public MediaSink , public MP4MuxerBase{ +class MP4Muxer : public MediaSinkInterface , public MP4MuxerBase{ public: MP4Muxer() = default; ~MP4Muxer() override = default; -protected: - /** - * 某track已经准备好,其ready()状态返回true, - * 此时代表可以获取其例如sps pps等相关信息了 - * @param track - */ - void onTrackReady(const Track::Ptr & track) override; /** - * 某Track输出frame,在onAllTrackReady触发后才会调用此方法 - * @param frame + * 添加已经ready状态的track */ - void onTrackFrame(const Frame::Ptr &frame) override; + void addTrack(const Track::Ptr & track) override; + /** + * 输入帧 + */ + void inputFrame(const Frame::Ptr &frame) override; + + /** + * 重置所有track + */ + void resetTracks() override ; private: struct track_info{ int track_id = -1; @@ -89,13 +90,16 @@ public: typedef std::shared_ptr Ptr; MP4MuxerFile(const char *file); ~MP4MuxerFile(); + void resetTracks() override ; protected: int onRead(void* data, uint64_t bytes) override; int onWrite(const void* data, uint64_t bytes) override; int onSeek( uint64_t offset) override; uint64_t onTell() override ; + void openFile(const char *file); private: std::shared_ptr _file; + string _file_name; }; }//namespace mediakit diff --git a/src/MediaFile/MP4Recorder.cpp b/src/MediaFile/MP4Recorder.cpp index 0f36561c..092258b4 100644 --- a/src/MediaFile/MP4Recorder.cpp +++ b/src/MediaFile/MP4Recorder.cpp @@ -128,7 +128,7 @@ void MP4Recorder::closeFile() { } } -void MP4Recorder::onTrackFrame(const Frame::Ptr &frame) { +void MP4Recorder::inputFrame(const Frame::Ptr &frame) { GET_CONFIG(uint32_t,recordSec,Record::kFileSecond); if(!_muxer || ((_createFileTicker.elapsedTime() > recordSec * 1000) && (!_haveVideo || (_haveVideo && frame->keyFrame()))) ){ @@ -145,7 +145,7 @@ void MP4Recorder::onTrackFrame(const Frame::Ptr &frame) { } } -void MP4Recorder::onTrackReady(const Track::Ptr & track){ +void MP4Recorder::addTrack(const Track::Ptr & track){ //保存所有的track,为创建MP4MuxerFile做准备 _tracks.emplace_back(track); if(track->getTrackType() == TrackVideo){ @@ -158,7 +158,6 @@ void MP4Recorder::resetTracks() { _tracks.clear(); _haveVideo = false; _createFileTicker.resetTime(); - MediaSink::resetTracks(); } } /* namespace mediakit */ diff --git a/src/MediaFile/MP4Recorder.h b/src/MediaFile/MP4Recorder.h index 0c619461..2d9e84e9 100644 --- a/src/MediaFile/MP4Recorder.h +++ b/src/MediaFile/MP4Recorder.h @@ -55,7 +55,7 @@ public: string strStreamId;//流ID string strVhost;//vhost }; -class MP4Recorder : public MediaSink{ +class MP4Recorder : public MediaSinkInterface{ public: typedef std::shared_ptr Ptr; MP4Recorder(const string &strPath, @@ -68,19 +68,16 @@ public: * 重置所有Track */ void resetTracks() override; -private: + /** - * 某Track输出frame,在onAllTrackReady触发后才会调用此方法 - * @param frame + * 输入frame */ - void onTrackFrame(const Frame::Ptr &frame) override ; + void inputFrame(const Frame::Ptr &frame) override; /** - * 某track已经准备好,其ready()状态返回true, - * 此时代表可以获取其例如sps pps等相关信息了 - * @param track + * 添加ready状态的track */ - void onTrackReady(const Track::Ptr & track) override; + void addTrack(const Track::Ptr & track) override; private: void createFile(); void closeFile(); diff --git a/src/MediaFile/MediaRecorder.h b/src/MediaFile/MediaRecorder.h index c36f8613..fbfba1e7 100644 --- a/src/MediaFile/MediaRecorder.h +++ b/src/MediaFile/MediaRecorder.h @@ -37,7 +37,7 @@ using namespace toolkit; namespace mediakit { -class MediaRecorder : public MediaSink{ +class MediaRecorder : public MediaSinkInterface{ public: typedef std::shared_ptr Ptr; MediaRecorder(const string &strVhost, diff --git a/src/MediaFile/TsMuxer.h b/src/MediaFile/TsMuxer.h index a876c8c3..3bd4509c 100644 --- a/src/MediaFile/TsMuxer.h +++ b/src/MediaFile/TsMuxer.h @@ -38,7 +38,7 @@ using namespace toolkit; namespace mediakit { -class TsMuxer : public MediaSink { +class TsMuxer : public MediaSinkInterface { public: TsMuxer(); virtual ~TsMuxer(); diff --git a/src/Player/PlayerBase.h b/src/Player/PlayerBase.h index 84fd6795..54aa0f3c 100644 --- a/src/Player/PlayerBase.h +++ b/src/Player/PlayerBase.h @@ -41,7 +41,7 @@ using namespace toolkit; namespace mediakit { -class DemuxerBase { +class DemuxerBase : public TrackSource{ public: typedef std::shared_ptr Ptr; @@ -57,29 +57,6 @@ public: * @return */ virtual bool isInited(int analysisMs) { return true; } - - /** - * 获取全部的Track - * @param trackReady 是否获取全部已经准备好的Track - * @return - */ - virtual vector getTracks(bool trackReady = true) const { return vector();} - - /** - * 获取特定Track - * @param type track类型 - * @param trackReady 是否获取全部已经准备好的Track - * @return - */ - virtual Track::Ptr getTrack(TrackType type , bool trackReady = true) const { - auto tracks = getTracks(trackReady); - for(auto &track : tracks){ - if(track->getTrackType() == type){ - return track; - } - } - return nullptr; - } }; @@ -274,14 +251,14 @@ public: bool isInited(int analysisMs) override; /** - * 获取所有可用Track,请在isInited()返回true时调用 - * @return + * 获取所有Track + * @return 所有Track */ vector getTracks(bool trackReady = true) const override; /** * 获取节目总时长 - * @return + * @return 节目总时长,单位秒 */ float getDuration() const override; protected: diff --git a/src/Rtmp/RtmpMediaSourceMuxer.h b/src/Rtmp/RtmpMediaSourceMuxer.h index e3d9561d..2f8e81cc 100644 --- a/src/Rtmp/RtmpMediaSourceMuxer.h +++ b/src/Rtmp/RtmpMediaSourceMuxer.h @@ -48,11 +48,12 @@ public: void setListener(const std::weak_ptr &listener){ _mediaSouce->setListener(listener); } + int readerCount() const{ return _mediaSouce->readerCount(); } -private: - void onAllTrackReady() override { + + void onAllTrackReady(){ _mediaSouce->onGetMetaData(getMetadata()); } private: diff --git a/src/Rtmp/RtmpMuxer.cpp b/src/Rtmp/RtmpMuxer.cpp index 6ed7e324..e252d410 100644 --- a/src/Rtmp/RtmpMuxer.cpp +++ b/src/Rtmp/RtmpMuxer.cpp @@ -38,13 +38,7 @@ RtmpMuxer::RtmpMuxer(const TitleMeta::Ptr &title) { _rtmpRing = std::make_shared(); } -void RtmpMuxer::onTrackReady(const Track::Ptr &track) { - //生成rtmp编码器 - //克隆该Track,防止循环引用 - auto encoder = Factory::getRtmpCodecByTrack(track->clone()); - if (!encoder) { - return; - } +void RtmpMuxer::addTrack(const Track::Ptr &track) { //根据track生产metadata Metadata::Ptr metadata; switch (track->getTrackType()){ @@ -57,26 +51,34 @@ void RtmpMuxer::onTrackReady(const Track::Ptr &track) { } break; default: - return;; + return; } + + auto &encoder = _encoder[track->getTrackType()]; + //生成rtmp编码器,克隆该Track,防止循环引用 + encoder = Factory::getRtmpCodecByTrack(track->clone()); + if (!encoder) { + return; + } + + //设置rtmp输出环形缓存 + encoder->setRtmpRing(_rtmpRing); + //添加其metadata metadata->getMetadata().object_for_each([&](const std::string &key, const AMFValue &value){ _metadata.set(key,value); }); - //设置Track的代理,这样输入frame至Track时,最终数据将输出到RtmpEncoder中 - track->addDelegate(encoder); - //Rtmp编码器共用同一个环形缓存 - encoder->setRtmpRing(_rtmpRing); } +void RtmpMuxer::inputFrame(const Frame::Ptr &frame) { + auto &encoder = _encoder[frame->getTrackType()]; + if(encoder){ + encoder->inputFrame(frame); + } +} const AMFValue &RtmpMuxer::getMetadata() const { - if(!isAllTrackReady()){ - //尚未就绪 - static AMFValue s_amf; - return s_amf; - } return _metadata; } @@ -84,4 +86,12 @@ RtmpRingInterface::RingType::Ptr RtmpMuxer::getRtmpRing() const { return _rtmpRing; } +void RtmpMuxer::resetTracks() { + _metadata.clear(); + for(auto &encoder : _encoder){ + encoder = nullptr; + } +} + + }/* namespace mediakit */ \ No newline at end of file diff --git a/src/Rtmp/RtmpMuxer.h b/src/Rtmp/RtmpMuxer.h index 87552eb5..3c4743b9 100644 --- a/src/Rtmp/RtmpMuxer.h +++ b/src/Rtmp/RtmpMuxer.h @@ -34,7 +34,7 @@ namespace mediakit{ -class RtmpMuxer : public MediaSink{ +class RtmpMuxer : public MediaSinkInterface{ public: typedef std::shared_ptr Ptr; @@ -55,16 +55,26 @@ public: * @return */ RtmpRingInterface::RingType::Ptr getRtmpRing() const; -protected: + /** - * 某track已经准备好,其ready()状态返回true, - * 此时代表可以获取其例如sps pps等相关信息了 - * @param track - */ - void onTrackReady(const Track::Ptr & track) override ; + * 添加ready状态的track + */ + void addTrack(const Track::Ptr & track) override; + + /** + * 写入帧数据 + * @param frame 帧 + */ + void inputFrame(const Frame::Ptr &frame) override; + + /** + * 重置所有track + */ + void resetTracks() override ; private: RtmpRingInterface::RingType::Ptr _rtmpRing; AMFValue _metadata; + RtmpCodec::Ptr _encoder[TrackMax]; }; diff --git a/src/Rtsp/RtspMediaSourceMuxer.h b/src/Rtsp/RtspMediaSourceMuxer.h index f4637942..9c496bd6 100644 --- a/src/Rtsp/RtspMediaSourceMuxer.h +++ b/src/Rtsp/RtspMediaSourceMuxer.h @@ -48,14 +48,16 @@ public: void setListener(const std::weak_ptr &listener){ _mediaSouce->setListener(listener); } + int readerCount() const{ return _mediaSouce->readerCount(); } + void setTimeStamp(uint32_t stamp){ _mediaSouce->setTimeStamp(stamp); } -private: - void onAllTrackReady() override { + + void onAllTrackReady(){ _mediaSouce->onGetSDP(getSdp()); } private: diff --git a/src/Rtsp/RtspMuxer.cpp b/src/Rtsp/RtspMuxer.cpp index 9f4da62a..1e482add 100644 --- a/src/Rtsp/RtspMuxer.cpp +++ b/src/Rtsp/RtspMuxer.cpp @@ -38,29 +38,34 @@ RtspMuxer::RtspMuxer(const TitleSdp::Ptr &title){ _rtpRing = std::make_shared(); } -void RtspMuxer::onTrackReady(const Track::Ptr &track) { +void RtspMuxer::addTrack(const Track::Ptr &track) { //根据track生成sdp Sdp::Ptr sdp = track->getSdp(); if (!sdp) { return; } - auto encoder = Factory::getRtpEncoderBySdp(sdp); + + auto &encoder = _encoder[track->getTrackType()]; + encoder = Factory::getRtpEncoderBySdp(sdp); if (!encoder) { return; } + + //设置rtp输出环形缓存 + encoder->setRtpRing(_rtpRing); + //添加其sdp _sdp.append(sdp->getSdp()); - //设置Track的代理,这样输入frame至Track时,最终数据将输出到RtpEncoder中 - track->addDelegate(encoder); - //rtp编码器共用同一个环形缓存 - encoder->setRtpRing(_rtpRing); +} + +void RtspMuxer::inputFrame(const Frame::Ptr &frame) { + auto &encoder = _encoder[frame->getTrackType()]; + if(encoder){ + encoder->inputFrame(frame); + } } string RtspMuxer::getSdp() { - if(!isAllTrackReady()){ - //尚未就绪 - return ""; - } return _sdp; } @@ -68,5 +73,12 @@ RtpRingInterface::RingType::Ptr RtspMuxer::getRtpRing() const { return _rtpRing; } +void RtspMuxer::resetTracks() { + _sdp.clear(); + for(auto &encoder : _encoder){ + encoder = nullptr; + } +} + } /* namespace mediakit */ \ No newline at end of file diff --git a/src/Rtsp/RtspMuxer.h b/src/Rtsp/RtspMuxer.h index 3d99537b..2d9a8736 100644 --- a/src/Rtsp/RtspMuxer.h +++ b/src/Rtsp/RtspMuxer.h @@ -35,7 +35,7 @@ namespace mediakit{ /** * rtsp生成器 */ -class RtspMuxer : public MediaSink{ +class RtspMuxer : public MediaSinkInterface{ public: typedef std::shared_ptr Ptr; @@ -57,16 +57,26 @@ public: * @return */ RtpRingInterface::RingType::Ptr getRtpRing() const; -protected: + /** - * 某track已经准备好,其ready()状态返回true, - * 此时代表可以获取其例如sps pps等相关信息了 - * @param track - */ - void onTrackReady(const Track::Ptr & track) override ; + * 添加ready状态的track + */ + void addTrack(const Track::Ptr & track) override; + + /** + * 写入帧数据 + * @param frame 帧 + */ + void inputFrame(const Frame::Ptr &frame) override; + + /** + * 重置所有track + */ + void resetTracks() override ; private: - RtpRingInterface::RingType::Ptr _rtpRing; string _sdp; + RtpCodec::Ptr _encoder[TrackMax]; + RtpRingInterface::RingType::Ptr _rtpRing; }; From b50a34bf1f18594626066e88daf92e780f9a6ea2 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 3 Dec 2019 12:43:48 +0800 Subject: [PATCH 053/272] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Player/PlayerBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Player/PlayerBase.cpp b/src/Player/PlayerBase.cpp index eab05ce7..7a66a0ee 100644 --- a/src/Player/PlayerBase.cpp +++ b/src/Player/PlayerBase.cpp @@ -104,7 +104,7 @@ vector Demuxer::getTracks(bool trackReady) const { ret.emplace_back(_audioTrack); } } - return ret; + return std::move(ret); } float Demuxer::getDuration() const { From 68718953d410a9a31a92d0b7ae0abcbc2800eef3 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 3 Dec 2019 13:05:23 +0800 Subject: [PATCH 054/272] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/MediaSink.cpp | 11 +++-------- src/Common/MediaSink.h | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/Common/MediaSink.cpp b/src/Common/MediaSink.cpp index c757b57c..64049dc5 100644 --- a/src/Common/MediaSink.cpp +++ b/src/Common/MediaSink.cpp @@ -49,14 +49,9 @@ void MediaSink::addTrack(const Track::Ptr &track_in) { _ticker.resetTime(); } - weak_ptr weakSelf = shared_from_this(); - track->addDelegate(std::make_shared([weakSelf](const Frame::Ptr &frame){ - auto strongSelf = weakSelf.lock(); - if(!strongSelf){ - return; - } - if(!strongSelf->_anyTrackUnReady){ - strongSelf->onTrackFrame(frame); + track->addDelegate(std::make_shared([this](const Frame::Ptr &frame){ + if(!_anyTrackUnReady){ + onTrackFrame(frame); } })); } diff --git a/src/Common/MediaSink.h b/src/Common/MediaSink.h index d117aae5..0f7fc92a 100644 --- a/src/Common/MediaSink.h +++ b/src/Common/MediaSink.h @@ -60,7 +60,7 @@ public: * 该类的作用是等待Track ready()返回true也就是就绪后再通知派生类进行下一步的操作 * 目的是输入Frame前由Track截取处理下,以便获取有效的信息(譬如sps pps aa_cfg) */ -class MediaSink : public MediaSinkInterface , public TrackSource , public std::enable_shared_from_this{ +class MediaSink : public MediaSinkInterface , public TrackSource{ public: typedef std::shared_ptr Ptr; MediaSink(){} From 5249c24430ba05e5945a9f36dc8951b80046ff17 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 3 Dec 2019 16:10:02 +0800 Subject: [PATCH 055/272] =?UTF-8?q?=E6=95=B4=E7=90=86MediaSource=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 34 ++---- src/Common/MediaSource.cpp | 187 ++++++++++++++++++++++++---- src/Common/MediaSource.h | 189 ++++++++--------------------- src/Common/MultiMediaSourceMuxer.h | 4 +- src/Extension/Track.h | 4 +- src/Http/HttpSession.cpp | 2 +- src/Player/MediaPlayer.cpp | 22 ++-- src/Player/PlayerBase.h | 60 ++++----- src/Player/PlayerProxy.cpp | 4 +- src/Rtmp/RtmpMediaSourceMuxer.h | 5 + src/Rtmp/RtmpPlayerImp.h | 8 +- src/Rtmp/RtmpSession.cpp | 2 +- src/Rtmp/RtmpToRtspMediaSource.h | 5 +- src/Rtsp/RtspMediaSourceMuxer.h | 5 + src/Rtsp/RtspPlayerImp.h | 8 +- src/Rtsp/RtspSession.cpp | 2 +- src/Rtsp/RtspToRtmpMediaSource.h | 5 +- src/Shell/ShellCMD.h | 40 +++--- 18 files changed, 323 insertions(+), 263 deletions(-) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index d1cb621c..580e34c8 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -395,25 +395,21 @@ void installWebApi() { API_REGIST(api,getMediaList,{ CHECK_SECRET(); //获取所有MediaSource列表 - MediaSource::for_each_media([&](const string &schema, - const string &vhost, - const string &app, - const string &stream, - const MediaSource::Ptr &media){ - if(!allArgs["schema"].empty() && allArgs["schema"] != schema){ + MediaSource::for_each_media([&](const MediaSource::Ptr &media){ + if(!allArgs["schema"].empty() && allArgs["schema"] != media->getSchema()){ return; } - if(!allArgs["vhost"].empty() && allArgs["vhost"] != vhost){ + if(!allArgs["vhost"].empty() && allArgs["vhost"] != media->getVhost()){ return; } - if(!allArgs["app"].empty() && allArgs["app"] != app){ + if(!allArgs["app"].empty() && allArgs["app"] != media->getApp()){ return; } Value item; - item["schema"] = schema; - item["vhost"] = vhost; - item["app"] = app; - item["stream"] = stream; + item["schema"] = media->getSchema(); + item["vhost"] = media->getVhost(); + item["app"] = media->getApp(); + item["stream"] = media->getId(); val["data"].append(item); }); }); @@ -453,21 +449,17 @@ void installWebApi() { int count_hit = 0; int count_closed = 0; list media_list; - MediaSource::for_each_media([&](const string &schema, - const string &vhost, - const string &app, - const string &stream, - const MediaSource::Ptr &media){ - if(!allArgs["schema"].empty() && allArgs["schema"] != schema){ + MediaSource::for_each_media([&](const MediaSource::Ptr &media){ + if(!allArgs["schema"].empty() && allArgs["schema"] != media->getSchema()){ return; } - if(!allArgs["vhost"].empty() && allArgs["vhost"] != vhost){ + if(!allArgs["vhost"].empty() && allArgs["vhost"] != media->getVhost()){ return; } - if(!allArgs["app"].empty() && allArgs["app"] != app){ + if(!allArgs["app"].empty() && allArgs["app"] != media->getApp()){ return; } - if(!allArgs["stream"].empty() && allArgs["stream"] != stream){ + if(!allArgs["stream"].empty() && allArgs["stream"] != media->getId()){ return; } ++count_hit; diff --git a/src/Common/MediaSource.cpp b/src/Common/MediaSource.cpp index cb668f37..96b63332 100644 --- a/src/Common/MediaSource.cpp +++ b/src/Common/MediaSource.cpp @@ -38,8 +38,142 @@ namespace mediakit { recursive_mutex MediaSource::g_mtxMediaSrc; MediaSource::SchemaVhostAppStreamMap MediaSource::g_mapMediaSrc; +MediaSource::MediaSource(const string &strSchema, const string &strVhost, const string &strApp, const string &strId) : + _strSchema(strSchema), + _strApp(strApp), + _strId(strId) { + if (strVhost.empty()) { + _strVhost = DEFAULT_VHOST; + } else { + _strVhost = strVhost; + } +} -void MediaSource::findAsync(const MediaInfo &info, +MediaSource::~MediaSource() { + unregist(); +} + +const string& MediaSource::getSchema() const { + return _strSchema; +} + +const string& MediaSource::getVhost() const { + return _strVhost; +} + +const string& MediaSource::getApp() const { + //获取该源的id + return _strApp; +} + +const string& MediaSource::getId() const { + return _strId; +} + +vector MediaSource::getTracks(bool trackReady) const { + auto strongPtr = _track_source.lock(); + if(strongPtr){ + return strongPtr->getTracks(trackReady); + } + return vector(); +} + +void MediaSource::setTrackSource(const std::weak_ptr &track_src) { + _track_source = track_src; +} + +void MediaSource::setListener(const std::weak_ptr &listener){ + _listener = listener; +} + +const std::weak_ptr& MediaSource::getListener() const{ + return _listener; +} + +bool MediaSource::seekTo(uint32_t ui32Stamp) { + auto listener = _listener.lock(); + if(!listener){ + return false; + } + return listener->seekTo(*this,ui32Stamp); +} + +bool MediaSource::close(bool force) { + auto listener = _listener.lock(); + if(!listener){ + return false; + } + return listener->close(*this,force); +} + +void MediaSource::onNoneReader(){ + auto listener = _listener.lock(); + if(!listener){ + return; + } + listener->onNoneReader(*this); +} + +void MediaSource::for_each_media(const function &cb) { + lock_guard lock(g_mtxMediaSrc); + for (auto &pr0 : g_mapMediaSrc) { + for (auto &pr1 : pr0.second) { + for (auto &pr2 : pr1.second) { + for (auto &pr3 : pr2.second) { + auto src = pr3.second.lock(); + if(src){ + cb(src); + } + } + } + } + } +} + +template +static bool searchMedia(MAP &map, + const string &schema, + const string &vhost, + const string &app, + const string &id, + FUNC &&func) { + auto it0 = map.find(schema); + if (it0 == map.end()) { + //未找到协议 + return false; + } + auto it1 = it0->second.find(vhost); + if (it1 == it0->second.end()) { + //未找到vhost + return false; + } + auto it2 = it1->second.find(app); + if (it2 == it1->second.end()) { + //未找到app + return false; + } + auto it3 = it2->second.find(id); + if (it3 == it2->second.end()) { + //未找到streamId + return false; + } + return func(it0, it1, it2, it3); +} + +template +static void eraseIfEmpty(MAP &map, IT0 it0, IT1 it1, IT2 it2) { + if (it2->second.empty()) { + it1->second.erase(it2); + if (it1->second.empty()) { + it0->second.erase(it1); + if (it0->second.empty()) { + map.erase(it0); + } + } + } +}; + +void findAsync_l(const MediaInfo &info, const std::shared_ptr &session, bool retry, const function &cb){ @@ -99,12 +233,17 @@ void MediaSource::findAsync(const MediaInfo &info, } DebugL << "收到媒体注册事件,回复播放器:" << info._schema << "/" << info._vhost << "/" << info._app << "/" << info._streamid; //再找一遍媒体源,一般能找到 - findAsync(info,strongSession,false,cb); + findAsync_l(info,strongSession,false,cb); }, false); }; //监听媒体注册事件 NoticeCenter::Instance().addListener(listener_tag, Broadcast::kBroadcastMediaChanged, onRegist); } + +void MediaSource::findAsync(const MediaInfo &info, const std::shared_ptr &session,const function &cb){ + return findAsync_l(info, session, true, cb); +} + MediaSource::Ptr MediaSource::find( const string &schema, const string &vhost_tmp, @@ -124,20 +263,19 @@ MediaSource::Ptr MediaSource::find( lock_guard lock(g_mtxMediaSrc); MediaSource::Ptr ret; //查找某一媒体源,找到后返回 - searchMedia(schema, vhost, app, id, - [&](SchemaVhostAppStreamMap::iterator &it0 , - VhostAppStreamMap::iterator &it1, - AppStreamMap::iterator &it2, - StreamMap::iterator &it3){ - ret = it3->second.lock(); - if(!ret){ - //该对象已经销毁 - it2->second.erase(it3); - eraseIfEmpty(it0,it1,it2); - return false; - } - return true; - }); + searchMedia(g_mapMediaSrc, schema, vhost, app, id, [&](SchemaVhostAppStreamMap::iterator &it0, + VhostAppStreamMap::iterator &it1, + AppStreamMap::iterator &it2, + StreamMap::iterator &it3) { + ret = it3->second.lock(); + if (!ret) { + //该对象已经销毁 + it2->second.erase(it3); + eraseIfEmpty(g_mapMediaSrc,it0, it1, it2); + return false; + } + return true; + }); if(!ret && bMake){ //未查找媒体源,则创建一个 ret = MediaReader::onMakeMediaSource(schema, vhost,app,id); @@ -166,17 +304,17 @@ void MediaSource::regist() { bool MediaSource::unregist() { //反注册该源 lock_guard lock(g_mtxMediaSrc); - return searchMedia(_strSchema, _strVhost, _strApp, _strId, [&](SchemaVhostAppStreamMap::iterator &it0 , - VhostAppStreamMap::iterator &it1, - AppStreamMap::iterator &it2, - StreamMap::iterator &it3){ + return searchMedia(g_mapMediaSrc, _strSchema, _strVhost, _strApp, _strId,[&](SchemaVhostAppStreamMap::iterator &it0, + VhostAppStreamMap::iterator &it1, + AppStreamMap::iterator &it2, + StreamMap::iterator &it3) { auto strongMedia = it3->second.lock(); - if(strongMedia && this != strongMedia.get()){ + if (strongMedia && this != strongMedia.get()) { //不是自己,不允许反注册 return false; } it2->second.erase(it3); - eraseIfEmpty(it0,it1,it2); + eraseIfEmpty(g_mapMediaSrc, it0, it1, it2); unregisted(); return true; }); @@ -192,6 +330,9 @@ void MediaSource::unregisted(){ *this); } + +/////////////////////////////////////MediaInfo////////////////////////////////////// + void MediaInfo::parse(const string &url){ //string url = "rtsp://127.0.0.1:8554/live/id?key=val&a=1&&b=2&vhost=vhost.com"; auto schema_pos = url.find("://"); @@ -241,6 +382,8 @@ void MediaInfo::parse(const string &url){ } } +/////////////////////////////////////MediaSourceEvent////////////////////////////////////// + void MediaSourceEvent::onNoneReader(MediaSource &sender){ //没有任何读取器消费该源,表明该源可以关闭了 WarnL << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId(); diff --git a/src/Common/MediaSource.h b/src/Common/MediaSource.h index 1e31b57b..69b77ccc 100644 --- a/src/Common/MediaSource.h +++ b/src/Common/MediaSource.h @@ -45,7 +45,7 @@ using namespace toolkit; namespace toolkit{ class TcpSession; -}//namespace toolkit +}// namespace toolkit namespace mediakit { @@ -54,17 +54,18 @@ class MediaSourceEvent{ public: MediaSourceEvent(){}; virtual ~MediaSourceEvent(){}; -public: + + // 通知拖动进度条 virtual bool seekTo(MediaSource &sender,uint32_t ui32Stamp){ - //拖动进度条 return false; } + // 通知其停止推流 virtual bool close(MediaSource &sender,bool force) { - //通知其停止推流 return false; } + // 通知无人观看 virtual void onNoneReader(MediaSource &sender); }; @@ -92,6 +93,9 @@ public: string _param_strs; }; +/** + * 媒体源,任何rtsp/rtmp的直播流都源自该对象 + */ class MediaSource: public TrackSource, public enable_shared_from_this { public: typedef std::shared_ptr Ptr; @@ -100,152 +104,59 @@ public: typedef unordered_map VhostAppStreamMap; typedef unordered_map SchemaVhostAppStreamMap; - MediaSource(const string &strSchema, - const string &strVhost, - const string &strApp, - const string &strId) : - _strSchema(strSchema), - _strApp(strApp), - _strId(strId) { - if(strVhost.empty()){ - _strVhost = DEFAULT_VHOST; - }else{ - _strVhost = strVhost; - } - } - virtual ~MediaSource() { - unregist(); - } + MediaSource(const string &strSchema, const string &strVhost, const string &strApp, const string &strId) ; + virtual ~MediaSource() ; - static Ptr find(const string &schema, - const string &vhost, - const string &app, - const string &id, - bool bMake = true) ; - - static void findAsync(const MediaInfo &info, - const std::shared_ptr &session, - bool retry, - const function &cb); - - const string& getSchema() const { - return _strSchema; - } - const string& getVhost() const { - return _strVhost; - } - const string& getApp() const { - //获取该源的id - return _strApp; - } - const string& getId() const { - return _strId; - } - - bool seekTo(uint32_t ui32Stamp) { - auto listener = _listener.lock(); - if(!listener){ - return false; - } - return listener->seekTo(*this,ui32Stamp); - } + // 获取协议类型 + const string& getSchema() const; + // 虚拟主机 + const string& getVhost() const; + // 应用名 + const string& getApp() const; + // 流id + const string& getId() const; + // 获取所有Track + vector getTracks(bool trackReady = true) const override; + // 获取监听者 + const std::weak_ptr& getListener() const; + // 设置TrackSource + void setTrackSource(const std::weak_ptr &track_src); + // 设置监听者 + virtual void setListener(const std::weak_ptr &listener); + // 获取观看者个数 + virtual int readerCount() = 0; + // 获取流当前时间戳 virtual uint32_t getTimeStamp(TrackType trackType) = 0; - bool close(bool force) { - auto listener = _listener.lock(); - if(!listener){ - return false; - } - return listener->close(*this,force); - } + // 拖动进度条 + bool seekTo(uint32_t ui32Stamp); + // 关闭该流 + bool close(bool force); + // 该流无人观看 + void onNoneReader(); - void onNoneReader(){ - auto listener = _listener.lock(); - if(!listener){ - return; - } - listener->onNoneReader(*this); - } + // 同步查找流 + static Ptr find(const string &schema, const string &vhost, const string &app, const string &id, bool bMake = true) ; + // 异步查找流 + static void findAsync(const MediaInfo &info, const std::shared_ptr &session, const function &cb); + // 遍历所有流 + static void for_each_media(const function &cb); - virtual void setListener(const std::weak_ptr &listener){ - _listener = listener; - } - - std::weak_ptr getListener(){ - return _listener; - } - - template - static void for_each_media(FUN && fun){ - lock_guard lock(g_mtxMediaSrc); - for (auto &pr0 : g_mapMediaSrc){ - for(auto &pr1 : pr0.second){ - for(auto &pr2 : pr1.second){ - for(auto &pr3 : pr2.second){ - fun(pr0.first,pr1.first,pr2.first,pr3.first,pr3.second.lock()); - } - } - } - } - } - - virtual int readerCount() = 0; protected: void regist() ; bool unregist() ; -private: - template - static bool searchMedia(const string &schema, - const string &vhost, - const string &app, - const string &id, - FUN &&fun){ - auto it0 = g_mapMediaSrc.find(schema); - if (it0 == g_mapMediaSrc.end()) { - //未找到协议 - return false; - } - auto it1 = it0->second.find(vhost); - if(it1 == it0->second.end()){ - //未找到vhost - return false; - } - auto it2 = it1->second.find(app); - if(it2 == it1->second.end()){ - //未找到app - return false; - } - auto it3 = it2->second.find(id); - if(it3 == it2->second.end()){ - //未找到streamId - return false; - } - return fun(it0,it1,it2,it3); - } - template - static void eraseIfEmpty(IT0 it0,IT1 it1,IT2 it2){ - if(it2->second.empty()){ - it1->second.erase(it2); - if(it1->second.empty()){ - it0->second.erase(it1); - if(it0->second.empty()){ - g_mapMediaSrc.erase(it0); - } - } - } - }; - void unregisted(); -protected: - std::weak_ptr _listener; + private: - string _strSchema;//协议类型 - string _strVhost; //vhost - string _strApp; //媒体app - string _strId; //媒体id - static SchemaVhostAppStreamMap g_mapMediaSrc; //静态的媒体源表 - static recursive_mutex g_mtxMediaSrc; //访问静态的媒体源表的互斥锁 + string _strSchema; + string _strVhost; + string _strApp; + string _strId; + std::weak_ptr _listener; + weak_ptr _track_source; + static SchemaVhostAppStreamMap g_mapMediaSrc; + static recursive_mutex g_mtxMediaSrc; }; } /* namespace mediakit */ diff --git a/src/Common/MultiMediaSourceMuxer.h b/src/Common/MultiMediaSourceMuxer.h index 4bf40c85..e1723309 100644 --- a/src/Common/MultiMediaSourceMuxer.h +++ b/src/Common/MultiMediaSourceMuxer.h @@ -31,7 +31,7 @@ #include "Rtmp/RtmpMediaSourceMuxer.h" #include "MediaFile/MediaRecorder.h" -class MultiMediaSourceMuxer : public MediaSink{ +class MultiMediaSourceMuxer : public MediaSink , public std::enable_shared_from_this{ public: typedef std::shared_ptr Ptr; @@ -128,9 +128,11 @@ protected: */ void onAllTrackReady() override{ if(_rtmp) { + _rtmp->setTrackSource(shared_from_this()); _rtmp->onAllTrackReady(); } if(_rtsp) { + _rtmp->setTrackSource(shared_from_this()); _rtsp->onAllTrackReady(); } } diff --git a/src/Extension/Track.h b/src/Extension/Track.h index b4771027..229a13ea 100644 --- a/src/Extension/Track.h +++ b/src/Extension/Track.h @@ -141,9 +141,7 @@ public: * @param trackReady 是否获取全部已经准备好的Track * @return */ - virtual vector getTracks(bool trackReady = true) const { - return vector(); - } + virtual vector getTracks(bool trackReady = true) const = 0; /** * 获取特定Track diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp index 1c36e5cc..88fa99e0 100644 --- a/src/Http/HttpSession.cpp +++ b/src/Http/HttpSession.cpp @@ -197,7 +197,7 @@ bool HttpSession::checkLiveFlvStream(const function &cb){ bool bClose = !strcasecmp(_parser["Connection"].data(),"close"); weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - MediaSource::findAsync(_mediaInfo,weakSelf.lock(), true,[weakSelf,bClose,this,cb](const MediaSource::Ptr &src){ + MediaSource::findAsync(_mediaInfo,weakSelf.lock(),[weakSelf,bClose,this,cb](const MediaSource::Ptr &src){ auto strongSelf = weakSelf.lock(); if(!strongSelf){ //本对象已经销毁 diff --git a/src/Player/MediaPlayer.cpp b/src/Player/MediaPlayer.cpp index 73c04d54..53365d1a 100644 --- a/src/Player/MediaPlayer.cpp +++ b/src/Player/MediaPlayer.cpp @@ -42,13 +42,13 @@ MediaPlayer::MediaPlayer(const EventPoller::Ptr &poller) { MediaPlayer::~MediaPlayer() { } void MediaPlayer::play(const string &strUrl) { - _parser = PlayerBase::createPlayer(_poller,strUrl); - _parser->setOnShutdown(_shutdownCB); - _parser->setOnPlayResult(_playResultCB); - _parser->setOnResume(_resumeCB); - _parser->setMediaSouce(_pMediaSrc); - _parser->mINI::operator=(*this); - _parser->play(strUrl); + _delegate = PlayerBase::createPlayer(_poller,strUrl); + _delegate->setOnShutdown(_shutdownCB); + _delegate->setOnPlayResult(_playResultCB); + _delegate->setOnResume(_resumeCB); + _delegate->setMediaSouce(_pMediaSrc); + _delegate->mINI::operator=(*this); + _delegate->play(strUrl); } EventPoller::Ptr MediaPlayer::getPoller(){ @@ -56,14 +56,14 @@ EventPoller::Ptr MediaPlayer::getPoller(){ } void MediaPlayer::pause(bool bPause) { - if (_parser) { - _parser->pause(bPause); + if (_delegate) { + _delegate->pause(bPause); } } void MediaPlayer::teardown() { - if (_parser) { - _parser->teardown(); + if (_delegate) { + _delegate->teardown(); } } diff --git a/src/Player/PlayerBase.h b/src/Player/PlayerBase.h index 54aa0f3c..7e8f55ff 100644 --- a/src/Player/PlayerBase.h +++ b/src/Player/PlayerBase.h @@ -127,6 +127,13 @@ public: * @return */ virtual float getPacketLossRate(TrackType trackType) const {return 0; } + + /** + * 获取所有track + */ + vector getTracks(bool trackReady = true) const override{ + return vector(); + } protected: virtual void onShutdown(const SockException &ex) {} virtual void onPlayResult(const SockException &ex) {} @@ -136,9 +143,8 @@ protected: virtual void onResume(){}; }; -template -class PlayerImp : public Parent -{ +template +class PlayerImp : public Parent { public: typedef std::shared_ptr Ptr; @@ -147,62 +153,62 @@ public: virtual ~PlayerImp(){} void setOnShutdown(const function &cb) override { - if (_parser) { - _parser->setOnShutdown(cb); + if (_delegate) { + _delegate->setOnShutdown(cb); } _shutdownCB = cb; } void setOnPlayResult(const function &cb) override { - if (_parser) { - _parser->setOnPlayResult(cb); + if (_delegate) { + _delegate->setOnPlayResult(cb); } _playResultCB = cb; } void setOnResume(const function &cb) override { - if (_parser) { - _parser->setOnResume(cb); + if (_delegate) { + _delegate->setOnResume(cb); } _resumeCB = cb; } bool isInited(int analysisMs) override{ - if (_parser) { - return _parser->isInited(analysisMs); + if (_delegate) { + return _delegate->isInited(analysisMs); } - return PlayerBase::isInited(analysisMs); + return Parent::isInited(analysisMs); } float getDuration() const override { - if (_parser) { - return _parser->getDuration(); + if (_delegate) { + return _delegate->getDuration(); } - return PlayerBase::getDuration(); + return Parent::getDuration(); } float getProgress() const override{ - if (_parser) { - return _parser->getProgress(); + if (_delegate) { + return _delegate->getProgress(); } - return PlayerBase::getProgress(); + return Parent::getProgress(); } void seekTo(float fProgress) override{ - if (_parser) { - return _parser->seekTo(fProgress); + if (_delegate) { + return _delegate->seekTo(fProgress); } - return PlayerBase::seekTo(fProgress); + return Parent::seekTo(fProgress); } void setMediaSouce(const MediaSource::Ptr & src) override { - if (_parser) { - _parser->setMediaSouce(src); + if (_delegate) { + _delegate->setMediaSouce(src); } _pMediaSrc = src; } vector getTracks(bool trackReady = true) const override{ - if (_parser) { - return _parser->getTracks(trackReady); + if (_delegate) { + return _delegate->getTracks(trackReady); } - return PlayerBase::getTracks(trackReady); + return Parent::getTracks(trackReady); } protected: void onShutdown(const SockException &ex) override { @@ -228,7 +234,7 @@ protected: function _shutdownCB; function _playResultCB; function _resumeCB; - std::shared_ptr _parser; + std::shared_ptr _delegate; MediaSource::Ptr _pMediaSrc; }; diff --git a/src/Player/PlayerProxy.cpp b/src/Player/PlayerProxy.cpp index 075394c9..766db316 100644 --- a/src/Player/PlayerProxy.cpp +++ b/src/Player/PlayerProxy.cpp @@ -138,13 +138,13 @@ void PlayerProxy::play(const string &strUrlTmp) { MediaPlayer::play(strUrlTmp); MediaSource::Ptr mediaSource; - if(dynamic_pointer_cast(_parser)){ + if(dynamic_pointer_cast(_delegate)){ //rtsp拉流 GET_CONFIG(bool,directProxy,Rtsp::kDirectProxy); if(directProxy && _bEnableRtsp){ mediaSource = std::make_shared(_strVhost,_strApp,_strSrc); } - } else if(dynamic_pointer_cast(_parser)){ + } else if(dynamic_pointer_cast(_delegate)){ //rtmp拉流 if(_bEnableRtmp){ mediaSource = std::make_shared(_strVhost,_strApp,_strSrc); diff --git a/src/Rtmp/RtmpMediaSourceMuxer.h b/src/Rtmp/RtmpMediaSourceMuxer.h index 2f8e81cc..55112dea 100644 --- a/src/Rtmp/RtmpMediaSourceMuxer.h +++ b/src/Rtmp/RtmpMediaSourceMuxer.h @@ -56,6 +56,11 @@ public: void onAllTrackReady(){ _mediaSouce->onGetMetaData(getMetadata()); } + + // 设置TrackSource + void setTrackSource(const std::weak_ptr &track_src){ + _mediaSouce->setTrackSource(track_src); + } private: RtmpMediaSource::Ptr _mediaSouce; }; diff --git a/src/Rtmp/RtmpPlayerImp.h b/src/Rtmp/RtmpPlayerImp.h index 49e8da6a..b170a19f 100644 --- a/src/Rtmp/RtmpPlayerImp.h +++ b/src/Rtmp/RtmpPlayerImp.h @@ -67,18 +67,18 @@ private: if(_pRtmpMediaSrc){ _pRtmpMediaSrc->onGetMetaData(val); } - _parser.reset(new RtmpDemuxer(val)); + _delegate.reset(new RtmpDemuxer(val)); return true; } void onMediaData(const RtmpPacket::Ptr &chunkData) override { if(_pRtmpMediaSrc){ _pRtmpMediaSrc->onWrite(chunkData); } - if(!_parser){ + if(!_delegate){ //这个流没有metadata - _parser.reset(new RtmpDemuxer()); + _delegate.reset(new RtmpDemuxer()); } - _parser->inputRtmp(chunkData); + _delegate->inputRtmp(chunkData); } private: RtmpMediaSource::Ptr _pRtmpMediaSrc; diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index 671ff101..a9f62916 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -319,7 +319,7 @@ void RtmpSession::doPlayResponse(const string &err,const std::function weakSelf = dynamic_pointer_cast(shared_from_this()); - MediaSource::findAsync(_mediaInfo,weakSelf.lock(), true,[weakSelf,cb](const MediaSource::Ptr &src){ + MediaSource::findAsync(_mediaInfo,weakSelf.lock(),[weakSelf,cb](const MediaSource::Ptr &src){ auto rtmp_src = dynamic_pointer_cast(src); auto strongSelf = weakSelf.lock(); if(strongSelf){ diff --git a/src/Rtmp/RtmpToRtspMediaSource.h b/src/Rtmp/RtmpToRtspMediaSource.h index 4d7c864b..31df804f 100644 --- a/src/Rtmp/RtmpToRtspMediaSource.h +++ b/src/Rtmp/RtmpToRtspMediaSource.h @@ -52,7 +52,8 @@ public: RtmpToRtspMediaSource(const string &vhost, const string &app, const string &id, - int ringSize = 0) : RtmpMediaSource(vhost, app, id,ringSize){ + int ringSize = 0) : + RtmpMediaSource(vhost, app, id,ringSize){ } virtual ~RtmpToRtspMediaSource(){} @@ -83,7 +84,7 @@ public: _muxer->addTrack(track); track->addDelegate(_muxer); } - _muxer->setListener(_listener); + _muxer->setListener(getListener()); } RtmpMediaSource::onWrite(pkt,key_pos); } diff --git a/src/Rtsp/RtspMediaSourceMuxer.h b/src/Rtsp/RtspMediaSourceMuxer.h index 9c496bd6..050e83c2 100644 --- a/src/Rtsp/RtspMediaSourceMuxer.h +++ b/src/Rtsp/RtspMediaSourceMuxer.h @@ -60,6 +60,11 @@ public: void onAllTrackReady(){ _mediaSouce->onGetSDP(getSdp()); } + + // 设置TrackSource + void setTrackSource(const std::weak_ptr &track_src){ + _mediaSouce->setTrackSource(track_src); + } private: RtspMediaSource::Ptr _mediaSouce; }; diff --git a/src/Rtsp/RtspPlayerImp.h b/src/Rtsp/RtspPlayerImp.h index 0bf77c34..8992bded 100644 --- a/src/Rtsp/RtspPlayerImp.h +++ b/src/Rtsp/RtspPlayerImp.h @@ -66,16 +66,16 @@ private: if(_pRtspMediaSrc){ _pRtspMediaSrc->onGetSDP(sdp); } - _parser.reset(new RtspDemuxer(sdp)); + _delegate.reset(new RtspDemuxer(sdp)); return true; } void onRecvRTP(const RtpPacket::Ptr &rtp, const SdpTrack::Ptr &track) override { if(_pRtspMediaSrc){ _pRtspMediaSrc->onWrite(rtp,true); } - _parser->inputRtp(rtp); + _delegate->inputRtp(rtp); - if(_maxAnalysisMS && _parser->isInited(_maxAnalysisMS)){ + if(_maxAnalysisMS && _delegate->isInited(_maxAnalysisMS)){ PlayerImp::onPlayResult(SockException(Err_success,"play rtsp success")); _maxAnalysisMS = 0; } @@ -87,7 +87,7 @@ private: //如果超过这个时间还未获取成功,那么会强制触发onPlayResult事件(虽然此时有些track还未初始化成功) void onPlayResult(const SockException &ex) override { //isInited判断条件:无超时 - if(ex || _parser->isInited(0)){ + if(ex || _delegate->isInited(0)){ //已经初始化成功,说明sdp里面有完善的信息 PlayerImp::onPlayResult(ex); }else{ diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index cb138308..1d679e23 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -371,7 +371,7 @@ void RtspSession::handleReq_Describe(const Parser &parser) { void RtspSession::onAuthSuccess() { TraceP(this); weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - MediaSource::findAsync(_mediaInfo,weakSelf.lock(), true,[weakSelf](const MediaSource::Ptr &src){ + MediaSource::findAsync(_mediaInfo,weakSelf.lock(),[weakSelf](const MediaSource::Ptr &src){ auto strongSelf = weakSelf.lock(); if(!strongSelf){ return; diff --git a/src/Rtsp/RtspToRtmpMediaSource.h b/src/Rtsp/RtspToRtmpMediaSource.h index a05cb393..c4cdaf18 100644 --- a/src/Rtsp/RtspToRtmpMediaSource.h +++ b/src/Rtsp/RtspToRtmpMediaSource.h @@ -43,7 +43,8 @@ public: RtspToRtmpMediaSource(const string &vhost, const string &app, const string &id, - int ringSize = 0) : RtspMediaSource(vhost, app, id,ringSize) { + int ringSize = 0) + : RtspMediaSource(vhost, app, id,ringSize) { } virtual ~RtspToRtmpMediaSource() {} @@ -69,7 +70,7 @@ public: _muxer->addTrack(track); track->addDelegate(_muxer); } - _muxer->setListener(_listener); + _muxer->setListener(getListener()); } } RtspMediaSource::onWrite(rtp, bKeyPos); diff --git a/src/Shell/ShellCMD.h b/src/Shell/ShellCMD.h index eef6b653..e3a44901 100644 --- a/src/Shell/ShellCMD.h +++ b/src/Shell/ShellCMD.h @@ -16,39 +16,35 @@ class CMD_media: public CMD { public: CMD_media(){ _parser.reset(new OptionParser([](const std::shared_ptr &stream,mINI &ini){ - MediaSource::for_each_media([&](const string &schema, - const string &vhost, - const string &app, - const string &streamid, - const MediaSource::Ptr &media){ - if(!ini["schema"].empty() && ini["schema"] != schema){ + MediaSource::for_each_media([&](const MediaSource::Ptr &media){ + if(!ini["schema"].empty() && ini["schema"] != media->getSchema()){ //筛选协议不匹配 return; } - if(!ini["vhost"].empty() && ini["vhost"] != vhost){ + if(!ini["vhost"].empty() && ini["vhost"] != media->getVhost()){ //筛选虚拟主机不匹配 return; } - if(!ini["app"].empty() && ini["app"] != app){ + if(!ini["app"].empty() && ini["app"] != media->getApp()){ //筛选应用名不匹配 return; } - if(!ini["stream"].empty() && ini["stream"] != streamid){ + if(!ini["stream"].empty() && ini["stream"] != media->getId()){ //流id不匹配 return; } if(ini.find("list") != ini.end()){ //列出源 (*stream) << "\t" - << schema << "/" - << vhost << "/" - << app << "/" - << streamid + << media->getSchema() << "/" + << media->getVhost() << "/" + << media->getApp() << "/" + << media->getId() << "\r\n"; return; } - EventPollerPool::Instance().getPoller()->async([ini,media,stream,schema,vhost,app,streamid](){ + EventPollerPool::Instance().getPoller()->async([ini,media,stream](){ if(ini.find("kick") != ini.end()){ //踢出源 do{ @@ -59,18 +55,18 @@ public: break; } (*stream) << "\t踢出成功:" - << schema << "/" - << vhost << "/" - << app << "/" - << streamid + << media->getSchema() << "/" + << media->getVhost() << "/" + << media->getApp() << "/" + << media->getId() << "\r\n"; return; }while(0); (*stream) << "\t踢出失败:" - << schema << "/" - << vhost << "/" - << app << "/" - << streamid + << media->getSchema() << "/" + << media->getVhost() << "/" + << media->getApp() << "/" + << media->getId() << "\r\n"; } },false); From 2c2e7262d6f79caa16485f884293482ce22b84de Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 3 Dec 2019 16:14:52 +0800 Subject: [PATCH 056/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/MultiMediaSourceMuxer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common/MultiMediaSourceMuxer.h b/src/Common/MultiMediaSourceMuxer.h index e1723309..cbe6e75c 100644 --- a/src/Common/MultiMediaSourceMuxer.h +++ b/src/Common/MultiMediaSourceMuxer.h @@ -132,7 +132,7 @@ protected: _rtmp->onAllTrackReady(); } if(_rtsp) { - _rtmp->setTrackSource(shared_from_this()); + _rtsp->setTrackSource(shared_from_this()); _rtsp->onAllTrackReady(); } } From de33d6a8474de3b5ad21e0b44d6f443752d79786 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 4 Dec 2019 10:45:38 +0800 Subject: [PATCH 057/272] =?UTF-8?q?=E6=95=B4=E7=90=86=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=BD=95=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebHook.cpp | 1 + src/Common/MediaSink.h | 2 + src/Common/MediaSource.cpp | 4 +- src/Common/MultiMediaSourceMuxer.h | 36 +++-- src/{MediaFile => Common}/Stamp.cpp | 0 src/{MediaFile => Common}/Stamp.h | 0 src/MediaFile/MediaRecorder.cpp | 133 ------------------ src/{MediaFile => Record}/HlsMaker.cpp | 0 src/{MediaFile => Record}/HlsMaker.h | 0 src/{MediaFile => Record}/HlsMakerImp.cpp | 0 src/{MediaFile => Record}/HlsMakerImp.h | 0 src/{MediaFile => Record}/HlsRecorder.h | 18 ++- src/{MediaFile => Record}/MP4Muxer.cpp | 0 src/{MediaFile => Record}/MP4Muxer.h | 2 +- .../MediaReader.cpp => Record/MP4Reader.cpp} | 39 ++--- .../MediaReader.h => Record/MP4Reader.h} | 16 +-- src/{MediaFile => Record}/MP4Recorder.cpp | 0 src/{MediaFile => Record}/MP4Recorder.h | 0 src/Record/Recorder.cpp | 95 +++++++++++++ .../MediaRecorder.h => Record/Recorder.h} | 54 ++----- src/{MediaFile => Record}/TsMuxer.cpp | 0 src/{MediaFile => Record}/TsMuxer.h | 2 +- src/Rtmp/FlvMuxer.h | 2 +- src/Rtmp/RtmpSession.h | 2 +- src/Rtsp/RtspPlayer.h | 2 +- src/Rtsp/RtspSession.h | 1 + 26 files changed, 187 insertions(+), 222 deletions(-) rename src/{MediaFile => Common}/Stamp.cpp (100%) rename src/{MediaFile => Common}/Stamp.h (100%) delete mode 100644 src/MediaFile/MediaRecorder.cpp rename src/{MediaFile => Record}/HlsMaker.cpp (100%) rename src/{MediaFile => Record}/HlsMaker.h (100%) rename src/{MediaFile => Record}/HlsMakerImp.cpp (100%) rename src/{MediaFile => Record}/HlsMakerImp.h (100%) rename src/{MediaFile => Record}/HlsRecorder.h (75%) rename src/{MediaFile => Record}/MP4Muxer.cpp (100%) rename src/{MediaFile => Record}/MP4Muxer.h (99%) rename src/{MediaFile/MediaReader.cpp => Record/MP4Reader.cpp} (90%) rename src/{MediaFile/MediaReader.h => Record/MP4Reader.h} (86%) rename src/{MediaFile => Record}/MP4Recorder.cpp (100%) rename src/{MediaFile => Record}/MP4Recorder.h (100%) create mode 100644 src/Record/Recorder.cpp rename src/{MediaFile/MediaRecorder.h => Record/Recorder.h} (53%) rename src/{MediaFile => Record}/TsMuxer.cpp (100%) rename src/{MediaFile => Record}/TsMuxer.h (98%) diff --git a/server/WebHook.cpp b/server/WebHook.cpp index e251f24b..b6283025 100644 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -39,6 +39,7 @@ #include "Rtsp/RtspSession.h" #include "Http/HttpSession.h" #include "WebHook.h" +#include "Record/MP4Recorder.h" using namespace Json; using namespace toolkit; diff --git a/src/Common/MediaSink.h b/src/Common/MediaSink.h index 0f7fc92a..64317385 100644 --- a/src/Common/MediaSink.h +++ b/src/Common/MediaSink.h @@ -40,6 +40,8 @@ namespace mediakit{ class MediaSinkInterface : public FrameWriterInterface { public: + typedef std::shared_ptr Ptr; + MediaSinkInterface(){}; virtual ~MediaSinkInterface(){}; diff --git a/src/Common/MediaSource.cpp b/src/Common/MediaSource.cpp index 96b63332..d489a217 100644 --- a/src/Common/MediaSource.cpp +++ b/src/Common/MediaSource.cpp @@ -26,7 +26,7 @@ #include "MediaSource.h" -#include "MediaFile/MediaReader.h" +#include "Record/MP4Reader.h" #include "Util/util.h" #include "Network/sockutil.h" #include "Network/TcpSession.h" @@ -278,7 +278,7 @@ MediaSource::Ptr MediaSource::find( }); if(!ret && bMake){ //未查找媒体源,则创建一个 - ret = MediaReader::onMakeMediaSource(schema, vhost,app,id); + ret = MP4Reader::onMakeMediaSource(schema, vhost,app,id); } return ret; } diff --git a/src/Common/MultiMediaSourceMuxer.h b/src/Common/MultiMediaSourceMuxer.h index cbe6e75c..431f609c 100644 --- a/src/Common/MultiMediaSourceMuxer.h +++ b/src/Common/MultiMediaSourceMuxer.h @@ -29,7 +29,7 @@ #include "Rtsp/RtspMediaSourceMuxer.h" #include "Rtmp/RtmpMediaSourceMuxer.h" -#include "MediaFile/MediaRecorder.h" +#include "Record/Recorder.h" class MultiMediaSourceMuxer : public MediaSink , public std::enable_shared_from_this{ public: @@ -42,15 +42,19 @@ public: bool bEanbleRtsp = true, bool bEanbleRtmp = true, bool bEanbleHls = true, - bool bEnableMp4 = false - ){ + bool bEnableMp4 = false){ if (bEanbleRtmp) { _rtmp = std::make_shared(vhost, strApp, strId, std::make_shared(dur_sec)); } if (bEanbleRtsp) { _rtsp = std::make_shared(vhost, strApp, strId, std::make_shared(dur_sec)); } - _record = std::make_shared(vhost,strApp,strId,bEanbleHls,bEnableMp4); + if(bEanbleHls){ + _hls.reset(Recorder::createHlsRecorder(vhost, strApp, strId)); + } + if(bEnableMp4){ + _mp4.reset(Recorder::createMP4Recorder(vhost, strApp, strId)); + } } virtual ~MultiMediaSourceMuxer(){} @@ -64,7 +68,12 @@ public: if(_rtsp){ _rtsp->resetTracks(); } - _record->resetTracks(); + if(_hls){ + _hls->resetTracks(); + } + if(_mp4){ + _mp4->resetTracks(); + } } /** @@ -106,7 +115,12 @@ protected: if(_rtsp){ _rtsp->addTrack(track); } - _record->addTrack(track); + if(_hls){ + _hls->addTrack(track); + } + if(_mp4){ + _mp4->addTrack(track); + } } /** @@ -120,7 +134,12 @@ protected: if(_rtsp) { _rtsp->inputFrame(frame); } - _record->inputFrame(frame); + if(_hls){ + _hls->inputFrame(frame); + } + if(_mp4){ + _mp4->inputFrame(frame); + } } /** @@ -139,7 +158,8 @@ protected: private: RtmpMediaSourceMuxer::Ptr _rtmp; RtspMediaSourceMuxer::Ptr _rtsp; - MediaRecorder::Ptr _record; + MediaSinkInterface::Ptr _hls; + MediaSinkInterface::Ptr _mp4; }; diff --git a/src/MediaFile/Stamp.cpp b/src/Common/Stamp.cpp similarity index 100% rename from src/MediaFile/Stamp.cpp rename to src/Common/Stamp.cpp diff --git a/src/MediaFile/Stamp.h b/src/Common/Stamp.h similarity index 100% rename from src/MediaFile/Stamp.h rename to src/Common/Stamp.h diff --git a/src/MediaFile/MediaRecorder.cpp b/src/MediaFile/MediaRecorder.cpp deleted file mode 100644 index e422b6e5..00000000 --- a/src/MediaFile/MediaRecorder.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> - * - * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "MediaRecorder.h" -#include "Common/config.h" -#include "Http/HttpSession.h" -#include "Util/util.h" -#include "Util/mini.h" -#include "Network/sockutil.h" -#include "HlsMakerImp.h" -using namespace toolkit; - -namespace mediakit { - -MediaRecorder::MediaRecorder(const string &strVhost_tmp, - const string &strApp, - const string &strId, - bool enableHls, - bool enableMp4) { - - GET_CONFIG(string,hlsPath,Hls::kFilePath); - GET_CONFIG(uint32_t,hlsBufSize,Hls::kFileBufSize); - GET_CONFIG(uint32_t,hlsDuration,Hls::kSegmentDuration); - GET_CONFIG(uint32_t,hlsNum,Hls::kSegmentNum); - GET_CONFIG(bool,enableVhost,General::kEnableVhost); - - string strVhost = strVhost_tmp; - if(trim(strVhost).empty()){ - //如果strVhost为空,则强制为默认虚拟主机 - strVhost = DEFAULT_VHOST; - } - -#if defined(ENABLE_HLS) - if(enableHls) { - string m3u8FilePath; - string params; - if(enableVhost){ - m3u8FilePath = strVhost + "/" + strApp + "/" + strId + "/hls.m3u8"; - params = string(VHOST_KEY) + "=" + strVhost; - }else{ - m3u8FilePath = strApp + "/" + strId + "/hls.m3u8"; - } - m3u8FilePath = File::absolutePath(m3u8FilePath,hlsPath); - _hlsRecorder.reset(new HlsRecorder(m3u8FilePath,params,hlsBufSize, hlsDuration, hlsNum)); - } -#endif //defined(ENABLE_HLS) - -#if defined(ENABLE_MP4RECORD) - GET_CONFIG(string,recordPath,Record::kFilePath); - GET_CONFIG(string,recordAppName,Record::kAppName); - - if(enableMp4){ - string mp4FilePath; - if(enableVhost){ - mp4FilePath = strVhost + "/" + recordAppName + "/" + strApp + "/" + strId + "/"; - } else { - mp4FilePath = recordAppName + "/" + strApp + "/" + strId + "/"; - } - mp4FilePath = File::absolutePath(mp4FilePath,recordPath); - _mp4Recorder.reset(new MP4Recorder(mp4FilePath,strVhost,strApp,strId)); - } -#endif //defined(ENABLE_MP4RECORD) -} - -MediaRecorder::~MediaRecorder() { -} - -void MediaRecorder::inputFrame(const Frame::Ptr &frame) { -#if defined(ENABLE_HLS) - if (_hlsRecorder) { - _hlsRecorder->inputFrame(frame); - } -#endif //defined(ENABLE_HLS) - -#if defined(ENABLE_MP4RECORD) - if (_mp4Recorder) { - _mp4Recorder->inputFrame(frame); - } -#endif //defined(ENABLE_MP4RECORD) -} - -void MediaRecorder::addTrack(const Track::Ptr &track) { -#if defined(ENABLE_HLS) - if (_hlsRecorder) { - _hlsRecorder->addTrack(track); - } -#endif //defined(ENABLE_HLS) - -#if defined(ENABLE_MP4RECORD) - if (_mp4Recorder) { - _mp4Recorder->addTrack(track); - } -#endif //defined(ENABLE_MP4RECORD) -} - -void MediaRecorder::resetTracks() { -#if defined(ENABLE_HLS) - if (_hlsRecorder) { - _hlsRecorder->resetTracks(); - } -#endif //defined(ENABLE_HLS) - -#if defined(ENABLE_MP4RECORD) - if (_mp4Recorder) { - _mp4Recorder->resetTracks(); - } -#endif //defined(ENABLE_MP4RECORD) -} - -} /* namespace mediakit */ diff --git a/src/MediaFile/HlsMaker.cpp b/src/Record/HlsMaker.cpp similarity index 100% rename from src/MediaFile/HlsMaker.cpp rename to src/Record/HlsMaker.cpp diff --git a/src/MediaFile/HlsMaker.h b/src/Record/HlsMaker.h similarity index 100% rename from src/MediaFile/HlsMaker.h rename to src/Record/HlsMaker.h diff --git a/src/MediaFile/HlsMakerImp.cpp b/src/Record/HlsMakerImp.cpp similarity index 100% rename from src/MediaFile/HlsMakerImp.cpp rename to src/Record/HlsMakerImp.cpp diff --git a/src/MediaFile/HlsMakerImp.h b/src/Record/HlsMakerImp.h similarity index 100% rename from src/MediaFile/HlsMakerImp.h rename to src/Record/HlsMakerImp.h diff --git a/src/MediaFile/HlsRecorder.h b/src/Record/HlsRecorder.h similarity index 75% rename from src/MediaFile/HlsRecorder.h rename to src/Record/HlsRecorder.h index ca20c5f0..c0def9bb 100644 --- a/src/MediaFile/HlsRecorder.h +++ b/src/Record/HlsRecorder.h @@ -32,15 +32,23 @@ namespace mediakit { -class HlsRecorder : public HlsMakerImp, public TsMuxer { +class HlsRecorder : public TsMuxer { public: - template - HlsRecorder(ArgsType &&...args):HlsMakerImp(std::forward(args)...){} - ~HlsRecorder(){}; + HlsRecorder(const string &m3u8_file, const string ¶ms){ + GET_CONFIG(uint32_t,hlsNum,Hls::kSegmentNum); + GET_CONFIG(uint32_t,hlsBufSize,Hls::kFileBufSize); + GET_CONFIG(uint32_t,hlsDuration,Hls::kSegmentDuration); + _hls = new HlsMakerImp(m3u8_file,params,hlsBufSize,hlsDuration,hlsNum); + } + ~HlsRecorder(){ + delete _hls; + } protected: void onTs(const void *packet, int bytes,uint32_t timestamp,int flags) override { - inputData((char *)packet,bytes,timestamp); + _hls->inputData((char *)packet,bytes,timestamp); }; +private: + HlsMakerImp *_hls; }; }//namespace mediakit diff --git a/src/MediaFile/MP4Muxer.cpp b/src/Record/MP4Muxer.cpp similarity index 100% rename from src/MediaFile/MP4Muxer.cpp rename to src/Record/MP4Muxer.cpp diff --git a/src/MediaFile/MP4Muxer.h b/src/Record/MP4Muxer.h similarity index 99% rename from src/MediaFile/MP4Muxer.h rename to src/Record/MP4Muxer.h index 1532d87e..401431d2 100644 --- a/src/MediaFile/MP4Muxer.h +++ b/src/Record/MP4Muxer.h @@ -39,7 +39,7 @@ #include "Extension/AAC.h" #include "Extension/H264.h" #include "Extension/H265.h" -#include "Stamp.h" +#include "Common/Stamp.h" namespace mediakit{ diff --git a/src/MediaFile/MediaReader.cpp b/src/Record/MP4Reader.cpp similarity index 90% rename from src/MediaFile/MediaReader.cpp rename to src/Record/MP4Reader.cpp index eae430bd..74521fd3 100644 --- a/src/MediaFile/MediaReader.cpp +++ b/src/Record/MP4Reader.cpp @@ -24,9 +24,10 @@ * SOFTWARE. */ -#include "MediaReader.h" +#include "MP4Reader.h" #include "Common/config.h" #include "Util/mini.h" +#include "Util/File.h" #include "Http/HttpSession.h" #include "Extension/AAC.h" #include "Extension/H264.h" @@ -37,7 +38,7 @@ using namespace toolkit; namespace mediakit { #ifdef ENABLE_MP4V2 -MediaReader::MediaReader(const string &strVhost,const string &strApp, const string &strId,const string &filePath ) { +MP4Reader::MP4Reader(const string &strVhost,const string &strApp, const string &strId,const string &filePath ) { _poller = WorkThreadPool::Instance().getPoller(); auto strFileName = filePath; if(strFileName.empty()){ @@ -153,7 +154,7 @@ MediaReader::MediaReader(const string &strVhost,const string &strApp, const stri } -MediaReader::~MediaReader() { +MP4Reader::~MP4Reader() { if (_hMP4File != MP4_INVALID_FILE_HANDLE) { MP4Close(_hMP4File); _hMP4File = MP4_INVALID_FILE_HANDLE; @@ -161,7 +162,7 @@ MediaReader::~MediaReader() { } -void MediaReader::startReadMP4() { +void MP4Reader::startReadMP4() { auto strongSelf = shared_from_this(); GET_CONFIG(uint32_t,sampleMS,Record::kSampleMS); @@ -173,11 +174,11 @@ void MediaReader::startReadMP4() { readSample(sampleMS, false); _mediaMuxer->setListener(strongSelf); } - bool MediaReader::seekTo(MediaSource &sender,uint32_t ui32Stamp){ + bool MP4Reader::seekTo(MediaSource &sender,uint32_t ui32Stamp){ seek(ui32Stamp); return true; } -bool MediaReader::close(MediaSource &sender,bool force){ +bool MP4Reader::close(MediaSource &sender,bool force){ if(!_mediaMuxer || (!force && _mediaMuxer->readerCount() != 0)){ return false; } @@ -186,14 +187,14 @@ bool MediaReader::close(MediaSource &sender,bool force){ return true; } -void MediaReader::onNoneReader(MediaSource &sender) { +void MP4Reader::onNoneReader(MediaSource &sender) { if(!_mediaMuxer || _mediaMuxer->readerCount() != 0){ return; } MediaSourceEvent::onNoneReader(sender); } -bool MediaReader::readSample(int iTimeInc,bool justSeekSyncFrame) { +bool MP4Reader::readSample(int iTimeInc,bool justSeekSyncFrame) { TimeTicker(); lock_guard lck(_mtx); auto bFlag0 = readVideoSample(iTimeInc,justSeekSyncFrame);//数据没读完 @@ -211,7 +212,7 @@ bool MediaReader::readSample(int iTimeInc,bool justSeekSyncFrame) { //3秒延时关闭 return _alive.elapsedTime() < 3 * 1000; } -inline bool MediaReader::readVideoSample(int iTimeInc,bool justSeekSyncFrame) { +inline bool MP4Reader::readVideoSample(int iTimeInc,bool justSeekSyncFrame) { if (_video_trId != MP4_INVALID_TRACK_ID) { auto iNextSample = getVideoSampleId(iTimeInc); MP4SampleId iIdx = _video_current; @@ -245,7 +246,7 @@ inline bool MediaReader::readVideoSample(int iTimeInc,bool justSeekSyncFrame) { return false; } -inline bool MediaReader::readAudioSample(int iTimeInc,bool justSeekSyncFrame) { +inline bool MP4Reader::readAudioSample(int iTimeInc,bool justSeekSyncFrame) { if (_audio_trId != MP4_INVALID_TRACK_ID) { auto iNextSample = getAudioSampleId(iTimeInc); for (auto i = _audio_current; i < iNextSample; i++) { @@ -267,27 +268,27 @@ inline bool MediaReader::readAudioSample(int iTimeInc,bool justSeekSyncFrame) { return false; } -inline void MediaReader::writeH264(uint8_t *pucData,int iLen,uint32_t dts,uint32_t pts) { +inline void MP4Reader::writeH264(uint8_t *pucData,int iLen,uint32_t dts,uint32_t pts) { _mediaMuxer->inputFrame(std::make_shared((char*)pucData,iLen,dts,pts)); } -inline void MediaReader::writeAAC(uint8_t *pucData,int iLen,uint32_t uiStamp) { +inline void MP4Reader::writeAAC(uint8_t *pucData,int iLen,uint32_t uiStamp) { _mediaMuxer->inputFrame(std::make_shared((char*)pucData,iLen,uiStamp)); } -inline MP4SampleId MediaReader::getVideoSampleId(int iTimeInc ) { +inline MP4SampleId MP4Reader::getVideoSampleId(int iTimeInc ) { MP4SampleId video_current = (double)_video_num_samples * (_iSeekTime + _ticker.elapsedTime() + iTimeInc) / _video_ms; video_current = MAX(0,MIN(_video_num_samples, video_current)); return video_current; } -inline MP4SampleId MediaReader::getAudioSampleId(int iTimeInc) { +inline MP4SampleId MP4Reader::getAudioSampleId(int iTimeInc) { MP4SampleId audio_current = (double)_audio_num_samples * (_iSeekTime + _ticker.elapsedTime() + iTimeInc) / _audio_ms ; audio_current = MAX(0,MIN(_audio_num_samples,audio_current)); return audio_current; } -inline void MediaReader::setSeekTime(uint32_t iSeekTime){ +inline void MP4Reader::setSeekTime(uint32_t iSeekTime){ _iSeekTime = MAX(0, MIN(iSeekTime,_iDuration)); _ticker.resetTime(); if (_audio_trId != MP4_INVALID_TRACK_ID) { @@ -298,10 +299,10 @@ inline void MediaReader::setSeekTime(uint32_t iSeekTime){ } } -inline uint32_t MediaReader::getVideoCurrentTime(){ +inline uint32_t MP4Reader::getVideoCurrentTime(){ return (double)_video_current * _video_ms /_video_num_samples; } -void MediaReader::seek(uint32_t iSeekTime,bool bReStart){ +void MP4Reader::seek(uint32_t iSeekTime,bool bReStart){ lock_guard lck(_mtx); if(iSeekTime == 0 || _video_trId == MP4_INVALID_TRACK_ID){ setSeekTime(iSeekTime); @@ -331,7 +332,7 @@ void MediaReader::seek(uint32_t iSeekTime,bool bReStart){ -MediaSource::Ptr MediaReader::onMakeMediaSource(const string &strSchema, +MediaSource::Ptr MP4Reader::onMakeMediaSource(const string &strSchema, const string &strVhost, const string &strApp, const string &strId, @@ -343,7 +344,7 @@ MediaSource::Ptr MediaReader::onMakeMediaSource(const string &strSchema, return nullptr; } try { - MediaReader::Ptr pReader(new MediaReader(strVhost,strApp, strId,filePath)); + MP4Reader::Ptr pReader(new MP4Reader(strVhost,strApp, strId,filePath)); pReader->startReadMP4(); return MediaSource::find(strSchema,strVhost,strApp, strId, false); } catch (std::exception &ex) { diff --git a/src/MediaFile/MediaReader.h b/src/Record/MP4Reader.h similarity index 86% rename from src/MediaFile/MediaReader.h rename to src/Record/MP4Reader.h index a2917367..576d0ed2 100644 --- a/src/MediaFile/MediaReader.h +++ b/src/Record/MP4Reader.h @@ -37,10 +37,10 @@ using namespace toolkit; namespace mediakit { -class MediaReader : public std::enable_shared_from_this ,public MediaSourceEvent{ +class MP4Reader : public std::enable_shared_from_this ,public MediaSourceEvent{ public: - typedef std::shared_ptr Ptr; - virtual ~MediaReader(); + typedef std::shared_ptr Ptr; + virtual ~MP4Reader(); /** * 流化一个mp4文件,使之转换成RtspMediaSource和RtmpMediaSource @@ -49,10 +49,10 @@ public: * @param strId 流id * @param filePath 文件路径,如果为空则根据配置文件和上面参数自动生成,否则使用指定的文件 */ - MediaReader(const string &strVhost,const string &strApp, const string &strId,const string &filePath = ""); + MP4Reader(const string &strVhost,const string &strApp, const string &strId,const string &filePath = ""); /** - * 开始流化MP4文件,需要指出的是,MediaReader对象一经过调用startReadMP4方法,它的强引用会自持有, - * 意思是在文件流化结束之前或中断之前,MediaReader对象是不会被销毁的(不管有没有被外部对象持有) + * 开始流化MP4文件,需要指出的是,MP4Reader对象一经过调用startReadMP4方法,它的强引用会自持有, + * 意思是在文件流化结束之前或中断之前,MP4Reader对象是不会被销毁的(不管有没有被外部对象持有) */ void startReadMP4(); @@ -64,13 +64,13 @@ public: bool seekTo(MediaSource &sender,uint32_t ui32Stamp) override; /** - * 关闭MediaReader的流化进程,会触发该对象放弃自持有 + * 关闭MP4Reader的流化进程,会触发该对象放弃自持有 * @return */ bool close(MediaSource &sender,bool force) override; /** - * 自动生成MediaReader对象然后查找相关的MediaSource对象 + * 自动生成MP4Reader对象然后查找相关的MediaSource对象 * @param strSchema 协议名 * @param strVhost 虚拟主机 * @param strApp 应用名 diff --git a/src/MediaFile/MP4Recorder.cpp b/src/Record/MP4Recorder.cpp similarity index 100% rename from src/MediaFile/MP4Recorder.cpp rename to src/Record/MP4Recorder.cpp diff --git a/src/MediaFile/MP4Recorder.h b/src/Record/MP4Recorder.h similarity index 100% rename from src/MediaFile/MP4Recorder.h rename to src/Record/MP4Recorder.h diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp new file mode 100644 index 00000000..cc73324c --- /dev/null +++ b/src/Record/Recorder.cpp @@ -0,0 +1,95 @@ +/* +* MIT License +* +* Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> +* +* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include "Recorder.h" +#include "Common/config.h" +#include "Http/HttpSession.h" +#include "Util/util.h" +#include "Util/mini.h" +#include "Network/sockutil.h" +#include "HlsMakerImp.h" +#include "Player/PlayerBase.h" +#include "Common/MediaSink.h" +#include "MP4Recorder.h" +#include "HlsRecorder.h" + +using namespace toolkit; + +namespace mediakit { + +MediaSinkInterface *Recorder::createHlsRecorder(const string &strVhost_tmp, const string &strApp, const string &strId) { +#if defined(ENABLE_HLS) + GET_CONFIG(bool, enableVhost, General::kEnableVhost); + GET_CONFIG(string, hlsPath, Hls::kFilePath); + + string strVhost = strVhost_tmp; + if (trim(strVhost).empty()) { + //如果strVhost为空,则强制为默认虚拟主机 + strVhost = DEFAULT_VHOST; + } + + string m3u8FilePath; + string params; + if (enableVhost) { + m3u8FilePath = strVhost + "/" + strApp + "/" + strId + "/hls.m3u8"; + params = string(VHOST_KEY) + "=" + strVhost; + } else { + m3u8FilePath = strApp + "/" + strId + "/hls.m3u8"; + } + m3u8FilePath = File::absolutePath(m3u8FilePath, hlsPath); + return new HlsRecorder(m3u8FilePath, params); +#else + return nullptr; +#endif //defined(ENABLE_HLS) +} + +MediaSinkInterface *Recorder::createMP4Recorder(const string &strVhost_tmp, const string &strApp, const string &strId) { +#if defined(ENABLE_MP4RECORD) + GET_CONFIG(bool, enableVhost, General::kEnableVhost); + GET_CONFIG(string, recordPath, Record::kFilePath); + GET_CONFIG(string, recordAppName, Record::kAppName); + + string strVhost = strVhost_tmp; + if (trim(strVhost).empty()) { + //如果strVhost为空,则强制为默认虚拟主机 + strVhost = DEFAULT_VHOST; + } + + string mp4FilePath; + if (enableVhost) { + mp4FilePath = strVhost + "/" + recordAppName + "/" + strApp + "/" + strId + "/"; + } else { + mp4FilePath = recordAppName + "/" + strApp + "/" + strId + "/"; + } + mp4FilePath = File::absolutePath(mp4FilePath, recordPath); + return new MP4Recorder(mp4FilePath, strVhost, strApp, strId); +#else + return nullptr; +#endif //defined(ENABLE_MP4RECORD) +} + + +} /* namespace mediakit */ diff --git a/src/MediaFile/MediaRecorder.h b/src/Record/Recorder.h similarity index 53% rename from src/MediaFile/MediaRecorder.h rename to src/Record/Recorder.h index fbfba1e7..63fdc147 100644 --- a/src/MediaFile/MediaRecorder.h +++ b/src/Record/Recorder.h @@ -24,56 +24,26 @@ * SOFTWARE. */ -#ifndef SRC_MEDIAFILE_MEDIARECORDER_H_ -#define SRC_MEDIAFILE_MEDIARECORDER_H_ +#ifndef SRC_MEDIAFILE_RECORDER_H_ +#define SRC_MEDIAFILE_RECORDER_H_ #include -#include "Player/PlayerBase.h" -#include "Common/MediaSink.h" -#include "MP4Recorder.h" -#include "HlsRecorder.h" - -using namespace toolkit; +#include +using namespace std; namespace mediakit { -class MediaRecorder : public MediaSinkInterface{ +class MediaSinkInterface; + +class Recorder{ public: - typedef std::shared_ptr Ptr; - MediaRecorder(const string &strVhost, - const string &strApp, - const string &strId, - bool enableHls = true, - bool enableMp4 = false); - virtual ~MediaRecorder(); - - /** - * 输入frame - * @param frame - */ - void inputFrame(const Frame::Ptr &frame) override; - - /** - * 添加track,内部会调用Track的clone方法 - * 只会克隆sps pps这些信息 ,而不会克隆Delegate相关关系 - * @param track - */ - void addTrack(const Track::Ptr &track) override; - - /** - * 重置track - */ - void resetTracks() override; + static MediaSinkInterface *createHlsRecorder(const string &strVhost, const string &strApp, const string &strId); + static MediaSinkInterface *createMP4Recorder(const string &strVhost, const string &strApp, const string &strId); private: -#if defined(ENABLE_HLS) - std::shared_ptr _hlsRecorder; -#endif //defined(ENABLE_HLS) - -#if defined(ENABLE_MP4RECORD) - std::shared_ptr _mp4Recorder; -#endif //defined(ENABLE_MP4RECORD) + Recorder() = delete; + ~Recorder() = delete; }; } /* namespace mediakit */ -#endif /* SRC_MEDIAFILE_MEDIARECORDER_H_ */ +#endif /* SRC_MEDIAFILE_RECORDER_H_ */ diff --git a/src/MediaFile/TsMuxer.cpp b/src/Record/TsMuxer.cpp similarity index 100% rename from src/MediaFile/TsMuxer.cpp rename to src/Record/TsMuxer.cpp diff --git a/src/MediaFile/TsMuxer.h b/src/Record/TsMuxer.h similarity index 98% rename from src/MediaFile/TsMuxer.h rename to src/Record/TsMuxer.h index 3bd4509c..26b262cc 100644 --- a/src/MediaFile/TsMuxer.h +++ b/src/Record/TsMuxer.h @@ -32,7 +32,7 @@ #include "Extension/Track.h" #include "Util/File.h" #include "Common/MediaSink.h" -#include "Stamp.h" +#include "Common/Stamp.h" using namespace toolkit; diff --git a/src/Rtmp/FlvMuxer.h b/src/Rtmp/FlvMuxer.h index 7f48d3fd..a01d6250 100644 --- a/src/Rtmp/FlvMuxer.h +++ b/src/Rtmp/FlvMuxer.h @@ -30,7 +30,7 @@ #include "Rtmp/Rtmp.h" #include "Rtmp/RtmpMediaSource.h" #include "Network/Socket.h" -#include "MediaFile/Stamp.h" +#include "Common/Stamp.h" using namespace toolkit; namespace mediakit { diff --git a/src/Rtmp/RtmpSession.h b/src/Rtmp/RtmpSession.h index a891b49b..9eac1e35 100644 --- a/src/Rtmp/RtmpSession.h +++ b/src/Rtmp/RtmpSession.h @@ -37,7 +37,7 @@ #include "Util/util.h" #include "Util/TimeTicker.h" #include "Network/TcpSession.h" -#include "MediaFile/Stamp.h" +#include "Common/Stamp.h" using namespace toolkit; diff --git a/src/Rtsp/RtspPlayer.h b/src/Rtsp/RtspPlayer.h index ee7afba0..8247dc9d 100644 --- a/src/Rtsp/RtspPlayer.h +++ b/src/Rtsp/RtspPlayer.h @@ -40,7 +40,7 @@ #include "Network/TcpClient.h" #include "RtspSplitter.h" #include "RtpReceiver.h" -#include "MediaFile/Stamp.h" +#include "Common/Stamp.h" using namespace std; using namespace toolkit; diff --git a/src/Rtsp/RtspSession.h b/src/Rtsp/RtspSession.h index 33637666..6ffa550a 100644 --- a/src/Rtsp/RtspSession.h +++ b/src/Rtsp/RtspSession.h @@ -41,6 +41,7 @@ #include "RtspSplitter.h" #include "RtpReceiver.h" #include "RtspToRtmpMediaSource.h" +#include "Common/Stamp.h" using namespace std; using namespace toolkit; From de77460621efa168607f7948ad98992eb5a191c7 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 4 Dec 2019 10:52:26 +0800 Subject: [PATCH 058/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_pusherMp4.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_pusherMp4.cpp b/tests/test_pusherMp4.cpp index fc502d29..9b94631b 100644 --- a/tests/test_pusherMp4.cpp +++ b/tests/test_pusherMp4.cpp @@ -33,7 +33,7 @@ #include "Rtmp/RtmpPusher.h" #include "Common/config.h" #include "Pusher/MediaPusher.h" -#include "MediaFile/MediaReader.h" +#include "Record/MP4Reader.h" using namespace std; using namespace toolkit; @@ -63,7 +63,7 @@ void createPusher(const EventPoller::Ptr &poller, const string &filePath, const string &url) { //不限制APP名,并且指定文件绝对路径 - auto src = MediaReader::onMakeMediaSource(schema,vhost,app,stream,filePath, false); + auto src = MP4Reader::onMakeMediaSource(schema,vhost,app,stream,filePath, false); if(!src){ //文件不存在 WarnL << "MP4文件不存在:" << filePath; From d5a81d7105db8f5207cc18f67604c297d2cddbec Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 4 Dec 2019 10:59:13 +0800 Subject: [PATCH 059/272] =?UTF-8?q?=E8=A7=84=E8=8C=83=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Pusher/MediaPusher.cpp | 10 +++++----- src/Pusher/PusherBase.h | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Pusher/MediaPusher.cpp b/src/Pusher/MediaPusher.cpp index 78475fb1..26f8de8b 100644 --- a/src/Pusher/MediaPusher.cpp +++ b/src/Pusher/MediaPusher.cpp @@ -52,11 +52,11 @@ MediaPusher::MediaPusher(const string &schema, MediaPusher::~MediaPusher() { } void MediaPusher::publish(const string &strUrl) { - _parser = PusherBase::createPusher(_poller,_src.lock(),strUrl); - _parser->setOnShutdown(_shutdownCB); - _parser->setOnPublished(_publishCB); - _parser->mINI::operator=(*this); - _parser->publish(strUrl); + _delegate = PusherBase::createPusher(_poller,_src.lock(),strUrl); + _delegate->setOnShutdown(_shutdownCB); + _delegate->setOnPublished(_publishCB); + _delegate->mINI::operator=(*this); + _delegate->publish(strUrl); } EventPoller::Ptr MediaPusher::getPoller(){ diff --git a/src/Pusher/PusherBase.h b/src/Pusher/PusherBase.h index 6fc86e5b..752f3358 100644 --- a/src/Pusher/PusherBase.h +++ b/src/Pusher/PusherBase.h @@ -75,7 +75,7 @@ public: virtual void setOnShutdown(const Event &cb) = 0; }; -template +template class PusherImp : public Parent { public: typedef std::shared_ptr Ptr; @@ -90,8 +90,8 @@ public: * @param strUrl 推流url,支持rtsp/rtmp */ void publish(const string &strUrl) override{ - if (_parser) { - _parser->publish(strUrl); + if (_delegate) { + _delegate->publish(strUrl); } } @@ -99,8 +99,8 @@ public: * 中断推流 */ void teardown() override{ - if (_parser) { - _parser->teardown(); + if (_delegate) { + _delegate->teardown(); } } @@ -109,8 +109,8 @@ public: * @param onPublished */ void setOnPublished(const PusherBase::Event &cb) override{ - if (_parser) { - _parser->setOnPublished(cb); + if (_delegate) { + _delegate->setOnPublished(cb); } _publishCB = cb; } @@ -120,15 +120,15 @@ public: * @param onShutdown */ void setOnShutdown(const PusherBase::Event &cb) override{ - if (_parser) { - _parser->setOnShutdown(cb); + if (_delegate) { + _delegate->setOnShutdown(cb); } _shutdownCB = cb; } protected: PusherBase::Event _shutdownCB; PusherBase::Event _publishCB; - std::shared_ptr _parser; + std::shared_ptr _delegate; }; From a79bee1d88b703744319c1e8f844de16363ac06b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Wed, 4 Dec 2019 12:11:36 +0800 Subject: [PATCH 060/272] Update README_CN.md --- README_CN.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README_CN.md b/README_CN.md index c117907d..037c5c5a 100644 --- a/README_CN.md +++ b/README_CN.md @@ -359,9 +359,16 @@ git submodule update --init ## 联系方式 - 邮箱:<771730766@qq.com>(本项目相关或流媒体相关问题请走issue流程,否则恕不邮件答复) - QQ群:542509000 + +## 怎么提问? +如果要对项目有相关疑问,建议您这么做: + - 1、仔细看下readme、wiki,如果有必要可以查看下issue. + - 2、如果您的问题还没解决,可以提issue. + - 3、有些问题,如果不具备参考性的,无需在issue提的,可以在qq群提. + - 4、QQ私聊一般不接受无偿技术咨询和支持(谈谈人生理想还是可以的😂),毕竟精力有限,谢谢理解. ## 捐赠 -如果本项目能切实帮助您减少重复开发的工作量,您可以在自愿的基础上支持下作者,谢谢! +欢迎捐赠以便更好的推动项目的发展,谢谢您的支持! [支付宝](https://raw.githubusercontent.com/xiongziliang/other/master/IMG_3919.JPG) From b3fcb4c038a342637731315cc5f32d5273187a87 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 4 Dec 2019 18:36:30 +0800 Subject: [PATCH 061/272] =?UTF-8?q?=E9=87=8D=E5=86=99mp4=E5=BD=95=E5=88=B6?= =?UTF-8?q?=E9=A9=B1=E5=8A=A8=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/MediaSource.cpp | 49 ++++- src/Common/MultiMediaSourceMuxer.h | 51 +++-- src/Common/config.cpp | 1 + src/Common/config.h | 6 +- src/Record/MP4Recorder.cpp | 4 +- src/Record/MP4Recorder.h | 12 +- src/Record/Recorder.cpp | 292 ++++++++++++++++++++++++++++- src/Record/Recorder.h | 21 ++- 8 files changed, 384 insertions(+), 52 deletions(-) diff --git a/src/Common/MediaSource.cpp b/src/Common/MediaSource.cpp index d489a217..07b81a5a 100644 --- a/src/Common/MediaSource.cpp +++ b/src/Common/MediaSource.cpp @@ -80,6 +80,19 @@ vector MediaSource::getTracks(bool trackReady) const { void MediaSource::setTrackSource(const std::weak_ptr &track_src) { _track_source = track_src; + weak_ptr weakPtr = shared_from_this(); + EventPollerPool::Instance().getPoller()->async([weakPtr,this](){ + auto strongPtr = weakPtr.lock(); + if (!strongPtr) { + return; + } + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaResetTracks, + _strSchema, + _strVhost, + _strApp, + _strId, + *this); + },false); } void MediaSource::setListener(const std::weak_ptr &listener){ @@ -293,13 +306,20 @@ void MediaSource::regist() { g_mapMediaSrc[_strSchema][_strVhost][_strApp][_strId] = shared_from_this(); } InfoL << _strSchema << " " << _strVhost << " " << _strApp << " " << _strId; - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaChanged, - true, - _strSchema, - _strVhost, - _strApp, - _strId, - *this); + weak_ptr weakPtr = shared_from_this(); + EventPollerPool::Instance().getPoller()->async([weakPtr,this](){ + auto strongPtr = weakPtr.lock(); + if (!strongPtr) { + return; + } + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaChanged, + true, + _strSchema, + _strVhost, + _strApp, + _strId, + *this); + },false); } bool MediaSource::unregist() { //反注册该源 @@ -328,6 +348,21 @@ void MediaSource::unregisted(){ _strApp, _strId, *this); + + weak_ptr weakPtr = shared_from_this(); + EventPollerPool::Instance().getPoller()->async([weakPtr,this](){ + auto strongPtr = weakPtr.lock(); + if (!strongPtr) { + return; + } + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaChanged, + true, + _strSchema, + _strVhost, + _strApp, + _strId, + *this); + },false); } diff --git a/src/Common/MultiMediaSourceMuxer.h b/src/Common/MultiMediaSourceMuxer.h index 431f609c..0cda92dc 100644 --- a/src/Common/MultiMediaSourceMuxer.h +++ b/src/Common/MultiMediaSourceMuxer.h @@ -49,14 +49,32 @@ public: if (bEanbleRtsp) { _rtsp = std::make_shared(vhost, strApp, strId, std::make_shared(dur_sec)); } - if(bEanbleHls){ - _hls.reset(Recorder::createHlsRecorder(vhost, strApp, strId)); - } - if(bEnableMp4){ - _mp4.reset(Recorder::createMP4Recorder(vhost, strApp, strId)); + + _recordFunc = [bEanbleHls,bEnableMp4,vhost, strApp, strId](bool start){ + if(bEanbleHls){ + if(start){ + Recorder::startRecord(Recorder::type_hls,vhost, strApp, strId, true, false); + }else{ + Recorder::stopRecord(Recorder::type_hls,vhost, strApp, strId); + } + } + + if(bEnableMp4){ + if(start){ + Recorder::startRecord(Recorder::type_mp4,vhost, strApp, strId, true, false); + }else{ + Recorder::stopRecord(Recorder::type_mp4,vhost, strApp, strId); + } + } + }; + + _recordFunc(true); + } + virtual ~MultiMediaSourceMuxer(){ + if(_recordFunc){ + _recordFunc(false); } } - virtual ~MultiMediaSourceMuxer(){} /** * 重置音视频媒体 @@ -68,12 +86,6 @@ public: if(_rtsp){ _rtsp->resetTracks(); } - if(_hls){ - _hls->resetTracks(); - } - if(_mp4){ - _mp4->resetTracks(); - } } /** @@ -115,12 +127,6 @@ protected: if(_rtsp){ _rtsp->addTrack(track); } - if(_hls){ - _hls->addTrack(track); - } - if(_mp4){ - _mp4->addTrack(track); - } } /** @@ -134,12 +140,6 @@ protected: if(_rtsp) { _rtsp->inputFrame(frame); } - if(_hls){ - _hls->inputFrame(frame); - } - if(_mp4){ - _mp4->inputFrame(frame); - } } /** @@ -158,8 +158,7 @@ protected: private: RtmpMediaSourceMuxer::Ptr _rtmp; RtspMediaSourceMuxer::Ptr _rtsp; - MediaSinkInterface::Ptr _hls; - MediaSinkInterface::Ptr _mp4; + function _recordFunc; }; diff --git a/src/Common/config.cpp b/src/Common/config.cpp index 37597b96..426bb082 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -55,6 +55,7 @@ bool loadIniConfig(const char *ini_path){ ////////////广播名称/////////// namespace Broadcast { const string kBroadcastMediaChanged = "kBroadcastMediaChanged"; +const string kBroadcastMediaResetTracks = "kBroadcastMediaResetTracks"; const string kBroadcastRecordMP4 = "kBroadcastRecordMP4"; const string kBroadcastHttpRequest = "kBroadcastHttpRequest"; const string kBroadcastHttpAccess = "kBroadcastHttpAccess"; diff --git a/src/Common/config.h b/src/Common/config.h index 3f597c5a..9a090144 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -71,9 +71,13 @@ namespace Broadcast { extern const string kBroadcastMediaChanged; #define BroadcastMediaChangedArgs const bool &bRegist, const string &schema,const string &vhost,const string &app,const string &stream,MediaSource &sender +//MediaSource重置Track事件 +extern const string kBroadcastMediaResetTracks; +#define BroadcastMediaResetTracksArgs const string &schema,const string &vhost,const string &app,const string &stream,MediaSource &sender + //录制mp4文件成功后广播 extern const string kBroadcastRecordMP4; -#define BroadcastRecordMP4Args const Mp4Info &info +#define BroadcastRecordMP4Args const MP4Info &info //收到http api请求广播 extern const string kBroadcastHttpRequest; diff --git a/src/Record/MP4Recorder.cpp b/src/Record/MP4Recorder.cpp index 092258b4..e0332a56 100644 --- a/src/Record/MP4Recorder.cpp +++ b/src/Record/MP4Recorder.cpp @@ -107,7 +107,7 @@ void MP4Recorder::asyncClose() { auto info = _info; WorkThreadPool::Instance().getExecutor()->async([muxer,strFileTmp,strFile,info]() { //获取文件录制时间,放在关闭mp4之前是为了忽略关闭mp4执行时间 - const_cast(info).ui64TimeLen = ::time(NULL) - info.ui64StartedTime; + const_cast(info).ui64TimeLen = ::time(NULL) - info.ui64StartedTime; //关闭mp4非常耗时,所以要放在后台线程执行 const_cast(muxer).reset(); //临时文件名改成正式文件名,防止mp4未完成时被访问 @@ -115,7 +115,7 @@ void MP4Recorder::asyncClose() { //获取文件大小 struct stat fileData; stat(strFile.data(), &fileData); - const_cast(info).ui64FileSize = fileData.st_size; + const_cast(info).ui64FileSize = fileData.st_size; /////record 业务逻辑////// NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastRecordMP4,info); }); diff --git a/src/Record/MP4Recorder.h b/src/Record/MP4Recorder.h index 2d9e84e9..3204fb51 100644 --- a/src/Record/MP4Recorder.h +++ b/src/Record/MP4Recorder.h @@ -42,7 +42,7 @@ using namespace toolkit; namespace mediakit { -class Mp4Info { +class MP4Info { public: time_t ui64StartedTime; //GMT标准时间,单位秒 time_t ui64TimeLen;//录像长度,单位秒 @@ -55,13 +55,15 @@ public: string strStreamId;//流ID string strVhost;//vhost }; + class MP4Recorder : public MediaSinkInterface{ public: typedef std::shared_ptr Ptr; + MP4Recorder(const string &strPath, - const string &strVhost , - const string &strApp, - const string &strStreamId); + const string &strVhost, + const string &strApp, + const string &strStreamId); virtual ~MP4Recorder(); /** @@ -87,7 +89,7 @@ private: string _strFile; string _strFileTmp; Ticker _createFileTicker; - Mp4Info _info; + MP4Info _info; bool _haveVideo = false; MP4MuxerFile::Ptr _muxer; list _tracks; diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index cc73324c..0627d318 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -26,13 +26,6 @@ #include "Recorder.h" #include "Common/config.h" -#include "Http/HttpSession.h" -#include "Util/util.h" -#include "Util/mini.h" -#include "Network/sockutil.h" -#include "HlsMakerImp.h" -#include "Player/PlayerBase.h" -#include "Common/MediaSink.h" #include "MP4Recorder.h" #include "HlsRecorder.h" @@ -40,7 +33,7 @@ using namespace toolkit; namespace mediakit { -MediaSinkInterface *Recorder::createHlsRecorder(const string &strVhost_tmp, const string &strApp, const string &strId) { +MediaSinkInterface *createHlsRecorder(const string &strVhost_tmp, const string &strApp, const string &strId) { #if defined(ENABLE_HLS) GET_CONFIG(bool, enableVhost, General::kEnableVhost); GET_CONFIG(string, hlsPath, Hls::kFilePath); @@ -66,7 +59,7 @@ MediaSinkInterface *Recorder::createHlsRecorder(const string &strVhost_tmp, cons #endif //defined(ENABLE_HLS) } -MediaSinkInterface *Recorder::createMP4Recorder(const string &strVhost_tmp, const string &strApp, const string &strId) { +MediaSinkInterface *createMP4Recorder(const string &strVhost_tmp, const string &strApp, const string &strId) { #if defined(ENABLE_MP4RECORD) GET_CONFIG(bool, enableVhost, General::kEnableVhost); GET_CONFIG(string, recordPath, Record::kFilePath); @@ -91,5 +84,286 @@ MediaSinkInterface *Recorder::createMP4Recorder(const string &strVhost_tmp, cons #endif //defined(ENABLE_MP4RECORD) } +//////////////////////////////////////////////////////////////////////////////////////// + +class RecorderHelper { +public: + typedef std::shared_ptr Ptr; + + /** + * 构建函数 + * @param bContinueRecord false表明hls录制从头开始录制(意味着hls临时文件在媒体反注册时会被删除) + */ + RecorderHelper(const MediaSinkInterface::Ptr &recorder, vector &&tracks , bool bContinueRecord, const string &schema) { + _recorder = recorder; + _continueRecord = bContinueRecord; + _schema = schema; + attachTracks(std::move(tracks)); + } + + ~RecorderHelper() { + resetTracks(); + } + + // 附则于track上 + void attachTracks(vector &&tracks){ + if(isTracksSame(tracks)){ + return; + } + resetTracks(); + _tracks = std::move(tracks); + for (auto &track : _tracks) { + _recorder->addTrack(track); + track->addDelegate(_recorder); + } + } + + + // 判断新的tracks是否与之前的一致 + bool isTracksSame(const vector &tracks){ + if(tracks.size() != _tracks.size()) { + return false; + } + int i = 0; + for(auto &track : tracks){ + if(track != _tracks[i++]){ + return false; + } + } + return true; + } + + // 重置所有track + void resetTracks(){ + if(_tracks.empty()){ + return; + } + for (auto &track : _tracks) { + track->delDelegate(_recorder.get()); + } + _tracks.clear(); + _recorder->resetTracks(); + } + + // 返回false表明hls录制从头开始录制(意味着hls临时文件在媒体反注册时会被删除) + bool continueRecord(){ + return _continueRecord; + } + + bool isRecording() { + return !_tracks.empty(); + } + + const string &getSchema() const{ + return _schema; + } +private: + MediaSinkInterface::Ptr _recorder; + vector _tracks; + bool _continueRecord; + string _schema; +}; + + +template +class MediaSourceWatcher { +public: + static MediaSourceWatcher& Instance(){ + static MediaSourceWatcher instance; + return instance; + } + + Recorder::status getRecordStatus(const string &vhost, const string &app, const string &stream_id) { + return getRecordStatus_l(getRecorderKey(vhost, app, stream_id)); + } + + int startRecord(const string &vhost, const string &app, const string &stream_id, bool waitForRecord, bool continueRecord) { + auto key = getRecorderKey(vhost, app, stream_id); + lock_guard lck(_recorder_mtx); + if (getRecordStatus_l(key) != Recorder::status_not_record) { + // 已经在录制了 + return 0; + } + + string schema; + auto tracks = findTracks(vhost, app, stream_id,schema); + if (!waitForRecord && tracks.empty()) { + // 暂时无法开启录制 + return -1; + } + + auto recorder = MediaSinkInterface::Ptr(createRecorder(vhost, app, stream_id)); + if (!recorder) { + // 创建录制器失败 + return -2; + } + _recorder_map[key] = std::make_shared(recorder, std::move(tracks), continueRecord, schema); + return 0; + } + + void stopRecord(const string &vhost, const string &app, const string &stream_id) { + lock_guard lck(_recorder_mtx); + _recorder_map.erase(getRecorderKey(vhost, app, stream_id)); + } + +private: + MediaSourceWatcher(){ + NoticeCenter::Instance().addListener(this,Broadcast::kBroadcastMediaChanged,[this](BroadcastMediaChangedArgs){ + if(bRegist){ + onRegist(schema,vhost,app,stream,sender); + }else{ + onUnRegist(schema,vhost,app,stream,sender); + } + }); + NoticeCenter::Instance().addListener(this,Broadcast::kBroadcastMediaResetTracks,[this](BroadcastMediaResetTracksArgs){ + onRegist(schema,vhost,app,stream,sender); + }); + } + + ~MediaSourceWatcher(){ + NoticeCenter::Instance().delListener(this,Broadcast::kBroadcastMediaChanged); + NoticeCenter::Instance().delListener(this,Broadcast::kBroadcastMediaResetTracks); + } + + void onRegist(const string &schema,const string &vhost,const string &app,const string &stream,MediaSource &sender){ + auto key = getRecorderKey(vhost,app,stream); + lock_guard lck(_recorder_mtx); + auto it = _recorder_map.find(key); + if(it == _recorder_map.end()){ + // 录像记录不存在 + return; + } + + auto tracks = sender.getTracks(true); + if (tracks.empty()) { + // 无有效的tracks + return; + } + + auto &helper = it->second; + if(!helper){ + // 对象不存在,创建之 + auto recorder = MediaSinkInterface::Ptr(createRecorder(vhost, app, stream)); + if (recorder) { + _recorder_map[key] = std::make_shared(recorder, std::move(tracks), false, schema); + } + return; + } + + if(helper->getSchema() == schema){ + // 对象存在且绑定的协议一致,替换tracks + helper->attachTracks(std::move(tracks)); + } + + } + + void onUnRegist(const string &schema,const string &vhost,const string &app,const string &stream,MediaSource &sender){ + auto key = getRecorderKey(vhost,app,stream); + lock_guard lck(_recorder_mtx); + auto it = _recorder_map.find(key); + if(it == _recorder_map.end()){ + // 录像记录不存在 + return; + } + + if(!it->second){ + // 录像对象为空,已经停止录制 + return; + } + if(it->second->continueRecord()){ + // 如果可以继续录制,那么只重置tracks,不删除对象 + it->second->resetTracks(); + }else{ + // 删除对象(意味着可能删除hls临时文件) + it->second.reset(); + } + } + + Recorder::status getRecordStatus_l(const string &key) { + auto it = _recorder_map.find(key); + if (it == _recorder_map.end()) { + return Recorder::status_not_record; + } + if (it->second && it->second->isRecording()) { + return Recorder::status_recording; + } + + return Recorder::status_wait_record; + } + + // 查找MediaSource以便录制 + vector findTracks(const string &vhost, const string &app, const string &stream_id,string &schema) { + auto src = MediaSource::find(RTMP_SCHEMA, vhost, app, stream_id); + if (src) { + auto ret = src->getTracks(true); + if (!ret.empty()) { + schema = RTMP_SCHEMA; + return std::move(ret); + } + } + + src = MediaSource::find(RTSP_SCHEMA, vhost, app, stream_id); + if (src) { + schema = RTSP_SCHEMA; + return src->getTracks(true); + } + return vector(); + } + + string getRecorderKey(const string &vhost, const string &app, const string &stream_id) { + return vhost + "/" + app + "/" + stream_id; + } + + MediaSinkInterface *createRecorder(const string &vhost, const string &app, const string &stream_id) { + MediaSinkInterface *ret = nullptr; + switch (type) { + case Recorder::type_hls: + ret = createHlsRecorder(vhost, app, stream_id); + break; + case Recorder::type_mp4: + ret = createMP4Recorder(vhost, app, stream_id); + break; + default: + break; + } + if(!ret){ + WarnL << "can not recorder of: " << type; + } + return ret; + } +private: + recursive_mutex _recorder_mtx; + unordered_map _recorder_map; +}; + + +Recorder::status Recorder::getRecordStatus(Recorder::type type, const string &vhost, const string &app, const string &stream_id) { + switch (type){ + case type_mp4: + return MediaSourceWatcher::Instance().getRecordStatus(vhost,app,stream_id); + case type_hls: + return MediaSourceWatcher::Instance().getRecordStatus(vhost,app,stream_id); + } + return status_not_record; +} + +int Recorder::startRecord(Recorder::type type, const string &vhost, const string &app, const string &stream_id, bool waitForRecord, bool continueRecord) { + switch (type){ + case type_mp4: + return MediaSourceWatcher::Instance().startRecord(vhost,app,stream_id,waitForRecord,continueRecord); + case type_hls: + return MediaSourceWatcher::Instance().startRecord(vhost,app,stream_id,waitForRecord,continueRecord); + } + return -3; +} + +void Recorder::stopRecord(Recorder::type type, const string &vhost, const string &app, const string &stream_id) { + switch (type){ + case type_mp4: + return MediaSourceWatcher::Instance().stopRecord(vhost,app,stream_id); + case type_hls: + return MediaSourceWatcher::Instance().stopRecord(vhost,app,stream_id); + } +} } /* namespace mediakit */ diff --git a/src/Record/Recorder.h b/src/Record/Recorder.h index 63fdc147..b170e77a 100644 --- a/src/Record/Recorder.h +++ b/src/Record/Recorder.h @@ -37,8 +37,25 @@ class MediaSinkInterface; class Recorder{ public: - static MediaSinkInterface *createHlsRecorder(const string &strVhost, const string &strApp, const string &strId); - static MediaSinkInterface *createMP4Recorder(const string &strVhost, const string &strApp, const string &strId); + typedef enum { + // 未录制 + status_not_record = 0, + // 等待MediaSource注册,注册成功后立即开始录制 + status_wait_record = 1, + // MediaSource已注册,并且正在录制 + status_recording = 2, + } status; + + typedef enum { + // 录制hls + type_hls = 0, + // 录制MP4 + type_mp4 = 1 + } type; + + static status getRecordStatus(type type, const string &vhost, const string &app, const string &stream_id); + static int startRecord(type type, const string &vhost, const string &app, const string &stream_id,bool waitForRecord, bool continueRecord); + static void stopRecord(type type, const string &vhost, const string &app, const string &stream_id); private: Recorder() = delete; ~Recorder() = delete; From e69cbc1486684bb0b970d4c5fa932ef8a52fe74f Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 4 Dec 2019 18:46:31 +0800 Subject: [PATCH 062/272] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/Recorder.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index 0627d318..ada46cee 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -266,16 +266,12 @@ private: return; } - if(!it->second){ - // 录像对象为空,已经停止录制 - return; - } if(it->second->continueRecord()){ // 如果可以继续录制,那么只重置tracks,不删除对象 it->second->resetTracks(); }else{ // 删除对象(意味着可能删除hls临时文件) - it->second.reset(); + _recorder_map.erase(it); } } @@ -284,11 +280,7 @@ private: if (it == _recorder_map.end()) { return Recorder::status_not_record; } - if (it->second && it->second->isRecording()) { - return Recorder::status_recording; - } - - return Recorder::status_wait_record; + return it->second->isRecording() ? Recorder::status_recording : Recorder::status_wait_record; } // 查找MediaSource以便录制 From 57043138d8cb1f684347ae5f43f19385f44a07d4 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 4 Dec 2019 18:49:22 +0800 Subject: [PATCH 063/272] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/Recorder.cpp | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index ada46cee..a0a1f538 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -240,19 +240,9 @@ private: return; } - auto &helper = it->second; - if(!helper){ - // 对象不存在,创建之 - auto recorder = MediaSinkInterface::Ptr(createRecorder(vhost, app, stream)); - if (recorder) { - _recorder_map[key] = std::make_shared(recorder, std::move(tracks), false, schema); - } - return; - } - - if(helper->getSchema() == schema){ - // 对象存在且绑定的协议一致,替换tracks - helper->attachTracks(std::move(tracks)); + if(it->second->getSchema() == schema){ + // 绑定的协议一致,替换tracks + it->second->attachTracks(std::move(tracks)); } } From ae230bc1aafd4779ae3a2f376e11a1c20b86bf10 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 4 Dec 2019 18:53:12 +0800 Subject: [PATCH 064/272] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/Recorder.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index a0a1f538..4b95d08a 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -256,6 +256,10 @@ private: return; } + if(it->second->getSchema() != schema){ + // 绑定的协议不一致 + return; + } if(it->second->continueRecord()){ // 如果可以继续录制,那么只重置tracks,不删除对象 it->second->resetTracks(); From 728fd55e0cc2a7800a1e5a852e186471154f3e39 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 4 Dec 2019 18:58:19 +0800 Subject: [PATCH 065/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E5=BB=B6=E5=90=8E=E5=BD=95=E5=88=B6=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/Recorder.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index 4b95d08a..36cb8121 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -97,8 +97,7 @@ public: RecorderHelper(const MediaSinkInterface::Ptr &recorder, vector &&tracks , bool bContinueRecord, const string &schema) { _recorder = recorder; _continueRecord = bContinueRecord; - _schema = schema; - attachTracks(std::move(tracks)); + attachTracks(std::move(tracks),schema); } ~RecorderHelper() { @@ -106,12 +105,13 @@ public: } // 附则于track上 - void attachTracks(vector &&tracks){ + void attachTracks(vector &&tracks, const string &schema){ if(isTracksSame(tracks)){ return; } resetTracks(); _tracks = std::move(tracks); + _schema = schema; for (auto &track : _tracks) { _recorder->addTrack(track); track->addDelegate(_recorder); @@ -240,9 +240,9 @@ private: return; } - if(it->second->getSchema() == schema){ + if(!it->second->isRecording() || it->second->getSchema() == schema){ // 绑定的协议一致,替换tracks - it->second->attachTracks(std::move(tracks)); + it->second->attachTracks(std::move(tracks),schema); } } From 33047ddc94409827be9917857b9706c2b4b8af70 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 4 Dec 2019 19:15:48 +0800 Subject: [PATCH 066/272] =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E9=80=80=E5=87=BA?= =?UTF-8?q?=E5=89=8D=E5=81=9C=E6=AD=A2=E4=B8=80=E5=88=87=E5=BD=95=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/main.cpp | 1 + src/Record/Recorder.cpp | 10 ++++++++++ src/Record/Recorder.h | 1 + tests/test_server.cpp | 1 + 4 files changed, 13 insertions(+) diff --git a/server/main.cpp b/server/main.cpp index 763de656..40126969 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -336,6 +336,7 @@ int start_main(int argc,char *argv[]) { } unInstallWebApi(); unInstallWebHook(); + Recorder::stopAll(); //休眠1秒再退出,防止资源释放顺序错误 InfoL << "程序退出中,请等待..."; sleep(1); diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index 36cb8121..ed9dabae 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -206,6 +206,11 @@ public: _recorder_map.erase(getRecorderKey(vhost, app, stream_id)); } + void stopAll(){ + lock_guard lck(_recorder_mtx); + _recorder_map.clear(); + } + private: MediaSourceWatcher(){ NoticeCenter::Instance().addListener(this,Broadcast::kBroadcastMediaChanged,[this](BroadcastMediaChangedArgs){ @@ -352,4 +357,9 @@ void Recorder::stopRecord(Recorder::type type, const string &vhost, const string } } +void Recorder::stopAll() { + MediaSourceWatcher::Instance().stopAll(); + MediaSourceWatcher::Instance().stopAll(); +} + } /* namespace mediakit */ diff --git a/src/Record/Recorder.h b/src/Record/Recorder.h index b170e77a..a18e7bbd 100644 --- a/src/Record/Recorder.h +++ b/src/Record/Recorder.h @@ -56,6 +56,7 @@ public: static status getRecordStatus(type type, const string &vhost, const string &app, const string &stream_id); static int startRecord(type type, const string &vhost, const string &app, const string &stream_id,bool waitForRecord, bool continueRecord); static void stopRecord(type type, const string &vhost, const string &app, const string &stream_id); + static void stopAll(); private: Recorder() = delete; ~Recorder() = delete; diff --git a/tests/test_server.cpp b/tests/test_server.cpp index 6c9d6bf7..27c9c86c 100644 --- a/tests/test_server.cpp +++ b/tests/test_server.cpp @@ -354,6 +354,7 @@ int main(int argc,char *argv[]) { signal(SIGHUP, [](int) { loadIniConfig(); }); sem.wait(); + Recorder::stopAll(); lock_guard lck(s_mtxFlvRecorder); s_mapFlvRecorder.clear(); return 0; From 6eb4d5916dfdc1a1c885600bcff2cfbb4e3cb09c Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 4 Dec 2019 19:19:03 +0800 Subject: [PATCH 067/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/MediaSource.cpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/Common/MediaSource.cpp b/src/Common/MediaSource.cpp index 07b81a5a..036f541c 100644 --- a/src/Common/MediaSource.cpp +++ b/src/Common/MediaSource.cpp @@ -348,21 +348,6 @@ void MediaSource::unregisted(){ _strApp, _strId, *this); - - weak_ptr weakPtr = shared_from_this(); - EventPollerPool::Instance().getPoller()->async([weakPtr,this](){ - auto strongPtr = weakPtr.lock(); - if (!strongPtr) { - return; - } - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaChanged, - true, - _strSchema, - _strVhost, - _strApp, - _strId, - *this); - },false); } From f581f0f2a7b9d75085a66ba193711c685d95491c Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 5 Dec 2019 10:47:23 +0800 Subject: [PATCH 068/272] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=B2=BE=E7=AE=80?= =?UTF-8?q?=E5=BD=95=E5=88=B6=E7=9B=B8=E5=85=B3=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/MultiMediaSourceMuxer.h | 32 +++++++-------------------- src/Record/Recorder.cpp | 35 ++++++++++++++---------------- src/Record/Recorder.h | 31 ++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 43 deletions(-) diff --git a/src/Common/MultiMediaSourceMuxer.h b/src/Common/MultiMediaSourceMuxer.h index 0cda92dc..7c39e5f8 100644 --- a/src/Common/MultiMediaSourceMuxer.h +++ b/src/Common/MultiMediaSourceMuxer.h @@ -50,31 +50,16 @@ public: _rtsp = std::make_shared(vhost, strApp, strId, std::make_shared(dur_sec)); } - _recordFunc = [bEanbleHls,bEnableMp4,vhost, strApp, strId](bool start){ - if(bEanbleHls){ - if(start){ - Recorder::startRecord(Recorder::type_hls,vhost, strApp, strId, true, false); - }else{ - Recorder::stopRecord(Recorder::type_hls,vhost, strApp, strId); - } - } - - if(bEnableMp4){ - if(start){ - Recorder::startRecord(Recorder::type_mp4,vhost, strApp, strId, true, false); - }else{ - Recorder::stopRecord(Recorder::type_mp4,vhost, strApp, strId); - } - } - }; - - _recordFunc(true); - } - virtual ~MultiMediaSourceMuxer(){ - if(_recordFunc){ - _recordFunc(false); + if(bEanbleHls){ + Recorder::startRecord(Recorder::type_hls,vhost, strApp, strId, true, false); } + + if(bEnableMp4){ + Recorder::startRecord(Recorder::type_mp4,vhost, strApp, strId, true, false); + } + } + virtual ~MultiMediaSourceMuxer(){} /** * 重置音视频媒体 @@ -158,7 +143,6 @@ protected: private: RtmpMediaSourceMuxer::Ptr _rtmp; RtspMediaSourceMuxer::Ptr _rtsp; - function _recordFunc; }; diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index ed9dabae..2a1646f1 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -26,6 +26,7 @@ #include "Recorder.h" #include "Common/config.h" +#include "Common/MediaSource.h" #include "MP4Recorder.h" #include "HlsRecorder.h" @@ -94,10 +95,9 @@ public: * 构建函数 * @param bContinueRecord false表明hls录制从头开始录制(意味着hls临时文件在媒体反注册时会被删除) */ - RecorderHelper(const MediaSinkInterface::Ptr &recorder, vector &&tracks , bool bContinueRecord, const string &schema) { + RecorderHelper(const MediaSinkInterface::Ptr &recorder, bool bContinueRecord) { _recorder = recorder; _continueRecord = bContinueRecord; - attachTracks(std::move(tracks),schema); } ~RecorderHelper() { @@ -197,7 +197,11 @@ public: // 创建录制器失败 return -2; } - _recorder_map[key] = std::make_shared(recorder, std::move(tracks), continueRecord, schema); + auto helper = std::make_shared(recorder, continueRecord); + if(tracks.size()){ + helper->attachTracks(std::move(tracks),schema); + } + _recorder_map[key] = std::move(helper); return 0; } @@ -239,32 +243,24 @@ private: return; } - auto tracks = sender.getTracks(true); - if (tracks.empty()) { - // 无有效的tracks - return; - } - if(!it->second->isRecording() || it->second->getSchema() == schema){ - // 绑定的协议一致,替换tracks - it->second->attachTracks(std::move(tracks),schema); + // 绑定的协议一致或者并未正在录制则替换tracks + auto tracks = sender.getTracks(true); + if (!tracks.empty()) { + it->second->attachTracks(std::move(tracks),schema); + } } - } void onUnRegist(const string &schema,const string &vhost,const string &app,const string &stream,MediaSource &sender){ auto key = getRecorderKey(vhost,app,stream); lock_guard lck(_recorder_mtx); auto it = _recorder_map.find(key); - if(it == _recorder_map.end()){ - // 录像记录不存在 + if(it == _recorder_map.end() || it->second->getSchema() != schema){ + // 录像记录不存在或绑定的协议不一致 return; } - if(it->second->getSchema() != schema){ - // 绑定的协议不一致 - return; - } if(it->second->continueRecord()){ // 如果可以继续录制,那么只重置tracks,不删除对象 it->second->resetTracks(); @@ -318,7 +314,7 @@ private: break; } if(!ret){ - WarnL << "can not recorder of: " << type; + WarnL << "can not create recorder of type: " << type; } return ret; } @@ -345,6 +341,7 @@ int Recorder::startRecord(Recorder::type type, const string &vhost, const string case type_hls: return MediaSourceWatcher::Instance().startRecord(vhost,app,stream_id,waitForRecord,continueRecord); } + WarnL << "unknown record type: " << type; return -3; } diff --git a/src/Record/Recorder.h b/src/Record/Recorder.h index a18e7bbd..64d3af9a 100644 --- a/src/Record/Recorder.h +++ b/src/Record/Recorder.h @@ -53,9 +53,40 @@ public: type_mp4 = 1 } type; + /** + * 获取录制状态 + * @param type hls还是MP4录制 + * @param vhost 虚拟主机 + * @param app 应用名 + * @param stream_id 流id + * @return 录制状态 + */ static status getRecordStatus(type type, const string &vhost, const string &app, const string &stream_id); + + /** + * 开始录制 + * @param type hls还是MP4录制 + * @param vhost 虚拟主机 + * @param app 应用名 + * @param stream_id 流id + * @param waitForRecord 是否等待流注册后再录制,未注册时,置false将返回失败 + * @param continueRecord 流注销时是否继续等待录制还是立即停止注册 + * @return 0代表成功,负数代表失败 + */ static int startRecord(type type, const string &vhost, const string &app, const string &stream_id,bool waitForRecord, bool continueRecord); + + /** + * 停止录制 + * @param type hls还是MP4录制 + * @param vhost 虚拟主机 + * @param app 应用名 + * @param stream_id 流id + */ static void stopRecord(type type, const string &vhost, const string &app, const string &stream_id); + + /** + * 停止所有录制,一般程序退出时调用 + */ static void stopAll(); private: Recorder() = delete; From 69c0c3e237d4c15174675e4172bd7bc1212e1d51 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 5 Dec 2019 12:04:08 +0800 Subject: [PATCH 069/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=BD=95=E5=88=B6?= =?UTF-8?q?=E7=9B=B8=E5=85=B3http=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 34 ++++++++++++++++++++++++++++++++++ src/Record/Recorder.h | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 580e34c8..4e2e14d7 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -692,6 +692,40 @@ void installWebApi() { invoker.responseFile(headerIn,StrCaseMap(),exePath()); }); + // 开始录制hls或MP4 + API_REGIST_INVOKER(api,startRecord,{ + CHECK_SECRET(); + CHECK_ARGS("type","vhost","app","stream","wait_for_record","continue_record"); + int result = Recorder::startRecord((Recorder::type)allArgs["type"].as(), + allArgs["vhost"], + allArgs["app"], + allArgs["stream"], + allArgs["wait_for_record"], + allArgs["continue_record"]); + val["result"] = result; + }); + + // 停止录制hls或MP4 + API_REGIST_INVOKER(api,stopRecord,{ + CHECK_SECRET(); + CHECK_ARGS("type","vhost","app","stream"); + Recorder::stopRecord((Recorder::type)allArgs["type"].as(), + allArgs["vhost"], + allArgs["app"], + allArgs["stream"]); + }); + + // 获取hls或MP4录制状态 + API_REGIST_INVOKER(api,stopRecord,{ + CHECK_SECRET(); + CHECK_ARGS("type","vhost","app","stream"); + auto status = Recorder::getRecordStatus((Recorder::type)allArgs["type"].as(), + allArgs["vhost"], + allArgs["app"], + allArgs["stream"]); + val["status"] = (int)status; + }); + ////////////以下是注册的Hook API//////////// API_REGIST(hook,on_publish,{ //开始推流事件 diff --git a/src/Record/Recorder.h b/src/Record/Recorder.h index 64d3af9a..ffb8017c 100644 --- a/src/Record/Recorder.h +++ b/src/Record/Recorder.h @@ -70,7 +70,7 @@ public: * @param app 应用名 * @param stream_id 流id * @param waitForRecord 是否等待流注册后再录制,未注册时,置false将返回失败 - * @param continueRecord 流注销时是否继续等待录制还是立即停止注册 + * @param continueRecord 流注销时是否继续等待录制还是立即停止录制 * @return 0代表成功,负数代表失败 */ static int startRecord(type type, const string &vhost, const string &app, const string &stream_id,bool waitForRecord, bool continueRecord); From 8502ed27c40fe3103ec234173362763589158139 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 5 Dec 2019 12:55:43 +0800 Subject: [PATCH 070/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BD=95=E5=88=B6htt?= =?UTF-8?q?p=20ap=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 4e2e14d7..5e14fce9 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -693,7 +693,7 @@ void installWebApi() { }); // 开始录制hls或MP4 - API_REGIST_INVOKER(api,startRecord,{ + API_REGIST(api,startRecord,{ CHECK_SECRET(); CHECK_ARGS("type","vhost","app","stream","wait_for_record","continue_record"); int result = Recorder::startRecord((Recorder::type)allArgs["type"].as(), @@ -706,7 +706,7 @@ void installWebApi() { }); // 停止录制hls或MP4 - API_REGIST_INVOKER(api,stopRecord,{ + API_REGIST(api,stopRecord,{ CHECK_SECRET(); CHECK_ARGS("type","vhost","app","stream"); Recorder::stopRecord((Recorder::type)allArgs["type"].as(), @@ -716,7 +716,7 @@ void installWebApi() { }); // 获取hls或MP4录制状态 - API_REGIST_INVOKER(api,stopRecord,{ + API_REGIST(api,stopRecord,{ CHECK_SECRET(); CHECK_ARGS("type","vhost","app","stream"); auto status = Recorder::getRecordStatus((Recorder::type)allArgs["type"].as(), From 055c3125790580af1e40f731d4563fcbd426280e Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 5 Dec 2019 14:12:22 +0800 Subject: [PATCH 071/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8DgetRecordStatus?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 5e14fce9..b9e6664f 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -716,7 +716,7 @@ void installWebApi() { }); // 获取hls或MP4录制状态 - API_REGIST(api,stopRecord,{ + API_REGIST(api,getRecordStatus,{ CHECK_SECRET(); CHECK_ARGS("type","vhost","app","stream"); auto status = Recorder::getRecordStatus((Recorder::type)allArgs["type"].as(), From 10dc254510656ee4d86fcd0837c776d206a94b48 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 5 Dec 2019 14:31:44 +0800 Subject: [PATCH 072/272] =?UTF-8?q?=E5=AE=8C=E5=96=84stopRecord=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 9 +++++---- src/Record/Recorder.cpp | 7 ++++--- src/Record/Recorder.h | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index b9e6664f..31810f7e 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -709,10 +709,11 @@ void installWebApi() { API_REGIST(api,stopRecord,{ CHECK_SECRET(); CHECK_ARGS("type","vhost","app","stream"); - Recorder::stopRecord((Recorder::type)allArgs["type"].as(), - allArgs["vhost"], - allArgs["app"], - allArgs["stream"]); + int result = Recorder::stopRecord((Recorder::type)allArgs["type"].as(), + allArgs["vhost"], + allArgs["app"], + allArgs["stream"]); + val["result"] = result; }); // 获取hls或MP4录制状态 diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index 2a1646f1..42d145e3 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -205,9 +205,9 @@ public: return 0; } - void stopRecord(const string &vhost, const string &app, const string &stream_id) { + bool stopRecord(const string &vhost, const string &app, const string &stream_id) { lock_guard lck(_recorder_mtx); - _recorder_map.erase(getRecorderKey(vhost, app, stream_id)); + return _recorder_map.erase(getRecorderKey(vhost, app, stream_id)); } void stopAll(){ @@ -345,13 +345,14 @@ int Recorder::startRecord(Recorder::type type, const string &vhost, const string return -3; } -void Recorder::stopRecord(Recorder::type type, const string &vhost, const string &app, const string &stream_id) { +bool Recorder::stopRecord(Recorder::type type, const string &vhost, const string &app, const string &stream_id) { switch (type){ case type_mp4: return MediaSourceWatcher::Instance().stopRecord(vhost,app,stream_id); case type_hls: return MediaSourceWatcher::Instance().stopRecord(vhost,app,stream_id); } + return false; } void Recorder::stopAll() { diff --git a/src/Record/Recorder.h b/src/Record/Recorder.h index ffb8017c..6172cabd 100644 --- a/src/Record/Recorder.h +++ b/src/Record/Recorder.h @@ -82,7 +82,7 @@ public: * @param app 应用名 * @param stream_id 流id */ - static void stopRecord(type type, const string &vhost, const string &app, const string &stream_id); + static bool stopRecord(type type, const string &vhost, const string &app, const string &stream_id); /** * 停止所有录制,一般程序退出时调用 From 55d258fafc555d75838dbc769c063fcc9fb60c98 Mon Sep 17 00:00:00 2001 From: Gemfield Date: Thu, 5 Dec 2019 19:20:12 +0800 Subject: [PATCH 073/272] Add rtp server --- src/Rtp/PSDecoder.cpp | 49 +++++++ src/Rtp/PSDecoder.h | 57 ++++++++ src/Rtp/RtpDecoder.cpp | 70 ++++++++++ src/Rtp/RtpDecoder.h | 55 ++++++++ src/Rtp/RtpFileLoader.cpp | 79 +++++++++++ src/Rtp/RtpFileLoader.h | 39 ++++++ src/Rtp/RtpProcess.cpp | 286 ++++++++++++++++++++++++++++++++++++++ src/Rtp/RtpProcess.h | 74 ++++++++++ src/Rtp/RtpSelector.cpp | 99 +++++++++++++ src/Rtp/RtpSelector.h | 55 ++++++++ src/Rtp/RtpSession.cpp | 72 ++++++++++ src/Rtp/RtpSession.h | 54 +++++++ src/Rtp/RtpSplitter.cpp | 53 +++++++ src/Rtp/RtpSplitter.h | 50 +++++++ src/Rtp/UdpRecver.cpp | 50 +++++++ src/Rtp/UdpRecver.h | 52 +++++++ 16 files changed, 1194 insertions(+) create mode 100644 src/Rtp/PSDecoder.cpp create mode 100644 src/Rtp/PSDecoder.h create mode 100644 src/Rtp/RtpDecoder.cpp create mode 100644 src/Rtp/RtpDecoder.h create mode 100644 src/Rtp/RtpFileLoader.cpp create mode 100644 src/Rtp/RtpFileLoader.h create mode 100644 src/Rtp/RtpProcess.cpp create mode 100644 src/Rtp/RtpProcess.h create mode 100644 src/Rtp/RtpSelector.cpp create mode 100644 src/Rtp/RtpSelector.h create mode 100644 src/Rtp/RtpSession.cpp create mode 100644 src/Rtp/RtpSession.h create mode 100644 src/Rtp/RtpSplitter.cpp create mode 100644 src/Rtp/RtpSplitter.h create mode 100644 src/Rtp/UdpRecver.cpp create mode 100644 src/Rtp/UdpRecver.h diff --git a/src/Rtp/PSDecoder.cpp b/src/Rtp/PSDecoder.cpp new file mode 100644 index 00000000..835bc068 --- /dev/null +++ b/src/Rtp/PSDecoder.cpp @@ -0,0 +1,49 @@ +/* +* MIT License +* +* Copyright (c) 2016-2019 Gemfield +* +* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include "PSDecoder.h" + +PSDecoder::PSDecoder() { + _ps_demuxer = ps_demuxer_create([](void* param, + int stream, + int codecid, + int flags, + int64_t pts, + int64_t dts, + const void* data, + size_t bytes){ + PSDecoder *thiz = (PSDecoder *)param; + thiz->onPSDecode(stream, codecid, flags, pts, dts, data, bytes); + },this); +} + +PSDecoder::~PSDecoder() { + ps_demuxer_destroy(_ps_demuxer); +} + +int PSDecoder::decodePS(const uint8_t *data, size_t bytes) { + return ps_demuxer_input(_ps_demuxer,data,bytes); +} diff --git a/src/Rtp/PSDecoder.h b/src/Rtp/PSDecoder.h new file mode 100644 index 00000000..63d50008 --- /dev/null +++ b/src/Rtp/PSDecoder.h @@ -0,0 +1,57 @@ +/* +* MIT License +* +* Copyright (c) 2016-2019 Gemfield +* +* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef RTPPROXY_PSDECODER_H +#define RTPPROXY_PSDECODER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mpeg-ps.h" +#ifdef __cplusplus +} +#endif + +class PSDecoder { +public: + PSDecoder(); + virtual ~PSDecoder(); + int decodePS(const uint8_t *data, size_t bytes); +protected: + virtual void onPSDecode(int stream, + int codecid, + int flags, + int64_t pts, + int64_t dts, + const void *data, + size_t bytes) = 0; +private: + struct ps_demuxer_t *_ps_demuxer = nullptr; +}; + + +#endif //RTPPROXY_PSDECODER_H diff --git a/src/Rtp/RtpDecoder.cpp b/src/Rtp/RtpDecoder.cpp new file mode 100644 index 00000000..a5d40aa8 --- /dev/null +++ b/src/Rtp/RtpDecoder.cpp @@ -0,0 +1,70 @@ +/* +* MIT License +* +* Copyright (c) 2016-2019 Gemfield +* +* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include +#include "Util/logger.h" +#include "RtpDecoder.h" +using namespace toolkit; + +RtpDecoder::RtpDecoder() { + _buffer = std::make_shared(); +} + +RtpDecoder::~RtpDecoder() { + if(_rtp_decoder){ + rtp_payload_decode_destroy(_rtp_decoder); + _rtp_decoder = nullptr; + } +} + +void RtpDecoder::decodeRtp(const void *data, int bytes,const char *type_name) { + if(!_rtp_decoder){ + static rtp_payload_t s_func= { + [](void* param, int bytes){ + RtpDecoder *obj = (RtpDecoder *)param; + obj->_buffer->setCapacity(bytes); + return (void *)obj->_buffer->data(); + }, + [](void* param, void* packet){ + //do nothing + }, + [](void* param, const void *packet, int bytes, uint32_t timestamp, int flags){ + RtpDecoder *obj = (RtpDecoder *)param; + obj->onRtpDecode(packet, bytes, timestamp, flags); + } + }; + + uint8_t rtp_type = 0x7F & ((uint8_t *) data)[1]; + InfoL << "rtp type:" << (int) rtp_type; + _rtp_decoder = rtp_payload_decode_create(rtp_type, type_name, &s_func, this); + if (!_rtp_decoder) { + WarnL << "unsupported rtp type:" << (int) rtp_type << ",size:" << bytes << ",hexdump" << hexdump(data, bytes > 16 ? 16 : bytes); + } + } + if(_rtp_decoder){ + rtp_payload_decode_input(_rtp_decoder,data,bytes); + } +} diff --git a/src/Rtp/RtpDecoder.h b/src/Rtp/RtpDecoder.h new file mode 100644 index 00000000..de2ad240 --- /dev/null +++ b/src/Rtp/RtpDecoder.h @@ -0,0 +1,55 @@ +/* +* MIT License +* +* Copyright (c) 2016-2019 Gemfield +* +* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef IPTV_RTPTOTS_H +#define IPTV_RTPTOTS_H + +#include "Network/Buffer.h" +using namespace toolkit; +#ifdef __cplusplus +extern "C" { +#endif + +#include "rtp-payload.h" +#include "mpeg-ts.h" +#ifdef __cplusplus +} +#endif + +class RtpDecoder { +public: + RtpDecoder(); + virtual ~RtpDecoder(); +protected: + void decodeRtp(const void *data, int bytes,const char *type_name); + virtual void onRtpDecode(const void *packet, int bytes, uint32_t timestamp, int flags) = 0; +private: + void *_rtp_decoder = nullptr; + BufferRaw::Ptr _buffer; +}; + + +#endif //IPTV_RTPTOTS_H diff --git a/src/Rtp/RtpFileLoader.cpp b/src/Rtp/RtpFileLoader.cpp new file mode 100644 index 00000000..785304fc --- /dev/null +++ b/src/Rtp/RtpFileLoader.cpp @@ -0,0 +1,79 @@ +/* +* MIT License +* +* Copyright (c) 2016-2019 Gemfield +* +* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include +#include +#include +#include "RtpFileLoader.h" +#include "Util/logger.h" +#include "RtpSelector.h" +using namespace toolkit; + +bool RtpFileLoader::loadFile(const char *path) { + FILE *fp = fopen(path, "rb"); + if (!fp) { + WarnL << "open file failed:" << path; + return false; + } + + uint32_t timeStamp_last = 0; + uint16_t len; + char rtp[2 * 1024]; + while (true) { + if (2 != fread(&len, 1, 2, fp)) { + WarnL; + break; + } + len = ntohs(len); + if (len < 12 || len > sizeof(rtp)) { + WarnL << len; + break; + } + + if (len != fread(rtp, 1, len, fp)) { + WarnL; + break; + } + + uint32_t timeStamp; + memcpy(&timeStamp, rtp + 4, 4); + timeStamp = ntohl(timeStamp); + timeStamp /= 90; + + if(timeStamp_last){ + auto diff = timeStamp - timeStamp_last; + if(diff > 0){ + usleep(diff * 1000); + } + } + + timeStamp_last = timeStamp; + RtpSelector::Instance().inputRtp(rtp,len, nullptr); + } + + fclose(fp); + return true; +} diff --git a/src/Rtp/RtpFileLoader.h b/src/Rtp/RtpFileLoader.h new file mode 100644 index 00000000..a715d618 --- /dev/null +++ b/src/Rtp/RtpFileLoader.h @@ -0,0 +1,39 @@ +/* +* MIT License +* +* Copyright (c) 2016-2019 Gemfield +* +* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef RTPPROXY_RTPFILELOADER_H +#define RTPPROXY_RTPFILELOADER_H + + +class RtpFileLoader { +public: + RtpFileLoader(){}; + ~RtpFileLoader(){}; + static bool loadFile(const char *path); +}; + + +#endif //RTPPROXY_RTPFILELOADER_H diff --git a/src/Rtp/RtpProcess.cpp b/src/Rtp/RtpProcess.cpp new file mode 100644 index 00000000..f74cb8da --- /dev/null +++ b/src/Rtp/RtpProcess.cpp @@ -0,0 +1,286 @@ +/* +* MIT License +* +* Copyright (c) 2016-2019 Gemfield +* +* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include "RtpProcess.h" +#include "Util/File.h" +#include "Util/logger.h" +#include "Extension/H265.h" +#include "Extension/H264.h" +#include "Extension/AAC.h" + +using namespace toolkit; + +namespace dump { +const string kEnable = "dump.enable"; +const string kDir = "dump.dir"; +const string kChcekSource = "dump.checkSource"; +onceToken token([](){ + mINI::Instance()[kEnable] = 0; + mINI::Instance()[kDir] = "./dump/"; + mINI::Instance()[kChcekSource] = 1; +}); +}//namespace dump + +namespace Rtp { +const string kRtpType = "rtp.rtp_type"; +const string kTimeoutSec = "rtp.timeoutSec"; +onceToken token([](){ + mINI::Instance()[kRtpType] = "MP2P"; + mINI::Instance()[kTimeoutSec] = 15; +}); +}//namespace dump + +/** + * 合并一些时间戳相同的frame + */ +class FrameMerger { +public: + FrameMerger() = default; + virtual ~FrameMerger() = default; + + void inputFrame(const Frame::Ptr &frame,const function &cb){ + if (!_frameCached.empty() && _frameCached.back()->dts() != frame->dts()) { + Frame::Ptr back = _frameCached.back(); + Buffer::Ptr merged_frame = back; + if(_frameCached.size() != 1){ + string merged; + _frameCached.for_each([&](const Frame::Ptr &frame){ + merged.append(frame->data(),frame->size()); + }); + merged_frame = std::make_shared(std::move(merged)); + } + cb(back->dts(),back->pts(),merged_frame); + _frameCached.clear(); + } + _frameCached.emplace_back(Frame::getCacheAbleFrame(frame)); + } +private: + List _frameCached; +}; + +string printSSRC(uint32_t ui32Ssrc) { + char tmp[9] = { 0 }; + ui32Ssrc = htonl(ui32Ssrc); + uint8_t *pSsrc = (uint8_t *) &ui32Ssrc; + for (int i = 0; i < 4; i++) { + sprintf(tmp + 2 * i, "%02X", pSsrc[i]); + } + return tmp; +} + +static string printAddress(const struct sockaddr *addr){ + return StrPrinter << inet_ntoa(((struct sockaddr_in *) addr)->sin_addr) << ":" << ntohs(((struct sockaddr_in *) addr)->sin_port); +} + +RtpProcess::RtpProcess(uint32_t ssrc) { + _ssrc = ssrc; + _track = std::make_shared(); + _track = std::make_shared(); + _track->_interleaved = 0; + _track->_samplerate = 90000; + _track->_type = TrackVideo; + _track->_ssrc = _ssrc; + DebugL << printSSRC(_ssrc); + _muxer = std::make_shared(DEFAULT_VHOST,"rtp",printSSRC(_ssrc)); + + GET_CONFIG(bool,dump_enable,dump::kEnable); + GET_CONFIG(string,dump_dir,dump::kDir); + { + FILE *fp = dump_enable ? File::createfile_file(File::absolutePath(printSSRC(_ssrc) + ".rtp",dump_dir).data(),"wb") : nullptr; + if(fp){ + _save_file_rtp.reset(fp,[](FILE *fp){ + fclose(fp); + }); + } + } + + { + FILE *fp = dump_enable ? File::createfile_file(File::absolutePath(printSSRC(_ssrc) + ".mp2",dump_dir).data(),"wb") : nullptr; + if(fp){ + _save_file_ps.reset(fp,[](FILE *fp){ + fclose(fp); + }); + } + } + + { + FILE *fp = dump_enable ? File::createfile_file(File::absolutePath(printSSRC(_ssrc) + ".video",dump_dir).data(),"wb") : nullptr; + if(fp){ + _save_file_video.reset(fp,[](FILE *fp){ + fclose(fp); + }); + } + } + _merger = std::make_shared(); +} + +RtpProcess::~RtpProcess() { + if(_addr){ + DebugL << printSSRC(_ssrc) << " " << printAddress(_addr); + delete _addr; + }else{ + DebugL << printSSRC(_ssrc); + } +} + +bool RtpProcess::inputRtp(const char *data, int data_len,const struct sockaddr *addr) { + GET_CONFIG(bool,check_source,dump::kChcekSource); + //检查源是否合法 + if(!_addr){ + _addr = new struct sockaddr; + memcpy(_addr,addr, sizeof(struct sockaddr)); + DebugL << "RtpProcess(" << printSSRC(_ssrc) << ") bind to address:" << printAddress(_addr); + } + + if(check_source && memcmp(_addr,addr,sizeof(struct sockaddr)) != 0){ + DebugL << "RtpProcess(" << printSSRC(_ssrc) << ") address dismatch:" << printAddress(addr) << " != " << printAddress(_addr); + return false; + } + + _last_rtp_time.resetTime(); + return handleOneRtp(0,_track,(unsigned char *)data,data_len); +} + +void RtpProcess::onRtpSorted(const RtpPacket::Ptr &rtp, int) { + if(rtp->sequence != _sequence + 1){ + WarnL << rtp->sequence << " != " << _sequence << "+1"; + } + _sequence = rtp->sequence; + + if(_save_file_rtp){ + uint16_t size = rtp->size() - 4; + size = htons(size); + fwrite((uint8_t *) &size, 2, 1, _save_file_rtp.get()); + fwrite((uint8_t *) rtp->data() + 4, rtp->size() - 4, 1, _save_file_rtp.get()); + } + + GET_CONFIG(string,rtp_type,::Rtp::kRtpType); + decodeRtp(rtp->data() + 4 ,rtp->size() - 4,rtp_type.data()); +} + +void RtpProcess::onRtpDecode(const void *packet, int bytes, uint32_t, int flags) { + if(_save_file_ps){ + fwrite((uint8_t *)packet,bytes, 1, _save_file_ps.get()); + } + + auto ret = decodePS((uint8_t *)packet,bytes); + if(ret != bytes){ + WarnL << ret << " != " << bytes << " " << flags; + } +} + +void RtpProcess::onPSDecode(int stream, + int codecid, + int flags, + int64_t pts, + int64_t dts, + const void *data, + size_t bytes) { + + switch (codecid) { + case STREAM_VIDEO_H264: { + if (!_codecid_video) { + //获取到视频 + _codecid_video = codecid; + InfoL << "got video track: H264"; + auto track = std::make_shared(); + _muxer->addTrack(track); + } + + if (codecid != _codecid_video) { + WarnL << "video track change to H264 from codecid:" << _codecid_video; + return; + } + + if(_save_file_video){ + fwrite((uint8_t *)data,bytes, 1, _save_file_video.get()); + } + auto frame = std::make_shared((char *) data, bytes, dts / 90, pts / 90,0); + _merger->inputFrame(frame,[this](uint32_t dts, uint32_t pts, const Buffer::Ptr &buffer) { + _muxer->inputFrame(std::make_shared(buffer->data(), buffer->size(), dts, pts,4)); + }); + break; + } + + case STREAM_VIDEO_H265: { + if (!_codecid_video) { + //获取到视频 + _codecid_video = codecid; + InfoL << "got video track: H265"; + auto track = std::make_shared(); + _muxer->addTrack(track); + } + if (codecid != _codecid_video) { + WarnL << "video track change to H265 from codecid:" << _codecid_video; + return; + } + if(_save_file_video){ + fwrite((uint8_t *)data,bytes, 1, _save_file_video.get()); + } + auto frame = std::make_shared((char *) data, bytes, dts / 90, pts / 90, 0); + _merger->inputFrame(frame,[this](uint32_t dts, uint32_t pts, const Buffer::Ptr &buffer) { + _muxer->inputFrame(std::make_shared(buffer->data(), buffer->size(), dts, pts, 4)); + }); + break; + } + + case STREAM_AUDIO_AAC: { + if (!_codecid_audio) { + //获取到音频 + _codecid_audio = codecid; + InfoL << "got audio track: AAC"; + auto track = std::make_shared(); + _muxer->addTrack(track); + } + + if (codecid != _codecid_audio) { + WarnL << "audio track change to AAC from codecid:" << _codecid_audio; + return; + } + _muxer->inputFrame(std::make_shared((char *) data, bytes, dts / 90, 7)); + break; + } + default: + WarnL << "unsupported codec type:" << codecid; + return; + } +} + +bool RtpProcess::alive() { + GET_CONFIG(int,timeoutSec,::Rtp::kTimeoutSec) + if(_last_rtp_time.elapsedTime() / 1000 < timeoutSec){ + return true; + } + return false; +} + +string RtpProcess::get_peer_ip() { + return inet_ntoa(((struct sockaddr_in *) _addr)->sin_addr); +} + +uint16_t RtpProcess::get_peer_port() { + return ntohs(((struct sockaddr_in *) _addr)->sin_port); +} diff --git a/src/Rtp/RtpProcess.h b/src/Rtp/RtpProcess.h new file mode 100644 index 00000000..ac1c4874 --- /dev/null +++ b/src/Rtp/RtpProcess.h @@ -0,0 +1,74 @@ +/* +* MIT License +* +* Copyright (c) 2016-2019 Gemfield +* +* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef RTPPROXY_RTPDECODER_H +#define RTPPROXY_RTPDECODER_H + +#include "Rtsp/RtpReceiver.h" +#include "RtpDecoder.h" +#include "PSDecoder.h" +#include "Common/Device.h" +using namespace mediakit; + +string printSSRC(uint32_t ui32Ssrc); + +class FrameMerger; +class RtpProcess : public RtpReceiver , public RtpDecoder , public PSDecoder { +public: + typedef std::shared_ptr Ptr; + RtpProcess(uint32_t ssrc); + ~RtpProcess(); + bool inputRtp(const char *data,int data_len, const struct sockaddr *addr); + bool alive(); + string get_peer_ip(); + uint16_t get_peer_port(); +protected: + void onRtpSorted(const RtpPacket::Ptr &rtp, int track_index) override ; + void onRtpDecode(const void *packet, int bytes, uint32_t timestamp, int flags) override; + void onPSDecode(int stream, + int codecid, + int flags, + int64_t pts, + int64_t dts, + const void *data, + size_t bytes) override ; +private: + std::shared_ptr _save_file_rtp; + std::shared_ptr _save_file_ps; + std::shared_ptr _save_file_video; + uint32_t _ssrc; + SdpTrack::Ptr _track; + struct sockaddr *_addr = nullptr; + uint16_t _sequence = 0; + int _codecid_video = 0; + int _codecid_audio = 0; + MultiMediaSourceMuxer::Ptr _muxer; + std::shared_ptr _merger; + Ticker _last_rtp_time; +}; + + +#endif //RTPPROXY_RTPDECODER_H diff --git a/src/Rtp/RtpSelector.cpp b/src/Rtp/RtpSelector.cpp new file mode 100644 index 00000000..a407ef7a --- /dev/null +++ b/src/Rtp/RtpSelector.cpp @@ -0,0 +1,99 @@ +/* +* MIT License +* +* Copyright (c) 2016-2019 Gemfield +* +* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include "RtpSelector.h" + +INSTANCE_IMP(RtpSelector); + +bool RtpSelector::inputRtp(const char *data, int data_len,const struct sockaddr *addr) { + if(_last_rtp_time.elapsedTime() > 3000){ + _last_rtp_time.resetTime(); + onManager(); + } + auto ssrc = getSSRC(data,data_len); + if(!ssrc){ + WarnL << "get ssrc from rtp failed:" << data_len; + return false; + } + auto process = getProcess(ssrc, true); + if(process){ + return process->inputRtp(data,data_len, addr); + } + return false; +} + +uint32_t RtpSelector::getSSRC(const char *data, int data_len) { + if(data_len < 12){ + return 0; + } + uint32_t *ssrc_ptr = (uint32_t *)(data + 8); + return ntohl(*ssrc_ptr); +} + +RtpProcess::Ptr RtpSelector::getProcess(uint32_t ssrc,bool makeNew) { + lock_guard lck(_mtx_map); + auto it = _map_rtp_process.find(ssrc); + if(it == _map_rtp_process.end() && !makeNew){ + return nullptr; + } + RtpProcess::Ptr &ref = _map_rtp_process[ssrc]; + if(!ref){ + ref = std::make_shared(ssrc); + } + return ref; +} + +void RtpSelector::delProcess(uint32_t ssrc,const RtpProcess *ptr) { + lock_guard lck(_mtx_map); + auto it = _map_rtp_process.find(ssrc); + if(it == _map_rtp_process.end()){ + return; + } + + if(it->second.get() != ptr){ + return; + } + + _map_rtp_process.erase(it); +} + +void RtpSelector::onManager() { + lock_guard lck(_mtx_map); + for (auto it = _map_rtp_process.begin(); it != _map_rtp_process.end();) { + if (it->second->alive()) { + ++it; + continue; + } + WarnL << "RtpProcess timeout:" << printSSRC(it->first); + it = _map_rtp_process.erase(it); + } +} + +RtpSelector::RtpSelector() { +} + +RtpSelector::~RtpSelector() { +} diff --git a/src/Rtp/RtpSelector.h b/src/Rtp/RtpSelector.h new file mode 100644 index 00000000..e7011c4a --- /dev/null +++ b/src/Rtp/RtpSelector.h @@ -0,0 +1,55 @@ +/* +* MIT License +* +* Copyright (c) 2016-2019 Gemfield +* +* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef RTPPROXY_RTPSELECTOR_H +#define RTPPROXY_RTPSELECTOR_H + +#include +#include +#include +#include "RtpProcess.h" + + +class RtpSelector : public std::enable_shared_from_this{ +public: + RtpSelector(); + ~RtpSelector(); + + static RtpSelector &Instance(); + bool inputRtp(const char *data,int data_len,const struct sockaddr *addr); + static uint32_t getSSRC(const char *data,int data_len); + RtpProcess::Ptr getProcess(uint32_t ssrc,bool makeNew); + void delProcess(uint32_t ssrc,const RtpProcess *ptr); +private: + void onManager(); +private: + unordered_map _map_rtp_process; + recursive_mutex _mtx_map; + Ticker _last_rtp_time; +}; + + +#endif //RTPPROXY_RTPSELECTOR_H diff --git a/src/Rtp/RtpSession.cpp b/src/Rtp/RtpSession.cpp new file mode 100644 index 00000000..a7faa4a7 --- /dev/null +++ b/src/Rtp/RtpSession.cpp @@ -0,0 +1,72 @@ +/* +* MIT License +* +* Copyright (c) 2016-2019 Gemfield +* +* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ +#include "RtpSession.h" +#include "RtpSelector.h" + +RtpSession::RtpSession(const Socket::Ptr &sock) : TcpSession(sock) { + DebugP(this); + socklen_t addr_len = sizeof(addr); + getpeername(sock->rawFD(), &addr, &addr_len); +} +RtpSession::~RtpSession() { + DebugP(this); + if(_ssrc){ + RtpSelector::Instance().delProcess(_ssrc,_process.get()); + } +} + +void RtpSession::onRecv(const Buffer::Ptr &data) { + try { + RtpSplitter::input(data->data(), data->size()); + } catch (SockException &ex) { + shutdown(ex); + } catch (std::exception &ex) { + shutdown(SockException(Err_other, ex.what())); + } +} + +void RtpSession::onError(const SockException &err) { + WarnL << _ssrc << " " << err.what(); +} + +void RtpSession::onManager() { + if(_process && !_process->alive()){ + shutdown(SockException(Err_timeout, "receive rtp timeout")); + } + + if(!_process && _ticker.createdTime() > 10 * 1000){ + shutdown(SockException(Err_timeout, "illegal connection")); + } +} + +void RtpSession::onRtpPacket(const char *data, uint64_t len) { + if(!_ssrc){ + _ssrc = RtpSelector::getSSRC(data,len); + _process = RtpSelector::Instance().getProcess(_ssrc, true); + } + _process->inputRtp(data + 2,len - 2,&addr); + _ticker.resetTime(); +} diff --git a/src/Rtp/RtpSession.h b/src/Rtp/RtpSession.h new file mode 100644 index 00000000..68ce863c --- /dev/null +++ b/src/Rtp/RtpSession.h @@ -0,0 +1,54 @@ +/* +* MIT License +* +* Copyright (c) 2016-2019 Gemfield +* +* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef RTPPROXY_RTPSESSION_H +#define RTPPROXY_RTPSESSION_H + +#include "Network/TcpSession.h" +#include "RtpSplitter.h" +#include "RtpProcess.h" +#include "Util/TimeTicker.h" + +using namespace toolkit; + +class RtpSession : public TcpSession , public RtpSplitter{ +public: + RtpSession(const Socket::Ptr &sock); + ~RtpSession() override; + void onRecv(const Buffer::Ptr &) override; + void onError(const SockException &err) override; + void onManager() override; +private: + void onRtpPacket(const char *data,uint64_t len) override; +private: + uint32_t _ssrc = 0; + RtpProcess::Ptr _process; + Ticker _ticker; + struct sockaddr addr; +}; + + +#endif //RTPPROXY_RTPSESSION_H diff --git a/src/Rtp/RtpSplitter.cpp b/src/Rtp/RtpSplitter.cpp new file mode 100644 index 00000000..b28697b6 --- /dev/null +++ b/src/Rtp/RtpSplitter.cpp @@ -0,0 +1,53 @@ +/* +* MIT License +* +* Copyright (c) 2016-2019 Gemfield +* +* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include "RtpSplitter.h" + +RtpSplitter::RtpSplitter() { +} + +RtpSplitter::~RtpSplitter() { +} + +const char *RtpSplitter::onSearchPacketTail(const char *data, int len) { + //这是rtp包 + if(len < 2){ + //数据不够 + return nullptr; + } + uint16_t length = (((uint8_t *)data)[0] << 8) | ((uint8_t *)data)[1]; + if(len < length + 2){ + //数据不够 + return nullptr; + } + //返回rtp包末尾 + return data + 2 + length; +} + +int64_t RtpSplitter::onRecvHeader(const char *data, uint64_t len) { + onRtpPacket(data,len); + return 0; +} \ No newline at end of file diff --git a/src/Rtp/RtpSplitter.h b/src/Rtp/RtpSplitter.h new file mode 100644 index 00000000..66dc1c88 --- /dev/null +++ b/src/Rtp/RtpSplitter.h @@ -0,0 +1,50 @@ +/* +* MIT License +* +* Copyright (c) 2016-2019 Gemfield +* +* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef RTPPROXY_RTPSPLITTER_H +#define RTPPROXY_RTPSPLITTER_H + +#include "Http/HttpRequestSplitter.h" +using namespace mediakit; + +class RtpSplitter : public HttpRequestSplitter{ +public: + RtpSplitter(); + virtual ~RtpSplitter(); +protected: + /** + * 收到rtp包回调 + * @param data + * @param len + */ + virtual void onRtpPacket(const char *data,uint64_t len) = 0; +protected: + const char *onSearchPacketTail(const char *data,int len) override ; + int64_t onRecvHeader(const char *data,uint64_t len) override; +}; + + +#endif //RTPPROXY_RTPSPLITTER_H diff --git a/src/Rtp/UdpRecver.cpp b/src/Rtp/UdpRecver.cpp new file mode 100644 index 00000000..5ac3ecd5 --- /dev/null +++ b/src/Rtp/UdpRecver.cpp @@ -0,0 +1,50 @@ +/* +* MIT License +* +* Copyright (c) 2016-2019 Gemfield +* +* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include "UdpRecver.h" +#include "RtpSelector.h" +UdpRecver::UdpRecver() { +} + +UdpRecver::~UdpRecver() { +} + +bool UdpRecver::initSock(uint16_t local_port,const char *local_ip) { + _sock.reset(new Socket(nullptr, false)); + onceToken token(nullptr,[&](){ + SockUtil::setRecvBuf(_sock->rawFD(),4 * 1024 * 1024); + }); + + auto &ref = RtpSelector::Instance(); + _sock->setOnRead([&ref](const Buffer::Ptr &buf, struct sockaddr *addr, int ){ + ref.inputRtp(buf->data(),buf->size(),addr); + }); + return _sock->bindUdpSock(local_port,local_ip); +} + +EventPoller::Ptr UdpRecver::getPoller() { + return _sock->getPoller(); +} diff --git a/src/Rtp/UdpRecver.h b/src/Rtp/UdpRecver.h new file mode 100644 index 00000000..4f412a49 --- /dev/null +++ b/src/Rtp/UdpRecver.h @@ -0,0 +1,52 @@ +/* +* MIT License +* +* Copyright (c) 2016-2019 Gemfield +* +* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#ifndef ZLMEDIAKIT_UDPRECVER_H +#define ZLMEDIAKIT_UDPRECVER_H + +#include +#include "Network/Socket.h" + +using namespace std; +using namespace toolkit; +/** + * 组播接收器 + */ +class UdpRecver { +public: + typedef std::shared_ptr Ptr; + typedef function onRecv; + + UdpRecver(); + virtual ~UdpRecver(); + bool initSock(uint16_t local_port,const char *local_ip = "0.0.0.0"); + EventPoller::Ptr getPoller(); +protected: + Socket::Ptr _sock; +}; + + +#endif //ZLMEDIAKIT_UDPRECVER_H From 55814070f89a61db48d98abcd8d1fabd46d8e3f5 Mon Sep 17 00:00:00 2001 From: Gemfield Date: Thu, 5 Dec 2019 19:53:55 +0800 Subject: [PATCH 074/272] Update CMakeFiles for RTP Proxy --- CMakeLists.txt | 56 +++++++++++++++++++++++----------------- server/WebApi.cpp | 14 ++++++++++ tests/test_rtp.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 111 insertions(+), 23 deletions(-) create mode 100644 tests/test_rtp.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f90e5cd3..2cec4ab8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,8 @@ LINK_DIRECTORIES(${LIBRARY_OUTPUT_PATH}) #设置工程源码根目录 set(ToolKit_Root ${CMAKE_CURRENT_SOURCE_DIR}/3rdpart/ZLToolKit/src) set(MediaKit_Root ${CMAKE_CURRENT_SOURCE_DIR}/src) +set(MediaServer_Root ${CMAKE_CURRENT_SOURCE_DIR}/3rdpart/media-server) + #设置头文件目录 INCLUDE_DIRECTORIES(${ToolKit_Root}) @@ -59,22 +61,8 @@ set(ENABLE_FAAC false) set(ENABLE_X264 false) set(ENABLE_MP4RECORD true) -#添加两个静态库 -if(ENABLE_HLS) - message(STATUS "ENABLE_HLS defined") - add_definitions(-DENABLE_HLS) - set(MediaServer_Root ${CMAKE_CURRENT_SOURCE_DIR}/3rdpart/media-server) - set(LINK_LIB_LIST zlmediakit zltoolkit mpeg) -else() - set(LINK_LIB_LIST zlmediakit zltoolkit) -endif() +set(LINK_LIB_LIST zlmediakit zltoolkit) -if(ENABLE_MP4RECORD) - message(STATUS "ENABLE_MP4RECORD defined") - add_definitions(-DENABLE_MP4RECORD) - set(MediaServer_Root ${CMAKE_CURRENT_SOURCE_DIR}/3rdpart/media-server) - list(APPEND LINK_LIB_LIST mov flv) -endif() #查找openssl是否安装 find_package(OpenSSL QUIET) if (OPENSSL_FOUND AND ENABLE_OPENSSL) @@ -122,32 +110,42 @@ if (FAAC_FOUND AND ENABLE_FAAC) endif () -#添加库 -add_library(zltoolkit STATIC ${ToolKit_src_list}) -add_library(zlmediakit STATIC ${MediaKit_src_list}) - - set(VS_FALGS "/wd4819 /wd4996 /wd4018 /wd4267 /wd4244 /wd4101 /wd4828 /wd4309 /wd4573" ) -#libmpeg + +#添加mpeg用于支持ts生成 if(ENABLE_HLS) + message(STATUS "ENABLE_HLS defined") + add_definitions(-DENABLE_HLS) + aux_source_directory(${MediaServer_Root}/libmpeg/include src_mpeg) aux_source_directory(${MediaServer_Root}/libmpeg/source src_mpeg) include_directories(${MediaServer_Root}/libmpeg/include) + add_library(mpeg STATIC ${src_mpeg}) + list(APPEND LINK_LIB_LIST mpeg) + if(WIN32) set_target_properties(mpeg PROPERTIES COMPILE_FLAGS ${VS_FALGS} ) endif(WIN32) endif() +#添加mov、flv库用于MP4录制 if(ENABLE_MP4RECORD) + message(STATUS "ENABLE_MP4RECORD defined") + add_definitions(-DENABLE_MP4RECORD) + aux_source_directory(${MediaServer_Root}/libmov/include src_mov) aux_source_directory(${MediaServer_Root}/libmov/source src_mov) include_directories(${MediaServer_Root}/libmov/include) + aux_source_directory(${MediaServer_Root}/libflv/include src_flv) aux_source_directory(${MediaServer_Root}/libflv/source src_flv) include_directories(${MediaServer_Root}/libflv/include) - add_library(mov STATIC ${src_mov}) + + add_library(mov STATIC ${src_mov}) add_library(flv STATIC ${src_flv}) + list(APPEND LINK_LIB_LIST mov flv) + if(WIN32) set_target_properties(mov flv PROPERTIES COMPILE_FLAGS ${VS_FALGS} ) endif(WIN32) @@ -163,6 +161,19 @@ if(${CMAKE_BUILD_TYPE} MATCHES "Release") endif() endif() + +#添加rtp库用于rtp转ps/ts +aux_source_directory(${MediaServer_Root}/librtp/include src_rtp) +aux_source_directory(${MediaServer_Root}/librtp/source src_rtp) +aux_source_directory(${MediaServer_Root}/librtp/payload src_rtp) +include_directories(${MediaServer_Root}/librtp/include) +add_library(rtp STATIC ${src_rtp}) +list(APPEND LINK_LIB_LIST rtp) + +#添加库 +add_library(zltoolkit STATIC ${ToolKit_src_list}) +add_library(zlmediakit STATIC ${MediaKit_src_list}) + if (WIN32) list(APPEND LINK_LIB_LIST WS2_32 Iphlpapi shlwapi) set_target_properties(zltoolkit PROPERTIES COMPILE_FLAGS ${VS_FALGS} ) @@ -172,7 +183,6 @@ elseif(NOT ANDROID OR IOS) endif () - #测试程序 add_subdirectory(tests) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 31810f7e..6fdf6192 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -46,6 +46,7 @@ #include "WebApi.h" #include "WebHook.h" #include "Thread/WorkThreadPool.h" +#include "Rtp/RtpSelector.h" #if !defined(_WIN32) #include "FFmpegSource.h" @@ -692,6 +693,19 @@ void installWebApi() { invoker.responseFile(headerIn,StrCaseMap(),exePath()); }); + API_REGIST(api,getSsrcInfo,{ + CHECK_SECRET(); + CHECK_ARGS("ssrc"); + auto process = RtpSelector::Instance().getProcess(allArgs["ssrc"],false); + if(!process){ + val["exist"] = false; + return; + } + val["exist"] = true; + val["peer_ip"] = process->get_peer_ip(); + val["peer_port"] = process->get_peer_port(); + }); + // 开始录制hls或MP4 API_REGIST(api,startRecord,{ CHECK_SECRET(); diff --git a/tests/test_rtp.cpp b/tests/test_rtp.cpp new file mode 100644 index 00000000..864e1655 --- /dev/null +++ b/tests/test_rtp.cpp @@ -0,0 +1,64 @@ +/* + * MIT License + * + * Copyright (c) 2016-2019 Gemfield + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include "Util/MD5.h" +#include "Util/File.h" +#include "Util/logger.h" +#include "Util/SSLBox.h" +#include "Util/util.h" +#include "Network/TcpServer.h" +#include "Common/config.h" +#include "Rtsp/RtspSession.h" +#include "Rtmp/RtmpSession.h" +#include "Http/HttpSession.h" +#include "Rtp/RtpFileLoader.h" + +using namespace std; +using namespace toolkit; +using namespace mediakit; + +int main(int argc,char *argv[]) { + { + //设置日志 + Logger::Instance().add(std::make_shared("ConsoleChannel")); + //启动异步日志线程 + Logger::Instance().setWriter(std::make_shared()); + loadIniConfig((exeDir() + "config.ini").data()); + TcpServer::Ptr rtspSrv(new TcpServer()); + TcpServer::Ptr rtmpSrv(new TcpServer()); + TcpServer::Ptr httpSrv(new TcpServer()); + rtspSrv->start(554);//默认554 + rtmpSrv->start(1935);//默认1935 + httpSrv->start(80);//默认80 + RtpFileLoader::loadFile(argv[1]); + } + return 0; +} + + + From 0d9bdec1829f09fb7f0ffdab00bfba354e53b85e Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 6 Dec 2019 11:54:10 +0800 Subject: [PATCH 075/272] =?UTF-8?q?=E6=94=AF=E6=8C=81rtp=E4=BB=A3=E7=90=86?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/media-server | 2 +- CMakeLists.txt | 84 ++++++++++++---------------------- LICENSE | 2 + README.md | 3 ++ conf/config.ini | 12 +++++ server/WebApi.cpp | 2 + server/main.cpp | 46 +++++++++++++------ src/Common/config.cpp | 22 +++++++++ src/Common/config.h | 16 ++++--- src/Rtp/PSDecoder.cpp | 62 +++++++++++++++----------- src/Rtp/PSDecoder.h | 72 +++++++++++++++--------------- src/Rtp/RtpDecoder.cpp | 56 +++++++++++++---------- src/Rtp/RtpDecoder.h | 67 +++++++++++++--------------- src/Rtp/RtpFileLoader.cpp | 79 -------------------------------- src/Rtp/RtpFileLoader.h | 39 ---------------- src/Rtp/RtpProcess.cpp | 94 ++++++++++++++++----------------------- src/Rtp/RtpProcess.h | 63 ++++++++++++++------------ src/Rtp/RtpSelector.cpp | 54 ++++++++++++---------- src/Rtp/RtpSelector.h | 61 +++++++++++++------------ src/Rtp/RtpSession.cpp | 54 ++++++++++++---------- src/Rtp/RtpSession.h | 61 +++++++++++++------------ src/Rtp/RtpSplitter.cpp | 55 ++++++++++++----------- src/Rtp/RtpSplitter.h | 61 +++++++++++++------------ src/Rtp/UdpRecver.cpp | 54 ++++++++++++---------- src/Rtp/UdpRecver.h | 56 ++++++++++++----------- tests/test_rtp.cpp | 83 +++++++++++++++++++++++++++------- 26 files changed, 631 insertions(+), 629 deletions(-) delete mode 100644 src/Rtp/RtpFileLoader.cpp delete mode 100644 src/Rtp/RtpFileLoader.h diff --git a/3rdpart/media-server b/3rdpart/media-server index 678678b2..358b7bd3 160000 --- a/3rdpart/media-server +++ b/3rdpart/media-server @@ -1 +1 @@ -Subproject commit 678678b2ee7118d21d33a90a303698e0da02790c +Subproject commit 358b7bd3bf89564371fed48797ad396882f9d130 diff --git a/CMakeLists.txt b/CMakeLists.txt index 2cec4ab8..db0117e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,29 +30,15 @@ endif () LINK_DIRECTORIES(${LIBRARY_OUTPUT_PATH}) - #设置工程源码根目录 set(ToolKit_Root ${CMAKE_CURRENT_SOURCE_DIR}/3rdpart/ZLToolKit/src) set(MediaKit_Root ${CMAKE_CURRENT_SOURCE_DIR}/src) set(MediaServer_Root ${CMAKE_CURRENT_SOURCE_DIR}/3rdpart/media-server) - #设置头文件目录 INCLUDE_DIRECTORIES(${ToolKit_Root}) INCLUDE_DIRECTORIES(${MediaKit_Root}) -#收集源代码 -file(GLOB ToolKit_src_list ${ToolKit_Root}/*/*.cpp ${ToolKit_Root}/*/*.h ${ToolKit_Root}/*/*.c) -file(GLOB MediaKit_src_list ${MediaKit_Root}/*/*.cpp ${MediaKit_Root}/*/*.h ${MediaKit_Root}/*/*.c) - -#去除win32的适配代码 -if (NOT WIN32) - list(REMOVE_ITEM ToolKit_src_list ${ToolKit_Root}/win32/getopt.c) -else() - #防止Windows.h包含Winsock.h - add_definitions(-DWIN32_LEAN_AND_MEAN -DMP4V2_NO_STDINT_DEFS) -endif () - set(ENABLE_HLS true) set(ENABLE_OPENSSL true) set(ENABLE_MYSQL false) @@ -60,6 +46,7 @@ set(ENABLE_MP4V2 true) set(ENABLE_FAAC false) set(ENABLE_X264 false) set(ENABLE_MP4RECORD true) +set(ENABLE_RTPPROXY true) set(LINK_LIB_LIST zlmediakit zltoolkit) @@ -109,6 +96,15 @@ if (FAAC_FOUND AND ENABLE_FAAC) list(APPEND LINK_LIB_LIST ${FAAC_LIBRARIES}) endif () +if(${CMAKE_BUILD_TYPE} MATCHES "Release") + #查找jemalloc是否安装 + find_package(JEMALLOC QUIET) + if(JEMALLOC_FOUND) + message(STATUS "found library:\"${JEMALLOC_LIBRARIES}\"") + include_directories(${JEMALLOC_INCLUDE_DIR}) + list(APPEND LINK_LIB_LIST ${JEMALLOC_LIBRARIES}) + endif() +endif() set(VS_FALGS "/wd4819 /wd4996 /wd4018 /wd4267 /wd4244 /wd4101 /wd4828 /wd4309 /wd4573" ) @@ -151,24 +147,28 @@ if(ENABLE_MP4RECORD) endif(WIN32) endif() -if(${CMAKE_BUILD_TYPE} MATCHES "Release") - #查找jemalloc是否安装 - find_package(JEMALLOC QUIET) - if(JEMALLOC_FOUND) - message(STATUS "found library:\"${JEMALLOC_LIBRARIES}\"") - include_directories(${JEMALLOC_INCLUDE_DIR}) - list(APPEND LINK_LIB_LIST ${JEMALLOC_LIBRARIES}) - endif() +if(ENABLE_RTPPROXY) + #添加rtp库用于rtp转ps/ts + aux_source_directory(${MediaServer_Root}/librtp/include src_rtp) + aux_source_directory(${MediaServer_Root}/librtp/source src_rtp) + aux_source_directory(${MediaServer_Root}/librtp/payload src_rtp) + include_directories(${MediaServer_Root}/librtp/include) + add_library(rtp STATIC ${src_rtp}) + add_definitions(-DENABLE_RTPPROXY) + list(APPEND LINK_LIB_LIST rtp) endif() +#收集源代码 +file(GLOB ToolKit_src_list ${ToolKit_Root}/*/*.cpp ${ToolKit_Root}/*/*.h ${ToolKit_Root}/*/*.c) +file(GLOB MediaKit_src_list ${MediaKit_Root}/*/*.cpp ${MediaKit_Root}/*/*.h ${MediaKit_Root}/*/*.c) -#添加rtp库用于rtp转ps/ts -aux_source_directory(${MediaServer_Root}/librtp/include src_rtp) -aux_source_directory(${MediaServer_Root}/librtp/source src_rtp) -aux_source_directory(${MediaServer_Root}/librtp/payload src_rtp) -include_directories(${MediaServer_Root}/librtp/include) -add_library(rtp STATIC ${src_rtp}) -list(APPEND LINK_LIB_LIST rtp) +#去除win32的适配代码 +if (NOT WIN32) + list(REMOVE_ITEM ToolKit_src_list ${ToolKit_Root}/win32/getopt.c) +else() + #防止Windows.h包含Winsock.h + add_definitions(-DWIN32_LEAN_AND_MEAN -DMP4V2_NO_STDINT_DEFS) +endif () #添加库 add_library(zltoolkit STATIC ${ToolKit_src_list}) @@ -182,34 +182,8 @@ elseif(NOT ANDROID OR IOS) list(APPEND LINK_LIB_LIST pthread) endif () - #测试程序 add_subdirectory(tests) #主服务器 add_subdirectory(server) - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/LICENSE b/LICENSE index d4008608..367501b6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,8 @@ MIT License Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> +Copyright (c) 2019 Gemfield +Copyright (c) 2018 huohuo <913481084@qq.com> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index d02ad8e3..e3c0c950 100644 --- a/README.md +++ b/README.md @@ -331,6 +331,8 @@ docker build -t zlmediakit . MIT License Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> +Copyright (c) 2019 Gemfield +Copyright (c) 2018 huohuo <913481084@qq.com> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -349,6 +351,7 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + ``` diff --git a/conf/config.ini b/conf/config.ini index 8df1dd3b..a7588b80 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -161,6 +161,18 @@ maxRtpCount=50 #视频mtu大小,该参数限制rtp最大字节数,推荐不要超过1400 videoMtuSize=1400 +[rtp_proxy] +#udp类型的代理服务器是否检查rtp源地址,地址不配备将丢弃数据 +checkSource=1 +#导出调试数据(包括rtp/ps/h264)至该目录,置空则关闭数据导出 +dumpDir= +#udp和tcp代理服务器,支持rtp(必须是ts或ps类型)代理 +port=10000 +#rtp如果是ts/ps类型则选择MP2P,还可以设置为MP4V-ES +rtp_type=MP2P +#rtp超时时间,单位秒 +timeoutSec=15 + [rtsp] #rtsp专有鉴权方式是采用base64还是md5方式 authBasic=0 diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 6fdf6192..64402ad8 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -693,6 +693,7 @@ void installWebApi() { invoker.responseFile(headerIn,StrCaseMap(),exePath()); }); +#if defined(ENABLE_RTPPROXY) API_REGIST(api,getSsrcInfo,{ CHECK_SECRET(); CHECK_ARGS("ssrc"); @@ -705,6 +706,7 @@ void installWebApi() { val["peer_ip"] = process->get_peer_ip(); val["peer_port"] = process->get_peer_port(); }); +#endif//ENABLE_RTPPROXY // 开始录制hls或MP4 API_REGIST(api,startRecord,{ diff --git a/server/main.cpp b/server/main.cpp index 40126969..6d26bbe9 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -38,11 +38,11 @@ #include "Common/config.h" #include "Rtsp/UDPServer.h" #include "Rtsp/RtspSession.h" +#include "Rtp/RtpSession.h" #include "Rtmp/RtmpSession.h" #include "Shell/ShellSession.h" -#include "Rtmp/FlvMuxer.h" -#include "Player/PlayerProxy.h" #include "Http/WebSocketSession.h" +#include "Rtp/UdpRecver.h" #include "WebApi.h" #include "WebHook.h" @@ -58,36 +58,31 @@ namespace mediakit { ////////////HTTP配置/////////// namespace Http { #define HTTP_FIELD "http." -#define HTTP_PORT 80 const string kPort = HTTP_FIELD"port"; -#define HTTPS_PORT 443 const string kSSLPort = HTTP_FIELD"sslport"; onceToken token1([](){ - mINI::Instance()[kPort] = HTTP_PORT; - mINI::Instance()[kSSLPort] = HTTPS_PORT; + mINI::Instance()[kPort] = 80; + mINI::Instance()[kSSLPort] = 443; },nullptr); }//namespace Http ////////////SHELL配置/////////// namespace Shell { #define SHELL_FIELD "shell." -#define SHELL_PORT 9000 const string kPort = SHELL_FIELD"port"; onceToken token1([](){ - mINI::Instance()[kPort] = SHELL_PORT; + mINI::Instance()[kPort] = 9000; },nullptr); } //namespace Shell ////////////RTSP服务器配置/////////// namespace Rtsp { #define RTSP_FIELD "rtsp." -#define RTSP_PORT 554 -#define RTSPS_PORT 322 const string kPort = RTSP_FIELD"port"; const string kSSLPort = RTSP_FIELD"sslport"; onceToken token1([](){ - mINI::Instance()[kPort] = RTSP_PORT; - mINI::Instance()[kSSLPort] = RTSPS_PORT; + mINI::Instance()[kPort] = 554; + mINI::Instance()[kSSLPort] = 332; },nullptr); } //namespace Rtsp @@ -95,12 +90,21 @@ onceToken token1([](){ ////////////RTMP服务器配置/////////// namespace Rtmp { #define RTMP_FIELD "rtmp." -#define RTMP_PORT 1935 const string kPort = RTMP_FIELD"port"; onceToken token1([](){ - mINI::Instance()[kPort] = RTMP_PORT; + mINI::Instance()[kPort] = 1935; },nullptr); } //namespace RTMP + +////////////Rtp代理相关配置/////////// +namespace RtpProxy { +#define RTP_PROXY_FIELD "rtp_proxy." +const string kPort = RTP_PROXY_FIELD"port"; +onceToken token1([](){ + mINI::Instance()[kPort] = 10000; +},nullptr); +} //namespace RtpProxy + } // namespace mediakit @@ -269,6 +273,7 @@ int start_main(int argc,char *argv[]) { uint16_t rtmpPort = mINI::Instance()[Rtmp::kPort]; uint16_t httpPort = mINI::Instance()[Http::kPort]; uint16_t httpsPort = mINI::Instance()[Http::kSSLPort]; + uint16_t rtp_proxy = mINI::Instance()[RtpProxy::kPort]; //设置poller线程数,该函数必须在使用ZLToolKit网络相关对象之前调用才能生效 EventPollerPool::setPoolSize(threads); @@ -284,6 +289,11 @@ int start_main(int argc,char *argv[]) { //支持ssl加密的rtsp服务器,可用于诸如亚马逊echo show这样的设备访问 TcpServer::Ptr rtspSSLSrv(new TcpServer()); +#if defined(ENABLE_RTPPROXY) + UdpRecver recver; + TcpServer::Ptr tcpRtpServer(new TcpServer()); +#endif//defined(ENABLE_RTPPROXY) + try { //rtsp服务器,端口默认554 rtspSrv->start(rtspPort);//默认554 @@ -297,6 +307,14 @@ int start_main(int argc,char *argv[]) { httpsSrv->start(httpsPort); //telnet远程调试服务器 shellSrv->start(shellPort); + +#if defined(ENABLE_RTPPROXY) + //创建rtp udp服务器 + recver.initSock(rtp_proxy); + //创建rtp tcp服务器 + tcpRtpServer->start(rtp_proxy); +#endif//defined(ENABLE_RTPPROXY) + }catch (std::exception &ex){ WarnL << "端口占用或无权限:" << ex.what() << endl; ErrorL << "程序启动失败,请修改配置文件中端口号后重试!" << endl; diff --git a/src/Common/config.cpp b/src/Common/config.cpp index 426bb082..d4f08373 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -275,6 +275,28 @@ onceToken token([](){ },nullptr); } //namespace Hls + +////////////Rtp代理相关配置/////////// +namespace RtpProxy { +#define RTP_PROXY_FIELD "rtp_proxy." +//rtp调试数据保存目录 +const string kDumpDir = RTP_PROXY_FIELD"dumpDir"; +//是否限制udp数据来源ip和端口 +const string kCheckSource = RTP_PROXY_FIELD"checkSource"; +//rtp类型,支持MP2P/MP4V-ES +const string kRtpType = RTP_PROXY_FIELD"rtp_type"; +//rtp接收超时时间 +const string kTimeoutSec = RTP_PROXY_FIELD"timeoutSec"; + +onceToken token([](){ + mINI::Instance()[kDumpDir] = ""; + mINI::Instance()[kCheckSource] = 1; + mINI::Instance()[kRtpType] = "MP2P"; + mINI::Instance()[kTimeoutSec] = 15; +},nullptr); +} //namespace RtpProxy + + namespace Client { const string kNetAdapter = "net_adapter"; const string kRtpType = "rtp_type"; diff --git a/src/Common/config.h b/src/Common/config.h index 9a090144..8eb447b3 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -159,11 +159,6 @@ extern const string kBroadcastReloadConfig; static type arg = mINI::Instance()[key]; \ LISTEN_RELOAD_KEY(arg,key); - -//兼容老代码 -#define GET_CONFIG_AND_REGISTER GET_CONFIG -#define BroadcastRtmpPublishArgs BroadcastMediaPublishArgs -#define kBroadcastRtmpPublish kBroadcastMediaPublish } //namespace Broadcast ////////////通用配置/////////// @@ -301,6 +296,17 @@ extern const string kFileBufSize; extern const string kFilePath; } //namespace Hls +////////////Rtp代理相关配置/////////// +namespace RtpProxy { +//rtp调试数据保存目录,置空则不生成 +extern const string kDumpDir; +//是否限制udp数据来源ip和端口 +extern const string kCheckSource; +//rtp类型,支持MP2P/MP4V-ES +extern const string kRtpType; +//rtp接收超时时间 +extern const string kTimeoutSec; +} //namespace RtpProxy /** * rtsp/rtmp播放器、推流器相关设置名, diff --git a/src/Rtp/PSDecoder.cpp b/src/Rtp/PSDecoder.cpp index 835bc068..47abb768 100644 --- a/src/Rtp/PSDecoder.cpp +++ b/src/Rtp/PSDecoder.cpp @@ -1,30 +1,34 @@ /* -* MIT License -* -* Copyright (c) 2016-2019 Gemfield -* -* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ + * MIT License + * + * Copyright (c) 2019 Gemfield + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#if defined(ENABLE_RTPPROXY) #include "PSDecoder.h" +#include "mpeg-ps.h" + +namespace mediakit{ PSDecoder::PSDecoder() { _ps_demuxer = ps_demuxer_create([](void* param, @@ -41,9 +45,13 @@ PSDecoder::PSDecoder() { } PSDecoder::~PSDecoder() { - ps_demuxer_destroy(_ps_demuxer); + ps_demuxer_destroy((struct ps_demuxer_t*)_ps_demuxer); } -int PSDecoder::decodePS(const uint8_t *data, size_t bytes) { - return ps_demuxer_input(_ps_demuxer,data,bytes); +int PSDecoder::decodePS(const uint8_t *data, int bytes) { + return ps_demuxer_input((struct ps_demuxer_t*)_ps_demuxer,data,bytes); } + +}//namespace mediakit + +#endif//#if defined(ENABLE_RTPPROXY) \ No newline at end of file diff --git a/src/Rtp/PSDecoder.h b/src/Rtp/PSDecoder.h index 63d50008..c8a73872 100644 --- a/src/Rtp/PSDecoder.h +++ b/src/Rtp/PSDecoder.h @@ -1,46 +1,42 @@ /* -* MIT License -* -* Copyright (c) 2016-2019 Gemfield -* -* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ + * MIT License + * + * Copyright (c) 2019 Gemfield + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ -#ifndef RTPPROXY_PSDECODER_H -#define RTPPROXY_PSDECODER_H +#ifndef ZLMEDIAKIT_PSDECODER_H +#define ZLMEDIAKIT_PSDECODER_H -#ifdef __cplusplus -extern "C" { -#endif +#if defined(ENABLE_RTPPROXY) +#include -#include "mpeg-ps.h" -#ifdef __cplusplus -} -#endif +namespace mediakit{ class PSDecoder { public: PSDecoder(); virtual ~PSDecoder(); - int decodePS(const uint8_t *data, size_t bytes); + int decodePS(const uint8_t *data, int bytes); protected: virtual void onPSDecode(int stream, int codecid, @@ -48,10 +44,12 @@ protected: int64_t pts, int64_t dts, const void *data, - size_t bytes) = 0; + int bytes) = 0; private: - struct ps_demuxer_t *_ps_demuxer = nullptr; + void *_ps_demuxer = nullptr; }; +}//namespace mediakit -#endif //RTPPROXY_PSDECODER_H +#endif//defined(ENABLE_RTPPROXY) +#endif //ZLMEDIAKIT_PSDECODER_H diff --git a/src/Rtp/RtpDecoder.cpp b/src/Rtp/RtpDecoder.cpp index a5d40aa8..5f78cd7c 100644 --- a/src/Rtp/RtpDecoder.cpp +++ b/src/Rtp/RtpDecoder.cpp @@ -1,34 +1,39 @@ /* -* MIT License -* -* Copyright (c) 2016-2019 Gemfield -* -* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ + * MIT License + * + * Copyright (c) 2019 Gemfield + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#if defined(ENABLE_RTPPROXY) #include #include "Util/logger.h" #include "RtpDecoder.h" +#include "rtp-payload.h" + using namespace toolkit; +namespace mediakit{ + RtpDecoder::RtpDecoder() { _buffer = std::make_shared(); } @@ -68,3 +73,6 @@ void RtpDecoder::decodeRtp(const void *data, int bytes,const char *type_name) { rtp_payload_decode_input(_rtp_decoder,data,bytes); } } + +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) \ No newline at end of file diff --git a/src/Rtp/RtpDecoder.h b/src/Rtp/RtpDecoder.h index de2ad240..845dbdbd 100644 --- a/src/Rtp/RtpDecoder.h +++ b/src/Rtp/RtpDecoder.h @@ -1,43 +1,37 @@ /* -* MIT License -* -* Copyright (c) 2016-2019 Gemfield -* -* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ + * MIT License + * + * Copyright (c) 2019 Gemfield + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ -#ifndef IPTV_RTPTOTS_H -#define IPTV_RTPTOTS_H +#ifndef ZLMEDIAKIT_RTPDECODER_H +#define ZLMEDIAKIT_RTPDECODER_H +#if defined(ENABLE_RTPPROXY) #include "Network/Buffer.h" using namespace toolkit; -#ifdef __cplusplus -extern "C" { -#endif -#include "rtp-payload.h" -#include "mpeg-ts.h" -#ifdef __cplusplus -} -#endif +namespace mediakit{ class RtpDecoder { public: @@ -51,5 +45,6 @@ private: BufferRaw::Ptr _buffer; }; - -#endif //IPTV_RTPTOTS_H +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) +#endif //ZLMEDIAKIT_RTPDECODER_H diff --git a/src/Rtp/RtpFileLoader.cpp b/src/Rtp/RtpFileLoader.cpp deleted file mode 100644 index 785304fc..00000000 --- a/src/Rtp/RtpFileLoader.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* -* MIT License -* -* Copyright (c) 2016-2019 Gemfield -* -* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ - -#include -#include -#include -#include "RtpFileLoader.h" -#include "Util/logger.h" -#include "RtpSelector.h" -using namespace toolkit; - -bool RtpFileLoader::loadFile(const char *path) { - FILE *fp = fopen(path, "rb"); - if (!fp) { - WarnL << "open file failed:" << path; - return false; - } - - uint32_t timeStamp_last = 0; - uint16_t len; - char rtp[2 * 1024]; - while (true) { - if (2 != fread(&len, 1, 2, fp)) { - WarnL; - break; - } - len = ntohs(len); - if (len < 12 || len > sizeof(rtp)) { - WarnL << len; - break; - } - - if (len != fread(rtp, 1, len, fp)) { - WarnL; - break; - } - - uint32_t timeStamp; - memcpy(&timeStamp, rtp + 4, 4); - timeStamp = ntohl(timeStamp); - timeStamp /= 90; - - if(timeStamp_last){ - auto diff = timeStamp - timeStamp_last; - if(diff > 0){ - usleep(diff * 1000); - } - } - - timeStamp_last = timeStamp; - RtpSelector::Instance().inputRtp(rtp,len, nullptr); - } - - fclose(fp); - return true; -} diff --git a/src/Rtp/RtpFileLoader.h b/src/Rtp/RtpFileLoader.h deleted file mode 100644 index a715d618..00000000 --- a/src/Rtp/RtpFileLoader.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -* MIT License -* -* Copyright (c) 2016-2019 Gemfield -* -* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ - -#ifndef RTPPROXY_RTPFILELOADER_H -#define RTPPROXY_RTPFILELOADER_H - - -class RtpFileLoader { -public: - RtpFileLoader(){}; - ~RtpFileLoader(){}; - static bool loadFile(const char *path); -}; - - -#endif //RTPPROXY_RTPFILELOADER_H diff --git a/src/Rtp/RtpProcess.cpp b/src/Rtp/RtpProcess.cpp index f74cb8da..b19c43c9 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -1,57 +1,37 @@ /* -* MIT License -* -* Copyright (c) 2016-2019 Gemfield -* -* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ + * MIT License + * + * Copyright (c) 2019 Gemfield + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#if defined(ENABLE_RTPPROXY) +#include "mpeg-ps.h" #include "RtpProcess.h" #include "Util/File.h" -#include "Util/logger.h" #include "Extension/H265.h" -#include "Extension/H264.h" #include "Extension/AAC.h" -using namespace toolkit; - -namespace dump { -const string kEnable = "dump.enable"; -const string kDir = "dump.dir"; -const string kChcekSource = "dump.checkSource"; -onceToken token([](){ - mINI::Instance()[kEnable] = 0; - mINI::Instance()[kDir] = "./dump/"; - mINI::Instance()[kChcekSource] = 1; -}); -}//namespace dump - -namespace Rtp { -const string kRtpType = "rtp.rtp_type"; -const string kTimeoutSec = "rtp.timeoutSec"; -onceToken token([](){ - mINI::Instance()[kRtpType] = "MP2P"; - mINI::Instance()[kTimeoutSec] = 15; -}); -}//namespace dump +namespace mediakit{ /** * 合并一些时间戳相同的frame @@ -106,10 +86,9 @@ RtpProcess::RtpProcess(uint32_t ssrc) { DebugL << printSSRC(_ssrc); _muxer = std::make_shared(DEFAULT_VHOST,"rtp",printSSRC(_ssrc)); - GET_CONFIG(bool,dump_enable,dump::kEnable); - GET_CONFIG(string,dump_dir,dump::kDir); + GET_CONFIG(string,dump_dir,RtpProxy::kDumpDir); { - FILE *fp = dump_enable ? File::createfile_file(File::absolutePath(printSSRC(_ssrc) + ".rtp",dump_dir).data(),"wb") : nullptr; + FILE *fp = !dump_dir.empty() ? File::createfile_file(File::absolutePath(printSSRC(_ssrc) + ".rtp",dump_dir).data(),"wb") : nullptr; if(fp){ _save_file_rtp.reset(fp,[](FILE *fp){ fclose(fp); @@ -118,7 +97,7 @@ RtpProcess::RtpProcess(uint32_t ssrc) { } { - FILE *fp = dump_enable ? File::createfile_file(File::absolutePath(printSSRC(_ssrc) + ".mp2",dump_dir).data(),"wb") : nullptr; + FILE *fp = !dump_dir.empty() ? File::createfile_file(File::absolutePath(printSSRC(_ssrc) + ".mp2",dump_dir).data(),"wb") : nullptr; if(fp){ _save_file_ps.reset(fp,[](FILE *fp){ fclose(fp); @@ -127,7 +106,7 @@ RtpProcess::RtpProcess(uint32_t ssrc) { } { - FILE *fp = dump_enable ? File::createfile_file(File::absolutePath(printSSRC(_ssrc) + ".video",dump_dir).data(),"wb") : nullptr; + FILE *fp = !dump_dir.empty() ? File::createfile_file(File::absolutePath(printSSRC(_ssrc) + ".video",dump_dir).data(),"wb") : nullptr; if(fp){ _save_file_video.reset(fp,[](FILE *fp){ fclose(fp); @@ -147,7 +126,7 @@ RtpProcess::~RtpProcess() { } bool RtpProcess::inputRtp(const char *data, int data_len,const struct sockaddr *addr) { - GET_CONFIG(bool,check_source,dump::kChcekSource); + GET_CONFIG(bool,check_source,RtpProxy::kCheckSource); //检查源是否合法 if(!_addr){ _addr = new struct sockaddr; @@ -177,7 +156,7 @@ void RtpProcess::onRtpSorted(const RtpPacket::Ptr &rtp, int) { fwrite((uint8_t *) rtp->data() + 4, rtp->size() - 4, 1, _save_file_rtp.get()); } - GET_CONFIG(string,rtp_type,::Rtp::kRtpType); + GET_CONFIG(string,rtp_type,::RtpProxy::kRtpType); decodeRtp(rtp->data() + 4 ,rtp->size() - 4,rtp_type.data()); } @@ -198,7 +177,7 @@ void RtpProcess::onPSDecode(int stream, int64_t pts, int64_t dts, const void *data, - size_t bytes) { + int bytes) { switch (codecid) { case STREAM_VIDEO_H264: { @@ -270,7 +249,7 @@ void RtpProcess::onPSDecode(int stream, } bool RtpProcess::alive() { - GET_CONFIG(int,timeoutSec,::Rtp::kTimeoutSec) + GET_CONFIG(int,timeoutSec,RtpProxy::kTimeoutSec) if(_last_rtp_time.elapsedTime() / 1000 < timeoutSec){ return true; } @@ -284,3 +263,6 @@ string RtpProcess::get_peer_ip() { uint16_t RtpProcess::get_peer_port() { return ntohs(((struct sockaddr_in *) _addr)->sin_port); } + +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) \ No newline at end of file diff --git a/src/Rtp/RtpProcess.h b/src/Rtp/RtpProcess.h index ac1c4874..13ddfbeb 100644 --- a/src/Rtp/RtpProcess.h +++ b/src/Rtp/RtpProcess.h @@ -1,31 +1,33 @@ /* -* MIT License -* -* Copyright (c) 2016-2019 Gemfield -* -* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ + * MIT License + * + * Copyright (c) 2019 Gemfield + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ -#ifndef RTPPROXY_RTPDECODER_H -#define RTPPROXY_RTPDECODER_H +#ifndef ZLMEDIAKIT_RTPPROCESS_H +#define ZLMEDIAKIT_RTPPROCESS_H + +#if defined(ENABLE_RTPPROXY) #include "Rtsp/RtpReceiver.h" #include "RtpDecoder.h" @@ -33,6 +35,8 @@ #include "Common/Device.h" using namespace mediakit; +namespace mediakit{ + string printSSRC(uint32_t ui32Ssrc); class FrameMerger; @@ -54,7 +58,7 @@ protected: int64_t pts, int64_t dts, const void *data, - size_t bytes) override ; + int bytes) override ; private: std::shared_ptr _save_file_rtp; std::shared_ptr _save_file_ps; @@ -70,5 +74,6 @@ private: Ticker _last_rtp_time; }; - -#endif //RTPPROXY_RTPDECODER_H +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) +#endif //ZLMEDIAKIT_RTPPROCESS_H diff --git a/src/Rtp/RtpSelector.cpp b/src/Rtp/RtpSelector.cpp index a407ef7a..2ff997a7 100644 --- a/src/Rtp/RtpSelector.cpp +++ b/src/Rtp/RtpSelector.cpp @@ -1,31 +1,34 @@ /* -* MIT License -* -* Copyright (c) 2016-2019 Gemfield -* -* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ + * MIT License + * + * Copyright (c) 2019 Gemfield + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#if defined(ENABLE_RTPPROXY) #include "RtpSelector.h" +namespace mediakit{ + INSTANCE_IMP(RtpSelector); bool RtpSelector::inputRtp(const char *data, int data_len,const struct sockaddr *addr) { @@ -97,3 +100,6 @@ RtpSelector::RtpSelector() { RtpSelector::~RtpSelector() { } + +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) \ No newline at end of file diff --git a/src/Rtp/RtpSelector.h b/src/Rtp/RtpSelector.h index e7011c4a..872d3ada 100644 --- a/src/Rtp/RtpSelector.h +++ b/src/Rtp/RtpSelector.h @@ -1,37 +1,39 @@ /* -* MIT License -* -* Copyright (c) 2016-2019 Gemfield -* -* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ + * MIT License + * + * Copyright (c) 2019 Gemfield + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ -#ifndef RTPPROXY_RTPSELECTOR_H -#define RTPPROXY_RTPSELECTOR_H +#ifndef ZLMEDIAKIT_RTPSELECTOR_H +#define ZLMEDIAKIT_RTPSELECTOR_H -#include +#if defined(ENABLE_RTPPROXY) +#include #include #include #include "RtpProcess.h" +namespace mediakit{ class RtpSelector : public std::enable_shared_from_this{ public: @@ -51,5 +53,6 @@ private: Ticker _last_rtp_time; }; - -#endif //RTPPROXY_RTPSELECTOR_H +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) +#endif //ZLMEDIAKIT_RTPSELECTOR_H diff --git a/src/Rtp/RtpSession.cpp b/src/Rtp/RtpSession.cpp index a7faa4a7..b1ad9137 100644 --- a/src/Rtp/RtpSession.cpp +++ b/src/Rtp/RtpSession.cpp @@ -1,30 +1,33 @@ /* -* MIT License -* -* Copyright (c) 2016-2019 Gemfield -* -* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ + * MIT License + * + * Copyright (c) 2019 Gemfield + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if defined(ENABLE_RTPPROXY) #include "RtpSession.h" #include "RtpSelector.h" +namespace mediakit{ RtpSession::RtpSession(const Socket::Ptr &sock) : TcpSession(sock) { DebugP(this); @@ -70,3 +73,6 @@ void RtpSession::onRtpPacket(const char *data, uint64_t len) { _process->inputRtp(data + 2,len - 2,&addr); _ticker.resetTime(); } + +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) \ No newline at end of file diff --git a/src/Rtp/RtpSession.h b/src/Rtp/RtpSession.h index 68ce863c..ff8771a1 100644 --- a/src/Rtp/RtpSession.h +++ b/src/Rtp/RtpSession.h @@ -1,39 +1,41 @@ /* -* MIT License -* -* Copyright (c) 2016-2019 Gemfield -* -* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ + * MIT License + * + * Copyright (c) 2019 Gemfield + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ -#ifndef RTPPROXY_RTPSESSION_H -#define RTPPROXY_RTPSESSION_H +#ifndef ZLMEDIAKIT_RTPSESSION_H +#define ZLMEDIAKIT_RTPSESSION_H +#if defined(ENABLE_RTPPROXY) #include "Network/TcpSession.h" #include "RtpSplitter.h" #include "RtpProcess.h" #include "Util/TimeTicker.h" - using namespace toolkit; +namespace mediakit{ + class RtpSession : public TcpSession , public RtpSplitter{ public: RtpSession(const Socket::Ptr &sock); @@ -50,5 +52,6 @@ private: struct sockaddr addr; }; - -#endif //RTPPROXY_RTPSESSION_H +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) +#endif //ZLMEDIAKIT_RTPSESSION_H diff --git a/src/Rtp/RtpSplitter.cpp b/src/Rtp/RtpSplitter.cpp index b28697b6..2317f68b 100644 --- a/src/Rtp/RtpSplitter.cpp +++ b/src/Rtp/RtpSplitter.cpp @@ -1,30 +1,32 @@ /* -* MIT License -* -* Copyright (c) 2016-2019 Gemfield -* -* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ + * MIT License + * + * Copyright (c) 2019 Gemfield + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#if defined(ENABLE_RTPPROXY) #include "RtpSplitter.h" +namespace mediakit{ RtpSplitter::RtpSplitter() { } @@ -50,4 +52,7 @@ const char *RtpSplitter::onSearchPacketTail(const char *data, int len) { int64_t RtpSplitter::onRecvHeader(const char *data, uint64_t len) { onRtpPacket(data,len); return 0; -} \ No newline at end of file +} + +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) \ No newline at end of file diff --git a/src/Rtp/RtpSplitter.h b/src/Rtp/RtpSplitter.h index 66dc1c88..34a026a4 100644 --- a/src/Rtp/RtpSplitter.h +++ b/src/Rtp/RtpSplitter.h @@ -1,34 +1,36 @@ /* -* MIT License -* -* Copyright (c) 2016-2019 Gemfield -* -* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ + * MIT License + * + * Copyright (c) 2019 Gemfield + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ -#ifndef RTPPROXY_RTPSPLITTER_H -#define RTPPROXY_RTPSPLITTER_H +#ifndef ZLMEDIAKIT_RTPSPLITTER_H +#define ZLMEDIAKIT_RTPSPLITTER_H +#if defined(ENABLE_RTPPROXY) #include "Http/HttpRequestSplitter.h" -using namespace mediakit; + +namespace mediakit{ class RtpSplitter : public HttpRequestSplitter{ public: @@ -46,5 +48,6 @@ protected: int64_t onRecvHeader(const char *data,uint64_t len) override; }; - -#endif //RTPPROXY_RTPSPLITTER_H +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) +#endif //ZLMEDIAKIT_RTPSPLITTER_H diff --git a/src/Rtp/UdpRecver.cpp b/src/Rtp/UdpRecver.cpp index 5ac3ecd5..79995a85 100644 --- a/src/Rtp/UdpRecver.cpp +++ b/src/Rtp/UdpRecver.cpp @@ -1,31 +1,34 @@ /* -* MIT License -* -* Copyright (c) 2016-2019 Gemfield -* -* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ + * MIT License + * + * Copyright (c) 2019 Gemfield + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#if defined(ENABLE_RTPPROXY) #include "UdpRecver.h" #include "RtpSelector.h" +namespace mediakit{ + UdpRecver::UdpRecver() { } @@ -48,3 +51,6 @@ bool UdpRecver::initSock(uint16_t local_port,const char *local_ip) { EventPoller::Ptr UdpRecver::getPoller() { return _sock->getPoller(); } + +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) \ No newline at end of file diff --git a/src/Rtp/UdpRecver.h b/src/Rtp/UdpRecver.h index 4f412a49..f2a19a36 100644 --- a/src/Rtp/UdpRecver.h +++ b/src/Rtp/UdpRecver.h @@ -1,37 +1,40 @@ /* -* MIT License -* -* Copyright (c) 2016-2019 Gemfield -* -* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). -* -* Permission is hereby granted, free of charge, to any person obtaining a copy -* of this software and associated documentation files (the "Software"), to deal -* in the Software without restriction, including without limitation the rights -* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -* copies of the Software, and to permit persons to whom the Software is -* furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all -* copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -* SOFTWARE. -*/ + * MIT License + * + * Copyright (c) 2019 Gemfield + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ #ifndef ZLMEDIAKIT_UDPRECVER_H #define ZLMEDIAKIT_UDPRECVER_H +#if defined(ENABLE_RTPPROXY) #include #include "Network/Socket.h" - using namespace std; using namespace toolkit; + +namespace mediakit{ + /** * 组播接收器 */ @@ -48,5 +51,6 @@ protected: Socket::Ptr _sock; }; - +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) #endif //ZLMEDIAKIT_UDPRECVER_H diff --git a/tests/test_rtp.cpp b/tests/test_rtp.cpp index 864e1655..1be10ceb 100644 --- a/tests/test_rtp.cpp +++ b/tests/test_rtp.cpp @@ -1,7 +1,7 @@ /* * MIT License * - * Copyright (c) 2016-2019 Gemfield + * Copyright (c) 2019 Gemfield * * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). * @@ -36,29 +36,78 @@ #include "Rtsp/RtspSession.h" #include "Rtmp/RtmpSession.h" #include "Http/HttpSession.h" -#include "Rtp/RtpFileLoader.h" +#include "Rtp/RtpSelector.h" using namespace std; using namespace toolkit; using namespace mediakit; -int main(int argc,char *argv[]) { - { - //设置日志 - Logger::Instance().add(std::make_shared("ConsoleChannel")); - //启动异步日志线程 - Logger::Instance().setWriter(std::make_shared()); - loadIniConfig((exeDir() + "config.ini").data()); - TcpServer::Ptr rtspSrv(new TcpServer()); - TcpServer::Ptr rtmpSrv(new TcpServer()); - TcpServer::Ptr httpSrv(new TcpServer()); - rtspSrv->start(554);//默认554 - rtmpSrv->start(1935);//默认1935 - httpSrv->start(80);//默认80 - RtpFileLoader::loadFile(argv[1]); +#if defined(ENABLE_RTPPROXY) +static bool loadFile(const char *path){ + FILE *fp = fopen(path, "rb"); + if (!fp) { + WarnL << "open file failed:" << path; + return false; } + + uint32_t timeStamp_last = 0; + uint16_t len; + char rtp[2 * 1024]; + while (true) { + if (2 != fread(&len, 1, 2, fp)) { + WarnL; + break; + } + len = ntohs(len); + if (len < 12 || len > sizeof(rtp)) { + WarnL << len; + break; + } + + if (len != fread(rtp, 1, len, fp)) { + WarnL; + break; + } + + uint32_t timeStamp; + memcpy(&timeStamp, rtp + 4, 4); + timeStamp = ntohl(timeStamp); + timeStamp /= 90; + + if(timeStamp_last){ + auto diff = timeStamp - timeStamp_last; + if(diff > 0){ + usleep(diff * 1000); + } + } + + timeStamp_last = timeStamp; + + RtpSelector::Instance().inputRtp(rtp,len, nullptr); + } + fclose(fp); + return true; +} +#endif//#if defined(ENABLE_RTPPROXY) + +int main(int argc,char *argv[]) { + //设置日志 + Logger::Instance().add(std::make_shared("ConsoleChannel")); +#if defined(ENABLE_RTPPROXY) + //启动异步日志线程 + Logger::Instance().setWriter(std::make_shared()); + loadIniConfig((exeDir() + "config.ini").data()); + TcpServer::Ptr rtspSrv(new TcpServer()); + TcpServer::Ptr rtmpSrv(new TcpServer()); + TcpServer::Ptr httpSrv(new TcpServer()); + rtspSrv->start(554);//默认554 + rtmpSrv->start(1935);//默认1935 + httpSrv->start(80);//默认80 + loadFile(argv[1]); +#else + ErrorL << "please ENABLE_RTPPROXY and then test"; +#endif//#if defined(ENABLE_RTPPROXY) return 0; } - From 1300e11507b5345b82c41272957218b6d140423a Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 6 Dec 2019 13:39:55 +0800 Subject: [PATCH 076/272] =?UTF-8?q?=E5=AE=8C=E5=96=84cmakelist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index db0117e1..c72217d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -147,8 +147,9 @@ if(ENABLE_MP4RECORD) endif(WIN32) endif() +#添加rtp库用于rtp转ps/ts if(ENABLE_RTPPROXY) - #添加rtp库用于rtp转ps/ts + message(STATUS "ENABLE_RTPPROXY defined") aux_source_directory(${MediaServer_Root}/librtp/include src_rtp) aux_source_directory(${MediaServer_Root}/librtp/source src_rtp) aux_source_directory(${MediaServer_Root}/librtp/payload src_rtp) From 030d8f6d53c75203141f5ff470985c876740e46a Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 6 Dec 2019 13:53:28 +0800 Subject: [PATCH 077/272] =?UTF-8?q?=E5=AE=8C=E6=88=90windows=E4=B8=8B?= =?UTF-8?q?=E7=BC=96=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/media-server | 2 +- CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/3rdpart/media-server b/3rdpart/media-server index 358b7bd3..4325386b 160000 --- a/3rdpart/media-server +++ b/3rdpart/media-server @@ -1 +1 @@ -Subproject commit 358b7bd3bf89564371fed48797ad396882f9d130 +Subproject commit 4325386be318889b7815cdd86a2e83f05a059c81 diff --git a/CMakeLists.txt b/CMakeLists.txt index c72217d6..6d20b60d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -168,7 +168,7 @@ if (NOT WIN32) list(REMOVE_ITEM ToolKit_src_list ${ToolKit_Root}/win32/getopt.c) else() #防止Windows.h包含Winsock.h - add_definitions(-DWIN32_LEAN_AND_MEAN -DMP4V2_NO_STDINT_DEFS) + add_definitions(-DWIN32_LEAN_AND_MEAN -DMP4V2_NO_STDINT_DEFS -DOS_WINDOWS) endif () #添加库 From adf0e09262bb814cb1faa362b65608bd6573d46b Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 6 Dec 2019 17:23:49 +0800 Subject: [PATCH 078/272] =?UTF-8?q?mp4=E7=82=B9=E6=92=AD=E6=94=AF=E6=8C=81?= =?UTF-8?q?pts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/MP4Reader.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Record/MP4Reader.cpp b/src/Record/MP4Reader.cpp index 74521fd3..b082234d 100644 --- a/src/Record/MP4Reader.cpp +++ b/src/Record/MP4Reader.cpp @@ -219,7 +219,8 @@ inline bool MP4Reader::readVideoSample(int iTimeInc,bool justSeekSyncFrame) { for (; iIdx < iNextSample; iIdx++) { uint8_t *pBytes = _pcVideoSample.get(); uint32_t numBytes = _video_sample_max_size; - if(MP4ReadSample(_hMP4File, _video_trId, iIdx + 1, &pBytes, &numBytes,NULL,NULL,NULL,&_bSyncSample)){ + MP4Duration pRenderingOffset; + if(MP4ReadSample(_hMP4File, _video_trId, iIdx + 1, &pBytes, &numBytes,NULL,NULL,&pRenderingOffset,&_bSyncSample)){ if (!justSeekSyncFrame) { uint32_t iOffset = 0; while (iOffset < numBytes) { @@ -230,7 +231,8 @@ inline bool MP4Reader::readVideoSample(int iTimeInc,bool justSeekSyncFrame) { break; } memcpy(pBytes + iOffset, "\x0\x0\x0\x1", 4); - writeH264(pBytes + iOffset, iFrameLen + 4, (double) _video_ms * iIdx / _video_num_samples, 0); + uint32_t dts = (double) _video_ms * iIdx / _video_num_samples; + writeH264(pBytes + iOffset, iFrameLen + 4, dts, dts + pRenderingOffset / 90); iOffset += (iFrameLen + 4); } }else if(_bSyncSample){ From 655a30081b360b32ed7197fd24ec867b28b58408 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 6 Dec 2019 20:18:08 +0800 Subject: [PATCH 079/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8Drtp(tcp)=E8=8E=B7?= =?UTF-8?q?=E5=8F=96ssrc=E5=A4=B1=E8=B4=A5=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtp/RtpSession.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Rtp/RtpSession.cpp b/src/Rtp/RtpSession.cpp index b1ad9137..c765997f 100644 --- a/src/Rtp/RtpSession.cpp +++ b/src/Rtp/RtpSession.cpp @@ -67,7 +67,7 @@ void RtpSession::onManager() { void RtpSession::onRtpPacket(const char *data, uint64_t len) { if(!_ssrc){ - _ssrc = RtpSelector::getSSRC(data,len); + _ssrc = RtpSelector::getSSRC(data + 2,len - 2); _process = RtpSelector::Instance().getProcess(_ssrc, true); } _process->inputRtp(data + 2,len - 2,&addr); From fa4fac1e0dbc4ec8150760ad1ca6c6f986385958 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 9 Dec 2019 17:49:00 +0800 Subject: [PATCH 080/272] =?UTF-8?q?rtp=E6=8E=A8=E6=B5=81=E7=94=B1=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=96=87=E4=BB=B6=E5=86=B3=E5=AE=9A=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=BD=95=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtp/RtpProcess.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Rtp/RtpProcess.cpp b/src/Rtp/RtpProcess.cpp index b19c43c9..d4d57847 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -84,7 +84,12 @@ RtpProcess::RtpProcess(uint32_t ssrc) { _track->_type = TrackVideo; _track->_ssrc = _ssrc; DebugL << printSSRC(_ssrc); - _muxer = std::make_shared(DEFAULT_VHOST,"rtp",printSSRC(_ssrc)); + + GET_CONFIG(bool,toRtxp,General::kPublishToRtxp); + GET_CONFIG(bool,toHls,General::kPublishToHls); + GET_CONFIG(bool,toMP4,General::kPublishToMP4); + + _muxer = std::make_shared(DEFAULT_VHOST,"rtp",printSSRC(_ssrc),0,toRtxp,toRtxp,toHls,toMP4); GET_CONFIG(string,dump_dir,RtpProxy::kDumpDir); { From df73eb2c10dfe817c413cc42afb48294287bfeee Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 9 Dec 2019 18:15:02 +0800 Subject: [PATCH 081/272] =?UTF-8?q?getSsrcInfo=E6=8E=A5=E5=8F=A3ssrc?= =?UTF-8?q?=E4=BD=BF=E7=94=A816=E8=BF=9B=E5=88=B6=E5=AD=97=E7=AC=A6?= =?UTF-8?q?=E4=B8=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 64402ad8..8b7487a5 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -697,7 +697,11 @@ void installWebApi() { API_REGIST(api,getSsrcInfo,{ CHECK_SECRET(); CHECK_ARGS("ssrc"); - auto process = RtpSelector::Instance().getProcess(allArgs["ssrc"],false); + uint32_t ssrc = 0; + stringstream ss(allArgs["ssrc"]); + ss >> std::hex >> ssrc; + + auto process = RtpSelector::Instance().getProcess(ssrc,false); if(!process){ val["exist"] = false; return; From 99c1dd339fe07b4dbc25ce4dc8cb2d5437212e11 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 11 Dec 2019 09:29:10 +0800 Subject: [PATCH 082/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dadts=E5=A4=B4?= =?UTF-8?q?=E7=9B=B8=E5=85=B3bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Extension/AAC.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Extension/AAC.h b/src/Extension/AAC.h index 156c4122..fe78509e 100644 --- a/src/Extension/AAC.h +++ b/src/Extension/AAC.h @@ -257,6 +257,9 @@ private: * 解析2个字节的aac配置 */ void onReady(){ + if(_cfg.size() < 2){ + return; + } AACFrame aacFrame; makeAdtsHeader(_cfg,aacFrame); getAACInfo(aacFrame,_sampleRate,_channel); From 2a518493d7dfccb3800392f61c84b61ceedf5f95 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 11 Dec 2019 20:01:51 +0800 Subject: [PATCH 083/272] =?UTF-8?q?=E5=AE=8C=E5=96=84rtp=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_rtp.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_rtp.cpp b/tests/test_rtp.cpp index 1be10ceb..7e23e8e6 100644 --- a/tests/test_rtp.cpp +++ b/tests/test_rtp.cpp @@ -53,6 +53,7 @@ static bool loadFile(const char *path){ uint32_t timeStamp_last = 0; uint16_t len; char rtp[2 * 1024]; + struct sockaddr addr = {0}; while (true) { if (2 != fread(&len, 1, 2, fp)) { WarnL; @@ -82,8 +83,7 @@ static bool loadFile(const char *path){ } timeStamp_last = timeStamp; - - RtpSelector::Instance().inputRtp(rtp,len, nullptr); + RtpSelector::Instance().inputRtp(rtp,len, &addr); } fclose(fp); return true; @@ -103,6 +103,8 @@ int main(int argc,char *argv[]) { rtspSrv->start(554);//默认554 rtmpSrv->start(1935);//默认1935 httpSrv->start(80);//默认80 + //此处可以选择MP4V-ES或MP2P + mINI::Instance()[RtpProxy::kRtpType] = "MP4V-ES"; loadFile(argv[1]); #else ErrorL << "please ENABLE_RTPPROXY and then test"; From 0adfb3ec83f26bf73b61d1c6264ac0318c0a8f4d Mon Sep 17 00:00:00 2001 From: Gemfield Date: Thu, 12 Dec 2019 20:07:21 +0800 Subject: [PATCH 084/272] Fix Docker timezone issue, and also expose RTP port --- docker/Dockerfile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 3f427538..d939ab14 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,18 +1,22 @@ FROM ubuntu:16.04 -#shell,rtmp,rtsp,rtsps,http,https +#shell,rtmp,rtsp,rtsps,http,https,rtp EXPOSE 9000/tcp EXPOSE 1935/tcp EXPOSE 554/tcp EXPOSE 322/tcp EXPOSE 80/tcp EXPOSE 443/tcp +EXPOSE 10000/udp +EXPOSE 10000/tcp RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ cmake \ git \ curl \ + vim \ ca-certificates \ + tzdata \ libssl-dev \ libmysqlclient-dev \ libx264-dev \ From ee66d945e8d1b681bc57d608ffb6091837544269 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 12 Dec 2019 22:25:55 +0800 Subject: [PATCH 085/272] =?UTF-8?q?=E6=9B=B4=E6=96=B0media-server=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/media-server | 2 +- src/Extension/Factory.cpp | 24 ++++++++++-------------- src/Record/MP4Muxer.cpp | 4 ++-- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/3rdpart/media-server b/3rdpart/media-server index 4325386b..8d40dad3 160000 --- a/3rdpart/media-server +++ b/3rdpart/media-server @@ -1 +1 @@ -Subproject commit 4325386be318889b7815cdd86a2e83f05a059c81 +Subproject commit 8d40dad3dbdce171756691d4511aca49fcf2a231 diff --git a/src/Extension/Factory.cpp b/src/Extension/Factory.cpp index 4aad5e9f..cff9a4c0 100644 --- a/src/Extension/Factory.cpp +++ b/src/Extension/Factory.cpp @@ -180,6 +180,9 @@ CodecId Factory::getCodecIdByAmf(const AMFValue &val){ if(str == "mp4a"){ return CodecAAC; } + if(str == "hev1" || str == "hvc1"){ + return CodecH265; + } WarnL << "暂不支持该Amf:" << str; return CodecInvalid; } @@ -187,12 +190,9 @@ CodecId Factory::getCodecIdByAmf(const AMFValue &val){ if (val.type() != AMF_NULL){ auto type_id = val.as_integer(); switch (type_id){ - case 7:{ - return CodecH264; - } - case 10:{ - return CodecAAC; - } + case 7: return CodecH264; + case 10: return CodecAAC; + case 12: return CodecH265; default: WarnL << "暂不支持该Amf:" << type_id; return CodecInvalid; @@ -219,14 +219,10 @@ RtmpCodec::Ptr Factory::getRtmpCodecByTrack(const Track::Ptr &track) { AMFValue Factory::getAmfByCodecId(CodecId codecId) { switch (codecId){ - case CodecAAC:{ - return AMFValue("mp4a"); - } - case CodecH264:{ - return AMFValue("avc1"); - } - default: - return AMFValue(AMF_NULL); + case CodecAAC: return AMFValue("mp4a"); + case CodecH264: return AMFValue("avc1"); + case CodecH265: return AMFValue(12); + default: return AMFValue(AMF_NULL); } } diff --git a/src/Record/MP4Muxer.cpp b/src/Record/MP4Muxer.cpp index 70d6e38c..53bd61ae 100644 --- a/src/Record/MP4Muxer.cpp +++ b/src/Record/MP4Muxer.cpp @@ -152,7 +152,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { struct mpeg4_avc_t avc; string sps_pps = string("\x00\x00\x00\x01", 4) + h264_track->getSps() + string("\x00\x00\x00\x01", 4) + h264_track->getPps(); - h264_annexbtomp4(&avc, sps_pps.data(), sps_pps.size(), NULL, 0, NULL); + h264_annexbtomp4(&avc, sps_pps.data(), sps_pps.size(), NULL, 0, NULL, NULL); uint8_t extra_data[1024]; int extra_data_size = mpeg4_avc_decoder_configuration_record_save(&avc, extra_data, sizeof(extra_data)); @@ -186,7 +186,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { string vps_sps_pps = string("\x00\x00\x00\x01", 4) + h265_track->getVps() + string("\x00\x00\x00\x01", 4) + h265_track->getSps() + string("\x00\x00\x00\x01", 4) + h265_track->getPps(); - h265_annexbtomp4(&hevc, vps_sps_pps.data(), vps_sps_pps.size(), NULL, 0, NULL); + h265_annexbtomp4(&hevc, vps_sps_pps.data(), vps_sps_pps.size(), NULL, 0, NULL, NULL); uint8_t extra_data[1024]; int extra_data_size = mpeg4_hevc_decoder_configuration_record_save(&hevc, extra_data, sizeof(extra_data)); From 5fe5dd9de60f44fb6e6644b9c263fe331fd43f80 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 12 Dec 2019 22:58:31 +0800 Subject: [PATCH 086/272] =?UTF-8?q?hls=E4=BF=9D=E5=AD=98ts=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=97=B6=E6=A0=B9=E6=8D=AE=E6=97=A5=E6=9C=9F=E3=80=81?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E4=BF=9D=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- src/Record/HlsMakerImp.cpp | 29 ++++++++++++++++++----------- src/Record/HlsMakerImp.h | 2 +- src/Record/MP4Recorder.cpp | 22 ++-------------------- 4 files changed, 22 insertions(+), 33 deletions(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 628d3b25..ecb38246 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 628d3b2527f63b54a5eb38b9e9973254d4a2192b +Subproject commit ecb382460ffa2f6f5cdf1e11b394797e3396b2d0 diff --git a/src/Record/HlsMakerImp.cpp b/src/Record/HlsMakerImp.cpp index 8fa9d4fd..88cd724f 100644 --- a/src/Record/HlsMakerImp.cpp +++ b/src/Record/HlsMakerImp.cpp @@ -56,21 +56,31 @@ HlsMakerImp::~HlsMakerImp() { } string HlsMakerImp::onOpenSegment(int index) { - auto full_path = fullPath(index); - _file = makeFile(full_path, true); + string segment_name , segment_path; + { + auto strDate = getTimeStr("%Y-%m-%d"); + auto strTime = getTimeStr("%H-%M-%S"); + segment_name = StrPrinter << strDate + "/" + strTime << "_" << index << ".ts"; + segment_path = _path_prefix + "/" + segment_name; + _segment_file_paths.emplace(index,segment_path); + } + _file = makeFile(segment_path, true); if(!_file){ - WarnL << "create file falied," << full_path << " " << get_uv_errmsg(); + WarnL << "create file falied," << segment_path << " " << get_uv_errmsg(); } - //DebugL << index << " " << full_path; if(_params.empty()){ - return StrPrinter << index << ".ts"; + return std::move(segment_name); } - return StrPrinter << index << ".ts" << "?" << _params; + return std::move(segment_name + "?" + _params); } void HlsMakerImp::onDelSegment(int index) { - //WarnL << index; - File::delete_file(fullPath(index).data()); + auto it = _segment_file_paths.find(index); + if(it == _segment_file_paths.end()){ + return; + } + File::delete_file(it->second.data()); + _segment_file_paths.erase(it); } void HlsMakerImp::onWriteSegment(const char *data, int len) { @@ -90,9 +100,6 @@ void HlsMakerImp::onWriteHls(const char *data, int len) { //DebugL << "\r\n" << string(data,len); } -string HlsMakerImp::fullPath(int index) { - return StrPrinter << _path_prefix << "/" << index << ".ts"; -} std::shared_ptr HlsMakerImp::makeFile(const string &file,bool setbuf) { auto ret= shared_ptr(File::createfile_file(file.data(), "wb"), [](FILE *fp) { diff --git a/src/Record/HlsMakerImp.h b/src/Record/HlsMakerImp.h index 55637fd8..ece0213a 100644 --- a/src/Record/HlsMakerImp.h +++ b/src/Record/HlsMakerImp.h @@ -49,9 +49,9 @@ protected: void onWriteSegment(const char *data, int len) override; void onWriteHls(const char *data, int len) override; private: - string fullPath(int index); std::shared_ptr makeFile(const string &file,bool setbuf = false); private: + map _segment_file_paths; std::shared_ptr _file; std::shared_ptr _file_buf; string _path_prefix; diff --git a/src/Record/MP4Recorder.cpp b/src/Record/MP4Recorder.cpp index e0332a56..a92f3fde 100644 --- a/src/Record/MP4Recorder.cpp +++ b/src/Record/MP4Recorder.cpp @@ -29,30 +29,12 @@ #include #include "Common/config.h" #include "MP4Recorder.h" -#include "Util/util.h" -#include "Util/NoticeCenter.h" #include "Thread/WorkThreadPool.h" using namespace toolkit; namespace mediakit { -string timeStr(const char *fmt) { - std::tm tm_snapshot; - auto time = ::time(NULL); -#if defined(_WIN32) - localtime_s(&tm_snapshot, &time); // thread-safe -#else - localtime_r(&time, &tm_snapshot); // POSIX -#endif - const size_t size = 1024; - char buffer[size]; - auto success = std::strftime(buffer, size, fmt, &tm_snapshot); - if (0 == success) - return string(fmt); - return buffer; -} - MP4Recorder::MP4Recorder(const string& strPath, const string &strVhost, const string &strApp, @@ -70,8 +52,8 @@ MP4Recorder::~MP4Recorder() { void MP4Recorder::createFile() { closeFile(); - auto strDate = timeStr("%Y-%m-%d"); - auto strTime = timeStr("%H-%M-%S"); + auto strDate = getTimeStr("%Y-%m-%d"); + auto strTime = getTimeStr("%H-%M-%S"); auto strFileTmp = _strPath + strDate + "/." + strTime + ".mp4"; auto strFile = _strPath + strDate + "/" + strTime + ".mp4"; From d9a5de5a63cfce079dd90b32d4372a9f3703156e Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 12 Dec 2019 23:02:11 +0800 Subject: [PATCH 087/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=8C=81=E7=BB=AD?= =?UTF-8?q?=E9=9B=86=E6=88=90=E7=BC=96=E8=AF=91=E5=A4=B1=E8=B4=A5=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ccpp.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 55bb0021..59766f69 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -14,7 +14,7 @@ jobs: run: git submodule update --init - name: apt-get安装依赖库(非必选) - run: sudo apt-get install -y cmake libssl-dev libmp4v2-dev libsdl-dev libavcodec-dev libavutil-dev + run: sudo apt-get update && sudo apt-get install -y cmake libssl-dev libmp4v2-dev libsdl-dev libavcodec-dev libavutil-dev - name: 编译 run: mkdir -p linux_build && cd linux_build && cmake .. && make -j4 From fbf0f6350371029f081c63d8bc6dc64f6106b5ce Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 13 Dec 2019 13:35:27 +0800 Subject: [PATCH 088/272] =?UTF-8?q?1=E3=80=81getMediaList=E6=8E=A5?= =?UTF-8?q?=E5=85=A5=E6=B7=BB=E5=8A=A0tracks=E3=80=81readerCount=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=202=E3=80=81=E6=B7=BB=E5=8A=A0getMediaInfo=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 8b7487a5..7b48493d 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -411,6 +411,14 @@ void installWebApi() { item["vhost"] = media->getVhost(); item["app"] = media->getApp(); item["stream"] = media->getId(); + item["readerCount"] = media->readerCount(); + for(auto &track : media->getTracks()){ + Value obj; + obj["codec_id"] = track->getCodecId(); + obj["codec_type"] = track->getTrackType(); + obj["ready"] = track->ready(); + item["tracks"].append(obj); + } val["data"].append(item); }); }); @@ -422,6 +430,26 @@ void installWebApi() { val["online"] = (bool) (MediaSource::find(allArgs["schema"],allArgs["vhost"],allArgs["app"],allArgs["stream"],false)); }); + //测试url http://127.0.0.1/index/api/getMediaInfo?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs + API_REGIST(api,getMediaInfo,{ + CHECK_SECRET(); + CHECK_ARGS("schema","vhost","app","stream"); + auto src = MediaSource::find(allArgs["schema"],allArgs["vhost"],allArgs["app"],allArgs["stream"],false); + if(!src){ + val["online"] = false; + return; + } + val["online"] = true; + val["readerCount"] = src->readerCount(); + for(auto &track : src->getTracks()){ + Value obj; + obj["codec_id"] = track->getCodecId(); + obj["codec_type"] = track->getTrackType(); + obj["ready"] = track->ready(); + val["tracks"].append(obj); + } + }); + //主动关断流,包括关断拉流、推流 //测试url http://127.0.0.1/index/api/close_stream?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1 API_REGIST(api,close_stream,{ From bd8a9a78e86c012efe48f81af5fca7d8098ad943 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 13 Dec 2019 15:42:12 +0800 Subject: [PATCH 089/272] =?UTF-8?q?rtp=E4=BB=A3=E7=90=86=E9=98=B2=E6=AD=A2?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E6=88=B3=E5=9B=9E=E7=8E=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtp/RtpProcess.cpp | 9 ++++++--- src/Rtp/RtpProcess.h | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Rtp/RtpProcess.cpp b/src/Rtp/RtpProcess.cpp index d4d57847..aa9261a8 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -183,6 +183,9 @@ void RtpProcess::onPSDecode(int stream, int64_t dts, const void *data, int bytes) { + pts /= 90; + dts /= 90; + _stamps[codecid].revise(dts,pts,dts,pts,false); switch (codecid) { case STREAM_VIDEO_H264: { @@ -202,7 +205,7 @@ void RtpProcess::onPSDecode(int stream, if(_save_file_video){ fwrite((uint8_t *)data,bytes, 1, _save_file_video.get()); } - auto frame = std::make_shared((char *) data, bytes, dts / 90, pts / 90,0); + auto frame = std::make_shared((char *) data, bytes, dts, pts,0); _merger->inputFrame(frame,[this](uint32_t dts, uint32_t pts, const Buffer::Ptr &buffer) { _muxer->inputFrame(std::make_shared(buffer->data(), buffer->size(), dts, pts,4)); }); @@ -224,7 +227,7 @@ void RtpProcess::onPSDecode(int stream, if(_save_file_video){ fwrite((uint8_t *)data,bytes, 1, _save_file_video.get()); } - auto frame = std::make_shared((char *) data, bytes, dts / 90, pts / 90, 0); + auto frame = std::make_shared((char *) data, bytes, dts, pts, 0); _merger->inputFrame(frame,[this](uint32_t dts, uint32_t pts, const Buffer::Ptr &buffer) { _muxer->inputFrame(std::make_shared(buffer->data(), buffer->size(), dts, pts, 4)); }); @@ -244,7 +247,7 @@ void RtpProcess::onPSDecode(int stream, WarnL << "audio track change to AAC from codecid:" << _codecid_audio; return; } - _muxer->inputFrame(std::make_shared((char *) data, bytes, dts / 90, 7)); + _muxer->inputFrame(std::make_shared((char *) data, bytes, dts, 7)); break; } default: diff --git a/src/Rtp/RtpProcess.h b/src/Rtp/RtpProcess.h index 13ddfbeb..42f8f20a 100644 --- a/src/Rtp/RtpProcess.h +++ b/src/Rtp/RtpProcess.h @@ -33,6 +33,7 @@ #include "RtpDecoder.h" #include "PSDecoder.h" #include "Common/Device.h" +#include "Common/Stamp.h" using namespace mediakit; namespace mediakit{ @@ -72,6 +73,7 @@ private: MultiMediaSourceMuxer::Ptr _muxer; std::shared_ptr _merger; Ticker _last_rtp_time; + map _stamps; }; }//namespace mediakit From 6e59000265055a39bb1a40387ef24acc418071e7 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 13 Dec 2019 15:42:58 +0800 Subject: [PATCH 090/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=BF=87=E6=BB=A4?= =?UTF-8?q?=E5=A4=9A=E4=BD=99=E5=B8=A7=E7=B1=BB=E5=9E=8B=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Extension/H264.h | 8 ++--- src/Extension/H264Rtmp.cpp | 65 ++++++++++++++++---------------------- 2 files changed, 30 insertions(+), 43 deletions(-) diff --git a/src/Extension/H264.h b/src/Extension/H264.h index 394752ec..4bb8ea1c 100644 --- a/src/Extension/H264.h +++ b/src/Extension/H264.h @@ -49,7 +49,6 @@ public: NAL_SPS = 7, NAL_PPS = 8, NAL_IDR = 5, - NAL_B_P = 1 } NalType; char *data() const override{ @@ -315,18 +314,15 @@ private: //I insertConfigFrame(frame); VideoTrack::inputFrame(frame); - _last_frame_is_idr = true; } break; - case H264Frame::NAL_B_P:{ - //B or P + default: VideoTrack::inputFrame(frame); - _last_frame_is_idr = false; - } break; } + _last_frame_is_idr = type == H264Frame::NAL_IDR; if(_width == 0 && ready()){ onReady(); } diff --git a/src/Extension/H264Rtmp.cpp b/src/Extension/H264Rtmp.cpp index 2f56b9e6..5bea72de 100644 --- a/src/Extension/H264Rtmp.cpp +++ b/src/Extension/H264Rtmp.cpp @@ -144,44 +144,35 @@ void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) { } } - switch (type){ - case H264Frame::NAL_IDR: - case H264Frame::NAL_B_P:{ - if(_lastPacket && _lastPacket->timeStamp != frame->stamp()) { - RtmpCodec::inputRtmp(_lastPacket, _lastPacket->isVideoKeyFrame()); - _lastPacket = nullptr; - } - - if(!_lastPacket) { - //I or P or B frame - int8_t flags = 7; //h.264 - bool is_config = false; - flags |= ((frame->keyFrame() ? FLV_KEY_FRAME : FLV_INTER_FRAME) << 4); - - _lastPacket = ResourcePoolHelper::obtainObj(); - _lastPacket->strBuf.clear(); - _lastPacket->strBuf.push_back(flags); - _lastPacket->strBuf.push_back(!is_config); - auto cts = frame->pts() - frame->dts(); - cts = htonl(cts); - _lastPacket->strBuf.append((char *)&cts + 1, 3); - - _lastPacket->chunkId = CHUNK_VIDEO; - _lastPacket->streamId = STREAM_MEDIA; - _lastPacket->timeStamp = frame->stamp(); - _lastPacket->typeId = MSG_VIDEO; - - } - auto size = htonl(iLen); - _lastPacket->strBuf.append((char *) &size, 4); - _lastPacket->strBuf.append(pcData, iLen); - _lastPacket->bodySize = _lastPacket->strBuf.size(); - } - break; - - default: - break; + if(_lastPacket && _lastPacket->timeStamp != frame->stamp()) { + RtmpCodec::inputRtmp(_lastPacket, _lastPacket->isVideoKeyFrame()); + _lastPacket = nullptr; } + + if(!_lastPacket) { + //I or P or B frame + int8_t flags = 7; //h.264 + bool is_config = false; + flags |= ((frame->keyFrame() ? FLV_KEY_FRAME : FLV_INTER_FRAME) << 4); + + _lastPacket = ResourcePoolHelper::obtainObj(); + _lastPacket->strBuf.clear(); + _lastPacket->strBuf.push_back(flags); + _lastPacket->strBuf.push_back(!is_config); + auto cts = frame->pts() - frame->dts(); + cts = htonl(cts); + _lastPacket->strBuf.append((char *)&cts + 1, 3); + + _lastPacket->chunkId = CHUNK_VIDEO; + _lastPacket->streamId = STREAM_MEDIA; + _lastPacket->timeStamp = frame->stamp(); + _lastPacket->typeId = MSG_VIDEO; + + } + auto size = htonl(iLen); + _lastPacket->strBuf.append((char *) &size, 4); + _lastPacket->strBuf.append(pcData, iLen); + _lastPacket->bodySize = _lastPacket->strBuf.size(); } From 14ba899c610f533ff460dcafe100f4b6952a8283 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 13 Dec 2019 15:43:24 +0800 Subject: [PATCH 091/272] =?UTF-8?q?=E9=80=89=E6=8B=A9=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E5=AF=BC=E5=87=BA=E8=B0=83=E8=AF=95=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_rtp.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_rtp.cpp b/tests/test_rtp.cpp index 7e23e8e6..14b3d61a 100644 --- a/tests/test_rtp.cpp +++ b/tests/test_rtp.cpp @@ -105,6 +105,9 @@ int main(int argc,char *argv[]) { httpSrv->start(80);//默认80 //此处可以选择MP4V-ES或MP2P mINI::Instance()[RtpProxy::kRtpType] = "MP4V-ES"; + //此处选择是否导出调试文件 +// mINI::Instance()[RtpProxy::kDumpDir] = "/Users/xzl/Desktop/"; + loadFile(argv[1]); #else ErrorL << "please ENABLE_RTPPROXY and then test"; From 6d697daeb068b9e1d1d25e399223f4722dc544d1 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 13 Dec 2019 15:49:57 +0800 Subject: [PATCH 092/272] =?UTF-8?q?=E5=8E=BB=E9=99=A4FrameMerger=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtp/RtpProcess.cpp | 41 ++++------------------------------------- src/Rtp/RtpProcess.h | 2 -- 2 files changed, 4 insertions(+), 39 deletions(-) diff --git a/src/Rtp/RtpProcess.cpp b/src/Rtp/RtpProcess.cpp index aa9261a8..e328063d 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -33,34 +33,6 @@ namespace mediakit{ -/** - * 合并一些时间戳相同的frame - */ -class FrameMerger { -public: - FrameMerger() = default; - virtual ~FrameMerger() = default; - - void inputFrame(const Frame::Ptr &frame,const function &cb){ - if (!_frameCached.empty() && _frameCached.back()->dts() != frame->dts()) { - Frame::Ptr back = _frameCached.back(); - Buffer::Ptr merged_frame = back; - if(_frameCached.size() != 1){ - string merged; - _frameCached.for_each([&](const Frame::Ptr &frame){ - merged.append(frame->data(),frame->size()); - }); - merged_frame = std::make_shared(std::move(merged)); - } - cb(back->dts(),back->pts(),merged_frame); - _frameCached.clear(); - } - _frameCached.emplace_back(Frame::getCacheAbleFrame(frame)); - } -private: - List _frameCached; -}; - string printSSRC(uint32_t ui32Ssrc) { char tmp[9] = { 0 }; ui32Ssrc = htonl(ui32Ssrc); @@ -118,7 +90,6 @@ RtpProcess::RtpProcess(uint32_t ssrc) { }); } } - _merger = std::make_shared(); } RtpProcess::~RtpProcess() { @@ -205,10 +176,8 @@ void RtpProcess::onPSDecode(int stream, if(_save_file_video){ fwrite((uint8_t *)data,bytes, 1, _save_file_video.get()); } - auto frame = std::make_shared((char *) data, bytes, dts, pts,0); - _merger->inputFrame(frame,[this](uint32_t dts, uint32_t pts, const Buffer::Ptr &buffer) { - _muxer->inputFrame(std::make_shared(buffer->data(), buffer->size(), dts, pts,4)); - }); + auto frame = std::make_shared((char *) data, bytes, dts, pts,4); + _muxer->inputFrame(frame); break; } @@ -227,10 +196,8 @@ void RtpProcess::onPSDecode(int stream, if(_save_file_video){ fwrite((uint8_t *)data,bytes, 1, _save_file_video.get()); } - auto frame = std::make_shared((char *) data, bytes, dts, pts, 0); - _merger->inputFrame(frame,[this](uint32_t dts, uint32_t pts, const Buffer::Ptr &buffer) { - _muxer->inputFrame(std::make_shared(buffer->data(), buffer->size(), dts, pts, 4)); - }); + auto frame = std::make_shared((char *) data, bytes, dts, pts, 4); + _muxer->inputFrame(frame); break; } diff --git a/src/Rtp/RtpProcess.h b/src/Rtp/RtpProcess.h index 42f8f20a..08d56b6b 100644 --- a/src/Rtp/RtpProcess.h +++ b/src/Rtp/RtpProcess.h @@ -40,7 +40,6 @@ namespace mediakit{ string printSSRC(uint32_t ui32Ssrc); -class FrameMerger; class RtpProcess : public RtpReceiver , public RtpDecoder , public PSDecoder { public: typedef std::shared_ptr Ptr; @@ -71,7 +70,6 @@ private: int _codecid_video = 0; int _codecid_audio = 0; MultiMediaSourceMuxer::Ptr _muxer; - std::shared_ptr _merger; Ticker _last_rtp_time; map _stamps; }; From e6235e00e1d5679cb8762094e688ec5301402a14 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 13 Dec 2019 16:17:15 +0800 Subject: [PATCH 093/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dwindows=E4=B8=8B?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E7=BC=96=E8=AF=91=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index ecb38246..2415fa73 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit ecb382460ffa2f6f5cdf1e11b394797e3396b2d0 +Subproject commit 2415fa732a37859795516f5eb17a045763dc71d3 From caf8274026cdec36a9b573adc2655df644735fbb Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 13 Dec 2019 16:20:03 +0800 Subject: [PATCH 094/272] =?UTF-8?q?=E6=9B=B4=E6=96=B0ZLToolKit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 2415fa73..e87444e5 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 2415fa732a37859795516f5eb17a045763dc71d3 +Subproject commit e87444e56f4196ab9d67e9033aa09d8a89a11d3a From bfa9a3b67868857177aedf2c1c1fadb682bf0694 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 13 Dec 2019 16:29:26 +0800 Subject: [PATCH 095/272] #include --- 3rdpart/ZLToolKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index e87444e5..2a7381ff 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit e87444e56f4196ab9d67e9033aa09d8a89a11d3a +Subproject commit 2a7381ff6dbe5a2f419957734425d5423f3850c6 From c353c626c181eb79c0427f1842300b6f8a07f097 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 16 Dec 2019 11:00:40 +0800 Subject: [PATCH 096/272] =?UTF-8?q?1=E3=80=81=E6=A0=B9=E6=8D=AEPS=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E6=97=B6=E9=97=B4=E6=88=B3=202=E3=80=81=E4=BF=AE?= =?UTF-8?q?=E5=A4=8Drtp=E4=BB=A3=E7=90=86=E5=8F=AF=E8=83=BD=E8=8A=B1?= =?UTF-8?q?=E5=B1=8F=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtp/RtpProcess.cpp | 51 ++++++++++++++++++++++++++++++++++++----- src/Rtp/RtpProcess.h | 6 +++-- src/Rtp/RtpSelector.cpp | 4 ++-- src/Rtp/RtpSelector.h | 2 +- tests/test_rtp.cpp | 7 +----- 5 files changed, 53 insertions(+), 17 deletions(-) diff --git a/src/Rtp/RtpProcess.cpp b/src/Rtp/RtpProcess.cpp index e328063d..d26e2e56 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -33,6 +33,35 @@ namespace mediakit{ + +/** +* 合并一些时间戳相同的frame +*/ +class FrameMerger { +public: + FrameMerger() = default; + virtual ~FrameMerger() = default; + + void inputFrame(const Frame::Ptr &frame,const function &cb){ + if (!_frameCached.empty() && _frameCached.back()->dts() != frame->dts()) { + Frame::Ptr back = _frameCached.back(); + Buffer::Ptr merged_frame = back; + if(_frameCached.size() != 1){ + string merged; + _frameCached.for_each([&](const Frame::Ptr &frame){ + merged.append(frame->data(),frame->size()); + }); + merged_frame = std::make_shared(std::move(merged)); + } + cb(back->dts(),back->pts(),merged_frame); + _frameCached.clear(); + } + _frameCached.emplace_back(Frame::getCacheAbleFrame(frame)); + } +private: + List _frameCached; +}; + string printSSRC(uint32_t ui32Ssrc) { char tmp[9] = { 0 }; ui32Ssrc = htonl(ui32Ssrc); @@ -90,6 +119,7 @@ RtpProcess::RtpProcess(uint32_t ssrc) { }); } } + _merger = std::make_shared(); } RtpProcess::~RtpProcess() { @@ -101,7 +131,7 @@ RtpProcess::~RtpProcess() { } } -bool RtpProcess::inputRtp(const char *data, int data_len,const struct sockaddr *addr) { +bool RtpProcess::inputRtp(const char *data, int data_len,const struct sockaddr *addr,uint32_t *dts_out) { GET_CONFIG(bool,check_source,RtpProxy::kCheckSource); //检查源是否合法 if(!_addr){ @@ -116,7 +146,11 @@ bool RtpProcess::inputRtp(const char *data, int data_len,const struct sockaddr * } _last_rtp_time.resetTime(); - return handleOneRtp(0,_track,(unsigned char *)data,data_len); + bool ret = handleOneRtp(0,_track,(unsigned char *)data,data_len); + if(dts_out){ + *dts_out = _dts; + } + return ret; } void RtpProcess::onRtpSorted(const RtpPacket::Ptr &rtp, int) { @@ -156,6 +190,7 @@ void RtpProcess::onPSDecode(int stream, int bytes) { pts /= 90; dts /= 90; + _dts = dts; _stamps[codecid].revise(dts,pts,dts,pts,false); switch (codecid) { @@ -176,8 +211,10 @@ void RtpProcess::onPSDecode(int stream, if(_save_file_video){ fwrite((uint8_t *)data,bytes, 1, _save_file_video.get()); } - auto frame = std::make_shared((char *) data, bytes, dts, pts,4); - _muxer->inputFrame(frame); + auto frame = std::make_shared((char *) data, bytes, dts, pts,0); + _merger->inputFrame(frame,[this](uint32_t dts, uint32_t pts, const Buffer::Ptr &buffer) { + _muxer->inputFrame(std::make_shared(buffer->data(), buffer->size(), dts, pts,4)); + }); break; } @@ -196,8 +233,10 @@ void RtpProcess::onPSDecode(int stream, if(_save_file_video){ fwrite((uint8_t *)data,bytes, 1, _save_file_video.get()); } - auto frame = std::make_shared((char *) data, bytes, dts, pts, 4); - _muxer->inputFrame(frame); + auto frame = std::make_shared((char *) data, bytes, dts, pts, 0); + _merger->inputFrame(frame,[this](uint32_t dts, uint32_t pts, const Buffer::Ptr &buffer) { + _muxer->inputFrame(std::make_shared(buffer->data(), buffer->size(), dts, pts, 4)); + }); break; } diff --git a/src/Rtp/RtpProcess.h b/src/Rtp/RtpProcess.h index 08d56b6b..7053956e 100644 --- a/src/Rtp/RtpProcess.h +++ b/src/Rtp/RtpProcess.h @@ -39,13 +39,13 @@ using namespace mediakit; namespace mediakit{ string printSSRC(uint32_t ui32Ssrc); - +class FrameMerger; class RtpProcess : public RtpReceiver , public RtpDecoder , public PSDecoder { public: typedef std::shared_ptr Ptr; RtpProcess(uint32_t ssrc); ~RtpProcess(); - bool inputRtp(const char *data,int data_len, const struct sockaddr *addr); + bool inputRtp(const char *data,int data_len, const struct sockaddr *addr , uint32_t *dts_out = nullptr); bool alive(); string get_peer_ip(); uint16_t get_peer_port(); @@ -70,8 +70,10 @@ private: int _codecid_video = 0; int _codecid_audio = 0; MultiMediaSourceMuxer::Ptr _muxer; + std::shared_ptr _merger; Ticker _last_rtp_time; map _stamps; + uint32_t _dts = 0; }; }//namespace mediakit diff --git a/src/Rtp/RtpSelector.cpp b/src/Rtp/RtpSelector.cpp index 2ff997a7..f57081d2 100644 --- a/src/Rtp/RtpSelector.cpp +++ b/src/Rtp/RtpSelector.cpp @@ -31,7 +31,7 @@ namespace mediakit{ INSTANCE_IMP(RtpSelector); -bool RtpSelector::inputRtp(const char *data, int data_len,const struct sockaddr *addr) { +bool RtpSelector::inputRtp(const char *data, int data_len,const struct sockaddr *addr,uint32_t *dts_out) { if(_last_rtp_time.elapsedTime() > 3000){ _last_rtp_time.resetTime(); onManager(); @@ -43,7 +43,7 @@ bool RtpSelector::inputRtp(const char *data, int data_len,const struct sockaddr } auto process = getProcess(ssrc, true); if(process){ - return process->inputRtp(data,data_len, addr); + return process->inputRtp(data,data_len, addr,dts_out); } return false; } diff --git a/src/Rtp/RtpSelector.h b/src/Rtp/RtpSelector.h index 872d3ada..bbbd9665 100644 --- a/src/Rtp/RtpSelector.h +++ b/src/Rtp/RtpSelector.h @@ -41,7 +41,7 @@ public: ~RtpSelector(); static RtpSelector &Instance(); - bool inputRtp(const char *data,int data_len,const struct sockaddr *addr); + bool inputRtp(const char *data,int data_len,const struct sockaddr *addr ,uint32_t *dts_out = nullptr ); static uint32_t getSSRC(const char *data,int data_len); RtpProcess::Ptr getProcess(uint32_t ssrc,bool makeNew); void delProcess(uint32_t ssrc,const RtpProcess *ptr); diff --git a/tests/test_rtp.cpp b/tests/test_rtp.cpp index 14b3d61a..52c8fe03 100644 --- a/tests/test_rtp.cpp +++ b/tests/test_rtp.cpp @@ -71,19 +71,14 @@ static bool loadFile(const char *path){ } uint32_t timeStamp; - memcpy(&timeStamp, rtp + 4, 4); - timeStamp = ntohl(timeStamp); - timeStamp /= 90; - + RtpSelector::Instance().inputRtp(rtp,len, &addr,&timeStamp); if(timeStamp_last){ auto diff = timeStamp - timeStamp_last; if(diff > 0){ usleep(diff * 1000); } } - timeStamp_last = timeStamp; - RtpSelector::Instance().inputRtp(rtp,len, &addr); } fclose(fp); return true; From 3f12c0ccdead899539c0334661bc531664105380 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 16 Dec 2019 15:49:52 +0800 Subject: [PATCH 097/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E6=88=B3=E8=A6=86=E7=9B=96=E7=9A=84=E7=B4=AF=E7=A7=AF=E8=AF=AF?= =?UTF-8?q?=E5=B7=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/Stamp.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Common/Stamp.cpp b/src/Common/Stamp.cpp index 4474fe45..f3507869 100644 --- a/src/Common/Stamp.cpp +++ b/src/Common/Stamp.cpp @@ -62,7 +62,11 @@ void Stamp::revise(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out, if(_last_dts != dts){ //时间戳发生变更 - _relativeStamp += deltaStamp(modifyStamp ? _ticker.elapsedTime() : dts); + if(modifyStamp){ + _relativeStamp = _ticker.elapsedTime(); + }else{ + _relativeStamp += deltaStamp(dts); + } _last_dts = dts; } dts_out = _relativeStamp; From 5322a5761b1d35f188ad58c5ead3d56c5834ce2d Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 16 Dec 2019 16:17:52 +0800 Subject: [PATCH 098/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E6=88=B3=E8=B7=B3=E8=B7=83=E5=8F=AF=E8=83=BD=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/Stamp.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Common/Stamp.cpp b/src/Common/Stamp.cpp index f3507869..9d2c2d45 100644 --- a/src/Common/Stamp.cpp +++ b/src/Common/Stamp.cpp @@ -26,6 +26,8 @@ #include "Stamp.h" +#define MAX_DELTA_STAMP 300 + namespace mediakit { int64_t DeltaStamp::deltaStamp(int64_t stamp) { @@ -39,7 +41,8 @@ int64_t DeltaStamp::deltaStamp(int64_t stamp) { if(ret >= 0){ //时间戳增量为正,返回之 _last_stamp = stamp; - return ret; + //在直播情况下,时间戳增量不得大于MAX_DELTA_STAMP + return ret < MAX_DELTA_STAMP ? ret : (_playback ? ret : 0); } //时间戳增量为负,说明时间戳回环了或回退了 @@ -72,8 +75,8 @@ void Stamp::revise(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out, dts_out = _relativeStamp; //////////////以下是播放时间戳的计算////////////////// - if(pts_dts_diff > 200 || pts_dts_diff < -200){ - //如果差值大于200毫秒,则认为由于回环导致时间戳错乱了 + if(pts_dts_diff > MAX_DELTA_STAMP || pts_dts_diff < -MAX_DELTA_STAMP){ + //如果差值太大,则认为由于回环导致时间戳错乱了 pts_dts_diff = 0; } From 0670f9f62ba5691fd3fbcea9bfc12d427bf201e5 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 16 Dec 2019 17:12:24 +0800 Subject: [PATCH 099/272] =?UTF-8?q?=E6=9B=B4=E6=96=B0ios=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 +- README_CN.md | 4 +- cmake/FindZLTOOLKIT.cmake | 18 - cmake/iOS.cmake | 209 ------------ cmake/ios.toolchain.cmake | 674 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 678 insertions(+), 237 deletions(-) delete mode 100644 cmake/FindZLTOOLKIT.cmake delete mode 100644 cmake/iOS.cmake create mode 100644 cmake/ios.toolchain.cmake diff --git a/README.md b/README.md index e3c0c950..400b9134 100644 --- a/README.md +++ b/README.md @@ -184,21 +184,15 @@ git submodule update --init Same with Build on macOS -- Guidance - ``` - cd ZLMediaKit - ./build_for_ios.sh - ``` - -- You can also generate Xcode projects and recompile them: +- You can generate Xcode projects and recompile them , [learn more](https://github.com/leetal/ios-cmake): ``` cd ZLMediaKit mkdir -p build cd build # Generate Xcode project, project file is in build directory - cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/iOS.cmake -DIOS_PLATFORM=SIMULATOR64 -G "Xcode" + cmake .. -G Xcode -DCMAKE_TOOLCHAIN_FILE=../cmake/ios.toolchain.cmake -DPLATFORM=OS64COMBINED ``` diff --git a/README_CN.md b/README_CN.md index 037c5c5a..37917955 100644 --- a/README_CN.md +++ b/README_CN.md @@ -196,14 +196,14 @@ git submodule update --init cd ZLMediaKit ./build_for_ios.sh ``` -- 你也可以生成Xcode工程再编译: +- 你也可以生成Xcode工程再编译,[了解更多](https://github.com/leetal/ios-cmake): ``` cd ZLMediaKit mkdir -p build cd build # 生成Xcode工程,工程文件在build目录下 - cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/iOS.cmake -DIOS_PLATFORM=SIMULATOR64 -G "Xcode" + cmake .. -G Xcode -DCMAKE_TOOLCHAIN_FILE=../cmake/ios.toolchain.cmake -DPLATFORM=OS64COMBINED ``` ## 编译(Android) diff --git a/cmake/FindZLTOOLKIT.cmake b/cmake/FindZLTOOLKIT.cmake deleted file mode 100644 index 3839ca3f..00000000 --- a/cmake/FindZLTOOLKIT.cmake +++ /dev/null @@ -1,18 +0,0 @@ -find_path(ZLTOOLKIT_INCLUDE_DIR - NAMES Network/Socket.h - PATHS - ${PROJECT_SOURCE_DIR}/../ZLToolKit/src - $ENV{HOME}/ZLToolKit/include) - -find_library(ZLTOOLKIT_LIBRARY - NAMES ZLToolKit - PATHS - ${PROJECT_SOURCE_DIR}/../ZLToolKit/build/lib - $ENV{HOME}/ZLToolKit/lib) - -set(ZLTOOLKIT_LIBRARIES ${ZLTOOLKIT_LIBRARY}) -set(ZLTOOLKIT_INCLUDE_DIRS ${ZLTOOLKIT_INCLUDE_DIR}) - -include(FindPackageHandleStandardArgs) - -find_package_handle_standard_args(ZLTOOLKIT DEFAULT_MSG ZLTOOLKIT_LIBRARY ZLTOOLKIT_INCLUDE_DIR) diff --git a/cmake/iOS.cmake b/cmake/iOS.cmake deleted file mode 100644 index f4367787..00000000 --- a/cmake/iOS.cmake +++ /dev/null @@ -1,209 +0,0 @@ -# This file is based off of the Platform/Darwin.cmake and Platform/UnixPaths.cmake -# files which are included with CMake 2.8.4 -# It has been altered for iOS development - -# Options: -# -# IOS_PLATFORM = OS (default) or SIMULATOR or SIMULATOR64 -# This decides if SDKS will be selected from the iPhoneOS.platform or iPhoneSimulator.platform folders -# OS - the default, used to build for iPhone and iPad physical devices, which have an arm arch. -# SIMULATOR - used to build for the Simulator platforms, which have an x86 arch. -# -# CMAKE_IOS_DEVELOPER_ROOT = automatic(default) or /path/to/platform/Developer folder -# By default this location is automatcially chosen based on the IOS_PLATFORM value above. -# If set manually, it will override the default location and force the user of a particular Developer Platform -# -# CMAKE_IOS_SDK_ROOT = automatic(default) or /path/to/platform/Developer/SDKs/SDK folder -# By default this location is automatcially chosen based on the CMAKE_IOS_DEVELOPER_ROOT value. -# In this case it will always be the most up-to-date SDK found in the CMAKE_IOS_DEVELOPER_ROOT path. -# If set manually, this will force the use of a specific SDK version - -# Macros: -# -# set_xcode_property (TARGET XCODE_PROPERTY XCODE_VALUE) -# A convenience macro for setting xcode specific properties on targets -# example: set_xcode_property (myioslib IPHONEOS_DEPLOYMENT_TARGET "3.1") -# -# find_host_package (PROGRAM ARGS) -# A macro used to find executable programs on the host system, not within the iOS environment. -# Thanks to the android-cmake project for providing the command - -# Standard settings -set (CMAKE_SYSTEM_NAME Darwin) -set (CMAKE_SYSTEM_VERSION 1) -set (UNIX True) -set (APPLE True) -set (IOS True) - -# Required as of cmake 2.8.10 -set (CMAKE_OSX_DEPLOYMENT_TARGET "" CACHE STRING "Force unset of the deployment target for iOS" FORCE) - -# Determine the cmake host system version so we know where to find the iOS SDKs -find_program (CMAKE_UNAME uname /bin /usr/bin /usr/local/bin) -if (CMAKE_UNAME) -exec_program(uname ARGS -r OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION) -string (REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\1" DARWIN_MAJOR_VERSION "${CMAKE_HOST_SYSTEM_VERSION}") -endif (CMAKE_UNAME) - -# Force the compilers to gcc for iOS -include (CMakeForceCompiler) -#CMAKE_FORCE_C_COMPILER (/usr/bin/gcc Apple) -set(CMAKE_C_COMPILER /usr/bin/clang) -#CMAKE_FORCE_CXX_COMPILER (/usr/bin/g++ Apple) -set(CMAKE_CXX_COMPILER /usr/bin/clang++) -set(CMAKE_AR ar CACHE FILEPATH "" FORCE) - -# Skip the platform compiler checks for cross compiling -set (CMAKE_CXX_COMPILER_WORKS TRUE) -set (CMAKE_C_COMPILER_WORKS TRUE) - -# All iOS/Darwin specific settings - some may be redundant -set (CMAKE_SHARED_LIBRARY_PREFIX "lib") -set (CMAKE_SHARED_LIBRARY_SUFFIX ".dylib") -set (CMAKE_SHARED_MODULE_PREFIX "lib") -set (CMAKE_SHARED_MODULE_SUFFIX ".so") -set (CMAKE_MODULE_EXISTS 1) -set (CMAKE_DL_LIBS "") - -set (CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ") -set (CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ") -set (CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}") -set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") - -# Hidden visibilty is required for cxx on iOS -set (CMAKE_C_FLAGS_INIT "") -set (CMAKE_CXX_FLAGS_INIT "-fvisibility=hidden -fvisibility-inlines-hidden") - -set (CMAKE_C_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}") -set (CMAKE_CXX_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}") - -set (CMAKE_PLATFORM_HAS_INSTALLNAME 1) -set (CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib -headerpad_max_install_names") -set (CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -headerpad_max_install_names") -set (CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") -set (CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,") -set (CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".so" ".a") - -# hack: if a new cmake (which uses CMAKE_INSTALL_NAME_TOOL) runs on an old build tree -# (where install_name_tool was hardcoded) and where CMAKE_INSTALL_NAME_TOOL isn't in the cache -# and still cmake didn't fail in CMakeFindBinUtils.cmake (because it isn't rerun) -# hardcode CMAKE_INSTALL_NAME_TOOL here to install_name_tool, so it behaves as it did before, Alex -if (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) -find_program(CMAKE_INSTALL_NAME_TOOL install_name_tool) -endif (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) - -# Setup iOS platform unless specified manually with IOS_PLATFORM -if (NOT DEFINED IOS_PLATFORM) -set (IOS_PLATFORM "OS") -endif (NOT DEFINED IOS_PLATFORM) -set (IOS_PLATFORM ${IOS_PLATFORM} CACHE STRING "Type of iOS Platform") - -# Setup building for arm64 or not -if (NOT DEFINED BUILD_ARM64) -set (BUILD_ARM64 true) -endif (NOT DEFINED BUILD_ARM64) -set (BUILD_ARM64 ${BUILD_ARM64} CACHE STRING "Build arm64 arch or not") - -# Check the platform selection and setup for developer root -if (${IOS_PLATFORM} STREQUAL "OS") -set (IOS_PLATFORM_LOCATION "iPhoneOS.platform") - -# This causes the installers to properly locate the output libraries -set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphoneos") -elseif (${IOS_PLATFORM} STREQUAL "SIMULATOR") -set (SIMULATOR true) -set (IOS_PLATFORM_LOCATION "iPhoneSimulator.platform") - -# This causes the installers to properly locate the output libraries -set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator") -elseif (${IOS_PLATFORM} STREQUAL "SIMULATOR64") -set (SIMULATOR true) -set (IOS_PLATFORM_LOCATION "iPhoneSimulator.platform") - -# This causes the installers to properly locate the output libraries -set (CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator") -else (${IOS_PLATFORM} STREQUAL "OS") -message (FATAL_ERROR "Unsupported IOS_PLATFORM value selected. Please choose OS or SIMULATOR") -endif (${IOS_PLATFORM} STREQUAL "OS") - -# Setup iOS developer location unless specified manually with CMAKE_IOS_DEVELOPER_ROOT -# Note Xcode 4.3 changed the installation location, choose the most recent one available -exec_program(/usr/bin/xcode-select ARGS -print-path OUTPUT_VARIABLE CMAKE_XCODE_DEVELOPER_DIR) -set (XCODE_POST_43_ROOT "${CMAKE_XCODE_DEVELOPER_DIR}/Platforms/${IOS_PLATFORM_LOCATION}/Developer") -set (XCODE_PRE_43_ROOT "/Developer/Platforms/${IOS_PLATFORM_LOCATION}/Developer") -if (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT) -if (EXISTS ${XCODE_POST_43_ROOT}) -set (CMAKE_IOS_DEVELOPER_ROOT ${XCODE_POST_43_ROOT}) -elseif(EXISTS ${XCODE_PRE_43_ROOT}) -set (CMAKE_IOS_DEVELOPER_ROOT ${XCODE_PRE_43_ROOT}) -endif (EXISTS ${XCODE_POST_43_ROOT}) -endif (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT) -set (CMAKE_IOS_DEVELOPER_ROOT ${CMAKE_IOS_DEVELOPER_ROOT} CACHE PATH "Location of iOS Platform") - -# Find and use the most recent iOS sdk unless specified manually with CMAKE_IOS_SDK_ROOT -if (NOT DEFINED CMAKE_IOS_SDK_ROOT) -file (GLOB _CMAKE_IOS_SDKS "${CMAKE_IOS_DEVELOPER_ROOT}/SDKs/*") -if (_CMAKE_IOS_SDKS) -list (SORT _CMAKE_IOS_SDKS) -list (REVERSE _CMAKE_IOS_SDKS) -list (GET _CMAKE_IOS_SDKS 0 CMAKE_IOS_SDK_ROOT) -else (_CMAKE_IOS_SDKS) -message (FATAL_ERROR "No iOS SDK's found in default search path ${CMAKE_IOS_DEVELOPER_ROOT}. Manually set CMAKE_IOS_SDK_ROOT or install the iOS SDK.") -endif (_CMAKE_IOS_SDKS) -message (STATUS "Toolchain using default iOS SDK: ${CMAKE_IOS_SDK_ROOT}") -endif (NOT DEFINED CMAKE_IOS_SDK_ROOT) -set (CMAKE_IOS_SDK_ROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Location of the selected iOS SDK") - -# Set the sysroot default to the most recent SDK -set (CMAKE_OSX_SYSROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Sysroot used for iOS support") - -# set the architecture for iOS -if (${IOS_PLATFORM} STREQUAL "OS") -set (IOS_ARCH armv7 armv7s arm64) -elseif (${IOS_PLATFORM} STREQUAL "SIMULATOR") -set (IOS_ARCH i386) -elseif (${IOS_PLATFORM} STREQUAL "SIMULATOR64") -set (IOS_ARCH x86_64) -endif (${IOS_PLATFORM} STREQUAL "OS") - -set (CMAKE_OSX_ARCHITECTURES ${IOS_ARCH} CACHE string "Build architecture for iOS") - -# Set the find root to the iOS developer roots and to user defined paths -set (CMAKE_FIND_ROOT_PATH ${CMAKE_IOS_DEVELOPER_ROOT} ${CMAKE_IOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} CACHE string "iOS find search path root") - -# default to searching for frameworks first -set (CMAKE_FIND_FRAMEWORK FIRST) - -# set up the default search directories for frameworks -set (CMAKE_SYSTEM_FRAMEWORK_PATH -${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks -${CMAKE_IOS_SDK_ROOT}/System/Library/PrivateFrameworks -${CMAKE_IOS_SDK_ROOT}/Developer/Library/Frameworks -) - -# only search the iOS sdks, not the remainder of the host filesystem -set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) -set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) - - -# This little macro lets you set any XCode specific property -macro (set_xcode_property TARGET XCODE_PROPERTY XCODE_VALUE) -set_property (TARGET ${TARGET} PROPERTY XCODE_ATTRIBUTE_${XCODE_PROPERTY} ${XCODE_VALUE}) -endmacro (set_xcode_property) - - -# This macro lets you find executable programs on the host system -macro (find_host_package) -set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER) -set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER) -set (IOS FALSE) - -find_package(${ARGN}) - -set (IOS TRUE) -set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) -set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -endmacro (find_host_package) diff --git a/cmake/ios.toolchain.cmake b/cmake/ios.toolchain.cmake new file mode 100644 index 00000000..146fb661 --- /dev/null +++ b/cmake/ios.toolchain.cmake @@ -0,0 +1,674 @@ +# This file is part of the ios-cmake project. It was retrieved from +# https://github.com/cristeab/ios-cmake.git, which is a fork of +# https://code.google.com/p/ios-cmake/. Which in turn is based off of +# the Platform/Darwin.cmake and Platform/UnixPaths.cmake files which +# are included with CMake 2.8.4 +# +# The ios-cmake project is licensed under the new BSD license. +# +# Copyright (c) 2014, Bogdan Cristea and LTE Engineering Software, +# Kitware, Inc., Insight Software Consortium. All rights reserved. +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# This file is based off of the Platform/Darwin.cmake and +# Platform/UnixPaths.cmake files which are included with CMake 2.8.4 +# It has been altered for iOS development. +# +# Updated by Alex Stewart (alexs.mac@gmail.com) +# +# ***************************************************************************** +# Now maintained by Alexander Widerberg (widerbergaren [at] gmail.com) +# under the BSD-3-Clause license +# https://github.com/leetal/ios-cmake +# ***************************************************************************** +# +# INFORMATION / HELP +# +# The following arguments control the behaviour of this toolchain: +# +# PLATFORM: (default "OS") +# OS = Build for iPhoneOS. +# OS64 = Build for arm64 iphoneOS. +# OS64COMBINED = Build for arm64 x86_64 iphoneOS. Combined into FAT STATIC lib (supported on 3.14+ of CMakewith "-G Xcode" argument ONLY) +# SIMULATOR = Build for x86 i386 iphoneOS Simulator. +# SIMULATOR64 = Build for x86_64 iphoneOS Simulator. +# TVOS = Build for arm64 tvOS. +# TVOSCOMBINED = Build for arm64 x86_64 tvOS. Combined into FAT STATIC lib (supported on 3.14+ of CMake with "-G Xcode" argument ONLY) +# SIMULATOR_TVOS = Build for x86_64 tvOS Simulator. +# WATCHOS = Build for armv7k arm64_32 for watchOS. +# WATCHOSCOMBINED = Build for armv7k arm64_32 x86_64 watchOS. Combined into FAT STATIC lib (supported on 3.14+ of CMake with "-G Xcode" argument ONLY) +# SIMULATOR_WATCHOS = Build for x86_64 for watchOS Simulator. +# +# CMAKE_OSX_SYSROOT: Path to the SDK to use. By default this is +# automatically determined from PLATFORM and xcodebuild, but +# can also be manually specified (although this should not be required). +# +# CMAKE_DEVELOPER_ROOT: Path to the Developer directory for the platform +# being compiled for. By default this is automatically determined from +# CMAKE_OSX_SYSROOT, but can also be manually specified (although this should +# not be required). +# +# DEPLOYMENT_TARGET: Minimum SDK version to target. Default 2.0 on watchOS and 9.0 on tvOS+iOS +# +# ENABLE_BITCODE: (1|0) Enables or disables bitcode support. Default 1 (true) +# +# ENABLE_ARC: (1|0) Enables or disables ARC support. Default 1 (true, ARC enabled by default) +# +# ENABLE_VISIBILITY: (1|0) Enables or disables symbol visibility support. Default 0 (false, visibility hidden by default) +# +# ENABLE_STRICT_TRY_COMPILE: (1|0) Enables or disables strict try_compile() on all Check* directives (will run linker +# to actually check if linking is possible). Default 0 (false, will set CMAKE_TRY_COMPILE_TARGET_TYPE to STATIC_LIBRARY) +# +# ARCHS: (armv7 armv7s armv7k arm64 arm64_32 i386 x86_64) If specified, will override the default architectures for the given PLATFORM +# OS = armv7 armv7s arm64 (if applicable) +# OS64 = arm64 (if applicable) +# SIMULATOR = i386 +# SIMULATOR64 = x86_64 +# TVOS = arm64 +# SIMULATOR_TVOS = x86_64 (i386 has since long been deprecated) +# WATCHOS = armv7k arm64_32 (if applicable) +# SIMULATOR_WATCHOS = x86_64 (i386 has since long been deprecated) +# +# This toolchain defines the following variables for use externally: +# +# XCODE_VERSION: Version number (not including Build version) of Xcode detected. +# SDK_VERSION: Version of SDK being used. +# CMAKE_OSX_ARCHITECTURES: Architectures being compiled for (generated from PLATFORM). +# +# This toolchain defines the following macros for use externally: +# +# set_xcode_property (TARGET XCODE_PROPERTY XCODE_VALUE XCODE_VARIANT) +# A convenience macro for setting xcode specific properties on targets. +# Available variants are: All, Release, RelWithDebInfo, Debug, MinSizeRel +# example: set_xcode_property (myioslib IPHONEOS_DEPLOYMENT_TARGET "3.1" "all"). +# +# find_host_package (PROGRAM ARGS) +# A macro used to find executable programs on the host system, not within the +# environment. Thanks to the android-cmake project for providing the +# command. +# +# ******************************** DEPRECATIONS ******************************* +# +# IOS_DEPLOYMENT_TARGET: (Deprecated) Alias to DEPLOYMENT_TARGET +# CMAKE_IOS_DEVELOPER_ROOT: (Deprecated) Alias to CMAKE_DEVELOPER_ROOT +# IOS_PLATFORM: (Deprecated) Alias to PLATFORM +# IOS_ARCH: (Deprecated) Alias to ARCHS +# +# ***************************************************************************** +# + +# Fix for PThread library not in path +set(CMAKE_THREAD_LIBS_INIT "-lpthread") +set(CMAKE_HAVE_THREADS_LIBRARY 1) +set(CMAKE_USE_WIN32_THREADS_INIT 0) +set(CMAKE_USE_PTHREADS_INIT 1) + +# Cache what generator is used +set(USED_CMAKE_GENERATOR "${CMAKE_GENERATOR}" CACHE STRING "Expose CMAKE_GENERATOR" FORCE) + +if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.14") + set(MODERN_CMAKE YES) + message(STATUS "Merging integrated CMake 3.14+ iOS,tvOS,watchOS,macOS toolchain(s) with this toolchain!") +endif() + +# Get the Xcode version being used. +execute_process(COMMAND xcodebuild -version + OUTPUT_VARIABLE XCODE_VERSION + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) +string(REGEX MATCH "Xcode [0-9\\.]+" XCODE_VERSION "${XCODE_VERSION}") +string(REGEX REPLACE "Xcode ([0-9\\.]+)" "\\1" XCODE_VERSION "${XCODE_VERSION}") +message(STATUS "Building with Xcode version: ${XCODE_VERSION}") + +######## ALIASES (DEPRECATION WARNINGS) + +if(DEFINED IOS_PLATFORM) + set(PLATFORM ${IOS_PLATFORM}) + message(DEPRECATION "IOS_PLATFORM argument is DEPRECATED. Consider using the new PLATFORM argument instead.") +endif() + +if(DEFINED IOS_DEPLOYMENT_TARGET) + set(DEPLOYMENT_TARGET ${IOS_DEPLOYMENT_TARGET}) + message(DEPRECATION "IOS_DEPLOYMENT_TARGET argument is DEPRECATED. Consider using the new DEPLOYMENT_TARGET argument instead.") +endif() + +if(DEFINED CMAKE_IOS_DEVELOPER_ROOT) + set(CMAKE_DEVELOPER_ROOT ${CMAKE_IOS_DEVELOPER_ROOT}) + message(DEPRECATION "CMAKE_IOS_DEVELOPER_ROOT argument is DEPRECATED. Consider using the new CMAKE_DEVELOPER_ROOT argument instead.") +endif() + +if(DEFINED IOS_ARCH) + set(ARCHS ${IOS_ARCH}) + message(DEPRECATION "IOS_ARCH argument is DEPRECATED. Consider using the new ARCHS argument instead.") +endif() + +######## END ALIASES + +# Unset the FORCE on cache variables if in try_compile() +set(FORCE_CACHE FORCE) +get_property(_CMAKE_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE) +if(_CMAKE_IN_TRY_COMPILE) + unset(FORCE_CACHE) +endif() + +# Default to building for iPhoneOS if not specified otherwise, and we cannot +# determine the platform from the CMAKE_OSX_ARCHITECTURES variable. The use +# of CMAKE_OSX_ARCHITECTURES is such that try_compile() projects can correctly +# determine the value of PLATFORM from the root project, as +# CMAKE_OSX_ARCHITECTURES is propagated to them by CMake. +if(NOT DEFINED PLATFORM) + if (CMAKE_OSX_ARCHITECTURES) + if(CMAKE_OSX_ARCHITECTURES MATCHES ".*arm.*" AND CMAKE_OSX_SYSROOT MATCHES ".*iphoneos.*") + set(PLATFORM "OS") + elseif(CMAKE_OSX_ARCHITECTURES MATCHES "i386" AND CMAKE_OSX_SYSROOT MATCHES ".*iphonesimulator.*") + set(PLATFORM "SIMULATOR") + elseif(CMAKE_OSX_ARCHITECTURES MATCHES "x86_64" AND CMAKE_OSX_SYSROOT MATCHES ".*iphonesimulator.*") + set(PLATFORM "SIMULATOR64") + elseif(CMAKE_OSX_ARCHITECTURES MATCHES "arm64" AND CMAKE_OSX_SYSROOT MATCHES ".*appletvos.*") + set(PLATFORM "TVOS") + elseif(CMAKE_OSX_ARCHITECTURES MATCHES "x86_64" AND CMAKE_OSX_SYSROOT MATCHES ".*appletvsimulator.*") + set(PLATFORM "SIMULATOR_TVOS") + elseif(CMAKE_OSX_ARCHITECTURES MATCHES ".*armv7k.*" AND CMAKE_OSX_SYSROOT MATCHES ".*watchos.*") + set(PLATFORM "WATCHOS") + elseif(CMAKE_OSX_ARCHITECTURES MATCHES "i386" AND CMAKE_OSX_SYSROOT MATCHES ".*watchsimulator.*") + set(PLATFORM "SIMULATOR_WATCHOS") + endif() + endif() + if (NOT PLATFORM) + set(PLATFORM "OS") + endif() +endif() + +set(PLATFORM_INT "${PLATFORM}" CACHE STRING "Type of platform for which the build targets.") + +# Handle the case where we are targeting iOS and a version above 10.3.4 (32-bit support dropped officially) +if(PLATFORM_INT STREQUAL "OS" AND DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 10.3.4) + set(PLATFORM_INT "OS64") + message(STATUS "Targeting minimum SDK version ${DEPLOYMENT_TARGET}. Dropping 32-bit support.") +elseif(PLATFORM_INT STREQUAL "SIMULATOR" AND DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 10.3.4) + set(PLATFORM_INT "SIMULATOR64") + message(STATUS "Targeting minimum SDK version ${DEPLOYMENT_TARGET}. Dropping 32-bit support.") +endif() + +# Determine the platform name and architectures for use in xcodebuild commands +# from the specified PLATFORM name. +if(PLATFORM_INT STREQUAL "OS") + set(SDK_NAME iphoneos) + if(NOT ARCHS) + set(ARCHS armv7 armv7s arm64) + endif() +elseif(PLATFORM_INT STREQUAL "OS64") + set(SDK_NAME iphoneos) + if(NOT ARCHS) + if (XCODE_VERSION VERSION_GREATER 10.0) + set(ARCHS arm64) # Add arm64e when Apple have fixed the integration issues with it, libarclite_iphoneos.a is currently missung bitcode markers for example + else() + set(ARCHS arm64) + endif() + endif() +elseif(PLATFORM_INT STREQUAL "OS64COMBINED") + set(SDK_NAME iphoneos) + if(MODERN_CMAKE) + if(NOT ARCHS) + if (XCODE_VERSION VERSION_GREATER 10.0) + set(ARCHS arm64 x86_64) # Add arm64e when Apple have fixed the integration issues with it, libarclite_iphoneos.a is currently missung bitcode markers for example + else() + set(ARCHS arm64 x86_64) + endif() + endif() + else() + message(FATAL_ERROR "Please make sure that you are running CMake 3.14+ to make the OS64COMBINED setting work") + endif() +elseif(PLATFORM_INT STREQUAL "SIMULATOR") + set(SDK_NAME iphonesimulator) + if(NOT ARCHS) + set(ARCHS i386) + endif() + message(DEPRECATION "SIMULATOR IS DEPRECATED. Consider using SIMULATOR64 instead.") +elseif(PLATFORM_INT STREQUAL "SIMULATOR64") + set(SDK_NAME iphonesimulator) + if(NOT ARCHS) + set(ARCHS x86_64) + endif() +elseif(PLATFORM_INT STREQUAL "TVOS") + set(SDK_NAME appletvos) + if(NOT ARCHS) + set(ARCHS arm64) + endif() +elseif (PLATFORM_INT STREQUAL "TVOSCOMBINED") + set(SDK_NAME appletvos) + if(MODERN_CMAKE) + if(NOT ARCHS) + set(ARCHS arm64 x86_64) + endif() + else() + message(FATAL_ERROR "Please make sure that you are running CMake 3.14+ to make the TVOSCOMBINED setting work") + endif() +elseif(PLATFORM_INT STREQUAL "SIMULATOR_TVOS") + set(SDK_NAME appletvsimulator) + if(NOT ARCHS) + set(ARCHS x86_64) + endif() +elseif(PLATFORM_INT STREQUAL "WATCHOS") + set(SDK_NAME watchos) + if(NOT ARCHS) + if (XCODE_VERSION VERSION_GREATER 10.0) + set(ARCHS armv7k arm64_32) + else() + set(ARCHS armv7k) + endif() + endif() +elseif(PLATFORM_INT STREQUAL "WATCHOSCOMBINED") + set(SDK_NAME watchos) + if(MODERN_CMAKE) + if(NOT ARCHS) + if (XCODE_VERSION VERSION_GREATER 10.0) + set(ARCHS armv7k arm64_32 i386) + else() + set(ARCHS armv7k i386) + endif() + endif() + else() + message(FATAL_ERROR "Please make sure that you are running CMake 3.14+ to make the WATCHOSCOMBINED setting work") + endif() +elseif(PLATFORM_INT STREQUAL "SIMULATOR_WATCHOS") + set(SDK_NAME watchsimulator) + if(NOT ARCHS) + set(ARCHS i386) + endif() +else() + message(FATAL_ERROR "Invalid PLATFORM: ${PLATFORM_INT}") +endif() +message(STATUS "Configuring ${SDK_NAME} build for platform: ${PLATFORM_INT}, architecture(s): ${ARCHS}") + +if(MODERN_CMAKE AND PLATFORM_INT MATCHES ".*COMBINED" AND NOT USED_CMAKE_GENERATOR MATCHES "Xcode") + message(FATAL_ERROR "The COMBINED options only work with Xcode generator, -G Xcode") +endif() + +# If user did not specify the SDK root to use, then query xcodebuild for it. +execute_process(COMMAND xcodebuild -version -sdk ${SDK_NAME} Path + OUTPUT_VARIABLE CMAKE_OSX_SYSROOT_INT + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) +if (NOT DEFINED CMAKE_OSX_SYSROOT_INT AND NOT DEFINED CMAKE_OSX_SYSROOT) + message(SEND_ERROR "Please make sure that Xcode is installed and that the toolchain" + "is pointing to the correct path. Please run:" + "sudo xcode-select -s /Applications/Xcode.app/Contents/Developer" + "and see if that fixes the problem for you.") + message(FATAL_ERROR "Invalid CMAKE_OSX_SYSROOT: ${CMAKE_OSX_SYSROOT} " + "does not exist.") +elseif(DEFINED CMAKE_OSX_SYSROOT) + message(STATUS "Using SDK: ${CMAKE_OSX_SYSROOT} for platform: ${PLATFORM_INT} when checking compatibility") +elseif(DEFINED CMAKE_OSX_SYSROOT_INT) + message(STATUS "Using SDK: ${CMAKE_OSX_SYSROOT_INT} for platform: ${PLATFORM_INT}") + set(CMAKE_OSX_SYSROOT "${CMAKE_OSX_SYSROOT_INT}" CACHE INTERNAL "") +endif() + +# Set Xcode property for SDKROOT as well if Xcode generator is used +if(USED_CMAKE_GENERATOR MATCHES "Xcode") + set(CMAKE_OSX_SYSROOT "${SDK_NAME}" CACHE INTERNAL "") + if(NOT DEFINED CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM) + set(CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM 123456789A CACHE INTERNAL "") + endif() +endif() + +# Specify minimum version of deployment target. +if(NOT DEFINED DEPLOYMENT_TARGET) + if (PLATFORM_INT STREQUAL "WATCHOS" OR PLATFORM_INT STREQUAL "SIMULATOR_WATCHOS") + # Unless specified, SDK version 2.0 is used by default as minimum target version (watchOS). + set(DEPLOYMENT_TARGET "2.0" + CACHE STRING "Minimum SDK version to build for." ) + else() + # Unless specified, SDK version 9.0 is used by default as minimum target version (iOS, tvOS). + set(DEPLOYMENT_TARGET "9.0" + CACHE STRING "Minimum SDK version to build for." ) + endif() + message(STATUS "Using the default min-version since DEPLOYMENT_TARGET not provided!") +endif() +# Use bitcode or not +if(NOT DEFINED ENABLE_BITCODE AND NOT ARCHS MATCHES "((^|;|, )(i386|x86_64))+") + # Unless specified, enable bitcode support by default + message(STATUS "Enabling bitcode support by default. ENABLE_BITCODE not provided!") + set(ENABLE_BITCODE TRUE) +elseif(NOT DEFINED ENABLE_BITCODE) + message(STATUS "Disabling bitcode support by default on simulators. ENABLE_BITCODE not provided for override!") + set(ENABLE_BITCODE FALSE) +endif() +set(ENABLE_BITCODE_INT ${ENABLE_BITCODE} CACHE BOOL "Whether or not to enable bitcode" ${FORCE_CACHE}) +# Use ARC or not +if(NOT DEFINED ENABLE_ARC) + # Unless specified, enable ARC support by default + set(ENABLE_ARC TRUE) + message(STATUS "Enabling ARC support by default. ENABLE_ARC not provided!") +endif() +set(ENABLE_ARC_INT ${ENABLE_ARC} CACHE BOOL "Whether or not to enable ARC" ${FORCE_CACHE}) +# Use hidden visibility or not +if(NOT DEFINED ENABLE_VISIBILITY) + # Unless specified, disable symbols visibility by default + set(ENABLE_VISIBILITY FALSE) + message(STATUS "Hiding symbols visibility by default. ENABLE_VISIBILITY not provided!") +endif() +set(ENABLE_VISIBILITY_INT ${ENABLE_VISIBILITY} CACHE BOOL "Whether or not to hide symbols (-fvisibility=hidden)" ${FORCE_CACHE}) +# Set strict compiler checks or not +if(NOT DEFINED ENABLE_STRICT_TRY_COMPILE) + # Unless specified, disable strict try_compile() + set(ENABLE_STRICT_TRY_COMPILE FALSE) + message(STATUS "Using NON-strict compiler checks by default. ENABLE_STRICT_TRY_COMPILE not provided!") +endif() +set(ENABLE_STRICT_TRY_COMPILE_INT ${ENABLE_STRICT_TRY_COMPILE} CACHE BOOL "Whether or not to use strict compiler checks" ${FORCE_CACHE}) +# Get the SDK version information. +execute_process(COMMAND xcodebuild -sdk ${CMAKE_OSX_SYSROOT} -version SDKVersion + OUTPUT_VARIABLE SDK_VERSION + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + +# Find the Developer root for the specific iOS platform being compiled for +# from CMAKE_OSX_SYSROOT. Should be ../../ from SDK specified in +# CMAKE_OSX_SYSROOT. There does not appear to be a direct way to obtain +# this information from xcrun or xcodebuild. +if (NOT DEFINED CMAKE_DEVELOPER_ROOT AND NOT USED_CMAKE_GENERATOR MATCHES "Xcode") + get_filename_component(PLATFORM_SDK_DIR ${CMAKE_OSX_SYSROOT} PATH) + get_filename_component(CMAKE_DEVELOPER_ROOT ${PLATFORM_SDK_DIR} PATH) + + if (NOT DEFINED CMAKE_DEVELOPER_ROOT) + message(FATAL_ERROR "Invalid CMAKE_DEVELOPER_ROOT: " + "${CMAKE_DEVELOPER_ROOT} does not exist.") + endif() +endif() +# Find the C & C++ compilers for the specified SDK. +if(NOT CMAKE_C_COMPILER) + execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT} -find clang + OUTPUT_VARIABLE CMAKE_C_COMPILER + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "Using C compiler: ${CMAKE_C_COMPILER}") +endif() +if(NOT CMAKE_CXX_COMPILER) + execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT} -find clang++ + OUTPUT_VARIABLE CMAKE_CXX_COMPILER + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "Using CXX compiler: ${CMAKE_CXX_COMPILER}") +endif() +# Find (Apple's) libtool. +execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT} -find libtool + OUTPUT_VARIABLE BUILD_LIBTOOL + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) +message(STATUS "Using libtool: ${BUILD_LIBTOOL}") +# Configure libtool to be used instead of ar + ranlib to build static libraries. +# This is required on Xcode 7+, but should also work on previous versions of +# Xcode. +set(CMAKE_C_CREATE_STATIC_LIBRARY + "${BUILD_LIBTOOL} -static -o ") +set(CMAKE_CXX_CREATE_STATIC_LIBRARY + "${BUILD_LIBTOOL} -static -o ") +# Find the toolchain's provided install_name_tool if none is found on the host +if(NOT CMAKE_INSTALL_NAME_TOOL) + execute_process(COMMAND xcrun -sdk ${CMAKE_OSX_SYSROOT} -find install_name_tool + OUTPUT_VARIABLE CMAKE_INSTALL_NAME_TOOL_INT + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + set(CMAKE_INSTALL_NAME_TOOL ${CMAKE_INSTALL_NAME_TOOL_INT} CACHE STRING "" ${FORCE_CACHE}) + message(STATUS "Using install_name_tool: ${CMAKE_INSTALL_NAME_TOOL}") +endif() +# Get the version of Darwin (OS X) of the host. +execute_process(COMMAND uname -r + OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) +# CMake 3.14+ support building for iOS, watchOS and tvOS out of the box. +if(MODERN_CMAKE) + if(SDK_NAME MATCHES "iphone") + set(CMAKE_SYSTEM_NAME iOS CACHE INTERNAL "" ${FORCE_CACHE}) + elseif(SDK_NAME MATCHES "appletv") + set(CMAKE_SYSTEM_NAME tvOS CACHE INTERNAL "" ${FORCE_CACHE}) + elseif(SDK_NAME MATCHES "watch") + set(CMAKE_SYSTEM_NAME watchOS CACHE INTERNAL "" ${FORCE_CACHE}) + endif() + + # Provide flags for a combined FAT library build on newer CMake versions + if(PLATFORM_INT MATCHES ".*COMBINED") + set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH NO CACHE INTERNAL "" ${FORCE_CACHE}) + set(CMAKE_IOS_INSTALL_COMBINED YES CACHE INTERNAL "" ${FORCE_CACHE}) + message(STATUS "Will combine built (static) artifacts into FAT lib...") + endif() +else() + # Legacy code path prior to CMake 3.14 + set(CMAKE_SYSTEM_NAME Darwin CACHE INTERNAL "" ${FORCE_CACHE}) +endif() +# Standard settings. +set(CMAKE_SYSTEM_VERSION ${SDK_VERSION} CACHE INTERNAL "") +set(UNIX TRUE CACHE BOOL "") +set(APPLE TRUE CACHE BOOL "") +set(IOS TRUE CACHE BOOL "") +set(CMAKE_AR ar CACHE FILEPATH "" FORCE) +set(CMAKE_RANLIB ranlib CACHE FILEPATH "" FORCE) +set(CMAKE_STRIP strip CACHE FILEPATH "" FORCE) +# Set the architectures for which to build. +set(CMAKE_OSX_ARCHITECTURES ${ARCHS} CACHE STRING "Build architecture for iOS") +# Change the type of target generated for try_compile() so it'll work when cross-compiling, weak compiler checks +if(ENABLE_STRICT_TRY_COMPILE_INT) + message(STATUS "Using strict compiler checks (default in CMake).") +else() + set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +endif() +# All iOS/Darwin specific settings - some may be redundant. +set(CMAKE_MACOSX_BUNDLE YES) +set(CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED "NO") +set(CMAKE_SHARED_LIBRARY_PREFIX "lib") +set(CMAKE_SHARED_LIBRARY_SUFFIX ".dylib") +set(CMAKE_SHARED_MODULE_PREFIX "lib") +set(CMAKE_SHARED_MODULE_SUFFIX ".so") +set(CMAKE_C_COMPILER_ABI ELF) +set(CMAKE_CXX_COMPILER_ABI ELF) +set(CMAKE_C_HAS_ISYSROOT 1) +set(CMAKE_CXX_HAS_ISYSROOT 1) +set(CMAKE_MODULE_EXISTS 1) +set(CMAKE_DL_LIBS "") +set(CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ") +set(CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ") +set(CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}") +set(CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") + +if(ARCHS MATCHES "((^|;|, )(arm64|arm64e|x86_64))+") + set(CMAKE_C_SIZEOF_DATA_PTR 8) + set(CMAKE_CXX_SIZEOF_DATA_PTR 8) + if(ARCHS MATCHES "((^|;|, )(arm64|arm64e))+") + set(CMAKE_SYSTEM_PROCESSOR "aarch64") + else() + set(CMAKE_SYSTEM_PROCESSOR "x86_64") + endif() + message(STATUS "Using a data_ptr size of 8") +else() + set(CMAKE_C_SIZEOF_DATA_PTR 4) + set(CMAKE_CXX_SIZEOF_DATA_PTR 4) + set(CMAKE_SYSTEM_PROCESSOR "arm") + message(STATUS "Using a data_ptr size of 4") +endif() + +message(STATUS "Building for minimum ${SDK_NAME} version: ${DEPLOYMENT_TARGET}" + " (SDK version: ${SDK_VERSION})") +# Note that only Xcode 7+ supports the newer more specific: +# -m${SDK_NAME}-version-min flags, older versions of Xcode use: +# -m(ios/ios-simulator)-version-min instead. +if(PLATFORM_INT STREQUAL "OS" OR PLATFORM_INT STREQUAL "OS64") + if(XCODE_VERSION VERSION_LESS 7.0) + set(SDK_NAME_VERSION_FLAGS + "-mios-version-min=${DEPLOYMENT_TARGET}") + else() + # Xcode 7.0+ uses flags we can build directly from SDK_NAME. + set(SDK_NAME_VERSION_FLAGS + "-m${SDK_NAME}-version-min=${DEPLOYMENT_TARGET}") + endif() +elseif(PLATFORM_INT STREQUAL "TVOS") + set(SDK_NAME_VERSION_FLAGS + "-mtvos-version-min=${DEPLOYMENT_TARGET}") +elseif(PLATFORM_INT STREQUAL "SIMULATOR_TVOS") + set(SDK_NAME_VERSION_FLAGS + "-mtvos-simulator-version-min=${DEPLOYMENT_TARGET}") +elseif(PLATFORM_INT STREQUAL "WATCHOS") + set(SDK_NAME_VERSION_FLAGS + "-mwatchos-version-min=${DEPLOYMENT_TARGET}") +elseif(PLATFORM_INT STREQUAL "SIMULATOR_WATCHOS") + set(SDK_NAME_VERSION_FLAGS + "-mwatchos-simulator-version-min=${DEPLOYMENT_TARGET}") +else() + # SIMULATOR or SIMULATOR64 both use -mios-simulator-version-min. + set(SDK_NAME_VERSION_FLAGS + "-mios-simulator-version-min=${DEPLOYMENT_TARGET}") +endif() +message(STATUS "Version flags set to: ${SDK_NAME_VERSION_FLAGS}") +set(CMAKE_OSX_DEPLOYMENT_TARGET ${DEPLOYMENT_TARGET} CACHE STRING + "Set CMake deployment target" ${FORCE_CACHE}) + +if(ENABLE_BITCODE_INT) + set(BITCODE "-fembed-bitcode") + set(CMAKE_XCODE_ATTRIBUTE_BITCODE_GENERATION_MODE bitcode CACHE INTERNAL "") + message(STATUS "Enabling bitcode support.") +else() + set(BITCODE "") + set(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE NO CACHE INTERNAL "") + message(STATUS "Disabling bitcode support.") +endif() + +if(ENABLE_ARC_INT) + set(FOBJC_ARC "-fobjc-arc") + set(CMAKE_XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES CACHE INTERNAL "") + message(STATUS "Enabling ARC support.") +else() + set(FOBJC_ARC "-fno-objc-arc") + set(CMAKE_XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC NO CACHE INTERNAL "") + message(STATUS "Disabling ARC support.") +endif() + +if(NOT ENABLE_VISIBILITY_INT) + set(VISIBILITY "-fvisibility=hidden") + set(CMAKE_XCODE_ATTRIBUTE_GCC_SYMBOLS_PRIVATE_EXTERN YES CACHE INTERNAL "") + message(STATUS "Hiding symbols (-fvisibility=hidden).") +else() + set(VISIBILITY "") + set(CMAKE_XCODE_ATTRIBUTE_GCC_SYMBOLS_PRIVATE_EXTERN NO CACHE INTERNAL "") +endif() + +#Check if Xcode generator is used, since that will handle these flags automagically +if(USED_CMAKE_GENERATOR MATCHES "Xcode") + message(STATUS "Not setting any manual command-line buildflags, since Xcode is selected as generator.") +else() + set(CMAKE_C_FLAGS + "${SDK_NAME_VERSION_FLAGS} ${BITCODE} -fobjc-abi-version=2 ${FOBJC_ARC} ${CMAKE_C_FLAGS}") + # Hidden visibilty is required for C++ on iOS. + set(CMAKE_CXX_FLAGS + "${SDK_NAME_VERSION_FLAGS} ${BITCODE} ${VISIBILITY} -fvisibility-inlines-hidden -fobjc-abi-version=2 ${FOBJC_ARC} ${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -O0 -g ${CMAKE_CXX_FLAGS_DEBUG}") + set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS} -DNDEBUG -Os -ffast-math ${CMAKE_CXX_FLAGS_MINSIZEREL}") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS} -DNDEBUG -O2 -g -ffast-math ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -DNDEBUG -O3 -ffast-math ${CMAKE_CXX_FLAGS_RELEASE}") + set(CMAKE_C_LINK_FLAGS "${SDK_NAME_VERSION_FLAGS} -Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}") + set(CMAKE_CXX_LINK_FLAGS "${SDK_NAME_VERSION_FLAGS} -Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}") + + # In order to ensure that the updated compiler flags are used in try_compile() + # tests, we have to forcibly set them in the CMake cache, not merely set them + # in the local scope. + list(APPEND VARS_TO_FORCE_IN_CACHE + CMAKE_C_FLAGS + CMAKE_CXX_FLAGS + CMAKE_CXX_FLAGS_DEBUG + CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS_MINSIZEREL + CMAKE_CXX_FLAGS_RELEASE + CMAKE_C_LINK_FLAGS + CMAKE_CXX_LINK_FLAGS) + foreach(VAR_TO_FORCE ${VARS_TO_FORCE_IN_CACHE}) + set(${VAR_TO_FORCE} "${${VAR_TO_FORCE}}" CACHE STRING "") + endforeach() +endif() + +set(CMAKE_PLATFORM_HAS_INSTALLNAME 1) +set(CMAKE_SHARED_LINKER_FLAGS "-rpath @executable_path/Frameworks -rpath @loader_path/Frameworks") +set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib -Wl,-headerpad_max_install_names") +set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -Wl,-headerpad_max_install_names") +set(CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") +set(CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,") +set(CMAKE_FIND_LIBRARY_SUFFIXES ".tbd" ".dylib" ".so" ".a") +set(CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-install_name") + +# Set the find root to the iOS developer roots and to user defined paths. +set(CMAKE_FIND_ROOT_PATH ${CMAKE_OSX_SYSROOT_INT} ${CMAKE_PREFIX_PATH} CACHE STRING "Root path that will be prepended + to all search paths") +# Default to searching for frameworks first. +set(CMAKE_FIND_FRAMEWORK FIRST) +# Set up the default search directories for frameworks. +set(CMAKE_FRAMEWORK_PATH + ${CMAKE_DEVELOPER_ROOT}/Library/PrivateFrameworks + ${CMAKE_OSX_SYSROOT_INT}/System/Library/Frameworks + ${CMAKE_FRAMEWORK_PATH} CACHE STRING "Frameworks search paths" ${FORCE_CACHE}) + +# By default, search both the specified iOS SDK and the remainder of the host filesystem. +if(NOT CMAKE_FIND_ROOT_PATH_MODE_PROGRAM) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH CACHE STRING "" ${FORCE_CACHE}) +endif() +if(NOT CMAKE_FIND_ROOT_PATH_MODE_LIBRARY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY CACHE STRING "" ${FORCE_CACHE}) +endif() +if(NOT CMAKE_FIND_ROOT_PATH_MODE_INCLUDE) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY CACHE STRING "" ${FORCE_CACHE}) +endif() +if(NOT CMAKE_FIND_ROOT_PATH_MODE_PACKAGE) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY CACHE STRING "" ${FORCE_CACHE}) +endif() + +# +# Some helper-macros below to simplify and beautify the CMakeFile +# + +# This little macro lets you set any Xcode specific property. +macro(set_xcode_property TARGET XCODE_PROPERTY XCODE_VALUE XCODE_RELVERSION) + set(XCODE_RELVERSION_I "${XCODE_RELVERSION}") + if(XCODE_RELVERSION_I STREQUAL "All") + set_property(TARGET ${TARGET} PROPERTY + XCODE_ATTRIBUTE_${XCODE_PROPERTY} "${XCODE_VALUE}") + else() + set_property(TARGET ${TARGET} PROPERTY + XCODE_ATTRIBUTE_${XCODE_PROPERTY}[variant=${XCODE_RELVERSION_I}] "${XCODE_VALUE}") + endif() +endmacro(set_xcode_property) +# This macro lets you find executable programs on the host system. +macro(find_host_package) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE NEVER) + set(IOS FALSE) + find_package(${ARGN}) + set(IOS TRUE) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH) +endmacro(find_host_package) From 1cc6a0dce5e4a0971ea2d7179632427d3b00e379 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 17 Dec 2019 09:04:10 +0800 Subject: [PATCH 100/272] fixed #175 --- src/Player/PlayerProxy.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Player/PlayerProxy.cpp b/src/Player/PlayerProxy.cpp index 766db316..23a59ee2 100644 --- a/src/Player/PlayerProxy.cpp +++ b/src/Player/PlayerProxy.cpp @@ -248,6 +248,9 @@ void PlayerProxy::onPlaySuccess() { } } _mediaMuxer->setListener(shared_from_this()); + if(_pMediaSrc){ + _pMediaSrc->setTrackSource(_mediaMuxer); + } auto videoTrack = getTrack(TrackVideo,false); if(videoTrack){ From 1169f29ca609b2f9c2fd2550d1ff62e3457cd1df Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 17 Dec 2019 09:05:34 +0800 Subject: [PATCH 101/272] fixed #175 --- src/Player/PlayerProxy.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Player/PlayerProxy.cpp b/src/Player/PlayerProxy.cpp index 23a59ee2..2babb7c4 100644 --- a/src/Player/PlayerProxy.cpp +++ b/src/Player/PlayerProxy.cpp @@ -248,9 +248,6 @@ void PlayerProxy::onPlaySuccess() { } } _mediaMuxer->setListener(shared_from_this()); - if(_pMediaSrc){ - _pMediaSrc->setTrackSource(_mediaMuxer); - } auto videoTrack = getTrack(TrackVideo,false); if(videoTrack){ @@ -279,6 +276,10 @@ void PlayerProxy::onPlaySuccess() { //MuteAudioMaker生成静音音频然后写入_mediaMuxer; audioMaker->addDelegate(_mediaMuxer); } + + if(_pMediaSrc){ + _pMediaSrc->setTrackSource(_mediaMuxer); + } } From a1e5724c7051dcb9f4355bd9f8156b5a70161ca9 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 17 Dec 2019 09:18:11 +0800 Subject: [PATCH 102/272] =?UTF-8?q?=E4=BC=98=E5=8C=96hls=E5=BD=95=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/HlsMaker.cpp | 4 ++++ src/Record/HlsMaker.h | 5 +++++ src/Record/HlsMakerImp.cpp | 7 ++++--- src/Record/HlsMakerImp.h | 2 -- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Record/HlsMaker.cpp b/src/Record/HlsMaker.cpp index 8c5b2994..039254b9 100644 --- a/src/Record/HlsMaker.cpp +++ b/src/Record/HlsMaker.cpp @@ -134,4 +134,8 @@ void HlsMaker::flushLastSegment(bool eof){ _last_file_name.clear(); } +bool HlsMaker::isLive() { + return _seg_number != 0; +} + }//namespace mediakit \ No newline at end of file diff --git a/src/Record/HlsMaker.h b/src/Record/HlsMaker.h index 49635a60..cb7f6b23 100644 --- a/src/Record/HlsMaker.h +++ b/src/Record/HlsMaker.h @@ -87,6 +87,11 @@ protected: * @param eof */ void flushLastSegment(bool eof = false); + + /** + * 是否为直播 + */ + bool isLive(); private: /** * 生成m3u8文件 diff --git a/src/Record/HlsMakerImp.cpp b/src/Record/HlsMakerImp.cpp index 88cd724f..cfb76e17 100644 --- a/src/Record/HlsMakerImp.cpp +++ b/src/Record/HlsMakerImp.cpp @@ -40,7 +40,6 @@ HlsMakerImp::HlsMakerImp(const string &m3u8_file, _path_hls = m3u8_file; _params = params; _buf_size = bufSize; - _is_vod = seg_number == 0; _file_buf.reset(new char[bufSize],[](char *ptr){ delete[] ptr; }); @@ -49,7 +48,7 @@ HlsMakerImp::HlsMakerImp(const string &m3u8_file, HlsMakerImp::~HlsMakerImp() { //录制完了 flushLastSegment(true); - if(!_is_vod){ + if(isLive()){ //hls直播才删除文件 File::delete_file(_path_prefix.data()); } @@ -62,7 +61,9 @@ string HlsMakerImp::onOpenSegment(int index) { auto strTime = getTimeStr("%H-%M-%S"); segment_name = StrPrinter << strDate + "/" + strTime << "_" << index << ".ts"; segment_path = _path_prefix + "/" + segment_name; - _segment_file_paths.emplace(index,segment_path); + if(isLive()){ + _segment_file_paths.emplace(index,segment_path); + } } _file = makeFile(segment_path, true); if(!_file){ diff --git a/src/Record/HlsMakerImp.h b/src/Record/HlsMakerImp.h index ece0213a..c9683510 100644 --- a/src/Record/HlsMakerImp.h +++ b/src/Record/HlsMakerImp.h @@ -58,8 +58,6 @@ private: string _path_hls; string _params; int _buf_size; - //是否为点播 - bool _is_vod; }; }//namespace mediakit From ee88297d4a6eef19ff2fe493b798c6bf23287abf Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 17 Dec 2019 09:24:35 +0800 Subject: [PATCH 103/272] fixed #174 --- CMakeLists.txt | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d20b60d..9b9ab0ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -161,6 +161,10 @@ endif() #收集源代码 file(GLOB ToolKit_src_list ${ToolKit_Root}/*/*.cpp ${ToolKit_Root}/*/*.h ${ToolKit_Root}/*/*.c) +if(IOS) + list(APPEND ToolKit_src_list ${ToolKit_Root}/Network/Socket_ios.mm) +endif() + file(GLOB MediaKit_src_list ${MediaKit_Root}/*/*.cpp ${MediaKit_Root}/*/*.h ${MediaKit_Root}/*/*.c) #去除win32的适配代码 @@ -183,8 +187,9 @@ elseif(NOT ANDROID OR IOS) list(APPEND LINK_LIB_LIST pthread) endif () -#测试程序 -add_subdirectory(tests) - -#主服务器 -add_subdirectory(server) +if (NOT IOS) + #测试程序 + add_subdirectory(tests) + #主服务器 + add_subdirectory(server) +endif () From 31438cfde4d898417759f46d3f3061159a99a2c8 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 17 Dec 2019 18:45:31 +0800 Subject: [PATCH 104/272] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E6=8F=90=E4=BE=9Bc?= =?UTF-8?q?=E6=A0=87=E5=87=86=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 3 + api/CMakeLists.txt | 11 +++ api/include/common.h | 129 +++++++++++++++++++++++++++++++ api/include/flvrecorder.h | 64 ++++++++++++++++ api/include/httpdownloader.h | 73 ++++++++++++++++++ api/include/media.h | 127 +++++++++++++++++++++++++++++++ api/include/mediakit.h | 36 +++++++++ api/include/proxyplayer.h | 66 ++++++++++++++++ api/source/common.cpp | 139 ++++++++++++++++++++++++++++++++++ api/source/flvrecorder.cpp | 50 ++++++++++++ api/source/httpdownloader.cpp | 54 +++++++++++++ api/source/media.cpp | 95 +++++++++++++++++++++++ api/source/proxyplayer.cpp | 47 ++++++++++++ 13 files changed, 894 insertions(+) create mode 100644 api/CMakeLists.txt create mode 100755 api/include/common.h create mode 100644 api/include/flvrecorder.h create mode 100755 api/include/httpdownloader.h create mode 100755 api/include/media.h create mode 100755 api/include/mediakit.h create mode 100644 api/include/proxyplayer.h create mode 100755 api/source/common.cpp create mode 100644 api/source/flvrecorder.cpp create mode 100755 api/source/httpdownloader.cpp create mode 100755 api/source/media.cpp create mode 100644 api/source/proxyplayer.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b9ab0ba..39d69a6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -187,6 +187,9 @@ elseif(NOT ANDROID OR IOS) list(APPEND LINK_LIB_LIST pthread) endif () +#添加c库 +add_subdirectory(api) + if (NOT IOS) #测试程序 add_subdirectory(tests) diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt new file mode 100644 index 00000000..03901c12 --- /dev/null +++ b/api/CMakeLists.txt @@ -0,0 +1,11 @@ +include_directories(include source) +file(GLOB api_src_list include/*.h source/*.cpp source/*.h source/*.c) +add_library(api SHARED ${api_src_list}) + +if(WIN32) + add_definitions(-DMediaKitApi_EXPORTS) +endif() + +target_link_libraries(api ${LINK_LIB_LIST}) + + diff --git a/api/include/common.h b/api/include/common.h new file mode 100755 index 00000000..85b970e2 --- /dev/null +++ b/api/include/common.h @@ -0,0 +1,129 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MK_COMMON_H +#define MK_COMMON_H + +#include + +#if defined(_WIN32) +#if defined(MediaKitApi_EXPORTS) + #define API_EXPORT __declspec(dllexport) + #else + #define API_EXPORT __declspec(dllimport) + #endif + + #define API_CALL __cdecl +#else +#define API_EXPORT +#define API_CALL +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + // 线程数 + int thread_num; + // 日志级别,支持0~4 + int log_level; + + // 配置文件是内容还是路径 + int ini_is_path; + // 配置文件内容或路径,可以为NULL + const char *ini; + + // ssl证书是内容还是路径 + int ssl_is_path; + // ssl证书内容或路径,可以为NULL + const char *ssl; + // 证书密码,可以为NULL + const char *ssl_pwd; +} config; + +/** + * 初始化环境,调用该库前需要先调用此函数 + * @param cfg 库运行相关参数 + */ +API_EXPORT void API_CALL mk_env_init(const config *cfg); + +/** + * 设置配置项 + * @param key 配置项名 + * @param val 配置项值 + */ +API_EXPORT void API_CALL mk_set_option(const char *key, const char *val); + +/** + * 创建http[s]服务器 + * @param port htt监听端口,推荐80,传入0则随机分配 + * @param ssl 是否为ssl类型服务器 + * @return 0:失败,非0:端口号 + */ +API_EXPORT uint16_t API_CALL mk_http_server_start(uint16_t port, int ssl); + +/** + * 创建rtsp[s]服务器 + * @param port rtsp监听端口,推荐554,传入0则随机分配 + * @param ssl 是否为ssl类型服务器 + * @return 0:失败,非0:端口号 + */ +API_EXPORT uint16_t API_CALL mk_rtsp_server_start(uint16_t port, int ssl); + +/** + * 创建rtmp[s]服务器 + * @param port rtmp监听端口,推荐1935,传入0则随机分配 + * @param ssl 是否为ssl类型服务器 + * @return 0:失败,非0:端口号 + */ +API_EXPORT uint16_t API_CALL mk_rtmp_server_start(uint16_t port, int ssl); + +/** + * 打印日志 + * @param level 日志级别,支持0~4 + * @param file __FILE__ + * @param function __FUNCTION__ + * @param line __LINE__ + * @param fmt printf类型的格式控制字符串 + * @param ... 不定长参数 + */ +API_EXPORT void API_CALL mk_log_printf(int level, const char *file, const char *function, int line, const char *fmt, ...); + +// 以下宏可以替换printf使用 +#define log_trace(fmt,...) mk_log_printf(0,__FILE__,__FUNCTION__,__LINE__,fmt,##__VA_ARGS__) +#define log_debug(fmt,...) mk_log_printf(1,__FILE__,__FUNCTION__,__LINE__,fmt,##__VA_ARGS__) +#define log_info(fmt,...) mk_log_printf(2,__FILE__,__FUNCTION__,__LINE__,fmt,##__VA_ARGS__) +#define log_warn(fmt,...) mk_log_printf(3,__FILE__,__FUNCTION__,__LINE__,fmt,##__VA_ARGS__) +#define log_error(fmt,...) mk_log_printf(4,__FILE__,__FUNCTION__,__LINE__,fmt,##__VA_ARGS__) +#define log_printf(lev,fmt,...) mk_log_printf(lev,__FILE__,__FUNCTION__,__LINE__,fmt,##__VA_ARGS__) + +#ifdef __cplusplus +} +#endif + + +#endif /* MK_COMMON_H */ diff --git a/api/include/flvrecorder.h b/api/include/flvrecorder.h new file mode 100644 index 00000000..6e310ed7 --- /dev/null +++ b/api/include/flvrecorder.h @@ -0,0 +1,64 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MK_FLV_RECORDER_API_H_ +#define MK_FLV_RECORDER_API_H_ + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* mk_flv_recorder; + +/** + * 创建flv录制器 + * @return + */ +API_EXPORT mk_flv_recorder API_CALL mk_flv_recorder_create(); + +/** + * 释放flv录制器 + * @param ctx + */ +API_EXPORT void API_CALL mk_flv_recorder_release(mk_flv_recorder ctx); + +/** + * 开始录制flv + * @param ctx flv录制器 + * @param app 绑定的RtmpMediaSource的 app名 + * @param stream 绑定的RtmpMediaSource的 stream名 + * @param file_path 文件存放地址 + * @return 0:开始超过,-1:失败,打开文件失败或该RtmpMediaSource不存在 + */ +API_EXPORT int API_CALL mk_flv_recorder_start(mk_flv_recorder ctx, const char *app, const char *stream, const char *file_path); + +#ifdef __cplusplus +} +#endif + +#endif /* MK_FLV_RECORDER_API_H_ */ diff --git a/api/include/httpdownloader.h b/api/include/httpdownloader.h new file mode 100755 index 00000000..73e7a1dd --- /dev/null +++ b/api/include/httpdownloader.h @@ -0,0 +1,73 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MK_HTTP_DOWNLOADER_H_ +#define MK_HTTP_DOWNLOADER_H_ + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *mk_http_downloader; + +/** + * @param user_data 用户数据指针 + * @param code 错误代码,0代表成功 + * @param err_msg 错误提示 + * @param file_path 文件保存路径 + */ +typedef void(API_CALL *on_download_complete)(void *user_data, int code, const char *err_msg, const char *file_path); + +/** + * 创建http[s]下载器 + * @return 下载器指针 + */ +API_EXPORT mk_http_downloader API_CALL mk_http_downloader_create(); + +/** + * 销毁http[s]下载器 + * @param ctx 下载器指针 + */ +API_EXPORT void API_CALL mk_http_downloader_release(mk_http_downloader ctx); + +/** + * 开始http[s]下载 + * @param ctx 下载器指针 + * @param url http[s]下载url + * @param file 文件保存路径 + * @param cb 回调函数 + * @param user_data 用户数据指针 + */ +API_EXPORT void API_CALL mk_http_downloader_start(mk_http_downloader ctx, const char *url, const char *file, on_download_complete cb, void *user_data); + + +#ifdef __cplusplus +} +#endif + +#endif /* MK_HTTP_DOWNLOADER_H_ */ diff --git a/api/include/media.h b/api/include/media.h new file mode 100755 index 00000000..4226b649 --- /dev/null +++ b/api/include/media.h @@ -0,0 +1,127 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MK_MEDIA_H_ +#define MK_MEDIA_H_ + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *mk_media; + +/** + * 创建一个媒体源 + * @param app 应用名,推荐为live + * @param stream 流id,例如camera + * @param duration 时长(单位秒),直播则为0 + * @param hls_enabled 是否生成hls + * @param mp4_enabled 是否生成mp4 + * @return 对象指针 + */ +API_EXPORT mk_media API_CALL mk_media_create(const char *app, const char *stream, float duration, int hls_enabled, int mp4_enabled); + +/** + * 销毁媒体源 + * @param ctx 对象指针 + */ +API_EXPORT void API_CALL mk_media_release(mk_media ctx); + +/** + * 添加h264视频轨道 + * @param ctx 对象指针 + * @param width 视频宽度 + * @param height 视频高度 + * @param fps 视频fps + */ +API_EXPORT void API_CALL mk_media_init_h264(mk_media ctx, int width, int height, int fps); + +/** + * 添加h265视频轨道 + * @param ctx 对象指针 + * @param width 视频宽度 + * @param height 视频高度 + * @param fps 视频fps + */ +API_EXPORT void API_CALL mk_media_init_h265(mk_media ctx, int width, int height, int fps); + +/** + * 添加aac音频轨道 + * @param ctx 对象指针 + * @param channel 通道数 + * @param sample_bit 采样位数,只支持16 + * @param sample_rate 采样率 + * @param profile aac编码profile,在不输入adts头时用于生产adts头 + */ +API_EXPORT void API_CALL mk_media_init_aac(mk_media ctx, int channel, int sample_bit, int sample_rate, int profile); + +/** + * 输入单帧H264视频,帧起始字节00 00 01,00 00 00 01均可 + * @param ctx 对象指针 + * @param data 单帧H264数据 + * @param len 单帧H264数据字节数 + * @param dts 解码时间戳,单位毫秒 + * @param dts 播放时间戳,单位毫秒 + */ +API_EXPORT void API_CALL mk_media_input_h264(mk_media ctx, void *data, int len, uint32_t dts, uint32_t pts); + +/** + * 输入单帧H265视频,帧起始字节00 00 01,00 00 00 01均可 + * @param ctx 对象指针 + * @param data 单帧H265数据 + * @param len 单帧H265数据字节数 + * @param dts 解码时间戳,单位毫秒 + * @param dts 播放时间戳,单位毫秒 + */ +API_EXPORT void API_CALL mk_media_input_h265(mk_media ctx, void *data, int len, uint32_t dts, uint32_t pts); + +/** + * 输入单帧AAC音频 + * @param ctx 对象指针 + * @param data 单帧AAC数据 + * @param len 单帧AAC数据字节数 + * @param dts 时间戳,毫秒 + * @param with_adts_header data中是否包含7个字节的adts头 + */ +API_EXPORT void API_CALL mk_media_input_aac(mk_media ctx, void *data, int len, uint32_t dts, int with_adts_header); + +/** + * 输入单帧AAC音频(单独指定adts头) + * @param ctx 对象指针 + * @param data 不包含adts头的单帧AAC数据 + * @param len 单帧AAC数据字节数 + * @param dts 时间戳,毫秒 + * @param adts adts头 + */ +API_EXPORT void API_CALL mk_media_input_aac1(mk_media ctx, void *data, int len, uint32_t dts, void *adts); + +#ifdef __cplusplus +} +#endif + +#endif /* MK_MEDIA_H_ */ diff --git a/api/include/mediakit.h b/api/include/mediakit.h new file mode 100755 index 00000000..d8956749 --- /dev/null +++ b/api/include/mediakit.h @@ -0,0 +1,36 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MK_API_H_ +#define MK_API_H_ + +#include "common.h" +#include "httpdownloader.h" +#include "media.h" +#include "proxyplayer.h" +#include "flvrecorder.h" + +#endif /* MK_API_H_ */ diff --git a/api/include/proxyplayer.h b/api/include/proxyplayer.h new file mode 100644 index 00000000..2ad354ae --- /dev/null +++ b/api/include/proxyplayer.h @@ -0,0 +1,66 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MK_PROXY_PLAYER_H_ +#define MK_PROXY_PLAYER_H_ + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *mk_proxy_player; + +/** + * 创建一个代理播放器 + * @param app 应用名 + * @param stream 流名 + * @param rtp_type rtsp播放方式:RTP_TCP = 0, RTP_UDP = 1, RTP_MULTICAST = 2 + * @param hls_enabled 是否生成hls + * @param mp4_enabled 是否生成mp4 + * @return 对象指针 + */ +API_EXPORT mk_proxy_player API_CALL mk_proxy_player_create(const char *app, const char *stream, int rtp_type, int hls_enabled, int mp4_enabled); + +/** + * 销毁代理播放器 + * @param ctx 对象指针 + */ +API_EXPORT void API_CALL mk_proxy_player_release(mk_proxy_player ctx); + +/** + * 开始播放 + * @param ctx 对象指针 + * @param url 播放url,支持rtsp/rtmp + */ +API_EXPORT void API_CALL mk_proxy_player_play(mk_proxy_player ctx, const char *url); + +#ifdef __cplusplus +} +#endif + +#endif /* MK_PROXY_PLAYER_H_ */ diff --git a/api/source/common.cpp b/api/source/common.cpp new file mode 100755 index 00000000..a7a16480 --- /dev/null +++ b/api/source/common.cpp @@ -0,0 +1,139 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "common.h" +#include +#include +#include "Util/logger.h" +#include "Util/SSLBox.h" +#include "Network/TcpServer.h" +#include "Thread/WorkThreadPool.h" + +#include "Rtsp/RtspSession.h" +#include "Rtmp/RtmpSession.h" +#include "Http/HttpSession.h" +using namespace std; +using namespace toolkit; +using namespace mediakit; + +static TcpServer::Ptr rtsp_server[2]; +static TcpServer::Ptr rtmp_server[2]; +static TcpServer::Ptr http_server[2]; + +//////////////////////////environment init/////////////////////////// +API_EXPORT void API_CALL mk_env_init(const config *cfg) { + assert(cfg != nullptr); + static onceToken token([&]() { + Logger::Instance().add(std::make_shared("console", (LogLevel) cfg->log_level)); + EventPollerPool::setPoolSize(cfg->thread_num); + WorkThreadPool::setPoolSize(cfg->thread_num); + + if (cfg->ini) { + //设置配置文件 + if (cfg->ini_is_path) { + mINI::Instance().parseFile(cfg->ini); + } else { + mINI::Instance().parse(cfg->ini); + } + } + + if (cfg->ssl) { + //设置ssl证书 + SSL_Initor::Instance().loadCertificate(cfg->ssl, true, cfg->ssl_pwd ? cfg->ssl_pwd : "", cfg->ssl_is_path); + } + }); +} + +API_EXPORT void API_CALL mk_set_option(const char *key, const char *val) { + if (mINI::Instance().find(key) == mINI::Instance().end()) { + WarnL << "key:" << key << " not existed!"; + return; + } + mINI::Instance()[key] = val; +} + +API_EXPORT uint16_t API_CALL mk_http_server_start(uint16_t port, int ssl) { + ssl = MAX(0,MIN(ssl,1)); + try { + http_server[ssl].reset(new TcpServer()); + if(ssl){ + http_server[ssl]->start >(port); + } else{ + http_server[ssl]->start(port); + } + return http_server[ssl]->getPort(); + } catch (std::exception &ex) { + http_server[ssl].reset(); + WarnL << ex.what(); + return 0; + } +} + +API_EXPORT uint16_t API_CALL mk_rtsp_server_start(uint16_t port, int ssl) { + ssl = MAX(0,MIN(ssl,1)); + try { + rtsp_server[ssl].reset(new TcpServer()); + if(ssl){ + rtsp_server[ssl]->start >(port); + }else{ + rtsp_server[ssl]->start(port); + } + return rtsp_server[ssl]->getPort(); + } catch (std::exception &ex) { + rtsp_server[ssl].reset(); + WarnL << ex.what(); + return 0; + } +} + +API_EXPORT uint16_t API_CALL mk_rtmp_server_start(uint16_t port, int ssl) { + ssl = MAX(0,MIN(ssl,1)); + try { + rtmp_server[ssl].reset(new TcpServer()); + if(ssl){ + rtmp_server[ssl]->start >(port); + }else{ + rtmp_server[ssl]->start(port); + } + return rtmp_server[ssl]->getPort(); + } catch (std::exception &ex) { + rtmp_server[ssl].reset(); + WarnL << ex.what(); + return 0; + } +} + +API_EXPORT void API_CALL mk_log_printf(int level, const char *file, const char *function, int line, const char *fmt, ...) { + LogContextCapturer info(Logger::Instance(), (LogLevel) level, file, function, line); + va_list pArg; + va_start(pArg, fmt); + char buf[4096]; + int n = vsprintf(buf, fmt, pArg); + buf[n] = '\0'; + va_end(pArg); + info << buf; +} + diff --git a/api/source/flvrecorder.cpp b/api/source/flvrecorder.cpp new file mode 100644 index 00000000..bc45defe --- /dev/null +++ b/api/source/flvrecorder.cpp @@ -0,0 +1,50 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "flvrecorder.h" +#include "Rtmp/FlvMuxer.h" +using namespace toolkit; +using namespace mediakit; + +API_EXPORT mk_flv_recorder API_CALL mk_flv_recorder_create(){ + FlvRecorder::Ptr *ret = new FlvRecorder::Ptr(new FlvRecorder); + return ret; +} +API_EXPORT void API_CALL mk_flv_recorder_release(mk_flv_recorder ctx){ + FlvRecorder::Ptr *record = (FlvRecorder::Ptr *)(ctx); + delete record; +} +API_EXPORT int API_CALL mk_flv_recorder_start(mk_flv_recorder ctx, const char *app, const char *stream, const char *file_path){ + DebugL << app << " " << stream << " " << file_path; + FlvRecorder::Ptr *record = (FlvRecorder::Ptr *)(ctx); + try { + (*record)->startRecord(EventPollerPool::Instance().getPoller(), DEFAULT_VHOST,app,stream,file_path); + return 0; + }catch (std::exception &ex){ + WarnL << ex.what(); + return -1; + } +} \ No newline at end of file diff --git a/api/source/httpdownloader.cpp b/api/source/httpdownloader.cpp new file mode 100755 index 00000000..f7a381ae --- /dev/null +++ b/api/source/httpdownloader.cpp @@ -0,0 +1,54 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "httpdownloader.h" + +#include "Util/logger.h" +#include "Util/TimeTicker.h" +#include "Util/onceToken.h" +#include "Http/HttpDownloader.h" +using namespace std; +using namespace toolkit; +using namespace mediakit; + +API_EXPORT mk_http_downloader API_CALL mk_http_downloader_create() { + HttpDownloader::Ptr *obj(new HttpDownloader::Ptr(new HttpDownloader())); + return (mk_http_downloader) obj; +} + +API_EXPORT void API_CALL mk_http_downloader_release(mk_http_downloader ctx) { + HttpDownloader::Ptr *obj = (HttpDownloader::Ptr *) ctx; + delete obj; +} + +API_EXPORT void API_CALL mk_http_downloader_start(mk_http_downloader ctx, const char *url, const char *file, on_download_complete cb, void *user_data) { + HttpDownloader::Ptr *obj = (HttpDownloader::Ptr *) ctx; + (*obj)->setOnResult([cb, user_data](ErrCode code, const string &errMsg, const string &filePath) { + if (cb) { + cb(user_data, code, errMsg.data(), filePath.data()); + } + }); + (*obj)->startDownload(url, file, false); +} diff --git a/api/source/media.cpp b/api/source/media.cpp new file mode 100755 index 00000000..bcc3473b --- /dev/null +++ b/api/source/media.cpp @@ -0,0 +1,95 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "media.h" +#include "Util/logger.h" +#include "Common/Device.h" + +using namespace std; +using namespace toolkit; +using namespace mediakit; + +API_EXPORT mk_media API_CALL mk_media_create(const char *app, const char *stream, float duration, int hls_enabled, int mp4_enabled) { + DevChannel::Ptr *obj(new DevChannel::Ptr(new DevChannel(DEFAULT_VHOST, app, stream, duration, true, true, hls_enabled, mp4_enabled))); + return (mk_media) obj; +} + +API_EXPORT void API_CALL mk_media_release(mk_media ctx) { + DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; + delete obj; +} + +API_EXPORT void API_CALL mk_media_init_h264(mk_media ctx, int width, int height, int frameRate) { + DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; + VideoInfo info; + info.iFrameRate = frameRate; + info.iWidth = width; + info.iHeight = height; + (*obj)->initVideo(info); +} + +API_EXPORT void API_CALL mk_media_init_h265(mk_media ctx, int width, int height, int frameRate) { + DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; + VideoInfo info; + info.iFrameRate = frameRate; + info.iWidth = width; + info.iHeight = height; + (*obj)->initH265Video(info); +} + +API_EXPORT void API_CALL mk_media_init_aac(mk_media ctx, int channel, int sample_bit, int sample_rate, int profile) { + DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; + AudioInfo info; + info.iSampleRate = sample_rate; + info.iChannel = channel; + info.iSampleBit = sample_bit; + info.iProfile = profile; + (*obj)->initAudio(info); +} + +API_EXPORT void API_CALL mk_media_input_h264(mk_media ctx, void *data, int len, uint32_t dts, uint32_t pts) { + DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; + (*obj)->inputH264((char *) data, len, dts, pts); +} + +API_EXPORT void API_CALL mk_media_input_h265(mk_media ctx, void *data, int len, uint32_t dts, uint32_t pts) { + DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; + (*obj)->inputH265((char *) data, len, dts, pts); +} + +API_EXPORT void API_CALL mk_media_input_aac(mk_media ctx, void *data, int len, uint32_t dts, int with_adts_header) { + DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; + (*obj)->inputAAC((char *) data, len, dts, with_adts_header); +} + +API_EXPORT void API_CALL mk_media_input_aac1(mk_media ctx, void *data, int len, uint32_t dts, void *adts) { + DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; + (*obj)->inputAAC((char *) data, len, dts, (char *) adts); +} + + + + diff --git a/api/source/proxyplayer.cpp b/api/source/proxyplayer.cpp new file mode 100644 index 00000000..ffaf1f4d --- /dev/null +++ b/api/source/proxyplayer.cpp @@ -0,0 +1,47 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "proxyplayer.h" +#include "Player/PlayerProxy.h" + +using namespace toolkit; +using namespace mediakit; + +API_EXPORT mk_proxy_player API_CALL mk_proxy_player_create(const char *app, const char *stream, int rtp_type, int hls_enabled, int mp4_enabled) { + PlayerProxy::Ptr *obj(new PlayerProxy::Ptr(new PlayerProxy(DEFAULT_VHOST, app, stream, true, true, hls_enabled, mp4_enabled))); + (**obj)[Client::kRtpType] = rtp_type; + return (mk_proxy_player) obj; +} + +API_EXPORT void API_CALL mk_proxy_player_release(mk_proxy_player ctx) { + PlayerProxy::Ptr *obj = (PlayerProxy::Ptr *) ctx; + delete obj; +} + +API_EXPORT void API_CALL mk_proxy_player_play(mk_proxy_player ctx, const char *url) { + PlayerProxy::Ptr *obj = (PlayerProxy::Ptr *) ctx; + (*obj)->play(url); +} From 97bf82e54cabc1e292d5866fb9555008a87149d4 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 18 Dec 2019 10:19:08 +0800 Subject: [PATCH 105/272] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E4=BF=AE=E5=A4=8Dlin?= =?UTF-8?q?ux=E7=BC=96=E8=AF=91=E5=A4=B1=E8=B4=A5=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt index 03901c12..233a8580 100644 --- a/api/CMakeLists.txt +++ b/api/CMakeLists.txt @@ -2,6 +2,10 @@ file(GLOB api_src_list include/*.h source/*.cpp source/*.h source/*.c) add_library(api SHARED ${api_src_list}) +if (CMAKE_SYSTEM_NAME MATCHES "Linux") + add_compile_options(-fPIC) +endif() + if(WIN32) add_definitions(-DMediaKitApi_EXPORTS) endif() From 239dc309f28e593a5692ebf27cd862629cdd59d0 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 18 Dec 2019 10:37:23 +0800 Subject: [PATCH 106/272] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E4=BF=AE=E5=A4=8Dlin?= =?UTF-8?q?ux=E4=B8=8B=E7=BC=96=E8=AF=91=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 1 + api/CMakeLists.txt | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 39d69a6f..7b2baa92 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,7 @@ set(RELEASE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/release) if (CMAKE_SYSTEM_NAME MATCHES "Linux") SET(LIBRARY_OUTPUT_PATH ${RELEASE_DIR}/linux/${BuildType}) SET(EXECUTABLE_OUTPUT_PATH ${RELEASE_DIR}/linux/${BuildType}) + add_compile_options(-fPIC) elseif (CMAKE_SYSTEM_NAME MATCHES "Windows") SET(LIBRARY_OUTPUT_PATH ${RELEASE_DIR}/windows/${BuildType}) SET(EXECUTABLE_OUTPUT_PATH ${RELEASE_DIR}/windows/${BuildType}) diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt index 233a8580..03901c12 100644 --- a/api/CMakeLists.txt +++ b/api/CMakeLists.txt @@ -2,10 +2,6 @@ file(GLOB api_src_list include/*.h source/*.cpp source/*.h source/*.c) add_library(api SHARED ${api_src_list}) -if (CMAKE_SYSTEM_NAME MATCHES "Linux") - add_compile_options(-fPIC) -endif() - if(WIN32) add_definitions(-DMediaKitApi_EXPORTS) endif() From 026aa5e75c2dee16eaefe8376e66be1549802569 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 18 Dec 2019 11:45:33 +0800 Subject: [PATCH 107/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=92=AD=E6=94=BE?= =?UTF-8?q?=E5=99=A8=E7=9A=84C=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/httpdownloader.h | 4 +- api/include/mediakit.h | 1 + api/include/player.h | 175 ++++++++++++++++++++++++++++++++ api/source/httpdownloader.cpp | 4 +- api/source/player.cpp | 183 ++++++++++++++++++++++++++++++++++ 5 files changed, 362 insertions(+), 5 deletions(-) create mode 100755 api/include/player.h create mode 100755 api/source/player.cpp diff --git a/api/include/httpdownloader.h b/api/include/httpdownloader.h index 73e7a1dd..8aeaa3d3 100755 --- a/api/include/httpdownloader.h +++ b/api/include/httpdownloader.h @@ -41,7 +41,7 @@ typedef void *mk_http_downloader; * @param err_msg 错误提示 * @param file_path 文件保存路径 */ -typedef void(API_CALL *on_download_complete)(void *user_data, int code, const char *err_msg, const char *file_path); +typedef void(API_CALL *on_mk_download_complete)(void *user_data, int code, const char *err_msg, const char *file_path); /** * 创建http[s]下载器 @@ -63,7 +63,7 @@ API_EXPORT void API_CALL mk_http_downloader_release(mk_http_downloader ctx); * @param cb 回调函数 * @param user_data 用户数据指针 */ -API_EXPORT void API_CALL mk_http_downloader_start(mk_http_downloader ctx, const char *url, const char *file, on_download_complete cb, void *user_data); +API_EXPORT void API_CALL mk_http_downloader_start(mk_http_downloader ctx, const char *url, const char *file, on_mk_download_complete cb, void *user_data); #ifdef __cplusplus diff --git a/api/include/mediakit.h b/api/include/mediakit.h index d8956749..789e70b6 100755 --- a/api/include/mediakit.h +++ b/api/include/mediakit.h @@ -32,5 +32,6 @@ #include "media.h" #include "proxyplayer.h" #include "flvrecorder.h" +#include "player.h" #endif /* MK_API_H_ */ diff --git a/api/include/player.h b/api/include/player.h new file mode 100755 index 00000000..50181c26 --- /dev/null +++ b/api/include/player.h @@ -0,0 +1,175 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MK_PLAYER_H_ +#define MK_PLAYER_H_ + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* mk_player; + +/** + * 播放结果或播放中断事件的回调 + * @param user_data 用户数据指针 + * @param err_code 错误代码,0为成功 + * @param err_msg 错误提示 + */ +typedef void(API_CALL *on_mk_play_event)(void *user_data,int err_code,const char *err_msg); + +/** + * 收到音视频数据回调 + * @param user_data 用户数据指针 + * @param track_type 0:视频,1:音频 + * @param codec_id 0:H264,1:H265,2:AAC + * @param data 数据指针 + * @param len 数据长度 + * @param dts 解码时间戳,单位毫秒 + * @param pts 显示时间戳,单位毫秒 + */ +typedef void(API_CALL *on_mk_play_data)(void *user_data,int track_type,int codec_id,void *data,int len,uint32_t dts,uint32_t pts); + +/** + * 创建一个播放器,支持rtmp/rtsp + * @return 播放器指针 + */ +API_EXPORT mk_player API_CALL mk_player_create(); + +/** + * 销毁播放器 + * @param ctx 播放器指针 + */ +API_EXPORT void API_CALL mk_player_release(mk_player ctx); + +/** + * 设置播放器配置选项 + * @param ctx 播放器指针 + * @param key 配置项键,支持 net_adapter/rtp_type/rtsp_user/rtsp_pwd/protocol_timeout_ms/media_timeout_ms/beat_interval_ms/max_analysis_ms + * @param val 配置项值,如果是整形,需要转换成统一转换成string + */ +API_EXPORT void API_CALL mk_player_set_option(mk_player ctx, const char *key, const char *val); + +/** + * 开始播放url + * @param ctx 播放器指针 + * @param url rtsp/rtmp url + */ +API_EXPORT void API_CALL mk_player_play(mk_player ctx, const char *url); + +/** + * 暂停或恢复播放,仅对点播有用 + * @param ctx 播放器指针 + * @param pause 1:暂停播放,0:恢复播放 + */ +API_EXPORT void API_CALL mk_player_pause(mk_player ctx, int pause); + +/** + * 设置点播进度条 + * @param ctx 对象指针 + * @param progress 取值范围未 0.0~1.0 + */ +API_EXPORT void API_CALL mk_player_seekto(mk_player ctx, float progress); + +/** + * 设置播放器开启播放结果回调函数 + * @param ctx 播放器指针 + * @param cb 回调函数指针 + * @param user_data 用户数据指针 + */ +API_EXPORT void API_CALL mk_player_set_on_result(mk_player ctx, on_mk_play_event cb, void *user_data); + +/** + * 设置播放被异常中断的回调 + * @param ctx 播放器指针 + * @param cb 回调函数指针,不得为null + * @param user_data 用户数据指针 + */ +API_EXPORT void API_CALL mk_player_set_on_shutdown(mk_player ctx, on_mk_play_event cb, void *user_data); + +/** + * 设置音视频数据回调函数 + * @param ctx 播放器指针 + * @param cb 回调函数指针,不得为null + * @param user_data 用户数据指针 + */ +API_EXPORT void API_CALL mk_player_set_on_data(mk_player ctx, on_mk_play_data cb, void *user_data); + +/** + * 获取视频宽度 + */ +API_EXPORT int API_CALL mk_player_video_width(mk_player ctx); + +/** + * 获取视频高度 + */ +API_EXPORT int API_CALL mk_player_video_height(mk_player ctx); + +/** + * 获取视频帧率 + */ +API_EXPORT int API_CALL mk_player_video_fps(mk_player ctx); + +/** + * 获取音频采样率 + */ +API_EXPORT int API_CALL mk_player_audio_samplerate(mk_player ctx); + +/** + * 获取音频采样位数,一般为16 + */ +API_EXPORT int API_CALL mk_player_audio_bit(mk_player ctx); + +/** + * 获取音频通道数 + */ +API_EXPORT int API_CALL mk_player_audio_channel(mk_player ctx); + +/** + * 获取点播节目时长,如果是直播返回0,否则返回秒数 + */ +API_EXPORT float API_CALL mk_player_duration(mk_player ctx); + +/** + * 获取点播播放进度,取值范围未 0.0~1.0 + */ +API_EXPORT float API_CALL mk_player_progress(mk_player ctx); + +/** + * 获取丢包率,rtsp时有效 + * @param ctx 对象指针 + * @param track_type 0:视频,1:音频 + */ +API_EXPORT float API_CALL mk_player_loss_rate(mk_player ctx, int track_type); + + +#ifdef __cplusplus +} +#endif + +#endif /* MK_PLAYER_H_ */ diff --git a/api/source/httpdownloader.cpp b/api/source/httpdownloader.cpp index f7a381ae..7a7bbf5f 100755 --- a/api/source/httpdownloader.cpp +++ b/api/source/httpdownloader.cpp @@ -26,8 +26,6 @@ #include "httpdownloader.h" #include "Util/logger.h" -#include "Util/TimeTicker.h" -#include "Util/onceToken.h" #include "Http/HttpDownloader.h" using namespace std; using namespace toolkit; @@ -43,7 +41,7 @@ API_EXPORT void API_CALL mk_http_downloader_release(mk_http_downloader ctx) { delete obj; } -API_EXPORT void API_CALL mk_http_downloader_start(mk_http_downloader ctx, const char *url, const char *file, on_download_complete cb, void *user_data) { +API_EXPORT void API_CALL mk_http_downloader_start(mk_http_downloader ctx, const char *url, const char *file, on_mk_download_complete cb, void *user_data) { HttpDownloader::Ptr *obj = (HttpDownloader::Ptr *) ctx; (*obj)->setOnResult([cb, user_data](ErrCode code, const string &errMsg, const string &filePath) { if (cb) { diff --git a/api/source/player.cpp b/api/source/player.cpp new file mode 100755 index 00000000..6686e837 --- /dev/null +++ b/api/source/player.cpp @@ -0,0 +1,183 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "player.h" +#include "Util/logger.h" +#include "Player/MediaPlayer.h" +using namespace std; +using namespace toolkit; +using namespace mediakit; + +API_EXPORT mk_player API_CALL mk_player_create() { + MediaPlayer::Ptr *obj = new MediaPlayer::Ptr(new MediaPlayer()); + return obj; +} +API_EXPORT void API_CALL mk_player_release(mk_player ctx) { + assert(ctx); + MediaPlayer::Ptr *obj = (MediaPlayer::Ptr *)ctx; + delete obj; +} + +API_EXPORT void API_CALL mk_player_set_option(mk_player ctx,const char* key,const char *val){ + assert(ctx); + assert(key); + assert(val); + MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); + string key_str(key), val_str(val); + player->getPoller()->async([key_str,val_str,player](){ + //切换线程后再操作 + (*player)[key_str] = val_str; + }); +} +API_EXPORT void API_CALL mk_player_play(mk_player ctx, const char *url) { + assert(ctx); + assert(url); + MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); + string url_str(url); + player->getPoller()->async([url_str,player](){ + //切换线程后再操作 + player->play(url_str); + }); +} + +API_EXPORT void API_CALL mk_player_pause(mk_player ctx, int pause) { + assert(ctx); + MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); + player->getPoller()->async([pause,player](){ + //切换线程后再操作 + player->pause(pause); + }); +} + +API_EXPORT void API_CALL mk_player_seekto(mk_player ctx, float progress) { + assert(ctx); + MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); + player->getPoller()->async([progress,player](){ + //切换线程后再操作 + player->seekTo(progress); + }); +} + +static void mk_player_set_on_event(mk_player ctx, on_mk_play_event cb, void *user_data, int type) { + assert(ctx); + assert(cb); + MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); + player->getPoller()->async([cb,user_data,type,player](){ + //切换线程后再操作 + if(type == 0){ + player->setOnPlayResult([cb,user_data](const SockException &ex){ + cb(user_data,ex.getErrCode(),ex.what()); + }); + }else{ + player->setOnShutdown([cb,user_data](const SockException &ex){ + cb(user_data,ex.getErrCode(),ex.what()); + }); + } + }); +} + +API_EXPORT void API_CALL mk_player_set_on_result(mk_player ctx, on_mk_play_event cb, void *user_data) { + mk_player_set_on_event(ctx,cb,user_data,0); +} + +API_EXPORT void API_CALL mk_player_set_on_shutdown(mk_player ctx, on_mk_play_event cb, void *user_data) { + mk_player_set_on_event(ctx,cb,user_data,1); +} + +API_EXPORT void API_CALL mk_player_set_on_data(mk_player ctx, on_mk_play_data cb, void *user_data) { + assert(ctx); + assert(cb); + MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); + player->getPoller()->async([player,cb,user_data](){ + //切换线程后再操作 + auto delegate = std::make_shared([cb,user_data](const Frame::Ptr &frame){ + cb(user_data,frame->getTrackType(),frame->getCodecId(),frame->data(),frame->size(),frame->dts(),frame->pts()); + }); + for(auto &track : player->getTracks()){ + track->addDelegate(delegate); + } + }); +} + +API_EXPORT int API_CALL mk_player_video_width(mk_player ctx) { + assert(ctx); + MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); + auto track = dynamic_pointer_cast(player->getTrack(TrackVideo)); + return track ? track->getVideoWidth() : 0; +} + +API_EXPORT int API_CALL mk_player_video_height(mk_player ctx) { + assert(ctx); + MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); + auto track = dynamic_pointer_cast(player->getTrack(TrackVideo)); + return track ? track->getVideoHeight() : 0; +} + +API_EXPORT int API_CALL mk_player_video_fps(mk_player ctx) { + assert(ctx); + MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); + auto track = dynamic_pointer_cast(player->getTrack(TrackVideo)); + return track ? track->getVideoFps() : 0; +} + +API_EXPORT int API_CALL mk_player_audio_samplerate(mk_player ctx) { + assert(ctx); + MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); + auto track = dynamic_pointer_cast(player->getTrack(TrackAudio)); + return track ? track->getAudioSampleRate() : 0; +} + +API_EXPORT int API_CALL mk_player_audio_bit(mk_player ctx) { + assert(ctx); + MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); + auto track = dynamic_pointer_cast(player->getTrack(TrackAudio)); + return track ? track->getAudioSampleBit() : 0; +} + +API_EXPORT int API_CALL mk_player_audio_channel(mk_player ctx) { + assert(ctx); + MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); + auto track = dynamic_pointer_cast(player->getTrack(TrackAudio)); + return track ? track->getAudioChannel() : 0; +} + +API_EXPORT float API_CALL mk_player_duration(mk_player ctx) { + assert(ctx); + MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); + return player->getDuration(); +} + +API_EXPORT float API_CALL mk_player_progress(mk_player ctx) { + assert(ctx); + MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); + return player->getProgress(); +} + +API_EXPORT float API_CALL mk_player_loss_rate(mk_player ctx, int track_type) { + assert(ctx); + MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); + return player->getPacketLossRate((TrackType)track_type); +} From bf499916d811b3f3590ce13d0765a31210a7d62c Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 18 Dec 2019 11:47:49 +0800 Subject: [PATCH 108/272] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E6=B7=BB=E5=8A=A0UTF?= =?UTF-8?q?-8=20bom=20=E5=A4=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/common.h | 2 +- api/include/flvrecorder.h | 2 +- api/include/httpdownloader.h | 2 +- api/include/media.h | 2 +- api/include/mediakit.h | 2 +- api/include/player.h | 2 +- api/include/proxyplayer.h | 2 +- api/source/common.cpp | 2 +- api/source/flvrecorder.cpp | 2 +- api/source/httpdownloader.cpp | 2 +- api/source/media.cpp | 2 +- api/source/player.cpp | 2 +- api/source/proxyplayer.cpp | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/api/include/common.h b/api/include/common.h index 85b970e2..8a451d73 100755 --- a/api/include/common.h +++ b/api/include/common.h @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2019 xiongziliang <771730766@qq.com> diff --git a/api/include/flvrecorder.h b/api/include/flvrecorder.h index 6e310ed7..323d33a6 100644 --- a/api/include/flvrecorder.h +++ b/api/include/flvrecorder.h @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2019 xiongziliang <771730766@qq.com> diff --git a/api/include/httpdownloader.h b/api/include/httpdownloader.h index 8aeaa3d3..0354ec62 100755 --- a/api/include/httpdownloader.h +++ b/api/include/httpdownloader.h @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2019 xiongziliang <771730766@qq.com> diff --git a/api/include/media.h b/api/include/media.h index 4226b649..f80e4083 100755 --- a/api/include/media.h +++ b/api/include/media.h @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2019 xiongziliang <771730766@qq.com> diff --git a/api/include/mediakit.h b/api/include/mediakit.h index 789e70b6..534eac83 100755 --- a/api/include/mediakit.h +++ b/api/include/mediakit.h @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2019 xiongziliang <771730766@qq.com> diff --git a/api/include/player.h b/api/include/player.h index 50181c26..cb4da62c 100755 --- a/api/include/player.h +++ b/api/include/player.h @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2019 xiongziliang <771730766@qq.com> diff --git a/api/include/proxyplayer.h b/api/include/proxyplayer.h index 2ad354ae..660be015 100644 --- a/api/include/proxyplayer.h +++ b/api/include/proxyplayer.h @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2019 xiongziliang <771730766@qq.com> diff --git a/api/source/common.cpp b/api/source/common.cpp index a7a16480..72585388 100755 --- a/api/source/common.cpp +++ b/api/source/common.cpp @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2019 xiongziliang <771730766@qq.com> diff --git a/api/source/flvrecorder.cpp b/api/source/flvrecorder.cpp index bc45defe..ab89af80 100644 --- a/api/source/flvrecorder.cpp +++ b/api/source/flvrecorder.cpp @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2019 xiongziliang <771730766@qq.com> diff --git a/api/source/httpdownloader.cpp b/api/source/httpdownloader.cpp index 7a7bbf5f..0f9c5004 100755 --- a/api/source/httpdownloader.cpp +++ b/api/source/httpdownloader.cpp @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2019 xiongziliang <771730766@qq.com> diff --git a/api/source/media.cpp b/api/source/media.cpp index bcc3473b..1a1addb2 100755 --- a/api/source/media.cpp +++ b/api/source/media.cpp @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2019 xiongziliang <771730766@qq.com> diff --git a/api/source/player.cpp b/api/source/player.cpp index 6686e837..2e60f7cd 100755 --- a/api/source/player.cpp +++ b/api/source/player.cpp @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2019 xiongziliang <771730766@qq.com> diff --git a/api/source/proxyplayer.cpp b/api/source/proxyplayer.cpp index ffaf1f4d..d733fe01 100644 --- a/api/source/proxyplayer.cpp +++ b/api/source/proxyplayer.cpp @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2019 xiongziliang <771730766@qq.com> From ee4659a1852e9dc930059814f27e4aaed144e736 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 18 Dec 2019 13:54:55 +0800 Subject: [PATCH 109/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=BC=82=E6=AD=A5?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E7=BA=BF=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/source/common.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/source/common.cpp b/api/source/common.cpp index 72585388..9299bd79 100755 --- a/api/source/common.cpp +++ b/api/source/common.cpp @@ -48,6 +48,8 @@ API_EXPORT void API_CALL mk_env_init(const config *cfg) { assert(cfg != nullptr); static onceToken token([&]() { Logger::Instance().add(std::make_shared("console", (LogLevel) cfg->log_level)); + Logger::Instance().setWriter(std::make_shared()); + EventPollerPool::setPoolSize(cfg->thread_num); WorkThreadPool::setPoolSize(cfg->thread_num); From ef7e937808081c9fdb9802b2f003ce61d0c7945c Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 18 Dec 2019 14:43:32 +0800 Subject: [PATCH 110/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0pusher=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=20=E6=95=B4=E7=90=86=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/media.h | 3 ++- api/include/mediakit.h | 1 + api/include/player.h | 6 +++--- api/include/proxyplayer.h | 12 +++++++++++- api/source/common.cpp | 4 +++- api/source/flvrecorder.cpp | 5 +++-- api/source/httpdownloader.cpp | 2 ++ api/source/media.cpp | 13 +++++++++++-- api/source/player.cpp | 13 ++++--------- api/source/proxyplayer.cpp | 28 ++++++++++++++++++++++------ 10 files changed, 62 insertions(+), 25 deletions(-) diff --git a/api/include/media.h b/api/include/media.h index f80e4083..55c6f141 100755 --- a/api/include/media.h +++ b/api/include/media.h @@ -37,6 +37,7 @@ typedef void *mk_media; /** * 创建一个媒体源 + * @param vhost 虚拟主机名,一般为__defaultVhost__ * @param app 应用名,推荐为live * @param stream 流id,例如camera * @param duration 时长(单位秒),直播则为0 @@ -44,7 +45,7 @@ typedef void *mk_media; * @param mp4_enabled 是否生成mp4 * @return 对象指针 */ -API_EXPORT mk_media API_CALL mk_media_create(const char *app, const char *stream, float duration, int hls_enabled, int mp4_enabled); +API_EXPORT mk_media API_CALL mk_media_create(const char *vhost, const char *app, const char *stream, float duration, int hls_enabled, int mp4_enabled); /** * 销毁媒体源 diff --git a/api/include/mediakit.h b/api/include/mediakit.h index 534eac83..2cbcc442 100755 --- a/api/include/mediakit.h +++ b/api/include/mediakit.h @@ -33,5 +33,6 @@ #include "proxyplayer.h" #include "flvrecorder.h" #include "player.h" +#include "pusher.h" #endif /* MK_API_H_ */ diff --git a/api/include/player.h b/api/include/player.h index cb4da62c..ad119a72 100755 --- a/api/include/player.h +++ b/api/include/player.h @@ -56,7 +56,7 @@ typedef void(API_CALL *on_mk_play_event)(void *user_data,int err_code,const char typedef void(API_CALL *on_mk_play_data)(void *user_data,int track_type,int codec_id,void *data,int len,uint32_t dts,uint32_t pts); /** - * 创建一个播放器,支持rtmp/rtsp + * 创建一个播放器,支持rtmp[s]/rtsp[s] * @return 播放器指针 */ API_EXPORT mk_player API_CALL mk_player_create(); @@ -78,7 +78,7 @@ API_EXPORT void API_CALL mk_player_set_option(mk_player ctx, const char *key, co /** * 开始播放url * @param ctx 播放器指针 - * @param url rtsp/rtmp url + * @param url rtsp[s]/rtmp[s] url */ API_EXPORT void API_CALL mk_player_play(mk_player ctx, const char *url); @@ -99,7 +99,7 @@ API_EXPORT void API_CALL mk_player_seekto(mk_player ctx, float progress); /** * 设置播放器开启播放结果回调函数 * @param ctx 播放器指针 - * @param cb 回调函数指针 + * @param cb 回调函数指针,不得为null * @param user_data 用户数据指针 */ API_EXPORT void API_CALL mk_player_set_on_result(mk_player ctx, on_mk_play_event cb, void *user_data); diff --git a/api/include/proxyplayer.h b/api/include/proxyplayer.h index 660be015..d1230f7f 100644 --- a/api/include/proxyplayer.h +++ b/api/include/proxyplayer.h @@ -37,6 +37,7 @@ typedef void *mk_proxy_player; /** * 创建一个代理播放器 + * @param vhost 虚拟主机名,一般为__defaultVhost__ * @param app 应用名 * @param stream 流名 * @param rtp_type rtsp播放方式:RTP_TCP = 0, RTP_UDP = 1, RTP_MULTICAST = 2 @@ -44,7 +45,7 @@ typedef void *mk_proxy_player; * @param mp4_enabled 是否生成mp4 * @return 对象指针 */ -API_EXPORT mk_proxy_player API_CALL mk_proxy_player_create(const char *app, const char *stream, int rtp_type, int hls_enabled, int mp4_enabled); +API_EXPORT mk_proxy_player API_CALL mk_proxy_player_create(const char *vhost, const char *app, const char *stream, int hls_enabled, int mp4_enabled); /** * 销毁代理播放器 @@ -52,6 +53,15 @@ API_EXPORT mk_proxy_player API_CALL mk_proxy_player_create(const char *app, cons */ API_EXPORT void API_CALL mk_proxy_player_release(mk_proxy_player ctx); + +/** + * 设置代理播放器配置选项 + * @param ctx 代理播放器指针 + * @param key 配置项键,支持 net_adapter/rtp_type/rtsp_user/rtsp_pwd/protocol_timeout_ms/media_timeout_ms/beat_interval_ms/max_analysis_ms + * @param val 配置项值,如果是整形,需要转换成统一转换成string + */ +API_EXPORT void API_CALL mk_proxy_player_set_option(mk_proxy_player ctx, const char *key, const char *val); + /** * 开始播放 * @param ctx 对象指针 diff --git a/api/source/common.cpp b/api/source/common.cpp index 9299bd79..8f6d79b6 100755 --- a/api/source/common.cpp +++ b/api/source/common.cpp @@ -45,7 +45,7 @@ static TcpServer::Ptr http_server[2]; //////////////////////////environment init/////////////////////////// API_EXPORT void API_CALL mk_env_init(const config *cfg) { - assert(cfg != nullptr); + assert(cfg); static onceToken token([&]() { Logger::Instance().add(std::make_shared("console", (LogLevel) cfg->log_level)); Logger::Instance().setWriter(std::make_shared()); @@ -70,6 +70,7 @@ API_EXPORT void API_CALL mk_env_init(const config *cfg) { } API_EXPORT void API_CALL mk_set_option(const char *key, const char *val) { + assert(key && val); if (mINI::Instance().find(key) == mINI::Instance().end()) { WarnL << "key:" << key << " not existed!"; return; @@ -129,6 +130,7 @@ API_EXPORT uint16_t API_CALL mk_rtmp_server_start(uint16_t port, int ssl) { } API_EXPORT void API_CALL mk_log_printf(int level, const char *file, const char *function, int line, const char *fmt, ...) { + assert(file && function && fmt); LogContextCapturer info(Logger::Instance(), (LogLevel) level, file, function, line); va_list pArg; va_start(pArg, fmt); diff --git a/api/source/flvrecorder.cpp b/api/source/flvrecorder.cpp index ab89af80..93882320 100644 --- a/api/source/flvrecorder.cpp +++ b/api/source/flvrecorder.cpp @@ -34,13 +34,14 @@ API_EXPORT mk_flv_recorder API_CALL mk_flv_recorder_create(){ return ret; } API_EXPORT void API_CALL mk_flv_recorder_release(mk_flv_recorder ctx){ + assert(ctx); FlvRecorder::Ptr *record = (FlvRecorder::Ptr *)(ctx); delete record; } API_EXPORT int API_CALL mk_flv_recorder_start(mk_flv_recorder ctx, const char *app, const char *stream, const char *file_path){ - DebugL << app << " " << stream << " " << file_path; - FlvRecorder::Ptr *record = (FlvRecorder::Ptr *)(ctx); + assert(ctx && app && stream && file_path); try { + FlvRecorder::Ptr *record = (FlvRecorder::Ptr *)(ctx); (*record)->startRecord(EventPollerPool::Instance().getPoller(), DEFAULT_VHOST,app,stream,file_path); return 0; }catch (std::exception &ex){ diff --git a/api/source/httpdownloader.cpp b/api/source/httpdownloader.cpp index 0f9c5004..4e742924 100755 --- a/api/source/httpdownloader.cpp +++ b/api/source/httpdownloader.cpp @@ -37,11 +37,13 @@ API_EXPORT mk_http_downloader API_CALL mk_http_downloader_create() { } API_EXPORT void API_CALL mk_http_downloader_release(mk_http_downloader ctx) { + assert(ctx); HttpDownloader::Ptr *obj = (HttpDownloader::Ptr *) ctx; delete obj; } API_EXPORT void API_CALL mk_http_downloader_start(mk_http_downloader ctx, const char *url, const char *file, on_mk_download_complete cb, void *user_data) { + assert(ctx && url && file); HttpDownloader::Ptr *obj = (HttpDownloader::Ptr *) ctx; (*obj)->setOnResult([cb, user_data](ErrCode code, const string &errMsg, const string &filePath) { if (cb) { diff --git a/api/source/media.cpp b/api/source/media.cpp index 1a1addb2..df488b51 100755 --- a/api/source/media.cpp +++ b/api/source/media.cpp @@ -32,17 +32,20 @@ using namespace std; using namespace toolkit; using namespace mediakit; -API_EXPORT mk_media API_CALL mk_media_create(const char *app, const char *stream, float duration, int hls_enabled, int mp4_enabled) { - DevChannel::Ptr *obj(new DevChannel::Ptr(new DevChannel(DEFAULT_VHOST, app, stream, duration, true, true, hls_enabled, mp4_enabled))); +API_EXPORT mk_media API_CALL mk_media_create(const char *vhost, const char *app, const char *stream, float duration, int hls_enabled, int mp4_enabled) { + assert(vhost && app && stream); + DevChannel::Ptr *obj(new DevChannel::Ptr(new DevChannel(vhost, app, stream, duration, true, true, hls_enabled, mp4_enabled))); return (mk_media) obj; } API_EXPORT void API_CALL mk_media_release(mk_media ctx) { + assert(ctx); DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; delete obj; } API_EXPORT void API_CALL mk_media_init_h264(mk_media ctx, int width, int height, int frameRate) { + assert(ctx); DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; VideoInfo info; info.iFrameRate = frameRate; @@ -52,6 +55,7 @@ API_EXPORT void API_CALL mk_media_init_h264(mk_media ctx, int width, int height, } API_EXPORT void API_CALL mk_media_init_h265(mk_media ctx, int width, int height, int frameRate) { + assert(ctx); DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; VideoInfo info; info.iFrameRate = frameRate; @@ -61,6 +65,7 @@ API_EXPORT void API_CALL mk_media_init_h265(mk_media ctx, int width, int height, } API_EXPORT void API_CALL mk_media_init_aac(mk_media ctx, int channel, int sample_bit, int sample_rate, int profile) { + assert(ctx); DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; AudioInfo info; info.iSampleRate = sample_rate; @@ -71,21 +76,25 @@ API_EXPORT void API_CALL mk_media_init_aac(mk_media ctx, int channel, int sample } API_EXPORT void API_CALL mk_media_input_h264(mk_media ctx, void *data, int len, uint32_t dts, uint32_t pts) { + assert(ctx && data && len > 0); DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; (*obj)->inputH264((char *) data, len, dts, pts); } API_EXPORT void API_CALL mk_media_input_h265(mk_media ctx, void *data, int len, uint32_t dts, uint32_t pts) { + assert(ctx && data && len > 0); DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; (*obj)->inputH265((char *) data, len, dts, pts); } API_EXPORT void API_CALL mk_media_input_aac(mk_media ctx, void *data, int len, uint32_t dts, int with_adts_header) { + assert(ctx && data && len > 0); DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; (*obj)->inputAAC((char *) data, len, dts, with_adts_header); } API_EXPORT void API_CALL mk_media_input_aac1(mk_media ctx, void *data, int len, uint32_t dts, void *adts) { + assert(ctx && data && len > 0 && adts); DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; (*obj)->inputAAC((char *) data, len, dts, (char *) adts); } diff --git a/api/source/player.cpp b/api/source/player.cpp index 2e60f7cd..6a320f0e 100755 --- a/api/source/player.cpp +++ b/api/source/player.cpp @@ -42,9 +42,7 @@ API_EXPORT void API_CALL mk_player_release(mk_player ctx) { } API_EXPORT void API_CALL mk_player_set_option(mk_player ctx,const char* key,const char *val){ - assert(ctx); - assert(key); - assert(val); + assert(ctx && key && val); MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); string key_str(key), val_str(val); player->getPoller()->async([key_str,val_str,player](){ @@ -53,8 +51,7 @@ API_EXPORT void API_CALL mk_player_set_option(mk_player ctx,const char* key,cons }); } API_EXPORT void API_CALL mk_player_play(mk_player ctx, const char *url) { - assert(ctx); - assert(url); + assert(ctx && url); MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); string url_str(url); player->getPoller()->async([url_str,player](){ @@ -82,8 +79,7 @@ API_EXPORT void API_CALL mk_player_seekto(mk_player ctx, float progress) { } static void mk_player_set_on_event(mk_player ctx, on_mk_play_event cb, void *user_data, int type) { - assert(ctx); - assert(cb); + assert(ctx && cb); MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); player->getPoller()->async([cb,user_data,type,player](){ //切换线程后再操作 @@ -108,8 +104,7 @@ API_EXPORT void API_CALL mk_player_set_on_shutdown(mk_player ctx, on_mk_play_eve } API_EXPORT void API_CALL mk_player_set_on_data(mk_player ctx, on_mk_play_data cb, void *user_data) { - assert(ctx); - assert(cb); + assert(ctx && cb); MediaPlayer::Ptr &player = *((MediaPlayer::Ptr *)ctx); player->getPoller()->async([player,cb,user_data](){ //切换线程后再操作 diff --git a/api/source/proxyplayer.cpp b/api/source/proxyplayer.cpp index d733fe01..0bf9d43a 100644 --- a/api/source/proxyplayer.cpp +++ b/api/source/proxyplayer.cpp @@ -30,18 +30,34 @@ using namespace toolkit; using namespace mediakit; -API_EXPORT mk_proxy_player API_CALL mk_proxy_player_create(const char *app, const char *stream, int rtp_type, int hls_enabled, int mp4_enabled) { - PlayerProxy::Ptr *obj(new PlayerProxy::Ptr(new PlayerProxy(DEFAULT_VHOST, app, stream, true, true, hls_enabled, mp4_enabled))); - (**obj)[Client::kRtpType] = rtp_type; +API_EXPORT mk_proxy_player API_CALL mk_proxy_player_create(const char *vhost, const char *app, const char *stream, int hls_enabled, int mp4_enabled) { + assert(vhost && app && stream); + PlayerProxy::Ptr *obj(new PlayerProxy::Ptr(new PlayerProxy(vhost, app, stream, true, true, hls_enabled, mp4_enabled))); return (mk_proxy_player) obj; } API_EXPORT void API_CALL mk_proxy_player_release(mk_proxy_player ctx) { + assert(ctx); PlayerProxy::Ptr *obj = (PlayerProxy::Ptr *) ctx; delete obj; } -API_EXPORT void API_CALL mk_proxy_player_play(mk_proxy_player ctx, const char *url) { - PlayerProxy::Ptr *obj = (PlayerProxy::Ptr *) ctx; - (*obj)->play(url); +API_EXPORT void API_CALL mk_proxy_player_set_option(mk_proxy_player ctx, const char *key, const char *val){ + assert(ctx && key && val); + PlayerProxy::Ptr &obj = *((PlayerProxy::Ptr *) ctx); + string key_str(key),val_str(val); + obj->getPoller()->async([obj,key_str,val_str](){ + //切换线程再操作 + (*obj)[key_str] = val_str; + }); +} + +API_EXPORT void API_CALL mk_proxy_player_play(mk_proxy_player ctx, const char *url) { + assert(ctx && url); + PlayerProxy::Ptr &obj = *((PlayerProxy::Ptr *) ctx); + string url_str(url); + obj->getPoller()->async([obj,url_str](){ + //切换线程再操作 + obj->play(url_str); + }); } From d342cc3a76660504cf7b51693f00c6a2a33290f9 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 18 Dec 2019 14:44:19 +0800 Subject: [PATCH 111/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0pusher=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/pusher.h | 100 ++++++++++++++++++++++++++++++++++++++++++ api/source/pusher.cpp | 84 +++++++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 api/include/pusher.h create mode 100644 api/source/pusher.cpp diff --git a/api/include/pusher.h b/api/include/pusher.h new file mode 100644 index 00000000..f234c1bb --- /dev/null +++ b/api/include/pusher.h @@ -0,0 +1,100 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#ifndef MK_PUSHER_H +#define MK_PUSHER_H + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* mk_pusher; + +/** + * 推流结果或推流中断事件的回调 + * @param user_data 用户数据指针 + * @param err_code 错误代码,0为成功 + * @param err_msg 错误提示 + */ +typedef void(API_CALL *on_mk_push_event)(void *user_data,int err_code,const char *err_msg); + +/** + * 绑定的MediaSource对象并创建rtmp[s]/rtsp[s]推流器 + * MediaSource通过mk_media_create或mk_proxy_player_create生成 + * 该MediaSource对象必须已注册 + * + * @param schema 绑定的MediaSource对象所属协议,支持rtsp/rtmp + * @param vhost 绑定的MediaSource对象的虚拟主机,一般为__defaultVhost__ + * @param app 绑定的MediaSource对象的应用名,一般为live + * @param stream 绑定的MediaSource对象的流id + * @return 对象指针 + */ +API_EXPORT mk_pusher API_CALL mk_pusher_create(const char *schema,const char *vhost,const char *app, const char *stream); + +/** + * 释放推流器 + * @param ctx 推流器指针 + */ +API_EXPORT void API_CALL mk_pusher_release(mk_pusher ctx); + +/** + * 设置推流器配置选项 + * @param ctx 推流器指针 + * @param key 配置项键,支持 net_adapter/rtp_type/rtsp_user/rtsp_pwd/protocol_timeout_ms/media_timeout_ms/beat_interval_ms/max_analysis_ms + * @param val 配置项值,如果是整形,需要转换成统一转换成string + */ +API_EXPORT void API_CALL mk_pusher_set_option(mk_pusher ctx, const char *key, const char *val); + +/** + * 开始推流 + * @param ctx 推流器指针 + * @param url 推流地址,支持rtsp[s]/rtmp[s] + */ +API_EXPORT void API_CALL mk_pusher_publish(mk_pusher ctx,const char *url); + +/** + * 设置推流器推流结果回调函数 + * @param ctx 推流器指针 + * @param cb 回调函数指针,不得为null + * @param user_data 用户数据指针 + */ +API_EXPORT void API_CALL mk_pusher_set_on_result(mk_pusher ctx, on_mk_push_event cb, void *user_data); + +/** + * 设置推流被异常中断的回调 + * @param ctx 推流器指针 + * @param cb 回调函数指针,不得为null + * @param user_data 用户数据指针 + */ +API_EXPORT void API_CALL mk_pusher_set_on_shutdown(mk_pusher ctx, on_mk_push_event cb, void *user_data); + +#ifdef __cplusplus +} +#endif +#endif //MK_PUSHER_H diff --git a/api/source/pusher.cpp b/api/source/pusher.cpp new file mode 100644 index 00000000..df74f03a --- /dev/null +++ b/api/source/pusher.cpp @@ -0,0 +1,84 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "pusher.h" +#include "Pusher/MediaPusher.h" +using namespace mediakit; + +API_EXPORT mk_pusher API_CALL mk_pusher_create(const char *schema,const char *vhost,const char *app, const char *stream){ + assert(schema && vhost && app && schema); + MediaPusher::Ptr *obj = new MediaPusher::Ptr(new MediaPusher(schema,vhost,app,stream)); + return obj; +} + +API_EXPORT void API_CALL mk_pusher_release(mk_pusher ctx){ + assert(ctx); + MediaPusher::Ptr *obj = (MediaPusher::Ptr *)ctx; + delete obj; +} + +API_EXPORT void API_CALL mk_pusher_set_option(mk_pusher ctx, const char *key, const char *val){ + assert(ctx && key && val); + MediaPusher::Ptr &obj = *((MediaPusher::Ptr *)ctx); + string key_str(key),val_str(val); + obj->getPoller()->async([obj,key_str,val_str](){ + //切换线程再操作 + (*obj)[key_str] = val_str; + }); +} + +API_EXPORT void API_CALL mk_pusher_publish(mk_pusher ctx,const char *url){ + assert(ctx && url); + MediaPusher::Ptr &obj = *((MediaPusher::Ptr *)ctx); + string url_str(url); + obj->getPoller()->async([obj,url_str](){ + //切换线程再操作 + obj->publish(url_str); + }); +} + +API_EXPORT void API_CALL mk_pusher_set_on_result(mk_pusher ctx, on_mk_push_event cb, void *user_data){ + assert(ctx && cb); + MediaPusher::Ptr &obj = *((MediaPusher::Ptr *)ctx); + obj->getPoller()->async([obj,cb,user_data](){ + //切换线程再操作 + obj->setOnPublished([cb,user_data](const SockException &ex){ + cb(user_data,ex.getErrCode(),ex.what()); + }); + }); +} + +API_EXPORT void API_CALL mk_pusher_set_on_shutdown(mk_pusher ctx, on_mk_push_event cb, void *user_data){ + assert(ctx && cb); + MediaPusher::Ptr &obj = *((MediaPusher::Ptr *)ctx); + obj->getPoller()->async([obj,cb,user_data](){ + //切换线程再操作 + obj->setOnShutdown([cb,user_data](const SockException &ex){ + cb(user_data,ex.getErrCode(),ex.what()); + }); + }); +} From be5d98b968c8735584246f48325e9f2d3962b44c Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 18 Dec 2019 14:53:42 +0800 Subject: [PATCH 112/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0mk=5Frtp=5Fserver=5Fs?= =?UTF-8?q?tart=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/common.h | 7 +++++++ api/include/httpdownloader.h | 1 - api/include/proxyplayer.h | 1 - api/source/common.cpp | 29 +++++++++++++++++++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/api/include/common.h b/api/include/common.h index 8a451d73..da39e557 100755 --- a/api/include/common.h +++ b/api/include/common.h @@ -102,6 +102,13 @@ API_EXPORT uint16_t API_CALL mk_rtsp_server_start(uint16_t port, int ssl); */ API_EXPORT uint16_t API_CALL mk_rtmp_server_start(uint16_t port, int ssl); +/** + * 创建rtp服务器 + * @param port rtp监听端口(包括udp/tcp) + * @return 0:失败,非0:端口号 + */ +API_EXPORT uint16_t API_CALL mk_rtp_server_start(uint16_t port); + /** * 打印日志 * @param level 日志级别,支持0~4 diff --git a/api/include/httpdownloader.h b/api/include/httpdownloader.h index 0354ec62..0e8f85fd 100755 --- a/api/include/httpdownloader.h +++ b/api/include/httpdownloader.h @@ -65,7 +65,6 @@ API_EXPORT void API_CALL mk_http_downloader_release(mk_http_downloader ctx); */ API_EXPORT void API_CALL mk_http_downloader_start(mk_http_downloader ctx, const char *url, const char *file, on_mk_download_complete cb, void *user_data); - #ifdef __cplusplus } #endif diff --git a/api/include/proxyplayer.h b/api/include/proxyplayer.h index d1230f7f..24652e2e 100644 --- a/api/include/proxyplayer.h +++ b/api/include/proxyplayer.h @@ -53,7 +53,6 @@ API_EXPORT mk_proxy_player API_CALL mk_proxy_player_create(const char *vhost, co */ API_EXPORT void API_CALL mk_proxy_player_release(mk_proxy_player ctx); - /** * 设置代理播放器配置选项 * @param ctx 代理播放器指针 diff --git a/api/source/common.cpp b/api/source/common.cpp index 8f6d79b6..0b566645 100755 --- a/api/source/common.cpp +++ b/api/source/common.cpp @@ -43,6 +43,13 @@ static TcpServer::Ptr rtsp_server[2]; static TcpServer::Ptr rtmp_server[2]; static TcpServer::Ptr http_server[2]; +#ifdef ENABLE_RTPPROXY +#include "Rtp/UdpRecver.h" +#include "Rtp/RtpSession.h" +static std::shared_ptr udpRtpServer; +static TcpServer::Ptr tcpRtpServer(new TcpServer()); +#endif + //////////////////////////environment init/////////////////////////// API_EXPORT void API_CALL mk_env_init(const config *cfg) { assert(cfg); @@ -129,6 +136,28 @@ API_EXPORT uint16_t API_CALL mk_rtmp_server_start(uint16_t port, int ssl) { } } +API_EXPORT uint16_t API_CALL mk_rtp_server_start(uint16_t port){ +#ifdef ENABLE_RTPPROXY + try { + //创建rtp tcp服务器 + tcpRtpServer->start(port); + auto ret = tcpRtpServer->getPort(); + udpRtpServer = std::make_shared(); + //创建rtp udp服务器 + udpRtpServer->initSock(port); + return ret; + } catch (std::exception &ex) { + tcpRtpServer.reset(); + udpRtpServer.reset(); + WarnL << ex.what(); + return 0; + } +#else + WarnL << "未启用该功能!"; + return 0; +#endif +} + API_EXPORT void API_CALL mk_log_printf(int level, const char *file, const char *function, int line, const char *fmt, ...) { assert(file && function && fmt); LogContextCapturer info(Logger::Instance(), (LogLevel) level, file, function, line); From 4e1876fab8bafdecf79106ebfefd1c47d425e34f Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 18 Dec 2019 14:54:47 +0800 Subject: [PATCH 113/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0mk=5Frtp=5Fserver=5Fs?= =?UTF-8?q?tart=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/source/common.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/api/source/common.cpp b/api/source/common.cpp index 0b566645..b0c4b02f 100755 --- a/api/source/common.cpp +++ b/api/source/common.cpp @@ -140,10 +140,12 @@ API_EXPORT uint16_t API_CALL mk_rtp_server_start(uint16_t port){ #ifdef ENABLE_RTPPROXY try { //创建rtp tcp服务器 + tcpRtpServer = std::make_shared(); tcpRtpServer->start(port); + + //创建rtp udp服务器 auto ret = tcpRtpServer->getPort(); udpRtpServer = std::make_shared(); - //创建rtp udp服务器 udpRtpServer->initSock(port); return ret; } catch (std::exception &ex) { From 5f0bbb3f1767342baa179703fbbc14e67e1417a0 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 18 Dec 2019 14:56:24 +0800 Subject: [PATCH 114/272] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/source/common.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/source/common.cpp b/api/source/common.cpp index b0c4b02f..36446d49 100755 --- a/api/source/common.cpp +++ b/api/source/common.cpp @@ -88,7 +88,7 @@ API_EXPORT void API_CALL mk_set_option(const char *key, const char *val) { API_EXPORT uint16_t API_CALL mk_http_server_start(uint16_t port, int ssl) { ssl = MAX(0,MIN(ssl,1)); try { - http_server[ssl].reset(new TcpServer()); + http_server[ssl] = std::make_shared(); if(ssl){ http_server[ssl]->start >(port); } else{ @@ -105,7 +105,7 @@ API_EXPORT uint16_t API_CALL mk_http_server_start(uint16_t port, int ssl) { API_EXPORT uint16_t API_CALL mk_rtsp_server_start(uint16_t port, int ssl) { ssl = MAX(0,MIN(ssl,1)); try { - rtsp_server[ssl].reset(new TcpServer()); + rtsp_server[ssl] = std::make_shared(); if(ssl){ rtsp_server[ssl]->start >(port); }else{ @@ -122,7 +122,7 @@ API_EXPORT uint16_t API_CALL mk_rtsp_server_start(uint16_t port, int ssl) { API_EXPORT uint16_t API_CALL mk_rtmp_server_start(uint16_t port, int ssl) { ssl = MAX(0,MIN(ssl,1)); try { - rtmp_server[ssl].reset(new TcpServer()); + rtmp_server[ssl] = std::make_shared(); if(ssl){ rtmp_server[ssl]->start >(port); }else{ From 73df1bb949e35e7efc846d956c5c0912df1dc1dc Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 18 Dec 2019 15:02:55 +0800 Subject: [PATCH 115/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=AE=89=E5=85=A8?= =?UTF-8?q?=E5=86=97=E4=BD=99=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/MP4Muxer.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Record/MP4Muxer.cpp b/src/Record/MP4Muxer.cpp index 53bd61ae..b1805db4 100644 --- a/src/Record/MP4Muxer.cpp +++ b/src/Record/MP4Muxer.cpp @@ -129,6 +129,10 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { WarnL << "不是AAC Track"; return; } + if(!aac_track->ready()){ + WarnL << "AAC Track未就绪"; + return; + } auto track_id = mov_writer_add_audio(_mov_writter.get(), MOV_OBJECT_AAC, aac_track->getAudioChannel(), @@ -148,6 +152,10 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { WarnL << "不是H264 Track"; return; } + if(!h264_track->ready()){ + WarnL << "H264 Track未就绪"; + return; + } struct mpeg4_avc_t avc; string sps_pps = string("\x00\x00\x00\x01", 4) + h264_track->getSps() + @@ -181,6 +189,10 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { WarnL << "不是H265 Track"; return; } + if(!h265_track->ready()){ + WarnL << "H265 Track未就绪"; + return; + } struct mpeg4_hevc_t hevc; string vps_sps_pps = string("\x00\x00\x00\x01", 4) + h265_track->getVps() + From f32f5df4eb384de51ea3e9ef65ef4e3037ddd4ee Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 18 Dec 2019 18:40:41 +0800 Subject: [PATCH 116/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dmp4=E5=BD=95=E5=88=B6?= =?UTF-8?q?=E5=B4=A9=E6=BA=83=E7=9A=84bug:#181?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/MP4Muxer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Record/MP4Muxer.cpp b/src/Record/MP4Muxer.cpp index b1805db4..5d7c68e9 100644 --- a/src/Record/MP4Muxer.cpp +++ b/src/Record/MP4Muxer.cpp @@ -157,7 +157,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { return; } - struct mpeg4_avc_t avc; + struct mpeg4_avc_t avc = {0}; string sps_pps = string("\x00\x00\x00\x01", 4) + h264_track->getSps() + string("\x00\x00\x00\x01", 4) + h264_track->getPps(); h264_annexbtomp4(&avc, sps_pps.data(), sps_pps.size(), NULL, 0, NULL, NULL); @@ -194,7 +194,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { return; } - struct mpeg4_hevc_t hevc; + struct mpeg4_hevc_t hevc = {0}; string vps_sps_pps = string("\x00\x00\x00\x01", 4) + h265_track->getVps() + string("\x00\x00\x00\x01", 4) + h265_track->getSps() + string("\x00\x00\x00\x01", 4) + h265_track->getPps(); From 15fbb3d0873e2fbba3de20ebbef9c87093f21bc6 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 19 Dec 2019 16:45:32 +0800 Subject: [PATCH 117/272] =?UTF-8?q?=E8=A7=84=E8=8C=83=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/common.h | 4 ++-- api/source/common.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/include/common.h b/api/include/common.h index da39e557..39198d55 100755 --- a/api/include/common.h +++ b/api/include/common.h @@ -63,13 +63,13 @@ typedef struct { const char *ssl; // 证书密码,可以为NULL const char *ssl_pwd; -} config; +} mk_config; /** * 初始化环境,调用该库前需要先调用此函数 * @param cfg 库运行相关参数 */ -API_EXPORT void API_CALL mk_env_init(const config *cfg); +API_EXPORT void API_CALL mk_env_init(const mk_config *cfg); /** * 设置配置项 diff --git a/api/source/common.cpp b/api/source/common.cpp index 36446d49..27cb2a80 100755 --- a/api/source/common.cpp +++ b/api/source/common.cpp @@ -51,7 +51,7 @@ static TcpServer::Ptr tcpRtpServer(new TcpServer()); #endif //////////////////////////environment init/////////////////////////// -API_EXPORT void API_CALL mk_env_init(const config *cfg) { +API_EXPORT void API_CALL mk_env_init(const mk_config *cfg) { assert(cfg); static onceToken token([&]() { Logger::Instance().add(std::make_shared("console", (LogLevel) cfg->log_level)); From 0b8c4e75e17b9e4b1f0814bf0ab5330f3bb46b06 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 20 Dec 2019 11:04:18 +0800 Subject: [PATCH 118/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0mk=5Fenv=5Finit1?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/common.h | 18 ++++++++++++++++++ api/source/common.cpp | 35 ++++++++++++++++++++++++++--------- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/api/include/common.h b/api/include/common.h index 39198d55..972188c2 100755 --- a/api/include/common.h +++ b/api/include/common.h @@ -71,6 +71,24 @@ typedef struct { */ API_EXPORT void API_CALL mk_env_init(const mk_config *cfg); +/** + * 基础类型参数版本的mk_env_init,为了方便其他语言调用 + */ +API_EXPORT void API_CALL mk_env_init1( // 线程数 + int thread_num, + // 日志级别,支持0~4 + int log_level, + // 配置文件是内容还是路径 + int ini_is_path, + // 配置文件内容或路径,可以为NULL + const char *ini, + // ssl证书是内容还是路径 + int ssl_is_path, + // ssl证书内容或路径,可以为NULL + const char *ssl, + // 证书密码,可以为NULL + const char *ssl_pwd); + /** * 设置配置项 * @param key 配置项名 diff --git a/api/source/common.cpp b/api/source/common.cpp index 27cb2a80..ef89390d 100755 --- a/api/source/common.cpp +++ b/api/source/common.cpp @@ -53,25 +53,42 @@ static TcpServer::Ptr tcpRtpServer(new TcpServer()); //////////////////////////environment init/////////////////////////// API_EXPORT void API_CALL mk_env_init(const mk_config *cfg) { assert(cfg); + mk_env_init1(cfg->thread_num, + cfg->log_level, + cfg->ini_is_path, + cfg->ini, + cfg->ssl_is_path, + cfg->ssl, + cfg->ssl_pwd); +} + +API_EXPORT void API_CALL mk_env_init1( int thread_num, + int log_level, + int ini_is_path, + const char *ini, + int ssl_is_path, + const char *ssl, + const char *ssl_pwd) { + static onceToken token([&]() { - Logger::Instance().add(std::make_shared("console", (LogLevel) cfg->log_level)); + Logger::Instance().add(std::make_shared("console", (LogLevel) log_level)); Logger::Instance().setWriter(std::make_shared()); - EventPollerPool::setPoolSize(cfg->thread_num); - WorkThreadPool::setPoolSize(cfg->thread_num); + EventPollerPool::setPoolSize(thread_num); + WorkThreadPool::setPoolSize(thread_num); - if (cfg->ini) { + if (ini && ini[0]) { //设置配置文件 - if (cfg->ini_is_path) { - mINI::Instance().parseFile(cfg->ini); + if (ini_is_path) { + mINI::Instance().parseFile(ini); } else { - mINI::Instance().parse(cfg->ini); + mINI::Instance().parse(ini); } } - if (cfg->ssl) { + if (ssl && ssl[0]) { //设置ssl证书 - SSL_Initor::Instance().loadCertificate(cfg->ssl, true, cfg->ssl_pwd ? cfg->ssl_pwd : "", cfg->ssl_is_path); + SSL_Initor::Instance().loadCertificate(ssl, true, ssl_pwd ? ssl_pwd : "", ssl_is_path); } }); } From 2da0c884a1db96a3a238dd1cecfc08a81a6974ac Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 23 Dec 2019 12:47:04 +0800 Subject: [PATCH 119/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0kBroadcastHttpBeforeA?= =?UTF-8?q?ccess=E4=BA=8B=E4=BB=B6=EF=BC=8C=E6=8B=A6=E6=88=AAhttp=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E4=B8=8B=E8=BD=BD=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- src/Common/config.cpp | 1 + src/Common/config.h | 7 ++++++- src/Http/HttpFileManager.cpp | 14 +++++++++----- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 2a7381ff..e1b29b7f 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 2a7381ff6dbe5a2f419957734425d5423f3850c6 +Subproject commit e1b29b7fa22e18bb0976a281268c0431a411e7ce diff --git a/src/Common/config.cpp b/src/Common/config.cpp index d4f08373..5cc44dd9 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -68,6 +68,7 @@ const string kBroadcastReloadConfig = "kBroadcastReloadConfig"; const string kBroadcastShellLogin = "kBroadcastShellLogin"; const string kBroadcastNotFoundStream = "kBroadcastNotFoundStream"; const string kBroadcastStreamNoneReader = "kBroadcastStreamNoneReader"; +const string kBroadcastHttpBeforeAccess = "kBroadcastHttpBeforeAccess"; } //namespace Broadcast //通用配置项目 diff --git a/src/Common/config.h b/src/Common/config.h index 8eb447b3..9e9b3c1e 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -83,10 +83,15 @@ extern const string kBroadcastRecordMP4; extern const string kBroadcastHttpRequest; #define BroadcastHttpRequestArgs const Parser &parser,const HttpSession::HttpResponseInvoker &invoker,bool &consumed,TcpSession &sender -//收到http 访问文件或目录的广播,通过该事件控制访问http目录的权限 +//在http文件服务器中,收到http访问文件或目录的广播,通过该事件控制访问http目录的权限 extern const string kBroadcastHttpAccess; #define BroadcastHttpAccessArgs const Parser &parser,const MediaInfo &args,const string &path,const bool &is_dir,const HttpSession::HttpAccessPathInvoker &invoker,TcpSession &sender +//在http文件服务器中,收到http访问文件或目录前的广播,通过该事件可以控制http url到文件路径的映射 +//在该事件中通过自行覆盖path参数,可以做到譬如根据虚拟主机或者app选择不同http根目录的目的 +extern const string kBroadcastHttpBeforeAccess; +#define BroadcastHttpBeforeAccessArgs const Parser &parser,const MediaInfo &args,string &path,TcpSession &sender + //该流是否需要认证?是的话调用invoker并传入realm,否则传入空的realm.如果该事件不监听则不认证 extern const string kBroadcastOnGetRtspRealm; #define BroadcastOnGetRtspRealmArgs const MediaInfo &args,const RtspSession::onGetRealm &invoker,TcpSession &sender diff --git a/src/Http/HttpFileManager.cpp b/src/Http/HttpFileManager.cpp index d44f736d..70fd75df 100644 --- a/src/Http/HttpFileManager.cpp +++ b/src/Http/HttpFileManager.cpp @@ -375,6 +375,14 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo }); } +static string getFilePath(const Parser &parser,const MediaInfo &mediaInfo, TcpSession &sender){ + GET_CONFIG(bool, enableVhost, General::kEnableVhost); + GET_CONFIG(string, rootPath, Http::kRootPath); + auto ret = File::absolutePath(enableVhost ? mediaInfo._vhost + parser.Url() : parser.Url(), rootPath); + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastHttpBeforeAccess, parser, mediaInfo, ret, sender); + return std::move(ret); +} + /** * 访问文件或文件夹 * @param sender 事件触发者 @@ -384,11 +392,7 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo void HttpFileManager::onAccessPath(TcpSession &sender, Parser &parser, const HttpFileManager::invoker &cb) { auto fullUrl = string(HTTP_SCHEMA) + "://" + parser["Host"] + parser.FullUrl(); MediaInfo mediaInfo(fullUrl); - - GET_CONFIG(bool, enableVhost, General::kEnableVhost); - GET_CONFIG(string, rootPath, Http::kRootPath); - auto strFile = File::absolutePath(enableVhost ? mediaInfo._vhost + parser.Url() : parser.Url(), rootPath); - + auto strFile = getFilePath(parser, mediaInfo, sender); //访问的是文件夹 if (File::is_dir(strFile.data())) { auto indexFile = searchIndexFile(strFile); From 6796d0b59231874271e7627f4453d6bff91c4936 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 23 Dec 2019 14:01:00 +0800 Subject: [PATCH 120/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0c=20api=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E8=8C=83=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/CMakeLists.txt | 2 ++ api/source/common.cpp | 2 +- api/tests/CMakeLists.txt | 25 +++++++++++++++++++++++++ api/tests/server.cpp | 21 +++++++++++++++++++++ 4 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 api/tests/CMakeLists.txt create mode 100644 api/tests/server.cpp diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt index 03901c12..3cea8196 100644 --- a/api/CMakeLists.txt +++ b/api/CMakeLists.txt @@ -7,5 +7,7 @@ if(WIN32) endif() target_link_libraries(api ${LINK_LIB_LIST}) +add_subdirectory(tests) + diff --git a/api/source/common.cpp b/api/source/common.cpp index ef89390d..6dfc99fb 100755 --- a/api/source/common.cpp +++ b/api/source/common.cpp @@ -47,7 +47,7 @@ static TcpServer::Ptr http_server[2]; #include "Rtp/UdpRecver.h" #include "Rtp/RtpSession.h" static std::shared_ptr udpRtpServer; -static TcpServer::Ptr tcpRtpServer(new TcpServer()); +static TcpServer::Ptr tcpRtpServer; #endif //////////////////////////environment init/////////////////////////// diff --git a/api/tests/CMakeLists.txt b/api/tests/CMakeLists.txt new file mode 100644 index 00000000..02c6cdd6 --- /dev/null +++ b/api/tests/CMakeLists.txt @@ -0,0 +1,25 @@ +aux_source_directory(. TEST_SRC_LIST) +foreach(TEST_SRC ${TEST_SRC_LIST}) + STRING(REGEX REPLACE "^\\./|\\.c[a-zA-Z0-9_]*$" "" TEST_EXE_NAME ${TEST_SRC}) + message(STATUS "add c api tester:${TEST_EXE_NAME}") + set(exe_name api_tester_${TEST_EXE_NAME}) + add_executable(${exe_name} ${TEST_SRC}) + + if(WIN32) + set_target_properties(${exe_name} PROPERTIES COMPILE_FLAGS ${VS_FALGS} ) + endif(WIN32) + + target_link_libraries(${exe_name} api) +endforeach() + + + + + + + + + + + + diff --git a/api/tests/server.cpp b/api/tests/server.cpp new file mode 100644 index 00000000..10bad23e --- /dev/null +++ b/api/tests/server.cpp @@ -0,0 +1,21 @@ +// +// Created by xzl on 2019/12/23. +// + +#include +#include "mediakit.h" +#include "unistd.h" +int main(int argc,char *argv[]){ + mk_env_init1(0,0,0, nullptr,0, nullptr, nullptr); + mk_http_server_start(80,false); + mk_rtsp_server_start(554,false); + mk_rtmp_server_start(1935,false); + mk_rtp_server_start(10000); + + static bool flag = true; + signal(SIGINT, [](int) { flag = false; });// 设置退出信号 + + while (flag){ + sleep(1); + } +} \ No newline at end of file From 92bc4fbbb15537cf81b0df3a5aee026ed0986733 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 23 Dec 2019 14:20:49 +0800 Subject: [PATCH 121/272] =?UTF-8?q?c=20api=E6=B5=8B=E8=AF=95=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E9=98=B2=E6=AD=A2=E9=80=80=E5=87=BA=E6=97=B6=E5=B4=A9?= =?UTF-8?q?=E6=BA=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- api/include/common.h | 5 +++++ api/source/common.cpp | 8 ++++++++ api/tests/server.cpp | 4 ++-- src/Record/Recorder.cpp | 11 +++++++---- 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index e1b29b7f..4d88a395 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit e1b29b7fa22e18bb0976a281268c0431a411e7ce +Subproject commit 4d88a395a25e9265c579aa930acdf60796bd4e4c diff --git a/api/include/common.h b/api/include/common.h index 972188c2..033bc077 100755 --- a/api/include/common.h +++ b/api/include/common.h @@ -71,6 +71,11 @@ typedef struct { */ API_EXPORT void API_CALL mk_env_init(const mk_config *cfg); +/** + * 关闭所有服务器,请在main函数退出时调用 + */ +API_EXPORT void API_CALL mk_stop_all_server(); + /** * 基础类型参数版本的mk_env_init,为了方便其他语言调用 */ diff --git a/api/source/common.cpp b/api/source/common.cpp index 6dfc99fb..78a0f849 100755 --- a/api/source/common.cpp +++ b/api/source/common.cpp @@ -62,6 +62,14 @@ API_EXPORT void API_CALL mk_env_init(const mk_config *cfg) { cfg->ssl_pwd); } +API_EXPORT void API_CALL mk_stop_all_server(){ + CLEAR_ARR(rtsp_server); + CLEAR_ARR(rtmp_server); + CLEAR_ARR(http_server); + udpRtpServer = nullptr; + tcpRtpServer = nullptr; +} + API_EXPORT void API_CALL mk_env_init1( int thread_num, int log_level, int ini_is_path, diff --git a/api/tests/server.cpp b/api/tests/server.cpp index 10bad23e..8a188905 100644 --- a/api/tests/server.cpp +++ b/api/tests/server.cpp @@ -11,11 +11,11 @@ int main(int argc,char *argv[]){ mk_rtsp_server_start(554,false); mk_rtmp_server_start(1935,false); mk_rtp_server_start(10000); - static bool flag = true; signal(SIGINT, [](int) { flag = false; });// 设置退出信号 - while (flag){ sleep(1); } + mk_stop_all_server(); + return 0; } \ No newline at end of file diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index 42d145e3..43baa0e4 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -217,21 +217,23 @@ public: private: MediaSourceWatcher(){ - NoticeCenter::Instance().addListener(this,Broadcast::kBroadcastMediaChanged,[this](BroadcastMediaChangedArgs){ + //保存NoticeCenter的强引用,防止在MediaSourceWatcher单例释放前释放NoticeCenter单例 + _notice_center = NoticeCenter::Instance().shared_from_this(); + _notice_center->addListener(this,Broadcast::kBroadcastMediaChanged,[this](BroadcastMediaChangedArgs){ if(bRegist){ onRegist(schema,vhost,app,stream,sender); }else{ onUnRegist(schema,vhost,app,stream,sender); } }); - NoticeCenter::Instance().addListener(this,Broadcast::kBroadcastMediaResetTracks,[this](BroadcastMediaResetTracksArgs){ + _notice_center->addListener(this,Broadcast::kBroadcastMediaResetTracks,[this](BroadcastMediaResetTracksArgs){ onRegist(schema,vhost,app,stream,sender); }); } ~MediaSourceWatcher(){ - NoticeCenter::Instance().delListener(this,Broadcast::kBroadcastMediaChanged); - NoticeCenter::Instance().delListener(this,Broadcast::kBroadcastMediaResetTracks); + _notice_center->delListener(this,Broadcast::kBroadcastMediaChanged); + _notice_center->delListener(this,Broadcast::kBroadcastMediaResetTracks); } void onRegist(const string &schema,const string &vhost,const string &app,const string &stream,MediaSource &sender){ @@ -320,6 +322,7 @@ private: } private: recursive_mutex _recorder_mtx; + NoticeCenter::Ptr _notice_center; unordered_map _recorder_map; }; From c72dd5b4c539735b5ff203f870788e846b9905d7 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 23 Dec 2019 14:31:02 +0800 Subject: [PATCH 122/272] =?UTF-8?q?=E9=80=9A=E8=BF=87windows=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/tests/server.cpp | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/api/tests/server.cpp b/api/tests/server.cpp index 8a188905..7b11a0df 100644 --- a/api/tests/server.cpp +++ b/api/tests/server.cpp @@ -1,10 +1,36 @@ -// -// Created by xzl on 2019/12/23. -// +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ #include #include "mediakit.h" +#ifdef _WIN32 +#include "windows.h" +#else #include "unistd.h" +#endif int main(int argc,char *argv[]){ mk_env_init1(0,0,0, nullptr,0, nullptr, nullptr); mk_http_server_start(80,false); @@ -14,7 +40,11 @@ int main(int argc,char *argv[]){ static bool flag = true; signal(SIGINT, [](int) { flag = false; });// 设置退出信号 while (flag){ +#ifdef _WIN32 + Sleep(1000); +#else sleep(1); +#endif } mk_stop_all_server(); return 0; From 756d10aa55c686b1ff191d0ce29022ccb9a9767f Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 23 Dec 2019 15:31:35 +0800 Subject: [PATCH 123/272] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/common.h | 18 +++++++++--------- api/source/common.cpp | 7 ++++++- api/tests/server.cpp | 5 ++++- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/api/include/common.h b/api/include/common.h index 033bc077..041968c4 100755 --- a/api/include/common.h +++ b/api/include/common.h @@ -54,7 +54,7 @@ typedef struct { // 配置文件是内容还是路径 int ini_is_path; - // 配置文件内容或路径,可以为NULL + // 配置文件内容或路径,可以为NULL,如果该文件不存在,那么将导出默认配置至该文件 const char *ini; // ssl证书是内容还是路径 @@ -78,20 +78,20 @@ API_EXPORT void API_CALL mk_stop_all_server(); /** * 基础类型参数版本的mk_env_init,为了方便其他语言调用 + * @param thread_num 线程数 + * @param log_level 日志级别,支持0~4 + * @param ini_is_path 配置文件是内容还是路径 + * @param ini 配置文件内容或路径,可以为NULL,如果该文件不存在,那么将导出默认配置至该文件 + * @param ssl_is_path ssl证书是内容还是路径 + * @param ssl ssl证书内容或路径,可以为NULL + * @param ssl_pwd 证书密码,可以为NULL */ -API_EXPORT void API_CALL mk_env_init1( // 线程数 - int thread_num, - // 日志级别,支持0~4 +API_EXPORT void API_CALL mk_env_init1( int thread_num, int log_level, - // 配置文件是内容还是路径 int ini_is_path, - // 配置文件内容或路径,可以为NULL const char *ini, - // ssl证书是内容还是路径 int ssl_is_path, - // ssl证书内容或路径,可以为NULL const char *ssl, - // 证书密码,可以为NULL const char *ssl_pwd); /** diff --git a/api/source/common.cpp b/api/source/common.cpp index 78a0f849..287559d3 100755 --- a/api/source/common.cpp +++ b/api/source/common.cpp @@ -88,7 +88,12 @@ API_EXPORT void API_CALL mk_env_init1( int thread_num, if (ini && ini[0]) { //设置配置文件 if (ini_is_path) { - mINI::Instance().parseFile(ini); + try{ + mINI::Instance().parseFile(ini); + }catch (std::exception &ex) { + InfoL << "dump ini file to:" << ini; + mINI::Instance().dumpFile(ini); + } } else { mINI::Instance().parse(ini); } diff --git a/api/tests/server.cpp b/api/tests/server.cpp index 7b11a0df..514f4941 100644 --- a/api/tests/server.cpp +++ b/api/tests/server.cpp @@ -25,14 +25,17 @@ */ #include +#include #include "mediakit.h" #ifdef _WIN32 #include "windows.h" #else #include "unistd.h" #endif + +using namespace std; int main(int argc,char *argv[]){ - mk_env_init1(0,0,0, nullptr,0, nullptr, nullptr); + mk_env_init1(0,0,1, (string(argv[0]) + ".ini").data(),0, nullptr, nullptr); mk_http_server_start(80,false); mk_rtsp_server_start(554,false); mk_rtmp_server_start(1935,false); From 40c232695051ec6fd8e4064a39c590b8e2316eec Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 23 Dec 2019 18:50:46 +0800 Subject: [PATCH 124/272] =?UTF-8?q?=E5=AE=8C=E5=96=84c=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/event_objects.h | 235 ++++++++++++++++++++++++ api/include/mediakit.h | 1 + api/include/mk_event.h | 150 ++++++++++++++++ api/source/event_objects.cpp | 333 +++++++++++++++++++++++++++++++++++ api/source/mk_event.cpp | 27 +++ src/Record/MP4Recorder.h | 7 +- 6 files changed, 749 insertions(+), 4 deletions(-) create mode 100644 api/include/event_objects.h create mode 100644 api/include/mk_event.h create mode 100644 api/source/event_objects.cpp create mode 100644 api/source/mk_event.cpp diff --git a/api/include/event_objects.h b/api/include/event_objects.h new file mode 100644 index 00000000..be30b787 --- /dev/null +++ b/api/include/event_objects.h @@ -0,0 +1,235 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MK_EVENT_OBJECTS_H +#define MK_EVENT_OBJECTS_H +#include "common.h" +#ifdef __cplusplus +extern "C" { +#endif + +///////////////////////////////////////////MP4Info///////////////////////////////////////////// +//MP4Info对象的C映射 +typedef void *mk_mp4_info; +//MP4Info::ui64StartedTime +API_EXPORT uint64_t API_CALL mk_mp4_info_get_start_time(const mk_mp4_info ctx); +//MP4Info::ui64TimeLen +API_EXPORT uint64_t API_CALL mk_mp4_info_get_time_len(const mk_mp4_info ctx); +//MP4Info::ui64FileSize +API_EXPORT uint64_t API_CALL mk_mp4_info_get_file_size(const mk_mp4_info ctx); +//MP4Info::strFilePath +API_EXPORT const char* API_CALL mk_mp4_info_get_file_path(const mk_mp4_info ctx); +//MP4Info::strFileName +API_EXPORT const char* API_CALL mk_mp4_info_get_file_name(const mk_mp4_info ctx); +//MP4Info::strFolder +API_EXPORT const char* API_CALL mk_mp4_info_get_folder(const mk_mp4_info ctx); +//MP4Info::strUrl +API_EXPORT const char* API_CALL mk_mp4_info_get_url(const mk_mp4_info ctx); +//MP4Info::strVhost +API_EXPORT const char* API_CALL mk_mp4_info_get_vhost(const mk_mp4_info ctx); +//MP4Info::strAppName +API_EXPORT const char* API_CALL mk_mp4_info_get_app(const mk_mp4_info ctx); +//MP4Info::strStreamId +API_EXPORT const char* API_CALL mk_mp4_info_get_stream(const mk_mp4_info ctx); + +///////////////////////////////////////////Parser///////////////////////////////////////////// +//Parser对象的C映射 +typedef void* mk_parser; +//Parser::Method(),获取命令字,譬如GET/POST +API_EXPORT const char* API_CALL mk_parser_get_method(const mk_parser ctx); +//Parser::Url(),获取HTTP的访问url(不包括?后面的参数) +API_EXPORT const char* API_CALL mk_parser_get_url(const mk_parser ctx); +//Parser::FullUrl(),包括?后面的参数 +API_EXPORT const char* API_CALL mk_parser_get_full_url(const mk_parser ctx); +//Parser::Params(),?后面的参数字符串 +API_EXPORT const char* API_CALL mk_parser_get_url_params(const mk_parser ctx); +//Parser::getUrlArgs()["key"],获取?后面的参数中的特定参数 +API_EXPORT const char* API_CALL mk_parser_get_url_param(const mk_parser ctx,const char *key); +//Parser::Tail(),获取协议相关信息,譬如 HTTP/1.1 +API_EXPORT const char* API_CALL mk_parser_get_tail(const mk_parser ctx); +//Parser::getValues()["key"],获取HTTP头中特定字段 +API_EXPORT const char* API_CALL mk_parser_get_header(const mk_parser ctx,const char *key); +//Parser::Content(),获取HTTP body +API_EXPORT const char* API_CALL mk_parser_get_content(const mk_parser ctx); + +///////////////////////////////////////////MediaInfo///////////////////////////////////////////// +//MediaInfo对象的C映射 +typedef void* mk_media_info; +//MediaInfo::_param_strs +API_EXPORT const char* API_CALL mk_media_info_get_params(const mk_media_info ctx); +//MediaInfo["key"] +API_EXPORT const char* API_CALL mk_media_info_get_param(const mk_media_info ctx,const char *key); +//MediaInfo::_schema +API_EXPORT const char* API_CALL mk_media_info_get_schema(const mk_media_info ctx); +//MediaInfo::_vhost +API_EXPORT const char* API_CALL mk_media_info_get_vhost(const mk_media_info ctx); +//MediaInfo::_app +API_EXPORT const char* API_CALL mk_media_info_get_app(const mk_media_info ctx); +//MediaInfo::_streamid +API_EXPORT const char* API_CALL mk_media_info_get_stream(const mk_media_info ctx); + + +///////////////////////////////////////////MediaSource///////////////////////////////////////////// +//MediaSource对象的C映射 +typedef void* mk_media_source; +//查找MediaSource的回调函数 +typedef void(API_CALL *on_mk_media_source_find_cb)(void *user_data, const mk_media_source ctx); + +//MediaSource::getSchema() +API_EXPORT const char* API_CALL mk_media_source_get_schema(const mk_media_source ctx); +//MediaSource::getVhost() +API_EXPORT const char* API_CALL mk_media_source_get_vhost(const mk_media_source ctx); +//MediaSource::getApp() +API_EXPORT const char* API_CALL mk_media_source_get_app(const mk_media_source ctx); +//MediaSource::getId() +API_EXPORT const char* API_CALL mk_media_source_get_stream(const mk_media_source ctx); +//MediaSource::readerCount() +API_EXPORT int API_CALL mk_media_source_get_reader_count(const mk_media_source ctx); +//MediaSource::close() +API_EXPORT int API_CALL mk_media_source_close(const mk_media_source ctx,int force); +//MediaSource::seekTo() +API_EXPORT int API_CALL mk_media_source_seek_to(const mk_media_source ctx,uint32_t stamp); + +//MediaSource::find() +API_EXPORT void API_CALL mk_media_source_find(const char *schema, + const char *vhost, + const char *app, + const char *stream, + void *user_data, + on_mk_media_source_find_cb cb); +//MediaSource::for_each_media() +API_EXPORT void API_CALL mk_media_source_for_each(void *user_data, on_mk_media_source_find_cb cb); + +///////////////////////////////////////////TcpSession///////////////////////////////////////////// +//TcpSession对象的C映射 +typedef void* mk_tcp_session; +//TcpSession::safeShutdown() +API_EXPORT void API_CALL mk_tcp_session_shutdown(const mk_tcp_session ctx,int err,const char *err_msg); +//TcpSession::get_peer_ip() +API_EXPORT const char* API_CALL mk_tcp_session_peer_ip(const mk_tcp_session ctx); +//TcpSession::get_local_ip() +API_EXPORT const char* API_CALL mk_tcp_session_local_ip(const mk_tcp_session ctx); +//TcpSession::get_peer_port() +API_EXPORT uint16_t API_CALL mk_tcp_session_peer_port(const mk_tcp_session ctx); +//TcpSession::get_local_port() +API_EXPORT uint16_t API_CALL mk_tcp_session_local_port(const mk_tcp_session ctx); + +///////////////////////////////////////////HttpResponseInvoker///////////////////////////////////////////// +//HttpSession::HttpResponseInvoker对象的C映射 +typedef void* mk_http_response_invoker; + +/** + * HttpSession::HttpResponseInvoker(const string &codeOut, const StrCaseMap &headerOut, const string &body); + * @param response_code 譬如200 OK + * @param response_header 返回的http头,譬如 {"Content-Type","text/html",NULL} 必须以NULL结尾 + * @param response_content 返回的content部分,譬如一个网页内容 + */ +API_EXPORT void API_CALL mk_http_response_invoker_do(const mk_http_response_invoker ctx, + const char *response_code, + const char *response_header[], + const char *response_content); +/** + * HttpSession::HttpResponseInvoker(const StrCaseMap &requestHeader,const StrCaseMap &responseHeader,const string &filePath); + * @param request_parser 请求事件中的mk_parser对象,用于提取其中http头中的Range字段 + * @param response_header 返回的http头,譬如 {"Content-Type","text/html",NULL} 必须以NULL结尾 + * @param response_file_path 返回的content部分,譬如/path/to/html/file + */ +API_EXPORT void API_CALL mk_http_response_invoker_do_file(const mk_http_response_invoker ctx, + const mk_parser request_parser, + const char *response_header[], + const char *response_file_path); + + +///////////////////////////////////////////HttpAccessPathInvoker///////////////////////////////////////////// +//HttpSession::HttpAccessPathInvoker对象的C映射 +typedef void* mk_http_access_path_invoker; +/** + * HttpSession::HttpAccessPathInvoker(const string &errMsg,const string &accessPath, int cookieLifeSecond); + * @param err_msg 如果为空,则代表鉴权通过,否则为错误提示,可以为null + * @param access_path 运行或禁止访问的根目录,可以为null + * @param cookie_life_second 鉴权cookie有效期 + **/ +API_EXPORT void API_CALL mk_http_access_path_invoker_do(const mk_http_access_path_invoker ctx, + const char *err_msg, + const char *access_path, + int cookie_life_second); + +///////////////////////////////////////////RtspSession::onGetRealm///////////////////////////////////////////// +//RtspSession::onGetRealm对象的C映射 +typedef void* mk_rtsp_get_realm_invoker; +/** + * 执行RtspSession::onGetRealm + * @param realm 该rtsp流是否需要开启rtsp专属鉴权,至null或空字符串则不鉴权 + */ +API_EXPORT void API_CALL mk_rtsp_get_realm_invoker_do(const mk_rtsp_get_realm_invoker ctx, + const char *realm); + +///////////////////////////////////////////RtspSession::onAuth///////////////////////////////////////////// +//RtspSession::onAuth对象的C映射 +typedef void* mk_rtsp_auth_invoker; + +/** + * 执行RtspSession::onAuth + * @param encrypted 为true是则表明是md5加密的密码,否则是明文密码, 在请求明文密码时如果提供md5密码者则会导致认证失败 + * @param pwd_or_md5 明文密码或者md5加密的密码 + */ +API_EXPORT void API_CALL mk_rtsp_auth_invoker_do(const mk_rtsp_auth_invoker ctx, + int encrypted, + const char *pwd_or_md5); + + +///////////////////////////////////////////Broadcast::PublishAuthInvoker///////////////////////////////////////////// +//Broadcast::PublishAuthInvoker对象的C映射 +typedef void* mk_publish_auth_invoker; + +/** + * 执行Broadcast::PublishAuthInvoker + * @param err_msg 为空或null则代表鉴权成功 + * @param enable_rtxp rtmp推流时是否运行转rtsp;rtsp推流时,是否允许转rtmp + * @param enable_hls 是否允许转换hls + * @param enable_mp4 是否运行MP4录制 + */ +API_EXPORT void API_CALL mk_publish_auth_invoker_do(const mk_publish_auth_invoker ctx, + const char *err_msg, + int enable_rtxp, + int enable_hls, + int enable_mp4); + +///////////////////////////////////////////Broadcast::AuthInvoker///////////////////////////////////////////// +//Broadcast::AuthInvoker对象的C映射 +typedef void* mk_auth_invoker; + +/** + * 执行Broadcast::AuthInvoker + * @param err_msg 为空或null则代表鉴权成功 + */ +API_EXPORT void API_CALL mk_auth_invoker_do(const mk_auth_invoker ctx, const char *err_msg); + + +#ifdef __cplusplus +} +#endif +#endif //MK_EVENT_OBJECTS_H diff --git a/api/include/mediakit.h b/api/include/mediakit.h index 2cbcc442..5886f9a5 100755 --- a/api/include/mediakit.h +++ b/api/include/mediakit.h @@ -34,5 +34,6 @@ #include "flvrecorder.h" #include "player.h" #include "pusher.h" +#include "mk_event.h" #endif /* MK_API_H_ */ diff --git a/api/include/mk_event.h b/api/include/mk_event.h new file mode 100644 index 00000000..303af64f --- /dev/null +++ b/api/include/mk_event.h @@ -0,0 +1,150 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MK_EVENT_H +#define MK_EVENT_H + +#include "common.h" +#include "event_objects.h" +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct{ + /** + * 注册或反注册MediaSource事件广播 + * @param regist 注册为1,注销为0 + * @param sender 该MediaSource对象 + */ + typedef void (*on_mk_media_changed)(int regist,mk_media_source sender); + + /** + * 收到rtsp/rtmp推流事件广播,通过该事件控制推流鉴权 + * @see mk_publish_auth_invoker_do + * @param url_info 推流url相关信息 + * @param invoker 执行invoker返回鉴权结果 + * @param sender 该tcp客户端相关信息 + */ + typedef void (*on_mk_media_publish)(mk_media_info url_info,mk_publish_auth_invoker invoker,mk_tcp_session sender); + + /** + * 播放rtsp/rtmp/http-flv/hls事件广播,通过该事件控制播放鉴权 + * @see mk_auth_invoker_do + * @param url_info 播放url相关信息 + * @param invoker 执行invoker返回鉴权结果 + * @param sender 播放客户端相关信息 + */ + typedef void (*on_mk_media_play)(mk_media_info url_info,mk_auth_invoker invoker,mk_tcp_session sender); + + /** + * 未找到流后会广播该事件,请在监听该事件后去拉流或其他方式产生流,这样就能按需拉流了 + * @param url_info 播放url相关信息 + * @param sender 播放客户端相关信息 + */ + typedef void (*on_mk_media_not_found)(mk_media_info url_info,mk_tcp_session sender); + + /** + * 某个流无人消费时触发,目的为了实现无人观看时主动断开拉流等业务逻辑 + * @param sender 该MediaSource对象 + */ + typedef void (*on_mk_media_no_reader)(mk_media_source sender); + + /** + * 收到http api请求广播(包括GET/POST) + * @param parser http请求内容对象 + * @param invoker 执行该invoker返回http回复 + * @param consumed 置1则说明我们要处理该事件 + * @param sender http客户端相关信息 + */ + typedef void (*on_mk_http_request)(mk_parser parser, mk_http_response_invoker invoker, int *consumed,mk_tcp_session sender); + + /** + * 在http文件服务器中,收到http访问文件或目录的广播,通过该事件控制访问http目录的权限 + * @param parser http请求内容对象 + * @param url_info 请求url相关信息 + * @param path 文件绝对路径 + * @param is_dir path是否为文件夹 + * @param invoker 执行invoker返回本次访问文件的结果 + * @param sender http客户端相关信息 + */ + typedef void (*on_mk_http_access)(mk_parser parser, mk_media_info url_info, const char *path,int is_dir, mk_http_access_path_invoker invoker,mk_tcp_session sender); + + /** + * 在http文件服务器中,收到http访问文件或目录前的广播,通过该事件可以控制http url到文件路径的映射 + * 在该事件中通过自行覆盖path参数,可以做到譬如根据虚拟主机或者app选择不同http根目录的目的 + * @param parser http请求内容对象 + * @param url_info 请求url相关信息 + * @param path 文件绝对路径,覆盖之可以重定向到其他文件 + * @param sender http客户端相关信息 + */ + typedef void (*on_mk_http_before_access)(mk_parser parser, mk_media_info url_info,char *path, mk_tcp_session sender); + + /** + * 该rtsp流是否需要认证?是的话调用invoker并传入realm,否则传入空的realm + * @param url_info 请求rtsp url相关信息 + * @param invoker 执行invoker返回是否需要rtsp专属认证 + * @param sender rtsp客户端相关信息 + */ + typedef void (*on_mk_rtsp_get_realm)(mk_media_info url_info, mk_rtsp_get_realm_invoker invoker, mk_tcp_session sender); + + /** + * 请求认证用户密码事件,user_name为用户名,must_no_encrypt如果为true,则必须提供明文密码(因为此时是base64认证方式),否则会导致认证失败 + * 获取到密码后请调用invoker并输入对应类型的密码和密码类型,invoker执行时会匹配密码 + * @param url_info 请求rtsp url相关信息 + * @param realm rtsp认证realm + * @param user_name rtsp认证用户名 + * @param must_no_encrypt 如果为true,则必须提供明文密码(因为此时是base64认证方式),否则会导致认证失败 + * @param invoker 执行invoker返回rtsp专属认证的密码 + * @param sender rtsp客户端信息 + */ + typedef void (*on_mk_rtsp_auth)(mk_media_info url_info,const char *realm,const char *user_name, int must_no_encrypt, mk_rtsp_auth_invoker invoker, mk_tcp_session sender); + + /** + * 录制mp4分片文件成功后广播 + */ + typedef void (*on_mk_record_mp4)(mk_mp4_info mp4); + + /** + * shell登录鉴权 + */ + typedef void (*on_mk_shell_login_mp4)(const char *user_name, const char *passwd,mk_auth_invoker invoker,mk_tcp_session sender); + + /** + * 停止rtsp/rtmp/http-flv会话后流量汇报事件广播 + * @param url_info 播放url相关信息 + * @param total_bytes 耗费上下行总流量,单位字节数 + * @param total_seconds 本次tcp会话时长,单位秒 + * @param is_player 客户端是否为播放器 + */ + typedef void (*on_mk_flow_report)(mk_media_info url_info,uint64_t total_bytes,uint64_t total_seconds,int is_player,mk_tcp_session sender); +} mk_events; + + +#ifdef __cplusplus +} +#endif +#endif //MK_EVENT_H + diff --git a/api/source/event_objects.cpp b/api/source/event_objects.cpp new file mode 100644 index 00000000..15f165db --- /dev/null +++ b/api/source/event_objects.cpp @@ -0,0 +1,333 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include "event_objects.h" +#include "Common/config.h" +#include "Record/MP4Recorder.h" +#include "Network/TcpSession.h" +#include "Http/HttpSession.h" +#include "Rtsp/RtspSession.h" +using namespace mediakit; + +///////////////////////////////////////////MP4Info///////////////////////////////////////////// +API_EXPORT uint64_t API_CALL mk_mp4_info_get_start_time(const mk_mp4_info ctx){ + assert(ctx); + MP4Info *info = (MP4Info *)ctx; + return info->ui64StartedTime; +} + +API_EXPORT uint64_t API_CALL mk_mp4_info_get_time_len(const mk_mp4_info ctx){ + assert(ctx); + MP4Info *info = (MP4Info *)ctx; + return info->ui64TimeLen; +} + +API_EXPORT uint64_t API_CALL mk_mp4_info_get_file_size(const mk_mp4_info ctx){ + assert(ctx); + MP4Info *info = (MP4Info *)ctx; + return info->ui64FileSize; +} + +API_EXPORT const char* API_CALL mk_mp4_info_get_file_path(const mk_mp4_info ctx){ + assert(ctx); + MP4Info *info = (MP4Info *)ctx; + return info->strFilePath.c_str(); +} + +API_EXPORT const char* API_CALL mk_mp4_info_get_file_name(const mk_mp4_info ctx){ + assert(ctx); + MP4Info *info = (MP4Info *)ctx; + return info->strFileName.c_str(); +} + +API_EXPORT const char* API_CALL mk_mp4_info_get_folder(const mk_mp4_info ctx){ + assert(ctx); + MP4Info *info = (MP4Info *)ctx; + return info->strFolder.c_str(); +} + +API_EXPORT const char* API_CALL mk_mp4_info_get_url(const mk_mp4_info ctx){ + assert(ctx); + MP4Info *info = (MP4Info *)ctx; + return info->strUrl.c_str(); +} + +API_EXPORT const char* API_CALL mk_mp4_info_get_vhost(const mk_mp4_info ctx){ + assert(ctx); + MP4Info *info = (MP4Info *)ctx; + return info->strVhost.c_str(); +} + +API_EXPORT const char* API_CALL mk_mp4_info_get_app(const mk_mp4_info ctx){ + assert(ctx); + MP4Info *info = (MP4Info *)ctx; + return info->strAppName.c_str(); +} + +API_EXPORT const char* API_CALL mk_mp4_info_get_stream(const mk_mp4_info ctx){ + assert(ctx); + MP4Info *info = (MP4Info *)ctx; + return info->strStreamId.c_str(); +} + +///////////////////////////////////////////Parser///////////////////////////////////////////// +API_EXPORT const char* API_CALL mk_parser_get_method(const mk_parser ctx){ + assert(ctx); + Parser *parser = (Parser *)ctx; + return parser->Method().c_str(); +} +API_EXPORT const char* API_CALL mk_parser_get_url(const mk_parser ctx){ + assert(ctx); + Parser *parser = (Parser *)ctx; + return parser->Url().c_str(); +} +API_EXPORT const char* API_CALL mk_parser_get_full_url(const mk_parser ctx){ + assert(ctx); + Parser *parser = (Parser *)ctx; + return parser->FullUrl().c_str(); +} +API_EXPORT const char* API_CALL mk_parser_get_url_params(const mk_parser ctx){ + assert(ctx); + Parser *parser = (Parser *)ctx; + return parser->Params().c_str(); +} +API_EXPORT const char* API_CALL mk_parser_get_url_param(const mk_parser ctx,const char *key){ + assert(ctx && key); + Parser *parser = (Parser *)ctx; + return parser->getUrlArgs()[key].c_str(); +} +API_EXPORT const char* API_CALL mk_parser_get_tail(const mk_parser ctx){ + assert(ctx); + Parser *parser = (Parser *)ctx; + return parser->Tail().c_str(); +} +API_EXPORT const char* API_CALL mk_parser_get_header(const mk_parser ctx,const char *key){ + assert(ctx && key); + Parser *parser = (Parser *)ctx; + return parser->getValues()[key].c_str(); +} +API_EXPORT const char* API_CALL mk_parser_get_content(const mk_parser ctx){ + assert(ctx); + Parser *parser = (Parser *)ctx; + return parser->Content().c_str(); +} + +///////////////////////////////////////////MediaInfo///////////////////////////////////////////// +API_EXPORT const char* API_CALL mk_media_info_get_params(const mk_media_info ctx){ + assert(ctx); + MediaInfo *info = (MediaInfo *)ctx; + return info->_param_strs.c_str(); +} +API_EXPORT const char* API_CALL mk_media_info_get_param(const mk_media_info ctx,const char *key){ + assert(ctx && key); + MediaInfo *info = (MediaInfo *)ctx; + return info->_params[key].c_str(); +} +API_EXPORT const char* API_CALL mk_media_info_get_schema(const mk_media_info ctx){ + assert(ctx); + MediaInfo *info = (MediaInfo *)ctx; + return info->_schema.c_str(); +} +API_EXPORT const char* API_CALL mk_media_info_get_vhost(const mk_media_info ctx){ + assert(ctx); + MediaInfo *info = (MediaInfo *)ctx; + return info->_vhost.c_str(); +} +API_EXPORT const char* API_CALL mk_media_info_get_app(const mk_media_info ctx){ + assert(ctx); + MediaInfo *info = (MediaInfo *)ctx; + return info->_app.c_str(); +} +API_EXPORT const char* API_CALL mk_media_info_get_stream(const mk_media_info ctx){ + assert(ctx); + MediaInfo *info = (MediaInfo *)ctx; + return info->_streamid.c_str(); +} + +///////////////////////////////////////////MediaSource///////////////////////////////////////////// +API_EXPORT const char* API_CALL mk_media_source_get_schema(const mk_media_source ctx){ + assert(ctx); + MediaSource *src = (MediaSource *)ctx; + return src->getSchema().c_str(); +} +API_EXPORT const char* API_CALL mk_media_source_get_vhost(const mk_media_source ctx){ + assert(ctx); + MediaSource *src = (MediaSource *)ctx; + return src->getVhost().c_str(); +} +API_EXPORT const char* API_CALL mk_media_source_get_app(const mk_media_source ctx){ + assert(ctx); + MediaSource *src = (MediaSource *)ctx; + return src->getApp().c_str(); +} +API_EXPORT const char* API_CALL mk_media_source_get_stream(const mk_media_source ctx){ + assert(ctx); + MediaSource *src = (MediaSource *)ctx; + return src->getId().c_str(); +} +API_EXPORT int API_CALL mk_media_source_get_reader_count(const mk_media_source ctx){ + assert(ctx); + MediaSource *src = (MediaSource *)ctx; + return src->readerCount(); +} +API_EXPORT int API_CALL mk_media_source_close(const mk_media_source ctx,int force){ + assert(ctx); + MediaSource *src = (MediaSource *)ctx; + return src->close(force); +} +API_EXPORT int API_CALL mk_media_source_seek_to(const mk_media_source ctx,uint32_t stamp){ + assert(ctx); + MediaSource *src = (MediaSource *)ctx; + return src->seekTo(stamp); +} + +API_EXPORT void API_CALL mk_media_source_find(const char *schema, + const char *vhost, + const char *app, + const char *stream, + void *user_data, + on_mk_media_source_find_cb cb) { + assert(schema && vhost && app && stream && cb); + auto src = MediaSource::find(schema, vhost, app, stream); + cb(user_data, src.get()); +} + +API_EXPORT void API_CALL mk_media_source_for_each(void *user_data, on_mk_media_source_find_cb cb){ + assert(cb); + MediaSource::for_each_media([&](const MediaSource::Ptr &src){ + cb(user_data,src.get()); + }); +} + +///////////////////////////////////////////TcpSession///////////////////////////////////////////// +API_EXPORT void API_CALL mk_tcp_session_shutdown(const mk_tcp_session ctx,int err,const char *err_msg){ + assert(ctx); + TcpSession *session = (TcpSession *)ctx; + session->safeShutdown(SockException((ErrCode)err,err_msg)); +} +API_EXPORT const char* API_CALL mk_tcp_session_peer_ip(const mk_tcp_session ctx){ + assert(ctx); + TcpSession *session = (TcpSession *)ctx; + return session->get_peer_ip().c_str(); +} +API_EXPORT const char* API_CALL mk_tcp_session_local_ip(const mk_tcp_session ctx){ + assert(ctx); + TcpSession *session = (TcpSession *)ctx; + return session->get_local_ip().c_str(); +} +API_EXPORT uint16_t API_CALL mk_tcp_session_peer_port(const mk_tcp_session ctx){ + assert(ctx); + TcpSession *session = (TcpSession *)ctx; + return session->get_peer_port(); +} +API_EXPORT uint16_t API_CALL mk_tcp_session_local_port(const mk_tcp_session ctx){ + assert(ctx); + TcpSession *session = (TcpSession *)ctx; + return session->get_local_port(); +} + +///////////////////////////////////////////HttpResponseInvoker///////////////////////////////////////////// +static StrCaseMap get_http_header( const char *response_header[]){ + StrCaseMap header; + for (int i = 0; response_header[i] != NULL;) { + auto key = response_header[i]; + auto value = response_header[i + 1]; + if (key && value) { + i += 2; + header[key] = value; + continue; + } + break; + } + return std::move(header); +} +API_EXPORT void API_CALL mk_http_response_invoker_do(const mk_http_response_invoker ctx, + const char *response_code, + const char *response_header[], + const char *response_content){ + assert(ctx && response_code && response_header && response_content); + auto header = get_http_header(response_header); + HttpSession::HttpResponseInvoker *invoker = (HttpSession::HttpResponseInvoker *)ctx; + (*invoker)(response_code,header,response_content); +} + +API_EXPORT void API_CALL mk_http_response_invoker_do_file(const mk_http_response_invoker ctx, + const mk_parser request_parser, + const char *response_header[], + const char *response_file_path){ + assert(ctx && request_parser && response_header && response_file_path); + auto header = get_http_header(response_header); + HttpSession::HttpResponseInvoker *invoker = (HttpSession::HttpResponseInvoker *)ctx; + (*invoker).responseFile(((Parser*)(request_parser))->getValues(),header,response_file_path); +} + +///////////////////////////////////////////HttpAccessPathInvoker///////////////////////////////////////////// +API_EXPORT void API_CALL mk_http_access_path_invoker_do(const mk_http_access_path_invoker ctx, + const char *err_msg, + const char *access_path, + int cookie_life_second){ + assert(ctx); + HttpSession::HttpAccessPathInvoker *invoer = (HttpSession::HttpAccessPathInvoker *)ctx; + (*invoer)(err_msg ? err_msg : "", + access_path? access_path : "", + cookie_life_second); +} +///////////////////////////////////////////RtspSession::onGetRealm///////////////////////////////////////////// +API_EXPORT void API_CALL mk_rtsp_get_realm_invoker_do(const mk_rtsp_get_realm_invoker ctx, + const char *realm){ + assert(ctx); + RtspSession::onGetRealm *invoker = (RtspSession::onGetRealm *)ctx; + (*invoker)(realm ? realm : ""); +} + +///////////////////////////////////////////RtspSession::onAuth///////////////////////////////////////////// +API_EXPORT void API_CALL mk_rtsp_auth_invoker_do(const mk_rtsp_auth_invoker ctx, + int encrypted, + const char *pwd_or_md5){ + assert(ctx); + RtspSession::onAuth *invoker = (RtspSession::onAuth *)ctx; + (*invoker)(encrypted, pwd_or_md5 ? pwd_or_md5 : ""); +} + +///////////////////////////////////////////Broadcast::PublishAuthInvoker///////////////////////////////////////////// +API_EXPORT void API_CALL mk_publish_auth_invoker_do(const mk_publish_auth_invoker ctx, + const char *err_msg, + int enable_rtxp, + int enable_hls, + int enable_mp4){ + assert(ctx); + Broadcast::PublishAuthInvoker *invoker = (Broadcast::PublishAuthInvoker *)ctx; + (*invoker)(err_msg ? err_msg : "", enable_rtxp, enable_hls, enable_mp4); +} + +///////////////////////////////////////////Broadcast::AuthInvoker///////////////////////////////////////////// +API_EXPORT void API_CALL mk_auth_invoker_do(const mk_auth_invoker ctx, const char *err_msg){ + assert(ctx); + Broadcast::AuthInvoker *invoker = (Broadcast::AuthInvoker *)ctx; + (*invoker)(err_msg ? err_msg : ""); +} + diff --git a/api/source/mk_event.cpp b/api/source/mk_event.cpp new file mode 100644 index 00000000..3f124b15 --- /dev/null +++ b/api/source/mk_event.cpp @@ -0,0 +1,27 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "mk_event.h" diff --git a/src/Record/MP4Recorder.h b/src/Record/MP4Recorder.h index 3204fb51..838f2507 100644 --- a/src/Record/MP4Recorder.h +++ b/src/Record/MP4Recorder.h @@ -27,8 +27,6 @@ #ifndef MP4MAKER_H_ #define MP4MAKER_H_ -#ifdef ENABLE_MP4RECORD - #include #include #include "Player/PlayerBase.h" @@ -56,6 +54,7 @@ public: string strVhost;//vhost }; +#ifdef ENABLE_MP4RECORD class MP4Recorder : public MediaSinkInterface{ public: typedef std::shared_ptr Ptr; @@ -95,8 +94,8 @@ private: list _tracks; }; -} /* namespace mediakit */ - #endif ///ENABLE_MP4RECORD +} /* namespace mediakit */ + #endif /* MP4MAKER_H_ */ From eb89a3e5048bac4285787a13a36eddf4bab3bd30 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 24 Dec 2019 10:25:28 +0800 Subject: [PATCH 125/272] =?UTF-8?q?=E5=AE=8C=E5=96=84http=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=99=A8=E5=AF=B9=E4=B8=8D=E5=9B=BA=E5=AE=9A=E9=95=BF?= =?UTF-8?q?=E5=BA=A6body=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/HttpBody.h | 4 ++-- src/Http/HttpSession.cpp | 22 ++++++++++------------ src/Http/HttpSession.h | 2 +- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/Http/HttpBody.h b/src/Http/HttpBody.h index d119cbc5..a340c1b0 100644 --- a/src/Http/HttpBody.h +++ b/src/Http/HttpBody.h @@ -52,14 +52,14 @@ public: virtual ~HttpBody(){} /** - * 剩余数据大小 + * 剩余数据大小,如果返回>=INT64_MAX, 那么就不设置content-length */ virtual uint64_t remainSize() { return 0;}; /** * 读取一定字节数,返回大小可能小于size * @param size 请求大小 - * @return 字节对象 + * @return 字节对象,如果读完了,那么请返回nullptr */ virtual Buffer::Ptr readData(uint32_t size) { return nullptr;}; }; diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp index 88fa99e0..d02ac142 100644 --- a/src/Http/HttpSession.cpp +++ b/src/Http/HttpSession.cpp @@ -156,7 +156,7 @@ bool HttpSession::checkWebSocket(){ auto res_cb = [this,headerOut](){ _flv_over_websocket = true; - sendResponse("101 Switching Protocols",false,nullptr,headerOut,nullptr,false); + sendResponse("101 Switching Protocols",false,nullptr,headerOut,nullptr, true); }; //判断是否为websocket-flv @@ -219,7 +219,7 @@ bool HttpSession::checkLiveFlvStream(const function &cb){ if(!cb) { //找到rtmp源,发送http头,负载后续发送 - sendResponse("200 OK", false, "video/x-flv",KeyValue(),nullptr,false); + sendResponse("200 OK", false, "video/x-flv",KeyValue(),nullptr,true); }else{ cb(); } @@ -313,7 +313,7 @@ void HttpSession::sendResponse(const char *pcStatus, const char *pcContentType, const HttpSession::KeyValue &header, const HttpBody::Ptr &body, - bool set_content_len ){ + bool is_http_flv ){ GET_CONFIG(string,charSet,Http::kCharSet); GET_CONFIG(uint32_t,keepAliveSec,Http::kKeepAliveSecond); @@ -322,16 +322,14 @@ void HttpSession::sendResponse(const char *pcStatus, if (body && body->remainSize()) { //有body,获取body大小 size = body->remainSize(); - if (size >= INT64_MAX) { - //不固定长度的body,那么不设置content-length字段 - size = -1; - } } - if(!set_content_len || size == -1){ - //如果是不定长度body,或者不设置conten-length, - //那么一定是Keep-Alive类型 + if(is_http_flv){ + //http-flv直播是Keep-Alive类型 bClose = false; + }else if(size >= INT64_MAX){ + //不固定长度的body,那么发送完body后应该关闭socket,以便浏览器做下载完毕的判断 + bClose = true; } HttpSession::KeyValue &headerOut = const_cast(header); @@ -348,8 +346,8 @@ void HttpSession::sendResponse(const char *pcStatus, headerOut.emplace("Access-Control-Allow-Credentials", "true"); } - if(set_content_len && size >= 0){ - //文件长度为定值或者,且不是http-flv强制设置Content-Length + if(!is_http_flv && size >= 0 && size < INT64_MAX){ + //文件长度为固定值,且不是http-flv强制设置Content-Length headerOut["Content-Length"] = StrPrinter << size << endl; } diff --git a/src/Http/HttpSession.h b/src/Http/HttpSession.h index 207b061b..ec128c52 100644 --- a/src/Http/HttpSession.h +++ b/src/Http/HttpSession.h @@ -115,7 +115,7 @@ private: void sendNotFound(bool bClose); void sendResponse(const char *pcStatus, bool bClose, const char *pcContentType = nullptr, const HttpSession::KeyValue &header = HttpSession::KeyValue(), - const HttpBody::Ptr &body = nullptr,bool set_content_len = true); + const HttpBody::Ptr &body = nullptr,bool is_http_flv = false); //设置socket标志 void setSocketFlags(); From 5886a38a9f33ca4563947783aa3491e273f00aaa Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 24 Dec 2019 13:56:53 +0800 Subject: [PATCH 126/272] =?UTF-8?q?=E5=AE=8C=E6=88=90=E4=BA=8B=E4=BB=B6c?= =?UTF-8?q?=20api=E7=9A=84=E5=BC=80=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/{mk_event.h => events.h} | 72 ++++++-- .../{event_objects.h => events_objects.h} | 4 +- api/include/mediakit.h | 2 +- api/source/events.cpp | 162 ++++++++++++++++++ .../{event_objects.cpp => events_objects.cpp} | 4 +- api/source/mk_event.cpp | 27 --- 6 files changed, 222 insertions(+), 49 deletions(-) rename api/include/{mk_event.h => events.h} (63%) rename api/include/{event_objects.h => events_objects.h} (99%) create mode 100644 api/source/events.cpp rename api/source/{event_objects.cpp => events_objects.cpp} (99%) delete mode 100644 api/source/mk_event.cpp diff --git a/api/include/mk_event.h b/api/include/events.h similarity index 63% rename from api/include/mk_event.h rename to api/include/events.h index 303af64f..32ee86a2 100644 --- a/api/include/mk_event.h +++ b/api/include/events.h @@ -24,11 +24,11 @@ * SOFTWARE. */ -#ifndef MK_EVENT_H -#define MK_EVENT_H +#ifndef MK_EVENTS_H +#define MK_EVENTS_H #include "common.h" -#include "event_objects.h" +#include "events_objects.h" #ifdef __cplusplus extern "C" { #endif @@ -39,7 +39,8 @@ typedef struct{ * @param regist 注册为1,注销为0 * @param sender 该MediaSource对象 */ - typedef void (*on_mk_media_changed)(int regist,mk_media_source sender); + void (*on_mk_media_changed)(int regist, + const mk_media_source sender); /** * 收到rtsp/rtmp推流事件广播,通过该事件控制推流鉴权 @@ -48,7 +49,9 @@ typedef struct{ * @param invoker 执行invoker返回鉴权结果 * @param sender 该tcp客户端相关信息 */ - typedef void (*on_mk_media_publish)(mk_media_info url_info,mk_publish_auth_invoker invoker,mk_tcp_session sender); + void (*on_mk_media_publish)(const mk_media_info url_info, + const mk_publish_auth_invoker invoker, + const mk_tcp_session sender); /** * 播放rtsp/rtmp/http-flv/hls事件广播,通过该事件控制播放鉴权 @@ -57,20 +60,23 @@ typedef struct{ * @param invoker 执行invoker返回鉴权结果 * @param sender 播放客户端相关信息 */ - typedef void (*on_mk_media_play)(mk_media_info url_info,mk_auth_invoker invoker,mk_tcp_session sender); + void (*on_mk_media_play)(const mk_media_info url_info, + const mk_auth_invoker invoker, + const mk_tcp_session sender); /** * 未找到流后会广播该事件,请在监听该事件后去拉流或其他方式产生流,这样就能按需拉流了 * @param url_info 播放url相关信息 * @param sender 播放客户端相关信息 */ - typedef void (*on_mk_media_not_found)(mk_media_info url_info,mk_tcp_session sender); + void (*on_mk_media_not_found)(const mk_media_info url_info, + const mk_tcp_session sender); /** * 某个流无人消费时触发,目的为了实现无人观看时主动断开拉流等业务逻辑 * @param sender 该MediaSource对象 */ - typedef void (*on_mk_media_no_reader)(mk_media_source sender); + void (*on_mk_media_no_reader)(const mk_media_source sender); /** * 收到http api请求广播(包括GET/POST) @@ -79,7 +85,10 @@ typedef struct{ * @param consumed 置1则说明我们要处理该事件 * @param sender http客户端相关信息 */ - typedef void (*on_mk_http_request)(mk_parser parser, mk_http_response_invoker invoker, int *consumed,mk_tcp_session sender); + void (*on_mk_http_request)(const mk_parser parser, + const mk_http_response_invoker invoker, + int *consumed, + const mk_tcp_session sender); /** * 在http文件服务器中,收到http访问文件或目录的广播,通过该事件控制访问http目录的权限 @@ -90,7 +99,12 @@ typedef struct{ * @param invoker 执行invoker返回本次访问文件的结果 * @param sender http客户端相关信息 */ - typedef void (*on_mk_http_access)(mk_parser parser, mk_media_info url_info, const char *path,int is_dir, mk_http_access_path_invoker invoker,mk_tcp_session sender); + void (*on_mk_http_access)(const mk_parser parser, + const mk_media_info url_info, + const char *path, + int is_dir, + const mk_http_access_path_invoker invoker, + mk_tcp_session sender); /** * 在http文件服务器中,收到http访问文件或目录前的广播,通过该事件可以控制http url到文件路径的映射 @@ -100,7 +114,10 @@ typedef struct{ * @param path 文件绝对路径,覆盖之可以重定向到其他文件 * @param sender http客户端相关信息 */ - typedef void (*on_mk_http_before_access)(mk_parser parser, mk_media_info url_info,char *path, mk_tcp_session sender); + void (*on_mk_http_before_access)(const mk_parser parser, + const mk_media_info url_info, + char *path, + const mk_tcp_session sender); /** * 该rtsp流是否需要认证?是的话调用invoker并传入realm,否则传入空的realm @@ -108,7 +125,9 @@ typedef struct{ * @param invoker 执行invoker返回是否需要rtsp专属认证 * @param sender rtsp客户端相关信息 */ - typedef void (*on_mk_rtsp_get_realm)(mk_media_info url_info, mk_rtsp_get_realm_invoker invoker, mk_tcp_session sender); + void (*on_mk_rtsp_get_realm)(const mk_media_info url_info, + const mk_rtsp_get_realm_invoker invoker, + const mk_tcp_session sender); /** * 请求认证用户密码事件,user_name为用户名,must_no_encrypt如果为true,则必须提供明文密码(因为此时是base64认证方式),否则会导致认证失败 @@ -120,17 +139,25 @@ typedef struct{ * @param invoker 执行invoker返回rtsp专属认证的密码 * @param sender rtsp客户端信息 */ - typedef void (*on_mk_rtsp_auth)(mk_media_info url_info,const char *realm,const char *user_name, int must_no_encrypt, mk_rtsp_auth_invoker invoker, mk_tcp_session sender); + void (*on_mk_rtsp_auth)(const mk_media_info url_info, + const char *realm, + const char *user_name, + int must_no_encrypt, + const mk_rtsp_auth_invoker invoker, + const mk_tcp_session sender); /** * 录制mp4分片文件成功后广播 */ - typedef void (*on_mk_record_mp4)(mk_mp4_info mp4); + void (*on_mk_record_mp4)(const mk_mp4_info mp4); /** * shell登录鉴权 */ - typedef void (*on_mk_shell_login_mp4)(const char *user_name, const char *passwd,mk_auth_invoker invoker,mk_tcp_session sender); + void (*on_mk_shell_login)(const char *user_name, + const char *passwd, + const mk_auth_invoker invoker, + const mk_tcp_session sender); /** * 停止rtsp/rtmp/http-flv会话后流量汇报事件广播 @@ -139,12 +166,23 @@ typedef struct{ * @param total_seconds 本次tcp会话时长,单位秒 * @param is_player 客户端是否为播放器 */ - typedef void (*on_mk_flow_report)(mk_media_info url_info,uint64_t total_bytes,uint64_t total_seconds,int is_player,mk_tcp_session sender); + void (*on_mk_flow_report)(const mk_media_info url_info, + uint64_t total_bytes, + uint64_t total_seconds, + int is_player, + const mk_tcp_session sender); } mk_events; +/** + * 监听ZLMediaKit里面的事件 + * @param events 各个事件的结构体,这个对象在内部会再拷贝一次,可以设置为null以便取消监听 + */ +API_EXPORT void API_CALL mk_events_listen(const mk_events *events); + + #ifdef __cplusplus } #endif -#endif //MK_EVENT_H +#endif //MK_EVENTS_H diff --git a/api/include/event_objects.h b/api/include/events_objects.h similarity index 99% rename from api/include/event_objects.h rename to api/include/events_objects.h index be30b787..aca46ed2 100644 --- a/api/include/event_objects.h +++ b/api/include/events_objects.h @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2019 xiongziliang <771730766@qq.com> @@ -33,7 +33,7 @@ extern "C" { ///////////////////////////////////////////MP4Info///////////////////////////////////////////// //MP4Info对象的C映射 -typedef void *mk_mp4_info; +typedef void* mk_mp4_info; //MP4Info::ui64StartedTime API_EXPORT uint64_t API_CALL mk_mp4_info_get_start_time(const mk_mp4_info ctx); //MP4Info::ui64TimeLen diff --git a/api/include/mediakit.h b/api/include/mediakit.h index 5886f9a5..3d38b9e8 100755 --- a/api/include/mediakit.h +++ b/api/include/mediakit.h @@ -34,6 +34,6 @@ #include "flvrecorder.h" #include "player.h" #include "pusher.h" -#include "mk_event.h" +#include "events.h" #endif /* MK_API_H_ */ diff --git a/api/source/events.cpp b/api/source/events.cpp new file mode 100644 index 00000000..781899fd --- /dev/null +++ b/api/source/events.cpp @@ -0,0 +1,162 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "events.h" +#include "Common/config.h" +#include "Common/MediaSource.h" +#include "Http/HttpSession.h" +#include "Rtsp/RtspSession.h" +#include "Record/MP4Recorder.h" +using namespace mediakit; + +static void* s_tag; +static mk_events s_events = {0}; + +API_EXPORT void API_CALL mk_events_listen(const mk_events *events){ + if(events){ + memcpy(&s_events,events, sizeof(s_events)); + }else{ + memset(&s_events,0,sizeof(s_events)); + } + + static onceToken tokne([]{ + NoticeCenter::Instance().addListener(&s_tag,Broadcast::kBroadcastMediaChanged,[](BroadcastMediaChangedArgs){ + if(s_events.on_mk_media_changed){ + s_events.on_mk_media_changed(bRegist, + (mk_media_source)&sender); + } + }); + + NoticeCenter::Instance().addListener(&s_tag,Broadcast::kBroadcastRecordMP4,[](BroadcastRecordMP4Args){ + if(s_events.on_mk_record_mp4){ + s_events.on_mk_record_mp4((mk_mp4_info)&info); + } + }); + + NoticeCenter::Instance().addListener(&s_tag,Broadcast::kBroadcastHttpRequest,[](BroadcastHttpRequestArgs){ + if(s_events.on_mk_http_request){ + int consumed_int = consumed; + s_events.on_mk_http_request((mk_parser)&parser, + (mk_http_response_invoker)&invoker, + &consumed_int, + (mk_tcp_session)&sender); + consumed = consumed_int; + } + }); + + NoticeCenter::Instance().addListener(&s_tag,Broadcast::kBroadcastHttpAccess,[](BroadcastHttpAccessArgs){ + if(s_events.on_mk_http_access){ + s_events.on_mk_http_access((mk_parser)&parser, + (mk_media_info)&args, + path.c_str(), + is_dir, + (mk_http_access_path_invoker)&invoker, + (mk_tcp_session)&sender); + } + }); + + NoticeCenter::Instance().addListener(&s_tag,Broadcast::kBroadcastHttpBeforeAccess,[](BroadcastHttpBeforeAccessArgs){ + if(s_events.on_mk_http_before_access){ + char path_c[4 * 1024] = {0}; + strcpy(path_c,path.c_str()); + s_events.on_mk_http_before_access((mk_parser) &parser, + (mk_media_info) &args, + path_c, + (mk_tcp_session) &sender); + path = path_c; + } + }); + + + NoticeCenter::Instance().addListener(&s_tag,Broadcast::kBroadcastOnGetRtspRealm,[](BroadcastOnGetRtspRealmArgs){ + if (s_events.on_mk_rtsp_get_realm) { + s_events.on_mk_rtsp_get_realm((mk_media_info) &args, + (mk_rtsp_get_realm_invoker) &invoker, + (mk_tcp_session) &sender); + } + }); + + NoticeCenter::Instance().addListener(&s_tag,Broadcast::kBroadcastOnRtspAuth,[](BroadcastOnRtspAuthArgs){ + if (s_events.on_mk_rtsp_auth) { + s_events.on_mk_rtsp_auth((mk_media_info) &args, + realm.c_str(), + user_name.c_str(), + must_no_encrypt, + (mk_rtsp_auth_invoker) &invoker, + (mk_tcp_session) &sender); + } + }); + + NoticeCenter::Instance().addListener(&s_tag,Broadcast::kBroadcastMediaPublish,[](BroadcastMediaPublishArgs){ + if (s_events.on_mk_media_publish) { + s_events.on_mk_media_publish((mk_media_info) &args, + (mk_publish_auth_invoker) &invoker, + (mk_tcp_session) &sender); + } + }); + + NoticeCenter::Instance().addListener(&s_tag,Broadcast::kBroadcastMediaPlayed,[](BroadcastMediaPlayedArgs){ + if (s_events.on_mk_media_play) { + s_events.on_mk_media_play((mk_media_info) &args, + (mk_auth_invoker) &invoker, + (mk_tcp_session) &sender); + } + }); + + NoticeCenter::Instance().addListener(&s_tag,Broadcast::kBroadcastShellLogin,[](BroadcastShellLoginArgs){ + if (s_events.on_mk_shell_login) { + s_events.on_mk_shell_login(user_name.c_str(), + passwd.c_str(), + (mk_auth_invoker) &invoker, + (mk_tcp_session) &sender); + } + }); + + NoticeCenter::Instance().addListener(&s_tag,Broadcast::kBroadcastFlowReport,[](BroadcastFlowReportArgs){ + if (s_events.on_mk_flow_report) { + s_events.on_mk_flow_report((mk_media_info) &args, + totalBytes, + totalDuration, + isPlayer, + (mk_tcp_session) &sender); + } + }); + + NoticeCenter::Instance().addListener(&s_tag,Broadcast::kBroadcastNotFoundStream,[](BroadcastNotFoundStreamArgs){ + if (s_events.on_mk_media_not_found) { + s_events.on_mk_media_not_found((mk_media_info) &args, + (mk_tcp_session) &sender); + } + }); + + NoticeCenter::Instance().addListener(&s_tag,Broadcast::kBroadcastStreamNoneReader,[](BroadcastStreamNoneReaderArgs){ + if (s_events.on_mk_media_no_reader) { + s_events.on_mk_media_no_reader((mk_media_source) &sender); + } + }); + }); + +} diff --git a/api/source/event_objects.cpp b/api/source/events_objects.cpp similarity index 99% rename from api/source/event_objects.cpp rename to api/source/events_objects.cpp index 15f165db..7fe24418 100644 --- a/api/source/event_objects.cpp +++ b/api/source/events_objects.cpp @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2019 xiongziliang <771730766@qq.com> @@ -25,7 +25,7 @@ */ #include -#include "event_objects.h" +#include "events_objects.h" #include "Common/config.h" #include "Record/MP4Recorder.h" #include "Network/TcpSession.h" diff --git a/api/source/mk_event.cpp b/api/source/mk_event.cpp deleted file mode 100644 index 3f124b15..00000000 --- a/api/source/mk_event.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 xiongziliang <771730766@qq.com> - * - * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "mk_event.h" From e663ddd9a63270b3423e42c37fb43ef283569438 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 24 Dec 2019 14:08:16 +0800 Subject: [PATCH 127/272] =?UTF-8?q?=E7=AE=80=E5=8C=96=E4=BA=8B=E4=BB=B6?= =?UTF-8?q?=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/FFmpegSource.cpp | 10 +++++----- server/WebHook.cpp | 8 ++++---- src/Common/MediaSource.cpp | 29 ++++++++--------------------- src/Common/config.h | 4 ++-- src/Record/Recorder.cpp | 20 ++++++++++---------- tests/test_pusher.cpp | 4 ++-- tests/test_server.cpp | 10 +++++----- 7 files changed, 36 insertions(+), 49 deletions(-) diff --git a/server/FFmpegSource.cpp b/server/FFmpegSource.cpp index 81d98bd5..12c8a072 100644 --- a/server/FFmpegSource.cpp +++ b/server/FFmpegSource.cpp @@ -147,11 +147,11 @@ void FFmpegSource::findAsync(int maxWaitMS, const function_media_info._schema || - vhost != strongSelf->_media_info._vhost || - app != strongSelf->_media_info._app || - stream != strongSelf->_media_info._streamid){ + if (!bRegist || + sender.getSchema() != strongSelf->_media_info._schema || + sender.getVhost() != strongSelf->_media_info._vhost || + sender.getApp() != strongSelf->_media_info._app || + sender.getId() != strongSelf->_media_info._streamid) { //不是自己感兴趣的事件,忽略之 return; } diff --git a/server/WebHook.cpp b/server/WebHook.cpp index b6283025..6c911588 100644 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -341,10 +341,10 @@ void installWebHook(){ } ArgsType body; body["regist"] = bRegist; - body["schema"] = schema; - body["vhost"] = vhost; - body["app"] = app; - body["stream"] = stream; + body["schema"] = sender.getSchema(); + body["vhost"] = sender.getVhost(); + body["app"] = sender.getApp(); + body["stream"] = sender.getId(); //执行hook do_http_hook(hook_stream_chaned,body, nullptr); }); diff --git a/src/Common/MediaSource.cpp b/src/Common/MediaSource.cpp index 036f541c..d1e8409b 100644 --- a/src/Common/MediaSource.cpp +++ b/src/Common/MediaSource.cpp @@ -86,12 +86,7 @@ void MediaSource::setTrackSource(const std::weak_ptr &track_src) { if (!strongPtr) { return; } - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaResetTracks, - _strSchema, - _strVhost, - _strApp, - _strId, - *this); + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaResetTracks, *this); },false); } @@ -228,7 +223,11 @@ void findAsync_l(const MediaInfo &info, return; } - if(!bRegist || schema != info._schema || vhost != info._vhost || app != info._app ||stream != info._streamid){ + if (!bRegist || + sender.getSchema() != info._schema || + sender.getVhost() != info._vhost || + sender.getApp() != info._app || + sender.getId() != info._streamid) { //不是自己感兴趣的事件,忽略之 return; } @@ -312,13 +311,7 @@ void MediaSource::regist() { if (!strongPtr) { return; } - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaChanged, - true, - _strSchema, - _strVhost, - _strApp, - _strId, - *this); + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaChanged, true, *this); },false); } bool MediaSource::unregist() { @@ -341,13 +334,7 @@ bool MediaSource::unregist() { } void MediaSource::unregisted(){ InfoL << "" << _strSchema << " " << _strVhost << " " << _strApp << " " << _strId; - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaChanged, - false, - _strSchema, - _strVhost, - _strApp, - _strId, - *this); + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaChanged, false, *this); } diff --git a/src/Common/config.h b/src/Common/config.h index 9e9b3c1e..53910dfc 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -69,11 +69,11 @@ namespace Broadcast { //注册或反注册MediaSource事件广播 extern const string kBroadcastMediaChanged; -#define BroadcastMediaChangedArgs const bool &bRegist, const string &schema,const string &vhost,const string &app,const string &stream,MediaSource &sender +#define BroadcastMediaChangedArgs const bool &bRegist, MediaSource &sender //MediaSource重置Track事件 extern const string kBroadcastMediaResetTracks; -#define BroadcastMediaResetTracksArgs const string &schema,const string &vhost,const string &app,const string &stream,MediaSource &sender +#define BroadcastMediaResetTracksArgs MediaSource &sender //录制mp4文件成功后广播 extern const string kBroadcastRecordMP4; diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index 43baa0e4..b2062c28 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -221,13 +221,13 @@ private: _notice_center = NoticeCenter::Instance().shared_from_this(); _notice_center->addListener(this,Broadcast::kBroadcastMediaChanged,[this](BroadcastMediaChangedArgs){ if(bRegist){ - onRegist(schema,vhost,app,stream,sender); + onRegist(sender); }else{ - onUnRegist(schema,vhost,app,stream,sender); + onUnRegist(sender); } }); _notice_center->addListener(this,Broadcast::kBroadcastMediaResetTracks,[this](BroadcastMediaResetTracksArgs){ - onRegist(schema,vhost,app,stream,sender); + onRegist(sender); }); } @@ -236,8 +236,8 @@ private: _notice_center->delListener(this,Broadcast::kBroadcastMediaResetTracks); } - void onRegist(const string &schema,const string &vhost,const string &app,const string &stream,MediaSource &sender){ - auto key = getRecorderKey(vhost,app,stream); + void onRegist(MediaSource &sender){ + auto key = getRecorderKey(sender.getVhost(),sender.getApp(),sender.getId()); lock_guard lck(_recorder_mtx); auto it = _recorder_map.find(key); if(it == _recorder_map.end()){ @@ -245,20 +245,20 @@ private: return; } - if(!it->second->isRecording() || it->second->getSchema() == schema){ + if(!it->second->isRecording() || it->second->getSchema() == sender.getSchema()){ // 绑定的协议一致或者并未正在录制则替换tracks auto tracks = sender.getTracks(true); if (!tracks.empty()) { - it->second->attachTracks(std::move(tracks),schema); + it->second->attachTracks(std::move(tracks),sender.getSchema()); } } } - void onUnRegist(const string &schema,const string &vhost,const string &app,const string &stream,MediaSource &sender){ - auto key = getRecorderKey(vhost,app,stream); + void onUnRegist(MediaSource &sender){ + auto key = getRecorderKey(sender.getVhost(),sender.getApp(),sender.getId()); lock_guard lck(_recorder_mtx); auto it = _recorder_map.find(key); - if(it == _recorder_map.end() || it->second->getSchema() != schema){ + if(it == _recorder_map.end() || it->second->getSchema() != sender.getSchema()){ // 录像记录不存在或绑定的协议不一致 return; } diff --git a/tests/test_pusher.cpp b/tests/test_pusher.cpp index c7a56aa0..d51aeadb 100644 --- a/tests/test_pusher.cpp +++ b/tests/test_pusher.cpp @@ -99,8 +99,8 @@ int domain(const string &playUrl, const string &pushUrl) { NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastMediaChanged, [pushUrl,poller](BroadcastMediaChangedArgs) { //媒体源"app/stream"已经注册,这时方可新建一个RtmpPusher对象并绑定该媒体源 - if(bRegist && pushUrl.find(schema) == 0){ - createPusher(poller,schema,vhost,app, stream, pushUrl); + if(bRegist && pushUrl.find(sender.getSchema()) == 0){ + createPusher(poller,sender.getSchema(),sender.getVhost(),sender.getApp(), sender.getId(), pushUrl); } }); diff --git a/tests/test_server.cpp b/tests/test_server.cpp index 27c9c86c..cb6dc2da 100644 --- a/tests/test_server.cpp +++ b/tests/test_server.cpp @@ -178,23 +178,23 @@ void initEventListener() { //监听rtsp、rtmp源注册或注销事件;此处用于测试rtmp保存为flv录像,保存在http根目录下 NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastMediaChanged, [](BroadcastMediaChangedArgs) { - if (schema == RTMP_SCHEMA && app == "live") { + if (sender.getSchema() == RTMP_SCHEMA && sender.getApp() == "live") { lock_guard lck(s_mtxFlvRecorder); if (bRegist) { - DebugL << "开始录制RTMP:" << schema << " " << vhost << " " << app << " " << stream; + DebugL << "开始录制RTMP:" << sender.getSchema() << " " << sender.getVhost() << " " << sender.getApp() << " " << sender.getId(); GET_CONFIG(string, http_root, Http::kRootPath); auto path = - http_root + "/" + vhost + "/" + app + "/" + stream + "_" + to_string(time(NULL)) + ".flv"; + http_root + "/" + sender.getVhost() + "/" + sender.getApp() + "/" + sender.getId() + "_" + to_string(time(NULL)) + ".flv"; FlvRecorder::Ptr recorder(new FlvRecorder); try { recorder->startRecord(EventPollerPool::Instance().getPoller(), dynamic_pointer_cast(sender.shared_from_this()), path); - s_mapFlvRecorder[vhost + "/" + app + "/" + stream] = recorder; + s_mapFlvRecorder[sender.getVhost() + "/" + sender.getApp() + "/" + sender.getId()] = recorder; } catch (std::exception &ex) { WarnL << ex.what(); } } else { - s_mapFlvRecorder.erase(vhost + "/" + app + "/" + stream); + s_mapFlvRecorder.erase(sender.getVhost() + "/" + sender.getApp() + "/" + sender.getId()); } } }); From 824eeee6f5089326e541dc41e26a31bbebfc960a Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 24 Dec 2019 14:24:32 +0800 Subject: [PATCH 128/272] =?UTF-8?q?c=20api=20=E5=9B=9E=E8=B0=83invoker?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=BC=82=E6=AD=A5=E6=89=A7=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/events_objects.h | 62 ++++++++++++++++++++++++++++ api/source/events_objects.cpp | 76 ++++++++++++++++++++++++++++++++++- 2 files changed, 136 insertions(+), 2 deletions(-) diff --git a/api/include/events_objects.h b/api/include/events_objects.h index aca46ed2..9c3b9e42 100644 --- a/api/include/events_objects.h +++ b/api/include/events_objects.h @@ -161,7 +161,16 @@ API_EXPORT void API_CALL mk_http_response_invoker_do_file(const mk_http_response const mk_parser request_parser, const char *response_header[], const char *response_file_path); +/** +* 克隆mk_http_response_invoker对象,通过克隆对象为堆对象,可以实现跨线程异步执行mk_http_response_invoker_do +* 如果是同步执行mk_http_response_invoker_do,那么没必要克隆对象 +*/ +API_EXPORT mk_http_response_invoker API_CALL mk_http_response_invoker_clone(const mk_http_response_invoker ctx); +/** + * 销毁堆上的克隆对象 + */ +API_EXPORT void API_CALL mk_http_response_invoker_clone_release(const mk_http_response_invoker ctx); ///////////////////////////////////////////HttpAccessPathInvoker///////////////////////////////////////////// //HttpSession::HttpAccessPathInvoker对象的C映射 @@ -177,6 +186,17 @@ API_EXPORT void API_CALL mk_http_access_path_invoker_do(const mk_http_access_pat const char *access_path, int cookie_life_second); +/** +* 克隆mk_http_access_path_invoker对象,通过克隆对象为堆对象,可以实现跨线程异步执行mk_http_access_path_invoker_do +* 如果是同步执行mk_http_access_path_invoker_do,那么没必要克隆对象 +*/ +API_EXPORT mk_http_access_path_invoker API_CALL mk_http_access_path_invoker_clone(const mk_http_access_path_invoker ctx); + +/** + * 销毁堆上的克隆对象 + */ +API_EXPORT void API_CALL mk_http_access_path_invoker_clone_release(const mk_http_access_path_invoker ctx); + ///////////////////////////////////////////RtspSession::onGetRealm///////////////////////////////////////////// //RtspSession::onGetRealm对象的C映射 typedef void* mk_rtsp_get_realm_invoker; @@ -187,6 +207,17 @@ typedef void* mk_rtsp_get_realm_invoker; API_EXPORT void API_CALL mk_rtsp_get_realm_invoker_do(const mk_rtsp_get_realm_invoker ctx, const char *realm); +/** +* 克隆mk_rtsp_get_realm_invoker对象,通过克隆对象为堆对象,可以实现跨线程异步执行mk_rtsp_get_realm_invoker_do +* 如果是同步执行mk_rtsp_get_realm_invoker_do,那么没必要克隆对象 +*/ +API_EXPORT mk_rtsp_get_realm_invoker API_CALL mk_rtsp_get_realm_invoker_clone(const mk_rtsp_get_realm_invoker ctx); + +/** + * 销毁堆上的克隆对象 + */ +API_EXPORT void API_CALL mk_rtsp_get_realm_invoker_clone_release(const mk_rtsp_get_realm_invoker ctx); + ///////////////////////////////////////////RtspSession::onAuth///////////////////////////////////////////// //RtspSession::onAuth对象的C映射 typedef void* mk_rtsp_auth_invoker; @@ -200,6 +231,16 @@ API_EXPORT void API_CALL mk_rtsp_auth_invoker_do(const mk_rtsp_auth_invoker ctx, int encrypted, const char *pwd_or_md5); +/** + * 克隆mk_rtsp_auth_invoker对象,通过克隆对象为堆对象,可以实现跨线程异步执行mk_rtsp_auth_invoker_do + * 如果是同步执行mk_rtsp_auth_invoker_do,那么没必要克隆对象 + */ +API_EXPORT mk_rtsp_auth_invoker API_CALL mk_rtsp_auth_invoker_clone(const mk_rtsp_auth_invoker ctx); + +/** + * 销毁堆上的克隆对象 + */ +API_EXPORT void API_CALL mk_rtsp_auth_invoker_clone_release(const mk_rtsp_auth_invoker ctx); ///////////////////////////////////////////Broadcast::PublishAuthInvoker///////////////////////////////////////////// //Broadcast::PublishAuthInvoker对象的C映射 @@ -218,6 +259,17 @@ API_EXPORT void API_CALL mk_publish_auth_invoker_do(const mk_publish_auth_invoke int enable_hls, int enable_mp4); +/** + * 克隆mk_publish_auth_invoker对象,通过克隆对象为堆对象,可以实现跨线程异步执行mk_publish_auth_invoker_do + * 如果是同步执行mk_publish_auth_invoker_do,那么没必要克隆对象 + */ +API_EXPORT mk_publish_auth_invoker API_CALL mk_publish_auth_invoker_clone(const mk_publish_auth_invoker ctx); + +/** + * 销毁堆上的克隆对象 + */ +API_EXPORT void API_CALL mk_publish_auth_invoker_clone_release(const mk_publish_auth_invoker ctx); + ///////////////////////////////////////////Broadcast::AuthInvoker///////////////////////////////////////////// //Broadcast::AuthInvoker对象的C映射 typedef void* mk_auth_invoker; @@ -228,6 +280,16 @@ typedef void* mk_auth_invoker; */ API_EXPORT void API_CALL mk_auth_invoker_do(const mk_auth_invoker ctx, const char *err_msg); +/** + * 克隆mk_auth_invoker对象,通过克隆对象为堆对象,可以实现跨线程异步执行mk_auth_invoker_do + * 如果是同步执行mk_auth_invoker_do,那么没必要克隆对象 + */ +API_EXPORT mk_auth_invoker API_CALL mk_auth_invoker_clone(const mk_auth_invoker ctx); + +/** + * 销毁堆上的克隆对象 + */ +API_EXPORT void API_CALL mk_auth_invoker_clone_relase(const mk_auth_invoker ctx); #ifdef __cplusplus } diff --git a/api/source/events_objects.cpp b/api/source/events_objects.cpp index 7fe24418..0a22566d 100644 --- a/api/source/events_objects.cpp +++ b/api/source/events_objects.cpp @@ -285,17 +285,42 @@ API_EXPORT void API_CALL mk_http_response_invoker_do_file(const mk_http_response (*invoker).responseFile(((Parser*)(request_parser))->getValues(),header,response_file_path); } +API_EXPORT mk_http_response_invoker API_CALL mk_http_response_invoker_clone(const mk_http_response_invoker ctx){ + assert(ctx); + HttpSession::HttpResponseInvoker *invoker = (HttpSession::HttpResponseInvoker *)ctx; + return new HttpSession::HttpResponseInvoker (*invoker); +} + +API_EXPORT void API_CALL mk_http_response_invoker_clone_release(const mk_http_response_invoker ctx){ + assert(ctx); + HttpSession::HttpResponseInvoker *invoker = (HttpSession::HttpResponseInvoker *)ctx; + delete invoker; +} + ///////////////////////////////////////////HttpAccessPathInvoker///////////////////////////////////////////// API_EXPORT void API_CALL mk_http_access_path_invoker_do(const mk_http_access_path_invoker ctx, const char *err_msg, const char *access_path, int cookie_life_second){ assert(ctx); - HttpSession::HttpAccessPathInvoker *invoer = (HttpSession::HttpAccessPathInvoker *)ctx; - (*invoer)(err_msg ? err_msg : "", + HttpSession::HttpAccessPathInvoker *invoker = (HttpSession::HttpAccessPathInvoker *)ctx; + (*invoker)(err_msg ? err_msg : "", access_path? access_path : "", cookie_life_second); } + +API_EXPORT mk_http_access_path_invoker API_CALL mk_http_access_path_invoker_clone(const mk_http_access_path_invoker ctx){ + assert(ctx); + HttpSession::HttpAccessPathInvoker *invoker = (HttpSession::HttpAccessPathInvoker *)ctx; + return new HttpSession::HttpAccessPathInvoker(*invoker); +} + +API_EXPORT void API_CALL mk_http_access_path_invoker_clone_release(const mk_http_access_path_invoker ctx){ + assert(ctx); + HttpSession::HttpAccessPathInvoker *invoker = (HttpSession::HttpAccessPathInvoker *)ctx; + delete invoker; +} + ///////////////////////////////////////////RtspSession::onGetRealm///////////////////////////////////////////// API_EXPORT void API_CALL mk_rtsp_get_realm_invoker_do(const mk_rtsp_get_realm_invoker ctx, const char *realm){ @@ -304,6 +329,18 @@ API_EXPORT void API_CALL mk_rtsp_get_realm_invoker_do(const mk_rtsp_get_realm_in (*invoker)(realm ? realm : ""); } +API_EXPORT mk_rtsp_get_realm_invoker API_CALL mk_rtsp_get_realm_invoker_clone(const mk_rtsp_get_realm_invoker ctx){ + assert(ctx); + RtspSession::onGetRealm *invoker = (RtspSession::onGetRealm *)ctx; + return new RtspSession::onGetRealm (*invoker); +} + +API_EXPORT void API_CALL mk_rtsp_get_realm_invoker_clone_release(const mk_rtsp_get_realm_invoker ctx){ + assert(ctx); + RtspSession::onGetRealm *invoker = (RtspSession::onGetRealm *)ctx; + delete invoker; +} + ///////////////////////////////////////////RtspSession::onAuth///////////////////////////////////////////// API_EXPORT void API_CALL mk_rtsp_auth_invoker_do(const mk_rtsp_auth_invoker ctx, int encrypted, @@ -313,6 +350,18 @@ API_EXPORT void API_CALL mk_rtsp_auth_invoker_do(const mk_rtsp_auth_invoker ctx, (*invoker)(encrypted, pwd_or_md5 ? pwd_or_md5 : ""); } +API_EXPORT mk_rtsp_auth_invoker API_CALL mk_rtsp_auth_invoker_clone(const mk_rtsp_auth_invoker ctx){ + assert(ctx); + RtspSession::onAuth *invoker = (RtspSession::onAuth *)ctx; + return new RtspSession::onAuth(*invoker); +} + +API_EXPORT void API_CALL mk_rtsp_auth_invoker_clone_release(const mk_rtsp_auth_invoker ctx){ + assert(ctx); + RtspSession::onAuth *invoker = (RtspSession::onAuth *)ctx; + delete invoker; +} + ///////////////////////////////////////////Broadcast::PublishAuthInvoker///////////////////////////////////////////// API_EXPORT void API_CALL mk_publish_auth_invoker_do(const mk_publish_auth_invoker ctx, const char *err_msg, @@ -324,6 +373,18 @@ API_EXPORT void API_CALL mk_publish_auth_invoker_do(const mk_publish_auth_invoke (*invoker)(err_msg ? err_msg : "", enable_rtxp, enable_hls, enable_mp4); } +API_EXPORT mk_publish_auth_invoker API_CALL mk_publish_auth_invoker_clone(const mk_publish_auth_invoker ctx){ + assert(ctx); + Broadcast::PublishAuthInvoker *invoker = (Broadcast::PublishAuthInvoker *)ctx; + return new Broadcast::PublishAuthInvoker(*invoker); +} + +API_EXPORT void API_CALL mk_publish_auth_invoker_clone_release(const mk_publish_auth_invoker ctx){ + assert(ctx); + Broadcast::PublishAuthInvoker *invoker = (Broadcast::PublishAuthInvoker *)ctx; + delete invoker; +} + ///////////////////////////////////////////Broadcast::AuthInvoker///////////////////////////////////////////// API_EXPORT void API_CALL mk_auth_invoker_do(const mk_auth_invoker ctx, const char *err_msg){ assert(ctx); @@ -331,3 +392,14 @@ API_EXPORT void API_CALL mk_auth_invoker_do(const mk_auth_invoker ctx, const cha (*invoker)(err_msg ? err_msg : ""); } +API_EXPORT mk_auth_invoker API_CALL mk_auth_invoker_clone(const mk_auth_invoker ctx){ + assert(ctx); + Broadcast::AuthInvoker *invoker = (Broadcast::AuthInvoker *)ctx; + return new Broadcast::AuthInvoker(*invoker); +} + +API_EXPORT void API_CALL mk_auth_invoker_clone_relase(const mk_auth_invoker ctx){ + assert(ctx); + Broadcast::AuthInvoker *invoker = (Broadcast::AuthInvoker *)ctx; + delete invoker; +} \ No newline at end of file From 4e0919d13b1e6250f7b9e1c6d43103e20f2a1123 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 24 Dec 2019 16:02:41 +0800 Subject: [PATCH 129/272] =?UTF-8?q?=E6=8F=90=E4=BE=9B=E5=AE=8C=E6=88=90?= =?UTF-8?q?=E7=9A=84c=20api=E4=BA=8B=E4=BB=B6=E6=B5=8B=E8=AF=95=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- api/include/events.h | 93 ++++++----- api/tests/server.cpp | 387 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 429 insertions(+), 53 deletions(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 4d88a395..77c2a6d9 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 4d88a395a25e9265c579aa930acdf60796bd4e4c +Subproject commit 77c2a6d9f94ab6e7e40bc1f223eb206d2542cb72 diff --git a/api/include/events.h b/api/include/events.h index 32ee86a2..26a9f5c2 100644 --- a/api/include/events.h +++ b/api/include/events.h @@ -29,19 +29,20 @@ #include "common.h" #include "events_objects.h" + #ifdef __cplusplus extern "C" { #endif -typedef struct{ +typedef struct { /** * 注册或反注册MediaSource事件广播 * @param regist 注册为1,注销为0 * @param sender 该MediaSource对象 */ - void (*on_mk_media_changed)(int regist, - const mk_media_source sender); - + void (API_CALL *on_mk_media_changed)(int regist, + const mk_media_source sender); + /** * 收到rtsp/rtmp推流事件广播,通过该事件控制推流鉴权 * @see mk_publish_auth_invoker_do @@ -49,9 +50,9 @@ typedef struct{ * @param invoker 执行invoker返回鉴权结果 * @param sender 该tcp客户端相关信息 */ - void (*on_mk_media_publish)(const mk_media_info url_info, - const mk_publish_auth_invoker invoker, - const mk_tcp_session sender); + void (API_CALL *on_mk_media_publish)(const mk_media_info url_info, + const mk_publish_auth_invoker invoker, + const mk_tcp_session sender); /** * 播放rtsp/rtmp/http-flv/hls事件广播,通过该事件控制播放鉴权 @@ -60,23 +61,23 @@ typedef struct{ * @param invoker 执行invoker返回鉴权结果 * @param sender 播放客户端相关信息 */ - void (*on_mk_media_play)(const mk_media_info url_info, - const mk_auth_invoker invoker, - const mk_tcp_session sender); + void (API_CALL *on_mk_media_play)(const mk_media_info url_info, + const mk_auth_invoker invoker, + const mk_tcp_session sender); /** * 未找到流后会广播该事件,请在监听该事件后去拉流或其他方式产生流,这样就能按需拉流了 * @param url_info 播放url相关信息 * @param sender 播放客户端相关信息 */ - void (*on_mk_media_not_found)(const mk_media_info url_info, - const mk_tcp_session sender); + void (API_CALL *on_mk_media_not_found)(const mk_media_info url_info, + const mk_tcp_session sender); /** * 某个流无人消费时触发,目的为了实现无人观看时主动断开拉流等业务逻辑 * @param sender 该MediaSource对象 */ - void (*on_mk_media_no_reader)(const mk_media_source sender); + void (API_CALL *on_mk_media_no_reader)(const mk_media_source sender); /** * 收到http api请求广播(包括GET/POST) @@ -85,10 +86,10 @@ typedef struct{ * @param consumed 置1则说明我们要处理该事件 * @param sender http客户端相关信息 */ - void (*on_mk_http_request)(const mk_parser parser, - const mk_http_response_invoker invoker, - int *consumed, - const mk_tcp_session sender); + void (API_CALL *on_mk_http_request)(const mk_parser parser, + const mk_http_response_invoker invoker, + int *consumed, + const mk_tcp_session sender); /** * 在http文件服务器中,收到http访问文件或目录的广播,通过该事件控制访问http目录的权限 @@ -99,12 +100,12 @@ typedef struct{ * @param invoker 执行invoker返回本次访问文件的结果 * @param sender http客户端相关信息 */ - void (*on_mk_http_access)(const mk_parser parser, - const mk_media_info url_info, - const char *path, - int is_dir, - const mk_http_access_path_invoker invoker, - mk_tcp_session sender); + void (API_CALL *on_mk_http_access)(const mk_parser parser, + const mk_media_info url_info, + const char *path, + int is_dir, + const mk_http_access_path_invoker invoker, + mk_tcp_session sender); /** * 在http文件服务器中,收到http访问文件或目录前的广播,通过该事件可以控制http url到文件路径的映射 @@ -114,10 +115,10 @@ typedef struct{ * @param path 文件绝对路径,覆盖之可以重定向到其他文件 * @param sender http客户端相关信息 */ - void (*on_mk_http_before_access)(const mk_parser parser, - const mk_media_info url_info, - char *path, - const mk_tcp_session sender); + void (API_CALL *on_mk_http_before_access)(const mk_parser parser, + const mk_media_info url_info, + char *path, + const mk_tcp_session sender); /** * 该rtsp流是否需要认证?是的话调用invoker并传入realm,否则传入空的realm @@ -125,9 +126,9 @@ typedef struct{ * @param invoker 执行invoker返回是否需要rtsp专属认证 * @param sender rtsp客户端相关信息 */ - void (*on_mk_rtsp_get_realm)(const mk_media_info url_info, - const mk_rtsp_get_realm_invoker invoker, - const mk_tcp_session sender); + void (API_CALL *on_mk_rtsp_get_realm)(const mk_media_info url_info, + const mk_rtsp_get_realm_invoker invoker, + const mk_tcp_session sender); /** * 请求认证用户密码事件,user_name为用户名,must_no_encrypt如果为true,则必须提供明文密码(因为此时是base64认证方式),否则会导致认证失败 @@ -139,25 +140,25 @@ typedef struct{ * @param invoker 执行invoker返回rtsp专属认证的密码 * @param sender rtsp客户端信息 */ - void (*on_mk_rtsp_auth)(const mk_media_info url_info, - const char *realm, - const char *user_name, - int must_no_encrypt, - const mk_rtsp_auth_invoker invoker, - const mk_tcp_session sender); + void (API_CALL *on_mk_rtsp_auth)(const mk_media_info url_info, + const char *realm, + const char *user_name, + int must_no_encrypt, + const mk_rtsp_auth_invoker invoker, + const mk_tcp_session sender); /** * 录制mp4分片文件成功后广播 */ - void (*on_mk_record_mp4)(const mk_mp4_info mp4); + void (API_CALL *on_mk_record_mp4)(const mk_mp4_info mp4); /** * shell登录鉴权 */ - void (*on_mk_shell_login)(const char *user_name, - const char *passwd, - const mk_auth_invoker invoker, - const mk_tcp_session sender); + void (API_CALL *on_mk_shell_login)(const char *user_name, + const char *passwd, + const mk_auth_invoker invoker, + const mk_tcp_session sender); /** * 停止rtsp/rtmp/http-flv会话后流量汇报事件广播 @@ -166,11 +167,11 @@ typedef struct{ * @param total_seconds 本次tcp会话时长,单位秒 * @param is_player 客户端是否为播放器 */ - void (*on_mk_flow_report)(const mk_media_info url_info, - uint64_t total_bytes, - uint64_t total_seconds, - int is_player, - const mk_tcp_session sender); + void (API_CALL *on_mk_flow_report)(const mk_media_info url_info, + uint64_t total_bytes, + uint64_t total_seconds, + int is_player, + const mk_tcp_session sender); } mk_events; diff --git a/api/tests/server.cpp b/api/tests/server.cpp index 514f4941..38acf606 100644 --- a/api/tests/server.cpp +++ b/api/tests/server.cpp @@ -27,22 +27,397 @@ #include #include #include "mediakit.h" + #ifdef _WIN32 #include "windows.h" #else + #include "unistd.h" + #endif using namespace std; -int main(int argc,char *argv[]){ - mk_env_init1(0,0,1, (string(argv[0]) + ".ini").data(),0, nullptr, nullptr); - mk_http_server_start(80,false); - mk_rtsp_server_start(554,false); - mk_rtmp_server_start(1935,false); + +#define LOG_LEV 4 + +/** + * 注册或反注册MediaSource事件广播 + * @param regist 注册为1,注销为0 + * @param sender 该MediaSource对象 + */ +void API_CALL on_mk_media_changed(int regist, + const mk_media_source sender) { + log_printf(LOG_LEV,"%d %s/%s/%s/%s",(int)regist, + mk_media_source_get_schema(sender), + mk_media_source_get_vhost(sender), + mk_media_source_get_app(sender), + mk_media_source_get_stream(sender)); + +} + +/** + * 收到rtsp/rtmp推流事件广播,通过该事件控制推流鉴权 + * @see mk_publish_auth_invoker_do + * @param url_info 推流url相关信息 + * @param invoker 执行invoker返回鉴权结果 + * @param sender 该tcp客户端相关信息 + */ +void API_CALL on_mk_media_publish(const mk_media_info url_info, + const mk_publish_auth_invoker invoker, + const mk_tcp_session sender) { + log_printf(LOG_LEV, + "client info, local: %s:%d, peer: %s:%d\n" + "%s/%s/%s/%s, url params: %s", + mk_tcp_session_local_ip(sender), + mk_tcp_session_local_port(sender), + mk_tcp_session_peer_ip(sender), + mk_tcp_session_peer_port(sender), + mk_media_info_get_schema(url_info), + mk_media_info_get_vhost(url_info), + mk_media_info_get_app(url_info), + mk_media_info_get_stream(url_info), + mk_media_info_get_params(url_info)); + + //允许推流,并且允许转rtxp/hls/mp4 + mk_publish_auth_invoker_do(invoker, nullptr, 1, 1, true); +} + +/** + * 播放rtsp/rtmp/http-flv/hls事件广播,通过该事件控制播放鉴权 + * @see mk_auth_invoker_do + * @param url_info 播放url相关信息 + * @param invoker 执行invoker返回鉴权结果 + * @param sender 播放客户端相关信息 + */ +void API_CALL on_mk_media_play(const mk_media_info url_info, + const mk_auth_invoker invoker, + const mk_tcp_session sender) { + + log_printf(LOG_LEV, + "client info, local: %s:%d, peer: %s:%d\n" + "%s/%s/%s/%s, url params: %s", + mk_tcp_session_local_ip(sender), + mk_tcp_session_local_port(sender), + mk_tcp_session_peer_ip(sender), + mk_tcp_session_peer_port(sender), + mk_media_info_get_schema(url_info), + mk_media_info_get_vhost(url_info), + mk_media_info_get_app(url_info), + mk_media_info_get_stream(url_info), + mk_media_info_get_params(url_info)); + + //允许播放 + mk_auth_invoker_do(invoker, nullptr); +} + +/** + * 未找到流后会广播该事件,请在监听该事件后去拉流或其他方式产生流,这样就能按需拉流了 + * @param url_info 播放url相关信息 + * @param sender 播放客户端相关信息 + */ +void API_CALL on_mk_media_not_found(const mk_media_info url_info, + const mk_tcp_session sender) { + log_printf(LOG_LEV, + "client info, local: %s:%d, peer: %s:%d\n" + "%s/%s/%s/%s, url params: %s", + mk_tcp_session_local_ip(sender), + mk_tcp_session_local_port(sender), + mk_tcp_session_peer_ip(sender), + mk_tcp_session_peer_port(sender), + mk_media_info_get_schema(url_info), + mk_media_info_get_vhost(url_info), + mk_media_info_get_app(url_info), + mk_media_info_get_stream(url_info), + mk_media_info_get_params(url_info)); +} + +/** + * 某个流无人消费时触发,目的为了实现无人观看时主动断开拉流等业务逻辑 + * @param sender 该MediaSource对象 + */ +void API_CALL on_mk_media_no_reader(const mk_media_source sender) { + log_printf(LOG_LEV, + "%s/%s/%s/%s", + mk_media_source_get_schema(sender), + mk_media_source_get_vhost(sender), + mk_media_source_get_app(sender), + mk_media_source_get_stream(sender)); +} + +/** + * 收到http api请求广播(包括GET/POST) + * @param parser http请求内容对象 + * @param invoker 执行该invoker返回http回复 + * @param consumed 置1则说明我们要处理该事件 + * @param sender http客户端相关信息 + */ +//测试url : http://127.0.0.1/api/test +void API_CALL on_mk_http_request(const mk_parser parser, + const mk_http_response_invoker invoker, + int *consumed, + const mk_tcp_session sender) { + + log_printf(LOG_LEV, + "client info, local: %s:%d, peer: %s:%d\n" + "%s %s?%s %s\n" + "User-Agent: %s\n" + "%s", + mk_tcp_session_local_ip(sender), + mk_tcp_session_local_port(sender), + mk_tcp_session_peer_ip(sender), + mk_tcp_session_peer_port(sender), + mk_parser_get_method(parser), + mk_parser_get_url(parser), + mk_parser_get_url_params(parser), + mk_parser_get_tail(parser), + mk_parser_get_header(parser, "User-Agent"), + mk_parser_get_content(parser)); + + const char *url = mk_parser_get_url(parser); + if(strcasecmp(url,"/api/test") != 0){ + *consumed = false; + return; + } + + //只拦截api: /api/test + *consumed = true; + const char *response_header[] = {"Content-Type","text/html",NULL}; + const char *content = + "" + "" + "hello world" + "" + "" + "

    hello world


    " + "
    ""ZLMediaKit-4.0
    " + "" + ""; + mk_http_response_invoker_do(invoker,"200 OK",response_header,content); +} + +/** + * 在http文件服务器中,收到http访问文件或目录的广播,通过该事件控制访问http目录的权限 + * @param parser http请求内容对象 + * @param url_info 请求url相关信息 + * @param path 文件绝对路径 + * @param is_dir path是否为文件夹 + * @param invoker 执行invoker返回本次访问文件的结果 + * @param sender http客户端相关信息 + */ +void API_CALL on_mk_http_access(const mk_parser parser, + const mk_media_info url_info, + const char *path, + int is_dir, + const mk_http_access_path_invoker invoker, + mk_tcp_session sender) { + + log_printf(LOG_LEV, + "client info, local: %s:%d, peer: %s:%d, path: %s ,is_dir: %d\n" + "%s %s?%s %s\n" + "User-Agent: %s\n" + "%s", + mk_tcp_session_local_ip(sender), + mk_tcp_session_local_port(sender), + mk_tcp_session_peer_ip(sender), + mk_tcp_session_peer_port(sender), + path,(int)is_dir, + mk_parser_get_method(parser), + mk_parser_get_url(parser), + mk_parser_get_url_params(parser), + mk_parser_get_tail(parser), + mk_parser_get_header(parser,"User-Agent"), + mk_parser_get_content(parser)); + + //有访问权限,每次访问文件都需要鉴权 + mk_http_access_path_invoker_do(invoker, nullptr, nullptr, 0); +} + +/** + * 在http文件服务器中,收到http访问文件或目录前的广播,通过该事件可以控制http url到文件路径的映射 + * 在该事件中通过自行覆盖path参数,可以做到譬如根据虚拟主机或者app选择不同http根目录的目的 + * @param parser http请求内容对象 + * @param url_info 请求url相关信息 + * @param path 文件绝对路径,覆盖之可以重定向到其他文件 + * @param sender http客户端相关信息 + */ +void API_CALL on_mk_http_before_access(const mk_parser parser, + const mk_media_info url_info, + char *path, + const mk_tcp_session sender) { + log_printf(LOG_LEV, + "client info, local: %s:%d, peer: %s:%d, path: %s\n" + "%s %s?%s %s\n" + "User-Agent: %s\n" + "%s", + mk_tcp_session_local_ip(sender), + mk_tcp_session_local_port(sender), + mk_tcp_session_peer_ip(sender), + mk_tcp_session_peer_port(sender), + path, + mk_parser_get_method(parser), + mk_parser_get_url(parser), + mk_parser_get_url_params(parser), + mk_parser_get_tail(parser), + mk_parser_get_header(parser, "User-Agent"), + mk_parser_get_content(parser)); + //覆盖path的值可以重定向文件 +} + +/** + * 该rtsp流是否需要认证?是的话调用invoker并传入realm,否则传入空的realm + * @param url_info 请求rtsp url相关信息 + * @param invoker 执行invoker返回是否需要rtsp专属认证 + * @param sender rtsp客户端相关信息 + */ +void API_CALL on_mk_rtsp_get_realm(const mk_media_info url_info, + const mk_rtsp_get_realm_invoker invoker, + const mk_tcp_session sender) { + log_printf(LOG_LEV, + "client info, local: %s:%d, peer: %s:%d\n" + "%s/%s/%s/%s, url params: %s", + mk_tcp_session_local_ip(sender), + mk_tcp_session_local_port(sender), + mk_tcp_session_peer_ip(sender), + mk_tcp_session_peer_port(sender), + mk_media_info_get_schema(url_info), + mk_media_info_get_vhost(url_info), + mk_media_info_get_app(url_info), + mk_media_info_get_stream(url_info), + mk_media_info_get_params(url_info)); + + //rtsp播放默认鉴权 + mk_rtsp_get_realm_invoker_do(invoker, "zlmediakit"); +} + +/** + * 请求认证用户密码事件,user_name为用户名,must_no_encrypt如果为true,则必须提供明文密码(因为此时是base64认证方式),否则会导致认证失败 + * 获取到密码后请调用invoker并输入对应类型的密码和密码类型,invoker执行时会匹配密码 + * @param url_info 请求rtsp url相关信息 + * @param realm rtsp认证realm + * @param user_name rtsp认证用户名 + * @param must_no_encrypt 如果为true,则必须提供明文密码(因为此时是base64认证方式),否则会导致认证失败 + * @param invoker 执行invoker返回rtsp专属认证的密码 + * @param sender rtsp客户端信息 + */ +void API_CALL on_mk_rtsp_auth(const mk_media_info url_info, + const char *realm, + const char *user_name, + int must_no_encrypt, + const mk_rtsp_auth_invoker invoker, + const mk_tcp_session sender) { + + log_printf(LOG_LEV, + "client info, local: %s:%d, peer: %s:%d\n" + "%s/%s/%s/%s, url params: %s\n" + "realm: %s, user_name: %s, must_no_encrypt: %d", + mk_tcp_session_local_ip(sender), + mk_tcp_session_local_port(sender), + mk_tcp_session_peer_ip(sender), + mk_tcp_session_peer_port(sender), + mk_media_info_get_schema(url_info), + mk_media_info_get_vhost(url_info), + mk_media_info_get_app(url_info), + mk_media_info_get_stream(url_info), + mk_media_info_get_params(url_info), + realm,user_name,(int)must_no_encrypt); + + //rtsp播放用户名跟密码一致 + mk_rtsp_auth_invoker_do(invoker,false,user_name); +} + +/** + * 录制mp4分片文件成功后广播 + */ +void API_CALL on_mk_record_mp4(const mk_mp4_info mp4) { + log_printf(LOG_LEV, + "\nstart_time: %d\n" + "time_len: %d\n" + "file_size: %d\n" + "file_path: %s\n" + "file_name: %s\n" + "folder: %s\n" + "url: %s\n" + "vhost: %s\n" + "app: %s\n" + "stream: %s\n", + mk_mp4_info_get_start_time(mp4), + mk_mp4_info_get_time_len(mp4), + mk_mp4_info_get_file_size(mp4), + mk_mp4_info_get_file_path(mp4), + mk_mp4_info_get_file_name(mp4), + mk_mp4_info_get_folder(mp4), + mk_mp4_info_get_url(mp4), + mk_mp4_info_get_vhost(mp4), + mk_mp4_info_get_app(mp4), + mk_mp4_info_get_stream(mp4)); +} + +/** + * shell登录鉴权 + */ +void API_CALL on_mk_shell_login(const char *user_name, + const char *passwd, + const mk_auth_invoker invoker, + const mk_tcp_session sender) { + log_printf(LOG_LEV,"client info, local: %s:%d, peer: %s:%d\n" + "user_name: %s, passwd: %s", + mk_tcp_session_local_ip(sender), + mk_tcp_session_local_port(sender), + mk_tcp_session_peer_ip(sender), + mk_tcp_session_peer_port(sender), + user_name, passwd); + //允许登录shell + mk_auth_invoker_do(invoker, nullptr); +} + +/** + * 停止rtsp/rtmp/http-flv会话后流量汇报事件广播 + * @param url_info 播放url相关信息 + * @param total_bytes 耗费上下行总流量,单位字节数 + * @param total_seconds 本次tcp会话时长,单位秒 + * @param is_player 客户端是否为播放器 + */ +void API_CALL on_mk_flow_report(const mk_media_info url_info, + uint64_t total_bytes, + uint64_t total_seconds, + int is_player, + const mk_tcp_session sender) { + log_printf(LOG_LEV,"client info, local: %s:%d, peer: %s:%d\n" + "total_bytes: %d, total_seconds: %d, is_player: %d", + mk_tcp_session_local_ip(sender), + mk_tcp_session_local_port(sender), + mk_tcp_session_peer_ip(sender), + mk_tcp_session_peer_port(sender), + (int)total_bytes, (int)total_seconds, (int)is_player); +} + +int main(int argc, char *argv[]) { + mk_env_init1(0, 0, 1, (string(argv[0]) + ".ini").data(), 0, nullptr, nullptr); + mk_http_server_start(80, false); + mk_rtsp_server_start(554, false); + mk_rtmp_server_start(1935, false); mk_rtp_server_start(10000); + + mk_events events = { + .on_mk_media_changed = on_mk_media_changed, + .on_mk_media_publish = on_mk_media_publish, + .on_mk_media_play = on_mk_media_play, + .on_mk_media_not_found = on_mk_media_not_found, + .on_mk_media_no_reader = on_mk_media_no_reader, + .on_mk_http_request = on_mk_http_request, + .on_mk_http_access = on_mk_http_access, + .on_mk_http_before_access = on_mk_http_before_access, + .on_mk_rtsp_get_realm = on_mk_rtsp_get_realm, + .on_mk_rtsp_auth = on_mk_rtsp_auth, + .on_mk_record_mp4 = on_mk_record_mp4, + .on_mk_shell_login = on_mk_shell_login, + .on_mk_flow_report = on_mk_flow_report + }; + mk_events_listen(&events); + static bool flag = true; signal(SIGINT, [](int) { flag = false; });// 设置退出信号 - while (flag){ + while (flag) { #ifdef _WIN32 Sleep(1000); #else From a3c482c1ef20185086ad950a9ea2cd90ea759804 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 24 Dec 2019 16:09:09 +0800 Subject: [PATCH 130/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0shell=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/common.h | 7 +++++++ api/source/common.cpp | 14 ++++++++++++++ api/tests/server.cpp | 1 + 3 files changed, 22 insertions(+) diff --git a/api/include/common.h b/api/include/common.h index 041968c4..c54f7170 100755 --- a/api/include/common.h +++ b/api/include/common.h @@ -132,6 +132,13 @@ API_EXPORT uint16_t API_CALL mk_rtmp_server_start(uint16_t port, int ssl); */ API_EXPORT uint16_t API_CALL mk_rtp_server_start(uint16_t port); +/** + * 创建shell服务器 + * @param port shell监听端口 + * @return 0:失败,非0:端口号 + */ +API_EXPORT uint16_t API_CALL mk_shell_server_start(uint16_t port); + /** * 打印日志 * @param level 日志级别,支持0~4 diff --git a/api/source/common.cpp b/api/source/common.cpp index 287559d3..34b701d1 100755 --- a/api/source/common.cpp +++ b/api/source/common.cpp @@ -35,6 +35,7 @@ #include "Rtsp/RtspSession.h" #include "Rtmp/RtmpSession.h" #include "Http/HttpSession.h" +#include "Shell/ShellSession.h" using namespace std; using namespace toolkit; using namespace mediakit; @@ -42,6 +43,7 @@ using namespace mediakit; static TcpServer::Ptr rtsp_server[2]; static TcpServer::Ptr rtmp_server[2]; static TcpServer::Ptr http_server[2]; +static TcpServer::Ptr shell_server; #ifdef ENABLE_RTPPROXY #include "Rtp/UdpRecver.h" @@ -190,6 +192,18 @@ API_EXPORT uint16_t API_CALL mk_rtp_server_start(uint16_t port){ #endif } +API_EXPORT uint16_t API_CALL mk_shell_server_start(uint16_t port){ + try { + shell_server = std::make_shared(); + shell_server->start(port); + return shell_server->getPort(); + } catch (std::exception &ex) { + shell_server.reset(); + WarnL << ex.what(); + return 0; + } +} + API_EXPORT void API_CALL mk_log_printf(int level, const char *file, const char *function, int line, const char *fmt, ...) { assert(file && function && fmt); LogContextCapturer info(Logger::Instance(), (LogLevel) level, file, function, line); diff --git a/api/tests/server.cpp b/api/tests/server.cpp index 38acf606..05d3564e 100644 --- a/api/tests/server.cpp +++ b/api/tests/server.cpp @@ -396,6 +396,7 @@ int main(int argc, char *argv[]) { mk_http_server_start(80, false); mk_rtsp_server_start(554, false); mk_rtmp_server_start(1935, false); + mk_shell_server_start(9000); mk_rtp_server_start(10000); mk_events events = { From aa03d7d0bb9ec2cf7441f720e25246f20c193c11 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 24 Dec 2019 16:19:33 +0800 Subject: [PATCH 131/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0hls/mp4=E5=BD=95?= =?UTF-8?q?=E5=88=B6=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mediakit.h | 2 +- api/include/{flvrecorder.h => recorder.h} | 48 ++++++++++++++++++-- api/source/{flvrecorder.cpp => recorder.cpp} | 23 +++++++++- 3 files changed, 68 insertions(+), 5 deletions(-) rename api/include/{flvrecorder.h => recorder.h} (56%) rename api/source/{flvrecorder.cpp => recorder.cpp} (69%) diff --git a/api/include/mediakit.h b/api/include/mediakit.h index 3d38b9e8..a6f42496 100755 --- a/api/include/mediakit.h +++ b/api/include/mediakit.h @@ -31,7 +31,7 @@ #include "httpdownloader.h" #include "media.h" #include "proxyplayer.h" -#include "flvrecorder.h" +#include "recorder.h" #include "player.h" #include "pusher.h" #include "events.h" diff --git a/api/include/flvrecorder.h b/api/include/recorder.h similarity index 56% rename from api/include/flvrecorder.h rename to api/include/recorder.h index 323d33a6..de697872 100644 --- a/api/include/flvrecorder.h +++ b/api/include/recorder.h @@ -24,8 +24,8 @@ * SOFTWARE. */ -#ifndef MK_FLV_RECORDER_API_H_ -#define MK_FLV_RECORDER_API_H_ +#ifndef MK_RECORDER_API_H_ +#define MK_RECORDER_API_H_ #include "common.h" @@ -33,6 +33,8 @@ extern "C" { #endif +///////////////////////////////////////////flv录制///////////////////////////////////////////// + typedef void* mk_flv_recorder; /** @@ -57,8 +59,48 @@ API_EXPORT void API_CALL mk_flv_recorder_release(mk_flv_recorder ctx); */ API_EXPORT int API_CALL mk_flv_recorder_start(mk_flv_recorder ctx, const char *app, const char *stream, const char *file_path); + +///////////////////////////////////////////hls/mp4录制///////////////////////////////////////////// + +/** + * 获取录制状态 + * @param type 0:hls,1:MP4 + * @param vhost 虚拟主机 + * @param app 应用名 + * @param stream 流id + * @return 录制状态,0:未录制,1:等待MediaSource注册,注册成功后立即开始录制,2:MediaSource已注册,并且正在录制 + */ +API_EXPORT int mk_recorder_status(int type, const char *vhost, const char *app, const char *stream); + +/** + * 开始录制 + * @param type 0:hls,1:MP4 + * @param vhost 虚拟主机 + * @param app 应用名 + * @param stream 流id + * @param wait_for_record 是否等待流注册后再录制,未注册时,置false将返回失败 + * @param continue_record 流注销时是否继续等待录制还是立即停止录制 + * @return 0代表成功,负数代表失败 + */ +API_EXPORT int mk_recorder_start(int type, const char *vhost, const char *app, const char *stream,bool wait_for_record, bool continue_record); + +/** + * 停止录制 + * @param type 0:hls,1:MP4 + * @param vhost 虚拟主机 + * @param app 应用名 + * @param stream 流id + * @return 1:成功,0:失败 + */ +API_EXPORT int mk_recorder_stop(int type, const char *vhost, const char *app, const char *stream); + +/** + * 停止所有录制,一般程序退出时调用 + */ +API_EXPORT void mk_recorder_stop_all(); + #ifdef __cplusplus } #endif -#endif /* MK_FLV_RECORDER_API_H_ */ +#endif /* MK_RECORDER_API_H_ */ diff --git a/api/source/flvrecorder.cpp b/api/source/recorder.cpp similarity index 69% rename from api/source/flvrecorder.cpp rename to api/source/recorder.cpp index 93882320..04030d37 100644 --- a/api/source/flvrecorder.cpp +++ b/api/source/recorder.cpp @@ -24,8 +24,9 @@ * SOFTWARE. */ -#include "flvrecorder.h" +#include "recorder.h" #include "Rtmp/FlvMuxer.h" +#include "Record/Recorder.h" using namespace toolkit; using namespace mediakit; @@ -48,4 +49,24 @@ API_EXPORT int API_CALL mk_flv_recorder_start(mk_flv_recorder ctx, const char *a WarnL << ex.what(); return -1; } +} + +///////////////////////////////////////////hls/mp4录制///////////////////////////////////////////// +API_EXPORT int mk_recorder_status(int type, const char *vhost, const char *app, const char *stream){ + assert(vhost && app && stream); + return Recorder::getRecordStatus((Recorder::type)type,vhost,app,stream); +} + +API_EXPORT int mk_recorder_start(int type, const char *vhost, const char *app, const char *stream,bool wait_for_record, bool continue_record){ + assert(vhost && app && stream); + return Recorder::startRecord((Recorder::type)type,vhost,app,stream,wait_for_record,continue_record); +} + +API_EXPORT int mk_recorder_stop(int type, const char *vhost, const char *app, const char *stream){ + assert(vhost && app && stream); + return Recorder::stopRecord((Recorder::type)type,vhost,app,stream); +} + +API_EXPORT void mk_recorder_stop_all(){ + Recorder::stopAll(); } \ No newline at end of file From 7b3527e7f5a5a5e2410372b5fe65b08ba8835141 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 24 Dec 2019 16:20:39 +0800 Subject: [PATCH 132/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0hls/mp4=E5=BD=95?= =?UTF-8?q?=E5=88=B6=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/recorder.h | 8 ++++---- api/source/recorder.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/include/recorder.h b/api/include/recorder.h index de697872..33c05949 100644 --- a/api/include/recorder.h +++ b/api/include/recorder.h @@ -70,7 +70,7 @@ API_EXPORT int API_CALL mk_flv_recorder_start(mk_flv_recorder ctx, const char *a * @param stream 流id * @return 录制状态,0:未录制,1:等待MediaSource注册,注册成功后立即开始录制,2:MediaSource已注册,并且正在录制 */ -API_EXPORT int mk_recorder_status(int type, const char *vhost, const char *app, const char *stream); +API_EXPORT int API_CALL mk_recorder_status(int type, const char *vhost, const char *app, const char *stream); /** * 开始录制 @@ -82,7 +82,7 @@ API_EXPORT int mk_recorder_status(int type, const char *vhost, const char *app, * @param continue_record 流注销时是否继续等待录制还是立即停止录制 * @return 0代表成功,负数代表失败 */ -API_EXPORT int mk_recorder_start(int type, const char *vhost, const char *app, const char *stream,bool wait_for_record, bool continue_record); +API_EXPORT int API_CALL mk_recorder_start(int type, const char *vhost, const char *app, const char *stream,bool wait_for_record, bool continue_record); /** * 停止录制 @@ -92,12 +92,12 @@ API_EXPORT int mk_recorder_start(int type, const char *vhost, const char *app, c * @param stream 流id * @return 1:成功,0:失败 */ -API_EXPORT int mk_recorder_stop(int type, const char *vhost, const char *app, const char *stream); +API_EXPORT int API_CALL mk_recorder_stop(int type, const char *vhost, const char *app, const char *stream); /** * 停止所有录制,一般程序退出时调用 */ -API_EXPORT void mk_recorder_stop_all(); +API_EXPORT void API_CALL mk_recorder_stop_all(); #ifdef __cplusplus } diff --git a/api/source/recorder.cpp b/api/source/recorder.cpp index 04030d37..4763faa3 100644 --- a/api/source/recorder.cpp +++ b/api/source/recorder.cpp @@ -52,21 +52,21 @@ API_EXPORT int API_CALL mk_flv_recorder_start(mk_flv_recorder ctx, const char *a } ///////////////////////////////////////////hls/mp4录制///////////////////////////////////////////// -API_EXPORT int mk_recorder_status(int type, const char *vhost, const char *app, const char *stream){ +API_EXPORT int API_CALL mk_recorder_status(int type, const char *vhost, const char *app, const char *stream){ assert(vhost && app && stream); return Recorder::getRecordStatus((Recorder::type)type,vhost,app,stream); } -API_EXPORT int mk_recorder_start(int type, const char *vhost, const char *app, const char *stream,bool wait_for_record, bool continue_record){ +API_EXPORT int API_CALL mk_recorder_start(int type, const char *vhost, const char *app, const char *stream,bool wait_for_record, bool continue_record){ assert(vhost && app && stream); return Recorder::startRecord((Recorder::type)type,vhost,app,stream,wait_for_record,continue_record); } -API_EXPORT int mk_recorder_stop(int type, const char *vhost, const char *app, const char *stream){ +API_EXPORT int API_CALL mk_recorder_stop(int type, const char *vhost, const char *app, const char *stream){ assert(vhost && app && stream); return Recorder::stopRecord((Recorder::type)type,vhost,app,stream); } -API_EXPORT void mk_recorder_stop_all(){ +API_EXPORT void API_CALL mk_recorder_stop_all(){ Recorder::stopAll(); } \ No newline at end of file From eab5a00c5cd9f9b9055be4087b2894f6ea528654 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 24 Dec 2019 16:47:24 +0800 Subject: [PATCH 133/272] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E7=BC=96=E8=AF=91?= =?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 --- api/tests/server.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/tests/server.cpp b/api/tests/server.cpp index 05d3564e..3d1e57ec 100644 --- a/api/tests/server.cpp +++ b/api/tests/server.cpp @@ -25,6 +25,7 @@ */ #include +#include #include #include "mediakit.h" @@ -174,7 +175,7 @@ void API_CALL on_mk_http_request(const mk_parser parser, mk_parser_get_content(parser)); const char *url = mk_parser_get_url(parser); - if(strcasecmp(url,"/api/test") != 0){ + if(strcmp(url,"/api/test") != 0){ *consumed = false; return; } From cdd49df443c8a8faa850abba746bc98bf605012f Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 24 Dec 2019 18:28:39 +0800 Subject: [PATCH 134/272] =?UTF-8?q?c=20api=E6=B7=BB=E5=8A=A0=E7=BC=BA?= =?UTF-8?q?=E7=9C=81=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/source/events.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/api/source/events.cpp b/api/source/events.cpp index 781899fd..832ceb92 100644 --- a/api/source/events.cpp +++ b/api/source/events.cpp @@ -75,6 +75,8 @@ API_EXPORT void API_CALL mk_events_listen(const mk_events *events){ is_dir, (mk_http_access_path_invoker)&invoker, (mk_tcp_session)&sender); + } else{ + invoker("","",0); } }); @@ -96,6 +98,8 @@ API_EXPORT void API_CALL mk_events_listen(const mk_events *events){ s_events.on_mk_rtsp_get_realm((mk_media_info) &args, (mk_rtsp_get_realm_invoker) &invoker, (mk_tcp_session) &sender); + }else{ + invoker(""); } }); @@ -115,6 +119,11 @@ API_EXPORT void API_CALL mk_events_listen(const mk_events *events){ s_events.on_mk_media_publish((mk_media_info) &args, (mk_publish_auth_invoker) &invoker, (mk_tcp_session) &sender); + }else{ + GET_CONFIG(bool,toRtxp,General::kPublishToRtxp); + GET_CONFIG(bool,toHls,General::kPublishToHls); + GET_CONFIG(bool,toMP4,General::kPublishToMP4); + invoker("",toRtxp,toHls,toMP4); } }); @@ -123,6 +132,8 @@ API_EXPORT void API_CALL mk_events_listen(const mk_events *events){ s_events.on_mk_media_play((mk_media_info) &args, (mk_auth_invoker) &invoker, (mk_tcp_session) &sender); + }else{ + invoker(""); } }); @@ -132,6 +143,8 @@ API_EXPORT void API_CALL mk_events_listen(const mk_events *events){ passwd.c_str(), (mk_auth_invoker) &invoker, (mk_tcp_session) &sender); + }else{ + invoker(""); } }); From 82ca8015a5d9cdd8619dececed4074bcb54f93ba Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 24 Dec 2019 18:32:16 +0800 Subject: [PATCH 135/272] =?UTF-8?q?=E5=AE=8C=E5=96=84readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README_CN.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README_CN.md b/README_CN.md index 37917955..8135a8e9 100644 --- a/README_CN.md +++ b/README_CN.md @@ -14,11 +14,13 @@ git submodule update --init - 打包多种流媒体协议(RTSP/RTMP/HLS/HTTP-FLV/Websocket-FLV),支持协议间的互相转换,提供一站式的服务。 - 使用epoll+线程池+异步网络IO模式开发,并发性能优越。 - 已实现主流的的H264/H265+AAC流媒体方案,代码精简,脉络清晰,适合学习。 -- 编码格式与框架代码解耦,方便自由简洁的添加支持其他编码格式 +- 编码格式与框架代码解耦,方便自由简洁的添加支持其他编码格式。 - 代码经过大量的稳定性、性能测试,可满足商用服务器项目。 -- 支持linux、macos、ios、android、windows平台 -- 支持画面秒开(GOP缓存)、极低延时([500毫秒内,最低可达100毫秒](https://github.com/zlmediakit/ZLMediaKit/wiki/%E5%BB%B6%E6%97%B6%E6%B5%8B%E8%AF%95)) -- [ZLMediaKit高并发实现原理](https://github.com/xiongziliang/ZLMediaKit/wiki/ZLMediaKit%E9%AB%98%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86) +- 支持linux、macos、ios、android、windows平台。 +- 支持画面秒开(GOP缓存)、极低延时([500毫秒内,最低可达100毫秒](https://github.com/zlmediakit/ZLMediaKit/wiki/%E5%BB%B6%E6%97%B6%E6%B5%8B%E8%AF%95))。 +- [ZLMediaKit高并发实现原理](https://github.com/xiongziliang/ZLMediaKit/wiki/ZLMediaKit%E9%AB%98%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86)。 +- 提供完善的标准C API,可以作SDK用,或供其他语言调用。 +- 提供完整的MediaServer服务器,可以直接部署为商用服务器。 ## 项目定位 - 移动嵌入式跨平台流媒体解决方案。 From 725e75cd233b7904ef769c042393afb619cd0d43 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 24 Dec 2019 18:34:10 +0800 Subject: [PATCH 136/272] =?UTF-8?q?=E5=AE=8C=E5=96=84readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README_CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_CN.md b/README_CN.md index 8135a8e9..0a4c005d 100644 --- a/README_CN.md +++ b/README_CN.md @@ -20,7 +20,7 @@ git submodule update --init - 支持画面秒开(GOP缓存)、极低延时([500毫秒内,最低可达100毫秒](https://github.com/zlmediakit/ZLMediaKit/wiki/%E5%BB%B6%E6%97%B6%E6%B5%8B%E8%AF%95))。 - [ZLMediaKit高并发实现原理](https://github.com/xiongziliang/ZLMediaKit/wiki/ZLMediaKit%E9%AB%98%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86)。 - 提供完善的标准C API,可以作SDK用,或供其他语言调用。 -- 提供完整的MediaServer服务器,可以直接部署为商用服务器。 +- 提供完整的MediaServer服务器,可以免开发直接部署为商用服务器。 ## 项目定位 - 移动嵌入式跨平台流媒体解决方案。 From 3f9f9c4d81b9b849421a7ee0a9200d9972be99cf Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 24 Dec 2019 18:35:46 +0800 Subject: [PATCH 137/272] =?UTF-8?q?=E5=AE=8C=E5=96=84readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 418 +++++++++++++++++++++++++++------------------------ README_CN.md | 380 ---------------------------------------------- README_en.md | 356 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 578 insertions(+), 576 deletions(-) delete mode 100644 README_CN.md create mode 100644 README_en.md diff --git a/README.md b/README.md index 400b9134..6ed4253c 100644 --- a/README.md +++ b/README.md @@ -1,236 +1,245 @@ ![logo](https://raw.githubusercontent.com/zlmediakit/ZLMediaKit/master/logo.png) -# A lightweight ,high performance and stable stream server and client framework based on C++11. +[english readme](https://github.com/xiongziliang/ZLMediaKit/blob/master/README_en.md) +# 一个基于C++11的高性能运营级流媒体服务框架 [![Build Status](https://travis-ci.org/xiongziliang/ZLMediaKit.svg?branch=master)](https://travis-ci.org/xiongziliang/ZLMediaKit) -[中文](https://github.com/xiongziliang/ZLMediaKit/blob/master/README_CN.md) -## Why ZLMediaKit? -- Developed based on C++ 11, the code is stable and reliable, avoiding the use of raw pointers, cross-platform porting is simple and convenient, and the code is clear and concise. -- Support rich streaming media protocols(`RTSP/RTMP/HLS/HTTP-FLV/Websocket-flv`),and support Inter-protocol conversion. -- Multiplexing asynchronous network IO based on epoll and multi thread,extreme performance. -- Well performance and stable test,can be used commercially. -- Support linux, macos, ios, android, Windows Platforms. -- Very low latency(lower then one second), video opened immediately. +## 国内用户请使用gitee镜像下载 +``` +git clone --depth 1 https://gitee.com/xiahcu/ZLMediaKit +cd ZLMediaKit +git submodule update --init +``` +## 项目特点 +- 基于C++11开发,避免使用裸指针,代码稳定可靠;同时跨平台移植简单方便,代码清晰简洁。 +- 打包多种流媒体协议(RTSP/RTMP/HLS/HTTP-FLV/Websocket-FLV),支持协议间的互相转换,提供一站式的服务。 +- 使用epoll+线程池+异步网络IO模式开发,并发性能优越。 +- 已实现主流的的H264/H265+AAC流媒体方案,代码精简,脉络清晰,适合学习。 +- 编码格式与框架代码解耦,方便自由简洁的添加支持其他编码格式。 +- 代码经过大量的稳定性、性能测试,可满足商用服务器项目。 +- 支持linux、macos、ios、android、windows平台。 +- 支持画面秒开(GOP缓存)、极低延时([500毫秒内,最低可达100毫秒](https://github.com/zlmediakit/ZLMediaKit/wiki/%E5%BB%B6%E6%97%B6%E6%B5%8B%E8%AF%95))。 +- [ZLMediaKit高并发实现原理](https://github.com/xiongziliang/ZLMediaKit/wiki/ZLMediaKit%E9%AB%98%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86)。 +- 提供完善的标准C API,可以作SDK用,或供其他语言调用。 +- 提供完整的MediaServer服务器,可以免开发直接部署为商用服务器。 -## Features +## 项目定位 +- 移动嵌入式跨平台流媒体解决方案。 +- 商用级流媒体服务器。 +- 网络编程二次开发SDK。 + +## 功能清单 - RTSP - - RTSP[S] server,support rtsp push. - - RTSP player and pusher. - - RTP Transport : `rtp over udp` `rtp over tcp` `rtp over http` `rtp udp multicast` . - - Basic/Digest/Url Authentication. - - H265/H264/AAC codec. - - Recorded as mp4. - - Vod of mp4. - + - RTSP 服务器,支持RTMP/MP4转RTSP。 + - RTSPS 服务器,支持亚马逊echo show这样的设备 + - RTSP 播放器,支持RTSP代理,支持生成静音音频 + - RTSP 推流客户端与服务器 + - 支持 `rtp over udp` `rtp over tcp` `rtp over http` `rtp组播` 四种RTP传输方式 。 + - 服务器/客户端完整支持Basic/Digest方式的登录鉴权,全异步可配置化的鉴权接口。 + - 支持H265编码 + - 服务器支持RTSP推流(包括`rtp over udp` `rtp over tcp`方式) + - 支持任意编码格式的rtsp推流,只是除H264/H265+AAC外无法转协议 + - RTMP - - RTMP server,support player and pusher. - - RTMP player and pusher. - - Support HTTP-FLV player. - - H264/AAC codec. - - Recorded as flv or mp4. - - Vod of mp4. - + - RTMP 播放服务器,支持RTSP/MP4转RTMP。 + - RTMP 发布服务器,支持录制发布流。 + - RTMP 播放器,支持RTMP代理,支持生成静音音频 + - RTMP 推流客户端。 + - 支持http-flv直播。 + - 支持https-flv直播。 + - 支持任意编码格式的rtmp推流,只是除H264/H265+AAC外无法转协议 + - HLS - - RTSP RTMP can be converted into HLS,built-in HTTP server. - - Play authentication based on cookie. + - 支持HLS文件生成,自带HTTP文件服务器。 + - 支持播放鉴权,鉴权结果可以缓存为cookie - HTTP[S] - - HTTP server,suppor directory meun、RESTful http api. - - HTTP client,downloader,uploader,and http api requester. - - Cookie supported. - - WebSocket Server and Client. - - File access authentication. - -- Others - - Support stream proxy by ffmpeg. - - RESTful http api and http hook event api. - - Config file hot loading. - - Vhost supported. - - Auto close stream when nobody played. - - Play and push authentication. - - Pull stream on Demand. + - 服务器支持`目录索引生成`,`文件下载`,`表单提交请求`。 + - 客户端提供`文件下载器(支持断点续传)`,`接口请求器`,`文件上传器`。 + - 完整HTTP API服务器,可以作为web后台开发框架。 + - 支持跨域访问。 + - 支持http客户端、服务器cookie + - 支持WebSocket服务器和客户端 + - 支持http文件访问鉴权 + +- 其他 + - 支持输入YUV+PCM自动生成RTSP/RTMP/HLS/MP4. + - 支持简单的telnet调试。 + - 支持H264的解析,支持B帧的POC计算排序。 + - 支持配置文件热加载 + - 支持流量统计、推流播放鉴权等事件 + - 支持rtsp/rtmp/http虚拟主机 + - 支持flv、mp4文件录制 + - 支持rtps/rtmp协议的mp4点播,支持seek + - 支持按需拉流,无人观看自动关断拉流 + - 支持先拉流后推流,提高及时推流画面打开率 + - 支持rtsp/rtmp/http-flv/hls播放鉴权(url参数方式) + +## 其他功能细节表 -- Protocol conversion: +- 转协议: -| protocol/codec | H264 | H265 | AAC | other | -| :------------------------------: | :--: | :--: | :--: | :---: | -| RTSP[S] --> RTMP/HTTP[S]-FLV/FLV | Y | N | Y | N | -| RTMP --> RTSP[S] | Y | N | Y | N | -| RTSP[S] --> HLS | Y | Y | Y | N | -| RTMP --> HLS | Y | N | Y | N | -| RTSP[S] --> MP4 | Y | Y | Y | N | -| RTMP --> MP4 | Y | N | Y | N | -| MP4 --> RTSP[S] | Y | N | Y | N | -| MP4 --> RTMP | Y | N | Y | N | + | 功能/编码格式 | H264 | H265 | AAC | other | + | :------------------------------: | :--: | :--: | :--: | :---: | + | RTSP[S] --> RTMP/HTTP[S]-FLV/FLV | Y | N | Y | N | + | RTMP --> RTSP[S] | Y | N | Y | N | + | RTSP[S] --> HLS | Y | Y | Y | N | + | RTMP --> HLS | Y | N | Y | N | + | RTSP[S] --> MP4 | Y | Y | Y | N | + | RTMP --> MP4 | Y | N | Y | N | + | MP4 --> RTSP[S] | Y | N | Y | N | + | MP4 --> RTMP | Y | N | Y | N | -- Stream generation: +- 流生成: -| feature/codec | H264 | H265 | AAC | other | -| :-----------: | :--: | :--: | :--: | :---: | -| RTSP[S] push | Y | Y | Y | Y | -| RTSP proxy | Y | Y | Y | Y | -| RTMP push | Y | Y | Y | Y | -| RTMP proxy | Y | Y | Y | Y | + | 功能/编码格式 | H264 | H265 | AAC | other | + | :------------------------------: | :--: | :--: | :--: | :---: | + | RTSP[S]推流 | Y | Y | Y | Y | + | RTSP拉流代理 | Y | Y | Y | Y | + | RTMP推流 | Y | Y | Y | Y | + | RTMP拉流代理 | Y | Y | Y | Y | -- RTP transport: +- RTP传输方式: -| feature/transport | tcp | udp | http | udp_multicast | -| :-----------------: | :--: | :--: | :--: | :-----------: | -| RTSP[S] Play Server | Y | Y | Y | Y | -| RTSP[S] Push Server | Y | Y | N | N | -| RTSP Player | Y | Y | N | Y | -| RTSP Pusher | Y | Y | N | N | + | 功能/RTP传输方式 | tcp | udp | http | udp_multicast | + | :-----------------: | :--: | :--: | :--: | :-----------: | + | RTSP[S] Play Server | Y | Y | Y | Y | + | RTSP[S] Push Server | Y | Y | N | N | + | RTSP Player | Y | Y | N | Y | + | RTSP Pusher | Y | Y | N | N | -- Server supported: +- 支持的服务器类型列表 -| Server | Y/N | -| :-----------------: | :--: | -| RTSP[S] Play Server | Y | -| RTSP[S] Push Server | Y | -| RTMP | Y | -| HTTP[S]/WebSocket[S] | Y | + | 服务类型 | Y/N | + | :-----------------: | :--: | + | RTSP[S] Play Server | Y | + | RTSP[S] Push Server | Y | + | RTMP | Y | + | HTTP[S]/WebSocket[S] | Y | -- Client supported: +- 支持的客户端类型 -| Client | Y/N | -| :---------: | :--: | -| RTSP Player | Y | -| RTSP Pusher | Y | -| RTMP Player | Y | -| RTMP Pusher | Y | -| HTTP[S] | Y | -| WebSocket[S] | Y | + | 客户端类型 | Y/N | + | :---------: | :--: | + | RTSP Player | Y | + | RTSP Pusher | Y | + | RTMP Player | Y | + | RTMP Pusher | Y | + | HTTP[S] | Y | + | WebSocket[S] | Y | +## 后续任务 +- 完善支持H265 +## 编译要求 +- 编译器支持C++11,GCC4.8/Clang3.3/VC2015或以上 +- cmake3.2或以上 -## System Requirements +## 编译前必看!!! -- Compiler support c++11,GCC4.8/Clang3.3/VC2015 or above. -- cmake3.1 or above. -- All Linux , both 32 and 64 bits -- Apple OSX(Darwin), both 32 and 64bits. -- All hardware with x86/x86_64/arm/mips cpu. -- Windows. - -## How to build - -It is recommended to compile on Ubuntu or MacOS,compiling on windows is cumbersome, and some features are not compiled by default. - -### Before build -- **You must use git to clone the complete code. Do not download the source code by downloading zip package. Otherwise, the sub-module code will not be downloaded by default.You can do it like this:** +- **必须使用git下载完整的代码,不要使用下载zip包的方式下载源码,否则子模块代码默认不下载!你可以像以下这样操作:** ``` git clone https://github.com/zlmediakit/ZLMediaKit.git cd ZLMediaKit git submodule update --init ``` -### Build on linux - -- My environment - - Ubuntu16.04 64 bit and gcc5.4 +## 编译(Linux) +- 我的编译环境 + - Ubuntu16.04 64 bit + gcc5.4 - cmake 3.5.1 -- Guidance +- 编译 ``` - # If it is on centos6.x, you need to install the newer version of GCC and cmake first, - # and then compile manually according to the script "build_for_linux.sh". - # If it is on a newer version of a system such as Ubuntu or Debain, - # step 4 can be manipulated directly. - - # 1、Install GCC5.2 (this step can be skipped if the GCC version is higher than 4.7) + //如果是centos6.x,需要先安装较新版本的gcc以及cmake,然后打开脚本build_for_linux.sh手动编译 + //如果是ubuntu这样的比较新的系统版本可以直接操作第4步 + + 1、安装GCC5.2(如果gcc版本高于4.7可以跳过此步骤) sudo yum install centos-release-scl -y sudo yum install devtoolset-4-toolchain -y scl enable devtoolset-4 bash - - # 2、Install cmake (this step can be skipped if the cmake version is higher than 3.1) - tar -xvf cmake-3.10.0-rc4.tar.gz #you need download cmake source file manually + + 2、安装cmake + #需要安装新版本cmake,当然你也可以通过yum或者apt-get方式安装(前提是版本够新) + tar -xvf cmake-3.10.0-rc4.tar.gz cd cmake-3.10.0-rc4 ./configure make -j4 sudo make install - - # 3、Switch to high version GCC + + 3、切换高版本gcc scl enable devtoolset-4 bash - - # 4、build + + 4、编译 cd ZLMediaKit ./build_for_linux.sh ``` -### Build on macOS - -- My environment +## 编译(macOS) +- 我的编译环境 - macOS Sierra(10.12.1) + xcode8.3.1 - Homebrew 1.1.3 - cmake 3.8.0 -- Guidance +- 编译 ``` cd ZLMediaKit ./build_for_mac.sh ``` -### Build on iOS - - This build method is no longer recommended.It is recommended that make Xcode project by yourself. - -- My environment - - Same with Build on macOS - +## 编译(iOS) +- 编译环境:`请参考macOS的编译指导。` +- 编译 -- You can generate Xcode projects and recompile them , [learn more](https://github.com/leetal/ios-cmake): + ``` + cd ZLMediaKit + ./build_for_ios.sh + ``` +- 你也可以生成Xcode工程再编译,[了解更多](https://github.com/leetal/ios-cmake): ``` cd ZLMediaKit mkdir -p build cd build - # Generate Xcode project, project file is in build directory + # 生成Xcode工程,工程文件在build目录下 cmake .. -G Xcode -DCMAKE_TOOLCHAIN_FILE=../cmake/ios.toolchain.cmake -DPLATFORM=OS64COMBINED ``` - - - -### Build on Android - - Now you can open android sudio project in `Android` folder,this is a `aar library` and damo project. - -- My environment +## 编译(Android) +- 我的编译环境 - macOS Sierra(10.12.1) + xcode8.3.1 - Homebrew 1.1.3 - cmake 3.8.0 - [android-ndk-r14b](https://dl.google.com/android/repository/android-ndk-r14b-darwin-x86_64.zip) -- Guidance +- 编译 ``` cd ZLMediaKit export ANDROID_NDK_ROOT=/path/to/ndk ./build_for_android.sh ``` -### Build on Windows - -- My environment +## 编译(Windows) +- 我的编译环境 - windows 10 - visual studio 2017 - [cmake-gui](https://cmake.org/files/v3.10/cmake-3.10.0-rc1-win32-x86.msi) -- Guidance +- 编译 ``` -1 Enter the ZLMediaKit directory and execute git submodule update -- init downloads the code for ZLToolKit -2 Open the project with cmake-gui and generate the vs project file. -3 Find the project file (ZLMediaKit.sln), double-click to open it with vs2017. -4 Choose to compile Release version. Find the target file and run the test case. + 1 进入ZLMediaKit目录执行 git submodule update --init 以下载ZLToolKit的代码 + 2 使用cmake-gui打开工程并生成vs工程文件. +   3 找到工程文件(ZLMediaKit.sln),双击用vs2017打开. +   4 选择编译Release 版本. + 5 找到目标文件并运行测试用例. ``` -## Usage - -- As server: +## 使用方法 +- 作为服务器: ```cpp TcpServer::Ptr rtspSrv(new TcpServer()); TcpServer::Ptr rtmpSrv(new TcpServer()); @@ -243,7 +252,7 @@ git submodule update --init httpsSrv->start(mINI::Instance()[Config::Http::kSSLPort]); ``` -- As player: +- 作为播放器: ```cpp MediaPlayer::Ptr player(new MediaPlayer()); weak_ptr weakPlayer = player; @@ -256,11 +265,11 @@ git submodule update --init auto viedoTrack = strongPlayer->getTrack(TrackVideo); if (!viedoTrack) { - WarnL << "none video Track!"; + WarnL << "没有视频Track!"; return; } viedoTrack->addDelegate(std::make_shared([](const Frame::Ptr &frame) { - //please decode video here + //此处解码并播放 })); }); @@ -268,11 +277,11 @@ git submodule update --init ErrorL << "OnShutdown:" << ex.what(); }); - //rtp transport over tcp + //支持rtmp、rtsp (*player)[Client::kRtpType] = Rtsp::RTP_TCP; player->play("rtsp://admin:jzan123456@192.168.0.122/"); ``` -- As proxy server: +- 作为代理服务器: ```cpp //support rtmp and rtsp url //just support H264+AAC @@ -281,77 +290,94 @@ git submodule update --init map proxyMap; int i=0; for(auto url : urlList){ + //PlayerProxy构造函数前两个参数分别为应用名(app),流id(streamId) + //比如说应用为live,流id为0,那么直播地址为: + //http://127.0.0.1/live/0/hls.m3u8 + //rtsp://127.0.0.1/live/0 + //rtmp://127.0.0.1/live/0 + //录像地址为: + //http://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4 + //rtsp://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4 + //rtmp://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4 PlayerProxy::Ptr player(new PlayerProxy("live",to_string(i++).data())); player->play(url); proxyMap.emplace(string(url),player); } ``` -- As puser: +- 作为推流客户端器: ```cpp PlayerProxy::Ptr player(new PlayerProxy("app","stream")); + //拉一个流,生成一个RtmpMediaSource,源的名称是"app/stream" + //你也可以以其他方式生成RtmpMediaSource,比如说MP4文件(请研读MediaReader代码) player->play("rtmp://live.hkstv.hk.lxdns.com/live/hks"); - + RtmpPusher::Ptr pusher; + //监听RtmpMediaSource注册事件,在PlayerProxy播放成功后触发。 NoticeCenter::Instance().addListener(nullptr,Config::Broadcast::kBroadcastRtmpSrcRegisted, [&pusher](BroadcastRtmpSrcRegistedArgs){ + //媒体源"app/stream"已经注册,这时方可新建一个RtmpPusher对象并绑定该媒体源 const_cast(pusher).reset(new RtmpPusher(app,stream)); + + //推流地址,请改成你自己的服务器。 + //这个范例地址(也是基于mediakit)是可用的,但是带宽只有1mb,访问可能很卡顿。 pusher->publish("rtmp://jizan.iok.la/live/test"); }); - + ``` -## Docker Image -You can pull a pre-built docker image from Docker Hub and run with -```bash -docker run -id -p 1935:1935 -p 8080:80 gemfield/zlmediakit -``` +## QA +- 怎么测试服务器性能? -Dockerfile is also supplied to build images on Ubuntu 16.04 -```bash -cd docker -docker build -t zlmediakit . -``` + ZLMediaKit提供了测试性能的示例,代码在tests/test_benchmark.cpp。 -## Mirrors + 这里是测试报告:[benchmark.md](https://github.com/xiongziliang/ZLMediaKit/blob/master/benchmark.md) -[ZLToolKit](http://git.oschina.net/xiahcu/ZLToolKit) +- github下载太慢了,有其他下载方式吗? -[ZLMediaKit](http://git.oschina.net/xiahcu/ZLMediaKit) + 你可以在通过开源中国获取最新的代码,地址为: + + [ZLToolKit](http://git.oschina.net/xiahcu/ZLToolKit) + + [ZLMediaKit](http://git.oschina.net/xiahcu/ZLMediaKit) -## Licence +- 在windows下编译很多错误? -``` -MIT License + 由于本项目主体代码在macOS/linux下开发,部分源码采用的是无bom头的UTF-8编码;由于windows对于utf-8支持不甚友好,所以如果发现编译错误请先尝试添 加bom头再编译。 + 也可以通过参考这篇博客解决: + [vs2015:/utf-8选项解决UTF-8 without BOM 源码中文输出乱码问题](https://blog.csdn.net/10km/article/details/80203286) -Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> -Copyright (c) 2019 Gemfield -Copyright (c) 2018 huohuo <913481084@qq.com> +## 参考案例 + - [IOS摄像头实时录制,生成rtsp/rtmp/hls/http-flv](https://gitee.com/xiahcu/IOSMedia) + - [IOS rtmp/rtsp播放器,视频推流器](https://gitee.com/xiahcu/IOSPlayer) + - [支持linux、windows、mac的rtmp/rtsp播放器](https://github.com/xiongziliang/ZLMediaPlayer) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: + 上述工程可能在最新的代码的情况下编译不过,请手动修改 -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +## 授权协议 -``` +本项目自有代码使用宽松的MIT协议,在保留版权信息的情况下可以自由应用于各自商用、非商业的项目。 +但是本项目也零碎的使用了一些其他的开源代码,在商用的情况下请自行替代或剔除; +由于使用本项目而产生的商业纠纷或侵权行为一概与本项项目及开发者无关,请自行承担法律风险。 + +## 联系方式 + - 邮箱:<771730766@qq.com>(本项目相关或流媒体相关问题请走issue流程,否则恕不邮件答复) + - QQ群:542509000 + +## 怎么提问? +如果要对项目有相关疑问,建议您这么做: + - 1、仔细看下readme、wiki,如果有必要可以查看下issue. + - 2、如果您的问题还没解决,可以提issue. + - 3、有些问题,如果不具备参考性的,无需在issue提的,可以在qq群提. + - 4、QQ私聊一般不接受无偿技术咨询和支持(谈谈人生理想还是可以的😂),毕竟精力有限,谢谢理解. + +## 捐赠 +欢迎捐赠以便更好的推动项目的发展,谢谢您的支持! + +[支付宝](https://raw.githubusercontent.com/xiongziliang/other/master/IMG_3919.JPG) + +[微信](https://raw.githubusercontent.com/xiongziliang/other/master/IMG_3920.JPG) -## Contact - - Email:<771730766@qq.com> - - QQ chat group:542509000 - - diff --git a/README_CN.md b/README_CN.md deleted file mode 100644 index 0a4c005d..00000000 --- a/README_CN.md +++ /dev/null @@ -1,380 +0,0 @@ -![logo](https://raw.githubusercontent.com/zlmediakit/ZLMediaKit/master/logo.png) -# 一个基于C++11的高性能运营级流媒体服务框架 - [![Build Status](https://travis-ci.org/xiongziliang/ZLMediaKit.svg?branch=master)](https://travis-ci.org/xiongziliang/ZLMediaKit) - - -## 国内用户请使用gitee镜像下载 -``` -git clone --depth 1 https://gitee.com/xiahcu/ZLMediaKit -cd ZLMediaKit -git submodule update --init -``` -## 项目特点 -- 基于C++11开发,避免使用裸指针,代码稳定可靠;同时跨平台移植简单方便,代码清晰简洁。 -- 打包多种流媒体协议(RTSP/RTMP/HLS/HTTP-FLV/Websocket-FLV),支持协议间的互相转换,提供一站式的服务。 -- 使用epoll+线程池+异步网络IO模式开发,并发性能优越。 -- 已实现主流的的H264/H265+AAC流媒体方案,代码精简,脉络清晰,适合学习。 -- 编码格式与框架代码解耦,方便自由简洁的添加支持其他编码格式。 -- 代码经过大量的稳定性、性能测试,可满足商用服务器项目。 -- 支持linux、macos、ios、android、windows平台。 -- 支持画面秒开(GOP缓存)、极低延时([500毫秒内,最低可达100毫秒](https://github.com/zlmediakit/ZLMediaKit/wiki/%E5%BB%B6%E6%97%B6%E6%B5%8B%E8%AF%95))。 -- [ZLMediaKit高并发实现原理](https://github.com/xiongziliang/ZLMediaKit/wiki/ZLMediaKit%E9%AB%98%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86)。 -- 提供完善的标准C API,可以作SDK用,或供其他语言调用。 -- 提供完整的MediaServer服务器,可以免开发直接部署为商用服务器。 - -## 项目定位 -- 移动嵌入式跨平台流媒体解决方案。 -- 商用级流媒体服务器。 -- 网络编程二次开发SDK。 - - -## 功能清单 -- RTSP - - RTSP 服务器,支持RTMP/MP4转RTSP。 - - RTSPS 服务器,支持亚马逊echo show这样的设备 - - RTSP 播放器,支持RTSP代理,支持生成静音音频 - - RTSP 推流客户端与服务器 - - 支持 `rtp over udp` `rtp over tcp` `rtp over http` `rtp组播` 四种RTP传输方式 。 - - 服务器/客户端完整支持Basic/Digest方式的登录鉴权,全异步可配置化的鉴权接口。 - - 支持H265编码 - - 服务器支持RTSP推流(包括`rtp over udp` `rtp over tcp`方式) - - 支持任意编码格式的rtsp推流,只是除H264/H265+AAC外无法转协议 - -- RTMP - - RTMP 播放服务器,支持RTSP/MP4转RTMP。 - - RTMP 发布服务器,支持录制发布流。 - - RTMP 播放器,支持RTMP代理,支持生成静音音频 - - RTMP 推流客户端。 - - 支持http-flv直播。 - - 支持https-flv直播。 - - 支持任意编码格式的rtmp推流,只是除H264/H265+AAC外无法转协议 - -- HLS - - 支持HLS文件生成,自带HTTP文件服务器。 - - 支持播放鉴权,鉴权结果可以缓存为cookie - -- HTTP[S] - - 服务器支持`目录索引生成`,`文件下载`,`表单提交请求`。 - - 客户端提供`文件下载器(支持断点续传)`,`接口请求器`,`文件上传器`。 - - 完整HTTP API服务器,可以作为web后台开发框架。 - - 支持跨域访问。 - - 支持http客户端、服务器cookie - - 支持WebSocket服务器和客户端 - - 支持http文件访问鉴权 - -- 其他 - - 支持输入YUV+PCM自动生成RTSP/RTMP/HLS/MP4. - - 支持简单的telnet调试。 - - 支持H264的解析,支持B帧的POC计算排序。 - - 支持配置文件热加载 - - 支持流量统计、推流播放鉴权等事件 - - 支持rtsp/rtmp/http虚拟主机 - - 支持flv、mp4文件录制 - - 支持rtps/rtmp协议的mp4点播,支持seek - - 支持按需拉流,无人观看自动关断拉流 - - 支持先拉流后推流,提高及时推流画面打开率 - - 支持rtsp/rtmp/http-flv/hls播放鉴权(url参数方式) - - - -## 其他功能细节表 - -- 转协议: - - | 功能/编码格式 | H264 | H265 | AAC | other | - | :------------------------------: | :--: | :--: | :--: | :---: | - | RTSP[S] --> RTMP/HTTP[S]-FLV/FLV | Y | N | Y | N | - | RTMP --> RTSP[S] | Y | N | Y | N | - | RTSP[S] --> HLS | Y | Y | Y | N | - | RTMP --> HLS | Y | N | Y | N | - | RTSP[S] --> MP4 | Y | Y | Y | N | - | RTMP --> MP4 | Y | N | Y | N | - | MP4 --> RTSP[S] | Y | N | Y | N | - | MP4 --> RTMP | Y | N | Y | N | - -- 流生成: - - | 功能/编码格式 | H264 | H265 | AAC | other | - | :------------------------------: | :--: | :--: | :--: | :---: | - | RTSP[S]推流 | Y | Y | Y | Y | - | RTSP拉流代理 | Y | Y | Y | Y | - | RTMP推流 | Y | Y | Y | Y | - | RTMP拉流代理 | Y | Y | Y | Y | - -- RTP传输方式: - - | 功能/RTP传输方式 | tcp | udp | http | udp_multicast | - | :-----------------: | :--: | :--: | :--: | :-----------: | - | RTSP[S] Play Server | Y | Y | Y | Y | - | RTSP[S] Push Server | Y | Y | N | N | - | RTSP Player | Y | Y | N | Y | - | RTSP Pusher | Y | Y | N | N | - - -- 支持的服务器类型列表 - - | 服务类型 | Y/N | - | :-----------------: | :--: | - | RTSP[S] Play Server | Y | - | RTSP[S] Push Server | Y | - | RTMP | Y | - | HTTP[S]/WebSocket[S] | Y | - -- 支持的客户端类型 - - | 客户端类型 | Y/N | - | :---------: | :--: | - | RTSP Player | Y | - | RTSP Pusher | Y | - | RTMP Player | Y | - | RTMP Pusher | Y | - | HTTP[S] | Y | - | WebSocket[S] | Y | - -## 后续任务 -- 完善支持H265 - -## 编译要求 -- 编译器支持C++11,GCC4.8/Clang3.3/VC2015或以上 -- cmake3.2或以上 - -## 编译前必看!!! - -- **必须使用git下载完整的代码,不要使用下载zip包的方式下载源码,否则子模块代码默认不下载!你可以像以下这样操作:** -``` -git clone https://github.com/zlmediakit/ZLMediaKit.git -cd ZLMediaKit -git submodule update --init -``` - -## 编译(Linux) -- 我的编译环境 - - Ubuntu16.04 64 bit + gcc5.4 - - cmake 3.5.1 -- 编译 - - ``` - //如果是centos6.x,需要先安装较新版本的gcc以及cmake,然后打开脚本build_for_linux.sh手动编译 - //如果是ubuntu这样的比较新的系统版本可以直接操作第4步 - - 1、安装GCC5.2(如果gcc版本高于4.7可以跳过此步骤) - sudo yum install centos-release-scl -y - sudo yum install devtoolset-4-toolchain -y - scl enable devtoolset-4 bash - - 2、安装cmake - #需要安装新版本cmake,当然你也可以通过yum或者apt-get方式安装(前提是版本够新) - tar -xvf cmake-3.10.0-rc4.tar.gz - cd cmake-3.10.0-rc4 - ./configure - make -j4 - sudo make install - - 3、切换高版本gcc - scl enable devtoolset-4 bash - - 4、编译 - cd ZLMediaKit - ./build_for_linux.sh - ``` - -## 编译(macOS) -- 我的编译环境 - - macOS Sierra(10.12.1) + xcode8.3.1 - - Homebrew 1.1.3 - - cmake 3.8.0 -- 编译 - - ``` - cd ZLMediaKit - ./build_for_mac.sh - ``` - -## 编译(iOS) -- 编译环境:`请参考macOS的编译指导。` -- 编译 - - ``` - cd ZLMediaKit - ./build_for_ios.sh - ``` -- 你也可以生成Xcode工程再编译,[了解更多](https://github.com/leetal/ios-cmake): - - ``` - cd ZLMediaKit - mkdir -p build - cd build - # 生成Xcode工程,工程文件在build目录下 - cmake .. -G Xcode -DCMAKE_TOOLCHAIN_FILE=../cmake/ios.toolchain.cmake -DPLATFORM=OS64COMBINED - ``` - -## 编译(Android) -- 我的编译环境 - - macOS Sierra(10.12.1) + xcode8.3.1 - - Homebrew 1.1.3 - - cmake 3.8.0 - - [android-ndk-r14b](https://dl.google.com/android/repository/android-ndk-r14b-darwin-x86_64.zip) -- 编译 - - ``` - cd ZLMediaKit - export ANDROID_NDK_ROOT=/path/to/ndk - ./build_for_android.sh - ``` -## 编译(Windows) -- 我的编译环境 - - windows 10 - - visual studio 2017 - - [cmake-gui](https://cmake.org/files/v3.10/cmake-3.10.0-rc1-win32-x86.msi) - -- 编译 -``` - 1 进入ZLMediaKit目录执行 git submodule update --init 以下载ZLToolKit的代码 - 2 使用cmake-gui打开工程并生成vs工程文件. -   3 找到工程文件(ZLMediaKit.sln),双击用vs2017打开. -   4 选择编译Release 版本. - 5 找到目标文件并运行测试用例. -``` -## 使用方法 -- 作为服务器: - ```cpp - TcpServer::Ptr rtspSrv(new TcpServer()); - TcpServer::Ptr rtmpSrv(new TcpServer()); - TcpServer::Ptr httpSrv(new TcpServer()); - TcpServer::Ptr httpsSrv(new TcpServer()); - - rtspSrv->start(mINI::Instance()[Config::Rtsp::kPort]); - rtmpSrv->start(mINI::Instance()[Config::Rtmp::kPort]); - httpSrv->start(mINI::Instance()[Config::Http::kPort]); - httpsSrv->start(mINI::Instance()[Config::Http::kSSLPort]); - ``` - -- 作为播放器: - ```cpp - MediaPlayer::Ptr player(new MediaPlayer()); - weak_ptr weakPlayer = player; - player->setOnPlayResult([weakPlayer](const SockException &ex) { - InfoL << "OnPlayResult:" << ex.what(); - auto strongPlayer = weakPlayer.lock(); - if (ex || !strongPlayer) { - return; - } - - auto viedoTrack = strongPlayer->getTrack(TrackVideo); - if (!viedoTrack) { - WarnL << "没有视频Track!"; - return; - } - viedoTrack->addDelegate(std::make_shared([](const Frame::Ptr &frame) { - //此处解码并播放 - })); - }); - - player->setOnShutdown([](const SockException &ex) { - ErrorL << "OnShutdown:" << ex.what(); - }); - - //支持rtmp、rtsp - (*player)[Client::kRtpType] = Rtsp::RTP_TCP; - player->play("rtsp://admin:jzan123456@192.168.0.122/"); - ``` -- 作为代理服务器: - ```cpp - //support rtmp and rtsp url - //just support H264+AAC - auto urlList = {"rtmp://live.hkstv.hk.lxdns.com/live/hks", - "rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov"}; - map proxyMap; - int i=0; - for(auto url : urlList){ - //PlayerProxy构造函数前两个参数分别为应用名(app),流id(streamId) - //比如说应用为live,流id为0,那么直播地址为: - //http://127.0.0.1/live/0/hls.m3u8 - //rtsp://127.0.0.1/live/0 - //rtmp://127.0.0.1/live/0 - //录像地址为: - //http://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4 - //rtsp://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4 - //rtmp://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4 - PlayerProxy::Ptr player(new PlayerProxy("live",to_string(i++).data())); - player->play(url); - proxyMap.emplace(string(url),player); - } - ``` - -- 作为推流客户端器: - ```cpp - PlayerProxy::Ptr player(new PlayerProxy("app","stream")); - //拉一个流,生成一个RtmpMediaSource,源的名称是"app/stream" - //你也可以以其他方式生成RtmpMediaSource,比如说MP4文件(请研读MediaReader代码) - player->play("rtmp://live.hkstv.hk.lxdns.com/live/hks"); - - RtmpPusher::Ptr pusher; - //监听RtmpMediaSource注册事件,在PlayerProxy播放成功后触发。 - NoticeCenter::Instance().addListener(nullptr,Config::Broadcast::kBroadcastRtmpSrcRegisted, - [&pusher](BroadcastRtmpSrcRegistedArgs){ - //媒体源"app/stream"已经注册,这时方可新建一个RtmpPusher对象并绑定该媒体源 - const_cast(pusher).reset(new RtmpPusher(app,stream)); - - //推流地址,请改成你自己的服务器。 - //这个范例地址(也是基于mediakit)是可用的,但是带宽只有1mb,访问可能很卡顿。 - pusher->publish("rtmp://jizan.iok.la/live/test"); - }); - - ``` -## QA -- 怎么测试服务器性能? - - ZLMediaKit提供了测试性能的示例,代码在tests/test_benchmark.cpp。 - - 这里是测试报告:[benchmark.md](https://github.com/xiongziliang/ZLMediaKit/blob/master/benchmark.md) - -- github下载太慢了,有其他下载方式吗? - - 你可以在通过开源中国获取最新的代码,地址为: - - [ZLToolKit](http://git.oschina.net/xiahcu/ZLToolKit) - - [ZLMediaKit](http://git.oschina.net/xiahcu/ZLMediaKit) - - -- 在windows下编译很多错误? - - 由于本项目主体代码在macOS/linux下开发,部分源码采用的是无bom头的UTF-8编码;由于windows对于utf-8支持不甚友好,所以如果发现编译错误请先尝试添 加bom头再编译。 - 也可以通过参考这篇博客解决: - [vs2015:/utf-8选项解决UTF-8 without BOM 源码中文输出乱码问题](https://blog.csdn.net/10km/article/details/80203286) - -## 参考案例 - - [IOS摄像头实时录制,生成rtsp/rtmp/hls/http-flv](https://gitee.com/xiahcu/IOSMedia) - - [IOS rtmp/rtsp播放器,视频推流器](https://gitee.com/xiahcu/IOSPlayer) - - [支持linux、windows、mac的rtmp/rtsp播放器](https://github.com/xiongziliang/ZLMediaPlayer) - - 上述工程可能在最新的代码的情况下编译不过,请手动修改 - - -## 授权协议 - -本项目自有代码使用宽松的MIT协议,在保留版权信息的情况下可以自由应用于各自商用、非商业的项目。 -但是本项目也零碎的使用了一些其他的开源代码,在商用的情况下请自行替代或剔除; -由于使用本项目而产生的商业纠纷或侵权行为一概与本项项目及开发者无关,请自行承担法律风险。 - -## 联系方式 - - 邮箱:<771730766@qq.com>(本项目相关或流媒体相关问题请走issue流程,否则恕不邮件答复) - - QQ群:542509000 - -## 怎么提问? -如果要对项目有相关疑问,建议您这么做: - - 1、仔细看下readme、wiki,如果有必要可以查看下issue. - - 2、如果您的问题还没解决,可以提issue. - - 3、有些问题,如果不具备参考性的,无需在issue提的,可以在qq群提. - - 4、QQ私聊一般不接受无偿技术咨询和支持(谈谈人生理想还是可以的😂),毕竟精力有限,谢谢理解. - -## 捐赠 -欢迎捐赠以便更好的推动项目的发展,谢谢您的支持! - -[支付宝](https://raw.githubusercontent.com/xiongziliang/other/master/IMG_3919.JPG) - -[微信](https://raw.githubusercontent.com/xiongziliang/other/master/IMG_3920.JPG) - - - diff --git a/README_en.md b/README_en.md new file mode 100644 index 00000000..20d908e6 --- /dev/null +++ b/README_en.md @@ -0,0 +1,356 @@ +![logo](https://raw.githubusercontent.com/zlmediakit/ZLMediaKit/master/logo.png) + +# A lightweight ,high performance and stable stream server and client framework based on C++11. + + [![Build Status](https://travis-ci.org/xiongziliang/ZLMediaKit.svg?branch=master)](https://travis-ci.org/xiongziliang/ZLMediaKit) + + +## Why ZLMediaKit? +- Developed based on C++ 11, the code is stable and reliable, avoiding the use of raw pointers, cross-platform porting is simple and convenient, and the code is clear and concise. +- Support rich streaming media protocols(`RTSP/RTMP/HLS/HTTP-FLV/Websocket-flv`),and support Inter-protocol conversion. +- Multiplexing asynchronous network IO based on epoll and multi thread,extreme performance. +- Well performance and stable test,can be used commercially. +- Support linux, macos, ios, android, Windows Platforms. +- Very low latency(lower then one second), video opened immediately. + +## Features + +- RTSP + - RTSP[S] server,support rtsp push. + - RTSP player and pusher. + - RTP Transport : `rtp over udp` `rtp over tcp` `rtp over http` `rtp udp multicast` . + - Basic/Digest/Url Authentication. + - H265/H264/AAC codec. + - Recorded as mp4. + - Vod of mp4. + +- RTMP + - RTMP server,support player and pusher. + - RTMP player and pusher. + - Support HTTP-FLV player. + - H264/AAC codec. + - Recorded as flv or mp4. + - Vod of mp4. + +- HLS + - RTSP RTMP can be converted into HLS,built-in HTTP server. + - Play authentication based on cookie. + +- HTTP[S] + - HTTP server,suppor directory meun、RESTful http api. + - HTTP client,downloader,uploader,and http api requester. + - Cookie supported. + - WebSocket Server and Client. + - File access authentication. + +- Others + - Support stream proxy by ffmpeg. + - RESTful http api and http hook event api. + - Config file hot loading. + - Vhost supported. + - Auto close stream when nobody played. + - Play and push authentication. + - Pull stream on Demand. + + + +- Protocol conversion: + +| protocol/codec | H264 | H265 | AAC | other | +| :------------------------------: | :--: | :--: | :--: | :---: | +| RTSP[S] --> RTMP/HTTP[S]-FLV/FLV | Y | N | Y | N | +| RTMP --> RTSP[S] | Y | N | Y | N | +| RTSP[S] --> HLS | Y | Y | Y | N | +| RTMP --> HLS | Y | N | Y | N | +| RTSP[S] --> MP4 | Y | Y | Y | N | +| RTMP --> MP4 | Y | N | Y | N | +| MP4 --> RTSP[S] | Y | N | Y | N | +| MP4 --> RTMP | Y | N | Y | N | + +- Stream generation: + +| feature/codec | H264 | H265 | AAC | other | +| :-----------: | :--: | :--: | :--: | :---: | +| RTSP[S] push | Y | Y | Y | Y | +| RTSP proxy | Y | Y | Y | Y | +| RTMP push | Y | Y | Y | Y | +| RTMP proxy | Y | Y | Y | Y | + +- RTP transport: + +| feature/transport | tcp | udp | http | udp_multicast | +| :-----------------: | :--: | :--: | :--: | :-----------: | +| RTSP[S] Play Server | Y | Y | Y | Y | +| RTSP[S] Push Server | Y | Y | N | N | +| RTSP Player | Y | Y | N | Y | +| RTSP Pusher | Y | Y | N | N | + + +- Server supported: + +| Server | Y/N | +| :-----------------: | :--: | +| RTSP[S] Play Server | Y | +| RTSP[S] Push Server | Y | +| RTMP | Y | +| HTTP[S]/WebSocket[S] | Y | + +- Client supported: + +| Client | Y/N | +| :---------: | :--: | +| RTSP Player | Y | +| RTSP Pusher | Y | +| RTMP Player | Y | +| RTMP Pusher | Y | +| HTTP[S] | Y | +| WebSocket[S] | Y | + + + +## System Requirements + +- Compiler support c++11,GCC4.8/Clang3.3/VC2015 or above. +- cmake3.1 or above. +- All Linux , both 32 and 64 bits +- Apple OSX(Darwin), both 32 and 64bits. +- All hardware with x86/x86_64/arm/mips cpu. +- Windows. + +## How to build + +It is recommended to compile on Ubuntu or MacOS,compiling on windows is cumbersome, and some features are not compiled by default. + +### Before build +- **You must use git to clone the complete code. Do not download the source code by downloading zip package. Otherwise, the sub-module code will not be downloaded by default.You can do it like this:** +``` +git clone https://github.com/zlmediakit/ZLMediaKit.git +cd ZLMediaKit +git submodule update --init +``` + +### Build on linux + +- My environment + - Ubuntu16.04 64 bit and gcc5.4 + - cmake 3.5.1 +- Guidance + + ``` + # If it is on centos6.x, you need to install the newer version of GCC and cmake first, + # and then compile manually according to the script "build_for_linux.sh". + # If it is on a newer version of a system such as Ubuntu or Debain, + # step 4 can be manipulated directly. + + # 1、Install GCC5.2 (this step can be skipped if the GCC version is higher than 4.7) + sudo yum install centos-release-scl -y + sudo yum install devtoolset-4-toolchain -y + scl enable devtoolset-4 bash + + # 2、Install cmake (this step can be skipped if the cmake version is higher than 3.1) + tar -xvf cmake-3.10.0-rc4.tar.gz #you need download cmake source file manually + cd cmake-3.10.0-rc4 + ./configure + make -j4 + sudo make install + + # 3、Switch to high version GCC + scl enable devtoolset-4 bash + + # 4、build + cd ZLMediaKit + ./build_for_linux.sh + ``` + +### Build on macOS + +- My environment + - macOS Sierra(10.12.1) + xcode8.3.1 + - Homebrew 1.1.3 + - cmake 3.8.0 +- Guidance + + ``` + cd ZLMediaKit + ./build_for_mac.sh + ``` + +### Build on iOS + + This build method is no longer recommended.It is recommended that make Xcode project by yourself. + +- My environment + + Same with Build on macOS + + +- You can generate Xcode projects and recompile them , [learn more](https://github.com/leetal/ios-cmake): + + ``` + cd ZLMediaKit + mkdir -p build + cd build + # Generate Xcode project, project file is in build directory + cmake .. -G Xcode -DCMAKE_TOOLCHAIN_FILE=../cmake/ios.toolchain.cmake -DPLATFORM=OS64COMBINED + ``` + + + + +### Build on Android + + Now you can open android sudio project in `Android` folder,this is a `aar library` and damo project. + +- My environment + - macOS Sierra(10.12.1) + xcode8.3.1 + - Homebrew 1.1.3 + - cmake 3.8.0 + - [android-ndk-r14b](https://dl.google.com/android/repository/android-ndk-r14b-darwin-x86_64.zip) +- Guidance + + ``` + cd ZLMediaKit + export ANDROID_NDK_ROOT=/path/to/ndk + ./build_for_android.sh + ``` +### Build on Windows + +- My environment + - windows 10 + - visual studio 2017 + - [cmake-gui](https://cmake.org/files/v3.10/cmake-3.10.0-rc1-win32-x86.msi) + +- Guidance +``` +1 Enter the ZLMediaKit directory and execute git submodule update -- init downloads the code for ZLToolKit +2 Open the project with cmake-gui and generate the vs project file. +3 Find the project file (ZLMediaKit.sln), double-click to open it with vs2017. +4 Choose to compile Release version. Find the target file and run the test case. +``` +## Usage + +- As server: + ```cpp + TcpServer::Ptr rtspSrv(new TcpServer()); + TcpServer::Ptr rtmpSrv(new TcpServer()); + TcpServer::Ptr httpSrv(new TcpServer()); + TcpServer::Ptr httpsSrv(new TcpServer()); + + rtspSrv->start(mINI::Instance()[Config::Rtsp::kPort]); + rtmpSrv->start(mINI::Instance()[Config::Rtmp::kPort]); + httpSrv->start(mINI::Instance()[Config::Http::kPort]); + httpsSrv->start(mINI::Instance()[Config::Http::kSSLPort]); + ``` + +- As player: + ```cpp + MediaPlayer::Ptr player(new MediaPlayer()); + weak_ptr weakPlayer = player; + player->setOnPlayResult([weakPlayer](const SockException &ex) { + InfoL << "OnPlayResult:" << ex.what(); + auto strongPlayer = weakPlayer.lock(); + if (ex || !strongPlayer) { + return; + } + + auto viedoTrack = strongPlayer->getTrack(TrackVideo); + if (!viedoTrack) { + WarnL << "none video Track!"; + return; + } + viedoTrack->addDelegate(std::make_shared([](const Frame::Ptr &frame) { + //please decode video here + })); + }); + + player->setOnShutdown([](const SockException &ex) { + ErrorL << "OnShutdown:" << ex.what(); + }); + + //rtp transport over tcp + (*player)[Client::kRtpType] = Rtsp::RTP_TCP; + player->play("rtsp://admin:jzan123456@192.168.0.122/"); + ``` +- As proxy server: + ```cpp + //support rtmp and rtsp url + //just support H264+AAC + auto urlList = {"rtmp://live.hkstv.hk.lxdns.com/live/hks", + "rtsp://184.72.239.149/vod/mp4://BigBuckBunny_175k.mov"}; + map proxyMap; + int i=0; + for(auto url : urlList){ + PlayerProxy::Ptr player(new PlayerProxy("live",to_string(i++).data())); + player->play(url); + proxyMap.emplace(string(url),player); + } + ``` + +- As puser: + ```cpp + PlayerProxy::Ptr player(new PlayerProxy("app","stream")); + player->play("rtmp://live.hkstv.hk.lxdns.com/live/hks"); + + RtmpPusher::Ptr pusher; + NoticeCenter::Instance().addListener(nullptr,Config::Broadcast::kBroadcastRtmpSrcRegisted, + [&pusher](BroadcastRtmpSrcRegistedArgs){ + const_cast(pusher).reset(new RtmpPusher(app,stream)); + pusher->publish("rtmp://jizan.iok.la/live/test"); + }); + + ``` +## Docker Image +You can pull a pre-built docker image from Docker Hub and run with +```bash +docker run -id -p 1935:1935 -p 8080:80 gemfield/zlmediakit +``` + +Dockerfile is also supplied to build images on Ubuntu 16.04 +```bash +cd docker +docker build -t zlmediakit . +``` + +## Mirrors + +[ZLToolKit](http://git.oschina.net/xiahcu/ZLToolKit) + +[ZLMediaKit](http://git.oschina.net/xiahcu/ZLMediaKit) + + +## Licence + +``` +MIT License + +Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> +Copyright (c) 2019 Gemfield +Copyright (c) 2018 huohuo <913481084@qq.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + + + +## Contact + - Email:<771730766@qq.com> + - QQ chat group:542509000 + + From 9dc1f3a804ea8dc2bb7daf9827243e7efdc33af6 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 25 Dec 2019 09:39:24 +0800 Subject: [PATCH 138/272] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=88=90c=E6=BA=90?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/recorder.h | 2 +- api/source/recorder.cpp | 2 +- api/tests/{server.cpp => server.c} | 45 +++++++++++++++++------------- 3 files changed, 27 insertions(+), 22 deletions(-) rename api/tests/{server.cpp => server.c} (94%) diff --git a/api/include/recorder.h b/api/include/recorder.h index 33c05949..95bd9a84 100644 --- a/api/include/recorder.h +++ b/api/include/recorder.h @@ -82,7 +82,7 @@ API_EXPORT int API_CALL mk_recorder_status(int type, const char *vhost, const ch * @param continue_record 流注销时是否继续等待录制还是立即停止录制 * @return 0代表成功,负数代表失败 */ -API_EXPORT int API_CALL mk_recorder_start(int type, const char *vhost, const char *app, const char *stream,bool wait_for_record, bool continue_record); +API_EXPORT int API_CALL mk_recorder_start(int type, const char *vhost, const char *app, const char *stream,int wait_for_record, int continue_record); /** * 停止录制 diff --git a/api/source/recorder.cpp b/api/source/recorder.cpp index 4763faa3..b5315bc8 100644 --- a/api/source/recorder.cpp +++ b/api/source/recorder.cpp @@ -57,7 +57,7 @@ API_EXPORT int API_CALL mk_recorder_status(int type, const char *vhost, const ch return Recorder::getRecordStatus((Recorder::type)type,vhost,app,stream); } -API_EXPORT int API_CALL mk_recorder_start(int type, const char *vhost, const char *app, const char *stream,bool wait_for_record, bool continue_record){ +API_EXPORT int API_CALL mk_recorder_start(int type, const char *vhost, const char *app, const char *stream,int wait_for_record, int continue_record){ assert(vhost && app && stream); return Recorder::startRecord((Recorder::type)type,vhost,app,stream,wait_for_record,continue_record); } diff --git a/api/tests/server.cpp b/api/tests/server.c similarity index 94% rename from api/tests/server.cpp rename to api/tests/server.c index 3d1e57ec..8da94261 100644 --- a/api/tests/server.cpp +++ b/api/tests/server.c @@ -24,9 +24,8 @@ * SOFTWARE. */ -#include -#include -#include +#include +#include #include "mediakit.h" #ifdef _WIN32 @@ -37,8 +36,6 @@ #endif -using namespace std; - #define LOG_LEV 4 /** @@ -80,7 +77,7 @@ void API_CALL on_mk_media_publish(const mk_media_info url_info, mk_media_info_get_params(url_info)); //允许推流,并且允许转rtxp/hls/mp4 - mk_publish_auth_invoker_do(invoker, nullptr, 1, 1, true); + mk_publish_auth_invoker_do(invoker, NULL, 1, 1, 1); } /** @@ -108,7 +105,7 @@ void API_CALL on_mk_media_play(const mk_media_info url_info, mk_media_info_get_params(url_info)); //允许播放 - mk_auth_invoker_do(invoker, nullptr); + mk_auth_invoker_do(invoker, NULL); } /** @@ -176,12 +173,12 @@ void API_CALL on_mk_http_request(const mk_parser parser, const char *url = mk_parser_get_url(parser); if(strcmp(url,"/api/test") != 0){ - *consumed = false; + *consumed = 0; return; } //只拦截api: /api/test - *consumed = true; + *consumed = 1; const char *response_header[] = {"Content-Type","text/html",NULL}; const char *content = "" @@ -230,7 +227,7 @@ void API_CALL on_mk_http_access(const mk_parser parser, mk_parser_get_content(parser)); //有访问权限,每次访问文件都需要鉴权 - mk_http_access_path_invoker_do(invoker, nullptr, nullptr, 0); + mk_http_access_path_invoker_do(invoker, NULL, NULL, 0); } /** @@ -291,12 +288,12 @@ void API_CALL on_mk_rtsp_get_realm(const mk_media_info url_info, } /** - * 请求认证用户密码事件,user_name为用户名,must_no_encrypt如果为true,则必须提供明文密码(因为此时是base64认证方式),否则会导致认证失败 + * 请求认证用户密码事件,user_name为用户名,must_no_encrypt如果为1,则必须提供明文密码(因为此时是base64认证方式),否则会导致认证失败 * 获取到密码后请调用invoker并输入对应类型的密码和密码类型,invoker执行时会匹配密码 * @param url_info 请求rtsp url相关信息 * @param realm rtsp认证realm * @param user_name rtsp认证用户名 - * @param must_no_encrypt 如果为true,则必须提供明文密码(因为此时是base64认证方式),否则会导致认证失败 + * @param must_no_encrypt 如果为1,则必须提供明文密码(因为此时是base64认证方式),否则会导致认证失败 * @param invoker 执行invoker返回rtsp专属认证的密码 * @param sender rtsp客户端信息 */ @@ -323,7 +320,7 @@ void API_CALL on_mk_rtsp_auth(const mk_media_info url_info, realm,user_name,(int)must_no_encrypt); //rtsp播放用户名跟密码一致 - mk_rtsp_auth_invoker_do(invoker,false,user_name); + mk_rtsp_auth_invoker_do(invoker,0,user_name); } /** @@ -368,7 +365,7 @@ void API_CALL on_mk_shell_login(const char *user_name, mk_tcp_session_peer_port(sender), user_name, passwd); //允许登录shell - mk_auth_invoker_do(invoker, nullptr); + mk_auth_invoker_do(invoker, NULL); } /** @@ -392,11 +389,19 @@ void API_CALL on_mk_flow_report(const mk_media_info url_info, (int)total_bytes, (int)total_seconds, (int)is_player); } +static int flag = 1; +static void on_exit(int sig){ + flag = 0; +} int main(int argc, char *argv[]) { - mk_env_init1(0, 0, 1, (string(argv[0]) + ".ini").data(), 0, nullptr, nullptr); - mk_http_server_start(80, false); - mk_rtsp_server_start(554, false); - mk_rtmp_server_start(1935, false); + char ini_path[2048] = {0}; + strcpy(ini_path,argv[0]); + strcat(ini_path,".ini"); + + mk_env_init1(0, 0, 1, ini_path, 0, NULL, NULL); + mk_http_server_start(80, 0); + mk_rtsp_server_start(554, 0); + mk_rtmp_server_start(1935, 0); mk_shell_server_start(9000); mk_rtp_server_start(10000); @@ -417,8 +422,8 @@ int main(int argc, char *argv[]) { }; mk_events_listen(&events); - static bool flag = true; - signal(SIGINT, [](int) { flag = false; });// 设置退出信号 + + signal(SIGINT, on_exit );// 设置退出信号 while (flag) { #ifdef _WIN32 Sleep(1000); From bb4e8b73b534951aca4666e22d83384f59f53f8f Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 25 Dec 2019 10:13:59 +0800 Subject: [PATCH 139/272] =?UTF-8?q?=E5=85=BC=E5=AE=B9=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E4=B8=8D=E8=A7=84=E8=8C=83=E7=9A=84rtmp=E6=8E=A8=E6=B5=81:#190?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtmp/RtmpMediaSource.h | 22 ++++++---------------- src/Rtmp/RtmpSession.cpp | 8 +------- src/Rtmp/RtmpSession.h | 1 - 3 files changed, 7 insertions(+), 24 deletions(-) diff --git a/src/Rtmp/RtmpMediaSource.h b/src/Rtmp/RtmpMediaSource.h index 5e13faae..e9f31915 100644 --- a/src/Rtmp/RtmpMediaSource.h +++ b/src/Rtmp/RtmpMediaSource.h @@ -57,8 +57,9 @@ public: const string &strApp, const string &strId, int ringSize = 0) : - MediaSource(RTMP_SCHEMA,vhost,strApp,strId), - _ringSize(ringSize) {} + MediaSource(RTMP_SCHEMA,vhost,strApp,strId), _ringSize(ringSize) { + _metadata = TitleMeta().getMetadata(); + } virtual ~RtmpMediaSource() {} @@ -95,7 +96,6 @@ public: return; } - _mapStamp[pkt->typeId] = pkt->timeStamp; if(!_pRing){ weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); _pRing = std::make_shared(_ringSize,[weakSelf](const EventPoller::Ptr &,int size,bool){ @@ -108,12 +108,13 @@ public: onReaderChanged(0); } - if(!_registed && _metadata && _mapCfgFrame.size() >= getTrackSize()){ - //在未注册的情况下,需要获取到metadata并且获取到全部的config帧才可以注册 + //如果输入了非config帧,那么说明不再可能获取config帧以及metadata,所以我们强制其为已注册 + if(!_registed){ _registed = true; regist(); } + _mapStamp[pkt->typeId] = pkt->timeStamp; _pRing->write(pkt,pkt->isVideoKeyFrame()); checkNoneReader(); } @@ -149,17 +150,6 @@ private: onNoneReader(); } } - - int getTrackSize(){ - int ret = 0; - if(_metadata["videocodecid"]){ - ++ret; - } - if(_metadata["audiocodecid"]){ - ++ret; - } - return ret; - } protected: AMFValue _metadata; unordered_map _mapCfgFrame; diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index a9f62916..93ac1929 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -437,8 +437,7 @@ void RtmpSession::setMetaData(AMFDecoder &dec) { throw std::runtime_error("can only set metadata"); } auto metadata = dec.load(); - //dumpMetadata(metadata); - _metadata_got = true; +// dumpMetadata(metadata); _pPublisherSrc->onGetMetaData(metadata); } @@ -496,11 +495,6 @@ void RtmpSession::onRtmpChunk(RtmpPacket &chunkData) { _stamp[chunkData.typeId % 2].revise(chunkData.timeStamp, chunkData.timeStamp, dts_out, dts_out, true); chunkData.timeStamp = dts_out; } - if(!_metadata_got && !chunkData.isCfgFrame()){ - //有些rtmp推流客户端不产生metadata,我们产生一个默认的metadata,目的是为了触发注册操作 - _metadata_got = true; - _pPublisherSrc->onGetMetaData(TitleMeta().getMetadata()); - } _pPublisherSrc->onWrite(std::make_shared(std::move(chunkData))); } break; diff --git a/src/Rtmp/RtmpSession.h b/src/Rtmp/RtmpSession.h index 9eac1e35..3930e871 100644 --- a/src/Rtmp/RtmpSession.h +++ b/src/Rtmp/RtmpSession.h @@ -89,7 +89,6 @@ private: string getStreamId(const string &str); void dumpMetadata(const AMFValue &metadata); private: - bool _metadata_got = false; std::string _strTcUrl; MediaInfo _mediaInfo; double _dNowReqID = 0; From 1bfe4937cd947727246852bc00af0e881f939ffb Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 25 Dec 2019 11:04:12 +0800 Subject: [PATCH 140/272] =?UTF-8?q?=E6=95=B4=E7=90=86MediaSource=E6=B4=BE?= =?UTF-8?q?=E7=94=9F=E7=B1=BB=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtmp/RtmpMediaSource.h | 177 +++++++++++++++++---------- src/Rtmp/RtmpMediaSourceMuxer.h | 2 +- src/Rtmp/RtmpPlayerImp.h | 2 +- src/Rtmp/RtmpSession.cpp | 2 +- src/Rtmp/RtmpToRtspMediaSource.h | 4 +- src/Rtsp/RtspMediaSource.h | 202 +++++++++++++++++++------------ src/Rtsp/RtspMediaSourceMuxer.h | 2 +- src/Rtsp/RtspPlayerImp.h | 2 +- src/Rtsp/RtspSession.cpp | 2 +- src/Rtsp/RtspToRtmpMediaSource.h | 4 +- 10 files changed, 246 insertions(+), 153 deletions(-) diff --git a/src/Rtmp/RtmpMediaSource.h b/src/Rtmp/RtmpMediaSource.h index e9f31915..77c014eb 100644 --- a/src/Rtmp/RtmpMediaSource.h +++ b/src/Rtmp/RtmpMediaSource.h @@ -48,118 +48,161 @@ using namespace toolkit; namespace mediakit { -class RtmpMediaSource: public MediaSource ,public RingDelegate { +/** + * rtmp媒体源的数据抽象 + * rtmp有关键的三要素,分别是metadata、config帧,普通帧 + * 其中metadata是非必须的,有些编码格式也没有config帧(比如MP3) + * 只要生成了这三要素,那么要实现rtmp推流、rtmp服务器就很简单了 + * rtmp推拉流协议中,先传递metadata,然后传递config帧,然后一直传递普通帧 + */ +class RtmpMediaSource : public MediaSource, public RingDelegate { public: typedef std::shared_ptr Ptr; typedef RingBuffer RingType; + /** + * 构造函数 + * @param vhost 虚拟主机名 + * @param app 应用名 + * @param stream_id 流id + * @param ring_size 可以设置固定的环形缓冲大小,0则自适应 + */ RtmpMediaSource(const string &vhost, - const string &strApp, - const string &strId, - int ringSize = 0) : - MediaSource(RTMP_SCHEMA,vhost,strApp,strId), _ringSize(ringSize) { + const string &app, + const string &stream_id, + int ring_size = 0) : + MediaSource(RTMP_SCHEMA, vhost, app, stream_id), _ring_size(ring_size) { _metadata = TitleMeta().getMetadata(); } virtual ~RtmpMediaSource() {} + /** + * 获取媒体源的环形缓冲 + */ const RingType::Ptr &getRing() const { - //获取媒体源的rtp环形缓冲 - return _pRing; + return _ring; } + /** + * 获取播放器个数 + * @return + */ int readerCount() override { - return _pRing ? _pRing->readerCount() : 0; + return _ring ? _ring->readerCount() : 0; } + /** + * 获取metadata + */ const AMFValue &getMetaData() const { - lock_guard lock(_mtxMap); + lock_guard lock(_mtx); return _metadata; } - template - void getConfigFrame(const FUN &f) { - lock_guard lock(_mtxMap); - for (auto &pr : _mapCfgFrame) { + + /** + * 获取所有的config帧 + */ + template + void getConfigFrame(const FUNC &f) { + lock_guard lock(_mtx); + for (auto &pr : _config_frame_map) { f(pr.second); } } - virtual void onGetMetaData(const AMFValue &metadata) { - lock_guard lock(_mtxMap); + /** + * 设置metadata + */ + virtual void setMetaData(const AMFValue &metadata) { + lock_guard lock(_mtx); _metadata = metadata; } - void onWrite(const RtmpPacket::Ptr &pkt,bool isKey = true) override { - lock_guard lock(_mtxMap); + /** + * 输入rtmp包 + * @param pkt rtmp包 + * @param isKey 是否为关键帧 + */ + void onWrite(const RtmpPacket::Ptr &pkt, bool isKey = true) override { + lock_guard lock(_mtx); if (pkt->isCfgFrame()) { - _mapCfgFrame[pkt->typeId] = pkt; - return; + _config_frame_map[pkt->typeId] = pkt; + return; } - if(!_pRing){ - weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - _pRing = std::make_shared(_ringSize,[weakSelf](const EventPoller::Ptr &,int size,bool){ - auto strongSelf = weakSelf.lock(); - if(!strongSelf){ - return; - } - strongSelf->onReaderChanged(size); - }); - onReaderChanged(0); - } + if (!_ring) { + weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); + auto lam = [weakSelf](const EventPoller::Ptr &, int size, bool) { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { + return; + } + strongSelf->onReaderChanged(size); + }; + _ring = std::make_shared(_ring_size, std::move(lam)); + onReaderChanged(0); - //如果输入了非config帧,那么说明不再可能获取config帧以及metadata,所以我们强制其为已注册 - if(!_registed){ - _registed = true; + //如果输入了非config帧, + //那么说明不再可能获取config帧以及metadata, + //所以我们强制其为已注册 regist(); } - - _mapStamp[pkt->typeId] = pkt->timeStamp; - _pRing->write(pkt,pkt->isVideoKeyFrame()); + _track_stamps_map[pkt->typeId] = pkt->timeStamp; + _ring->write(pkt, pkt->isVideoKeyFrame()); checkNoneReader(); - } + } + /** + * 获取当前时间戳 + */ uint32_t getTimeStamp(TrackType trackType) override { - lock_guard lock(_mtxMap); - switch (trackType){ + lock_guard lock(_mtx); + switch (trackType) { case TrackVideo: - return _mapStamp[MSG_VIDEO]; + return _track_stamps_map[MSG_VIDEO]; case TrackAudio: - return _mapStamp[MSG_AUDIO]; + return _track_stamps_map[MSG_AUDIO]; default: - return MAX(_mapStamp[MSG_VIDEO],_mapStamp[MSG_AUDIO]); + return MAX(_track_stamps_map[MSG_VIDEO], _track_stamps_map[MSG_AUDIO]); } } private: - void onReaderChanged(int size){ - //我们记录最后一次活动时间 - _readerTicker.resetTime(); - if(size != 0 || readerCount() != 0){ - //还有消费者正在观看该流 - _asyncEmitNoneReader = false; - return; - } - _asyncEmitNoneReader = true; - } + /** + * 每次增减消费者都会触发该函数 + */ + void onReaderChanged(int size) { + //我们记录最后一次活动时间 + _reader_changed_ticker.resetTime(); + if (size != 0 || readerCount() != 0) { + //还有消费者正在观看该流 + _async_emit_none_reader = false; + return; + } + _async_emit_none_reader = true; + } - void checkNoneReader(){ - GET_CONFIG(int,stream_none_reader_delay,General::kStreamNoneReaderDelayMS); - if(_asyncEmitNoneReader && _readerTicker.elapsedTime() > stream_none_reader_delay){ - _asyncEmitNoneReader = false; - onNoneReader(); - } - } + /** + * 检查是否无人消费该流, + * 如果无人消费且超过一定时间会触发onNoneReader事件 + */ + void checkNoneReader() { + GET_CONFIG(int, stream_none_reader_delay, General::kStreamNoneReaderDelayMS); + if (_async_emit_none_reader && _reader_changed_ticker.elapsedTime() > stream_none_reader_delay) { + _async_emit_none_reader = false; + onNoneReader(); + } + } protected: + int _ring_size; + bool _async_emit_none_reader = false; + mutable recursive_mutex _mtx; + Ticker _reader_changed_ticker; AMFValue _metadata; - unordered_map _mapCfgFrame; - unordered_map _mapStamp; - mutable recursive_mutex _mtxMap; - RingBuffer::Ptr _pRing; //rtp环形缓冲 - int _ringSize; - Ticker _readerTicker; - bool _asyncEmitNoneReader = false; - bool _registed = false; + RingBuffer::Ptr _ring; + unordered_map _track_stamps_map; + unordered_map _config_frame_map; }; } /* namespace mediakit */ diff --git a/src/Rtmp/RtmpMediaSourceMuxer.h b/src/Rtmp/RtmpMediaSourceMuxer.h index 55112dea..1ddb082a 100644 --- a/src/Rtmp/RtmpMediaSourceMuxer.h +++ b/src/Rtmp/RtmpMediaSourceMuxer.h @@ -54,7 +54,7 @@ public: } void onAllTrackReady(){ - _mediaSouce->onGetMetaData(getMetadata()); + _mediaSouce->setMetaData(getMetadata()); } // 设置TrackSource diff --git a/src/Rtmp/RtmpPlayerImp.h b/src/Rtmp/RtmpPlayerImp.h index b170a19f..3bcb5067 100644 --- a/src/Rtmp/RtmpPlayerImp.h +++ b/src/Rtmp/RtmpPlayerImp.h @@ -65,7 +65,7 @@ private: bool onCheckMeta(const AMFValue &val) override { _pRtmpMediaSrc = dynamic_pointer_cast(_pMediaSrc); if(_pRtmpMediaSrc){ - _pRtmpMediaSrc->onGetMetaData(val); + _pRtmpMediaSrc->setMetaData(val); } _delegate.reset(new RtmpDemuxer(val)); return true; diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index 93ac1929..f7f3632a 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -438,7 +438,7 @@ void RtmpSession::setMetaData(AMFDecoder &dec) { } auto metadata = dec.load(); // dumpMetadata(metadata); - _pPublisherSrc->onGetMetaData(metadata); + _pPublisherSrc->setMetaData(metadata); } void RtmpSession::onProcessCmd(AMFDecoder &dec) { diff --git a/src/Rtmp/RtmpToRtspMediaSource.h b/src/Rtmp/RtmpToRtspMediaSource.h index 31df804f..5acd206d 100644 --- a/src/Rtmp/RtmpToRtspMediaSource.h +++ b/src/Rtmp/RtmpToRtspMediaSource.h @@ -57,12 +57,12 @@ public: } virtual ~RtmpToRtspMediaSource(){} - void onGetMetaData(const AMFValue &metadata) override { + void setMetaData(const AMFValue &metadata) override { if(!_demuxer){ //在未调用onWrite前,设置Metadata能触发生成RtmpDemuxer _demuxer = std::make_shared(metadata); } - RtmpMediaSource::onGetMetaData(metadata); + RtmpMediaSource::setMetaData(metadata); } void onWrite(const RtmpPacket::Ptr &pkt,bool key_pos = true) override { diff --git a/src/Rtsp/RtspMediaSource.h b/src/Rtsp/RtspMediaSource.h index ef888ff7..b63b7be8 100644 --- a/src/Rtsp/RtspMediaSource.h +++ b/src/Rtsp/RtspMediaSource.h @@ -48,129 +48,179 @@ using namespace toolkit; namespace mediakit { -class RtspMediaSource: public MediaSource , public RingDelegate { +/** + * rtsp媒体源的数据抽象 + * rtsp有关键的两要素,分别是sdp、rtp包 + * 只要生成了这两要素,那么要实现rtsp推流、rtsp服务器就很简单了 + * rtsp推拉流协议中,先传递sdp,然后再协商传输方式(tcp/udp/组播),最后一直传递rtp + */ +class RtspMediaSource : public MediaSource, public RingDelegate { public: typedef ResourcePool PoolType; typedef std::shared_ptr Ptr; typedef RingBuffer RingType; - RtspMediaSource(const string &strVhost, - const string &strApp, - const string &strId, - int ringSize = 0) : - MediaSource(RTSP_SCHEMA,strVhost,strApp,strId), - _ringSize(ringSize){} + /** + * 构造函数 + * @param vhost 虚拟主机名 + * @param app 应用名 + * @param stream_id 流id + * @param ring_size 可以设置固定的环形缓冲大小,0则自适应 + */ + RtspMediaSource(const string &vhost, + const string &app, + const string &stream_id, + int ring_size = 0) : + MediaSource(RTSP_SCHEMA, vhost, app, stream_id), _ring_size(ring_size) {} virtual ~RtspMediaSource() {} + /** + * 获取媒体源的环形缓冲 + */ const RingType::Ptr &getRing() const { - //获取媒体源的rtp环形缓冲 - return _pRing; + return _ring; } - int readerCount() override { - return _pRing ? _pRing->readerCount() : 0; + /** + * 获取播放器个数 + */ + int readerCount() override { + return _ring ? _ring->readerCount() : 0; } - const string& getSdp() const { - //获取该源的媒体描述信息 - return _strSdp; + /** + * 获取该源的sdp + */ + const string &getSdp() const { + return _sdp; } + /** + * 获取相应轨道的ssrc + */ virtual uint32_t getSsrc(TrackType trackType) { - auto track = _sdpParser.getTrack(trackType); - if(!track){ + auto track = _sdp_parser.getTrack(trackType); + if (!track) { return 0; } return track->_ssrc; } + + /** + * 获取相应轨道的seqence + */ virtual uint16_t getSeqence(TrackType trackType) { - auto track = _sdpParser.getTrack(trackType); - if(!track){ + auto track = _sdp_parser.getTrack(trackType); + if (!track) { return 0; } return track->_seq; } + /** + * 获取相应轨道的时间戳,单位毫秒 + */ uint32_t getTimeStamp(TrackType trackType) override { - auto track = _sdpParser.getTrack(trackType); - if(track) { + auto track = _sdp_parser.getTrack(trackType); + if (track) { return track->_time_stamp; } - auto tracks = _sdpParser.getAvailableTrack(); - switch (tracks.size()){ - case 0: return 0; - case 1: return tracks[0]->_time_stamp; - default:return MAX(tracks[0]->_time_stamp,tracks[1]->_time_stamp); + auto tracks = _sdp_parser.getAvailableTrack(); + switch (tracks.size()) { + case 0: + return 0; + case 1: + return tracks[0]->_time_stamp; + default: + return MAX(tracks[0]->_time_stamp, tracks[1]->_time_stamp); } } + /** + * 更新时间戳 + */ virtual void setTimeStamp(uint32_t uiStamp) { - auto tracks = _sdpParser.getAvailableTrack(); + auto tracks = _sdp_parser.getAvailableTrack(); for (auto &track : tracks) { - track->_time_stamp = uiStamp; + track->_time_stamp = uiStamp; } } - virtual void onGetSDP(const string& sdp) { - //派生类设置该媒体源媒体描述信息 - _strSdp = sdp; - _sdpParser.load(sdp); - if(_pRing){ - regist(); + /** + * 设置sdp + */ + virtual void setSdp(const string &sdp) { + _sdp = sdp; + _sdp_parser.load(sdp); + if (_ring) { + regist(); } } - void onWrite(const RtpPacket::Ptr &rtppt, bool keyPos) override { - auto track = _sdpParser.getTrack(rtppt->type); - if(track){ - track->_seq = rtppt->sequence; - track->_time_stamp = rtppt->timeStamp; - track->_ssrc = rtppt->ssrc; + /** + * 输入rtp + * @param rtp rtp包 + * @param keyPos 该包是否为关键帧的第一个包 + */ + void onWrite(const RtpPacket::Ptr &rtp, bool keyPos) override { + auto track = _sdp_parser.getTrack(rtp->type); + if (track) { + track->_seq = rtp->sequence; + track->_time_stamp = rtp->timeStamp; + track->_ssrc = rtp->ssrc; } - if(!_pRing){ - weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - _pRing = std::make_shared(_ringSize,[weakSelf](const EventPoller::Ptr &,int size,bool){ - auto strongSelf = weakSelf.lock(); - if(!strongSelf){ - return; - } - strongSelf->onReaderChanged(size); - }); - onReaderChanged(0); - if(!_strSdp.empty()){ - regist(); - } + if (!_ring) { + weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); + auto lam = [weakSelf](const EventPoller::Ptr &, int size, bool) { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { + return; + } + strongSelf->onReaderChanged(size); + }; + _ring = std::make_shared(_ring_size, std::move(lam)); + onReaderChanged(0); + if (!_sdp.empty()) { + regist(); + } } - _pRing->write(rtppt,keyPos); - checkNoneReader(); + _ring->write(rtp, keyPos); + checkNoneReader(); } private: - void onReaderChanged(int size){ - //我们记录最后一次活动时间 - _readerTicker.resetTime(); - if(size != 0 || readerCount() != 0){ - //还有消费者正在观看该流 - _asyncEmitNoneReader = false; - return; - } - _asyncEmitNoneReader = true; - } + /** + * 每次增减消费者都会触发该函数 + */ + void onReaderChanged(int size) { + //我们记录最后一次活动时间 + _reader_changed_ticker.resetTime(); + if (size != 0 || readerCount() != 0) { + //还有消费者正在观看该流 + _async_emit_none_reader = false; + return; + } + _async_emit_none_reader = true; + } - void checkNoneReader(){ - GET_CONFIG(int,stream_none_reader_delay,General::kStreamNoneReaderDelayMS); - if(_asyncEmitNoneReader && _readerTicker.elapsedTime() > stream_none_reader_delay){ - _asyncEmitNoneReader = false; - onNoneReader(); - } + /** + * 检查是否无人消费该流, + * 如果无人消费且超过一定时间会触发onNoneReader事件 + */ + void checkNoneReader() { + GET_CONFIG(int, stream_none_reader_delay, General::kStreamNoneReaderDelayMS); + if (_async_emit_none_reader && _reader_changed_ticker.elapsedTime() > stream_none_reader_delay) { + _async_emit_none_reader = false; + onNoneReader(); + } } protected: - SdpParser _sdpParser; - string _strSdp; //媒体描述信息 - RingType::Ptr _pRing; //rtp环形缓冲 - int _ringSize; - Ticker _readerTicker; - bool _asyncEmitNoneReader = false; + int _ring_size; + bool _async_emit_none_reader = false; + Ticker _reader_changed_ticker; + SdpParser _sdp_parser; + string _sdp; + RingType::Ptr _ring; }; } /* namespace mediakit */ diff --git a/src/Rtsp/RtspMediaSourceMuxer.h b/src/Rtsp/RtspMediaSourceMuxer.h index 050e83c2..834374e6 100644 --- a/src/Rtsp/RtspMediaSourceMuxer.h +++ b/src/Rtsp/RtspMediaSourceMuxer.h @@ -58,7 +58,7 @@ public: } void onAllTrackReady(){ - _mediaSouce->onGetSDP(getSdp()); + _mediaSouce->setSdp(getSdp()); } // 设置TrackSource diff --git a/src/Rtsp/RtspPlayerImp.h b/src/Rtsp/RtspPlayerImp.h index 8992bded..fe6eff40 100644 --- a/src/Rtsp/RtspPlayerImp.h +++ b/src/Rtsp/RtspPlayerImp.h @@ -64,7 +64,7 @@ private: bool onCheckSDP(const string &sdp) override { _pRtspMediaSrc = dynamic_pointer_cast(_pMediaSrc); if(_pRtspMediaSrc){ - _pRtspMediaSrc->onGetSDP(sdp); + _pRtspMediaSrc->setSdp(sdp); } _delegate.reset(new RtspDemuxer(sdp)); return true; diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index 1d679e23..993ae7b3 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -265,7 +265,7 @@ void RtspSession::handleReq_ANNOUNCE(const Parser &parser) { _pushSrc = std::make_shared(_mediaInfo._vhost,_mediaInfo._app,_mediaInfo._streamid); _pushSrc->setListener(dynamic_pointer_cast(shared_from_this())); - _pushSrc->onGetSDP(sdpParser.toString()); + _pushSrc->setSdp(sdpParser.toString()); sendRtspResponse("200 OK",{"Content-Base",_strContentBase + "/"}); } diff --git a/src/Rtsp/RtspToRtmpMediaSource.h b/src/Rtsp/RtspToRtmpMediaSource.h index c4cdaf18..f27997f8 100644 --- a/src/Rtsp/RtspToRtmpMediaSource.h +++ b/src/Rtsp/RtspToRtmpMediaSource.h @@ -49,9 +49,9 @@ public: virtual ~RtspToRtmpMediaSource() {} - virtual void onGetSDP(const string &strSdp) override { + virtual void setSdp(const string &strSdp) override { _demuxer = std::make_shared(strSdp); - RtspMediaSource::onGetSDP(strSdp); + RtspMediaSource::setSdp(strSdp); } virtual void onWrite(const RtpPacket::Ptr &rtp, bool bKeyPos) override { From 37858f3b51d8ab296b5290a83eb1f456a336c00d Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 25 Dec 2019 11:33:31 +0800 Subject: [PATCH 141/272] update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6ed4253c..c5c63216 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,8 @@ git submodule update --init - 支持linux、macos、ios、android、windows平台。 - 支持画面秒开(GOP缓存)、极低延时([500毫秒内,最低可达100毫秒](https://github.com/zlmediakit/ZLMediaKit/wiki/%E5%BB%B6%E6%97%B6%E6%B5%8B%E8%AF%95))。 - [ZLMediaKit高并发实现原理](https://github.com/xiongziliang/ZLMediaKit/wiki/ZLMediaKit%E9%AB%98%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86)。 -- 提供完善的标准C API,可以作SDK用,或供其他语言调用。 -- 提供完整的MediaServer服务器,可以免开发直接部署为商用服务器。 +- 提供完善的标准[C API](https://github.com/xiongziliang/ZLMediaKit/tree/master/api/include),可以作SDK用,或供其他语言调用。 +- 提供完整的[MediaServer](https://github.com/xiongziliang/ZLMediaKit/tree/master/server)服务器,可以免开发直接部署为商用服务器。 ## 项目定位 - 移动嵌入式跨平台流媒体解决方案。 From 31f5d77880917e763e0e7d1ffefba7899228cd3e Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 25 Dec 2019 14:25:26 +0800 Subject: [PATCH 142/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0HttpRequester?= =?UTF-8?q?=E7=9A=84C=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/events_objects.h | 51 ++++++++-- api/include/httpclient.h | 174 ++++++++++++++++++++++++++++++++++ api/include/httpdownloader.h | 72 -------------- api/include/mediakit.h | 2 +- api/source/events_objects.cpp | 59 ++++++++++-- api/source/httpclient.cpp | 159 +++++++++++++++++++++++++++++++ api/source/httpdownloader.cpp | 54 ----------- api/tests/server.c | 10 +- src/Http/HttpRequester.cpp | 5 + src/Http/HttpRequester.h | 1 + 10 files changed, 442 insertions(+), 145 deletions(-) create mode 100755 api/include/httpclient.h delete mode 100755 api/include/httpdownloader.h create mode 100755 api/source/httpclient.cpp delete mode 100755 api/source/httpdownloader.cpp diff --git a/api/include/events_objects.h b/api/include/events_objects.h index 9c3b9e42..5c464caf 100644 --- a/api/include/events_objects.h +++ b/api/include/events_objects.h @@ -73,7 +73,7 @@ API_EXPORT const char* API_CALL mk_parser_get_tail(const mk_parser ctx); //Parser::getValues()["key"],获取HTTP头中特定字段 API_EXPORT const char* API_CALL mk_parser_get_header(const mk_parser ctx,const char *key); //Parser::Content(),获取HTTP body -API_EXPORT const char* API_CALL mk_parser_get_content(const mk_parser ctx); +API_EXPORT const char* API_CALL mk_parser_get_content(const mk_parser ctx, int *length); ///////////////////////////////////////////MediaInfo///////////////////////////////////////////// //MediaInfo对象的C映射 @@ -137,23 +137,62 @@ API_EXPORT uint16_t API_CALL mk_tcp_session_peer_port(const mk_tcp_session ctx); //TcpSession::get_local_port() API_EXPORT uint16_t API_CALL mk_tcp_session_local_port(const mk_tcp_session ctx); +///////////////////////////////////////////HttpBody///////////////////////////////////////////// +//HttpBody对象的C映射 +typedef void* mk_http_body; +/** + * 生成HttpStringBody + * @param str 字符串指针 + * @param len 字符串长度,为0则用strlen获取 + */ +API_EXPORT mk_http_body API_CALL mk_http_body_from_string(const char *str,int len); + +/** + * 生成HttpFileBody + * @param file_path 文件完整路径 + */ +API_EXPORT mk_http_body API_CALL mk_http_body_from_file(const char *file_path); + +/** + * 生成HttpMultiFormBody + * @param key_val 参数key-value + * @param file_path 文件完整路径 + */ +API_EXPORT mk_http_body API_CALL mk_http_body_from_multi_form(const char *key_val[],const char *file_path); + +/** + * 销毁HttpBody + */ +API_EXPORT void API_CALL mk_http_body_release(mk_http_body ctx); + ///////////////////////////////////////////HttpResponseInvoker///////////////////////////////////////////// //HttpSession::HttpResponseInvoker对象的C映射 typedef void* mk_http_response_invoker; +/** + * HttpSession::HttpResponseInvoker(const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body); + * @param response_code 譬如200 OK + * @param response_header 返回的http头,譬如 {"Content-Type","text/html",NULL} 必须以NULL结尾 + * @param response_body body对象 + */ +API_EXPORT void API_CALL mk_http_response_invoker_do(const mk_http_response_invoker ctx, + const char *response_code, + const char **response_header, + const mk_http_body response_body); + /** * HttpSession::HttpResponseInvoker(const string &codeOut, const StrCaseMap &headerOut, const string &body); * @param response_code 譬如200 OK * @param response_header 返回的http头,譬如 {"Content-Type","text/html",NULL} 必须以NULL结尾 * @param response_content 返回的content部分,譬如一个网页内容 */ -API_EXPORT void API_CALL mk_http_response_invoker_do(const mk_http_response_invoker ctx, - const char *response_code, - const char *response_header[], - const char *response_content); +API_EXPORT void API_CALL mk_http_response_invoker_do_string(const mk_http_response_invoker ctx, + const char *response_code, + const char **response_header, + const char *response_content); /** * HttpSession::HttpResponseInvoker(const StrCaseMap &requestHeader,const StrCaseMap &responseHeader,const string &filePath); - * @param request_parser 请求事件中的mk_parser对象,用于提取其中http头中的Range字段 + * @param request_parser 请求事件中的mk_parser对象,用于提取其中http头中的Range字段,通过该字段先fseek然后再发送文件部分片段 * @param response_header 返回的http头,譬如 {"Content-Type","text/html",NULL} 必须以NULL结尾 * @param response_file_path 返回的content部分,譬如/path/to/html/file */ diff --git a/api/include/httpclient.h b/api/include/httpclient.h new file mode 100755 index 00000000..06de495b --- /dev/null +++ b/api/include/httpclient.h @@ -0,0 +1,174 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MK_HTTPCLIENT_H_ +#define MK_HTTPCLIENT_H_ + +#include "common.h" +#include "events_objects.h" + +#ifdef __cplusplus +extern "C" { +#endif + +///////////////////////////////////////////HttpDownloader///////////////////////////////////////////// + +typedef void *mk_http_downloader; + +/** + * @param user_data 用户数据指针 + * @param code 错误代码,0代表成功 + * @param err_msg 错误提示 + * @param file_path 文件保存路径 + */ +typedef void(API_CALL *on_mk_download_complete)(void *user_data, int code, const char *err_msg, const char *file_path); + +/** + * 创建http[s]下载器 + * @return 下载器指针 + */ +API_EXPORT mk_http_downloader API_CALL mk_http_downloader_create(); + +/** + * 销毁http[s]下载器 + * @param ctx 下载器指针 + */ +API_EXPORT void API_CALL mk_http_downloader_release(mk_http_downloader ctx); + +/** + * 开始http[s]下载 + * @param ctx 下载器指针 + * @param url http[s]下载url + * @param file 文件保存路径 + * @param cb 回调函数 + * @param user_data 用户数据指针 + */ +API_EXPORT void API_CALL mk_http_downloader_start(mk_http_downloader ctx, const char *url, const char *file, on_mk_download_complete cb, void *user_data); + + +///////////////////////////////////////////HttpRequester///////////////////////////////////////////// +typedef void *mk_http_requester; + +/** + * http请求结果回调 + * 在code == 0时代表本次http会话是完整的(收到了http回复) + * 用户应该通过user_data获取到mk_http_requester对象 + * 然后通过mk_http_requester_get_response等函数获取相关回复数据 + * 在回调结束时,应该通过mk_http_requester_release函数销毁该对象 + * 或者调用mk_http_requester_clear函数后再复用该对象 + * @param user_data 用户数据指针 + * @param code 错误代码,0代表成功 + * @param err_msg 错误提示 + */ +typedef void(API_CALL *on_mk_http_requester_complete)(void *user_data, int code, const char *err_msg); + +/** + * 创建HttpRequester + */ +API_EXPORT mk_http_requester API_CALL mk_http_requester_create(); + +/** + * 在复用mk_http_requester对象时才需要用到此方法 + */ +API_EXPORT void API_CALL mk_http_requester_clear(mk_http_requester ctx); + +/** + * 销毁HttpRequester + * 如果调用了mk_http_requester_start函数且正在等待http回复, + * 也可以调用mk_http_requester_release方法取消本次http请求 + */ +API_EXPORT void API_CALL mk_http_requester_release(mk_http_requester ctx); + +/** + * 设置HTTP方法,批量GET/POST + */ +API_EXPORT void API_CALL mk_http_requester_set_method(mk_http_requester ctx,const char *method); + +/** + * 批量设置设置HTTP头 + * @param header 譬如 {"Content-Type","text/html",NULL} 必须以NULL结尾 + */ +API_EXPORT void API_CALL mk_http_requester_set_header(mk_http_requester ctx, const char *header[]); + +/** + * 添加HTTP头 + * @param key 譬如Content-Type + * @param value 譬如 text/html + * @param force 如果已经存在该key,是否强制替换 + */ +API_EXPORT void API_CALL mk_http_requester_add_header(mk_http_requester ctx,const char *key,const char *value,int force); + +/** + * 设置消息体, + * @param body mk_http_body对象,通过mk_http_body_from_string等函数生成,使用完毕后请调用mk_http_body_release释放之 + */ +API_EXPORT void API_CALL mk_http_requester_set_body(mk_http_requester ctx, mk_http_body body); + +/** + * 在收到HTTP回复后可调用该方法获取状态码 + * @return 譬如 200 OK + */ +API_EXPORT const char* API_CALL mk_http_requester_get_response_status(mk_http_requester ctx); + +/** + * 在收到HTTP回复后可调用该方法获取响应HTTP头 + * @param key HTTP头键名 + * @return HTTP头键值 + */ +API_EXPORT const char* API_CALL mk_http_requester_get_response_header(mk_http_requester ctx,const char *key); + +/** + * 在收到HTTP回复后可调用该方法获取响应HTTP body + * @param length 返回body长度,可以为null + * @return body指针 + */ +API_EXPORT const char* API_CALL mk_http_requester_get_response_body(mk_http_requester ctx, int *length); + +/** + * 在收到HTTP回复后可调用该方法获取响应 + * @return 响应对象 + */ +API_EXPORT mk_parser API_CALL mk_http_requester_get_response(mk_http_requester ctx); + +/** + * 设置回调函数 + * @param cb 回调函数,不能为空 + * @param user_data 用户数据指针 + */ +API_EXPORT void API_CALL mk_http_requester_set_cb(mk_http_requester ctx,on_mk_http_requester_complete cb, void *user_data); + +/** + * 开始url请求 + * @param url 请求url,支持http/https + * @param timeout_second 最大超时时间 + */ +API_EXPORT void API_CALL mk_http_requester_start(mk_http_requester ctx,const char *url, float timeout_second); + +#ifdef __cplusplus +} +#endif + +#endif /* MK_HTTPCLIENT_H_ */ diff --git a/api/include/httpdownloader.h b/api/include/httpdownloader.h deleted file mode 100755 index 0e8f85fd..00000000 --- a/api/include/httpdownloader.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 xiongziliang <771730766@qq.com> - * - * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef MK_HTTP_DOWNLOADER_H_ -#define MK_HTTP_DOWNLOADER_H_ - -#include "common.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void *mk_http_downloader; - -/** - * @param user_data 用户数据指针 - * @param code 错误代码,0代表成功 - * @param err_msg 错误提示 - * @param file_path 文件保存路径 - */ -typedef void(API_CALL *on_mk_download_complete)(void *user_data, int code, const char *err_msg, const char *file_path); - -/** - * 创建http[s]下载器 - * @return 下载器指针 - */ -API_EXPORT mk_http_downloader API_CALL mk_http_downloader_create(); - -/** - * 销毁http[s]下载器 - * @param ctx 下载器指针 - */ -API_EXPORT void API_CALL mk_http_downloader_release(mk_http_downloader ctx); - -/** - * 开始http[s]下载 - * @param ctx 下载器指针 - * @param url http[s]下载url - * @param file 文件保存路径 - * @param cb 回调函数 - * @param user_data 用户数据指针 - */ -API_EXPORT void API_CALL mk_http_downloader_start(mk_http_downloader ctx, const char *url, const char *file, on_mk_download_complete cb, void *user_data); - -#ifdef __cplusplus -} -#endif - -#endif /* MK_HTTP_DOWNLOADER_H_ */ diff --git a/api/include/mediakit.h b/api/include/mediakit.h index a6f42496..2be7bd67 100755 --- a/api/include/mediakit.h +++ b/api/include/mediakit.h @@ -28,7 +28,7 @@ #define MK_API_H_ #include "common.h" -#include "httpdownloader.h" +#include "httpclient.h" #include "media.h" #include "proxyplayer.h" #include "recorder.h" diff --git a/api/source/events_objects.cpp b/api/source/events_objects.cpp index 0a22566d..4108d57e 100644 --- a/api/source/events_objects.cpp +++ b/api/source/events_objects.cpp @@ -30,6 +30,8 @@ #include "Record/MP4Recorder.h" #include "Network/TcpSession.h" #include "Http/HttpSession.h" +#include "Http/HttpBody.h" +#include "Http/HttpClient.h" #include "Rtsp/RtspSession.h" using namespace mediakit; @@ -130,9 +132,12 @@ API_EXPORT const char* API_CALL mk_parser_get_header(const mk_parser ctx,const c Parser *parser = (Parser *)ctx; return parser->getValues()[key].c_str(); } -API_EXPORT const char* API_CALL mk_parser_get_content(const mk_parser ctx){ +API_EXPORT const char* API_CALL mk_parser_get_content(const mk_parser ctx, int *length){ assert(ctx); Parser *parser = (Parser *)ctx; + if(length){ + *length = parser->Content().size(); + } return parser->Content().c_str(); } @@ -250,9 +255,23 @@ API_EXPORT uint16_t API_CALL mk_tcp_session_local_port(const mk_tcp_session ctx) return session->get_local_port(); } -///////////////////////////////////////////HttpResponseInvoker///////////////////////////////////////////// -static StrCaseMap get_http_header( const char *response_header[]){ - StrCaseMap header; +///////////////////////////////////////////HttpBody///////////////////////////////////////////// +API_EXPORT mk_http_body API_CALL mk_http_body_from_string(const char *str,int len){ + assert(str); + if(!len){ + len = strlen(str); + } + return new HttpBody::Ptr(new HttpStringBody(string(str,len))); +} + +API_EXPORT mk_http_body API_CALL mk_http_body_from_file(const char *file_path){ + assert(file_path); + return new HttpBody::Ptr(new HttpFileBody(file_path)); +} + +template +static C get_http_header( const char *response_header[]){ + C header; for (int i = 0; response_header[i] != NULL;) { auto key = response_header[i]; auto value = response_header[i + 1]; @@ -265,10 +284,23 @@ static StrCaseMap get_http_header( const char *response_header[]){ } return std::move(header); } -API_EXPORT void API_CALL mk_http_response_invoker_do(const mk_http_response_invoker ctx, - const char *response_code, - const char *response_header[], - const char *response_content){ + +API_EXPORT mk_http_body API_CALL mk_http_body_from_multi_form(const char *key_val[],const char *file_path){ + assert(key_val && file_path); + return new HttpBody::Ptr(new HttpMultiFormBody(get_http_header(key_val),file_path)); +} + +API_EXPORT void API_CALL mk_http_body_release(mk_http_body ctx){ + assert(ctx); + HttpBody::Ptr *ptr = (HttpBody::Ptr *)ctx; + delete ptr; +} + +///////////////////////////////////////////HttpResponseInvoker///////////////////////////////////////////// +API_EXPORT void API_CALL mk_http_response_invoker_do_string(const mk_http_response_invoker ctx, + const char *response_code, + const char **response_header, + const char *response_content){ assert(ctx && response_code && response_header && response_content); auto header = get_http_header(response_header); HttpSession::HttpResponseInvoker *invoker = (HttpSession::HttpResponseInvoker *)ctx; @@ -285,6 +317,17 @@ API_EXPORT void API_CALL mk_http_response_invoker_do_file(const mk_http_response (*invoker).responseFile(((Parser*)(request_parser))->getValues(),header,response_file_path); } +API_EXPORT void API_CALL mk_http_response_invoker_do(const mk_http_response_invoker ctx, + const char *response_code, + const char **response_header, + const mk_http_body response_body){ + assert(ctx && response_code && response_header && response_body); + auto header = get_http_header(response_header); + HttpSession::HttpResponseInvoker *invoker = (HttpSession::HttpResponseInvoker *)ctx; + HttpBody::Ptr *body = (HttpBody::Ptr*) response_body; + (*invoker)(response_code,header,*body); +} + API_EXPORT mk_http_response_invoker API_CALL mk_http_response_invoker_clone(const mk_http_response_invoker ctx){ assert(ctx); HttpSession::HttpResponseInvoker *invoker = (HttpSession::HttpResponseInvoker *)ctx; diff --git a/api/source/httpclient.cpp b/api/source/httpclient.cpp new file mode 100755 index 00000000..01e4c68f --- /dev/null +++ b/api/source/httpclient.cpp @@ -0,0 +1,159 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include "httpclient.h" + +#include "Util/logger.h" +#include "Http/HttpDownloader.h" +#include "Http/HttpRequester.h" + +using namespace std; +using namespace toolkit; +using namespace mediakit; + +API_EXPORT mk_http_downloader API_CALL mk_http_downloader_create() { + HttpDownloader::Ptr *obj(new HttpDownloader::Ptr(new HttpDownloader())); + return (mk_http_downloader) obj; +} + +API_EXPORT void API_CALL mk_http_downloader_release(mk_http_downloader ctx) { + assert(ctx); + HttpDownloader::Ptr *obj = (HttpDownloader::Ptr *) ctx; + delete obj; +} + +API_EXPORT void API_CALL mk_http_downloader_start(mk_http_downloader ctx, const char *url, const char *file, on_mk_download_complete cb, void *user_data) { + assert(ctx && url && file); + HttpDownloader::Ptr *obj = (HttpDownloader::Ptr *) ctx; + (*obj)->setOnResult([cb, user_data](ErrCode code, const string &errMsg, const string &filePath) { + if (cb) { + cb(user_data, code, errMsg.data(), filePath.data()); + } + }); + (*obj)->startDownload(url, file, false); +} + + +///////////////////////////////////////////HttpRequester///////////////////////////////////////////// +API_EXPORT mk_http_requester API_CALL mk_http_requester_create(){ + HttpRequester::Ptr *ret = new HttpRequester::Ptr(new HttpRequester); + return ret; +} + +API_EXPORT void API_CALL mk_http_requester_clear(mk_http_requester ctx){ + assert(ctx); + HttpRequester::Ptr *obj = (HttpRequester::Ptr *)ctx; + (*obj)->clear(); +} + +API_EXPORT void API_CALL mk_http_requester_release(mk_http_requester ctx){ + assert(ctx); + HttpRequester::Ptr *obj = (HttpRequester::Ptr *)ctx; + delete obj; +} + +API_EXPORT void API_CALL mk_http_requester_set_method(mk_http_requester ctx,const char *method){ + assert(ctx); + HttpRequester::Ptr *obj = (HttpRequester::Ptr *)ctx; + (*obj)->setMethod(method); +} + +template +static C get_http_header( const char *response_header[]){ + C header; + for (int i = 0; response_header[i] != NULL;) { + auto key = response_header[i]; + auto value = response_header[i + 1]; + if (key && value) { + i += 2; + header[key] = value; + continue; + } + break; + } + return std::move(header); +} + +API_EXPORT void API_CALL mk_http_requester_set_body(mk_http_requester ctx, mk_http_body body){ + assert(ctx && body); + HttpRequester::Ptr *obj = (HttpRequester::Ptr *)ctx; + HttpBody::Ptr *body_obj = (HttpBody::Ptr *)body; + (*obj)->setBody(*body_obj); +} + +API_EXPORT void API_CALL mk_http_requester_set_header(mk_http_requester ctx, const char *header[]){ + assert(ctx && header); + auto header_obj = get_http_header(header); + HttpRequester::Ptr *obj = (HttpRequester::Ptr *)ctx; + (*obj)->setHeader(header_obj); +} + +API_EXPORT void API_CALL mk_http_requester_add_header(mk_http_requester ctx,const char *key,const char *value,int force){ + assert(ctx && key && value); + HttpRequester::Ptr *obj = (HttpRequester::Ptr *)ctx; + (*obj)->addHeader(key,value,force); +} + +API_EXPORT const char* API_CALL mk_http_requester_get_response_status(mk_http_requester ctx){ + assert(ctx); + HttpRequester::Ptr *obj = (HttpRequester::Ptr *)ctx; + return (*obj)->responseStatus().c_str(); +} + +API_EXPORT const char* API_CALL mk_http_requester_get_response_header(mk_http_requester ctx,const char *key){ + assert(ctx); + HttpRequester::Ptr *obj = (HttpRequester::Ptr *)ctx; + return (*obj)->response()[key].c_str(); +} + +API_EXPORT const char* API_CALL mk_http_requester_get_response_body(mk_http_requester ctx, int *length){ + assert(ctx); + HttpRequester::Ptr *obj = (HttpRequester::Ptr *)ctx; + if(length){ + *length = (*obj)->response().Content().size(); + } + return (*obj)->response().Content().c_str(); +} + +API_EXPORT mk_parser API_CALL mk_http_requester_get_response(mk_http_requester ctx){ + assert(ctx); + HttpRequester::Ptr *obj = (HttpRequester::Ptr *)ctx; + return (mk_parser)&((*obj)->response()); +} + +API_EXPORT void API_CALL mk_http_requester_set_cb(mk_http_requester ctx,on_mk_http_requester_complete cb, void *user_data){ + assert(ctx && cb); + HttpRequester::Ptr *obj = (HttpRequester::Ptr *)ctx; + (*obj)->setOnResult([cb,user_data](const SockException &ex,const string &status,const StrCaseMap &header,const string &strRecvBody){ + cb(user_data, ex.getErrCode(),ex.what()); + }); +} + +API_EXPORT void API_CALL mk_http_requester_start(mk_http_requester ctx,const char *url, float timeout_second){ + assert(ctx && url && url[0] && timeout_second > 0); + HttpRequester::Ptr *obj = (HttpRequester::Ptr *)ctx; + (*obj)->sendRequest(url,timeout_second); +} + diff --git a/api/source/httpdownloader.cpp b/api/source/httpdownloader.cpp deleted file mode 100755 index 4e742924..00000000 --- a/api/source/httpdownloader.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 xiongziliang <771730766@qq.com> - * - * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#include "httpdownloader.h" - -#include "Util/logger.h" -#include "Http/HttpDownloader.h" -using namespace std; -using namespace toolkit; -using namespace mediakit; - -API_EXPORT mk_http_downloader API_CALL mk_http_downloader_create() { - HttpDownloader::Ptr *obj(new HttpDownloader::Ptr(new HttpDownloader())); - return (mk_http_downloader) obj; -} - -API_EXPORT void API_CALL mk_http_downloader_release(mk_http_downloader ctx) { - assert(ctx); - HttpDownloader::Ptr *obj = (HttpDownloader::Ptr *) ctx; - delete obj; -} - -API_EXPORT void API_CALL mk_http_downloader_start(mk_http_downloader ctx, const char *url, const char *file, on_mk_download_complete cb, void *user_data) { - assert(ctx && url && file); - HttpDownloader::Ptr *obj = (HttpDownloader::Ptr *) ctx; - (*obj)->setOnResult([cb, user_data](ErrCode code, const string &errMsg, const string &filePath) { - if (cb) { - cb(user_data, code, errMsg.data(), filePath.data()); - } - }); - (*obj)->startDownload(url, file, false); -} diff --git a/api/tests/server.c b/api/tests/server.c index 8da94261..f29ac98b 100644 --- a/api/tests/server.c +++ b/api/tests/server.c @@ -169,7 +169,7 @@ void API_CALL on_mk_http_request(const mk_parser parser, mk_parser_get_url_params(parser), mk_parser_get_tail(parser), mk_parser_get_header(parser, "User-Agent"), - mk_parser_get_content(parser)); + mk_parser_get_content(parser,NULL)); const char *url = mk_parser_get_url(parser); if(strcmp(url,"/api/test") != 0){ @@ -190,7 +190,9 @@ void API_CALL on_mk_http_request(const mk_parser parser, "
    ""ZLMediaKit-4.0
    " "" ""; - mk_http_response_invoker_do(invoker,"200 OK",response_header,content); + mk_http_body body = mk_http_body_from_string(content,0); + mk_http_response_invoker_do(invoker, "200 OK", response_header, body); + mk_http_body_release(body); } /** @@ -224,7 +226,7 @@ void API_CALL on_mk_http_access(const mk_parser parser, mk_parser_get_url_params(parser), mk_parser_get_tail(parser), mk_parser_get_header(parser,"User-Agent"), - mk_parser_get_content(parser)); + mk_parser_get_content(parser,NULL)); //有访问权限,每次访问文件都需要鉴权 mk_http_access_path_invoker_do(invoker, NULL, NULL, 0); @@ -257,7 +259,7 @@ void API_CALL on_mk_http_before_access(const mk_parser parser, mk_parser_get_url_params(parser), mk_parser_get_tail(parser), mk_parser_get_header(parser, "User-Agent"), - mk_parser_get_content(parser)); + mk_parser_get_content(parser,NULL)); //覆盖path的值可以重定向文件 } diff --git a/src/Http/HttpRequester.cpp b/src/Http/HttpRequester.cpp index b96c7d45..aa9c962e 100644 --- a/src/Http/HttpRequester.cpp +++ b/src/Http/HttpRequester.cpp @@ -53,6 +53,7 @@ void HttpRequester::onResponseCompleted() { void HttpRequester::onDisconnect(const SockException &ex){ if(_onResult){ + const_cast(response()).setContent(_strRecvBody); _onResult(ex,responseStatus(),responseHeader(),_strRecvBody); _onResult = nullptr; } @@ -69,5 +70,9 @@ void HttpRequester::clear() { _onResult = nullptr; } +void HttpRequester::setOnResult(const HttpRequesterResult &onResult) { + _onResult = onResult; +} + }//namespace mediakit diff --git a/src/Http/HttpRequester.h b/src/Http/HttpRequester.h index dc6ecfb3..9de7523a 100644 --- a/src/Http/HttpRequester.h +++ b/src/Http/HttpRequester.h @@ -38,6 +38,7 @@ public: typedef std::function HttpRequesterResult; HttpRequester(); virtual ~HttpRequester(); + void setOnResult(const HttpRequesterResult &onResult); void startRequester(const string &url,const HttpRequesterResult &onResult,float timeOutSecond = 10); void clear() override ; private: From 0ea8f6fa28917cd2293cbc5d19f390e601074ad6 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 25 Dec 2019 14:27:43 +0800 Subject: [PATCH 143/272] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=8B=BC=E5=86=99?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/httpclient.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/include/httpclient.h b/api/include/httpclient.h index 06de495b..7afef024 100755 --- a/api/include/httpclient.h +++ b/api/include/httpclient.h @@ -103,7 +103,7 @@ API_EXPORT void API_CALL mk_http_requester_clear(mk_http_requester ctx); API_EXPORT void API_CALL mk_http_requester_release(mk_http_requester ctx); /** - * 设置HTTP方法,批量GET/POST + * 设置HTTP方法,譬如GET/POST */ API_EXPORT void API_CALL mk_http_requester_set_method(mk_http_requester ctx,const char *method); From 1b1071f62f23fcf18a4a6ef0fecec5bec3cecde9 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 25 Dec 2019 15:15:16 +0800 Subject: [PATCH 144/272] =?UTF-8?q?=E6=96=B0=E5=A2=9Ewebsocket=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=99=A8=E7=9A=84c=20api=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/events_objects.h | 4 ++ api/include/mediakit.h | 1 + api/include/websocket.h | 105 ++++++++++++++++++++++++++++++++++ api/source/events_objects.cpp | 27 +++++++++ api/source/websocket.cpp | 105 ++++++++++++++++++++++++++++++++++ 5 files changed, 242 insertions(+) create mode 100644 api/include/websocket.h create mode 100644 api/source/websocket.cpp diff --git a/api/include/events_objects.h b/api/include/events_objects.h index 5c464caf..e24a1603 100644 --- a/api/include/events_objects.h +++ b/api/include/events_objects.h @@ -136,6 +136,10 @@ API_EXPORT const char* API_CALL mk_tcp_session_local_ip(const mk_tcp_session ctx API_EXPORT uint16_t API_CALL mk_tcp_session_peer_port(const mk_tcp_session ctx); //TcpSession::get_local_port() API_EXPORT uint16_t API_CALL mk_tcp_session_local_port(const mk_tcp_session ctx); +//TcpSession::send() +API_EXPORT void API_CALL mk_tcp_session_send(const mk_tcp_session ctx,const char *data,int len); +//切换到该对象所在线程后再TcpSession::send() +API_EXPORT void API_CALL mk_tcp_session_send_safe(const mk_tcp_session ctx,const char *data,int len); ///////////////////////////////////////////HttpBody///////////////////////////////////////////// //HttpBody对象的C映射 diff --git a/api/include/mediakit.h b/api/include/mediakit.h index 2be7bd67..de08c278 100755 --- a/api/include/mediakit.h +++ b/api/include/mediakit.h @@ -35,5 +35,6 @@ #include "player.h" #include "pusher.h" #include "events.h" +#include "websocket.h" #endif /* MK_API_H_ */ diff --git a/api/include/websocket.h b/api/include/websocket.h new file mode 100644 index 00000000..a5d6fdcd --- /dev/null +++ b/api/include/websocket.h @@ -0,0 +1,105 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MK_WEBSOCKET_H +#define MK_WEBSOCKET_H + +#include "common.h" +#include "events_objects.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + /** + * 当websocket客户端连接服务器时触发 + * @param session 会话处理对象 + */ + void (API_CALL *on_mk_websocket_session_create)(mk_tcp_session session); + + /** + * session会话对象销毁时触发 + * 请在本回调中清理释放你的用户数据 + * 本事件中不能调用mk_tcp_session_send/mk_tcp_session_send_safe函数 + * @param session 会话处理对象 + */ + void (API_CALL *on_mk_websocket_session_destory)(mk_tcp_session session); + + /** + * 收到websocket客户端发过来的数据 + * @param session 会话处理对象 + * @param data 数据指针 + * @param len 数据长度 + */ + void (API_CALL *on_mk_websocket_session_data)(mk_tcp_session session,const char *data,int len); + + /** + * 每隔2秒的定时器,用于管理超时等任务 + * @param session 会话处理对象 + */ + void (API_CALL *on_mk_websocket_session_manager)(mk_tcp_session session); + + /** + * on_mk_websocket_session_destory之前触发on_mk_websocket_session_err + * 一般由于客户端断开tcp触发 + * 本事件中可以调用mk_tcp_session_send_safe函数 + * @param session 会话处理对象 + * @param code 错误代码 + * @param msg 错误提示 + */ + void (API_CALL *on_mk_websocket_session_err)(mk_tcp_session session,int code,const char *msg); +} mk_websocket_events; + +API_EXPORT void API_CALL mk_websocket_events_listen(const mk_websocket_events *events); + +/** + * 往websocket会话对象附着用户数据 + * @param session websocket会话对象 + * @param user_data 用户数据指针 + */ +API_EXPORT void API_CALL mk_websocket_session_set_user_data(mk_tcp_session session,void *user_data); + +/** + * 获取websocket会话对象上附着的用户数据 + * @param session websocket会话对象 + * @return 用户数据指针 + */ +API_EXPORT void* API_CALL mk_websocket_session_get_user_data(mk_tcp_session session,void *user_data); + +/** + * 开启websocket服务器,需要指出的是,websocket服务器包含了Http服务器的所有功能 + * 调用mk_websocket_server_start后不用再调用mk_http_server_start + * @param port 端口号,0则随机 + * @param ssl 是否为wss/ws + * @return 端口号,0代表失败 + */ +API_EXPORT uint16_t API_CALL mk_websocket_server_start(uint16_t port, int ssl); + +#ifdef __cplusplus +} +#endif +#endif //MK_WEBSOCKET_H diff --git a/api/source/events_objects.cpp b/api/source/events_objects.cpp index 4108d57e..41a8a982 100644 --- a/api/source/events_objects.cpp +++ b/api/source/events_objects.cpp @@ -254,6 +254,33 @@ API_EXPORT uint16_t API_CALL mk_tcp_session_local_port(const mk_tcp_session ctx) TcpSession *session = (TcpSession *)ctx; return session->get_local_port(); } +API_EXPORT void API_CALL mk_tcp_session_send(const mk_tcp_session ctx,const char *data,int len){ + assert(ctx && data); + if(!len){ + len = strlen(data); + } + TcpSession *session = (TcpSession *)ctx; + session->send(data,len); +} + +API_EXPORT void API_CALL mk_tcp_session_send_safe(const mk_tcp_session ctx,const char *data,int len){ + assert(ctx && data); + if(!len){ + len = strlen(data); + } + try { + weak_ptr weak_session = ((TcpSession *)ctx)->shared_from_this(); + string str = string(data,len); + ((TcpSession *)ctx)->async([weak_session,str](){ + auto session_session = weak_session.lock(); + if(session_session){ + session_session->send(str); + } + }); + }catch (std::exception &ex){ + WarnL << "can not got the strong pionter of this mk_tcp_session:" << ex.what(); + } +} ///////////////////////////////////////////HttpBody///////////////////////////////////////////// API_EXPORT mk_http_body API_CALL mk_http_body_from_string(const char *str,int len){ diff --git a/api/source/websocket.cpp b/api/source/websocket.cpp new file mode 100644 index 00000000..304963e1 --- /dev/null +++ b/api/source/websocket.cpp @@ -0,0 +1,105 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "websocket.h" +#include "Http/HttpSession.h" +#include "Http/WebSocketSession.h" +using namespace mediakit; + +static TcpServer::Ptr websocket_server[2]; +static mk_websocket_events s_events = {0}; + +class WebSocketSessionImp : public TcpSession { +public: + WebSocketSessionImp(const Socket::Ptr &pSock) : TcpSession(pSock){ + if(s_events.on_mk_websocket_session_create){ + s_events.on_mk_websocket_session_create(this); + } + } + + virtual ~WebSocketSessionImp(){ + if(s_events.on_mk_websocket_session_destory){ + s_events.on_mk_websocket_session_destory(this); + } + } + + void onRecv(const Buffer::Ptr &buffer) override { + if(s_events.on_mk_websocket_session_data){ + s_events.on_mk_websocket_session_data(this,buffer->data(),buffer->size()); + } + } + + void onError(const SockException &err) override{ + if(s_events.on_mk_websocket_session_err){ + s_events.on_mk_websocket_session_err(this,err.getErrCode(),err.what()); + } + } + + void onManager() override{ + if(s_events.on_mk_websocket_session_manager){ + s_events.on_mk_websocket_session_manager(this); + } + } + + void *_user_data; +}; + +API_EXPORT void API_CALL mk_websocket_events_listen(const mk_websocket_events *events){ + if(events){ + memcpy(&s_events,events, sizeof(s_events)); + }else{ + memset(&s_events,0,sizeof(s_events)); + } +} + +API_EXPORT void API_CALL mk_websocket_session_set_user_data(mk_tcp_session session,void *user_data){ + assert(session); + WebSocketSessionImp *obj = (WebSocketSessionImp *)session; + obj->_user_data = user_data; +} + +API_EXPORT void* API_CALL mk_websocket_session_get_user_data(mk_tcp_session session,void *user_data){ + assert(session); + WebSocketSessionImp *obj = (WebSocketSessionImp *)session; + return obj->_user_data; +} + +API_EXPORT uint16_t API_CALL mk_websocket_server_start(uint16_t port, int ssl){ + ssl = MAX(0,MIN(ssl,1)); + try { + websocket_server[ssl] = std::make_shared(); + if(ssl){ + websocket_server[ssl]->start>(port); + }else{ + websocket_server[ssl]->start>(port); + } + return websocket_server[ssl]->getPort(); + } catch (std::exception &ex) { + websocket_server[ssl].reset(); + WarnL << ex.what(); + return 0; + } +} \ No newline at end of file From b219a82ff5a89a6e18c45401a369629466c2fdfb Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 25 Dec 2019 15:45:22 +0800 Subject: [PATCH 145/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0websocket=20=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=99=A8=E7=9A=84c=E8=8C=83=E4=BE=8B=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/websocket.h | 2 +- api/source/common.cpp | 3 + api/source/websocket.cpp | 9 ++- api/tests/websocket.c | 129 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 api/tests/websocket.c diff --git a/api/include/websocket.h b/api/include/websocket.h index a5d6fdcd..c075fd5c 100644 --- a/api/include/websocket.h +++ b/api/include/websocket.h @@ -88,7 +88,7 @@ API_EXPORT void API_CALL mk_websocket_session_set_user_data(mk_tcp_session sessi * @param session websocket会话对象 * @return 用户数据指针 */ -API_EXPORT void* API_CALL mk_websocket_session_get_user_data(mk_tcp_session session,void *user_data); +API_EXPORT void* API_CALL mk_websocket_session_get_user_data(mk_tcp_session session); /** * 开启websocket服务器,需要指出的是,websocket服务器包含了Http服务器的所有功能 diff --git a/api/source/common.cpp b/api/source/common.cpp index 34b701d1..ca8074c8 100755 --- a/api/source/common.cpp +++ b/api/source/common.cpp @@ -64,12 +64,15 @@ API_EXPORT void API_CALL mk_env_init(const mk_config *cfg) { cfg->ssl_pwd); } +extern void mk_websocket_server_stop(); + API_EXPORT void API_CALL mk_stop_all_server(){ CLEAR_ARR(rtsp_server); CLEAR_ARR(rtmp_server); CLEAR_ARR(http_server); udpRtpServer = nullptr; tcpRtpServer = nullptr; + mk_websocket_server_stop(); } API_EXPORT void API_CALL mk_env_init1( int thread_num, diff --git a/api/source/websocket.cpp b/api/source/websocket.cpp index 304963e1..034a5a24 100644 --- a/api/source/websocket.cpp +++ b/api/source/websocket.cpp @@ -81,7 +81,7 @@ API_EXPORT void API_CALL mk_websocket_session_set_user_data(mk_tcp_session sessi obj->_user_data = user_data; } -API_EXPORT void* API_CALL mk_websocket_session_get_user_data(mk_tcp_session session,void *user_data){ +API_EXPORT void* API_CALL mk_websocket_session_get_user_data(mk_tcp_session session){ assert(session); WebSocketSessionImp *obj = (WebSocketSessionImp *)session; return obj->_user_data; @@ -102,4 +102,9 @@ API_EXPORT uint16_t API_CALL mk_websocket_server_start(uint16_t port, int ssl){ WarnL << ex.what(); return 0; } -} \ No newline at end of file +} + +void mk_websocket_server_stop(){ + CLEAR_ARR(websocket_server); +} + diff --git a/api/tests/websocket.c b/api/tests/websocket.c new file mode 100644 index 00000000..27ab33e5 --- /dev/null +++ b/api/tests/websocket.c @@ -0,0 +1,129 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include "mediakit.h" +#ifdef _WIN32 +#include "windows.h" +#else +#include "unistd.h" +#endif + +#define LOG_LEV 4 + +typedef struct { + mk_tcp_session *_session; + //下面你可以夹杂你的私货数据 + char your_some_useful_data[1024]; +} websocket_user_data; +/** + * 当websocket客户端连接服务器时触发 + * @param session 会话处理对象 + */ +void API_CALL on_mk_websocket_session_create(mk_tcp_session session){ + log_printf(LOG_LEV,"%s %d",mk_tcp_session_peer_ip(session),(int)mk_tcp_session_peer_port(session)); + websocket_user_data *user_data = malloc(sizeof(websocket_user_data)); + mk_websocket_session_set_user_data(session,user_data); +} + +/** + * session会话对象销毁时触发 + * 请在本回调中清理释放你的用户数据 + * 本事件中不能调用mk_tcp_session_send/mk_tcp_session_send_safe函数 + * @param session 会话处理对象 + */ +void API_CALL on_mk_websocket_session_destory(mk_tcp_session session){ + log_printf(LOG_LEV,"%s %d",mk_tcp_session_peer_ip(session),(int)mk_tcp_session_peer_port(session)); + websocket_user_data *user_data = (websocket_user_data *)mk_websocket_session_get_user_data(session); + free(user_data); +} + +/** + * 收到websocket客户端发过来的数据 + * @param session 会话处理对象 + * @param data 数据指针 + * @param len 数据长度 + */ +void API_CALL on_mk_websocket_session_data(mk_tcp_session session,const char *data,int len){ + log_printf(LOG_LEV,"from %s %d, data[%d]: %s",mk_tcp_session_peer_ip(session),(int)mk_tcp_session_peer_port(session), len,data); + mk_tcp_session_send(session,"echo:",0); + mk_tcp_session_send(session,data,len); +} + +/** + * 每隔2秒的定时器,用于管理超时等任务 + * @param session 会话处理对象 + */ +void API_CALL on_mk_websocket_session_manager(mk_tcp_session session){ + log_printf(LOG_LEV,"%s %d",mk_tcp_session_peer_ip(session),(int)mk_tcp_session_peer_port(session)); +} + +/** + * on_mk_websocket_session_destory之前触发on_mk_websocket_session_err + * 一般由于客户端断开tcp触发 + * 本事件中可以调用mk_tcp_session_send_safe函数 + * @param session 会话处理对象 + * @param code 错误代码 + * @param msg 错误提示 + */ +void API_CALL on_mk_websocket_session_err(mk_tcp_session session,int code,const char *msg){ + log_printf(LOG_LEV,"%s %d will destory: %d %s",mk_tcp_session_peer_ip(session),(int)mk_tcp_session_peer_port(session),code,msg); +} + +static int flag = 1; +static void on_exit(int sig){ + flag = 0; +} +int main(int argc, char *argv[]) { + char ini_path[2048] = {0}; + strcpy(ini_path,argv[0]); + strcat(ini_path,".ini"); + + mk_env_init1(0, 0, 1, ini_path, 0, NULL, NULL); + mk_websocket_events events = { + .on_mk_websocket_session_create = on_mk_websocket_session_create, + .on_mk_websocket_session_destory = on_mk_websocket_session_destory, + .on_mk_websocket_session_data = on_mk_websocket_session_data, + .on_mk_websocket_session_manager = on_mk_websocket_session_manager, + .on_mk_websocket_session_err = on_mk_websocket_session_err + }; + + mk_websocket_events_listen(&events); + mk_websocket_server_start(80,0); + + signal(SIGINT, on_exit );// 设置退出信号 + while (flag) { +#ifdef _WIN32 + Sleep(1000); +#else + sleep(1); +#endif + } + mk_stop_all_server(); + return 0; +} \ No newline at end of file From d59a706cc1a3b970fe1780672d4e1515677741fa Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 25 Dec 2019 20:07:42 +0800 Subject: [PATCH 146/272] =?UTF-8?q?=E7=AE=80=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Extension/Frame.h | 73 +++----------------------------------- src/Extension/Track.h | 2 +- src/Player/PlayerProxy.cpp | 4 +-- src/Rtmp/RtmpCodec.h | 2 +- src/Rtsp/RtpCodec.h | 2 +- 5 files changed, 9 insertions(+), 74 deletions(-) diff --git a/src/Extension/Frame.h b/src/Extension/Frame.h index 5e4234f7..5e5121f9 100644 --- a/src/Extension/Frame.h +++ b/src/Extension/Frame.h @@ -198,79 +198,15 @@ private: }; -/** - * 帧环形缓存接口类 - */ -class FrameRingInterface : public FrameWriterInterface{ -public: - typedef RingBuffer RingType; - typedef std::shared_ptr Ptr; - - FrameRingInterface(){} - virtual ~FrameRingInterface(){} - - /** - * 获取帧环形缓存 - * @return - */ - virtual RingType::Ptr getFrameRing() const = 0; - - /** - * 设置帧环形缓存 - * @param ring - */ - virtual void setFrameRing(const RingType::Ptr &ring) = 0; -}; - -/** - * 帧环形缓存 - */ -class FrameRing : public FrameRingInterface{ -public: - typedef std::shared_ptr Ptr; - - FrameRing(){ - } - virtual ~FrameRing(){} - - /** - * 获取帧环形缓存 - * @return - */ - RingType::Ptr getFrameRing() const override { - return _frameRing; - } - - /** - * 设置帧环形缓存 - * @param ring - */ - void setFrameRing(const RingType::Ptr &ring) override { - _frameRing = ring; - } - - /** - * 输入数据帧 - * @param frame - */ - void inputFrame(const Frame::Ptr &frame) override{ - if(_frameRing){ - _frameRing->write(frame,frame->keyFrame()); - } - } -protected: - RingType::Ptr _frameRing; -}; - /** * 支持代理转发的帧环形缓存 */ -class FrameRingInterfaceDelegate : public FrameRing { +class FrameDispatcher : public FrameWriterInterface { public: - typedef std::shared_ptr Ptr; + typedef std::shared_ptr Ptr; - FrameRingInterfaceDelegate(){} - virtual ~FrameRingInterfaceDelegate(){} + FrameDispatcher(){} + virtual ~FrameDispatcher(){} void addDelegate(const FrameWriterInterface::Ptr &delegate){ lock_guard lck(_mtx); @@ -287,7 +223,6 @@ public: * @param frame 帧 */ void inputFrame(const Frame::Ptr &frame) override{ - FrameRing::inputFrame(frame); lock_guard lck(_mtx); for(auto &pr : _delegateMap){ pr.second->inputFrame(frame); diff --git a/src/Extension/Track.h b/src/Extension/Track.h index 229a13ea..ccd675e9 100644 --- a/src/Extension/Track.h +++ b/src/Extension/Track.h @@ -39,7 +39,7 @@ namespace mediakit{ /** * 媒体通道描述类,也支持帧输入输出 */ -class Track : public FrameRingInterfaceDelegate , public CodecInfo{ +class Track : public FrameDispatcher , public CodecInfo{ public: typedef std::shared_ptr Ptr; Track(){} diff --git a/src/Player/PlayerProxy.cpp b/src/Player/PlayerProxy.cpp index 2babb7c4..656c9230 100644 --- a/src/Player/PlayerProxy.cpp +++ b/src/Player/PlayerProxy.cpp @@ -207,7 +207,7 @@ void PlayerProxy::onNoneReader(MediaSource &sender) { MediaSourceEvent::onNoneReader(sender); } -class MuteAudioMaker : public FrameRingInterfaceDelegate{ +class MuteAudioMaker : public FrameDispatcher{ public: typedef std::shared_ptr Ptr; @@ -221,7 +221,7 @@ public: auto aacFrame = std::make_shared((char *)MUTE_ADTS_DATA, MUTE_ADTS_DATA_LEN, _iAudioIndex * MUTE_ADTS_DATA_MS); - FrameRingInterfaceDelegate::inputFrame(aacFrame); + FrameDispatcher::inputFrame(aacFrame); } } } diff --git a/src/Rtmp/RtmpCodec.h b/src/Rtmp/RtmpCodec.h index 525a32da..4772a0c1 100644 --- a/src/Rtmp/RtmpCodec.h +++ b/src/Rtmp/RtmpCodec.h @@ -90,7 +90,7 @@ protected: }; -class RtmpCodec : public RtmpRing, public FrameRingInterfaceDelegate , public CodecInfo{ +class RtmpCodec : public RtmpRing, public FrameDispatcher , public CodecInfo{ public: typedef std::shared_ptr Ptr; RtmpCodec(){} diff --git a/src/Rtsp/RtpCodec.h b/src/Rtsp/RtpCodec.h index 7ce212c7..0ea893c8 100644 --- a/src/Rtsp/RtpCodec.h +++ b/src/Rtsp/RtpCodec.h @@ -147,7 +147,7 @@ protected: uint32_t _ui32TimeStamp = 0; }; -class RtpCodec : public RtpRing, public FrameRingInterfaceDelegate , public CodecInfo{ +class RtpCodec : public RtpRing, public FrameDispatcher , public CodecInfo{ public: typedef std::shared_ptr Ptr; RtpCodec(){} From 8b23f015090f2ea3e78563443e5c1da831f71312 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 25 Dec 2019 20:13:49 +0800 Subject: [PATCH 147/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/tests/server.c | 4 ++-- api/tests/websocket.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/tests/server.c b/api/tests/server.c index f29ac98b..16373389 100644 --- a/api/tests/server.c +++ b/api/tests/server.c @@ -392,7 +392,7 @@ void API_CALL on_mk_flow_report(const mk_media_info url_info, } static int flag = 1; -static void on_exit(int sig){ +static void s_on_exit(int sig){ flag = 0; } int main(int argc, char *argv[]) { @@ -425,7 +425,7 @@ int main(int argc, char *argv[]) { mk_events_listen(&events); - signal(SIGINT, on_exit );// 设置退出信号 + signal(SIGINT, s_on_exit );// 设置退出信号 while (flag) { #ifdef _WIN32 Sleep(1000); diff --git a/api/tests/websocket.c b/api/tests/websocket.c index 27ab33e5..7a39ee03 100644 --- a/api/tests/websocket.c +++ b/api/tests/websocket.c @@ -96,7 +96,7 @@ void API_CALL on_mk_websocket_session_err(mk_tcp_session session,int code,const } static int flag = 1; -static void on_exit(int sig){ +static void s_on_exit(int sig){ flag = 0; } int main(int argc, char *argv[]) { @@ -116,7 +116,7 @@ int main(int argc, char *argv[]) { mk_websocket_events_listen(&events); mk_websocket_server_start(80,0); - signal(SIGINT, on_exit );// 设置退出信号 + signal(SIGINT, s_on_exit );// 设置退出信号 while (flag) { #ifdef _WIN32 Sleep(1000); From 761a665b1b46c19105840e17470695b505faa266 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 26 Dec 2019 09:43:44 +0800 Subject: [PATCH 148/272] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=86=97=E4=BD=99?= =?UTF-8?q?=E8=AE=BE=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Extension/AACRtmp.cpp | 4 ---- src/Extension/AACRtp.cpp | 4 ---- src/Extension/H264Rtmp.cpp | 6 +----- src/Extension/H264Rtp.cpp | 6 +----- src/Extension/H265Rtp.cpp | 6 +----- src/Rtmp/RtmpCodec.h | 37 +++++++++++-------------------------- src/Rtmp/RtmpMuxer.cpp | 4 ++-- src/Rtmp/RtmpMuxer.h | 4 ++-- src/Rtsp/RtpCodec.h | 37 +++++++++++-------------------------- src/Rtsp/RtspMuxer.cpp | 4 ++-- src/Rtsp/RtspMuxer.h | 4 ++-- 11 files changed, 33 insertions(+), 83 deletions(-) diff --git a/src/Extension/AACRtmp.cpp b/src/Extension/AACRtmp.cpp index fd1d3733..71ea9e66 100644 --- a/src/Extension/AACRtmp.cpp +++ b/src/Extension/AACRtmp.cpp @@ -40,8 +40,6 @@ AACFrame::Ptr AACRtmpDecoder::obtainFrame() { } bool AACRtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt, bool key_pos) { - RtmpCodec::inputRtmp(pkt, false); - if (pkt->isCfgFrame()) { _aac_cfg = pkt->getAacCfg(); return false; @@ -79,8 +77,6 @@ AACRtmpEncoder::AACRtmpEncoder(const Track::Ptr &track) { } void AACRtmpEncoder::inputFrame(const Frame::Ptr &frame) { - RtmpCodec::inputFrame(frame); - if(_aac_cfg.empty()){ if(frame->prefixSize() >= 7){ //包含adts头,从adts头获取aac配置信息 diff --git a/src/Extension/AACRtp.cpp b/src/Extension/AACRtp.cpp index 69291f59..2f001959 100644 --- a/src/Extension/AACRtp.cpp +++ b/src/Extension/AACRtp.cpp @@ -40,8 +40,6 @@ AACRtpEncoder::AACRtpEncoder(uint32_t ui32Ssrc, } void AACRtpEncoder::inputFrame(const Frame::Ptr &frame) { - RtpCodec::inputFrame(frame); - GET_CONFIG(uint32_t, cycleMS, Rtp::kCycleMS); auto uiStamp = frame->stamp(); auto pcData = frame->data() + frame->prefixSize(); @@ -102,8 +100,6 @@ AACFrame::Ptr AACRtpDecoder::obtainFrame() { } bool AACRtpDecoder::inputRtp(const RtpPacket::Ptr &rtppack, bool key_pos) { - RtpCodec::inputRtp(rtppack, false); - // 获取rtp数据长度 int length = rtppack->size() - rtppack->offset; diff --git a/src/Extension/H264Rtmp.cpp b/src/Extension/H264Rtmp.cpp index 5bea72de..3ab77554 100644 --- a/src/Extension/H264Rtmp.cpp +++ b/src/Extension/H264Rtmp.cpp @@ -41,9 +41,7 @@ H264Frame::Ptr H264RtmpDecoder::obtainFrame() { } bool H264RtmpDecoder::inputRtmp(const RtmpPacket::Ptr &rtmp, bool key_pos) { - key_pos = decodeRtmp(rtmp); - RtmpCodec::inputRtmp(rtmp, key_pos); - return key_pos; + return decodeRtmp(rtmp); } bool H264RtmpDecoder::decodeRtmp(const RtmpPacket::Ptr &pkt) { @@ -105,8 +103,6 @@ H264RtmpEncoder::H264RtmpEncoder(const Track::Ptr &track) { } void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) { - RtmpCodec::inputFrame(frame); - auto pcData = frame->data() + frame->prefixSize(); auto iLen = frame->size() - frame->prefixSize(); auto type = H264_TYPE(((uint8_t*)pcData)[0]); diff --git a/src/Extension/H264Rtp.cpp b/src/Extension/H264Rtp.cpp index 367b3fce..c9861f16 100644 --- a/src/Extension/H264Rtp.cpp +++ b/src/Extension/H264Rtp.cpp @@ -76,9 +76,7 @@ H264Frame::Ptr H264RtpDecoder::obtainFrame() { } bool H264RtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool key_pos) { - key_pos = decodeRtp(rtp); - RtpCodec::inputRtp(rtp, key_pos); - return key_pos; + return decodeRtp(rtp); } bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { @@ -232,8 +230,6 @@ H264RtpEncoder::H264RtpEncoder(uint32_t ui32Ssrc, } void H264RtpEncoder::inputFrame(const Frame::Ptr &frame) { - RtpCodec::inputFrame(frame); - GET_CONFIG(uint32_t,cycleMS,Rtp::kCycleMS); auto pcData = frame->data() + frame->prefixSize(); auto uiStamp = frame->stamp(); diff --git a/src/Extension/H265Rtp.cpp b/src/Extension/H265Rtp.cpp index 6ac9e7e0..6d164dde 100644 --- a/src/Extension/H265Rtp.cpp +++ b/src/Extension/H265Rtp.cpp @@ -76,9 +76,7 @@ H265Frame::Ptr H265RtpDecoder::obtainFrame() { } bool H265RtpDecoder::inputRtp(const RtpPacket::Ptr &rtp, bool key_pos) { - key_pos = decodeRtp(rtp); - RtpCodec::inputRtp(rtp, key_pos); - return key_pos; + return decodeRtp(rtp); } bool H265RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { @@ -167,8 +165,6 @@ H265RtpEncoder::H265RtpEncoder(uint32_t ui32Ssrc, } void H265RtpEncoder::inputFrame(const Frame::Ptr &frame) { - RtpCodec::inputFrame(frame); - GET_CONFIG(uint32_t,cycleMS,Rtp::kCycleMS); uint8_t *pcData = (uint8_t*)frame->data() + frame->prefixSize(); auto uiStamp = frame->stamp(); diff --git a/src/Rtmp/RtmpCodec.h b/src/Rtmp/RtmpCodec.h index 4772a0c1..ccdb967a 100644 --- a/src/Rtmp/RtmpCodec.h +++ b/src/Rtmp/RtmpCodec.h @@ -34,25 +34,29 @@ using namespace toolkit; namespace mediakit{ -class RtmpRingInterface { +class RtmpRing{ public: + typedef std::shared_ptr Ptr; typedef RingBuffer RingType; - typedef std::shared_ptr Ptr; - RtmpRingInterface(){} - virtual ~RtmpRingInterface(){} + RtmpRing(){} + virtual ~RtmpRing(){} /** * 获取rtmp环形缓存 * @return */ - virtual RingType::Ptr getRtmpRing() const = 0; + virtual RingType::Ptr getRtmpRing() const{ + return _rtmpRing; + } /** * 设置rtmp环形缓存 * @param ring */ - virtual void setRtmpRing(const RingType::Ptr &ring) = 0; + virtual void setRtmpRing(const RingType::Ptr &ring){ + _rtmpRing = ring; + } /** * 输入rtmp包 @@ -60,26 +64,7 @@ public: * @param key_pos 是否为关键帧 * @return 是否为关键帧 */ - virtual bool inputRtmp(const RtmpPacket::Ptr &rtmp, bool key_pos) = 0; -}; - -class RtmpRing : public RtmpRingInterface { -public: - typedef std::shared_ptr Ptr; - - RtmpRing(){ - } - virtual ~RtmpRing(){} - - RingType::Ptr getRtmpRing() const override { - return _rtmpRing; - } - - void setRtmpRing(const RingType::Ptr &ring) override { - _rtmpRing = ring; - } - - bool inputRtmp(const RtmpPacket::Ptr &rtmp, bool key_pos) override{ + virtual bool inputRtmp(const RtmpPacket::Ptr &rtmp, bool key_pos){ if(_rtmpRing){ _rtmpRing->write(rtmp,key_pos); } diff --git a/src/Rtmp/RtmpMuxer.cpp b/src/Rtmp/RtmpMuxer.cpp index e252d410..d81a74fa 100644 --- a/src/Rtmp/RtmpMuxer.cpp +++ b/src/Rtmp/RtmpMuxer.cpp @@ -35,7 +35,7 @@ RtmpMuxer::RtmpMuxer(const TitleMeta::Ptr &title) { }else{ _metadata = title->getMetadata(); } - _rtmpRing = std::make_shared(); + _rtmpRing = std::make_shared(); } void RtmpMuxer::addTrack(const Track::Ptr &track) { @@ -82,7 +82,7 @@ const AMFValue &RtmpMuxer::getMetadata() const { return _metadata; } -RtmpRingInterface::RingType::Ptr RtmpMuxer::getRtmpRing() const { +RtmpRing::RingType::Ptr RtmpMuxer::getRtmpRing() const { return _rtmpRing; } diff --git a/src/Rtmp/RtmpMuxer.h b/src/Rtmp/RtmpMuxer.h index 3c4743b9..c1a007fa 100644 --- a/src/Rtmp/RtmpMuxer.h +++ b/src/Rtmp/RtmpMuxer.h @@ -54,7 +54,7 @@ public: * 获取rtmp环形缓存 * @return */ - RtmpRingInterface::RingType::Ptr getRtmpRing() const; + RtmpRing::RingType::Ptr getRtmpRing() const; /** * 添加ready状态的track @@ -72,7 +72,7 @@ public: */ void resetTracks() override ; private: - RtmpRingInterface::RingType::Ptr _rtmpRing; + RtmpRing::RingType::Ptr _rtmpRing; AMFValue _metadata; RtmpCodec::Ptr _encoder[TrackMax]; }; diff --git a/src/Rtsp/RtpCodec.h b/src/Rtsp/RtpCodec.h index 0ea893c8..338de751 100644 --- a/src/Rtsp/RtpCodec.h +++ b/src/Rtsp/RtpCodec.h @@ -34,25 +34,29 @@ using namespace toolkit; namespace mediakit{ -class RtpRingInterface { +class RtpRing{ public: + typedef std::shared_ptr Ptr; typedef RingBuffer RingType; - typedef std::shared_ptr Ptr; - RtpRingInterface(){} - virtual ~RtpRingInterface(){} + RtpRing(){} + virtual ~RtpRing(){} /** * 获取rtp环形缓存 * @return */ - virtual RingType::Ptr getRtpRing() const = 0; + virtual RingType::Ptr getRtpRing() const { + return _rtpRing; + } /** * 设置rtp环形缓存 * @param ring */ - virtual void setRtpRing(const RingType::Ptr &ring) = 0; + virtual void setRtpRing(const RingType::Ptr &ring){ + _rtpRing = ring; + } /** * 输入rtp包 @@ -60,26 +64,7 @@ public: * @param key_pos 是否为关键帧第一个rtp包 * @return 是否为关键帧第一个rtp包 */ - virtual bool inputRtp(const RtpPacket::Ptr &rtp, bool key_pos) = 0; -}; - -class RtpRing : public RtpRingInterface { -public: - typedef std::shared_ptr Ptr; - - RtpRing(){ - } - virtual ~RtpRing(){} - - RingType::Ptr getRtpRing() const override { - return _rtpRing; - } - - void setRtpRing(const RingType::Ptr &ring) override { - _rtpRing = ring; - } - - bool inputRtp(const RtpPacket::Ptr &rtp, bool key_pos) override{ + virtual bool inputRtp(const RtpPacket::Ptr &rtp, bool key_pos){ if(_rtpRing){ _rtpRing->write(rtp,key_pos); } diff --git a/src/Rtsp/RtspMuxer.cpp b/src/Rtsp/RtspMuxer.cpp index 1e482add..04ac57dd 100644 --- a/src/Rtsp/RtspMuxer.cpp +++ b/src/Rtsp/RtspMuxer.cpp @@ -35,7 +35,7 @@ RtspMuxer::RtspMuxer(const TitleSdp::Ptr &title){ } else{ _sdp = title->getSdp(); } - _rtpRing = std::make_shared(); + _rtpRing = std::make_shared(); } void RtspMuxer::addTrack(const Track::Ptr &track) { @@ -69,7 +69,7 @@ string RtspMuxer::getSdp() { return _sdp; } -RtpRingInterface::RingType::Ptr RtspMuxer::getRtpRing() const { +RtpRing::RingType::Ptr RtspMuxer::getRtpRing() const { return _rtpRing; } diff --git a/src/Rtsp/RtspMuxer.h b/src/Rtsp/RtspMuxer.h index 2d9a8736..18a74515 100644 --- a/src/Rtsp/RtspMuxer.h +++ b/src/Rtsp/RtspMuxer.h @@ -56,7 +56,7 @@ public: * 获取rtp环形缓存 * @return */ - RtpRingInterface::RingType::Ptr getRtpRing() const; + RtpRing::RingType::Ptr getRtpRing() const; /** * 添加ready状态的track @@ -76,7 +76,7 @@ public: private: string _sdp; RtpCodec::Ptr _encoder[TrackMax]; - RtpRingInterface::RingType::Ptr _rtpRing; + RtpRing::RingType::Ptr _rtpRing; }; From ca833c40d5b955d4e77b45b64f2523962754d0ba Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 26 Dec 2019 09:49:45 +0800 Subject: [PATCH 149/272] =?UTF-8?q?=E5=AE=8C=E5=96=84=E8=8C=83=E4=BE=8B?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/tests/websocket.c | 1 + 1 file changed, 1 insertion(+) diff --git a/api/tests/websocket.c b/api/tests/websocket.c index 7a39ee03..2ed6ffc9 100644 --- a/api/tests/websocket.c +++ b/api/tests/websocket.c @@ -48,6 +48,7 @@ typedef struct { void API_CALL on_mk_websocket_session_create(mk_tcp_session session){ log_printf(LOG_LEV,"%s %d",mk_tcp_session_peer_ip(session),(int)mk_tcp_session_peer_port(session)); websocket_user_data *user_data = malloc(sizeof(websocket_user_data)); + user_data->_session = session; mk_websocket_session_set_user_data(session,user_data); } From 806747a9d558497b3cd3f4315ac1db3f3d57893b Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 26 Dec 2019 09:50:10 +0800 Subject: [PATCH 150/272] =?UTF-8?q?=E5=AE=8C=E5=96=84=E8=8C=83=E4=BE=8B?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/tests/websocket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/tests/websocket.c b/api/tests/websocket.c index 2ed6ffc9..f9eaf7b1 100644 --- a/api/tests/websocket.c +++ b/api/tests/websocket.c @@ -37,7 +37,7 @@ #define LOG_LEV 4 typedef struct { - mk_tcp_session *_session; + mk_tcp_session _session; //下面你可以夹杂你的私货数据 char your_some_useful_data[1024]; } websocket_user_data; From 3830792c5717af6ed5b26159a8858b3079f400dc Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 26 Dec 2019 11:53:19 +0800 Subject: [PATCH 151/272] =?UTF-8?q?=E6=95=B4=E7=90=86MediaSource=E6=B4=BE?= =?UTF-8?q?=E7=94=9F=E7=B1=BB=20=E4=BF=AE=E5=A4=8D=E8=BD=AC=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=E6=88=96=E5=BD=95=E5=88=B6=E6=97=B6=E5=BF=BD=E7=84=B6?= =?UTF-8?q?Track=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/MultiMediaSourceMuxer.h | 16 ++- src/Player/PlayerBase.cpp | 11 ++ src/Player/PlayerBase.h | 15 +++ src/Record/Recorder.cpp | 32 ++++-- src/Rtmp/RtmpDemuxer.cpp | 19 +--- src/Rtmp/RtmpDemuxer.h | 6 - src/Rtmp/RtmpMediaSource.h | 4 +- ...RtspMediaSource.h => RtmpMediaSourceImp.h} | 103 ++++++++---------- src/Rtmp/RtmpPlayerImp.h | 1 - src/Rtmp/RtmpSession.cpp | 2 +- src/Rtmp/RtmpSession.h | 4 +- src/Rtsp/RtspDemuxer.cpp | 2 + ...RtmpMediaSource.h => RtspMediaSourceImp.h} | 101 ++++++++--------- src/Rtsp/RtspSession.cpp | 2 +- src/Rtsp/RtspSession.h | 4 +- 15 files changed, 172 insertions(+), 150 deletions(-) rename src/Rtmp/{RtmpToRtspMediaSource.h => RtmpMediaSourceImp.h} (59%) rename src/Rtsp/{RtspToRtmpMediaSource.h => RtspMediaSourceImp.h} (53%) diff --git a/src/Common/MultiMediaSourceMuxer.h b/src/Common/MultiMediaSourceMuxer.h index 7c39e5f8..d019b107 100644 --- a/src/Common/MultiMediaSourceMuxer.h +++ b/src/Common/MultiMediaSourceMuxer.h @@ -33,8 +33,14 @@ class MultiMediaSourceMuxer : public MediaSink , public std::enable_shared_from_this{ public: - typedef std::shared_ptr Ptr; + class Listener{ + public: + Listener() = default; + virtual ~Listener() = default; + virtual void onAllTrackReady() = 0; + }; + typedef std::shared_ptr Ptr; MultiMediaSourceMuxer(const string &vhost, const string &strApp, const string &strId, @@ -100,6 +106,9 @@ public: } } + void setTrackListener(Listener *listener){ + _listener = listener; + } protected: /** * 添加音视频媒体 @@ -139,10 +148,15 @@ protected: _rtsp->setTrackSource(shared_from_this()); _rtsp->onAllTrackReady(); } + + if(_listener){ + _listener->onAllTrackReady(); + } } private: RtmpMediaSourceMuxer::Ptr _rtmp; RtspMediaSourceMuxer::Ptr _rtsp; + Listener *_listener; }; diff --git a/src/Player/PlayerBase.cpp b/src/Player/PlayerBase.cpp index 7a66a0ee..5b94a976 100644 --- a/src/Player/PlayerBase.cpp +++ b/src/Player/PlayerBase.cpp @@ -110,4 +110,15 @@ vector Demuxer::getTracks(bool trackReady) const { float Demuxer::getDuration() const { return _fDuration; } + +void Demuxer::onAddTrack(const Track::Ptr &track){ + if(_listener){ + _listener->onAddTrack(track); + } +} + +void Demuxer::setTrackListener(Demuxer::Listener *listener) { + _listener = listener; +} + } /* namespace mediakit */ diff --git a/src/Player/PlayerBase.h b/src/Player/PlayerBase.h index 7e8f55ff..0dd74bf8 100644 --- a/src/Player/PlayerBase.h +++ b/src/Player/PlayerBase.h @@ -241,6 +241,13 @@ protected: class Demuxer : public PlayerBase{ public: + class Listener{ + public: + Listener() = default; + virtual ~Listener() = default; + virtual void onAddTrack(const Track::Ptr &track) = 0; + }; + Demuxer(){}; virtual ~Demuxer(){}; @@ -267,7 +274,15 @@ public: * @return 节目总时长,单位秒 */ float getDuration() const override; + + /** + * 设置track监听器 + */ + void setTrackListener(Listener *listener); protected: + void onAddTrack(const Track::Ptr &track); +protected: + Listener *_listener; AudioTrack::Ptr _audioTrack; VideoTrack::Ptr _videoTrack; Ticker _ticker; diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index b2062c28..5c7f1397 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -220,14 +220,12 @@ private: //保存NoticeCenter的强引用,防止在MediaSourceWatcher单例释放前释放NoticeCenter单例 _notice_center = NoticeCenter::Instance().shared_from_this(); _notice_center->addListener(this,Broadcast::kBroadcastMediaChanged,[this](BroadcastMediaChangedArgs){ - if(bRegist){ - onRegist(sender); - }else{ - onUnRegist(sender); + if(!bRegist){ + removeRecorder(sender); } }); _notice_center->addListener(this,Broadcast::kBroadcastMediaResetTracks,[this](BroadcastMediaResetTracksArgs){ - onRegist(sender); + addRecorder(sender); }); } @@ -236,7 +234,7 @@ private: _notice_center->delListener(this,Broadcast::kBroadcastMediaResetTracks); } - void onRegist(MediaSource &sender){ + void addRecorder(MediaSource &sender){ auto key = getRecorderKey(sender.getVhost(),sender.getApp(),sender.getId()); lock_guard lck(_recorder_mtx); auto it = _recorder_map.find(key); @@ -247,14 +245,14 @@ private: if(!it->second->isRecording() || it->second->getSchema() == sender.getSchema()){ // 绑定的协议一致或者并未正在录制则替换tracks - auto tracks = sender.getTracks(true); + auto tracks = sender.getTracks(needTrackReady()); if (!tracks.empty()) { it->second->attachTracks(std::move(tracks),sender.getSchema()); } } } - void onUnRegist(MediaSource &sender){ + void removeRecorder(MediaSource &sender){ auto key = getRecorderKey(sender.getVhost(),sender.getApp(),sender.getId()); lock_guard lck(_recorder_mtx); auto it = _recorder_map.find(key); @@ -284,7 +282,7 @@ private: vector findTracks(const string &vhost, const string &app, const string &stream_id,string &schema) { auto src = MediaSource::find(RTMP_SCHEMA, vhost, app, stream_id); if (src) { - auto ret = src->getTracks(true); + auto ret = src->getTracks(needTrackReady()); if (!ret.empty()) { schema = RTMP_SCHEMA; return std::move(ret); @@ -294,7 +292,7 @@ private: src = MediaSource::find(RTSP_SCHEMA, vhost, app, stream_id); if (src) { schema = RTSP_SCHEMA; - return src->getTracks(true); + return src->getTracks(needTrackReady()); } return vector(); } @@ -320,6 +318,20 @@ private: } return ret; } + + /** + * 有些录制类型不需要track就绪即可录制 + */ + bool needTrackReady(){ + switch (type){ + case Recorder::type_hls: + return false; + case Recorder::type_mp4: + return true; + default: + return true; + } + } private: recursive_mutex _recorder_mtx; NoticeCenter::Ptr _notice_center; diff --git a/src/Rtmp/RtmpDemuxer.cpp b/src/Rtmp/RtmpDemuxer.cpp index 818b0af5..ac57e0c6 100644 --- a/src/Rtmp/RtmpDemuxer.cpp +++ b/src/Rtmp/RtmpDemuxer.cpp @@ -29,23 +29,6 @@ namespace mediakit { - -RtmpDemuxer::RtmpDemuxer(const AMFValue &val) { - try { - makeVideoTrack(val["videocodecid"]); - makeAudioTrack(val["audiocodecid"]); - val.object_for_each([&](const string &key, const AMFValue &val) { - if (key == "duration") { - _fDuration = val.as_number(); - return; - } - }); - }catch (std::exception &ex){ - WarnL << ex.what(); - } -} - - bool RtmpDemuxer::inputRtmp(const RtmpPacket::Ptr &pkt) { switch (pkt->typeId) { case MSG_VIDEO: { @@ -86,6 +69,7 @@ void RtmpDemuxer::makeVideoTrack(const AMFValue &videoCodec) { if (_videoRtmpDecoder) { //设置rtmp解码器代理,生成的frame写入该Track _videoRtmpDecoder->addDelegate(_videoTrack); + onAddTrack(_videoTrack); } else { //找不到相应的rtmp解码器,该track无效 _videoTrack.reset(); @@ -102,6 +86,7 @@ void RtmpDemuxer::makeAudioTrack(const AMFValue &audioCodec) { if (_audioRtmpDecoder) { //设置rtmp解码器代理,生成的frame写入该Track _audioRtmpDecoder->addDelegate(_audioTrack); + onAddTrack(_audioTrack); } else { //找不到相应的rtmp解码器,该track无效 _audioTrack.reset(); diff --git a/src/Rtmp/RtmpDemuxer.h b/src/Rtmp/RtmpDemuxer.h index 64ef1cf5..243ea676 100644 --- a/src/Rtmp/RtmpDemuxer.h +++ b/src/Rtmp/RtmpDemuxer.h @@ -47,12 +47,6 @@ public: * 等效于RtmpDemuxer(AMFValue(AMF_NULL)) */ RtmpDemuxer(){} - /** - * 构造rtmp解复用器 - * @param val rtmp的metadata,可以传入null类型, - * 这样就会在inputRtmp时异步探测媒体编码格式 - */ - RtmpDemuxer(const AMFValue &val); virtual ~RtmpDemuxer(){}; /** diff --git a/src/Rtmp/RtmpMediaSource.h b/src/Rtmp/RtmpMediaSource.h index 77c014eb..12f8b10c 100644 --- a/src/Rtmp/RtmpMediaSource.h +++ b/src/Rtmp/RtmpMediaSource.h @@ -122,9 +122,9 @@ public: /** * 输入rtmp包 * @param pkt rtmp包 - * @param isKey 是否为关键帧 + * @param key 是否为关键帧 */ - void onWrite(const RtmpPacket::Ptr &pkt, bool isKey = true) override { + void onWrite(const RtmpPacket::Ptr &pkt, bool key = true) override { lock_guard lock(_mtx); if (pkt->isCfgFrame()) { _config_frame_map[pkt->typeId] = pkt; diff --git a/src/Rtmp/RtmpToRtspMediaSource.h b/src/Rtmp/RtmpMediaSourceImp.h similarity index 59% rename from src/Rtmp/RtmpToRtspMediaSource.h rename to src/Rtmp/RtmpMediaSourceImp.h index 5acd206d..95fba9fc 100644 --- a/src/Rtmp/RtmpToRtspMediaSource.h +++ b/src/Rtmp/RtmpMediaSourceImp.h @@ -39,56 +39,40 @@ #include "RtmpMediaSource.h" #include "RtmpDemuxer.h" #include "Common/MultiMediaSourceMuxer.h" - using namespace std; using namespace toolkit; namespace mediakit { - -class RtmpToRtspMediaSource: public RtmpMediaSource { +class RtmpMediaSourceImp: public RtmpMediaSource, public Demuxer::Listener , public MultiMediaSourceMuxer::Listener { public: - typedef std::shared_ptr Ptr; + typedef std::shared_ptr Ptr; - RtmpToRtspMediaSource(const string &vhost, - const string &app, - const string &id, - int ringSize = 0) : - RtmpMediaSource(vhost, app, id,ringSize){ - } - virtual ~RtmpToRtspMediaSource(){} - - void setMetaData(const AMFValue &metadata) override { - if(!_demuxer){ - //在未调用onWrite前,设置Metadata能触发生成RtmpDemuxer - _demuxer = std::make_shared(metadata); - } - RtmpMediaSource::setMetaData(metadata); + /** + * 构造函数 + * @param vhost 虚拟主机 + * @param app 应用名 + * @param id 流id + * @param ringSize 环形缓存大小 + */ + RtmpMediaSourceImp(const string &vhost, const string &app, const string &id, int ringSize = 0) : RtmpMediaSource(vhost, app, id, ringSize) { + _demuxer = std::make_shared(); + _demuxer->setTrackListener(this); } + ~RtmpMediaSourceImp() = default; + + /** + * 输入rtmp并解析 + */ void onWrite(const RtmpPacket::Ptr &pkt,bool key_pos = true) override { - if(!_demuxer){ - //尚未获取Metadata,那么不管有没有Metadata,都生成RtmpDemuxer - _demuxer = std::make_shared(); - } _demuxer->inputRtmp(pkt); - if(!_muxer && _demuxer->isInited(2000)){ - _muxer = std::make_shared(getVhost(), - getApp(), - getId(), - _demuxer->getDuration(), - _enableRtsp, - false,//不重复生成rtmp - _enableHls, - _enableMP4); - for (auto &track : _demuxer->getTracks(false)){ - _muxer->addTrack(track); - track->addDelegate(_muxer); - } - _muxer->setListener(getListener()); - } RtmpMediaSource::onWrite(pkt,key_pos); } + /** + * 设置监听器 + * @param listener + */ void setListener(const std::weak_ptr &listener) override { RtmpMediaSource::setListener(listener); if(_muxer){ @@ -96,41 +80,46 @@ public: } } + /** + * 播放器总数 + */ int readerCount() override { return RtmpMediaSource::readerCount() + (_muxer ? _muxer->readerCount() : 0); } - /** - * 获取track - * @return - */ - vector getTracks(bool trackReady) const override { - if(!_demuxer){ - return this->RtmpMediaSource::getTracks(trackReady); - } - return _demuxer->getTracks(trackReady); - } - /** * 设置协议转换 * @param enableRtsp 是否转换成rtsp * @param enableHls 是否转换成hls * @param enableMP4 是否mp4录制 */ - void setProtocolTranslation(bool enableRtsp,bool enableHls,bool enableMP4){ -// DebugL << enableRtsp << " " << enableHls << " " << enableMP4; - _enableRtsp = enableRtsp; - _enableHls = enableHls; - _enableMP4 = enableMP4; + void setProtocolTranslation(bool enableRtsp, bool enableHls, bool enableMP4) { + //不重复生成rtmp + _muxer = std::make_shared(getVhost(), getApp(), getId(), 0, enableRtsp, false, enableHls, enableMP4); + _muxer->setListener(getListener()); + _muxer->setTrackListener(this); + } + + /** + * _demuxer触发的添加Track事件 + */ + void onAddTrack(const Track::Ptr &track) override { + if(_muxer){ + _muxer->addTrack(track); + track->addDelegate(_muxer); + } + } + + /** + * _muxer触发的所有Track就绪的事件 + */ + void onAllTrackReady() override{ + setTrackSource(_muxer); } private: RtmpDemuxer::Ptr _demuxer; MultiMediaSourceMuxer::Ptr _muxer; - bool _enableHls = true; - bool _enableMP4 = false; - bool _enableRtsp = true; }; - } /* namespace mediakit */ #endif /* SRC_RTMP_RTMPTORTSPMEDIASOURCE_H_ */ diff --git a/src/Rtmp/RtmpPlayerImp.h b/src/Rtmp/RtmpPlayerImp.h index 3bcb5067..426ceedd 100644 --- a/src/Rtmp/RtmpPlayerImp.h +++ b/src/Rtmp/RtmpPlayerImp.h @@ -67,7 +67,6 @@ private: if(_pRtmpMediaSrc){ _pRtmpMediaSrc->setMetaData(val); } - _delegate.reset(new RtmpDemuxer(val)); return true; } void onMediaData(const RtmpPacket::Ptr &chunkData) override { diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index f7f3632a..e28c0d80 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -169,7 +169,7 @@ void RtmpSession::onCmd_publish(AMFDecoder &dec) { shutdown(SockException(Err_shutdown,errMsg)); return; } - _pPublisherSrc.reset(new RtmpToRtspMediaSource(_mediaInfo._vhost,_mediaInfo._app,_mediaInfo._streamid)); + _pPublisherSrc.reset(new RtmpMediaSourceImp(_mediaInfo._vhost,_mediaInfo._app,_mediaInfo._streamid)); _pPublisherSrc->setListener(dynamic_pointer_cast(shared_from_this())); //设置转协议 _pPublisherSrc->setProtocolTranslation(enableRtxp,enableHls,enableMP4); diff --git a/src/Rtmp/RtmpSession.h b/src/Rtmp/RtmpSession.h index 3930e871..437f4910 100644 --- a/src/Rtmp/RtmpSession.h +++ b/src/Rtmp/RtmpSession.h @@ -33,7 +33,7 @@ #include "utils.h" #include "Common/config.h" #include "RtmpProtocol.h" -#include "RtmpToRtspMediaSource.h" +#include "RtmpMediaSourceImp.h" #include "Util/util.h" #include "Util/TimeTicker.h" #include "Network/TcpSession.h" @@ -94,7 +94,7 @@ private: double _dNowReqID = 0; Ticker _ticker;//数据接收时间 RingBuffer::RingReader::Ptr _pRingReader; - std::shared_ptr _pPublisherSrc; + std::shared_ptr _pPublisherSrc; std::weak_ptr _pPlayerSrc; //时间戳修整器 Stamp _stamp[2]; diff --git a/src/Rtsp/RtspDemuxer.cpp b/src/Rtsp/RtspDemuxer.cpp index 06d75e83..69929463 100644 --- a/src/Rtsp/RtspDemuxer.cpp +++ b/src/Rtsp/RtspDemuxer.cpp @@ -89,6 +89,7 @@ void RtspDemuxer::makeAudioTrack(const SdpTrack::Ptr &audio) { if(_audioRtpDecoder){ //设置rtp解码器代理,生成的frame写入该Track _audioRtpDecoder->addDelegate(_audioTrack); + onAddTrack(_audioTrack); } else{ //找不到相应的rtp解码器,该track无效 _audioTrack.reset(); @@ -105,6 +106,7 @@ void RtspDemuxer::makeVideoTrack(const SdpTrack::Ptr &video) { if(_videoRtpDecoder){ //设置rtp解码器代理,生成的frame写入该Track _videoRtpDecoder->addDelegate(_videoTrack); + onAddTrack(_videoTrack); }else{ //找不到相应的rtp解码器,该track无效 _videoTrack.reset(); diff --git a/src/Rtsp/RtspToRtmpMediaSource.h b/src/Rtsp/RtspMediaSourceImp.h similarity index 53% rename from src/Rtsp/RtspToRtmpMediaSource.h rename to src/Rtsp/RtspMediaSourceImp.h index f27997f8..9ffee7ae 100644 --- a/src/Rtsp/RtspToRtmpMediaSource.h +++ b/src/Rtsp/RtspMediaSourceImp.h @@ -31,70 +31,58 @@ #include "RtspMediaSource.h" #include "RtspDemuxer.h" #include "Common/MultiMediaSourceMuxer.h" - using namespace toolkit; namespace mediakit { - -class RtspToRtmpMediaSource : public RtspMediaSource { +class RtspMediaSourceImp : public RtspMediaSource, public Demuxer::Listener , public MultiMediaSourceMuxer::Listener { public: - typedef std::shared_ptr Ptr; + typedef std::shared_ptr Ptr; - RtspToRtmpMediaSource(const string &vhost, - const string &app, - const string &id, - int ringSize = 0) - : RtspMediaSource(vhost, app, id,ringSize) { - } + /** + * 构造函数 + * @param vhost 虚拟主机 + * @param app 应用名 + * @param id 流id + * @param ringSize 环形缓存大小 + */ + RtspMediaSourceImp(const string &vhost, const string &app, const string &id, int ringSize = 0) : RtspMediaSource(vhost, app, id,ringSize) {} + ~RtspMediaSourceImp() = default; - virtual ~RtspToRtmpMediaSource() {} - - virtual void setSdp(const string &strSdp) override { + /** + * 设置sdp + */ + void setSdp(const string &strSdp) override { _demuxer = std::make_shared(strSdp); + _demuxer->setTrackListener(this); RtspMediaSource::setSdp(strSdp); } - virtual void onWrite(const RtpPacket::Ptr &rtp, bool bKeyPos) override { + /** + * 输入rtp并解析 + */ + void onWrite(const RtpPacket::Ptr &rtp, bool key_pos) override { if (_demuxer) { - bKeyPos = _demuxer->inputRtp(rtp); - if (!_muxer && _demuxer->isInited(2000)) { - _muxer = std::make_shared(getVhost(), - getApp(), - getId(), - _demuxer->getDuration(), - false,//不重复生成rtsp - _enableRtmp, - _enableHls, - _enableMP4); - for (auto &track : _demuxer->getTracks(false)) { - _muxer->addTrack(track); - track->addDelegate(_muxer); - } - _muxer->setListener(getListener()); - } + key_pos = _demuxer->inputRtp(rtp); } - RtspMediaSource::onWrite(rtp, bKeyPos); + RtspMediaSource::onWrite(rtp, key_pos); } + /** + * 设置监听器 + * @param listener + */ void setListener(const std::weak_ptr &listener) override { RtspMediaSource::setListener(listener); if(_muxer){ _muxer->setListener(listener); } } - int readerCount() override { - return RtspMediaSource::readerCount() + (_muxer ? _muxer->readerCount() : 0); - } /** - * 获取track - * @return - */ - vector getTracks(bool trackReady) const override { - if(!_demuxer){ - return this->RtspMediaSource::getTracks(trackReady); - } - return _demuxer->getTracks(trackReady); + * 播放器总数 + */ + int readerCount() override { + return RtspMediaSource::readerCount() + (_muxer ? _muxer->readerCount() : 0); } /** @@ -104,19 +92,32 @@ public: * @param enableMP4 是否mp4录制 */ void setProtocolTranslation(bool enableRtmp,bool enableHls,bool enableMP4){ -// DebugL << enableRtmp << " " << enableHls << " " << enableMP4; - _enableRtmp = enableRtmp; - _enableHls = enableHls; - _enableMP4 = enableMP4; + //不重复生成rtsp + _muxer = std::make_shared(getVhost(), getApp(), getId(), 0, false, enableRtmp, enableHls, enableMP4); + _muxer->setListener(getListener()); + _muxer->setTrackListener(this); + } + + /** + * _demuxer触发的添加Track事件 + */ + void onAddTrack(const Track::Ptr &track) override { + if(_muxer){ + _muxer->addTrack(track); + track->addDelegate(_muxer); + } + } + + /** + * _muxer触发的所有Track就绪的事件 + */ + void onAllTrackReady() override{ + setTrackSource(_muxer); } private: RtspDemuxer::Ptr _demuxer; MultiMediaSourceMuxer::Ptr _muxer; - bool _enableHls = true; - bool _enableMP4 = false; - bool _enableRtmp = true; }; - } /* namespace mediakit */ #endif /* SRC_RTSP_RTSPTORTMPMEDIASOURCE_H_ */ diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index 993ae7b3..804fea82 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -263,7 +263,7 @@ void RtspSession::handleReq_ANNOUNCE(const Parser &parser) { _strSession = makeRandStr(12); _aTrackInfo = sdpParser.getAvailableTrack(); - _pushSrc = std::make_shared(_mediaInfo._vhost,_mediaInfo._app,_mediaInfo._streamid); + _pushSrc = std::make_shared(_mediaInfo._vhost,_mediaInfo._app,_mediaInfo._streamid); _pushSrc->setListener(dynamic_pointer_cast(shared_from_this())); _pushSrc->setSdp(sdpParser.toString()); diff --git a/src/Rtsp/RtspSession.h b/src/Rtsp/RtspSession.h index 6ffa550a..9c586e76 100644 --- a/src/Rtsp/RtspSession.h +++ b/src/Rtsp/RtspSession.h @@ -40,7 +40,7 @@ #include "RtspMediaSource.h" #include "RtspSplitter.h" #include "RtpReceiver.h" -#include "RtspToRtmpMediaSource.h" +#include "RtspMediaSourceImp.h" #include "Common/Stamp.h" using namespace std; @@ -231,7 +231,7 @@ private: //是否开始发送rtp bool _enableSendRtp; //rtsp推流相关 - RtspToRtmpMediaSource::Ptr _pushSrc; + RtspMediaSourceImp::Ptr _pushSrc; //rtcp统计,trackid idx 为数组下标 RtcpCounter _aRtcpCnt[2]; //rtcp发送时间,trackid idx 为数组下标 From 9e8fe8c86a574dae50a826d21014252e9ef52846 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 26 Dec 2019 12:09:35 +0800 Subject: [PATCH 152/272] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtmp/RtmpPlayerImp.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Rtmp/RtmpPlayerImp.h b/src/Rtmp/RtmpPlayerImp.h index 426ceedd..b06be303 100644 --- a/src/Rtmp/RtmpPlayerImp.h +++ b/src/Rtmp/RtmpPlayerImp.h @@ -67,6 +67,8 @@ private: if(_pRtmpMediaSrc){ _pRtmpMediaSrc->setMetaData(val); } + _delegate.reset(new RtmpDemuxer); + _delegate->loadMetaData(val); return true; } void onMediaData(const RtmpPacket::Ptr &chunkData) override { From 453660ab798018ea058b367e850097636848b3a3 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 26 Dec 2019 12:10:54 +0800 Subject: [PATCH 153/272] =?UTF-8?q?=E6=95=B4=E7=90=86MediaSource=E6=B4=BE?= =?UTF-8?q?=E7=94=9F=E7=B1=BB=20=E4=BF=AE=E5=A4=8D=E8=BD=AC=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=E6=88=96=E5=BD=95=E5=88=B6=E6=97=B6=E5=BF=BD=E7=84=B6?= =?UTF-8?q?Track=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtmp/RtmpDemuxer.cpp | 15 +++++++++++++++ src/Rtmp/RtmpDemuxer.h | 9 ++++----- src/Rtmp/RtmpMediaSourceImp.h | 12 ++++++++++-- src/Rtsp/RtspDemuxer.cpp | 2 +- src/Rtsp/RtspDemuxer.h | 9 +++++++-- src/Rtsp/RtspMediaSourceImp.h | 15 ++++++++------- src/Rtsp/RtspPlayerImp.h | 3 ++- 7 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/Rtmp/RtmpDemuxer.cpp b/src/Rtmp/RtmpDemuxer.cpp index ac57e0c6..65f8fb91 100644 --- a/src/Rtmp/RtmpDemuxer.cpp +++ b/src/Rtmp/RtmpDemuxer.cpp @@ -29,6 +29,21 @@ namespace mediakit { +void RtmpDemuxer::loadMetaData(const AMFValue &val){ + try { + makeVideoTrack(val["videocodecid"]); + makeAudioTrack(val["audiocodecid"]); + val.object_for_each([&](const string &key, const AMFValue &val) { + if (key == "duration") { + _fDuration = val.as_number(); + return; + } + }); + }catch (std::exception &ex){ + WarnL << ex.what(); + } +} + bool RtmpDemuxer::inputRtmp(const RtmpPacket::Ptr &pkt) { switch (pkt->typeId) { case MSG_VIDEO: { diff --git a/src/Rtmp/RtmpDemuxer.h b/src/Rtmp/RtmpDemuxer.h index 243ea676..a2c900a8 100644 --- a/src/Rtmp/RtmpDemuxer.h +++ b/src/Rtmp/RtmpDemuxer.h @@ -43,11 +43,10 @@ class RtmpDemuxer : public Demuxer{ public: typedef std::shared_ptr Ptr; - /** - * 等效于RtmpDemuxer(AMFValue(AMF_NULL)) - */ - RtmpDemuxer(){} - virtual ~RtmpDemuxer(){}; + RtmpDemuxer() = default; + virtual ~RtmpDemuxer() = default; + + void loadMetaData(const AMFValue &metadata); /** * 开始解复用 diff --git a/src/Rtmp/RtmpMediaSourceImp.h b/src/Rtmp/RtmpMediaSourceImp.h index 95fba9fc..b7d0f4a7 100644 --- a/src/Rtmp/RtmpMediaSourceImp.h +++ b/src/Rtmp/RtmpMediaSourceImp.h @@ -61,11 +61,19 @@ public: ~RtmpMediaSourceImp() = default; + /** + * 设置metadata + */ + void setMetaData(const AMFValue &metadata) override{ + _demuxer->loadMetaData(metadata); + RtmpMediaSource::setMetaData(metadata); + } + /** * 输入rtmp并解析 */ void onWrite(const RtmpPacket::Ptr &pkt,bool key_pos = true) override { - _demuxer->inputRtmp(pkt); + key_pos = _demuxer->inputRtmp(pkt); RtmpMediaSource::onWrite(pkt,key_pos); } @@ -95,7 +103,7 @@ public: */ void setProtocolTranslation(bool enableRtsp, bool enableHls, bool enableMP4) { //不重复生成rtmp - _muxer = std::make_shared(getVhost(), getApp(), getId(), 0, enableRtsp, false, enableHls, enableMP4); + _muxer = std::make_shared(getVhost(), getApp(), getId(), _demuxer->getDuration(), enableRtsp, false, enableHls, enableMP4); _muxer->setListener(getListener()); _muxer->setTrackListener(this); } diff --git a/src/Rtsp/RtspDemuxer.cpp b/src/Rtsp/RtspDemuxer.cpp index 69929463..950d2e12 100644 --- a/src/Rtsp/RtspDemuxer.cpp +++ b/src/Rtsp/RtspDemuxer.cpp @@ -34,7 +34,7 @@ using namespace std; namespace mediakit { -RtspDemuxer::RtspDemuxer(const string& sdp) { +void RtspDemuxer::loadSdp(const string &sdp){ loadSdp(SdpParser(sdp)); } diff --git a/src/Rtsp/RtspDemuxer.h b/src/Rtsp/RtspDemuxer.h index 8a4d407e..c864c19a 100644 --- a/src/Rtsp/RtspDemuxer.h +++ b/src/Rtsp/RtspDemuxer.h @@ -40,8 +40,13 @@ namespace mediakit { class RtspDemuxer : public Demuxer{ public: typedef std::shared_ptr Ptr; - RtspDemuxer(const string &sdp); - virtual ~RtspDemuxer(){}; + RtspDemuxer() = default; + virtual ~RtspDemuxer() = default; + + /** + * 加载sdp + */ + void loadSdp(const string &sdp); /** * 开始解复用 diff --git a/src/Rtsp/RtspMediaSourceImp.h b/src/Rtsp/RtspMediaSourceImp.h index 9ffee7ae..9b355dd5 100644 --- a/src/Rtsp/RtspMediaSourceImp.h +++ b/src/Rtsp/RtspMediaSourceImp.h @@ -45,15 +45,18 @@ public: * @param id 流id * @param ringSize 环形缓存大小 */ - RtspMediaSourceImp(const string &vhost, const string &app, const string &id, int ringSize = 0) : RtspMediaSource(vhost, app, id,ringSize) {} + RtspMediaSourceImp(const string &vhost, const string &app, const string &id, int ringSize = 0) : RtspMediaSource(vhost, app, id,ringSize) { + _demuxer = std::make_shared(); + _demuxer->setTrackListener(this); + } + ~RtspMediaSourceImp() = default; /** * 设置sdp */ void setSdp(const string &strSdp) override { - _demuxer = std::make_shared(strSdp); - _demuxer->setTrackListener(this); + _demuxer->loadSdp(strSdp); RtspMediaSource::setSdp(strSdp); } @@ -61,9 +64,7 @@ public: * 输入rtp并解析 */ void onWrite(const RtpPacket::Ptr &rtp, bool key_pos) override { - if (_demuxer) { - key_pos = _demuxer->inputRtp(rtp); - } + key_pos = _demuxer->inputRtp(rtp); RtspMediaSource::onWrite(rtp, key_pos); } @@ -93,7 +94,7 @@ public: */ void setProtocolTranslation(bool enableRtmp,bool enableHls,bool enableMP4){ //不重复生成rtsp - _muxer = std::make_shared(getVhost(), getApp(), getId(), 0, false, enableRtmp, enableHls, enableMP4); + _muxer = std::make_shared(getVhost(), getApp(), getId(), _demuxer->getDuration(), false, enableRtmp, enableHls, enableMP4); _muxer->setListener(getListener()); _muxer->setTrackListener(this); } diff --git a/src/Rtsp/RtspPlayerImp.h b/src/Rtsp/RtspPlayerImp.h index fe6eff40..99b008ed 100644 --- a/src/Rtsp/RtspPlayerImp.h +++ b/src/Rtsp/RtspPlayerImp.h @@ -66,7 +66,8 @@ private: if(_pRtspMediaSrc){ _pRtspMediaSrc->setSdp(sdp); } - _delegate.reset(new RtspDemuxer(sdp)); + _delegate.reset(new RtspDemuxer); + _delegate->loadSdp(sdp); return true; } void onRecvRTP(const RtpPacket::Ptr &rtp, const SdpTrack::Ptr &track) override { From a30a4e24f13e847552318cc7506ec949f235c9b8 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 26 Dec 2019 12:20:34 +0800 Subject: [PATCH 154/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8Drtsp=E6=8E=A8?= =?UTF-8?q?=E6=B5=81=E8=BD=AC=E5=8D=8F=E8=AE=AE=E5=A4=B1=E8=B4=A5=E7=9A=84?= =?UTF-8?q?bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtmp/RtmpMediaSourceImp.h | 4 ++++ src/Rtsp/RtspMediaSourceImp.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/Rtmp/RtmpMediaSourceImp.h b/src/Rtmp/RtmpMediaSourceImp.h index b7d0f4a7..d6616ed4 100644 --- a/src/Rtmp/RtmpMediaSourceImp.h +++ b/src/Rtmp/RtmpMediaSourceImp.h @@ -106,6 +106,10 @@ public: _muxer = std::make_shared(getVhost(), getApp(), getId(), _demuxer->getDuration(), enableRtsp, false, enableHls, enableMP4); _muxer->setListener(getListener()); _muxer->setTrackListener(this); + for(auto &track : _demuxer->getTracks(false)){ + _muxer->addTrack(track); + track->addDelegate(_muxer); + } } /** diff --git a/src/Rtsp/RtspMediaSourceImp.h b/src/Rtsp/RtspMediaSourceImp.h index 9b355dd5..4f157f66 100644 --- a/src/Rtsp/RtspMediaSourceImp.h +++ b/src/Rtsp/RtspMediaSourceImp.h @@ -97,6 +97,10 @@ public: _muxer = std::make_shared(getVhost(), getApp(), getId(), _demuxer->getDuration(), false, enableRtmp, enableHls, enableMP4); _muxer->setListener(getListener()); _muxer->setTrackListener(this); + for(auto &track : _demuxer->getTracks(false)){ + _muxer->addTrack(track); + track->addDelegate(_muxer); + } } /** From f7db9d3690408c9f984debf69166c3ddca10f727 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 26 Dec 2019 12:23:38 +0800 Subject: [PATCH 155/272] =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=85=B3=E9=97=AD?= =?UTF-8?q?=E8=99=9A=E5=81=87=E4=B8=BB=E6=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common/config.cpp b/src/Common/config.cpp index 5cc44dd9..96cb54b1 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -89,7 +89,7 @@ onceToken token([](){ mINI::Instance()[kFlowThreshold] = 1024; mINI::Instance()[kStreamNoneReaderDelayMS] = 5 * 1000; mINI::Instance()[kMaxStreamWaitTimeMS] = 5 * 1000; - mINI::Instance()[kEnableVhost] = 1; + mINI::Instance()[kEnableVhost] = 0; mINI::Instance()[kUltraLowDelay] = 1; mINI::Instance()[kAddMuteAudio] = 1; mINI::Instance()[kResetWhenRePlay] = 1; From 003021e24b3d5c27698e303850735848a33bb9a2 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 26 Dec 2019 15:02:28 +0800 Subject: [PATCH 156/272] =?UTF-8?q?=E4=BC=98=E5=8C=96http=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=99=A8=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/HttpSession.cpp | 52 +++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp index d02ac142..85ee6b9b 100644 --- a/src/Http/HttpSession.cpp +++ b/src/Http/HttpSession.cpp @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> @@ -308,6 +308,16 @@ static string dateStr() { return buf; } +static const string kDate = "Date"; +static const string kServer = "Server"; +static const string kConnection = "Connection"; +static const string kKeepAlive = "Keep-Alive"; +static const string kContentType = "Content-Type"; +static const string kContentLength = "Content-Length"; +static const string kAccessControlAllowOrigin = "Access-Control-Allow-Origin"; +static const string kAccessControlAllowCredentials = "Access-Control-Allow-Credentials"; +static const string kServerName = SERVER_NAME; + void HttpSession::sendResponse(const char *pcStatus, bool bClose, const char *pcContentType, @@ -333,22 +343,25 @@ void HttpSession::sendResponse(const char *pcStatus, } HttpSession::KeyValue &headerOut = const_cast(header); - headerOut.emplace("Date", dateStr()); - headerOut.emplace("Server", SERVER_NAME); - headerOut.emplace("Connection", bClose ? "close" : "keep-alive"); + headerOut.emplace(kDate, dateStr()); + headerOut.emplace(kServer, kServerName); + headerOut.emplace(kConnection, bClose ? "close" : "keep-alive"); if(!bClose){ - headerOut.emplace("Keep-Alive",StrPrinter << "timeout=" << keepAliveSec << ", max=100" << endl); + string keepAliveString = "timeout="; + keepAliveString += to_string(keepAliveSec); + keepAliveString += ", max=100"; + headerOut.emplace(kKeepAlive,std::move(keepAliveString)); } if(!_origin.empty()){ //设置跨域 - headerOut.emplace("Access-Control-Allow-Origin",_origin); - headerOut.emplace("Access-Control-Allow-Credentials", "true"); + headerOut.emplace(kAccessControlAllowOrigin,_origin); + headerOut.emplace(kAccessControlAllowCredentials, "true"); } if(!is_http_flv && size >= 0 && size < INT64_MAX){ //文件长度为固定值,且不是http-flv强制设置Content-Length - headerOut["Content-Length"] = StrPrinter << size << endl; + headerOut[kContentLength] = to_string(size); } if(size && !pcContentType){ @@ -358,19 +371,26 @@ void HttpSession::sendResponse(const char *pcStatus, if(size && pcContentType){ //有body时,设置文件类型 - auto strContentType = StrPrinter << pcContentType << "; charset=" << charSet << endl; - headerOut.emplace("Content-Type",strContentType); + string strContentType = pcContentType; + strContentType += "; charset="; + strContentType += charSet; + headerOut.emplace(kContentType,std::move(strContentType)); } //发送http头 - _StrPrinter printer; - printer << "HTTP/1.1 " << pcStatus << "\r\n"; + string str; + str.reserve(256); + str += "HTTP/1.1 " ; + str += pcStatus ; + str += "\r\n"; for (auto &pr : header) { - printer << pr.first << ": " << pr.second << "\r\n"; + str += pr.first ; + str += ": "; + str += pr.second; + str += "\r\n"; } - - printer << "\r\n"; - send(printer << endl); + str += "\r\n"; + send(std::move(str)); _ticker.resetTime(); if(!size){ From 56397628da9449d02d9d902a5579851cdc5a9335 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 26 Dec 2019 19:36:57 +0800 Subject: [PATCH 157/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=9C=AA=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96=E5=AF=BC=E8=87=B4=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/MultiMediaSourceMuxer.h | 2 +- src/Player/PlayerBase.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Common/MultiMediaSourceMuxer.h b/src/Common/MultiMediaSourceMuxer.h index d019b107..10f63eae 100644 --- a/src/Common/MultiMediaSourceMuxer.h +++ b/src/Common/MultiMediaSourceMuxer.h @@ -156,7 +156,7 @@ protected: private: RtmpMediaSourceMuxer::Ptr _rtmp; RtspMediaSourceMuxer::Ptr _rtsp; - Listener *_listener; + Listener *_listener = nullptr; }; diff --git a/src/Player/PlayerBase.h b/src/Player/PlayerBase.h index 0dd74bf8..8fb397b3 100644 --- a/src/Player/PlayerBase.h +++ b/src/Player/PlayerBase.h @@ -282,7 +282,7 @@ public: protected: void onAddTrack(const Track::Ptr &track); protected: - Listener *_listener; + Listener *_listener = nullptr; AudioTrack::Ptr _audioTrack; VideoTrack::Ptr _videoTrack; Ticker _ticker; From 11a7d1e6c4a7532ec5fecfa13c9c702708d83dea Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 26 Dec 2019 21:22:19 +0800 Subject: [PATCH 158/272] =?UTF-8?q?=E5=AE=8C=E6=88=90tcp=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8/=E5=AE=A2=E6=88=B7=E7=AB=AF=20c=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/events_objects.h | 19 +-- api/include/mediakit.h | 2 +- api/include/tcp.h | 226 +++++++++++++++++++++++++ api/include/websocket.h | 105 ------------ api/source/common.cpp | 4 +- api/source/events_objects.cpp | 54 ------ api/source/tcp.cpp | 299 ++++++++++++++++++++++++++++++++++ api/source/websocket.cpp | 110 ------------- api/tests/websocket.c | 156 ++++++++++++++---- src/Http/WebSocketClient.h | 12 +- 10 files changed, 658 insertions(+), 329 deletions(-) create mode 100644 api/include/tcp.h delete mode 100644 api/include/websocket.h create mode 100644 api/source/tcp.cpp delete mode 100644 api/source/websocket.cpp diff --git a/api/include/events_objects.h b/api/include/events_objects.h index e24a1603..5881f0f7 100644 --- a/api/include/events_objects.h +++ b/api/include/events_objects.h @@ -27,6 +27,7 @@ #ifndef MK_EVENT_OBJECTS_H #define MK_EVENT_OBJECTS_H #include "common.h" +#include "tcp.h" #ifdef __cplusplus extern "C" { #endif @@ -123,24 +124,6 @@ API_EXPORT void API_CALL mk_media_source_find(const char *schema, //MediaSource::for_each_media() API_EXPORT void API_CALL mk_media_source_for_each(void *user_data, on_mk_media_source_find_cb cb); -///////////////////////////////////////////TcpSession///////////////////////////////////////////// -//TcpSession对象的C映射 -typedef void* mk_tcp_session; -//TcpSession::safeShutdown() -API_EXPORT void API_CALL mk_tcp_session_shutdown(const mk_tcp_session ctx,int err,const char *err_msg); -//TcpSession::get_peer_ip() -API_EXPORT const char* API_CALL mk_tcp_session_peer_ip(const mk_tcp_session ctx); -//TcpSession::get_local_ip() -API_EXPORT const char* API_CALL mk_tcp_session_local_ip(const mk_tcp_session ctx); -//TcpSession::get_peer_port() -API_EXPORT uint16_t API_CALL mk_tcp_session_peer_port(const mk_tcp_session ctx); -//TcpSession::get_local_port() -API_EXPORT uint16_t API_CALL mk_tcp_session_local_port(const mk_tcp_session ctx); -//TcpSession::send() -API_EXPORT void API_CALL mk_tcp_session_send(const mk_tcp_session ctx,const char *data,int len); -//切换到该对象所在线程后再TcpSession::send() -API_EXPORT void API_CALL mk_tcp_session_send_safe(const mk_tcp_session ctx,const char *data,int len); - ///////////////////////////////////////////HttpBody///////////////////////////////////////////// //HttpBody对象的C映射 typedef void* mk_http_body; diff --git a/api/include/mediakit.h b/api/include/mediakit.h index de08c278..6feb9eaf 100755 --- a/api/include/mediakit.h +++ b/api/include/mediakit.h @@ -35,6 +35,6 @@ #include "player.h" #include "pusher.h" #include "events.h" -#include "websocket.h" +#include "tcp.h" #endif /* MK_API_H_ */ diff --git a/api/include/tcp.h b/api/include/tcp.h new file mode 100644 index 00000000..b0511e86 --- /dev/null +++ b/api/include/tcp.h @@ -0,0 +1,226 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MK_TCP_H +#define MK_TCP_H + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +///////////////////////////////////////////TcpSession///////////////////////////////////////////// +//TcpSession对象的C映射 +typedef void* mk_tcp_session; +//TcpSession::safeShutdown() +API_EXPORT void API_CALL mk_tcp_session_shutdown(const mk_tcp_session ctx,int err,const char *err_msg); +//TcpSession::get_peer_ip() +API_EXPORT const char* API_CALL mk_tcp_session_peer_ip(const mk_tcp_session ctx); +//TcpSession::get_local_ip() +API_EXPORT const char* API_CALL mk_tcp_session_local_ip(const mk_tcp_session ctx); +//TcpSession::get_peer_port() +API_EXPORT uint16_t API_CALL mk_tcp_session_peer_port(const mk_tcp_session ctx); +//TcpSession::get_local_port() +API_EXPORT uint16_t API_CALL mk_tcp_session_local_port(const mk_tcp_session ctx); +//TcpSession::send() +API_EXPORT void API_CALL mk_tcp_session_send(const mk_tcp_session ctx,const char *data,int len); +//切换到该对象所在线程后再TcpSession::send() +API_EXPORT void API_CALL mk_tcp_session_send_safe(const mk_tcp_session ctx,const char *data,int len); + +///////////////////////////////////////////自定义tcp服务///////////////////////////////////////////// + +typedef struct { + /** + * 收到mk_tcp_session创建对象 + * @param server_port 服务器端口号 + * @param session 会话处理对象 + */ + void (API_CALL *on_mk_tcp_session_create)(uint16_t server_port,mk_tcp_session session); + + /** + * 收到客户端发过来的数据 + * @param server_port 服务器端口号 + * @param session 会话处理对象 + * @param data 数据指针 + * @param len 数据长度 + */ + void (API_CALL *on_mk_tcp_session_data)(uint16_t server_port,mk_tcp_session session,const char *data,int len); + + /** + * 每隔2秒的定时器,用于管理超时等任务 + * @param server_port 服务器端口号 + * @param session 会话处理对象 + */ + void (API_CALL *on_mk_tcp_session_manager)(uint16_t server_port,mk_tcp_session session); + + /** + * 一般由于客户端断开tcp触发 + * @param server_port 服务器端口号 + * @param session 会话处理对象 + * @param code 错误代码 + * @param msg 错误提示 + */ + void (API_CALL *on_mk_tcp_session_disconnect)(uint16_t server_port,mk_tcp_session session,int code,const char *msg); +} mk_tcp_session_events; + + +typedef enum { + //普通的tcp + mk_type_tcp = 0, + //ssl类型的tcp + mk_type_ssl = 1, + //基于websocket的连接 + mk_type_ws = 2, + //基于ssl websocket的连接 + mk_type_wss = 3 +}mk_tcp_type; + +/** + * tcp会话对象附着用户数据 + * 该函数只对mk_tcp_server_server_start启动的服务类型有效 + * @param session 会话对象 + * @param user_data 用户数据指针 + */ +API_EXPORT void API_CALL mk_tcp_session_set_user_data(mk_tcp_session session,void *user_data); + +/** + * 获取tcp会话对象上附着的用户数据 + * 该函数只对mk_tcp_server_server_start启动的服务类型有效 + * @param session tcp会话对象 + * @return 用户数据指针 + */ +API_EXPORT void* API_CALL mk_tcp_session_get_user_data(mk_tcp_session session); + +/** + * 开启tcp服务器 + * @param port 监听端口号,0则为随机 + * @param type 服务器类型 + */ +API_EXPORT uint16_t API_CALL mk_tcp_server_server_start(uint16_t port, mk_tcp_type type); + +/** + * 监听tcp服务器事件 + */ +API_EXPORT void API_CALL mk_tcp_server_events_listen(const mk_tcp_session_events *events); + + +///////////////////////////////////////////自定义tcp客户端///////////////////////////////////////////// + +typedef void* mk_tcp_client; + +typedef struct { + /** + * tcp客户端连接服务器成功或失败回调 + * @param client tcp客户端 + * @param code 0为连接成功,否则为失败原因 + * @param msg 连接失败错误提示 + */ + void (API_CALL *on_mk_tcp_client_connect)(mk_tcp_client client,int code,const char *msg); + + /** + * tcp客户端与tcp服务器之间断开回调 + * 一般是eof事件导致 + * @param client tcp客户端 + * @param code 错误代码 + * @param msg 错误提示 + */ + void (API_CALL *on_mk_tcp_client_disconnect)(mk_tcp_client client,int code,const char *msg); + + /** + * 收到tcp服务器发来的数据 + * @param client tcp客户端 + * @param data 数据指针 + * @param len 数据长度 + */ + void (API_CALL *on_mk_tcp_client_data)(mk_tcp_client client,const char *data,int len); + + /** + * 每隔2秒的定时器,用于管理超时等任务 + * @param client tcp客户端 + */ + void (API_CALL *on_mk_tcp_client_manager)(mk_tcp_client client); +} mk_tcp_client_events; + +/** + * 创建tcp客户端 + * @param events 回调函数结构体 + * @param user_data 用户数据指针 + * @param type 客户端类型 + * @return 客户端对象 + */ +API_EXPORT mk_tcp_client API_CALL mk_tcp_client_create(mk_tcp_client_events *events, mk_tcp_type type); + +/** + * 释放tcp客户端 + * @param ctx 客户端对象 + */ +API_EXPORT void API_CALL mk_tcp_client_release(mk_tcp_client ctx); + +/** + * 发起连接 + * @param ctx 客户端对象 + * @param host 服务器ip或域名 + * @param port 服务器端口号 + * @param time_out_sec 超时时间 + */ +API_EXPORT void API_CALL mk_tcp_client_connect(mk_tcp_client ctx, const char *host, uint16_t port, float time_out_sec); + +/** + * 非线程安全的发送数据 + * 开发者如果能确保在本对象网络线程内,可以调用此此函数 + * @param ctx 客户端对象 + * @param data 数据指针 + * @param len 数据长度,等于0时,内部通过strlen获取 + */ +API_EXPORT void API_CALL mk_tcp_client_send(mk_tcp_client ctx, const char *data, int len); + +/** + * 切换到本对象的网络线程后再发送数据 + * @param ctx 客户端对象 + * @param data 数据指针 + * @param len 数据长度,等于0时,内部通过strlen获取 + */ +API_EXPORT void API_CALL mk_tcp_client_send_safe(mk_tcp_client ctx, const char *data, int len); + +/** + * 客户端附着用户数据 + * @param ctx 客户端对象 + * @param user_data 用户数据指针 + */ +API_EXPORT void API_CALL mk_tcp_client_set_user_data(mk_tcp_client ctx,void *user_data); + +/** + * 获取客户端对象上附着的用户数据 + * @param ctx 客户端对象 + * @return 用户数据指针 + */ +API_EXPORT void* API_CALL mk_tcp_client_get_user_data(mk_tcp_client ctx); + +#ifdef __cplusplus +} +#endif +#endif //MK_TCP_H diff --git a/api/include/websocket.h b/api/include/websocket.h deleted file mode 100644 index c075fd5c..00000000 --- a/api/include/websocket.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 xiongziliang <771730766@qq.com> - * - * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef MK_WEBSOCKET_H -#define MK_WEBSOCKET_H - -#include "common.h" -#include "events_objects.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - /** - * 当websocket客户端连接服务器时触发 - * @param session 会话处理对象 - */ - void (API_CALL *on_mk_websocket_session_create)(mk_tcp_session session); - - /** - * session会话对象销毁时触发 - * 请在本回调中清理释放你的用户数据 - * 本事件中不能调用mk_tcp_session_send/mk_tcp_session_send_safe函数 - * @param session 会话处理对象 - */ - void (API_CALL *on_mk_websocket_session_destory)(mk_tcp_session session); - - /** - * 收到websocket客户端发过来的数据 - * @param session 会话处理对象 - * @param data 数据指针 - * @param len 数据长度 - */ - void (API_CALL *on_mk_websocket_session_data)(mk_tcp_session session,const char *data,int len); - - /** - * 每隔2秒的定时器,用于管理超时等任务 - * @param session 会话处理对象 - */ - void (API_CALL *on_mk_websocket_session_manager)(mk_tcp_session session); - - /** - * on_mk_websocket_session_destory之前触发on_mk_websocket_session_err - * 一般由于客户端断开tcp触发 - * 本事件中可以调用mk_tcp_session_send_safe函数 - * @param session 会话处理对象 - * @param code 错误代码 - * @param msg 错误提示 - */ - void (API_CALL *on_mk_websocket_session_err)(mk_tcp_session session,int code,const char *msg); -} mk_websocket_events; - -API_EXPORT void API_CALL mk_websocket_events_listen(const mk_websocket_events *events); - -/** - * 往websocket会话对象附着用户数据 - * @param session websocket会话对象 - * @param user_data 用户数据指针 - */ -API_EXPORT void API_CALL mk_websocket_session_set_user_data(mk_tcp_session session,void *user_data); - -/** - * 获取websocket会话对象上附着的用户数据 - * @param session websocket会话对象 - * @return 用户数据指针 - */ -API_EXPORT void* API_CALL mk_websocket_session_get_user_data(mk_tcp_session session); - -/** - * 开启websocket服务器,需要指出的是,websocket服务器包含了Http服务器的所有功能 - * 调用mk_websocket_server_start后不用再调用mk_http_server_start - * @param port 端口号,0则随机 - * @param ssl 是否为wss/ws - * @return 端口号,0代表失败 - */ -API_EXPORT uint16_t API_CALL mk_websocket_server_start(uint16_t port, int ssl); - -#ifdef __cplusplus -} -#endif -#endif //MK_WEBSOCKET_H diff --git a/api/source/common.cpp b/api/source/common.cpp index ca8074c8..b96414e4 100755 --- a/api/source/common.cpp +++ b/api/source/common.cpp @@ -64,7 +64,7 @@ API_EXPORT void API_CALL mk_env_init(const mk_config *cfg) { cfg->ssl_pwd); } -extern void mk_websocket_server_stop(); +extern void stopAllTcpServer(); API_EXPORT void API_CALL mk_stop_all_server(){ CLEAR_ARR(rtsp_server); @@ -72,7 +72,7 @@ API_EXPORT void API_CALL mk_stop_all_server(){ CLEAR_ARR(http_server); udpRtpServer = nullptr; tcpRtpServer = nullptr; - mk_websocket_server_stop(); + stopAllTcpServer(); } API_EXPORT void API_CALL mk_env_init1( int thread_num, diff --git a/api/source/events_objects.cpp b/api/source/events_objects.cpp index 41a8a982..435031f0 100644 --- a/api/source/events_objects.cpp +++ b/api/source/events_objects.cpp @@ -228,60 +228,6 @@ API_EXPORT void API_CALL mk_media_source_for_each(void *user_data, on_mk_media_s }); } -///////////////////////////////////////////TcpSession///////////////////////////////////////////// -API_EXPORT void API_CALL mk_tcp_session_shutdown(const mk_tcp_session ctx,int err,const char *err_msg){ - assert(ctx); - TcpSession *session = (TcpSession *)ctx; - session->safeShutdown(SockException((ErrCode)err,err_msg)); -} -API_EXPORT const char* API_CALL mk_tcp_session_peer_ip(const mk_tcp_session ctx){ - assert(ctx); - TcpSession *session = (TcpSession *)ctx; - return session->get_peer_ip().c_str(); -} -API_EXPORT const char* API_CALL mk_tcp_session_local_ip(const mk_tcp_session ctx){ - assert(ctx); - TcpSession *session = (TcpSession *)ctx; - return session->get_local_ip().c_str(); -} -API_EXPORT uint16_t API_CALL mk_tcp_session_peer_port(const mk_tcp_session ctx){ - assert(ctx); - TcpSession *session = (TcpSession *)ctx; - return session->get_peer_port(); -} -API_EXPORT uint16_t API_CALL mk_tcp_session_local_port(const mk_tcp_session ctx){ - assert(ctx); - TcpSession *session = (TcpSession *)ctx; - return session->get_local_port(); -} -API_EXPORT void API_CALL mk_tcp_session_send(const mk_tcp_session ctx,const char *data,int len){ - assert(ctx && data); - if(!len){ - len = strlen(data); - } - TcpSession *session = (TcpSession *)ctx; - session->send(data,len); -} - -API_EXPORT void API_CALL mk_tcp_session_send_safe(const mk_tcp_session ctx,const char *data,int len){ - assert(ctx && data); - if(!len){ - len = strlen(data); - } - try { - weak_ptr weak_session = ((TcpSession *)ctx)->shared_from_this(); - string str = string(data,len); - ((TcpSession *)ctx)->async([weak_session,str](){ - auto session_session = weak_session.lock(); - if(session_session){ - session_session->send(str); - } - }); - }catch (std::exception &ex){ - WarnL << "can not got the strong pionter of this mk_tcp_session:" << ex.what(); - } -} - ///////////////////////////////////////////HttpBody///////////////////////////////////////////// API_EXPORT mk_http_body API_CALL mk_http_body_from_string(const char *str,int len){ assert(str); diff --git a/api/source/tcp.cpp b/api/source/tcp.cpp new file mode 100644 index 00000000..61dd22e4 --- /dev/null +++ b/api/source/tcp.cpp @@ -0,0 +1,299 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "tcp.h" +#include "Network/TcpSession.h" +#include "Network/TcpClient.h" +#include "Http/WebSocketClient.h" +#include "Http/WebSocketSession.h" +using namespace mediakit; + +//////////////////////////////////////////////////////////////////////////////////////// +API_EXPORT void API_CALL mk_tcp_session_shutdown(const mk_tcp_session ctx,int err,const char *err_msg){ + assert(ctx); + TcpSession *session = (TcpSession *)ctx; + session->safeShutdown(SockException((ErrCode)err,err_msg)); +} +API_EXPORT const char* API_CALL mk_tcp_session_peer_ip(const mk_tcp_session ctx){ + assert(ctx); + TcpSession *session = (TcpSession *)ctx; + return session->get_peer_ip().c_str(); +} +API_EXPORT const char* API_CALL mk_tcp_session_local_ip(const mk_tcp_session ctx){ + assert(ctx); + TcpSession *session = (TcpSession *)ctx; + return session->get_local_ip().c_str(); +} +API_EXPORT uint16_t API_CALL mk_tcp_session_peer_port(const mk_tcp_session ctx){ + assert(ctx); + TcpSession *session = (TcpSession *)ctx; + return session->get_peer_port(); +} +API_EXPORT uint16_t API_CALL mk_tcp_session_local_port(const mk_tcp_session ctx){ + assert(ctx); + TcpSession *session = (TcpSession *)ctx; + return session->get_local_port(); +} +API_EXPORT void API_CALL mk_tcp_session_send(const mk_tcp_session ctx,const char *data,int len){ + assert(ctx && data); + if(!len){ + len = strlen(data); + } + TcpSession *session = (TcpSession *)ctx; + session->send(data,len); +} + +API_EXPORT void API_CALL mk_tcp_session_send_safe(const mk_tcp_session ctx,const char *data,int len){ + assert(ctx && data); + if(!len){ + len = strlen(data); + } + try { + weak_ptr weak_session = ((TcpSession *)ctx)->shared_from_this(); + string str = string(data,len); + ((TcpSession *)ctx)->async([weak_session,str](){ + auto session_session = weak_session.lock(); + if(session_session){ + session_session->send(str); + } + }); + }catch (std::exception &ex){ + WarnL << "can not got the strong pionter of this mk_tcp_session:" << ex.what(); + } +} + +////////////////////////////////////////TcpSessionForC//////////////////////////////////////////////// +static TcpServer::Ptr s_tcp_server[4]; +static mk_tcp_session_events s_events_server = {0}; + +class TcpSessionForC : public TcpSession { +public: + TcpSessionForC(const Socket::Ptr &pSock) ; + ~TcpSessionForC() override = default; + void onRecv(const Buffer::Ptr &buffer) override ; + void onError(const SockException &err) override; + void onManager() override; + void *_user_data; + uint16_t _local_port; +}; + +TcpSessionForC::TcpSessionForC(const Socket::Ptr &pSock) : TcpSession(pSock) { + _local_port = get_local_port(); + if (s_events_server.on_mk_tcp_session_create) { + s_events_server.on_mk_tcp_session_create(_local_port,this); + } +} + +void TcpSessionForC::onRecv(const Buffer::Ptr &buffer) { + if (s_events_server.on_mk_tcp_session_data) { + s_events_server.on_mk_tcp_session_data(_local_port,this, buffer->data(), buffer->size()); + } +} + +void TcpSessionForC::onError(const SockException &err) { + if (s_events_server.on_mk_tcp_session_disconnect) { + s_events_server.on_mk_tcp_session_disconnect(_local_port,this, err.getErrCode(), err.what()); + } +} + +void TcpSessionForC::onManager() { + if (s_events_server.on_mk_tcp_session_manager) { + s_events_server.on_mk_tcp_session_manager(_local_port,this); + } +} + +void stopAllTcpServer(){ + CLEAR_ARR(s_tcp_server); +} + +API_EXPORT void API_CALL mk_tcp_session_set_user_data(mk_tcp_session session,void *user_data){ + assert(session); + TcpSessionForC *obj = (TcpSessionForC *)session; + obj->_user_data = user_data; +} + +API_EXPORT void* API_CALL mk_tcp_session_get_user_data(mk_tcp_session session){ + assert(session); + TcpSessionForC *obj = (TcpSessionForC *)session; + return obj->_user_data; +} + +API_EXPORT void API_CALL mk_tcp_server_events_listen(const mk_tcp_session_events *events){ + if (events) { + memcpy(&s_events_server, events, sizeof(s_events_server)); + } else { + memset(&s_events_server, 0, sizeof(s_events_server)); + } +} + +API_EXPORT uint16_t API_CALL mk_tcp_server_server_start(uint16_t port, mk_tcp_type type){ + type = MAX(mk_type_tcp, MIN(type, mk_type_wss)); + try { + s_tcp_server[type] = std::make_shared(); + switch (type) { + case mk_type_tcp: + s_tcp_server[type]->start(port); + break; + case mk_type_ssl: + s_tcp_server[type]->start >(port); + break; + case mk_type_ws: + s_tcp_server[type]->start>(port); + break; + case mk_type_wss: + s_tcp_server[type]->start>(port); + break; + default: + return 0; + } + return s_tcp_server[type]->getPort(); + } catch (std::exception &ex) { + s_tcp_server[type].reset(); + WarnL << ex.what(); + return 0; + } +} + +///////////////////////////////////////////////////TcpClientForC///////////////////////////////////////////////////////// +class TcpClientForC : public TcpClient { +public: + typedef std::shared_ptr Ptr; + TcpClientForC(mk_tcp_client_events *events) ; + ~TcpClientForC() override ; + void onRecv(const Buffer::Ptr &pBuf) override; + void onErr(const SockException &ex) override; + void onManager() override; + void onConnect(const SockException &ex) override; + void setClient(mk_tcp_client client); + void *_user_data; +private: + mk_tcp_client_events _events; + mk_tcp_client _client; +}; + +TcpClientForC::TcpClientForC(mk_tcp_client_events *events){ + _events = *events; +} + + +void TcpClientForC::onRecv(const Buffer::Ptr &pBuf) { + if(_events.on_mk_tcp_client_data){ + _events.on_mk_tcp_client_data(_client,pBuf->data(),pBuf->size()); + } +} + +void TcpClientForC::onErr(const SockException &ex) { + if(_events.on_mk_tcp_client_disconnect){ + _events.on_mk_tcp_client_disconnect(_client,ex.getErrCode(),ex.what()); + } +} + +void TcpClientForC::onManager() { + if(_events.on_mk_tcp_client_manager){ + _events.on_mk_tcp_client_manager(_client); + } +} + +void TcpClientForC::onConnect(const SockException &ex) { + if(_events.on_mk_tcp_client_connect){ + _events.on_mk_tcp_client_connect(_client,ex.getErrCode(),ex.what()); + } +} + +TcpClientForC::~TcpClientForC() { + TraceL << "mk_tcp_client_release:" << _client; +} + +void TcpClientForC::setClient(mk_tcp_client client) { + _client = client; + TraceL << "mk_tcp_client_create:" << _client; +} + +TcpClientForC::Ptr *mk_tcp_client_create_l(mk_tcp_client_events *events, mk_tcp_type type){ + assert(events); + type = MAX(mk_type_tcp, MIN(type, mk_type_wss)); + switch (type) { + case mk_type_tcp: + return new TcpClientForC::Ptr(new TcpClientForC(events)); + case mk_type_ssl: + return (TcpClientForC::Ptr *)new shared_ptr >(new TcpSessionWithSSL(events)); + case mk_type_ws: + return (TcpClientForC::Ptr *)new shared_ptr >(new WebSocketClient(events)); + case mk_type_wss: + return (TcpClientForC::Ptr *)new shared_ptr >(new WebSocketClient(events)); + default: + return nullptr; + } +} + +API_EXPORT mk_tcp_client API_CALL mk_tcp_client_create(mk_tcp_client_events *events, mk_tcp_type type){ + auto ret = mk_tcp_client_create_l(events,type); + (*ret)->setClient(ret); + return ret; +} + +API_EXPORT void API_CALL mk_tcp_client_release(mk_tcp_client ctx){ + assert(ctx); + TcpClient::Ptr *client = (TcpClient::Ptr *)ctx; + delete client; +} + +API_EXPORT void API_CALL mk_tcp_client_connect(mk_tcp_client ctx, const char *host, uint16_t port, float time_out_sec){ + assert(ctx); + TcpClient::Ptr *client = (TcpClient::Ptr *)ctx; + (*client)->startConnect(host,port); +} + +API_EXPORT void API_CALL mk_tcp_client_send(mk_tcp_client ctx, const char *data, int len){ + assert(ctx && data); + TcpClient::Ptr *client = (TcpClient::Ptr *)ctx; + (*client)->send(data,len); +} + +API_EXPORT void API_CALL mk_tcp_client_send_safe(mk_tcp_client ctx, const char *data, int len){ + assert(ctx && data); + TcpClient::Ptr *client = (TcpClient::Ptr *)ctx; + weak_ptr weakClient = *client; + Buffer::Ptr buf = (*client)->obtainBuffer(data,len); + (*client)->async([weakClient,buf](){ + auto strongClient = weakClient.lock(); + if(strongClient){ + strongClient->send(buf); + } + }); +} + +API_EXPORT void API_CALL mk_tcp_client_set_user_data(mk_tcp_client ctx,void *user_data){ + assert(ctx); + TcpClientForC::Ptr *client = (TcpClientForC::Ptr *)ctx; + (*client)->_user_data = user_data; +} + +API_EXPORT void* API_CALL mk_tcp_client_get_user_data(mk_tcp_client ctx){ + assert(ctx); + TcpClientForC::Ptr *client = (TcpClientForC::Ptr *)ctx; + return (*client)->_user_data; +} diff --git a/api/source/websocket.cpp b/api/source/websocket.cpp deleted file mode 100644 index 034a5a24..00000000 --- a/api/source/websocket.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2019 xiongziliang <771730766@qq.com> - * - * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "websocket.h" -#include "Http/HttpSession.h" -#include "Http/WebSocketSession.h" -using namespace mediakit; - -static TcpServer::Ptr websocket_server[2]; -static mk_websocket_events s_events = {0}; - -class WebSocketSessionImp : public TcpSession { -public: - WebSocketSessionImp(const Socket::Ptr &pSock) : TcpSession(pSock){ - if(s_events.on_mk_websocket_session_create){ - s_events.on_mk_websocket_session_create(this); - } - } - - virtual ~WebSocketSessionImp(){ - if(s_events.on_mk_websocket_session_destory){ - s_events.on_mk_websocket_session_destory(this); - } - } - - void onRecv(const Buffer::Ptr &buffer) override { - if(s_events.on_mk_websocket_session_data){ - s_events.on_mk_websocket_session_data(this,buffer->data(),buffer->size()); - } - } - - void onError(const SockException &err) override{ - if(s_events.on_mk_websocket_session_err){ - s_events.on_mk_websocket_session_err(this,err.getErrCode(),err.what()); - } - } - - void onManager() override{ - if(s_events.on_mk_websocket_session_manager){ - s_events.on_mk_websocket_session_manager(this); - } - } - - void *_user_data; -}; - -API_EXPORT void API_CALL mk_websocket_events_listen(const mk_websocket_events *events){ - if(events){ - memcpy(&s_events,events, sizeof(s_events)); - }else{ - memset(&s_events,0,sizeof(s_events)); - } -} - -API_EXPORT void API_CALL mk_websocket_session_set_user_data(mk_tcp_session session,void *user_data){ - assert(session); - WebSocketSessionImp *obj = (WebSocketSessionImp *)session; - obj->_user_data = user_data; -} - -API_EXPORT void* API_CALL mk_websocket_session_get_user_data(mk_tcp_session session){ - assert(session); - WebSocketSessionImp *obj = (WebSocketSessionImp *)session; - return obj->_user_data; -} - -API_EXPORT uint16_t API_CALL mk_websocket_server_start(uint16_t port, int ssl){ - ssl = MAX(0,MIN(ssl,1)); - try { - websocket_server[ssl] = std::make_shared(); - if(ssl){ - websocket_server[ssl]->start>(port); - }else{ - websocket_server[ssl]->start>(port); - } - return websocket_server[ssl]->getPort(); - } catch (std::exception &ex) { - websocket_server[ssl].reset(); - WarnL << ex.what(); - return 0; - } -} - -void mk_websocket_server_stop(){ - CLEAR_ARR(websocket_server); -} - diff --git a/api/tests/websocket.c b/api/tests/websocket.c index f9eaf7b1..77411d6a 100644 --- a/api/tests/websocket.c +++ b/api/tests/websocket.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "mediakit.h" #ifdef _WIN32 #include "windows.h" @@ -35,42 +36,37 @@ #endif #define LOG_LEV 4 +#define TCP_TYPE mk_type_wss +static int flag = 1; +static void s_on_exit(int sig){ + flag = 0; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// typedef struct { mk_tcp_session _session; //下面你可以夹杂你的私货数据 char your_some_useful_data[1024]; -} websocket_user_data; +} tcp_session_user_data; /** - * 当websocket客户端连接服务器时触发 + * 当tcp客户端连接服务器时触发 * @param session 会话处理对象 */ -void API_CALL on_mk_websocket_session_create(mk_tcp_session session){ +void API_CALL on_mk_tcp_session_create(uint16_t server_port,mk_tcp_session session){ log_printf(LOG_LEV,"%s %d",mk_tcp_session_peer_ip(session),(int)mk_tcp_session_peer_port(session)); - websocket_user_data *user_data = malloc(sizeof(websocket_user_data)); + tcp_session_user_data *user_data = malloc(sizeof(tcp_session_user_data)); user_data->_session = session; - mk_websocket_session_set_user_data(session,user_data); + mk_tcp_session_set_user_data(session,user_data); } /** - * session会话对象销毁时触发 - * 请在本回调中清理释放你的用户数据 - * 本事件中不能调用mk_tcp_session_send/mk_tcp_session_send_safe函数 - * @param session 会话处理对象 - */ -void API_CALL on_mk_websocket_session_destory(mk_tcp_session session){ - log_printf(LOG_LEV,"%s %d",mk_tcp_session_peer_ip(session),(int)mk_tcp_session_peer_port(session)); - websocket_user_data *user_data = (websocket_user_data *)mk_websocket_session_get_user_data(session); - free(user_data); -} - -/** - * 收到websocket客户端发过来的数据 + * 收到tcp客户端发过来的数据 * @param session 会话处理对象 * @param data 数据指针 * @param len 数据长度 */ -void API_CALL on_mk_websocket_session_data(mk_tcp_session session,const char *data,int len){ +void API_CALL on_mk_tcp_session_data(uint16_t server_port,mk_tcp_session session,const char *data,int len){ log_printf(LOG_LEV,"from %s %d, data[%d]: %s",mk_tcp_session_peer_ip(session),(int)mk_tcp_session_peer_port(session), len,data); mk_tcp_session_send(session,"echo:",0); mk_tcp_session_send(session,data,len); @@ -80,42 +76,132 @@ void API_CALL on_mk_websocket_session_data(mk_tcp_session session,const char *da * 每隔2秒的定时器,用于管理超时等任务 * @param session 会话处理对象 */ -void API_CALL on_mk_websocket_session_manager(mk_tcp_session session){ +void API_CALL on_mk_tcp_session_manager(uint16_t server_port,mk_tcp_session session){ log_printf(LOG_LEV,"%s %d",mk_tcp_session_peer_ip(session),(int)mk_tcp_session_peer_port(session)); } /** - * on_mk_websocket_session_destory之前触发on_mk_websocket_session_err * 一般由于客户端断开tcp触发 * 本事件中可以调用mk_tcp_session_send_safe函数 * @param session 会话处理对象 * @param code 错误代码 * @param msg 错误提示 */ -void API_CALL on_mk_websocket_session_err(mk_tcp_session session,int code,const char *msg){ - log_printf(LOG_LEV,"%s %d will destory: %d %s",mk_tcp_session_peer_ip(session),(int)mk_tcp_session_peer_port(session),code,msg); +void API_CALL on_mk_tcp_session_disconnect(uint16_t server_port,mk_tcp_session session,int code,const char *msg){ + log_printf(LOG_LEV,"%s %d: %d %s",mk_tcp_session_peer_ip(session),(int)mk_tcp_session_peer_port(session),code,msg); + tcp_session_user_data *user_data = (tcp_session_user_data *)mk_tcp_session_get_user_data(session); + free(user_data); } -static int flag = 1; -static void s_on_exit(int sig){ - flag = 0; +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +typedef struct { + mk_tcp_client client; + //下面你可以夹杂你的私货数据 + char your_some_useful_data[1024]; + int count; +} tcp_client_user_data; + +/** + * tcp客户端连接服务器成功或失败回调 + * @param client tcp客户端 + * @param code 0为连接成功,否则为失败原因 + * @param msg 连接失败错误提示 + */ +void API_CALL on_mk_tcp_client_connect(mk_tcp_client client,int code,const char *msg){ + log_printf(LOG_LEV,"connect result:%d %s",code,msg); + if(code == 0){ + //连接上后我们发送一个hello world测试数据 + mk_tcp_client_send(client,"hello world",0); + }else{ + tcp_client_user_data *user_data = mk_tcp_client_get_user_data(client); + mk_tcp_client_release(client); + free(user_data); + } } + +/** + * tcp客户端与tcp服务器之间断开回调 + * 一般是eof事件导致 + * @param client tcp客户端 + * @param code 错误代码 + * @param msg 错误提示 + */ +void API_CALL on_mk_tcp_client_disconnect(mk_tcp_client client,int code,const char *msg){ + log_printf(LOG_LEV,"disconnect:%d %s",code,msg); + //服务器主动断开了,我们销毁对象 + tcp_client_user_data *user_data = mk_tcp_client_get_user_data(client); + mk_tcp_client_release(client); + free(user_data); +} + +/** + * 收到tcp服务器发来的数据 + * @param client tcp客户端 + * @param data 数据指针 + * @param len 数据长度 + */ +void API_CALL on_mk_tcp_client_data(mk_tcp_client client,const char *data,int len){ + log_printf(LOG_LEV,"data[%d]:%s",len,data); +} + +/** + * 每隔2秒的定时器,用于管理超时等任务 + * @param client tcp客户端 + */ +void API_CALL on_mk_tcp_client_manager(mk_tcp_client client){ + tcp_client_user_data *user_data = mk_tcp_client_get_user_data(client); + char buf[1024]; + sprintf(buf,"on_mk_tcp_client_manager:%d",user_data->count); + mk_tcp_client_send(client,buf,0); + + if(++user_data->count == 5){ + //发送5遍后主动释放对象 + mk_tcp_client_release(client); + free(user_data); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////// +void test_server(){ + mk_tcp_session_events events_server = { + .on_mk_tcp_session_create = on_mk_tcp_session_create, + .on_mk_tcp_session_data = on_mk_tcp_session_data, + .on_mk_tcp_session_manager = on_mk_tcp_session_manager, + .on_mk_tcp_session_disconnect = on_mk_tcp_session_disconnect + }; + + mk_tcp_server_events_listen(&events_server); + mk_tcp_server_server_start(80,TCP_TYPE); +} + +void test_client(){ + mk_tcp_client_events events_clent = { + .on_mk_tcp_client_connect = on_mk_tcp_client_connect, + .on_mk_tcp_client_data = on_mk_tcp_client_data, + .on_mk_tcp_client_disconnect = on_mk_tcp_client_disconnect, + .on_mk_tcp_client_manager = on_mk_tcp_client_manager, + }; + mk_tcp_client client = mk_tcp_client_create(&events_clent, TCP_TYPE); + + tcp_client_user_data *user_data = (tcp_client_user_data *)malloc(sizeof(tcp_client_user_data)); + user_data->client = client; + user_data->count = 0; + mk_tcp_client_set_user_data(client,user_data); + + mk_tcp_client_connect(client, "121.40.165.18", 8800, 3); + //你可以连接127.0.0.1 80测试 + //mk_tcp_client_connect(client, "127.0.0.1", 80, 3); +} + int main(int argc, char *argv[]) { char ini_path[2048] = {0}; strcpy(ini_path,argv[0]); strcat(ini_path,".ini"); mk_env_init1(0, 0, 1, ini_path, 0, NULL, NULL); - mk_websocket_events events = { - .on_mk_websocket_session_create = on_mk_websocket_session_create, - .on_mk_websocket_session_destory = on_mk_websocket_session_destory, - .on_mk_websocket_session_data = on_mk_websocket_session_data, - .on_mk_websocket_session_manager = on_mk_websocket_session_manager, - .on_mk_websocket_session_err = on_mk_websocket_session_err - }; - mk_websocket_events_listen(&events); - mk_websocket_server_start(80,0); + test_server(); + test_client(); signal(SIGINT, s_on_exit );// 设置退出信号 while (flag) { diff --git a/src/Http/WebSocketClient.h b/src/Http/WebSocketClient.h index 6eeae0ac..cb140757 100644 --- a/src/Http/WebSocketClient.h +++ b/src/Http/WebSocketClient.h @@ -348,21 +348,25 @@ public: /** * 重载startConnect方法, * 目的是替换TcpClient的连接服务器行为,使之先完成WebSocket握手 - * @param strUrl websocket服务器ip或域名 + * @param host websocket服务器ip或域名 * @param iPort websocket服务器端口 * @param fTimeOutSec 超时时间 */ - void startConnect(const string &strUrl, uint16_t iPort, float fTimeOutSec = 3) override { + void startConnect(const string &host, uint16_t iPort, float fTimeOutSec = 3) override { string ws_url; if(useWSS){ //加密的ws - ws_url = StrPrinter << "wss://" + strUrl << ":" << iPort << "/" ; + ws_url = StrPrinter << "wss://" + host << ":" << iPort << "/" ; }else{ //明文ws - ws_url = StrPrinter << "ws://" + strUrl << ":" << iPort << "/" ; + ws_url = StrPrinter << "ws://" + host << ":" << iPort << "/" ; } _wsClient->startWsClient(ws_url,fTimeOutSec); } + + void startWebSocket(const string &ws_url,float fTimeOutSec = 3){ + _wsClient->startWsClient(ws_url,fTimeOutSec); + } private: typename HttpWsClient::Ptr _wsClient; }; From 528e4fffe5add389a7a8bca0e3e12741651e360a Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 26 Dec 2019 21:24:40 +0800 Subject: [PATCH 159/272] =?UTF-8?q?=E5=AE=8C=E6=88=90tcp=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8/=E5=AE=A2=E6=88=B7=E7=AB=AF=20c=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/tests/websocket.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/tests/websocket.c b/api/tests/websocket.c index 77411d6a..0398bf97 100644 --- a/api/tests/websocket.c +++ b/api/tests/websocket.c @@ -36,7 +36,7 @@ #endif #define LOG_LEV 4 -#define TCP_TYPE mk_type_wss +#define TCP_TYPE mk_type_ws static int flag = 1; static void s_on_exit(int sig){ @@ -190,7 +190,7 @@ void test_client(){ mk_tcp_client_connect(client, "121.40.165.18", 8800, 3); //你可以连接127.0.0.1 80测试 - //mk_tcp_client_connect(client, "127.0.0.1", 80, 3); +// mk_tcp_client_connect(client, "127.0.0.1", 80, 3); } int main(int argc, char *argv[]) { From 5b43b2c0043525aa83fb242bc2fea89c428b4b4b Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 27 Dec 2019 10:10:31 +0800 Subject: [PATCH 160/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0util=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E5=87=BD=E6=95=B0=EF=BC=8C=E6=95=B4=E7=90=86=E5=BA=93?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/{common.h => mk_common.h} | 19 ---- api/include/{events.h => mk_events.h} | 4 +- .../{events_objects.h => mk_events_objects.h} | 4 +- api/include/{httpclient.h => mk_httpclient.h} | 4 +- api/include/{media.h => mk_media.h} | 2 +- api/include/{mediakit.h => mk_mediakit.h} | 19 ++-- api/include/{player.h => mk_player.h} | 2 +- .../{proxyplayer.h => mk_proxyplayer.h} | 2 +- api/include/{pusher.h => mk_pusher.h} | 2 +- api/include/{recorder.h => mk_recorder.h} | 2 +- api/include/{tcp.h => mk_tcp.h} | 2 +- api/include/mk_util.h | 94 +++++++++++++++++++ api/source/{common.cpp => mk_common.cpp} | 15 +-- api/source/{events.cpp => mk_events.cpp} | 2 +- ...ents_objects.cpp => mk_events_objects.cpp} | 2 +- .../{httpclient.cpp => mk_httpclient.cpp} | 2 +- api/source/{media.cpp => mk_media.cpp} | 2 +- api/source/{player.cpp => mk_player.cpp} | 2 +- .../{proxyplayer.cpp => mk_proxyplayer.cpp} | 2 +- api/source/{pusher.cpp => mk_pusher.cpp} | 2 +- api/source/{recorder.cpp => mk_recorder.cpp} | 2 +- api/source/{tcp.cpp => mk_tcp.cpp} | 2 +- api/source/mk_util.cpp | 69 ++++++++++++++ api/tests/server.c | 23 +++-- api/tests/websocket.c | 21 ++++- 25 files changed, 228 insertions(+), 74 deletions(-) rename api/include/{common.h => mk_common.h} (82%) rename api/include/{events.h => mk_events.h} (99%) rename api/include/{events_objects.h => mk_events_objects.h} (99%) rename api/include/{httpclient.h => mk_httpclient.h} (99%) rename api/include/{media.h => mk_media.h} (99%) rename api/include/{mediakit.h => mk_mediakit.h} (84%) rename api/include/{player.h => mk_player.h} (99%) rename api/include/{proxyplayer.h => mk_proxyplayer.h} (99%) rename api/include/{pusher.h => mk_pusher.h} (99%) rename api/include/{recorder.h => mk_recorder.h} (99%) rename api/include/{tcp.h => mk_tcp.h} (99%) create mode 100644 api/include/mk_util.h rename api/source/{common.cpp => mk_common.cpp} (93%) rename api/source/{events.cpp => mk_events.cpp} (99%) rename api/source/{events_objects.cpp => mk_events_objects.cpp} (99%) rename api/source/{httpclient.cpp => mk_httpclient.cpp} (99%) rename api/source/{media.cpp => mk_media.cpp} (99%) rename api/source/{player.cpp => mk_player.cpp} (99%) rename api/source/{proxyplayer.cpp => mk_proxyplayer.cpp} (98%) rename api/source/{pusher.cpp => mk_pusher.cpp} (99%) rename api/source/{recorder.cpp => mk_recorder.cpp} (99%) rename api/source/{tcp.cpp => mk_tcp.cpp} (99%) create mode 100644 api/source/mk_util.cpp diff --git a/api/include/common.h b/api/include/mk_common.h similarity index 82% rename from api/include/common.h rename to api/include/mk_common.h index c54f7170..08100f69 100755 --- a/api/include/common.h +++ b/api/include/mk_common.h @@ -139,25 +139,6 @@ API_EXPORT uint16_t API_CALL mk_rtp_server_start(uint16_t port); */ API_EXPORT uint16_t API_CALL mk_shell_server_start(uint16_t port); -/** - * 打印日志 - * @param level 日志级别,支持0~4 - * @param file __FILE__ - * @param function __FUNCTION__ - * @param line __LINE__ - * @param fmt printf类型的格式控制字符串 - * @param ... 不定长参数 - */ -API_EXPORT void API_CALL mk_log_printf(int level, const char *file, const char *function, int line, const char *fmt, ...); - -// 以下宏可以替换printf使用 -#define log_trace(fmt,...) mk_log_printf(0,__FILE__,__FUNCTION__,__LINE__,fmt,##__VA_ARGS__) -#define log_debug(fmt,...) mk_log_printf(1,__FILE__,__FUNCTION__,__LINE__,fmt,##__VA_ARGS__) -#define log_info(fmt,...) mk_log_printf(2,__FILE__,__FUNCTION__,__LINE__,fmt,##__VA_ARGS__) -#define log_warn(fmt,...) mk_log_printf(3,__FILE__,__FUNCTION__,__LINE__,fmt,##__VA_ARGS__) -#define log_error(fmt,...) mk_log_printf(4,__FILE__,__FUNCTION__,__LINE__,fmt,##__VA_ARGS__) -#define log_printf(lev,fmt,...) mk_log_printf(lev,__FILE__,__FUNCTION__,__LINE__,fmt,##__VA_ARGS__) - #ifdef __cplusplus } #endif diff --git a/api/include/events.h b/api/include/mk_events.h similarity index 99% rename from api/include/events.h rename to api/include/mk_events.h index 26a9f5c2..f9336177 100644 --- a/api/include/events.h +++ b/api/include/mk_events.h @@ -27,8 +27,8 @@ #ifndef MK_EVENTS_H #define MK_EVENTS_H -#include "common.h" -#include "events_objects.h" +#include "mk_common.h" +#include "mk_events_objects.h" #ifdef __cplusplus extern "C" { diff --git a/api/include/events_objects.h b/api/include/mk_events_objects.h similarity index 99% rename from api/include/events_objects.h rename to api/include/mk_events_objects.h index 5881f0f7..ee456bf5 100644 --- a/api/include/events_objects.h +++ b/api/include/mk_events_objects.h @@ -26,8 +26,8 @@ #ifndef MK_EVENT_OBJECTS_H #define MK_EVENT_OBJECTS_H -#include "common.h" -#include "tcp.h" +#include "mk_common.h" +#include "mk_tcp.h" #ifdef __cplusplus extern "C" { #endif diff --git a/api/include/httpclient.h b/api/include/mk_httpclient.h similarity index 99% rename from api/include/httpclient.h rename to api/include/mk_httpclient.h index 7afef024..7acb91dd 100755 --- a/api/include/httpclient.h +++ b/api/include/mk_httpclient.h @@ -27,8 +27,8 @@ #ifndef MK_HTTPCLIENT_H_ #define MK_HTTPCLIENT_H_ -#include "common.h" -#include "events_objects.h" +#include "mk_common.h" +#include "mk_events_objects.h" #ifdef __cplusplus extern "C" { diff --git a/api/include/media.h b/api/include/mk_media.h similarity index 99% rename from api/include/media.h rename to api/include/mk_media.h index 55c6f141..d8162f6e 100755 --- a/api/include/media.h +++ b/api/include/mk_media.h @@ -27,7 +27,7 @@ #ifndef MK_MEDIA_H_ #define MK_MEDIA_H_ -#include "common.h" +#include "mk_common.h" #ifdef __cplusplus extern "C" { diff --git a/api/include/mediakit.h b/api/include/mk_mediakit.h similarity index 84% rename from api/include/mediakit.h rename to api/include/mk_mediakit.h index 6feb9eaf..e2b3dc35 100755 --- a/api/include/mediakit.h +++ b/api/include/mk_mediakit.h @@ -27,14 +27,15 @@ #ifndef MK_API_H_ #define MK_API_H_ -#include "common.h" -#include "httpclient.h" -#include "media.h" -#include "proxyplayer.h" -#include "recorder.h" -#include "player.h" -#include "pusher.h" -#include "events.h" -#include "tcp.h" +#include "mk_common.h" +#include "mk_httpclient.h" +#include "mk_media.h" +#include "mk_proxyplayer.h" +#include "mk_recorder.h" +#include "mk_player.h" +#include "mk_pusher.h" +#include "mk_events.h" +#include "mk_tcp.h" +#include "mk_util.h" #endif /* MK_API_H_ */ diff --git a/api/include/player.h b/api/include/mk_player.h similarity index 99% rename from api/include/player.h rename to api/include/mk_player.h index ad119a72..14092cd7 100755 --- a/api/include/player.h +++ b/api/include/mk_player.h @@ -27,7 +27,7 @@ #ifndef MK_PLAYER_H_ #define MK_PLAYER_H_ -#include "common.h" +#include "mk_common.h" #ifdef __cplusplus extern "C" { diff --git a/api/include/proxyplayer.h b/api/include/mk_proxyplayer.h similarity index 99% rename from api/include/proxyplayer.h rename to api/include/mk_proxyplayer.h index 24652e2e..a8495eda 100644 --- a/api/include/proxyplayer.h +++ b/api/include/mk_proxyplayer.h @@ -27,7 +27,7 @@ #ifndef MK_PROXY_PLAYER_H_ #define MK_PROXY_PLAYER_H_ -#include "common.h" +#include "mk_common.h" #ifdef __cplusplus extern "C" { diff --git a/api/include/pusher.h b/api/include/mk_pusher.h similarity index 99% rename from api/include/pusher.h rename to api/include/mk_pusher.h index f234c1bb..cc4f2645 100644 --- a/api/include/pusher.h +++ b/api/include/mk_pusher.h @@ -28,7 +28,7 @@ #ifndef MK_PUSHER_H #define MK_PUSHER_H -#include "common.h" +#include "mk_common.h" #ifdef __cplusplus extern "C" { diff --git a/api/include/recorder.h b/api/include/mk_recorder.h similarity index 99% rename from api/include/recorder.h rename to api/include/mk_recorder.h index 95bd9a84..effd77e7 100644 --- a/api/include/recorder.h +++ b/api/include/mk_recorder.h @@ -27,7 +27,7 @@ #ifndef MK_RECORDER_API_H_ #define MK_RECORDER_API_H_ -#include "common.h" +#include "mk_common.h" #ifdef __cplusplus extern "C" { diff --git a/api/include/tcp.h b/api/include/mk_tcp.h similarity index 99% rename from api/include/tcp.h rename to api/include/mk_tcp.h index b0511e86..3bd81885 100644 --- a/api/include/tcp.h +++ b/api/include/mk_tcp.h @@ -27,7 +27,7 @@ #ifndef MK_TCP_H #define MK_TCP_H -#include "common.h" +#include "mk_common.h" #ifdef __cplusplus extern "C" { diff --git a/api/include/mk_util.h b/api/include/mk_util.h new file mode 100644 index 00000000..99d7084c --- /dev/null +++ b/api/include/mk_util.h @@ -0,0 +1,94 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MK_UTIL_H +#define MK_UTIL_H + +#include +#include "mk_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * 获取本程序可执行文件路径 + * @return 文件路径,使用完后需要自己free + */ +API_EXPORT char* API_CALL mk_util_get_exe_path(); + +/** + * 获取本程序可执行文件相同目录下文件的绝对路径 + * @param relative_path 同目录下文件的路径相对,可以为null + * @return 文件路径,使用完后需要自己free + */ +API_EXPORT char* API_CALL mk_uitl_get_exe_dir(const char *relative_path); + +/** + * 获取unix标准的系统时间戳 + * @return 当前系统时间戳 + */ +API_EXPORT uint64_t API_CALL mk_uitl_get_current_millisecond(); + +/** + * 获取时间字符串 + * @param fmt 时间格式,譬如%Y-%m-%d %H:%M:%S + * @return 时间字符串,使用完后需要自己free + */ +API_EXPORT char* API_CALL mk_uitl_get_current_time_string(const char *fmt); + +/** + * 打印二进制为字符串 + * @param buf 二进制数据 + * @param len 数据长度 + * @return 可打印的调试信息,使用完后需要自己free + */ +API_EXPORT char* API_CALL mk_uitl_hex_dump(const void *buf, int len); +///////////////////////////////////////////日志///////////////////////////////////////////// + +/** + * 打印日志 + * @param level 日志级别,支持0~4 + * @param file __FILE__ + * @param function __FUNCTION__ + * @param line __LINE__ + * @param fmt printf类型的格式控制字符串 + * @param ... 不定长参数 + */ +API_EXPORT void API_CALL mk_log_printf(int level, const char *file, const char *function, int line, const char *fmt, ...); + +// 以下宏可以替换printf使用 +#define log_trace(fmt,...) mk_log_printf(0,__FILE__,__FUNCTION__,__LINE__,fmt,##__VA_ARGS__) +#define log_debug(fmt,...) mk_log_printf(1,__FILE__,__FUNCTION__,__LINE__,fmt,##__VA_ARGS__) +#define log_info(fmt,...) mk_log_printf(2,__FILE__,__FUNCTION__,__LINE__,fmt,##__VA_ARGS__) +#define log_warn(fmt,...) mk_log_printf(3,__FILE__,__FUNCTION__,__LINE__,fmt,##__VA_ARGS__) +#define log_error(fmt,...) mk_log_printf(4,__FILE__,__FUNCTION__,__LINE__,fmt,##__VA_ARGS__) +#define log_printf(lev,fmt,...) mk_log_printf(lev,__FILE__,__FUNCTION__,__LINE__,fmt,##__VA_ARGS__) + +#ifdef __cplusplus +} +#endif +#endif //MK_UTIL_H diff --git a/api/source/common.cpp b/api/source/mk_common.cpp similarity index 93% rename from api/source/common.cpp rename to api/source/mk_common.cpp index b96414e4..19849621 100755 --- a/api/source/common.cpp +++ b/api/source/mk_common.cpp @@ -24,7 +24,7 @@ * SOFTWARE. */ -#include "common.h" +#include "mk_common.h" #include #include #include "Util/logger.h" @@ -206,16 +206,3 @@ API_EXPORT uint16_t API_CALL mk_shell_server_start(uint16_t port){ return 0; } } - -API_EXPORT void API_CALL mk_log_printf(int level, const char *file, const char *function, int line, const char *fmt, ...) { - assert(file && function && fmt); - LogContextCapturer info(Logger::Instance(), (LogLevel) level, file, function, line); - va_list pArg; - va_start(pArg, fmt); - char buf[4096]; - int n = vsprintf(buf, fmt, pArg); - buf[n] = '\0'; - va_end(pArg); - info << buf; -} - diff --git a/api/source/events.cpp b/api/source/mk_events.cpp similarity index 99% rename from api/source/events.cpp rename to api/source/mk_events.cpp index 832ceb92..8d915eb1 100644 --- a/api/source/events.cpp +++ b/api/source/mk_events.cpp @@ -24,7 +24,7 @@ * SOFTWARE. */ -#include "events.h" +#include "mk_events.h" #include "Common/config.h" #include "Common/MediaSource.h" #include "Http/HttpSession.h" diff --git a/api/source/events_objects.cpp b/api/source/mk_events_objects.cpp similarity index 99% rename from api/source/events_objects.cpp rename to api/source/mk_events_objects.cpp index 435031f0..69e9fa3a 100644 --- a/api/source/events_objects.cpp +++ b/api/source/mk_events_objects.cpp @@ -25,7 +25,7 @@ */ #include -#include "events_objects.h" +#include "mk_events_objects.h" #include "Common/config.h" #include "Record/MP4Recorder.h" #include "Network/TcpSession.h" diff --git a/api/source/httpclient.cpp b/api/source/mk_httpclient.cpp similarity index 99% rename from api/source/httpclient.cpp rename to api/source/mk_httpclient.cpp index 01e4c68f..0e32b472 100755 --- a/api/source/httpclient.cpp +++ b/api/source/mk_httpclient.cpp @@ -23,7 +23,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#include "httpclient.h" +#include "mk_httpclient.h" #include "Util/logger.h" #include "Http/HttpDownloader.h" diff --git a/api/source/media.cpp b/api/source/mk_media.cpp similarity index 99% rename from api/source/media.cpp rename to api/source/mk_media.cpp index df488b51..1751005b 100755 --- a/api/source/media.cpp +++ b/api/source/mk_media.cpp @@ -24,7 +24,7 @@ * SOFTWARE. */ -#include "media.h" +#include "mk_media.h" #include "Util/logger.h" #include "Common/Device.h" diff --git a/api/source/player.cpp b/api/source/mk_player.cpp similarity index 99% rename from api/source/player.cpp rename to api/source/mk_player.cpp index 6a320f0e..75c556ac 100755 --- a/api/source/player.cpp +++ b/api/source/mk_player.cpp @@ -24,7 +24,7 @@ * SOFTWARE. */ -#include "player.h" +#include "mk_player.h" #include "Util/logger.h" #include "Player/MediaPlayer.h" using namespace std; diff --git a/api/source/proxyplayer.cpp b/api/source/mk_proxyplayer.cpp similarity index 98% rename from api/source/proxyplayer.cpp rename to api/source/mk_proxyplayer.cpp index 0bf9d43a..23702a9d 100644 --- a/api/source/proxyplayer.cpp +++ b/api/source/mk_proxyplayer.cpp @@ -24,7 +24,7 @@ * SOFTWARE. */ -#include "proxyplayer.h" +#include "mk_proxyplayer.h" #include "Player/PlayerProxy.h" using namespace toolkit; diff --git a/api/source/pusher.cpp b/api/source/mk_pusher.cpp similarity index 99% rename from api/source/pusher.cpp rename to api/source/mk_pusher.cpp index df74f03a..cdab8d26 100644 --- a/api/source/pusher.cpp +++ b/api/source/mk_pusher.cpp @@ -25,7 +25,7 @@ */ #include -#include "pusher.h" +#include "mk_pusher.h" #include "Pusher/MediaPusher.h" using namespace mediakit; diff --git a/api/source/recorder.cpp b/api/source/mk_recorder.cpp similarity index 99% rename from api/source/recorder.cpp rename to api/source/mk_recorder.cpp index b5315bc8..ee5baa4a 100644 --- a/api/source/recorder.cpp +++ b/api/source/mk_recorder.cpp @@ -24,7 +24,7 @@ * SOFTWARE. */ -#include "recorder.h" +#include "mk_recorder.h" #include "Rtmp/FlvMuxer.h" #include "Record/Recorder.h" using namespace toolkit; diff --git a/api/source/tcp.cpp b/api/source/mk_tcp.cpp similarity index 99% rename from api/source/tcp.cpp rename to api/source/mk_tcp.cpp index 61dd22e4..613622a0 100644 --- a/api/source/tcp.cpp +++ b/api/source/mk_tcp.cpp @@ -24,7 +24,7 @@ * SOFTWARE. */ -#include "tcp.h" +#include "mk_tcp.h" #include "Network/TcpSession.h" #include "Network/TcpClient.h" #include "Http/WebSocketClient.h" diff --git a/api/source/mk_util.cpp b/api/source/mk_util.cpp new file mode 100644 index 00000000..872bf672 --- /dev/null +++ b/api/source/mk_util.cpp @@ -0,0 +1,69 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "mk_util.h" +#include +#include "Util/logger.h" +using namespace std; +using namespace toolkit; + +API_EXPORT char* API_CALL mk_util_get_exe_path(){ + return strdup(exePath().data()); +} + +API_EXPORT char* API_CALL mk_uitl_get_exe_dir(const char *relative_path){ + if(relative_path){ + return strdup((exeDir() + relative_path).data()); + } + return strdup(exeDir().data()); +} + +API_EXPORT uint64_t API_CALL mk_uitl_get_current_millisecond(){ + return getCurrentMillisecond(); +} + +API_EXPORT char* API_CALL mk_uitl_get_current_time_string(const char *fmt){ + assert(fmt); + return strdup(getTimeStr(fmt).data()); +} + +API_EXPORT char* API_CALL mk_uitl_hex_dump(const void *buf, int len){ + assert(buf && len > 0); + return strdup(hexdump(buf,len).data()); +} + +API_EXPORT void API_CALL mk_log_printf(int level, const char *file, const char *function, int line, const char *fmt, ...) { + assert(file && function && fmt); + LogContextCapturer info(Logger::Instance(), (LogLevel) level, file, function, line); + va_list pArg; + va_start(pArg, fmt); + char buf[4096]; + int n = vsprintf(buf, fmt, pArg); + buf[n] = '\0'; + va_end(pArg); + info << buf; +} + diff --git a/api/tests/server.c b/api/tests/server.c index 16373389..34e3db1b 100644 --- a/api/tests/server.c +++ b/api/tests/server.c @@ -26,7 +26,7 @@ #include #include -#include "mediakit.h" +#include "mk_mediakit.h" #ifdef _WIN32 #include "windows.h" @@ -396,12 +396,24 @@ static void s_on_exit(int sig){ flag = 0; } int main(int argc, char *argv[]) { - char ini_path[2048] = {0}; - strcpy(ini_path,argv[0]); - strcat(ini_path,".ini"); + char *ini_path = mk_uitl_get_exe_dir("c_api.ini"); + char *ssl_path = mk_uitl_get_exe_dir("ssl.p12"); + + mk_config config = { + .ini = ini_path, + .ini_is_path = 1, + .log_level = 0, + .ssl = ssl_path, + .ssl_is_path = 1, + .ssl_pwd = NULL, + .thread_num = 0 + }; + mk_env_init(&config); + free(ini_path); + free(ssl_path); - mk_env_init1(0, 0, 1, ini_path, 0, NULL, NULL); mk_http_server_start(80, 0); + mk_http_server_start(443, 1); mk_rtsp_server_start(554, 0); mk_rtmp_server_start(1935, 0); mk_shell_server_start(9000); @@ -424,7 +436,6 @@ int main(int argc, char *argv[]) { }; mk_events_listen(&events); - signal(SIGINT, s_on_exit );// 设置退出信号 while (flag) { #ifdef _WIN32 diff --git a/api/tests/websocket.c b/api/tests/websocket.c index 0398bf97..845a78d6 100644 --- a/api/tests/websocket.c +++ b/api/tests/websocket.c @@ -28,7 +28,7 @@ #include #include #include -#include "mediakit.h" +#include "mk_mediakit.h" #ifdef _WIN32 #include "windows.h" #else @@ -36,6 +36,7 @@ #endif #define LOG_LEV 4 +//修改此宏,可以选择协议类型 #define TCP_TYPE mk_type_ws static int flag = 1; @@ -194,11 +195,21 @@ void test_client(){ } int main(int argc, char *argv[]) { - char ini_path[2048] = {0}; - strcpy(ini_path,argv[0]); - strcat(ini_path,".ini"); + char *ini_path = mk_uitl_get_exe_dir("c_api.ini"); + char *ssl_path = mk_uitl_get_exe_dir("ssl.p12"); - mk_env_init1(0, 0, 1, ini_path, 0, NULL, NULL); + mk_config config = { + .ini = ini_path, + .ini_is_path = 1, + .log_level = 0, + .ssl = ssl_path, + .ssl_is_path = 1, + .ssl_pwd = NULL, + .thread_num = 0 + }; + mk_env_init(&config); + free(ini_path); + free(ssl_path); test_server(); test_client(); From e8951c129f87091a3169ddb1473fc9fdc56deaac Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 27 Dec 2019 10:46:40 +0800 Subject: [PATCH 161/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=BA=BF=E7=A8=8B?= =?UTF-8?q?=E7=9B=B8=E5=85=B3api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mk_thread.h | 102 ++++++++++++++++++++++++++++++++++++ api/source/mk_tcp.cpp | 30 +---------- api/source/mk_tcp_private.h | 62 ++++++++++++++++++++++ api/source/mk_thread.cpp | 76 +++++++++++++++++++++++++++ src/Http/WebSocketClient.h | 1 + 5 files changed, 242 insertions(+), 29 deletions(-) create mode 100644 api/include/mk_thread.h create mode 100644 api/source/mk_tcp_private.h create mode 100644 api/source/mk_thread.cpp diff --git a/api/include/mk_thread.h b/api/include/mk_thread.h new file mode 100644 index 00000000..2f9b0f37 --- /dev/null +++ b/api/include/mk_thread.h @@ -0,0 +1,102 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MK_THREAD_H +#define MK_THREAD_H + +#include +#include "mk_common.h" +#include "mk_tcp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +///////////////////////////////////////////事件线程///////////////////////////////////////////// +typedef void* mk_thread; + +/** + * 获取tcp会话对象所在事件线程 + * @param ctx tcp会话对象 + * @return 对象所在事件线程 + */ +API_EXPORT mk_thread API_CALL mk_thread_from_tcp_session(mk_tcp_session ctx); + +/** + * 获取tcp客户端对象所在事件线程 + * @param ctx tcp客户端 + * @return 对象所在事件线程 + */ +API_EXPORT mk_thread API_CALL mk_thread_from_tcp_client(mk_tcp_client ctx); + +///////////////////////////////////////////线程切换///////////////////////////////////////////// +typedef void (API_CALL *on_mk_async)(void *user_data); + +/** + * 切换到事件线程并异步执行 + * @param ctx 事件线程 + * @param cb 回调函数 + * @param user_data 用户数据指针 + */ +API_EXPORT void API_CALL mk_async_do(mk_thread ctx,on_mk_async cb, void *user_data); + +/** + * 切换到事件线程并同步执行 + * @param ctx 事件线程 + * @param cb 回调函数 + * @param user_data 用户数据指针 + */ +API_EXPORT void API_CALL mk_sync_do(mk_thread ctx,on_mk_async cb, void *user_data); + +///////////////////////////////////////////定时器///////////////////////////////////////////// +typedef void* mk_timer; + +/** + * 定时器触发事件 + * @return 下一次触发延时(单位毫秒),返回0则不再重复 + */ +typedef uint64_t (API_CALL *on_mk_timer)(void *user_data); + +/** + * 创建定时器 + * @param ctx 线程对象 + * @param delay_ms 执行延时,单位毫秒 + * @param cb 回调函数 + * @param user_data 用户数据指针 + * @return 定时器对象 + */ +API_EXPORT mk_timer API_CALL mk_timer_create(mk_thread ctx,uint64_t delay_ms, on_mk_timer cb, void *user_data); + +/** + * 销毁和取消定时器 + * @param ctx 定时器对象 + */ +API_EXPORT void API_CALL mk_timer_release(mk_timer ctx); + +#ifdef __cplusplus +} +#endif +#endif //MK_THREAD_H diff --git a/api/source/mk_tcp.cpp b/api/source/mk_tcp.cpp index 613622a0..2d18557c 100644 --- a/api/source/mk_tcp.cpp +++ b/api/source/mk_tcp.cpp @@ -25,8 +25,7 @@ */ #include "mk_tcp.h" -#include "Network/TcpSession.h" -#include "Network/TcpClient.h" +#include "mk_tcp_private.h" #include "Http/WebSocketClient.h" #include "Http/WebSocketSession.h" using namespace mediakit; @@ -89,17 +88,6 @@ API_EXPORT void API_CALL mk_tcp_session_send_safe(const mk_tcp_session ctx,const static TcpServer::Ptr s_tcp_server[4]; static mk_tcp_session_events s_events_server = {0}; -class TcpSessionForC : public TcpSession { -public: - TcpSessionForC(const Socket::Ptr &pSock) ; - ~TcpSessionForC() override = default; - void onRecv(const Buffer::Ptr &buffer) override ; - void onError(const SockException &err) override; - void onManager() override; - void *_user_data; - uint16_t _local_port; -}; - TcpSessionForC::TcpSessionForC(const Socket::Ptr &pSock) : TcpSession(pSock) { _local_port = get_local_port(); if (s_events_server.on_mk_tcp_session_create) { @@ -178,22 +166,6 @@ API_EXPORT uint16_t API_CALL mk_tcp_server_server_start(uint16_t port, mk_tcp_ty } ///////////////////////////////////////////////////TcpClientForC///////////////////////////////////////////////////////// -class TcpClientForC : public TcpClient { -public: - typedef std::shared_ptr Ptr; - TcpClientForC(mk_tcp_client_events *events) ; - ~TcpClientForC() override ; - void onRecv(const Buffer::Ptr &pBuf) override; - void onErr(const SockException &ex) override; - void onManager() override; - void onConnect(const SockException &ex) override; - void setClient(mk_tcp_client client); - void *_user_data; -private: - mk_tcp_client_events _events; - mk_tcp_client _client; -}; - TcpClientForC::TcpClientForC(mk_tcp_client_events *events){ _events = *events; } diff --git a/api/source/mk_tcp_private.h b/api/source/mk_tcp_private.h new file mode 100644 index 00000000..379068f4 --- /dev/null +++ b/api/source/mk_tcp_private.h @@ -0,0 +1,62 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef MK_TCP_PRIVATE_H +#define MK_TCP_PRIVATE_H + +#include "mk_tcp.h" +#include "Network/TcpClient.h" +#include "Network/TcpSession.h" +using namespace toolkit; + +class TcpClientForC : public TcpClient { + public: + typedef std::shared_ptr Ptr; + TcpClientForC(mk_tcp_client_events *events) ; + ~TcpClientForC() override ; + void onRecv(const Buffer::Ptr &pBuf) override; + void onErr(const SockException &ex) override; + void onManager() override; + void onConnect(const SockException &ex) override; + void setClient(mk_tcp_client client); + void *_user_data; + private: + mk_tcp_client_events _events; + mk_tcp_client _client; +}; + +class TcpSessionForC : public TcpSession { + public: + TcpSessionForC(const Socket::Ptr &pSock) ; + ~TcpSessionForC() override = default; + void onRecv(const Buffer::Ptr &buffer) override ; + void onError(const SockException &err) override; + void onManager() override; + void *_user_data; + uint16_t _local_port; +}; + +#endif //MK_TCP_PRIVATE_H diff --git a/api/source/mk_thread.cpp b/api/source/mk_thread.cpp new file mode 100644 index 00000000..185e54f1 --- /dev/null +++ b/api/source/mk_thread.cpp @@ -0,0 +1,76 @@ +/* + * MIT License + * + * Copyright (c) 2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "mk_thread.h" +#include "mk_tcp_private.h" +#include "Util/logger.h" +#include "Poller/EventPoller.h" +using namespace std; +using namespace toolkit; + +API_EXPORT mk_thread API_CALL mk_thread_from_tcp_session(mk_tcp_session ctx){ + assert(ctx); + TcpSession *obj = (TcpSession *)ctx; + return obj->getPoller().get(); +} + +API_EXPORT mk_thread API_CALL mk_thread_from_tcp_client(mk_tcp_client ctx){ + assert(ctx); + TcpClient::Ptr *client = (TcpClient::Ptr *)ctx; + return (*client)->getPoller().get(); +} + +API_EXPORT void API_CALL mk_async_do(mk_thread ctx,on_mk_async cb, void *user_data){ + assert(ctx && cb); + EventPoller *poller = (EventPoller *)ctx; + poller->async([cb,user_data](){ + cb(user_data); + }); +} + +API_EXPORT void API_CALL mk_sync_do(mk_thread ctx,on_mk_async cb, void *user_data){ + assert(ctx && cb); + EventPoller *poller = (EventPoller *)ctx; + poller->sync([cb,user_data](){ + cb(user_data); + }); +} + +API_EXPORT mk_timer API_CALL mk_timer_create(mk_thread ctx,uint64_t delay_ms,on_mk_timer cb, void *user_data){ + assert(ctx && cb); + EventPoller *poller = (EventPoller *)ctx; + auto ret = poller->doDelayTask(delay_ms,[cb,user_data](){ + return cb(user_data); + }); + return new DelayTask::Ptr(ret); +} + +API_EXPORT void API_CALL mk_timer_release(mk_timer ctx){ + assert(ctx); + DelayTask::Ptr *obj = (DelayTask::Ptr *)ctx; + (*obj)->cancel(); + delete obj; +} \ No newline at end of file diff --git a/src/Http/WebSocketClient.h b/src/Http/WebSocketClient.h index cb140757..85051fc3 100644 --- a/src/Http/WebSocketClient.h +++ b/src/Http/WebSocketClient.h @@ -89,6 +89,7 @@ public: HttpWsClient(ClientTypeImp &delegate) : _delegate(delegate){ _Sec_WebSocket_Key = encodeBase64(SHA1::encode_bin(makeRandStr(16, false))); + setPoller(delegate.getPoller()); } ~HttpWsClient(){} From 7d7b29825fbced7ea2319aa4680c8cb30e7f12b9 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 27 Dec 2019 10:47:01 +0800 Subject: [PATCH 162/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=BA=BF=E7=A8=8B?= =?UTF-8?q?=E7=9B=B8=E5=85=B3api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mk_mediakit.h | 1 + 1 file changed, 1 insertion(+) diff --git a/api/include/mk_mediakit.h b/api/include/mk_mediakit.h index e2b3dc35..c8911a1b 100755 --- a/api/include/mk_mediakit.h +++ b/api/include/mk_mediakit.h @@ -37,5 +37,6 @@ #include "mk_events.h" #include "mk_tcp.h" #include "mk_util.h" +#include "mk_thread.h" #endif /* MK_API_H_ */ From c3a32e862ac384f62fc8ea0368391875ddddeff6 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 27 Dec 2019 10:52:14 +0800 Subject: [PATCH 163/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/source/mk_util.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/api/source/mk_util.cpp b/api/source/mk_util.cpp index 872bf672..860af2a3 100644 --- a/api/source/mk_util.cpp +++ b/api/source/mk_util.cpp @@ -26,6 +26,7 @@ #include "mk_util.h" #include +#include #include "Util/logger.h" using namespace std; using namespace toolkit; From 2aec68fd6fc4a9ba882c435e3442afdd426bab0f Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 27 Dec 2019 12:03:54 +0800 Subject: [PATCH 164/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0install=E5=91=BD?= =?UTF-8?q?=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/CMakeLists.txt | 15 +++++++++++++-- api/tests/CMakeLists.txt | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt index 3cea8196..ff5db73e 100644 --- a/api/CMakeLists.txt +++ b/api/CMakeLists.txt @@ -1,13 +1,24 @@ include_directories(include source) file(GLOB api_src_list include/*.h source/*.cpp source/*.h source/*.c) -add_library(api SHARED ${api_src_list}) +add_library(mk_api SHARED ${api_src_list}) if(WIN32) add_definitions(-DMediaKitApi_EXPORTS) endif() -target_link_libraries(api ${LINK_LIB_LIST}) +target_link_libraries(mk_api ${LINK_LIB_LIST}) add_subdirectory(tests) +#安装目录 +if(WIN32) + set(INSTALL_PATH_LIB $ENV{HOME}/${CMAKE_PROJECT_NAME}/lib) + set(INSTALL_PATH_INCLUDE $ENV{HOME}/${CMAKE_PROJECT_NAME}/include) +else() + set(INSTALL_PATH_LIB lib) + set(INSTALL_PATH_INCLUDE include) +endif() +file(GLOB api_header_list include/*.h) +install(FILES ${api_header_list} DESTINATION ${INSTALL_PATH_INCLUDE}) +install(TARGETS mk_api ARCHIVE DESTINATION ${INSTALL_PATH_LIB} LIBRARY DESTINATION ${INSTALL_PATH_LIB}) diff --git a/api/tests/CMakeLists.txt b/api/tests/CMakeLists.txt index 02c6cdd6..24f5cdb5 100644 --- a/api/tests/CMakeLists.txt +++ b/api/tests/CMakeLists.txt @@ -9,7 +9,7 @@ foreach(TEST_SRC ${TEST_SRC_LIST}) set_target_properties(${exe_name} PROPERTIES COMPILE_FLAGS ${VS_FALGS} ) endif(WIN32) - target_link_libraries(${exe_name} api) + target_link_libraries(${exe_name} mk_api) endforeach() From aaab52c94f76d4b9d3072f45e8aad33abf7df1fe Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 27 Dec 2019 12:15:38 +0800 Subject: [PATCH 165/272] =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=A0=B9=E7=9B=AE?= =?UTF-8?q?=E5=BD=95httpRoot=E6=94=B9=E6=88=90www=EF=BC=8C=E5=B9=B6?= =?UTF-8?q?=E4=B8=94=E8=87=AA=E5=8A=A8=E5=A4=8D=E5=88=B6=E5=88=B0=E7=9B=B8?= =?UTF-8?q?=E5=BA=94=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 4 ++++ conf/config.ini | 8 ++++---- src/Common/config.cpp | 6 +++--- www/index.html | 8 ++++++++ 4 files changed, 19 insertions(+), 7 deletions(-) create mode 100644 www/index.html diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b2baa92..a76876e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -188,6 +188,10 @@ elseif(NOT ANDROID OR IOS) list(APPEND LINK_LIB_LIST pthread) endif () +#复制文件过来 +execute_process(COMMAND cp -r ${CMAKE_CURRENT_SOURCE_DIR}/www ${EXECUTABLE_OUTPUT_PATH}/) +execute_process(COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/conf/config.ini ${EXECUTABLE_OUTPUT_PATH}/) + #添加c库 add_subdirectory(api) diff --git a/conf/config.ini b/conf/config.ini index a7588b80..7e4e3029 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -16,7 +16,7 @@ log=./ffmpeg/ffmpeg.log [general] #是否启用虚拟主机 -enableVhost=1 +enableVhost=0 #播放器或推流器在断开后会触发hook.on_flow_report事件(使用多少流量事件), #flowThreshold参数控制触发hook.on_flow_report事件阈值,使用流量超过该阈值后才触发,单位KB flowThreshold=1024 @@ -48,7 +48,7 @@ publishToMP4=0 fileBufSize=65536 #hls保存文件路径 #可以为相对(相对于本可执行程序目录)或绝对路径 -filePath=./httpRoot +filePath=./www #hls最大切片时间 segDur=2 #m3u8索引中,hls保留切片个数(实际保留切片个数大2~3个) @@ -104,7 +104,7 @@ notFound=404 Not Found404 Not Found" @@ -246,7 +246,7 @@ onceToken token([](){ mINI::Instance()[kAppName] = "record"; mINI::Instance()[kSampleMS] = 500; mINI::Instance()[kFileSecond] = 60*60; - mINI::Instance()[kFilePath] = "./httpRoot"; + mINI::Instance()[kFilePath] = "./www"; mINI::Instance()[kFileBufSize] = 64 * 1024; mINI::Instance()[kFastStart] = false; mINI::Instance()[kFileRepeat] = false; @@ -272,7 +272,7 @@ onceToken token([](){ mINI::Instance()[kSegmentNum] = 3; mINI::Instance()[kSegmentRetain] = 5; mINI::Instance()[kFileBufSize] = 64 * 1024; - mINI::Instance()[kFilePath] = "./httpRoot"; + mINI::Instance()[kFilePath] = "./www"; },nullptr); } //namespace Hls diff --git a/www/index.html b/www/index.html new file mode 100644 index 00000000..964500e1 --- /dev/null +++ b/www/index.html @@ -0,0 +1,8 @@ + +欢迎使用ZLMediaKit + +

    欢迎使用ZLMediaKit,请阅读wiki 获取更多帮助!

    +
    +
    ZLMediaKit
    + + \ No newline at end of file From 118be421d6432c8c4bbf3f9a615f75e82c229869 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 27 Dec 2019 12:22:17 +0800 Subject: [PATCH 166/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0bom=E5=A4=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/HttpSession.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp index 85ee6b9b..405a67ac 100644 --- a/src/Http/HttpSession.cpp +++ b/src/Http/HttpSession.cpp @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> From 37bca15dc9a83a3895a207902a54f5a13bf9d23f Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 27 Dec 2019 13:56:02 +0800 Subject: [PATCH 167/272] =?UTF-8?q?flv=E5=BD=95=E5=88=B6=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=8F=82=E6=95=B0vhost?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mk_recorder.h | 3 ++- api/source/mk_recorder.cpp | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/api/include/mk_recorder.h b/api/include/mk_recorder.h index effd77e7..7419fce3 100644 --- a/api/include/mk_recorder.h +++ b/api/include/mk_recorder.h @@ -52,12 +52,13 @@ API_EXPORT void API_CALL mk_flv_recorder_release(mk_flv_recorder ctx); /** * 开始录制flv * @param ctx flv录制器 + * @param vhost 虚拟主机 * @param app 绑定的RtmpMediaSource的 app名 * @param stream 绑定的RtmpMediaSource的 stream名 * @param file_path 文件存放地址 * @return 0:开始超过,-1:失败,打开文件失败或该RtmpMediaSource不存在 */ -API_EXPORT int API_CALL mk_flv_recorder_start(mk_flv_recorder ctx, const char *app, const char *stream, const char *file_path); +API_EXPORT int API_CALL mk_flv_recorder_start(mk_flv_recorder ctx, const char *vhost, const char *app, const char *stream, const char *file_path); ///////////////////////////////////////////hls/mp4录制///////////////////////////////////////////// diff --git a/api/source/mk_recorder.cpp b/api/source/mk_recorder.cpp index ee5baa4a..7ac4968c 100644 --- a/api/source/mk_recorder.cpp +++ b/api/source/mk_recorder.cpp @@ -39,11 +39,11 @@ API_EXPORT void API_CALL mk_flv_recorder_release(mk_flv_recorder ctx){ FlvRecorder::Ptr *record = (FlvRecorder::Ptr *)(ctx); delete record; } -API_EXPORT int API_CALL mk_flv_recorder_start(mk_flv_recorder ctx, const char *app, const char *stream, const char *file_path){ - assert(ctx && app && stream && file_path); +API_EXPORT int API_CALL mk_flv_recorder_start(mk_flv_recorder ctx, const char *vhost, const char *app, const char *stream, const char *file_path){ + assert(ctx && vhost && app && stream && file_path); try { FlvRecorder::Ptr *record = (FlvRecorder::Ptr *)(ctx); - (*record)->startRecord(EventPollerPool::Instance().getPoller(), DEFAULT_VHOST,app,stream,file_path); + (*record)->startRecord(EventPollerPool::Instance().getPoller(),vhost,app,stream,file_path); return 0; }catch (std::exception &ex){ WarnL << ex.what(); From 291969aee6a262dfa542b76ebdbdfebef3bb3ed1 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 27 Dec 2019 15:36:54 +0800 Subject: [PATCH 168/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=86=99=E7=BC=93=E5=AD=98=E6=8F=90=E5=89=8D=E9=94=80=E6=AF=81?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98:#196?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/HlsMakerImp.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Record/HlsMakerImp.cpp b/src/Record/HlsMakerImp.cpp index cfb76e17..cfb42ed6 100644 --- a/src/Record/HlsMakerImp.cpp +++ b/src/Record/HlsMakerImp.cpp @@ -103,7 +103,8 @@ void HlsMakerImp::onWriteHls(const char *data, int len) { std::shared_ptr HlsMakerImp::makeFile(const string &file,bool setbuf) { - auto ret= shared_ptr(File::createfile_file(file.data(), "wb"), [](FILE *fp) { + auto file_buf = _file_buf; + auto ret= shared_ptr(File::createfile_file(file.data(), "wb"), [file_buf](FILE *fp) { if (fp) { fclose(fp); } From 582f76989346e038b55b010c9c0bb77772d0daef Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 27 Dec 2019 15:50:45 +0800 Subject: [PATCH 169/272] =?UTF-8?q?=E7=A7=BB=E5=8A=A8index.html?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- www/{ => readme}/index.html | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename www/{ => readme}/index.html (100%) diff --git a/www/index.html b/www/readme/index.html similarity index 100% rename from www/index.html rename to www/readme/index.html From 71631a33c439aa1af3ed62ce3731f5e71320f505 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 28 Dec 2019 13:11:41 +0800 Subject: [PATCH 170/272] =?UTF-8?q?http=E6=9C=8D=E5=8A=A1=E5=99=A8?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=90=8E=E5=8F=B0=E7=BA=BF=E7=A8=8B=E8=AF=BB?= =?UTF-8?q?=E5=8F=96=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/HttpBody.h | 30 ++++++++- src/Http/HttpSession.cpp | 131 ++++++++++++++++++++++++--------------- src/Http/HttpSession.h | 2 +- 3 files changed, 110 insertions(+), 53 deletions(-) diff --git a/src/Http/HttpBody.h b/src/Http/HttpBody.h index a340c1b0..9428ab07 100644 --- a/src/Http/HttpBody.h +++ b/src/Http/HttpBody.h @@ -32,6 +32,7 @@ #include "Network/Buffer.h" #include "Util/ResourcePool.h" #include "Util/logger.h" +#include "Thread/WorkThreadPool.h" using namespace std; using namespace toolkit; @@ -45,10 +46,12 @@ namespace mediakit { /** * http content部分基类定义 */ -class HttpBody{ +class HttpBody : public std::enable_shared_from_this{ public: typedef std::shared_ptr Ptr; - HttpBody(){} + HttpBody(){ + _async_read_thread = WorkThreadPool::Instance().getPoller(); + } virtual ~HttpBody(){} /** @@ -62,6 +65,29 @@ public: * @return 字节对象,如果读完了,那么请返回nullptr */ virtual Buffer::Ptr readData(uint32_t size) { return nullptr;}; + + /** + * 异步请求读取一定字节数,返回大小可能小于size + * @param size 请求大小 + * @param cb 回调函数 + */ + virtual void readDataAsync(uint32_t size,const function &cb){ + if(size >= remainSize()){ + //假如剩余数据很小,那么同步获取(为了优化性能) + cb(readData(size)); + return; + } + //如果是大文件,那么后台读取 + weak_ptr weakSelf = shared_from_this(); + _async_read_thread->async([cb,size,weakSelf](){ + auto strongSelf = weakSelf.lock(); + if(strongSelf){ + cb(strongSelf->readData(size)); + } + }); + } +private: + EventPoller::Ptr _async_read_thread; }; /** diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp index 405a67ac..4b8daf0d 100644 --- a/src/Http/HttpSession.cpp +++ b/src/Http/HttpSession.cpp @@ -308,6 +308,79 @@ static string dateStr() { return buf; } +class AsyncSenderData { +public: + friend class AsyncSender; + typedef std::shared_ptr Ptr; + AsyncSenderData(const TcpSession::Ptr &session, const HttpBody::Ptr &body, bool close_when_complete) { + _session = dynamic_pointer_cast(session); + _body = body; + _close_when_complete = close_when_complete; + } + ~AsyncSenderData() = default; +private: + std::weak_ptr _session; + HttpBody::Ptr _body; + bool _close_when_complete; + bool _read_complete = false; +}; + +class AsyncSender { +public: + typedef std::shared_ptr Ptr; + static bool onSocketFlushed(const AsyncSenderData::Ptr &data) { + if (data->_read_complete) { + if (data->_close_when_complete) { + //发送完毕需要关闭socket + shutdown(data->_session.lock()); + } + return false; + } + + GET_CONFIG(uint32_t, sendBufSize, Http::kSendBufSize); + data->_body->readDataAsync(sendBufSize, [data](const Buffer::Ptr &sendBuf) { + auto session = data->_session.lock(); + if (!session) { + //本对象已经销毁 + return; + } + session->async([data, sendBuf]() { + auto session = data->_session.lock(); + if (!session) { + //本对象已经销毁 + return; + } + onRequestData(data, session, sendBuf); + }, false); + }); + return true; + } + +private: + static void onRequestData(const AsyncSenderData::Ptr &data, const std::shared_ptr &session, const Buffer::Ptr &sendBuf) { + session->_ticker.resetTime(); + if (sendBuf && session->send(sendBuf) != -1) { + //文件还未读完,还需要继续发送 + if (!session->isSocketBusy()) { + //socket还可写,继续请求数据 + onSocketFlushed(data); + } + return; + } + //文件写完了 + data->_read_complete = true; + if (!session->isSocketBusy() && data->_close_when_complete) { + shutdown(session); + } + } + + static void shutdown(const std::shared_ptr &session) { + if(session){ + session->shutdown(SockException(Err_shutdown, StrPrinter << "close connection after send http body completed.")); + } + } +}; + static const string kDate = "Date"; static const string kServer = "Server"; static const string kConnection = "Connection"; @@ -401,60 +474,18 @@ void HttpSession::sendResponse(const char *pcStatus, return; } - //发送http body - GET_CONFIG(uint32_t,sendBufSize,Http::kSendBufSize); - weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - - string status_code = pcStatus; - auto onFlush = [body,bClose,weakSelf,status_code]() { - auto strongSelf = weakSelf.lock(); - if(!strongSelf){ - //本对象已经销毁 - return false; - } - while(true){ - //更新超时计时器 - strongSelf->_ticker.resetTime(); - //读取文件 - auto sendBuf = body->readData(sendBufSize); - if (!sendBuf) { - //文件读完 - if(strongSelf->isSocketBusy() && bClose){ - //套接字忙,我们等待触发下一次onFlush事件 - //待所有数据flush到socket fd再移除onFlush事件监听 - //标记文件读写完毕 - return true; - } - //文件全部flush到socket fd,可以直接关闭socket了 - break; - } - - //文件还未读完 - if(strongSelf->send(sendBuf) == -1) { - //socket已经销毁,不再监听onFlush事件 - return false; - } - if(strongSelf->isSocketBusy()){ - //socket忙,那么停止继续写,等待下一次onFlush事件,然后再读文件写socket - return true; - } - //socket还可写,继续写socket - } - - if(bClose) { - //最后一次flush事件,文件也发送完毕了,可以关闭socket了 - strongSelf->shutdown(SockException(Err_shutdown, StrPrinter << "close connection after send http body completed with status code:" << status_code)); - } - //不再监听onFlush事件 - return false; - }; - + GET_CONFIG(uint32_t, sendBufSize, Http::kSendBufSize); if(body->remainSize() > sendBufSize){ //文件下载提升发送性能 setSocketFlags(); } - onFlush(); - _sock->setOnFlush(onFlush); + + //发送http body + AsyncSenderData::Ptr data = std::make_shared(shared_from_this(),body,bClose); + _sock->setOnFlush([data](){ + return AsyncSender::onSocketFlushed(data); + }); + AsyncSender::onSocketFlushed(data); } string HttpSession::urlDecode(const string &str){ diff --git a/src/Http/HttpSession.h b/src/Http/HttpSession.h index ec128c52..0d9ce18e 100644 --- a/src/Http/HttpSession.h +++ b/src/Http/HttpSession.h @@ -47,7 +47,7 @@ class HttpSession: public TcpSession, public: typedef StrCaseMap KeyValue; typedef HttpResponseInvokerImp HttpResponseInvoker; - + friend class AsyncSender; /** * @param errMsg 如果为空,则代表鉴权通过,否则为错误提示 * @param accessPath 运行或禁止访问的根目录 From 20d56b713f63cd284456f49b0ac57c65c885a095 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 28 Dec 2019 13:39:25 +0800 Subject: [PATCH 171/272] =?UTF-8?q?=E6=80=A7=E8=83=BD=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/source/mk_events_objects.cpp | 2 +- api/source/mk_httpclient.cpp | 2 +- src/Common/Parser.h | 21 ++++++++++----------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/api/source/mk_events_objects.cpp b/api/source/mk_events_objects.cpp index 69e9fa3a..f959668a 100644 --- a/api/source/mk_events_objects.cpp +++ b/api/source/mk_events_objects.cpp @@ -250,7 +250,7 @@ static C get_http_header( const char *response_header[]){ auto value = response_header[i + 1]; if (key && value) { i += 2; - header[key] = value; + header.emplace(key,value); continue; } break; diff --git a/api/source/mk_httpclient.cpp b/api/source/mk_httpclient.cpp index 0e32b472..e1262011 100755 --- a/api/source/mk_httpclient.cpp +++ b/api/source/mk_httpclient.cpp @@ -88,7 +88,7 @@ static C get_http_header( const char *response_header[]){ auto value = response_header[i + 1]; if (key && value) { i += 2; - header[key] = value; + header.emplace(key,value); continue; } break; diff --git a/src/Common/Parser.h b/src/Common/Parser.h index eaa1874d..bd15d0b8 100644 --- a/src/Common/Parser.h +++ b/src/Common/Parser.h @@ -28,27 +28,26 @@ class StrCaseMap : public multimap{ StrCaseMap() = default; ~StrCaseMap() = default; - template - string &operator[](K &&k){ - auto it = find(std::forward(k)); + string &operator[](const string &k){ + auto it = find(k); if(it == end()){ - it = Super::emplace(std::forward(k),""); + it = Super::emplace(k,""); } return it->second; } - template - void emplace(K &&k , V &&v) { - auto it = find(std::forward(k)); + template + void emplace(const string &k, V &&v) { + auto it = find(k); if(it != end()){ return; } - Super::emplace(std::forward(k),std::forward(v)); + Super::emplace(k,std::forward(v)); } - template - void emplace_force(K &&k , V &&v) { - Super::emplace(std::forward(k),std::forward(v)); + template + void emplace_force(const string k , V &&v) { + Super::emplace(k,std::forward(v)); } }; From 94806b2cd6509ea59c464c299e4d64fbc7ef1b53 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 28 Dec 2019 16:48:11 +0800 Subject: [PATCH 172/272] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=AE=9E=E7=8E=B0hls?= =?UTF-8?q?=E6=92=AD=E6=94=BE=E5=99=A8=E4=B8=AA=E6=95=B0=E8=AE=A1=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 8 +++++++- server/FFmpegSource.cpp | 8 ++++++++ server/FFmpegSource.h | 2 ++ src/Common/MediaSource.cpp | 7 +++++++ src/Common/MediaSource.h | 7 ++++++- src/Common/MultiMediaSourceMuxer.h | 9 +++++++-- src/Http/HttpCookieManager.cpp | 4 ++-- src/Http/HttpCookieManager.h | 6 ++---- src/Http/HttpFileManager.cpp | 22 ++++++++++++++++++---- src/Player/PlayerProxy.cpp | 16 ++++++++++------ src/Player/PlayerProxy.h | 13 +++++-------- src/Record/MP4Reader.cpp | 10 +++++++--- src/Record/MP4Reader.h | 17 ++++------------- src/Rtmp/RtmpMediaSourceImp.h | 10 +++++----- src/Rtmp/RtmpSession.cpp | 8 ++++++-- src/Rtmp/RtmpSession.h | 5 ++++- src/Rtsp/RtspMediaSourceImp.h | 8 ++++---- src/Rtsp/RtspSession.cpp | 7 +++++-- src/Rtsp/RtspSession.h | 3 ++- 19 files changed, 111 insertions(+), 59 deletions(-) diff --git a/.gitignore b/.gitignore index 853ad8f7..3496b697 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,12 @@ *.DS_Store /cmake-build-debug/ +/cmake-build-release/ +/linux/ +/.vs/ /.idea/ /c_wrapper/.idea/ -/release/mac/Debug/ \ No newline at end of file +/release/ +/Android/.idea/ +/Android/app/src/main/cpp/libs_export/ +/3rdpart/media-server/.idea/ diff --git a/server/FFmpegSource.cpp b/server/FFmpegSource.cpp index 12c8a072..2ea621a8 100644 --- a/server/FFmpegSource.cpp +++ b/server/FFmpegSource.cpp @@ -232,6 +232,14 @@ void FFmpegSource::onNoneReader(MediaSource &sender) { } } +int FFmpegSource::totalReaderCount(MediaSource &sender) { + auto listener = _listener.lock(); + if(listener){ + return listener->totalReaderCount(sender); + } + return 0; +} + void FFmpegSource::onGetMediaSource(const MediaSource::Ptr &src) { _listener = src->getListener(); src->setListener(shared_from_this()); diff --git a/server/FFmpegSource.h b/server/FFmpegSource.h index 5cf2c4f7..f71091da 100644 --- a/server/FFmpegSource.h +++ b/server/FFmpegSource.h @@ -57,8 +57,10 @@ private: void startTimer(int timeout_ms); void onGetMediaSource(const MediaSource::Ptr &src); + //MediaSourceEvent override bool close(MediaSource &sender,bool force) override; void onNoneReader(MediaSource &sender) override ; + int totalReaderCount(MediaSource &sender) override; private: Process _process; Timer::Ptr _timer; diff --git a/src/Common/MediaSource.cpp b/src/Common/MediaSource.cpp index d1e8409b..f5ae9213 100644 --- a/src/Common/MediaSource.cpp +++ b/src/Common/MediaSource.cpp @@ -98,6 +98,13 @@ const std::weak_ptr& MediaSource::getListener() const{ return _listener; } +int MediaSource::totalReaderCount(){ + auto listener = _listener.lock(); + if(!listener){ + return readerCount(); + } + return listener->totalReaderCount(*this); +} bool MediaSource::seekTo(uint32_t ui32Stamp) { auto listener = _listener.lock(); if(!listener){ diff --git a/src/Common/MediaSource.h b/src/Common/MediaSource.h index 69b77ccc..03b86dbb 100644 --- a/src/Common/MediaSource.h +++ b/src/Common/MediaSource.h @@ -67,6 +67,9 @@ public: // 通知无人观看 virtual void onNoneReader(MediaSource &sender); + + // 观看总人数 + virtual int totalReaderCount(MediaSource &sender) = 0; }; class MediaInfo{ @@ -124,8 +127,10 @@ public: void setTrackSource(const std::weak_ptr &track_src); // 设置监听者 virtual void setListener(const std::weak_ptr &listener); - // 获取观看者个数 + // 本协议获取观看者个数,可能返回本协议的观看人数,也可能返回总人数 virtual int readerCount() = 0; + // 观看者个数,包括(hls/rtsp/rtmp) + virtual int totalReaderCount(); // 获取流当前时间戳 virtual uint32_t getTimeStamp(TrackType trackType) = 0; diff --git a/src/Common/MultiMediaSourceMuxer.h b/src/Common/MultiMediaSourceMuxer.h index 10f63eae..2e189174 100644 --- a/src/Common/MultiMediaSourceMuxer.h +++ b/src/Common/MultiMediaSourceMuxer.h @@ -30,6 +30,7 @@ #include "Rtsp/RtspMediaSourceMuxer.h" #include "Rtmp/RtmpMediaSourceMuxer.h" #include "Record/Recorder.h" +#include "Record/HlsManager.h" class MultiMediaSourceMuxer : public MediaSink , public std::enable_shared_from_this{ public: @@ -64,6 +65,9 @@ public: Recorder::startRecord(Recorder::type_mp4,vhost, strApp, strId, true, false); } + _get_hls_player = [vhost,strApp,strId](){ + return HlsManager::Instance().hlsPlayerCount(vhost,strApp,strId); + }; } virtual ~MultiMediaSourceMuxer(){} @@ -96,8 +100,8 @@ public: * 返回总的消费者个数 * @return */ - int readerCount() const{ - return (_rtsp ? _rtsp->readerCount() : 0) + (_rtmp ? _rtmp->readerCount() : 0); + int totalReaderCount() const{ + return (_rtsp ? _rtsp->readerCount() : 0) + (_rtmp ? _rtmp->readerCount() : 0) + _get_hls_player(); } void setTimeStamp(uint32_t stamp){ @@ -157,6 +161,7 @@ private: RtmpMediaSourceMuxer::Ptr _rtmp; RtspMediaSourceMuxer::Ptr _rtsp; Listener *_listener = nullptr; + function _get_hls_player; }; diff --git a/src/Http/HttpCookieManager.cpp b/src/Http/HttpCookieManager.cpp index 8bd5da1b..0c210dab 100644 --- a/src/Http/HttpCookieManager.cpp +++ b/src/Http/HttpCookieManager.cpp @@ -76,8 +76,8 @@ bool HttpServerCookie::isExpired() { return _ticker.elapsedTime() > _max_elapsed * 1000; } -std::shared_ptr > HttpServerCookie::getLock(){ - return std::make_shared >(_mtx); +std::shared_ptr > HttpServerCookie::getLock(){ + return std::make_shared >(_mtx); } string HttpServerCookie::cookieExpireTime() const{ diff --git a/src/Http/HttpCookieManager.h b/src/Http/HttpCookieManager.h index 4d67e386..73f7afae 100644 --- a/src/Http/HttpCookieManager.h +++ b/src/Http/HttpCookieManager.h @@ -108,9 +108,7 @@ public: * 获取区域锁 * @return */ - std::shared_ptr > getLock(); - - + std::shared_ptr > getLock(); private: string cookieExpireTime() const ; private: @@ -119,7 +117,7 @@ private: string _cookie_uuid; uint64_t _max_elapsed; Ticker _ticker; - mutex _mtx; + recursive_mutex _mtx; std::weak_ptr _manager; }; diff --git a/src/Http/HttpFileManager.cpp b/src/Http/HttpFileManager.cpp index 70fd75df..c35b0729 100644 --- a/src/Http/HttpFileManager.cpp +++ b/src/Http/HttpFileManager.cpp @@ -32,6 +32,7 @@ #include "HttpFileManager.h" #include "Util/File.h" #include "HttpSession.h" +#include "Record/HlsManager.h" namespace mediakit { @@ -44,6 +45,7 @@ static const string kCookiePathKey = "kCookiePathKey"; static const string kAccessErrKey = "kAccessErrKey"; static const string kAccessHls = "kAccessHls"; static const string kHlsSuffix = "/hls.m3u8"; +static const string kHlsData = "kHlsData"; static const string &getContentType(const char *name) { const char *dot; @@ -284,9 +286,8 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI bool is_hls = end_of(path,kHlsSuffix); //该用户从来未获取过cookie,这个时候我们广播是否允许该用户访问该http目录 - HttpSession::HttpAccessPathInvoker accessPathInvoker = [callback, uid, path, is_dir, is_hls]( const string &errMsg, - const string &cookie_path_in, - int cookieLifeSecond) { + HttpSession::HttpAccessPathInvoker accessPathInvoker = [callback, uid, path, is_dir, is_hls, mediaInfo] + (const string &errMsg, const string &cookie_path_in, int cookieLifeSecond) { HttpServerCookie::Ptr cookie; if (cookieLifeSecond) { //本次鉴权设置了有效期,我们把鉴权结果缓存在cookie中 @@ -305,8 +306,15 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI (*cookie)[kAccessErrKey].set(errMsg); //记录访问的是否为hls (*cookie)[kAccessHls].set(is_hls); + if(is_hls){ + //hls相关信息 + replace(const_cast(mediaInfo._streamid),kHlsSuffix,""); + (*cookie)[kHlsData].set(mediaInfo); + } + callback(errMsg, cookie); + }else{ + callback(errMsg, nullptr); } - callback(errMsg, cookie); }; if (is_hls && emitHlsPlayed(parser, mediaInfo, path, is_dir, accessPathInvoker, sender)) { @@ -369,6 +377,12 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo httpHeader["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); } HttpSession::HttpResponseInvoker invoker = [&](const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body) { + if(cookie){ + auto is_hls = (*cookie)[kAccessHls].get(); + if(is_hls){ + (*cookie)[kHlsData].get().addByteUsage(body->remainSize()); + } + } cb(codeOut.data(), getContentType(strFile.data()), headerOut, body); }; invoker.responseFile(parser.getValues(), httpHeader, strFile); diff --git a/src/Player/PlayerProxy.cpp b/src/Player/PlayerProxy.cpp index 656c9230..5badcce0 100644 --- a/src/Player/PlayerProxy.cpp +++ b/src/Player/PlayerProxy.cpp @@ -174,12 +174,8 @@ void PlayerProxy::rePlay(const string &strUrl,int iFailedCnt){ }, getPoller()); } -int PlayerProxy::readerCount(){ - return (_mediaMuxer ? _mediaMuxer->readerCount() : 0) + (_pMediaSrc ? _pMediaSrc->readerCount() : 0); -} - bool PlayerProxy::close(MediaSource &sender,bool force) { - if(!force && readerCount() != 0){ + if(!force && totalReaderCount()){ return false; } @@ -201,12 +197,20 @@ bool PlayerProxy::close(MediaSource &sender,bool force) { } void PlayerProxy::onNoneReader(MediaSource &sender) { - if(!_mediaMuxer || _mediaMuxer->readerCount() != 0){ + if(!_mediaMuxer || totalReaderCount()){ return; } MediaSourceEvent::onNoneReader(sender); } +int PlayerProxy::totalReaderCount(){ + return (_mediaMuxer ? _mediaMuxer->totalReaderCount() : 0) + (_pMediaSrc ? _pMediaSrc->readerCount() : 0); +} + +int PlayerProxy::totalReaderCount(MediaSource &sender) { + return totalReaderCount(); +} + class MuteAudioMaker : public FrameDispatcher{ public: typedef std::shared_ptr Ptr; diff --git a/src/Player/PlayerProxy.h b/src/Player/PlayerProxy.h index aff0e3d4..e4ed9807 100644 --- a/src/Player/PlayerProxy.h +++ b/src/Player/PlayerProxy.h @@ -75,18 +75,15 @@ public: * @param strUrl */ void play(const string &strUrl) override; - - - /** - * 被主动关闭 - * @return - */ - bool close(MediaSource &sender,bool force) override; private: + //MediaSourceEvent override + bool close(MediaSource &sender,bool force) override; void onNoneReader(MediaSource &sender) override; + int totalReaderCount(MediaSource &sender) override; + int totalReaderCount() ; + void rePlay(const string &strUrl,int iFailedCnt); void onPlaySuccess(); - int readerCount() ; private: bool _bEnableRtsp; bool _bEnableRtmp; diff --git a/src/Record/MP4Reader.cpp b/src/Record/MP4Reader.cpp index b082234d..9bf20525 100644 --- a/src/Record/MP4Reader.cpp +++ b/src/Record/MP4Reader.cpp @@ -179,7 +179,7 @@ void MP4Reader::startReadMP4() { return true; } bool MP4Reader::close(MediaSource &sender,bool force){ - if(!_mediaMuxer || (!force && _mediaMuxer->readerCount() != 0)){ + if(!_mediaMuxer || (!force && _mediaMuxer->totalReaderCount())){ return false; } _timer.reset(); @@ -188,18 +188,22 @@ bool MP4Reader::close(MediaSource &sender,bool force){ } void MP4Reader::onNoneReader(MediaSource &sender) { - if(!_mediaMuxer || _mediaMuxer->readerCount() != 0){ + if(!_mediaMuxer || _mediaMuxer->totalReaderCount()){ return; } MediaSourceEvent::onNoneReader(sender); } +int MP4Reader::totalReaderCount(MediaSource &sender) { + return _mediaMuxer ? _mediaMuxer->totalReaderCount() : sender.readerCount(); +} + bool MP4Reader::readSample(int iTimeInc,bool justSeekSyncFrame) { TimeTicker(); lock_guard lck(_mtx); auto bFlag0 = readVideoSample(iTimeInc,justSeekSyncFrame);//数据没读完 auto bFlag1 = readAudioSample(iTimeInc,justSeekSyncFrame);//数据没读完 - auto bFlag2 = _mediaMuxer->readerCount() > 0;//读取者大于0 + auto bFlag2 = _mediaMuxer->totalReaderCount() > 0;//读取者大于0 if((bFlag0 || bFlag1) && bFlag2){ _alive.resetTime(); } diff --git a/src/Record/MP4Reader.h b/src/Record/MP4Reader.h index 576d0ed2..30487c41 100644 --- a/src/Record/MP4Reader.h +++ b/src/Record/MP4Reader.h @@ -56,19 +56,6 @@ public: */ void startReadMP4(); - /** - * 设置时移偏移量 - * @param ui32Stamp 偏移量,单位毫秒 - * @return - */ - bool seekTo(MediaSource &sender,uint32_t ui32Stamp) override; - - /** - * 关闭MP4Reader的流化进程,会触发该对象放弃自持有 - * @return - */ - bool close(MediaSource &sender,bool force) override; - /** * 自动生成MP4Reader对象然后查找相关的MediaSource对象 * @param strSchema 协议名 @@ -87,7 +74,11 @@ public: bool checkApp = true); private: + //MediaSourceEvent override + bool seekTo(MediaSource &sender,uint32_t ui32Stamp) override; + bool close(MediaSource &sender,bool force) override; void onNoneReader(MediaSource &sender) override; + int totalReaderCount(MediaSource &sender) override; #ifdef ENABLE_MP4V2 void seek(uint32_t iSeekTime,bool bReStart = true); inline void setSeekTime(uint32_t iSeekTime); diff --git a/src/Rtmp/RtmpMediaSourceImp.h b/src/Rtmp/RtmpMediaSourceImp.h index d6616ed4..482ad5aa 100644 --- a/src/Rtmp/RtmpMediaSourceImp.h +++ b/src/Rtmp/RtmpMediaSourceImp.h @@ -88,11 +88,11 @@ public: } } - /** - * 播放器总数 - */ - int readerCount() override { - return RtmpMediaSource::readerCount() + (_muxer ? _muxer->readerCount() : 0); + /** + * 获取观看总人数,包括(hls/rtsp/rtmp) + */ + int totalReaderCount() override{ + return readerCount() + (_muxer ? _muxer->totalReaderCount() : 0); } /** diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index e28c0d80..9db2c175 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -532,7 +532,7 @@ void RtmpSession::onSendMedia(const RtmpPacket::Ptr &pkt) { bool RtmpSession::close(MediaSource &sender,bool force) { //此回调在其他线程触发 - if(!_pPublisherSrc || (!force && _pPublisherSrc->readerCount() != 0)){ + if(!_pPublisherSrc || (!force && _pPublisherSrc->totalReaderCount())){ return false; } string err = StrPrinter << "close media:" << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId() << " " << force; @@ -542,12 +542,16 @@ bool RtmpSession::close(MediaSource &sender,bool force) { void RtmpSession::onNoneReader(MediaSource &sender) { //此回调在其他线程触发 - if(!_pPublisherSrc || _pPublisherSrc->readerCount() != 0){ + if(!_pPublisherSrc || _pPublisherSrc->totalReaderCount()){ return; } MediaSourceEvent::onNoneReader(sender); } +int RtmpSession::totalReaderCount(MediaSource &sender) { + return _pPublisherSrc ? _pPublisherSrc->totalReaderCount() : sender.readerCount(); +} + void RtmpSession::setSocketFlags(){ GET_CONFIG(bool,ultraLowDelay,General::kUltraLowDelay); if(!ultraLowDelay) { diff --git a/src/Rtmp/RtmpSession.h b/src/Rtmp/RtmpSession.h index 437f4910..572d40f8 100644 --- a/src/Rtmp/RtmpSession.h +++ b/src/Rtmp/RtmpSession.h @@ -83,8 +83,11 @@ private: sendResponse(MSG_CMD, invoke.data()); } - bool close(MediaSource &sender,bool force) override ; + //MediaSourceEvent override + bool close(MediaSource &sender,bool force) override ; void onNoneReader(MediaSource &sender) override; + int totalReaderCount(MediaSource &sender) override; + void setSocketFlags(); string getStreamId(const string &str); void dumpMetadata(const AMFValue &metadata); diff --git a/src/Rtsp/RtspMediaSourceImp.h b/src/Rtsp/RtspMediaSourceImp.h index 4f157f66..e5f78121 100644 --- a/src/Rtsp/RtspMediaSourceImp.h +++ b/src/Rtsp/RtspMediaSourceImp.h @@ -80,10 +80,10 @@ public: } /** - * 播放器总数 - */ - int readerCount() override { - return RtspMediaSource::readerCount() + (_muxer ? _muxer->readerCount() : 0); + * 获取观看总人数,包括(hls/rtsp/rtmp) + */ + int totalReaderCount() override{ + return readerCount() + (_muxer ? _muxer->totalReaderCount() : 0); } /** diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index 804fea82..ef61f3e3 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -1136,7 +1136,7 @@ inline int RtspSession::getTrackIndexByInterleaved(int interleaved){ bool RtspSession::close(MediaSource &sender,bool force) { //此回调在其他线程触发 - if(!_pushSrc || (!force && _pushSrc->readerCount() != 0)){ + if(!_pushSrc || (!force && _pushSrc->totalReaderCount())){ return false; } string err = StrPrinter << "close media:" << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId() << " " << force; @@ -1147,12 +1147,15 @@ bool RtspSession::close(MediaSource &sender,bool force) { void RtspSession::onNoneReader(MediaSource &sender){ //此回调在其他线程触发 - if(!_pushSrc || _pushSrc->readerCount() != 0){ + if(!_pushSrc || _pushSrc->totalReaderCount()){ return; } MediaSourceEvent::onNoneReader(sender); } +int RtspSession::totalReaderCount(MediaSource &sender) { + return _pushSrc ? _pushSrc->totalReaderCount() : sender.readerCount(); +} void RtspSession::sendRtpPacket(const RtpPacket::Ptr & pkt) { //InfoP(this) <<(int)pkt.Interleaved; diff --git a/src/Rtsp/RtspSession.h b/src/Rtsp/RtspSession.h index 9c586e76..6a77dfc5 100644 --- a/src/Rtsp/RtspSession.h +++ b/src/Rtsp/RtspSession.h @@ -108,8 +108,9 @@ protected: //MediaSourceEvent override bool close(MediaSource &sender,bool force) override ; void onNoneReader(MediaSource &sender) override; + int totalReaderCount(MediaSource &sender) override; - //TcpSession override + //TcpSession override int send(const Buffer::Ptr &pkt) override; /** From f93b32740f14c091a0db32553a9006cb85bad99c Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 28 Dec 2019 16:48:42 +0800 Subject: [PATCH 173/272] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E5=AE=9E=E7=8E=B0hls?= =?UTF-8?q?=E6=92=AD=E6=94=BE=E5=99=A8=E4=B8=AA=E6=95=B0=E8=AE=A1=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/HlsManager.cpp | 101 ++++++++++++++++++++++++++++++++++++++ src/Record/HlsManager.h | 85 ++++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 src/Record/HlsManager.cpp create mode 100644 src/Record/HlsManager.h diff --git a/src/Record/HlsManager.cpp b/src/Record/HlsManager.cpp new file mode 100644 index 00000000..72fad860 --- /dev/null +++ b/src/Record/HlsManager.cpp @@ -0,0 +1,101 @@ +/* + * MIT License + * + * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "HlsManager.h" +#include "Util/util.h" +using namespace toolkit; + +namespace mediakit{ + +HlsCookieData::HlsCookieData(const MediaInfo &info) { + _info = info; + _manager = HlsManager::Instance().shared_from_this(); + _manager->onAddHlsPlayer(_info); +} + +HlsCookieData::~HlsCookieData() { + _manager->onAddHlsPlayer(_info); +} + +void HlsCookieData::addByteUsage(uint64_t bytes) { + _bytes += bytes; +} + +//////////////////////////////////////////////////////// +HlsManager::HlsManager() {} +HlsManager::~HlsManager() {} +INSTANCE_IMP(HlsManager); + +void HlsManager::onAddHlsPlayer(const MediaInfo &info) { + lock_guard lck(_mtx); + ++_player_counter[info._vhost][info._app][info._streamid]._count; +} + +void HlsManager::onDelHlsPlayer(const MediaInfo &info) { + lock_guard lck(_mtx); + auto it0 = _player_counter.find(info._vhost); + if(it0 == _player_counter.end()){ + return; + } + auto it1 = it0->second.find(info._app); + if(it1 == it0->second.end()){ + return; + } + auto it2 = it1->second.find(info._streamid); + if(it2 == it1->second.end()){ + return; + } + if(--(it2->second._count) == 0){ + it1->second.erase(it2); + if(it1->second.empty()){ + it0->second.erase(it1); + if(it0->second.empty()){ + _player_counter.erase(it0); + } + } + } +} + +int HlsManager::hlsPlayerCount(const string &vhost, const string &app, const string &stream) { + lock_guard lck(_mtx); + auto it0 = _player_counter.find(vhost); + if(it0 == _player_counter.end()){ + return 0; + } + auto it1 = it0->second.find(app); + if(it1 == it0->second.end()){ + return 0; + } + auto it2 = it1->second.find(stream); + if(it2 == it1->second.end()){ + return 0; + } + return it2->second._count; +} + + +}//namespace mediakit + diff --git a/src/Record/HlsManager.h b/src/Record/HlsManager.h new file mode 100644 index 00000000..059c1777 --- /dev/null +++ b/src/Record/HlsManager.h @@ -0,0 +1,85 @@ +/* + * MIT License + * + * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#ifndef ZLMEDIAKIT_HLSMANAGER_H +#define ZLMEDIAKIT_HLSMANAGER_H + +#include +#include +#include +#include "Common/MediaSource.h" +using namespace std; + +namespace mediakit{ + +class HlsManager; +class HlsCookieData{ +public: + HlsCookieData(const MediaInfo &info); + ~HlsCookieData(); + void addByteUsage(uint64_t bytes); +private: + uint64_t _bytes = 0; + MediaInfo _info; + std::shared_ptr _manager; +}; + + +class HlsManager : public std::enable_shared_from_this{ +public: + friend class HlsCookieData; + ~HlsManager(); + static HlsManager& Instance(); + + /** + * 获取hls播放器个数 + * @param vhost 虚拟主机 + * @param app 应用名 + * @param stream 流id + * @return 播放器个数 + */ + int hlsPlayerCount(const string &vhost,const string &app,const string &stream); +private: + void onAddHlsPlayer(const MediaInfo &info); + void onDelHlsPlayer(const MediaInfo &info); + HlsManager(); +private: + class HlsPlayerCounter{ + private: + friend class HlsManager; + int _count = 0; + }; +private: + recursive_mutex _mtx; + unordered_map > > _player_counter; + + +}; + +}//namespace mediakit +#endif //ZLMEDIAKIT_HLSMANAGER_H From e72fa359b638b12f683b75d48e7769a4771e527e Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 28 Dec 2019 16:57:35 +0800 Subject: [PATCH 174/272] =?UTF-8?q?=E5=AE=8C=E5=96=84hls=E6=92=AD=E6=94=BE?= =?UTF-8?q?=E5=99=A8=E4=B8=AA=E6=95=B0=E8=AE=A1=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mk_events_objects.h | 2 ++ api/source/mk_events_objects.cpp | 7 +++++++ server/WebApi.cpp | 2 ++ src/Rtmp/RtmpMediaSource.h | 2 +- src/Rtmp/RtmpSession.cpp | 2 +- src/Rtsp/RtspMediaSource.h | 2 +- src/Rtsp/RtspSession.cpp | 2 +- 7 files changed, 15 insertions(+), 4 deletions(-) diff --git a/api/include/mk_events_objects.h b/api/include/mk_events_objects.h index ee456bf5..30b4cb7c 100644 --- a/api/include/mk_events_objects.h +++ b/api/include/mk_events_objects.h @@ -109,6 +109,8 @@ API_EXPORT const char* API_CALL mk_media_source_get_app(const mk_media_source ct API_EXPORT const char* API_CALL mk_media_source_get_stream(const mk_media_source ctx); //MediaSource::readerCount() API_EXPORT int API_CALL mk_media_source_get_reader_count(const mk_media_source ctx); +//MediaSource::totalReaderCount() +API_EXPORT int API_CALL mk_media_source_get_total_reader_count(const mk_media_source ctx); //MediaSource::close() API_EXPORT int API_CALL mk_media_source_close(const mk_media_source ctx,int force); //MediaSource::seekTo() diff --git a/api/source/mk_events_objects.cpp b/api/source/mk_events_objects.cpp index f959668a..cc341cc8 100644 --- a/api/source/mk_events_objects.cpp +++ b/api/source/mk_events_objects.cpp @@ -199,6 +199,13 @@ API_EXPORT int API_CALL mk_media_source_get_reader_count(const mk_media_source c MediaSource *src = (MediaSource *)ctx; return src->readerCount(); } + +API_EXPORT int API_CALL mk_media_source_get_total_reader_count(const mk_media_source ctx){ + assert(ctx); + MediaSource *src = (MediaSource *)ctx; + return src->totalReaderCount(); +} + API_EXPORT int API_CALL mk_media_source_close(const mk_media_source ctx,int force){ assert(ctx); MediaSource *src = (MediaSource *)ctx; diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 7b48493d..0ce721b6 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -412,6 +412,7 @@ void installWebApi() { item["app"] = media->getApp(); item["stream"] = media->getId(); item["readerCount"] = media->readerCount(); + item["totalReaderCount"] = media->totalReaderCount(); for(auto &track : media->getTracks()){ Value obj; obj["codec_id"] = track->getCodecId(); @@ -441,6 +442,7 @@ void installWebApi() { } val["online"] = true; val["readerCount"] = src->readerCount(); + val["totalReaderCount"] = src->totalReaderCount(); for(auto &track : src->getTracks()){ Value obj; obj["codec_id"] = track->getCodecId(); diff --git a/src/Rtmp/RtmpMediaSource.h b/src/Rtmp/RtmpMediaSource.h index 12f8b10c..982d4401 100644 --- a/src/Rtmp/RtmpMediaSource.h +++ b/src/Rtmp/RtmpMediaSource.h @@ -175,7 +175,7 @@ private: void onReaderChanged(int size) { //我们记录最后一次活动时间 _reader_changed_ticker.resetTime(); - if (size != 0 || readerCount() != 0) { + if (size != 0 || totalReaderCount() != 0) { //还有消费者正在观看该流 _async_emit_none_reader = false; return; diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index 9db2c175..42518016 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -302,7 +302,7 @@ void RtmpSession::sendPlayResponse(const string &err,const RtmpMediaSource::Ptr strongSelf->shutdown(SockException(Err_shutdown,"rtmp ring buffer detached")); }); _pPlayerSrc = src; - if (src->readerCount() == 1) { + if (src->totalReaderCount() == 1) { src->seekTo(0); } //提高服务器发送性能 diff --git a/src/Rtsp/RtspMediaSource.h b/src/Rtsp/RtspMediaSource.h index b63b7be8..f4354ae4 100644 --- a/src/Rtsp/RtspMediaSource.h +++ b/src/Rtsp/RtspMediaSource.h @@ -195,7 +195,7 @@ private: void onReaderChanged(int size) { //我们记录最后一次活动时间 _reader_changed_ticker.resetTime(); - if (size != 0 || readerCount() != 0) { + if (size != 0 || totalReaderCount() != 0) { //还有消费者正在观看该流 _async_emit_none_reader = false; return; diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index ef61f3e3..7d874cd8 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -773,7 +773,7 @@ void RtspSession::handleReq_Play(const Parser &parser) { auto iStartTime = 1000 * atof(strStart.data()); InfoP(this) << "rtsp seekTo(ms):" << iStartTime; useBuf = !pMediaSrc->seekTo(iStartTime); - }else if(pMediaSrc->readerCount() == 0){ + }else if(pMediaSrc->totalReaderCount() == 0){ //第一个消费者 pMediaSrc->seekTo(0); } From 1afacdcff85a8a75ac6ccdc9491b783a5e5c6181 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 28 Dec 2019 18:50:56 +0800 Subject: [PATCH 175/272] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E6=B7=BB=E5=8A=A0Hls?= =?UTF-8?q?MediaSource?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/MediaSource.cpp | 31 ++---- src/Common/MediaSource.h | 2 +- src/Common/MultiMediaSourceMuxer.h | 5 +- src/Common/config.h | 1 + src/Http/HttpFileManager.cpp | 2 +- src/Record/HlsMakerImp.cpp | 7 ++ src/Record/HlsMakerImp.h | 10 ++ src/Record/HlsManager.cpp | 101 ------------------ src/Record/HlsMediaSource.cpp | 52 +++++++++ src/Record/{HlsManager.h => HlsMediaSource.h} | 85 ++++++++------- src/Record/HlsRecorder.h | 3 + src/Record/Recorder.cpp | 4 +- 12 files changed, 135 insertions(+), 168 deletions(-) delete mode 100644 src/Record/HlsManager.cpp create mode 100644 src/Record/HlsMediaSource.cpp rename src/Record/{HlsManager.h => HlsMediaSource.h} (59%) diff --git a/src/Common/MediaSource.cpp b/src/Common/MediaSource.cpp index f5ae9213..ff0b6094 100644 --- a/src/Common/MediaSource.cpp +++ b/src/Common/MediaSource.cpp @@ -39,9 +39,7 @@ recursive_mutex MediaSource::g_mtxMediaSrc; MediaSource::SchemaVhostAppStreamMap MediaSource::g_mapMediaSrc; MediaSource::MediaSource(const string &strSchema, const string &strVhost, const string &strApp, const string &strId) : - _strSchema(strSchema), - _strApp(strApp), - _strId(strId) { + _strSchema(strSchema), _strApp(strApp), _strId(strId) { if (strVhost.empty()) { _strVhost = DEFAULT_VHOST; } else { @@ -146,12 +144,7 @@ void MediaSource::for_each_media(const function -static bool searchMedia(MAP &map, - const string &schema, - const string &vhost, - const string &app, - const string &id, - FUNC &&func) { +static bool searchMedia(MAP &map, const string &schema, const string &vhost, const string &app, const string &id, FUNC &&func) { auto it0 = map.find(schema); if (it0 == map.end()) { //未找到协议 @@ -188,16 +181,9 @@ static void eraseIfEmpty(MAP &map, IT0 it0, IT1 it1, IT2 it2) { } }; -void findAsync_l(const MediaInfo &info, - const std::shared_ptr &session, - bool retry, - const function &cb){ - - auto src = MediaSource::find(info._schema, - info._vhost, - info._app, - info._streamid, - true); +void findAsync_l(const MediaInfo &info, const std::shared_ptr &session, bool retry, + const function &cb){ + auto src = MediaSource::find(info._schema, info._vhost, info._app, info._streamid, true); if(src || !retry){ cb(src); return; @@ -263,12 +249,7 @@ void MediaSource::findAsync(const MediaInfo &info, const std::shared_ptr{ public: @@ -66,7 +66,8 @@ public: } _get_hls_player = [vhost,strApp,strId](){ - return HlsManager::Instance().hlsPlayerCount(vhost,strApp,strId); + auto src = MediaSource::find(HLS_SCHEMA,vhost,strApp,strId); + return src ? src->readerCount() : 0; }; } virtual ~MultiMediaSourceMuxer(){} diff --git a/src/Common/config.h b/src/Common/config.h index 53910dfc..cf22c3f0 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -62,6 +62,7 @@ bool loadIniConfig(const char *ini_path = nullptr); #define HTTP_SCHEMA "http" #define RTSP_SCHEMA "rtsp" #define RTMP_SCHEMA "rtmp" +#define HLS_SCHEMA "hls" #define DEFAULT_VHOST "__defaultVhost__" ////////////广播名称/////////// diff --git a/src/Http/HttpFileManager.cpp b/src/Http/HttpFileManager.cpp index c35b0729..5e39823c 100644 --- a/src/Http/HttpFileManager.cpp +++ b/src/Http/HttpFileManager.cpp @@ -32,7 +32,7 @@ #include "HttpFileManager.h" #include "Util/File.h" #include "HttpSession.h" -#include "Record/HlsManager.h" +#include "Record/HlsMediaSource.h" namespace mediakit { diff --git a/src/Record/HlsMakerImp.cpp b/src/Record/HlsMakerImp.cpp index cfb42ed6..2c7825bb 100644 --- a/src/Record/HlsMakerImp.cpp +++ b/src/Record/HlsMakerImp.cpp @@ -95,6 +95,9 @@ void HlsMakerImp::onWriteHls(const char *data, int len) { if(hls){ fwrite(data,len,1,hls.get()); hls.reset(); + if(_media_src){ + _media_src->registHls(); + } } else{ WarnL << "create hls file falied," << _path_hls << " " << get_uv_errmsg(); } @@ -115,4 +118,8 @@ std::shared_ptr HlsMakerImp::makeFile(const string &file,bool setbuf) { return ret; } +void HlsMakerImp::setMediaInfo(const string &vhost, const string &app, const string &stream_id) { + _media_src = std::make_shared(vhost, app, stream_id); +} + }//namespace mediakit \ No newline at end of file diff --git a/src/Record/HlsMakerImp.h b/src/Record/HlsMakerImp.h index c9683510..3a267243 100644 --- a/src/Record/HlsMakerImp.h +++ b/src/Record/HlsMakerImp.h @@ -31,6 +31,7 @@ #include #include #include "HlsMaker.h" +#include "HlsMediaSource.h" using namespace std; namespace mediakit { @@ -43,6 +44,14 @@ public: float seg_duration = 5, uint32_t seg_number = 3); virtual ~HlsMakerImp(); + + /** + * 设置媒体信息 + * @param vhost 虚拟主机 + * @param app 应用名 + * @param stream_id 流id + */ + void setMediaInfo(const string &vhost, const string &app, const string &stream_id); protected: string onOpenSegment(int index) override ; void onDelSegment(int index) override; @@ -51,6 +60,7 @@ protected: private: std::shared_ptr makeFile(const string &file,bool setbuf = false); private: + HlsMediaSource::Ptr _media_src; map _segment_file_paths; std::shared_ptr _file; std::shared_ptr _file_buf; diff --git a/src/Record/HlsManager.cpp b/src/Record/HlsManager.cpp deleted file mode 100644 index 72fad860..00000000 --- a/src/Record/HlsManager.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> - * - * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "HlsManager.h" -#include "Util/util.h" -using namespace toolkit; - -namespace mediakit{ - -HlsCookieData::HlsCookieData(const MediaInfo &info) { - _info = info; - _manager = HlsManager::Instance().shared_from_this(); - _manager->onAddHlsPlayer(_info); -} - -HlsCookieData::~HlsCookieData() { - _manager->onAddHlsPlayer(_info); -} - -void HlsCookieData::addByteUsage(uint64_t bytes) { - _bytes += bytes; -} - -//////////////////////////////////////////////////////// -HlsManager::HlsManager() {} -HlsManager::~HlsManager() {} -INSTANCE_IMP(HlsManager); - -void HlsManager::onAddHlsPlayer(const MediaInfo &info) { - lock_guard lck(_mtx); - ++_player_counter[info._vhost][info._app][info._streamid]._count; -} - -void HlsManager::onDelHlsPlayer(const MediaInfo &info) { - lock_guard lck(_mtx); - auto it0 = _player_counter.find(info._vhost); - if(it0 == _player_counter.end()){ - return; - } - auto it1 = it0->second.find(info._app); - if(it1 == it0->second.end()){ - return; - } - auto it2 = it1->second.find(info._streamid); - if(it2 == it1->second.end()){ - return; - } - if(--(it2->second._count) == 0){ - it1->second.erase(it2); - if(it1->second.empty()){ - it0->second.erase(it1); - if(it0->second.empty()){ - _player_counter.erase(it0); - } - } - } -} - -int HlsManager::hlsPlayerCount(const string &vhost, const string &app, const string &stream) { - lock_guard lck(_mtx); - auto it0 = _player_counter.find(vhost); - if(it0 == _player_counter.end()){ - return 0; - } - auto it1 = it0->second.find(app); - if(it1 == it0->second.end()){ - return 0; - } - auto it2 = it1->second.find(stream); - if(it2 == it1->second.end()){ - return 0; - } - return it2->second._count; -} - - -}//namespace mediakit - diff --git a/src/Record/HlsMediaSource.cpp b/src/Record/HlsMediaSource.cpp new file mode 100644 index 00000000..4f1933b2 --- /dev/null +++ b/src/Record/HlsMediaSource.cpp @@ -0,0 +1,52 @@ +/* + * MIT License + * + * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "HlsMediaSource.h" + +namespace mediakit{ + +HlsCookieData::HlsCookieData(const MediaInfo &info) { + _info = info; + auto src = dynamic_pointer_cast(MediaSource::find(HLS_SCHEMA,_info._vhost,_info._app,_info._streamid)); + if(src){ + src->modifyCount(true); + } +} + +HlsCookieData::~HlsCookieData() { + auto src = dynamic_pointer_cast(MediaSource::find(HLS_SCHEMA,_info._vhost,_info._app,_info._streamid)); + if(src){ + src->modifyCount(false); + } +} + +void HlsCookieData::addByteUsage(uint64_t bytes) { + _bytes += bytes; +} + + +}//namespace mediakit + diff --git a/src/Record/HlsManager.h b/src/Record/HlsMediaSource.h similarity index 59% rename from src/Record/HlsManager.h rename to src/Record/HlsMediaSource.h index 059c1777..e7a8eb2a 100644 --- a/src/Record/HlsManager.h +++ b/src/Record/HlsMediaSource.h @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> @@ -23,20 +23,13 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ +#ifndef ZLMEDIAKIT_HLSMEDIASOURCE_H +#define ZLMEDIAKIT_HLSMEDIASOURCE_H - -#ifndef ZLMEDIAKIT_HLSMANAGER_H -#define ZLMEDIAKIT_HLSMANAGER_H - -#include -#include -#include +#include #include "Common/MediaSource.h" -using namespace std; - namespace mediakit{ -class HlsManager; class HlsCookieData{ public: HlsCookieData(const MediaInfo &info); @@ -45,41 +38,59 @@ public: private: uint64_t _bytes = 0; MediaInfo _info; - std::shared_ptr _manager; }; - -class HlsManager : public std::enable_shared_from_this{ +class HlsMediaSource : public MediaSource { public: friend class HlsCookieData; - ~HlsManager(); - static HlsManager& Instance(); + typedef std::shared_ptr Ptr; + HlsMediaSource(const string &vhost, + const string &app, + const string &stream_id) : + MediaSource(HLS_SCHEMA, vhost, app, stream_id){ + _readerCount = 0; + } + + virtual ~HlsMediaSource() = default; /** - * 获取hls播放器个数 - * @param vhost 虚拟主机 - * @param app 应用名 - * @param stream 流id - * @return 播放器个数 + * 获取播放器个数 + * @return + */ + int readerCount() override { + return _readerCount.load(); + } + + /** + * 注册hls */ - int hlsPlayerCount(const string &vhost,const string &app,const string &stream); -private: - void onAddHlsPlayer(const MediaInfo &info); - void onDelHlsPlayer(const MediaInfo &info); - HlsManager(); -private: - class HlsPlayerCounter{ - private: - friend class HlsManager; - int _count = 0; - }; -private: - recursive_mutex _mtx; - unordered_map > > _player_counter; + void registHls(){ + if(!_registed){ + regist(); + _registed = true; + } + } +private: + /** + * 修改观看者个数 + * @param add 添加海思删除 + */ + void modifyCount(bool add) { + if (add) { + ++_readerCount; + return; + } + if (--_readerCount == 0 && totalReaderCount() == 0) { + onNoneReader(); + } + } +private: + atomic_int _readerCount; + bool _registed = false; }; + }//namespace mediakit -#endif //ZLMEDIAKIT_HLSMANAGER_H +#endif //ZLMEDIAKIT_HLSMEDIASOURCE_H diff --git a/src/Record/HlsRecorder.h b/src/Record/HlsRecorder.h index c0def9bb..c29d27cd 100644 --- a/src/Record/HlsRecorder.h +++ b/src/Record/HlsRecorder.h @@ -43,6 +43,9 @@ public: ~HlsRecorder(){ delete _hls; } + void setMediaInfo(const string &vhost, const string &app, const string &stream_id){ + _hls->setMediaInfo(vhost,app,stream_id); + } protected: void onTs(const void *packet, int bytes,uint32_t timestamp,int flags) override { _hls->inputData((char *)packet,bytes,timestamp); diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index 5c7f1397..d2ad95aa 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -54,7 +54,9 @@ MediaSinkInterface *createHlsRecorder(const string &strVhost_tmp, const string & m3u8FilePath = strApp + "/" + strId + "/hls.m3u8"; } m3u8FilePath = File::absolutePath(m3u8FilePath, hlsPath); - return new HlsRecorder(m3u8FilePath, params); + auto ret = new HlsRecorder(m3u8FilePath, params); + ret->setMediaInfo(strVhost,strApp,strId); + return ret; #else return nullptr; #endif //defined(ENABLE_HLS) From 54736859d48baf189e42d8b3201764074ff24eec Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 29 Dec 2019 10:49:04 +0800 Subject: [PATCH 176/272] =?UTF-8?q?=E5=9F=BA=E6=9C=AC=E5=AE=8C=E6=88=90hls?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E7=9A=84=E4=B8=BB=E8=A6=81=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mk_events_objects.h | 2 - api/source/mk_events_objects.cpp | 6 +-- server/WebApi.cpp | 15 ++++--- src/Common/MediaSource.cpp | 6 +-- src/Common/MediaSource.h | 29 +++++++------- src/Common/MultiMediaSourceMuxer.h | 56 ++++++++++++++++---------- src/Record/HlsMakerImp.cpp | 6 ++- src/Record/HlsMakerImp.h | 8 +++- src/Record/HlsRecorder.h | 9 ++++- src/Record/Recorder.cpp | 64 +++++++++++++++++++++++------- src/Record/Recorder.h | 9 +++++ src/Rtmp/RtmpMediaSourceMuxer.h | 4 ++ src/Rtsp/RtspMediaSource.h | 2 +- 13 files changed, 142 insertions(+), 74 deletions(-) diff --git a/api/include/mk_events_objects.h b/api/include/mk_events_objects.h index 30b4cb7c..f407e17d 100644 --- a/api/include/mk_events_objects.h +++ b/api/include/mk_events_objects.h @@ -81,8 +81,6 @@ API_EXPORT const char* API_CALL mk_parser_get_content(const mk_parser ctx, int * typedef void* mk_media_info; //MediaInfo::_param_strs API_EXPORT const char* API_CALL mk_media_info_get_params(const mk_media_info ctx); -//MediaInfo["key"] -API_EXPORT const char* API_CALL mk_media_info_get_param(const mk_media_info ctx,const char *key); //MediaInfo::_schema API_EXPORT const char* API_CALL mk_media_info_get_schema(const mk_media_info ctx); //MediaInfo::_vhost diff --git a/api/source/mk_events_objects.cpp b/api/source/mk_events_objects.cpp index cc341cc8..3818ad47 100644 --- a/api/source/mk_events_objects.cpp +++ b/api/source/mk_events_objects.cpp @@ -147,11 +147,7 @@ API_EXPORT const char* API_CALL mk_media_info_get_params(const mk_media_info ctx MediaInfo *info = (MediaInfo *)ctx; return info->_param_strs.c_str(); } -API_EXPORT const char* API_CALL mk_media_info_get_param(const mk_media_info ctx,const char *key){ - assert(ctx && key); - MediaInfo *info = (MediaInfo *)ctx; - return info->_params[key].c_str(); -} + API_EXPORT const char* API_CALL mk_media_info_get_schema(const mk_media_info ctx){ assert(ctx); MediaInfo *info = (MediaInfo *)ctx; diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 0ce721b6..fc8083a3 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -135,30 +135,29 @@ public: //获取HTTP请求中url参数、content参数 static ApiArgsType getAllArgs(const Parser &parser) { ApiArgsType allArgs; - if(parser["Content-Type"].find("application/x-www-form-urlencoded") == 0){ + if (parser["Content-Type"].find("application/x-www-form-urlencoded") == 0) { auto contentArgs = parser.parseArgs(parser.Content()); for (auto &pr : contentArgs) { allArgs[pr.first] = HttpSession::urlDecode(pr.second); } - }else if(parser["Content-Type"].find("application/json") == 0){ + } else if (parser["Content-Type"].find("application/json") == 0) { try { stringstream ss(parser.Content()); Value jsonArgs; ss >> jsonArgs; auto keys = jsonArgs.getMemberNames(); - for (auto key = keys.begin(); key != keys.end(); ++key){ + for (auto key = keys.begin(); key != keys.end(); ++key) { allArgs[*key] = jsonArgs[*key].asString(); } - }catch (std::exception &ex){ + } catch (std::exception &ex) { WarnL << ex.what(); } - }else if(!parser["Content-Type"].empty()){ + } else if (!parser["Content-Type"].empty()) { WarnL << "invalid Content-Type:" << parser["Content-Type"]; } - auto &urlArgs = parser.getUrlArgs(); - for (auto &pr : urlArgs) { - allArgs[pr.first] = HttpSession::urlDecode(pr.second); + for (auto &pr : parser.getUrlArgs()) { + allArgs[pr.first] = pr.second; } return std::move(allArgs); } diff --git a/src/Common/MediaSource.cpp b/src/Common/MediaSource.cpp index ff0b6094..7ab8b574 100644 --- a/src/Common/MediaSource.cpp +++ b/src/Common/MediaSource.cpp @@ -362,9 +362,9 @@ void MediaInfo::parse(const string &url){ if(pos != string::npos){ _streamid = steamid.substr(0,pos); _param_strs = steamid.substr(pos + 1); - _params = Parser::parseArgs(_param_strs); - if(_params.find(VHOST_KEY) != _params.end()){ - _vhost = _params[VHOST_KEY]; + auto params = Parser::parseArgs(_param_strs); + if(params.find(VHOST_KEY) != params.end()){ + _vhost = params[VHOST_KEY]; } } else{ _streamid = steamid; diff --git a/src/Common/MediaSource.h b/src/Common/MediaSource.h index 2b2e799b..385d6d8c 100644 --- a/src/Common/MediaSource.h +++ b/src/Common/MediaSource.h @@ -72,19 +72,15 @@ public: virtual int totalReaderCount(MediaSource &sender) = 0; }; +/** + * 解析url获取媒体相关信息 + */ class MediaInfo{ public: MediaInfo(){} - MediaInfo(const string &url){ - parse(url); - } ~MediaInfo(){} - + MediaInfo(const string &url){ parse(url); } void parse(const string &url); - - string &operator[](const string &key){ - return _params[key]; - } public: string _schema; string _host; @@ -92,7 +88,6 @@ public: string _vhost; string _app; string _streamid; - StrCaseMap _params; string _param_strs; }; @@ -118,21 +113,27 @@ public: const string& getApp() const; // 流id const string& getId() const; - // 获取所有Track - vector getTracks(bool trackReady = true) const override; - // 获取监听者 - const std::weak_ptr& getListener() const; // 设置TrackSource void setTrackSource(const std::weak_ptr &track_src); + // 获取所有Track + vector getTracks(bool trackReady = true) const override; + // 设置监听者 virtual void setListener(const std::weak_ptr &listener); + // 获取监听者 + const std::weak_ptr& getListener() const; + + // 本协议获取观看者个数,可能返回本协议的观看人数,也可能返回总人数 virtual int readerCount() = 0; // 观看者个数,包括(hls/rtsp/rtmp) virtual int totalReaderCount(); + // 获取流当前时间戳 virtual uint32_t getTimeStamp(TrackType trackType) { return 0; }; + // 设置时间戳 + virtual void setTimeStamp(uint32_t uiStamp) {}; // 拖动进度条 bool seekTo(uint32_t ui32Stamp); @@ -147,12 +148,10 @@ public: static void findAsync(const MediaInfo &info, const std::shared_ptr &session, const function &cb); // 遍历所有流 static void for_each_media(const function &cb); - protected: void regist() ; bool unregist() ; void unregisted(); - private: string _strSchema; string _strVhost; diff --git a/src/Common/MultiMediaSourceMuxer.h b/src/Common/MultiMediaSourceMuxer.h index bb977f7b..b711f638 100644 --- a/src/Common/MultiMediaSourceMuxer.h +++ b/src/Common/MultiMediaSourceMuxer.h @@ -31,6 +31,7 @@ #include "Rtmp/RtmpMediaSourceMuxer.h" #include "Record/Recorder.h" #include "Record/HlsMediaSource.h" +#include "Record/HlsRecorder.h" class MultiMediaSourceMuxer : public MediaSink , public std::enable_shared_from_this{ public: @@ -42,32 +43,29 @@ public: }; typedef std::shared_ptr Ptr; - MultiMediaSourceMuxer(const string &vhost, - const string &strApp, - const string &strId, - float dur_sec = 0.0, - bool bEanbleRtsp = true, - bool bEanbleRtmp = true, - bool bEanbleHls = true, - bool bEnableMp4 = false){ - if (bEanbleRtmp) { - _rtmp = std::make_shared(vhost, strApp, strId, std::make_shared(dur_sec)); + MultiMediaSourceMuxer(const string &vhost, const string &app, const string &stream, float dur_sec = 0.0, + bool enable_rtsp = true, bool enable_rtmp = true, bool enable_hls = true, bool enable_mp4 = false){ + if (enable_rtmp) { + _rtmp = std::make_shared(vhost, app, stream, std::make_shared(dur_sec)); } - if (bEanbleRtsp) { - _rtsp = std::make_shared(vhost, strApp, strId, std::make_shared(dur_sec)); + if (enable_rtsp) { + _rtsp = std::make_shared(vhost, app, stream, std::make_shared(dur_sec)); } - if(bEanbleHls){ - Recorder::startRecord(Recorder::type_hls,vhost, strApp, strId, true, false); + if(enable_hls){ + Recorder::startRecord(Recorder::type_hls,vhost, app, stream, true, false); } - if(bEnableMp4){ - Recorder::startRecord(Recorder::type_mp4,vhost, strApp, strId, true, false); + if(enable_mp4){ + Recorder::startRecord(Recorder::type_mp4,vhost, app, stream, true, false); } - _get_hls_player = [vhost,strApp,strId](){ - auto src = MediaSource::find(HLS_SCHEMA,vhost,strApp,strId); - return src ? src->readerCount() : 0; + _get_hls_media_source = [vhost,app,stream](){ + auto recorder = dynamic_pointer_cast(Recorder::getRecorder(Recorder::type_hls,vhost,app,stream)); + if(recorder){ + return recorder->getMediaSource(); + } + return MediaSource::Ptr(); }; } virtual ~MultiMediaSourceMuxer(){} @@ -92,9 +90,15 @@ public: if(_rtmp) { _rtmp->setListener(listener); } + if(_rtsp) { _rtsp->setListener(listener); } + + auto hls_src = _get_hls_media_source(); + if(hls_src){ + hls_src->setListener(listener); + } } /** @@ -102,10 +106,15 @@ public: * @return */ int totalReaderCount() const{ - return (_rtsp ? _rtsp->readerCount() : 0) + (_rtmp ? _rtmp->readerCount() : 0) + _get_hls_player(); + auto hls_src = _get_hls_media_source(); + return (_rtsp ? _rtsp->readerCount() : 0) + (_rtmp ? _rtmp->readerCount() : 0) + (hls_src ? hls_src->readerCount() : 0); } void setTimeStamp(uint32_t stamp){ + if(_rtmp){ + _rtmp->setTimeStamp(stamp); + } + if(_rtsp){ _rtsp->setTimeStamp(stamp); } @@ -154,6 +163,11 @@ protected: _rtsp->onAllTrackReady(); } + auto hls_src = _get_hls_media_source(); + if(hls_src){ + hls_src->setTrackSource(shared_from_this()); + } + if(_listener){ _listener->onAllTrackReady(); } @@ -162,7 +176,7 @@ private: RtmpMediaSourceMuxer::Ptr _rtmp; RtspMediaSourceMuxer::Ptr _rtsp; Listener *_listener = nullptr; - function _get_hls_player; + function _get_hls_media_source; }; diff --git a/src/Record/HlsMakerImp.cpp b/src/Record/HlsMakerImp.cpp index 2c7825bb..39fe77b9 100644 --- a/src/Record/HlsMakerImp.cpp +++ b/src/Record/HlsMakerImp.cpp @@ -118,8 +118,12 @@ std::shared_ptr HlsMakerImp::makeFile(const string &file,bool setbuf) { return ret; } -void HlsMakerImp::setMediaInfo(const string &vhost, const string &app, const string &stream_id) { +void HlsMakerImp::setMediaSource(const string &vhost, const string &app, const string &stream_id) { _media_src = std::make_shared(vhost, app, stream_id); } +MediaSource::Ptr HlsMakerImp::getMediaSource() const{ + return _media_src; +} + }//namespace mediakit \ No newline at end of file diff --git a/src/Record/HlsMakerImp.h b/src/Record/HlsMakerImp.h index 3a267243..d12aef5d 100644 --- a/src/Record/HlsMakerImp.h +++ b/src/Record/HlsMakerImp.h @@ -51,7 +51,13 @@ public: * @param app 应用名 * @param stream_id 流id */ - void setMediaInfo(const string &vhost, const string &app, const string &stream_id); + void setMediaSource(const string &vhost, const string &app, const string &stream_id); + + /** + * 获取MediaSource + * @return + */ + MediaSource::Ptr getMediaSource() const; protected: string onOpenSegment(int index) override ; void onDelSegment(int index) override; diff --git a/src/Record/HlsRecorder.h b/src/Record/HlsRecorder.h index c29d27cd..d9e20e33 100644 --- a/src/Record/HlsRecorder.h +++ b/src/Record/HlsRecorder.h @@ -34,6 +34,7 @@ namespace mediakit { class HlsRecorder : public TsMuxer { public: + typedef std::shared_ptr Ptr; HlsRecorder(const string &m3u8_file, const string ¶ms){ GET_CONFIG(uint32_t,hlsNum,Hls::kSegmentNum); GET_CONFIG(uint32_t,hlsBufSize,Hls::kFileBufSize); @@ -43,8 +44,12 @@ public: ~HlsRecorder(){ delete _hls; } - void setMediaInfo(const string &vhost, const string &app, const string &stream_id){ - _hls->setMediaInfo(vhost,app,stream_id); + void setMediaSource(const string &vhost, const string &app, const string &stream_id){ + _hls->setMediaSource(vhost, app, stream_id); + } + + MediaSource::Ptr getMediaSource() const{ + return _hls->getMediaSource(); } protected: void onTs(const void *packet, int bytes,uint32_t timestamp,int flags) override { diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index d2ad95aa..e3ef8106 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -55,7 +55,7 @@ MediaSinkInterface *createHlsRecorder(const string &strVhost_tmp, const string & } m3u8FilePath = File::absolutePath(m3u8FilePath, hlsPath); auto ret = new HlsRecorder(m3u8FilePath, params); - ret->setMediaInfo(strVhost,strApp,strId); + ret->setMediaSource(strVhost, strApp, strId); return ret; #else return nullptr; @@ -159,6 +159,10 @@ public: const string &getSchema() const{ return _schema; } + + const MediaSinkInterface::Ptr& getRecorder() const{ + return _recorder; + } private: MediaSinkInterface::Ptr _recorder; vector _tracks; @@ -179,6 +183,16 @@ public: return getRecordStatus_l(getRecorderKey(vhost, app, stream_id)); } + MediaSinkInterface::Ptr getRecorder(const string &vhost, const string &app, const string &stream_id) const{ + auto key = getRecorderKey(vhost, app, stream_id); + lock_guard lck(_recorder_mtx); + auto it = _recorder_map.find(key); + if (it == _recorder_map.end()) { + return nullptr; + } + return it->second->getRecorder(); + } + int startRecord(const string &vhost, const string &app, const string &stream_id, bool waitForRecord, bool continueRecord) { auto key = getRecorderKey(vhost, app, stream_id); lock_guard lck(_recorder_mtx); @@ -187,9 +201,8 @@ public: return 0; } - string schema; - auto tracks = findTracks(vhost, app, stream_id,schema); - if (!waitForRecord && tracks.empty()) { + auto src = findMediaSource(vhost, app, stream_id); + if (!waitForRecord && !src) { // 暂时无法开启录制 return -1; } @@ -200,9 +213,17 @@ public: return -2; } auto helper = std::make_shared(recorder, continueRecord); - if(tracks.size()){ - helper->attachTracks(std::move(tracks),schema); + if(src){ + auto tracks = src->getTracks(needTrackReady()); + if(tracks.size()){ + helper->attachTracks(std::move(tracks),src->getSchema()); + } + auto hls_recorder = dynamic_pointer_cast(recorder); + if(hls_recorder){ + hls_recorder->getMediaSource()->setListener(src->getListener()); + } } + _recorder_map[key] = std::move(helper); return 0; } @@ -281,25 +302,27 @@ private: } // 查找MediaSource以便录制 - vector findTracks(const string &vhost, const string &app, const string &stream_id,string &schema) { + MediaSource::Ptr findMediaSource(const string &vhost, const string &app, const string &stream_id) { + bool need_ready = needTrackReady(); auto src = MediaSource::find(RTMP_SCHEMA, vhost, app, stream_id); if (src) { - auto ret = src->getTracks(needTrackReady()); + auto ret = src->getTracks(need_ready); if (!ret.empty()) { - schema = RTMP_SCHEMA; - return std::move(ret); + return std::move(src); } } src = MediaSource::find(RTSP_SCHEMA, vhost, app, stream_id); if (src) { - schema = RTSP_SCHEMA; - return src->getTracks(needTrackReady()); + auto ret = src->getTracks(need_ready); + if (!ret.empty()) { + return std::move(src); + } } - return vector(); + return nullptr; } - string getRecorderKey(const string &vhost, const string &app, const string &stream_id) { + string getRecorderKey(const string &vhost, const string &app, const string &stream_id) const{ return vhost + "/" + app + "/" + stream_id; } @@ -335,7 +358,7 @@ private: } } private: - recursive_mutex _recorder_mtx; + mutable recursive_mutex _recorder_mtx; NoticeCenter::Ptr _notice_center; unordered_map _recorder_map; }; @@ -351,6 +374,17 @@ Recorder::status Recorder::getRecordStatus(Recorder::type type, const string &vh return status_not_record; } +std::shared_ptr Recorder::getRecorder(type type, const string &vhost, const string &app, const string &stream_id){ + switch (type){ + case type_mp4: + return MediaSourceWatcher::Instance().getRecorder(vhost,app,stream_id); + case type_hls: + return MediaSourceWatcher::Instance().getRecorder(vhost,app,stream_id); + } + return nullptr; +} + + int Recorder::startRecord(Recorder::type type, const string &vhost, const string &app, const string &stream_id, bool waitForRecord, bool continueRecord) { switch (type){ case type_mp4: diff --git a/src/Record/Recorder.h b/src/Record/Recorder.h index 6172cabd..26921214 100644 --- a/src/Record/Recorder.h +++ b/src/Record/Recorder.h @@ -88,6 +88,15 @@ public: * 停止所有录制,一般程序退出时调用 */ static void stopAll(); + + /** + * 获取录制对象 + * @param type hls还是MP4录制 + * @param vhost 虚拟主机 + * @param app 应用名 + * @param stream_id 流id + */ + static std::shared_ptr getRecorder(type type, const string &vhost, const string &app, const string &stream_id); private: Recorder() = delete; ~Recorder() = delete; diff --git a/src/Rtmp/RtmpMediaSourceMuxer.h b/src/Rtmp/RtmpMediaSourceMuxer.h index 1ddb082a..2cecd127 100644 --- a/src/Rtmp/RtmpMediaSourceMuxer.h +++ b/src/Rtmp/RtmpMediaSourceMuxer.h @@ -49,6 +49,10 @@ public: _mediaSouce->setListener(listener); } + void setTimeStamp(uint32_t stamp){ + _mediaSouce->setTimeStamp(stamp); + } + int readerCount() const{ return _mediaSouce->readerCount(); } diff --git a/src/Rtsp/RtspMediaSource.h b/src/Rtsp/RtspMediaSource.h index f4354ae4..2fc0cc99 100644 --- a/src/Rtsp/RtspMediaSource.h +++ b/src/Rtsp/RtspMediaSource.h @@ -140,7 +140,7 @@ public: /** * 更新时间戳 */ - virtual void setTimeStamp(uint32_t uiStamp) { + void setTimeStamp(uint32_t uiStamp) override { auto tracks = _sdp_parser.getAvailableTrack(); for (auto &track : tracks) { track->_time_stamp = uiStamp; From 4b4c4e0cecfe8899fdcc9f84a000ca7e518dd1c6 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 29 Dec 2019 11:52:02 +0800 Subject: [PATCH 177/272] =?UTF-8?q?=E5=AE=9E=E7=8E=B0hls=E6=8C=89=E9=9C=80?= =?UTF-8?q?=E6=8B=89=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mk_events.h | 4 -- api/source/mk_events.cpp | 2 - api/tests/server.c | 4 -- conf/config.ini | 4 +- server/WebApi.cpp | 2 +- server/WebHook.cpp | 2 +- src/Common/config.cpp | 4 +- src/Common/config.h | 4 +- src/Http/HttpFileManager.cpp | 94 +++++++++++++++++++++++------------- src/Record/HlsMediaSource.h | 5 +- 10 files changed, 69 insertions(+), 56 deletions(-) diff --git a/api/include/mk_events.h b/api/include/mk_events.h index f9336177..e9a4b07a 100644 --- a/api/include/mk_events.h +++ b/api/include/mk_events.h @@ -94,14 +94,12 @@ typedef struct { /** * 在http文件服务器中,收到http访问文件或目录的广播,通过该事件控制访问http目录的权限 * @param parser http请求内容对象 - * @param url_info 请求url相关信息 * @param path 文件绝对路径 * @param is_dir path是否为文件夹 * @param invoker 执行invoker返回本次访问文件的结果 * @param sender http客户端相关信息 */ void (API_CALL *on_mk_http_access)(const mk_parser parser, - const mk_media_info url_info, const char *path, int is_dir, const mk_http_access_path_invoker invoker, @@ -111,12 +109,10 @@ typedef struct { * 在http文件服务器中,收到http访问文件或目录前的广播,通过该事件可以控制http url到文件路径的映射 * 在该事件中通过自行覆盖path参数,可以做到譬如根据虚拟主机或者app选择不同http根目录的目的 * @param parser http请求内容对象 - * @param url_info 请求url相关信息 * @param path 文件绝对路径,覆盖之可以重定向到其他文件 * @param sender http客户端相关信息 */ void (API_CALL *on_mk_http_before_access)(const mk_parser parser, - const mk_media_info url_info, char *path, const mk_tcp_session sender); diff --git a/api/source/mk_events.cpp b/api/source/mk_events.cpp index 8d915eb1..d44413e7 100644 --- a/api/source/mk_events.cpp +++ b/api/source/mk_events.cpp @@ -70,7 +70,6 @@ API_EXPORT void API_CALL mk_events_listen(const mk_events *events){ NoticeCenter::Instance().addListener(&s_tag,Broadcast::kBroadcastHttpAccess,[](BroadcastHttpAccessArgs){ if(s_events.on_mk_http_access){ s_events.on_mk_http_access((mk_parser)&parser, - (mk_media_info)&args, path.c_str(), is_dir, (mk_http_access_path_invoker)&invoker, @@ -85,7 +84,6 @@ API_EXPORT void API_CALL mk_events_listen(const mk_events *events){ char path_c[4 * 1024] = {0}; strcpy(path_c,path.c_str()); s_events.on_mk_http_before_access((mk_parser) &parser, - (mk_media_info) &args, path_c, (mk_tcp_session) &sender); path = path_c; diff --git a/api/tests/server.c b/api/tests/server.c index 34e3db1b..72afc1b0 100644 --- a/api/tests/server.c +++ b/api/tests/server.c @@ -198,14 +198,12 @@ void API_CALL on_mk_http_request(const mk_parser parser, /** * 在http文件服务器中,收到http访问文件或目录的广播,通过该事件控制访问http目录的权限 * @param parser http请求内容对象 - * @param url_info 请求url相关信息 * @param path 文件绝对路径 * @param is_dir path是否为文件夹 * @param invoker 执行invoker返回本次访问文件的结果 * @param sender http客户端相关信息 */ void API_CALL on_mk_http_access(const mk_parser parser, - const mk_media_info url_info, const char *path, int is_dir, const mk_http_access_path_invoker invoker, @@ -236,12 +234,10 @@ void API_CALL on_mk_http_access(const mk_parser parser, * 在http文件服务器中,收到http访问文件或目录前的广播,通过该事件可以控制http url到文件路径的映射 * 在该事件中通过自行覆盖path参数,可以做到譬如根据虚拟主机或者app选择不同http根目录的目的 * @param parser http请求内容对象 - * @param url_info 请求url相关信息 * @param path 文件绝对路径,覆盖之可以重定向到其他文件 * @param sender http客户端相关信息 */ void API_CALL on_mk_http_before_access(const mk_parser parser, - const mk_media_info url_info, char *path, const mk_tcp_session sender) { log_printf(LOG_LEV, diff --git a/conf/config.ini b/conf/config.ini index 7e4e3029..c2e01d2b 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -25,10 +25,10 @@ flowThreshold=1024 #ZLMediaKit会最多让播放器等待maxStreamWaitMS毫秒 #如果在这个时间内,该流注册成功,那么会立即返回播放器播放成功 #否则返回播放器未找到该流,该机制的目的是可以先播放再推流 -maxStreamWaitMS=5000 +maxStreamWaitMS=15000 #某个流无人观看时,触发hook.on_stream_none_reader事件的最大等待时间,单位毫秒 #在配合hook.on_stream_none_reader事件时,可以做到无人观看自动停止拉流或停止接收推流 -streamNoneReaderDelayMS=5000 +streamNoneReaderDelayMS=20000 #是否开启低延时模式,该模式下禁用MSG_MORE,启用TCP_NODEALY,延时将降低,但数据发送性能将降低 ultraLowDelay=1 #拉流代理是否添加静音音频(直接拉流模式本协议无效) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index fc8083a3..91a158f6 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -834,7 +834,7 @@ void installWebApi() { << allArgs["stream"] << "?vhost=" << allArgs["vhost"]; - addFFmpegSource("http://live.hkstv.hk.lxdns.com/live/hks2/playlist.m3u8",/** ffmpeg拉流支持任意编码格式任意协议 **/ + addFFmpegSource("http://hls-ott-zhibo.wasu.tv/live/272/index.m3u8",/** ffmpeg拉流支持任意编码格式任意协议 **/ dst_url, (1000 * timeout_sec) - 500, [invoker,val,headerOut](const SockException &ex,const string &key){ diff --git a/server/WebHook.cpp b/server/WebHook.cpp index 6c911588..87ab067f 100644 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -441,7 +441,7 @@ void installWebHook(){ //如果没有url参数,客户端又不支持cookie,那么会根据ip和端口追踪用户 //追踪用户的目的是为了缓存上次鉴权结果,减少鉴权次数,提高性能 NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastHttpAccess,[](BroadcastHttpAccessArgs){ - if(sender.get_peer_ip() == "127.0.0.1" && args._param_strs == hook_adminparams){ + if(sender.get_peer_ip() == "127.0.0.1" && parser.Params() == hook_adminparams){ //如果是本机或超级管理员访问,那么不做访问鉴权;权限有效期1个小时 invoker("","",60 * 60); return; diff --git a/src/Common/config.cpp b/src/Common/config.cpp index 26239807..764830a1 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -87,8 +87,8 @@ const string kPublishToMP4 = GENERAL_FIELD"publishToMP4"; onceToken token([](){ mINI::Instance()[kFlowThreshold] = 1024; - mINI::Instance()[kStreamNoneReaderDelayMS] = 5 * 1000; - mINI::Instance()[kMaxStreamWaitTimeMS] = 5 * 1000; + mINI::Instance()[kStreamNoneReaderDelayMS] = 20 * 1000; + mINI::Instance()[kMaxStreamWaitTimeMS] = 15 * 1000; mINI::Instance()[kEnableVhost] = 0; mINI::Instance()[kUltraLowDelay] = 1; mINI::Instance()[kAddMuteAudio] = 1; diff --git a/src/Common/config.h b/src/Common/config.h index cf22c3f0..53ad8e3c 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -86,12 +86,12 @@ extern const string kBroadcastHttpRequest; //在http文件服务器中,收到http访问文件或目录的广播,通过该事件控制访问http目录的权限 extern const string kBroadcastHttpAccess; -#define BroadcastHttpAccessArgs const Parser &parser,const MediaInfo &args,const string &path,const bool &is_dir,const HttpSession::HttpAccessPathInvoker &invoker,TcpSession &sender +#define BroadcastHttpAccessArgs const Parser &parser,const string &path,const bool &is_dir,const HttpSession::HttpAccessPathInvoker &invoker,TcpSession &sender //在http文件服务器中,收到http访问文件或目录前的广播,通过该事件可以控制http url到文件路径的映射 //在该事件中通过自行覆盖path参数,可以做到譬如根据虚拟主机或者app选择不同http根目录的目的 extern const string kBroadcastHttpBeforeAccess; -#define BroadcastHttpBeforeAccessArgs const Parser &parser,const MediaInfo &args,string &path,TcpSession &sender +#define BroadcastHttpBeforeAccessArgs const Parser &parser,string &path,TcpSession &sender //该流是否需要认证?是的话调用invoker并传入realm,否则传入空的realm.如果该事件不监听则不认证 extern const string kBroadcastOnGetRtspRealm; diff --git a/src/Http/HttpFileManager.cpp b/src/Http/HttpFileManager.cpp index 5e39823c..4ba84482 100644 --- a/src/Http/HttpFileManager.cpp +++ b/src/Http/HttpFileManager.cpp @@ -218,16 +218,13 @@ static bool end_of(const string &str, const string &substr){ }; //拦截hls的播放请求 -static bool emitHlsPlayed(BroadcastHttpAccessArgs){ +static bool emitHlsPlayed(const Parser &parser, const MediaInfo &mediaInfo, const HttpSession::HttpAccessPathInvoker &invoker,TcpSession &sender){ //访问的hls.m3u8结尾,我们转换成kBroadcastMediaPlayed事件 - Broadcast::AuthInvoker mediaAuthInvoker = [invoker,path](const string &err){ + Broadcast::AuthInvoker mediaAuthInvoker = [invoker](const string &err){ //cookie有效期为kHlsCookieSecond invoker(err,"",kHlsCookieSecond); }; - - auto args_copy = args; - replace(args_copy._streamid,kHlsSuffix,""); - return NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed,args_copy,mediaAuthInvoker,sender); + return NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed,mediaInfo,mediaAuthInvoker,sender); } @@ -284,7 +281,7 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI HttpCookieManager::Instance().delCookie(cookie); } - bool is_hls = end_of(path,kHlsSuffix); + bool is_hls = mediaInfo._schema == HLS_SCHEMA; //该用户从来未获取过cookie,这个时候我们广播是否允许该用户访问该http目录 HttpSession::HttpAccessPathInvoker accessPathInvoker = [callback, uid, path, is_dir, is_hls, mediaInfo] (const string &errMsg, const string &cookie_path_in, int cookieLifeSecond) { @@ -308,7 +305,6 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI (*cookie)[kAccessHls].set(is_hls); if(is_hls){ //hls相关信息 - replace(const_cast(mediaInfo._streamid),kHlsSuffix,""); (*cookie)[kHlsData].set(mediaInfo); } callback(errMsg, cookie); @@ -317,13 +313,13 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI } }; - if (is_hls && emitHlsPlayed(parser, mediaInfo, path, is_dir, accessPathInvoker, sender)) { + if (is_hls && emitHlsPlayed(parser, mediaInfo, accessPathInvoker, sender)) { //是hls的播放鉴权,拦截之 return; } //事件未被拦截,则认为是http下载请求 - bool flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastHttpAccess, parser, mediaInfo, path, is_dir, accessPathInvoker, sender); + bool flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastHttpAccess, parser, path, is_dir, accessPathInvoker, sender); if (!flag) { //此事件无人监听,我们默认都有权限访问 callback("", nullptr); @@ -357,35 +353,65 @@ 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) { - if (!File::is_file(strFile.data())) { + bool is_hls = end_of(strFile, kHlsSuffix); + if (!is_hls && !File::is_file(strFile.data())) { + //文件不存在且不是hls,那么直接返回404 sendNotFound(cb); return; } - //判断是否有权限访问该文件 - canAccessPath(sender, parser, mediaInfo, false, [cb, strFile, parser](const string &errMsg, const HttpServerCookie::Ptr &cookie) { - if (!errMsg.empty()) { - StrCaseMap headerOut; - if (cookie) { - headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); - } - cb("401 Unauthorized", "text/html", headerOut, std::make_shared(errMsg)); - return; - } - StrCaseMap httpHeader; - if (cookie) { - httpHeader["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); - } - HttpSession::HttpResponseInvoker invoker = [&](const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body) { - if(cookie){ - auto is_hls = (*cookie)[kAccessHls].get(); - if(is_hls){ - (*cookie)[kHlsData].get().addByteUsage(body->remainSize()); + if(is_hls){ + //hls,那么移除掉后缀获取真实的stream_id并且修改协议为HLS + const_cast(mediaInfo._schema) = HLS_SCHEMA; + replace(const_cast(mediaInfo._streamid), kHlsSuffix, ""); + } + + weak_ptr weakSession = sender.shared_from_this(); + //判断是否有权限访问该文件 + canAccessPath(sender, parser, mediaInfo, false, [cb, strFile, parser, is_hls, mediaInfo, weakSession](const string &errMsg, const HttpServerCookie::Ptr &cookie) { + if (!errMsg.empty()) { + //文件鉴权失败 + StrCaseMap headerOut; + if (cookie) { + headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); } + cb("401 Unauthorized", "text/html", headerOut, std::make_shared(errMsg)); + return; + } + + auto response_file = [](const HttpServerCookie::Ptr &cookie, const HttpFileManager::invoker &cb, const string &strFile, const Parser &parser) { + StrCaseMap httpHeader; + if (cookie) { + httpHeader["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); + } + HttpSession::HttpResponseInvoker invoker = [&](const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body) { + if (cookie) { + cookie->getLock(); + auto is_hls = (*cookie)[kAccessHls].get(); + if (is_hls) { + (*cookie)[kHlsData].get().addByteUsage(body->remainSize()); + } + } + cb(codeOut.data(), getContentType(strFile.data()), headerOut, body); + }; + invoker.responseFile(parser.getValues(), httpHeader, strFile); + }; + + if (!is_hls) { + //不是hls,直接回复 + response_file(cookie, cb, strFile, parser); + } else { + //文件不存在,那么说明是hls,我们等待其生成并延后回复 + auto strongSession = weakSession.lock(); + if(!strongSession){ + //http客户端已经断开,不需要回复 + return; + } + MediaSource::findAsync(mediaInfo, strongSession, [response_file, cookie, cb, strFile, parser](const MediaSource::Ptr &src) { + //hls已经生成或者超时后仍未生成,那么不管怎么样都返回客户端 + response_file(cookie, cb, strFile, parser); + }); } - cb(codeOut.data(), getContentType(strFile.data()), headerOut, body); - }; - invoker.responseFile(parser.getValues(), httpHeader, strFile); }); } @@ -393,7 +419,7 @@ static string getFilePath(const Parser &parser,const MediaInfo &mediaInfo, TcpSe GET_CONFIG(bool, enableVhost, General::kEnableVhost); GET_CONFIG(string, rootPath, Http::kRootPath); auto ret = File::absolutePath(enableVhost ? mediaInfo._vhost + parser.Url() : parser.Url(), rootPath); - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastHttpBeforeAccess, parser, mediaInfo, ret, sender); + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastHttpBeforeAccess, parser, ret, sender); return std::move(ret); } diff --git a/src/Record/HlsMediaSource.h b/src/Record/HlsMediaSource.h index e7a8eb2a..98390108 100644 --- a/src/Record/HlsMediaSource.h +++ b/src/Record/HlsMediaSource.h @@ -44,10 +44,7 @@ class HlsMediaSource : public MediaSource { public: friend class HlsCookieData; typedef std::shared_ptr Ptr; - HlsMediaSource(const string &vhost, - const string &app, - const string &stream_id) : - MediaSource(HLS_SCHEMA, vhost, app, stream_id){ + HlsMediaSource(const string &vhost, const string &app, const string &stream_id) : MediaSource(HLS_SCHEMA, vhost, app, stream_id){ _readerCount = 0; } From 236dc46b82fda3bd72c3960f45b12815bb1bac19 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 29 Dec 2019 12:10:31 +0800 Subject: [PATCH 178/272] =?UTF-8?q?=E5=AE=8C=E6=88=90HLS=E6=8C=89=E9=9C=80?= =?UTF-8?q?=E7=94=9F=E6=88=90=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/HlsMediaSource.cpp | 23 +++++++++++++++++------ src/Record/HlsMediaSource.h | 5 ++++- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/Record/HlsMediaSource.cpp b/src/Record/HlsMediaSource.cpp index 4f1933b2..8dd0a702 100644 --- a/src/Record/HlsMediaSource.cpp +++ b/src/Record/HlsMediaSource.cpp @@ -30,20 +30,31 @@ namespace mediakit{ HlsCookieData::HlsCookieData(const MediaInfo &info) { _info = info; - auto src = dynamic_pointer_cast(MediaSource::find(HLS_SCHEMA,_info._vhost,_info._app,_info._streamid)); - if(src){ - src->modifyCount(true); + addReaderCount(); +} + +void HlsCookieData::addReaderCount(){ + if(!_added){ + auto src = dynamic_pointer_cast(MediaSource::find(HLS_SCHEMA,_info._vhost,_info._app,_info._streamid)); + if(src){ + src->modifyReaderCount(true); + _added = true; + } } + } HlsCookieData::~HlsCookieData() { - auto src = dynamic_pointer_cast(MediaSource::find(HLS_SCHEMA,_info._vhost,_info._app,_info._streamid)); - if(src){ - src->modifyCount(false); + if(_added){ + auto src = dynamic_pointer_cast(MediaSource::find(HLS_SCHEMA,_info._vhost,_info._app,_info._streamid)); + if(src){ + src->modifyReaderCount(false); + } } } void HlsCookieData::addByteUsage(uint64_t bytes) { + addReaderCount(); _bytes += bytes; } diff --git a/src/Record/HlsMediaSource.h b/src/Record/HlsMediaSource.h index 98390108..89a96772 100644 --- a/src/Record/HlsMediaSource.h +++ b/src/Record/HlsMediaSource.h @@ -35,9 +35,12 @@ public: HlsCookieData(const MediaInfo &info); ~HlsCookieData(); void addByteUsage(uint64_t bytes); +private: + void addReaderCount(); private: uint64_t _bytes = 0; MediaInfo _info; + bool _added = false; }; class HlsMediaSource : public MediaSource { @@ -73,7 +76,7 @@ private: * 修改观看者个数 * @param add 添加海思删除 */ - void modifyCount(bool add) { + void modifyReaderCount(bool add) { if (add) { ++_readerCount; return; From 97ad062dcf31dee59559797387d2bc41dd994a70 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 29 Dec 2019 12:16:02 +0800 Subject: [PATCH 179/272] =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=85=B3=E9=97=ADrtm?= =?UTF-8?q?p=E6=97=B6=E9=97=B4=E6=88=B3=E8=A6=86=E7=9B=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/config.ini | 2 +- src/Common/config.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/config.ini b/conf/config.ini index c2e01d2b..e60dc830 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -144,7 +144,7 @@ handshakeSecond=15 #或者tcp发送缓存超过这个时间,则会断开连接,单位秒 keepAliveSecond=15 #在接收rtmp推流时,是否重新生成时间戳(很多推流器的时间戳着实很烂) -modifyStamp=1 +modifyStamp=0 #rtmp服务器监听端口 port=1935 diff --git a/src/Common/config.cpp b/src/Common/config.cpp index 764830a1..74e92e72 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -178,7 +178,7 @@ const string kHandshakeSecond = RTMP_FIELD"handshakeSecond"; const string kKeepAliveSecond = RTMP_FIELD"keepAliveSecond"; onceToken token([](){ - mINI::Instance()[kModifyStamp] = true; + mINI::Instance()[kModifyStamp] = false; mINI::Instance()[kHandshakeSecond] = 15; mINI::Instance()[kKeepAliveSecond] = 15; },nullptr); From c3302eec1eb9c8542701b7fd5d723da0be3c9490 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 29 Dec 2019 13:38:16 +0800 Subject: [PATCH 180/272] =?UTF-8?q?=E4=BC=98=E5=8C=96hls=E6=92=AD=E6=94=BE?= =?UTF-8?q?=E5=99=A8=E8=AE=A1=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/HlsMediaSource.cpp | 3 ++- src/Record/HlsMediaSource.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Record/HlsMediaSource.cpp b/src/Record/HlsMediaSource.cpp index 8dd0a702..e5b0af23 100644 --- a/src/Record/HlsMediaSource.cpp +++ b/src/Record/HlsMediaSource.cpp @@ -39,6 +39,7 @@ void HlsCookieData::addReaderCount(){ if(src){ src->modifyReaderCount(true); _added = true; + _src = src; } } @@ -46,7 +47,7 @@ void HlsCookieData::addReaderCount(){ HlsCookieData::~HlsCookieData() { if(_added){ - auto src = dynamic_pointer_cast(MediaSource::find(HLS_SCHEMA,_info._vhost,_info._app,_info._streamid)); + auto src = _src.lock(); if(src){ src->modifyReaderCount(false); } diff --git a/src/Record/HlsMediaSource.h b/src/Record/HlsMediaSource.h index 89a96772..a402b1cb 100644 --- a/src/Record/HlsMediaSource.h +++ b/src/Record/HlsMediaSource.h @@ -30,6 +30,7 @@ #include "Common/MediaSource.h" namespace mediakit{ +class HlsMediaSource; class HlsCookieData{ public: HlsCookieData(const MediaInfo &info); @@ -41,6 +42,7 @@ private: uint64_t _bytes = 0; MediaInfo _info; bool _added = false; + weak_ptr _src; }; class HlsMediaSource : public MediaSource { From e951efc61514bfa1268ce3ada79431b83b66b843 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 29 Dec 2019 14:00:20 +0800 Subject: [PATCH 181/272] =?UTF-8?q?=E4=BC=98=E5=8C=96hls=E7=9B=B4=E6=92=AD?= =?UTF-8?q?=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/HttpFileManager.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Http/HttpFileManager.cpp b/src/Http/HttpFileManager.cpp index 4ba84482..367e6dc1 100644 --- a/src/Http/HttpFileManager.cpp +++ b/src/Http/HttpFileManager.cpp @@ -354,7 +354,8 @@ static string pathCat(const string &a, const string &b){ */ 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); - if (!is_hls && !File::is_file(strFile.data())) { + bool file_exist = File::is_file(strFile.data()); + if (!is_hls && !file_exist) { //文件不存在且不是hls,那么直接返回404 sendNotFound(cb); return; @@ -368,7 +369,7 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo weak_ptr weakSession = sender.shared_from_this(); //判断是否有权限访问该文件 - canAccessPath(sender, parser, mediaInfo, false, [cb, strFile, parser, is_hls, mediaInfo, weakSession](const string &errMsg, const HttpServerCookie::Ptr &cookie) { + canAccessPath(sender, parser, mediaInfo, false, [cb, strFile, parser, is_hls, mediaInfo, weakSession , file_exist](const string &errMsg, const HttpServerCookie::Ptr &cookie) { if (!errMsg.empty()) { //文件鉴权失败 StrCaseMap headerOut; @@ -397,11 +398,11 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo invoker.responseFile(parser.getValues(), httpHeader, strFile); }; - if (!is_hls) { - //不是hls,直接回复 + if (file_exist || !is_hls) { + //不是hls或者文件存在,直接回复文件或404 response_file(cookie, cb, strFile, parser); - } else { - //文件不存在,那么说明是hls,我们等待其生成并延后回复 + } else { + //hls文件不存在,我们等待其生成并延后回复 auto strongSession = weakSession.lock(); if(!strongSession){ //http客户端已经断开,不需要回复 From e91c26c052e30010df116cbecc4d9375acce78b0 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 29 Dec 2019 15:38:29 +0800 Subject: [PATCH 182/272] =?UTF-8?q?=E5=AE=9E=E7=8E=B0hls=E7=9A=84=E6=B5=81?= =?UTF-8?q?=E9=87=8F=E6=B1=87=E6=8A=A5=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mk_events.h | 3 +- api/source/mk_events.cpp | 3 +- api/tests/server.c | 14 +++--- server/WebHook.cpp | 12 +++-- src/Common/config.h | 2 +- src/Http/HttpFileManager.cpp | 94 +++++++++++++++++++++-------------- src/Http/HttpSession.cpp | 9 +--- src/Record/HlsMediaSource.cpp | 12 +++-- src/Record/HlsMediaSource.h | 2 + src/Rtmp/RtmpSession.cpp | 9 +--- src/Rtsp/RtspSession.cpp | 9 +--- 11 files changed, 91 insertions(+), 78 deletions(-) diff --git a/api/include/mk_events.h b/api/include/mk_events.h index e9a4b07a..c9373af3 100644 --- a/api/include/mk_events.h +++ b/api/include/mk_events.h @@ -166,8 +166,7 @@ typedef struct { void (API_CALL *on_mk_flow_report)(const mk_media_info url_info, uint64_t total_bytes, uint64_t total_seconds, - int is_player, - const mk_tcp_session sender); + int is_player); } mk_events; diff --git a/api/source/mk_events.cpp b/api/source/mk_events.cpp index d44413e7..5e693587 100644 --- a/api/source/mk_events.cpp +++ b/api/source/mk_events.cpp @@ -151,8 +151,7 @@ API_EXPORT void API_CALL mk_events_listen(const mk_events *events){ s_events.on_mk_flow_report((mk_media_info) &args, totalBytes, totalDuration, - isPlayer, - (mk_tcp_session) &sender); + isPlayer); } }); diff --git a/api/tests/server.c b/api/tests/server.c index 72afc1b0..85714d50 100644 --- a/api/tests/server.c +++ b/api/tests/server.c @@ -376,14 +376,14 @@ void API_CALL on_mk_shell_login(const char *user_name, void API_CALL on_mk_flow_report(const mk_media_info url_info, uint64_t total_bytes, uint64_t total_seconds, - int is_player, - const mk_tcp_session sender) { - log_printf(LOG_LEV,"client info, local: %s:%d, peer: %s:%d\n" + int is_player) { + log_printf(LOG_LEV,"%s/%s/%s/%s, url params: %s," "total_bytes: %d, total_seconds: %d, is_player: %d", - mk_tcp_session_local_ip(sender), - mk_tcp_session_local_port(sender), - mk_tcp_session_peer_ip(sender), - mk_tcp_session_peer_port(sender), + mk_media_info_get_schema(url_info), + mk_media_info_get_vhost(url_info), + mk_media_info_get_app(url_info), + mk_media_info_get_stream(url_info), + mk_media_info_get_params(url_info), (int)total_bytes, (int)total_seconds, (int)is_player); } diff --git a/server/WebHook.cpp b/server/WebHook.cpp index 87ab067f..058fee39 100644 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -269,16 +269,20 @@ void installWebHook(){ }); NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastFlowReport,[](BroadcastFlowReportArgs){ - if(!hook_enable || args._param_strs == hook_adminparams || hook_flowreport.empty() || sender.get_peer_ip() == "127.0.0.1"){ + if(!hook_enable || args._param_strs == hook_adminparams || hook_flowreport.empty()){ return; } auto body = make_json(args); - body["ip"] = sender.get_peer_ip(); - body["port"] = sender.get_peer_port(); - body["id"] = sender.getIdentifier(); body["totalBytes"] = (Json::UInt64)totalBytes; body["duration"] = (Json::UInt64)totalDuration; body["player"] = isPlayer; + + body["schema"] = args._schema; + body["vhost"] = args._vhost; + body["app"] = args._app; + body["stream"] = args._streamid; + body["params"] = args._param_strs; + //执行hook do_http_hook(hook_flowreport,body, nullptr); }); diff --git a/src/Common/config.h b/src/Common/config.h index 53ad8e3c..7cadbf70 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -127,7 +127,7 @@ extern const string kBroadcastShellLogin; //停止rtsp/rtmp/http-flv会话后流量汇报事件广播 extern const string kBroadcastFlowReport; -#define BroadcastFlowReportArgs const MediaInfo &args,const uint64_t &totalBytes,const uint64_t &totalDuration,const bool &isPlayer,TcpSession &sender +#define BroadcastFlowReportArgs const MediaInfo &args,const uint64_t &totalBytes,const uint64_t &totalDuration,const bool &isPlayer //未找到流后会广播该事件,请在监听该事件后去拉流或其他方式产生流,这样就能按需拉流了 extern const string kBroadcastNotFoundStream; diff --git a/src/Http/HttpFileManager.cpp b/src/Http/HttpFileManager.cpp index 367e6dc1..430df39f 100644 --- a/src/Http/HttpFileManager.cpp +++ b/src/Http/HttpFileManager.cpp @@ -46,6 +46,7 @@ static const string kAccessErrKey = "kAccessErrKey"; static const string kAccessHls = "kAccessHls"; static const string kHlsSuffix = "/hls.m3u8"; static const string kHlsData = "kHlsData"; +static const string kHlsHaveFindMediaSource = "kHlsHaveFindMediaSource"; static const string &getContentType(const char *name) { const char *dot; @@ -306,6 +307,8 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI if(is_hls){ //hls相关信息 (*cookie)[kHlsData].set(mediaInfo); + //hls未查找MediaSource + (*cookie)[kHlsHaveFindMediaSource].set(false); } callback(errMsg, cookie); }else{ @@ -370,49 +373,64 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo weak_ptr weakSession = sender.shared_from_this(); //判断是否有权限访问该文件 canAccessPath(sender, parser, mediaInfo, false, [cb, strFile, parser, is_hls, mediaInfo, weakSession , file_exist](const string &errMsg, const HttpServerCookie::Ptr &cookie) { - if (!errMsg.empty()) { - //文件鉴权失败 - StrCaseMap headerOut; + auto strongSession = weakSession.lock(); + if(!strongSession){ + //http客户端已经断开,不需要回复 + return; + } + if (!errMsg.empty()) { + //文件鉴权失败 + StrCaseMap headerOut; + if (cookie) { + headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); + } + cb("401 Unauthorized", "text/html", headerOut, std::make_shared(errMsg)); + return; + } + + auto response_file = [](const HttpServerCookie::Ptr &cookie, const HttpFileManager::invoker &cb, const string &strFile, const Parser &parser) { + StrCaseMap httpHeader; + if (cookie) { + httpHeader["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); + } + HttpSession::HttpResponseInvoker invoker = [&](const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body) { if (cookie) { - headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); + cookie->getLock(); + auto is_hls = (*cookie)[kAccessHls].get(); + if (is_hls) { + (*cookie)[kHlsData].get().addByteUsage(body->remainSize()); + } } - cb("401 Unauthorized", "text/html", headerOut, std::make_shared(errMsg)); + cb(codeOut.data(), getContentType(strFile.data()), headerOut, body); + }; + invoker.responseFile(parser.getValues(), httpHeader, strFile); + }; + + //如果程序未正常退出,会残余上次的hls文件,所以判断hls直播是否存在的关键不是文件存在与否 + //而是应该判断HlsMediaSource是否已注册,但是这样会每次获取m3u8文件时都会用MediaSource::findAsync判断一次 + //会导致程序性能低下,所以我们应该在cookie声明周期的第一次判断HlsMediaSource是否已经注册,后续通过文件存在与否判断 + if (!is_hls) { + //不是hls,直接回复文件或404 + response_file(cookie, cb, strFile, parser); + } else { + bool have_find_media_src = false; + if(cookie){ + have_find_media_src = (*cookie)[kHlsHaveFindMediaSource].get(); + if(!have_find_media_src){ + (*cookie)[kHlsHaveFindMediaSource].set(true); + } + } + if(have_find_media_src){ + //之前该cookie已经通过MediaSource::findAsync查找过了,所以现在只以文件系统查找结果为准 + response_file(cookie, cb, strFile, parser); return; } - - auto response_file = [](const HttpServerCookie::Ptr &cookie, const HttpFileManager::invoker &cb, const string &strFile, const Parser &parser) { - StrCaseMap httpHeader; - if (cookie) { - httpHeader["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); - } - HttpSession::HttpResponseInvoker invoker = [&](const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body) { - if (cookie) { - cookie->getLock(); - auto is_hls = (*cookie)[kAccessHls].get(); - if (is_hls) { - (*cookie)[kHlsData].get().addByteUsage(body->remainSize()); - } - } - cb(codeOut.data(), getContentType(strFile.data()), headerOut, body); - }; - invoker.responseFile(parser.getValues(), httpHeader, strFile); - }; - - if (file_exist || !is_hls) { - //不是hls或者文件存在,直接回复文件或404 + //hls文件不存在,我们等待其生成并延后回复 + MediaSource::findAsync(mediaInfo, strongSession, [response_file, cookie, cb, strFile, parser](const MediaSource::Ptr &src) { + //hls已经生成或者超时后仍未生成,那么不管怎么样都返回客户端 response_file(cookie, cb, strFile, parser); - } else { - //hls文件不存在,我们等待其生成并延后回复 - auto strongSession = weakSession.lock(); - if(!strongSession){ - //http客户端已经断开,不需要回复 - return; - } - MediaSource::findAsync(mediaInfo, strongSession, [response_file, cookie, cb, strFile, parser](const MediaSource::Ptr &src) { - //hls已经生成或者超时后仍未生成,那么不管怎么样都返回客户端 - response_file(cookie, cb, strFile, parser); - }); - } + }); + } }); } diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp index 4b8daf0d..320e7338 100644 --- a/src/Http/HttpSession.cpp +++ b/src/Http/HttpSession.cpp @@ -104,7 +104,7 @@ void HttpSession::onRecv(const Buffer::Ptr &pBuf) { void HttpSession::onError(const SockException& err) { if(_is_flv_stream){ //flv播放器 - WarnP(this) << "播放器(" + WarnP(this) << "FLV播放器(" << _mediaInfo._vhost << "/" << _mediaInfo._app << "/" << _mediaInfo._streamid @@ -112,12 +112,7 @@ void HttpSession::onError(const SockException& err) { GET_CONFIG(uint32_t,iFlowThreshold,General::kFlowThreshold); if(_ui64TotalBytes > iFlowThreshold * 1024){ - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, - _mediaInfo, - _ui64TotalBytes, - _ticker.createdTime()/1000, - true, - *this); + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _mediaInfo, _ui64TotalBytes, _ticker.createdTime()/1000, true); } return; } diff --git a/src/Record/HlsMediaSource.cpp b/src/Record/HlsMediaSource.cpp index e5b0af23..9ff3f010 100644 --- a/src/Record/HlsMediaSource.cpp +++ b/src/Record/HlsMediaSource.cpp @@ -42,21 +42,27 @@ void HlsCookieData::addReaderCount(){ _src = src; } } - } HlsCookieData::~HlsCookieData() { - if(_added){ + if (_added) { auto src = _src.lock(); - if(src){ + if (src) { src->modifyReaderCount(false); } + auto duration = (_ticker.createdTime() - _ticker.elapsedTime()) / 1000; + WarnL << "HLS播放器(" << _info._vhost << "/" << _info._app << "/" << _info._streamid << ")断开,播放时间:" << duration; + GET_CONFIG(uint32_t, iFlowThreshold, General::kFlowThreshold); + if (_bytes > iFlowThreshold * 1024) { + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _info, _bytes, duration, true); + } } } void HlsCookieData::addByteUsage(uint64_t bytes) { addReaderCount(); _bytes += bytes; + _ticker.resetTime(); } diff --git a/src/Record/HlsMediaSource.h b/src/Record/HlsMediaSource.h index a402b1cb..760676e1 100644 --- a/src/Record/HlsMediaSource.h +++ b/src/Record/HlsMediaSource.h @@ -27,6 +27,7 @@ #define ZLMEDIAKIT_HLSMEDIASOURCE_H #include +#include "Util/TimeTicker.h" #include "Common/MediaSource.h" namespace mediakit{ @@ -43,6 +44,7 @@ private: MediaInfo _info; bool _added = false; weak_ptr _src; + Ticker _ticker; }; class HlsMediaSource : public MediaSource { diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index 42518016..e7e93688 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -45,7 +45,7 @@ RtmpSession::~RtmpSession() { void RtmpSession::onError(const SockException& err) { bool isPlayer = !_pPublisherSrc; - WarnP(this) << (isPlayer ? "播放器(" : "推流器(") + WarnP(this) << (isPlayer ? "RTMP播放器(" : "RTMP推流器(") << _mediaInfo._vhost << "/" << _mediaInfo._app << "/" << _mediaInfo._streamid @@ -55,12 +55,7 @@ void RtmpSession::onError(const SockException& err) { GET_CONFIG(uint32_t,iFlowThreshold,General::kFlowThreshold); if(_ui64TotalBytes > iFlowThreshold * 1024){ - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, - _mediaInfo, - _ui64TotalBytes, - _ticker.createdTime()/1000, - isPlayer, - *this); + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _mediaInfo, _ui64TotalBytes, _ticker.createdTime()/1000, isPlayer); } } diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index 7d874cd8..b9a3c424 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -86,7 +86,7 @@ RtspSession::~RtspSession() { void RtspSession::onError(const SockException& err) { bool isPlayer = !_pushSrc; - WarnP(this) << (isPlayer ? "播放器(" : "推流器(") + WarnP(this) << (isPlayer ? "RTSP播放器(" : "RTSP推流器(") << _mediaInfo._vhost << "/" << _mediaInfo._app << "/" << _mediaInfo._streamid @@ -106,12 +106,7 @@ void RtspSession::onError(const SockException& err) { //流量统计事件广播 GET_CONFIG(uint32_t,iFlowThreshold,General::kFlowThreshold); if(_ui64TotalBytes > iFlowThreshold * 1024){ - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, - _mediaInfo, - _ui64TotalBytes, - _ticker.createdTime()/1000, - isPlayer, - *this); + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _mediaInfo, _ui64TotalBytes, _ticker.createdTime()/1000, isPlayer); } } From 345db4b9998b749199dbc434224bca073f85b832 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 29 Dec 2019 15:43:51 +0800 Subject: [PATCH 183/272] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebHook.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/server/WebHook.cpp b/server/WebHook.cpp index 058fee39..b28a5844 100644 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -276,13 +276,6 @@ void installWebHook(){ body["totalBytes"] = (Json::UInt64)totalBytes; body["duration"] = (Json::UInt64)totalDuration; body["player"] = isPlayer; - - body["schema"] = args._schema; - body["vhost"] = args._vhost; - body["app"] = args._app; - body["stream"] = args._streamid; - body["params"] = args._param_strs; - //执行hook do_http_hook(hook_flowreport,body, nullptr); }); From f48e6112537d6fee1e24e6849978f7ec9b2aa358 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 29 Dec 2019 15:55:20 +0800 Subject: [PATCH 184/272] =?UTF-8?q?=E4=BC=98=E5=8C=96http=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=9C=8D=E5=8A=A1=E5=99=A8=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/HttpBody.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Http/HttpBody.h b/src/Http/HttpBody.h index 9428ab07..66443ad0 100644 --- a/src/Http/HttpBody.h +++ b/src/Http/HttpBody.h @@ -72,6 +72,7 @@ public: * @param cb 回调函数 */ virtual void readDataAsync(uint32_t size,const function &cb){ +#if 0 if(size >= remainSize()){ //假如剩余数据很小,那么同步获取(为了优化性能) cb(readData(size)); @@ -85,6 +86,12 @@ public: cb(strongSelf->readData(size)); } }); +#else + //由于unix和linux是通过mmap的方式读取文件,所以把读文件操作放在后台线程并不能提高性能 + //反而会由于频繁的线程切换导致性能降低以及延时增加,所以我们默认同步获取文件内容 + //(其实并没有读,拷贝文件数据时在内核态完成文件读) + cb(readData(size)); +#endif } private: EventPoller::Ptr _async_read_thread; From 4d424590043fe3a8c323e58922151536b738ade5 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 29 Dec 2019 16:54:02 +0800 Subject: [PATCH 185/272] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/HttpBody.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Http/HttpBody.h b/src/Http/HttpBody.h index 66443ad0..c82163a4 100644 --- a/src/Http/HttpBody.h +++ b/src/Http/HttpBody.h @@ -50,7 +50,7 @@ class HttpBody : public std::enable_shared_from_this{ public: typedef std::shared_ptr Ptr; HttpBody(){ - _async_read_thread = WorkThreadPool::Instance().getPoller(); +// _async_read_thread = WorkThreadPool::Instance().getPoller(); } virtual ~HttpBody(){} @@ -94,7 +94,7 @@ public: #endif } private: - EventPoller::Ptr _async_read_thread; +// EventPoller::Ptr _async_read_thread; }; /** From 7e1e36784448845c858e2cf16b3bcfdde84595e8 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 29 Dec 2019 17:14:23 +0800 Subject: [PATCH 186/272] =?UTF-8?q?=E4=BC=98=E5=8C=96FFmpeg=E6=8B=89?= =?UTF-8?q?=E6=B5=81=E5=A4=B1=E8=B4=A5=E9=87=8D=E8=AF=95=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/FFmpegSource.cpp | 6 +++++- server/FFmpegSource.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/server/FFmpegSource.cpp b/server/FFmpegSource.cpp index 2ea621a8..e76df0ed 100644 --- a/server/FFmpegSource.cpp +++ b/server/FFmpegSource.cpp @@ -192,7 +192,11 @@ void FFmpegSource::startTimer(int timeout_ms) { //同步查找流 if (!src) { //流不在线,重新拉流 - strongSelf->play(strongSelf->_src_url, strongSelf->_dst_url, timeout_ms, [](const SockException &) {}); + if(strongSelf->_replay_ticker.elapsedTime() > 10 * 1000){ + //上次重试时间超过10秒,那么再重试FFmpeg拉流 + strongSelf->_replay_ticker.resetTime(); + strongSelf->play(strongSelf->_src_url, strongSelf->_dst_url, timeout_ms, [](const SockException &) {}); + } } }); } else { diff --git a/server/FFmpegSource.h b/server/FFmpegSource.h index f71091da..a11144d7 100644 --- a/server/FFmpegSource.h +++ b/server/FFmpegSource.h @@ -70,6 +70,7 @@ private: string _dst_url; function _onClose; std::weak_ptr _listener; + Ticker _replay_ticker; }; From 0063571f3a4879218626675aba53549f2d53ab55 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 29 Dec 2019 17:55:02 +0800 Subject: [PATCH 187/272] =?UTF-8?q?=E8=A7=A3=E5=86=B3hls=E4=B8=AD=E6=96=AD?= =?UTF-8?q?=E6=81=A2=E5=A4=8D=E6=97=B6=E6=92=AD=E6=94=BE=E5=99=A8=E8=AE=A1?= =?UTF-8?q?=E6=95=B0=E4=B8=8D=E5=87=86=E7=A1=AE=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/HttpFileManager.cpp | 14 ++++++------ src/Record/HlsMediaSource.cpp | 13 ++++++++--- src/Record/HlsMediaSource.h | 42 ++++++++++++++++++++++------------- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/Http/HttpFileManager.cpp b/src/Http/HttpFileManager.cpp index 430df39f..7cd2d26f 100644 --- a/src/Http/HttpFileManager.cpp +++ b/src/Http/HttpFileManager.cpp @@ -258,13 +258,13 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI auto lck = cookie->getLock(); auto accessErr = (*cookie)[kAccessErrKey].get(); auto cookiePath = (*cookie)[kCookiePathKey].get(); - auto is_hls = (*cookie)[kAccessHls].get(); + auto cookie_is_hls = (*cookie)[kAccessHls].get(); if (path.find(cookiePath) == 0) { //上次cookie是限定本目录 if (accessErr.empty()) { //上次鉴权成功 - if(is_hls){ - //如果播放的是hls,那么刷新hls的cookie + if(cookie_is_hls){ + //如果播放的是hls,那么刷新hls的cookie(获取ts文件也会刷新) cookie->updateTime(); cookie_from_header = false; } @@ -284,8 +284,8 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI bool is_hls = mediaInfo._schema == HLS_SCHEMA; //该用户从来未获取过cookie,这个时候我们广播是否允许该用户访问该http目录 - HttpSession::HttpAccessPathInvoker accessPathInvoker = [callback, uid, path, is_dir, is_hls, mediaInfo] - (const string &errMsg, const string &cookie_path_in, int cookieLifeSecond) { + HttpSession::HttpAccessPathInvoker accessPathInvoker = [callback, uid, path, is_dir, is_hls, mediaInfo] + (const string &errMsg, const string &cookie_path_in, int cookieLifeSecond) { HttpServerCookie::Ptr cookie; if (cookieLifeSecond) { //本次鉴权设置了有效期,我们把鉴权结果缓存在cookie中 @@ -388,13 +388,13 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo return; } - auto response_file = [](const HttpServerCookie::Ptr &cookie, const HttpFileManager::invoker &cb, const string &strFile, const Parser &parser) { + auto response_file = [file_exist](const HttpServerCookie::Ptr &cookie, const HttpFileManager::invoker &cb, const string &strFile, const Parser &parser) { StrCaseMap httpHeader; if (cookie) { httpHeader["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); } HttpSession::HttpResponseInvoker invoker = [&](const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body) { - if (cookie) { + if (cookie && file_exist) { cookie->getLock(); auto is_hls = (*cookie)[kAccessHls].get(); if (is_hls) { diff --git a/src/Record/HlsMediaSource.cpp b/src/Record/HlsMediaSource.cpp index 9ff3f010..1f48723a 100644 --- a/src/Record/HlsMediaSource.cpp +++ b/src/Record/HlsMediaSource.cpp @@ -30,22 +30,29 @@ namespace mediakit{ HlsCookieData::HlsCookieData(const MediaInfo &info) { _info = info; + _added = std::make_shared(false); addReaderCount(); } void HlsCookieData::addReaderCount(){ - if(!_added){ + if(!*_added){ auto src = dynamic_pointer_cast(MediaSource::find(HLS_SCHEMA,_info._vhost,_info._app,_info._streamid)); if(src){ src->modifyReaderCount(true); - _added = true; + *_added = true; _src = src; + _ring_reader = src->getRing()->attach(EventPollerPool::Instance().getPoller()); + auto added = _added; + _ring_reader->setDetachCB([added](){ + //HlsMediaSource已经销毁 + *added = false; + }); } } } HlsCookieData::~HlsCookieData() { - if (_added) { + if (*_added) { auto src = _src.lock(); if (src) { src->modifyReaderCount(false); diff --git a/src/Record/HlsMediaSource.h b/src/Record/HlsMediaSource.h index 760676e1..7dd83c71 100644 --- a/src/Record/HlsMediaSource.h +++ b/src/Record/HlsMediaSource.h @@ -31,32 +31,25 @@ #include "Common/MediaSource.h" namespace mediakit{ -class HlsMediaSource; -class HlsCookieData{ -public: - HlsCookieData(const MediaInfo &info); - ~HlsCookieData(); - void addByteUsage(uint64_t bytes); -private: - void addReaderCount(); -private: - uint64_t _bytes = 0; - MediaInfo _info; - bool _added = false; - weak_ptr _src; - Ticker _ticker; -}; - class HlsMediaSource : public MediaSource { public: friend class HlsCookieData; + typedef RingBuffer RingType; typedef std::shared_ptr Ptr; HlsMediaSource(const string &vhost, const string &app, const string &stream_id) : MediaSource(HLS_SCHEMA, vhost, app, stream_id){ _readerCount = 0; + _ring = std::make_shared(); } virtual ~HlsMediaSource() = default; + /** + * 获取媒体源的环形缓冲 + */ + const RingType::Ptr &getRing() const { + return _ring; + } + /** * 获取播放器个数 * @return @@ -93,6 +86,23 @@ private: private: atomic_int _readerCount; bool _registed = false; + RingType::Ptr _ring; +}; + +class HlsCookieData{ +public: + HlsCookieData(const MediaInfo &info); + ~HlsCookieData(); + void addByteUsage(uint64_t bytes); +private: + void addReaderCount(); +private: + uint64_t _bytes = 0; + MediaInfo _info; + std::shared_ptr _added; + weak_ptr _src; + Ticker _ticker; + HlsMediaSource::RingType::RingReader::Ptr _ring_reader; }; From 84de9720b5c517da4229dd46ba27e554998a9af0 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 29 Dec 2019 18:32:26 +0800 Subject: [PATCH 188/272] =?UTF-8?q?=E6=9B=B4=E6=96=B0ZLToolKit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 77c2a6d9..ba5a796d 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 77c2a6d9f94ab6e7e40bc1f223eb206d2542cb72 +Subproject commit ba5a796d583cd2906e06ec15c0d942e484ea1dbf From bedf088e67d721ab697de20d64579565c9531396 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 2 Jan 2020 11:56:44 +0800 Subject: [PATCH 189/272] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/config.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/config.ini b/conf/config.ini index e60dc830..8010e4e8 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -95,7 +95,7 @@ timeoutSec=10 #http服务器字符编码,windows上默认gb2312 charSet=utf-8 #http链接超时时间 -keepAliveSecond=10 +keepAliveSecond=30 #http请求体最大字节数,如果post的body太大,则不适合缓存body在内存 maxReqSize=4096 #404网页内容,用户可以自定义404网页 @@ -192,7 +192,7 @@ port=554 #rtsps服务器监听地址 sslport=322 #在接收rtsp推流时,是否重新生成时间戳(很多推流器的时间戳着实很烂) -modifyStamp=1 +modifyStamp=0 [shell] #调试telnet服务器接受最大bufffer大小 From e31c1ee207e13101a57b3b42d82aace9c530684d Mon Sep 17 00:00:00 2001 From: Gemfield Date: Thu, 2 Jan 2020 12:47:12 +0800 Subject: [PATCH 190/272] Add optional customized_path parameter for startRecord --- api/include/mk_recorder.h | 2 +- api/source/mk_recorder.cpp | 4 ++-- server/WebApi.cpp | 2 ++ src/Common/MultiMediaSourceMuxer.h | 4 ++-- src/Record/HlsMakerImp.cpp | 5 +++-- src/Record/Recorder.cpp | 28 ++++++++++++++++++---------- src/Record/Recorder.h | 2 +- 7 files changed, 29 insertions(+), 18 deletions(-) diff --git a/api/include/mk_recorder.h b/api/include/mk_recorder.h index 7419fce3..a09b0147 100644 --- a/api/include/mk_recorder.h +++ b/api/include/mk_recorder.h @@ -83,7 +83,7 @@ API_EXPORT int API_CALL mk_recorder_status(int type, const char *vhost, const ch * @param continue_record 流注销时是否继续等待录制还是立即停止录制 * @return 0代表成功,负数代表失败 */ -API_EXPORT int API_CALL mk_recorder_start(int type, const char *vhost, const char *app, const char *stream,int wait_for_record, int continue_record); +API_EXPORT int API_CALL mk_recorder_start(int type, const char *vhost, const char *app, const char *stream, const char *customized_path, int wait_for_record, int continue_record); /** * 停止录制 diff --git a/api/source/mk_recorder.cpp b/api/source/mk_recorder.cpp index 7ac4968c..175a1e78 100644 --- a/api/source/mk_recorder.cpp +++ b/api/source/mk_recorder.cpp @@ -57,9 +57,9 @@ API_EXPORT int API_CALL mk_recorder_status(int type, const char *vhost, const ch return Recorder::getRecordStatus((Recorder::type)type,vhost,app,stream); } -API_EXPORT int API_CALL mk_recorder_start(int type, const char *vhost, const char *app, const char *stream,int wait_for_record, int continue_record){ +API_EXPORT int API_CALL mk_recorder_start(int type, const char *vhost, const char *app, const char *stream,const char *customized_path,int wait_for_record, int continue_record){ assert(vhost && app && stream); - return Recorder::startRecord((Recorder::type)type,vhost,app,stream,wait_for_record,continue_record); + return Recorder::startRecord((Recorder::type)type,vhost,app,stream,customized_path,wait_for_record,continue_record); } API_EXPORT int API_CALL mk_recorder_stop(int type, const char *vhost, const char *app, const char *stream){ diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 91a158f6..04dd3715 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -745,10 +745,12 @@ void installWebApi() { API_REGIST(api,startRecord,{ CHECK_SECRET(); CHECK_ARGS("type","vhost","app","stream","wait_for_record","continue_record"); + int result = Recorder::startRecord((Recorder::type)allArgs["type"].as(), allArgs["vhost"], allArgs["app"], allArgs["stream"], + allArgs["customized_path"], allArgs["wait_for_record"], allArgs["continue_record"]); val["result"] = result; diff --git a/src/Common/MultiMediaSourceMuxer.h b/src/Common/MultiMediaSourceMuxer.h index b711f638..0ec1f2ed 100644 --- a/src/Common/MultiMediaSourceMuxer.h +++ b/src/Common/MultiMediaSourceMuxer.h @@ -53,11 +53,11 @@ public: } if(enable_hls){ - Recorder::startRecord(Recorder::type_hls,vhost, app, stream, true, false); + Recorder::startRecord(Recorder::type_hls,vhost, app, stream, "", true, false); } if(enable_mp4){ - Recorder::startRecord(Recorder::type_mp4,vhost, app, stream, true, false); + Recorder::startRecord(Recorder::type_mp4,vhost, app, stream, "", true, false); } _get_hls_media_source = [vhost,app,stream](){ diff --git a/src/Record/HlsMakerImp.cpp b/src/Record/HlsMakerImp.cpp index 39fe77b9..cdbe8e01 100644 --- a/src/Record/HlsMakerImp.cpp +++ b/src/Record/HlsMakerImp.cpp @@ -58,8 +58,9 @@ string HlsMakerImp::onOpenSegment(int index) { string segment_name , segment_path; { auto strDate = getTimeStr("%Y-%m-%d"); - auto strTime = getTimeStr("%H-%M-%S"); - segment_name = StrPrinter << strDate + "/" + strTime << "_" << index << ".ts"; + auto strHour = getTimeStr("%H"); + auto strTime = getTimeStr("%M-%S"); + segment_name = StrPrinter << strDate + "/" + strHour + "/" + strTime << "_" << index << ".ts"; segment_path = _path_prefix + "/" + segment_name; if(isLive()){ _segment_file_paths.emplace(index,segment_path); diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index e3ef8106..851a9d32 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -34,7 +34,7 @@ using namespace toolkit; namespace mediakit { -MediaSinkInterface *createHlsRecorder(const string &strVhost_tmp, const string &strApp, const string &strId) { +MediaSinkInterface *createHlsRecorder(const string &strVhost_tmp, const string &strApp, const string &strId, const string &customized_path) { #if defined(ENABLE_HLS) GET_CONFIG(bool, enableVhost, General::kEnableVhost); GET_CONFIG(string, hlsPath, Hls::kFilePath); @@ -53,6 +53,10 @@ MediaSinkInterface *createHlsRecorder(const string &strVhost_tmp, const string & } else { m3u8FilePath = strApp + "/" + strId + "/hls.m3u8"; } + //Here we use the customized file path. + if(!customized_path.empty()){ + m3u8FilePath = customized_path + "/hls.m3u8"; + } m3u8FilePath = File::absolutePath(m3u8FilePath, hlsPath); auto ret = new HlsRecorder(m3u8FilePath, params); ret->setMediaSource(strVhost, strApp, strId); @@ -62,7 +66,7 @@ MediaSinkInterface *createHlsRecorder(const string &strVhost_tmp, const string & #endif //defined(ENABLE_HLS) } -MediaSinkInterface *createMP4Recorder(const string &strVhost_tmp, const string &strApp, const string &strId) { +MediaSinkInterface *createMP4Recorder(const string &strVhost_tmp, const string &strApp, const string &strId, const string &customized_path) { #if defined(ENABLE_MP4RECORD) GET_CONFIG(bool, enableVhost, General::kEnableVhost); GET_CONFIG(string, recordPath, Record::kFilePath); @@ -80,6 +84,10 @@ MediaSinkInterface *createMP4Recorder(const string &strVhost_tmp, const string & } else { mp4FilePath = recordAppName + "/" + strApp + "/" + strId + "/"; } + //Here we use the customized file path. + if(!customized_path.empty()){ + mp4FilePath = customized_path + "/"; + } mp4FilePath = File::absolutePath(mp4FilePath, recordPath); return new MP4Recorder(mp4FilePath, strVhost, strApp, strId); #else @@ -193,7 +201,7 @@ public: return it->second->getRecorder(); } - int startRecord(const string &vhost, const string &app, const string &stream_id, bool waitForRecord, bool continueRecord) { + int startRecord(const string &vhost, const string &app, const string &stream_id, const string &customized_path, bool waitForRecord, bool continueRecord) { auto key = getRecorderKey(vhost, app, stream_id); lock_guard lck(_recorder_mtx); if (getRecordStatus_l(key) != Recorder::status_not_record) { @@ -207,7 +215,7 @@ public: return -1; } - auto recorder = MediaSinkInterface::Ptr(createRecorder(vhost, app, stream_id)); + auto recorder = MediaSinkInterface::Ptr(createRecorder(vhost, app, stream_id, customized_path)); if (!recorder) { // 创建录制器失败 return -2; @@ -326,14 +334,14 @@ private: return vhost + "/" + app + "/" + stream_id; } - MediaSinkInterface *createRecorder(const string &vhost, const string &app, const string &stream_id) { + MediaSinkInterface *createRecorder(const string &vhost, const string &app, const string &stream_id, const string &customized_path) { MediaSinkInterface *ret = nullptr; switch (type) { case Recorder::type_hls: - ret = createHlsRecorder(vhost, app, stream_id); + ret = createHlsRecorder(vhost, app, stream_id,customized_path); break; case Recorder::type_mp4: - ret = createMP4Recorder(vhost, app, stream_id); + ret = createMP4Recorder(vhost, app, stream_id,customized_path); break; default: break; @@ -385,12 +393,12 @@ std::shared_ptr Recorder::getRecorder(type type, const strin } -int Recorder::startRecord(Recorder::type type, const string &vhost, const string &app, const string &stream_id, bool waitForRecord, bool continueRecord) { +int Recorder::startRecord(Recorder::type type, const string &vhost, const string &app, const string &stream_id, const string &customized_path, bool waitForRecord, bool continueRecord) { switch (type){ case type_mp4: - return MediaSourceWatcher::Instance().startRecord(vhost,app,stream_id,waitForRecord,continueRecord); + return MediaSourceWatcher::Instance().startRecord(vhost,app,stream_id,customized_path,waitForRecord,continueRecord); case type_hls: - return MediaSourceWatcher::Instance().startRecord(vhost,app,stream_id,waitForRecord,continueRecord); + return MediaSourceWatcher::Instance().startRecord(vhost,app,stream_id,customized_path,waitForRecord,continueRecord); } WarnL << "unknown record type: " << type; return -3; diff --git a/src/Record/Recorder.h b/src/Record/Recorder.h index 26921214..84107950 100644 --- a/src/Record/Recorder.h +++ b/src/Record/Recorder.h @@ -73,7 +73,7 @@ public: * @param continueRecord 流注销时是否继续等待录制还是立即停止录制 * @return 0代表成功,负数代表失败 */ - static int startRecord(type type, const string &vhost, const string &app, const string &stream_id,bool waitForRecord, bool continueRecord); + static int startRecord(type type, const string &vhost, const string &app, const string &stream_id,const string &customized_path,bool waitForRecord, bool continueRecord); /** * 停止录制 From d08295551032674a18bcbbf41d54517e692f310a Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 2 Jan 2020 16:29:20 +0800 Subject: [PATCH 191/272] =?UTF-8?q?=E5=90=88=E5=B9=B6#199?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mk_recorder.h | 1 + api/source/mk_recorder.cpp | 2 +- src/Record/Recorder.h | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/api/include/mk_recorder.h b/api/include/mk_recorder.h index a09b0147..c60e0e54 100644 --- a/api/include/mk_recorder.h +++ b/api/include/mk_recorder.h @@ -79,6 +79,7 @@ API_EXPORT int API_CALL mk_recorder_status(int type, const char *vhost, const ch * @param vhost 虚拟主机 * @param app 应用名 * @param stream 流id + * @param customized_path 录像文件保存自定义目录,默认为空或null则自动生成 * @param wait_for_record 是否等待流注册后再录制,未注册时,置false将返回失败 * @param continue_record 流注销时是否继续等待录制还是立即停止录制 * @return 0代表成功,负数代表失败 diff --git a/api/source/mk_recorder.cpp b/api/source/mk_recorder.cpp index 175a1e78..efd29c42 100644 --- a/api/source/mk_recorder.cpp +++ b/api/source/mk_recorder.cpp @@ -59,7 +59,7 @@ API_EXPORT int API_CALL mk_recorder_status(int type, const char *vhost, const ch API_EXPORT int API_CALL mk_recorder_start(int type, const char *vhost, const char *app, const char *stream,const char *customized_path,int wait_for_record, int continue_record){ assert(vhost && app && stream); - return Recorder::startRecord((Recorder::type)type,vhost,app,stream,customized_path,wait_for_record,continue_record); + return Recorder::startRecord((Recorder::type)type,vhost,app,stream,customized_path ? customized_path : "",wait_for_record,continue_record); } API_EXPORT int API_CALL mk_recorder_stop(int type, const char *vhost, const char *app, const char *stream){ diff --git a/src/Record/Recorder.h b/src/Record/Recorder.h index 84107950..67aef75b 100644 --- a/src/Record/Recorder.h +++ b/src/Record/Recorder.h @@ -69,6 +69,7 @@ public: * @param vhost 虚拟主机 * @param app 应用名 * @param stream_id 流id + * @param customized_path 录像文件保存自定义目录,默认为空则自动生成 * @param waitForRecord 是否等待流注册后再录制,未注册时,置false将返回失败 * @param continueRecord 流注销时是否继续等待录制还是立即停止录制 * @return 0代表成功,负数代表失败 From 52d831e9907e009cc82fced4a371b28467703029 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 2 Jan 2020 17:46:20 +0800 Subject: [PATCH 192/272] =?UTF-8?q?=E7=AE=80=E5=8C=96hls=20cookie=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/HttpFileManager.cpp | 67 +++++++++++++++++++++--------------- src/Record/HlsMediaSource.h | 1 + 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/src/Http/HttpFileManager.cpp b/src/Http/HttpFileManager.cpp index 7cd2d26f..6bb1895f 100644 --- a/src/Http/HttpFileManager.cpp +++ b/src/Http/HttpFileManager.cpp @@ -41,12 +41,27 @@ namespace mediakit { // 假如播放器在60秒内都未访问该cookie,那么将重新触发hls播放鉴权 static int kHlsCookieSecond = 60; static const string kCookieName = "ZL_COOKIE"; -static const string kCookiePathKey = "kCookiePathKey"; -static const string kAccessErrKey = "kAccessErrKey"; -static const string kAccessHls = "kAccessHls"; static const string kHlsSuffix = "/hls.m3u8"; -static const string kHlsData = "kHlsData"; -static const string kHlsHaveFindMediaSource = "kHlsHaveFindMediaSource"; + +class HttpCookieAttachment{ +public: + HttpCookieAttachment() {}; + ~HttpCookieAttachment() {}; +public: + //cookie生效作用域,本cookie只对该目录下的文件生效 + string _path; + //上次鉴权失败信息,为空则上次鉴权成功 + string _err_msg; + //本cookie是否为hls直播的 + bool _is_hls = false; + //hls直播时的其他一些信息,主要用于播放器个数计数以及流量计数 + HlsCookieData::Ptr _hls_data; + //如果是hls直播,那么判断该cookie是否使用过MediaSource::findAsync查找过 + //如果程序未正常退出,会残余上次的hls文件,所以判断hls直播是否存在的关键不是文件存在与否 + //而是应该判断HlsMediaSource是否已注册,但是这样会每次获取m3u8文件时都会用MediaSource::findAsync判断一次 + //会导致程序性能低下,所以我们应该在cookie声明周期的第一次判断HlsMediaSource是否已经注册,后续通过文件存在与否判断 + bool _have_find_media_source = false; +}; static const string &getContentType(const char *name) { const char *dot; @@ -256,14 +271,12 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI if (cookie) { //找到了cookie,对cookie上锁先 auto lck = cookie->getLock(); - auto accessErr = (*cookie)[kAccessErrKey].get(); - auto cookiePath = (*cookie)[kCookiePathKey].get(); - auto cookie_is_hls = (*cookie)[kAccessHls].get(); - if (path.find(cookiePath) == 0) { + auto attachment = (*cookie)[kCookieName].get(); + if (path.find(attachment._path) == 0) { //上次cookie是限定本目录 - if (accessErr.empty()) { + if (attachment._err_msg.empty()) { //上次鉴权成功 - if(cookie_is_hls){ + if(attachment._is_hls){ //如果播放的是hls,那么刷新hls的cookie(获取ts文件也会刷新) cookie->updateTime(); cookie_from_header = false; @@ -274,7 +287,7 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI //上次鉴权失败,但是如果url参数发生变更,那么也重新鉴权下 if (parser.Params().empty() || parser.Params() == cookie->getUid()) { //url参数未变,或者本来就没有url参数,那么判断本次请求为重复请求,无访问权限 - callback(accessErr, cookie_from_header ? nullptr : cookie); + callback(attachment._err_msg, cookie_from_header ? nullptr : cookie); return; } } @@ -298,18 +311,20 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI cookie = HttpCookieManager::Instance().addCookie(kCookieName, uid, cookieLifeSecond); //对cookie上锁 auto lck = cookie->getLock(); + HttpCookieAttachment attachment; //记录用户能访问的路径 - (*cookie)[kCookiePathKey].set(cookie_path); + attachment._path = cookie_path; //记录能否访问 - (*cookie)[kAccessErrKey].set(errMsg); + attachment._err_msg = errMsg; //记录访问的是否为hls - (*cookie)[kAccessHls].set(is_hls); + attachment._is_hls = is_hls; if(is_hls){ //hls相关信息 - (*cookie)[kHlsData].set(mediaInfo); + attachment._hls_data = std::make_shared(mediaInfo); //hls未查找MediaSource - (*cookie)[kHlsHaveFindMediaSource].set(false); + attachment._have_find_media_source = false; } + (*cookie)[kCookieName].set(std::move(attachment)); callback(errMsg, cookie); }else{ callback(errMsg, nullptr); @@ -382,7 +397,7 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo //文件鉴权失败 StrCaseMap headerOut; if (cookie) { - headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); + headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookieName].get()._path); } cb("401 Unauthorized", "text/html", headerOut, std::make_shared(errMsg)); return; @@ -391,14 +406,14 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo auto response_file = [file_exist](const HttpServerCookie::Ptr &cookie, const HttpFileManager::invoker &cb, const string &strFile, const Parser &parser) { StrCaseMap httpHeader; if (cookie) { - httpHeader["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); + httpHeader["Set-Cookie"] = cookie->getCookie((*cookie)[kCookieName].get()._path); } HttpSession::HttpResponseInvoker invoker = [&](const string &codeOut, const StrCaseMap &headerOut, const HttpBody::Ptr &body) { if (cookie && file_exist) { cookie->getLock(); - auto is_hls = (*cookie)[kAccessHls].get(); + auto is_hls = (*cookie)[kCookieName].get()._is_hls; if (is_hls) { - (*cookie)[kHlsData].get().addByteUsage(body->remainSize()); + (*cookie)[kCookieName].get()._hls_data->addByteUsage(body->remainSize()); } } cb(codeOut.data(), getContentType(strFile.data()), headerOut, body); @@ -406,18 +421,16 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo invoker.responseFile(parser.getValues(), httpHeader, strFile); }; - //如果程序未正常退出,会残余上次的hls文件,所以判断hls直播是否存在的关键不是文件存在与否 - //而是应该判断HlsMediaSource是否已注册,但是这样会每次获取m3u8文件时都会用MediaSource::findAsync判断一次 - //会导致程序性能低下,所以我们应该在cookie声明周期的第一次判断HlsMediaSource是否已经注册,后续通过文件存在与否判断 if (!is_hls) { //不是hls,直接回复文件或404 response_file(cookie, cb, strFile, parser); } else { + //是hls直播,判断是否存在 bool have_find_media_src = false; if(cookie){ - have_find_media_src = (*cookie)[kHlsHaveFindMediaSource].get(); + have_find_media_src = (*cookie)[kCookieName].get()._have_find_media_source; if(!have_find_media_src){ - (*cookie)[kHlsHaveFindMediaSource].set(true); + (*cookie)[kCookieName].get()._have_find_media_source = true; } } if(have_find_media_src){ @@ -476,7 +489,7 @@ void HttpFileManager::onAccessPath(TcpSession &sender, Parser &parser, const Htt } StrCaseMap headerOut; if (cookie) { - headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); + headerOut["Set-Cookie"] = cookie->getCookie((*cookie)[kCookieName].get()._path); } cb(errMsg.empty() ? "200 OK" : "401 Unauthorized", "text/html", headerOut, std::make_shared(strMenu)); }); diff --git a/src/Record/HlsMediaSource.h b/src/Record/HlsMediaSource.h index 7dd83c71..df0379be 100644 --- a/src/Record/HlsMediaSource.h +++ b/src/Record/HlsMediaSource.h @@ -91,6 +91,7 @@ private: class HlsCookieData{ public: + typedef std::shared_ptr Ptr; HlsCookieData(const MediaInfo &info); ~HlsCookieData(); void addByteUsage(uint64_t bytes); From d482dc1c05b319efa3167d05e6d99b6aadaab7b9 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 2 Jan 2020 18:24:11 +0800 Subject: [PATCH 193/272] =?UTF-8?q?=E5=AE=8C=E5=96=84http=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=99=A8mime=E7=B1=BB=E5=88=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/HttpFileManager.cpp | 146 +++++++++++++++++++++++++++-------- src/Http/HttpFileManager.h | 7 ++ 2 files changed, 121 insertions(+), 32 deletions(-) diff --git a/src/Http/HttpFileManager.cpp b/src/Http/HttpFileManager.cpp index 6bb1895f..2ffc0819 100644 --- a/src/Http/HttpFileManager.cpp +++ b/src/Http/HttpFileManager.cpp @@ -63,46 +63,128 @@ public: bool _have_find_media_source = false; }; -static const string &getContentType(const char *name) { +static const char *s_mime_src[][2] = { + {"html", "text/html"}, + {"htm", "text/html"}, + {"shtml", "text/html"}, + {"css", "text/css"}, + {"xml", "text/xml"}, + {"gif", "image/gif"}, + {"jpeg", "image/jpeg"}, + {"jpg", "image/jpeg"}, + {"js", "application/javascript"}, + {"map", "application/javascript" }, + {"atom", "application/atom+xml"}, + {"rss", "application/rss+xml"}, + {"mml", "text/mathml"}, + {"txt", "text/plain"}, + {"jad", "text/vnd.sun.j2me.app-descriptor"}, + {"wml", "text/vnd.wap.wml"}, + {"htc", "text/x-component"}, + {"png", "image/png"}, + {"tif", "image/tiff"}, + {"tiff", "image/tiff"}, + {"wbmp", "image/vnd.wap.wbmp"}, + {"ico", "image/x-icon"}, + {"jng", "image/x-jng"}, + {"bmp", "image/x-ms-bmp"}, + {"svg", "image/svg+xml"}, + {"svgz", "image/svg+xml"}, + {"webp", "image/webp"}, + {"woff", "application/font-woff"}, + {"woff2","application/font-woff" }, + {"jar", "application/java-archive"}, + {"war", "application/java-archive"}, + {"ear", "application/java-archive"}, + {"json", "application/json"}, + {"hqx", "application/mac-binhex40"}, + {"doc", "application/msword"}, + {"pdf", "application/pdf"}, + {"ps", "application/postscript"}, + {"eps", "application/postscript"}, + {"ai", "application/postscript"}, + {"rtf", "application/rtf"}, + {"m3u8", "application/vnd.apple.mpegurl"}, + {"xls", "application/vnd.ms-excel"}, + {"eot", "application/vnd.ms-fontobject"}, + {"ppt", "application/vnd.ms-powerpoint"}, + {"wmlc", "application/vnd.wap.wmlc"}, + {"kml", "application/vnd.google-earth.kml+xml"}, + {"kmz", "application/vnd.google-earth.kmz"}, + {"7z", "application/x-7z-compressed"}, + {"cco", "application/x-cocoa"}, + {"jardiff", "application/x-java-archive-diff"}, + {"jnlp", "application/x-java-jnlp-file"}, + {"run", "application/x-makeself"}, + {"pl", "application/x-perl"}, + {"pm", "application/x-perl"}, + {"prc", "application/x-pilot"}, + {"pdb", "application/x-pilot"}, + {"rar", "application/x-rar-compressed"}, + {"rpm", "application/x-redhat-package-manager"}, + {"sea", "application/x-sea"}, + {"swf", "application/x-shockwave-flash"}, + {"sit", "application/x-stuffit"}, + {"tcl", "application/x-tcl"}, + {"tk", "application/x-tcl"}, + {"der", "application/x-x509-ca-cert"}, + {"pem", "application/x-x509-ca-cert"}, + {"crt", "application/x-x509-ca-cert"}, + {"xpi", "application/x-xpinstall"}, + {"xhtml", "application/xhtml+xml"}, + {"xspf", "application/xspf+xml"}, + {"zip", "application/zip"}, + {"bin", "application/octet-stream"}, + {"exe", "application/octet-stream"}, + {"dll", "application/octet-stream"}, + {"deb", "application/octet-stream"}, + {"dmg", "application/octet-stream"}, + {"iso", "application/octet-stream"}, + {"img", "application/octet-stream"}, + {"msi", "application/octet-stream"}, + {"msp", "application/octet-stream"}, + {"msm", "application/octet-stream"}, + {"docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, + {"xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, + {"pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, + {"mid", "audio/midi"}, + {"midi", "audio/midi"}, + {"kar", "audio/midi"}, + {"mp3", "audio/mpeg"}, + {"ogg", "audio/ogg"}, + {"m4a", "audio/x-m4a"}, + {"ra", "audio/x-realaudio"}, + {"3gpp", "video/3gpp"}, + {"3gp", "video/3gpp"}, + {"ts", "video/mp2t"}, + {"mp4", "video/mp4"}, + {"mpeg", "video/mpeg"}, + {"mpg", "video/mpeg"}, + {"mov", "video/quicktime"}, + {"webm", "video/webm"}, + {"flv", "video/x-flv"}, + {"m4v", "video/x-m4v"}, + {"mng", "video/x-mng"}, + {"asx", "video/x-ms-asf"}, + {"asf", "video/x-ms-asf"}, + {"wmv", "video/x-ms-wmv"}, + {"avi", "video/x-msvideo"}, +}; + +const string &HttpFileManager::getContentType(const char *name) { const char *dot; dot = strrchr(name, '.'); static StrCaseMap mapType; static onceToken token([&]() { - mapType.emplace(".html", "text/html"); - mapType.emplace(".htm", "text/html"); - mapType.emplace(".mp4", "video/mp4"); - mapType.emplace(".mkv", "video/x-matroska"); - mapType.emplace(".rmvb", "application/vnd.rn-realmedia"); - mapType.emplace(".rm", "application/vnd.rn-realmedia"); - mapType.emplace(".m3u8", "application/vnd.apple.mpegurl"); - mapType.emplace(".jpg", "image/jpeg"); - mapType.emplace(".jpeg", "image/jpeg"); - mapType.emplace(".gif", "image/gif"); - mapType.emplace(".png", "image/png"); - mapType.emplace(".ico", "image/x-icon"); - mapType.emplace(".css", "text/css"); - mapType.emplace(".js", "application/javascript"); - mapType.emplace(".au", "audio/basic"); - mapType.emplace(".wav", "audio/wav"); - mapType.emplace(".avi", "video/x-msvideo"); - mapType.emplace(".mov", "video/quicktime"); - mapType.emplace(".qt", "video/quicktime"); - mapType.emplace(".mpeg", "video/mpeg"); - mapType.emplace(".mpe", "video/mpeg"); - mapType.emplace(".vrml", "model/vrml"); - mapType.emplace(".wrl", "model/vrml"); - mapType.emplace(".midi", "audio/midi"); - mapType.emplace(".mid", "audio/midi"); - mapType.emplace(".mp3", "audio/mpeg"); - mapType.emplace(".ogg", "application/ogg"); - mapType.emplace(".pac", "application/x-ns-proxy-autoconfig"); - mapType.emplace(".flv", "video/x-flv"); + for (unsigned int i = 0; i < sizeof (s_mime_src) / sizeof (s_mime_src[0]); ++i) { + mapType.emplace(s_mime_src[i][0], s_mime_src[i][1]); + } }); static string defaultType = "text/plain"; if (!dot) { return defaultType; } - auto it = mapType.find(dot); + auto it = mapType.find(dot + 1); if (it == mapType.end()) { return defaultType; } @@ -416,7 +498,7 @@ static void accessFile(TcpSession &sender, const Parser &parser, const MediaInfo (*cookie)[kCookieName].get()._hls_data->addByteUsage(body->remainSize()); } } - cb(codeOut.data(), getContentType(strFile.data()), headerOut, body); + cb(codeOut.data(), HttpFileManager::getContentType(strFile.data()), headerOut, body); }; invoker.responseFile(parser.getValues(), httpHeader, strFile); }; diff --git a/src/Http/HttpFileManager.h b/src/Http/HttpFileManager.h index 4421431a..dc3a613e 100644 --- a/src/Http/HttpFileManager.h +++ b/src/Http/HttpFileManager.h @@ -69,6 +69,13 @@ public: * @param cb 回调对象 */ static void onAccessPath(TcpSession &sender, Parser &parser, const invoker &cb); + + /** + * 获取mime值 + * @param name 文件后缀 + * @return mime值 + */ + static const string &getContentType(const char *name); private: HttpFileManager() = delete; ~HttpFileManager() = delete; From cdba214e79ee80855db14c0b6b37f3a348015273 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 3 Jan 2020 14:27:26 +0800 Subject: [PATCH 194/272] =?UTF-8?q?MP4=E5=BD=95=E5=88=B6=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=85=BC=E5=AE=B9=E6=80=A7=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/MP4Muxer.cpp | 82 ++++++++++++++++++++++++++++------------- src/Record/MP4Muxer.h | 1 + 2 files changed, 57 insertions(+), 26 deletions(-) diff --git a/src/Record/MP4Muxer.cpp b/src/Record/MP4Muxer.cpp index 5d7c68e9..51f5e440 100644 --- a/src/Record/MP4Muxer.cpp +++ b/src/Record/MP4Muxer.cpp @@ -73,10 +73,6 @@ void MP4Muxer::resetTracks() { } void MP4Muxer::inputFrame(const Frame::Ptr &frame) { - if(frame->configFrame()){ - //忽略配置帧 - return; - } auto it = _codec_to_trackid.find(frame->getCodecId()); if(it == _codec_to_trackid.end()){ //该Track不存在或初始化失败 @@ -93,32 +89,66 @@ void MP4Muxer::inputFrame(const Frame::Ptr &frame) { _started = true; } - int with_nalu_size ; - switch (frame->getCodecId()){ - case CodecH264: - case CodecH265: - //我们输入264、265是没有头四个字节表明数据长度的 - with_nalu_size = 0; - break; - default: - //aac或其他类型frame不用添加4个nalu_size的字节 - with_nalu_size = 1; - break; - } - //mp4文件时间戳需要从0开始 auto &track_info = it->second; int64_t dts_out, pts_out; - track_info.stamp.revise(frame->dts(),frame->pts(),dts_out,pts_out); - mov_writer_write_l(_mov_writter.get(), - track_info.track_id, - frame->data() + frame->prefixSize(), - frame->size() - frame->prefixSize(), - pts_out, - dts_out, - frame->keyFrame() ? MOV_AV_FLAG_KEYFREAME : 0, - with_nalu_size); + switch (frame->getCodecId()) { + case CodecH264: + case CodecH265: { + //这里的代码逻辑是让SPS、PPS、IDR这些时间戳相同的帧打包到一起当做一个帧处理, + if (!_frameCached.empty() && _frameCached.back()->dts() != frame->dts()) { + Frame::Ptr back = _frameCached.back(); + //求相对时间戳 + track_info.stamp.revise(back->dts(), back->pts(), dts_out, pts_out); + + if (_frameCached.size() != 1) { + //缓存中有多帧,需要按照mp4格式合并一起 + string merged; + _frameCached.for_each([&](const Frame::Ptr &frame) { + uint32_t nalu_size = frame->size() - frame->prefixSize(); + nalu_size = htonl(nalu_size); + merged.append((char *) &nalu_size, 4); + merged.append(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize()); + }); + mov_writer_write_l(_mov_writter.get(), + track_info.track_id, + merged.data(), + merged.size(), + pts_out, + dts_out, + back->keyFrame() ? MOV_AV_FLAG_KEYFREAME : 0, + 1/*我们合并时已经生成了4个字节的MP4格式start code*/); + } else { + //缓存中只有一帧视频 + mov_writer_write_l(_mov_writter.get(), + track_info.track_id, + back->data() + back->prefixSize(), + back->size() - back->prefixSize(), + pts_out, + dts_out, + back->keyFrame() ? MOV_AV_FLAG_KEYFREAME : 0, + 0/*需要生成头4个字节的MP4格式start code*/); + } + _frameCached.clear(); + } + //缓存帧,时间戳相同的帧合并一起写入mp4 + _frameCached.emplace_back(Frame::getCacheAbleFrame(frame)); + } + break; + default: { + track_info.stamp.revise(frame->dts(), frame->pts(), dts_out, pts_out); + mov_writer_write_l(_mov_writter.get(), + track_info.track_id, + frame->data() + frame->prefixSize(), + frame->size() - frame->prefixSize(), + pts_out, + dts_out, + frame->keyFrame() ? MOV_AV_FLAG_KEYFREAME : 0, + 1/*aac或其他类型frame不用添加4个nalu_size的字节*/); + } + break; + } } void MP4Muxer::addTrack(const Track::Ptr &track) { diff --git a/src/Record/MP4Muxer.h b/src/Record/MP4Muxer.h index 401431d2..1edac519 100644 --- a/src/Record/MP4Muxer.h +++ b/src/Record/MP4Muxer.h @@ -82,6 +82,7 @@ private: }; unordered_map _codec_to_trackid; bool _started = false; + List _frameCached; }; From e4f9e377c509ae40d36167a09d2a4c6fe160e35d Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 3 Jan 2020 17:26:36 +0800 Subject: [PATCH 195/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8DFFmpeg=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E8=B7=AF=E5=BE=84=E7=9B=B8=E5=85=B3=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- server/FFmpegSource.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index ba5a796d..7cb850d7 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit ba5a796d583cd2906e06ec15c0d942e484ea1dbf +Subproject commit 7cb850d79ba798e6d1f0bba7c655d38d2865ed92 diff --git a/server/FFmpegSource.cpp b/server/FFmpegSource.cpp index e76df0ed..3824f910 100644 --- a/server/FFmpegSource.cpp +++ b/server/FFmpegSource.cpp @@ -63,7 +63,7 @@ void FFmpegSource::play(const string &src_url,const string &dst_url,int timeout_ char cmd[1024] = {0}; snprintf(cmd, sizeof(cmd),ffmpeg_cmd.data(),ffmpeg_bin.data(),src_url.data(),dst_url.data()); - _process.run(cmd,File::absolutePath("",ffmpeg_log)); + _process.run(cmd,ffmpeg_log.empty() ? "" : File::absolutePath("",ffmpeg_log)); InfoL << cmd; if(_media_info._host == "127.0.0.1"){ From 1ce9af35e34d6b350d4912edc659f981abfb4ddd Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 4 Jan 2020 12:03:53 +0800 Subject: [PATCH 196/272] =?UTF-8?q?1=E3=80=81=E5=A4=8D=E7=94=A8rtsp=20url?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E4=BB=A3=E7=A0=81=202=E3=80=81=E4=BF=AE?= =?UTF-8?q?=E5=A4=8Drtsp/rtmp=E6=8E=A8=E6=B5=81=E5=BC=82=E5=B8=B8=E5=9B=9E?= =?UTF-8?q?=E8=B0=83=E7=B4=8A=E4=B9=B1=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtmp/RtmpPusher.cpp | 24 +++++---- src/Rtmp/RtmpPusher.h | 2 +- src/Rtsp/Rtsp.cpp | 53 +++++++++++++++++++ src/Rtsp/Rtsp.h | 18 +++++++ src/Rtsp/RtspPlayer.cpp | 78 +++++++--------------------- src/Rtsp/RtspPlayer.h | 1 - src/Rtsp/RtspPusher.cpp | 110 +++++++++++++++------------------------- src/Rtsp/RtspPusher.h | 3 +- 8 files changed, 145 insertions(+), 144 deletions(-) diff --git a/src/Rtmp/RtmpPusher.cpp b/src/Rtmp/RtmpPusher.cpp index 834276a7..ea2ebca8 100644 --- a/src/Rtmp/RtmpPusher.cpp +++ b/src/Rtmp/RtmpPusher.cpp @@ -60,8 +60,8 @@ void RtmpPusher::teardown() { } } -void RtmpPusher::onPublishResult(const SockException &ex) { - if(_pPublishTimer){ +void RtmpPusher::onPublishResult(const SockException &ex,bool handshakeCompleted) { + if(!handshakeCompleted){ //播放结果回调 _pPublishTimer.reset(); if(_onPublished){ @@ -87,7 +87,7 @@ void RtmpPusher::publish(const string &strUrl) { _strTcUrl = string("rtmp://") + strHost + "/" + _strApp; if (!_strApp.size() || !_strStream.size()) { - onPublishResult(SockException(Err_other,"rtmp url非法")); + onPublishResult(SockException(Err_other,"rtmp url非法"),false); return; } DebugL << strHost << " " << _strApp << " " << _strStream; @@ -102,13 +102,13 @@ void RtmpPusher::publish(const string &strUrl) { } weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - float playTimeOutSec = (*this)[kTimeoutMS].as() / 1000.0; - _pPublishTimer.reset( new Timer(playTimeOutSec, [weakSelf]() { + float publishTimeOutSec = (*this)[kTimeoutMS].as() / 1000.0; + _pPublishTimer.reset( new Timer(publishTimeOutSec, [weakSelf]() { auto strongSelf=weakSelf.lock(); if(!strongSelf) { return false; } - strongSelf->onPublishResult(SockException(Err_timeout,"publish rtmp timeout")); + strongSelf->onPublishResult(SockException(Err_timeout,"publish rtmp timeout"), false); return false; },getPoller())); @@ -120,11 +120,12 @@ void RtmpPusher::publish(const string &strUrl) { } void RtmpPusher::onErr(const SockException &ex){ - onPublishResult(ex); + //定时器_pPublishTimer为空后表明握手结束了 + onPublishResult(ex,!_pPublishTimer); } void RtmpPusher::onConnect(const SockException &err){ if(err) { - onPublishResult(err); + onPublishResult(err,false); return; } //推流器不需要多大的接收缓存,节省内存占用 @@ -146,7 +147,8 @@ void RtmpPusher::onRecv(const Buffer::Ptr &pBuf){ onParseRtmp(pBuf->data(), pBuf->size()); } catch (exception &e) { SockException ex(Err_other, e.what()); - onPublishResult(ex); + //定时器_pPublishTimer为空后表明握手结束了 + onPublishResult(ex,!_pPublishTimer); } } @@ -223,10 +225,10 @@ inline void RtmpPusher::send_metaData(){ _pRtmpReader->setDetachCB([weakSelf](){ auto strongSelf = weakSelf.lock(); if(strongSelf){ - strongSelf->onPublishResult(SockException(Err_other,"媒体源被释放")); + strongSelf->onPublishResult(SockException(Err_other,"媒体源被释放"), !strongSelf->_pPublishTimer); } }); - onPublishResult(SockException(Err_success,"success")); + onPublishResult(SockException(Err_success,"success"), false); //提升发送性能 setSocketFlags(); } diff --git a/src/Rtmp/RtmpPusher.h b/src/Rtmp/RtmpPusher.h index bc696c2e..9be06f37 100644 --- a/src/Rtmp/RtmpPusher.h +++ b/src/Rtmp/RtmpPusher.h @@ -63,7 +63,7 @@ protected: send(buffer); } private: - void onPublishResult(const SockException &ex); + void onPublishResult(const SockException &ex,bool handshakeCompleted); template inline void addOnResultCB(const FUN &fun) { diff --git a/src/Rtsp/Rtsp.cpp b/src/Rtsp/Rtsp.cpp index 8cf3dae6..bc8dcce4 100644 --- a/src/Rtsp/Rtsp.cpp +++ b/src/Rtsp/Rtsp.cpp @@ -242,5 +242,58 @@ string SdpParser::toString() const { return title + video + audio; } +bool RtspUrl::parse(const string &strUrl) { + auto schema = FindField(strUrl.data(), nullptr, "://"); + bool isSSL = strcasecmp(schema.data(), "rtsps") == 0; + //查找"://"与"/"之间的字符串,用于提取用户名密码 + auto middle_url = FindField(strUrl.data(), "://", "/"); + if (middle_url.empty()) { + middle_url = FindField(strUrl.data(), "://", nullptr); + } + auto pos = middle_url.rfind('@'); + if (pos == string::npos) { + //并没有用户名密码 + return setup(isSSL, strUrl, "", ""); + } + + //包含用户名密码 + auto user_pwd = middle_url.substr(0, pos); + auto suffix = strUrl.substr(schema.size() + 3 + pos + 1); + auto url = StrPrinter << "rtsp://" << suffix << endl; + if (user_pwd.find(":") == string::npos) { + return setup(isSSL, url, user_pwd, ""); + } + auto user = FindField(user_pwd.data(), nullptr, ":"); + auto pwd = FindField(user_pwd.data(), ":", nullptr); + return setup(isSSL, url, user, pwd); +} + +bool RtspUrl::setup(bool isSSL, const string &strUrl, const string &strUser, const string &strPwd) { + auto ip = FindField(strUrl.data(), "://", "/"); + if (ip.empty()) { + ip = split(FindField(strUrl.data(), "://", NULL), "?")[0]; + } + auto port = atoi(FindField(ip.data(), ":", NULL).data()); + if (port <= 0 || port > UINT16_MAX) { + //rtsp 默认端口554 + port = isSSL ? 322 : 554; + } else { + //服务器域名 + ip = FindField(ip.data(), NULL, ":"); + } + + if (ip.empty()) { + return false; + } + + _url = std::move(strUrl); + _user = std::move(strUser); + _passwd = std::move(strPwd); + _host = std::move(ip); + _port = port; + _is_ssl = isSSL; + return true; +} + }//namespace mediakit diff --git a/src/Rtsp/Rtsp.h b/src/Rtsp/Rtsp.h index e005c971..c8eb5554 100644 --- a/src/Rtsp/Rtsp.h +++ b/src/Rtsp/Rtsp.h @@ -125,6 +125,24 @@ private: map _track_map; }; +/** + * 解析rtsp url的工具类 + */ +class RtspUrl{ +public: + string _url; + string _user; + string _passwd; + string _host; + uint16_t _port; + bool _is_ssl; +public: + RtspUrl() = default; + ~RtspUrl() = default; + bool parse(const string &url); +private: + bool setup(bool,const string &, const string &, const string &); +}; /** * rtsp sdp基类 diff --git a/src/Rtsp/RtspPlayer.cpp b/src/Rtsp/RtspPlayer.cpp index 10a1f7e3..682cbe19 100644 --- a/src/Rtsp/RtspPlayer.cpp +++ b/src/Rtsp/RtspPlayer.cpp @@ -81,70 +81,25 @@ void RtspPlayer::teardown(){ } void RtspPlayer::play(const string &strUrl){ - Rtsp::eRtpType eType = (Rtsp::eRtpType)(int)(*this)[kRtpType]; - auto schema = FindField(strUrl.data(), nullptr,"://"); - bool isSSL = strcasecmp(schema.data(),"rtsps") == 0; - //查找"://"与"/"之间的字符串,用于提取用户名密码 - auto middle_url = FindField(strUrl.data(),"://","/"); - if(middle_url.empty()){ - middle_url = FindField(strUrl.data(),"://", nullptr); - } - auto pos = middle_url.rfind('@'); - if(pos == string::npos){ - //并没有用户名密码 - play(isSSL,strUrl,"","",eType); - return; - } - - //包含用户名密码 - auto user_pwd = middle_url.substr(0,pos); - auto suffix = strUrl.substr(schema.size() + 3 + pos + 1); - auto url = StrPrinter << "rtsp://" << suffix << endl; - if(user_pwd.find(":") == string::npos){ - play(isSSL,url,user_pwd,"",eType); - return; - } - auto user = FindField(user_pwd.data(),nullptr,":"); - auto pwd = FindField(user_pwd.data(),":",nullptr); - play(isSSL,url,user,pwd,eType); -} - -void RtspPlayer::play(bool isSSL,const string &strUrl, const string &strUser, const string &strPwd, Rtsp::eRtpType eType ) { - DebugL << strUrl << " " - << (strUser.size() ? strUser : "null") << " " - << (strPwd.size() ? strPwd:"null") << " " - << eType; - teardown(); - - if(strUser.size()){ - (*this)[kRtspUser] = strUser; - } - if(strPwd.size()){ - (*this)[kRtspPwd] = strPwd; - (*this)[kRtspPwdIsMD5] = false; - } - - _eType = eType; - - auto ip = FindField(strUrl.data(), "://", "/"); - if (ip.empty()) { - ip = split(FindField(strUrl.data(), "://", NULL),"?")[0]; - } - auto port = atoi(FindField(ip.data(), ":", NULL).data()); - if (port <= 0) { - //rtsp 默认端口554 - port = isSSL ? 322 : 554; - } else { - //服务器域名 - ip = FindField(ip.data(), NULL, ":"); - } - - if(ip.empty()){ + RtspUrl url; + if(!url.parse(strUrl)){ onPlayResult_l(SockException(Err_other,StrPrinter << "illegal rtsp url:" << strUrl),false); return; } - _strUrl = strUrl; + teardown(); + + if (url._user.size()) { + (*this)[kRtspUser] = url._user; + } + if (url._passwd.size()) { + (*this)[kRtspPwd] = url._passwd; + (*this)[kRtspPwdIsMD5] = false; + } + + _strUrl = url._url; + _eType = (Rtsp::eRtpType)(int)(*this)[kRtpType]; + DebugL << url._url << " " << (url._user.size() ? url._user : "null") << " " << (url._passwd.size() ? url._passwd : "null") << " " << _eType; weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); float playTimeOutSec = (*this)[kTimeoutMS].as() / 1000.0; @@ -160,8 +115,9 @@ void RtspPlayer::play(bool isSSL,const string &strUrl, const string &strUser, co if(!(*this)[kNetAdapter].empty()){ setNetAdapter((*this)[kNetAdapter]); } - startConnect(ip, port , playTimeOutSec); + startConnect(url._host, url._port, playTimeOutSec); } + void RtspPlayer::onConnect(const SockException &err){ if(err.getErrCode() != Err_success) { onPlayResult_l(err,false); diff --git a/src/Rtsp/RtspPlayer.h b/src/Rtsp/RtspPlayer.h index 8247dc9d..5f4f7395 100644 --- a/src/Rtsp/RtspPlayer.h +++ b/src/Rtsp/RtspPlayer.h @@ -106,7 +106,6 @@ private: int getTrackIndexByInterleaved(int interleaved) const; int getTrackIndexByTrackType(TrackType trackType) const; - void play(bool isSSL,const string &strUrl, const string &strUser, const string &strPwd, Rtsp::eRtpType eType); void handleResSETUP(const Parser &parser, unsigned int uiTrackIndex); void handleResDESCRIBE(const Parser &parser); bool handleAuthenticationFailure(const string &wwwAuthenticateParamsStr); diff --git a/src/Rtsp/RtspPusher.cpp b/src/Rtsp/RtspPusher.cpp index e40a185b..7dd6a61f 100644 --- a/src/Rtsp/RtspPusher.cpp +++ b/src/Rtsp/RtspPusher.cpp @@ -43,25 +43,46 @@ void RtspPusher::teardown() { } void RtspPusher::publish(const string &strUrl) { - auto userAndPwd = FindField(strUrl.data(),"://","@"); - Rtsp::eRtpType eType = (Rtsp::eRtpType)(int)(*this)[ kRtpType]; - if(userAndPwd.empty()){ - publish(strUrl,"","",eType); + RtspUrl url; + if(!url.parse(strUrl)){ + onPublishResult(SockException(Err_other,StrPrinter << "illegal rtsp url:" << strUrl),false); return; } - auto suffix = FindField(strUrl.data(),"@",nullptr); - auto url = StrPrinter << "rtsp://" << suffix << endl; - if(userAndPwd.find(":") == string::npos){ - publish(url,userAndPwd,"",eType); - return; + + teardown(); + + if (url._user.size()) { + (*this)[kRtspUser] = url._user; } - auto user = FindField(userAndPwd.data(),nullptr,":"); - auto pwd = FindField(userAndPwd.data(),":",nullptr); - publish(url,user,pwd,eType); + if (url._passwd.size()) { + (*this)[kRtspPwd] = url._passwd; + (*this)[kRtspPwdIsMD5] = false; + } + + _strUrl = strUrl; + _eType = (Rtsp::eRtpType)(int)(*this)[kRtpType]; + DebugL << url._url << " " << (url._user.size() ? url._user : "null") << " " << (url._passwd.size() ? url._passwd : "null") << " " << _eType; + + weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); + float publishTimeOutSec = (*this)[kTimeoutMS].as() / 1000.0; + _pPublishTimer.reset( new Timer(publishTimeOutSec, [weakSelf]() { + auto strongSelf=weakSelf.lock(); + if(!strongSelf) { + return false; + } + strongSelf->onPublishResult(SockException(Err_timeout,"publish rtsp timeout"),false); + return false; + },getPoller())); + + if(!(*this)[kNetAdapter].empty()){ + setNetAdapter((*this)[kNetAdapter]); + } + + startConnect(url._host, url._port, publishTimeOutSec); } -void RtspPusher::onPublishResult(const SockException &ex) { - if(_pPublishTimer){ +void RtspPusher::onPublishResult(const SockException &ex, bool handshakeCompleted) { + if(!handshakeCompleted){ //播放结果回调 _pPublishTimer.reset(); if(_onPublished){ @@ -79,62 +100,14 @@ void RtspPusher::onPublishResult(const SockException &ex) { } } -void RtspPusher::publish(const string & strUrl, const string &strUser, const string &strPwd, Rtsp::eRtpType eType ) { - DebugL << strUrl << " " - << (strUser.size() ? strUser : "null") << " " - << (strPwd.size() ? strPwd:"null") << " " - << eType; - teardown(); - - if(strUser.size()){ - (*this)[kRtspUser] = strUser; - } - if(strPwd.size()){ - (*this)[kRtspPwd] = strPwd; - (*this)[kRtspPwdIsMD5] = false; - } - - _eType = eType; - - auto ip = FindField(strUrl.data(), "://", "/"); - if (!ip.size()) { - ip = FindField(strUrl.data(), "://", NULL); - } - auto port = atoi(FindField(ip.data(), ":", NULL).data()); - if (port <= 0) { - //rtsp 默认端口554 - port = 554; - } else { - //服务器域名 - ip = FindField(ip.data(), NULL, ":"); - } - - _strUrl = strUrl; - - weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - float playTimeOutSec = (*this)[kTimeoutMS].as() / 1000.0; - _pPublishTimer.reset( new Timer(playTimeOutSec, [weakSelf]() { - auto strongSelf=weakSelf.lock(); - if(!strongSelf) { - return false; - } - strongSelf->onPublishResult(SockException(Err_timeout,"publish rtsp timeout")); - return false; - },getPoller())); - - if(!(*this)[kNetAdapter].empty()){ - setNetAdapter((*this)[kNetAdapter]); - } - startConnect(ip, port , playTimeOutSec); -} - void RtspPusher::onErr(const SockException &ex) { - onPublishResult(ex); + //定时器_pPublishTimer为空后表明握手结束了 + onPublishResult(ex,!_pPublishTimer); } void RtspPusher::onConnect(const SockException &err) { if(err) { - onPublishResult(err); + onPublishResult(err,false); return; } //推流器不需要多大的接收缓存,节省内存占用 @@ -147,7 +120,8 @@ void RtspPusher::onRecv(const Buffer::Ptr &pBuf){ input(pBuf->data(), pBuf->size()); } catch (exception &e) { SockException ex(Err_other, e.what()); - onPublishResult(ex); + //定时器_pPublishTimer为空后表明握手结束了 + onPublishResult(ex,!_pPublishTimer); } } @@ -377,7 +351,7 @@ void RtspPusher::sendRecord() { _pRtspReader->setDetachCB([weakSelf](){ auto strongSelf = weakSelf.lock(); if(strongSelf){ - strongSelf->onPublishResult(SockException(Err_other,"媒体源被释放")); + strongSelf->onPublishResult(SockException(Err_other,"媒体源被释放"), !strongSelf->_pPublishTimer); } }); if(_eType != Rtsp::RTP_TCP){ @@ -392,7 +366,7 @@ void RtspPusher::sendRecord() { return true; },getPoller())); } - onPublishResult(SockException(Err_success,"success")); + onPublishResult(SockException(Err_success,"success"), false); //提升发送性能 setSocketFlags(); }; diff --git a/src/Rtsp/RtspPusher.h b/src/Rtsp/RtspPusher.h index 958c1c87..99431f4d 100644 --- a/src/Rtsp/RtspPusher.h +++ b/src/Rtsp/RtspPusher.h @@ -48,8 +48,7 @@ protected: void onWholeRtspPacket(Parser &parser) override ; void onRtpPacket(const char *data,uint64_t len) override {}; private: - void publish(const string &strUrl, const string &strUser, const string &strPwd, Rtsp::eRtpType eType ); - void onPublishResult(const SockException &ex); + void onPublishResult(const SockException &ex, bool handshakeCompleted); void sendAnnounce(); void sendSetup(unsigned int uiTrackIndex); From 3ddb9e48438d383a7e23f5f4cd588f1c552c318f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Mon, 6 Jan 2020 14:19:15 +0800 Subject: [PATCH 197/272] Update README.md --- README.md | 49 +++++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index c5c63216..c3c5d3bb 100644 --- a/README.md +++ b/README.md @@ -33,50 +33,55 @@ git submodule update --init ## 功能清单 - RTSP - - RTSP 服务器,支持RTMP/MP4转RTSP。 + - RTSP 服务器,支持RTMP/MP4转RTSP - RTSPS 服务器,支持亚马逊echo show这样的设备 - RTSP 播放器,支持RTSP代理,支持生成静音音频 - RTSP 推流客户端与服务器 - - 支持 `rtp over udp` `rtp over tcp` `rtp over http` `rtp组播` 四种RTP传输方式 。 - - 服务器/客户端完整支持Basic/Digest方式的登录鉴权,全异步可配置化的鉴权接口。 + - 支持 `rtp over udp` `rtp over tcp` `rtp over http` `rtp组播` 四种RTP传输方式 + - 服务器/客户端完整支持Basic/Digest方式的登录鉴权,全异步可配置化的鉴权接口 - 支持H265编码 - 服务器支持RTSP推流(包括`rtp over udp` `rtp over tcp`方式) - 支持任意编码格式的rtsp推流,只是除H264/H265+AAC外无法转协议 - RTMP - - RTMP 播放服务器,支持RTSP/MP4转RTMP。 - - RTMP 发布服务器,支持录制发布流。 + - RTMP 播放服务器,支持RTSP/MP4转RTMP + - RTMP 发布服务器,支持录制发布流 - RTMP 播放器,支持RTMP代理,支持生成静音音频 - - RTMP 推流客户端。 - - 支持http-flv直播。 - - 支持https-flv直播。 + - RTMP 推流客户端 + - 支持http[s]-flv直播 + - 支持websocket-flv直播 - 支持任意编码格式的rtmp推流,只是除H264/H265+AAC外无法转协议 - HLS - - 支持HLS文件生成,自带HTTP文件服务器。 - - 支持播放鉴权,鉴权结果可以缓存为cookie + - 支持HLS文件生成,自带HTTP文件服务器 + - 通过cookie追踪技术,可以模拟HLS播放为长连接,实现丰富的业务逻辑 + - 支持完备的HLS用户追踪、播放统计等业务功能,可以实现HLS按需拉流等业务 - HTTP[S] - - 服务器支持`目录索引生成`,`文件下载`,`表单提交请求`。 - - 客户端提供`文件下载器(支持断点续传)`,`接口请求器`,`文件上传器`。 - - 完整HTTP API服务器,可以作为web后台开发框架。 - - 支持跨域访问。 + - 服务器支持`目录索引生成`,`文件下载`,`表单提交请求` + - 客户端提供`文件下载器(支持断点续传)`,`接口请求器`,`文件上传器` + - 完整HTTP API服务器,可以作为web后台开发框架 + - 支持跨域访问 - 支持http客户端、服务器cookie - 支持WebSocket服务器和客户端 - 支持http文件访问鉴权 +- GB28181 + - 支持国标RTP推流,可以UDP/TCP国标RTP推流,可以转换成RTSP/RTMP/HLS等协议 + +- 点播 + - 支持录制为FLV/HLS/MP4 + - RTSP/RTMP/HTTP-FLV/WS-FLV支持MP4文件点播,支持seek + - 其他 - - 支持输入YUV+PCM自动生成RTSP/RTMP/HLS/MP4. - - 支持简单的telnet调试。 - - 支持H264的解析,支持B帧的POC计算排序。 + - 支持丰富的restful api已经web hook事件 + - 支持简单的telnet调试 - 支持配置文件热加载 - - 支持流量统计、推流播放鉴权等事件 - - 支持rtsp/rtmp/http虚拟主机 - - 支持flv、mp4文件录制 - - 支持rtps/rtmp协议的mp4点播,支持seek + - 支持流量统计、推拉流鉴权等事件 + - 支持虚拟主机,可以隔离不同域名 - 支持按需拉流,无人观看自动关断拉流 - 支持先拉流后推流,提高及时推流画面打开率 - - 支持rtsp/rtmp/http-flv/hls播放鉴权(url参数方式) + - 提供c api sdk From 4591ece5660c4e33a03b8cb590ba8b5ed12573f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Mon, 6 Jan 2020 14:23:23 +0800 Subject: [PATCH 198/272] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c3c5d3bb..38731a39 100644 --- a/README.md +++ b/README.md @@ -67,14 +67,14 @@ git submodule update --init - 支持http文件访问鉴权 - GB28181 - - 支持国标RTP推流,可以UDP/TCP国标RTP推流,可以转换成RTSP/RTMP/HLS等协议 + - 支持UDP/TCP国标RTP推流,可以转换成RTSP/RTMP/HLS等协议 - 点播 - 支持录制为FLV/HLS/MP4 - RTSP/RTMP/HTTP-FLV/WS-FLV支持MP4文件点播,支持seek - 其他 - - 支持丰富的restful api已经web hook事件 + - 支持丰富的restful api以及web hook事件 - 支持简单的telnet调试 - 支持配置文件热加载 - 支持流量统计、推拉流鉴权等事件 From 01bae24fdc2b49f85fcf8584da0b88ca10d0d8bc Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 7 Jan 2020 10:45:55 +0800 Subject: [PATCH 199/272] update zltoolkit --- 3rdpart/ZLToolKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 7cb850d7..72013f51 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 7cb850d79ba798e6d1f0bba7c655d38d2865ed92 +Subproject commit 72013f5128d8c04bad8b973370da463c311081c0 From 306e202ca10d7b3606245af37a2e146493126b5d Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 7 Jan 2020 11:09:11 +0800 Subject: [PATCH 200/272] =?UTF-8?q?1.=E6=B7=BB=E5=8A=A0windows=E4=B8=8Bffm?= =?UTF-8?q?peg=E6=8B=89=E6=B5=81=E5=88=86=E5=8F=91=E6=94=AF=E6=8C=81?= =?UTF-8?q?=EF=BC=8C=E7=9B=AE=E5=89=8D=E4=B8=8D=E6=98=AF=E5=BE=88=E5=AE=8C?= =?UTF-8?q?=E5=96=84=EF=BC=8C=E5=90=8E=E7=BB=AD=E5=86=8D=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=202.server/system=E4=B8=8B=E6=9C=AA=E5=85=BC=E5=AE=B9=E5=AE=8C?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- README.md | 24 +---- server/CMakeLists.txt | 7 +- server/FFmpegSource.cpp | 17 +-- server/Process.cpp | 225 ++++++++++++++++++++++++++-------------- server/Process.h | 7 ++ server/System.cpp | 72 ++++++++++--- server/WebApi.cpp | 9 -- tests/test_server.cpp | 4 +- 9 files changed, 230 insertions(+), 137 deletions(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index ba5a796d..7cb850d7 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit ba5a796d583cd2906e06ec15c0d942e484ea1dbf +Subproject commit 7cb850d79ba798e6d1f0bba7c655d38d2865ed92 diff --git a/README.md b/README.md index c5c63216..7c1d07b2 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,5 @@ -![logo](https://raw.githubusercontent.com/zlmediakit/ZLMediaKit/master/logo.png) - -[english readme](https://github.com/xiongziliang/ZLMediaKit/blob/master/README_en.md) - # 一个基于C++11的高性能运营级流媒体服务框架 - [![Build Status](https://travis-ci.org/xiongziliang/ZLMediaKit.svg?branch=master)](https://travis-ci.org/xiongziliang/ZLMediaKit) - -## 国内用户请使用gitee镜像下载 -``` -git clone --depth 1 https://gitee.com/xiahcu/ZLMediaKit -cd ZLMediaKit -git submodule update --init -``` ## 项目特点 - 基于C++11开发,避免使用裸指针,代码稳定可靠;同时跨平台移植简单方便,代码清晰简洁。 - 打包多种流媒体协议(RTSP/RTMP/HLS/HTTP-FLV/Websocket-FLV),支持协议间的互相转换,提供一站式的服务。 @@ -29,6 +17,7 @@ git submodule update --init - 移动嵌入式跨平台流媒体解决方案。 - 商用级流媒体服务器。 - 网络编程二次开发SDK。 +- 免费NVR(rtxp拉流代理、分发)。 ## 功能清单 @@ -362,8 +351,9 @@ git submodule update --init 由于使用本项目而产生的商业纠纷或侵权行为一概与本项项目及开发者无关,请自行承担法律风险。 ## 联系方式 - - 邮箱:<771730766@qq.com>(本项目相关或流媒体相关问题请走issue流程,否则恕不邮件答复) - - QQ群:542509000 + - 邮箱:<18675721@qq.com>(本项目相关或流媒体相关问题请走issue流程,否则恕不邮件答复) + - QQ群:542509000 (ZLMediakit) + - QQ群:936467414 (FreeNVR) ## 怎么提问? 如果要对项目有相关疑问,建议您这么做: @@ -372,12 +362,6 @@ git submodule update --init - 3、有些问题,如果不具备参考性的,无需在issue提的,可以在qq群提. - 4、QQ私聊一般不接受无偿技术咨询和支持(谈谈人生理想还是可以的😂),毕竟精力有限,谢谢理解. -## 捐赠 -欢迎捐赠以便更好的推动项目的发展,谢谢您的支持! - -[支付宝](https://raw.githubusercontent.com/xiongziliang/other/master/IMG_3919.JPG) - -[微信](https://raw.githubusercontent.com/xiongziliang/other/master/IMG_3920.JPG) diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 5bc42a8d..092a4221 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -3,12 +3,7 @@ file(GLOB jsoncpp_src_list ../3rdpart/jsoncpp/*.cpp ../3rdpart/jsoncpp/*.h ) add_library(jsoncpp STATIC ${jsoncpp_src_list}) -if (CMAKE_SYSTEM_NAME MATCHES "Windows") - set(MediaServer_src_list ./WebApi.cpp ./WebHook.cpp main.cpp) -else() - file(GLOB MediaServer_src_list ./*.cpp ./*.h) -endif() - +file(GLOB MediaServer_src_list ./*.cpp ./*.h) #message(STATUS ${MediaServer_src_list}) add_executable(MediaServer ${MediaServer_src_list}) diff --git a/server/FFmpegSource.cpp b/server/FFmpegSource.cpp index e76df0ed..ac692a73 100644 --- a/server/FFmpegSource.cpp +++ b/server/FFmpegSource.cpp @@ -37,9 +37,15 @@ const char kCmd[] = FFmpeg_FIELD"cmd"; const char kLog[] = FFmpeg_FIELD"log"; onceToken token([]() { - mINI::Instance()[kBin] = trim(System::execute("which ffmpeg")); - mINI::Instance()[kCmd] = "%s -re -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s"; - mINI::Instance()[kLog] = "./ffmpeg/ffmpeg.log"; +#ifdef _WIN32 + string strFFmpeg = "where ffmpeg"; +#else + string strFFmpeg = "which ffmpeg"; +#endif + + mINI::Instance()[kBin] = trim(System::execute(strFFmpeg)); //todo:暂定如此:配置文件无此配置的话,改为环境变量的ffmpeg + mINI::Instance()[kCmd] = "%s -re -i \"%s\" -loglevel quiet -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s "; //防止url中特殊字符 + mINI::Instance()[kLog] = "./ffmpeg/ffmpeg.log"; //win下建议使用ffmpeg/ffmpeg.log }); } @@ -51,7 +57,6 @@ FFmpegSource::~FFmpegSource() { DebugL; } - void FFmpegSource::play(const string &src_url,const string &dst_url,int timeout_ms,const onPlay &cb) { GET_CONFIG(string,ffmpeg_bin,FFmpeg::kBin); GET_CONFIG(string,ffmpeg_cmd,FFmpeg::kCmd); @@ -62,8 +67,8 @@ void FFmpegSource::play(const string &src_url,const string &dst_url,int timeout_ _media_info.parse(dst_url); char cmd[1024] = {0}; - snprintf(cmd, sizeof(cmd),ffmpeg_cmd.data(),ffmpeg_bin.data(),src_url.data(),dst_url.data()); - _process.run(cmd,File::absolutePath("",ffmpeg_log)); + snprintf(cmd, sizeof(cmd), ffmpeg_cmd.data(), ffmpeg_bin.data(), src_url.data(), dst_url.data()); + _process.run(cmd, ffmpeg_log.empty() ? "" : File::absolutePath("", ffmpeg_log)); InfoL << cmd; if(_media_info._host == "127.0.0.1"){ diff --git a/server/Process.cpp b/server/Process.cpp index e215dc81..b1d2438d 100644 --- a/server/Process.cpp +++ b/server/Process.cpp @@ -26,8 +26,15 @@ #include #include + +#ifndef _WIN32 #include #include +#else +//#include +#include +#endif + #include #include #include "Util/util.h" @@ -39,75 +46,109 @@ using namespace toolkit; void Process::run(const string &cmd, const string &log_file_tmp) { - kill(2000); - _pid = fork(); - if (_pid < 0) { - throw std::runtime_error(StrPrinter << "fork child process falied,err:" << get_uv_errmsg()); - } - if (_pid == 0) { - //子进程关闭core文件生成 - struct rlimit rlim = {0,0}; - setrlimit(RLIMIT_CORE, &rlim); + kill(2000); +#ifdef _WIN32 +// string log_file = ""; +// if (log_file_tmp.empty()) +// { +// log_file = R"( >2&1)"; +// } +// else +// { +// log_file = log_file_tmp + R"( >2&1)"; +// } +// string strCmd = cmd + " " + log_file; //防止cmd后面没有空格 - //在启动子进程时,暂时禁用SIGINT、SIGTERM信号 - // ignore the SIGINT and SIGTERM - signal(SIGINT, SIG_IGN); - signal(SIGTERM, SIG_IGN); + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory(&si, sizeof(si)); //结构体初始化; + ZeroMemory(&pi, sizeof(pi)); - string log_file ; - if(log_file_tmp.empty()){ - log_file = "/dev/null"; - }else{ - log_file = StrPrinter << log_file_tmp << "." << getpid(); - } + LPTSTR lpDir = const_cast(cmd .data()); - int log_fd = -1; - int flags = O_CREAT | O_WRONLY | O_APPEND; - mode_t mode = S_IRWXO | S_IRWXG | S_IRWXU;// S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; - File::createfile_path(log_file.data(), mode); - if ((log_fd = ::open(log_file.c_str(), flags, mode)) < 0) { - fprintf(stderr, "open log file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno)); - } else { - // dup to stdout and stderr. - if (dup2(log_fd, STDOUT_FILENO) < 0) { - fprintf(stderr, "dup2 stdout file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno)); - } - if (dup2(log_fd, STDERR_FILENO) < 0) { - fprintf(stderr, "dup2 stderr file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno)); - } - // close log fd - ::close(log_fd); - } - fprintf(stderr, "\r\n\r\n#### pid=%d,cmd=%s #####\r\n\r\n", getpid(), cmd.data()); + if (CreateProcess(NULL, lpDir, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) + { + //下面两行关闭句柄,解除本进程和新进程的关系,不然有可能 不小心调用TerminateProcess函数关掉子进程 + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); - // close other fds - // TODO: do in right way. - for (int i = 3; i < 1024; i++) { - ::close(i); - } + _pid = pi.dwProcessId; + InfoL << "start child proces " << _pid; + } + else + { + WarnL << "start child proces fail: " << GetLastError(); + } +#else + _pid = fork(); + if (_pid < 0) { + throw std::runtime_error(StrPrinter << "fork child process falied,err:" << get_uv_errmsg()); + } + if (_pid == 0) { + //子进程关闭core文件生成 + struct rlimit rlim = { 0,0 }; + setrlimit(RLIMIT_CORE, &rlim); - auto params = split(cmd, " "); - // memory leak in child process, it's ok. - char **charpv_params = new char *[params.size() + 1]; - for (int i = 0; i < (int) params.size(); i++) { - std::string &p = params[i]; - charpv_params[i] = (char *) p.data(); - } - // EOF: NULL - charpv_params[params.size()] = NULL; + //在启动子进程时,暂时禁用SIGINT、SIGTERM信号 + // ignore the SIGINT and SIGTERM + signal(SIGINT, SIG_IGN); + signal(SIGTERM, SIG_IGN); - // TODO: execv or execvp - auto ret = execv(params[0].c_str(), charpv_params); - if (ret < 0) { - fprintf(stderr, "fork process failed, errno=%d(%s)\r\n", errno, strerror(errno)); - } - exit(ret); - } + string log_file; + if (log_file_tmp.empty()) { + log_file = "/dev/null"; + } + else { + log_file = StrPrinter << log_file_tmp << "." << getpid(); + } - InfoL << "start child proces " << _pid; + int log_fd = -1; + int flags = O_CREAT | O_WRONLY | O_APPEND; + mode_t mode = S_IRWXO | S_IRWXG | S_IRWXU;// S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; + File::createfile_path(log_file.data(), mode); + if ((log_fd = ::open(log_file.c_str(), flags, mode)) < 0) { + fprintf(stderr, "open log file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno)); + } + else { + // dup to stdout and stderr. + if (dup2(log_fd, STDOUT_FILENO) < 0) { + fprintf(stderr, "dup2 stdout file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno)); + } + if (dup2(log_fd, STDERR_FILENO) < 0) { + fprintf(stderr, "dup2 stderr file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno)); + } + // close log fd + ::close(log_fd); + } + fprintf(stderr, "\r\n\r\n#### pid=%d,cmd=%s #####\r\n\r\n", getpid(), cmd.data()); + + // close other fds + // TODO: do in right way. + for (int i = 3; i < 1024; i++) { + ::close(i); + } + + auto params = split(cmd, " "); + // memory leak in child process, it's ok. + char **charpv_params = new char *[params.size() + 1]; + for (int i = 0; i < (int)params.size(); i++) { + std::string &p = params[i]; + charpv_params[i] = (char *)p.data(); + } + // EOF: NULL + charpv_params[params.size()] = NULL; + + // TODO: execv or execvp + auto ret = execv(params[0].c_str(), charpv_params); + if (ret < 0) { + fprintf(stderr, "fork process failed, errno=%d(%s)\r\n", errno, strerror(errno)); + } + exit(ret); + } + InfoL << "start child proces " << _pid; +#endif // _WIN32 } - /** * 获取进程是否存活状态 * @param pid 进程号 @@ -120,20 +161,31 @@ static bool s_wait(pid_t pid,int *exit_code_ptr,bool block) { return false; } int status = 0; - pid_t p = waitpid(pid, &status, block ? 0 : WNOHANG); - int exit_code = (status & 0xFF00) >> 8; - if(exit_code_ptr){ - *exit_code_ptr = (status & 0xFF00) >> 8; - } - if (p < 0) { - WarnL << "waitpid failed, pid=" << pid << ", err=" << get_uv_errmsg(); - return false; - } - if (p > 0) { - InfoL << "process terminated, pid=" << pid << ", exit code=" << exit_code; - return false; - } - //WarnL << "process is running, pid=" << _pid; +#ifdef _WIN32 + HANDLE hProcess = NULL; + hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); //打开目标进程 + if (hProcess == NULL) { + return false; + } + + CloseHandle(hProcess); +#else + pid_t p = waitpid(pid, &status, block ? 0 : WNOHANG); + int exit_code = (status & 0xFF00) >> 8; + if (exit_code_ptr) { + *exit_code_ptr = (status & 0xFF00) >> 8; + } + if (p < 0) { + WarnL << "waitpid failed, pid=" << pid << ", err=" << get_uv_errmsg(); + return false; + } + if (p > 0) { + InfoL << "process terminated, pid=" << pid << ", exit code=" << exit_code; + return false; + } + WarnL << "process is running, pid=" << _pid; +#endif // _WIN32 + return true; } @@ -142,12 +194,27 @@ static void s_kill(pid_t pid,int max_delay,bool force){ //pid无效 return; } +#ifdef _WIN32 + HANDLE hProcess = NULL; + hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); //打开目标进程 + if (hProcess == NULL) + { + WarnL << "\nOpen Process fAiled: " << GetLastError(); + return; + } + DWORD ret = TerminateProcess(hProcess, 0); //结束目标进程 + if (ret == 0) + { + WarnL << GetLastError; + } +#else + if (::kill(pid, force ? SIGKILL : SIGTERM) == -1) { + //进程可能已经退出了 + WarnL << "kill process " << pid << " failed:" << get_uv_errmsg(); + return; + } +#endif // _WIN32 - if (::kill(pid, force ? SIGKILL : SIGTERM) == -1) { - //进程可能已经退出了 - WarnL << "kill process " << pid << " failed:" << get_uv_errmsg(); - return; - } if(force){ //发送SIGKILL信号后,阻塞等待退出 diff --git a/server/Process.h b/server/Process.h index cce8470a..68e40ac1 100644 --- a/server/Process.h +++ b/server/Process.h @@ -26,9 +26,16 @@ #ifndef IPTV_PROCESS_H #define IPTV_PROCESS_H + +#ifdef _WIN32 +typedef int pid_t; +#else #include +#endif // _WIN32 + #include #include + using namespace std; class Process { diff --git a/server/System.cpp b/server/System.cpp index dc9fdb15..930d2261 100644 --- a/server/System.cpp +++ b/server/System.cpp @@ -27,14 +27,19 @@ #include "System.h" #include #include -#include #include +#ifdef _WIN32 + +#else +#include #include #include #include -#ifndef ANDROID +#ifndef ANDROID #include #endif +#endif + #include #include #include @@ -247,16 +252,38 @@ bool System::getNetworkUsage(vector &usage) { bool System::getTcpUsage(System::TcpUsage &usage) { - usage.established = atoi(trim(System::execute("netstat -na|grep ESTABLISHED|wc -l")).data()); - usage.syn_recv = atoi(trim(System::execute("netstat -na|grep SYN_RECV|wc -l")).data()); - usage.time_wait = atoi(trim(System::execute("netstat -na|grep TIME_WAIT|wc -l")).data()); - usage.close_wait = atoi(trim(System::execute("netstat -na|grep CLOSE_WAIT|wc -l")).data()); + string strEstab; + string strSynRecv; + string strTimeWait; + string strCloseWait; +#ifdef _WIN32 + //使用R"()" 可以不用转义 + strEstab = R"(netstat -na|find /i /c "ESTABLISHED")"; + strSynRecv = R"(netstat -na|find /i /c "SYN_RECV")"; + strTimeWait = R"(netstat -na|find /i /c "TIME_WAIT")"; + strCloseWait = R"(netstat -na|find /i /c "CLOSE_WAIT")"; +#else + strEstab = "netstat -na|grep ESTABLISHED|wc -l"; + strSynRecv = "netstat -na|grep SYN_RECV|wc -l"; + strTimeWait = "netstat -na|grep TIME_WAIT|wc -l"; + strCloseWait = "netstat -na|grep CLOSE_WAIT|wc -l"; +#endif + usage.established = atoi(trim(System::execute(strEstab)).data()); + usage.syn_recv = atoi(trim(System::execute(strSynRecv)).data()); + usage.time_wait = atoi(trim(System::execute(strTimeWait)).data()); + usage.close_wait = atoi(trim(System::execute(strCloseWait)).data()); + return true; } string System::execute(const string &cmd) { // DebugL << cmd; - FILE *fPipe = popen(cmd.data(), "r"); + FILE *fPipe = NULL; +#ifdef _WIN32 + fPipe = _popen(cmd.data(), "r"); +#else + fPipe = popen(cmd.data(), "r"); +#endif if(!fPipe){ return ""; } @@ -265,7 +292,12 @@ string System::execute(const string &cmd) { while(fgets(buff, sizeof(buff) - 1, fPipe)){ ret.append(buff); } +#ifdef _WIN32 + _pclose(fPipe); +#else pclose(fPipe); +#endif + return ret; } @@ -274,8 +306,11 @@ static string addr2line(const string &address) { return System::execute(cmd); } -#ifndef ANDROID static void sig_crash(int sig) { +#ifdef _WIN32 + +#else +#ifndef ANDROID signal(sig, SIG_DFL); void *array[MAX_STACK_FRAMES]; int size = backtrace(array, MAX_STACK_FRAMES); @@ -296,12 +331,14 @@ static void sig_crash(int sig) { free(strings); NoticeCenter::Instance().emitEvent(kBroadcastOnCrashDump,sig,stack); +#endif//#ifndef ANDROID +#endif // _WIN32 } -#endif//#ifndef ANDROID - - void System::startDaemon() { +#ifdef _WIN32 + +#else static pid_t pid; do{ pid = fork(); @@ -336,14 +373,17 @@ void System::startDaemon() { DebugL << "waitpid被中断:" << get_uv_errmsg(); }while (true); }while (true); +#endif // _WIN32 } - - static string currentDateTime(){ time_t ts = time(NULL); std::tm tm_snapshot; - localtime_r(&ts, &tm_snapshot); +#ifndef _WIN32 + localtime_r(&ts, &tm_snapshot); +#else + localtime_s(&tm_snapshot, &ts); +#endif // !_WIN32 char buffer[1024] = {0}; std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm_snapshot); @@ -351,6 +391,9 @@ static string currentDateTime(){ } void System::systemSetup(){ +#ifdef _WIN32 + +#else struct rlimit rlim,rlim_new; if (getrlimit(RLIMIT_CORE, &rlim)==0) { rlim_new.rlim_cur = rlim_new.rlim_max = RLIM_INFINITY; @@ -394,5 +437,6 @@ void System::systemSetup(){ cerr << stack_info << endl; }); +#endif } diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 04dd3715..b2eb6713 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -47,10 +47,7 @@ #include "WebHook.h" #include "Thread/WorkThreadPool.h" #include "Rtp/RtpSelector.h" - -#if !defined(_WIN32) #include "FFmpegSource.h" -#endif//!defined(_WIN32) using namespace Json; using namespace toolkit; @@ -268,10 +265,8 @@ static inline string getProxyKey(const string &vhost,const string &app,const str return vhost + "/" + app + "/" + stream; } -#if !defined(_WIN32) static unordered_map s_ffmpegMap; static recursive_mutex s_ffmpegMapMtx; -#endif//#if !defined(_WIN32) /** * 安装api接口 @@ -646,7 +641,6 @@ void installWebApi() { val["data"]["flag"] = s_proxyMap.erase(allArgs["key"]) == 1; }); -#if !defined(_WIN32) static auto addFFmpegSource = [](const string &src_url, const string &dst_url, int timeout_ms, @@ -713,7 +707,6 @@ void installWebApi() { API_REGIST(api,delFFmepgSource,{ api_delFFmpegSource(API_ARGS_VALUE); }); -#endif //新增http api下载可执行程序文件接口 //测试url http://127.0.0.1/index/api/downloadBin @@ -932,10 +925,8 @@ void unInstallWebApi(){ s_proxyMap.clear(); } -#if !defined(_WIN32) { lock_guard lck(s_ffmpegMapMtx); s_ffmpegMap.clear(); } -#endif } \ No newline at end of file diff --git a/tests/test_server.cpp b/tests/test_server.cpp index cb6dc2da..02d67a1c 100644 --- a/tests/test_server.cpp +++ b/tests/test_server.cpp @@ -239,7 +239,7 @@ int main(int argc,char *argv[]) { //这里是拉流地址,支持rtmp/rtsp协议,负载必须是H264+AAC //如果是其他不识别的音视频将会被忽略(譬如说h264+adpcm转发后会去除音频) - auto urlList = {"rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov" + auto urlList = {"rtsp://admin:admin123@192.168.1.64:554/cam/realmonitor?channel=1&subtype=1" //rtsp链接支持输入用户名密码 /*"rtsp://admin:jzan123456@192.168.0.122/"*/}; map proxyMap; @@ -258,7 +258,7 @@ int main(int argc,char *argv[]) { //rtsp://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4 //rtmp://127.0.0.1/record/live/0/2017-04-11/11-09-38.mp4 - PlayerProxy::Ptr player(new PlayerProxy(DEFAULT_VHOST, "live", to_string(i).data())); + PlayerProxy::Ptr player(new PlayerProxy(DEFAULT_VHOST, "live", std::string("chn") + to_string(i).data())); //指定RTP over TCP(播放rtsp时有效) (*player)[kRtpType] = Rtsp::RTP_TCP; //开始播放,如果播放失败或者播放中止,将会自动重试若干次,重试次数在配置文件中配置,默认一直重试 From 5b533354c3490f1417ded1575fd11bdaa8d71816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AD=90=E9=BE=99?= <18675721@qq.com> Date: Tue, 7 Jan 2020 11:17:55 +0800 Subject: [PATCH 201/272] =?UTF-8?q?=E5=A2=9E=E5=8A=A0todo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 7c1d07b2..20edd50f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # 一个基于C++11的高性能运营级流媒体服务框架 +## FreeNVR TODO +- 完善ffmpeg日志 +- 完善system + ## 项目特点 - 基于C++11开发,避免使用裸指针,代码稳定可靠;同时跨平台移植简单方便,代码清晰简洁。 - 打包多种流媒体协议(RTSP/RTMP/HLS/HTTP-FLV/Websocket-FLV),支持协议间的互相转换,提供一站式的服务。 From 9c42c5ed8c317fa8793cd2aa96369595c8d03f51 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 7 Jan 2020 14:37:18 +0800 Subject: [PATCH 202/272] =?UTF-8?q?windows=E8=A7=86=E9=A2=91FFmpeg?= =?UTF-8?q?=E6=8B=89=E6=B5=81=E4=BB=A3=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/CMakeLists.txt | 7 +- server/FFmpegSource.cpp | 19 ++- server/Process.cpp | 208 ++++++++++++++++++++------------ server/Process.h | 11 +- server/System.cpp | 261 ++++------------------------------------ server/System.h | 56 +-------- server/WebApi.cpp | 9 -- 7 files changed, 178 insertions(+), 393 deletions(-) diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 5bc42a8d..092a4221 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -3,12 +3,7 @@ file(GLOB jsoncpp_src_list ../3rdpart/jsoncpp/*.cpp ../3rdpart/jsoncpp/*.h ) add_library(jsoncpp STATIC ${jsoncpp_src_list}) -if (CMAKE_SYSTEM_NAME MATCHES "Windows") - set(MediaServer_src_list ./WebApi.cpp ./WebHook.cpp main.cpp) -else() - file(GLOB MediaServer_src_list ./*.cpp ./*.h) -endif() - +file(GLOB MediaServer_src_list ./*.cpp ./*.h) #message(STATUS ${MediaServer_src_list}) add_executable(MediaServer ${MediaServer_src_list}) diff --git a/server/FFmpegSource.cpp b/server/FFmpegSource.cpp index 3824f910..d8a821c6 100644 --- a/server/FFmpegSource.cpp +++ b/server/FFmpegSource.cpp @@ -32,13 +32,22 @@ namespace FFmpeg { #define FFmpeg_FIELD "ffmpeg." -const char kBin[] = FFmpeg_FIELD"bin"; -const char kCmd[] = FFmpeg_FIELD"cmd"; -const char kLog[] = FFmpeg_FIELD"log"; +const string kBin = FFmpeg_FIELD"bin"; +const string kCmd = FFmpeg_FIELD"cmd"; +const string kLog = FFmpeg_FIELD"log"; onceToken token([]() { - mINI::Instance()[kBin] = trim(System::execute("which ffmpeg")); - mINI::Instance()[kCmd] = "%s -re -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s"; +#ifdef _WIN32 + string ffmpeg_bin = System::execute("where ffmpeg"); + //windows下先关闭FFmpeg日志(目前不支持日志重定向) + mINI::Instance()[kCmd] = "%s -re -i \"%s\" -loglevel quiet -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s "; +#else + string ffmpeg_bin = System::execute("which ffmpeg"); + mINI::Instance()[kCmd] = "%s -re -i \"%s\" -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s "; +#endif + //默认ffmpeg命令路径为环境变量中路径 + mINI::Instance()[kBin] = ffmpeg_bin.empty() ? "ffmpeg" : ffmpeg_bin; + //ffmpeg日志保存路径 mINI::Instance()[kLog] = "./ffmpeg/ffmpeg.log"; }); } diff --git a/server/Process.cpp b/server/Process.cpp index e215dc81..d64ae72a 100644 --- a/server/Process.cpp +++ b/server/Process.cpp @@ -26,8 +26,15 @@ #include #include + +#ifndef _WIN32 #include #include +#else +//#include +#include +#endif + #include #include #include "Util/util.h" @@ -39,75 +46,95 @@ using namespace toolkit; void Process::run(const string &cmd, const string &log_file_tmp) { - kill(2000); - _pid = fork(); - if (_pid < 0) { - throw std::runtime_error(StrPrinter << "fork child process falied,err:" << get_uv_errmsg()); - } - if (_pid == 0) { - //子进程关闭core文件生成 - struct rlimit rlim = {0,0}; - setrlimit(RLIMIT_CORE, &rlim); + kill(2000); +#ifdef _WIN32 + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory(&si, sizeof(si)); //结构体初始化; + ZeroMemory(&pi, sizeof(pi)); - //在启动子进程时,暂时禁用SIGINT、SIGTERM信号 - // ignore the SIGINT and SIGTERM - signal(SIGINT, SIG_IGN); - signal(SIGTERM, SIG_IGN); + LPTSTR lpDir = const_cast(cmd .data()); - string log_file ; - if(log_file_tmp.empty()){ - log_file = "/dev/null"; - }else{ - log_file = StrPrinter << log_file_tmp << "." << getpid(); - } + if (CreateProcess(NULL, lpDir, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)){ + //下面两行关闭句柄,解除本进程和新进程的关系,不然有可能 不小心调用TerminateProcess函数关掉子进程 + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); - int log_fd = -1; - int flags = O_CREAT | O_WRONLY | O_APPEND; - mode_t mode = S_IRWXO | S_IRWXG | S_IRWXU;// S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; - File::createfile_path(log_file.data(), mode); - if ((log_fd = ::open(log_file.c_str(), flags, mode)) < 0) { - fprintf(stderr, "open log file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno)); - } else { - // dup to stdout and stderr. - if (dup2(log_fd, STDOUT_FILENO) < 0) { - fprintf(stderr, "dup2 stdout file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno)); - } - if (dup2(log_fd, STDERR_FILENO) < 0) { - fprintf(stderr, "dup2 stderr file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno)); - } - // close log fd - ::close(log_fd); - } - fprintf(stderr, "\r\n\r\n#### pid=%d,cmd=%s #####\r\n\r\n", getpid(), cmd.data()); + _pid = pi.dwProcessId; + InfoL << "start child proces " << _pid; + } else { + WarnL << "start child proces fail: " << GetLastError(); + } +#else + _pid = fork(); + if (_pid < 0) { + throw std::runtime_error(StrPrinter << "fork child process falied,err:" << get_uv_errmsg()); + } + if (_pid == 0) { + //子进程关闭core文件生成 + struct rlimit rlim = { 0,0 }; + setrlimit(RLIMIT_CORE, &rlim); - // close other fds - // TODO: do in right way. - for (int i = 3; i < 1024; i++) { - ::close(i); - } + //在启动子进程时,暂时禁用SIGINT、SIGTERM信号 + // ignore the SIGINT and SIGTERM + signal(SIGINT, SIG_IGN); + signal(SIGTERM, SIG_IGN); - auto params = split(cmd, " "); - // memory leak in child process, it's ok. - char **charpv_params = new char *[params.size() + 1]; - for (int i = 0; i < (int) params.size(); i++) { - std::string &p = params[i]; - charpv_params[i] = (char *) p.data(); - } - // EOF: NULL - charpv_params[params.size()] = NULL; + string log_file; + if (log_file_tmp.empty()) { + log_file = "/dev/null"; + } + else { + log_file = StrPrinter << log_file_tmp << "." << getpid(); + } - // TODO: execv or execvp - auto ret = execv(params[0].c_str(), charpv_params); - if (ret < 0) { - fprintf(stderr, "fork process failed, errno=%d(%s)\r\n", errno, strerror(errno)); - } - exit(ret); - } + int log_fd = -1; + int flags = O_CREAT | O_WRONLY | O_APPEND; + mode_t mode = S_IRWXO | S_IRWXG | S_IRWXU;// S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; + File::createfile_path(log_file.data(), mode); + if ((log_fd = ::open(log_file.c_str(), flags, mode)) < 0) { + fprintf(stderr, "open log file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno)); + } + else { + // dup to stdout and stderr. + if (dup2(log_fd, STDOUT_FILENO) < 0) { + fprintf(stderr, "dup2 stdout file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno)); + } + if (dup2(log_fd, STDERR_FILENO) < 0) { + fprintf(stderr, "dup2 stderr file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno)); + } + // close log fd + ::close(log_fd); + } + fprintf(stderr, "\r\n\r\n#### pid=%d,cmd=%s #####\r\n\r\n", getpid(), cmd.data()); - InfoL << "start child proces " << _pid; + // close other fds + // TODO: do in right way. + for (int i = 3; i < 1024; i++) { + ::close(i); + } + + auto params = split(cmd, " "); + // memory leak in child process, it's ok. + char **charpv_params = new char *[params.size() + 1]; + for (int i = 0; i < (int)params.size(); i++) { + std::string &p = params[i]; + charpv_params[i] = (char *)p.data(); + } + // EOF: NULL + charpv_params[params.size()] = NULL; + + // TODO: execv or execvp + auto ret = execv(params[0].c_str(), charpv_params); + if (ret < 0) { + fprintf(stderr, "fork process failed, errno=%d(%s)\r\n", errno, strerror(errno)); + } + exit(ret); + } + InfoL << "start child proces " << _pid; +#endif // _WIN32 } - /** * 获取进程是否存活状态 * @param pid 进程号 @@ -120,20 +147,30 @@ static bool s_wait(pid_t pid,int *exit_code_ptr,bool block) { return false; } int status = 0; - pid_t p = waitpid(pid, &status, block ? 0 : WNOHANG); - int exit_code = (status & 0xFF00) >> 8; - if(exit_code_ptr){ - *exit_code_ptr = (status & 0xFF00) >> 8; - } - if (p < 0) { - WarnL << "waitpid failed, pid=" << pid << ", err=" << get_uv_errmsg(); - return false; - } - if (p > 0) { - InfoL << "process terminated, pid=" << pid << ", exit code=" << exit_code; - return false; - } - //WarnL << "process is running, pid=" << _pid; +#ifdef _WIN32 + HANDLE hProcess = NULL; + hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); //打开目标进程 + if (hProcess == NULL) { + return false; + } + + CloseHandle(hProcess); +#else + pid_t p = waitpid(pid, &status, block ? 0 : WNOHANG); + int exit_code = (status & 0xFF00) >> 8; + if (exit_code_ptr) { + *exit_code_ptr = (status & 0xFF00) >> 8; + } + if (p < 0) { + WarnL << "waitpid failed, pid=" << pid << ", err=" << get_uv_errmsg(); + return false; + } + if (p > 0) { + InfoL << "process terminated, pid=" << pid << ", exit code=" << exit_code; + return false; + } +#endif // _WIN32 + return true; } @@ -142,12 +179,25 @@ static void s_kill(pid_t pid,int max_delay,bool force){ //pid无效 return; } +#ifdef _WIN32 + HANDLE hProcess = NULL; + hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); //打开目标进程 + if (hProcess == NULL) { + WarnL << "\nOpen Process fAiled: " << GetLastError(); + return; + } + DWORD ret = TerminateProcess(hProcess, 0); //结束目标进程 + if (ret == 0) { + WarnL << GetLastError; + } +#else + if (::kill(pid, force ? SIGKILL : SIGTERM) == -1) { + //进程可能已经退出了 + WarnL << "kill process " << pid << " failed:" << get_uv_errmsg(); + return; + } +#endif // _WIN32 - if (::kill(pid, force ? SIGKILL : SIGTERM) == -1) { - //进程可能已经退出了 - WarnL << "kill process " << pid << " failed:" << get_uv_errmsg(); - return; - } if(force){ //发送SIGKILL信号后,阻塞等待退出 diff --git a/server/Process.h b/server/Process.h index cce8470a..392ccbbf 100644 --- a/server/Process.h +++ b/server/Process.h @@ -23,10 +23,15 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -#ifndef IPTV_PROCESS_H -#define IPTV_PROCESS_H +#ifndef ZLMEDIAKIT_PROCESS_H +#define ZLMEDIAKIT_PROCESS_H +#ifdef _WIN32 +typedef int pid_t; +#else #include +#endif // _WIN32 + #include #include using namespace std; @@ -45,4 +50,4 @@ private: }; -#endif //IPTV_PROCESS_H +#endif //ZLMEDIAKIT_PROCESS_H diff --git a/server/System.cpp b/server/System.cpp index dc9fdb15..df666120 100644 --- a/server/System.cpp +++ b/server/System.cpp @@ -24,239 +24,36 @@ * SOFTWARE. */ -#include "System.h" -#include -#include -#include +#if !defined(_WIN32) #include #include -#include #include -#ifndef ANDROID +#if !defined(ANDROID) #include -#endif -#include -#include -#include -#include "Util/mini.h" -#include "Util/util.h" -#include "Util/logger.h" -#include "Util/onceToken.h" -#include "Util/NoticeCenter.h" +#endif//!defined(ANDROID) +#endif//!defined(_WIN32) + #include "System.h" +#include +#include +#include +#include "Util/logger.h" +#include "Util/NoticeCenter.h" #include "Util/uv_errno.h" -#include "Util/CMD.h" -#include "Util/MD5.h" using namespace toolkit; const int MAX_STACK_FRAMES = 128; #define BroadcastOnCrashDumpArgs int &sig,const vector > &stack const char kBroadcastOnCrashDump[] = "kBroadcastOnCrashDump"; -//#if defined(__MACH__) || defined(__APPLE__) -//#define TEST_LINUX -//#endif - -vector splitWithEmptyLine(const string &s, const char *delim) { - vector ret; - int last = 0; - int index = s.find(delim, last); - while (index != string::npos) { - ret.push_back(s.substr(last, index - last)); - last = index + strlen(delim); - index = s.find(delim, last); - } - if (s.size() - last > 0) { - ret.push_back(s.substr(last)); - } - return ret; -} - -map splitTopStr(const string &cmd_str) { - map ret; - auto lines = splitWithEmptyLine(cmd_str, "\n"); - int i = 0; - for (auto &line : lines) { - if(i++ < 1 && line.empty()){ - continue; - } - if (line.empty()) { - break; - } - auto line_vec = split(line, ":"); - - if (line_vec.size() < 2) { - continue; - } - trim(line_vec[0], " \r\n\t"); - auto args_vec = split(line_vec[1], ","); - for (auto &arg : args_vec) { - auto arg_vec = split(trim(arg, " \r\n\t."), " "); - if (arg_vec.size() < 2) { - continue; - } - ret[line_vec[0]].emplace(arg_vec[1], arg_vec[0]); - } - } - return ret; -} - -bool System::getSystemUsage(SystemUsage &usage) { - try { -#if defined(__linux) || defined(__linux__) || defined(TEST_LINUX) - string cmd_str; -#if !defined(TEST_LINUX) - cmd_str = System::execute("top -b -n 1"); -#else - cmd_str = "top - 07:21:31 up 5:48, 2 users, load average: 0.03, 0.62, 0.54\n" - "Tasks: 80 total, 1 running, 78 sleeping, 0 stopped, 1 zombie\n" - "%Cpu(s): 0.8 us, 0.4 sy, 0.0 ni, 98.8 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st\n" - "KiB Mem: 2058500 total, 249100 used, 1809400 free, 19816 buffers\n" - "KiB Swap: 1046524 total, 0 used, 1046524 free. 153012 cached Mem\n" - "\n"; +#ifdef _WIN32 +#define popen _popen +#define pclose _pclose #endif - if (cmd_str.empty()) { - WarnL << "System::execute(\"top -b -n 1\") return empty"; - return false; - } - auto topMap = splitTopStr(cmd_str); - - usage.task_total = topMap["Tasks"]["total"]; - usage.task_running = topMap["Tasks"]["running"]; - usage.task_sleeping = topMap["Tasks"]["sleeping"]; - usage.task_stopped = topMap["Tasks"]["stopped"]; - - usage.cpu_user = topMap["%Cpu(s)"]["us"]; - usage.cpu_sys = topMap["%Cpu(s)"]["sy"]; - usage.cpu_idle = topMap["%Cpu(s)"]["id"]; - - usage.mem_total = topMap["KiB Mem"]["total"]; - usage.mem_free = topMap["KiB Mem"]["free"]; - usage.mem_used = topMap["KiB Mem"]["used"]; - return true; - -#elif defined(__MACH__) || defined(__APPLE__) - /* - "Processes: 275 total, 2 running, 1 stuck, 272 sleeping, 1258 threads \n" - "2018/09/12 10:41:32\n" - "Load Avg: 2.06, 2.88, 2.86 \n" - "CPU usage: 14.54% user, 25.45% sys, 60.0% idle \n" - "SharedLibs: 117M resident, 37M data, 15M linkedit.\n" - "MemRegions: 46648 total, 3654M resident, 62M private, 714M shared.\n" - "PhysMem: 7809M used (1906M wired), 381M unused.\n" - "VM: 751G vsize, 614M framework vsize, 0(0) swapins, 0(0) swapouts.\n" - "Networks: packets: 502366/248M in, 408957/87M out.\n" - "Disks: 349435/6037M read, 78622/2577M written."; - */ - - string cmd_str = System::execute("top -l 1"); - if(cmd_str.empty()){ - WarnL << "System::execute(\"top -n 1\") return empty"; - return false; - } - auto topMap = splitTopStr(cmd_str); - usage.task_total = topMap["Processes"]["total"]; - usage.task_running = topMap["Processes"]["running"]; - usage.task_sleeping = topMap["Processes"]["sleeping"]; - usage.task_stopped = topMap["Processes"]["stuck"]; - - usage.cpu_user = topMap["CPU usage"]["user"]; - usage.cpu_sys = topMap["CPU usage"]["sys"]; - usage.cpu_idle = topMap["CPU usage"]["idle"]; - - usage.mem_free = topMap["PhysMem"]["unused"].as() * 1024 * 1024; - usage.mem_used = topMap["PhysMem"]["used"].as() * 1024 * 1024; - usage.mem_total = usage.mem_free + usage.mem_used; - return true; -#else - WarnL << "System not supported"; - return false; -#endif - } catch (std::exception &ex) { - WarnL << ex.what(); - return false; - } -} - -bool System::getNetworkUsage(vector &usage) { - try { -#if defined(__linux) || defined(__linux__) || defined(TEST_LINUX) - string cmd_str; -#if !defined(TEST_LINUX) - cmd_str = System::execute("cat /proc/net/dev"); -#else - cmd_str = - "Inter-| Receive | Transmit\n" - " face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed\n" - " lo: 475978 7546 0 0 0 0 0 0 475978 7546 0 0 0 0 0 0\n" - "enp3s0: 151747818 315136 0 0 0 0 0 145 1590783447 1124616 0 0 0 0 0 0"; -#endif - if (cmd_str.empty()) { - return false; - } - auto lines = split(cmd_str, "\n"); - int i = 0; - vector column_name_vec; - vector category_prefix_vec; - for (auto &line : lines) { - switch (i++) { - case 0: { - category_prefix_vec = split(line, "|"); - } - break; - case 1: { - auto category_suffix_vec = split(line, "|"); - int j = 0; - for (auto &category_suffix : category_suffix_vec) { - auto column_suffix_vec = split(category_suffix, " "); - for (auto &column_suffix : column_suffix_vec) { - column_name_vec.emplace_back(trim(category_prefix_vec[j]) + "-" + trim(column_suffix)); - } - j++; - } - } - break; - default: { - mINI valMap; - auto vals = split(line, " "); - int j = 0; - for (auto &val : vals) { - valMap[column_name_vec[j++]] = trim(val, " \r\n\t:"); - } - usage.emplace_back(NetworkUsage()); - auto &ifrUsage = usage.back(); - ifrUsage.interface = valMap["Inter--face"]; - ifrUsage.recv_bytes = valMap["Receive-bytes"]; - ifrUsage.recv_packets = valMap["Receive-packets"]; - ifrUsage.snd_bytes = valMap["Transmit-bytes"]; - ifrUsage.snd_packets = valMap["Transmit-packets"]; - } - break; - } - } - return true; -#else - WarnL << "System not supported"; - return false; -#endif - } catch (std::exception &ex) { - WarnL << ex.what(); - return false; - } -} - - -bool System::getTcpUsage(System::TcpUsage &usage) { - usage.established = atoi(trim(System::execute("netstat -na|grep ESTABLISHED|wc -l")).data()); - usage.syn_recv = atoi(trim(System::execute("netstat -na|grep SYN_RECV|wc -l")).data()); - usage.time_wait = atoi(trim(System::execute("netstat -na|grep TIME_WAIT|wc -l")).data()); - usage.close_wait = atoi(trim(System::execute("netstat -na|grep CLOSE_WAIT|wc -l")).data()); - return true; -} string System::execute(const string &cmd) { -// DebugL << cmd; - FILE *fPipe = popen(cmd.data(), "r"); + FILE *fPipe = NULL; + fPipe = popen(cmd.data(), "r"); if(!fPipe){ return ""; } @@ -269,12 +66,12 @@ string System::execute(const string &cmd) { return ret; } +#if !defined(ANDROID) && !defined(_WIN32) static string addr2line(const string &address) { string cmd = StrPrinter << "addr2line -e " << exePath() << " " << address; return System::execute(cmd); } -#ifndef ANDROID static void sig_crash(int sig) { signal(sig, SIG_DFL); void *array[MAX_STACK_FRAMES]; @@ -294,14 +91,13 @@ static void sig_crash(int sig) { #endif//__linux } free(strings); - NoticeCenter::Instance().emitEvent(kBroadcastOnCrashDump,sig,stack); } - -#endif//#ifndef ANDROID +#endif // !defined(ANDROID) && !defined(_WIN32) void System::startDaemon() { +#ifndef _WIN32 static pid_t pid; do{ pid = fork(); @@ -336,21 +132,11 @@ void System::startDaemon() { DebugL << "waitpid被中断:" << get_uv_errmsg(); }while (true); }while (true); -} - - - -static string currentDateTime(){ - time_t ts = time(NULL); - std::tm tm_snapshot; - localtime_r(&ts, &tm_snapshot); - - char buffer[1024] = {0}; - std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &tm_snapshot); - return buffer; +#endif // _WIN32 } void System::systemSetup(){ +#if !defined(_WIN32) struct rlimit rlim,rlim_new; if (getrlimit(RLIMIT_CORE, &rlim)==0) { rlim_new.rlim_cur = rlim_new.rlim_max = RLIM_INFINITY; @@ -373,11 +159,9 @@ void System::systemSetup(){ #ifndef ANDROID signal(SIGSEGV, sig_crash); signal(SIGABRT, sig_crash); -#endif//#ifndef ANDROID - NoticeCenter::Instance().addListener(nullptr,kBroadcastOnCrashDump,[](BroadcastOnCrashDumpArgs){ stringstream ss; - ss << "## crash date:" << currentDateTime() << endl; + ss << "## crash date:" << getTimeStr("%Y-%m-%d %H:%M:%S") << endl; ss << "## exe: " << exeName() << endl; ss << "## signal: " << sig << endl; ss << "## stack: " << endl; @@ -391,8 +175,9 @@ void System::systemSetup(){ ofstream out(StrPrinter << exeDir() << "/crash." << getpid(), ios::out | ios::binary | ios::trunc); out << stack_info; out.flush(); - cerr << stack_info << endl; }); +#endif// ANDROID +#endif//!defined(_WIN32) } diff --git a/server/System.h b/server/System.h index c8857a73..565b99b3 100644 --- a/server/System.h +++ b/server/System.h @@ -24,67 +24,17 @@ * SOFTWARE. */ -#ifndef IPTV_BASH_H -#define IPTV_BASH_H +#ifndef ZLMEDIAKIT_SYSTEM_H +#define ZLMEDIAKIT_SYSTEM_H -#include #include -#include -#include -#include "Util/NoticeCenter.h" - using namespace std; -using namespace toolkit; class System { public: - typedef struct { - uint32_t task_total; - uint32_t task_running; - uint32_t task_sleeping; - uint32_t task_stopped; - - uint64_t mem_total; - uint64_t mem_free; - uint64_t mem_used; - - float cpu_user; - float cpu_sys; - float cpu_idle; - } SystemUsage; - - typedef struct { - uint64_t recv_bytes; - uint64_t recv_packets; - uint64_t snd_bytes; - uint64_t snd_packets; - string interface; - } NetworkUsage; - - typedef struct { - uint64_t available; - uint64_t used; - float used_per; - string mounted_on; - string filesystem; - bool mounted; - } DiskUsage; - - typedef struct { - uint32_t established; - uint32_t syn_recv; - uint32_t time_wait; - uint32_t close_wait; - } TcpUsage; - - static bool getSystemUsage(SystemUsage &usage); - static bool getNetworkUsage(vector &usage); - static bool getTcpUsage(TcpUsage &usage); static string execute(const string &cmd); static void startDaemon(); static void systemSetup(); - }; - -#endif //IPTV_BASH_H +#endif //ZLMEDIAKIT_SYSTEM_H diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 04dd3715..b2eb6713 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -47,10 +47,7 @@ #include "WebHook.h" #include "Thread/WorkThreadPool.h" #include "Rtp/RtpSelector.h" - -#if !defined(_WIN32) #include "FFmpegSource.h" -#endif//!defined(_WIN32) using namespace Json; using namespace toolkit; @@ -268,10 +265,8 @@ static inline string getProxyKey(const string &vhost,const string &app,const str return vhost + "/" + app + "/" + stream; } -#if !defined(_WIN32) static unordered_map s_ffmpegMap; static recursive_mutex s_ffmpegMapMtx; -#endif//#if !defined(_WIN32) /** * 安装api接口 @@ -646,7 +641,6 @@ void installWebApi() { val["data"]["flag"] = s_proxyMap.erase(allArgs["key"]) == 1; }); -#if !defined(_WIN32) static auto addFFmpegSource = [](const string &src_url, const string &dst_url, int timeout_ms, @@ -713,7 +707,6 @@ void installWebApi() { API_REGIST(api,delFFmepgSource,{ api_delFFmpegSource(API_ARGS_VALUE); }); -#endif //新增http api下载可执行程序文件接口 //测试url http://127.0.0.1/index/api/downloadBin @@ -932,10 +925,8 @@ void unInstallWebApi(){ s_proxyMap.clear(); } -#if !defined(_WIN32) { lock_guard lck(s_ffmpegMapMtx); s_ffmpegMap.clear(); } -#endif } \ No newline at end of file From 16751ddf04ab6eb78cecbc0f7b4b77e7a30c6486 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 7 Jan 2020 15:10:59 +0800 Subject: [PATCH 203/272] =?UTF-8?q?windows=E6=94=AF=E6=8C=81FFmpeg?= =?UTF-8?q?=E6=8B=89=E6=B5=81=E4=BB=A3=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/FFmpegSource.cpp | 2 +- server/Process.cpp | 2 +- server/Process.h | 2 +- server/System.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server/FFmpegSource.cpp b/server/FFmpegSource.cpp index 43c9b7ea..d8a821c6 100644 --- a/server/FFmpegSource.cpp +++ b/server/FFmpegSource.cpp @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> diff --git a/server/Process.cpp b/server/Process.cpp index b18fa563..d64ae72a 100644 --- a/server/Process.cpp +++ b/server/Process.cpp @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> diff --git a/server/Process.h b/server/Process.h index 048ba9a0..392ccbbf 100644 --- a/server/Process.h +++ b/server/Process.h @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> diff --git a/server/System.cpp b/server/System.cpp index c934b421..df666120 100644 --- a/server/System.cpp +++ b/server/System.cpp @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> From 09a2623e1bfcd3ee21d3e30a6116052d77e51f25 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 7 Jan 2020 15:16:01 +0800 Subject: [PATCH 204/272] =?UTF-8?q?=E8=BF=98=E5=8E=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0aaa1b47..38731a39 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,17 @@ +![logo](https://raw.githubusercontent.com/zlmediakit/ZLMediaKit/master/logo.png) + +[english readme](https://github.com/xiongziliang/ZLMediaKit/blob/master/README_en.md) + # 一个基于C++11的高性能运营级流媒体服务框架 + [![Build Status](https://travis-ci.org/xiongziliang/ZLMediaKit.svg?branch=master)](https://travis-ci.org/xiongziliang/ZLMediaKit) -## FreeNVR TODO -- 完善ffmpeg日志 -- 完善system +## 国内用户请使用gitee镜像下载 +``` +git clone --depth 1 https://gitee.com/xiahcu/ZLMediaKit +cd ZLMediaKit +git submodule update --init +``` ## 项目特点 - 基于C++11开发,避免使用裸指针,代码稳定可靠;同时跨平台移植简单方便,代码清晰简洁。 - 打包多种流媒体协议(RTSP/RTMP/HLS/HTTP-FLV/Websocket-FLV),支持协议间的互相转换,提供一站式的服务。 @@ -21,7 +29,6 @@ - 移动嵌入式跨平台流媒体解决方案。 - 商用级流媒体服务器。 - 网络编程二次开发SDK。 -- 免费NVR(rtxp拉流代理、分发)。 ## 功能清单 @@ -360,9 +367,8 @@ git submodule update --init 由于使用本项目而产生的商业纠纷或侵权行为一概与本项项目及开发者无关,请自行承担法律风险。 ## 联系方式 - - 邮箱:<18675721@qq.com>(本项目相关或流媒体相关问题请走issue流程,否则恕不邮件答复) - - QQ群:542509000 (ZLMediakit) - - QQ群:936467414 (FreeNVR) + - 邮箱:<771730766@qq.com>(本项目相关或流媒体相关问题请走issue流程,否则恕不邮件答复) + - QQ群:542509000 ## 怎么提问? 如果要对项目有相关疑问,建议您这么做: @@ -371,6 +377,12 @@ git submodule update --init - 3、有些问题,如果不具备参考性的,无需在issue提的,可以在qq群提. - 4、QQ私聊一般不接受无偿技术咨询和支持(谈谈人生理想还是可以的😂),毕竟精力有限,谢谢理解. +## 捐赠 +欢迎捐赠以便更好的推动项目的发展,谢谢您的支持! + +[支付宝](https://raw.githubusercontent.com/xiongziliang/other/master/IMG_3919.JPG) + +[微信](https://raw.githubusercontent.com/xiongziliang/other/master/IMG_3920.JPG) From 5258169a0730c63e104b2be26ff1fd5207cab6fc Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 7 Jan 2020 15:37:48 +0800 Subject: [PATCH 205/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8D=8F=E8=AE=AE?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2=E9=85=8D=E7=BD=AE=E5=BC=80=E5=85=B3=E4=B8=8D?= =?UTF-8?q?=E7=94=9F=E6=95=88=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebHook.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/server/WebHook.cpp b/server/WebHook.cpp index b28a5844..6e0605bf 100644 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -211,10 +211,10 @@ void installWebHook(){ GET_CONFIG(string,hook_http_access,Hook::kOnHttpAccess); NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastMediaPublish,[](BroadcastMediaPublishArgs){ + GET_CONFIG(bool,toRtxp,General::kPublishToRtxp); + GET_CONFIG(bool,toHls,General::kPublishToHls); + GET_CONFIG(bool,toMP4,General::kPublishToMP4); if(!hook_enable || args._param_strs == hook_adminparams || hook_publish.empty() || sender.get_peer_ip() == "127.0.0.1"){ - GET_CONFIG(bool,toRtxp,General::kPublishToRtxp); - GET_CONFIG(bool,toHls,General::kPublishToHls); - GET_CONFIG(bool,toMP4,General::kPublishToMP4); invoker("",toRtxp,toHls,toMP4); return; } @@ -227,24 +227,20 @@ void installWebHook(){ do_http_hook(hook_publish,body,[invoker](const Value &obj,const string &err){ if(err.empty()){ //推流鉴权成功 - bool enableRtxp = true; - bool enableHls = true; - bool enableMP4 = false; - //兼容用户不传递enableRtxp、enableHls、enableMP4参数 if(obj.isMember("enableRtxp")){ - enableRtxp = obj["enableRtxp"].asBool(); + toRtxp = obj["enableRtxp"].asBool(); } if(obj.isMember("enableHls")){ - enableHls = obj["enableHls"].asBool(); + toHls = obj["enableHls"].asBool(); } if(obj.isMember("enableMP4")){ - enableMP4 = obj["enableMP4"].asBool(); + toMP4 = obj["enableMP4"].asBool(); } - invoker(err,enableRtxp,enableHls,enableMP4); + invoker(err,toRtxp,toHls,toMP4); }else{ //推流鉴权失败 invoker(err,false, false, false); From 9cdbe4c4efd592410dde0565c4b14c205b75c624 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 7 Jan 2020 15:38:35 +0800 Subject: [PATCH 206/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8D=8F=E8=AE=AE?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2=E9=85=8D=E7=BD=AE=E5=BC=80=E5=85=B3=E4=B8=8D?= =?UTF-8?q?=E7=94=9F=E6=95=88=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebHook.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/server/WebHook.cpp b/server/WebHook.cpp index 6e0605bf..4eb54eec 100644 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -227,20 +227,24 @@ void installWebHook(){ do_http_hook(hook_publish,body,[invoker](const Value &obj,const string &err){ if(err.empty()){ //推流鉴权成功 + bool enableRtxp = toRtxp; + bool enableHls = toHls; + bool enableMP4 = toMP4; + //兼容用户不传递enableRtxp、enableHls、enableMP4参数 if(obj.isMember("enableRtxp")){ - toRtxp = obj["enableRtxp"].asBool(); + enableRtxp = obj["enableRtxp"].asBool(); } if(obj.isMember("enableHls")){ - toHls = obj["enableHls"].asBool(); + enableHls = obj["enableHls"].asBool(); } if(obj.isMember("enableMP4")){ - toMP4 = obj["enableMP4"].asBool(); + enableMP4 = obj["enableMP4"].asBool(); } - invoker(err,toRtxp,toHls,toMP4); + invoker(err,enableRtxp,enableHls,enableMP4); }else{ //推流鉴权失败 invoker(err,false, false, false); From 57c5d2963af6f72383eb2622e174fe95b13b4549 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 8 Jan 2020 12:14:27 +0800 Subject: [PATCH 207/272] =?UTF-8?q?=E4=BC=98=E5=8C=96DevChannel=E5=86=85?= =?UTF-8?q?=E5=AD=98=E6=8B=B7=E8=B4=9D=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/Device.cpp | 16 ++++++++++++++-- src/Extension/AAC.h | 1 - src/Extension/AACRtp.cpp | 1 - src/Player/PlayerProxy.cpp | 16 +++++++++++++--- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/Common/Device.cpp b/src/Common/Device.cpp index a04dba72..450ed9e3 100644 --- a/src/Common/Device.cpp +++ b/src/Common/Device.cpp @@ -104,7 +104,13 @@ void DevChannel::inputH264(const char* pcData, int iDataLen, uint32_t dts,uint32 } else { prefixeSize = 0; } - inputFrame(std::make_shared((char *)pcData,iDataLen,dts,pts,prefixeSize)); + + H264Frame::Ptr frame = std::make_shared(); + frame->timeStamp = dts; + frame->ptsStamp = pts; + frame->buffer.assign("\x00\x00\x00\x01",4); + frame->buffer.append(pcData + prefixeSize, iDataLen - prefixeSize); + inputFrame(frame); } void DevChannel::inputH265(const char* pcData, int iDataLen, uint32_t dts,uint32_t pts) { @@ -122,7 +128,13 @@ void DevChannel::inputH265(const char* pcData, int iDataLen, uint32_t dts,uint32 } else { prefixeSize = 0; } - inputFrame(std::make_shared((char *)pcData,iDataLen,dts,pts,prefixeSize)); + + H265Frame::Ptr frame = std::make_shared(); + frame->timeStamp = dts; + frame->ptsStamp = pts; + frame->buffer.assign("\x00\x00\x00\x01",4); + frame->buffer.append(pcData + prefixeSize, iDataLen - prefixeSize); + inputFrame(frame); } void DevChannel::inputAAC(const char* pcData, int iDataLen, uint32_t uiStamp,bool withAdtsHeader) { diff --git a/src/Extension/AAC.h b/src/Extension/AAC.h index fe78509e..063756f6 100644 --- a/src/Extension/AAC.h +++ b/src/Extension/AAC.h @@ -104,7 +104,6 @@ public: //表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据) unsigned int no_raw_data_blocks_in_frame; //2 uimsfb unsigned char buffer[2 * 1024 + 7]; - uint16_t sequence; uint32_t timeStamp; uint32_t iPrefixSize = 7; } ; diff --git a/src/Extension/AACRtp.cpp b/src/Extension/AACRtp.cpp index 2f001959..7b436fb0 100644 --- a/src/Extension/AACRtp.cpp +++ b/src/Extension/AACRtp.cpp @@ -149,7 +149,6 @@ bool AACRtpDecoder::inputRtp(const RtpPacket::Ptr &rtppack, bool key_pos) { memcpy(_adts->buffer + _adts->aac_frame_length, rtp_packet_payload + next_aac_payload_offset, cur_aac_payload_len); _adts->aac_frame_length += (cur_aac_payload_len); if (rtppack->mark == true) { - _adts->sequence = rtppack->sequence; _adts->timeStamp = rtppack->timeStamp; writeAdtsHeader(*_adts, _adts->buffer); onGetAAC(_adts); diff --git a/src/Player/PlayerProxy.cpp b/src/Player/PlayerProxy.cpp index 5badcce0..250ea932 100644 --- a/src/Player/PlayerProxy.cpp +++ b/src/Player/PlayerProxy.cpp @@ -222,13 +222,23 @@ public: auto iAudioIndex = frame->stamp() / MUTE_ADTS_DATA_MS; if(_iAudioIndex != iAudioIndex){ _iAudioIndex = iAudioIndex; - auto aacFrame = std::make_shared((char *)MUTE_ADTS_DATA, - MUTE_ADTS_DATA_LEN, - _iAudioIndex * MUTE_ADTS_DATA_MS); + auto aacFrame = std::make_shared((char *)MUTE_ADTS_DATA, MUTE_ADTS_DATA_LEN, _iAudioIndex * MUTE_ADTS_DATA_MS); FrameDispatcher::inputFrame(aacFrame); } } } + +private: + class AACFrameCacheAble : public AACFrameNoCacheAble{ + public: + template + AACFrameCacheAble(ARGS && ...args) : AACFrameNoCacheAble(std::forward(args)...){}; + virtual ~AACFrameCacheAble() = default; + + bool cacheAble() const override { + return true; + } + }; private: int _iAudioIndex = 0; }; From 5d2864cff20fd742f633e80690f1006d7989ab4a Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 8 Jan 2020 12:15:59 +0800 Subject: [PATCH 208/272] =?UTF-8?q?=E4=BC=98=E5=8C=96DevChannel=E5=86=85?= =?UTF-8?q?=E5=AD=98=E6=8B=B7=E8=B4=9D=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/Device.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Common/Device.cpp b/src/Common/Device.cpp index 450ed9e3..39a76825 100644 --- a/src/Common/Device.cpp +++ b/src/Common/Device.cpp @@ -110,6 +110,7 @@ void DevChannel::inputH264(const char* pcData, int iDataLen, uint32_t dts,uint32 frame->ptsStamp = pts; frame->buffer.assign("\x00\x00\x00\x01",4); frame->buffer.append(pcData + prefixeSize, iDataLen - prefixeSize); + frame->iPrefixSize = 4; inputFrame(frame); } @@ -134,6 +135,7 @@ void DevChannel::inputH265(const char* pcData, int iDataLen, uint32_t dts,uint32 frame->ptsStamp = pts; frame->buffer.assign("\x00\x00\x00\x01",4); frame->buffer.append(pcData + prefixeSize, iDataLen - prefixeSize); + frame->iPrefixSize = 4; inputFrame(frame); } From ab32ca39a944dae4c1f74ca876273c4de2b77476 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 8 Jan 2020 14:00:53 +0800 Subject: [PATCH 209/272] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtmp/RtmpProtocol.cpp | 10 +++++----- src/Rtmp/RtmpSession.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Rtmp/RtmpProtocol.cpp b/src/Rtmp/RtmpProtocol.cpp index ad21351f..4de21a47 100644 --- a/src/Rtmp/RtmpProtocol.cpp +++ b/src/Rtmp/RtmpProtocol.cpp @@ -332,7 +332,7 @@ void RtmpProtocol::handle_C0C1() { //complex handsharke handle_C1_complex(); #else - WarnL << "未打开ENABLE_OPENSSL宏,复杂握手采用简单方式处理!"; + WarnL << "未打开ENABLE_OPENSSL宏,复杂握手采用简单方式处理,flash播放器可能无法播放!"; handle_C1_simple(); #endif//ENABLE_OPENSSL } @@ -372,10 +372,10 @@ void RtmpProtocol::handle_C1_complex(){ check_C1_Digest(digest,c1_joined); send_complex_S0S1S2(0,digest); - InfoL << "schema0"; +// InfoL << "schema0"; }catch(std::exception &ex){ //貌似flash从来都不用schema1 - WarnL << "try rtmp complex schema0 failed:" << ex.what(); +// WarnL << "try rtmp complex schema0 failed:" << ex.what(); try{ /* c1s1 schema1 time: 4bytes @@ -389,9 +389,9 @@ void RtmpProtocol::handle_C1_complex(){ check_C1_Digest(digest,c1_joined); send_complex_S0S1S2(1,digest); - InfoL << "schema1"; +// InfoL << "schema1"; }catch(std::exception &ex){ - WarnL << "try rtmp complex schema1 failed:" << ex.what(); +// WarnL << "try rtmp complex schema1 failed:" << ex.what(); handle_C1_simple(); } } diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index e7e93688..bf3aa217 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -452,7 +452,7 @@ void RtmpSession::onProcessCmd(AMFDecoder &dec) { std::string method = dec.load(); auto it = s_cmd_functions.find(method); if (it == s_cmd_functions.end()) { - TraceP(this) << "can not support cmd:" << method; +// TraceP(this) << "can not support cmd:" << method; return; } _dNowReqID = dec.load(); From 653132b38cdd59409c3bc067f57c4352b840afb8 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 8 Jan 2020 14:03:56 +0800 Subject: [PATCH 210/272] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtmp/RtmpSession.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index bf3aa217..6f067fea 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -473,10 +473,11 @@ void RtmpSession::onRtmpChunk(RtmpPacket &chunkData) { case MSG_DATA3: { AMFDecoder dec(chunkData.strBuf, chunkData.typeId == MSG_CMD3 ? 1 : 0); std::string type = dec.load(); - TraceP(this) << "notify:" << type; if (type == "@setDataFrame") { setMetaData(dec); - } + }else{ + TraceP(this) << "unknown notify:" << type; + } } break; case MSG_AUDIO: From bad036fdac2f1a48e99987d8a8f1ff298189b5e5 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 9 Jan 2020 10:06:30 +0800 Subject: [PATCH 211/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=87=B4=E8=B0=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 38731a39..c62cf3c8 100644 --- a/README.md +++ b/README.md @@ -376,6 +376,25 @@ git submodule update --init - 2、如果您的问题还没解决,可以提issue. - 3、有些问题,如果不具备参考性的,无需在issue提的,可以在qq群提. - 4、QQ私聊一般不接受无偿技术咨询和支持(谈谈人生理想还是可以的😂),毕竟精力有限,谢谢理解. + +## 致谢 +感谢以下各位对本项目包括但不限于代码贡献、问题反馈、资金捐赠等各种方式的支持!以下排名不分先后: + +[老陈](https://github.com/ireader) +[Gemfield](https://github.com/gemfield) +[南冠彤](https://github.com/nanguantong2) +[凹凸慢](https://github.com/tsingeye) +[chenxiaolei](https://github.com/chenxiaolei) +[史前小虫](https://github.com/zqsong) +[清涩绿茶](https://github.com/baiyfcu) +[3503207480](https://github.com/3503207480) +[DroidChow](https://github.com/DroidChow) +[阿塞](https://github.com/HuoQiShuai) +[火宣](https://github.com/ChinaCCF) +[γ瑞γミ](https://github.com/JerryLinGd) +[linkingvision](https://www.linkingvision.com/) +[茄子](https://github.com/taotaobujue2008) +[好心情](<409257224@qq.com>) ## 捐赠 欢迎捐赠以便更好的推动项目的发展,谢谢您的支持! From 835d6bd9b590ea46e757f6c002fa101149b621eb Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 9 Jan 2020 17:03:29 +0800 Subject: [PATCH 212/272] =?UTF-8?q?=E5=85=81=E8=AE=B8=E6=9C=AC=E6=9C=BA?= =?UTF-8?q?=E5=92=8C=E8=B6=85=E7=BA=A7=E7=94=A8=E6=88=B7=E4=BB=BB=E6=84=8F?= =?UTF-8?q?=E8=AE=BF=E9=97=AEhttp=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebHook.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/WebHook.cpp b/server/WebHook.cpp index 4eb54eec..e2e7824e 100644 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -438,7 +438,7 @@ void installWebHook(){ //如果没有url参数,客户端又不支持cookie,那么会根据ip和端口追踪用户 //追踪用户的目的是为了缓存上次鉴权结果,减少鉴权次数,提高性能 NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastHttpAccess,[](BroadcastHttpAccessArgs){ - if(sender.get_peer_ip() == "127.0.0.1" && parser.Params() == hook_adminparams){ + if(sender.get_peer_ip() == "127.0.0.1" || parser.Params() == hook_adminparams){ //如果是本机或超级管理员访问,那么不做访问鉴权;权限有效期1个小时 invoker("","",60 * 60); return; From 8acb6e209358e2fe4a77dd51384928567bba4bbc Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 9 Jan 2020 17:17:48 +0800 Subject: [PATCH 213/272] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mk_media.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/include/mk_media.h b/api/include/mk_media.h index d8162f6e..16516de4 100755 --- a/api/include/mk_media.h +++ b/api/include/mk_media.h @@ -87,7 +87,7 @@ API_EXPORT void API_CALL mk_media_init_aac(mk_media ctx, int channel, int sample * @param data 单帧H264数据 * @param len 单帧H264数据字节数 * @param dts 解码时间戳,单位毫秒 - * @param dts 播放时间戳,单位毫秒 + * @param pts 播放时间戳,单位毫秒 */ API_EXPORT void API_CALL mk_media_input_h264(mk_media ctx, void *data, int len, uint32_t dts, uint32_t pts); @@ -97,7 +97,7 @@ API_EXPORT void API_CALL mk_media_input_h264(mk_media ctx, void *data, int len, * @param data 单帧H265数据 * @param len 单帧H265数据字节数 * @param dts 解码时间戳,单位毫秒 - * @param dts 播放时间戳,单位毫秒 + * @param pts 播放时间戳,单位毫秒 */ API_EXPORT void API_CALL mk_media_input_h265(mk_media ctx, void *data, int len, uint32_t dts, uint32_t pts); From db146406c31a3e8d618585908605f4f148d4d821 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 10 Jan 2020 15:29:21 +0800 Subject: [PATCH 214/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dgcc4.85=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E7=BC=96=E8=AF=91=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/WebSocketClient.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/WebSocketClient.h b/src/Http/WebSocketClient.h index 85051fc3..e3d215a7 100644 --- a/src/Http/WebSocketClient.h +++ b/src/Http/WebSocketClient.h @@ -303,7 +303,7 @@ private: //拦截websocket数据接收 _onRecv = [this](const Buffer::Ptr &pBuf){ //解析websocket数据包 - WebSocketSplitter::decode((uint8_t*)pBuf->data(),pBuf->size()); + this->WebSocketSplitter::decode((uint8_t*)pBuf->data(),pBuf->size()); }; return; } From 66ec67bfb9c752837496c83277c1f347f8ef5bdd Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 13 Jan 2020 11:51:29 +0800 Subject: [PATCH 215/272] =?UTF-8?q?1=E3=80=81=E4=BF=AE=E5=A4=8D=E7=94=9F?= =?UTF-8?q?=E6=88=90=E7=9A=84rtmp=E6=97=A0=E6=B3=95=E8=A2=ABflash=E6=92=AD?= =?UTF-8?q?=E6=94=BE=E7=9A=84=E9=97=AE=E9=A2=98=202=E3=80=81=E4=BF=AE?= =?UTF-8?q?=E5=A4=8DRTSP=E6=9C=89B=E5=B8=A7=E6=97=B6=EF=BC=8C=E7=9B=B8?= =?UTF-8?q?=E5=AF=B9=E6=97=B6=E9=97=B4=E6=88=B3=E8=AE=A1=E7=AE=97=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/Stamp.cpp | 14 ++++++++------ src/Extension/H264.h | 1 + src/Extension/H264Rtmp.cpp | 6 +++++- src/Record/MP4Reader.cpp | 12 +++++++----- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/Common/Stamp.cpp b/src/Common/Stamp.cpp index 9d2c2d45..7330de21 100644 --- a/src/Common/Stamp.cpp +++ b/src/Common/Stamp.cpp @@ -27,25 +27,27 @@ #include "Stamp.h" #define MAX_DELTA_STAMP 300 +#define ABS(x) ((x) > 0 ? (x) : (-x)) namespace mediakit { int64_t DeltaStamp::deltaStamp(int64_t stamp) { if(!_last_stamp){ //第一次计算时间戳增量,时间戳增量为0 - _last_stamp = stamp; + if(stamp){ + _last_stamp = stamp; + } return 0; } int64_t ret = stamp - _last_stamp; - if(ret >= 0){ - //时间戳增量为正,返回之 + if(ABS(ret) < MAX_DELTA_STAMP){ + //时间戳变化不明显 _last_stamp = stamp; - //在直播情况下,时间戳增量不得大于MAX_DELTA_STAMP - return ret < MAX_DELTA_STAMP ? ret : (_playback ? ret : 0); + return ret; } - //时间戳增量为负,说明时间戳回环了或回退了 + //时间戳变化太明显,可能回环了或者seek了 _last_stamp = stamp; return _playback ? ret : 0; } diff --git a/src/Extension/H264.h b/src/Extension/H264.h index 4bb8ea1c..3ffb058d 100644 --- a/src/Extension/H264.h +++ b/src/Extension/H264.h @@ -49,6 +49,7 @@ public: NAL_SPS = 7, NAL_PPS = 8, NAL_IDR = 5, + NAL_SEI = 6, } NalType; char *data() const override{ diff --git a/src/Extension/H264Rtmp.cpp b/src/Extension/H264Rtmp.cpp index 3ab77554..9ee877e7 100644 --- a/src/Extension/H264Rtmp.cpp +++ b/src/Extension/H264Rtmp.cpp @@ -140,6 +140,10 @@ void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) { } } + if(type == H264Frame::NAL_SEI){ + return; + } + if(_lastPacket && _lastPacket->timeStamp != frame->stamp()) { RtmpCodec::inputRtmp(_lastPacket, _lastPacket->isVideoKeyFrame()); _lastPacket = nullptr; @@ -149,7 +153,7 @@ void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) { //I or P or B frame int8_t flags = 7; //h.264 bool is_config = false; - flags |= ((frame->keyFrame() ? FLV_KEY_FRAME : FLV_INTER_FRAME) << 4); + flags |= (((frame->configFrame() || frame->keyFrame()) ? FLV_KEY_FRAME : FLV_INTER_FRAME) << 4); _lastPacket = ResourcePoolHelper::obtainObj(); _lastPacket->strBuf.clear(); diff --git a/src/Record/MP4Reader.cpp b/src/Record/MP4Reader.cpp index 9bf20525..020fa6bc 100644 --- a/src/Record/MP4Reader.cpp +++ b/src/Record/MP4Reader.cpp @@ -225,8 +225,10 @@ inline bool MP4Reader::readVideoSample(int iTimeInc,bool justSeekSyncFrame) { uint32_t numBytes = _video_sample_max_size; MP4Duration pRenderingOffset; if(MP4ReadSample(_hMP4File, _video_trId, iIdx + 1, &pBytes, &numBytes,NULL,NULL,&pRenderingOffset,&_bSyncSample)){ - if (!justSeekSyncFrame) { - uint32_t iOffset = 0; + if (!justSeekSyncFrame) { + uint32_t dts = (double) _video_ms * iIdx / _video_num_samples; + uint32_t pts = dts + pRenderingOffset / 90; + uint32_t iOffset = 0; while (iOffset < numBytes) { uint32_t iFrameLen; memcpy(&iFrameLen,pBytes + iOffset,4); @@ -235,8 +237,7 @@ inline bool MP4Reader::readVideoSample(int iTimeInc,bool justSeekSyncFrame) { break; } memcpy(pBytes + iOffset, "\x0\x0\x0\x1", 4); - uint32_t dts = (double) _video_ms * iIdx / _video_num_samples; - writeH264(pBytes + iOffset, iFrameLen + 4, dts, dts + pRenderingOffset / 90); + writeH264(pBytes + iOffset, iFrameLen + 4, dts, pts); iOffset += (iFrameLen + 4); } }else if(_bSyncSample){ @@ -260,9 +261,10 @@ inline bool MP4Reader::readAudioSample(int iTimeInc,bool justSeekSyncFrame) { uint8_t *pBytes = _adts.buffer + 7; if(MP4ReadSample(_hMP4File, _audio_trId, i + 1, &pBytes, &numBytes)){ if (!justSeekSyncFrame) { + uint32_t dts = (double) _audio_ms * i / _audio_num_samples; _adts.aac_frame_length = 7 + numBytes; writeAdtsHeader(_adts, _adts.buffer); - writeAAC(_adts.buffer, _adts.aac_frame_length, (double) _audio_ms * i / _audio_num_samples); + writeAAC(_adts.buffer, _adts.aac_frame_length, dts); } }else{ ErrorL << "读取音频失败:" << i+ 1; From b55db11de314b2321213275b2d01cf1900b7f5f0 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 13 Jan 2020 15:48:55 +0800 Subject: [PATCH 216/272] =?UTF-8?q?=E8=A7=A3=E5=86=B3rtmp=E8=BF=87?= =?UTF-8?q?=E6=97=A9=E6=B3=A8=E5=86=8C=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/MediaSink.cpp | 82 ++++++++++++++++++++++++-------------- src/Common/MediaSink.h | 3 +- src/Common/Stamp.cpp | 5 ++- src/Rtmp/RtmpMediaSource.h | 11 ++--- src/Rtmp/RtmpPlayerImp.h | 6 +++ src/Rtmp/RtmpSession.cpp | 6 +++ src/Rtmp/RtmpSession.h | 1 + 7 files changed, 75 insertions(+), 39 deletions(-) diff --git a/src/Common/MediaSink.cpp b/src/Common/MediaSink.cpp index 64049dc5..eb7cf533 100644 --- a/src/Common/MediaSink.cpp +++ b/src/Common/MediaSink.cpp @@ -26,7 +26,11 @@ #include "MediaSink.h" //最多等待未初始化的Track 10秒,超时之后会忽略未初始化的Track -#define MAX_WAIT_MS 10000 +#define MAX_WAIT_MS_READY 10000 + +//如果添加Track,最多等待3秒 +#define MAX_WAIT_MS_ADD_TRACK 3000 + namespace mediakit{ @@ -34,23 +38,16 @@ void MediaSink::addTrack(const Track::Ptr &track_in) { lock_guard lck(_mtx); //克隆Track,只拷贝其数据,不拷贝其数据转发关系 auto track = track_in->clone(); - auto codec_id = track->getCodecId(); _track_map[codec_id] = track; - auto lam = [this,track](){ + _allTrackReady = false; + _trackReadyCallback[codec_id] = [this, track]() { onTrackReady(track); }; - if(track->ready()){ - lam(); - }else{ - _anyTrackUnReady = true; - _allTrackReady = false; - _trackReadyCallback[codec_id] = lam; - _ticker.resetTime(); - } + _ticker.resetTime(); - track->addDelegate(std::make_shared([this](const Frame::Ptr &frame){ - if(!_anyTrackUnReady){ + track->addDelegate(std::make_shared([this](const Frame::Ptr &frame) { + if (_allTrackReady) { onTrackFrame(frame); } })); @@ -58,7 +55,6 @@ void MediaSink::addTrack(const Track::Ptr &track_in) { void MediaSink::resetTracks() { lock_guard lck(_mtx); - _anyTrackUnReady = false; _allTrackReady = false; _track_map.clear(); _trackReadyCallback.clear(); @@ -83,26 +79,50 @@ void MediaSink::inputFrame(const Frame::Ptr &frame) { } } - if(!_allTrackReady && (_trackReadyCallback.empty() || _ticker.elapsedTime() > MAX_WAIT_MS)){ - _allTrackReady = true; - _anyTrackUnReady = false; - if(!_trackReadyCallback.empty()){ - //这是超时强制忽略未准备好的Track - _trackReadyCallback.clear(); - //移除未准备好的Track - for(auto it = _track_map.begin() ; it != _track_map.end() ; ){ - if(!it->second->ready()){ - it = _track_map.erase(it); - continue; - } - ++it; - } + if(!_allTrackReady){ + if(_ticker.elapsedTime() > MAX_WAIT_MS_READY){ + //如果超过规定时间,那么不再等待并忽略未准备好的Track + emitAllTrackReady(); + return; } - if(!_track_map.empty()){ - //最少有一个有效的Track - onAllTrackReady(); + if(!_trackReadyCallback.empty()){ + //在超时时间内,如果存在未准备好的Track,那么继续等待 + return; } + + if(_track_map.size() == 2){ + //如果已经添加了音视频Track,并且不存在未准备好的Track,那么说明所有Track都准备好了 + emitAllTrackReady(); + return; + } + + if(_track_map.size() == 1 && _ticker.elapsedTime() > MAX_WAIT_MS_ADD_TRACK){ + //如果只有一个Track,那么在该Track添加后,我们最多还等待若干时间(可能后面还会添加Track) + emitAllTrackReady(); + return; + } + } +} + +void MediaSink::emitAllTrackReady() { + _allTrackReady = true; + if(!_trackReadyCallback.empty()){ + //这是超时强制忽略未准备好的Track + _trackReadyCallback.clear(); + //移除未准备好的Track + for(auto it = _track_map.begin() ; it != _track_map.end() ; ){ + if(!it->second->ready()){ + it = _track_map.erase(it); + continue; + } + ++it; + } + } + + if(!_track_map.empty()){ + //最少有一个有效的Track + onAllTrackReady(); } } diff --git a/src/Common/MediaSink.h b/src/Common/MediaSink.h index 64317385..e37980e1 100644 --- a/src/Common/MediaSink.h +++ b/src/Common/MediaSink.h @@ -109,12 +109,13 @@ protected: * @param frame */ virtual void onTrackFrame(const Frame::Ptr &frame) {}; +private: + void emitAllTrackReady(); private: mutable recursive_mutex _mtx; map _track_map; map > _trackReadyCallback; bool _allTrackReady = false; - bool _anyTrackUnReady = false; Ticker _ticker; }; diff --git a/src/Common/Stamp.cpp b/src/Common/Stamp.cpp index 7330de21..e01887e3 100644 --- a/src/Common/Stamp.cpp +++ b/src/Common/Stamp.cpp @@ -26,7 +26,8 @@ #include "Stamp.h" -#define MAX_DELTA_STAMP 300 +#define MAX_DELTA_STAMP 1000 +#define MAX_CTS 500 #define ABS(x) ((x) > 0 ? (x) : (-x)) namespace mediakit { @@ -77,7 +78,7 @@ void Stamp::revise(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out, dts_out = _relativeStamp; //////////////以下是播放时间戳的计算////////////////// - if(pts_dts_diff > MAX_DELTA_STAMP || pts_dts_diff < -MAX_DELTA_STAMP){ + if(ABS(pts_dts_diff) > MAX_CTS){ //如果差值太大,则认为由于回环导致时间戳错乱了 pts_dts_diff = 0; } diff --git a/src/Rtmp/RtmpMediaSource.h b/src/Rtmp/RtmpMediaSource.h index 982d4401..928c9066 100644 --- a/src/Rtmp/RtmpMediaSource.h +++ b/src/Rtmp/RtmpMediaSource.h @@ -72,7 +72,6 @@ public: const string &stream_id, int ring_size = 0) : MediaSource(RTMP_SCHEMA, vhost, app, stream_id), _ring_size(ring_size) { - _metadata = TitleMeta().getMetadata(); } virtual ~RtmpMediaSource() {} @@ -117,6 +116,9 @@ public: virtual void setMetaData(const AMFValue &metadata) { lock_guard lock(_mtx); _metadata = metadata; + if(_ring){ + regist(); + } } /** @@ -143,10 +145,9 @@ public: _ring = std::make_shared(_ring_size, std::move(lam)); onReaderChanged(0); - //如果输入了非config帧, - //那么说明不再可能获取config帧以及metadata, - //所以我们强制其为已注册 - regist(); + if(_metadata){ + regist(); + } } _track_stamps_map[pkt->typeId] = pkt->timeStamp; _ring->write(pkt, pkt->isVideoKeyFrame()); diff --git a/src/Rtmp/RtmpPlayerImp.h b/src/Rtmp/RtmpPlayerImp.h index b06be303..e90acc43 100644 --- a/src/Rtmp/RtmpPlayerImp.h +++ b/src/Rtmp/RtmpPlayerImp.h @@ -66,6 +66,7 @@ private: _pRtmpMediaSrc = dynamic_pointer_cast(_pMediaSrc); if(_pRtmpMediaSrc){ _pRtmpMediaSrc->setMetaData(val); + _set_meta_data = true; } _delegate.reset(new RtmpDemuxer); _delegate->loadMetaData(val); @@ -73,6 +74,10 @@ private: } void onMediaData(const RtmpPacket::Ptr &chunkData) override { if(_pRtmpMediaSrc){ + if(!_set_meta_data && !chunkData->isCfgFrame()){ + _set_meta_data = true; + _pRtmpMediaSrc->setMetaData(TitleMeta().getMetadata()); + } _pRtmpMediaSrc->onWrite(chunkData); } if(!_delegate){ @@ -83,6 +88,7 @@ private: } private: RtmpMediaSource::Ptr _pRtmpMediaSrc; + bool _set_meta_data = false; }; diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index 6f067fea..061e512a 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -434,6 +434,7 @@ void RtmpSession::setMetaData(AMFDecoder &dec) { auto metadata = dec.load(); // dumpMetadata(metadata); _pPublisherSrc->setMetaData(metadata); + _set_meta_data = true; } void RtmpSession::onProcessCmd(AMFDecoder &dec) { @@ -491,6 +492,11 @@ void RtmpSession::onRtmpChunk(RtmpPacket &chunkData) { _stamp[chunkData.typeId % 2].revise(chunkData.timeStamp, chunkData.timeStamp, dts_out, dts_out, true); chunkData.timeStamp = dts_out; } + + if(!_set_meta_data && !chunkData.isCfgFrame()){ + _set_meta_data = true; + _pPublisherSrc->setMetaData(TitleMeta().getMetadata()); + } _pPublisherSrc->onWrite(std::make_shared(std::move(chunkData))); } break; diff --git a/src/Rtmp/RtmpSession.h b/src/Rtmp/RtmpSession.h index 572d40f8..f5b6df79 100644 --- a/src/Rtmp/RtmpSession.h +++ b/src/Rtmp/RtmpSession.h @@ -95,6 +95,7 @@ private: std::string _strTcUrl; MediaInfo _mediaInfo; double _dNowReqID = 0; + bool _set_meta_data = false; Ticker _ticker;//数据接收时间 RingBuffer::RingReader::Ptr _pRingReader; std::shared_ptr _pPublisherSrc; From 9fa6e9d8d9f75651fb7224a291d21237c3ec90e8 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 14 Jan 2020 10:04:24 +0800 Subject: [PATCH 217/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0dts=E7=94=9F=E6=88=90?= =?UTF-8?q?=E7=AE=97=E6=B3=95=EF=BC=8C=E5=85=BC=E5=AE=B9=E5=90=ABB?= =?UTF-8?q?=E5=B8=A7=E7=9A=84rtsp=E6=8E=A8=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/Device.cpp | 20 ++++++------- src/Common/Stamp.cpp | 57 +++++++++++++++++++++++++++++++++++--- src/Common/Stamp.h | 24 +++++++++++++++- src/Extension/AACRtmp.cpp | 2 +- src/Extension/AACRtp.cpp | 2 +- src/Extension/Frame.h | 7 ----- src/Extension/H264.h | 38 ++++++++++++------------- src/Extension/H264Rtmp.cpp | 16 +++++------ src/Extension/H264Rtp.cpp | 42 +++++++++++++++------------- src/Extension/H264Rtp.h | 2 ++ src/Extension/H265.h | 46 +++++++++++++++--------------- src/Extension/H265Rtp.cpp | 38 +++++++++++++------------ src/Extension/H265Rtp.h | 2 ++ src/Player/PlayerProxy.cpp | 2 +- 14 files changed, 187 insertions(+), 111 deletions(-) diff --git a/src/Common/Device.cpp b/src/Common/Device.cpp index 39a76825..58fd53cb 100644 --- a/src/Common/Device.cpp +++ b/src/Common/Device.cpp @@ -106,11 +106,11 @@ void DevChannel::inputH264(const char* pcData, int iDataLen, uint32_t dts,uint32 } H264Frame::Ptr frame = std::make_shared(); - frame->timeStamp = dts; - frame->ptsStamp = pts; - frame->buffer.assign("\x00\x00\x00\x01",4); - frame->buffer.append(pcData + prefixeSize, iDataLen - prefixeSize); - frame->iPrefixSize = 4; + frame->_dts = dts; + frame->_pts = pts; + frame->_buffer.assign("\x00\x00\x00\x01",4); + frame->_buffer.append(pcData + prefixeSize, iDataLen - prefixeSize); + frame->_prefix_size = 4; inputFrame(frame); } @@ -131,11 +131,11 @@ void DevChannel::inputH265(const char* pcData, int iDataLen, uint32_t dts,uint32 } H265Frame::Ptr frame = std::make_shared(); - frame->timeStamp = dts; - frame->ptsStamp = pts; - frame->buffer.assign("\x00\x00\x00\x01",4); - frame->buffer.append(pcData + prefixeSize, iDataLen - prefixeSize); - frame->iPrefixSize = 4; + frame->_dts = dts; + frame->_pts = pts; + frame->_buffer.assign("\x00\x00\x00\x01",4); + frame->_buffer.append(pcData + prefixeSize, iDataLen - prefixeSize); + frame->_prefix_size = 4; inputFrame(frame); } diff --git a/src/Common/Stamp.cpp b/src/Common/Stamp.cpp index e01887e3..3a0ed5bf 100644 --- a/src/Common/Stamp.cpp +++ b/src/Common/Stamp.cpp @@ -42,13 +42,14 @@ int64_t DeltaStamp::deltaStamp(int64_t stamp) { } int64_t ret = stamp - _last_stamp; - if(ABS(ret) < MAX_DELTA_STAMP){ - //时间戳变化不明显 + if(ret >= 0){ + //时间戳增量为正,返回之 _last_stamp = stamp; - return ret; + //在直播情况下,时间戳增量不得大于MAX_DELTA_STAMP + return ret < MAX_DELTA_STAMP ? ret : (_playback ? ret : 0); } - //时间戳变化太明显,可能回环了或者seek了 + //时间戳增量为负,说明时间戳回环了或回退了 _last_stamp = stamp; return _playback ? ret : 0; } @@ -99,4 +100,52 @@ int64_t Stamp::getRelativeStamp() const { } +bool DtsGenerator::getDts(uint32_t pts, uint32_t &dts){ + bool ret = false; + if(pts == _last_pts){ + //pts未变,返回上次结果 + if(_last_dts){ + dts = _last_dts; + ret = true; + } + return ret; + } + + ret = getDts_l(pts,dts); + if(ret){ + //保存本次结果 + _last_dts = dts; + } + //记录上次pts + _last_pts = pts; + return ret; +} + +bool DtsGenerator::getDts_l(uint32_t pts, uint32_t &dts){ + if(pts > _last_max_pts){ + if(!_sorter_max_size && _frames_since_last_max_pts && _count_sorter_max_size++ > 0){ + _sorter_max_size = _frames_since_last_max_pts; + _dts_pts_offset = (pts - _last_max_pts) / 2; + InfoL << _sorter_max_size << " " << _dts_pts_offset; + } + _frames_since_last_max_pts = 0; + _last_max_pts = pts; + } + + _pts_sorter.emplace(pts); + ++_frames_since_last_max_pts; + + if(_sorter_max_size && _pts_sorter.size() > _sorter_max_size){ + auto it = _pts_sorter.begin(); + dts = *it + _dts_pts_offset; + if(dts > pts){ + //dts不能大于pts(基本不可能到达这个逻辑) + dts = pts; + } + _pts_sorter.erase(it); + return true; + } + return false; +} + }//namespace mediakit \ No newline at end of file diff --git a/src/Common/Stamp.h b/src/Common/Stamp.h index 09e77338..43d11159 100644 --- a/src/Common/Stamp.h +++ b/src/Common/Stamp.h @@ -27,8 +27,9 @@ #ifndef ZLMEDIAKIT_STAMP_H #define ZLMEDIAKIT_STAMP_H -#include "Util/TimeTicker.h" +#include #include +#include "Util/TimeTicker.h" using namespace toolkit; namespace mediakit { @@ -88,6 +89,27 @@ private: SmoothTicker _ticker; }; + +class DtsGenerator{ +public: + DtsGenerator() = default; + ~DtsGenerator() = default; + bool getDts(uint32_t pts, uint32_t &dts); +private: + bool getDts_l(uint32_t pts, uint32_t &dts); +private: + uint32_t _dts_pts_offset = 0; + uint32_t _last_dts = 0; + uint32_t _last_pts = 0; + uint32_t _last_max_pts = 0; + int _frames_since_last_max_pts = 0; + int _sorter_max_size = 0; + int _count_sorter_max_size = 0; + set _pts_sorter; + + +}; + }//namespace mediakit #endif //ZLMEDIAKIT_STAMP_H diff --git a/src/Extension/AACRtmp.cpp b/src/Extension/AACRtmp.cpp index 71ea9e66..c3efddb1 100644 --- a/src/Extension/AACRtmp.cpp +++ b/src/Extension/AACRtmp.cpp @@ -102,7 +102,7 @@ void AACRtmpEncoder::inputFrame(const Frame::Ptr &frame) { rtmpPkt->bodySize = rtmpPkt->strBuf.size(); rtmpPkt->chunkId = CHUNK_AUDIO; rtmpPkt->streamId = STREAM_MEDIA; - rtmpPkt->timeStamp = frame->stamp(); + rtmpPkt->timeStamp = frame->dts(); rtmpPkt->typeId = MSG_AUDIO; RtmpCodec::inputRtmp(rtmpPkt, false); } diff --git a/src/Extension/AACRtp.cpp b/src/Extension/AACRtp.cpp index 7b436fb0..a84c22be 100644 --- a/src/Extension/AACRtp.cpp +++ b/src/Extension/AACRtp.cpp @@ -41,7 +41,7 @@ AACRtpEncoder::AACRtpEncoder(uint32_t ui32Ssrc, void AACRtpEncoder::inputFrame(const Frame::Ptr &frame) { GET_CONFIG(uint32_t, cycleMS, Rtp::kCycleMS); - auto uiStamp = frame->stamp(); + auto uiStamp = frame->dts(); auto pcData = frame->data() + frame->prefixSize(); auto iLen = frame->size() - frame->prefixSize(); diff --git a/src/Extension/Frame.h b/src/Extension/Frame.h index 5e5121f9..d4cbca6b 100644 --- a/src/Extension/Frame.h +++ b/src/Extension/Frame.h @@ -81,13 +81,6 @@ class Frame : public Buffer, public CodecInfo { public: typedef std::shared_ptr Ptr; virtual ~Frame(){} - /** - * 时间戳,已经废弃,请使用dts() 、pts()接口 - */ - inline uint32_t stamp() const { - return dts(); - }; - /** * 返回解码时间戳,单位毫秒 diff --git a/src/Extension/H264.h b/src/Extension/H264.h index 3ffb058d..b4c71a6b 100644 --- a/src/Extension/H264.h +++ b/src/Extension/H264.h @@ -53,21 +53,21 @@ public: } NalType; char *data() const override{ - return (char *)buffer.data(); + return (char *)_buffer.data(); } uint32_t size() const override { - return buffer.size(); + return _buffer.size(); } uint32_t dts() const override { - return timeStamp; + return _dts; } uint32_t pts() const override { - return ptsStamp ? ptsStamp : timeStamp; + return _pts ? _pts : _dts; } uint32_t prefixSize() const override{ - return iPrefixSize; + return _prefix_size; } TrackType getTrackType() const override{ @@ -79,11 +79,11 @@ public: } bool keyFrame() const override { - return H264_TYPE(buffer[iPrefixSize]) == H264Frame::NAL_IDR; + return H264_TYPE(_buffer[_prefix_size]) == H264Frame::NAL_IDR; } bool configFrame() const override{ - switch(H264_TYPE(buffer[iPrefixSize]) ){ + switch(H264_TYPE(_buffer[_prefix_size]) ){ case H264Frame::NAL_SPS: case H264Frame::NAL_PPS: return true; @@ -92,10 +92,10 @@ public: } } public: - uint32_t timeStamp; - uint32_t ptsStamp = 0; - string buffer; - uint32_t iPrefixSize = 4; + uint32_t _dts = 0; + uint32_t _pts = 0; + uint32_t _prefix_size = 4; + string _buffer; }; @@ -340,19 +340,19 @@ private: if(!_sps.empty()){ auto spsFrame = std::make_shared(); - spsFrame->iPrefixSize = 4; - spsFrame->buffer.assign("\x0\x0\x0\x1",4); - spsFrame->buffer.append(_sps); - spsFrame->timeStamp = frame->stamp(); + spsFrame->_prefix_size = 4; + spsFrame->_buffer.assign("\x0\x0\x0\x1",4); + spsFrame->_buffer.append(_sps); + spsFrame->_dts = frame->dts(); VideoTrack::inputFrame(spsFrame); } if(!_pps.empty()){ auto ppsFrame = std::make_shared(); - ppsFrame->iPrefixSize = 4; - ppsFrame->buffer.assign("\x0\x0\x0\x1",4); - ppsFrame->buffer.append(_pps); - ppsFrame->timeStamp = frame->stamp(); + ppsFrame->_prefix_size = 4; + ppsFrame->_buffer.assign("\x0\x0\x0\x1",4); + ppsFrame->_buffer.append(_pps); + ppsFrame->_dts = frame->dts(); VideoTrack::inputFrame(ppsFrame); } } diff --git a/src/Extension/H264Rtmp.cpp b/src/Extension/H264Rtmp.cpp index 9ee877e7..c995317f 100644 --- a/src/Extension/H264Rtmp.cpp +++ b/src/Extension/H264Rtmp.cpp @@ -35,8 +35,8 @@ H264RtmpDecoder::H264RtmpDecoder() { H264Frame::Ptr H264RtmpDecoder::obtainFrame() { //从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象 auto frame = obtainObj(); - frame->buffer.clear(); - frame->iPrefixSize = 4; + frame->_buffer.clear(); + frame->_prefix_size = 4; return frame; } @@ -78,10 +78,10 @@ bool H264RtmpDecoder::decodeRtmp(const RtmpPacket::Ptr &pkt) { inline void H264RtmpDecoder::onGetH264(const char* pcData, int iLen, uint32_t dts,uint32_t pts) { #if 1 - _h264frame->timeStamp = dts; - _h264frame->ptsStamp = pts; - _h264frame->buffer.assign("\x0\x0\x0\x1", 4); //添加264头 - _h264frame->buffer.append(pcData, iLen); + _h264frame->_dts = dts; + _h264frame->_pts = pts; + _h264frame->_buffer.assign("\x0\x0\x0\x1", 4); //添加264头 + _h264frame->_buffer.append(pcData, iLen); //写入环形缓存 RtmpCodec::inputFrame(_h264frame); @@ -144,7 +144,7 @@ void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) { return; } - if(_lastPacket && _lastPacket->timeStamp != frame->stamp()) { + if(_lastPacket && _lastPacket->timeStamp != frame->dts()) { RtmpCodec::inputRtmp(_lastPacket, _lastPacket->isVideoKeyFrame()); _lastPacket = nullptr; } @@ -165,7 +165,7 @@ void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) { _lastPacket->chunkId = CHUNK_VIDEO; _lastPacket->streamId = STREAM_MEDIA; - _lastPacket->timeStamp = frame->stamp(); + _lastPacket->timeStamp = frame->dts(); _lastPacket->typeId = MSG_VIDEO; } diff --git a/src/Extension/H264Rtp.cpp b/src/Extension/H264Rtp.cpp index c9861f16..f90f3c56 100644 --- a/src/Extension/H264Rtp.cpp +++ b/src/Extension/H264Rtp.cpp @@ -70,8 +70,8 @@ H264RtpDecoder::H264RtpDecoder() { H264Frame::Ptr H264RtpDecoder::obtainFrame() { //从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象 auto frame = ResourcePoolHelper::obtainObj(); - frame->buffer.clear(); - frame->iPrefixSize = 4; + frame->_buffer.clear(); + frame->_prefix_size = 4; return frame; } @@ -113,9 +113,9 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { if (nal.type >= 0 && nal.type < 24) { //a full frame - _h264frame->buffer.assign("\x0\x0\x0\x1", 4); - _h264frame->buffer.append((char *)frame, length); - _h264frame->timeStamp = rtppack->timeStamp; + _h264frame->_buffer.assign("\x0\x0\x0\x1", 4); + _h264frame->_buffer.append((char *)frame, length); + _h264frame->_pts = rtppack->timeStamp; auto key = _h264frame->keyFrame(); onGetH264(_h264frame); return (key); //i frame @@ -142,9 +142,9 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { //过小的帧丢弃 NALU nal; MakeNalu(ptr[0], nal); - _h264frame->buffer.assign("\x0\x0\x0\x1", 4); - _h264frame->buffer.append((char *)ptr, len); - _h264frame->timeStamp = rtppack->timeStamp; + _h264frame->_buffer.assign("\x0\x0\x0\x1", 4); + _h264frame->_buffer.append((char *)ptr, len); + _h264frame->_pts = rtppack->timeStamp; if(nal.type == H264Frame::NAL_IDR){ haveIDR = true; } @@ -162,10 +162,10 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { if (fu.S) { //该帧的第一个rtp包 FU-A start char tmp = (nal.forbidden_zero_bit << 7 | nal.nal_ref_idc << 5 | fu.type); - _h264frame->buffer.assign("\x0\x0\x0\x1", 4); - _h264frame->buffer.push_back(tmp); - _h264frame->buffer.append((char *)frame + 2, length - 2); - _h264frame->timeStamp = rtppack->timeStamp; + _h264frame->_buffer.assign("\x0\x0\x0\x1", 4); + _h264frame->_buffer.push_back(tmp); + _h264frame->_buffer.append((char *)frame + 2, length - 2); + _h264frame->_pts = rtppack->timeStamp; //该函数return时,保存下当前sequence,以便下次对比seq是否连续 _lastSeq = rtppack->sequence; return _h264frame->keyFrame(); @@ -173,22 +173,22 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { if (rtppack->sequence != _lastSeq + 1 && rtppack->sequence != 0) { //中间的或末尾的rtp包,其seq必须连续(如果回环了则判定为连续),否则说明rtp丢包,那么该帧不完整,必须得丢弃 - _h264frame->buffer.clear(); + _h264frame->_buffer.clear(); WarnL << "rtp sequence不连续: " << rtppack->sequence << " != " << _lastSeq << " + 1,该帧被废弃"; return false; } if (!fu.E) { //该帧的中间rtp包 FU-A mid - _h264frame->buffer.append((char *)frame + 2, length - 2); + _h264frame->_buffer.append((char *)frame + 2, length - 2); //该函数return时,保存下当前sequence,以便下次对比seq是否连续 _lastSeq = rtppack->sequence; return false; } //该帧最后一个rtp包 FU-A end - _h264frame->buffer.append((char *)frame + 2, length - 2); - _h264frame->timeStamp = rtppack->timeStamp; + _h264frame->_buffer.append((char *)frame + 2, length - 2); + _h264frame->_pts = rtppack->timeStamp; auto key = _h264frame->keyFrame(); onGetH264(_h264frame); return key; @@ -209,8 +209,12 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { } void H264RtpDecoder::onGetH264(const H264Frame::Ptr &frame) { - //写入环形缓存 - RtpCodec::inputFrame(frame); + //根据pts计算dts + auto flag = _dts_generator.getDts(frame->_pts,frame->_dts); + if(flag){ + //写入环形缓存 + RtpCodec::inputFrame(frame); + } _h264frame = obtainFrame(); } @@ -232,7 +236,7 @@ H264RtpEncoder::H264RtpEncoder(uint32_t ui32Ssrc, void H264RtpEncoder::inputFrame(const Frame::Ptr &frame) { GET_CONFIG(uint32_t,cycleMS,Rtp::kCycleMS); auto pcData = frame->data() + frame->prefixSize(); - auto uiStamp = frame->stamp(); + auto uiStamp = frame->pts(); auto iLen = frame->size() - frame->prefixSize(); //获取NALU的5bit 帧类型 unsigned char naluType = H264_TYPE(pcData[0]); diff --git a/src/Extension/H264Rtp.h b/src/Extension/H264Rtp.h index 5ccac81d..da4aa2d6 100644 --- a/src/Extension/H264Rtp.h +++ b/src/Extension/H264Rtp.h @@ -30,6 +30,7 @@ #include "Rtsp/RtpCodec.h" #include "Util/ResourcePool.h" #include "Extension/H264.h" +#include "Common/Stamp.h" using namespace toolkit; namespace mediakit{ @@ -66,6 +67,7 @@ private: H264Frame::Ptr obtainFrame(); private: H264Frame::Ptr _h264frame; + DtsGenerator _dts_generator; int _lastSeq = 0; }; diff --git a/src/Extension/H265.h b/src/Extension/H265.h index 021ef3d1..80591e32 100644 --- a/src/Extension/H265.h +++ b/src/Extension/H265.h @@ -74,23 +74,23 @@ public: } NaleType; char *data() const override { - return (char *) buffer.data(); + return (char *) _buffer.data(); } uint32_t size() const override { - return buffer.size(); + return _buffer.size(); } uint32_t dts() const override { - return timeStamp; + return _dts; } uint32_t pts() const override { - return ptsStamp ? ptsStamp : timeStamp; + return _pts ? _pts : _dts; } uint32_t prefixSize() const override { - return iPrefixSize; + return _prefix_size; } TrackType getTrackType() const override { @@ -102,11 +102,11 @@ public: } bool keyFrame() const override { - return isKeyFrame(H265_TYPE(buffer[iPrefixSize])); + return isKeyFrame(H265_TYPE(_buffer[_prefix_size])); } bool configFrame() const override{ - switch(H265_TYPE(buffer[iPrefixSize])){ + switch(H265_TYPE(_buffer[_prefix_size])){ case H265Frame::NAL_VPS: case H265Frame::NAL_SPS: case H265Frame::NAL_PPS: @@ -131,10 +131,10 @@ public: } public: - uint32_t timeStamp; - uint32_t ptsStamp = 0; - string buffer; - uint32_t iPrefixSize = 4; + uint32_t _dts = 0; + uint32_t _pts = 0; + uint32_t _prefix_size = 4; + string _buffer; }; @@ -356,27 +356,27 @@ private: } if(!_vps.empty()){ auto vpsFrame = std::make_shared(); - vpsFrame->iPrefixSize = 4; - vpsFrame->buffer.assign("\x0\x0\x0\x1", 4); - vpsFrame->buffer.append(_vps); - vpsFrame->timeStamp = frame->stamp(); + vpsFrame->_prefix_size = 4; + vpsFrame->_buffer.assign("\x0\x0\x0\x1", 4); + vpsFrame->_buffer.append(_vps); + vpsFrame->_dts = frame->dts(); VideoTrack::inputFrame(vpsFrame); } if (!_sps.empty()) { auto spsFrame = std::make_shared(); - spsFrame->iPrefixSize = 4; - spsFrame->buffer.assign("\x0\x0\x0\x1", 4); - spsFrame->buffer.append(_sps); - spsFrame->timeStamp = frame->stamp(); + spsFrame->_prefix_size = 4; + spsFrame->_buffer.assign("\x0\x0\x0\x1", 4); + spsFrame->_buffer.append(_sps); + spsFrame->_dts = frame->dts(); VideoTrack::inputFrame(spsFrame); } if (!_pps.empty()) { auto ppsFrame = std::make_shared(); - ppsFrame->iPrefixSize = 4; - ppsFrame->buffer.assign("\x0\x0\x0\x1", 4); - ppsFrame->buffer.append(_pps); - ppsFrame->timeStamp = frame->stamp(); + ppsFrame->_prefix_size = 4; + ppsFrame->_buffer.assign("\x0\x0\x0\x1", 4); + ppsFrame->_buffer.append(_pps); + ppsFrame->_dts = frame->dts(); VideoTrack::inputFrame(ppsFrame); } } diff --git a/src/Extension/H265Rtp.cpp b/src/Extension/H265Rtp.cpp index 6d164dde..4b8df94c 100644 --- a/src/Extension/H265Rtp.cpp +++ b/src/Extension/H265Rtp.cpp @@ -70,8 +70,8 @@ H265RtpDecoder::H265RtpDecoder() { H265Frame::Ptr H265RtpDecoder::obtainFrame() { //从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象 auto frame = ResourcePoolHelper::obtainObj(); - frame->buffer.clear(); - frame->iPrefixSize = 4; + frame->_buffer.clear(); + frame->_prefix_size = 4; return frame; } @@ -99,11 +99,11 @@ bool H265RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { MakeFU(frame[2], fu); if (fu.S) { //该帧的第一个rtp包 - _h265frame->buffer.assign("\x0\x0\x0\x1", 4); - _h265frame->buffer.push_back(fu.type << 1); - _h265frame->buffer.push_back(0x01); - _h265frame->buffer.append((char *) frame + 3, length - 3); - _h265frame->timeStamp = rtppack->timeStamp; + _h265frame->_buffer.assign("\x0\x0\x0\x1", 4); + _h265frame->_buffer.push_back(fu.type << 1); + _h265frame->_buffer.push_back(0x01); + _h265frame->_buffer.append((char *) frame + 3, length - 3); + _h265frame->_pts = rtppack->timeStamp; //该函数return时,保存下当前sequence,以便下次对比seq是否连续 _lastSeq = rtppack->sequence; return (_h265frame->keyFrame()); //i frame @@ -111,22 +111,22 @@ bool H265RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { if (rtppack->sequence != _lastSeq + 1 && rtppack->sequence != 0) { //中间的或末尾的rtp包,其seq必须连续(如果回环了则判定为连续),否则说明rtp丢包,那么该帧不完整,必须得丢弃 - _h265frame->buffer.clear(); + _h265frame->_buffer.clear(); WarnL << "rtp sequence不连续: " << rtppack->sequence << " != " << _lastSeq << " + 1,该帧被废弃"; return false; } if (!fu.E) { //该帧的中间rtp包 - _h265frame->buffer.append((char *) frame + 3, length - 3); + _h265frame->_buffer.append((char *) frame + 3, length - 3); //该函数return时,保存下当前sequence,以便下次对比seq是否连续 _lastSeq = rtppack->sequence; return false; } //该帧最后一个rtp包 - _h265frame->buffer.append((char *) frame + 3, length - 3); - _h265frame->timeStamp = rtppack->timeStamp; + _h265frame->_buffer.append((char *) frame + 3, length - 3); + _h265frame->_pts = rtppack->timeStamp; auto key = _h265frame->keyFrame(); onGetH265(_h265frame); return key; @@ -134,9 +134,9 @@ bool H265RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { default: // 4.4.1. Single NAL Unit Packets (p24) //a full frame - _h265frame->buffer.assign("\x0\x0\x0\x1", 4); - _h265frame->buffer.append((char *)frame, length); - _h265frame->timeStamp = rtppack->timeStamp; + _h265frame->_buffer.assign("\x0\x0\x0\x1", 4); + _h265frame->_buffer.append((char *)frame, length); + _h265frame->_pts = rtppack->timeStamp; auto key = _h265frame->keyFrame(); onGetH265(_h265frame); return key; @@ -144,8 +144,12 @@ bool H265RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { } void H265RtpDecoder::onGetH265(const H265Frame::Ptr &frame) { - //写入环形缓存 - RtpCodec::inputFrame(frame); + //计算dts + auto flag = _dts_generator.getDts(frame->_pts,frame->_dts); + if(flag){ + //写入环形缓存 + RtpCodec::inputFrame(frame); + } _h265frame = obtainFrame(); } @@ -167,7 +171,7 @@ H265RtpEncoder::H265RtpEncoder(uint32_t ui32Ssrc, void H265RtpEncoder::inputFrame(const Frame::Ptr &frame) { GET_CONFIG(uint32_t,cycleMS,Rtp::kCycleMS); uint8_t *pcData = (uint8_t*)frame->data() + frame->prefixSize(); - auto uiStamp = frame->stamp(); + auto uiStamp = frame->pts(); auto iLen = frame->size() - frame->prefixSize(); unsigned char naluType = H265_TYPE(pcData[0]); //获取NALU的5bit 帧类型 uiStamp %= cycleMS; diff --git a/src/Extension/H265Rtp.h b/src/Extension/H265Rtp.h index 8b51f880..a2844092 100644 --- a/src/Extension/H265Rtp.h +++ b/src/Extension/H265Rtp.h @@ -30,6 +30,7 @@ #include "Rtsp/RtpCodec.h" #include "Util/ResourcePool.h" #include "Extension/H265.h" +#include "Common/Stamp.h" using namespace toolkit; @@ -67,6 +68,7 @@ private: H265Frame::Ptr obtainFrame(); private: H265Frame::Ptr _h265frame; + DtsGenerator _dts_generator; int _lastSeq = 0; }; diff --git a/src/Player/PlayerProxy.cpp b/src/Player/PlayerProxy.cpp index 250ea932..6b5fca6e 100644 --- a/src/Player/PlayerProxy.cpp +++ b/src/Player/PlayerProxy.cpp @@ -219,7 +219,7 @@ public: virtual ~MuteAudioMaker(){} void inputFrame(const Frame::Ptr &frame) override { if(frame->getTrackType() == TrackVideo){ - auto iAudioIndex = frame->stamp() / MUTE_ADTS_DATA_MS; + auto iAudioIndex = frame->dts() / MUTE_ADTS_DATA_MS; if(_iAudioIndex != iAudioIndex){ _iAudioIndex = iAudioIndex; auto aacFrame = std::make_shared((char *)MUTE_ADTS_DATA, MUTE_ADTS_DATA_LEN, _iAudioIndex * MUTE_ADTS_DATA_MS); From ec679006658002a62cb484b3d169a7b4d234eb76 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 14 Jan 2020 10:06:35 +0800 Subject: [PATCH 218/272] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=B8=8D=E5=90=ABB?= =?UTF-8?q?=E5=B8=A7=E6=97=B6=E7=9A=84dts=E7=94=9F=E6=88=90=E6=80=A7?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/Stamp.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Common/Stamp.cpp b/src/Common/Stamp.cpp index 3a0ed5bf..1c037d32 100644 --- a/src/Common/Stamp.cpp +++ b/src/Common/Stamp.cpp @@ -122,11 +122,16 @@ bool DtsGenerator::getDts(uint32_t pts, uint32_t &dts){ } bool DtsGenerator::getDts_l(uint32_t pts, uint32_t &dts){ + if(_sorter_max_size == 1){ + //没有B帧 + dts = pts; + return true; + } + if(pts > _last_max_pts){ if(!_sorter_max_size && _frames_since_last_max_pts && _count_sorter_max_size++ > 0){ _sorter_max_size = _frames_since_last_max_pts; _dts_pts_offset = (pts - _last_max_pts) / 2; - InfoL << _sorter_max_size << " " << _dts_pts_offset; } _frames_since_last_max_pts = 0; _last_max_pts = pts; From d8dbf434909335be49fb6228fa5390cd375227de Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 14 Jan 2020 10:25:14 +0800 Subject: [PATCH 219/272] =?UTF-8?q?=E4=BC=98=E5=8C=96dts=E7=94=9F=E6=88=90?= =?UTF-8?q?=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/Stamp.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Common/Stamp.cpp b/src/Common/Stamp.cpp index 1c037d32..a0017752 100644 --- a/src/Common/Stamp.cpp +++ b/src/Common/Stamp.cpp @@ -128,18 +128,19 @@ bool DtsGenerator::getDts_l(uint32_t pts, uint32_t &dts){ return true; } - if(pts > _last_max_pts){ - if(!_sorter_max_size && _frames_since_last_max_pts && _count_sorter_max_size++ > 0){ - _sorter_max_size = _frames_since_last_max_pts; - _dts_pts_offset = (pts - _last_max_pts) / 2; + if(!_sorter_max_size){ + if(pts > _last_max_pts){ + if(_frames_since_last_max_pts && _count_sorter_max_size++ > 0){ + _sorter_max_size = _frames_since_last_max_pts; + _dts_pts_offset = (pts - _last_max_pts) / 2; + } + _frames_since_last_max_pts = 0; + _last_max_pts = pts; } - _frames_since_last_max_pts = 0; - _last_max_pts = pts; + ++_frames_since_last_max_pts; } _pts_sorter.emplace(pts); - ++_frames_since_last_max_pts; - if(_sorter_max_size && _pts_sorter.size() > _sorter_max_size){ auto it = _pts_sorter.begin(); dts = *it + _dts_pts_offset; From 4a7d17334c218ee7f01739f5f1ebc60f1dd561c4 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 14 Jan 2020 10:29:27 +0800 Subject: [PATCH 220/272] =?UTF-8?q?rtsp=E4=B8=8D=E5=85=81=E8=AE=B8?= =?UTF-8?q?=E8=A6=86=E7=9B=96=E6=97=B6=E9=97=B4=E6=88=B3(pts)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/config.ini | 2 -- src/Common/config.cpp | 2 -- src/Common/config.h | 2 -- 3 files changed, 6 deletions(-) diff --git a/conf/config.ini b/conf/config.ini index 8010e4e8..c158d843 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -191,8 +191,6 @@ keepAliveSecond=15 port=554 #rtsps服务器监听地址 sslport=322 -#在接收rtsp推流时,是否重新生成时间戳(很多推流器的时间戳着实很烂) -modifyStamp=0 [shell] #调试telnet服务器接受最大bufffer大小 diff --git a/src/Common/config.cpp b/src/Common/config.cpp index 74e92e72..89ad9bbf 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -158,7 +158,6 @@ const string kAuthBasic = RTSP_FIELD"authBasic"; const string kHandshakeSecond = RTSP_FIELD"handshakeSecond"; const string kKeepAliveSecond = RTSP_FIELD"keepAliveSecond"; const string kDirectProxy = RTSP_FIELD"directProxy"; -const string kModifyStamp = RTSP_FIELD"modifyStamp"; onceToken token([](){ //默认Md5方式认证 @@ -166,7 +165,6 @@ onceToken token([](){ mINI::Instance()[kHandshakeSecond] = 15; mINI::Instance()[kKeepAliveSecond] = 15; mINI::Instance()[kDirectProxy] = 1; - mINI::Instance()[kModifyStamp] = false; },nullptr); } //namespace Rtsp diff --git a/src/Common/config.h b/src/Common/config.h index 7cadbf70..d8d51e35 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -232,8 +232,6 @@ extern const string kKeepAliveSecond; //假定您的拉流源地址不是264或265或AAC,那么你可以使用直接代理的方式来支持rtsp代理 //默认开启rtsp直接代理,rtmp由于没有这些问题,是强制开启直接代理的 extern const string kDirectProxy; -//rtsp推流是否修改时间戳 -extern const string kModifyStamp; } //namespace Rtsp ////////////RTMP服务器配置/////////// From 491ed6f83d5933ce35d405156e1e08595c00efb6 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 14 Jan 2020 10:34:05 +0800 Subject: [PATCH 221/272] =?UTF-8?q?rtsp=E4=B8=8D=E5=85=81=E8=AE=B8?= =?UTF-8?q?=E8=A6=86=E7=9B=96=E6=97=B6=E9=97=B4=E6=88=B3(pts)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtsp/RtspSession.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index b9a3c424..6da40b06 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -932,12 +932,6 @@ inline void RtspSession::send_NotAcceptable() { void RtspSession::onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx) { - GET_CONFIG(bool,modify_stamp,Rtsp::kModifyStamp); - if(modify_stamp){ - int64_t dts_out; - _stamp[trackidx].revise(rtppt->timeStamp, rtppt->timeStamp, dts_out, dts_out, true); - rtppt->timeStamp = dts_out; - } _pushSrc->onWrite(rtppt, false); } inline void RtspSession::onRcvPeerUdpData(int intervaled, const Buffer::Ptr &pBuf, const struct sockaddr& addr) { From 2d9537ec843cf39fa8fb514a41f3f4aac60e7cc1 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 14 Jan 2020 10:36:38 +0800 Subject: [PATCH 222/272] snap --- server/FFmpegSource.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/FFmpegSource.cpp b/server/FFmpegSource.cpp index d8a821c6..5ba1d618 100644 --- a/server/FFmpegSource.cpp +++ b/server/FFmpegSource.cpp @@ -34,6 +34,7 @@ namespace FFmpeg { #define FFmpeg_FIELD "ffmpeg." const string kBin = FFmpeg_FIELD"bin"; const string kCmd = FFmpeg_FIELD"cmd"; +const string kSnap = FFmpeg_FIELD"snap"; const string kLog = FFmpeg_FIELD"log"; onceToken token([]() { @@ -41,9 +42,12 @@ onceToken token([]() { string ffmpeg_bin = System::execute("where ffmpeg"); //windows下先关闭FFmpeg日志(目前不支持日志重定向) mINI::Instance()[kCmd] = "%s -re -i \"%s\" -loglevel quiet -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s "; + //利用ffmpeg进行抓拍 + mINI::Instance()[kSnap] = "%s -i \"%s\" -loglevel quiet -y -f mjpeg -t 0.001 -s 720*576 %s " #else string ffmpeg_bin = System::execute("which ffmpeg"); mINI::Instance()[kCmd] = "%s -re -i \"%s\" -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s "; + mINI::Instance()[kSnap] = "%s -i \"%s\" -y -f mjpeg -t 0.001 -s 720*576 %s " #endif //默认ffmpeg命令路径为环境变量中路径 mINI::Instance()[kBin] = ffmpeg_bin.empty() ? "ffmpeg" : ffmpeg_bin; @@ -64,6 +68,7 @@ FFmpegSource::~FFmpegSource() { void FFmpegSource::play(const string &src_url,const string &dst_url,int timeout_ms,const onPlay &cb) { GET_CONFIG(string,ffmpeg_bin,FFmpeg::kBin); GET_CONFIG(string,ffmpeg_cmd,FFmpeg::kCmd); + GET_CONFIG(string, ffmpeg_snap, FFmpeg::kSnap); GET_CONFIG(string,ffmpeg_log,FFmpeg::kLog); _src_url = src_url; From 2b511a05cc25db1407b3404013e1234766fbc9b1 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 14 Jan 2020 11:21:21 +0800 Subject: [PATCH 223/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/source/mk_common.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/source/mk_common.cpp b/api/source/mk_common.cpp index 19849621..15eff659 100755 --- a/api/source/mk_common.cpp +++ b/api/source/mk_common.cpp @@ -70,8 +70,10 @@ API_EXPORT void API_CALL mk_stop_all_server(){ CLEAR_ARR(rtsp_server); CLEAR_ARR(rtmp_server); CLEAR_ARR(http_server); +#ifdef ENABLE_RTPPROXY udpRtpServer = nullptr; tcpRtpServer = nullptr; +#endif stopAllTcpServer(); } From a573d7302badd6f354c429c2e6acc543e7d3e472 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 14 Jan 2020 18:11:10 +0800 Subject: [PATCH 224/272] =?UTF-8?q?=E6=8F=90=E9=AB=98rtsp=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=AB=AF=E5=85=BC=E5=AE=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtsp/Rtsp.cpp | 177 ++++++++++++++++++++++++++-------------------- src/Rtsp/Rtsp.h | 2 +- 2 files changed, 100 insertions(+), 79 deletions(-) diff --git a/src/Rtsp/Rtsp.cpp b/src/Rtsp/Rtsp.cpp index bc8dcce4..d6d95258 100644 --- a/src/Rtsp/Rtsp.cpp +++ b/src/Rtsp/Rtsp.cpp @@ -85,74 +85,84 @@ string SdpTrack::toString() const { } return _printer; } -void SdpParser::load(const string &sdp) { - _track_map.clear(); - string key; - SdpTrack::Ptr track = std::make_shared(); - auto lines = split(sdp,"\n"); - for (auto &line : lines){ - trim(line); - if(line.size() < 2 || line[1] != '='){ - continue; - } - char opt = line[0]; - string opt_val = line.substr(2); - switch (opt){ - case 'o': - track->_o = opt_val; - break; - case 's': - track->_s = opt_val; - break; - case 'i': - track->_i = opt_val; - break; - case 'c': - track->_c = opt_val; - break; - case 't': - track->_t = opt_val; - break; - case 'b': - track->_b = opt_val; - break; - case 'm':{ - _track_map[key] = track; - track = std::make_shared(); - key = FindField(opt_val.data(), nullptr," "); - track->_m = opt_val; - } - break; - case 'a':{ - string attr = FindField(opt_val.data(), nullptr,":"); - if(attr.empty()){ - track->_attr[opt_val] = ""; - }else{ - track->_attr[attr] = FindField(opt_val.data(),":", nullptr); - } - } - break; - default: - track->_other[opt] = opt_val; - break; - } +static TrackType toTrackType(const string &str) { + if (str == "") { + return TrackTitle; } - _track_map[key] = track; + if (str == "video") { + return TrackVideo; + } - for (auto &pr : _track_map) { - auto &track = *pr.second; - if (pr.first == "") { - track._type = TrackTitle; - } else if (pr.first == "video") { - track._type = TrackVideo; - } else if (pr.first == "audio") { - track._type = TrackAudio; - } else { - track._type = TrackInvalid; + if (str == "audio") { + return TrackAudio; + } + + return TrackInvalid; +} + +void SdpParser::load(const string &sdp) { + { + _track_vec.clear(); + string key; + SdpTrack::Ptr track = std::make_shared(); + + auto lines = split(sdp, "\n"); + for (auto &line : lines) { + trim(line); + if (line.size() < 2 || line[1] != '=') { + continue; + } + char opt = line[0]; + string opt_val = line.substr(2); + switch (opt) { + case 'o': + track->_o = opt_val; + break; + case 's': + track->_s = opt_val; + break; + case 'i': + track->_i = opt_val; + break; + case 'c': + track->_c = opt_val; + break; + case 't': + track->_t = opt_val; + break; + case 'b': + track->_b = opt_val; + break; + case 'm': { + track->_type = toTrackType(key); + _track_vec.emplace_back(track); + track = std::make_shared(); + key = FindField(opt_val.data(), nullptr, " "); + track->_m = opt_val; + } + break; + case 'a': { + string attr = FindField(opt_val.data(), nullptr, ":"); + if (attr.empty()) { + track->_attr[opt_val] = ""; + } else { + track->_attr[attr] = FindField(opt_val.data(), ":", nullptr); + } + } + break; + default: + track->_other[opt] = opt_val; + break; + } } + track->_type = toTrackType(key); + _track_vec.emplace_back(track); + } + for (auto &track_ptr : _track_vec) { + auto &track = *track_ptr; auto it = track._attr.find("range"); if (it != track._attr.end()) { char name[16] = {0}, start[16] = {0}, end[16] = {0}; @@ -198,9 +208,9 @@ bool SdpParser::available() const { } SdpTrack::Ptr SdpParser::getTrack(TrackType type) const { - for (auto &pr : _track_map){ - if(pr.second->_type == type){ - return pr.second; + for (auto &track : _track_vec){ + if(track->_type == type){ + return track; } } return nullptr; @@ -208,31 +218,42 @@ SdpTrack::Ptr SdpParser::getTrack(TrackType type) const { vector SdpParser::getAvailableTrack() const { vector ret; - auto video = getTrack(TrackVideo); - if(video){ - ret.emplace_back(video); + bool audio_added = false; + bool video_added = false; + for (auto &track : _track_vec){ + if(track->_type == TrackAudio ){ + if(!audio_added){ + ret.emplace_back(track); + audio_added = true; + } + continue; + } + + if(track->_type == TrackVideo ){ + if(!video_added){ + ret.emplace_back(track); + video_added = true; + } + continue; + } } - auto audio = getTrack(TrackAudio); - if(audio){ - ret.emplace_back(audio); - } - return ret; + return std::move(ret); } string SdpParser::toString() const { string title,audio,video; - for(auto &pr : _track_map){ - switch (pr.second->_type){ + for(auto &track : _track_vec){ + switch (track->_type){ case TrackTitle:{ - title = pr.second->toString(); + title = track->toString(); } break; case TrackVideo:{ - video = pr.second->toString(); + video = track->toString(); } break; case TrackAudio:{ - audio = pr.second->toString(); + audio = track->toString(); } break; default: diff --git a/src/Rtsp/Rtsp.h b/src/Rtsp/Rtsp.h index c8eb5554..26e2a979 100644 --- a/src/Rtsp/Rtsp.h +++ b/src/Rtsp/Rtsp.h @@ -122,7 +122,7 @@ public: vector getAvailableTrack() const; string toString() const ; private: - map _track_map; + vector _track_vec; }; /** From 5ee913a3bf45195b121684f5c015a720e26c6984 Mon Sep 17 00:00:00 2001 From: Gemfield Date: Tue, 14 Jan 2020 19:36:36 +0800 Subject: [PATCH 225/272] Split Dockerfile to runtime and devel version, and also reduce the image size for runtime version --- build_docker_images.sh | 3 + .../Dockerfile.devel} | 7 ++- docker/ubuntu16.04/Dockerfile.runtime | 62 +++++++++++++++++++ docker/ubuntu18.04/Dockerfile.devel | 44 +++++++++++++ docker/ubuntu18.04/Dockerfile.runtime | 62 +++++++++++++++++++ 5 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 build_docker_images.sh rename docker/{Dockerfile => ubuntu16.04/Dockerfile.devel} (82%) create mode 100644 docker/ubuntu16.04/Dockerfile.runtime create mode 100644 docker/ubuntu18.04/Dockerfile.devel create mode 100644 docker/ubuntu18.04/Dockerfile.runtime diff --git a/build_docker_images.sh b/build_docker_images.sh new file mode 100644 index 00000000..a642584f --- /dev/null +++ b/build_docker_images.sh @@ -0,0 +1,3 @@ +#!/bin/bash +docker build -t gemfield/zlmediakit:20.01-runtime-ubuntu18.04 -f docker/ubuntu18.04/Dockerfile.runtime . +#docker build -t gemfield/zlmediakit:20.01-devel-ubuntu18.04 -f docker/ubuntu18.04/Dockerfile.devel . diff --git a/docker/Dockerfile b/docker/ubuntu16.04/Dockerfile.devel similarity index 82% rename from docker/Dockerfile rename to docker/ubuntu16.04/Dockerfile.devel index d939ab14..650bd206 100644 --- a/docker/Dockerfile +++ b/docker/ubuntu16.04/Dockerfile.devel @@ -9,7 +9,9 @@ EXPOSE 443/tcp EXPOSE 10000/udp EXPOSE 10000/tcp -RUN apt-get update && apt-get install -y --no-install-recommends \ +RUN apt-get update && \ + DEBIAN_FRONTEND="noninteractive" \ + apt-get install -y --no-install-recommends \ build-essential \ cmake \ git \ @@ -22,6 +24,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libx264-dev \ libfaac-dev \ libmp4v2-dev && \ + apt autoremove -y && \ + apt clean -y && \ rm -rf /var/lib/apt/lists/* RUN mkdir -p /opt/media @@ -36,5 +40,4 @@ RUN cmake -DCMAKE_BUILD_TYPE=Release .. && \ make -j4 ENV PATH /opt/media/ZLMediaKit/release/linux/Release/:$PATH - CMD MediaServer diff --git a/docker/ubuntu16.04/Dockerfile.runtime b/docker/ubuntu16.04/Dockerfile.runtime new file mode 100644 index 00000000..4bd6382f --- /dev/null +++ b/docker/ubuntu16.04/Dockerfile.runtime @@ -0,0 +1,62 @@ +FROM ubuntu:16.04 AS build +#shell,rtmp,rtsp,rtsps,http,https,rtp +EXPOSE 9000/tcp +EXPOSE 1935/tcp +EXPOSE 554/tcp +EXPOSE 322/tcp +EXPOSE 80/tcp +EXPOSE 443/tcp +EXPOSE 10000/udp +EXPOSE 10000/tcp + +RUN apt-get update && \ + DEBIAN_FRONTEND="noninteractive" \ + apt-get install -y --no-install-recommends \ + build-essential \ + cmake \ + git \ + curl \ + vim \ + ca-certificates \ + tzdata \ + libssl-dev \ + libmysqlclient-dev \ + libx264-dev \ + libfaac-dev \ + libmp4v2-dev && \ + apt autoremove -y && \ + apt clean -y && \ + rm -rf /var/lib/apt/lists/* + +RUN mkdir -p /opt/media + +WORKDIR /opt/media +RUN git clone --depth=1 https://github.com/xiongziliang/ZLMediaKit && \ + cd ZLMediaKit && git submodule update --init --recursive && \ + mkdir -p build release/linux/Release/ + +WORKDIR /opt/media/ZLMediaKit/build +RUN cmake -DCMAKE_BUILD_TYPE=Release .. && \ + make -j4 + +FROM ubuntu:16.04 +LABEL maintainer "Gemfield " + +RUN apt-get update && \ + DEBIAN_FRONTEND="noninteractive" \ + apt-get install -y --no-install-recommends \ + vim \ + ca-certificates \ + tzdata \ + libssl-dev \ + libx264-dev \ + libfaac-dev \ + libmp4v2-dev && \ + apt autoremove -y && \ + apt clean -y && \ + rm -rf /var/lib/apt/lists/* + +WORKDIR /opt/media/bin/ +COPY --from=build /opt/media/ZLMediaKit/release/linux/Release/MediaServer /opt/media/bin/MediaServer +ENV PATH /opt/media/bin:$PATH +CMD MediaServer \ No newline at end of file diff --git a/docker/ubuntu18.04/Dockerfile.devel b/docker/ubuntu18.04/Dockerfile.devel new file mode 100644 index 00000000..335c9a3f --- /dev/null +++ b/docker/ubuntu18.04/Dockerfile.devel @@ -0,0 +1,44 @@ +FROM ubuntu:18.04 +LABEL maintainer "Gemfield " +#shell,rtmp,rtsp,rtsps,http,https,rtp +EXPOSE 9000/tcp +EXPOSE 1935/tcp +EXPOSE 554/tcp +EXPOSE 322/tcp +EXPOSE 80/tcp +EXPOSE 443/tcp +EXPOSE 10000/udp +EXPOSE 10000/tcp + +RUN apt-get update && \ + DEBIAN_FRONTEND="noninteractive" \ + apt-get install -y --no-install-recommends \ + build-essential \ + cmake \ + git \ + curl \ + vim \ + ca-certificates \ + tzdata \ + libssl-dev \ + libmysqlclient-dev \ + libx264-dev \ + libfaac-dev \ + libmp4v2-dev && \ + apt autoremove -y && \ + apt clean -y && \ + rm -rf /var/lib/apt/lists/* + +RUN mkdir -p /opt/media + +WORKDIR /opt/media +RUN git clone --depth=1 https://github.com/xiongziliang/ZLMediaKit && \ + cd ZLMediaKit && git submodule update --init --recursive && \ + mkdir -p build release/linux/Release/ + +WORKDIR /opt/media/ZLMediaKit/build +RUN cmake -DCMAKE_BUILD_TYPE=Release .. && \ + make -j4 + +ENV PATH /opt/media/ZLMediaKit/release/linux/Release:$PATH +CMD MediaServer \ No newline at end of file diff --git a/docker/ubuntu18.04/Dockerfile.runtime b/docker/ubuntu18.04/Dockerfile.runtime new file mode 100644 index 00000000..f10c2658 --- /dev/null +++ b/docker/ubuntu18.04/Dockerfile.runtime @@ -0,0 +1,62 @@ +FROM ubuntu:18.04 AS build +#shell,rtmp,rtsp,rtsps,http,https,rtp +EXPOSE 9000/tcp +EXPOSE 1935/tcp +EXPOSE 554/tcp +EXPOSE 322/tcp +EXPOSE 80/tcp +EXPOSE 443/tcp +EXPOSE 10000/udp +EXPOSE 10000/tcp + +RUN apt-get update && \ + DEBIAN_FRONTEND="noninteractive" \ + apt-get install -y --no-install-recommends \ + build-essential \ + cmake \ + git \ + curl \ + vim \ + ca-certificates \ + tzdata \ + libssl-dev \ + libmysqlclient-dev \ + libx264-dev \ + libfaac-dev \ + libmp4v2-dev && \ + apt autoremove -y && \ + apt clean -y && \ + rm -rf /var/lib/apt/lists/* + +RUN mkdir -p /opt/media + +WORKDIR /opt/media +RUN git clone --depth=1 https://github.com/xiongziliang/ZLMediaKit && \ + cd ZLMediaKit && git submodule update --init --recursive && \ + mkdir -p build release/linux/Release/ + +WORKDIR /opt/media/ZLMediaKit/build +RUN cmake -DCMAKE_BUILD_TYPE=Release .. && \ + make -j4 + +FROM ubuntu:18.04 +LABEL maintainer "Gemfield " + +RUN apt-get update && \ + DEBIAN_FRONTEND="noninteractive" \ + apt-get install -y --no-install-recommends \ + vim \ + ca-certificates \ + tzdata \ + libssl-dev \ + libx264-dev \ + libfaac-dev \ + libmp4v2-dev && \ + apt autoremove -y && \ + apt clean -y && \ + rm -rf /var/lib/apt/lists/* + +WORKDIR /opt/media/bin/ +COPY --from=build /opt/media/ZLMediaKit/release/linux/Release/MediaServer /opt/media/bin/MediaServer +ENV PATH /opt/media/bin:$PATH +CMD MediaServer \ No newline at end of file From 0a4b59031f99f8f998a4d04a9fb57753e12cfaac Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 14 Jan 2020 19:41:55 +0800 Subject: [PATCH 226/272] =?UTF-8?q?=E5=8F=8A=E6=97=B6=E6=8E=A8=E6=8B=89?= =?UTF-8?q?=E6=B5=81=E4=B9=9F=E8=83=BD=E7=A1=AE=E4=BF=9D=E7=AC=AC=E4=B8=80?= =?UTF-8?q?=E5=B8=A7=E4=B8=BA=E5=85=B3=E9=94=AE=E5=B8=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- src/Extension/H264Rtp.cpp | 12 +++++++++--- src/Extension/H265Rtp.cpp | 9 +++++++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 72013f51..a6b41db9 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 72013f5128d8c04bad8b973370da463c311081c0 +Subproject commit a6b41db987aa2633762fd06e809e0fdb7d60fef0 diff --git a/src/Extension/H264Rtp.cpp b/src/Extension/H264Rtp.cpp index f90f3c56..5df37767 100644 --- a/src/Extension/H264Rtp.cpp +++ b/src/Extension/H264Rtp.cpp @@ -189,9 +189,8 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { //该帧最后一个rtp包 FU-A end _h264frame->_buffer.append((char *)frame + 2, length - 2); _h264frame->_pts = rtppack->timeStamp; - auto key = _h264frame->keyFrame(); onGetH264(_h264frame); - return key; + return false; } default:{ @@ -209,8 +208,15 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { } void H264RtpDecoder::onGetH264(const H264Frame::Ptr &frame) { - //根据pts计算dts auto flag = _dts_generator.getDts(frame->_pts,frame->_dts); + if(!flag){ + if(frame->configFrame() || frame->keyFrame()){ + flag = true; + frame->_dts = frame->_pts; + } + } + + //根据pts计算dts if(flag){ //写入环形缓存 RtpCodec::inputFrame(frame); diff --git a/src/Extension/H265Rtp.cpp b/src/Extension/H265Rtp.cpp index 4b8df94c..8f41ec76 100644 --- a/src/Extension/H265Rtp.cpp +++ b/src/Extension/H265Rtp.cpp @@ -127,9 +127,8 @@ bool H265RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { //该帧最后一个rtp包 _h265frame->_buffer.append((char *) frame + 3, length - 3); _h265frame->_pts = rtppack->timeStamp; - auto key = _h265frame->keyFrame(); onGetH265(_h265frame); - return key; + return false; } default: // 4.4.1. Single NAL Unit Packets (p24) @@ -146,6 +145,12 @@ bool H265RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { void H265RtpDecoder::onGetH265(const H265Frame::Ptr &frame) { //计算dts auto flag = _dts_generator.getDts(frame->_pts,frame->_dts); + if(!flag){ + if(frame->configFrame() || frame->keyFrame()){ + flag = true; + frame->_dts = frame->_pts; + } + } if(flag){ //写入环形缓存 RtpCodec::inputFrame(frame); From 444137e5b71a44b890c99e03422440825ec85ef6 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 15 Jan 2020 11:07:55 +0800 Subject: [PATCH 227/272] =?UTF-8?q?=E9=87=8D=E5=86=99GOP=E7=BC=93=E5=AD=98?= =?UTF-8?q?=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- src/Rtmp/RtmpMediaSource.h | 6 +++++- src/Rtsp/RtspMediaSource.h | 5 ++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index a6b41db9..3b516504 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit a6b41db987aa2633762fd06e809e0fdb7d60fef0 +Subproject commit 3b5165043d4b40061b7dfcd9800691ab45e167c7 diff --git a/src/Rtmp/RtmpMediaSource.h b/src/Rtmp/RtmpMediaSource.h index 928c9066..e6f9178f 100644 --- a/src/Rtmp/RtmpMediaSource.h +++ b/src/Rtmp/RtmpMediaSource.h @@ -142,7 +142,11 @@ public: } strongSelf->onReaderChanged(size); }; - _ring = std::make_shared(_ring_size, std::move(lam)); + + //rtmp包缓存最大允许512个,如果是纯视频(25fps)大概为20秒数据 + //但是这个是GOP缓存的上限值,真实的GOP缓存大小等于两个I帧之间的包数的两倍 + //而且每次遇到I帧,则会清空GOP缓存,所以真实的GOP缓存远小于此值 + _ring = std::make_shared(_ring_size,512,std::move(lam)); onReaderChanged(0); if(_metadata){ diff --git a/src/Rtsp/RtspMediaSource.h b/src/Rtsp/RtspMediaSource.h index 2fc0cc99..21263b9d 100644 --- a/src/Rtsp/RtspMediaSource.h +++ b/src/Rtsp/RtspMediaSource.h @@ -179,7 +179,10 @@ public: } strongSelf->onReaderChanged(size); }; - _ring = std::make_shared(_ring_size, std::move(lam)); + //rtp包缓存最大允许2048个,大概最多3MB数据 + //但是这个是GOP缓存的上限值,真实的GOP缓存大小等于两个I帧之间的包数的两倍 + //而且每次遇到I帧,则会清空GOP缓存,所以真实的GOP缓存远小于此值 + _ring = std::make_shared(_ring_size,2048, std::move(lam)); onReaderChanged(0); if (!_sdp.empty()) { regist(); From 2610ba7c8bbe86532035ff625e1daef69d4e5301 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 15 Jan 2020 11:46:15 +0800 Subject: [PATCH 228/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8Drtmp=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=E6=97=B6=E8=BF=98=E6=9C=AA=E7=94=9F=E6=88=90=E5=AE=8C?= =?UTF-8?q?=E6=95=B4config=E5=8C=85=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Extension/AACRtmp.cpp | 21 ++++++++++----- src/Extension/AACRtmp.h | 4 +++ src/Extension/H264Rtmp.cpp | 46 ++++++++++++++++----------------- src/Extension/H264Rtmp.h | 5 ++++ src/Rtmp/RtmpCodec.h | 1 + src/Rtmp/RtmpMediaSourceMuxer.h | 1 + src/Rtmp/RtmpMuxer.cpp | 8 ++++++ src/Rtmp/RtmpMuxer.h | 5 ++++ 8 files changed, 61 insertions(+), 30 deletions(-) diff --git a/src/Extension/AACRtmp.cpp b/src/Extension/AACRtmp.cpp index c3efddb1..6a657aab 100644 --- a/src/Extension/AACRtmp.cpp +++ b/src/Extension/AACRtmp.cpp @@ -76,17 +76,24 @@ AACRtmpEncoder::AACRtmpEncoder(const Track::Ptr &track) { _track = dynamic_pointer_cast(track); } +void AACRtmpEncoder::makeConfigPacket() { + if (_track && _track->ready()) { + //从track中和获取aac配置信息 + _aac_cfg = _track->getAacCfg(); + } + + if (!_aac_cfg.empty()) { + makeAudioConfigPkt(); + } +} + void AACRtmpEncoder::inputFrame(const Frame::Ptr &frame) { - if(_aac_cfg.empty()){ - if(frame->prefixSize() >= 7){ + if (_aac_cfg.empty()) { + if (frame->prefixSize() >= 7) { //包含adts头,从adts头获取aac配置信息 _aac_cfg = makeAdtsConfig(reinterpret_cast(frame->data())); - makeAudioConfigPkt(); - } else if(_track && _track->ready()){ - //从track中和获取aac配置信息 - _aac_cfg = _track->getAacCfg(); - makeAudioConfigPkt(); } + makeConfigPacket(); } if(!_aac_cfg.empty()){ diff --git a/src/Extension/AACRtmp.h b/src/Extension/AACRtmp.h index 661a37a1..8181ebfd 100644 --- a/src/Extension/AACRtmp.h +++ b/src/Extension/AACRtmp.h @@ -88,6 +88,10 @@ public: */ void inputFrame(const Frame::Ptr &frame) override; + /** + * 生成config包 + */ + void makeConfigPacket() override; private: void makeAudioConfigPkt(); private: diff --git a/src/Extension/H264Rtmp.cpp b/src/Extension/H264Rtmp.cpp index c995317f..2aa4a7cb 100644 --- a/src/Extension/H264Rtmp.cpp +++ b/src/Extension/H264Rtmp.cpp @@ -99,7 +99,20 @@ inline void H264RtmpDecoder::onGetH264(const char* pcData, int iLen, uint32_t dt H264RtmpEncoder::H264RtmpEncoder(const Track::Ptr &track) { _track = dynamic_pointer_cast(track); +} +void H264RtmpEncoder::makeConfigPacket(){ + if (_track && _track->ready()) { + //尝试从track中获取sps pps信息 + _sps = _track->getSps(); + _pps = _track->getPps(); + } + + if (!_sps.empty() && !_pps.empty()) { + //获取到sps/pps + makeVideoConfigPkt(); + _gotSpsPps = true; + } } void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) { @@ -107,37 +120,24 @@ void H264RtmpEncoder::inputFrame(const Frame::Ptr &frame) { auto iLen = frame->size() - frame->prefixSize(); auto type = H264_TYPE(((uint8_t*)pcData)[0]); - if(!_gotSpsPps){ + if (!_gotSpsPps) { //尝试从frame中获取sps pps - switch (type){ - case H264Frame::NAL_SPS:{ + switch (type) { + case H264Frame::NAL_SPS: { //sps - if(_sps.empty()){ - _sps = string(pcData,iLen); - } - } + _sps = string(pcData, iLen); + makeConfigPacket(); break; - case H264Frame::NAL_PPS:{ + } + case H264Frame::NAL_PPS: { //pps - if(_pps.empty()){ - _pps = string(pcData,iLen); - } - } + _pps = string(pcData, iLen); + makeConfigPacket(); break; + } default: break; } - - if(_track && _track->ready()){ - //尝试从track中获取sps pps信息 - _sps = _track->getSps(); - _pps = _track->getPps(); - } - - if(!_sps.empty() && !_pps.empty()){ - _gotSpsPps = true; - makeVideoConfigPkt(); - } } if(type == H264Frame::NAL_SEI){ diff --git a/src/Extension/H264Rtmp.h b/src/Extension/H264Rtmp.h index 68802d68..a1cae242 100644 --- a/src/Extension/H264Rtmp.h +++ b/src/Extension/H264Rtmp.h @@ -90,6 +90,11 @@ public: * @param frame 帧数据 */ void inputFrame(const Frame::Ptr &frame) override; + + /** + * 生成config包 + */ + void makeConfigPacket() override; private: void makeVideoConfigPkt(); private: diff --git a/src/Rtmp/RtmpCodec.h b/src/Rtmp/RtmpCodec.h index ccdb967a..25f2fc5a 100644 --- a/src/Rtmp/RtmpCodec.h +++ b/src/Rtmp/RtmpCodec.h @@ -80,6 +80,7 @@ public: typedef std::shared_ptr Ptr; RtmpCodec(){} virtual ~RtmpCodec(){} + virtual void makeConfigPacket() {}; }; diff --git a/src/Rtmp/RtmpMediaSourceMuxer.h b/src/Rtmp/RtmpMediaSourceMuxer.h index 2cecd127..dce7e70e 100644 --- a/src/Rtmp/RtmpMediaSourceMuxer.h +++ b/src/Rtmp/RtmpMediaSourceMuxer.h @@ -58,6 +58,7 @@ public: } void onAllTrackReady(){ + makeConfigPacket(); _mediaSouce->setMetaData(getMetadata()); } diff --git a/src/Rtmp/RtmpMuxer.cpp b/src/Rtmp/RtmpMuxer.cpp index d81a74fa..89d345ca 100644 --- a/src/Rtmp/RtmpMuxer.cpp +++ b/src/Rtmp/RtmpMuxer.cpp @@ -78,6 +78,14 @@ void RtmpMuxer::inputFrame(const Frame::Ptr &frame) { } } +void RtmpMuxer::makeConfigPacket(){ + for(auto &encoder : _encoder){ + if(encoder){ + encoder->makeConfigPacket(); + } + } +} + const AMFValue &RtmpMuxer::getMetadata() const { return _metadata; } diff --git a/src/Rtmp/RtmpMuxer.h b/src/Rtmp/RtmpMuxer.h index c1a007fa..cc09178e 100644 --- a/src/Rtmp/RtmpMuxer.h +++ b/src/Rtmp/RtmpMuxer.h @@ -71,6 +71,11 @@ public: * 重置所有track */ void resetTracks() override ; + + /** + * 生成config包 + */ + void makeConfigPacket(); private: RtmpRing::RingType::Ptr _rtmpRing; AMFValue _metadata; From 34e3e9f7202d897a25e54901f3e43fb5deefd945 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 15 Jan 2020 11:55:16 +0800 Subject: [PATCH 229/272] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8F=8A=E6=97=B6?= =?UTF-8?q?=E6=8E=A8=E6=8B=89=E6=B5=81=E7=A7=92=E5=BC=80=E6=88=90=E5=8A=9F?= =?UTF-8?q?=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 3b516504..0654b00b 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 3b5165043d4b40061b7dfcd9800691ab45e167c7 +Subproject commit 0654b00bc50225a27b1da18f8d38d4a681ad39e9 From caa870c37e1613a4cd5a9a7be55116fe288bc635 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 15 Jan 2020 15:11:42 +0800 Subject: [PATCH 230/272] =?UTF-8?q?websocket=E6=9C=8D=E5=8A=A1=E5=99=A8?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=A0=B9=E6=8D=AEurl=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E4=B8=8D=E5=90=8C=E7=9A=84=E8=BF=90=E8=A1=8C=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/WebSocketSession.h | 137 +++++++++++++++++++++++------------- tests/test_wsServer.cpp | 55 ++++++++++++++- 2 files changed, 139 insertions(+), 53 deletions(-) diff --git a/src/Http/WebSocketSession.h b/src/Http/WebSocketSession.h index 682a2559..af302b8a 100644 --- a/src/Http/WebSocketSession.h +++ b/src/Http/WebSocketSession.h @@ -30,16 +30,80 @@ #include "HttpSession.h" #include "Network/TcpServer.h" +/** + * 数据发送拦截器 + */ +class SendInterceptor{ +public: + typedef function onBeforeSendCB; + SendInterceptor() = default; + virtual ~SendInterceptor() = default; + virtual void setOnBeforeSendCB(const onBeforeSendCB &cb) = 0; +}; + +/** + * 该类实现了TcpSession派生类发送数据的截取 + * 目的是发送业务数据前进行websocket协议的打包 + */ +template +class TcpSessionTypeImp : public TcpSessionType, public SendInterceptor{ +public: + typedef std::shared_ptr Ptr; + + TcpSessionTypeImp(const Parser &header, const HttpSession &parent, const Socket::Ptr &pSock) : + _identifier(parent.getIdentifier()), TcpSessionType(pSock) {} + + ~TcpSessionTypeImp() {} + + /** + * 设置发送数据截取回调函数 + * @param cb 截取回调函数 + */ + void setOnBeforeSendCB(const onBeforeSendCB &cb) override { + _beforeSendCB = cb; + } + +protected: + /** + * 重载send函数截取数据 + * @param buf 需要截取的数据 + * @return 数据字节数 + */ + int send(const Buffer::Ptr &buf) override { + if (_beforeSendCB) { + return _beforeSendCB(buf); + } + return TcpSessionType::send(buf); + } + + string getIdentifier() const override { + return _identifier; + } + +private: + onBeforeSendCB _beforeSendCB; + string _identifier; +}; + +template +class TcpSessionCreator { +public: + //返回的TcpSession必须派生于SendInterceptor,可以返回null + TcpSession::Ptr operator()(const Parser &header, const HttpSession &parent, const Socket::Ptr &pSock){ + return std::make_shared >(header,parent,pSock); + } +}; + + /** * 通过该模板类可以透明化WebSocket协议, * 用户只要实现WebSock协议下的具体业务协议,譬如基于WebSocket协议的Rtmp协议等 -* @tparam SessionType 业务协议的TcpSession类 */ -template -class WebSocketSession : public HttpSessionType { +template +class WebSocketSessionBase : public HttpSessionType { public: - WebSocketSession(const Socket::Ptr &pSock) : HttpSessionType(pSock){} - virtual ~WebSocketSession(){} + WebSocketSessionBase(const Socket::Ptr &pSock) : HttpSessionType(pSock){} + virtual ~WebSocketSessionBase(){} //收到eof或其他导致脱离TcpServer事件的回调 void onError(const SockException &err) override{ @@ -69,23 +133,27 @@ protected: */ bool onWebSocketConnect(const Parser &header) override{ //创建websocket session类 - _session = std::make_shared(HttpSessionType::getIdentifier(),HttpSessionType::_sock); + _session = _creator(header, *this,HttpSessionType::_sock); + if(!_session){ + //此url不允许创建websocket连接 + return false; + } auto strongServer = _weakServer.lock(); if(strongServer){ _session->attachServer(*strongServer); } //此处截取数据并进行websocket协议打包 - weak_ptr weakSelf = dynamic_pointer_cast(HttpSessionType::shared_from_this()); - _session->setOnBeforeSendCB([weakSelf](const Buffer::Ptr &buf){ + weak_ptr weakSelf = dynamic_pointer_cast(HttpSessionType::shared_from_this()); + dynamic_pointer_cast(_session)->setOnBeforeSendCB([weakSelf](const Buffer::Ptr &buf) { auto strongSelf = weakSelf.lock(); - if(strongSelf){ + if (strongSelf) { WebSocketHeader header; header._fin = true; header._reserved = 0; header._opcode = DataType; header._mask_flag = false; - strongSelf->WebSocketSplitter::encode(header,buf); + strongSelf->WebSocketSplitter::encode(header, buf); } return buf->size(); }); @@ -155,50 +223,19 @@ protected: void onWebSocketEncodeData(const Buffer::Ptr &buffer) override{ SocketHelper::send(buffer); } -private: - typedef function onBeforeSendCB; - /** - * 该类实现了TcpSession派生类发送数据的截取 - * 目的是发送业务数据前进行websocket协议的打包 - */ - class SessionImp : public SessionType{ - public: - SessionImp(const string &identifier,const Socket::Ptr &pSock) : - _identifier(identifier),SessionType(pSock){} - - ~SessionImp(){} - - /** - * 设置发送数据截取回调函数 - * @param cb 截取回调函数 - */ - void setOnBeforeSendCB(const onBeforeSendCB &cb){ - _beforeSendCB = cb; - } - protected: - /** - * 重载send函数截取数据 - * @param buf 需要截取的数据 - * @return 数据字节数 - */ - int send(const Buffer::Ptr &buf) override { - if(_beforeSendCB){ - return _beforeSendCB(buf); - } - return SessionType::send(buf); - } - string getIdentifier() const override{ - return _identifier; - } - private: - onBeforeSendCB _beforeSendCB; - string _identifier; - }; private: string _remian_data; weak_ptr _weakServer; - std::shared_ptr _session; + TcpSession::Ptr _session; + Creator _creator; }; +template +class WebSocketSession : public WebSocketSessionBase,HttpSessionType,DataType>{ +public: + WebSocketSession(const Socket::Ptr &pSock) : WebSocketSessionBase,HttpSessionType,DataType>(pSock){} + virtual ~WebSocketSession(){} +}; + #endif //ZLMEDIAKIT_WEBSOCKETSESSION_H diff --git a/tests/test_wsServer.cpp b/tests/test_wsServer.cpp index 12891889..1821467b 100644 --- a/tests/test_wsServer.cpp +++ b/tests/test_wsServer.cpp @@ -51,6 +51,7 @@ public: } void onRecv(const Buffer::Ptr &buffer) override { //回显数据 + send("from EchoSession:"); send(buffer); } void onError(const SockException &err) override{ @@ -62,6 +63,48 @@ public: } }; + +class EchoSessionWithUrl : public TcpSession { +public: + EchoSessionWithUrl(const Socket::Ptr &pSock) : TcpSession(pSock){ + DebugL; + } + virtual ~EchoSessionWithUrl(){ + DebugL; + } + + void attachServer(const TcpServer &server) override{ + DebugL << getIdentifier() << " " << TcpSession::getIdentifier(); + } + void onRecv(const Buffer::Ptr &buffer) override { + //回显数据 + send("from EchoSessionWithUrl:"); + send(buffer); + } + void onError(const SockException &err) override{ + WarnL << err.what(); + } + //每隔一段时间触发,用来做超时管理 + void onManager() override{ + DebugL; + } +}; + + +/** + * 此对象可以根据websocket 客户端访问的url选择创建不同的对象 + */ +struct EchoSessionCreator { + //返回的TcpSession必须派生于SendInterceptor,可以返回null(拒绝连接) + TcpSession::Ptr operator()(const Parser &header, const HttpSession &parent, const Socket::Ptr &pSock) { +// return nullptr; + if (header.Url() == "/") { + return std::make_shared >(header, parent, pSock); + } + return std::make_shared >(header, parent, pSock); + } +}; + int main(int argc, char *argv[]) { //设置日志 Logger::Instance().add(std::make_shared()); @@ -71,13 +114,19 @@ int main(int argc, char *argv[]) { TcpServer::Ptr httpSrv(new TcpServer()); //http服务器,支持websocket - httpSrv->start>(80);//默认80 + httpSrv->start >(80);//默认80 TcpServer::Ptr httpsSrv(new TcpServer()); //https服务器,支持websocket - httpsSrv->start>(443);//默认443 + httpsSrv->start >(443);//默认443 + + TcpServer::Ptr httpSrvOld(new TcpServer()); + //兼容之前的代码(但是不支持根据url选择生成TcpSession类型) + httpSrvOld->start >(8080); + + DebugL << "请打开网页:http://www.websocket-test.com/,进行测试"; + DebugL << "连接 ws://127.0.0.1/xxxx,ws://127.0.0.1/ 测试的效果将不同,支持根据url选择不同的处理逻辑"; - DebugL << "请打开网页:http://www.websocket-test.com/,连接 ws://127.0.0.1/测试"; //设置退出信号处理函数 static semaphore sem; From 95c8474d48708d83f640aa93a6c906ecacd7cf89 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 15 Jan 2020 19:59:47 +0800 Subject: [PATCH 231/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8DGOP=E7=BC=93=E5=AD=98?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E5=B0=8Fbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 0654b00b..be08f018 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 0654b00bc50225a27b1da18f8d38d4a681ad39e9 +Subproject commit be08f01869ebf1391245c24c1e328422eb11ab77 From 81d3e54d453575f8b0f8dd94e7f351bd3ecfbb93 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 16 Jan 2020 11:08:15 +0800 Subject: [PATCH 232/272] mark --- server/FFmpegSource.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/FFmpegSource.cpp b/server/FFmpegSource.cpp index 5ba1d618..0deb3bb2 100644 --- a/server/FFmpegSource.cpp +++ b/server/FFmpegSource.cpp @@ -43,11 +43,11 @@ onceToken token([]() { //windows下先关闭FFmpeg日志(目前不支持日志重定向) mINI::Instance()[kCmd] = "%s -re -i \"%s\" -loglevel quiet -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s "; //利用ffmpeg进行抓拍 - mINI::Instance()[kSnap] = "%s -i \"%s\" -loglevel quiet -y -f mjpeg -t 0.001 -s 720*576 %s " + mINI::Instance()[kSnap] = "%s -i \"%s\" -loglevel quiet -y -f mjpeg -t 0.001 -s 720*576 %s "; #else string ffmpeg_bin = System::execute("which ffmpeg"); mINI::Instance()[kCmd] = "%s -re -i \"%s\" -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s "; - mINI::Instance()[kSnap] = "%s -i \"%s\" -y -f mjpeg -t 0.001 -s 720*576 %s " + mINI::Instance()[kSnap] = "%s -i \"%s\" -y -f mjpeg -t 0.001 -s 720*576 %s "; #endif //默认ffmpeg命令路径为环境变量中路径 mINI::Instance()[kBin] = ffmpeg_bin.empty() ? "ffmpeg" : ffmpeg_bin; From cbd6cd4e60af8fec2aa07e1d8014410a8a2cafef Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 17 Jan 2020 11:44:20 +0800 Subject: [PATCH 233/272] =?UTF-8?q?=E6=9B=BF=E6=8D=A2=E4=B8=8D=E5=8F=AF?= =?UTF-8?q?=E8=B0=83=E8=AF=95=E7=9A=84=E5=AE=8F=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 144 +++++++++++++++++++++++----------------------- 1 file changed, 73 insertions(+), 71 deletions(-) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index b2eb6713..09ba8a54 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -48,38 +48,10 @@ #include "Thread/WorkThreadPool.h" #include "Rtp/RtpSelector.h" #include "FFmpegSource.h" - using namespace Json; using namespace toolkit; using namespace mediakit; - -typedef map ApiArgsType; - - -#define API_ARGS TcpSession &sender, \ - HttpSession::KeyValue &headerIn, \ - HttpSession::KeyValue &headerOut, \ - ApiArgsType &allArgs, \ - Json::Value &val - -#define API_REGIST(field, name, ...) \ - s_map_api.emplace("/index/"#field"/"#name,[](API_ARGS,const HttpSession::HttpResponseInvoker &invoker){ \ - static auto lam = [&](API_ARGS) __VA_ARGS__ ; \ - lam(sender,headerIn, headerOut, allArgs, val); \ - invoker("200 OK", headerOut, val.toStyledString()); \ - }); - -#define API_ARGS_VALUE sender,headerIn,headerOut,allArgs,val,invoker - -#define API_REGIST_INVOKER(field, name, ...) \ - s_map_api.emplace("/index/"#field"/"#name,[](API_ARGS,const HttpSession::HttpResponseInvoker &invoker) __VA_ARGS__); - -//异步http api lambad定义 -typedef std::function AsyncHttpApi; -//api列表 -static map s_map_api; - namespace API { typedef enum { InvalidArgs = -300, @@ -128,6 +100,27 @@ public: ~SuccessException() = default; }; +#define API_ARGS1 TcpSession &sender,HttpSession::KeyValue &headerIn, HttpSession::KeyValue &headerOut, ApiArgsType &allArgs, Json::Value &val +#define API_ARGS2 API_ARGS1, const HttpSession::HttpResponseInvoker &invoker +#define API_ARGS_VALUE1 sender,headerIn,headerOut,allArgs,val +#define API_ARGS_VALUE2 API_ARGS_VALUE1, invoker + +typedef map ApiArgsType; +//http api列表 +static map > s_map_api; + +template +static void api_regist1(const string &api_path, FUNC &&func) { + s_map_api.emplace(api_path, [func](API_ARGS2) { + func(API_ARGS_VALUE1); + invoker("200 OK", headerOut, val.toStyledString()); + }); +} + +template +static void api_regist2(const string &api_path, FUNC &&func) { + s_map_api.emplace(api_path, std::forward(func)); +} //获取HTTP请求中url参数、content参数 static ApiArgsType getAllArgs(const Parser &parser) { @@ -275,12 +268,11 @@ static recursive_mutex s_ffmpegMapMtx; */ void installWebApi() { addHttpListener(); - GET_CONFIG(string,api_secret,API::kSecret); //获取线程负载 //测试url http://127.0.0.1/index/api/getThreadsLoad - API_REGIST_INVOKER(api, getThreadsLoad, { + api_regist2("/index/api/getThreadsLoad",[](API_ARGS2){ EventPollerPool::Instance().getExecutorDelay([invoker, headerOut](const vector &vecDelay) { Value val; auto vec = EventPollerPool::Instance().getExecutorLoad(); @@ -298,7 +290,7 @@ void installWebApi() { //获取后台工作线程负载 //测试url http://127.0.0.1/index/api/getWorkThreadsLoad - API_REGIST_INVOKER(api, getWorkThreadsLoad, { + api_regist2("/index/api/getWorkThreadsLoad", [](API_ARGS2){ WorkThreadPool::Instance().getExecutorDelay([invoker, headerOut](const vector &vecDelay) { Value val; auto vec = WorkThreadPool::Instance().getExecutorLoad(); @@ -316,7 +308,7 @@ void installWebApi() { //获取服务器配置 //测试url http://127.0.0.1/index/api/getServerConfig - API_REGIST(api, getServerConfig, { + api_regist1("/index/api/getServerConfig",[](API_ARGS1){ CHECK_SECRET(); Value obj; for (auto &pr : mINI::Instance()) { @@ -328,7 +320,7 @@ void installWebApi() { //设置服务器配置 //测试url(比如关闭http api调试) http://127.0.0.1/index/api/setServerConfig?api.apiDebug=0 //你也可以通过http post方式传参,可以通过application/x-www-form-urlencoded或application/json方式传参 - API_REGIST(api, setServerConfig, { + api_regist1("/index/api/setServerConfig",[](API_ARGS1){ CHECK_SECRET(); auto &ini = mINI::Instance(); int changed = API::Success; @@ -352,19 +344,29 @@ void installWebApi() { }); - //获取服务器api列表 - //测试url http://127.0.0.1/index/api/getApiList - API_REGIST(api,getApiList,{ + static auto s_get_api_list = [](API_ARGS1){ CHECK_SECRET(); for(auto &pr : s_map_api){ val["data"].append(pr.first); } + }; + + //获取服务器api列表 + //测试url http://127.0.0.1/index/api/getApiList + api_regist1("/index/api/getApiList",[](API_ARGS1){ + s_get_api_list(API_ARGS_VALUE1); + }); + + //获取服务器api列表 + //测试url http://127.0.0.1/index/ + api_regist1("/index/",[](API_ARGS1){ + s_get_api_list(API_ARGS_VALUE1); }); #if !defined(_WIN32) //重启服务器,只有Daemon方式才能重启,否则是直接关闭! //测试url http://127.0.0.1/index/api/restartServer - API_REGIST(api,restartServer,{ + api_regist1("/index/api/restartServer",[](API_ARGS1){ CHECK_SECRET(); EventPollerPool::Instance().getPoller()->doDelayTask(1000,[](){ //尝试正常退出 @@ -387,7 +389,7 @@ void installWebApi() { //测试url0(获取所有流) http://127.0.0.1/index/api/getMediaList //测试url1(获取虚拟主机为"__defaultVost__"的流) http://127.0.0.1/index/api/getMediaList?vhost=__defaultVost__ //测试url2(获取rtsp类型的流) http://127.0.0.1/index/api/getMediaList?schema=rtsp - API_REGIST(api,getMediaList,{ + api_regist1("/index/api/getMediaList",[](API_ARGS1){ CHECK_SECRET(); //获取所有MediaSource列表 MediaSource::for_each_media([&](const MediaSource::Ptr &media){ @@ -419,14 +421,14 @@ void installWebApi() { }); //测试url http://127.0.0.1/index/api/isMediaOnline?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs - API_REGIST(api,isMediaOnline,{ + api_regist1("/index/api/isMediaOnline",[](API_ARGS1){ CHECK_SECRET(); CHECK_ARGS("schema","vhost","app","stream"); val["online"] = (bool) (MediaSource::find(allArgs["schema"],allArgs["vhost"],allArgs["app"],allArgs["stream"],false)); }); //测试url http://127.0.0.1/index/api/getMediaInfo?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs - API_REGIST(api,getMediaInfo,{ + api_regist1("/index/api/getMediaInfo",[](API_ARGS1){ CHECK_SECRET(); CHECK_ARGS("schema","vhost","app","stream"); auto src = MediaSource::find(allArgs["schema"],allArgs["vhost"],allArgs["app"],allArgs["stream"],false); @@ -448,7 +450,7 @@ void installWebApi() { //主动关断流,包括关断拉流、推流 //测试url http://127.0.0.1/index/api/close_stream?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1 - API_REGIST(api,close_stream,{ + api_regist1("/index/api/close_stream",[](API_ARGS1){ CHECK_SECRET(); CHECK_ARGS("schema","vhost","app","stream"); //踢掉推流器 @@ -468,7 +470,7 @@ void installWebApi() { //批量主动关断流,包括关断拉流、推流 //测试url http://127.0.0.1/index/api/close_streams?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1 - API_REGIST(api,close_streams,{ + api_regist1("/index/api/close_streams",[](API_ARGS1){ CHECK_SECRET(); //筛选命中个数 int count_hit = 0; @@ -504,7 +506,7 @@ void installWebApi() { //获取所有TcpSession列表信息 //可以根据本地端口和远端ip来筛选 //测试url(筛选某端口下的tcp会话) http://127.0.0.1/index/api/getAllSession?local_port=1935 - API_REGIST(api,getAllSession,{ + api_regist1("/index/api/getAllSession",[](API_ARGS1){ CHECK_SECRET(); Value jsession; uint16_t local_port = allArgs["local_port"].as(); @@ -529,7 +531,7 @@ void installWebApi() { //断开tcp连接,比如说可以断开rtsp、rtmp播放器等 //测试url http://127.0.0.1/index/api/kick_session?id=123456 - API_REGIST(api,kick_session,{ + api_regist1("/index/api/kick_session",[](API_ARGS1){ CHECK_SECRET(); CHECK_ARGS("id"); //踢掉tcp会话 @@ -543,7 +545,7 @@ void installWebApi() { //批量断开tcp连接,比如说可以断开rtsp、rtmp播放器等 //测试url http://127.0.0.1/index/api/kick_sessions?local_port=1935 - API_REGIST(api,kick_sessions,{ + api_regist1("/index/api/kick_sessions",[](API_ARGS1){ CHECK_SECRET(); uint16_t local_port = allArgs["local_port"].as(); string &peer_ip = allArgs["peer_ip"]; @@ -609,7 +611,7 @@ void installWebApi() { //动态添加rtsp/rtmp拉流代理 //测试url http://127.0.0.1/index/api/addStreamProxy?vhost=__defaultVhost__&app=proxy&enable_rtsp=1&enable_rtmp=1&stream=0&url=rtmp://127.0.0.1/live/obs - API_REGIST_INVOKER(api,addStreamProxy,{ + api_regist2("/index/api/addStreamProxy",[](API_ARGS2){ CHECK_SECRET(); CHECK_ARGS("vhost","app","stream","url","enable_rtsp","enable_rtmp"); addStreamProxy(allArgs["vhost"], @@ -634,7 +636,7 @@ void installWebApi() { //关闭拉流代理 //测试url http://127.0.0.1/index/api/delStreamProxy?key=__defaultVhost__/proxy/0 - API_REGIST(api,delStreamProxy,{ + api_regist1("/index/api/delStreamProxy",[](API_ARGS1){ CHECK_SECRET(); CHECK_ARGS("key"); lock_guard lck(s_proxyMapMtx); @@ -671,7 +673,7 @@ void installWebApi() { //动态添加rtsp/rtmp拉流代理 //测试url http://127.0.0.1/index/api/addFFmpegSource?src_url=http://live.hkstv.hk.lxdns.com/live/hks2/playlist.m3u8&dst_url=rtmp://127.0.0.1/live/hks2&timeout_ms=10000 - API_REGIST_INVOKER(api,addFFmpegSource,{ + api_regist2("/index/api/addFFmpegSource",[](API_ARGS2){ CHECK_SECRET(); CHECK_ARGS("src_url","dst_url","timeout_ms"); auto src_url = allArgs["src_url"]; @@ -690,7 +692,7 @@ void installWebApi() { }); - static auto api_delFFmpegSource = [](API_ARGS,const HttpSession::HttpResponseInvoker &invoker){ + static auto api_delFFmpegSource = [](API_ARGS1){ CHECK_SECRET(); CHECK_ARGS("key"); lock_guard lck(s_ffmpegMapMtx); @@ -699,24 +701,24 @@ void installWebApi() { //关闭拉流代理 //测试url http://127.0.0.1/index/api/delFFmepgSource?key=key - API_REGIST(api,delFFmpegSource,{ - api_delFFmpegSource(API_ARGS_VALUE); + api_regist1("/index/api/delFFmpegSource",[](API_ARGS1){ + api_delFFmpegSource(API_ARGS_VALUE1); }); //此处为了兼容之前的拼写错误 - API_REGIST(api,delFFmepgSource,{ - api_delFFmpegSource(API_ARGS_VALUE); + api_regist1("/index/api/delFFmepgSource",[](API_ARGS1){ + api_delFFmpegSource(API_ARGS_VALUE1); }); //新增http api下载可执行程序文件接口 //测试url http://127.0.0.1/index/api/downloadBin - API_REGIST_INVOKER(api,downloadBin,{ + api_regist2("/index/api/downloadBin",[](API_ARGS2){ CHECK_SECRET(); invoker.responseFile(headerIn,StrCaseMap(),exePath()); }); #if defined(ENABLE_RTPPROXY) - API_REGIST(api,getSsrcInfo,{ + api_regist1("/index/api/getSsrcInfo",[](API_ARGS1){ CHECK_SECRET(); CHECK_ARGS("ssrc"); uint32_t ssrc = 0; @@ -735,7 +737,7 @@ void installWebApi() { #endif//ENABLE_RTPPROXY // 开始录制hls或MP4 - API_REGIST(api,startRecord,{ + api_regist1("/index/api/startRecord",[](API_ARGS1){ CHECK_SECRET(); CHECK_ARGS("type","vhost","app","stream","wait_for_record","continue_record"); @@ -750,7 +752,7 @@ void installWebApi() { }); // 停止录制hls或MP4 - API_REGIST(api,stopRecord,{ + api_regist1("/index/api/stopRecord",[](API_ARGS1){ CHECK_SECRET(); CHECK_ARGS("type","vhost","app","stream"); int result = Recorder::stopRecord((Recorder::type)allArgs["type"].as(), @@ -761,7 +763,7 @@ void installWebApi() { }); // 获取hls或MP4录制状态 - API_REGIST(api,getRecordStatus,{ + api_regist1("/index/api/getRecordStatus",[](API_ARGS1){ CHECK_SECRET(); CHECK_ARGS("type","vhost","app","stream"); auto status = Recorder::getRecordStatus((Recorder::type)allArgs["type"].as(), @@ -772,7 +774,7 @@ void installWebApi() { }); ////////////以下是注册的Hook API//////////// - API_REGIST(hook,on_publish,{ + api_regist1("/index/hook/on_publish",[](API_ARGS1){ //开始推流事件 //转换成rtsp或rtmp val["enableRtxp"] = true; @@ -782,23 +784,23 @@ void installWebApi() { val["enableMP4"] = false; }); - API_REGIST(hook,on_play,{ + api_regist1("/index/hook/on_play",[](API_ARGS1){ //开始播放事件 throw SuccessException(); }); - API_REGIST(hook,on_flow_report,{ + api_regist1("/index/hook/on_flow_report",[](API_ARGS1){ //流量统计hook api throw SuccessException(); }); - API_REGIST(hook,on_rtsp_realm,{ + api_regist1("/index/hook/on_rtsp_realm",[](API_ARGS1){ //rtsp是否需要鉴权,默认需要鉴权 val["code"] = API::Success; val["realm"] = "zlmediakit_reaml"; }); - API_REGIST(hook,on_rtsp_auth,{ + api_regist1("/index/hook/on_rtsp_auth",[](API_ARGS1){ //rtsp鉴权密码,密码等于用户名 //rtsp可以有双重鉴权!后面还会触发on_play事件 CHECK_ARGS("user_name"); @@ -807,14 +809,14 @@ void installWebApi() { val["passwd"] = allArgs["user_name"].data(); }); - API_REGIST(hook,on_stream_changed,{ + api_regist1("/index/hook/on_stream_changed",[](API_ARGS1){ //媒体注册或反注册事件 throw SuccessException(); }); #if !defined(_WIN32) - API_REGIST_INVOKER(hook,on_stream_not_found_ffmpeg,{ + api_regist2("/index/hook/on_stream_not_found_ffmpeg",[](API_ARGS2){ //媒体未找到事件,我们都及时拉流hks作为替代品,目的是为了测试按需拉流 CHECK_SECRET(); CHECK_ARGS("vhost","app","stream"); @@ -844,7 +846,7 @@ void installWebApi() { }); #endif//!defined(_WIN32) - API_REGIST_INVOKER(hook,on_stream_not_found,{ + api_regist2("/index/hook/on_stream_not_found",[](API_ARGS2){ //媒体未找到事件,我们都及时拉流hks作为替代品,目的是为了测试按需拉流 CHECK_SECRET(); CHECK_ARGS("vhost","app","stream"); @@ -870,17 +872,17 @@ void installWebApi() { }); }); - API_REGIST(hook,on_record_mp4,{ + api_regist1("/index/hook/on_record_mp4",[](API_ARGS1){ //录制mp4分片完毕事件 throw SuccessException(); }); - API_REGIST(hook,on_shell_login,{ + api_regist1("/index/hook/on_shell_login",[](API_ARGS1){ //shell登录调试事件 throw SuccessException(); }); - API_REGIST(hook,on_stream_none_reader,{ + api_regist1("/index/hook/on_stream_none_reader",[](API_ARGS1){ //无人观看流默认关闭 val["close"] = true; }); @@ -890,7 +892,7 @@ void installWebApi() { return true; }; - API_REGIST(hook,on_http_access,{ + api_regist1("/index/hook/on_http_access",[](API_ARGS1){ //在这里根据allArgs["params"](url参数)来判断该http客户端是否有权限访问该文件 if(!checkAccess(allArgs["params"])){ //无访问权限 @@ -911,7 +913,7 @@ void installWebApi() { }); - API_REGIST(hook,on_server_started,{ + api_regist1("/index/hook/on_server_started",[](API_ARGS1){ //服务器重启报告 throw SuccessException(); }); From d2d84680c0d1b044d2c684b338aaffcb1fba4429 Mon Sep 17 00:00:00 2001 From: liuziloong <18675721@qq.com> Date: Sun, 19 Jan 2020 14:54:31 +0800 Subject: [PATCH 234/272] temp --- server/Process.cpp | 2 +- server/WebApi.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/server/Process.cpp b/server/Process.cpp index d64ae72a..eb8eac4d 100644 --- a/server/Process.cpp +++ b/server/Process.cpp @@ -53,7 +53,7 @@ void Process::run(const string &cmd, const string &log_file_tmp) { ZeroMemory(&si, sizeof(si)); //结构体初始化; ZeroMemory(&pi, sizeof(pi)); - LPTSTR lpDir = const_cast(cmd .data()); + LPTSTR lpDir = const_cast(cmd.data()); if (CreateProcess(NULL, lpDir, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)){ //下面两行关闭句柄,解除本进程和新进程的关系,不然有可能 不小心调用TerminateProcess函数关掉子进程 diff --git a/server/WebApi.cpp b/server/WebApi.cpp index b2eb6713..c303b154 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -771,6 +771,60 @@ void installWebApi() { val["status"] = (int)status; }); + //获取录像列表(按月) + //http://127.0.0.1/index/api/getMp4RecordMonthly?app=pzstll&stream=stream_4&period=2020-01 + API_REGIST(api, getMp4RecordMonthly, { + CHECK_SECRET(); + CHECK_ARGS("app", "stream", "period"); + + GET_CONFIG(string, recordAppName, Record::kAppName); + GET_CONFIG(string, recordPath, Record::kFilePath); + GET_CONFIG(bool, enableVhost, General::kEnableVhost); + + auto _vhost = allArgs["vhost"]; + auto _app = allArgs["app"]; + auto _stream = allArgs["stream"]; + auto _period = allArgs["period"]; + + //TODO:判断日期格式 + + //Vhost为空的话,存储路径不一样 + if (_vhost.empty()) + { + _vhost = DEFAULT_VHOST; + } + + string strMp4RecordPath; + if (enableVhost) + { + strMp4RecordPath = recordPath + "/" + _vhost + "/" + recordAppName + "/" + _app + "/" + _stream; + } + else + { + strMp4RecordPath = recordPath + "/" + recordAppName + "/" + _app + "/" + _stream; + } + DebugL << strMp4RecordPath; + + Json::Value nVal; + nVal["vhost"] = _vhost.data(); + nVal["app"] = _app.data(); + nVal["stream"] = _stream.data(); + nVal["app"] = _app.data(); + + val["data"] = nVal; + + }); + + //获取录像列表(按天) + //http://127.0.0.1/index/api/getMp4RecordDaily?app=pzstll&stream=stream_4&period=2020-01-17 + API_REGIST(api, getMp4RecordDaily, { + CHECK_SECRET(); + CHECK_ARGS("app", "stream", "period"); + + + + }); + ////////////以下是注册的Hook API//////////// API_REGIST(hook,on_publish,{ //开始推流事件 From 2384aa5bbdbda963224cb7cbf4dce115649eca5f Mon Sep 17 00:00:00 2001 From: liuziloong <18675721@qq.com> Date: Mon, 20 Jan 2020 15:02:45 +0800 Subject: [PATCH 235/272] =?UTF-8?q?MP4=E5=BD=95=E5=83=8F=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=B8=80=E7=BA=A7=E7=9B=AE=E5=BD=95=EF=BC=8C=E6=8C=89=E7=85=A7?= =?UTF-8?q?=EF=BC=9A=E5=B9=B4=E6=9C=88/=E5=B9=B4=E6=9C=88=E6=97=A5/*.mp4?= =?UTF-8?q?=E5=AD=98=E5=82=A8=EF=BC=9B=20=E5=A2=9E=E5=8A=A0=E6=8C=89?= =?UTF-8?q?=E6=9C=88=EF=BC=8C=E6=8C=89=E5=A4=A9=E6=9F=A5=E8=AF=A2mp4?= =?UTF-8?q?=E5=BD=95=E5=83=8F=EF=BC=9B=20=E6=B6=88=E9=99=A4=E4=B8=80?= =?UTF-8?q?=E5=A4=84=E7=BC=96=E8=AF=91=E5=99=A8=E8=AD=A6=E5=91=8A=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 98 ++++++++++++++++++++++++++++++-------- src/Extension/SPSParser.c | 2 +- src/Record/MP4Recorder.cpp | 6 ++- 3 files changed, 84 insertions(+), 22 deletions(-) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 28cce944..724328ac 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -71,6 +71,7 @@ static onceToken token([]() { }); }//namespace API + class ApiRetException: public std::runtime_error { public: ApiRetException(const char *str = "success" ,int code = API::Success):runtime_error(str){ @@ -773,9 +774,9 @@ void installWebApi() { val["status"] = (int)status; }); - //获取录像列表(按月) - //http://127.0.0.1/index/api/getMp4RecordMonthly?app=pzstll&stream=stream_4&period=2020-01 - API_REGIST(api, getMp4RecordMonthly, { + //获取录像列表(按月),返回天 + //http://127.0.0.1/index/api/getMp4RecordMonthly?app=live&stream=onvif&period=2020-01 + api_regist1("/index/api/getMp4RecordMonthly", [](API_ARGS1){ CHECK_SECRET(); CHECK_ARGS("app", "stream", "period"); @@ -787,45 +788,104 @@ void installWebApi() { auto _app = allArgs["app"]; auto _stream = allArgs["stream"]; auto _period = allArgs["period"]; - + //TODO:判断日期格式 //Vhost为空的话,存储路径不一样 - if (_vhost.empty()) - { + if (_vhost.empty()) { _vhost = DEFAULT_VHOST; } - string strMp4RecordPath; - if (enableVhost) - { - strMp4RecordPath = recordPath + "/" + _vhost + "/" + recordAppName + "/" + _app + "/" + _stream; + string strMp4RecordPath = ""; + if (enableVhost) { + strMp4RecordPath = recordPath + "/" + _vhost + "/" + recordAppName + "/" + _app + "/" + _stream + "/" + _period; + } else { + strMp4RecordPath = recordPath + "/" + recordAppName + "/" + _app + "/" + _stream + "/" + _period; } - else - { - strMp4RecordPath = recordPath + "/" + recordAppName + "/" + _app + "/" + _stream; - } - DebugL << strMp4RecordPath; + + //录像相对路径转绝对路径, 不然vs调试需要设置 + strMp4RecordPath = File::absolutePath(strMp4RecordPath, ""); + string strMp4Path = ""; + File::scanDir(strMp4RecordPath, [&strMp4Path](const string &path, bool isDir) { + if (isDir) { + //DebugL << "dir:" << path.data(); + int iPost = path.rfind('/'); + if (iPost != string::npos) + { + string strPath = path.substr(iPost + 1); + strMp4Path += strPath + ","; + } + } + return true; + },false); + + //去掉strMp4Path最后一个 ‘,’ + strMp4Path = strMp4Path.substr(0, strMp4Path.length() - 1); Json::Value nVal; nVal["vhost"] = _vhost.data(); nVal["app"] = _app.data(); nVal["stream"] = _stream.data(); nVal["app"] = _app.data(); + nVal["path"] = strMp4Path; val["data"] = nVal; - }); //获取录像列表(按天) - //http://127.0.0.1/index/api/getMp4RecordDaily?app=pzstll&stream=stream_4&period=2020-01-17 - API_REGIST(api, getMp4RecordDaily, { + //http://127.0.0.1/index/api/getMp4RecordDaily?app=live&stream=onvif&period=2020-01-17 + api_regist1("/index/api/getMp4RecordDaily",[](API_ARGS1) { CHECK_SECRET(); CHECK_ARGS("app", "stream", "period"); + GET_CONFIG(string, recordAppName, Record::kAppName); + GET_CONFIG(string, recordPath, Record::kFilePath); + GET_CONFIG(bool, enableVhost, General::kEnableVhost); + auto _vhost = allArgs["vhost"]; + auto _app = allArgs["app"]; + auto _stream = allArgs["stream"]; + auto _period = allArgs["period"]; - }); + //TODO:判断日期格式 + + //Vhost为空的话,存储路径不一样 + if (_vhost.empty()) { + _vhost = DEFAULT_VHOST; + } + + string strMp4RecordPath = ""; + //取录上一级目录,默认前7位位上级目录 + string strParentPeriod = string(_period).substr(0, 7); + if (enableVhost) { + strMp4RecordPath = recordPath + "/" + _vhost + "/" + recordAppName + "/" + _app + "/" + _stream + "/" + strParentPeriod + "/" + _period; + } + else { + strMp4RecordPath = recordPath + "/" + recordAppName + "/" + _app + "/" + _stream + "/" + strParentPeriod + "/" + _period; + } + + //录像相对路径转绝对路径, 不然vs调试需要设置 + strMp4RecordPath = File::absolutePath(strMp4RecordPath, ""); + Json::Value nVal; + DebugL << recordPath; + File::scanDir(strMp4RecordPath, [&](const string &path, bool isDir) { + if (!isDir) { + //去掉绝对路径,从record开始返回 + int iPos = path.find(recordAppName); + string strPath = path.substr(iPos); + + nVal["path"].append(strPath); + } + return true; + }, false); + + nVal["vhost"] = _vhost.data(); + nVal["app"] = _app.data(); + nVal["stream"] = _stream.data(); + nVal["app"] = _app.data(); + + val["data"] = nVal; + }); ////////////以下是注册的Hook API//////////// api_regist1("/index/hook/on_publish",[](API_ARGS1){ diff --git a/src/Extension/SPSParser.c b/src/Extension/SPSParser.c index b073b9cb..54c19592 100644 --- a/src/Extension/SPSParser.c +++ b/src/Extension/SPSParser.c @@ -1330,7 +1330,7 @@ int hevcDecodeShortTermRps(T_GetBitContext *pvBuf, int iDeltaRps; unsigned int uiAbsDeltaRps; uint8_t u8UseDeltaFlag = 0; - uint8_t u8DeltaRpsSign; + uint8_t u8DeltaRpsSign = 0; if (is_slice_header) { unsigned int uiDeltaIdx = parseUe(pvBuf) + 1; diff --git a/src/Record/MP4Recorder.cpp b/src/Record/MP4Recorder.cpp index a92f3fde..cd439820 100644 --- a/src/Record/MP4Recorder.cpp +++ b/src/Record/MP4Recorder.cpp @@ -53,9 +53,10 @@ MP4Recorder::~MP4Recorder() { void MP4Recorder::createFile() { closeFile(); auto strDate = getTimeStr("%Y-%m-%d"); + auto strDay = getTimeStr("%Y-%m-%d"); auto strTime = getTimeStr("%H-%M-%S"); - auto strFileTmp = _strPath + strDate + "/." + strTime + ".mp4"; - auto strFile = _strPath + strDate + "/" + strTime + ".mp4"; + auto strFileTmp = _strPath + strDate + "/" + strDay + "/." + strTime + ".mp4"; + auto strFile = _strPath + strDate + "/" + strDay + "/" + strTime + ".mp4"; /////record 业务逻辑////// _info.ui64StartedTime = ::time(NULL); @@ -66,6 +67,7 @@ void MP4Recorder::createFile() { + _info.strAppName + "/" + _info.strStreamId + "/" + strDate + "/" + + strDay + "/" + strTime + ".mp4"; try { From 795860a0197e3b37c49ae71cdb22ffb8289190e3 Mon Sep 17 00:00:00 2001 From: liuziloong <18675721@qq.com> Date: Mon, 20 Jan 2020 15:22:20 +0800 Subject: [PATCH 236/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E7=BA=A7=E5=88=AB=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/MP4Recorder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Record/MP4Recorder.cpp b/src/Record/MP4Recorder.cpp index cd439820..97cc9fac 100644 --- a/src/Record/MP4Recorder.cpp +++ b/src/Record/MP4Recorder.cpp @@ -52,7 +52,7 @@ MP4Recorder::~MP4Recorder() { void MP4Recorder::createFile() { closeFile(); - auto strDate = getTimeStr("%Y-%m-%d"); + auto strDate = getTimeStr("%Y-%m"); auto strDay = getTimeStr("%Y-%m-%d"); auto strTime = getTimeStr("%H-%M-%S"); auto strFileTmp = _strPath + strDate + "/" + strDay + "/." + strTime + ".mp4"; From f45366868339a7fe8485247ddf8dc3f1afb5288c Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 20 Jan 2020 15:34:06 +0800 Subject: [PATCH 237/272] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E4=BE=A6=E6=8E=A2gop?= =?UTF-8?q?=E9=95=BF=E5=BA=A6=E7=9A=84=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index be08f018..3850c1ad 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit be08f01869ebf1391245c24c1e328422eb11ab77 +Subproject commit 3850c1adcc5d184c6da0dd263ac011388bb2ffee From f5187e8b0d2e2c5e29d24c02e45044b07fbc8a7a Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 20 Jan 2020 16:22:25 +0800 Subject: [PATCH 238/272] =?UTF-8?q?=E4=BC=98=E5=8C=96GOP=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- src/Rtmp/RtmpMediaSource.h | 5 +++-- src/Rtmp/RtmpMediaSourceImp.h | 2 +- src/Rtsp/RtspMediaSource.h | 8 ++++---- src/Rtsp/RtspMediaSourceImp.h | 2 +- src/Rtsp/RtspPlayerImp.h | 2 ++ 6 files changed, 12 insertions(+), 9 deletions(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 3850c1ad..8eaecbd6 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 3850c1adcc5d184c6da0dd263ac011388bb2ffee +Subproject commit 8eaecbd6dd8bed0bee6a61168255653127a20a43 diff --git a/src/Rtmp/RtmpMediaSource.h b/src/Rtmp/RtmpMediaSource.h index e6f9178f..b09bbf53 100644 --- a/src/Rtmp/RtmpMediaSource.h +++ b/src/Rtmp/RtmpMediaSource.h @@ -46,6 +46,7 @@ #include "Thread/ThreadPool.h" using namespace toolkit; +#define RTMP_GOP_SIZE 512 namespace mediakit { /** @@ -70,7 +71,7 @@ public: RtmpMediaSource(const string &vhost, const string &app, const string &stream_id, - int ring_size = 0) : + int ring_size = RTMP_GOP_SIZE) : MediaSource(RTMP_SCHEMA, vhost, app, stream_id), _ring_size(ring_size) { } @@ -146,7 +147,7 @@ public: //rtmp包缓存最大允许512个,如果是纯视频(25fps)大概为20秒数据 //但是这个是GOP缓存的上限值,真实的GOP缓存大小等于两个I帧之间的包数的两倍 //而且每次遇到I帧,则会清空GOP缓存,所以真实的GOP缓存远小于此值 - _ring = std::make_shared(_ring_size,512,std::move(lam)); + _ring = std::make_shared(_ring_size,std::move(lam)); onReaderChanged(0); if(_metadata){ diff --git a/src/Rtmp/RtmpMediaSourceImp.h b/src/Rtmp/RtmpMediaSourceImp.h index 482ad5aa..fca06936 100644 --- a/src/Rtmp/RtmpMediaSourceImp.h +++ b/src/Rtmp/RtmpMediaSourceImp.h @@ -54,7 +54,7 @@ public: * @param id 流id * @param ringSize 环形缓存大小 */ - RtmpMediaSourceImp(const string &vhost, const string &app, const string &id, int ringSize = 0) : RtmpMediaSource(vhost, app, id, ringSize) { + RtmpMediaSourceImp(const string &vhost, const string &app, const string &id, int ringSize = RTMP_GOP_SIZE) : RtmpMediaSource(vhost, app, id, ringSize) { _demuxer = std::make_shared(); _demuxer->setTrackListener(this); } diff --git a/src/Rtsp/RtspMediaSource.h b/src/Rtsp/RtspMediaSource.h index 21263b9d..a99ee60d 100644 --- a/src/Rtsp/RtspMediaSource.h +++ b/src/Rtsp/RtspMediaSource.h @@ -35,17 +35,17 @@ #include "Common/config.h" #include "Common/MediaSource.h" #include "RtpCodec.h" - #include "Util/logger.h" #include "Util/RingBuffer.h" #include "Util/TimeTicker.h" #include "Util/ResourcePool.h" #include "Util/NoticeCenter.h" #include "Thread/ThreadPool.h" - using namespace std; using namespace toolkit; +#define RTP_GOP_SIZE 2048 + namespace mediakit { /** @@ -70,7 +70,7 @@ public: RtspMediaSource(const string &vhost, const string &app, const string &stream_id, - int ring_size = 0) : + int ring_size = RTP_GOP_SIZE) : MediaSource(RTSP_SCHEMA, vhost, app, stream_id), _ring_size(ring_size) {} virtual ~RtspMediaSource() {} @@ -182,7 +182,7 @@ public: //rtp包缓存最大允许2048个,大概最多3MB数据 //但是这个是GOP缓存的上限值,真实的GOP缓存大小等于两个I帧之间的包数的两倍 //而且每次遇到I帧,则会清空GOP缓存,所以真实的GOP缓存远小于此值 - _ring = std::make_shared(_ring_size,2048, std::move(lam)); + _ring = std::make_shared(_ring_size, std::move(lam)); onReaderChanged(0); if (!_sdp.empty()) { regist(); diff --git a/src/Rtsp/RtspMediaSourceImp.h b/src/Rtsp/RtspMediaSourceImp.h index e5f78121..4586df2b 100644 --- a/src/Rtsp/RtspMediaSourceImp.h +++ b/src/Rtsp/RtspMediaSourceImp.h @@ -45,7 +45,7 @@ public: * @param id 流id * @param ringSize 环形缓存大小 */ - RtspMediaSourceImp(const string &vhost, const string &app, const string &id, int ringSize = 0) : RtspMediaSource(vhost, app, id,ringSize) { + RtspMediaSourceImp(const string &vhost, const string &app, const string &id, int ringSize = RTP_GOP_SIZE) : RtspMediaSource(vhost, app, id,ringSize) { _demuxer = std::make_shared(); _demuxer->setTrackListener(this); } diff --git a/src/Rtsp/RtspPlayerImp.h b/src/Rtsp/RtspPlayerImp.h index 99b008ed..f61ac7c6 100644 --- a/src/Rtsp/RtspPlayerImp.h +++ b/src/Rtsp/RtspPlayerImp.h @@ -72,6 +72,8 @@ private: } void onRecvRTP(const RtpPacket::Ptr &rtp, const SdpTrack::Ptr &track) override { if(_pRtspMediaSrc){ + // rtsp直接代理是无法判断该rtp是否是I帧,所以GOP缓存基本是无效的 + // 为了减少内存使用,那么我们设置为一直关键帧以便清空GOP缓存 _pRtspMediaSrc->onWrite(rtp,true); } _delegate->inputRtp(rtp); From ac3359231902bf5e616fea10a9b65c5ce04a6dae Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 21 Jan 2020 11:05:51 +0800 Subject: [PATCH 239/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=AD=BB=E9=94=81?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/Recorder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index 851a9d32..39fdc989 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -266,6 +266,7 @@ private: } void addRecorder(MediaSource &sender){ + auto tracks = sender.getTracks(needTrackReady()); auto key = getRecorderKey(sender.getVhost(),sender.getApp(),sender.getId()); lock_guard lck(_recorder_mtx); auto it = _recorder_map.find(key); @@ -276,7 +277,6 @@ private: if(!it->second->isRecording() || it->second->getSchema() == sender.getSchema()){ // 绑定的协议一致或者并未正在录制则替换tracks - auto tracks = sender.getTracks(needTrackReady()); if (!tracks.empty()) { it->second->attachTracks(std::move(tracks),sender.getSchema()); } From 4470f2e4279e720dcaec5d82cc5b6439fce3f0c4 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 22 Jan 2020 19:09:53 +0800 Subject: [PATCH 240/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0addTrackCompleted?= =?UTF-8?q?=E6=96=B9=E6=B3=95=EF=BC=8C=E7=BC=A9=E7=9F=AD=E6=B5=81=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=E5=BB=B6=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/MediaSink.cpp | 22 +++++++++++++--------- src/Common/MediaSink.h | 9 +++++++-- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/Common/MediaSink.cpp b/src/Common/MediaSink.cpp index eb7cf533..f579f122 100644 --- a/src/Common/MediaSink.cpp +++ b/src/Common/MediaSink.cpp @@ -82,7 +82,7 @@ void MediaSink::inputFrame(const Frame::Ptr &frame) { if(!_allTrackReady){ if(_ticker.elapsedTime() > MAX_WAIT_MS_READY){ //如果超过规定时间,那么不再等待并忽略未准备好的Track - emitAllTrackReady(); + addTrackCompleted(); return; } @@ -93,26 +93,29 @@ void MediaSink::inputFrame(const Frame::Ptr &frame) { if(_track_map.size() == 2){ //如果已经添加了音视频Track,并且不存在未准备好的Track,那么说明所有Track都准备好了 - emitAllTrackReady(); + addTrackCompleted(); return; } if(_track_map.size() == 1 && _ticker.elapsedTime() > MAX_WAIT_MS_ADD_TRACK){ //如果只有一个Track,那么在该Track添加后,我们最多还等待若干时间(可能后面还会添加Track) - emitAllTrackReady(); + addTrackCompleted(); return; } } } -void MediaSink::emitAllTrackReady() { - _allTrackReady = true; - if(!_trackReadyCallback.empty()){ +void MediaSink::addTrackCompleted() { + if (_allTrackReady) { + return; + } + + if (!_trackReadyCallback.empty()) { //这是超时强制忽略未准备好的Track _trackReadyCallback.clear(); //移除未准备好的Track - for(auto it = _track_map.begin() ; it != _track_map.end() ; ){ - if(!it->second->ready()){ + for (auto it = _track_map.begin(); it != _track_map.end();) { + if (!it->second->ready()) { it = _track_map.erase(it); continue; } @@ -120,8 +123,9 @@ void MediaSink::emitAllTrackReady() { } } - if(!_track_map.empty()){ + if (!_track_map.empty()) { //最少有一个有效的Track + _allTrackReady = true; onAllTrackReady(); } } diff --git a/src/Common/MediaSink.h b/src/Common/MediaSink.h index e37980e1..832bbe9c 100644 --- a/src/Common/MediaSink.h +++ b/src/Common/MediaSink.h @@ -81,6 +81,13 @@ public: */ void addTrack(const Track::Ptr & track) override; + /** + * 添加Track完毕,如果是单Track,会最多等待3秒才会触发onAllTrackReady + * 这样会增加生成流的延时,如果添加了音视频双Track,那么可以不调用此方法 + * 否则为了降低流注册延时,请手动调用此方法 + */ + void addTrackCompleted(); + /** * 重置track */ @@ -109,8 +116,6 @@ protected: * @param frame */ virtual void onTrackFrame(const Frame::Ptr &frame) {}; -private: - void emitAllTrackReady(); private: mutable recursive_mutex _mtx; map _track_map; From 90c6dd4bfab4a27566893270bb20370892f3b8c5 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 24 Jan 2020 20:54:39 +0800 Subject: [PATCH 241/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=85=B3=E9=97=ADhls?= =?UTF-8?q?=E5=90=8E=E6=97=A0=E6=B3=95=E7=BC=96=E8=AF=91=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 4 ++-- src/Record/HlsRecorder.h | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a76876e2..ee853b01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -project(ZLMediaKit) +project(ZLMediaKit) cmake_minimum_required(VERSION 3.1.3) #使能c++11 set(CMAKE_CXX_STANDARD 11) @@ -149,7 +149,7 @@ if(ENABLE_MP4RECORD) endif() #添加rtp库用于rtp转ps/ts -if(ENABLE_RTPPROXY) +if(ENABLE_RTPPROXY AND ENABLE_HLS) message(STATUS "ENABLE_RTPPROXY defined") aux_source_directory(${MediaServer_Root}/librtp/include src_rtp) aux_source_directory(${MediaServer_Root}/librtp/source src_rtp) diff --git a/src/Record/HlsRecorder.h b/src/Record/HlsRecorder.h index d9e20e33..7b1da82b 100644 --- a/src/Record/HlsRecorder.h +++ b/src/Record/HlsRecorder.h @@ -29,10 +29,13 @@ #include "HlsMakerImp.h" #include "TsMuxer.h" - namespace mediakit { -class HlsRecorder : public TsMuxer { +class HlsRecorder +#if defined(ENABLE_HLS) +: public TsMuxer +#endif + { public: typedef std::shared_ptr Ptr; HlsRecorder(const string &m3u8_file, const string ¶ms){ @@ -51,14 +54,14 @@ public: MediaSource::Ptr getMediaSource() const{ return _hls->getMediaSource(); } +#if defined(ENABLE_HLS) protected: void onTs(const void *packet, int bytes,uint32_t timestamp,int flags) override { _hls->inputData((char *)packet,bytes,timestamp); }; +#endif private: HlsMakerImp *_hls; }; - }//namespace mediakit - #endif //HLSRECORDER_H From 224a35dbd5c495896c5ac60dbc5df80a4479ab67 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 24 Jan 2020 21:09:47 +0800 Subject: [PATCH 242/272] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=8D=95=E9=9F=B3?= =?UTF-8?q?=E9=A2=91=E6=97=A0=E6=B3=95=E5=BD=95=E5=88=B6mp4=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/MP4Muxer.cpp | 19 +++++++++++++------ src/Record/MP4Muxer.h | 3 ++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/Record/MP4Muxer.cpp b/src/Record/MP4Muxer.cpp index 51f5e440..785fedbd 100644 --- a/src/Record/MP4Muxer.cpp +++ b/src/Record/MP4Muxer.cpp @@ -70,6 +70,7 @@ void MP4MuxerBase::init(int flags) { void MP4Muxer::resetTracks() { _codec_to_trackid.clear(); _started = false; + _have_video = false; } void MP4Muxer::inputFrame(const Frame::Ptr &frame) { @@ -79,14 +80,18 @@ void MP4Muxer::inputFrame(const Frame::Ptr &frame) { return; } - if(!_started){ + if (!_started) { //还没开始 - if(frame->getTrackType() != TrackVideo || !frame->keyFrame()){ - //如果首帧是音频或者是视频但是不是i帧,那么不能开始写文件 - return; + if (!_have_video) { + _started = true; + } else { + if (frame->getTrackType() != TrackVideo || !frame->keyFrame()) { + //如果首帧是音频或者是视频但是不是i帧,那么不能开始写文件 + return; + } + //开始写文件 + _started = true; } - //开始写文件 - _started = true; } //mp4文件时间戳需要从0开始 @@ -211,6 +216,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { return; } _codec_to_trackid[track->getCodecId()].track_id = track_id; + _have_video = true; } break; case CodecH265: { @@ -248,6 +254,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { return; } _codec_to_trackid[track->getCodecId()].track_id = track_id; + _have_video = true; } break; default: diff --git a/src/Record/MP4Muxer.h b/src/Record/MP4Muxer.h index 1edac519..3285107e 100644 --- a/src/Record/MP4Muxer.h +++ b/src/Record/MP4Muxer.h @@ -81,8 +81,9 @@ private: Stamp stamp; }; unordered_map _codec_to_trackid; - bool _started = false; List _frameCached; + bool _started = false; + bool _have_video = false; }; From a9539eb62629841408f97f4c40d9862614b34ed5 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 24 Jan 2020 22:00:55 +0800 Subject: [PATCH 243/272] =?UTF-8?q?HLS=E9=81=87=E5=88=B0I=E5=B8=A7?= =?UTF-8?q?=E6=89=8D=E5=88=87=E7=89=87:#226?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/HlsMaker.cpp | 8 +++++--- src/Record/HlsMaker.h | 3 ++- src/Record/HlsRecorder.h | 4 ++-- src/Record/TsMuxer.cpp | 22 ++++++++++++++++------ src/Record/TsMuxer.h | 4 +++- 5 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/Record/HlsMaker.cpp b/src/Record/HlsMaker.cpp index 039254b9..704e5f61 100644 --- a/src/Record/HlsMaker.cpp +++ b/src/Record/HlsMaker.cpp @@ -75,14 +75,16 @@ void HlsMaker::makeIndexFile(bool eof) { } -void HlsMaker::inputData(void *data, uint32_t len, uint32_t timestamp) { - //分片数据中断结束 +void HlsMaker::inputData(void *data, uint32_t len, uint32_t timestamp, bool is_idr_fast_packet) { if (data && len) { - addNewSegment(timestamp); + if(is_idr_fast_packet){ + addNewSegment(timestamp); + } onWriteSegment((char *) data, len); //记录上次写入数据时间 _ticker_last_data.resetTime(); } else { + //resetTracks时触发此逻辑 flushLastSegment(true); } } diff --git a/src/Record/HlsMaker.h b/src/Record/HlsMaker.h index cb7f6b23..12e5dab4 100644 --- a/src/Record/HlsMaker.h +++ b/src/Record/HlsMaker.h @@ -52,8 +52,9 @@ public: * @param data 数据 * @param len 数据长度 * @param timestamp 毫秒时间戳 + * @param is_idr_fast_packet 是否为关键帧第一个包 */ - void inputData(void *data, uint32_t len, uint32_t timestamp); + void inputData(void *data, uint32_t len, uint32_t timestamp, bool is_idr_fast_packet); protected: /** * 创建ts切片文件回调 diff --git a/src/Record/HlsRecorder.h b/src/Record/HlsRecorder.h index 7b1da82b..7bebd20b 100644 --- a/src/Record/HlsRecorder.h +++ b/src/Record/HlsRecorder.h @@ -56,8 +56,8 @@ public: } #if defined(ENABLE_HLS) protected: - void onTs(const void *packet, int bytes,uint32_t timestamp,int flags) override { - _hls->inputData((char *)packet,bytes,timestamp); + void onTs(const void *packet, int bytes,uint32_t timestamp,bool is_idr_fast_packet) override { + _hls->inputData((char *)packet,bytes,timestamp, is_idr_fast_packet); }; #endif private: diff --git a/src/Record/TsMuxer.cpp b/src/Record/TsMuxer.cpp index fe63624b..e63c4f8f 100644 --- a/src/Record/TsMuxer.cpp +++ b/src/Record/TsMuxer.cpp @@ -40,16 +40,21 @@ TsMuxer::~TsMuxer() { } void TsMuxer::addTrack(const Track::Ptr &track) { - switch (track->getCodecId()){ + switch (track->getCodecId()) { case CodecH264: { + _have_video = true; _codec_to_trackid[track->getCodecId()].track_id = mpeg_ts_add_stream(_context, PSI_STREAM_H264, nullptr, 0); - } break; + } + break; case CodecH265: { + _have_video = true; _codec_to_trackid[track->getCodecId()].track_id = mpeg_ts_add_stream(_context, PSI_STREAM_H265, nullptr, 0); - }break; + } + break; case CodecAAC: { _codec_to_trackid[track->getCodecId()].track_id = mpeg_ts_add_stream(_context, PSI_STREAM_AAC, nullptr, 0); - }break; + } + break; default: break; } @@ -63,7 +68,7 @@ void TsMuxer::inputFrame(const Frame::Ptr &frame) { //mp4文件时间戳需要从0开始 auto &track_info = it->second; int64_t dts_out, pts_out; - + _is_idr_fast_packet = !_have_video; switch (frame->getCodecId()){ case CodecH265: case CodecH264: { @@ -80,6 +85,9 @@ void TsMuxer::inputFrame(const Frame::Ptr &frame) { merged.append("\x00\x00\x00\x01",4); merged.append(frame->data(),frame->size()); } + if(frame->keyFrame()){ + _is_idr_fast_packet = true; + } }); merged_frame = std::make_shared(std::move(merged)); } @@ -101,6 +109,7 @@ void TsMuxer::inputFrame(const Frame::Ptr &frame) { } void TsMuxer::resetTracks() { + _have_video = false; //通知片段中断 onTs(nullptr, 0, 0, 0); uninit(); @@ -119,7 +128,8 @@ void TsMuxer::init() { }, [](void* param, const void* packet, size_t bytes){ TsMuxer *muxer = (TsMuxer *)param; - muxer->onTs(packet, bytes,muxer->_timestamp,0); + muxer->onTs(packet, bytes,muxer->_timestamp,muxer->_is_idr_fast_packet); + muxer->_is_idr_fast_packet = false; } }; if(_context == nullptr){ diff --git a/src/Record/TsMuxer.h b/src/Record/TsMuxer.h index 26b262cc..b484a864 100644 --- a/src/Record/TsMuxer.h +++ b/src/Record/TsMuxer.h @@ -46,7 +46,7 @@ public: void resetTracks() override; void inputFrame(const Frame::Ptr &frame) override; protected: - virtual void onTs(const void *packet, int bytes,uint32_t timestamp,int flags) = 0; + virtual void onTs(const void *packet, int bytes,uint32_t timestamp,bool is_idr_fast_packet) = 0; private: void init(); void uninit(); @@ -61,6 +61,8 @@ private: }; unordered_map _codec_to_trackid; List _frameCached; + bool _is_idr_fast_packet = false; + bool _have_video = false; }; }//namespace mediakit From 92f69c39156447e9d0573ab372871d37b8a2c5ad Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 24 Jan 2020 22:16:28 +0800 Subject: [PATCH 244/272] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=BA=AF=E9=9F=B3?= =?UTF-8?q?=E9=A2=91=E6=B5=81=E7=9A=84=E5=BB=B6=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtmp/RtmpMediaSource.h | 8 +++++++- src/Rtsp/RtspMediaSource.h | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Rtmp/RtmpMediaSource.h b/src/Rtmp/RtmpMediaSource.h index b09bbf53..db13960e 100644 --- a/src/Rtmp/RtmpMediaSource.h +++ b/src/Rtmp/RtmpMediaSource.h @@ -129,6 +129,10 @@ public: */ void onWrite(const RtmpPacket::Ptr &pkt, bool key = true) override { lock_guard lock(_mtx); + if(pkt->typeId == MSG_VIDEO){ + //有视频,那么启用GOP缓存 + _have_video = true; + } if (pkt->isCfgFrame()) { _config_frame_map[pkt->typeId] = pkt; return; @@ -155,7 +159,8 @@ public: } } _track_stamps_map[pkt->typeId] = pkt->timeStamp; - _ring->write(pkt, pkt->isVideoKeyFrame()); + //不存在视频,为了减少缓存延时,那么关闭GOP缓存 + _ring->write(pkt, _have_video ? pkt->isVideoKeyFrame() : true); checkNoneReader(); } @@ -203,6 +208,7 @@ private: protected: int _ring_size; bool _async_emit_none_reader = false; + bool _have_video = false; mutable recursive_mutex _mtx; Ticker _reader_changed_ticker; AMFValue _metadata; diff --git a/src/Rtsp/RtspMediaSource.h b/src/Rtsp/RtspMediaSource.h index a99ee60d..48edce20 100644 --- a/src/Rtsp/RtspMediaSource.h +++ b/src/Rtsp/RtspMediaSource.h @@ -153,6 +153,7 @@ public: virtual void setSdp(const string &sdp) { _sdp = sdp; _sdp_parser.load(sdp); + _have_video = (bool)_sdp_parser.getTrack(TrackVideo); if (_ring) { regist(); } @@ -188,7 +189,8 @@ public: regist(); } } - _ring->write(rtp, keyPos); + //不存在视频,为了减少缓存延时,那么关闭GOP缓存 + _ring->write(rtp, _have_video ? keyPos : true); checkNoneReader(); } private: @@ -220,6 +222,7 @@ private: protected: int _ring_size; bool _async_emit_none_reader = false; + bool _have_video = false; Ticker _reader_changed_ticker; SdpParser _sdp_parser; string _sdp; From 6a97fcb3de08fb0b2ba573301cf0364f5792cc72 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 1 Feb 2020 22:57:48 +0800 Subject: [PATCH 245/272] =?UTF-8?q?=E5=AA=92=E4=BD=93=E6=B3=A8=E5=86=8C?= =?UTF-8?q?=E3=80=81=E9=87=8D=E7=BD=AE=E4=BA=8B=E4=BB=B6=E6=94=B9=E6=88=90?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E8=A7=A6=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/MediaSource.cpp | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/Common/MediaSource.cpp b/src/Common/MediaSource.cpp index 7ab8b574..2b2f5b35 100644 --- a/src/Common/MediaSource.cpp +++ b/src/Common/MediaSource.cpp @@ -78,14 +78,7 @@ vector MediaSource::getTracks(bool trackReady) const { void MediaSource::setTrackSource(const std::weak_ptr &track_src) { _track_source = track_src; - weak_ptr weakPtr = shared_from_this(); - EventPollerPool::Instance().getPoller()->async([weakPtr,this](){ - auto strongPtr = weakPtr.lock(); - if (!strongPtr) { - return; - } - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaResetTracks, *this); - },false); + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaResetTracks, *this); } void MediaSource::setListener(const std::weak_ptr &listener){ @@ -293,14 +286,7 @@ void MediaSource::regist() { g_mapMediaSrc[_strSchema][_strVhost][_strApp][_strId] = shared_from_this(); } InfoL << _strSchema << " " << _strVhost << " " << _strApp << " " << _strId; - weak_ptr weakPtr = shared_from_this(); - EventPollerPool::Instance().getPoller()->async([weakPtr,this](){ - auto strongPtr = weakPtr.lock(); - if (!strongPtr) { - return; - } - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaChanged, true, *this); - },false); + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaChanged, true, *this); } bool MediaSource::unregist() { //反注册该源 From e8ba94ba09b9a8150aa496108bfeae7c69270679 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 1 Feb 2020 22:58:21 +0800 Subject: [PATCH 246/272] =?UTF-8?q?=E8=BF=98=E5=8E=9F=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/FFmpegSource.cpp | 5 ----- src/Record/MP4Recorder.cpp | 8 +++----- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/server/FFmpegSource.cpp b/server/FFmpegSource.cpp index 0deb3bb2..d8a821c6 100644 --- a/server/FFmpegSource.cpp +++ b/server/FFmpegSource.cpp @@ -34,7 +34,6 @@ namespace FFmpeg { #define FFmpeg_FIELD "ffmpeg." const string kBin = FFmpeg_FIELD"bin"; const string kCmd = FFmpeg_FIELD"cmd"; -const string kSnap = FFmpeg_FIELD"snap"; const string kLog = FFmpeg_FIELD"log"; onceToken token([]() { @@ -42,12 +41,9 @@ onceToken token([]() { string ffmpeg_bin = System::execute("where ffmpeg"); //windows下先关闭FFmpeg日志(目前不支持日志重定向) mINI::Instance()[kCmd] = "%s -re -i \"%s\" -loglevel quiet -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s "; - //利用ffmpeg进行抓拍 - mINI::Instance()[kSnap] = "%s -i \"%s\" -loglevel quiet -y -f mjpeg -t 0.001 -s 720*576 %s "; #else string ffmpeg_bin = System::execute("which ffmpeg"); mINI::Instance()[kCmd] = "%s -re -i \"%s\" -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s "; - mINI::Instance()[kSnap] = "%s -i \"%s\" -y -f mjpeg -t 0.001 -s 720*576 %s "; #endif //默认ffmpeg命令路径为环境变量中路径 mINI::Instance()[kBin] = ffmpeg_bin.empty() ? "ffmpeg" : ffmpeg_bin; @@ -68,7 +64,6 @@ FFmpegSource::~FFmpegSource() { void FFmpegSource::play(const string &src_url,const string &dst_url,int timeout_ms,const onPlay &cb) { GET_CONFIG(string,ffmpeg_bin,FFmpeg::kBin); GET_CONFIG(string,ffmpeg_cmd,FFmpeg::kCmd); - GET_CONFIG(string, ffmpeg_snap, FFmpeg::kSnap); GET_CONFIG(string,ffmpeg_log,FFmpeg::kLog); _src_url = src_url; diff --git a/src/Record/MP4Recorder.cpp b/src/Record/MP4Recorder.cpp index 97cc9fac..a92f3fde 100644 --- a/src/Record/MP4Recorder.cpp +++ b/src/Record/MP4Recorder.cpp @@ -52,11 +52,10 @@ MP4Recorder::~MP4Recorder() { void MP4Recorder::createFile() { closeFile(); - auto strDate = getTimeStr("%Y-%m"); - auto strDay = getTimeStr("%Y-%m-%d"); + auto strDate = getTimeStr("%Y-%m-%d"); auto strTime = getTimeStr("%H-%M-%S"); - auto strFileTmp = _strPath + strDate + "/" + strDay + "/." + strTime + ".mp4"; - auto strFile = _strPath + strDate + "/" + strDay + "/" + strTime + ".mp4"; + auto strFileTmp = _strPath + strDate + "/." + strTime + ".mp4"; + auto strFile = _strPath + strDate + "/" + strTime + ".mp4"; /////record 业务逻辑////// _info.ui64StartedTime = ::time(NULL); @@ -67,7 +66,6 @@ void MP4Recorder::createFile() { + _info.strAppName + "/" + _info.strStreamId + "/" + strDate + "/" - + strDay + "/" + strTime + ".mp4"; try { From 51e0c7100771a81c6a40aa26ba3af46950c83cfd Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 1 Feb 2020 22:58:58 +0800 Subject: [PATCH 247/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=9B=B4=E5=A4=9A?= =?UTF-8?q?=E5=BD=95=E5=88=B6=E7=9B=B8=E5=85=B3=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/Recorder.cpp | 134 +++++++++++++++++----------------------- src/Record/Recorder.h | 22 +++++++ 2 files changed, 80 insertions(+), 76 deletions(-) diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index 39fdc989..d5710b9b 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -34,67 +34,42 @@ using namespace toolkit; namespace mediakit { -MediaSinkInterface *createHlsRecorder(const string &strVhost_tmp, const string &strApp, const string &strId, const string &customized_path) { -#if defined(ENABLE_HLS) +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); - GET_CONFIG(string, hlsPath, Hls::kFilePath); - - string strVhost = strVhost_tmp; - if (trim(strVhost).empty()) { - //如果strVhost为空,则强制为默认虚拟主机 - strVhost = DEFAULT_VHOST; + 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 File::absolutePath(mp4FilePath, recordPath); + } + default: + return ""; } - - string m3u8FilePath; - string params; - if (enableVhost) { - m3u8FilePath = strVhost + "/" + strApp + "/" + strId + "/hls.m3u8"; - params = string(VHOST_KEY) + "=" + strVhost; - } else { - m3u8FilePath = strApp + "/" + strId + "/hls.m3u8"; - } - //Here we use the customized file path. - if(!customized_path.empty()){ - m3u8FilePath = customized_path + "/hls.m3u8"; - } - m3u8FilePath = File::absolutePath(m3u8FilePath, hlsPath); - auto ret = new HlsRecorder(m3u8FilePath, params); - ret->setMediaSource(strVhost, strApp, strId); - return ret; -#else - return nullptr; -#endif //defined(ENABLE_HLS) } - -MediaSinkInterface *createMP4Recorder(const string &strVhost_tmp, const string &strApp, const string &strId, const string &customized_path) { -#if defined(ENABLE_MP4RECORD) - GET_CONFIG(bool, enableVhost, General::kEnableVhost); - GET_CONFIG(string, recordPath, Record::kFilePath); - GET_CONFIG(string, recordAppName, Record::kAppName); - - string strVhost = strVhost_tmp; - if (trim(strVhost).empty()) { - //如果strVhost为空,则强制为默认虚拟主机 - strVhost = DEFAULT_VHOST; - } - - string mp4FilePath; - if (enableVhost) { - mp4FilePath = strVhost + "/" + recordAppName + "/" + strApp + "/" + strId + "/"; - } else { - mp4FilePath = recordAppName + "/" + strApp + "/" + strId + "/"; - } - //Here we use the customized file path. - if(!customized_path.empty()){ - mp4FilePath = customized_path + "/"; - } - mp4FilePath = File::absolutePath(mp4FilePath, recordPath); - return new MP4Recorder(mp4FilePath, strVhost, strApp, strId); -#else - return nullptr; -#endif //defined(ENABLE_MP4RECORD) -} - //////////////////////////////////////////////////////////////////////////////////////// class RecorderHelper { @@ -215,9 +190,10 @@ public: return -1; } - auto recorder = MediaSinkInterface::Ptr(createRecorder(vhost, app, stream_id, customized_path)); + auto recorder = Recorder::createRecorder(type, vhost, app, stream_id, customized_path); if (!recorder) { // 创建录制器失败 + WarnL << "不支持该录制类型:" << type; return -2; } auto helper = std::make_shared(recorder, continueRecord); @@ -334,23 +310,6 @@ private: return vhost + "/" + app + "/" + stream_id; } - MediaSinkInterface *createRecorder(const string &vhost, const string &app, const string &stream_id, const string &customized_path) { - MediaSinkInterface *ret = nullptr; - switch (type) { - case Recorder::type_hls: - ret = createHlsRecorder(vhost, app, stream_id,customized_path); - break; - case Recorder::type_mp4: - ret = createMP4Recorder(vhost, app, stream_id,customized_path); - break; - default: - break; - } - if(!ret){ - WarnL << "can not create recorder of type: " << type; - } - return ret; - } /** * 有些录制类型不需要track就绪即可录制 @@ -392,6 +351,29 @@ std::shared_ptr Recorder::getRecorder(type type, const strin return nullptr; } +std::shared_ptr Recorder::createRecorder(type type, const string &vhost, const string &app, const string &stream_id, const string &customized_path){ + auto path = Recorder::getRecordPath(type, vhost, app, stream_id); + switch (type) { + case Recorder::type_hls: { +#if defined(ENABLE_HLS) + auto ret = std::make_shared(path, string(VHOST_KEY) + "=" + vhost); + ret->setMediaSource(vhost, app, stream_id); + return ret; +#endif + return nullptr; + } + + case Recorder::type_mp4: { +#if defined(ENABLE_MP4RECORD) + return std::make_shared(path, vhost, app, stream_id); +#endif + return nullptr; + } + + default: + return nullptr; + } +} int Recorder::startRecord(Recorder::type type, const string &vhost, const string &app, const string &stream_id, const string &customized_path, bool waitForRecord, bool continueRecord) { switch (type){ diff --git a/src/Record/Recorder.h b/src/Record/Recorder.h index 67aef75b..cb3d9cc6 100644 --- a/src/Record/Recorder.h +++ b/src/Record/Recorder.h @@ -53,6 +53,17 @@ public: type_mp4 = 1 } type; + /** + * 获取录制文件绝对路径 + * @param type hls还是MP4录制 + * @param vhost 虚拟主机 + * @param app 应用名 + * @param stream_id 流id + * @param customized_path 录像文件保存自定义目录,默认为空则自动生成 + * @return 录制文件绝对路径 + */ + static string getRecordPath(type type, const string &vhost, const string &app, const string &stream_id,const string &customized_path = ""); + /** * 获取录制状态 * @param type hls还是MP4录制 @@ -98,6 +109,17 @@ public: * @param stream_id 流id */ static std::shared_ptr getRecorder(type type, const string &vhost, const string &app, const string &stream_id); + + /** + * 创建录制器对象 + * @param type hls还是MP4录制 + * @param vhost 虚拟主机 + * @param app 应用名 + * @param stream_id 流id + * @param customized_path 录像文件保存自定义目录,默认为空则自动生成 + * @return 对象指针,可能为nullptr + */ + static std::shared_ptr createRecorder(type type, const string &vhost, const string &app, const string &stream_id, const string &customized_path); private: Recorder() = delete; ~Recorder() = delete; From 89e191f7fd04ace1ff3042d155b7921ee84f4810 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 1 Feb 2020 23:26:33 +0800 Subject: [PATCH 248/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=EF=BC=9AgetMp4RecordFile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 140 +++++++++++----------------------------------- 1 file changed, 32 insertions(+), 108 deletions(-) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 724328ac..e72d36ec 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -774,117 +774,41 @@ void installWebApi() { val["status"] = (int)status; }); - //获取录像列表(按月),返回天 - //http://127.0.0.1/index/api/getMp4RecordMonthly?app=live&stream=onvif&period=2020-01 - api_regist1("/index/api/getMp4RecordMonthly", [](API_ARGS1){ - CHECK_SECRET(); - CHECK_ARGS("app", "stream", "period"); + //获取录像文件夹列表或mp4文件列表 + //http://127.0.0.1/index/api/getMp4RecordFile?vhost=__defaultVhost__&app=live&stream=ss&period=2020-01 + api_regist1("/index/api/getMp4RecordFile", [](API_ARGS1){ + CHECK_SECRET(); + CHECK_ARGS("vhost", "app", "stream"); + auto record_path = Recorder::getRecordPath(Recorder::type_mp4, allArgs["vhost"], allArgs["app"],allArgs["stream"]); + auto period = allArgs["period"]; - GET_CONFIG(string, recordAppName, Record::kAppName); - GET_CONFIG(string, recordPath, Record::kFilePath); - GET_CONFIG(bool, enableVhost, General::kEnableVhost); + //判断是获取mp4文件列表还是获取文件夹列表 + bool search_mp4 = period.size() == sizeof("2020-02-01") - 1; + if (search_mp4) { + record_path = record_path + period + "/"; + } - auto _vhost = allArgs["vhost"]; - auto _app = allArgs["app"]; - auto _stream = allArgs["stream"]; - auto _period = allArgs["period"]; + Json::Value paths(arrayValue); + //这是筛选日期,获取文件夹列表 + File::scanDir(record_path, [&](const string &path, bool isDir) { + int pos = path.rfind('/'); + if (pos != string::npos) { + string relative_path = path.substr(pos + 1); + if (search_mp4) { + if (!isDir) { + //我们只收集mp4文件,对文件夹不感兴趣 + paths.append(relative_path); + } + } else if (isDir && relative_path.find(period) == 0) { + //匹配到对应日期的文件夹 + paths.append(relative_path); + } + } + return true; + }, false); - //TODO:判断日期格式 - - //Vhost为空的话,存储路径不一样 - if (_vhost.empty()) { - _vhost = DEFAULT_VHOST; - } - - string strMp4RecordPath = ""; - if (enableVhost) { - strMp4RecordPath = recordPath + "/" + _vhost + "/" + recordAppName + "/" + _app + "/" + _stream + "/" + _period; - } else { - strMp4RecordPath = recordPath + "/" + recordAppName + "/" + _app + "/" + _stream + "/" + _period; - } - - //录像相对路径转绝对路径, 不然vs调试需要设置 - strMp4RecordPath = File::absolutePath(strMp4RecordPath, ""); - string strMp4Path = ""; - File::scanDir(strMp4RecordPath, [&strMp4Path](const string &path, bool isDir) { - if (isDir) { - //DebugL << "dir:" << path.data(); - int iPost = path.rfind('/'); - if (iPost != string::npos) - { - string strPath = path.substr(iPost + 1); - strMp4Path += strPath + ","; - } - } - return true; - },false); - - //去掉strMp4Path最后一个 ‘,’ - strMp4Path = strMp4Path.substr(0, strMp4Path.length() - 1); - - Json::Value nVal; - nVal["vhost"] = _vhost.data(); - nVal["app"] = _app.data(); - nVal["stream"] = _stream.data(); - nVal["app"] = _app.data(); - nVal["path"] = strMp4Path; - - val["data"] = nVal; - }); - - //获取录像列表(按天) - //http://127.0.0.1/index/api/getMp4RecordDaily?app=live&stream=onvif&period=2020-01-17 - api_regist1("/index/api/getMp4RecordDaily",[](API_ARGS1) { - CHECK_SECRET(); - CHECK_ARGS("app", "stream", "period"); - - GET_CONFIG(string, recordAppName, Record::kAppName); - GET_CONFIG(string, recordPath, Record::kFilePath); - GET_CONFIG(bool, enableVhost, General::kEnableVhost); - - auto _vhost = allArgs["vhost"]; - auto _app = allArgs["app"]; - auto _stream = allArgs["stream"]; - auto _period = allArgs["period"]; - - //TODO:判断日期格式 - - //Vhost为空的话,存储路径不一样 - if (_vhost.empty()) { - _vhost = DEFAULT_VHOST; - } - - string strMp4RecordPath = ""; - //取录上一级目录,默认前7位位上级目录 - string strParentPeriod = string(_period).substr(0, 7); - if (enableVhost) { - strMp4RecordPath = recordPath + "/" + _vhost + "/" + recordAppName + "/" + _app + "/" + _stream + "/" + strParentPeriod + "/" + _period; - } - else { - strMp4RecordPath = recordPath + "/" + recordAppName + "/" + _app + "/" + _stream + "/" + strParentPeriod + "/" + _period; - } - - //录像相对路径转绝对路径, 不然vs调试需要设置 - strMp4RecordPath = File::absolutePath(strMp4RecordPath, ""); - Json::Value nVal; - DebugL << recordPath; - File::scanDir(strMp4RecordPath, [&](const string &path, bool isDir) { - if (!isDir) { - //去掉绝对路径,从record开始返回 - int iPos = path.find(recordAppName); - string strPath = path.substr(iPos); - - nVal["path"].append(strPath); - } - return true; - }, false); - - nVal["vhost"] = _vhost.data(); - nVal["app"] = _app.data(); - nVal["stream"] = _stream.data(); - nVal["app"] = _app.data(); - - val["data"] = nVal; + val["data"]["rootPath"] = record_path; + val["data"]["paths"] = paths; }); ////////////以下是注册的Hook API//////////// From ead8b5a89c0cde7e56dd39ebc1b7b9f6ffa00622 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 5 Feb 2020 23:34:51 +0800 Subject: [PATCH 249/272] =?UTF-8?q?ios=E9=BB=98=E8=AE=A4=E5=8F=AA=E7=94=9F?= =?UTF-8?q?=E6=88=90c=E9=9D=99=E6=80=81=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 21 ++++++++++++++------- README_en.md | 8 -------- api/CMakeLists.txt | 42 +++++++++++++++++++++++------------------- build_for_ios.sh | 13 ------------- 4 files changed, 37 insertions(+), 47 deletions(-) delete mode 100755 build_for_ios.sh diff --git a/README.md b/README.md index c62cf3c8..c5fc66ee 100644 --- a/README.md +++ b/README.md @@ -200,13 +200,7 @@ git submodule update --init ## 编译(iOS) - 编译环境:`请参考macOS的编译指导。` -- 编译 - - ``` - cd ZLMediaKit - ./build_for_ios.sh - ``` -- 你也可以生成Xcode工程再编译,[了解更多](https://github.com/leetal/ios-cmake): +- 生成Xcode工程再编译,[了解更多](https://github.com/leetal/ios-cmake): ``` cd ZLMediaKit @@ -243,6 +237,19 @@ git submodule update --init   4 选择编译Release 版本. 5 找到目标文件并运行测试用例. ``` + +## Docker Image +You can pull a pre-built docker image from Docker Hub and run with +```bash +docker run -id -p 1935:1935 -p 8080:80 gemfield/zlmediakit +``` + +Dockerfile is also supplied to build images on Ubuntu 16.04 +```bash +cd docker +docker build -t zlmediakit . +``` + ## 使用方法 - 作为服务器: ```cpp diff --git a/README_en.md b/README_en.md index 20d908e6..d9947c01 100644 --- a/README_en.md +++ b/README_en.md @@ -176,14 +176,6 @@ git submodule update --init ``` ### Build on iOS - - This build method is no longer recommended.It is recommended that make Xcode project by yourself. - -- My environment - - Same with Build on macOS - - - You can generate Xcode projects and recompile them , [learn more](https://github.com/leetal/ios-cmake): ``` diff --git a/api/CMakeLists.txt b/api/CMakeLists.txt index ff5db73e..9cdb0d83 100644 --- a/api/CMakeLists.txt +++ b/api/CMakeLists.txt @@ -1,24 +1,28 @@ include_directories(include source) + file(GLOB api_src_list include/*.h source/*.cpp source/*.h source/*.c) -add_library(mk_api SHARED ${api_src_list}) +if (IOS) + add_library(mk_api STATIC ${api_src_list}) + target_link_libraries(mk_api ${LINK_LIB_LIST}) +else () + add_library(mk_api SHARED ${api_src_list}) + if (WIN32) + add_definitions(-DMediaKitApi_EXPORTS) + endif () -if(WIN32) - add_definitions(-DMediaKitApi_EXPORTS) -endif() + target_link_libraries(mk_api ${LINK_LIB_LIST}) + add_subdirectory(tests) -target_link_libraries(mk_api ${LINK_LIB_LIST}) -add_subdirectory(tests) - -#安装目录 -if(WIN32) - set(INSTALL_PATH_LIB $ENV{HOME}/${CMAKE_PROJECT_NAME}/lib) - set(INSTALL_PATH_INCLUDE $ENV{HOME}/${CMAKE_PROJECT_NAME}/include) -else() - set(INSTALL_PATH_LIB lib) - set(INSTALL_PATH_INCLUDE include) -endif() - -file(GLOB api_header_list include/*.h) -install(FILES ${api_header_list} DESTINATION ${INSTALL_PATH_INCLUDE}) -install(TARGETS mk_api ARCHIVE DESTINATION ${INSTALL_PATH_LIB} LIBRARY DESTINATION ${INSTALL_PATH_LIB}) + #安装目录 + if (WIN32) + set(INSTALL_PATH_LIB $ENV{HOME}/${CMAKE_PROJECT_NAME}/lib) + set(INSTALL_PATH_INCLUDE $ENV{HOME}/${CMAKE_PROJECT_NAME}/include) + else () + set(INSTALL_PATH_LIB lib) + set(INSTALL_PATH_INCLUDE include) + endif () + file(GLOB api_header_list include/*.h) + install(FILES ${api_header_list} DESTINATION ${INSTALL_PATH_INCLUDE}) + install(TARGETS mk_api ARCHIVE DESTINATION ${INSTALL_PATH_LIB} LIBRARY DESTINATION ${INSTALL_PATH_LIB}) +endif () \ No newline at end of file diff --git a/build_for_ios.sh b/build_for_ios.sh deleted file mode 100755 index c7bf929b..00000000 --- a/build_for_ios.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -cd .. -git clone --depth=1 https://github.com/xiongziliang/ZLMediaKit.git -cd ZLMediaKit -git submodule init -git submodule update - -mkdir -p ios_build -rm -rf ./build -ln -s ./ios_build build -cd ios_build -cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/iOS.cmake -DIOS_PLATFORM=OS -make -j4 From 7e25537957185e2f350ee8029d41800617a81f2b Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 13 Feb 2020 11:33:59 +0800 Subject: [PATCH 250/272] =?UTF-8?q?=E6=B5=81=E9=87=8F=E6=B1=87=E6=8A=A5?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E6=B7=BB=E5=8A=A0=E5=AE=A2=E6=88=B7=E7=AB=AF?= =?UTF-8?q?ip=E5=92=8C=E7=AB=AF=E5=8F=A3=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mk_events.h | 6 +++++- api/source/mk_events.cpp | 4 +++- api/tests/server.c | 10 +++++++--- server/WebHook.cpp | 4 +++- src/Common/config.h | 2 +- src/Http/HttpFileManager.cpp | 7 +++++-- src/Http/HttpSession.cpp | 2 +- src/Record/HlsMediaSource.cpp | 6 ++++-- src/Record/HlsMediaSource.h | 4 +++- src/Rtmp/RtmpSession.cpp | 2 +- src/Rtsp/RtspSession.cpp | 2 +- 11 files changed, 34 insertions(+), 15 deletions(-) diff --git a/api/include/mk_events.h b/api/include/mk_events.h index c9373af3..3c7bd4f7 100644 --- a/api/include/mk_events.h +++ b/api/include/mk_events.h @@ -162,11 +162,15 @@ typedef struct { * @param total_bytes 耗费上下行总流量,单位字节数 * @param total_seconds 本次tcp会话时长,单位秒 * @param is_player 客户端是否为播放器 + * @param peer_ip 客户端ip + * @param peer_port 客户端端口号 */ void (API_CALL *on_mk_flow_report)(const mk_media_info url_info, uint64_t total_bytes, uint64_t total_seconds, - int is_player); + int is_player, + const char *peer_ip, + uint16_t peer_port); } mk_events; diff --git a/api/source/mk_events.cpp b/api/source/mk_events.cpp index 5e693587..90499f86 100644 --- a/api/source/mk_events.cpp +++ b/api/source/mk_events.cpp @@ -151,7 +151,9 @@ API_EXPORT void API_CALL mk_events_listen(const mk_events *events){ s_events.on_mk_flow_report((mk_media_info) &args, totalBytes, totalDuration, - isPlayer); + isPlayer, + peerIP.c_str(), + peerPort); } }); diff --git a/api/tests/server.c b/api/tests/server.c index 85714d50..af203e90 100644 --- a/api/tests/server.c +++ b/api/tests/server.c @@ -372,19 +372,23 @@ void API_CALL on_mk_shell_login(const char *user_name, * @param total_bytes 耗费上下行总流量,单位字节数 * @param total_seconds 本次tcp会话时长,单位秒 * @param is_player 客户端是否为播放器 + * @param peer_ip 客户端ip + * @param peer_port 客户端端口号 */ void API_CALL on_mk_flow_report(const mk_media_info url_info, uint64_t total_bytes, uint64_t total_seconds, - int is_player) { + int is_player, + const char *peer_ip, + uint16_t peer_port) { log_printf(LOG_LEV,"%s/%s/%s/%s, url params: %s," - "total_bytes: %d, total_seconds: %d, is_player: %d", + "total_bytes: %d, total_seconds: %d, is_player: %d, peer_ip:%s, peer_port:%d", mk_media_info_get_schema(url_info), mk_media_info_get_vhost(url_info), mk_media_info_get_app(url_info), mk_media_info_get_stream(url_info), mk_media_info_get_params(url_info), - (int)total_bytes, (int)total_seconds, (int)is_player); + (int)total_bytes, (int)total_seconds, (int)is_player,peer_ip, (int)peer_port); } static int flag = 1; diff --git a/server/WebHook.cpp b/server/WebHook.cpp index e2e7824e..5a5d3720 100644 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -269,13 +269,15 @@ void installWebHook(){ }); NoticeCenter::Instance().addListener(nullptr,Broadcast::kBroadcastFlowReport,[](BroadcastFlowReportArgs){ - if(!hook_enable || args._param_strs == hook_adminparams || hook_flowreport.empty()){ + if(!hook_enable || args._param_strs == hook_adminparams || hook_flowreport.empty() || peerIP == "127.0.0.1"){ return; } auto body = make_json(args); body["totalBytes"] = (Json::UInt64)totalBytes; body["duration"] = (Json::UInt64)totalDuration; body["player"] = isPlayer; + body["ip"] = peerIP; + body["port"] = peerPort; //执行hook do_http_hook(hook_flowreport,body, nullptr); }); diff --git a/src/Common/config.h b/src/Common/config.h index d8d51e35..32fb7b84 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -127,7 +127,7 @@ extern const string kBroadcastShellLogin; //停止rtsp/rtmp/http-flv会话后流量汇报事件广播 extern const string kBroadcastFlowReport; -#define BroadcastFlowReportArgs const MediaInfo &args,const uint64_t &totalBytes,const uint64_t &totalDuration,const bool &isPlayer +#define BroadcastFlowReportArgs const MediaInfo &args,const uint64_t &totalBytes,const uint64_t &totalDuration,const bool &isPlayer, const string &peerIP,const uint16_t &peerPort //未找到流后会广播该事件,请在监听该事件后去拉流或其他方式产生流,这样就能按需拉流了 extern const string kBroadcastNotFoundStream; diff --git a/src/Http/HttpFileManager.cpp b/src/Http/HttpFileManager.cpp index 2ffc0819..ce0960ac 100644 --- a/src/Http/HttpFileManager.cpp +++ b/src/Http/HttpFileManager.cpp @@ -378,8 +378,11 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI } bool is_hls = mediaInfo._schema == HLS_SCHEMA; + string peer_ip = sender.get_peer_ip(); + uint16_t peer_port = sender.get_peer_port(); + //该用户从来未获取过cookie,这个时候我们广播是否允许该用户访问该http目录 - HttpSession::HttpAccessPathInvoker accessPathInvoker = [callback, uid, path, is_dir, is_hls, mediaInfo] + HttpSession::HttpAccessPathInvoker accessPathInvoker = [callback, uid, path, is_dir, is_hls, mediaInfo, peer_ip, peer_port] (const string &errMsg, const string &cookie_path_in, int cookieLifeSecond) { HttpServerCookie::Ptr cookie; if (cookieLifeSecond) { @@ -402,7 +405,7 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI attachment._is_hls = is_hls; if(is_hls){ //hls相关信息 - attachment._hls_data = std::make_shared(mediaInfo); + attachment._hls_data = std::make_shared(mediaInfo, peer_ip, peer_port); //hls未查找MediaSource attachment._have_find_media_source = false; } diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp index 320e7338..4280e28a 100644 --- a/src/Http/HttpSession.cpp +++ b/src/Http/HttpSession.cpp @@ -112,7 +112,7 @@ void HttpSession::onError(const SockException& err) { GET_CONFIG(uint32_t,iFlowThreshold,General::kFlowThreshold); if(_ui64TotalBytes > iFlowThreshold * 1024){ - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _mediaInfo, _ui64TotalBytes, _ticker.createdTime()/1000, true); + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _mediaInfo, _ui64TotalBytes, _ticker.createdTime()/1000, true, get_peer_ip(), get_peer_port()); } return; } diff --git a/src/Record/HlsMediaSource.cpp b/src/Record/HlsMediaSource.cpp index 1f48723a..c10cf0ca 100644 --- a/src/Record/HlsMediaSource.cpp +++ b/src/Record/HlsMediaSource.cpp @@ -28,8 +28,10 @@ namespace mediakit{ -HlsCookieData::HlsCookieData(const MediaInfo &info) { +HlsCookieData::HlsCookieData(const MediaInfo &info, const string &peer_ip, uint16_t peer_port) { _info = info; + _peer_ip = peer_ip; + _peer_port = peer_port; _added = std::make_shared(false); addReaderCount(); } @@ -61,7 +63,7 @@ HlsCookieData::~HlsCookieData() { WarnL << "HLS播放器(" << _info._vhost << "/" << _info._app << "/" << _info._streamid << ")断开,播放时间:" << duration; GET_CONFIG(uint32_t, iFlowThreshold, General::kFlowThreshold); if (_bytes > iFlowThreshold * 1024) { - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _info, _bytes, duration, true); + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _info, _bytes, duration, true, _peer_ip, _peer_port); } } } diff --git a/src/Record/HlsMediaSource.h b/src/Record/HlsMediaSource.h index df0379be..82952e21 100644 --- a/src/Record/HlsMediaSource.h +++ b/src/Record/HlsMediaSource.h @@ -92,7 +92,7 @@ private: class HlsCookieData{ public: typedef std::shared_ptr Ptr; - HlsCookieData(const MediaInfo &info); + HlsCookieData(const MediaInfo &info, const string &peer_ip, uint16_t peer_port); ~HlsCookieData(); void addByteUsage(uint64_t bytes); private: @@ -100,6 +100,8 @@ private: private: uint64_t _bytes = 0; MediaInfo _info; + string _peer_ip; + uint16_t _peer_port; std::shared_ptr _added; weak_ptr _src; Ticker _ticker; diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index 061e512a..7ef95ea1 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -55,7 +55,7 @@ void RtmpSession::onError(const SockException& err) { GET_CONFIG(uint32_t,iFlowThreshold,General::kFlowThreshold); if(_ui64TotalBytes > iFlowThreshold * 1024){ - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _mediaInfo, _ui64TotalBytes, _ticker.createdTime()/1000, isPlayer); + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _mediaInfo, _ui64TotalBytes, _ticker.createdTime()/1000, isPlayer, get_peer_ip(), get_peer_port()); } } diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index 6da40b06..19eae91b 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -106,7 +106,7 @@ void RtspSession::onError(const SockException& err) { //流量统计事件广播 GET_CONFIG(uint32_t,iFlowThreshold,General::kFlowThreshold); if(_ui64TotalBytes > iFlowThreshold * 1024){ - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _mediaInfo, _ui64TotalBytes, _ticker.createdTime()/1000, isPlayer); + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _mediaInfo, _ui64TotalBytes, _ticker.createdTime()/1000, isPlayer, get_peer_ip(), get_peer_port()); } } From a7a376b3f25cff0e864005502a5b7138d18162a3 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 13 Feb 2020 12:10:08 +0800 Subject: [PATCH 251/272] =?UTF-8?q?=E6=B5=81=E9=87=8F=E6=B1=87=E6=8A=A5?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E6=B7=BB=E5=8A=A0id=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +++ 3rdpart/ZLToolKit | 2 +- server/WebHook.cpp | 1 + src/Common/config.h | 2 +- src/Http/HttpFileManager.cpp | 5 +++-- src/Http/HttpSession.cpp | 6 ++++-- src/Record/HlsMediaSource.cpp | 12 ++++++++---- src/Record/HlsMediaSource.h | 3 ++- src/Rtmp/RtmpSession.cpp | 8 +++++--- src/Rtsp/RtspSession.cpp | 6 ++++-- 10 files changed, 32 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 3496b697..f5ec7af1 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,6 @@ /Android/.idea/ /Android/app/src/main/cpp/libs_export/ /3rdpart/media-server/.idea/ +/3rdpart/media-server/.idea/ +/build/ +/3rdpart/media-server/.idea/ diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 8eaecbd6..ca6ab8b4 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 8eaecbd6dd8bed0bee6a61168255653127a20a43 +Subproject commit ca6ab8b4c406c3c2e91ca0979c843908d1edb4bd diff --git a/server/WebHook.cpp b/server/WebHook.cpp index 5a5d3720..65933573 100644 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -278,6 +278,7 @@ void installWebHook(){ body["player"] = isPlayer; body["ip"] = peerIP; body["port"] = peerPort; + body["id"] = sessionIdentifier; //执行hook do_http_hook(hook_flowreport,body, nullptr); }); diff --git a/src/Common/config.h b/src/Common/config.h index 32fb7b84..9b9cab0c 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -127,7 +127,7 @@ extern const string kBroadcastShellLogin; //停止rtsp/rtmp/http-flv会话后流量汇报事件广播 extern const string kBroadcastFlowReport; -#define BroadcastFlowReportArgs const MediaInfo &args,const uint64_t &totalBytes,const uint64_t &totalDuration,const bool &isPlayer, const string &peerIP,const uint16_t &peerPort +#define BroadcastFlowReportArgs const MediaInfo &args,const uint64_t &totalBytes,const uint64_t &totalDuration,const bool &isPlayer, const string &sessionIdentifier, const string &peerIP,const uint16_t &peerPort //未找到流后会广播该事件,请在监听该事件后去拉流或其他方式产生流,这样就能按需拉流了 extern const string kBroadcastNotFoundStream; diff --git a/src/Http/HttpFileManager.cpp b/src/Http/HttpFileManager.cpp index ce0960ac..81cf9a8a 100644 --- a/src/Http/HttpFileManager.cpp +++ b/src/Http/HttpFileManager.cpp @@ -378,11 +378,12 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI } bool is_hls = mediaInfo._schema == HLS_SCHEMA; + string identifier = sender.getIdentifier(); string peer_ip = sender.get_peer_ip(); uint16_t peer_port = sender.get_peer_port(); //该用户从来未获取过cookie,这个时候我们广播是否允许该用户访问该http目录 - HttpSession::HttpAccessPathInvoker accessPathInvoker = [callback, uid, path, is_dir, is_hls, mediaInfo, peer_ip, peer_port] + HttpSession::HttpAccessPathInvoker accessPathInvoker = [callback, uid, path, is_dir, is_hls, mediaInfo, identifier, peer_ip, peer_port] (const string &errMsg, const string &cookie_path_in, int cookieLifeSecond) { HttpServerCookie::Ptr cookie; if (cookieLifeSecond) { @@ -405,7 +406,7 @@ static void canAccessPath(TcpSession &sender, const Parser &parser, const MediaI attachment._is_hls = is_hls; if(is_hls){ //hls相关信息 - attachment._hls_data = std::make_shared(mediaInfo, peer_ip, peer_port); + attachment._hls_data = std::make_shared(mediaInfo, identifier, peer_ip, peer_port); //hls未查找MediaSource attachment._have_find_media_source = false; } diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp index 4280e28a..d9144d11 100644 --- a/src/Http/HttpSession.cpp +++ b/src/Http/HttpSession.cpp @@ -103,16 +103,18 @@ void HttpSession::onRecv(const Buffer::Ptr &pBuf) { void HttpSession::onError(const SockException& err) { if(_is_flv_stream){ + uint64_t duration = _ticker.createdTime()/1000; //flv播放器 WarnP(this) << "FLV播放器(" << _mediaInfo._vhost << "/" << _mediaInfo._app << "/" << _mediaInfo._streamid - << ")断开:" << err.what(); + << ")断开:" << err.what() + << ",耗时(s):" << duration; GET_CONFIG(uint32_t,iFlowThreshold,General::kFlowThreshold); if(_ui64TotalBytes > iFlowThreshold * 1024){ - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _mediaInfo, _ui64TotalBytes, _ticker.createdTime()/1000, true, get_peer_ip(), get_peer_port()); + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _mediaInfo, _ui64TotalBytes, duration , true, getIdentifier(), get_peer_ip(), get_peer_port()); } return; } diff --git a/src/Record/HlsMediaSource.cpp b/src/Record/HlsMediaSource.cpp index c10cf0ca..88bc0c71 100644 --- a/src/Record/HlsMediaSource.cpp +++ b/src/Record/HlsMediaSource.cpp @@ -28,8 +28,9 @@ namespace mediakit{ -HlsCookieData::HlsCookieData(const MediaInfo &info, const string &peer_ip, uint16_t peer_port) { +HlsCookieData::HlsCookieData(const MediaInfo &info, const string &sessionIdentifier, const string &peer_ip, uint16_t peer_port) { _info = info; + _sessionIdentifier = sessionIdentifier; _peer_ip = peer_ip; _peer_port = peer_port; _added = std::make_shared(false); @@ -59,11 +60,14 @@ HlsCookieData::~HlsCookieData() { if (src) { src->modifyReaderCount(false); } - auto duration = (_ticker.createdTime() - _ticker.elapsedTime()) / 1000; - WarnL << "HLS播放器(" << _info._vhost << "/" << _info._app << "/" << _info._streamid << ")断开,播放时间:" << duration; + uint64_t duration = (_ticker.createdTime() - _ticker.elapsedTime()) / 1000; + WarnL << _sessionIdentifier << "(" << _peer_ip << ":" << _peer_port << ") " + << "HLS播放器(" << _info._vhost << "/" << _info._app << "/" << _info._streamid + << ")断开,耗时(s):" << duration; + GET_CONFIG(uint32_t, iFlowThreshold, General::kFlowThreshold); if (_bytes > iFlowThreshold * 1024) { - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _info, _bytes, duration, true, _peer_ip, _peer_port); + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _info, _bytes, duration, true, _sessionIdentifier, _peer_ip, _peer_port); } } } diff --git a/src/Record/HlsMediaSource.h b/src/Record/HlsMediaSource.h index 82952e21..8da52e2c 100644 --- a/src/Record/HlsMediaSource.h +++ b/src/Record/HlsMediaSource.h @@ -92,7 +92,7 @@ private: class HlsCookieData{ public: typedef std::shared_ptr Ptr; - HlsCookieData(const MediaInfo &info, const string &peer_ip, uint16_t peer_port); + HlsCookieData(const MediaInfo &info, const string &sessionIdentifier, const string &peer_ip, uint16_t peer_port); ~HlsCookieData(); void addByteUsage(uint64_t bytes); private: @@ -100,6 +100,7 @@ private: private: uint64_t _bytes = 0; MediaInfo _info; + string _sessionIdentifier; string _peer_ip; uint16_t _peer_port; std::shared_ptr _added; diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index 7ef95ea1..15e4342f 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -45,17 +45,19 @@ RtmpSession::~RtmpSession() { void RtmpSession::onError(const SockException& err) { bool isPlayer = !_pPublisherSrc; + uint64_t duration = _ticker.createdTime()/1000; WarnP(this) << (isPlayer ? "RTMP播放器(" : "RTMP推流器(") << _mediaInfo._vhost << "/" << _mediaInfo._app << "/" << _mediaInfo._streamid - << ")断开:" << err.what(); + << ")断开:" << err.what() + << ",耗时(s):" << duration; //流量统计事件广播 GET_CONFIG(uint32_t,iFlowThreshold,General::kFlowThreshold); if(_ui64TotalBytes > iFlowThreshold * 1024){ - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _mediaInfo, _ui64TotalBytes, _ticker.createdTime()/1000, isPlayer, get_peer_ip(), get_peer_port()); + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _mediaInfo, _ui64TotalBytes, duration, isPlayer, getIdentifier(), get_peer_ip(), get_peer_port()); } } @@ -206,7 +208,7 @@ void RtmpSession::onCmd_deleteStream(AMFDecoder &dec) { status.set("code", "NetStream.Unpublish.Success"); status.set("description", "Stop publishing."); sendReply("onStatus", nullptr, status); - throw std::runtime_error(StrPrinter << "Stop publishing." << endl); + throw std::runtime_error(StrPrinter << "Stop publishing" << endl); } diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index 19eae91b..29a62779 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -86,11 +86,13 @@ RtspSession::~RtspSession() { void RtspSession::onError(const SockException& err) { bool isPlayer = !_pushSrc; + uint64_t duration = _ticker.createdTime()/1000; WarnP(this) << (isPlayer ? "RTSP播放器(" : "RTSP推流器(") << _mediaInfo._vhost << "/" << _mediaInfo._app << "/" << _mediaInfo._streamid - << ")断开:" << err.what(); + << ")断开:" << err.what() + << ",耗时(s):" << duration; if (_rtpType == Rtsp::RTP_MULTICAST) { //取消UDP端口监听 @@ -106,7 +108,7 @@ void RtspSession::onError(const SockException& err) { //流量统计事件广播 GET_CONFIG(uint32_t,iFlowThreshold,General::kFlowThreshold); if(_ui64TotalBytes > iFlowThreshold * 1024){ - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _mediaInfo, _ui64TotalBytes, _ticker.createdTime()/1000, isPlayer, get_peer_ip(), get_peer_port()); + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport, _mediaInfo, _ui64TotalBytes, duration, isPlayer, getIdentifier(), get_peer_ip(), get_peer_port()); } } From 2ecb9c68875e1fbb7a46a2f0dc61a4f836155335 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 18 Feb 2020 11:25:39 +0800 Subject: [PATCH 252/272] =?UTF-8?q?=E6=9B=B4=E6=96=B0ZLToolKit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index ca6ab8b4..7b0d2e4a 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit ca6ab8b4c406c3c2e91ca0979c843908d1edb4bd +Subproject commit 7b0d2e4abccbab206cdcc0b4fe7dd7a2fa185ff4 From c56a0a0624894339f8dbb1eaa7f42bfea2de53e7 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 18 Feb 2020 21:13:30 +0800 Subject: [PATCH 253/272] =?UTF-8?q?=E6=9B=B4=E6=96=B0ZLToolKit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 7b0d2e4a..1f87bc42 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 7b0d2e4abccbab206cdcc0b4fe7dd7a2fa185ff4 +Subproject commit 1f87bc42900e88d5443fb63bfb82523a2ac3ea33 From 618b028da4461994c16d39a649f54bf1f8c72fa7 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 21 Feb 2020 11:35:53 +0800 Subject: [PATCH 254/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=8B=BC=E5=86=99?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mk_util.h | 8 ++++---- api/source/mk_util.cpp | 8 ++++---- api/tests/server.c | 4 ++-- api/tests/websocket.c | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/api/include/mk_util.h b/api/include/mk_util.h index 99d7084c..e87599f8 100644 --- a/api/include/mk_util.h +++ b/api/include/mk_util.h @@ -45,20 +45,20 @@ API_EXPORT char* API_CALL mk_util_get_exe_path(); * @param relative_path 同目录下文件的路径相对,可以为null * @return 文件路径,使用完后需要自己free */ -API_EXPORT char* API_CALL mk_uitl_get_exe_dir(const char *relative_path); +API_EXPORT char* API_CALL mk_util_get_exe_dir(const char *relative_path); /** * 获取unix标准的系统时间戳 * @return 当前系统时间戳 */ -API_EXPORT uint64_t API_CALL mk_uitl_get_current_millisecond(); +API_EXPORT uint64_t API_CALL mk_util_get_current_millisecond(); /** * 获取时间字符串 * @param fmt 时间格式,譬如%Y-%m-%d %H:%M:%S * @return 时间字符串,使用完后需要自己free */ -API_EXPORT char* API_CALL mk_uitl_get_current_time_string(const char *fmt); +API_EXPORT char* API_CALL mk_util_get_current_time_string(const char *fmt); /** * 打印二进制为字符串 @@ -66,7 +66,7 @@ API_EXPORT char* API_CALL mk_uitl_get_current_time_string(const char *fmt); * @param len 数据长度 * @return 可打印的调试信息,使用完后需要自己free */ -API_EXPORT char* API_CALL mk_uitl_hex_dump(const void *buf, int len); +API_EXPORT char* API_CALL mk_util_hex_dump(const void *buf, int len); ///////////////////////////////////////////日志///////////////////////////////////////////// /** diff --git a/api/source/mk_util.cpp b/api/source/mk_util.cpp index 860af2a3..97bd9158 100644 --- a/api/source/mk_util.cpp +++ b/api/source/mk_util.cpp @@ -35,23 +35,23 @@ API_EXPORT char* API_CALL mk_util_get_exe_path(){ return strdup(exePath().data()); } -API_EXPORT char* API_CALL mk_uitl_get_exe_dir(const char *relative_path){ +API_EXPORT char* API_CALL mk_util_get_exe_dir(const char *relative_path){ if(relative_path){ return strdup((exeDir() + relative_path).data()); } return strdup(exeDir().data()); } -API_EXPORT uint64_t API_CALL mk_uitl_get_current_millisecond(){ +API_EXPORT uint64_t API_CALL mk_util_get_current_millisecond(){ return getCurrentMillisecond(); } -API_EXPORT char* API_CALL mk_uitl_get_current_time_string(const char *fmt){ +API_EXPORT char* API_CALL mk_util_get_current_time_string(const char *fmt){ assert(fmt); return strdup(getTimeStr(fmt).data()); } -API_EXPORT char* API_CALL mk_uitl_hex_dump(const void *buf, int len){ +API_EXPORT char* API_CALL mk_util_hex_dump(const void *buf, int len){ assert(buf && len > 0); return strdup(hexdump(buf,len).data()); } diff --git a/api/tests/server.c b/api/tests/server.c index af203e90..575313bb 100644 --- a/api/tests/server.c +++ b/api/tests/server.c @@ -396,8 +396,8 @@ static void s_on_exit(int sig){ flag = 0; } int main(int argc, char *argv[]) { - char *ini_path = mk_uitl_get_exe_dir("c_api.ini"); - char *ssl_path = mk_uitl_get_exe_dir("ssl.p12"); + char *ini_path = mk_util_get_exe_dir("c_api.ini"); + char *ssl_path = mk_util_get_exe_dir("ssl.p12"); mk_config config = { .ini = ini_path, diff --git a/api/tests/websocket.c b/api/tests/websocket.c index 845a78d6..7a95e586 100644 --- a/api/tests/websocket.c +++ b/api/tests/websocket.c @@ -195,8 +195,8 @@ void test_client(){ } int main(int argc, char *argv[]) { - char *ini_path = mk_uitl_get_exe_dir("c_api.ini"); - char *ssl_path = mk_uitl_get_exe_dir("ssl.p12"); + char *ini_path = mk_util_get_exe_dir("c_api.ini"); + char *ssl_path = mk_util_get_exe_dir("ssl.p12"); mk_config config = { .ini = ini_path, From f76109c629dfcfe2fba48352dd25c0b28bacedb4 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 23 Feb 2020 12:16:20 +0800 Subject: [PATCH 255/272] =?UTF-8?q?=E5=85=BC=E5=AE=B9ssrc=E4=B8=BA0?= =?UTF-8?q?=E7=9A=84rtp=E6=8E=A8=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/ZLToolKit | 2 +- src/Rtp/RtpSelector.cpp | 11 ++++++----- src/Rtp/RtpSelector.h | 2 +- src/Rtp/RtpSession.cpp | 10 ++++++---- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 1f87bc42..34b42499 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 1f87bc42900e88d5443fb63bfb82523a2ac3ea33 +Subproject commit 34b42499ce9ee055b5c7cac9a270239984d0e839 diff --git a/src/Rtp/RtpSelector.cpp b/src/Rtp/RtpSelector.cpp index f57081d2..bc8e7924 100644 --- a/src/Rtp/RtpSelector.cpp +++ b/src/Rtp/RtpSelector.cpp @@ -36,8 +36,8 @@ bool RtpSelector::inputRtp(const char *data, int data_len,const struct sockaddr _last_rtp_time.resetTime(); onManager(); } - auto ssrc = getSSRC(data,data_len); - if(!ssrc){ + uint32_t ssrc = 0; + if(!getSSRC(data,data_len,ssrc)){ WarnL << "get ssrc from rtp failed:" << data_len; return false; } @@ -48,12 +48,13 @@ bool RtpSelector::inputRtp(const char *data, int data_len,const struct sockaddr return false; } -uint32_t RtpSelector::getSSRC(const char *data, int data_len) { +bool RtpSelector::getSSRC(const char *data,int data_len, uint32_t &ssrc){ if(data_len < 12){ - return 0; + return false; } uint32_t *ssrc_ptr = (uint32_t *)(data + 8); - return ntohl(*ssrc_ptr); + ssrc = ntohl(*ssrc_ptr); + return true; } RtpProcess::Ptr RtpSelector::getProcess(uint32_t ssrc,bool makeNew) { diff --git a/src/Rtp/RtpSelector.h b/src/Rtp/RtpSelector.h index bbbd9665..a701a83d 100644 --- a/src/Rtp/RtpSelector.h +++ b/src/Rtp/RtpSelector.h @@ -42,7 +42,7 @@ public: static RtpSelector &Instance(); bool inputRtp(const char *data,int data_len,const struct sockaddr *addr ,uint32_t *dts_out = nullptr ); - static uint32_t getSSRC(const char *data,int data_len); + static bool getSSRC(const char *data,int data_len, uint32_t &ssrc); RtpProcess::Ptr getProcess(uint32_t ssrc,bool makeNew); void delProcess(uint32_t ssrc,const RtpProcess *ptr); private: diff --git a/src/Rtp/RtpSession.cpp b/src/Rtp/RtpSession.cpp index c765997f..f807a85d 100644 --- a/src/Rtp/RtpSession.cpp +++ b/src/Rtp/RtpSession.cpp @@ -36,7 +36,7 @@ RtpSession::RtpSession(const Socket::Ptr &sock) : TcpSession(sock) { } RtpSession::~RtpSession() { DebugP(this); - if(_ssrc){ + if(_process){ RtpSelector::Instance().delProcess(_ssrc,_process.get()); } } @@ -66,11 +66,13 @@ void RtpSession::onManager() { } void RtpSession::onRtpPacket(const char *data, uint64_t len) { - if(!_ssrc){ - _ssrc = RtpSelector::getSSRC(data + 2,len - 2); + if (!_process) { + if (!RtpSelector::getSSRC(data + 2, len - 2, _ssrc)) { + return; + } _process = RtpSelector::Instance().getProcess(_ssrc, true); } - _process->inputRtp(data + 2,len - 2,&addr); + _process->inputRtp(data + 2, len - 2, &addr); _ticker.resetTime(); } From d60101b93ce3c53d75b53862ced025f6d36e8d37 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 25 Feb 2020 14:59:40 +0800 Subject: [PATCH 256/272] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8D=95Track?= =?UTF-8?q?=E6=83=85=E5=86=B5=E4=B8=8B=E7=AD=89=E5=BE=853=E7=A7=92?= =?UTF-8?q?=E7=9A=84=E6=83=85=E5=86=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/MediaSink.cpp | 16 +++++++++++----- src/Common/MediaSink.h | 6 ++++++ src/Player/PlayerProxy.cpp | 3 +++ src/Record/MP4Reader.cpp | 3 +++ 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/Common/MediaSink.cpp b/src/Common/MediaSink.cpp index f579f122..048f98e4 100644 --- a/src/Common/MediaSink.cpp +++ b/src/Common/MediaSink.cpp @@ -59,6 +59,7 @@ void MediaSink::resetTracks() { _track_map.clear(); _trackReadyCallback.clear(); _ticker.resetTime(); + _max_track_size = 2; } void MediaSink::inputFrame(const Frame::Ptr &frame) { @@ -82,7 +83,7 @@ void MediaSink::inputFrame(const Frame::Ptr &frame) { if(!_allTrackReady){ if(_ticker.elapsedTime() > MAX_WAIT_MS_READY){ //如果超过规定时间,那么不再等待并忽略未准备好的Track - addTrackCompleted(); + emitAllTrackReady(); return; } @@ -91,21 +92,26 @@ void MediaSink::inputFrame(const Frame::Ptr &frame) { return; } - if(_track_map.size() == 2){ + if(_track_map.size() == _max_track_size){ //如果已经添加了音视频Track,并且不存在未准备好的Track,那么说明所有Track都准备好了 - addTrackCompleted(); + emitAllTrackReady(); return; } if(_track_map.size() == 1 && _ticker.elapsedTime() > MAX_WAIT_MS_ADD_TRACK){ //如果只有一个Track,那么在该Track添加后,我们最多还等待若干时间(可能后面还会添加Track) - addTrackCompleted(); + emitAllTrackReady(); return; } } } -void MediaSink::addTrackCompleted() { +void MediaSink::addTrackCompleted(){ + lock_guard lck(_mtx); + _max_track_size = _track_map.size(); +} + +void MediaSink::emitAllTrackReady() { if (_allTrackReady) { return; } diff --git a/src/Common/MediaSink.h b/src/Common/MediaSink.h index 832bbe9c..02bf1460 100644 --- a/src/Common/MediaSink.h +++ b/src/Common/MediaSink.h @@ -116,12 +116,18 @@ protected: * @param frame */ virtual void onTrackFrame(const Frame::Ptr &frame) {}; +private: + /** + * 触发onAllTrackReady事件 + */ + void emitAllTrackReady(); private: mutable recursive_mutex _mtx; map _track_map; map > _trackReadyCallback; bool _allTrackReady = false; Ticker _ticker; + int _max_track_size = 2; }; diff --git a/src/Player/PlayerProxy.cpp b/src/Player/PlayerProxy.cpp index 6b5fca6e..1ba1d1b7 100644 --- a/src/Player/PlayerProxy.cpp +++ b/src/Player/PlayerProxy.cpp @@ -291,6 +291,9 @@ void PlayerProxy::onPlaySuccess() { audioMaker->addDelegate(_mediaMuxer); } + //添加完毕所有track,防止单track情况下最大等待3秒 + _mediaMuxer->addTrackCompleted(); + if(_pMediaSrc){ _pMediaSrc->setTrackSource(_mediaMuxer); } diff --git a/src/Record/MP4Reader.cpp b/src/Record/MP4Reader.cpp index 020fa6bc..3fd552b4 100644 --- a/src/Record/MP4Reader.cpp +++ b/src/Record/MP4Reader.cpp @@ -151,6 +151,9 @@ MP4Reader::MP4Reader(const string &strVhost,const string &strApp, const string & H264Track::Ptr track = std::make_shared(_strSps,_strPps); _mediaMuxer->addTrack(track); } + + //添加完毕所有track,防止单track情况下最大等待3秒 + _mediaMuxer->addTrackCompleted(); } From 86b053db3b4597328b2a36835c8da394ff146ccb Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 25 Feb 2020 18:53:11 +0800 Subject: [PATCH 257/272] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtp/RtpProcess.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Rtp/RtpProcess.cpp b/src/Rtp/RtpProcess.cpp index d26e2e56..2d9c5471 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -190,8 +190,8 @@ void RtpProcess::onPSDecode(int stream, int bytes) { pts /= 90; dts /= 90; - _dts = dts; _stamps[codecid].revise(dts,pts,dts,pts,false); + _dts = dts; switch (codecid) { case STREAM_VIDEO_H264: { From a0001bf2f47581afc8743e4e29bef3ba729b3b14 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 25 Feb 2020 19:00:22 +0800 Subject: [PATCH 258/272] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E5=8F=8B=E5=A5=BD=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtp/RtpProcess.cpp | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/Rtp/RtpProcess.cpp b/src/Rtp/RtpProcess.cpp index 2d9c5471..aacd1352 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -181,6 +181,25 @@ void RtpProcess::onRtpDecode(const void *packet, int bytes, uint32_t, int flags) } } +#define SWITCH_CASE(codec_id) case codec_id : return #codec_id +static const char *getCodecName(int codec_id) { + switch (codec_id) { + SWITCH_CASE(STREAM_VIDEO_MPEG4); + SWITCH_CASE(STREAM_VIDEO_H264); + SWITCH_CASE(STREAM_VIDEO_H265); + SWITCH_CASE(STREAM_VIDEO_SVAC); + SWITCH_CASE(STREAM_AUDIO_MP3); + SWITCH_CASE(STREAM_AUDIO_AAC); + SWITCH_CASE(STREAM_AUDIO_G711); + SWITCH_CASE(STREAM_AUDIO_G722); + SWITCH_CASE(STREAM_AUDIO_G723); + SWITCH_CASE(STREAM_AUDIO_G729); + SWITCH_CASE(STREAM_AUDIO_SVAC); + default: + return "unknown codec"; + } +} + void RtpProcess::onPSDecode(int stream, int codecid, int flags, @@ -204,7 +223,7 @@ void RtpProcess::onPSDecode(int stream, } if (codecid != _codecid_video) { - WarnL << "video track change to H264 from codecid:" << _codecid_video; + WarnL << "video track change to H264 from codecid:" << getCodecName(_codecid_video); return; } @@ -227,7 +246,7 @@ void RtpProcess::onPSDecode(int stream, _muxer->addTrack(track); } if (codecid != _codecid_video) { - WarnL << "video track change to H265 from codecid:" << _codecid_video; + WarnL << "video track change to H265 from codecid:" << getCodecName(_codecid_video); return; } if(_save_file_video){ @@ -250,14 +269,16 @@ void RtpProcess::onPSDecode(int stream, } if (codecid != _codecid_audio) { - WarnL << "audio track change to AAC from codecid:" << _codecid_audio; + WarnL << "audio track change to AAC from codecid:" << getCodecName(_codecid_audio); return; } _muxer->inputFrame(std::make_shared((char *) data, bytes, dts, 7)); break; } default: - WarnL << "unsupported codec type:" << codecid; + if(codecid != 0){ + WarnL << "unsupported codec type:" << getCodecName(codecid); + } return; } } From 38584a7478c16a9d59aeb3cf694ab95ab4387184 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 27 Feb 2020 11:40:38 +0800 Subject: [PATCH 259/272] =?UTF-8?q?=E6=B7=BB=E5=8A=A0mk=5Fmedia=5Finit=5Fc?= =?UTF-8?q?omplete=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mk_media.h | 8 ++++++++ api/source/mk_media.cpp | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/api/include/mk_media.h b/api/include/mk_media.h index 16516de4..e2eeba8c 100755 --- a/api/include/mk_media.h +++ b/api/include/mk_media.h @@ -81,6 +81,14 @@ API_EXPORT void API_CALL mk_media_init_h265(mk_media ctx, int width, int height, */ API_EXPORT void API_CALL mk_media_init_aac(mk_media ctx, int channel, int sample_bit, int sample_rate, int profile); +/** + * 初始化h264/h265/aac完毕后调用此函数, + * 在单track(只有音频或视频)时,因为ZLMediaKit不知道后续是否还要添加track,所以会多等待3秒钟 + * 如果产生的流是单Track类型,请调用此函数以便加快流生成速度,当然不调用该函数,影响也不大(会多等待3秒) + * @param ctx 对象指针 + */ +API_EXPORT void API_CALL mk_media_init_complete(mk_media ctx); + /** * 输入单帧H264视频,帧起始字节00 00 01,00 00 00 01均可 * @param ctx 对象指针 diff --git a/api/source/mk_media.cpp b/api/source/mk_media.cpp index 1751005b..a09525fd 100755 --- a/api/source/mk_media.cpp +++ b/api/source/mk_media.cpp @@ -75,6 +75,12 @@ API_EXPORT void API_CALL mk_media_init_aac(mk_media ctx, int channel, int sample (*obj)->initAudio(info); } +API_EXPORT void API_CALL mk_media_init_complete(mk_media ctx){ + assert(ctx); + DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; + (*obj)->addTrackCompleted(); +} + API_EXPORT void API_CALL mk_media_input_h264(mk_media ctx, void *data, int len, uint32_t dts, uint32_t pts) { assert(ctx && data && len > 0); DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; From e6eee321eae7c94b2807cc87da5536b823178fc2 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 27 Feb 2020 12:31:57 +0800 Subject: [PATCH 260/272] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=B8=B4=E7=95=8C?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E4=B8=A2=E8=B5=B7=E5=A7=8B=E5=B8=A7=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/MediaSink.cpp | 35 +++++++++++++++++++++++++---------- src/Common/MediaSink.h | 6 ++++++ 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/Common/MediaSink.cpp b/src/Common/MediaSink.cpp index 048f98e4..9d224ac2 100644 --- a/src/Common/MediaSink.cpp +++ b/src/Common/MediaSink.cpp @@ -64,19 +64,31 @@ void MediaSink::resetTracks() { void MediaSink::inputFrame(const Frame::Ptr &frame) { lock_guard lck(_mtx); - auto codec_id = frame->getCodecId(); - auto it = _track_map.find(codec_id); + auto it = _track_map.find(frame->getCodecId()); if (it == _track_map.end()) { return; } it->second->inputFrame(frame); + checkTrackIfReady(it->second); +} - if(!_allTrackReady && !_trackReadyCallback.empty() && it->second->ready()){ - //Track由未就绪状态转换成就绪状态,我们就触发onTrackReady回调 - auto it_callback = _trackReadyCallback.find(codec_id); - if(it_callback != _trackReadyCallback.end()){ - it_callback->second(); - _trackReadyCallback.erase(it_callback); +void MediaSink::checkTrackIfReady_l(const Track::Ptr &track){ + //Track由未就绪状态转换成就绪状态,我们就触发onTrackReady回调 + auto it_callback = _trackReadyCallback.find(track->getCodecId()); + if (it_callback != _trackReadyCallback.end() && track->ready()) { + it_callback->second(); + _trackReadyCallback.erase(it_callback); + } +} + +void MediaSink::checkTrackIfReady(const Track::Ptr &track){ + if (!_allTrackReady && !_trackReadyCallback.empty()) { + if (track) { + checkTrackIfReady_l(track); + } else { + for (auto &pr : _track_map) { + checkTrackIfReady_l(pr.second); + } } } @@ -107,8 +119,11 @@ void MediaSink::inputFrame(const Frame::Ptr &frame) { } void MediaSink::addTrackCompleted(){ - lock_guard lck(_mtx); - _max_track_size = _track_map.size(); + { + lock_guard lck(_mtx); + _max_track_size = _track_map.size(); + } + checkTrackIfReady(nullptr); } void MediaSink::emitAllTrackReady() { diff --git a/src/Common/MediaSink.h b/src/Common/MediaSink.h index 02bf1460..c948aaa1 100644 --- a/src/Common/MediaSink.h +++ b/src/Common/MediaSink.h @@ -121,6 +121,12 @@ private: * 触发onAllTrackReady事件 */ void emitAllTrackReady(); + + /** + * 检查track是否准备完毕 + */ + void checkTrackIfReady(const Track::Ptr &track); + void checkTrackIfReady_l(const Track::Ptr &track); private: mutable recursive_mutex _mtx; map _track_map; From 5acdf1f789774bb79ee52e8d2bf09fa24a8ed66e Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 27 Feb 2020 15:48:52 +0800 Subject: [PATCH 261/272] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mk_player.h | 1 + 1 file changed, 1 insertion(+) diff --git a/api/include/mk_player.h b/api/include/mk_player.h index 14092cd7..13ac2bbc 100755 --- a/api/include/mk_player.h +++ b/api/include/mk_player.h @@ -114,6 +114,7 @@ API_EXPORT void API_CALL mk_player_set_on_shutdown(mk_player ctx, on_mk_play_eve /** * 设置音视频数据回调函数 + * 该接口只能在播放成功事件触发后才能调用 * @param ctx 播放器指针 * @param cb 回调函数指针,不得为null * @param user_data 用户数据指针 From 109fab2cb14c4fd2d4aff2d6954960d4e599f5c9 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 28 Feb 2020 16:25:14 +0800 Subject: [PATCH 262/272] =?UTF-8?q?RTP=E4=BB=A3=E7=90=86=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=AE=8C=E6=95=B4=E7=9A=84=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtp/RtpProcess.cpp | 8 +++++++ src/Rtp/RtpProcess.h | 3 +++ src/Rtp/RtpSelector.cpp | 53 +++++++++++++++++++++++++++++++++++++---- src/Rtp/RtpSelector.h | 24 ++++++++++++++++++- src/Rtp/RtpSession.cpp | 24 +++++++++++++++++++ src/Rtp/RtpSession.h | 10 ++++++-- 6 files changed, 114 insertions(+), 8 deletions(-) diff --git a/src/Rtp/RtpProcess.cpp b/src/Rtp/RtpProcess.cpp index aacd1352..85e68656 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -299,5 +299,13 @@ uint16_t RtpProcess::get_peer_port() { return ntohs(((struct sockaddr_in *) _addr)->sin_port); } +int RtpProcess::totalReaderCount(){ + return _muxer->totalReaderCount(); +} + +void RtpProcess::setListener(const std::weak_ptr &listener){ + _muxer->setListener(listener); +} + }//namespace mediakit #endif//defined(ENABLE_RTPPROXY) \ No newline at end of file diff --git a/src/Rtp/RtpProcess.h b/src/Rtp/RtpProcess.h index 7053956e..c9fb7397 100644 --- a/src/Rtp/RtpProcess.h +++ b/src/Rtp/RtpProcess.h @@ -49,6 +49,9 @@ public: bool alive(); string get_peer_ip(); uint16_t get_peer_port(); + + int totalReaderCount(); + void setListener(const std::weak_ptr &listener); protected: void onRtpSorted(const RtpPacket::Ptr &rtp, int track_index) override ; void onRtpDecode(const void *packet, int bytes, uint32_t timestamp, int flags) override; diff --git a/src/Rtp/RtpSelector.cpp b/src/Rtp/RtpSelector.cpp index bc8e7924..e30d4af3 100644 --- a/src/Rtp/RtpSelector.cpp +++ b/src/Rtp/RtpSelector.cpp @@ -63,11 +63,12 @@ RtpProcess::Ptr RtpSelector::getProcess(uint32_t ssrc,bool makeNew) { if(it == _map_rtp_process.end() && !makeNew){ return nullptr; } - RtpProcess::Ptr &ref = _map_rtp_process[ssrc]; + RtpProcessHelper::Ptr &ref = _map_rtp_process[ssrc]; if(!ref){ - ref = std::make_shared(ssrc); + ref = std::make_shared(ssrc,shared_from_this()); + ref->attachEvent(); } - return ref; + return ref->getProcess(); } void RtpSelector::delProcess(uint32_t ssrc,const RtpProcess *ptr) { @@ -77,7 +78,7 @@ void RtpSelector::delProcess(uint32_t ssrc,const RtpProcess *ptr) { return; } - if(it->second.get() != ptr){ + if(it->second->getProcess().get() != ptr){ return; } @@ -87,7 +88,7 @@ void RtpSelector::delProcess(uint32_t ssrc,const RtpProcess *ptr) { void RtpSelector::onManager() { lock_guard lck(_mtx_map); for (auto it = _map_rtp_process.begin(); it != _map_rtp_process.end();) { - if (it->second->alive()) { + if (it->second->getProcess()->alive()) { ++it; continue; } @@ -102,5 +103,47 @@ RtpSelector::RtpSelector() { RtpSelector::~RtpSelector() { } +RtpProcessHelper::RtpProcessHelper(uint32_t ssrc, const weak_ptr &parent) { + _ssrc = ssrc; + _parent = parent; + _process = std::make_shared(_ssrc); +} + +RtpProcessHelper::~RtpProcessHelper() { +} + +void RtpProcessHelper::attachEvent() { + _process->setListener(shared_from_this()); +} + +bool RtpProcessHelper::close(MediaSource &sender, bool force) { + //此回调在其他线程触发 + if(!_process || (!force && _process->totalReaderCount())){ + return false; + } + auto parent = _parent.lock(); + if(!parent){ + return false; + } + parent->delProcess(_ssrc,_process.get()); + string err = StrPrinter << "close media:" << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId() << " " << force; + return true; +} + +void RtpProcessHelper::onNoneReader(MediaSource &sender) { + if(!_process || _process->totalReaderCount()){ + return; + } + MediaSourceEvent::onNoneReader(sender); +} + +int RtpProcessHelper::totalReaderCount(MediaSource &sender) { + return _process ? _process->totalReaderCount() : sender.totalReaderCount(); +} + +RtpProcess::Ptr &RtpProcessHelper::getProcess() { + return _process; +} + }//namespace mediakit #endif//defined(ENABLE_RTPPROXY) \ No newline at end of file diff --git a/src/Rtp/RtpSelector.h b/src/Rtp/RtpSelector.h index a701a83d..f1b4b97f 100644 --- a/src/Rtp/RtpSelector.h +++ b/src/Rtp/RtpSelector.h @@ -32,9 +32,31 @@ #include #include #include "RtpProcess.h" +#include "Common/MediaSource.h" namespace mediakit{ +class RtpSelector; +class RtpProcessHelper : public MediaSourceEvent , public std::enable_shared_from_this { +public: + typedef std::shared_ptr Ptr; + RtpProcessHelper(uint32_t ssrc,const weak_ptr &parent); + ~RtpProcessHelper(); + void attachEvent(); + RtpProcess::Ptr & getProcess(); +protected: + // 通知其停止推流 + bool close(MediaSource &sender,bool force) override; + // 通知无人观看 + void onNoneReader(MediaSource &sender) override; + // 观看总人数 + int totalReaderCount(MediaSource &sender) override; +private: + weak_ptr _parent; + RtpProcess::Ptr _process; + uint32_t _ssrc = 0; +}; + class RtpSelector : public std::enable_shared_from_this{ public: RtpSelector(); @@ -48,7 +70,7 @@ public: private: void onManager(); private: - unordered_map _map_rtp_process; + unordered_map _map_rtp_process; recursive_mutex _mtx_map; Ticker _last_rtp_time; }; diff --git a/src/Rtp/RtpSession.cpp b/src/Rtp/RtpSession.cpp index f807a85d..e638b88e 100644 --- a/src/Rtp/RtpSession.cpp +++ b/src/Rtp/RtpSession.cpp @@ -71,10 +71,34 @@ void RtpSession::onRtpPacket(const char *data, uint64_t len) { return; } _process = RtpSelector::Instance().getProcess(_ssrc, true); + _process->setListener(dynamic_pointer_cast(shared_from_this())); } _process->inputRtp(data + 2, len - 2, &addr); _ticker.resetTime(); } +bool RtpSession::close(MediaSource &sender, bool force) { + //此回调在其他线程触发 + if(!_process || (!force && _process->totalReaderCount())){ + return false; + } + string err = StrPrinter << "close media:" << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId() << " " << force; + safeShutdown(SockException(Err_shutdown,err)); + return true; +} + +void RtpSession::onNoneReader(MediaSource &sender) { + //此回调在其他线程触发 + if(!_process || _process->totalReaderCount()){ + return; + } + MediaSourceEvent::onNoneReader(sender); +} + +int RtpSession::totalReaderCount(MediaSource &sender) { + //此回调在其他线程触发 + return _process ? _process->totalReaderCount() : sender.totalReaderCount(); +} + }//namespace mediakit #endif//defined(ENABLE_RTPPROXY) \ No newline at end of file diff --git a/src/Rtp/RtpSession.h b/src/Rtp/RtpSession.h index ff8771a1..e8d2afed 100644 --- a/src/Rtp/RtpSession.h +++ b/src/Rtp/RtpSession.h @@ -36,14 +36,20 @@ using namespace toolkit; namespace mediakit{ -class RtpSession : public TcpSession , public RtpSplitter{ +class RtpSession : public TcpSession , public RtpSplitter , public MediaSourceEvent{ public: RtpSession(const Socket::Ptr &sock); ~RtpSession() override; void onRecv(const Buffer::Ptr &) override; void onError(const SockException &err) override; void onManager() override; -private: +protected: + // 通知其停止推流 + bool close(MediaSource &sender,bool force) override; + // 通知无人观看 + void onNoneReader(MediaSource &sender) override; + // 观看总人数 + int totalReaderCount(MediaSource &sender) override; void onRtpPacket(const char *data,uint64_t len) override; private: uint32_t _ssrc = 0; From 1e53500eb0895e67d9db6d85b00ad7e891d84187 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 28 Feb 2020 17:53:21 +0800 Subject: [PATCH 263/272] =?UTF-8?q?mk=5Fmedia=E6=8E=A5=E5=8F=A3=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=97=A0=E4=BA=BA=E8=A7=82=E7=9C=8B=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mk_media.h | 25 +++++++++ api/source/mk_media.cpp | 110 +++++++++++++++++++++++++++++++++------- 2 files changed, 117 insertions(+), 18 deletions(-) diff --git a/api/include/mk_media.h b/api/include/mk_media.h index e2eeba8c..4f56a4f8 100755 --- a/api/include/mk_media.h +++ b/api/include/mk_media.h @@ -129,6 +129,31 @@ API_EXPORT void API_CALL mk_media_input_aac(mk_media ctx, void *data, int len, u */ API_EXPORT void API_CALL mk_media_input_aac1(mk_media ctx, void *data, int len, uint32_t dts, void *adts); +/** + * 在调用对应的MediaSource.close()时会触发该回调 + * 你可以在该事件中做清理工作(比如说关闭摄像头,同时调用mk_media_release函数销毁该对象) + * @param user_data 用户数据指针,通过mk_media_set_on_close函数设置 + * @return 返回0告知事件触发者关闭媒体失败,非0代表成功 + */ +typedef int(API_CALL *on_mk_media_close)(void *user_data); + +/** + * 监听MediaSource.close()事件 + * 在选择关闭一个MediaSource时,将会最终触发到该回调 + * 你可以通过该事件选择删除对象,当然你在该事件中也可以什么都不做 + * @param ctx 对象指针 + * @param cb 回调指针 + * @param user_data 用户数据指针 + */ +API_EXPORT void API_CALL mk_media_set_on_close(mk_media ctx, on_mk_media_close cb, void *user_data); + +/** + * 获取总的观看人数 + * @param ctx 对象指针 + * @return 观看人数 + */ +API_EXPORT int API_CALL mk_media_total_reader_count(mk_media ctx); + #ifdef __cplusplus } #endif diff --git a/api/source/mk_media.cpp b/api/source/mk_media.cpp index a09525fd..e1ffea59 100755 --- a/api/source/mk_media.cpp +++ b/api/source/mk_media.cpp @@ -32,77 +32,151 @@ using namespace std; using namespace toolkit; using namespace mediakit; +class MediaHelper : public MediaSourceEvent , public std::enable_shared_from_this { +public: + typedef std::shared_ptr Ptr; + template + MediaHelper(ArgsType &&...args){ + _channel = std::make_shared(std::forward(args)...); + } + ~MediaHelper(){} + + void attachEvent(){ + _channel->setListener(shared_from_this()); + } + + DevChannel::Ptr &getChannel(){ + return _channel; + } + + void setCallBack(on_mk_media_close cb, void *user_data){ + _cb = cb; + _user_data = user_data; + } +protected: + // 通知其停止推流 + bool close(MediaSource &sender,bool force) override{ + if(!force && _channel->totalReaderCount()){ + //非强制关闭且正有人在观看该视频 + return false; + } + if(!_cb){ + //未设置回调,没法关闭 + return false; + } + if(!_cb(_user_data)){ + //回调选择返回不关闭该视频 + return false; + } + + //回调中已经关闭该视频 + string err = StrPrinter << "close media:" << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId() << " " << force; + return true; + } + + // 通知无人观看 + void onNoneReader(MediaSource &sender) override{ + if(_channel->totalReaderCount()){ + //统计有误,还有人在看 + return; + } + MediaSourceEvent::onNoneReader(sender); + } + + // 观看总人数 + int totalReaderCount(MediaSource &sender) override{ + return _channel->totalReaderCount(); + } +private: + DevChannel::Ptr _channel; + on_mk_media_close _cb; + void *_user_data; +}; + +API_EXPORT void API_CALL mk_media_set_on_close(mk_media ctx, on_mk_media_close cb, void *user_data){ + assert(ctx); + MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx; + (*obj)->setCallBack(cb,user_data); +} + +API_EXPORT int API_CALL mk_media_total_reader_count(mk_media ctx){ + assert(ctx); + MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx; + return (*obj)->getChannel()->totalReaderCount(); +} + API_EXPORT mk_media API_CALL mk_media_create(const char *vhost, const char *app, const char *stream, float duration, int hls_enabled, int mp4_enabled) { assert(vhost && app && stream); - DevChannel::Ptr *obj(new DevChannel::Ptr(new DevChannel(vhost, app, stream, duration, true, true, hls_enabled, mp4_enabled))); + MediaHelper::Ptr *obj(new MediaHelper::Ptr(new MediaHelper(vhost, app, stream, duration, true, true, hls_enabled, mp4_enabled))); + (*obj)->attachEvent(); return (mk_media) obj; } API_EXPORT void API_CALL mk_media_release(mk_media ctx) { assert(ctx); - DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; + MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx; delete obj; } API_EXPORT void API_CALL mk_media_init_h264(mk_media ctx, int width, int height, int frameRate) { assert(ctx); - DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; + MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx; VideoInfo info; info.iFrameRate = frameRate; info.iWidth = width; info.iHeight = height; - (*obj)->initVideo(info); + (*obj)->getChannel()->initVideo(info); } API_EXPORT void API_CALL mk_media_init_h265(mk_media ctx, int width, int height, int frameRate) { assert(ctx); - DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; + MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx; VideoInfo info; info.iFrameRate = frameRate; info.iWidth = width; info.iHeight = height; - (*obj)->initH265Video(info); + (*obj)->getChannel()->initH265Video(info); } API_EXPORT void API_CALL mk_media_init_aac(mk_media ctx, int channel, int sample_bit, int sample_rate, int profile) { assert(ctx); - DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; + MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx; AudioInfo info; info.iSampleRate = sample_rate; info.iChannel = channel; info.iSampleBit = sample_bit; info.iProfile = profile; - (*obj)->initAudio(info); + (*obj)->getChannel()->initAudio(info); } API_EXPORT void API_CALL mk_media_init_complete(mk_media ctx){ assert(ctx); - DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; - (*obj)->addTrackCompleted(); + MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx; + (*obj)->getChannel()->addTrackCompleted(); } API_EXPORT void API_CALL mk_media_input_h264(mk_media ctx, void *data, int len, uint32_t dts, uint32_t pts) { assert(ctx && data && len > 0); - DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; - (*obj)->inputH264((char *) data, len, dts, pts); + MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx; + (*obj)->getChannel()->inputH264((char *) data, len, dts, pts); } API_EXPORT void API_CALL mk_media_input_h265(mk_media ctx, void *data, int len, uint32_t dts, uint32_t pts) { assert(ctx && data && len > 0); - DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; - (*obj)->inputH265((char *) data, len, dts, pts); + MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx; + (*obj)->getChannel()->inputH265((char *) data, len, dts, pts); } API_EXPORT void API_CALL mk_media_input_aac(mk_media ctx, void *data, int len, uint32_t dts, int with_adts_header) { assert(ctx && data && len > 0); - DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; - (*obj)->inputAAC((char *) data, len, dts, with_adts_header); + MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx; + (*obj)->getChannel()->inputAAC((char *) data, len, dts, with_adts_header); } API_EXPORT void API_CALL mk_media_input_aac1(mk_media ctx, void *data, int len, uint32_t dts, void *adts) { assert(ctx && data && len > 0 && adts); - DevChannel::Ptr *obj = (DevChannel::Ptr *) ctx; - (*obj)->inputAAC((char *) data, len, dts, (char *) adts); + MediaHelper::Ptr *obj = (MediaHelper::Ptr *) ctx; + (*obj)->getChannel()->inputAAC((char *) data, len, dts, (char *) adts); } From 0a4f2ad42cc9fce6cd0e04bcbe68ed8b229818cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Fri, 28 Feb 2020 22:55:47 +0800 Subject: [PATCH 264/272] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c5fc66ee..17e34628 100644 --- a/README.md +++ b/README.md @@ -363,7 +363,7 @@ docker build -t zlmediakit . - [IOS摄像头实时录制,生成rtsp/rtmp/hls/http-flv](https://gitee.com/xiahcu/IOSMedia) - [IOS rtmp/rtsp播放器,视频推流器](https://gitee.com/xiahcu/IOSPlayer) - [支持linux、windows、mac的rtmp/rtsp播放器](https://github.com/xiongziliang/ZLMediaPlayer) - + - [配套的管理WEB网站](https://github.com/chenxiaolei/ZLMediaKit_NVR_UI) 上述工程可能在最新的代码的情况下编译不过,请手动修改 From a245e6bec250815b6999769547b74274f913d1f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Fri, 28 Feb 2020 23:08:44 +0800 Subject: [PATCH 265/272] Update README.md --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 17e34628..1377e922 100644 --- a/README.md +++ b/README.md @@ -364,9 +364,7 @@ docker build -t zlmediakit . - [IOS rtmp/rtsp播放器,视频推流器](https://gitee.com/xiahcu/IOSPlayer) - [支持linux、windows、mac的rtmp/rtsp播放器](https://github.com/xiongziliang/ZLMediaPlayer) - [配套的管理WEB网站](https://github.com/chenxiaolei/ZLMediaKit_NVR_UI) - 上述工程可能在最新的代码的情况下编译不过,请手动修改 - - + ## 授权协议 本项目自有代码使用宽松的MIT协议,在保留版权信息的情况下可以自由应用于各自商用、非商业的项目。 From 899dcc738630771cfbe8ed2a07e96fcadfd729a1 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 29 Feb 2020 10:27:30 +0800 Subject: [PATCH 266/272] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=8B=BC=E5=86=99?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mk_events_objects.h | 2 +- api/source/mk_events_objects.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/include/mk_events_objects.h b/api/include/mk_events_objects.h index f407e17d..a385fdfb 100644 --- a/api/include/mk_events_objects.h +++ b/api/include/mk_events_objects.h @@ -315,7 +315,7 @@ API_EXPORT mk_auth_invoker API_CALL mk_auth_invoker_clone(const mk_auth_invoker /** * 销毁堆上的克隆对象 */ -API_EXPORT void API_CALL mk_auth_invoker_clone_relase(const mk_auth_invoker ctx); +API_EXPORT void API_CALL mk_auth_invoker_clone_release(const mk_auth_invoker ctx); #ifdef __cplusplus } diff --git a/api/source/mk_events_objects.cpp b/api/source/mk_events_objects.cpp index 3818ad47..d7ea5534 100644 --- a/api/source/mk_events_objects.cpp +++ b/api/source/mk_events_objects.cpp @@ -417,7 +417,7 @@ API_EXPORT mk_auth_invoker API_CALL mk_auth_invoker_clone(const mk_auth_invoker return new Broadcast::AuthInvoker(*invoker); } -API_EXPORT void API_CALL mk_auth_invoker_clone_relase(const mk_auth_invoker ctx){ +API_EXPORT void API_CALL mk_auth_invoker_clone_release(const mk_auth_invoker ctx){ assert(ctx); Broadcast::AuthInvoker *invoker = (Broadcast::AuthInvoker *)ctx; delete invoker; From f384f5e0254a515fac7bad741ea8840dba2494ec Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 4 Mar 2020 21:57:31 +0800 Subject: [PATCH 267/272] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E6=AD=BB=E9=94=81?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/MediaSource.cpp | 82 +++++++++++++++++++++----------------- src/Common/MediaSource.h | 1 - 2 files changed, 45 insertions(+), 38 deletions(-) diff --git a/src/Common/MediaSource.cpp b/src/Common/MediaSource.cpp index 2b2f5b35..75d6c269 100644 --- a/src/Common/MediaSource.cpp +++ b/src/Common/MediaSource.cpp @@ -253,22 +253,25 @@ MediaSource::Ptr MediaSource::find(const string &schema, const string &vhost_tmp vhost = DEFAULT_VHOST; } - lock_guard lock(g_mtxMediaSrc); MediaSource::Ptr ret; - //查找某一媒体源,找到后返回 - searchMedia(g_mapMediaSrc, schema, vhost, app, id, [&](SchemaVhostAppStreamMap::iterator &it0, - VhostAppStreamMap::iterator &it1, - AppStreamMap::iterator &it2, - StreamMap::iterator &it3) { - ret = it3->second.lock(); - if (!ret) { - //该对象已经销毁 - it2->second.erase(it3); - eraseIfEmpty(g_mapMediaSrc,it0, it1, it2); - return false; - } - return true; - }); + { + lock_guard lock(g_mtxMediaSrc); + //查找某一媒体源,找到后返回 + searchMedia(g_mapMediaSrc, schema, vhost, app, id, [&](SchemaVhostAppStreamMap::iterator &it0, + VhostAppStreamMap::iterator &it1, + AppStreamMap::iterator &it2, + StreamMap::iterator &it3) { + ret = it3->second.lock(); + if (!ret) { + //该对象已经销毁 + it2->second.erase(it3); + eraseIfEmpty(g_mapMediaSrc, it0, it1, it2); + return false; + } + return true; + }); + } + if(!ret && bMake){ //未查找媒体源,则创建一个 ret = MP4Reader::onMakeMediaSource(schema, vhost,app,id); @@ -288,29 +291,34 @@ void MediaSource::regist() { InfoL << _strSchema << " " << _strVhost << " " << _strApp << " " << _strId; NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaChanged, true, *this); } -bool MediaSource::unregist() { - //反注册该源 - lock_guard lock(g_mtxMediaSrc); - return searchMedia(g_mapMediaSrc, _strSchema, _strVhost, _strApp, _strId,[&](SchemaVhostAppStreamMap::iterator &it0, - VhostAppStreamMap::iterator &it1, - AppStreamMap::iterator &it2, - StreamMap::iterator &it3) { - auto strongMedia = it3->second.lock(); - if (strongMedia && this != strongMedia.get()) { - //不是自己,不允许反注册 - return false; - } - it2->second.erase(it3); - eraseIfEmpty(g_mapMediaSrc, it0, it1, it2); - unregisted(); - return true; - }); -} -void MediaSource::unregisted(){ - InfoL << "" << _strSchema << " " << _strVhost << " " << _strApp << " " << _strId; - NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaChanged, false, *this); -} +//反注册该源 +bool MediaSource::unregist() { + bool ret; + { + lock_guard lock(g_mtxMediaSrc); + ret = searchMedia(g_mapMediaSrc, _strSchema, _strVhost, _strApp, _strId, + [&](SchemaVhostAppStreamMap::iterator &it0, + VhostAppStreamMap::iterator &it1, + AppStreamMap::iterator &it2, + StreamMap::iterator &it3) { + auto strongMedia = it3->second.lock(); + if (strongMedia && this != strongMedia.get()) { + //不是自己,不允许反注册 + return false; + } + it2->second.erase(it3); + eraseIfEmpty(g_mapMediaSrc, it0, it1, it2); + return true; + }); + } + + if(ret){ + InfoL << "" << _strSchema << " " << _strVhost << " " << _strApp << " " << _strId; + NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaChanged, false, *this); + } + return ret; +} /////////////////////////////////////MediaInfo////////////////////////////////////// diff --git a/src/Common/MediaSource.h b/src/Common/MediaSource.h index 385d6d8c..e6920050 100644 --- a/src/Common/MediaSource.h +++ b/src/Common/MediaSource.h @@ -151,7 +151,6 @@ public: protected: void regist() ; bool unregist() ; - void unregisted(); private: string _strSchema; string _strVhost; From c77f82f02c6b158b5645ceca7695e415b8bed6ac Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 4 Mar 2020 22:25:00 +0800 Subject: [PATCH 268/272] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E6=B7=BB=E5=8A=A0rtp?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E8=87=AA=E5=8A=A8=E5=88=A4=E6=96=AD=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/config.ini | 2 -- src/Common/config.cpp | 3 --- src/Common/config.h | 2 -- src/Rtp/RtpDecoder.cpp | 14 +++++++++++--- src/Rtp/RtpDecoder.h | 3 ++- src/Rtp/RtpProcess.cpp | 20 ++++++++++++++++++-- src/Rtp/RtpProcess.h | 5 +++++ tests/test_rtp.cpp | 2 -- 8 files changed, 36 insertions(+), 15 deletions(-) diff --git a/conf/config.ini b/conf/config.ini index c158d843..ed4727af 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -168,8 +168,6 @@ checkSource=1 dumpDir= #udp和tcp代理服务器,支持rtp(必须是ts或ps类型)代理 port=10000 -#rtp如果是ts/ps类型则选择MP2P,还可以设置为MP4V-ES -rtp_type=MP2P #rtp超时时间,单位秒 timeoutSec=15 diff --git a/src/Common/config.cpp b/src/Common/config.cpp index 89ad9bbf..ded712dc 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -282,15 +282,12 @@ namespace RtpProxy { const string kDumpDir = RTP_PROXY_FIELD"dumpDir"; //是否限制udp数据来源ip和端口 const string kCheckSource = RTP_PROXY_FIELD"checkSource"; -//rtp类型,支持MP2P/MP4V-ES -const string kRtpType = RTP_PROXY_FIELD"rtp_type"; //rtp接收超时时间 const string kTimeoutSec = RTP_PROXY_FIELD"timeoutSec"; onceToken token([](){ mINI::Instance()[kDumpDir] = ""; mINI::Instance()[kCheckSource] = 1; - mINI::Instance()[kRtpType] = "MP2P"; mINI::Instance()[kTimeoutSec] = 15; },nullptr); } //namespace RtpProxy diff --git a/src/Common/config.h b/src/Common/config.h index 9b9cab0c..ba81611c 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -306,8 +306,6 @@ namespace RtpProxy { extern const string kDumpDir; //是否限制udp数据来源ip和端口 extern const string kCheckSource; -//rtp类型,支持MP2P/MP4V-ES -extern const string kRtpType; //rtp接收超时时间 extern const string kTimeoutSec; } //namespace RtpProxy diff --git a/src/Rtp/RtpDecoder.cpp b/src/Rtp/RtpDecoder.cpp index 5f78cd7c..e7b63fb9 100644 --- a/src/Rtp/RtpDecoder.cpp +++ b/src/Rtp/RtpDecoder.cpp @@ -29,7 +29,6 @@ #include "Util/logger.h" #include "RtpDecoder.h" #include "rtp-payload.h" - using namespace toolkit; namespace mediakit{ @@ -45,7 +44,13 @@ RtpDecoder::~RtpDecoder() { } } -void RtpDecoder::decodeRtp(const void *data, int bytes,const char *type_name) { +void RtpDecoder::decodeRtp(const void *data, int bytes,const string &type_name) { + if(_rtp_type != type_name && _rtp_decoder){ + //rtp类型发生变化,切换之 + rtp_payload_decode_destroy(_rtp_decoder); + _rtp_decoder = nullptr; + } + if(!_rtp_decoder){ static rtp_payload_t s_func= { [](void* param, int bytes){ @@ -64,11 +69,14 @@ void RtpDecoder::decodeRtp(const void *data, int bytes,const char *type_name) { uint8_t rtp_type = 0x7F & ((uint8_t *) data)[1]; InfoL << "rtp type:" << (int) rtp_type; - _rtp_decoder = rtp_payload_decode_create(rtp_type, type_name, &s_func, this); + _rtp_decoder = rtp_payload_decode_create(rtp_type, type_name.data(), &s_func, this); if (!_rtp_decoder) { WarnL << "unsupported rtp type:" << (int) rtp_type << ",size:" << bytes << ",hexdump" << hexdump(data, bytes > 16 ? 16 : bytes); + }else{ + _rtp_type = type_name; } } + if(_rtp_decoder){ rtp_payload_decode_input(_rtp_decoder,data,bytes); } diff --git a/src/Rtp/RtpDecoder.h b/src/Rtp/RtpDecoder.h index 845dbdbd..c89aebe3 100644 --- a/src/Rtp/RtpDecoder.h +++ b/src/Rtp/RtpDecoder.h @@ -38,11 +38,12 @@ public: RtpDecoder(); virtual ~RtpDecoder(); protected: - void decodeRtp(const void *data, int bytes,const char *type_name); + void decodeRtp(const void *data, int bytes,const string &type_name); virtual void onRtpDecode(const void *packet, int bytes, uint32_t timestamp, int flags) = 0; private: void *_rtp_decoder = nullptr; BufferRaw::Ptr _buffer; + string _rtp_type; }; }//namespace mediakit diff --git a/src/Rtp/RtpProcess.cpp b/src/Rtp/RtpProcess.cpp index 85e68656..83ceb843 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -33,6 +33,7 @@ namespace mediakit{ +static const vector kRtpTypes = {"MP2P","MP4V-ES"}; /** * 合并一些时间戳相同的frame @@ -84,6 +85,7 @@ RtpProcess::RtpProcess(uint32_t ssrc) { _track->_samplerate = 90000; _track->_type = TrackVideo; _track->_ssrc = _ssrc; + getNextRtpType(); DebugL << printSSRC(_ssrc); GET_CONFIG(bool,toRtxp,General::kPublishToRtxp); @@ -153,6 +155,14 @@ bool RtpProcess::inputRtp(const char *data, int data_len,const struct sockaddr * return ret; } +void RtpProcess::getNextRtpType(){ + _rtp_type = kRtpTypes[_rtp_type_idx++]; + _rtp_dec_failed_cnt = 0; + if(_rtp_type_idx == kRtpTypes.size()){ + _rtp_type_idx = 0; + } +} + void RtpProcess::onRtpSorted(const RtpPacket::Ptr &rtp, int) { if(rtp->sequence != _sequence + 1){ WarnL << rtp->sequence << " != " << _sequence << "+1"; @@ -166,8 +176,7 @@ void RtpProcess::onRtpSorted(const RtpPacket::Ptr &rtp, int) { fwrite((uint8_t *) rtp->data() + 4, rtp->size() - 4, 1, _save_file_rtp.get()); } - GET_CONFIG(string,rtp_type,::RtpProxy::kRtpType); - decodeRtp(rtp->data() + 4 ,rtp->size() - 4,rtp_type.data()); + decodeRtp(rtp->data() + 4 ,rtp->size() - 4,_rtp_type); } void RtpProcess::onRtpDecode(const void *packet, int bytes, uint32_t, int flags) { @@ -178,6 +187,12 @@ void RtpProcess::onRtpDecode(const void *packet, int bytes, uint32_t, int flags) auto ret = decodePS((uint8_t *)packet,bytes); if(ret != bytes){ WarnL << ret << " != " << bytes << " " << flags; + if(++_rtp_dec_failed_cnt == 10){ + getNextRtpType(); + InfoL << "rtp of ssrc " << printSSRC(_ssrc) << " change to type: " << _rtp_type ; + } + } else{ + _rtp_dec_failed_cnt = 0; } } @@ -307,5 +322,6 @@ void RtpProcess::setListener(const std::weak_ptr &listener){ _muxer->setListener(listener); } + }//namespace mediakit #endif//defined(ENABLE_RTPPROXY) \ No newline at end of file diff --git a/src/Rtp/RtpProcess.h b/src/Rtp/RtpProcess.h index c9fb7397..bf0b979a 100644 --- a/src/Rtp/RtpProcess.h +++ b/src/Rtp/RtpProcess.h @@ -62,6 +62,8 @@ protected: int64_t dts, const void *data, int bytes) override ; +private: + void getNextRtpType(); private: std::shared_ptr _save_file_rtp; std::shared_ptr _save_file_ps; @@ -77,6 +79,9 @@ private: Ticker _last_rtp_time; map _stamps; uint32_t _dts = 0; + int _rtp_type_idx = 0; + string _rtp_type; + int _rtp_dec_failed_cnt = 0; }; }//namespace mediakit diff --git a/tests/test_rtp.cpp b/tests/test_rtp.cpp index 52c8fe03..8e36a131 100644 --- a/tests/test_rtp.cpp +++ b/tests/test_rtp.cpp @@ -98,8 +98,6 @@ int main(int argc,char *argv[]) { rtspSrv->start(554);//默认554 rtmpSrv->start(1935);//默认1935 httpSrv->start(80);//默认80 - //此处可以选择MP4V-ES或MP2P - mINI::Instance()[RtpProxy::kRtpType] = "MP4V-ES"; //此处选择是否导出调试文件 // mINI::Instance()[RtpProxy::kDumpDir] = "/Users/xzl/Desktop/"; From 448f6fa3c701ce7f4ea72f299588bad7d1cfe20c Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 5 Mar 2020 11:36:31 +0800 Subject: [PATCH 269/272] =?UTF-8?q?=E4=BC=98=E5=8C=96rtp=E4=BB=A3=E7=90=86?= =?UTF-8?q?=E8=B6=85=E6=97=B6=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtp/RtpSelector.cpp | 20 ++++++++++++++++---- src/Rtp/RtpSelector.h | 3 ++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/Rtp/RtpSelector.cpp b/src/Rtp/RtpSelector.cpp index e30d4af3..f475159e 100644 --- a/src/Rtp/RtpSelector.cpp +++ b/src/Rtp/RtpSelector.cpp @@ -32,10 +32,6 @@ namespace mediakit{ INSTANCE_IMP(RtpSelector); bool RtpSelector::inputRtp(const char *data, int data_len,const struct sockaddr *addr,uint32_t *dts_out) { - if(_last_rtp_time.elapsedTime() > 3000){ - _last_rtp_time.resetTime(); - onManager(); - } uint32_t ssrc = 0; if(!getSSRC(data,data_len,ssrc)){ WarnL << "get ssrc from rtp failed:" << data_len; @@ -67,10 +63,26 @@ RtpProcess::Ptr RtpSelector::getProcess(uint32_t ssrc,bool makeNew) { if(!ref){ ref = std::make_shared(ssrc,shared_from_this()); ref->attachEvent(); + createTimer(); } return ref->getProcess(); } +void RtpSelector::createTimer() { + if (!_timer) { + //创建超时管理定时器 + weak_ptr weakSelf = shared_from_this(); + _timer = std::make_shared(3.0, [weakSelf] { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { + return false; + } + strongSelf->onManager(); + return true; + }, EventPollerPool::Instance().getPoller()); + } +} + void RtpSelector::delProcess(uint32_t ssrc,const RtpProcess *ptr) { lock_guard lck(_mtx_map); auto it = _map_rtp_process.find(ssrc); diff --git a/src/Rtp/RtpSelector.h b/src/Rtp/RtpSelector.h index f1b4b97f..9edaca54 100644 --- a/src/Rtp/RtpSelector.h +++ b/src/Rtp/RtpSelector.h @@ -69,10 +69,11 @@ public: void delProcess(uint32_t ssrc,const RtpProcess *ptr); private: void onManager(); + void createTimer(); private: unordered_map _map_rtp_process; recursive_mutex _mtx_map; - Ticker _last_rtp_time; + Timer::Ptr _timer; }; }//namespace mediakit From a89e81d3f32902d101d6855b581c1c5273bd62e5 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 5 Mar 2020 18:04:34 +0800 Subject: [PATCH 270/272] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/source/mk_media.cpp | 2 +- src/Rtp/RtpSelector.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/source/mk_media.cpp b/api/source/mk_media.cpp index e1ffea59..d513af54 100755 --- a/api/source/mk_media.cpp +++ b/api/source/mk_media.cpp @@ -70,7 +70,7 @@ protected: } //回调中已经关闭该视频 - string err = StrPrinter << "close media:" << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId() << " " << force; + WarnL << "close media:" << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId() << " " << force; return true; } diff --git a/src/Rtp/RtpSelector.cpp b/src/Rtp/RtpSelector.cpp index f475159e..95c6e473 100644 --- a/src/Rtp/RtpSelector.cpp +++ b/src/Rtp/RtpSelector.cpp @@ -138,7 +138,7 @@ bool RtpProcessHelper::close(MediaSource &sender, bool force) { return false; } parent->delProcess(_ssrc,_process.get()); - string err = StrPrinter << "close media:" << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId() << " " << force; + WarnL << "close media:" << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId() << " " << force; return true; } From d35f1d0bfe2f1400bb399f760788892ae52563b3 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 6 Mar 2020 11:11:27 +0800 Subject: [PATCH 271/272] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mk_tcp.h | 2 +- api/source/mk_tcp.cpp | 2 +- api/tests/websocket.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/api/include/mk_tcp.h b/api/include/mk_tcp.h index 3bd81885..34ffb342 100644 --- a/api/include/mk_tcp.h +++ b/api/include/mk_tcp.h @@ -120,7 +120,7 @@ API_EXPORT void* API_CALL mk_tcp_session_get_user_data(mk_tcp_session session); * @param port 监听端口号,0则为随机 * @param type 服务器类型 */ -API_EXPORT uint16_t API_CALL mk_tcp_server_server_start(uint16_t port, mk_tcp_type type); +API_EXPORT uint16_t API_CALL mk_tcp_server_start(uint16_t port, mk_tcp_type type); /** * 监听tcp服务器事件 diff --git a/api/source/mk_tcp.cpp b/api/source/mk_tcp.cpp index 2d18557c..4517ea00 100644 --- a/api/source/mk_tcp.cpp +++ b/api/source/mk_tcp.cpp @@ -137,7 +137,7 @@ API_EXPORT void API_CALL mk_tcp_server_events_listen(const mk_tcp_session_events } } -API_EXPORT uint16_t API_CALL mk_tcp_server_server_start(uint16_t port, mk_tcp_type type){ +API_EXPORT uint16_t API_CALL mk_tcp_server_start(uint16_t port, mk_tcp_type type){ type = MAX(mk_type_tcp, MIN(type, mk_type_wss)); try { s_tcp_server[type] = std::make_shared(); diff --git a/api/tests/websocket.c b/api/tests/websocket.c index 7a95e586..82e2a878 100644 --- a/api/tests/websocket.c +++ b/api/tests/websocket.c @@ -172,7 +172,7 @@ void test_server(){ }; mk_tcp_server_events_listen(&events_server); - mk_tcp_server_server_start(80,TCP_TYPE); + mk_tcp_server_start(80, TCP_TYPE); } void test_client(){ From 069bde09c1fefbb22218fc466c4813e95382ee75 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 6 Mar 2020 13:00:06 +0800 Subject: [PATCH 272/272] =?UTF-8?q?=E6=94=AF=E6=8C=81ts=20rtp=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/HlsMediaSource.h | 2 +- src/Rtp/Decoder.cpp | 41 ++++++++++++++++ src/Rtp/Decoder.h | 57 ++++++++++++++++++++++ src/Rtp/PSDecoder.cpp | 12 +++-- src/Rtp/PSDecoder.h | 20 +++----- src/Rtp/RtpDecoder.cpp | 2 +- src/Rtp/RtpDecoder.h | 2 +- src/Rtp/RtpProcess.cpp | 26 ++++++---- src/Rtp/RtpProcess.h | 15 ++---- src/Rtp/TSDecoder.cpp | 96 +++++++++++++++++++++++++++++++++++++ src/Rtp/TSDecoder.h | 68 ++++++++++++++++++++++++++ 11 files changed, 302 insertions(+), 39 deletions(-) create mode 100644 src/Rtp/Decoder.cpp create mode 100644 src/Rtp/Decoder.h create mode 100644 src/Rtp/TSDecoder.cpp create mode 100644 src/Rtp/TSDecoder.h diff --git a/src/Record/HlsMediaSource.h b/src/Record/HlsMediaSource.h index 8da52e2c..38a192b7 100644 --- a/src/Record/HlsMediaSource.h +++ b/src/Record/HlsMediaSource.h @@ -1,4 +1,4 @@ -/* +/* * MIT License * * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com> diff --git a/src/Rtp/Decoder.cpp b/src/Rtp/Decoder.cpp new file mode 100644 index 00000000..ca6e8a6b --- /dev/null +++ b/src/Rtp/Decoder.cpp @@ -0,0 +1,41 @@ +/* + * MIT License + * + * Copyright (c) 2020 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if defined(ENABLE_RTPPROXY) +#include "Decoder.h" +#include "PSDecoder.h" +#include "TSDecoder.h" +namespace mediakit { +Decoder::Ptr Decoder::createDecoder(Decoder::Type type) { + switch (type){ + case decoder_ps : return std::make_shared(); + case decoder_ts : return std::make_shared(); + default : return nullptr; + } +} + +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) diff --git a/src/Rtp/Decoder.h b/src/Rtp/Decoder.h new file mode 100644 index 00000000..406508d2 --- /dev/null +++ b/src/Rtp/Decoder.h @@ -0,0 +1,57 @@ +/* + * MIT License + * + * Copyright (c) 2020 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef ZLMEDIAKIT_DECODER_H +#define ZLMEDIAKIT_DECODER_H + +#if defined(ENABLE_RTPPROXY) +#include +#include +#include +#include "Decoder.h" +using namespace std; +namespace mediakit { + +class Decoder { +public: + typedef std::shared_ptr Ptr; + typedef enum { + decoder_ts = 0, + decoder_ps + }Type; + + typedef std::function onDecode; + virtual int input(const uint8_t *data, int bytes) = 0; + virtual void setOnDecode(const onDecode &decode) = 0; + static Ptr createDecoder(Type type); +protected: + Decoder() = default; + virtual ~Decoder() = default; +}; + +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) +#endif //ZLMEDIAKIT_DECODER_H diff --git a/src/Rtp/PSDecoder.cpp b/src/Rtp/PSDecoder.cpp index 47abb768..77733d2b 100644 --- a/src/Rtp/PSDecoder.cpp +++ b/src/Rtp/PSDecoder.cpp @@ -27,7 +27,6 @@ #if defined(ENABLE_RTPPROXY) #include "PSDecoder.h" #include "mpeg-ps.h" - namespace mediakit{ PSDecoder::PSDecoder() { @@ -40,7 +39,9 @@ PSDecoder::PSDecoder() { const void* data, size_t bytes){ PSDecoder *thiz = (PSDecoder *)param; - thiz->onPSDecode(stream, codecid, flags, pts, dts, data, bytes); + if(thiz->_on_decode){ + thiz->_on_decode(stream, codecid, flags, pts, dts, data, bytes); + } },this); } @@ -48,10 +49,13 @@ PSDecoder::~PSDecoder() { ps_demuxer_destroy((struct ps_demuxer_t*)_ps_demuxer); } -int PSDecoder::decodePS(const uint8_t *data, int bytes) { +int PSDecoder::input(const uint8_t *data, int bytes) { return ps_demuxer_input((struct ps_demuxer_t*)_ps_demuxer,data,bytes); } -}//namespace mediakit +void PSDecoder::setOnDecode(const Decoder::onDecode &decode) { + _on_decode = decode; +} +}//namespace mediakit #endif//#if defined(ENABLE_RTPPROXY) \ No newline at end of file diff --git a/src/Rtp/PSDecoder.h b/src/Rtp/PSDecoder.h index c8a73872..967a4ed4 100644 --- a/src/Rtp/PSDecoder.h +++ b/src/Rtp/PSDecoder.h @@ -29,27 +29,21 @@ #if defined(ENABLE_RTPPROXY) #include - +#include "Decoder.h" namespace mediakit{ -class PSDecoder { +//ps解析器 +class PSDecoder : public Decoder { public: PSDecoder(); - virtual ~PSDecoder(); - int decodePS(const uint8_t *data, int bytes); -protected: - virtual void onPSDecode(int stream, - int codecid, - int flags, - int64_t pts, - int64_t dts, - const void *data, - int bytes) = 0; + ~PSDecoder(); + int input(const uint8_t* data, int bytes) override; + void setOnDecode(const onDecode &decode) override; private: void *_ps_demuxer = nullptr; + onDecode _on_decode; }; }//namespace mediakit - #endif//defined(ENABLE_RTPPROXY) #endif //ZLMEDIAKIT_PSDECODER_H diff --git a/src/Rtp/RtpDecoder.cpp b/src/Rtp/RtpDecoder.cpp index e7b63fb9..af64f0d5 100644 --- a/src/Rtp/RtpDecoder.cpp +++ b/src/Rtp/RtpDecoder.cpp @@ -63,7 +63,7 @@ void RtpDecoder::decodeRtp(const void *data, int bytes,const string &type_name) }, [](void* param, const void *packet, int bytes, uint32_t timestamp, int flags){ RtpDecoder *obj = (RtpDecoder *)param; - obj->onRtpDecode(packet, bytes, timestamp, flags); + obj->onRtpDecode((uint8_t *)packet, bytes, timestamp, flags); } }; diff --git a/src/Rtp/RtpDecoder.h b/src/Rtp/RtpDecoder.h index c89aebe3..a6d78a65 100644 --- a/src/Rtp/RtpDecoder.h +++ b/src/Rtp/RtpDecoder.h @@ -39,7 +39,7 @@ public: virtual ~RtpDecoder(); protected: void decodeRtp(const void *data, int bytes,const string &type_name); - virtual void onRtpDecode(const void *packet, int bytes, uint32_t timestamp, int flags) = 0; + virtual void onRtpDecode(const uint8_t *packet, int bytes, uint32_t timestamp, int flags) = 0; private: void *_rtp_decoder = nullptr; BufferRaw::Ptr _buffer; diff --git a/src/Rtp/RtpProcess.cpp b/src/Rtp/RtpProcess.cpp index 83ceb843..32986f98 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -179,12 +179,26 @@ void RtpProcess::onRtpSorted(const RtpPacket::Ptr &rtp, int) { decodeRtp(rtp->data() + 4 ,rtp->size() - 4,_rtp_type); } -void RtpProcess::onRtpDecode(const void *packet, int bytes, uint32_t, int flags) { +void RtpProcess::onRtpDecode(const uint8_t *packet, int bytes, uint32_t, int flags) { if(_save_file_ps){ fwrite((uint8_t *)packet,bytes, 1, _save_file_ps.get()); } - auto ret = decodePS((uint8_t *)packet,bytes); + if(!_decoder){ + //创建解码器 + if(bytes % 188 == 0 || packet[0] == 0x47){ + //猜测是ts负载 + _decoder = Decoder::createDecoder(Decoder::decoder_ts); + }else{ + //猜测是ps负载 + _decoder = Decoder::createDecoder(Decoder::decoder_ps); + } + _decoder->setOnDecode([this](int stream,int codecid,int flags,int64_t pts,int64_t dts,const void *data,int bytes){ + onDecode(stream,codecid,flags,pts,dts,data,bytes); + }); + } + + auto ret = _decoder->input((uint8_t *)packet,bytes); if(ret != bytes){ WarnL << ret << " != " << bytes << " " << flags; if(++_rtp_dec_failed_cnt == 10){ @@ -215,13 +229,7 @@ static const char *getCodecName(int codec_id) { } } -void RtpProcess::onPSDecode(int stream, - int codecid, - int flags, - int64_t pts, - int64_t dts, - const void *data, - int bytes) { +void RtpProcess::onDecode(int stream,int codecid,int flags,int64_t pts,int64_t dts,const void *data,int bytes) { pts /= 90; dts /= 90; _stamps[codecid].revise(dts,pts,dts,pts,false); diff --git a/src/Rtp/RtpProcess.h b/src/Rtp/RtpProcess.h index bf0b979a..e8295969 100644 --- a/src/Rtp/RtpProcess.h +++ b/src/Rtp/RtpProcess.h @@ -31,7 +31,7 @@ #include "Rtsp/RtpReceiver.h" #include "RtpDecoder.h" -#include "PSDecoder.h" +#include "Decoder.h" #include "Common/Device.h" #include "Common/Stamp.h" using namespace mediakit; @@ -40,7 +40,7 @@ namespace mediakit{ string printSSRC(uint32_t ui32Ssrc); class FrameMerger; -class RtpProcess : public RtpReceiver , public RtpDecoder , public PSDecoder { +class RtpProcess : public RtpReceiver , public RtpDecoder{ public: typedef std::shared_ptr Ptr; RtpProcess(uint32_t ssrc); @@ -54,14 +54,8 @@ public: void setListener(const std::weak_ptr &listener); protected: void onRtpSorted(const RtpPacket::Ptr &rtp, int track_index) override ; - void onRtpDecode(const void *packet, int bytes, uint32_t timestamp, int flags) override; - void onPSDecode(int stream, - int codecid, - int flags, - int64_t pts, - int64_t dts, - const void *data, - int bytes) override ; + void onRtpDecode(const uint8_t *packet, int bytes, uint32_t timestamp, int flags) override; + void onDecode(int stream,int codecid,int flags,int64_t pts,int64_t dts, const void *data,int bytes); private: void getNextRtpType(); private: @@ -82,6 +76,7 @@ private: int _rtp_type_idx = 0; string _rtp_type; int _rtp_dec_failed_cnt = 0; + Decoder::Ptr _decoder; }; }//namespace mediakit diff --git a/src/Rtp/TSDecoder.cpp b/src/Rtp/TSDecoder.cpp new file mode 100644 index 00000000..9f1bee8c --- /dev/null +++ b/src/Rtp/TSDecoder.cpp @@ -0,0 +1,96 @@ +/* + * MIT License + * + * Copyright (c) 2020 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#if defined(ENABLE_RTPPROXY) +#include "mpeg-ts.h" +#include "TSDecoder.h" +#define TS_PACKET_SIZE 188 +namespace mediakit { + +void TSSegment::setOnSegment(const TSSegment::onSegment &cb) { + _onSegment = cb; +} + +int64_t TSSegment::onRecvHeader(const char *data, uint64_t len) { + _onSegment(data, len); + return 0; +} + +const char *TSSegment::onSearchPacketTail(const char *data, int len) { + if (len < _size + 1) { + if (len == _size && ((uint8_t *) data)[0] == 0x47) { + return data + _size; + } + return nullptr; + } + //下一个包头 + if (((uint8_t *) data)[_size] == 0x47) { + return data + _size; + } + + auto pos = memchr(data + _size, 0x47, len - _size); + if (pos) { + return (char *) pos; + } + return nullptr; +} + +//////////////////////////////////////////////////////////////// + +TSDecoder::TSDecoder() : _ts_segment(TS_PACKET_SIZE) { + _ts_segment.setOnSegment([this](const char *data,uint64_t len){ + if(((uint8_t*)data)[0] != 0x47 || len != TS_PACKET_SIZE ){ + WarnL << "不是ts包:" << (int)(data[0]) << " " << len; + return; + } + ts_demuxer_input(_demuxer_ctx,(uint8_t*)data,len); + }); + _demuxer_ctx = ts_demuxer_create([](void* param, int program, int stream, int codecid, int flags, int64_t pts, int64_t dts, const void* data, size_t bytes){ + TSDecoder *thiz = (TSDecoder*)param; + if(thiz->_on_decode){ + thiz->_on_decode(stream,codecid,flags,pts,dts,data,bytes); + } + return 0; + },this); +} + +TSDecoder::~TSDecoder() { + ts_demuxer_destroy(_demuxer_ctx); +} + +int TSDecoder::input(const uint8_t *data, int bytes) { + if(bytes == TS_PACKET_SIZE && ((uint8_t*)data)[0] == 0x47){ + return ts_demuxer_input(_demuxer_ctx,(uint8_t*)data,bytes); + } + _ts_segment.input((char*)data,bytes); + return bytes; +} + +void TSDecoder::setOnDecode(const Decoder::onDecode &decode) { + _on_decode = decode; +} + +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) \ No newline at end of file diff --git a/src/Rtp/TSDecoder.h b/src/Rtp/TSDecoder.h new file mode 100644 index 00000000..dc442726 --- /dev/null +++ b/src/Rtp/TSDecoder.h @@ -0,0 +1,68 @@ +/* + * MIT License + * + * Copyright (c) 2020 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef ZLMEDIAKIT_TSDECODER_H +#define ZLMEDIAKIT_TSDECODER_H + +#if defined(ENABLE_RTPPROXY) +#include "Util/logger.h" +#include "Http/HttpRequestSplitter.h" +#include "Decoder.h" + +using namespace toolkit; +namespace mediakit { + +//ts包拆分器 +class TSSegment : public HttpRequestSplitter { +public: + typedef std::function onSegment; + TSSegment(int size = 188) : _size(size){} + ~TSSegment(){} + void setOnSegment(const onSegment &cb); +protected: + int64_t onRecvHeader(const char *data, uint64_t len) override ; + const char *onSearchPacketTail(const char *data, int len) override ; +private: + int _size; + onSegment _onSegment; +}; + +//ts解析器 +class TSDecoder : public Decoder { +public: + TSDecoder(); + ~TSDecoder(); + int input(const uint8_t* data, int bytes) override ; + void setOnDecode(const onDecode &decode) override; +private: + TSSegment _ts_segment; + struct ts_demuxer_t* _demuxer_ctx = nullptr; + onDecode _on_decode; +}; + +}//namespace mediakit +#endif//defined(ENABLE_RTPPROXY) +#endif //ZLMEDIAKIT_TSDECODER_H