diff --git a/3rdpart/ZLToolKit b/3rdpart/ZLToolKit index 57e7c83d..0b406073 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 57e7c83d5667b1e06fb8f5ca73dbe3f04a9fc67f +Subproject commit 0b406073125080ab8edd13ee7c14e573e54baa35 diff --git a/Android/app/src/main/cpp/CMakeLists.txt b/Android/app/src/main/cpp/CMakeLists.txt index c4ba78b7..ef1abb06 100644 --- a/Android/app/src/main/cpp/CMakeLists.txt +++ b/Android/app/src/main/cpp/CMakeLists.txt @@ -6,12 +6,12 @@ cmake_minimum_required(VERSION 3.4.1) #设置生成的so动态库最后输出的路径 -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/libs_export/${ANDROID_ABI}) -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/libs_export/${ANDROID_ABI}) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/libs_export/${ANDROID_ABI}) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/libs_export/${ANDROID_ABI}) #设置工程源码根目录 -set(ZLMediaKit_Root ${CMAKE_SOURCE_DIR}/../../../../../) -set(JNI_Root ${CMAKE_SOURCE_DIR}) +set(ZLMediaKit_Root ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../) +set(JNI_Root ${CMAKE_CURRENT_SOURCE_DIR}) set(ToolKit_Root ${ZLMediaKit_Root}/3rdpart/ZLToolKit/src) set(MediaKit_Root ${ZLMediaKit_Root}/src) diff --git a/Android/app/src/main/cpp/test_server.cpp b/Android/app/src/main/cpp/test_server.cpp index 2867919b..107ea29f 100644 --- a/Android/app/src/main/cpp/test_server.cpp +++ b/Android/app/src/main/cpp/test_server.cpp @@ -168,7 +168,7 @@ static void initEvent() { << args._vhost << " " << args._app << " " << args._streamid << " " << args._param_strs; - invoker("");//鉴权成功 + invoker("",true,true,false);//鉴权成功 //invoker("this is auth failed message");//鉴权失败 }); diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c1265ea..e95de067 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.1.3) #使能c++11 set(CMAKE_CXX_STANDARD 11) #加载自定义模块 -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake") +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") #set(CMAKE_BUILD_TYPE "Release") @@ -16,7 +16,7 @@ else() endif() #设置bin和lib库目录 -set(RELEASE_DIR ${CMAKE_SOURCE_DIR}/release) +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}) @@ -72,7 +72,7 @@ endif() if(ENABLE_MP4RECORD) message(STATUS "ENABLE_MP4RECORD defined") add_definitions(-DENABLE_MP4RECORD) - set(MediaServer_Root ${CMAKE_SOURCE_DIR}/3rdpart/media-server) + set(MediaServer_Root ${CMAKE_CURRENT_SOURCE_DIR}/3rdpart/media-server) list(APPEND LINK_LIB_LIST mov flv) endif() #查找openssl是否安装 diff --git a/README_CN.md b/README_CN.md index 504cf0b6..33a43a08 100644 --- a/README_CN.md +++ b/README_CN.md @@ -10,7 +10,7 @@ - 编码格式与框架代码解耦,方便自由简洁的添加支持其他编码格式 - 代码经过大量的稳定性、性能测试,可满足商用服务器项目。 - 支持linux、macos、ios、android、windows平台 -- 支持画面秒开(GOP缓存)、极低延时(1秒内) +- 支持画面秒开(GOP缓存)、极低延时([500毫秒内,最低可达100毫秒](https://github.com/zlmediakit/ZLMediaKit/wiki/%E5%BB%B6%E6%97%B6%E6%B5%8B%E8%AF%95)) - **支持websocket-flv直播** - [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) diff --git a/conf/config.ini b/conf/config.ini index 8a36fdf5..902d2215 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -28,6 +28,8 @@ maxStreamWaitMS=5000 #某个流无人观看时,触发hook.on_stream_none_reader事件的最大等待时间,单位毫秒 #在配合hook.on_stream_none_reader事件时,可以做到无人观看自动停止拉流或停止接收推流 streamNoneReaderDelayMS=5000 +#是否开启低延时模式,该模式下禁用MSG_MORE,启用TCP_NODEALY,延时将降低,但数据发送性能将降低 +ultraLowDelay=1 [hls] #hls写文件的buf大小,调整参数可以提高文件io性能 @@ -101,10 +103,11 @@ udpTTL=64 [record] #mp4录制或mp4点播的应用名,通过限制应用名,可以防止随意点播 +#点播的文件必须放置在此文件夹下 appName=record #mp4录制写文件缓存,单位BYTE,调整参数可以提高文件io性能 fileBufSize=65536 -#mp4录制保存路径 +#mp4录制保存、mp4点播根路径 filePath=/Users/xzl/git/ZLMediaKit/release/mac/Release/httpRoot #mp4录制切片时间,单位秒 fileSecond=3600 diff --git a/release/mac/Release/MediaServer b/release/mac/Release/MediaServer index a6c6cc71..52972b45 100755 Binary files a/release/mac/Release/MediaServer and b/release/mac/Release/MediaServer differ diff --git a/release/mac/Release/libflv.a b/release/mac/Release/libflv.a index 7271c9a2..01cefb84 100644 Binary files a/release/mac/Release/libflv.a and b/release/mac/Release/libflv.a differ diff --git a/release/mac/Release/libjsoncpp.a b/release/mac/Release/libjsoncpp.a index 7ce43685..ba198124 100644 Binary files a/release/mac/Release/libjsoncpp.a and b/release/mac/Release/libjsoncpp.a differ diff --git a/release/mac/Release/libmov.a b/release/mac/Release/libmov.a index 7baaf5d2..851f0992 100644 Binary files a/release/mac/Release/libmov.a and b/release/mac/Release/libmov.a differ diff --git a/release/mac/Release/libmpeg.a b/release/mac/Release/libmpeg.a index bf592422..efa4a05b 100644 Binary files a/release/mac/Release/libmpeg.a and b/release/mac/Release/libmpeg.a differ diff --git a/release/mac/Release/libzlmediakit.a b/release/mac/Release/libzlmediakit.a index 95a8b4a7..25137b64 100644 Binary files a/release/mac/Release/libzlmediakit.a and b/release/mac/Release/libzlmediakit.a differ diff --git a/release/mac/Release/libzltoolkit.a b/release/mac/Release/libzltoolkit.a index cd16d54d..ef27844d 100644 Binary files a/release/mac/Release/libzltoolkit.a and b/release/mac/Release/libzltoolkit.a differ diff --git a/release/mac/Release/test_benchmark b/release/mac/Release/test_benchmark index 0d834e47..8bb48093 100755 Binary files a/release/mac/Release/test_benchmark and b/release/mac/Release/test_benchmark differ diff --git a/release/mac/Release/test_httpApi b/release/mac/Release/test_httpApi index 6dfa9efd..8f0bb289 100755 Binary files a/release/mac/Release/test_httpApi and b/release/mac/Release/test_httpApi differ diff --git a/release/mac/Release/test_httpClient b/release/mac/Release/test_httpClient index 81a292bc..34de1647 100755 Binary files a/release/mac/Release/test_httpClient and b/release/mac/Release/test_httpClient differ diff --git a/release/mac/Release/test_player b/release/mac/Release/test_player index d73ef2df..a95a4b93 100755 Binary files a/release/mac/Release/test_player and b/release/mac/Release/test_player differ diff --git a/release/mac/Release/test_pusher b/release/mac/Release/test_pusher index 2b2fa37a..93dd60a1 100755 Binary files a/release/mac/Release/test_pusher and b/release/mac/Release/test_pusher differ diff --git a/release/mac/Release/test_pusherMp4 b/release/mac/Release/test_pusherMp4 index 153a9409..44ad50f1 100755 Binary files a/release/mac/Release/test_pusherMp4 and b/release/mac/Release/test_pusherMp4 differ diff --git a/release/mac/Release/test_server b/release/mac/Release/test_server index d173faf3..8b6d5e48 100755 Binary files a/release/mac/Release/test_server and b/release/mac/Release/test_server differ diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 5adac5c8..3209b439 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -581,7 +581,12 @@ void installWebApi() { ////////////以下是注册的Hook API//////////// API_REGIST(hook,on_publish,{ //开始推流事件 - throw SuccessException(); + //转换成rtsp或rtmp + val["enableRtxp"] = true; + //转换hls + val["enableHls"] = true; + //不录制mp4 + val["enableMP4"] = false; }); API_REGIST(hook,on_play,{ diff --git a/server/WebHook.cpp b/server/WebHook.cpp index 72fb7b37..e4c8945e 100644 --- a/server/WebHook.cpp +++ b/server/WebHook.cpp @@ -195,7 +195,7 @@ 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(""); + invoker("",true, true,false); return; } //异步执行该hook api,防止阻塞NoticeCenter @@ -205,7 +205,31 @@ void installWebHook(){ body["id"] = sender.getIdentifier(); //执行hook do_http_hook(hook_publish,body,[invoker](const Value &obj,const string &err){ - invoker(err); + if(err.empty()){ + //推流鉴权成功 + bool enableRtxp = true; + bool enableHls = true; + bool enableMP4 = false; + + //兼容用户不传递enableRtxp、enableHls、enableMP4参数 + if(obj.isMember("enableRtxp")){ + enableRtxp = obj["enableRtxp"].asBool(); + } + + if(obj.isMember("enableHls")){ + enableHls = obj["enableHls"].asBool(); + } + + if(obj.isMember("enableMP4")){ + enableMP4 = obj["enableMP4"].asBool(); + } + + invoker(err,enableRtxp,enableHls,enableMP4); + }else{ + //推流鉴权失败 + invoker(err,false, false, false); + } + }); }); diff --git a/src/Common/config.cpp b/src/Common/config.cpp index 63a22495..b8cbc458 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -76,11 +76,13 @@ const string kFlowThreshold = GENERAL_FIELD"flowThreshold"; const string kStreamNoneReaderDelayMS = GENERAL_FIELD"streamNoneReaderDelayMS"; const string kMaxStreamWaitTimeMS = GENERAL_FIELD"maxStreamWaitMS"; const string kEnableVhost = GENERAL_FIELD"enableVhost"; +const string kUltraLowDelay = GENERAL_FIELD"ultraLowDelay"; onceToken token([](){ mINI::Instance()[kFlowThreshold] = 1024; mINI::Instance()[kStreamNoneReaderDelayMS] = 5 * 1000; mINI::Instance()[kMaxStreamWaitTimeMS] = 5 * 1000; mINI::Instance()[kEnableVhost] = 1; + mINI::Instance()[kUltraLowDelay] = 1; },nullptr); }//namespace General diff --git a/src/Common/config.h b/src/Common/config.h index 47c06bfd..7e115263 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -92,13 +92,20 @@ extern const string kBroadcastOnGetRtspRealm; extern const string kBroadcastOnRtspAuth; #define BroadcastOnRtspAuthArgs const MediaInfo &args,const string &realm,const string &user_name,const bool &must_no_encrypt,const RtspSession::onAuth &invoker,TcpSession &sender -//鉴权结果回调对象 +//推流鉴权结果回调对象 //如果errMessage为空则代表鉴权成功 -typedef std::function AuthInvoker; +//enableHls: 是否允许转换hls +//enableMP4: 是否运行MP4录制 +//enableRtxp: rtmp推流时是否运行转rtsp;rtsp推流时,是否允许转rtmp +typedef std::function PublishAuthInvoker; //收到rtsp/rtmp推流事件广播,通过该事件控制推流鉴权 extern const string kBroadcastMediaPublish; -#define BroadcastMediaPublishArgs const MediaInfo &args,const Broadcast::AuthInvoker &invoker,TcpSession &sender +#define BroadcastMediaPublishArgs const MediaInfo &args,const Broadcast::PublishAuthInvoker &invoker,TcpSession &sender + +//播放鉴权结果回调对象 +//如果errMessage为空则代表鉴权成功 +typedef std::function AuthInvoker; //播放rtsp/rtmp/http-flv事件广播,通过该事件控制播放鉴权 extern const string kBroadcastMediaPlayed; @@ -168,6 +175,8 @@ extern const string kStreamNoneReaderDelayMS; extern const string kMaxStreamWaitTimeMS; //是否启动虚拟主机 extern const string kEnableVhost; +//超低延时模式,默认打开,打开后会降低延时但是转发性能会稍差 +extern const string kUltraLowDelay; }//namespace General diff --git a/src/Extension/Factory.cpp b/src/Extension/Factory.cpp index 36dbc772..b4889f33 100644 --- a/src/Extension/Factory.cpp +++ b/src/Extension/Factory.cpp @@ -30,6 +30,7 @@ #include "H264Rtp.h" #include "AACRtp.h" #include "H265Rtp.h" +#include "Common/Parser.h" namespace mediakit{ @@ -76,26 +77,14 @@ Track::Ptr Factory::getTrackBySdp(const SdpTrack::Ptr &track) { if (strcasecmp(track->_codec.data(), "h265") == 0) { //a=fmtp:96 sprop-sps=QgEBAWAAAAMAsAAAAwAAAwBdoAKAgC0WNrkky/AIAAADAAgAAAMBlQg=; sprop-pps=RAHA8vA8kAA= - int pt, id; - char sprop_vps[128] = {0},sprop_sps[128] = {0},sprop_pps[128] = {0}; - if (5 == sscanf(track->_fmtp.data(), "%d profile-id=%d; sprop-sps=%127[^;]; sprop-pps=%127[^;]; sprop-vps=%127[^;]", &pt, &id, sprop_sps,sprop_pps, sprop_vps)) { - auto vps = decodeBase64(sprop_vps); - auto sps = decodeBase64(sprop_sps); - auto pps = decodeBase64(sprop_pps); - return std::make_shared(vps,sps,pps,0,0,0); + auto map = Parser::parseArgs(track->_fmtp," ","="); + for(auto &pr : map){ + trim(pr.second," ;"); } - if (4 == sscanf(track->_fmtp.data(), "%d sprop-vps=%127[^;]; sprop-sps=%127[^;]; sprop-pps=%127[^;]", &pt, sprop_vps,sprop_sps, sprop_pps)) { - auto vps = decodeBase64(sprop_vps); - auto sps = decodeBase64(sprop_sps); - auto pps = decodeBase64(sprop_pps); - return std::make_shared(vps,sps,pps,0,0,0); - } - if (3 == sscanf(track->_fmtp.data(), "%d sprop-sps=%127[^;]; sprop-pps=%127[^;]", &pt,sprop_sps, sprop_pps)) { - auto sps = decodeBase64(sprop_sps); - auto pps = decodeBase64(sprop_pps); - return std::make_shared("",sps,pps,0,0,0); - } - return std::make_shared(); + auto vps = decodeBase64(map["sprop-vps"]); + auto sps = decodeBase64(map["sprop-sps"]); + auto pps = decodeBase64(map["sprop-pps"]); + return std::make_shared(vps,sps,pps,0,0,0); } diff --git a/src/Extension/H264Rtp.cpp b/src/Extension/H264Rtp.cpp index 8a961e34..367b3fce 100644 --- a/src/Extension/H264Rtp.cpp +++ b/src/Extension/H264Rtp.cpp @@ -238,38 +238,51 @@ void H264RtpEncoder::inputFrame(const Frame::Ptr &frame) { auto pcData = frame->data() + frame->prefixSize(); auto uiStamp = frame->stamp(); auto iLen = frame->size() - frame->prefixSize(); - unsigned char naluType = H264_TYPE(pcData[0]); //获取NALU的5bit 帧类型 + //获取NALU的5bit 帧类型 + unsigned char naluType = H264_TYPE(pcData[0]); uiStamp %= cycleMS; int iSize = _ui32MtuSize - 2; - if (iLen > iSize) { //超过MTU - const unsigned char s_e_r_Start = 0x80; - const unsigned char s_e_r_Mid = 0x00; - const unsigned char s_e_r_End = 0x40; - //获取帧头数据,1byte - unsigned char nal_ref_idc = *((unsigned char *) pcData) & 0x60; //获取NALU的2bit 帧重要程度 00 可以丢 11不能丢 - //nal_ref_idc = 0x60; - //组装FU-A帧头数据 2byte - unsigned char f_nri_type = nal_ref_idc + 28;//F为0 1bit,nri上面获取到2bit,28为FU-A分片类型5bit - unsigned char s_e_r_type = naluType; + //超过MTU则按照FU-A模式打包 + if (iLen > iSize) { + //最高位bit为forbidden_zero_bit, + //后面2bit为nal_ref_idc(帧重要程度),00:可以丢,11:不能丢 + //末尾5bit为nalu type,固定为28(FU-A) + unsigned char f_nri_flags = (*((unsigned char *) pcData) & 0x60) | 28; + unsigned char s_e_r_flags; bool bFirst = true; bool mark = false; int nOffset = 1; while (!mark) { - if (iLen < nOffset + iSize) { //是否拆分结束 + if (iLen < nOffset + iSize) { + //已经拆分结束 iSize = iLen - nOffset; mark = true; - s_e_r_type = s_e_r_End + naluType; - } else if (bFirst) { - s_e_r_type = s_e_r_Start + naluType; + //FU-A end + s_e_r_flags = (1 << 6) | naluType; + } else if (bFirst) { + //FU-A start + s_e_r_flags = (1 << 7) | naluType; } else { - s_e_r_type = s_e_r_Mid + naluType; + //FU-A mid + s_e_r_flags = naluType; + } + + { + //传入nullptr先不做payload的内存拷贝 + auto rtp = makeRtp(getTrackType(), nullptr, iSize + 2, mark, uiStamp); + //rtp payload 负载部分 + uint8_t *payload = (uint8_t*)rtp->data() + rtp->offset; + //FU-A 第1个字节 + payload[0] = f_nri_flags; + //FU-A 第2个字节 + payload[1] = s_e_r_flags; + //H264 数据 + memcpy(payload + 2, (unsigned char *) pcData + nOffset, iSize); + //输入到rtp环形缓存 + RtpCodec::inputRtp(rtp,bFirst && naluType == H264Frame::NAL_IDR); } - memcpy(_aucSectionBuf, &f_nri_type, 1); - memcpy(_aucSectionBuf + 1, &s_e_r_type, 1); - memcpy(_aucSectionBuf + 2, (unsigned char *) pcData + nOffset, iSize); nOffset += iSize; - makeH264Rtp(naluType,_aucSectionBuf, iSize + 2, mark,bFirst, uiStamp); bFirst = false; } } else { diff --git a/src/Extension/H264Rtp.h b/src/Extension/H264Rtp.h index 94ad7c46..5ccac81d 100644 --- a/src/Extension/H264Rtp.h +++ b/src/Extension/H264Rtp.h @@ -97,8 +97,6 @@ public: void inputFrame(const Frame::Ptr &frame) override; private: void makeH264Rtp(int nal_type,const void *pData, unsigned int uiLen, bool bMark, bool first_packet, uint32_t uiStamp); -private: - unsigned char _aucSectionBuf[1600]; }; }//namespace mediakit{ diff --git a/src/Extension/H265.h b/src/Extension/H265.h index 295a98ac..021ef3d1 100644 --- a/src/Extension/H265.h +++ b/src/Extension/H265.h @@ -267,8 +267,10 @@ public: * @param frame 数据帧 */ void inputFrame(const Frame::Ptr &frame) override{ - bool first_frame = true; - splitH264(frame->data() + frame->prefixSize(), + int type = H265_TYPE(*((uint8_t *)frame->data() + frame->prefixSize())); + if(type == H265Frame::NAL_VPS){ + bool first_frame = true; + splitH264(frame->data() + frame->prefixSize(), frame->size() - frame->prefixSize(), [&](const char *ptr, int len){ if(first_frame){ @@ -286,6 +288,9 @@ public: inputFrame_l(sub_frame); } }); + }else{ + inputFrame_l(frame); + } } private: diff --git a/src/Extension/H265Rtp.cpp b/src/Extension/H265Rtp.cpp index 4fc00042..6ac9e7e0 100644 --- a/src/Extension/H265Rtp.cpp +++ b/src/Extension/H265Rtp.cpp @@ -177,9 +177,10 @@ void H265RtpEncoder::inputFrame(const Frame::Ptr &frame) { uiStamp %= cycleMS; int maxSize = _ui32MtuSize - 3; - if (iLen > maxSize) { //超过MTU + //超过MTU,按照FU方式打包 + if (iLen > maxSize) { //获取帧头数据,1byte - unsigned char s_e_type; + unsigned char s_e_flags; bool bFirst = true; bool mark = false; int nOffset = 2; @@ -187,20 +188,34 @@ void H265RtpEncoder::inputFrame(const Frame::Ptr &frame) { if (iLen < nOffset + maxSize) { //是否拆分结束 maxSize = iLen - nOffset; mark = true; - s_e_type = 1 << 6 | naluType; + //FU end + s_e_flags = (1 << 6) | naluType; } else if (bFirst) { - s_e_type = 1 << 7 | naluType; + //FU start + s_e_flags = (1 << 7) | naluType; } else { - s_e_type = naluType; + //FU mid + s_e_flags = naluType; + } + + { + //传入nullptr先不做payload的内存拷贝 + auto rtp = makeRtp(getTrackType(), nullptr, maxSize + 3, mark, uiStamp); + //rtp payload 负载部分 + uint8_t *payload = (uint8_t*)rtp->data() + rtp->offset; + //FU 第1个字节,表明为FU + payload[0] = 49 << 1; + //FU 第2个字节貌似固定为1 + payload[1] = 1; + //FU 第3个字节 + payload[2] = s_e_flags; + //H265 数据 + memcpy(payload + 3,pcData + nOffset, maxSize); + //输入到rtp环形缓存 + RtpCodec::inputRtp(rtp,bFirst && H265Frame::isKeyFrame(naluType)); } - //FU type - _aucSectionBuf[0] = 49 << 1; - _aucSectionBuf[1] = 1; - _aucSectionBuf[2] = s_e_type; - memcpy(_aucSectionBuf + 3, pcData + nOffset, maxSize); nOffset += maxSize; - makeH265Rtp(naluType,_aucSectionBuf, maxSize + 3, mark,bFirst, uiStamp); bFirst = false; } } else { diff --git a/src/Extension/H265Rtp.h b/src/Extension/H265Rtp.h index fbe84031..8b51f880 100644 --- a/src/Extension/H265Rtp.h +++ b/src/Extension/H265Rtp.h @@ -98,8 +98,6 @@ public: void inputFrame(const Frame::Ptr &frame) override; private: void makeH265Rtp(int nal_type,const void *pData, unsigned int uiLen, bool bMark, bool first_packet,uint32_t uiStamp); -private: - unsigned char _aucSectionBuf[1600]; }; }//namespace mediakit{ diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp index 4f9118c5..b986e7ac 100644 --- a/src/Http/HttpSession.cpp +++ b/src/Http/HttpSession.cpp @@ -48,7 +48,6 @@ using namespace toolkit; namespace mediakit { -static int kSockFlags = SOCKET_DEFAULE_FLAGS | FLAG_MORE; static int kHlsCookieSecond = 10 * 60; static const string kCookieName = "ZL_COOKIE"; static const string kCookiePathKey = "kCookiePathKey"; @@ -283,10 +282,9 @@ inline bool HttpSession::checkLiveFlvStream(const function &cb){ cb(); } - //开始发送rtmp负载 - //关闭tcp_nodelay ,优化性能 - SockUtil::setNoDelay(_sock->rawFD(),false); - (*this) << SocketFlags(kSockFlags); + //http-flv直播牺牲延时提升发送性能 + setSocketFlags(); + try{ start(getPoller(),rtmp_src); }catch (std::exception &ex){ @@ -393,26 +391,30 @@ inline void HttpSession::canAccessPath(const string &path_in,bool is_dir,const f 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]; - if(path.find((*cookie)[kCookiePathKey]) == 0){ + auto accessErr = (*cookie)[kAccessErrKey].get(); + auto cookiePath = (*cookie)[kCookiePathKey].get(); + if(path.find(cookiePath) == 0){ //上次cookie是限定本目录 if(accessErr.empty()){ //上次鉴权成功 - callback("", nullptr); + callback("", cookie_from_header ? nullptr : cookie); return; } - //上次鉴权失败,如果url发生变更,那么也重新鉴权 + //上次鉴权失败,但是如果url参数发生变更,那么也重新鉴权下 if (_parser.Params().empty() || _parser.Params() == cookie->getUid()) { - //url参数未变,那么判断无权限访问 - callback(accessErr.empty() ? "无权限访问该目录" : accessErr.get(), nullptr); + //url参数未变,或者本来就没有url参数,那么判断本次请求为重复请求,无访问权限 + callback(accessErr, cookie_from_header ? nullptr : cookie); return; } } @@ -593,12 +595,10 @@ inline void HttpSession::Handle_Req_GET(int64_t &content_len) { //分节下载返回Content-Range头 httpHeader.emplace("Content-Range",StrPrinter<<"bytes " << iRangeStart << "-" << iRangeEnd << "/" << tFileStat.st_size<< endl); } - auto Origin = parser["Origin"]; - if(!Origin.empty()){ - httpHeader["Access-Control-Allow-Origin"] = Origin; - httpHeader["Access-Control-Allow-Credentials"] = "true"; - } + if(cookie){ + httpHeader["Set-Cookie"] = cookie->getCookie((*cookie)[kCookiePathKey].get()); + } //先回复HTTP头部分 sendResponse(pcHttpResult,httpHeader,""); @@ -657,10 +657,9 @@ inline void HttpSession::Handle_Req_GET(int64_t &content_len) { } return false; }; - //关闭tcp_nodelay ,优化性能 - SockUtil::setNoDelay(_sock->rawFD(),false); - //设置MSG_MORE,优化性能 - (*this) << SocketFlags(kSockFlags); + + //文件下载提升发送性能 + setSocketFlags(); onFlush(); _sock->setOnFlush(onFlush); @@ -950,6 +949,15 @@ inline void HttpSession::sendNotFound(bool bClose) { sendResponse("404 Not Found", makeHttpHeader(bClose, notFound.size()), notFound); } +void HttpSession::setSocketFlags(){ + GET_CONFIG(bool,ultraLowDelay,General::kUltraLowDelay); + if(!ultraLowDelay) { + //推流模式下,关闭TCP_NODELAY会增加推流端的延时,但是服务器性能将提高 + SockUtil::setNoDelay(_sock->rawFD(), false); + //播放模式下,开启MSG_MORE会增加延时,但是能提高发送性能 + (*this) << SocketFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE); + } +} void HttpSession::onWrite(const Buffer::Ptr &buffer) { _ticker.resetTime(); diff --git a/src/Http/HttpSession.h b/src/Http/HttpSession.h index d99bff95..4ab9dcad 100644 --- a/src/Http/HttpSession.h +++ b/src/Http/HttpSession.h @@ -142,6 +142,9 @@ private: * @return */ inline string getClientUid(); + + //设置socket标志 + void setSocketFlags(); private: string _origin; Parser _parser; diff --git a/src/MediaFile/Stamp.cpp b/src/MediaFile/Stamp.cpp index 57045733..cf89e6e4 100644 --- a/src/MediaFile/Stamp.cpp +++ b/src/MediaFile/Stamp.cpp @@ -49,7 +49,7 @@ void Stamp::revise(uint32_t dts, uint32_t pts, int64_t &dts_out, int64_t &pts_ou //相对时间戳 dts_out = dts - _start_dts; - if(dts_out < _dts_inc){ + if(dts_out < _dts_inc && !_playback){ //本次相对时间戳竟然小于上次? if(dts_out < 0 || _dts_inc - dts_out > 0xFFFF){ //时间戳回环,保证下次相对时间戳与本次相对合理增长 @@ -77,4 +77,8 @@ void Stamp::revise(uint32_t dts, uint32_t pts, int64_t &dts_out, int64_t &pts_ou } } +void Stamp::setPlayBack(bool playback) { + _playback = playback; +} + }//namespace mediakit \ No newline at end of file diff --git a/src/MediaFile/Stamp.h b/src/MediaFile/Stamp.h index 4e6241b1..8616f8a7 100644 --- a/src/MediaFile/Stamp.h +++ b/src/MediaFile/Stamp.h @@ -39,8 +39,23 @@ class Stamp { public: Stamp() = default; ~Stamp() = default; + + /** + * 设置回放模式,回放模式时间戳可以回退 + * @param playback 是否为回放模式 + */ + void setPlayBack(bool playback = true); + + /** + * 修正时间戳 + * @param dts 输入dts,如果为0则根据系统时间戳生成 + * @param pts 输入pts,如果为0则等于dts + * @param dts_out 输出dts + * @param pts_out 输出pts + */ void revise(uint32_t dts, uint32_t pts, int64_t &dts_out, int64_t &pts_out); private: + bool _playback = false; int64_t _start_dts = 0; int64_t _dts_inc = 0; bool _first = true; diff --git a/src/Rtmp/RtmpPusher.cpp b/src/Rtmp/RtmpPusher.cpp index 65e17047..834276a7 100644 --- a/src/Rtmp/RtmpPusher.cpp +++ b/src/Rtmp/RtmpPusher.cpp @@ -33,8 +33,6 @@ using namespace mediakit::Client; namespace mediakit { -static int kSockFlags = SOCKET_DEFAULE_FLAGS | FLAG_MORE; - RtmpPusher::RtmpPusher(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &src) : TcpClient(poller){ _pMediaSrc=src; } @@ -229,10 +227,19 @@ inline void RtmpPusher::send_metaData(){ } }); onPublishResult(SockException(Err_success,"success")); - //提高发送性能 - (*this) << SocketFlags(kSockFlags); - SockUtil::setNoDelay(_sock->rawFD(),false); + //提升发送性能 + setSocketFlags(); } + +void RtmpPusher::setSocketFlags(){ + GET_CONFIG(bool,ultraLowDelay,General::kUltraLowDelay); + if(!ultraLowDelay) { + //提高发送性能 + (*this) << SocketFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE); + SockUtil::setNoDelay(_sock->rawFD(), false); + } +} + void RtmpPusher::onCmd_result(AMFDecoder &dec){ auto iReqId = dec.load(); lock_guard lck(_mtxOnResultCB); diff --git a/src/Rtmp/RtmpPusher.h b/src/Rtmp/RtmpPusher.h index 7a589cf9..bc696c2e 100644 --- a/src/Rtmp/RtmpPusher.h +++ b/src/Rtmp/RtmpPusher.h @@ -84,7 +84,7 @@ private: inline void send_createStream(); inline void send_publish(); inline void send_metaData(); - + void setSocketFlags(); private: string _strApp; string _strStream; diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index b1a045b1..d9c6e1d4 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -31,8 +31,6 @@ namespace mediakit { -static int kSockFlags = SOCKET_DEFAULE_FLAGS | FLAG_MORE; - RtmpSession::RtmpSession(const Socket::Ptr &pSock) : TcpSession(pSock) { DebugP(this); GET_CONFIG(uint32_t,keep_alive_sec,Rtmp::kKeepAliveSecond); @@ -142,10 +140,10 @@ void RtmpSession::onCmd_publish(AMFDecoder &dec) { } })); dec.load();/* NULL */ - _mediaInfo.parse(_strTcUrl + "/" + dec.load()); + _mediaInfo.parse(_strTcUrl + "/" + getStreamId(dec.load())); _mediaInfo._schema = RTMP_SCHEMA; - auto onRes = [this,pToken](const string &err){ + auto onRes = [this,pToken](const string &err,bool enableRtxp,bool enableHls,bool enableMP4){ auto src = dynamic_pointer_cast(MediaSource::find(RTMP_SCHEMA, _mediaInfo._vhost, _mediaInfo._app, @@ -169,21 +167,25 @@ void RtmpSession::onCmd_publish(AMFDecoder &dec) { } _pPublisherSrc.reset(new RtmpToRtspMediaSource(_mediaInfo._vhost,_mediaInfo._app,_mediaInfo._streamid)); _pPublisherSrc->setListener(dynamic_pointer_cast(shared_from_this())); + //设置转协议 + _pPublisherSrc->setProtocolTranslation(enableRtxp,enableHls,enableMP4); + //如果是rtmp推流客户端,那么加大TCP接收缓存,这样能提升接收性能 _sock->setReadBuffer(std::make_shared(256 * 1024)); + setSocketFlags(); }; - Broadcast::AuthInvoker invoker = [weakSelf,onRes,pToken](const string &err){ + Broadcast::PublishAuthInvoker invoker = [weakSelf,onRes,pToken](const string &err,bool enableRtxp,bool enableHls,bool enableMP4){ auto strongSelf = weakSelf.lock(); if(!strongSelf){ return; } - strongSelf->async([weakSelf,onRes,err,pToken](){ + strongSelf->async([weakSelf,onRes,err,pToken,enableRtxp,enableHls,enableMP4](){ auto strongSelf = weakSelf.lock(); if(!strongSelf){ return; } - onRes(err); + onRes(err,enableRtxp,enableHls,enableMP4); }); }; auto flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPublish, @@ -192,7 +194,7 @@ void RtmpSession::onCmd_publish(AMFDecoder &dec) { *this); if(!flag){ //该事件无人监听,默认鉴权成功 - onRes(""); + onRes("",true,true,false); } } @@ -272,7 +274,6 @@ void RtmpSession::sendPlayResponse(const string &err,const RtmpMediaSource::Ptr _pRingReader = src->getRing()->attach(getPoller()); weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - SockUtil::setNoDelay(_sock->rawFD(), false); _pRingReader->setReadCB([weakSelf](const RtmpPacket::Ptr &pkt) { auto strongSelf = weakSelf.lock(); if (!strongSelf) { @@ -291,10 +292,8 @@ void RtmpSession::sendPlayResponse(const string &err,const RtmpMediaSource::Ptr if (src->readerCount() == 1) { src->seekTo(0); } - - //提高发送性能 - (*this) << SocketFlags(kSockFlags); - SockUtil::setNoDelay(_sock->rawFD(),false); + //提高服务器发送性能 + setSocketFlags(); } void RtmpSession::doPlayResponse(const string &err,const std::function &cb){ @@ -348,9 +347,41 @@ void RtmpSession::doPlay(AMFDecoder &dec){ void RtmpSession::onCmd_play2(AMFDecoder &dec) { doPlay(dec); } + +string RtmpSession::getStreamId(const string &str){ + string stream_id; + string params; + auto pos = str.find('?'); + if(pos != string::npos){ + //有url参数 + stream_id = str.substr(0,pos); + //获取url参数 + params = str.substr(pos + 1); + }else{ + //没有url参数 + stream_id = str; + } + + pos = stream_id.find(":"); + if(pos != string::npos){ + //vlc和ffplay在播放 rtmp://127.0.0.1/record/0.mp4时, + //传过来的url会是rtmp://127.0.0.1/record/mp4:0, + //我们在这里还原成0.mp4 + stream_id = stream_id.substr(pos + 1) + "." + stream_id.substr(0,pos); + } + + if(params.empty()){ + //没有url参数 + return stream_id; + } + + //有url参数 + return stream_id + '?' + params; +} + void RtmpSession::onCmd_play(AMFDecoder &dec) { dec.load();/* NULL */ - _mediaInfo.parse(_strTcUrl + "/" + dec.load()); + _mediaInfo.parse(_strTcUrl + "/" + getStreamId(dec.load())); _mediaInfo._schema = RTMP_SCHEMA; doPlay(dec); } @@ -464,6 +495,8 @@ void RtmpSession::onCmd_seek(AMFDecoder &dec) { InfoP(this) << "rtmp seekTo(ms):" << milliSeconds; auto stongSrc = _pPlayerSrc.lock(); if (stongSrc) { + _stamp[0].setPlayBack(); + _stamp[1].setPlayBack(); stongSrc->seekTo(milliSeconds); } AMFValue status(AMF_OBJECT); @@ -500,4 +533,13 @@ void RtmpSession::onNoneReader(MediaSource &sender) { MediaSourceEvent::onNoneReader(sender); } +void RtmpSession::setSocketFlags(){ + GET_CONFIG(bool,ultraLowDelay,General::kUltraLowDelay); + if(!ultraLowDelay) { + //推流模式下,关闭TCP_NODELAY会增加推流端的延时,但是服务器性能将提高 + SockUtil::setNoDelay(_sock->rawFD(), false); + //播放模式下,开启MSG_MORE会增加延时,但是能提高发送性能 + (*this) << SocketFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE); + } +} } /* namespace mediakit */ diff --git a/src/Rtmp/RtmpSession.h b/src/Rtmp/RtmpSession.h index fbb8cf38..6e6d97fc 100644 --- a/src/Rtmp/RtmpSession.h +++ b/src/Rtmp/RtmpSession.h @@ -85,13 +85,15 @@ private: bool close(MediaSource &sender,bool force) override ; void onNoneReader(MediaSource &sender) override; + void setSocketFlags(); + string getStreamId(const string &str); private: std::string _strTcUrl; MediaInfo _mediaInfo; 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/Rtmp/RtmpToRtspMediaSource.h b/src/Rtmp/RtmpToRtspMediaSource.h index 4b8138b6..153fc5c6 100644 --- a/src/Rtmp/RtmpToRtspMediaSource.h +++ b/src/Rtmp/RtmpToRtspMediaSource.h @@ -52,11 +52,7 @@ public: RtmpToRtspMediaSource(const string &vhost, const string &app, const string &id, - bool bEnableHls = true, - bool bEnableMp4 = false, int ringSize = 0) : RtmpMediaSource(vhost, app, id,ringSize){ - _bEnableHls = bEnableHls; - _bEnableMp4 = bEnableMp4; _demuxer = std::make_shared(); } virtual ~RtmpToRtspMediaSource(){} @@ -66,17 +62,17 @@ public: RtmpMediaSource::onGetMetaData(metadata); } - void onWrite(const RtmpPacket::Ptr &pkt,bool key_pos) override { + void onWrite(const RtmpPacket::Ptr &pkt,bool key_pos = true) override { _demuxer->inputRtmp(pkt); if(!_muxer && _demuxer->isInited(2000)){ _muxer = std::make_shared(getVhost(), getApp(), getId(), _demuxer->getDuration(), - true,//转rtsp + _enableRtsp, false,//不重复生成rtmp - _bEnableHls, - _bEnableMp4); + _enableHls, + _enableMP4); for (auto &track : _demuxer->getTracks(false)){ _muxer->addTrack(track); track->addDelegate(_muxer); @@ -107,11 +103,25 @@ public: } 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; + } private: RtmpDemuxer::Ptr _demuxer; MultiMediaSourceMuxer::Ptr _muxer; - bool _bEnableHls; - bool _bEnableMp4; + bool _enableHls = true; + bool _enableMP4 = false; + bool _enableRtsp = true; }; } /* namespace mediakit */ diff --git a/src/Rtsp/RtpCodec.cpp b/src/Rtsp/RtpCodec.cpp index fe1b6a5a..9e5d5a3c 100644 --- a/src/Rtsp/RtpCodec.cpp +++ b/src/Rtsp/RtpCodec.cpp @@ -49,8 +49,11 @@ RtpPacket::Ptr RtpInfo::makeRtp(TrackType type, const void* data, unsigned int l memcpy(&pucRtp[8], &ts, 4); //ssrc memcpy(&pucRtp[12], &sc, 4); - //playload - memcpy(&pucRtp[16], data, len); + + if(data){ + //playload + memcpy(&pucRtp[16], data, len); + } rtppkt->PT = _ui8PlayloadType; rtppkt->interleaved = _ui8Interleaved; diff --git a/src/Rtsp/RtspPusher.cpp b/src/Rtsp/RtspPusher.cpp index d468ddc8..7e2ca8c2 100644 --- a/src/Rtsp/RtspPusher.cpp +++ b/src/Rtsp/RtspPusher.cpp @@ -11,8 +11,6 @@ using namespace mediakit::Client; namespace mediakit { -static int kSockFlags = SOCKET_DEFAULE_FLAGS | FLAG_MORE; - RtspPusher::RtspPusher(const EventPoller::Ptr &poller,const RtspMediaSource::Ptr &src) : TcpClient(poller){ _pMediaSrc = src; } @@ -392,13 +390,21 @@ void RtspPusher::sendRecord() { },getPoller())); } onPublishResult(SockException(Err_success,"success")); - //提高发送性能 - (*this) << SocketFlags(kSockFlags); - SockUtil::setNoDelay(_sock->rawFD(),false); + //提升发送性能 + setSocketFlags(); }; sendRtspRequest("RECORD",_strContentBase,{"Range","npt=0.000-"}); } +void RtspPusher::setSocketFlags(){ + GET_CONFIG(bool,ultraLowDelay,General::kUltraLowDelay); + if(!ultraLowDelay) { + //提高发送性能 + (*this) << SocketFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE); + SockUtil::setNoDelay(_sock->rawFD(), false); + } +} + void RtspPusher::sendRtspRequest(const string &cmd, const string &url, const std::initializer_list &header,const string &sdp ) { string key; StrCaseMap header_map; diff --git a/src/Rtsp/RtspPusher.h b/src/Rtsp/RtspPusher.h index aa8232bd..958c1c87 100644 --- a/src/Rtsp/RtspPusher.h +++ b/src/Rtsp/RtspPusher.h @@ -67,6 +67,7 @@ private: void sendRtspRequest(const string &cmd, const string &url ,const std::initializer_list &header,const string &sdp = ""); void createUdpSockIfNecessary(int track_idx); + void setSocketFlags(); private: //rtsp鉴权相关 string _rtspMd5Nonce; diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index eb0fa6e8..65b24c4e 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -71,7 +71,6 @@ namespace mediakit { static unordered_map > g_mapGetter; //对g_mapGetter上锁保护 static recursive_mutex g_mtxGetter; -static int kSockFlags = SOCKET_DEFAULE_FLAGS | FLAG_MORE; RtspSession::RtspSession(const Socket::Ptr &pSock) : TcpSession(pSock) { DebugP(this); @@ -143,6 +142,12 @@ void RtspSession::onRecv(const Buffer::Ptr &pBuf) { } } +//字符串是否以xx结尾 +static inline bool end_of(const string &str, const string &substr){ + auto pos = str.rfind(substr); + return pos != string::npos && pos == str.size() - substr.size(); +}; + void RtspSession::onWholeRtspPacket(Parser &parser) { string strCmd = parser.Method(); //提取出请求命令字 _iCseq = atoi(parser["CSeq"].data()); @@ -242,6 +247,13 @@ void RtspSession::handleReq_ANNOUNCE(const Parser &parser) { throw SockException(Err_shutdown,err); } + auto full_url = parser.FullUrl(); + if(end_of(full_url,".sdp")){ + //去除.sdp后缀,防止EasyDarwin推流器强制添加.sdp后缀 + full_url = full_url.substr(0,full_url.length() - 4); + _mediaInfo.parse(full_url); + } + SdpParser sdpParser(parser.Content()); _strSession = makeRandStr(12); _aTrackInfo = sdpParser.getAvailableTrack(); @@ -249,7 +261,8 @@ 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()); - sendRtspResponse("200 OK"); + + sendRtspResponse("200 OK",{"Content-Base",_strContentBase + "/"}); } void RtspSession::handleReq_RECORD(const Parser &parser){ @@ -257,7 +270,7 @@ void RtspSession::handleReq_RECORD(const Parser &parser){ send_SessionNotFound(); throw SockException(Err_shutdown,_aTrackInfo.empty() ? "can not find any availabe track when record" : "session not found when record"); } - auto onRes = [this](const string &err){ + auto onRes = [this](const string &err,bool enableRtxp,bool enableHls,bool enableMP4){ bool authSuccess = err.empty(); if(!authSuccess){ sendRtspResponse("401 Unauthorized", {"Content-Type", "text/plain"}, err); @@ -265,6 +278,9 @@ void RtspSession::handleReq_RECORD(const Parser &parser){ return; } + //设置转协议 + _pushSrc->setProtocolTranslation(enableRtxp,enableHls,enableMP4); + _StrPrinter rtp_info; for(auto &track : _aTrackInfo){ if (track->_inited == false) { @@ -277,26 +293,25 @@ void RtspSession::handleReq_RECORD(const Parser &parser){ rtp_info.pop_back(); sendRtspResponse("200 OK", {"RTP-Info",rtp_info}); - SockUtil::setNoDelay(_sock->rawFD(),false); if(_rtpType == Rtsp::RTP_TCP){ //如果是rtsp推流服务器,并且是TCP推流,那么加大TCP接收缓存,这样能提升接收性能 _sock->setReadBuffer(std::make_shared(256 * 1024)); + setSocketFlags(); } - (*this) << SocketFlags(kSockFlags); }; weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - Broadcast::AuthInvoker invoker = [weakSelf,onRes](const string &err){ + Broadcast::PublishAuthInvoker invoker = [weakSelf,onRes](const string &err,bool enableRtxp,bool enableHls,bool enableMP4){ auto strongSelf = weakSelf.lock(); if(!strongSelf){ return; } - strongSelf->async([weakSelf,onRes,err](){ + strongSelf->async([weakSelf,onRes,err,enableRtxp,enableHls,enableMP4](){ auto strongSelf = weakSelf.lock(); if(!strongSelf){ return; } - onRes(err); + onRes(err,enableRtxp,enableHls,enableMP4); }); }; @@ -304,7 +319,7 @@ void RtspSession::handleReq_RECORD(const Parser &parser){ auto flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPublish,_mediaInfo,invoker,*this); if(!flag){ //该事件无人监听,默认不鉴权 - onRes(""); + onRes("",true,true,false); } } @@ -571,7 +586,7 @@ inline void RtspSession::send_SessionNotFound() { void RtspSession::handleReq_Setup(const Parser &parser) { //处理setup命令,该函数可能进入多次 - auto controlSuffix = split(parser.Url(),"/").back();// parser.FullUrl().substr(_strContentBase.size()); + auto controlSuffix = split(parser.FullUrl(),"/").back();// parser.FullUrl().substr(_strContentBase.size()); if(controlSuffix.front() == '/'){ controlSuffix = controlSuffix.substr(1); } @@ -780,10 +795,7 @@ void RtspSession::handleReq_Play(const Parser &parser) { }); _enableSendRtp = true; - - //提高发送性能 - SockUtil::setNoDelay(_sock->rawFD(),false); - (*this) << SocketFlags(kSockFlags); + setSocketFlags(); if (!_pRtpReader && _rtpType != Rtsp::RTP_MULTICAST) { weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); @@ -1230,6 +1242,16 @@ void RtspSession::sendSenderReport(bool overTcp,int iTrackIndex) { } } +void RtspSession::setSocketFlags(){ + GET_CONFIG(bool,ultraLowDelay,General::kUltraLowDelay); + if(!ultraLowDelay) { + //推流模式下,关闭TCP_NODELAY会增加推流端的延时,但是服务器性能将提高 + SockUtil::setNoDelay(_sock->rawFD(), false); + //播放模式下,开启MSG_MORE会增加延时,但是能提高发送性能 + (*this) << SocketFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE); + } +} + } /* namespace mediakit */ diff --git a/src/Rtsp/RtspSession.h b/src/Rtsp/RtspSession.h index 90ebc94f..33637666 100644 --- a/src/Rtsp/RtspSession.h +++ b/src/Rtsp/RtspSession.h @@ -183,6 +183,8 @@ private: bool sendRtspResponse(const string &res_code,const StrCaseMap &header = StrCaseMap(), const string &sdp = "",const char *protocol = "RTSP/1.0"); //服务器发送rtcp void sendSenderReport(bool overTcp,int iTrackIndex); + //设置socket标志 + void setSocketFlags(); private: //用于判断客户端是否超时 Ticker _ticker; diff --git a/src/Rtsp/RtspToRtmpMediaSource.h b/src/Rtsp/RtspToRtmpMediaSource.h index 04980dff..a05cb393 100644 --- a/src/Rtsp/RtspToRtmpMediaSource.h +++ b/src/Rtsp/RtspToRtmpMediaSource.h @@ -43,11 +43,7 @@ public: RtspToRtmpMediaSource(const string &vhost, const string &app, const string &id, - bool bEnableHls = true, - bool bEnableMp4 = false, int ringSize = 0) : RtspMediaSource(vhost, app, id,ringSize) { - _bEnableHls = bEnableHls; - _bEnableMp4 = bEnableMp4; } virtual ~RtspToRtmpMediaSource() {} @@ -66,9 +62,9 @@ public: getId(), _demuxer->getDuration(), false,//不重复生成rtsp - true,//转rtmp - _bEnableHls, - _bEnableMp4); + _enableRtmp, + _enableHls, + _enableMP4); for (auto &track : _demuxer->getTracks(false)) { _muxer->addTrack(track); track->addDelegate(_muxer); @@ -99,11 +95,25 @@ public: } return _demuxer->getTracks(trackReady); } + + /** + * 设置协议转换 + * @param enableRtmp 是否转换成rtmp + * @param enableHls 是否转换成hls + * @param enableMP4 是否mp4录制 + */ + void setProtocolTranslation(bool enableRtmp,bool enableHls,bool enableMP4){ +// DebugL << enableRtmp << " " << enableHls << " " << enableMP4; + _enableRtmp = enableRtmp; + _enableHls = enableHls; + _enableMP4 = enableMP4; + } private: RtspDemuxer::Ptr _demuxer; MultiMediaSourceMuxer::Ptr _muxer; - bool _bEnableHls; - bool _bEnableMp4; + bool _enableHls = true; + bool _enableMP4 = false; + bool _enableRtmp = true; }; } /* namespace mediakit */ diff --git a/tests/test_player.cpp b/tests/test_player.cpp index 142a6036..626c3858 100644 --- a/tests/test_player.cpp +++ b/tests/test_player.cpp @@ -40,6 +40,40 @@ using namespace toolkit; using 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){ + if(frame->prefixSize()){ + merged.append(frame->data(),frame->size()); + } else{ + merged.append("\x00\x00\x00\x01",4); + 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; +}; + + #ifdef WIN32 #include @@ -101,18 +135,26 @@ int main(int argc, char *argv[]) { SDLDisplayerHelper::Instance().doTask([frame,storage]() { auto &decoder = (*storage)["decoder"]; auto &displayer = (*storage)["displayer"]; + auto &merger = (*storage)["merger"]; if(!decoder){ decoder.set(); } if(!displayer){ displayer.set(); } + if(!merger){ + merger.set(); + }; + + merger.get().inputFrame(frame,[&](uint32_t dts,uint32_t pts,const Buffer::Ptr &buffer){ + AVFrame *pFrame = nullptr; + bool flag = decoder.get().inputVideo((unsigned char *) buffer->data(), buffer->size(), dts, &pFrame); + if (flag) { + displayer.get().displayYUV(pFrame); + } + }); + - AVFrame *pFrame = nullptr; - bool flag = decoder.get().inputVideo((unsigned char *) frame->data(), frame->size(), frame->stamp(), &pFrame); - if (flag) { - displayer.get().displayYUV(pFrame); - } return true; }); })); diff --git a/tests/test_server.cpp b/tests/test_server.cpp index 7aaac9f7..6ba58bd8 100644 --- a/tests/test_server.cpp +++ b/tests/test_server.cpp @@ -157,7 +157,7 @@ void initEventListener() { NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastMediaPublish, [](BroadcastMediaPublishArgs) { DebugL << "推流鉴权:" << args._schema << " " << args._vhost << " " << args._app << " " << args._streamid << " " << args._param_strs; - invoker("");//鉴权成功 + invoker("", true, true, false);//鉴权成功 //invoker("this is auth failed message");//鉴权失败 });