From b649372873732106316e8e767b6d45523a195845 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 30 Apr 2020 10:00:55 +0800 Subject: [PATCH 001/100] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dsplit=20264/265?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E9=80=BB=E8=BE=91=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Extension/H264.cpp | 55 +++++++++++++++++++++++++++++------------- src/Extension/H264.h | 25 ++++--------------- src/Extension/H265.h | 29 ++++++---------------- 3 files changed, 50 insertions(+), 59 deletions(-) diff --git a/src/Extension/H264.cpp b/src/Extension/H264.cpp index 7cfb086a..0a5314ae 100644 --- a/src/Extension/H264.cpp +++ b/src/Extension/H264.cpp @@ -44,20 +44,32 @@ const char *memfind(const char *buf, int len, const char *subbuf, int sublen) { return NULL; } -void splitH264(const char *ptr, int len, const std::function &cb) { - auto nal = ptr; +void splitH264(const char *ptr, int len, int prefix, const std::function &cb) { + auto start = ptr + prefix; auto end = ptr + len; - while(true) { - auto next_nal = memfind(nal + 3,end - nal - 3,"\x0\x0\x1",3); - if(next_nal){ - if(*(next_nal - 1) == 0x00){ - next_nal -= 1; + int next_prefix; + while (true) { + auto next_start = memfind(start, end - start, "\x00\x00\x01", 3); + if (next_start) { + //找到下一帧 + if (*(next_start - 1) == 0x00) { + //这个是00 00 00 01开头 + next_start -= 1; + next_prefix = 4; + } else { + //这个是00 00 01开头 + next_prefix = 3; } - cb(nal,next_nal - nal); - nal = next_nal; + //记得加上本帧prefix长度 + cb(start - prefix, next_start - start + prefix, prefix); + //搜索下一帧末尾的起始位置 + start = next_start + next_prefix; + //记录下一帧的prefix长度 + prefix = next_prefix; continue; } - cb(nal,end - nal); + //未找到下一帧,这是最后一帧 + cb(start - prefix, end - start + prefix, prefix); break; } } @@ -65,13 +77,22 @@ void splitH264(const char *ptr, int len, const std::function &cb); +void splitH264(const char *ptr, int len, int prefix, const std::function &cb); /** * 264帧类 @@ -243,25 +243,10 @@ public: int type = H264_TYPE(*((uint8_t *)frame->data() + frame->prefixSize())); if(type == H264Frame::NAL_SPS || type == H264Frame::NAL_SEI){ //有些设备会把SPS PPS IDR帧当做一个帧打包,所以我们要split一下 - bool first_frame = true; - splitH264(frame->data() + frame->prefixSize(), - frame->size() - frame->prefixSize(), - [&](const char *ptr, int len){ - if(first_frame){ - H264FrameInternal::Ptr sub_frame = std::make_shared(frame, - frame->data(), - len + frame->prefixSize(), - frame->prefixSize()); - inputFrame_l(sub_frame); - first_frame = false; - }else{ - H264FrameInternal::Ptr sub_frame = std::make_shared(frame, - (char *)ptr, - len , - 3); - inputFrame_l(sub_frame); - } - }); + splitH264(frame->data(), frame->size(), frame->prefixSize(), [&](const char *ptr, int len, int prefix) { + H264FrameInternal::Ptr sub_frame = std::make_shared(frame, (char *)ptr, len, prefix); + inputFrame_l(sub_frame); + }); } else{ inputFrame_l(frame); } diff --git a/src/Extension/H265.h b/src/Extension/H265.h index fca162c3..676af0b2 100644 --- a/src/Extension/H265.h +++ b/src/Extension/H265.h @@ -246,28 +246,13 @@ public: void inputFrame(const Frame::Ptr &frame) override{ int type = H265_TYPE(*((uint8_t *)frame->data() + frame->prefixSize())); if(frame->configFrame()){ - bool first_frame = true; - splitH264(frame->data() + frame->prefixSize(), - frame->size() - frame->prefixSize(), - [&](const char *ptr, int len){ - if(first_frame){ - H265FrameInternal::Ptr sub_frame = std::make_shared(frame, - frame->data(), - len + frame->prefixSize(), - frame->prefixSize()); - inputFrame_l(sub_frame); - first_frame = false; - }else{ - H265FrameInternal::Ptr sub_frame = std::make_shared(frame, - (char *)ptr, - len , - 3); - inputFrame_l(sub_frame); - } - }); - }else{ - inputFrame_l(frame); - } + splitH264(frame->data(), frame->size(), frame->prefixSize(), [&](const char *ptr, int len, int prefix){ + H265FrameInternal::Ptr sub_frame = std::make_shared(frame, (char*)ptr, len, prefix); + inputFrame_l(sub_frame); + }); + } else { + inputFrame_l(frame); + } } private: From c64d1a3ad858fe46b240a12f3c5359aa482618d5 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 30 Apr 2020 10:02:27 +0800 Subject: [PATCH 002/100] =?UTF-8?q?=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtp/UdpRecver.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Rtp/UdpRecver.cpp b/src/Rtp/UdpRecver.cpp index 365b78a3..2b4a6f3b 100644 --- a/src/Rtp/UdpRecver.cpp +++ b/src/Rtp/UdpRecver.cpp @@ -17,7 +17,9 @@ UdpRecver::UdpRecver() { } UdpRecver::~UdpRecver() { - _sock->setOnRead(nullptr); + if(_sock){ + _sock->setOnRead(nullptr); + } } bool UdpRecver::initSock(uint16_t local_port,const char *local_ip) { From 787081eb0cce3290cbf4af2a9995196c5d72d144 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 30 Apr 2020 10:31:32 +0800 Subject: [PATCH 003/100] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dwebsocket=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=99=A8=E5=8F=AF=E8=83=BD=E5=AD=98=E5=9C=A8=E7=9A=84?= =?UTF-8?q?bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/WebSocketSession.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/WebSocketSession.h b/src/Http/WebSocketSession.h index cddee17b..0c4ff278 100644 --- a/src/Http/WebSocketSession.h +++ b/src/Http/WebSocketSession.h @@ -205,7 +205,7 @@ protected: * @param buffer */ void onWebSocketEncodeData(const Buffer::Ptr &buffer) override{ - SocketHelper::send(buffer); + HttpSessionType::send(buffer); } private: string _remian_data; From 3ad1fe4924f2d936d58cb36a499b88582e807aac Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 30 Apr 2020 12:52:13 +0800 Subject: [PATCH 004/100] =?UTF-8?q?=E6=94=AF=E6=8C=81265=E8=A7=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/{H264Decoder.h => FFMpegDecoder.h} | 23 +++++++++++++++++------ tests/test_player.cpp | 20 ++++++++------------ 2 files changed, 25 insertions(+), 18 deletions(-) rename tests/{H264Decoder.h => FFMpegDecoder.h} (78%) diff --git a/tests/H264Decoder.h b/tests/FFMpegDecoder.h similarity index 78% rename from tests/H264Decoder.h rename to tests/FFMpegDecoder.h index 31dd8229..465a87de 100644 --- a/tests/H264Decoder.h +++ b/tests/FFMpegDecoder.h @@ -13,6 +13,7 @@ #include #include #include +#include "Extension/Frame.h" #ifdef __cplusplus extern "C" { #endif @@ -27,14 +28,24 @@ using namespace std; namespace mediakit { -class H264Decoder -{ +class FFMpegDecoder{ public: - H264Decoder(void){ + FFMpegDecoder(int codec_id){ + auto ff_codec_id = AV_CODEC_ID_H264; + switch (codec_id){ + case CodecH264: + ff_codec_id = AV_CODEC_ID_H264; + break; + case CodecH265: + ff_codec_id = AV_CODEC_ID_H265; + break; + default: + throw std::invalid_argument("不支持该编码格式"); + } avcodec_register_all(); - AVCodec *pCodec = avcodec_find_decoder(AV_CODEC_ID_H264); + AVCodec *pCodec = avcodec_find_decoder(ff_codec_id); if (!pCodec) { - throw std::runtime_error("未找到H264解码器"); + throw std::runtime_error("未找到解码器"); } m_pContext.reset(avcodec_alloc_context3(pCodec), [](AVCodecContext *pCtx) { avcodec_close(pCtx); @@ -57,7 +68,7 @@ public: throw std::runtime_error("创建帧缓存失败"); } } - virtual ~H264Decoder(void){} + virtual ~FFMpegDecoder(void){} bool inputVideo(unsigned char* data,unsigned int dataSize,uint32_t ui32Stamp,AVFrame **ppFrame){ AVPacket pkt; av_init_packet(&pkt); diff --git a/tests/test_player.cpp b/tests/test_player.cpp index ed15f8b5..2954a43c 100644 --- a/tests/test_player.cpp +++ b/tests/test_player.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. * * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). @@ -12,13 +12,12 @@ #include "Util/util.h" #include "Util/logger.h" #include -#include "Poller/EventPoller.h" #include "Rtsp/UDPServer.h" #include "Player/MediaPlayer.h" #include "Util/onceToken.h" -#include "H264Decoder.h" +#include "FFMpegDecoder.h" #include "YuvDisplayer.h" -#include "Network/sockutil.h" +#include "Extension/H265.h" using namespace std; using namespace toolkit; @@ -111,8 +110,8 @@ int main(int argc, char *argv[]) { } auto viedoTrack = strongPlayer->getTrack(TrackVideo); - if (!viedoTrack || viedoTrack->getCodecId() != CodecH264) { - WarnL << "没有视频或者视频不是264编码!"; + if (!viedoTrack) { + WarnL << "没有视频!"; return; } @@ -123,24 +122,21 @@ int main(int argc, char *argv[]) { auto &displayer = (*storage)["displayer"]; auto &merger = (*storage)["merger"]; if(!decoder){ - decoder.set(); + decoder.set(frame->getCodecId()); } if(!displayer){ displayer.set(nullptr,url); } 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); + bool flag = decoder.get().inputVideo((unsigned char *) buffer->data(), buffer->size(), dts, &pFrame); if (flag) { displayer.get().displayYUV(pFrame); } }); - - return true; }); })); From 920f06a9961de53b9aa542d93366a8f47f87c63b Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 30 Apr 2020 13:35:38 +0800 Subject: [PATCH 005/100] =?UTF-8?q?AAC=20sdp=E6=B7=BB=E5=8A=A0=E9=80=9A?= =?UTF-8?q?=E9=81=93=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Extension/AAC.cpp | 2 +- src/Extension/AAC.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Extension/AAC.cpp b/src/Extension/AAC.cpp index 686fdd57..282bd2f0 100644 --- a/src/Extension/AAC.cpp +++ b/src/Extension/AAC.cpp @@ -103,7 +103,7 @@ Sdp::Ptr AACTrack::getSdp() { WarnL << getCodecName() << " Track未准备好"; return nullptr; } - return std::make_shared(getAacCfg(),getAudioSampleRate()); + return std::make_shared(getAacCfg(),getAudioSampleRate(), getAudioChannel()); } }//namespace mediakit diff --git a/src/Extension/AAC.h b/src/Extension/AAC.h index faf69198..2a57d6d4 100644 --- a/src/Extension/AAC.h +++ b/src/Extension/AAC.h @@ -276,11 +276,12 @@ public: */ AACSdp(const string &aac_cfg, int sample_rate, + int channels, int playload_type = 98, int bitrate = 128) : Sdp(sample_rate,playload_type){ _printer << "m=audio 0 RTP/AVP " << playload_type << "\r\n"; _printer << "b=AS:" << bitrate << "\r\n"; - _printer << "a=rtpmap:" << playload_type << " MPEG4-GENERIC/" << sample_rate << "\r\n"; + _printer << "a=rtpmap:" << playload_type << " MPEG4-GENERIC/" << sample_rate << "/" << channels << "\r\n"; char configStr[32] = {0}; snprintf(configStr, sizeof(configStr), "%02X%02X", (uint8_t)aac_cfg[0], (uint8_t)aac_cfg[1]); From a4d7b3463e009750cdb08a0d15328a70afb3cb22 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 6 May 2020 18:54:28 +0800 Subject: [PATCH 006/100] =?UTF-8?q?rtmp=E6=92=AD=E6=94=BE=E6=97=B6?= =?UTF-8?q?=EF=BC=8C=E5=8F=96=E7=9B=B8=E5=AF=B9=E6=97=B6=E9=97=B4=E6=88=B3?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E7=A1=AE=E4=BF=9D=E6=97=B6=E9=97=B4=E6=88=B3?= =?UTF-8?q?=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/Stamp.cpp | 35 +++++++++++++++++++++++++++++++++++ src/Common/Stamp.h | 18 ++++++++++++++---- src/Rtmp/FlvMuxer.cpp | 3 +++ src/Rtmp/RtmpSession.cpp | 2 ++ 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/src/Common/Stamp.cpp b/src/Common/Stamp.cpp index ecc52e81..fa449721 100644 --- a/src/Common/Stamp.cpp +++ b/src/Common/Stamp.cpp @@ -42,7 +42,40 @@ void Stamp::setPlayBack(bool playback) { _playback = playback; } +void Stamp::makeRelation(Stamp &other){ + _related = &other; +} + void Stamp::revise(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp) { + revise_l(dts,pts,dts_out,pts_out,modifyStamp); + if(modifyStamp || _playback){ + //自动生成时间戳或回放,不需要做音视频同步 + return; + } + + if(_related && _related->_last_dts){ + //音视频dts当前时间差 + int64_t dts_diff = _last_dts - _related->_last_dts; + if(ABS(dts_diff) < 5000){ + //如果绝对时间戳小于5秒,那么说明他们的起始时间戳是一致的,那么强制同步 + _last_relativeStamp = _relativeStamp; + _relativeStamp = _related->_relativeStamp + dts_diff; + dts_out += dts_diff; + pts_out += dts_diff; +// DebugL << "音视频同步事件差:" << dts_diff; + } + //下次不用再强制同步 + _related = nullptr; + } + + if(dts_out < 0){ + //相对时间戳小于0,那么说明是同步时间戳导致的,在这个过渡期内,我们一直返回上次的结果(目的是为了防止时间戳回退) + pts_out = _last_relativeStamp + (pts_out - dts_out); + dts_out = _last_relativeStamp; + } +} + +void Stamp::revise_l(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp) { if(!pts){ //没有播放时间戳,使其赋值为解码时间戳 pts = dts; @@ -53,6 +86,7 @@ void Stamp::revise(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out, dts_out = dts; pts_out = pts; _relativeStamp = dts_out; + _last_dts = dts; return; } @@ -62,6 +96,7 @@ void Stamp::revise(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out, if(_last_dts != dts){ //时间戳发生变更 if(modifyStamp){ + //内部自己生产时间戳 _relativeStamp = _ticker.elapsedTime(); }else{ _relativeStamp += deltaStamp(dts); diff --git a/src/Common/Stamp.h b/src/Common/Stamp.h index 9e49f393..2b57f7c0 100644 --- a/src/Common/Stamp.h +++ b/src/Common/Stamp.h @@ -32,6 +32,7 @@ public: private: int64_t _last_stamp = 0; }; + //该类解决时间戳回环、回退问题 //计算相对时间戳或者产生平滑时间戳 class Stamp : public DeltaStamp{ @@ -66,14 +67,25 @@ public: * @param playback 是否为回放模式 */ void setPlayBack(bool playback = true); + + /** + * 产生关联,用于音视频同步用 + */ + void makeRelation(Stamp &other); + +private: + void revise_l(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp = false); private: int64_t _relativeStamp = 0; - int64_t _last_dts = -1; + int64_t _last_relativeStamp = 0; + int64_t _last_dts = 0; SmoothTicker _ticker; bool _playback = false; + Stamp *_related = nullptr; }; - +//dts生成器, +//pts排序后就是dts class DtsGenerator{ public: DtsGenerator() = default; @@ -90,8 +102,6 @@ private: int _sorter_max_size = 0; int _count_sorter_max_size = 0; set _pts_sorter; - - }; }//namespace mediakit diff --git a/src/Rtmp/FlvMuxer.cpp b/src/Rtmp/FlvMuxer.cpp index 94ad3996..456f679b 100644 --- a/src/Rtmp/FlvMuxer.cpp +++ b/src/Rtmp/FlvMuxer.cpp @@ -50,6 +50,9 @@ void FlvMuxer::start(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr & } strongSelf->onDetach(); }); + + //音频同步于视频 + _stamp[0].makeRelation( _stamp[1]); _ring_reader->setReadCB([weakSelf](const RtmpMediaSource::RingDataType &pkt){ auto strongSelf = weakSelf.lock(); if(!strongSelf){ diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index a062c5d6..873b1726 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -266,6 +266,8 @@ void RtmpSession::sendPlayResponse(const string &err,const RtmpMediaSource::Ptr onSendMedia(pkt); }); + //音频同步于视频 + _stamp[0].makeRelation( _stamp[1]); _pRingReader = src->getRing()->attach(getPoller()); weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); _pRingReader->setReadCB([weakSelf](const RtmpMediaSource::RingDataType &pkt) { From 683c8eef15cff0546c58e99fe1531d88f3164dcd Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 8 May 2020 09:52:05 +0800 Subject: [PATCH 007/100] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E7=BA=BF=E7=A8=8B=E7=9A=84=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mk_thread.h | 16 ++++++++++++++++ api/source/mk_thread.cpp | 9 +++++++++ 2 files changed, 25 insertions(+) diff --git a/api/include/mk_thread.h b/api/include/mk_thread.h index 87c651d1..d37f04c8 100644 --- a/api/include/mk_thread.h +++ b/api/include/mk_thread.h @@ -36,6 +36,22 @@ API_EXPORT mk_thread API_CALL mk_thread_from_tcp_session(mk_tcp_session ctx); */ API_EXPORT mk_thread API_CALL mk_thread_from_tcp_client(mk_tcp_client ctx); +/** + * 根据负载均衡算法,从事件线程池中随机获取一个事件线程 + * 如果在事件线程内执行此函数将返回本事件线程 + * 事件线程指的是定时器、网络io事件线程 + * @return 事件线程 + */ +API_EXPORT mk_thread API_CALL mk_thread_from_pool(); + +/** + * 根据负载均衡算法,从后台线程池中随机获取一个线程 + * 后台线程本质与事件线程相同,只是优先级更低,同时可以执行短时间的阻塞任务 + * ZLMediaKit中后台线程用于dns解析、mp4点播时的文件解复用 + * @return 后台线程 + */ +API_EXPORT mk_thread API_CALL mk_thread_from_pool_work(); + ///////////////////////////////////////////线程切换///////////////////////////////////////////// typedef void (API_CALL *on_mk_async)(void *user_data); diff --git a/api/source/mk_thread.cpp b/api/source/mk_thread.cpp index b73594e1..08778287 100644 --- a/api/source/mk_thread.cpp +++ b/api/source/mk_thread.cpp @@ -12,6 +12,7 @@ #include "mk_tcp_private.h" #include "Util/logger.h" #include "Poller/EventPoller.h" +#include "Thread/WorkThreadPool.h" using namespace std; using namespace toolkit; @@ -27,6 +28,14 @@ API_EXPORT mk_thread API_CALL mk_thread_from_tcp_client(mk_tcp_client ctx){ return (*client)->getPoller().get(); } +API_EXPORT mk_thread API_CALL mk_thread_from_pool(){ + return EventPollerPool::Instance().getPoller().get(); +} + +API_EXPORT mk_thread API_CALL mk_thread_from_pool_work(){ + return WorkThreadPool::Instance().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; From 3252f131ad1b550113490aa0a2916bcaa715cc2b Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 8 May 2020 21:57:56 +0800 Subject: [PATCH 008/100] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=9F=90=E4=BA=9Brtp?= =?UTF-8?q?=E5=8C=85=E4=B8=8D=E5=85=BC=E5=AE=B9=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtsp/RtpReceiver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Rtsp/RtpReceiver.cpp b/src/Rtsp/RtpReceiver.cpp index 2b6602f1..e7703de7 100644 --- a/src/Rtsp/RtpReceiver.cpp +++ b/src/Rtsp/RtpReceiver.cpp @@ -34,11 +34,11 @@ bool RtpReceiver::handleOneRtp(int track_index,SdpTrack::Ptr &track, unsigned ch } uint8_t padding = 0; - if (rtp_raw_ptr[0] & 0x40) { + if (rtp_raw_ptr[0] & 0x20) { //获取padding大小 padding = rtp_raw_ptr[rtp_raw_len - 1]; //移除padding flag - rtp_raw_ptr[0] &= ~0x40; + rtp_raw_ptr[0] &= ~0x20; //移除padding字节 rtp_raw_len -= padding; } From d8e5dbb5b89f45b896aef526b786b3829a822e9b Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 8 May 2020 21:59:49 +0800 Subject: [PATCH 009/100] =?UTF-8?q?=E5=B4=A9=E6=BA=83=E6=8D=95=E8=8E=B7?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E6=89=93=E5=8D=B0=E5=87=BD=E6=95=B0=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/System.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/System.cpp b/server/System.cpp index c92bcc70..f38b6fe1 100644 --- a/server/System.cpp +++ b/server/System.cpp @@ -52,7 +52,7 @@ string System::execute(const string &cmd) { #if !defined(ANDROID) && !defined(_WIN32) static string addr2line(const string &address) { - string cmd = StrPrinter << "addr2line -e " << exePath() << " " << address; + string cmd = StrPrinter << "addr2line -C -f -e " << exePath() << " " << address; return System::execute(cmd); } From 76bece0217d902c98593d6ab10f8b77f088b0440 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 9 May 2020 00:06:36 +0800 Subject: [PATCH 010/100] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=AE=9E=E6=97=B6?= =?UTF-8?q?=E7=94=9F=E6=88=90=E5=92=8C=E8=8E=B7=E5=8F=96=E6=88=AA=E5=9B=BE?= =?UTF-8?q?=E7=9A=84http=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/config.ini | 5 ++++ server/FFmpegSource.cpp | 36 ++++++++++++++++++++++-- server/FFmpegSource.h | 17 +++++++++++ server/WebApi.cpp | 62 ++++++++++++++++++++++++++++++++++++++++- 4 files changed, 117 insertions(+), 3 deletions(-) diff --git a/conf/config.ini b/conf/config.ini index 02fc096c..d87c9547 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -4,16 +4,21 @@ apiDebug=1 #一些比较敏感的http api在访问时需要提供secret,否则无权限调用 #如果是通过127.0.0.1访问,那么可以不提供secret secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc +#截图保存路径根目录,截图通过http api(/index/api/makeSnap)生成和获取 +snapRoot=./www/snap/ [ffmpeg] #FFmpeg可执行程序绝对路径 bin=/usr/local/bin/ffmpeg #FFmpeg拉流再推流的命令模板,通过该模板可以设置再编码的一些参数 cmd=%s -re -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s +#FFmpeg生成截图的命令,可以通过修改该配置改变截图分辨率或质量 +snap=%s -i %s -y -f mjpeg -t 0.001 %s #FFmpeg日志的路径,如果置空则不生成FFmpeg日志 #可以为相对(相对于本可执行程序目录)或绝对路径 log=./ffmpeg/ffmpeg.log + [general] #是否启用虚拟主机 enableVhost=0 diff --git a/server/FFmpegSource.cpp b/server/FFmpegSource.cpp index 12e96345..ec9e918d 100644 --- a/server/FFmpegSource.cpp +++ b/server/FFmpegSource.cpp @@ -13,21 +13,25 @@ #include "Common/MediaSource.h" #include "Util/File.h" #include "System.h" +#include "Thread/WorkThreadPool.h" namespace FFmpeg { #define FFmpeg_FIELD "ffmpeg." const string kBin = FFmpeg_FIELD"bin"; const string kCmd = FFmpeg_FIELD"cmd"; const string kLog = FFmpeg_FIELD"log"; +const string kSnap = FFmpeg_FIELD"snap"; onceToken token([]() { #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 "; + mINI::Instance()[kCmd] = "%s -re -i %s -loglevel quiet -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s"; + mINI::Instance()[kSnap] = "%s -i %s -loglevel quiet -y -f mjpeg -t 0.001 %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()[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"; #endif //默认ffmpeg命令路径为环境变量中路径 mINI::Instance()[kBin] = ffmpeg_bin.empty() ? "ffmpeg" : ffmpeg_bin; @@ -232,3 +236,31 @@ void FFmpegSource::onGetMediaSource(const MediaSource::Ptr &src) { _listener = src->getListener(); src->setListener(shared_from_this()); } + +void FFmpegSnap::makeSnap(const string &play_url, const string &save_path, float timeout_sec, const function &cb) { + GET_CONFIG(string,ffmpeg_bin,FFmpeg::kBin); + GET_CONFIG(string,ffmpeg_snap,FFmpeg::kSnap); + GET_CONFIG(string,ffmpeg_log,FFmpeg::kLog); + + std::shared_ptr process = std::make_shared(); + auto delayTask = EventPollerPool::Instance().getPoller()->doDelayTask(timeout_sec * 1000,[process,cb](){ + if(process->wait(false)){ + //FFmpeg进程还在运行,超时就关闭它 + process->kill(2000); + } + return 0; + }); + + WorkThreadPool::Instance().getPoller()->async([process,play_url,save_path,delayTask,cb](){ + char cmd[1024] = {0}; + snprintf(cmd, sizeof(cmd),ffmpeg_snap.data(),ffmpeg_bin.data(),play_url.data(),save_path.data()); + process->run(cmd,ffmpeg_log.empty() ? "" : File::absolutePath("",ffmpeg_log)); + //等待FFmpeg进程退出 + process->wait(true); + //FFmpeg进程退出了可以取消定时器了 + delayTask->cancel(); + //执行回调函数 + cb(process->exit_code() == 0); + }); +} + diff --git a/server/FFmpegSource.h b/server/FFmpegSource.h index bbafcb68..b32ec8f8 100644 --- a/server/FFmpegSource.h +++ b/server/FFmpegSource.h @@ -23,6 +23,23 @@ using namespace std; using namespace toolkit; using namespace mediakit; +namespace FFmpeg { + extern const string kSnap; +} + +class FFmpegSnap { +public: + /// 创建截图 + /// \param play_url 播放url地址,只要FFmpeg支持即可 + /// \param save_path 截图jpeg文件保存路径 + /// \param timeout_sec 生成截图超时时间(防止阻塞太久) + /// \param cb 生成截图成功与否回调 + static void makeSnap(const string &play_url, const string &save_path, float timeout_sec, const function &cb); +private: + FFmpegSnap() = delete; + ~FFmpegSnap() = delete; +}; + class FFmpegSource : public std::enable_shared_from_this , public MediaSourceEvent{ public: typedef shared_ptr Ptr; diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 47bb2842..97846243 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -50,10 +50,13 @@ typedef enum { #define API_FIELD "api." const string kApiDebug = API_FIELD"apiDebug"; const string kSecret = API_FIELD"secret"; +const string kSnapRoot = API_FIELD"snapRoot"; static onceToken token([]() { mINI::Instance()[kApiDebug] = "1"; mINI::Instance()[kSecret] = "035c73f7-bb6b-4889-a715-d9eb2d1925cc"; + mINI::Instance()[kSnapRoot] = "./www/snap/"; + }); }//namespace API @@ -174,7 +177,7 @@ static inline void addHttpListener(){ size = body->remainSize(); } - if(size < 4 * 1024){ + if(size && size < 4 * 1024){ string contentOut = body->readData(size)->toString(); DebugL << "\r\n# request:\r\n" << parser.Method() << " " << parser.FullUrl() << "\r\n" << "# content:\r\n" << parser.Content() << "\r\n" @@ -817,6 +820,63 @@ void installWebApi() { val["data"]["paths"] = paths; }); + GET_CONFIG(string, snap_root, API::kSnapRoot); + + //获取截图缓存或者实时截图 + //http://127.0.0.1/index/api/getSnap?url=rtmp://127.0.0.1/record/robot.mp4&timeout_sec=10&expire_sec=3 + api_regist2("/index/api/getSnap", [](API_ARGS2){ + CHECK_SECRET(); + CHECK_ARGS("url", "timeout_sec", "expire_sec"); + auto file_prefix = MD5(allArgs["url"]).hexdigest() + "_"; + string file_path; + int expire_sec = allArgs["expire_sec"]; + File::scanDir(File::absolutePath(snap_root,""),[&](const string &path, bool isDir){ + if(!isDir){ + auto pos = path.find(file_prefix); + if(pos != string::npos){ + //找到截图 + auto tm = FindField(path.data() + pos + file_prefix.size(), nullptr, ".jpeg"); + if(atoll(tm.data()) + expire_sec < time(NULL)){ + //截图已经过期,删除之,后面重新生成 + File::delete_file(path.data()); + }else{ + //截图未过期 + file_path = path; + } + return false; + } + } + return true; + }); + + if(!file_path.empty()){ + //返回上次生成的截图 + StrCaseMap headerOut; + headerOut["Content-Type"] = HttpFileManager::getContentType(".jpeg"); + invoker.responseFile(headerIn,headerOut,file_path); + return; + } + + //无截图或者截图已经过期 + file_path = File::absolutePath(StrPrinter << file_prefix << time(NULL) << ".jpeg" ,snap_root); +#if !defined(_WIN32) + //创建文件夹 + File::create_path(file_path.c_str(), S_IRWXO | S_IRWXG | S_IRWXU); +#else + File::create_path(file_path.c_str(),0); +#endif + FFmpegSnap::makeSnap(allArgs["url"],file_path,allArgs["timeout_sec"],[invoker,headerIn,file_path](bool success){ + if(!success){ + //生成截图失败,可能残留空文件 + File::delete_file(file_path.data()); + } + + StrCaseMap headerOut; + headerOut["Content-Type"] = HttpFileManager::getContentType(".jpeg"); + invoker.responseFile(headerIn, headerOut, file_path); + }); + }); + ////////////以下是注册的Hook API//////////// api_regist1("/index/hook/on_publish",[](API_ARGS1){ //开始推流事件 From 5b4d4dab96214c4198ae8b2cfdb48cab9ef52a11 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 9 May 2020 09:29:45 +0800 Subject: [PATCH 011/100] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=88=AA=E5=9B=BE?= =?UTF-8?q?=E7=94=9F=E6=88=90=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 55 +++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 97846243..183b9ca0 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -827,53 +827,52 @@ void installWebApi() { api_regist2("/index/api/getSnap", [](API_ARGS2){ CHECK_SECRET(); CHECK_ARGS("url", "timeout_sec", "expire_sec"); - auto file_prefix = MD5(allArgs["url"]).hexdigest() + "_"; - string file_path; int expire_sec = allArgs["expire_sec"]; - File::scanDir(File::absolutePath(snap_root,""),[&](const string &path, bool isDir){ - if(!isDir){ - auto pos = path.find(file_prefix); - if(pos != string::npos){ - //找到截图 - auto tm = FindField(path.data() + pos + file_prefix.size(), nullptr, ".jpeg"); - if(atoll(tm.data()) + expire_sec < time(NULL)){ - //截图已经过期,删除之,后面重新生成 - File::delete_file(path.data()); - }else{ - //截图未过期 - file_path = path; - } - return false; - } + auto scan_path = File::absolutePath(MD5(allArgs["url"]).hexdigest(), snap_root) + "/"; + string snap_path; + File::scanDir(scan_path, [&](const string &path, bool isDir) { + if (isDir) { + //忽略文件夹 + return true; } - return true; + + //找到截图 + auto tm = FindField(path.data() + scan_path.size(), nullptr, ".jpeg"); + if (atoll(tm.data()) + expire_sec < time(NULL)) { + //截图已经过期,删除之,后面重新生成 + File::delete_file(path.data()); + return true; + } + + //截图未过期,中断遍历,返回上次生成的截图 + snap_path = path; + return false; }); - if(!file_path.empty()){ - //返回上次生成的截图 + if(!snap_path.empty()){ StrCaseMap headerOut; headerOut["Content-Type"] = HttpFileManager::getContentType(".jpeg"); - invoker.responseFile(headerIn,headerOut,file_path); - return; + invoker.responseFile(headerIn,headerOut,snap_path); + return ; } //无截图或者截图已经过期 - file_path = File::absolutePath(StrPrinter << file_prefix << time(NULL) << ".jpeg" ,snap_root); + snap_path = StrPrinter << scan_path << time(NULL) << ".jpeg"; #if !defined(_WIN32) //创建文件夹 - File::create_path(file_path.c_str(), S_IRWXO | S_IRWXG | S_IRWXU); + File::create_path(snap_path.c_str(), S_IRWXO | S_IRWXG | S_IRWXU); #else - File::create_path(file_path.c_str(),0); + File::create_path(snap_path.c_str(),0); #endif - FFmpegSnap::makeSnap(allArgs["url"],file_path,allArgs["timeout_sec"],[invoker,headerIn,file_path](bool success){ + FFmpegSnap::makeSnap(allArgs["url"],snap_path,allArgs["timeout_sec"],[invoker,headerIn,snap_path](bool success){ if(!success){ //生成截图失败,可能残留空文件 - File::delete_file(file_path.data()); + File::delete_file(snap_path.data()); } StrCaseMap headerOut; headerOut["Content-Type"] = HttpFileManager::getContentType(".jpeg"); - invoker.responseFile(headerIn, headerOut, file_path); + invoker.responseFile(headerIn, headerOut, snap_path); }); }); From abe1698601c8d19c1a9fbceccf45c339313173c2 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 9 May 2020 09:39:36 +0800 Subject: [PATCH 012/100] =?UTF-8?q?=E9=98=B2=E6=AD=A2=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E6=88=AA=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 183b9ca0..e4b18a6b 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -858,12 +858,17 @@ void installWebApi() { //无截图或者截图已经过期 snap_path = StrPrinter << scan_path << time(NULL) << ".jpeg"; -#if !defined(_WIN32) - //创建文件夹 - File::create_path(snap_path.c_str(), S_IRWXO | S_IRWXG | S_IRWXU); -#else - File::create_path(snap_path.c_str(),0); -#endif + + { + //生成一个空文件,目的是顺便创建文件夹路径, + //同时防止在FFmpeg生成截图途中不停的尝试调用该api启动FFmpeg生成相同的截图 + //当然,我们可以拷贝一个"正在截图中"的图来替换这个空图,这需要开发者自己实现 + auto file = File::create_file(snap_path.data(), "wb"); + if(file){ + fclose(file); + } + } + FFmpegSnap::makeSnap(allArgs["url"],snap_path,allArgs["timeout_sec"],[invoker,headerIn,snap_path](bool success){ if(!success){ //生成截图失败,可能残留空文件 From e545946e5347a377ed701ad35614f0c7d1e93040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Sat, 9 May 2020 09:59:40 +0800 Subject: [PATCH 013/100] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 82b8c4d6..91597dfa 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ - 点播 - 支持录制为FLV/HLS/MP4 - RTSP/RTMP/HTTP-FLV/WS-FLV支持MP4文件点播,支持seek - + - 其他 - 支持丰富的restful api以及web hook事件 - 支持简单的telnet调试 @@ -81,6 +81,8 @@ - 支持按需拉流,无人观看自动关断拉流 - 支持先拉流后推流,提高及时推流画面打开率 - 提供c api sdk + - 支持FFmpeg拉流代理任意格式的流 + - 支持http api生成并返回实时截图 ## 编译以及测试 From f68885777e88e0d4ea6b3a647155b4ce35e2a82f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Sat, 9 May 2020 10:10:36 +0800 Subject: [PATCH 014/100] Update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 91597dfa..0eabd5e8 100644 --- a/README.md +++ b/README.md @@ -116,8 +116,11 @@ bash build_docker_images.sh - [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) + - [基于ZLMediaKit分支的管理WEB网站](https://github.com/chenxiaolei/ZLMediaKit_NVR_UI) + - [基于ZLMediaKit主线的管理WEB网站](https://gitee.com/kkkkk5G/MediaServerUI) - [DotNetCore的RESTful客户端](https://github.com/MingZhuLiu/ZLMediaKit.DotNetCore.Sdk) + - [GB28181-2016网络视频平台](https://github.com/swwheihei/wvp) + ## 授权协议 From 91080e507cdd753f79f8dcef5dd2b9aa25f4126c Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 9 May 2020 14:04:08 +0800 Subject: [PATCH 015/100] =?UTF-8?q?=E5=8F=91=E9=80=81GET=5FPARAMETER?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E5=85=BC=E5=AE=B9=E4=B8=80=E4=BA=9Brtsp?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=99=A8:#284?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtsp/RtspPlayer.cpp | 8 ++++++++ src/Rtsp/RtspPlayer.h | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Rtsp/RtspPlayer.cpp b/src/Rtsp/RtspPlayer.cpp index dc44ee15..52b77a45 100644 --- a/src/Rtsp/RtspPlayer.cpp +++ b/src/Rtsp/RtspPlayer.cpp @@ -377,6 +377,11 @@ void RtspPlayer::sendDescribe() { sendRtspRequest("DESCRIBE",_strUrl,{"Accept","application/sdp"}); } +void RtspPlayer::sendGetParameter(){ + _onHandshake = [this](const Parser& parser){}; + sendRtspRequest("GET_PARAMETER",_strUrl); +} + void RtspPlayer::sendPause(int type , uint32_t seekMS){ _onHandshake = std::bind(&RtspPlayer::handleResPAUSE,this, placeholders::_1,type); //开启或暂停rtsp @@ -727,6 +732,9 @@ void RtspPlayer::onRecvRTP_l(const RtpPacket::Ptr &pkt, const SdpTrack::Ptr &tra sendReceiverReport(_eType == Rtsp::RTP_TCP,iTrackIndex); ticker.resetTime(); } + + //有些rtsp服务器需要rtcp保活,有些需要发送信令保活 + sendGetParameter(); } } diff --git a/src/Rtsp/RtspPlayer.h b/src/Rtsp/RtspPlayer.h index e2830799..0e643555 100644 --- a/src/Rtsp/RtspPlayer.h +++ b/src/Rtsp/RtspPlayer.h @@ -99,7 +99,7 @@ private: void sendSetup(unsigned int uiTrackIndex); void sendPause(int type , uint32_t ms); void sendDescribe(); - + void sendGetParameter(); void sendRtspRequest(const string &cmd, const string &url ,const StrCaseMap &header = StrCaseMap()); void sendRtspRequest(const string &cmd, const string &url ,const std::initializer_list &header); void sendReceiverReport(bool overTcp,int iTrackIndex); From 41f1df3acfe80efa0fec4e511018f9e4a3623726 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 11 May 2020 12:06:15 +0800 Subject: [PATCH 016/100] =?UTF-8?q?=E5=8F=AF=E8=87=AA=E5=AE=9A=E4=B9=89cod?= =?UTF-8?q?ec?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtp/RtpDecoder.cpp | 5 +++-- src/Rtp/RtpDecoder.h | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Rtp/RtpDecoder.cpp b/src/Rtp/RtpDecoder.cpp index a08ada4f..a576c8fe 100644 --- a/src/Rtp/RtpDecoder.cpp +++ b/src/Rtp/RtpDecoder.cpp @@ -16,8 +16,9 @@ using namespace toolkit; namespace mediakit{ -RtpDecoder::RtpDecoder() { +RtpDecoder::RtpDecoder(const char *codec) { _buffer = std::make_shared(); + _codec = codec; } RtpDecoder::~RtpDecoder() { @@ -46,7 +47,7 @@ void RtpDecoder::decodeRtp(const void *data, int bytes) { uint8_t rtp_type = 0x7F & ((uint8_t *) data)[1]; InfoL << "rtp type:" << (int) rtp_type; - _rtp_decoder = rtp_payload_decode_create(rtp_type, "MP2P", &s_func, this); + _rtp_decoder = rtp_payload_decode_create(rtp_type, _codec.data(), &s_func, this); if (!_rtp_decoder) { WarnL << "unsupported rtp type:" << (int) rtp_type << ",size:" << bytes << ",hexdump" << hexdump(data, bytes > 16 ? 16 : bytes); } diff --git a/src/Rtp/RtpDecoder.h b/src/Rtp/RtpDecoder.h index 6f54e432..78a5b634 100644 --- a/src/Rtp/RtpDecoder.h +++ b/src/Rtp/RtpDecoder.h @@ -19,14 +19,15 @@ namespace mediakit{ class RtpDecoder { public: - RtpDecoder(); + RtpDecoder(const char *codec = "MP2P"); virtual ~RtpDecoder(); -protected: void decodeRtp(const void *data, int bytes); +protected: virtual void onRtpDecode(const uint8_t *packet, int bytes, uint32_t timestamp, int flags) = 0; private: void *_rtp_decoder = nullptr; BufferRaw::Ptr _buffer; + string _codec; }; }//namespace mediakit From 625d7e30c032e4098012100bf4da22b796216139 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 11 May 2020 22:33:10 +0800 Subject: [PATCH 017/100] =?UTF-8?q?=E6=95=B4=E7=90=86=E5=92=8C=E7=B2=BE?= =?UTF-8?q?=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/Common/Device.cpp | 4 +- src/Extension/AAC.h | 86 +++++---------- src/Extension/AACRtmp.cpp | 4 +- src/Extension/AACRtmp.h | 4 - src/Extension/AACRtp.cpp | 4 +- src/Extension/AACRtp.h | 3 - src/Extension/Frame.cpp | 70 +++++++++++- src/Extension/Frame.h | 216 +++++++++++++++++-------------------- src/Extension/G711.h | 128 +++------------------- src/Extension/G711Rtmp.cpp | 8 +- src/Extension/G711Rtmp.h | 4 - src/Extension/G711Rtp.cpp | 19 ++-- src/Extension/G711Rtp.h | 4 - src/Extension/H264.h | 96 +++-------------- src/Extension/H264Rtmp.h | 4 - src/Extension/H264Rtp.h | 4 - src/Extension/H265.h | 96 ++++------------- src/Extension/H265Rtmp.h | 4 - src/Extension/H265Rtp.h | 4 - src/Extension/Track.h | 67 ++++++++++-- src/Rtmp/Rtmp.h | 41 +------ src/Rtp/RtpProcess.cpp | 8 +- src/Rtsp/Rtsp.h | 12 --- 23 files changed, 326 insertions(+), 564 deletions(-) diff --git a/src/Common/Device.cpp b/src/Common/Device.cpp index 91408acc..71f42b39 100644 --- a/src/Common/Device.cpp +++ b/src/Common/Device.cpp @@ -163,7 +163,9 @@ void DevChannel::inputG711(const char *data, int len, uint32_t dts){ if (dts == 0) { dts = (uint32_t)_aTicker[1].elapsedTime(); } - inputFrame(std::make_shared(_audio->codecId, (char*)data, len, dts, 0)); + auto frame = std::make_shared((char*)data, len, dts, 0); + frame->setCodec(_audio->codecId); + inputFrame(frame); } void DevChannel::initVideo(const VideoInfo &info) { diff --git a/src/Extension/AAC.h b/src/Extension/AAC.h index 2a57d6d4..40a3b6c2 100644 --- a/src/Extension/AAC.h +++ b/src/Extension/AAC.h @@ -18,19 +18,11 @@ namespace mediakit{ class AACFrame; -unsigned const samplingFrequencyTable[16] = { 96000, 88200, - 64000, 48000, - 44100, 32000, - 24000, 22050, - 16000, 12000, - 11025, 8000, - 7350, 0, 0, 0 }; - -void makeAdtsHeader(const string &strAudioCfg,AACFrame &adts); -void writeAdtsHeader(const AACFrame &adts, uint8_t *pcAdts) ; -string makeAdtsConfig(const uint8_t *pcAdts); -void getAACInfo(const AACFrame &adts,int &iSampleRate,int &iChannel); - +unsigned const samplingFrequencyTable[16] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0, 0 }; +void makeAdtsHeader(const string &strAudioCfg,AACFrame &adts); +void writeAdtsHeader(const AACFrame &adts, uint8_t *pcAdts) ; +string makeAdtsConfig(const uint8_t *pcAdts); +void getAACInfo(const AACFrame &adts,int &iSampleRate,int &iChannel); /** * aac帧,包含adts头 @@ -46,14 +38,10 @@ public: return aac_frame_length; } uint32_t dts() const override { - return timeStamp; + return _dts; } uint32_t prefixSize() const override{ - return iPrefixSize; - } - - TrackType getTrackType() const override{ - return TrackAudio; + return _prefix_size; } CodecId getCodecId() const override{ @@ -83,16 +71,16 @@ public: unsigned int copyright_identification_start; //1 bslbf unsigned int aac_frame_length; // 13 bslbf 一个ADTS帧的长度包括ADTS头和raw data block unsigned int adts_buffer_fullness; //11 bslbf 0x7FF 说明是码率可变的码流 -//no_raw_data_blocks_in_frame 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧. -//所以说number_of_raw_data_blocks_in_frame == 0 -//表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据) + //no_raw_data_blocks_in_frame 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧. + //所以说number_of_raw_data_blocks_in_frame == 0 + //表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据) unsigned int no_raw_data_blocks_in_frame; //2 uimsfb unsigned char buffer[2 * 1024 + 7]; - uint32_t timeStamp; - uint32_t iPrefixSize = 7; -} ; + uint32_t _dts; + uint32_t _prefix_size = 7; +}; -class AACFrameNoCacheAble : public FrameNoCacheAble { +class AACFrameNoCacheAble : public FrameFromPtr { public: typedef std::shared_ptr Ptr; @@ -100,11 +88,7 @@ public: _ptr = ptr; _size = size; _dts = dts; - _prefixSize = prefixeSize; - } - - TrackType getTrackType() const override{ - return TrackAudio; + _prefix_size = prefixeSize; } CodecId getCodecId() const override{ @@ -118,8 +102,7 @@ public: bool configFrame() const override{ return false; } -} ; - +}; /** * aac音频通道 @@ -173,7 +156,6 @@ public: /** * 获取aac两个字节的配置 - * @return */ const string &getAacCfg() const{ return _cfg; @@ -181,7 +163,6 @@ public: /** * 返回编码类型 - * @return */ CodecId getCodecId() const override{ return CodecAAC; @@ -189,39 +170,36 @@ public: /** * 在获取aac_cfg前是无效的Track - * @return */ bool ready() override { return !_cfg.empty(); } - /** - * 返回音频采样率 - * @return - */ + * 返回音频采样率 + */ int getAudioSampleRate() const override{ return _sampleRate; } + /** * 返回音频采样位数,一般为16或8 - * @return */ int getAudioSampleBit() const override{ return _sampleBit; } + /** * 返回音频通道数 - * @return */ int getAudioChannel() const override{ return _channel; } /** - * 输入数据帧,并获取aac_cfg - * @param frame 数据帧 - */ + * 输入数据帧,并获取aac_cfg + * @param frame 数据帧 + */ void inputFrame(const Frame::Ptr &frame) override{ if (_cfg.empty()) { //未获取到aac_cfg信息 @@ -247,6 +225,7 @@ private: makeAdtsHeader(_cfg,aacFrame); getAACInfo(aacFrame,_sampleRate,_channel); } + Track::Ptr clone() override { return std::make_shared::type >(*this); } @@ -260,15 +239,13 @@ private: int _channel = 0; }; - /** -* aac类型SDP -*/ + * aac类型SDP + */ class AACSdp : public Sdp { public: - /** - * + * 构造函数 * @param aac_cfg aac两个字节的配置描述 * @param sample_rate 音频采样率 * @param playload_type rtp playload type 默认98 @@ -288,16 +265,13 @@ public: _printer << "a=fmtp:" << playload_type << " streamtype=5;profile-level-id=1;mode=AAC-hbr;" << "sizelength=13;indexlength=3;indexdeltalength=3;config=" << configStr << "\r\n"; - _printer << "a=control:trackID=" << getTrackType() << "\r\n"; + _printer << "a=control:trackID=" << (int)TrackAudio << "\r\n"; } string getSdp() const override { return _printer; } - TrackType getTrackType() const override { - return TrackAudio; - } CodecId getCodecId() const override { return CodecAAC; } @@ -306,6 +280,4 @@ private: }; }//namespace mediakit - - -#endif //ZLMEDIAKIT_AAC_H +#endif //ZLMEDIAKIT_AAC_H \ No newline at end of file diff --git a/src/Extension/AACRtmp.cpp b/src/Extension/AACRtmp.cpp index 2b1d3bd2..0367664f 100644 --- a/src/Extension/AACRtmp.cpp +++ b/src/Extension/AACRtmp.cpp @@ -21,7 +21,7 @@ AACFrame::Ptr AACRtmpDecoder::obtainFrame() { //从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象 auto frame = ResourcePoolHelper::obtainObj(); frame->aac_frame_length = 7; - frame->iPrefixSize = 7; + frame->_prefix_size = 7; return frame; } @@ -63,7 +63,7 @@ void AACRtmpDecoder::onGetAAC(const char* pcData, int iLen, uint32_t ui32TimeSta //拷贝aac负载 memcpy(_adts->buffer + 7, pcData, iLen); _adts->aac_frame_length = 7 + iLen; - _adts->timeStamp = ui32TimeStamp; + _adts->_dts = ui32TimeStamp; //adts结构头转成头7个字节 writeAdtsHeader(*_adts, _adts->buffer); diff --git a/src/Extension/AACRtmp.h b/src/Extension/AACRtmp.h index 20f424d5..207fc18f 100644 --- a/src/Extension/AACRtmp.h +++ b/src/Extension/AACRtmp.h @@ -33,10 +33,6 @@ public: */ bool inputRtmp(const RtmpPacket::Ptr &Rtmp, bool key_pos = false) override; - TrackType getTrackType() const override{ - return TrackAudio; - } - CodecId getCodecId() const override{ return CodecAAC; } diff --git a/src/Extension/AACRtp.cpp b/src/Extension/AACRtp.cpp index a09a8d76..b3d7cc1b 100644 --- a/src/Extension/AACRtp.cpp +++ b/src/Extension/AACRtp.cpp @@ -78,7 +78,7 @@ AACFrame::Ptr AACRtpDecoder::obtainFrame() { //从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象 auto frame = ResourcePoolHelper::obtainObj(); frame->aac_frame_length = ADTS_HEADER_LEN; - frame->iPrefixSize = ADTS_HEADER_LEN; + frame->_prefix_size = ADTS_HEADER_LEN; if(frame->syncword == 0 && !_aac_cfg.empty()) { makeAdtsHeader(_aac_cfg,*frame); } @@ -109,7 +109,7 @@ bool AACRtpDecoder::inputRtp(const RtpPacket::Ptr &rtppack, bool key_pos) { //追加aac数据 memcpy(_adts->buffer + _adts->aac_frame_length, ptr, size); _adts->aac_frame_length += size; - _adts->timeStamp = rtppack->timeStamp; + _adts->_dts = rtppack->timeStamp; ptr += size; } diff --git a/src/Extension/AACRtp.h b/src/Extension/AACRtp.h index eba5ceb5..e4758fef 100644 --- a/src/Extension/AACRtp.h +++ b/src/Extension/AACRtp.h @@ -31,9 +31,6 @@ public: */ bool inputRtp(const RtpPacket::Ptr &rtp, bool key_pos = false) override; - TrackType getTrackType() const override{ - return TrackAudio; - } CodecId getCodecId() const override{ return CodecAAC; } diff --git a/src/Extension/Frame.cpp b/src/Extension/Frame.cpp index 9d543706..9aab3fad 100644 --- a/src/Extension/Frame.cpp +++ b/src/Extension/Frame.cpp @@ -15,6 +15,59 @@ using namespace toolkit; namespace mediakit{ +/** + * 该对象的功能是把一个不可缓存的帧转换成可缓存的帧 + */ +class FrameCacheAble : public FrameFromPtr { +public: + typedef std::shared_ptr Ptr; + + FrameCacheAble(const Frame::Ptr &frame){ + if(frame->cacheAble()){ + _frame = frame; + _ptr = frame->data(); + }else{ + _buffer = std::make_shared(); + _buffer->assign(frame->data(),frame->size()); + _ptr = _buffer->data(); + } + _size = frame->size(); + _dts = frame->dts(); + _pts = frame->pts(); + _prefix_size = frame->prefixSize(); + _codecid = frame->getCodecId(); + _key = frame->keyFrame(); + _config = frame->configFrame(); + } + + virtual ~FrameCacheAble() = default; + + /** + * 可以被缓存 + */ + bool cacheAble() const override { + return true; + } + + CodecId getCodecId() const override{ + return _codecid; + } + + bool keyFrame() const override{ + return _key; + } + + bool configFrame() const override{ + return _config; + } +private: + Frame::Ptr _frame; + BufferRaw::Ptr _buffer; + CodecId _codecid; + bool _key; + bool _config; +}; + Frame::Ptr Frame::getCacheAbleFrame(const Frame::Ptr &frame){ if(frame->cacheAble()){ return frame; @@ -30,10 +83,21 @@ const char *CodecInfo::getCodecName() { SWITCH_CASE(CodecAAC); SWITCH_CASE(CodecG711A); SWITCH_CASE(CodecG711U); - default: - return "unknown codec"; + SWITCH_CASE(CodecOpus); + default : return "unknown codec"; + } +} + +TrackType CodecInfo::getTrackType(){ + switch (getCodecId()){ + case CodecH264: + case CodecH265: return TrackVideo; + case CodecAAC: + case CodecG711A: + case CodecG711U: + case CodecOpus: return TrackAudio; + default: return TrackInvalid; } } }//namespace mediakit - diff --git a/src/Extension/Frame.h b/src/Extension/Frame.h index 32df0e3d..cafa876a 100644 --- a/src/Extension/Frame.h +++ b/src/Extension/Frame.h @@ -28,6 +28,7 @@ typedef enum { CodecAAC, CodecG711A, CodecG711U, + CodecOpus, CodecMax = 0x7FFF } CodecId; @@ -49,11 +50,6 @@ public: CodecInfo(){} virtual ~CodecInfo(){} - /** - * 获取音视频类型 - */ - virtual TrackType getTrackType() const = 0; - /** * 获取编解码器类型 */ @@ -61,9 +57,13 @@ public: /** * 获取编码器名称 - * @return 编码器名称 */ const char *getCodecName(); + + /** + * 获取音视频类型 + */ + TrackType getTrackType(); }; /** @@ -76,15 +76,11 @@ public: /** * 返回解码时间戳,单位毫秒 - * @return */ virtual uint32_t dts() const = 0; - - /** * 返回显示时间戳,单位毫秒 - * @return */ virtual uint32_t pts() const { return dts(); @@ -98,13 +94,11 @@ public: /** * 返回是否为关键帧 - * @return */ virtual bool keyFrame() const = 0; /** * 是否为配置帧,譬如sps pps vps - * @return */ virtual bool configFrame() const = 0; @@ -115,14 +109,77 @@ public: /** * 返回可缓存的frame - * @return */ static Ptr getCacheAbleFrame(const Ptr &frame); }; +class FrameImp : public Frame { +public: + typedef std::shared_ptr Ptr; + + char *data() const override{ + return (char *)_buffer.data(); + } + + uint32_t size() const override { + return _buffer.size(); + } + + uint32_t dts() const override { + return _dts; + } + + uint32_t pts() const override{ + return _pts ? _pts : _dts; + } + + uint32_t prefixSize() const override{ + return _prefix_size; + } + + CodecId getCodecId() const override{ + return _codecid; + } + + bool keyFrame() const override { + return false; + } + + bool configFrame() const override{ + return false; + } + +public: + CodecId _codecid = CodecInvalid; + string _buffer; + uint32_t _dts = 0; + uint32_t _pts = 0; + uint32_t _prefix_size = 0; +}; + +/** + * 一个Frame类中可以有多个帧,他们通过 0x 00 00 01 分隔 + * ZLMediaKit会先把这种复合帧split成单个帧然后再处理 + * 一个复合帧可以通过无内存拷贝的方式切割成多个子Frame + * 提供该类的目的是切割复合帧时防止内存拷贝,提高性能 + */ +template +class FrameInternal : public Parent{ +public: + typedef std::shared_ptr Ptr; + FrameInternal(const Frame::Ptr &parent_frame, char *ptr, uint32_t size, int prefix_size) + : Parent(ptr, size, parent_frame->dts(), parent_frame->pts(), prefix_size) { + _parent_frame = parent_frame; + } + bool cacheAble() const override { + return _parent_frame->cacheAble(); + } +private: + Frame::Ptr _parent_frame; +}; + /** * 循环池辅助类 - * @tparam T */ template class ResourcePoolHelper{ @@ -140,18 +197,17 @@ private: }; /** - * 写帧接口的抽闲接口 + * 写帧接口的抽象接口类 */ class FrameWriterInterface { public: typedef std::shared_ptr Ptr; - FrameWriterInterface(){} virtual ~FrameWriterInterface(){} + /** - * 写入帧数据 - * @param frame 帧 - */ + * 写入帧数据 + */ virtual void inputFrame(const Frame::Ptr &frame) = 0; }; @@ -165,16 +221,16 @@ public: /** * inputFrame后触发onWriteFrame回调 - * @param cb */ FrameWriterInterfaceHelper(const onWriteFrame& cb){ _writeCallback = cb; } + virtual ~FrameWriterInterfaceHelper(){} + /** - * 写入帧数据 - * @param frame 帧 - */ + * 写入帧数据 + */ void inputFrame(const Frame::Ptr &frame) override { _writeCallback(frame); } @@ -182,7 +238,6 @@ private: onWriteFrame _writeCallback; }; - /** * 支持代理转发的帧环形缓存 */ @@ -193,6 +248,9 @@ public: FrameDispatcher(){} virtual ~FrameDispatcher(){} + /** + * 添加代理 + */ void addDelegate(const FrameWriterInterface::Ptr &delegate){ //_delegates_write可能多线程同时操作 lock_guard lck(_mtx); @@ -200,6 +258,9 @@ public: _need_update = true; } + /** + * 删除代理 + */ void delDelegate(void *ptr){ //_delegates_write可能多线程同时操作 lock_guard lck(_mtx); @@ -208,8 +269,7 @@ public: } /** - * 写入帧数据 - * @param frame 帧 + * 写入帧并派发 */ void inputFrame(const Frame::Ptr &frame) override{ if(_need_update){ @@ -223,7 +283,13 @@ public: for(auto &pr : _delegates_read){ pr.second->inputFrame(frame); } + } + /** + * 返回代理个数 + */ + int size() const { + return _delegates_write.size(); } private: mutex _mtx; @@ -250,105 +316,23 @@ public: } uint32_t pts() const override{ - if(_pts){ - return _pts; - } - return dts(); + return _pts ? _pts : dts(); } uint32_t prefixSize() const override{ - return _prefixSize; + return _prefix_size; + } + + bool cacheAble() const override { + return false; } protected: char *_ptr; uint32_t _size; uint32_t _dts; uint32_t _pts = 0; - uint32_t _prefixSize; + uint32_t _prefix_size; }; -/** - * 不可缓存的帧,在DevChannel类中有用到。 - * 该帧类型用于防止内存拷贝,直接使用指针传递数据 - * 在大多数情况下,ZLMediaKit是同步对帧数据进行使用和处理的 - * 所以提供此类型的帧很有必要,但是有时又无法避免缓存帧做后续处理 - * 所以可以通过Frame::getCacheAbleFrame方法拷贝一个可缓存的帧 - */ -class FrameNoCacheAble : public FrameFromPtr{ -public: - typedef std::shared_ptr Ptr; - - /** - * 该帧不可缓存 - * @return - */ - bool cacheAble() const override { - return false; - } -}; - -/** - * 该对象的功能是把一个不可缓存的帧转换成可缓存的帧 - * @see FrameNoCacheAble - */ -class FrameCacheAble : public FrameFromPtr { -public: - typedef std::shared_ptr Ptr; - - FrameCacheAble(const Frame::Ptr &frame){ - if(frame->cacheAble()){ - _frame = frame; - _ptr = frame->data(); - }else{ - _buffer = std::make_shared(); - _buffer->assign(frame->data(),frame->size()); - _ptr = _buffer->data(); - } - _size = frame->size(); - _dts = frame->dts(); - _pts = frame->pts(); - _prefixSize = frame->prefixSize(); - _trackType = frame->getTrackType(); - _codec = frame->getCodecId(); - _key = frame->keyFrame(); - _config = frame->configFrame(); - } - - virtual ~FrameCacheAble() = default; - - /** - * 可以被缓存 - * @return - */ - bool cacheAble() const override { - return true; - } - - TrackType getTrackType() const override{ - return _trackType; - } - - CodecId getCodecId() const override{ - return _codec; - } - - bool keyFrame() const override{ - return _key; - } - - bool configFrame() const override{ - return _config; - } -private: - Frame::Ptr _frame; - BufferRaw::Ptr _buffer; - TrackType _trackType; - CodecId _codec; - bool _key; - bool _config; -}; - - }//namespace mediakit - -#endif //ZLMEDIAKIT_FRAME_H +#endif //ZLMEDIAKIT_FRAME_H \ No newline at end of file diff --git a/src/Extension/G711.h b/src/Extension/G711.h index e4929e7b..b2d22b23 100644 --- a/src/Extension/G711.h +++ b/src/Extension/G711.h @@ -19,76 +19,28 @@ namespace mediakit{ /** * G711帧 */ -class G711Frame : public Frame { +class G711Frame : public FrameImp { public: - typedef std::shared_ptr Ptr; - - char *data() const override{ - return (char *)buffer.data(); + G711Frame(){ + _codecid = CodecG711A; } +}; - uint32_t size() const override { - return buffer.size(); - } - - uint32_t dts() const override { - return timeStamp; - } - - uint32_t prefixSize() const override{ - return 0; - } - - TrackType getTrackType() const override{ - return TrackAudio; - } - - CodecId getCodecId() const override{ - return _codecId; - } - - bool keyFrame() const override { - return false; - } - - bool configFrame() const override{ - return false; - } -public: - CodecId _codecId = CodecG711A; - string buffer; - uint32_t timeStamp; -} ; - -class G711FrameNoCacheAble : public FrameNoCacheAble { +class G711FrameNoCacheAble : public FrameFromPtr { public: typedef std::shared_ptr Ptr; - //兼容通用接口 - G711FrameNoCacheAble(char *ptr,uint32_t size,uint32_t dts, uint32_t pts = 0,int prefixeSize = 0){ + G711FrameNoCacheAble(char *ptr,uint32_t size,uint32_t dts, uint32_t pts = 0,int prefix_size = 0){ _ptr = ptr; _size = size; _dts = dts; - _prefixSize = prefixeSize; + _prefix_size = prefix_size; } - //兼容通用接口 void setCodec(CodecId codecId){ _codecId = codecId; } - G711FrameNoCacheAble(CodecId codecId, char *ptr,uint32_t size,uint32_t dts,int prefixeSize = 0){ - _codecId = codecId; - _ptr = ptr; - _size = size; - _dts = dts; - _prefixSize = prefixeSize; - } - - TrackType getTrackType() const override{ - return TrackAudio; - } - CodecId getCodecId() const override{ return _codecId; } @@ -108,67 +60,18 @@ private: /** * G711音频通道 */ -class G711Track : public AudioTrack{ +class G711Track : public AudioTrackImp{ public: typedef std::shared_ptr Ptr; - - /** - * G711A G711U - */ - G711Track(CodecId codecId,int sample_rate, int channels, int sample_bit){ - _codecid = codecId; - _sample_rate = sample_rate; - _channels = channels; - _sample_bit = sample_bit; - } - - /** - * 返回编码类型 - */ - CodecId getCodecId() const override{ - return _codecid; - } - - /** - * 是否已经初始化 - */ - bool ready() override { - return true; - } - - /** - * 返回音频采样率 - */ - int getAudioSampleRate() const override{ - return _sample_rate; - } - - /** - * 返回音频采样位数,一般为16或8 - */ - int getAudioSampleBit() const override{ - return _sample_bit; - } - - /** - * 返回音频通道数 - */ - int getAudioChannel() const override{ - return _channels; - } + G711Track(CodecId codecId,int sample_rate, int channels, int sample_bit) : AudioTrackImp(codecId,sample_rate,channels,sample_bit){} private: + //克隆该Track Track::Ptr clone() override { return std::make_shared::type >(*this); } - //生成sdp Sdp::Ptr getSdp() override ; -private: - CodecId _codecid; - int _sample_rate; - int _channels; - int _sample_bit; }; /** @@ -190,27 +93,20 @@ public: int bitrate = 128) : Sdp(sample_rate,playload_type), _codecId(codecId){ _printer << "m=audio 0 RTP/AVP " << playload_type << "\r\n"; _printer << "a=rtpmap:" << playload_type << (codecId == CodecG711A ? " PCMA/" : " PCMU/") << sample_rate << "/" << channels << "\r\n"; - _printer << "a=control:trackID=" << getTrackType() << "\r\n"; + _printer << "a=control:trackID=" << (int)TrackAudio << "\r\n"; } string getSdp() const override { return _printer; } - TrackType getTrackType() const override { - return TrackAudio; - } - CodecId getCodecId() const override { return _codecId; } - private: _StrPrinter _printer; CodecId _codecId; }; }//namespace mediakit - - -#endif //ZLMEDIAKIT_AAC_H +#endif //ZLMEDIAKIT_G711_H \ No newline at end of file diff --git a/src/Extension/G711Rtmp.cpp b/src/Extension/G711Rtmp.cpp index 11e46d61..7ffca2f0 100644 --- a/src/Extension/G711Rtmp.cpp +++ b/src/Extension/G711Rtmp.cpp @@ -20,15 +20,15 @@ G711RtmpDecoder::G711RtmpDecoder(CodecId codecId) { G711Frame::Ptr G711RtmpDecoder::obtainFrame() { //从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象 auto frame = ResourcePoolHelper::obtainObj(); - frame->buffer.clear(); - frame->_codecId = _codecId; + frame->_buffer.clear(); + frame->_codecid = _codecId; return frame; } bool G711RtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt, bool) { //拷贝G711负载 - _frame->buffer.assign(pkt->strBuf.data() + 1, pkt->strBuf.size() - 1); - _frame->timeStamp = pkt->timeStamp; + _frame->_buffer.assign(pkt->strBuf.data() + 1, pkt->strBuf.size() - 1); + _frame->_dts = pkt->timeStamp; //写入环形缓存 RtmpCodec::inputFrame(_frame); _frame = obtainFrame(); diff --git a/src/Extension/G711Rtmp.h b/src/Extension/G711Rtmp.h index 71f3e069..c2fea41b 100644 --- a/src/Extension/G711Rtmp.h +++ b/src/Extension/G711Rtmp.h @@ -33,10 +33,6 @@ public: */ bool inputRtmp(const RtmpPacket::Ptr &Rtmp, bool key_pos = false) override; - TrackType getTrackType() const override{ - return TrackAudio; - } - CodecId getCodecId() const override{ return _codecId; } diff --git a/src/Extension/G711Rtp.cpp b/src/Extension/G711Rtp.cpp index e4d3329c..e9a77d1e 100644 --- a/src/Extension/G711Rtp.cpp +++ b/src/Extension/G711Rtp.cpp @@ -20,9 +20,9 @@ G711RtpDecoder::G711RtpDecoder(const Track::Ptr &track){ G711Frame::Ptr G711RtpDecoder::obtainFrame() { //从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象 auto frame = ResourcePoolHelper::obtainObj(); - frame->buffer.clear(); - frame->_codecId = _codecid; - frame->timeStamp = 0; + frame->_buffer.clear(); + frame->_codecid = _codecid; + frame->_dts = 0; return frame; } @@ -32,17 +32,17 @@ bool G711RtpDecoder::inputRtp(const RtpPacket::Ptr &rtppack, bool) { // 获取rtp数据 const char *rtp_packet_buf = rtppack->data() + rtppack->offset; - if (rtppack->timeStamp != _frame->timeStamp) { + if (rtppack->timeStamp != _frame->_dts) { //时间戳变更,清空上一帧 onGetG711(_frame); } //追加数据 - _frame->buffer.append(rtp_packet_buf, length); + _frame->_buffer.append(rtp_packet_buf, length); //赋值时间戳 - _frame->timeStamp = rtppack->timeStamp; + _frame->_dts = rtppack->timeStamp; - if (rtppack->mark || _frame->buffer.size() > 10 * 1024) { + if (rtppack->mark || _frame->_buffer.size() > 10 * 1024) { //标记为mark时,或者内存快溢出时,我们认为这是该帧最后一个包 onGetG711(_frame); } @@ -50,7 +50,7 @@ bool G711RtpDecoder::inputRtp(const RtpPacket::Ptr &rtppack, bool) { } void G711RtpDecoder::onGetG711(const G711Frame::Ptr &frame) { - if(!frame->buffer.empty()){ + if(!frame->_buffer.empty()){ //写入环形缓存 RtpCodec::inputFrame(frame); _frame = obtainFrame(); @@ -96,6 +96,3 @@ void G711RtpEncoder::makeG711Rtp(const void *data, unsigned int len, bool mark, } }//namespace mediakit - - - diff --git a/src/Extension/G711Rtp.h b/src/Extension/G711Rtp.h index 31547464..f829b7fc 100644 --- a/src/Extension/G711Rtp.h +++ b/src/Extension/G711Rtp.h @@ -31,10 +31,6 @@ public: */ bool inputRtp(const RtpPacket::Ptr &rtp, bool key_pos = false) override; - TrackType getTrackType() const override{ - return TrackAudio; - } - CodecId getCodecId() const override{ return _codecid; } diff --git a/src/Extension/H264.h b/src/Extension/H264.h index 955b1733..dbc5cfb1 100644 --- a/src/Extension/H264.h +++ b/src/Extension/H264.h @@ -25,7 +25,7 @@ void splitH264(const char *ptr, int len, int prefix, const std::function Ptr; @@ -36,30 +36,8 @@ public: NAL_SEI = 6, } NalType; - char *data() const override{ - return (char *)_buffer.data(); - } - uint32_t size() const override { - return _buffer.size(); - } - uint32_t dts() const override { - return _dts; - } - - uint32_t pts() const override { - return _pts ? _pts : _dts; - } - - uint32_t prefixSize() const override{ - return _prefix_size; - } - - TrackType getTrackType() const override{ - return TrackVideo; - } - - CodecId getCodecId() const override{ - return CodecH264; + H264Frame(){ + _codecid = CodecH264; } bool keyFrame() const override { @@ -69,39 +47,27 @@ public: bool configFrame() const override{ switch(H264_TYPE(_buffer[_prefix_size]) ){ case H264Frame::NAL_SPS: - case H264Frame::NAL_PPS: - return true; - default: - return false; + case H264Frame::NAL_PPS:return true; + default:return false; } } -public: - uint32_t _dts = 0; - uint32_t _pts = 0; - uint32_t _prefix_size = 4; - string _buffer; }; - /** * 防止内存拷贝的H264类 * 用户可以通过该类型快速把一个指针无拷贝的包装成Frame类 * 该类型在DevChannel中有使用 */ -class H264FrameNoCacheAble : public FrameNoCacheAble { +class H264FrameNoCacheAble : public FrameFromPtr { public: typedef std::shared_ptr Ptr; - H264FrameNoCacheAble(char *ptr,uint32_t size,uint32_t dts , uint32_t pts ,int prefixeSize = 4){ + H264FrameNoCacheAble(char *ptr,uint32_t size,uint32_t dts , uint32_t pts ,int prefix_size = 4){ _ptr = ptr; _size = size; _dts = dts; _pts = pts; - _prefixSize = prefixeSize; - } - - TrackType getTrackType() const override{ - return TrackVideo; + _prefix_size = prefix_size; } CodecId getCodecId() const override{ @@ -109,43 +75,18 @@ public: } bool keyFrame() const override { - return H264_TYPE(_ptr[_prefixSize]) == H264Frame::NAL_IDR; + return H264_TYPE(_ptr[_prefix_size]) == H264Frame::NAL_IDR; } bool configFrame() const override{ - switch(H264_TYPE(_ptr[_prefixSize])){ + switch(H264_TYPE(_ptr[_prefix_size])){ case H264Frame::NAL_SPS: - case H264Frame::NAL_PPS: - return true; - default: - return false; + case H264Frame::NAL_PPS:return true; + default:return false; } } }; -/** - * 一个H264Frame类中可以有多个帧,他们通过 0x 00 00 01 分隔 - * ZLMediaKit会先把这种复合帧split成单个帧然后再处理 - * 一个复合帧可以通过无内存拷贝的方式切割成多个H264FrameSubFrame - * 提供该类的目的是切换复合帧时防止内存拷贝,提高性能 - */ -template -class FrameInternal : public Parent{ -public: - typedef std::shared_ptr Ptr; - FrameInternal(const Frame::Ptr &parent_frame, - char *ptr, - uint32_t size, - int prefixeSize) : Parent(ptr,size,parent_frame->dts(),parent_frame->pts(),prefixeSize){ - _parent_frame = parent_frame; - } - bool cacheAble() const override { - return _parent_frame->cacheAble(); - } -private: - Frame::Ptr _parent_frame; -}; - typedef FrameInternal H264FrameInternal; /** @@ -334,13 +275,11 @@ private: bool _last_frame_is_idr = false; }; - /** * h264类型sdp */ class H264Sdp : public Sdp { public: - /** * * @param sps 264 sps,不带0x00000001头 @@ -375,17 +314,13 @@ public: memset(strTemp, 0, 100); av_base64_encode(strTemp, 100, (uint8_t *) strPPS.data(), strPPS.size()); _printer << strTemp << "\r\n"; - _printer << "a=control:trackID=" << getTrackType() << "\r\n"; + _printer << "a=control:trackID=" << (int)TrackVideo << "\r\n"; } string getSdp() const override { return _printer; } - TrackType getTrackType() const override { - return TrackVideo; - } - CodecId getCodecId() const override { return CodecH264; } @@ -393,8 +328,5 @@ private: _StrPrinter _printer; }; - }//namespace mediakit - - -#endif //ZLMEDIAKIT_H264_H +#endif //ZLMEDIAKIT_H264_H \ No newline at end of file diff --git a/src/Extension/H264Rtmp.h b/src/Extension/H264Rtmp.h index e63c6a34..cf8f4dbe 100644 --- a/src/Extension/H264Rtmp.h +++ b/src/Extension/H264Rtmp.h @@ -36,10 +36,6 @@ public: */ bool inputRtmp(const RtmpPacket::Ptr &rtmp, bool key_pos = true) override; - TrackType getTrackType() const override{ - return TrackVideo; - } - CodecId getCodecId() const override{ return CodecH264; } diff --git a/src/Extension/H264Rtp.h b/src/Extension/H264Rtp.h index a4e8793c..3453bf3e 100644 --- a/src/Extension/H264Rtp.h +++ b/src/Extension/H264Rtp.h @@ -38,10 +38,6 @@ public: */ bool inputRtp(const RtpPacket::Ptr &rtp, bool key_pos = true) override; - TrackType getTrackType() const override{ - return TrackVideo; - } - CodecId getCodecId() const override{ return CodecH264; } diff --git a/src/Extension/H265.h b/src/Extension/H265.h index 676af0b2..31a68415 100644 --- a/src/Extension/H265.h +++ b/src/Extension/H265.h @@ -23,9 +23,9 @@ namespace mediakit { bool getHEVCInfo(const string &strVps, const string &strSps, int &iVideoWidth, int &iVideoHeight, float &iVideoFps); /** -* 265帧类 -*/ -class H265Frame : public Frame { + * 265帧类 + */ +class H265Frame : public FrameImp { public: typedef std::shared_ptr Ptr; @@ -60,32 +60,8 @@ public: NAL_SEI_SUFFIX = 40, } NaleType; - char *data() const override { - return (char *) _buffer.data(); - } - - uint32_t size() const override { - return _buffer.size(); - } - - uint32_t dts() const override { - return _dts; - } - - uint32_t pts() const override { - return _pts ? _pts : _dts; - } - - uint32_t prefixSize() const override { - return _prefix_size; - } - - TrackType getTrackType() const override { - return TrackVideo; - } - - CodecId getCodecId() const override { - return CodecH265; + H265Frame(){ + _codecid = CodecH265; } bool keyFrame() const override { @@ -96,39 +72,26 @@ public: switch(H265_TYPE(_buffer[_prefix_size])){ case H265Frame::NAL_VPS: case H265Frame::NAL_SPS: - case H265Frame::NAL_PPS: - return true; - default: - return false; + case H265Frame::NAL_PPS : return true; + default : return false; } } static bool isKeyFrame(int type) { return type >= NAL_BLA_W_LP && type <= NAL_RSV_IRAP_VCL23; } - -public: - uint32_t _dts = 0; - uint32_t _pts = 0; - uint32_t _prefix_size = 4; - string _buffer; }; - -class H265FrameNoCacheAble : public FrameNoCacheAble { +class H265FrameNoCacheAble : public FrameFromPtr { public: typedef std::shared_ptr Ptr; - H265FrameNoCacheAble(char *ptr, uint32_t size, uint32_t dts,uint32_t pts, int prefixeSize = 4) { + H265FrameNoCacheAble(char *ptr, uint32_t size, uint32_t dts,uint32_t pts, int prefix_size = 4) { _ptr = ptr; _size = size; _dts = dts; _pts = pts; - _prefixSize = prefixeSize; - } - - TrackType getTrackType() const override { - return TrackVideo; + _prefix_size = prefix_size; } CodecId getCodecId() const override { @@ -136,17 +99,15 @@ public: } bool keyFrame() const override { - return H265Frame::isKeyFrame(H265_TYPE(((uint8_t *) _ptr)[_prefixSize])); + return H265Frame::isKeyFrame(H265_TYPE(((uint8_t *) _ptr)[_prefix_size])); } bool configFrame() const override{ - switch(H265_TYPE(((uint8_t *) _ptr)[_prefixSize])){ + switch(H265_TYPE(((uint8_t *) _ptr)[_prefix_size])){ case H265Frame::NAL_VPS: case H265Frame::NAL_SPS: - case H265Frame::NAL_PPS: - return true; - default: - return false; + case H265Frame::NAL_PPS:return true; + default:return false; } } }; @@ -184,7 +145,6 @@ public: /** * 返回不带0x00 00 00 01头的vps - * @return */ const string &getVps() const { return _vps; @@ -192,7 +152,6 @@ public: /** * 返回不带0x00 00 00 01头的sps - * @return */ const string &getSps() const { return _sps; @@ -200,7 +159,6 @@ public: /** * 返回不带0x00 00 00 01头的pps - * @return */ const string &getPps() const { return _pps; @@ -212,7 +170,6 @@ public: /** * 返回视频高度 - * @return */ int getVideoHeight() const override{ return _height ; @@ -220,7 +177,6 @@ public: /** * 返回视频宽度 - * @return */ int getVideoWidth() const override{ return _width; @@ -228,7 +184,6 @@ public: /** * 返回视频fps - * @return */ float getVideoFps() const override{ return _fps; @@ -238,11 +193,10 @@ public: return !_vps.empty() && !_sps.empty() && !_pps.empty(); } - /** - * 输入数据帧,并获取sps pps - * @param frame 数据帧 - */ + * 输入数据帧,并获取sps pps + * @param frame 数据帧 + */ void inputFrame(const Frame::Ptr &frame) override{ int type = H265_TYPE(*((uint8_t *)frame->data() + frame->prefixSize())); if(frame->configFrame()){ @@ -352,15 +306,13 @@ private: bool _last_frame_is_idr = false; }; - /** * h265类型sdp */ class H265Sdp : public Sdp { public: - /** - * + * 构造函数 * @param sps 265 sps,不带0x00000001头 * @param pps 265 pps,不带0x00000001头 * @param playload_type rtp playload type 默认96 @@ -382,17 +334,13 @@ public: _printer << encodeBase64(strSPS) << "; "; _printer << "sprop-pps="; _printer << encodeBase64(strPPS) << "\r\n"; - _printer << "a=control:trackID=" << getTrackType() << "\r\n"; + _printer << "a=control:trackID=" << (int)TrackVideo << "\r\n"; } string getSdp() const override { return _printer; } - TrackType getTrackType() const override { - return TrackVideo; - } - CodecId getCodecId() const override { return CodecH265; } @@ -400,9 +348,5 @@ private: _StrPrinter _printer; }; - - }//namespace mediakit - - -#endif //ZLMEDIAKIT_H265_H +#endif //ZLMEDIAKIT_H265_H \ No newline at end of file diff --git a/src/Extension/H265Rtmp.h b/src/Extension/H265Rtmp.h index 3bcf09c9..4a151251 100644 --- a/src/Extension/H265Rtmp.h +++ b/src/Extension/H265Rtmp.h @@ -36,10 +36,6 @@ public: */ bool inputRtmp(const RtmpPacket::Ptr &rtmp, bool key_pos = true) override; - TrackType getTrackType() const override{ - return TrackVideo; - } - CodecId getCodecId() const override{ return CodecH265; } diff --git a/src/Extension/H265Rtp.h b/src/Extension/H265Rtp.h index 1098983a..33e28b5f 100644 --- a/src/Extension/H265Rtp.h +++ b/src/Extension/H265Rtp.h @@ -39,10 +39,6 @@ public: */ bool inputRtp(const RtpPacket::Ptr &rtp, bool key_pos = true) override; - TrackType getTrackType() const override{ - return TrackVideo; - } - CodecId getCodecId() const override{ return CodecH265; } diff --git a/src/Extension/Track.h b/src/Extension/Track.h index 29b679e8..788af78f 100644 --- a/src/Extension/Track.h +++ b/src/Extension/Track.h @@ -65,8 +65,6 @@ class VideoTrack : public Track { public: typedef std::shared_ptr Ptr; - TrackType getTrackType() const override { return TrackVideo;}; - /** * 返回视频高度 * @return @@ -93,8 +91,6 @@ class AudioTrack : public Track { public: typedef std::shared_ptr Ptr; - TrackType getTrackType() const override { return TrackAudio;}; - /** * 返回音频采样率 * @return @@ -114,6 +110,64 @@ public: virtual int getAudioChannel() const {return 0;}; }; +class AudioTrackImp : public AudioTrack{ +public: + typedef std::shared_ptr Ptr; + + /** + * 构造函数 + * @param codecId 编码类型 + * @param sample_rate 采样率(HZ) + * @param channels 通道数 + * @param sample_bit 采样位数,一般为16 + */ + AudioTrackImp(CodecId codecId,int sample_rate, int channels, int sample_bit){ + _codecid = codecId; + _sample_rate = sample_rate; + _channels = channels; + _sample_bit = sample_bit; + } + + /** + * 返回编码类型 + */ + CodecId getCodecId() const override{ + return _codecid; + } + + /** + * 是否已经初始化 + */ + bool ready() override { + return true; + } + + /** + * 返回音频采样率 + */ + int getAudioSampleRate() const override{ + return _sample_rate; + } + + /** + * 返回音频采样位数,一般为16或8 + */ + int getAudioSampleBit() const override{ + return _sample_bit; + } + + /** + * 返回音频通道数 + */ + int getAudioChannel() const override{ + return _channels; + } +private: + CodecId _codecid; + int _sample_rate; + int _channels; + int _sample_bit; +}; class TrackSource{ public: @@ -123,7 +177,6 @@ public: /** * 获取全部的Track * @param trackReady 是否获取全部已经准备好的Track - * @return */ virtual vector getTracks(bool trackReady = true) const = 0; @@ -131,7 +184,6 @@ public: * 获取特定Track * @param type track类型 * @param trackReady 是否获取全部已经准备好的Track - * @return */ Track::Ptr getTrack(TrackType type , bool trackReady = true) const { auto tracks = getTracks(trackReady); @@ -145,5 +197,4 @@ public: }; }//namespace mediakit - -#endif //ZLMEDIAKIT_TRACK_H +#endif //ZLMEDIAKIT_TRACK_H \ No newline at end of file diff --git a/src/Rtmp/Rtmp.h b/src/Rtmp/Rtmp.h index ec4c4e72..22f0775f 100644 --- a/src/Rtmp/Rtmp.h +++ b/src/Rtmp/Rtmp.h @@ -242,18 +242,6 @@ public: } } - /** - * 返回音频或视频类型 - * @return - */ - TrackType getTrackType() const override { - return TrackTitle; - } - - /** - * 返回编码器id - * @return - */ CodecId getCodecId() const override{ return CodecInvalid; } @@ -266,18 +254,6 @@ public: VideoMeta(const VideoTrack::Ptr &video,int datarate = 5000); virtual ~VideoMeta(){} - /** - * 返回音频或视频类型 - * @return - */ - TrackType getTrackType() const override { - return TrackVideo; - } - - /** - * 返回编码器id - * @return - */ CodecId getCodecId() const override{ return _codecId; } @@ -294,18 +270,6 @@ public: virtual ~AudioMeta(){} - /** - * 返回音频或视频类型 - * @return - */ - TrackType getTrackType() const override { - return TrackAudio; - } - - /** - * 返回编码器id - * @return - */ CodecId getCodecId() const override{ return _codecId; } @@ -317,7 +281,4 @@ private: uint8_t getAudioRtmpFlags(const Track::Ptr &track); }//namespace mediakit - - - -#endif +#endif//__rtmp_h diff --git a/src/Rtp/RtpProcess.cpp b/src/Rtp/RtpProcess.cpp index 7c9053e2..4d27332d 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -20,8 +20,8 @@ namespace mediakit{ /** -* 合并一些时间戳相同的frame -*/ + * 合并一些时间戳相同的frame + */ class FrameMerger { public: FrameMerger() = default; @@ -319,7 +319,9 @@ void RtpProcess::onDecode(int stream,int codecid,int flags,int64_t pts,int64_t d WarnP(this) << "audio track change to G711 from codecid:" << getCodecName(_codecid_audio); return; } - _muxer->inputFrame(std::make_shared(codec, (char *) data, bytes, dts)); + auto frame = std::make_shared((char *) data, bytes, dts); + frame->setCodec(codec); + _muxer->inputFrame(frame); break; } default: diff --git a/src/Rtsp/Rtsp.h b/src/Rtsp/Rtsp.h index 0a42add5..102bae11 100644 --- a/src/Rtsp/Rtsp.h +++ b/src/Rtsp/Rtsp.h @@ -263,18 +263,7 @@ public: string getSdp() const override { return _printer; } - /** - * 返回音频或视频类型 - * @return - */ - TrackType getTrackType() const override { - return TrackTitle; - } - /** - * 返回编码器id - * @return - */ CodecId getCodecId() const override{ return CodecInvalid; } @@ -283,5 +272,4 @@ private: }; } //namespace mediakit - #endif //RTSP_RTSP_H_ From 70e9a20352ad97e1c0dd5d1776f40b9281a2f906 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 11 May 2020 23:25:12 +0800 Subject: [PATCH 018/100] =?UTF-8?q?=E7=B2=BE=E7=AE=80aac=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Extension/AAC.cpp | 144 ++++++++++++++++++++++---------------- src/Extension/AAC.h | 71 ++++--------------- src/Extension/AACRtmp.cpp | 31 ++++---- src/Extension/AACRtmp.h | 2 +- src/Extension/AACRtp.cpp | 22 +++--- 5 files changed, 120 insertions(+), 150 deletions(-) diff --git a/src/Extension/AAC.cpp b/src/Extension/AAC.cpp index 282bd2f0..71bbd5d0 100644 --- a/src/Extension/AAC.cpp +++ b/src/Extension/AAC.cpp @@ -12,62 +12,55 @@ namespace mediakit{ -void writeAdtsHeader(const AACFrame &hed, uint8_t *pcAdts) { - pcAdts[0] = (hed.syncword >> 4 & 0xFF); //8bit - pcAdts[1] = (hed.syncword << 4 & 0xF0); //4 bit - pcAdts[1] |= (hed.id << 3 & 0x08); //1 bit - pcAdts[1] |= (hed.layer << 1 & 0x06); //2bit - pcAdts[1] |= (hed.protection_absent & 0x01); //1 bit +class AdtsHeader{ +public: + unsigned int syncword = 0; //12 bslbf 同步字The bit string ‘1111 1111 1111’,说明一个ADTS帧的开始 + unsigned int id; //1 bslbf MPEG 标示符, 设置为1 + unsigned int layer; //2 uimsbf Indicates which layer is used. Set to ‘00’ + unsigned int protection_absent; //1 bslbf 表示是否误码校验 + unsigned int profile; //2 uimsbf 表示使用哪个级别的AAC,如01 Low Complexity(LC)--- AACLC + unsigned int sf_index; //4 uimsbf 表示使用的采样率下标 + unsigned int private_bit; //1 bslbf + unsigned int channel_configuration; //3 uimsbf 表示声道数 + unsigned int original; //1 bslbf + unsigned int home; //1 bslbf + //下面的为改变的参数即每一帧都不同 + unsigned int copyright_identification_bit; //1 bslbf + unsigned int copyright_identification_start; //1 bslbf + unsigned int aac_frame_length; // 13 bslbf 一个ADTS帧的长度包括ADTS头和raw data block + unsigned int adts_buffer_fullness; //11 bslbf 0x7FF 说明是码率可变的码流 + //no_raw_data_blocks_in_frame 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧. + //所以说number_of_raw_data_blocks_in_frame == 0 + //表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据) + unsigned int no_raw_data_blocks_in_frame; //2 uimsfb +}; - pcAdts[2] = (hed.profile << 6 & 0xC0); // 2 bit - pcAdts[2] |= (hed.sf_index << 2 & 0x3C); //4bit - pcAdts[2] |= (hed.private_bit << 1 & 0x02); //1 bit - pcAdts[2] |= (hed.channel_configuration >> 2 & 0x03); //1 bit - - pcAdts[3] = (hed.channel_configuration << 6 & 0xC0); // 2 bit - pcAdts[3] |= (hed.original << 5 & 0x20); //1 bit - pcAdts[3] |= (hed.home << 4 & 0x10); //1 bit - pcAdts[3] |= (hed.copyright_identification_bit << 3 & 0x08); //1 bit - pcAdts[3] |= (hed.copyright_identification_start << 2 & 0x04); //1 bit - pcAdts[3] |= (hed.aac_frame_length >> 11 & 0x03); //2 bit - - pcAdts[4] = (hed.aac_frame_length >> 3 & 0xFF); //8 bit - - pcAdts[5] = (hed.aac_frame_length << 5 & 0xE0); //3 bit - pcAdts[5] |= (hed.adts_buffer_fullness >> 6 & 0x1F); //5 bit - - pcAdts[6] = (hed.adts_buffer_fullness << 2 & 0xFC); //6 bit - pcAdts[6] |= (hed.no_raw_data_blocks_in_frame & 0x03); //2 bit +static void dumpAdtsHeader(const AdtsHeader &hed, uint8_t *out) { + out[0] = (hed.syncword >> 4 & 0xFF); //8bit + out[1] = (hed.syncword << 4 & 0xF0); //4 bit + out[1] |= (hed.id << 3 & 0x08); //1 bit + out[1] |= (hed.layer << 1 & 0x06); //2bit + out[1] |= (hed.protection_absent & 0x01); //1 bit + out[2] = (hed.profile << 6 & 0xC0); // 2 bit + out[2] |= (hed.sf_index << 2 & 0x3C); //4bit + out[2] |= (hed.private_bit << 1 & 0x02); //1 bit + out[2] |= (hed.channel_configuration >> 2 & 0x03); //1 bit + out[3] = (hed.channel_configuration << 6 & 0xC0); // 2 bit + out[3] |= (hed.original << 5 & 0x20); //1 bit + out[3] |= (hed.home << 4 & 0x10); //1 bit + out[3] |= (hed.copyright_identification_bit << 3 & 0x08); //1 bit + out[3] |= (hed.copyright_identification_start << 2 & 0x04); //1 bit + out[3] |= (hed.aac_frame_length >> 11 & 0x03); //2 bit + out[4] = (hed.aac_frame_length >> 3 & 0xFF); //8 bit + out[5] = (hed.aac_frame_length << 5 & 0xE0); //3 bit + out[5] |= (hed.adts_buffer_fullness >> 6 & 0x1F); //5 bit + out[6] = (hed.adts_buffer_fullness << 2 & 0xFC); //6 bit + out[6] |= (hed.no_raw_data_blocks_in_frame & 0x03); //2 bit } -string makeAdtsConfig(const uint8_t *pcAdts){ - if (!(pcAdts[0] == 0xFF && (pcAdts[1] & 0xF0) == 0xF0)) { - return ""; - } - // Get and check the 'profile': - unsigned char profile = (pcAdts[2] & 0xC0) >> 6; // 2 bits - if (profile == 3) { - return ""; - } - // Get and check the 'sampling_frequency_index': - unsigned char sampling_frequency_index = (pcAdts[2] & 0x3C) >> 2; // 4 bits - if (samplingFrequencyTable[sampling_frequency_index] == 0) { - return ""; - } - - // Get and check the 'channel_configuration': - unsigned char channel_configuration = ((pcAdts[2] & 0x01) << 2) - | ((pcAdts[3] & 0xC0) >> 6); // 3 bits - - unsigned char audioSpecificConfig[2]; - unsigned char const audioObjectType = profile + 1; - audioSpecificConfig[0] = (audioObjectType << 3) | (sampling_frequency_index >> 1); - audioSpecificConfig[1] = (sampling_frequency_index << 7) | (channel_configuration << 3); - return string((char *)audioSpecificConfig,2); -} -void makeAdtsHeader(const string &strAudioCfg,AACFrame &adts) { - uint8_t cfg1 = strAudioCfg[0]; - uint8_t cfg2 = strAudioCfg[1]; +static void parseAacConfig(const string &config, AdtsHeader &adts) { + uint8_t cfg1 = config[0]; + uint8_t cfg2 = config[1]; int audioObjectType; int sampling_frequency_index; @@ -93,9 +86,44 @@ void makeAdtsHeader(const string &strAudioCfg,AACFrame &adts) { adts.adts_buffer_fullness = 2047; adts.no_raw_data_blocks_in_frame = 0; } -void getAACInfo(const AACFrame &adts,int &iSampleRate,int &iChannel){ - iSampleRate = samplingFrequencyTable[adts.sf_index]; - iChannel = adts.channel_configuration; + +string makeAacConfig(const uint8_t *hex){ + if (!(hex[0] == 0xFF && (hex[1] & 0xF0) == 0xF0)) { + return ""; + } + // Get and check the 'profile': + unsigned char profile = (hex[2] & 0xC0) >> 6; // 2 bits + if (profile == 3) { + return ""; + } + + // Get and check the 'sampling_frequency_index': + unsigned char sampling_frequency_index = (hex[2] & 0x3C) >> 2; // 4 bits + if (samplingFrequencyTable[sampling_frequency_index] == 0) { + return ""; + } + + // Get and check the 'channel_configuration': + unsigned char channel_configuration = ((hex[2] & 0x01) << 2) | ((hex[3] & 0xC0) >> 6); // 3 bits + unsigned char audioSpecificConfig[2]; + unsigned char const audioObjectType = profile + 1; + audioSpecificConfig[0] = (audioObjectType << 3) | (sampling_frequency_index >> 1); + audioSpecificConfig[1] = (sampling_frequency_index << 7) | (channel_configuration << 3); + return string((char *)audioSpecificConfig,2); +} + +void dumpAacConfig(const string &config, int length, uint8_t *out){ + AdtsHeader header; + parseAacConfig(config, header); + header.aac_frame_length = length; + dumpAdtsHeader(header, out); +} + +void parseAacConfig(const string &config, int &samplerate, int &channels){ + AdtsHeader header; + parseAacConfig(config, header); + samplerate = samplingFrequencyTable[header.sf_index]; + channels = header.channel_configuration; } Sdp::Ptr AACTrack::getSdp() { @@ -106,6 +134,4 @@ Sdp::Ptr AACTrack::getSdp() { return std::make_shared(getAacCfg(),getAudioSampleRate(), getAudioChannel()); } -}//namespace mediakit - - +}//namespace mediakit \ No newline at end of file diff --git a/src/Extension/AAC.h b/src/Extension/AAC.h index 40a3b6c2..fa83722c 100644 --- a/src/Extension/AAC.h +++ b/src/Extension/AAC.h @@ -13,71 +13,26 @@ #include "Frame.h" #include "Track.h" +#define ADTS_HEADER_LEN 7 namespace mediakit{ class AACFrame; unsigned const samplingFrequencyTable[16] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0, 0 }; -void makeAdtsHeader(const string &strAudioCfg,AACFrame &adts); -void writeAdtsHeader(const AACFrame &adts, uint8_t *pcAdts) ; -string makeAdtsConfig(const uint8_t *pcAdts); -void getAACInfo(const AACFrame &adts,int &iSampleRate,int &iChannel); +string makeAacConfig(const uint8_t *hex); +void dumpAacConfig(const string &config, int length, uint8_t *out); +void parseAacConfig(const string &config, int &samplerate, int &channels); /** * aac帧,包含adts头 */ -class AACFrame : public Frame { +class AACFrame : public FrameImp { public: typedef std::shared_ptr Ptr; - - char *data() const override{ - return (char *)buffer; + AACFrame(){ + _codecid = CodecAAC; } - uint32_t size() const override { - return aac_frame_length; - } - uint32_t dts() const override { - return _dts; - } - uint32_t prefixSize() const override{ - return _prefix_size; - } - - CodecId getCodecId() const override{ - return CodecAAC; - } - - bool keyFrame() const override { - return false; - } - - bool configFrame() const override{ - return false; - } -public: - unsigned int syncword = 0; //12 bslbf 同步字The bit string ‘1111 1111 1111’,说明一个ADTS帧的开始 - unsigned int id; //1 bslbf MPEG 标示符, 设置为1 - unsigned int layer; //2 uimsbf Indicates which layer is used. Set to ‘00’ - unsigned int protection_absent; //1 bslbf 表示是否误码校验 - unsigned int profile; //2 uimsbf 表示使用哪个级别的AAC,如01 Low Complexity(LC)--- AACLC - unsigned int sf_index; //4 uimsbf 表示使用的采样率下标 - unsigned int private_bit; //1 bslbf - unsigned int channel_configuration; //3 uimsbf 表示声道数 - unsigned int original; //1 bslbf - unsigned int home; //1 bslbf - //下面的为改变的参数即每一帧都不同 - unsigned int copyright_identification_bit; //1 bslbf - unsigned int copyright_identification_start; //1 bslbf - unsigned int aac_frame_length; // 13 bslbf 一个ADTS帧的长度包括ADTS头和raw data block - unsigned int adts_buffer_fullness; //11 bslbf 0x7FF 说明是码率可变的码流 - //no_raw_data_blocks_in_frame 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧. - //所以说number_of_raw_data_blocks_in_frame == 0 - //表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据) - unsigned int no_raw_data_blocks_in_frame; //2 uimsfb - unsigned char buffer[2 * 1024 + 7]; - uint32_t _dts; - uint32_t _prefix_size = 7; }; class AACFrameNoCacheAble : public FrameFromPtr { @@ -138,7 +93,7 @@ public: if(adts_header_len < 7){ throw std::invalid_argument("adts头必须不少于7个字节"); } - _cfg = makeAdtsConfig((uint8_t*)adts_header); + _cfg = makeAacConfig((uint8_t *) adts_header); onReady(); } @@ -150,7 +105,7 @@ public: if(aac_frame_with_adts->getCodecId() != CodecAAC || aac_frame_with_adts->prefixSize() < 7){ throw std::invalid_argument("必须输入带adts头的aac帧"); } - _cfg = makeAdtsConfig((uint8_t*)aac_frame_with_adts->data()); + _cfg = makeAacConfig((uint8_t *) aac_frame_with_adts->data()); onReady(); } @@ -205,7 +160,7 @@ public: //未获取到aac_cfg信息 if (frame->prefixSize() >= 7) { //7个字节的adts头 - _cfg = makeAdtsConfig((uint8_t *)(frame->data())); + _cfg = makeAacConfig((uint8_t *) (frame->data())); onReady(); } else { WarnL << "无法获取adts头!"; @@ -218,12 +173,10 @@ private: * 解析2个字节的aac配置 */ void onReady(){ - if(_cfg.size() < 2){ + if (_cfg.size() < 2) { return; } - AACFrame aacFrame; - makeAdtsHeader(_cfg,aacFrame); - getAACInfo(aacFrame,_sampleRate,_channel); + parseAacConfig(_cfg, _sampleRate, _channel); } Track::Ptr clone() override { diff --git a/src/Extension/AACRtmp.cpp b/src/Extension/AACRtmp.cpp index 0367664f..2009dbc9 100644 --- a/src/Extension/AACRtmp.cpp +++ b/src/Extension/AACRtmp.cpp @@ -20,8 +20,9 @@ AACRtmpDecoder::AACRtmpDecoder() { AACFrame::Ptr AACRtmpDecoder::obtainFrame() { //从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象 auto frame = ResourcePoolHelper::obtainObj(); - frame->aac_frame_length = 7; - frame->_prefix_size = 7; + frame->_prefix_size = ADTS_HEADER_LEN; + //预留7个字节的空位以便后续覆盖 + frame->_buffer.assign(ADTS_HEADER_LEN,(char)0); return frame; } @@ -41,7 +42,7 @@ static string getAacCfg(const RtmpPacket &thiz) { return ret; } -bool AACRtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt, bool key_pos) { +bool AACRtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt, bool) { if (pkt->isCfgFrame()) { _aac_cfg = getAacCfg(*pkt); return false; @@ -52,26 +53,18 @@ bool AACRtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt, bool key_pos) { return false; } -void AACRtmpDecoder::onGetAAC(const char* pcData, int iLen, uint32_t ui32TimeStamp) { - if(iLen + 7 > sizeof(_adts->buffer)){ - WarnL << "Illegal adts data, exceeding the length limit."; - return; - } - //写adts结构头 - makeAdtsHeader(_aac_cfg,*_adts); - - //拷贝aac负载 - memcpy(_adts->buffer + 7, pcData, iLen); - _adts->aac_frame_length = 7 + iLen; - _adts->_dts = ui32TimeStamp; - - //adts结构头转成头7个字节 - writeAdtsHeader(*_adts, _adts->buffer); +void AACRtmpDecoder::onGetAAC(const char* data, int len, uint32_t stamp) { + _adts->_dts = stamp; + //先追加数据 + _adts->_buffer.append(data, len); + //覆盖adts头 + dumpAacConfig(_aac_cfg, _adts->size(), (uint8_t *) _adts->data()); //写入环形缓存 RtmpCodec::inputFrame(_adts); _adts = obtainFrame(); } + ///////////////////////////////////////////////////////////////////////////////////// AACRtmpEncoder::AACRtmpEncoder(const Track::Ptr &track) { @@ -93,7 +86,7 @@ void AACRtmpEncoder::inputFrame(const Frame::Ptr &frame) { if (_aac_cfg.empty()) { if (frame->prefixSize() >= 7) { //包含adts头,从adts头获取aac配置信息 - _aac_cfg = makeAdtsConfig((uint8_t *)(frame->data())); + _aac_cfg = makeAacConfig((uint8_t *) (frame->data())); } makeConfigPacket(); } diff --git a/src/Extension/AACRtmp.h b/src/Extension/AACRtmp.h index 207fc18f..653ac122 100644 --- a/src/Extension/AACRtmp.h +++ b/src/Extension/AACRtmp.h @@ -38,7 +38,7 @@ public: } protected: - void onGetAAC(const char* pcData, int iLen, uint32_t ui32TimeStamp); + void onGetAAC(const char* data, int len, uint32_t stamp); AACFrame::Ptr obtainFrame(); protected: AACFrame::Ptr _adts; diff --git a/src/Extension/AACRtp.cpp b/src/Extension/AACRtp.cpp index b3d7cc1b..3baadb36 100644 --- a/src/Extension/AACRtp.cpp +++ b/src/Extension/AACRtp.cpp @@ -9,7 +9,7 @@ */ #include "AACRtp.h" -#define ADTS_HEADER_LEN 7 +#define AAC_MAX_FRAME_SIZE (2 * 1024) namespace mediakit{ @@ -77,11 +77,9 @@ AACRtpDecoder::AACRtpDecoder() { AACFrame::Ptr AACRtpDecoder::obtainFrame() { //从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象 auto frame = ResourcePoolHelper::obtainObj(); - frame->aac_frame_length = ADTS_HEADER_LEN; frame->_prefix_size = ADTS_HEADER_LEN; - if(frame->syncword == 0 && !_aac_cfg.empty()) { - makeAdtsHeader(_aac_cfg,*frame); - } + //预留7个字节的空位以便后续覆盖 + frame->_buffer.assign(ADTS_HEADER_LEN,(char)0); return frame; } @@ -96,19 +94,18 @@ bool AACRtpDecoder::inputRtp(const RtpPacket::Ptr &rtppack, bool key_pos) { //忽略Au-Header区 ptr += 2 + au_header_count * 2; - static const uint32_t max_size = sizeof(AACFrame::buffer) - ADTS_HEADER_LEN; + static const uint32_t max_size = AAC_MAX_FRAME_SIZE - ADTS_HEADER_LEN; while (ptr < end) { auto size = (uint32_t) (end - ptr); if(size > max_size){ size = max_size; } - if (_adts->aac_frame_length + size > sizeof(AACFrame::buffer)) { + if (_adts->size() + size > AAC_MAX_FRAME_SIZE) { //数据太多了,先清空 flushData(); } //追加aac数据 - memcpy(_adts->buffer + _adts->aac_frame_length, ptr, size); - _adts->aac_frame_length += size; + _adts->_buffer.append((char *)ptr, size); _adts->_dts = rtppack->timeStamp; ptr += size; } @@ -120,13 +117,14 @@ bool AACRtpDecoder::inputRtp(const RtpPacket::Ptr &rtppack, bool key_pos) { return false; } - void AACRtpDecoder::flushData() { - if(_adts->aac_frame_length == ADTS_HEADER_LEN){ + if (_adts->size() == ADTS_HEADER_LEN) { //没有有效数据 return; } - writeAdtsHeader(*_adts, _adts->buffer); + + //覆盖adts头 + dumpAacConfig(_aac_cfg, _adts->size(), (uint8_t *) _adts->data()); RtpCodec::inputFrame(_adts); _adts = obtainFrame(); } From 6356ffcb4f0924fdab06c150af8d96167507f2ae Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 11 May 2020 23:34:57 +0800 Subject: [PATCH 019/100] =?UTF-8?q?=E9=9A=90=E8=97=8F=E7=BB=86=E8=8A=82?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Extension/AAC.cpp | 2 ++ src/Extension/AAC.h | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Extension/AAC.cpp b/src/Extension/AAC.cpp index 71bbd5d0..e138ccf2 100644 --- a/src/Extension/AAC.cpp +++ b/src/Extension/AAC.cpp @@ -12,6 +12,8 @@ namespace mediakit{ +unsigned const samplingFrequencyTable[16] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0, 0 }; + class AdtsHeader{ public: unsigned int syncword = 0; //12 bslbf 同步字The bit string ‘1111 1111 1111’,说明一个ADTS帧的开始 diff --git a/src/Extension/AAC.h b/src/Extension/AAC.h index fa83722c..a20a2cef 100644 --- a/src/Extension/AAC.h +++ b/src/Extension/AAC.h @@ -19,7 +19,6 @@ namespace mediakit{ class AACFrame; -unsigned const samplingFrequencyTable[16] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0, 0 }; string makeAacConfig(const uint8_t *hex); void dumpAacConfig(const string &config, int length, uint8_t *out); void parseAacConfig(const string &config, int &samplerate, int &channels); From 4ab270ca109e0da5359b9d788f2d2ef7863ab862 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 11 May 2020 23:43:28 +0800 Subject: [PATCH 020/100] =?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/Extension/AAC.h | 36 ++++-------------------------------- 1 file changed, 4 insertions(+), 32 deletions(-) diff --git a/src/Extension/AAC.h b/src/Extension/AAC.h index a20a2cef..4783d1d5 100644 --- a/src/Extension/AAC.h +++ b/src/Extension/AAC.h @@ -17,8 +17,6 @@ namespace mediakit{ -class AACFrame; - string makeAacConfig(const uint8_t *hex); void dumpAacConfig(const string &config, int length, uint8_t *out); void parseAacConfig(const string &config, int &samplerate, int &channels); @@ -38,11 +36,11 @@ class AACFrameNoCacheAble : public FrameFromPtr { public: typedef std::shared_ptr Ptr; - AACFrameNoCacheAble(char *ptr,uint32_t size,uint32_t dts,uint32_t pts = 0,int prefixeSize = 7){ + AACFrameNoCacheAble(char *ptr,uint32_t size,uint32_t dts,uint32_t pts = 0,int prefix_size = ADTS_HEADER_LEN){ _ptr = ptr; _size = size; _dts = dts; - _prefix_size = prefixeSize; + _prefix_size = prefix_size; } CodecId getCodecId() const override{ @@ -83,31 +81,6 @@ public: onReady(); } - /** - * 构造aac类型的媒体 - * @param adts_header adts头,7个字节 - * @param adts_header_len adts头长度,不少于7个字节 - */ - AACTrack(const char *adts_header,int adts_header_len = 7){ - if(adts_header_len < 7){ - throw std::invalid_argument("adts头必须不少于7个字节"); - } - _cfg = makeAacConfig((uint8_t *) adts_header); - onReady(); - } - - /** - * 构造aac类型的媒体 - * @param aac_frame_with_adts 带adts头的aac帧 - */ - AACTrack(const Frame::Ptr &aac_frame_with_adts){ - if(aac_frame_with_adts->getCodecId() != CodecAAC || aac_frame_with_adts->prefixSize() < 7){ - throw std::invalid_argument("必须输入带adts头的aac帧"); - } - _cfg = makeAacConfig((uint8_t *) aac_frame_with_adts->data()); - onReady(); - } - /** * 获取aac两个字节的配置 */ @@ -157,7 +130,7 @@ public: void inputFrame(const Frame::Ptr &frame) override{ if (_cfg.empty()) { //未获取到aac_cfg信息 - if (frame->prefixSize() >= 7) { + if (frame->prefixSize() >= ADTS_HEADER_LEN) { //7个字节的adts头 _cfg = makeAacConfig((uint8_t *) (frame->data())); onReady(); @@ -215,8 +188,7 @@ public: char configStr[32] = {0}; snprintf(configStr, sizeof(configStr), "%02X%02X", (uint8_t)aac_cfg[0], (uint8_t)aac_cfg[1]); _printer << "a=fmtp:" << playload_type << " streamtype=5;profile-level-id=1;mode=AAC-hbr;" - << "sizelength=13;indexlength=3;indexdeltalength=3;config=" - << configStr << "\r\n"; + << "sizelength=13;indexlength=3;indexdeltalength=3;config=" << configStr << "\r\n"; _printer << "a=control:trackID=" << (int)TrackAudio << "\r\n"; } From f303ab3222241cd5c84b6e8d3aa2b7dec9f2338c Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 12 May 2020 09:26:02 +0800 Subject: [PATCH 021/100] =?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?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtsp/RtspSession.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index a9cbcffb..37e7aaf6 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -640,6 +640,14 @@ void RtspSession::handleReq_Setup(const Parser &parser) { send_NotAcceptable(); throw SockException(Err_shutdown, "open rtcp socket failed"); } + + if(pSockRtp->get_local_port() % 2 != 0){ + //如果rtp端口不是偶数,那么与rtcp端口互换,目的是兼容一些要求严格的播放器 + Socket::Ptr tmp = pSockRtp; + pSockRtp = pSockRtcp; + pSockRtcp = tmp; + } + _apRtpSock[trackIdx] = pSockRtp; _apRtcpSock[trackIdx] = pSockRtcp; //设置客户端内网端口信息 From 8c466888862577dcf8a5b87d28e84c57afe7aa0d Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 12 May 2020 10:22:21 +0800 Subject: [PATCH 022/100] =?UTF-8?q?=E7=A1=AE=E4=BF=9Drtp/rtcp=E7=AB=AF?= =?UTF-8?q?=E5=8F=A3=E5=8F=B7=E4=B8=BA=E5=81=B6=E5=A5=87=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtsp/Rtsp.cpp | 40 +++++++++++++++++++++++++++++++++++++++- src/Rtsp/Rtsp.h | 2 ++ src/Rtsp/RtspPlayer.cpp | 27 ++++----------------------- src/Rtsp/RtspSession.cpp | 37 +++++++++++++------------------------ 4 files changed, 58 insertions(+), 48 deletions(-) diff --git a/src/Rtsp/Rtsp.cpp b/src/Rtsp/Rtsp.cpp index 03c24c7d..f4d79462 100644 --- a/src/Rtsp/Rtsp.cpp +++ b/src/Rtsp/Rtsp.cpp @@ -365,5 +365,43 @@ bool RtspUrl::setup(bool isSSL, const string &strUrl, const string &strUser, con return true; } -}//namespace mediakit +std::pair makeSockPair_l(const EventPoller::Ptr &poller, const string &local_ip){ + auto pSockRtp = std::make_shared(poller); + if (!pSockRtp->bindUdpSock(0, local_ip.data())) { + //分配端口失败 + throw runtime_error("open udp socket failed"); + } + //是否是偶数 + bool even_numbers = pSockRtp->get_local_port() % 2 != 0; + auto pSockRtcp = std::make_shared(poller); + if (!pSockRtcp->bindUdpSock(pSockRtp->get_local_port() + (even_numbers ? 1 : -1), local_ip.data())) { + //分配端口失败 + throw runtime_error("open udp socket failed"); + } + + if (!even_numbers) { + //如果rtp端口不是偶数,那么与rtcp端口互换,目的是兼容一些要求严格的播放器或服务器 + Socket::Ptr tmp = pSockRtp; + pSockRtp = pSockRtcp; + pSockRtcp = tmp; + } + + return std::make_pair(pSockRtp, pSockRtcp); +} + +std::pair makeSockPair(const EventPoller::Ptr &poller, const string &local_ip){ + int try_count = 0; + while (true) { + try { + return makeSockPair_l(poller, local_ip); + } catch (...) { + if (++try_count == 3) { + throw; + } + WarnL << "open udp socket failed, retry: " << try_count; + } + } +} + +}//namespace mediakit \ No newline at end of file diff --git a/src/Rtsp/Rtsp.h b/src/Rtsp/Rtsp.h index 102bae11..7eb55a66 100644 --- a/src/Rtsp/Rtsp.h +++ b/src/Rtsp/Rtsp.h @@ -271,5 +271,7 @@ private: _StrPrinter _printer; }; +std::pair makeSockPair(const EventPoller::Ptr &poller, const string &local_ip); + } //namespace mediakit #endif //RTSP_RTSP_H_ diff --git a/src/Rtsp/RtspPlayer.cpp b/src/Rtsp/RtspPlayer.cpp index 52b77a45..dad4abe2 100644 --- a/src/Rtsp/RtspPlayer.cpp +++ b/src/Rtsp/RtspPlayer.cpp @@ -214,29 +214,10 @@ void RtspPlayer::handleResDESCRIBE(const Parser& parser) { void RtspPlayer::createUdpSockIfNecessary(int track_idx){ auto &rtpSockRef = _apRtpSock[track_idx]; auto &rtcpSockRef = _apRtcpSock[track_idx]; - if(!rtpSockRef){ - rtpSockRef.reset(new Socket(getPoller())); - //rtp随机端口 - if (!rtpSockRef->bindUdpSock(0, get_local_ip().data())) { - rtpSockRef.reset(); - throw std::runtime_error("open rtp sock failed"); - } - } - - if(!rtcpSockRef){ - rtcpSockRef.reset(new Socket(getPoller())); - //rtcp端口为rtp端口+1,目的是为了兼容某些服务器,其实更推荐随机端口 - if (!rtcpSockRef->bindUdpSock(rtpSockRef->get_local_port() + 1, get_local_ip().data())) { - rtcpSockRef.reset(); - 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; + if (!rtpSockRef || !rtcpSockRef) { + auto pr = makeSockPair(getPoller(), get_local_ip()); + rtpSockRef = pr.first; + rtcpSockRef = pr.second; } } diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index 37e7aaf6..c1baafa2 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -627,29 +627,18 @@ void RtspSession::handleReq_Setup(const Parser &parser) { } break; case Rtsp::RTP_UDP: { - //我们用trackIdx区分rtp和rtcp包 - auto pSockRtp = std::make_shared(_sock->getPoller()); - if (!pSockRtp->bindUdpSock(0,get_local_ip().data())) { + std::pair pr; + try{ + pr = makeSockPair(_sock->getPoller(), get_local_ip()); + }catch(std::exception &ex) { //分配端口失败 send_NotAcceptable(); - throw SockException(Err_shutdown, "open rtp socket failed"); - } - auto pSockRtcp = std::make_shared(_sock->getPoller()); - if (!pSockRtcp->bindUdpSock(pSockRtp->get_local_port() + 1,get_local_ip().data())) { - //分配端口失败 - send_NotAcceptable(); - throw SockException(Err_shutdown, "open rtcp socket failed"); + throw SockException(Err_shutdown, ex.what()); } - if(pSockRtp->get_local_port() % 2 != 0){ - //如果rtp端口不是偶数,那么与rtcp端口互换,目的是兼容一些要求严格的播放器 - Socket::Ptr tmp = pSockRtp; - pSockRtp = pSockRtcp; - pSockRtcp = tmp; - } + _apRtpSock[trackIdx] = pr.first; + _apRtcpSock[trackIdx] = pr.second; - _apRtpSock[trackIdx] = pSockRtp; - _apRtcpSock[trackIdx] = pSockRtcp; //设置客户端内网端口信息 string strClientPort = FindField(parser["Transport"].data(), "client_port=", NULL); uint16_t ui16RtpPort = atoi( FindField(strClientPort.data(), NULL, "-").data()); @@ -661,24 +650,24 @@ void RtspSession::handleReq_Setup(const Parser &parser) { peerAddr.sin_port = htons(ui16RtpPort); peerAddr.sin_addr.s_addr = inet_addr(get_peer_ip().data()); bzero(&(peerAddr.sin_zero), sizeof peerAddr.sin_zero); - pSockRtp->setSendPeerAddr((struct sockaddr *)(&peerAddr)); + pr.first->setSendPeerAddr((struct sockaddr *)(&peerAddr)); //设置rtcp发送目标地址 peerAddr.sin_family = AF_INET; peerAddr.sin_port = htons(ui16RtcpPort); peerAddr.sin_addr.s_addr = inet_addr(get_peer_ip().data()); bzero(&(peerAddr.sin_zero), sizeof peerAddr.sin_zero); - pSockRtcp->setSendPeerAddr((struct sockaddr *)(&peerAddr)); + pr.second->setSendPeerAddr((struct sockaddr *)(&peerAddr)); //尝试获取客户端nat映射地址 startListenPeerUdpData(trackIdx); //InfoP(this) << "分配端口:" << srv_port; sendRtspResponse("200 OK", - {"Transport",StrPrinter << "RTP/AVP/UDP;unicast;" - << "client_port=" << strClientPort << ";" - << "server_port=" << pSockRtp->get_local_port() << "-" << pSockRtcp->get_local_port() << ";" - << "ssrc=" << printSSRC(trackRef->_ssrc) + {"Transport", StrPrinter << "RTP/AVP/UDP;unicast;" + << "client_port=" << strClientPort << ";" + << "server_port=" << pr.first->get_local_port() << "-" << pr.second->get_local_port() << ";" + << "ssrc=" << printSSRC(trackRef->_ssrc) }); } break; From 7102b85d674657d03a8c89a9469604f2c871abe2 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 12 May 2020 10:37:23 +0800 Subject: [PATCH 023/100] =?UTF-8?q?=E7=A1=AE=E4=BF=9Drtp/rtcp=E7=AB=AF?= =?UTF-8?q?=E5=8F=A3=E5=81=B6=E5=A5=87=E7=BA=A6=E5=AE=9A:#292?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtsp/Rtsp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Rtsp/Rtsp.cpp b/src/Rtsp/Rtsp.cpp index f4d79462..b55c1d0e 100644 --- a/src/Rtsp/Rtsp.cpp +++ b/src/Rtsp/Rtsp.cpp @@ -373,7 +373,7 @@ std::pair makeSockPair_l(const EventPoller::Ptr &polle } //是否是偶数 - bool even_numbers = pSockRtp->get_local_port() % 2 != 0; + bool even_numbers = pSockRtp->get_local_port() % 2 == 0; auto pSockRtcp = std::make_shared(poller); if (!pSockRtcp->bindUdpSock(pSockRtp->get_local_port() + (even_numbers ? 1 : -1), local_ip.data())) { //分配端口失败 From d27a8117e1900065972e66048d1daff83926d032 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 12 May 2020 11:48:15 +0800 Subject: [PATCH 024/100] =?UTF-8?q?mp4=E5=BD=95=E5=88=B6=E6=94=AF=E6=8C=81?= =?UTF-8?q?opus?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Extension/Opus.cpp | 23 +++++++++ src/Extension/Opus.h | 107 ++++++++++++++++++++++++++++++++++++++++ src/Record/MP4Muxer.cpp | 63 +++++++++++++---------- 3 files changed, 166 insertions(+), 27 deletions(-) create mode 100644 src/Extension/Opus.cpp create mode 100644 src/Extension/Opus.h diff --git a/src/Extension/Opus.cpp b/src/Extension/Opus.cpp new file mode 100644 index 00000000..e4e15a57 --- /dev/null +++ b/src/Extension/Opus.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "Opus.h" + +namespace mediakit{ + +Sdp::Ptr OpusTrack::getSdp() { + if(!ready()){ + WarnL << getCodecName() << " Track未准备好"; + return nullptr; + } + return std::make_shared(getAudioSampleRate(), getAudioChannel()); +} + +}//namespace mediakit \ No newline at end of file diff --git a/src/Extension/Opus.h b/src/Extension/Opus.h new file mode 100644 index 00000000..b763a2da --- /dev/null +++ b/src/Extension/Opus.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_OPUS_H +#define ZLMEDIAKIT_OPUS_H + +#include "Frame.h" +#include "Track.h" + +namespace mediakit{ + +/** + * Opus帧 + */ +class OpusFrame : public FrameImp { +public: + typedef std::shared_ptr Ptr; + + OpusFrame(){ + _codecid = CodecOpus; + } +}; + +/** + * 不可缓存的Opus帧 + */ +class OpusFrameNoCacheAble : public FrameFromPtr { +public: + typedef std::shared_ptr Ptr; + + OpusFrameNoCacheAble(char *ptr,uint32_t size,uint32_t dts, uint32_t pts = 0,int prefix_size = 0){ + _ptr = ptr; + _size = size; + _dts = dts; + _prefix_size = prefix_size; + } + + CodecId getCodecId() const override{ + return CodecOpus; + } + + bool keyFrame() const override { + return false; + } + + bool configFrame() const override{ + return false; + } +}; + +/** + * Opus帧音频通道 + */ +class OpusTrack : public AudioTrackImp{ +public: + typedef std::shared_ptr Ptr; + OpusTrack(int sample_rate, int channels, int sample_bit) : AudioTrackImp(CodecOpus,sample_rate,channels,sample_bit){} + +private: + //克隆该Track + Track::Ptr clone() override { + return std::make_shared::type >(*this); + } + //生成sdp + Sdp::Ptr getSdp() override ; +}; + +/** + * Opus类型SDP + */ +class OpusSdp : public Sdp { +public: + /** + * 构造opus sdp + * @param sample_rate 音频采样率 + * @param playload_type rtp playload + * @param bitrate 比特率 + */ + OpusSdp(int sample_rate, + int channels, + int playload_type = 98, + int bitrate = 128) : Sdp(sample_rate,playload_type){ + _printer << "m=audio 0 RTP/AVP " << playload_type << "\r\n"; + _printer << "a=rtpmap:" << playload_type << " opus/" << sample_rate << "/" << channels << "\r\n"; + _printer << "a=control:trackID=" << (int)TrackAudio << "\r\n"; + } + + string getSdp() const override { + return _printer; + } + + CodecId getCodecId() const override { + return CodecOpus; + } +private: + _StrPrinter _printer; +}; + +}//namespace mediakit +#endif //ZLMEDIAKIT_OPUS_H diff --git a/src/Record/MP4Muxer.cpp b/src/Record/MP4Muxer.cpp index 64cfbc4e..8f3bb60d 100644 --- a/src/Record/MP4Muxer.cpp +++ b/src/Record/MP4Muxer.cpp @@ -122,27 +122,48 @@ void MP4Muxer::inputFrame(const Frame::Ptr &frame) { } } +static uint8_t getObject(CodecId codecId){ + switch (codecId){ + case CodecG711A : return MOV_OBJECT_G711a; + case CodecG711U : return MOV_OBJECT_G711u; + case CodecOpus : return MOV_OBJECT_OPUS; + case CodecAAC : return MOV_OBJECT_AAC; + case CodecH264 : return MOV_OBJECT_H264; + case CodecH265 : return MOV_OBJECT_HEVC; + default : return 0; + } +} + void MP4Muxer::addTrack(const Track::Ptr &track) { + auto mp4_object = getObject(track->getCodecId()); + if (!mp4_object) { + WarnL << "MP4录制不支持该编码格式:" << track->getCodecName(); + return; + } + + if (!track->ready()) { + WarnL << "Track[" << track->getCodecName() << "]未就绪"; + return; + } + switch (track->getCodecId()) { case CodecG711A: - case CodecG711U: { - auto audio_track = dynamic_pointer_cast(track); + case CodecG711U: + case CodecOpus: { + auto audio_track = dynamic_pointer_cast(track); if (!audio_track) { - WarnL << "不是G711 Track"; - return; - } - if (!audio_track->ready()) { - WarnL << "G711 Track未就绪"; + WarnL << "不是音频Track:" << track->getCodecName(); return; } + auto track_id = mov_writer_add_audio(_mov_writter.get(), - track->getCodecId() == CodecG711A ? MOV_OBJECT_G711a : MOV_OBJECT_G711u, + mp4_object, audio_track->getAudioChannel(), audio_track->getAudioSampleBit() * audio_track->getAudioChannel(), audio_track->getAudioSampleRate(), nullptr, 0); if (track_id < 0) { - WarnL << "添加G711 Track失败:" << track_id; + WarnL << "添加Track[" << track->getCodecName() << "]失败:" << track_id; return; } _codec_to_trackid[track->getCodecId()].track_id = track_id; @@ -155,12 +176,9 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { WarnL << "不是AAC Track"; return; } - if(!audio_track->ready()){ - WarnL << "AAC Track未就绪"; - return; - } + auto track_id = mov_writer_add_audio(_mov_writter.get(), - MOV_OBJECT_AAC, + mp4_object, audio_track->getAudioChannel(), audio_track->getAudioSampleBit() * audio_track->getAudioChannel(), audio_track->getAudioSampleRate(), @@ -178,10 +196,6 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { WarnL << "不是H264 Track"; return; } - if(!h264_track->ready()){ - WarnL << "H264 Track未就绪"; - return; - } struct mpeg4_avc_t avc = {0}; string sps_pps = string("\x00\x00\x00\x01", 4) + h264_track->getSps() + @@ -196,7 +210,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { } auto track_id = mov_writer_add_video(_mov_writter.get(), - MOV_OBJECT_H264, + mp4_object, h264_track->getVideoWidth(), h264_track->getVideoHeight(), extra_data, @@ -216,10 +230,6 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { WarnL << "不是H265 Track"; return; } - if(!h265_track->ready()){ - WarnL << "H265 Track未就绪"; - return; - } struct mpeg4_hevc_t hevc = {0}; string vps_sps_pps = string("\x00\x00\x00\x01", 4) + h265_track->getVps() + @@ -235,7 +245,7 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { } auto track_id = mov_writer_add_video(_mov_writter.get(), - MOV_OBJECT_HEVC, + mp4_object, h265_track->getVideoWidth(), h265_track->getVideoHeight(), extra_data, @@ -248,9 +258,8 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { _have_video = true; } break; - default: - WarnL << "MP4录制不支持该编码格式:" << track->getCodecName(); - break; + + default: WarnL << "MP4录制不支持该编码格式:" << track->getCodecName(); break; } } From 0e9d8df2d00e548de1d896fd7dc44c91ff7c9154 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 14 May 2020 10:23:12 +0800 Subject: [PATCH 025/100] =?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 --- server/Process.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/Process.cpp b/server/Process.cpp index 79870f14..709721f3 100644 --- a/server/Process.cpp +++ b/server/Process.cpp @@ -143,7 +143,7 @@ static bool s_wait(pid_t pid,int *exit_code_ptr,bool block) { 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; + *exit_code_ptr = exit_code; } if (p < 0) { WarnL << "waitpid failed, pid=" << pid << ", err=" << get_uv_errmsg(); From 0779a4bee605e42c85b7bd941aa9409e123668e0 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 15 May 2020 09:53:17 +0800 Subject: [PATCH 026/100] =?UTF-8?q?=E5=AE=8C=E5=96=84rtsp=20=E4=BF=A1?= =?UTF-8?q?=E4=BB=A4=E5=BF=83=E8=B7=B3=E5=8C=85=E7=9B=B8=E5=85=B3=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/Rtsp/RtspPlayer.cpp | 48 +++++++++++++++++++++++++++++++---------- src/Rtsp/RtspPlayer.h | 7 ++++-- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/Rtsp/RtspPlayer.cpp b/src/Rtsp/RtspPlayer.cpp index dad4abe2..87afee56 100644 --- a/src/Rtsp/RtspPlayer.cpp +++ b/src/Rtsp/RtspPlayer.cpp @@ -107,8 +107,7 @@ void RtspPlayer::onConnect(const SockException &err){ onPlayResult_l(err,false); return; } - - sendDescribe(); + sendOptions(); } void RtspPlayer::onRecv(const Buffer::Ptr& pBuf) { @@ -162,7 +161,7 @@ void RtspPlayer::handleResDESCRIBE(const Parser& parser) { string authInfo = parser["WWW-Authenticate"]; //发送DESCRIBE命令后的回复 if ((parser.Url() == "401") && handleAuthenticationFailure(authInfo)) { - sendDescribe(); + sendOptions(); return; } if(parser.Url() == "302" || parser.Url() == "301"){ @@ -358,9 +357,33 @@ void RtspPlayer::sendDescribe() { sendRtspRequest("DESCRIBE",_strUrl,{"Accept","application/sdp"}); } -void RtspPlayer::sendGetParameter(){ +void RtspPlayer::sendOptions(){ + _onHandshake = [this](const Parser& parser){ + if (parser.Url() != "200") { + throw std::runtime_error(StrPrinter << "OPTIONS:" << parser.Url() << " " << parser.Tail() << endl); + } + //获取服务器支持的命令 + _supported_cmd.clear(); + auto public_val = split(parser["Public"],","); + for(auto &cmd : public_val){ + trim(cmd); + _supported_cmd.emplace(cmd); + } + //发送Describe请求,获取sdp + sendDescribe(); + }; + sendRtspRequest("OPTIONS",_strUrl); +} + +void RtspPlayer::sendKeepAlive(){ _onHandshake = [this](const Parser& parser){}; - sendRtspRequest("GET_PARAMETER",_strUrl); + if(_supported_cmd.find("GET_PARAMETER") != _supported_cmd.end()){ + //支持GET_PARAMETER,用此命令保活 + sendRtspRequest("GET_PARAMETER",_strUrl); + }else{ + //不支持GET_PARAMETER,用OPTIONS命令保活 + sendRtspRequest("OPTIONS",_strUrl); + } } void RtspPlayer::sendPause(int type , uint32_t seekMS){ @@ -695,10 +718,10 @@ void RtspPlayer::sendRtspRequest(const string &cmd, const string &url,const StrC void RtspPlayer::onRecvRTP_l(const RtpPacket::Ptr &pkt, const SdpTrack::Ptr &track) { _rtpTicker.resetTime(); - onRecvRTP(pkt,track); + onRecvRTP(pkt, track); int iTrackIndex = getTrackIndexByInterleaved(pkt->interleaved); - if(iTrackIndex == -1){ + if (iTrackIndex == -1) { return; } RtcpCounter &counter = _aRtcpCnt[iTrackIndex]; @@ -708,14 +731,17 @@ void RtspPlayer::onRecvRTP_l(const RtpPacket::Ptr &pkt, const SdpTrack::Ptr &tra //send rtcp every 5 second counter.lastTimeStamp = counter.timeStamp; //直接保存网络字节序 - memcpy(&counter.timeStamp, pkt->data() + 8 , 4); - if(counter.lastTimeStamp != 0){ - sendReceiverReport(_eType == Rtsp::RTP_TCP,iTrackIndex); + memcpy(&counter.timeStamp, pkt->data() + 8, 4); + if (counter.lastTimeStamp != 0) { + sendReceiverReport(_eType == Rtsp::RTP_TCP, iTrackIndex); ticker.resetTime(); } //有些rtsp服务器需要rtcp保活,有些需要发送信令保活 - sendGetParameter(); + if (iTrackIndex == 0) { + //只需要发送一次心跳信令包 + sendKeepAlive(); + } } } diff --git a/src/Rtsp/RtspPlayer.h b/src/Rtsp/RtspPlayer.h index 0e643555..f43753a9 100644 --- a/src/Rtsp/RtspPlayer.h +++ b/src/Rtsp/RtspPlayer.h @@ -95,11 +95,11 @@ private: bool handleAuthenticationFailure(const string &wwwAuthenticateParamsStr); void handleResPAUSE(const Parser &parser, int type); - //发送SETUP命令 + void sendOptions(); void sendSetup(unsigned int uiTrackIndex); void sendPause(int type , uint32_t ms); void sendDescribe(); - void sendGetParameter(); + void sendKeepAlive(); void sendRtspRequest(const string &cmd, const string &url ,const StrCaseMap &header = StrCaseMap()); void sendRtspRequest(const string &cmd, const string &url ,const std::initializer_list &header); void sendReceiverReport(bool overTcp,int iTrackIndex); @@ -141,6 +141,9 @@ private: bool _is_play_back; //是否为性能测试模式 bool _benchmark_mode = false; + + //服务器支持的命令 + set _supported_cmd; }; } /* namespace mediakit */ From 1970f6014e215ca768344eb861675a3e654fdaf0 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 15 May 2020 18:08:54 +0800 Subject: [PATCH 027/100] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=9F=B3=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/Stamp.cpp | 20 +++++++++----------- src/Common/Stamp.h | 7 ++++--- src/Extension/Frame.cpp | 15 +++++++++++---- src/Extension/Frame.h | 10 ++++++++++ src/Record/MP4Muxer.cpp | 23 +++++++++++++++++++++++ src/Record/MP4Muxer.h | 5 +++-- src/Record/TsMuxer.cpp | 26 ++++++++++++++++++++++++-- src/Record/TsMuxer.h | 40 +++++++++++++++++++++++++++++++--------- src/Rtmp/FlvMuxer.cpp | 2 +- src/Rtmp/RtmpSession.cpp | 2 +- 10 files changed, 117 insertions(+), 33 deletions(-) diff --git a/src/Common/Stamp.cpp b/src/Common/Stamp.cpp index fa449721..878fd183 100644 --- a/src/Common/Stamp.cpp +++ b/src/Common/Stamp.cpp @@ -42,8 +42,8 @@ void Stamp::setPlayBack(bool playback) { _playback = playback; } -void Stamp::makeRelation(Stamp &other){ - _related = &other; +void Stamp::syncTo(Stamp &other){ + _sync_master = &other; } void Stamp::revise(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp) { @@ -53,23 +53,21 @@ void Stamp::revise(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out, return; } - if(_related && _related->_last_dts){ + if(_sync_master && _sync_master->_last_dts){ //音视频dts当前时间差 - int64_t dts_diff = _last_dts - _related->_last_dts; + int64_t dts_diff = _last_dts - _sync_master->_last_dts; if(ABS(dts_diff) < 5000){ //如果绝对时间戳小于5秒,那么说明他们的起始时间戳是一致的,那么强制同步 _last_relativeStamp = _relativeStamp; - _relativeStamp = _related->_relativeStamp + dts_diff; - dts_out += dts_diff; - pts_out += dts_diff; -// DebugL << "音视频同步事件差:" << dts_diff; + _relativeStamp = _sync_master->_relativeStamp + dts_diff; } //下次不用再强制同步 - _related = nullptr; + _sync_master = nullptr; } - if(dts_out < 0){ - //相对时间戳小于0,那么说明是同步时间戳导致的,在这个过渡期内,我们一直返回上次的结果(目的是为了防止时间戳回退) + if(dts_out < 0 || dts_out < _last_relativeStamp){ + //相对时间戳小于0,或者小于上次的时间戳, + //那么说明是同步时间戳导致的,在这个过渡期内,我们一直返回上次的结果(目的是为了防止时间戳回退) pts_out = _last_relativeStamp + (pts_out - dts_out); dts_out = _last_relativeStamp; } diff --git a/src/Common/Stamp.h b/src/Common/Stamp.h index 2b57f7c0..2e497177 100644 --- a/src/Common/Stamp.h +++ b/src/Common/Stamp.h @@ -69,9 +69,10 @@ public: void setPlayBack(bool playback = true); /** - * 产生关联,用于音视频同步用 + * 音视频同步用,音频应该同步于视频(只修改音频时间戳) + * 因为音频时间戳修改后不影响播放速度 */ - void makeRelation(Stamp &other); + void syncTo(Stamp &other); private: void revise_l(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp = false); @@ -81,7 +82,7 @@ private: int64_t _last_dts = 0; SmoothTicker _ticker; bool _playback = false; - Stamp *_related = nullptr; + Stamp *_sync_master = nullptr; }; //dts生成器, diff --git a/src/Extension/Frame.cpp b/src/Extension/Frame.cpp index 9aab3fad..1a5f8fd3 100644 --- a/src/Extension/Frame.cpp +++ b/src/Extension/Frame.cpp @@ -76,8 +76,8 @@ Frame::Ptr Frame::getCacheAbleFrame(const Frame::Ptr &frame){ } #define SWITCH_CASE(codec_id) case codec_id : return #codec_id -const char *CodecInfo::getCodecName() { - switch (getCodecId()) { +const char *getCodecName(CodecId codecId) { + switch (codecId) { SWITCH_CASE(CodecH264); SWITCH_CASE(CodecH265); SWITCH_CASE(CodecAAC); @@ -88,8 +88,8 @@ const char *CodecInfo::getCodecName() { } } -TrackType CodecInfo::getTrackType(){ - switch (getCodecId()){ +TrackType getTrackType(CodecId codecId){ + switch (codecId){ case CodecH264: case CodecH265: return TrackVideo; case CodecAAC: @@ -100,4 +100,11 @@ TrackType CodecInfo::getTrackType(){ } } +const char *CodecInfo::getCodecName() { + return mediakit::getCodecName(getCodecId()); +} + +TrackType CodecInfo::getTrackType() { + return mediakit::getTrackType(getCodecId()); +} }//namespace mediakit diff --git a/src/Extension/Frame.h b/src/Extension/Frame.h index cafa876a..b3b37a32 100644 --- a/src/Extension/Frame.h +++ b/src/Extension/Frame.h @@ -40,6 +40,16 @@ typedef enum { TrackMax = 3 } TrackType; +/** + * 获取编码器名称 + */ +const char *getCodecName(CodecId codecId); + +/** + * 获取音视频类型 + */ +TrackType getTrackType(CodecId codecId); + /** * 编码信息的抽象接口 */ diff --git a/src/Record/MP4Muxer.cpp b/src/Record/MP4Muxer.cpp index 8f3bb60d..a543126a 100644 --- a/src/Record/MP4Muxer.cpp +++ b/src/Record/MP4Muxer.cpp @@ -134,6 +134,26 @@ static uint8_t getObject(CodecId codecId){ } } +void MP4Muxer::stampSync(){ + if(_codec_to_trackid.size() < 2){ + return; + } + + Stamp *audio = nullptr, *video = nullptr; + for(auto &pr : _codec_to_trackid){ + switch (getTrackType((CodecId) pr.first)){ + case TrackAudio : audio = &pr.second.stamp; break; + case TrackVideo : video = &pr.second.stamp; break; + default : break; + } + } + + if(audio && video){ + //音频时间戳同步于视频,因为音频时间戳被修改后不影响播放 + audio->syncTo(*video); + } +} + void MP4Muxer::addTrack(const Track::Ptr &track) { auto mp4_object = getObject(track->getCodecId()); if (!mp4_object) { @@ -261,6 +281,9 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { default: WarnL << "MP4录制不支持该编码格式:" << track->getCodecName(); break; } + + //尝试音视频同步 + stampSync(); } }//namespace mediakit diff --git a/src/Record/MP4Muxer.h b/src/Record/MP4Muxer.h index e5fd4807..eef8def9 100644 --- a/src/Record/MP4Muxer.h +++ b/src/Record/MP4Muxer.h @@ -45,13 +45,14 @@ public: private: void openMP4(); void closeMP4(); + void stampSync(); private: - struct track_info{ + struct track_info { int track_id = -1; Stamp stamp; }; - unordered_map _codec_to_trackid; + unordered_map _codec_to_trackid; List _frameCached; bool _started = false; bool _have_video = false; diff --git a/src/Record/TsMuxer.cpp b/src/Record/TsMuxer.cpp index 79419c74..560e2ef6 100644 --- a/src/Record/TsMuxer.cpp +++ b/src/Record/TsMuxer.cpp @@ -23,6 +23,26 @@ TsMuxer::~TsMuxer() { uninit(); } +void TsMuxer::stampSync(){ + if(_codec_to_trackid.size() < 2){ + return; + } + + Stamp *audio = nullptr, *video = nullptr; + for(auto &pr : _codec_to_trackid){ + switch (getTrackType((CodecId) pr.first)){ + case TrackAudio : audio = &pr.second.stamp; break; + case TrackVideo : video = &pr.second.stamp; break; + default : break; + } + } + + if(audio && video){ + //音频时间戳同步于视频,因为音频时间戳被修改后不影响播放 + audio->syncTo(*video); + } +} + void TsMuxer::addTrack(const Track::Ptr &track) { switch (track->getCodecId()) { case CodecH264: { @@ -52,9 +72,11 @@ void TsMuxer::addTrack(const Track::Ptr &track) { break; } - default: - break; + default: WarnL << "mpeg-ts 不支持该编码格式,已忽略:" << track->getCodecName(); break; } + + //尝试音视频同步 + stampSync(); } void TsMuxer::inputFrame(const Frame::Ptr &frame) { diff --git a/src/Record/TsMuxer.h b/src/Record/TsMuxer.h index 7f20e968..5a16f1cf 100644 --- a/src/Record/TsMuxer.h +++ b/src/Record/TsMuxer.h @@ -17,37 +17,59 @@ #include "Util/File.h" #include "Common/MediaSink.h" #include "Common/Stamp.h" - using namespace toolkit; - namespace mediakit { +//该类用于产生MPEG-TS class TsMuxer : public MediaSinkInterface { public: TsMuxer(); virtual ~TsMuxer(); + + /** + * 添加音视频轨道 + */ void addTrack(const Track::Ptr &track) override; + + /** + * 重置音视频轨道 + */ void resetTracks() override; + + /** + * 输入帧数据 + */ void inputFrame(const Frame::Ptr &frame) override; + protected: + /** + * 输出mpegts数据回调 + * @param packet mpegts数据 + * @param bytes mpegts数据长度 + * @param timestamp 时间戳,单位毫秒 + * @param is_idr_fast_packet 是否为关键帧的第一个TS包,用于确保ts切片第一帧为关键帧 + */ virtual void onTs(const void *packet, int bytes,uint32_t timestamp,bool is_idr_fast_packet) = 0; + private: void init(); void uninit(); -private: - void *_context = nullptr; - char *_tsbuf[188]; - uint32_t _timestamp = 0; + //音视频时间戳同步用 + void stampSync(); - struct track_info{ +private: + void *_context = nullptr; + char _tsbuf[188]; + uint32_t _timestamp = 0; + struct track_info { int track_id = -1; Stamp stamp; }; - unordered_map _codec_to_trackid; + unordered_map _codec_to_trackid; List _frameCached; bool _is_idr_fast_packet = false; bool _have_video = false; }; }//namespace mediakit -#endif //TSMUXER_H +#endif //TSMUXER_H \ No newline at end of file diff --git a/src/Rtmp/FlvMuxer.cpp b/src/Rtmp/FlvMuxer.cpp index 456f679b..bc29cf31 100644 --- a/src/Rtmp/FlvMuxer.cpp +++ b/src/Rtmp/FlvMuxer.cpp @@ -52,7 +52,7 @@ void FlvMuxer::start(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr & }); //音频同步于视频 - _stamp[0].makeRelation( _stamp[1]); + _stamp[0].syncTo(_stamp[1]); _ring_reader->setReadCB([weakSelf](const RtmpMediaSource::RingDataType &pkt){ auto strongSelf = weakSelf.lock(); if(!strongSelf){ diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index 873b1726..c2e55863 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -267,7 +267,7 @@ void RtmpSession::sendPlayResponse(const string &err,const RtmpMediaSource::Ptr }); //音频同步于视频 - _stamp[0].makeRelation( _stamp[1]); + _stamp[0].syncTo(_stamp[1]); _pRingReader = src->getRing()->attach(getPoller()); weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); _pRingReader->setReadCB([weakSelf](const RtmpMediaSource::RingDataType &pkt) { From cded823b65410bd83ca7b84268a89031b405f25e Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 15 May 2020 18:21:28 +0800 Subject: [PATCH 028/100] =?UTF-8?q?ps=20rtp=E6=8E=A8=E6=B5=81=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E9=9F=B3=E8=A7=86=E9=A2=91=E6=97=B6=E9=97=B4=E6=88=B3?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=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 | 6 +----- src/Rtp/RtpProcess.h | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Rtp/RtpProcess.cpp b/src/Rtp/RtpProcess.cpp index 4d27332d..bcfa69e0 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -233,11 +233,10 @@ static const char *getCodecName(int codec_id) { 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); + _dts = dts; switch (codecid) { case PSI_STREAM_H264: { - _dts = dts; if (!_codecid_video) { //获取到视频 _codecid_video = codecid; @@ -262,7 +261,6 @@ void RtpProcess::onDecode(int stream,int codecid,int flags,int64_t pts,int64_t d } case PSI_STREAM_H265: { - _dts = dts; if (!_codecid_video) { //获取到视频 _codecid_video = codecid; @@ -285,7 +283,6 @@ void RtpProcess::onDecode(int stream,int codecid,int flags,int64_t pts,int64_t d } case PSI_STREAM_AAC: { - _dts = dts; if (!_codecid_audio) { //获取到音频 _codecid_audio = codecid; @@ -304,7 +301,6 @@ void RtpProcess::onDecode(int stream,int codecid,int flags,int64_t pts,int64_t d case PSI_STREAM_AUDIO_G711A: case PSI_STREAM_AUDIO_G711U: { - _dts = dts; auto codec = codecid == PSI_STREAM_AUDIO_G711A ? CodecG711A : CodecG711U; if (!_codecid_audio) { //获取到音频 diff --git a/src/Rtp/RtpProcess.h b/src/Rtp/RtpProcess.h index cf252301..98140bc6 100644 --- a/src/Rtp/RtpProcess.h +++ b/src/Rtp/RtpProcess.h @@ -62,7 +62,6 @@ private: MultiMediaSourceMuxer::Ptr _muxer; std::shared_ptr _merger; Ticker _last_rtp_time; - unordered_map _stamps; uint32_t _dts = 0; Decoder::Ptr _decoder; std::weak_ptr _listener; From f71a9bfadfd3afedfb8256bd10311e6d05bc8c4b Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 15 May 2020 20:15:43 +0800 Subject: [PATCH 029/100] =?UTF-8?q?=E4=BF=AE=E5=A4=8Drtsp=E6=92=AD?= =?UTF-8?q?=E6=94=BE=E5=99=A8=E6=97=B6=E9=97=B4=E6=88=B3=E7=B4=8A=E4=B9=B1?= =?UTF-8?q?=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtsp/RtspPlayer.cpp | 267 +++++++++++++++++++--------------------- src/Rtsp/RtspPlayer.h | 50 ++++---- 2 files changed, 152 insertions(+), 165 deletions(-) diff --git a/src/Rtsp/RtspPlayer.cpp b/src/Rtsp/RtspPlayer.cpp index 87afee56..6e1aed29 100644 --- a/src/Rtsp/RtspPlayer.cpp +++ b/src/Rtsp/RtspPlayer.cpp @@ -10,14 +10,11 @@ #include #include -#include #include #include - #include "Common/config.h" #include "RtspPlayer.h" #include "Util/MD5.h" -#include "Util/mini.h" #include "Util/util.h" #include "Util/base64.h" #include "Network/sockutil.h" @@ -40,28 +37,28 @@ RtspPlayer::~RtspPlayer(void) { } void RtspPlayer::teardown(){ if (alive()) { - sendRtspRequest("TEARDOWN" ,_strContentBase); + sendRtspRequest("TEARDOWN" ,_content_base); shutdown(SockException(Err_shutdown,"teardown")); } - _rtspMd5Nonce.clear(); - _rtspRealm.clear(); - _aTrackInfo.clear(); - _strSession.clear(); - _strContentBase.clear(); + _md5_nonce.clear(); + _realm.clear(); + _sdp_track.clear(); + _session_id.clear(); + _content_base.clear(); RtpReceiver::clear(); - CLEAR_ARR(_apRtpSock); - CLEAR_ARR(_apRtcpSock); - CLEAR_ARR(_aui16FirstSeq) - CLEAR_ARR(_aui64RtpRecv) - CLEAR_ARR(_aui64RtpRecv) - CLEAR_ARR(_aui16NowSeq) + CLEAR_ARR(_rtp_sock); + CLEAR_ARR(_rtcp_sock); + CLEAR_ARR(_rtp_seq_start) + CLEAR_ARR(_rtp_recv_count) + CLEAR_ARR(_rtp_recv_count) + CLEAR_ARR(_rtp_seq_now) - _pPlayTimer.reset(); - _pRtpTimer.reset(); - _uiCseq = 1; - _onHandshake = nullptr; + _play_check_timer.reset(); + _rtp_check_timer.reset(); + _cseq_send = 1; + _on_response = nullptr; } void RtspPlayer::play(const string &strUrl){ @@ -81,20 +78,20 @@ void RtspPlayer::play(const string &strUrl){ (*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; + _play_url = url._url; + _rtp_type = (Rtsp::eRtpType)(int)(*this)[kRtpType]; + DebugL << url._url << " " << (url._user.size() ? url._user : "null") << " " << (url._passwd.size() ? url._passwd : "null") << " " << _rtp_type; weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); float playTimeOutSec = (*this)[kTimeoutMS].as() / 1000.0; - _pPlayTimer.reset( new Timer(playTimeOutSec, [weakSelf]() { + _play_check_timer.reset(new Timer(playTimeOutSec, [weakSelf]() { auto strongSelf=weakSelf.lock(); if(!strongSelf) { return false; } strongSelf->onPlayResult_l(SockException(Err_timeout,"play rtsp timeout"),false); return false; - },getPoller())); + }, getPoller())); if(!(*this)[kNetAdapter].empty()){ setNetAdapter((*this)[kNetAdapter]); @@ -111,9 +108,9 @@ void RtspPlayer::onConnect(const SockException &err){ } void RtspPlayer::onRecv(const Buffer::Ptr& pBuf) { - if(_benchmark_mode && !_pPlayTimer){ + if(_benchmark_mode && !_play_check_timer){ //在性能测试模式下,如果rtsp握手完毕后,不再解析rtp包 - _rtpTicker.resetTime(); + _rtp_recv_ticker.resetTime(); return; } input(pBuf->data(),pBuf->size()); @@ -121,12 +118,12 @@ void RtspPlayer::onRecv(const Buffer::Ptr& pBuf) { void RtspPlayer::onErr(const SockException &ex) { //定时器_pPlayTimer为空后表明握手结束了 - onPlayResult_l(ex,!_pPlayTimer); + onPlayResult_l(ex,!_play_check_timer); } // from live555 bool RtspPlayer::handleAuthenticationFailure(const string ¶msStr) { - if(!_rtspRealm.empty()){ + if(!_realm.empty()){ //已经认证过了 return false; } @@ -141,17 +138,17 @@ bool RtspPlayer::handleAuthenticationFailure(const string ¶msStr) { }); if (sscanf(paramsStr.data(), "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\", stale=%[a-zA-Z]", realm, nonce, stale) == 3) { - _rtspRealm = (const char *)realm; - _rtspMd5Nonce = (const char *)nonce; + _realm = (const char *)realm; + _md5_nonce = (const char *)nonce; return true; } if (sscanf(paramsStr.data(), "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"", realm, nonce) == 2) { - _rtspRealm = (const char *)realm; - _rtspMd5Nonce = (const char *)nonce; + _realm = (const char *)realm; + _md5_nonce = (const char *)nonce; return true; } if (sscanf(paramsStr.data(), "Basic realm=\"%[^\"]\"", realm) == 1) { - _rtspRealm = (const char *)realm; + _realm = (const char *)realm; return true; } return false; @@ -176,30 +173,25 @@ void RtspPlayer::handleResDESCRIBE(const Parser& parser) { throw std::runtime_error( StrPrinter << "DESCRIBE:" << parser.Url() << " " << parser.Tail() << endl); } - _strContentBase = parser["Content-Base"]; + _content_base = parser["Content-Base"]; - if(_strContentBase.empty()){ - _strContentBase = _strUrl; + if(_content_base.empty()){ + _content_base = _play_url; } - if (_strContentBase.back() == '/') { - _strContentBase.pop_back(); + if (_content_base.back() == '/') { + _content_base.pop_back(); } SdpParser sdpParser(parser.Content()); //解析sdp - _aTrackInfo = sdpParser.getAvailableTrack(); + _sdp_track = sdpParser.getAvailableTrack(); auto title = sdpParser.getTrack(TrackTitle); - _is_play_back = false; + bool is_play_back = false; if(title && title->_duration ){ - _is_play_back = true; + is_play_back = true; } - for(auto &stamp : _stamp){ - stamp.setPlayBack(_is_play_back); - stamp.setRelativeStamp(0); - } - - if (_aTrackInfo.empty()) { + if (_sdp_track.empty()) { throw std::runtime_error("无有效的Sdp Track"); } if (!onCheckSDP(sdpParser.toString())) { @@ -211,8 +203,8 @@ void RtspPlayer::handleResDESCRIBE(const Parser& parser) { //有必要的情况下创建udp端口 void RtspPlayer::createUdpSockIfNecessary(int track_idx){ - auto &rtpSockRef = _apRtpSock[track_idx]; - auto &rtcpSockRef = _apRtcpSock[track_idx]; + auto &rtpSockRef = _rtp_sock[track_idx]; + auto &rtcpSockRef = _rtcp_sock[track_idx]; if (!rtpSockRef || !rtcpSockRef) { auto pr = makeSockPair(getPoller(), get_local_ip()); rtpSockRef = pr.first; @@ -222,10 +214,10 @@ void RtspPlayer::createUdpSockIfNecessary(int track_idx){ //发送SETUP命令 void RtspPlayer::sendSetup(unsigned int trackIndex) { - _onHandshake = std::bind(&RtspPlayer::handleResSETUP,this, placeholders::_1,trackIndex); - auto &track = _aTrackInfo[trackIndex]; - auto baseUrl = _strContentBase + "/" + track->_control_surffix; - switch (_eType) { + _on_response = std::bind(&RtspPlayer::handleResSETUP, this, placeholders::_1, trackIndex); + auto &track = _sdp_track[trackIndex]; + auto baseUrl = _content_base + "/" + track->_control_surffix; + switch (_rtp_type) { case Rtsp::RTP_TCP: { sendRtspRequest("SETUP",baseUrl,{"Transport",StrPrinter << "RTP/AVP/TCP;unicast;interleaved=" << track->_type * 2 << "-" << track->_type * 2 + 1}); } @@ -236,10 +228,10 @@ void RtspPlayer::sendSetup(unsigned int trackIndex) { break; case Rtsp::RTP_UDP: { createUdpSockIfNecessary(trackIndex); - sendRtspRequest("SETUP",baseUrl,{"Transport", - StrPrinter << "RTP/AVP;unicast;client_port=" - << _apRtpSock[trackIndex]->get_local_port() << "-" - << _apRtcpSock[trackIndex]->get_local_port()}); + sendRtspRequest("SETUP", baseUrl, {"Transport", + StrPrinter << "RTP/AVP;unicast;client_port=" + << _rtp_sock[trackIndex]->get_local_port() << "-" + << _rtcp_sock[trackIndex]->get_local_port()}); } break; default: @@ -253,34 +245,34 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex) StrPrinter << "SETUP:" << parser.Url() << " " << parser.Tail() << endl); } if (uiTrackIndex == 0) { - _strSession = parser["Session"]; - _strSession.append(";"); - _strSession = FindField(_strSession.data(), nullptr, ";"); + _session_id = parser["Session"]; + _session_id.append(";"); + _session_id = FindField(_session_id.data(), nullptr, ";"); } auto strTransport = parser["Transport"]; if(strTransport.find("TCP") != string::npos || strTransport.find("interleaved") != string::npos){ - _eType = Rtsp::RTP_TCP; + _rtp_type = Rtsp::RTP_TCP; }else if(strTransport.find("multicast") != string::npos){ - _eType = Rtsp::RTP_MULTICAST; + _rtp_type = Rtsp::RTP_MULTICAST; }else{ - _eType = Rtsp::RTP_UDP; + _rtp_type = Rtsp::RTP_UDP; } - RtspSplitter::enableRecvRtp(_eType == Rtsp::RTP_TCP); + RtspSplitter::enableRecvRtp(_rtp_type == Rtsp::RTP_TCP); - if(_eType == Rtsp::RTP_TCP) { + if(_rtp_type == Rtsp::RTP_TCP) { string interleaved = FindField( FindField((strTransport + ";").data(), "interleaved=", ";").data(), NULL, "-"); - _aTrackInfo[uiTrackIndex]->_interleaved = atoi(interleaved.data()); + _sdp_track[uiTrackIndex]->_interleaved = atoi(interleaved.data()); }else{ - const char *strPos = (_eType == Rtsp::RTP_MULTICAST ? "port=" : "server_port=") ; + const char *strPos = (_rtp_type == Rtsp::RTP_MULTICAST ? "port=" : "server_port=") ; auto port_str = FindField((strTransport + ";").data(), strPos, ";"); uint16_t rtp_port = atoi(FindField(port_str.data(), NULL, "-").data()); uint16_t rtcp_port = atoi(FindField(port_str.data(), "-",NULL).data()); - auto &pRtpSockRef = _apRtpSock[uiTrackIndex]; - auto &pRtcpSockRef = _apRtcpSock[uiTrackIndex]; + auto &pRtpSockRef = _rtp_sock[uiTrackIndex]; + auto &pRtcpSockRef = _rtcp_sock[uiTrackIndex]; - if (_eType == Rtsp::RTP_MULTICAST) { + if (_rtp_type == Rtsp::RTP_MULTICAST) { //udp组播 auto multiAddr = FindField((strTransport + ";").data(), "destination=", ";"); pRtpSockRef.reset(new Socket(getPoller())); @@ -322,7 +314,7 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex) WarnL << "收到其他地址的rtp数据:" << SockUtil::inet_ntoa(((struct sockaddr_in *) addr)->sin_addr); return; } - strongSelf->handleOneRtp(uiTrackIndex, strongSelf->_aTrackInfo[uiTrackIndex], (unsigned char *) buf->data(), buf->size()); + strongSelf->handleOneRtp(uiTrackIndex, strongSelf->_sdp_track[uiTrackIndex], (unsigned char *) buf->data(), buf->size()); }); if(pRtcpSockRef) { @@ -336,12 +328,12 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex) WarnL << "收到其他地址的rtcp数据:" << SockUtil::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->_sdp_track[uiTrackIndex], (unsigned char *) buf->data(), buf->size()); }); } } - if (uiTrackIndex < _aTrackInfo.size() - 1) { + if (uiTrackIndex < _sdp_track.size() - 1) { //需要继续发送SETUP命令 sendSetup(uiTrackIndex + 1); return; @@ -353,12 +345,12 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex) void RtspPlayer::sendDescribe() { //发送DESCRIBE命令后处理函数:handleResDESCRIBE - _onHandshake = std::bind(&RtspPlayer::handleResDESCRIBE,this, placeholders::_1); - sendRtspRequest("DESCRIBE",_strUrl,{"Accept","application/sdp"}); + _on_response = std::bind(&RtspPlayer::handleResDESCRIBE, this, placeholders::_1); + sendRtspRequest("DESCRIBE", _play_url, {"Accept", "application/sdp"}); } void RtspPlayer::sendOptions(){ - _onHandshake = [this](const Parser& parser){ + _on_response = [this](const Parser& parser){ if (parser.Url() != "200") { throw std::runtime_error(StrPrinter << "OPTIONS:" << parser.Url() << " " << parser.Tail() << endl); } @@ -372,36 +364,36 @@ void RtspPlayer::sendOptions(){ //发送Describe请求,获取sdp sendDescribe(); }; - sendRtspRequest("OPTIONS",_strUrl); + sendRtspRequest("OPTIONS", _play_url); } void RtspPlayer::sendKeepAlive(){ - _onHandshake = [this](const Parser& parser){}; + _on_response = [this](const Parser& parser){}; if(_supported_cmd.find("GET_PARAMETER") != _supported_cmd.end()){ //支持GET_PARAMETER,用此命令保活 - sendRtspRequest("GET_PARAMETER",_strUrl); + sendRtspRequest("GET_PARAMETER", _play_url); }else{ //不支持GET_PARAMETER,用OPTIONS命令保活 - sendRtspRequest("OPTIONS",_strUrl); + sendRtspRequest("OPTIONS", _play_url); } } void RtspPlayer::sendPause(int type , uint32_t seekMS){ - _onHandshake = std::bind(&RtspPlayer::handleResPAUSE,this, placeholders::_1,type); + _on_response = std::bind(&RtspPlayer::handleResPAUSE, this, placeholders::_1, type); //开启或暂停rtsp switch (type){ case type_pause: - sendRtspRequest("PAUSE", _strContentBase); + sendRtspRequest("PAUSE", _content_base); break; case type_play: - sendRtspRequest("PLAY", _strContentBase); + sendRtspRequest("PLAY", _content_base); break; case type_seek: - sendRtspRequest("PLAY", _strContentBase, {"Range",StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << seekMS / 1000.0 << "-"}); + sendRtspRequest("PLAY", _content_base, {"Range",StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << seekMS / 1000.0 << "-"}); break; default: WarnL << "unknown type : " << type; - _onHandshake = nullptr; + _on_response = nullptr; break; } } @@ -428,7 +420,7 @@ void RtspPlayer::handleResPAUSE(const Parser& parser,int type) { if (type == type_pause) { //暂停成功! - _pRtpTimer.reset(); + _rtp_check_timer.reset(); return; } @@ -445,22 +437,20 @@ void RtspPlayer::handleResPAUSE(const Parser& parser,int type) { 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) { try { - decltype(_onHandshake) fun; - _onHandshake.swap(fun); - if(fun){ - fun(parser); + decltype(_on_response) func; + _on_response.swap(func); + if(func){ + func(parser); } parser.Clear(); } catch (std::exception &err) { //定时器_pPlayTimer为空后表明握手结束了 - onPlayResult_l(SockException(Err_other, err.what()),!_pPlayTimer); + onPlayResult_l(SockException(Err_other, err.what()),!_play_check_timer); } } @@ -470,12 +460,12 @@ void RtspPlayer::onRtpPacket(const char *data, uint64_t len) { if(interleaved %2 == 0){ trackIdx = getTrackIndexByInterleaved(interleaved); if (trackIdx != -1) { - handleOneRtp(trackIdx,_aTrackInfo[trackIdx],(unsigned char *)data + 4, len - 4); + handleOneRtp(trackIdx, _sdp_track[trackIdx], (unsigned char *)data + 4, len - 4); } }else{ trackIdx = getTrackIndexByInterleaved(interleaved - 1); if (trackIdx != -1) { - onRtcpPacket(trackIdx, _aTrackInfo[trackIdx], (unsigned char *) data + 4, len - 4); + onRtcpPacket(trackIdx, _sdp_track[trackIdx], (unsigned char *) data + 4, len - 4); } } } @@ -545,8 +535,8 @@ void RtspPlayer::sendReceiverReport(bool overTcp,int iTrackIndex){ static const char s_cname[] = "ZLMediaKitRtsp"; uint8_t aui8Rtcp[4 + 32 + 10 + sizeof(s_cname) + 1] = {0}; uint8_t *pui8Rtcp_RR = aui8Rtcp + 4, *pui8Rtcp_SDES = pui8Rtcp_RR + 32; - auto &track = _aTrackInfo[iTrackIndex]; - auto &counter = _aRtcpCnt[iTrackIndex]; + auto &track = _sdp_track[iTrackIndex]; + auto &counter = _rtcp_counter[iTrackIndex]; aui8Rtcp[0] = '$'; aui8Rtcp[1] = track->_interleaved + 1; @@ -602,25 +592,22 @@ void RtspPlayer::sendReceiverReport(bool overTcp,int iTrackIndex){ if(overTcp){ send(obtainBuffer((char *) aui8Rtcp, sizeof(aui8Rtcp))); - }else if(_apRtcpSock[iTrackIndex]) { - _apRtcpSock[iTrackIndex]->send((char *) aui8Rtcp + 4, sizeof(aui8Rtcp) - 4); + }else if(_rtcp_sock[iTrackIndex]) { + _rtcp_sock[iTrackIndex]->send((char *) aui8Rtcp + 4, sizeof(aui8Rtcp) - 4); } } void RtspPlayer::onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx){ //统计丢包率 - if (_aui16FirstSeq[trackidx] == 0 || rtppt->sequence < _aui16FirstSeq[trackidx]) { - _aui16FirstSeq[trackidx] = rtppt->sequence; - _aui64RtpRecv[trackidx] = 0; + if (_rtp_seq_start[trackidx] == 0 || rtppt->sequence < _rtp_seq_start[trackidx]) { + _rtp_seq_start[trackidx] = rtppt->sequence; + _rtp_recv_count[trackidx] = 0; } - _aui64RtpRecv[trackidx] ++; - _aui16NowSeq[trackidx] = rtppt->sequence; - + _rtp_recv_count[trackidx] ++; + _rtp_seq_now[trackidx] = rtppt->sequence; + _stamp[trackidx] = rtppt->timeStamp; //计算相对时间戳 - int64_t dts_out; - _stamp[trackidx].revise(rtppt->timeStamp,rtppt->timeStamp,dts_out,dts_out); - rtppt->timeStamp = dts_out; - onRecvRTP_l(rtppt,_aTrackInfo[trackidx]); + onRecvRTP_l(rtppt, _sdp_track[trackidx]); } float RtspPlayer::getPacketLossRate(TrackType type) const{ @@ -628,9 +615,9 @@ float RtspPlayer::getPacketLossRate(TrackType type) const{ if(iTrackIdx == -1){ uint64_t totalRecv = 0; uint64_t totalSend = 0; - for (unsigned int i = 0; i < _aTrackInfo.size(); i++) { - totalRecv += _aui64RtpRecv[i]; - totalSend += (_aui16NowSeq[i] - _aui16FirstSeq[i] + 1); + for (unsigned int i = 0; i < _sdp_track.size(); i++) { + totalRecv += _rtp_recv_count[i]; + totalSend += (_rtp_seq_now[i] - _rtp_seq_start[i] + 1); } if(totalSend == 0){ return 0; @@ -638,14 +625,14 @@ float RtspPlayer::getPacketLossRate(TrackType type) const{ return 1.0 - (double)totalRecv / totalSend; } - if(_aui16NowSeq[iTrackIdx] - _aui16FirstSeq[iTrackIdx] + 1 == 0){ + if(_rtp_seq_now[iTrackIdx] - _rtp_seq_start[iTrackIdx] + 1 == 0){ return 0; } - return 1.0 - (double)_aui64RtpRecv[iTrackIdx] / (_aui16NowSeq[iTrackIdx] - _aui16FirstSeq[iTrackIdx] + 1); + return 1.0 - (double)_rtp_recv_count[iTrackIdx] / (_rtp_seq_now[iTrackIdx] - _rtp_seq_start[iTrackIdx] + 1); } uint32_t RtspPlayer::getProgressMilliSecond() const{ - return MAX(_stamp[0].getRelativeStamp(),_stamp[1].getRelativeStamp()); + return MAX(_stamp[0],_stamp[1]); } void RtspPlayer::seekToMilliSecond(uint32_t ms) { @@ -668,15 +655,15 @@ void RtspPlayer::sendRtspRequest(const string &cmd, const string &url, const std void RtspPlayer::sendRtspRequest(const string &cmd, const string &url,const StrCaseMap &header_const) { auto header = header_const; - header.emplace("CSeq",StrPrinter << _uiCseq++); + header.emplace("CSeq",StrPrinter << _cseq_send++); header.emplace("User-Agent",SERVER_NAME); - if(!_strSession.empty()){ - header.emplace("Session",_strSession); + if(!_session_id.empty()){ + header.emplace("Session", _session_id); } - if(!_rtspRealm.empty() && !(*this)[kRtspUser].empty()){ - if(!_rtspMd5Nonce.empty()){ + if(!_realm.empty() && !(*this)[kRtspUser].empty()){ + if(!_md5_nonce.empty()){ //MD5认证 /* response计算方法如下: @@ -688,14 +675,14 @@ void RtspPlayer::sendRtspRequest(const string &cmd, const string &url,const StrC */ string encrypted_pwd = (*this)[kRtspPwd]; if(!(*this)[kRtspPwdIsMD5].as()){ - encrypted_pwd = MD5((*this)[kRtspUser]+ ":" + _rtspRealm + ":" + encrypted_pwd).hexdigest(); + encrypted_pwd = MD5((*this)[kRtspUser] + ":" + _realm + ":" + encrypted_pwd).hexdigest(); } - auto response = MD5( encrypted_pwd + ":" + _rtspMd5Nonce + ":" + MD5(cmd + ":" + url).hexdigest()).hexdigest(); + auto response = MD5(encrypted_pwd + ":" + _md5_nonce + ":" + MD5(cmd + ":" + url).hexdigest()).hexdigest(); _StrPrinter printer; printer << "Digest "; printer << "username=\"" << (*this)[kRtspUser] << "\", "; - printer << "realm=\"" << _rtspRealm << "\", "; - printer << "nonce=\"" << _rtspMd5Nonce << "\", "; + printer << "realm=\"" << _realm << "\", "; + printer << "nonce=\"" << _md5_nonce << "\", "; printer << "uri=\"" << url << "\", "; printer << "response=\"" << response << "\""; header.emplace("Authorization",printer); @@ -717,23 +704,23 @@ void RtspPlayer::sendRtspRequest(const string &cmd, const string &url,const StrC } void RtspPlayer::onRecvRTP_l(const RtpPacket::Ptr &pkt, const SdpTrack::Ptr &track) { - _rtpTicker.resetTime(); + _rtp_recv_ticker.resetTime(); onRecvRTP(pkt, track); int iTrackIndex = getTrackIndexByInterleaved(pkt->interleaved); if (iTrackIndex == -1) { return; } - RtcpCounter &counter = _aRtcpCnt[iTrackIndex]; + RtcpCounter &counter = _rtcp_counter[iTrackIndex]; counter.pktCnt = pkt->sequence; - auto &ticker = _aRtcpTicker[iTrackIndex]; + auto &ticker = _rtcp_send_ticker[iTrackIndex]; if (ticker.elapsedTime() > 5 * 1000) { //send rtcp every 5 second counter.lastTimeStamp = counter.timeStamp; //直接保存网络字节序 memcpy(&counter.timeStamp, pkt->data() + 8, 4); if (counter.lastTimeStamp != 0) { - sendReceiverReport(_eType == Rtsp::RTP_TCP, iTrackIndex); + sendReceiverReport(_rtp_type == Rtsp::RTP_TCP, iTrackIndex); ticker.resetTime(); } @@ -750,27 +737,27 @@ void RtspPlayer::onPlayResult_l(const SockException &ex , bool handshakeComplete if(!ex){ //播放成功,恢复rtp接收超时定时器 - _rtpTicker.resetTime(); + _rtp_recv_ticker.resetTime(); weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); int timeoutMS = (*this)[kMediaTimeoutMS].as(); //创建rtp数据接收超时检测定时器 - _pRtpTimer.reset( new Timer(timeoutMS / 2000.0, [weakSelf,timeoutMS]() { + _rtp_check_timer.reset(new Timer(timeoutMS / 2000.0, [weakSelf,timeoutMS]() { auto strongSelf=weakSelf.lock(); if(!strongSelf) { return false; } - if(strongSelf->_rtpTicker.elapsedTime()> timeoutMS) { + if(strongSelf->_rtp_recv_ticker.elapsedTime() > timeoutMS) { //接收rtp媒体数据包超时 strongSelf->onPlayResult_l(SockException(Err_timeout,"receive rtp timeout"), true); return false; } return true; - },getPoller())); + }, getPoller())); } if (!handshakeCompleted) { //开始播放阶段 - _pPlayTimer.reset(); + _play_check_timer.reset(); onPlayResult(ex); //是否为性能测试模式 _benchmark_mode = (*this)[Client::kBenchmarkMode].as(); @@ -787,25 +774,25 @@ void RtspPlayer::onPlayResult_l(const SockException &ex , bool handshakeComplete } } -int RtspPlayer::getTrackIndexByInterleaved(int interleaved) const{ - for (unsigned int i = 0; i < _aTrackInfo.size(); i++) { - if (_aTrackInfo[i]->_interleaved == interleaved) { +int RtspPlayer::getTrackIndexByInterleaved(int interleaved) const { + for (unsigned int i = 0; i < _sdp_track.size(); i++) { + if (_sdp_track[i]->_interleaved == interleaved) { return i; } } - if(_aTrackInfo.size() == 1){ + if (_sdp_track.size() == 1) { return 0; } return -1; } int RtspPlayer::getTrackIndexByTrackType(TrackType trackType) const { - for (unsigned int i = 0; i < _aTrackInfo.size(); i++) { - if (_aTrackInfo[i]->_type == trackType) { + for (unsigned int i = 0; i < _sdp_track.size(); i++) { + if (_sdp_track[i]->_type == trackType) { return i; } } - if(_aTrackInfo.size() == 1){ + if (_sdp_track.size() == 1) { return 0; } return -1; diff --git a/src/Rtsp/RtspPlayer.h b/src/Rtsp/RtspPlayer.h index f43753a9..97eadc24 100644 --- a/src/Rtsp/RtspPlayer.h +++ b/src/Rtsp/RtspPlayer.h @@ -105,40 +105,40 @@ private: void sendReceiverReport(bool overTcp,int iTrackIndex); void createUdpSockIfNecessary(int track_idx); private: - string _strUrl; - vector _aTrackInfo; - function _onHandshake; - Socket::Ptr _apRtpSock[2]; //RTP端口,trackid idx 为数组下标 - Socket::Ptr _apRtcpSock[2];//RTCP端口,trackid idx 为数组下标 + string _play_url; + vector _sdp_track; + function _on_response; + //RTP端口,trackid idx 为数组下标 + Socket::Ptr _rtp_sock[2]; + //RTCP端口,trackid idx 为数组下标 + Socket::Ptr _rtcp_sock[2]; //rtsp鉴权相关 - string _rtspMd5Nonce; - string _rtspRealm; + string _md5_nonce; + string _realm; //rtsp info - string _strSession; - unsigned int _uiCseq = 1; - string _strContentBase; - Rtsp::eRtpType _eType = Rtsp::RTP_TCP; + string _session_id; + uint32_t _cseq_send = 1; + string _content_base; + Rtsp::eRtpType _rtp_type = Rtsp::RTP_TCP; /* 丢包率统计需要用到的参数 */ - uint16_t _aui16FirstSeq[2] = { 0 , 0}; - uint16_t _aui16NowSeq[2] = { 0 , 0 }; - uint64_t _aui64RtpRecv[2] = { 0 , 0}; + uint16_t _rtp_seq_start[2] = {0, 0}; + uint16_t _rtp_seq_now[2] = {0, 0}; + uint64_t _rtp_recv_count[2] = {0, 0}; + //当前rtp时间戳 + uint32_t _stamp[2] = {0, 0}; //超时功能实现 - Ticker _rtpTicker; - std::shared_ptr _pPlayTimer; - std::shared_ptr _pRtpTimer; + Ticker _rtp_recv_ticker; + std::shared_ptr _play_check_timer; + std::shared_ptr _rtp_check_timer; - //时间戳 - Stamp _stamp[2]; + //rtcp统计,trackid idx 为数组下标 + RtcpCounter _rtcp_counter[2]; + //rtcp发送时间,trackid idx 为数组下标 + Ticker _rtcp_send_ticker[2]; - //rtcp相关 - RtcpCounter _aRtcpCnt[2]; //rtcp统计,trackid idx 为数组下标 - Ticker _aRtcpTicker[2]; //rtcp发送时间,trackid idx 为数组下标 - - //是否为rtsp点播 - bool _is_play_back; //是否为性能测试模式 bool _benchmark_mode = false; From be9e31bba25c493a5457567cb6c677697db757ee Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 15 May 2020 21:39:06 +0800 Subject: [PATCH 030/100] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=8C=87=E9=92=88?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2=E5=81=8F=E7=A7=BB=E9=87=8F=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/Frame.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Extension/Frame.h b/src/Extension/Frame.h index b3b37a32..7e26dc4b 100644 --- a/src/Extension/Frame.h +++ b/src/Extension/Frame.h @@ -264,7 +264,7 @@ public: void addDelegate(const FrameWriterInterface::Ptr &delegate){ //_delegates_write可能多线程同时操作 lock_guard lck(_mtx); - _delegates_write.emplace(delegate.get(),delegate); + _delegates_write.emplace((void *)delegate.get(),delegate); _need_update = true; } From cf599167c1fec27fea902fe69244110c1e2905b5 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 15 May 2020 21:48:29 +0800 Subject: [PATCH 031/100] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=8C=87=E9=92=88?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2=E5=81=8F=E7=A7=BB=E9=87=8F=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/Frame.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Extension/Frame.h b/src/Extension/Frame.h index 7e26dc4b..5b34654b 100644 --- a/src/Extension/Frame.h +++ b/src/Extension/Frame.h @@ -264,14 +264,14 @@ public: void addDelegate(const FrameWriterInterface::Ptr &delegate){ //_delegates_write可能多线程同时操作 lock_guard lck(_mtx); - _delegates_write.emplace((void *)delegate.get(),delegate); + _delegates_write.emplace(delegate.get(),delegate); _need_update = true; } /** * 删除代理 */ - void delDelegate(void *ptr){ + void delDelegate(FrameWriterInterface *ptr){ //_delegates_write可能多线程同时操作 lock_guard lck(_mtx); _delegates_write.erase(ptr); From 198f223d630edf372d81e7affbd23be025c756be Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 17 May 2020 18:00:23 +0800 Subject: [PATCH 032/100] =?UTF-8?q?=E8=BF=9B=E4=B8=80=E6=AD=A5=E6=8A=BD?= =?UTF-8?q?=E8=B1=A1ts/ps=E8=A7=A3=E6=9E=90=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtp/Decoder.cpp | 197 +++++++++++++++++++++++++++++++++++++++-- src/Rtp/Decoder.h | 51 +++++++++-- src/Rtp/RtpProcess.cpp | 187 ++++---------------------------------- src/Rtp/RtpProcess.h | 12 ++- src/Rtp/TSDecoder.cpp | 32 +++---- src/Rtp/TSDecoder.h | 12 ++- 6 files changed, 283 insertions(+), 208 deletions(-) diff --git a/src/Rtp/Decoder.cpp b/src/Rtp/Decoder.cpp index e37fec10..2e3349dd 100644 --- a/src/Rtp/Decoder.cpp +++ b/src/Rtp/Decoder.cpp @@ -8,18 +8,203 @@ * may be found in the AUTHORS file in the root of the source tree. */ -#if defined(ENABLE_RTPPROXY) #include "Decoder.h" #include "PSDecoder.h" #include "TSDecoder.h" +#include "mpeg-ts-proto.h" +#include "Extension/H264.h" +#include "Extension/H265.h" +#include "Extension/AAC.h" +#include "Extension/G711.h" + namespace mediakit { -Decoder::Ptr Decoder::createDecoder(Decoder::Type type) { +static Decoder::Ptr createDecoder_l(DecoderImp::Type type) { switch (type){ - case decoder_ps : return std::make_shared(); - case decoder_ts : return std::make_shared(); - default : return nullptr; + case DecoderImp::decoder_ps: +#ifdef ENABLE_RTPPROXY + return std::make_shared(); +#else + WarnL << "创建ps解复用器失败,请打开ENABLE_RTPPROXY然后重新编译"; + return nullptr; +#endif//ENABLE_RTPPROXY + + case DecoderImp::decoder_ts: +#ifdef ENABLE_HLS + return std::make_shared(); +#else + WarnL << "创建mpegts解复用器失败,请打开ENABLE_HLS然后重新编译"; + return nullptr; +#endif//ENABLE_HLS + + default: return nullptr; } } +///////////////////////////////////////////////////////////// + +DecoderImp::Ptr DecoderImp::createDecoder(Type type, const MediaSinkInterface::Ptr &sink){ + auto decoder = createDecoder_l(type); + if(!decoder){ + return nullptr; + } + return DecoderImp::Ptr(new DecoderImp(decoder, sink)); +} + +int DecoderImp::input(const uint8_t *data, int bytes){ + return _decoder->input(data, bytes); +} + +DecoderImp::DecoderImp(const Decoder::Ptr &decoder,const MediaSinkInterface::Ptr &sink){ + _decoder = decoder; + _sink = sink; + _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); + }); +} + +#define SWITCH_CASE(codec_id) case codec_id : return #codec_id +static const char *getCodecName(int codec_id) { + switch (codec_id) { + SWITCH_CASE(PSI_STREAM_MPEG1); + SWITCH_CASE(PSI_STREAM_MPEG2); + SWITCH_CASE(PSI_STREAM_AUDIO_MPEG1); + SWITCH_CASE(PSI_STREAM_MP3); + SWITCH_CASE(PSI_STREAM_AAC); + SWITCH_CASE(PSI_STREAM_MPEG4); + SWITCH_CASE(PSI_STREAM_MPEG4_AAC_LATM); + SWITCH_CASE(PSI_STREAM_H264); + SWITCH_CASE(PSI_STREAM_MPEG4_AAC); + SWITCH_CASE(PSI_STREAM_H265); + SWITCH_CASE(PSI_STREAM_AUDIO_AC3); + SWITCH_CASE(PSI_STREAM_AUDIO_EAC3); + SWITCH_CASE(PSI_STREAM_AUDIO_DTS); + SWITCH_CASE(PSI_STREAM_VIDEO_DIRAC); + SWITCH_CASE(PSI_STREAM_VIDEO_VC1); + SWITCH_CASE(PSI_STREAM_VIDEO_SVAC); + SWITCH_CASE(PSI_STREAM_AUDIO_SVAC); + SWITCH_CASE(PSI_STREAM_AUDIO_G711A); + SWITCH_CASE(PSI_STREAM_AUDIO_G711U); + SWITCH_CASE(PSI_STREAM_AUDIO_G722); + SWITCH_CASE(PSI_STREAM_AUDIO_G723); + SWITCH_CASE(PSI_STREAM_AUDIO_G729); + default : return "unknown codec"; + } +} + +void FrameMerger::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)); +} + +void DecoderImp::onDecode(int stream,int codecid,int flags,int64_t pts,int64_t dts,const void *data,int bytes) { + pts /= 90; + dts /= 90; + + switch (codecid) { + case PSI_STREAM_H264: { + if (!_codecid_video) { + //获取到视频 + _codecid_video = codecid; + InfoL<< "got video track: H264"; + auto track = std::make_shared(); + onTrack(track); + } + + if (codecid != _codecid_video) { + WarnL<< "video track change to H264 from codecid:" << getCodecName(_codecid_video); + return; + } + + 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) { + onFrame(std::make_shared(buffer->data(), buffer->size(), dts, pts,4)); + }); + break; + } + + case PSI_STREAM_H265: { + if (!_codecid_video) { + //获取到视频 + _codecid_video = codecid; + InfoL<< "got video track: H265"; + auto track = std::make_shared(); + onTrack(track); + } + if (codecid != _codecid_video) { + WarnL<< "video track change to H265 from codecid:" << getCodecName(_codecid_video); + return; + } + 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) { + onFrame(std::make_shared(buffer->data(), buffer->size(), dts, pts, 4)); + }); + break; + } + + case PSI_STREAM_AAC: { + if (!_codecid_audio) { + //获取到音频 + _codecid_audio = codecid; + InfoL<< "got audio track: AAC"; + auto track = std::make_shared(); + onTrack(track); + } + + if (codecid != _codecid_audio) { + WarnL<< "audio track change to AAC from codecid:" << getCodecName(_codecid_audio); + return; + } + onFrame(std::make_shared((char *) data, bytes, dts, 0, 7)); + break; + } + + case PSI_STREAM_AUDIO_G711A: + case PSI_STREAM_AUDIO_G711U: { + auto codec = codecid == PSI_STREAM_AUDIO_G711A ? CodecG711A : CodecG711U; + if (!_codecid_audio) { + //获取到音频 + _codecid_audio = codecid; + InfoL<< "got audio track: G711"; + //G711传统只支持 8000/1/16的规格,FFmpeg貌似做了扩展,但是这里不管它了 + auto track = std::make_shared(codec, 8000, 1, 16); + onTrack(track); + } + + if (codecid != _codecid_audio) { + WarnL<< "audio track change to G711 from codecid:" << getCodecName(_codecid_audio); + return; + } + auto frame = std::make_shared((char *) data, bytes, dts); + frame->setCodec(codec); + onFrame(frame); + break; + } + default: + if(codecid != 0){ + WarnL<< "unsupported codec type:" << getCodecName(codecid) << " " << (int)codecid; + } + break; + } +} + +void DecoderImp::onTrack(const Track::Ptr &track) { + _sink->addTrack(track); +} + +void DecoderImp::onFrame(const Frame::Ptr &frame) { + _sink->inputFrame(frame); +} + }//namespace mediakit -#endif//defined(ENABLE_RTPPROXY) diff --git a/src/Rtp/Decoder.h b/src/Rtp/Decoder.h index 203d51d9..c20bc65d 100644 --- a/src/Rtp/Decoder.h +++ b/src/Rtp/Decoder.h @@ -11,31 +11,66 @@ #ifndef ZLMEDIAKIT_DECODER_H #define ZLMEDIAKIT_DECODER_H -#if defined(ENABLE_RTPPROXY) #include #include #include #include "Decoder.h" +#include "Common/MediaSink.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; }; +/** + * 合并一些时间戳相同的frame + */ +class FrameMerger { +public: + FrameMerger() = default; + ~FrameMerger() = default; + void inputFrame(const Frame::Ptr &frame,const function &cb); +private: + List _frameCached; +}; + +class DecoderImp{ +public: + typedef enum { + decoder_ts = 0, + decoder_ps + }Type; + + typedef std::shared_ptr Ptr; + ~DecoderImp() = default; + + static Ptr createDecoder(Type type, const MediaSinkInterface::Ptr &sink); + int input(const uint8_t *data, int bytes); + +protected: + void onTrack(const Track::Ptr &track); + void onFrame(const Frame::Ptr &frame); + +private: + DecoderImp(const Decoder::Ptr &decoder, const MediaSinkInterface::Ptr &sink); + void onDecode(int stream,int codecid,int flags,int64_t pts,int64_t dts,const void *data,int bytes); + +private: + Decoder::Ptr _decoder; + MediaSinkInterface::Ptr _sink; + FrameMerger _merger; + int _codecid_video = 0; + int _codecid_audio = 0; +}; + }//namespace mediakit -#endif//defined(ENABLE_RTPPROXY) #endif //ZLMEDIAKIT_DECODER_H diff --git a/src/Rtp/RtpProcess.cpp b/src/Rtp/RtpProcess.cpp index bcfa69e0..008c7406 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -9,44 +9,13 @@ */ #if defined(ENABLE_RTPPROXY) -#include "mpeg-ts-proto.h" #include "RtpProcess.h" #include "Util/File.h" -#include "Extension/H265.h" -#include "Extension/AAC.h" -#include "Extension/G711.h" +#include "Http/HttpTSPlayer.h" #define RTP_APP_NAME "rtp" 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); @@ -101,7 +70,6 @@ RtpProcess::RtpProcess(uint32_t ssrc) { }); } } - _merger = std::make_shared(); } RtpProcess::~RtpProcess() { @@ -157,7 +125,7 @@ bool RtpProcess::inputRtp(const Socket::Ptr &sock, const char *data, int data_le //判断是否为ts负载 static inline bool checkTS(const uint8_t *packet, int bytes){ - return bytes % 188 == 0 && packet[0] == 0x47; + return bytes % TS_PACKET_SIZE == 0 && packet[0] == TS_SYNC_BYTE; } void RtpProcess::onRtpSorted(const RtpPacket::Ptr &rtp, int) { @@ -179,153 +147,37 @@ void RtpProcess::onRtpDecode(const uint8_t *packet, int bytes, uint32_t timestam fwrite((uint8_t *)packet,bytes, 1, _save_file_ps.get()); } - if(!_decoder){ + if (!_decoder) { //创建解码器 - if(checkTS(packet, bytes)){ + if (checkTS(packet, bytes)) { //猜测是ts负载 InfoP(this) << "judged to be TS"; - _decoder = Decoder::createDecoder(Decoder::decoder_ts); - }else{ + _decoder = DecoderImp::createDecoder(DecoderImp::decoder_ts, shared_from_this()); + } else { //猜测是ps负载 InfoP(this) << "judged to be PS"; - _decoder = Decoder::createDecoder(Decoder::decoder_ps); + _decoder = DecoderImp::createDecoder(DecoderImp::decoder_ps, shared_from_this()); } - _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){ - WarnP(this) << ret << " != " << bytes << " " << flags; + if (_decoder) { + auto ret = _decoder->input((uint8_t *) packet, bytes); + if (ret != bytes) { + WarnP(this) << ret << " != " << bytes << " " << flags; + } } } -#define SWITCH_CASE(codec_id) case codec_id : return #codec_id -static const char *getCodecName(int codec_id) { - switch (codec_id) { - SWITCH_CASE(PSI_STREAM_MPEG1); - SWITCH_CASE(PSI_STREAM_MPEG2); - SWITCH_CASE(PSI_STREAM_AUDIO_MPEG1); - SWITCH_CASE(PSI_STREAM_MP3); - SWITCH_CASE(PSI_STREAM_AAC); - SWITCH_CASE(PSI_STREAM_MPEG4); - SWITCH_CASE(PSI_STREAM_MPEG4_AAC_LATM); - SWITCH_CASE(PSI_STREAM_H264); - SWITCH_CASE(PSI_STREAM_MPEG4_AAC); - SWITCH_CASE(PSI_STREAM_H265); - SWITCH_CASE(PSI_STREAM_AUDIO_AC3); - SWITCH_CASE(PSI_STREAM_AUDIO_EAC3); - SWITCH_CASE(PSI_STREAM_AUDIO_DTS); - SWITCH_CASE(PSI_STREAM_VIDEO_DIRAC); - SWITCH_CASE(PSI_STREAM_VIDEO_VC1); - SWITCH_CASE(PSI_STREAM_VIDEO_SVAC); - SWITCH_CASE(PSI_STREAM_AUDIO_SVAC); - SWITCH_CASE(PSI_STREAM_AUDIO_G711A); - SWITCH_CASE(PSI_STREAM_AUDIO_G711U); - SWITCH_CASE(PSI_STREAM_AUDIO_G722); - SWITCH_CASE(PSI_STREAM_AUDIO_G723); - SWITCH_CASE(PSI_STREAM_AUDIO_G729); - default : return "unknown codec"; +void RtpProcess::inputFrame(const Frame::Ptr &frame){ + _dts = frame->dts(); + if (_save_file_video && frame->getTrackType() == TrackVideo) { + fwrite((uint8_t *) frame->data(), frame->size(), 1, _save_file_video.get()); } + _muxer->inputFrame(frame); } -void RtpProcess::onDecode(int stream,int codecid,int flags,int64_t pts,int64_t dts,const void *data,int bytes) { - pts /= 90; - dts /= 90; - _dts = dts; - - switch (codecid) { - case PSI_STREAM_H264: { - if (!_codecid_video) { - //获取到视频 - _codecid_video = codecid; - InfoP(this) << "got video track: H264"; - auto track = std::make_shared(); - _muxer->addTrack(track); - } - - if (codecid != _codecid_video) { - WarnP(this) << "video track change to H264 from codecid:" << getCodecName(_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, 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; - } - - case PSI_STREAM_H265: { - if (!_codecid_video) { - //获取到视频 - _codecid_video = codecid; - InfoP(this) << "got video track: H265"; - auto track = std::make_shared(); - _muxer->addTrack(track); - } - if (codecid != _codecid_video) { - WarnP(this) << "video track change to H265 from codecid:" << getCodecName(_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, 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; - } - - case PSI_STREAM_AAC: { - if (!_codecid_audio) { - //获取到音频 - _codecid_audio = codecid; - InfoP(this) << "got audio track: AAC"; - auto track = std::make_shared(); - _muxer->addTrack(track); - } - - if (codecid != _codecid_audio) { - WarnP(this) << "audio track change to AAC from codecid:" << getCodecName(_codecid_audio); - return; - } - _muxer->inputFrame(std::make_shared((char *) data, bytes, dts, 0, 7)); - break; - } - - case PSI_STREAM_AUDIO_G711A: - case PSI_STREAM_AUDIO_G711U: { - auto codec = codecid == PSI_STREAM_AUDIO_G711A ? CodecG711A : CodecG711U; - if (!_codecid_audio) { - //获取到音频 - _codecid_audio = codecid; - InfoP(this) << "got audio track: G711"; - //G711传统只支持 8000/1/16的规格,FFmpeg貌似做了扩展,但是这里不管它了 - auto track = std::make_shared(codec, 8000, 1, 16); - _muxer->addTrack(track); - } - - if (codecid != _codecid_audio) { - WarnP(this) << "audio track change to G711 from codecid:" << getCodecName(_codecid_audio); - return; - } - auto frame = std::make_shared((char *) data, bytes, dts); - frame->setCodec(codec); - _muxer->inputFrame(frame); - break; - } - default: - if(codecid != 0){ - WarnP(this) << "unsupported codec type:" << getCodecName(codecid) << " " << (int)codecid; - } - return; - } +void RtpProcess::addTrack(const Track::Ptr & track){ + _muxer->addTrack(track); } bool RtpProcess::alive() { @@ -410,6 +262,5 @@ void RtpProcess::emitOnPublish() { } } - }//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 98140bc6..7b054292 100644 --- a/src/Rtp/RtpProcess.h +++ b/src/Rtp/RtpProcess.h @@ -23,8 +23,7 @@ using namespace mediakit; namespace mediakit{ string printSSRC(uint32_t ui32Ssrc); -class FrameMerger; -class RtpProcess : public RtpReceiver , public RtpDecoder, public SockInfo, public std::enable_shared_from_this{ +class RtpProcess : public RtpReceiver , public RtpDecoder, public SockInfo, public MediaSinkInterface, public std::enable_shared_from_this{ public: typedef std::shared_ptr Ptr; RtpProcess(uint32_t ssrc); @@ -44,7 +43,9 @@ public: protected: void onRtpSorted(const RtpPacket::Ptr &rtp, int track_index) 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); + void inputFrame(const Frame::Ptr &frame) override; + void addTrack(const Track::Ptr & track) override; + void resetTracks() override {}; private: void emitOnPublish(); @@ -57,13 +58,10 @@ private: 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; uint32_t _dts = 0; - Decoder::Ptr _decoder; + DecoderImp::Ptr _decoder; std::weak_ptr _listener; MediaInfo _media_info; uint64_t _total_bytes = 0; diff --git a/src/Rtp/TSDecoder.cpp b/src/Rtp/TSDecoder.cpp index b8486fcf..0f25c20e 100644 --- a/src/Rtp/TSDecoder.cpp +++ b/src/Rtp/TSDecoder.cpp @@ -8,34 +8,38 @@ * may be found in the AUTHORS file in the root of the source tree. */ -#if defined(ENABLE_RTPPROXY) -#include "mpeg-ts.h" #include "TSDecoder.h" -#define TS_PACKET_SIZE 188 namespace mediakit { +bool TSSegment::isTSPacket(const char *data, int len){ + return len == TS_PACKET_SIZE && ((uint8_t*)data)[0] == TS_SYNC_BYTE; +} + void TSSegment::setOnSegment(const TSSegment::onSegment &cb) { _onSegment = cb; } int64_t TSSegment::onRecvHeader(const char *data, uint64_t len) { + if (!isTSPacket(data, len)) { + WarnL << "不是ts包:" << (int) (data[0]) << " " << len; + return 0; + } _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) { + if (len == _size && ((uint8_t *) data)[0] == TS_SYNC_BYTE) { return data + _size; } return nullptr; } //下一个包头 - if (((uint8_t *) data)[_size] == 0x47) { + if (((uint8_t *) data)[_size] == TS_SYNC_BYTE) { return data + _size; } - - auto pos = memchr(data + _size, 0x47, len - _size); + auto pos = memchr(data + _size, TS_SYNC_BYTE, len - _size); if (pos) { return (char *) pos; } @@ -44,12 +48,10 @@ const char *TSSegment::onSearchPacketTail(const char *data, int len) { //////////////////////////////////////////////////////////////// -TSDecoder::TSDecoder() : _ts_segment(TS_PACKET_SIZE) { +#if defined(ENABLE_HLS) +#include "mpeg-ts.h" +TSDecoder::TSDecoder() : _ts_segment() { _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){ @@ -66,8 +68,8 @@ TSDecoder::~TSDecoder() { } 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); + if (TSSegment::isTSPacket((char *)data, bytes)) { + return ts_demuxer_input(_demuxer_ctx, (uint8_t *) data, bytes); } _ts_segment.input((char*)data,bytes); return bytes; @@ -76,6 +78,6 @@ int TSDecoder::input(const uint8_t *data, int bytes) { void TSDecoder::setOnDecode(const Decoder::onDecode &decode) { _on_decode = decode; } +#endif//defined(ENABLE_HLS) }//namespace mediakit -#endif//defined(ENABLE_RTPPROXY) \ No newline at end of file diff --git a/src/Rtp/TSDecoder.h b/src/Rtp/TSDecoder.h index 6de2397c..42841404 100644 --- a/src/Rtp/TSDecoder.h +++ b/src/Rtp/TSDecoder.h @@ -11,7 +11,6 @@ #ifndef ZLMEDIAKIT_TSDECODER_H #define ZLMEDIAKIT_TSDECODER_H -#if defined(ENABLE_RTPPROXY) #include "Util/logger.h" #include "Http/HttpRequestSplitter.h" #include "Decoder.h" @@ -19,13 +18,17 @@ using namespace toolkit; namespace mediakit { -//ts包拆分器 +#define TS_PACKET_SIZE 188 +#define TS_SYNC_BYTE 0x47 + +//TS包分割器,用于split一个一个的ts包 class TSSegment : public HttpRequestSplitter { public: typedef std::function onSegment; - TSSegment(int size = 188) : _size(size){} + TSSegment(int size = TS_PACKET_SIZE) : _size(size){} ~TSSegment(){} void setOnSegment(const onSegment &cb); + static bool isTSPacket(const char *data, int len); protected: int64_t onRecvHeader(const char *data, uint64_t len) override ; const char *onSearchPacketTail(const char *data, int len) override ; @@ -34,6 +37,7 @@ private: onSegment _onSegment; }; +#if defined(ENABLE_HLS) //ts解析器 class TSDecoder : public Decoder { public: @@ -46,7 +50,7 @@ private: struct ts_demuxer_t* _demuxer_ctx = nullptr; onDecode _on_decode; }; +#endif//defined(ENABLE_HLS) }//namespace mediakit -#endif//defined(ENABLE_RTPPROXY) #endif //ZLMEDIAKIT_TSDECODER_H From a4aa34e4aea745ae8d12dd3e7c56da52cba8690c Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 17 May 2020 18:00:37 +0800 Subject: [PATCH 033/100] =?UTF-8?q?=E5=88=9D=E6=AD=A5=E6=B7=BB=E5=8A=A0hls?= =?UTF-8?q?=E6=92=AD=E6=94=BE=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/HlsParser.cpp | 139 ++++++++++++++++++++ src/Http/HlsParser.h | 95 ++++++++++++++ src/Http/HlsPlayer.cpp | 265 ++++++++++++++++++++++++++++++++++++++ src/Http/HlsPlayer.h | 144 +++++++++++++++++++++ src/Http/HttpClient.h | 3 +- src/Http/HttpClientImp.h | 3 - src/Http/HttpTSPlayer.cpp | 81 ++++++++++++ src/Http/HttpTSPlayer.h | 57 ++++++++ src/Player/PlayerBase.cpp | 11 ++ 9 files changed, 793 insertions(+), 5 deletions(-) create mode 100644 src/Http/HlsParser.cpp create mode 100644 src/Http/HlsParser.h create mode 100644 src/Http/HlsPlayer.cpp create mode 100644 src/Http/HlsPlayer.h create mode 100644 src/Http/HttpTSPlayer.cpp create mode 100644 src/Http/HttpTSPlayer.h diff --git a/src/Http/HlsParser.cpp b/src/Http/HlsParser.cpp new file mode 100644 index 00000000..613dcb59 --- /dev/null +++ b/src/Http/HlsParser.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include +#include "HlsParser.h" +#include "Util/util.h" +#include "Common/Parser.h" +using namespace toolkit; +namespace mediakit { + +bool HlsParser::parse(const string &http_url, const string &m3u8) { + float extinf_dur = 0; + ts_segment segment; + map ts_map; + _total_dur = 0; + _is_live = true; + _is_m3u8_inner = false; + int index = 0; + + auto lines = split(m3u8, "\n"); + for (auto &line : lines) { + trim(line); + if (line.size() < 2) { + continue; + } + + if ((_is_m3u8_inner || extinf_dur != 0) && line[0] != '#') { + segment.duration = extinf_dur; + if (line.find("http://") == 0 || line.find("https://") == 0) { + segment.url = line; + } else { + if (line.find("/") == 0) { + segment.url = http_url.substr(0, http_url.find("/", 8)) + line; + } else { + segment.url = http_url.substr(0, http_url.rfind("/") + 1) + line; + } + } + if (!_is_m3u8_inner) { + //ts按照先后顺序排序 + ts_map.emplace(index++, segment); + } else { + //子m3u8按照带宽排序 + ts_map.emplace(segment.bandwidth, segment); + } + extinf_dur = 0; + continue; + } + + _is_m3u8_inner = false; + if (line.find("#EXTINF:") == 0) { + sscanf(line.data(), "#EXTINF:%f,", &extinf_dur); + _total_dur += extinf_dur; + continue; + } + static const string s_stream_inf = "#EXT-X-STREAM-INF:"; + if (line.find(s_stream_inf) == 0) { + _is_m3u8_inner = true; + auto key_val = Parser::parseArgs(line.substr(s_stream_inf.size()), ",", "="); + segment.program_id = atoi(key_val["PROGRAM-ID"].data()); + segment.bandwidth = atoi(key_val["BANDWIDTH"].data()); + sscanf(key_val["RESOLUTION"].data(), "%dx%d", &segment.width, &segment.height); + continue; + } + + if (line == "#EXTM3U") { + _is_m3u8 = true; + continue; + } + + if (line.find("#EXT-X-ALLOW-CACHE:") == 0) { + _allow_cache = (line.find(":YES") != string::npos); + continue; + } + + if (line.find("#EXT-X-VERSION:") == 0) { + sscanf(line.data(), "#EXT-X-VERSION:%d", &_version); + continue; + } + + if (line.find("#EXT-X-TARGETDURATION:") == 0) { + sscanf(line.data(), "#EXT-X-TARGETDURATION:%d", &_target_dur); + continue; + } + + if (line.find("#EXT-X-MEDIA-SEQUENCE:") == 0) { + sscanf(line.data(), "#EXT-X-MEDIA-SEQUENCE:%lld", &_sequence); + continue; + } + + if (line.find("#EXT-X-ENDLIST") == 0) { + //点播 + _is_live = false; + continue; + } + continue; + } + + if (_is_m3u8) { + onParsed(_is_m3u8_inner, _sequence, ts_map); + } + return _is_m3u8; +} + +bool HlsParser::isM3u8() const { + return _is_m3u8; +} + +bool HlsParser::isLive() const{ + return _is_live; +} + +bool HlsParser::allowCache() const { + return _allow_cache; +} + +int HlsParser::getVersion() const { + return _version; +} + +int HlsParser::getTargetDur() const { + return _target_dur; +} + +int HlsParser::getSequence() const { + return _sequence; +} + +bool HlsParser::isM3u8Inner() const { + return _is_m3u8_inner; +} + +}//namespace mediakit \ No newline at end of file diff --git a/src/Http/HlsParser.h b/src/Http/HlsParser.h new file mode 100644 index 00000000..f33b46d0 --- /dev/null +++ b/src/Http/HlsParser.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef HTTP_HLSPARSER_H +#define HTTP_HLSPARSER_H + +#include +#include +#include +using namespace std; +namespace mediakit { + +typedef struct{ + //url地址 + string url; + //ts切片长度 + float duration; + + //////内嵌m3u8////// + //节目id + int program_id; + //带宽 + int bandwidth; + //宽度 + int width; + //高度 + int height; +} ts_segment; + +class HlsParser { +public: + HlsParser(){} + ~HlsParser(){} + bool parse(const string &http_url,const string &m3u8); + + /** + * 是否存在#EXTM3U字段,是否为m3u8文件 + */ + bool isM3u8() const; + + /** + * #EXT-X-ALLOW-CACHE值,是否允许cache + */ + bool allowCache() const; + + /** + * 是否存在#EXT-X-ENDLIST字段,是否为直播 + */ + bool isLive() const ; + + /** + * #EXT-X-VERSION值,版本号 + */ + int getVersion() const; + + /** + * #EXT-X-TARGETDURATION字段值 + */ + int getTargetDur() const; + + /** + * #EXT-X-MEDIA-SEQUENCE字段值,该m3u8序号 + */ + int getSequence() const; + + /** + * 内部是否含有子m3u8 + */ + bool isM3u8Inner() const; + +protected: + //解析出ts文件地址回调 + virtual void onParsed(bool is_m3u8_inner,int64_t sequence,const map &ts_list) {}; + +private: + bool _is_m3u8 = false; + bool _allow_cache = false; + bool _is_live = true; + int _version = 0; + int _target_dur = 0; + float _total_dur = 0; + int64_t _sequence = 0; + //每部是否有m3u8 + bool _is_m3u8_inner = false; +}; + +}//namespace mediakit +#endif //HTTP_HLSPARSER_H diff --git a/src/Http/HlsPlayer.cpp b/src/Http/HlsPlayer.cpp new file mode 100644 index 00000000..16499084 --- /dev/null +++ b/src/Http/HlsPlayer.cpp @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "HlsPlayer.h" +namespace mediakit { + +HlsPlayer::HlsPlayer(const EventPoller::Ptr &poller){ + _segment.setOnSegment([this](const char *data, uint64_t len) { onPacket(data, len); }); + _poller = poller ? poller : EventPollerPool::Instance().getPoller(); +} + +HlsPlayer::~HlsPlayer() {} + +void HlsPlayer::play(const string &strUrl) { + _m3u8_list.emplace_back(strUrl); + play_l(); +} + +void HlsPlayer::play_l(){ + if (_m3u8_list.empty()) { + teardown_l(SockException(Err_shutdown, "所有hls url都尝试播放失败!")); + return; + } + float playTimeOutSec = (*this)[Client::kTimeoutMS].as() / 1000.0; + setMethod("GET"); + if(!(*this)[kNetAdapter].empty()) { + setNetAdapter((*this)[kNetAdapter]); + } + sendRequest(_m3u8_list.back(), playTimeOutSec); +} + +void HlsPlayer::teardown_l(const SockException &ex){ + _timer.reset(); + _timer_ts.reset(); + _http_ts_player.reset(); + shutdown(ex); +} + +void HlsPlayer::teardown() { + teardown_l(SockException(Err_shutdown,"teardown")); +} + +void HlsPlayer::playNextTs(bool force){ + if (_ts_list.empty()) { + //播放列表为空,那么立即重新下载m3u8文件 + _timer.reset(); + play_l(); + return; + } + if (!force && _http_ts_player && _http_ts_player->alive()) { + //播放器目前还存活,正在下载中 + return; + } + auto ts_duration = _ts_list.front().duration * 1000; + weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); + std::shared_ptr ticker(new Ticker); + + _http_ts_player = std::make_shared(getPoller(), false); + _http_ts_player->setOnDisconnect([weakSelf, ticker, ts_duration](const SockException &err) { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { + return; + } + auto delay = ts_duration - 500 - ticker->elapsedTime(); + if (delay <= 0) { + //播放这个ts切片花费了太长时间,我们立即下一个切片的播放 + strongSelf->playNextTs(true); + } else { + //下一个切片慢点播放 + strongSelf->_timer_ts.reset(new Timer(delay / 1000.0, [weakSelf, delay]() { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { + return false; + } + strongSelf->playNextTs(true); + return false; + }, strongSelf->getPoller())); + } + }); + _http_ts_player->setOnPacket([weakSelf](const char *data, uint64_t len) { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { + return; + } + //收到ts包 + strongSelf->onPacket_l(data, len); + }); + + _http_ts_player->setMethod("GET"); + if(!(*this)[kNetAdapter].empty()) { + _http_ts_player->setNetAdapter((*this)[Client::kNetAdapter]); + } + _http_ts_player->sendRequest(_ts_list.front().url, 2 * _ts_list.front().duration); + _ts_list.pop_front(); +} + +void HlsPlayer::onParsed(bool is_m3u8_inner,int64_t sequence,const map &ts_map){ + if (!is_m3u8_inner) { + //这是ts播放列表 + if (_last_sequence == sequence) { + return; + } + _last_sequence = sequence; + for (auto &pr : ts_map) { + auto &ts = pr.second; + if (_ts_url_cache.emplace(ts.url).second) { + //该ts未重复 + _ts_list.emplace_back(ts); + //按时间排序 + _ts_url_sort.emplace_back(ts.url); + } + } + if (_ts_url_sort.size() > 2 * ts_map.size()) { + //去除防重列表中过多的数据 + _ts_url_cache.erase(_ts_url_sort.front()); + _ts_url_sort.pop_front(); + } + playNextTs(); + } else { + //这是m3u8列表,我们播放最高清的子hls + if (ts_map.empty()) { + teardown_l(SockException(Err_shutdown, StrPrinter << "empty sub hls list:" + getUrl())); + return; + } + _timer.reset(); + + weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); + auto url = ts_map.rbegin()->second.url; + getPoller()->async([weakSelf, url]() { + auto strongSelf = weakSelf.lock(); + if (strongSelf) { + strongSelf->play(url); + } + }, false); + } +} + +int64_t HlsPlayer::onResponseHeader(const string &status, const HttpClient::HttpHeader &headers) { + if (status != "200" && status != "206") { + //失败 + teardown_l(SockException(Err_shutdown, StrPrinter << "bad http status code:" + status)); + return 0; + } + auto contet_type = const_cast< HttpClient::HttpHeader &>(headers)["Content-Type"]; + _is_m3u8 = (contet_type.find("application/vnd.apple.mpegurl") == 0); + return -1; +} + +void HlsPlayer::onResponseBody(const char *buf, int64_t size, int64_t recvedSize, int64_t totalSize) { + if (recvedSize == size) { + //刚开始 + _m3u8.clear(); + } + _m3u8.append(buf, size); +} + +void HlsPlayer::onResponseCompleted() { + if (HlsParser::parse(getUrl(), _m3u8)) { + playDelay(); + if (_first) { + _first = false; + onPlayResult(SockException(Err_success, "play success")); + } + } else { + teardown_l(SockException(Err_shutdown, "解析m3u8文件失败")); + } +} + +float HlsPlayer::delaySecond(){ + if (HlsParser::isM3u8() && HlsParser::getTargetDur() > 0) { + return HlsParser::getTargetDur(); + } + return 1; +} + +void HlsPlayer::onDisconnect(const SockException &ex) { + if (_first) { + //第一次失败,则播放失败 + _first = false; + onPlayResult(ex); + return; + } + + //主动shutdown + if (ex.getErrCode() == Err_shutdown) { + if (_m3u8_list.size() <= 1) { + //全部url都播放失败 + onShutdown(ex); + } else { + _m3u8_list.pop_back(); + //还有上一级url可以重试播放 + play_l(); + } + return; + } + + //eof等,之后播放失败,那么重试播放m3u8 + playDelay(); +} + +bool HlsPlayer::onRedirectUrl(const string &url,bool temporary) { + _m3u8_list.emplace_back(url); + return true; +} + +void HlsPlayer::playDelay(){ + weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); + _timer.reset(new Timer(delaySecond(), [weakSelf]() { + auto strongSelf = weakSelf.lock(); + if (strongSelf) { + strongSelf->play_l(); + } + return false; + }, getPoller())); +} + +void HlsPlayer::onPacket_l(const char *data, uint64_t len){ + _segment.input(data,len); +} + +////////////////////////////////////////////////////////////////////////// + +HlsPlayerImp::HlsPlayerImp(const EventPoller::Ptr &poller) : PlayerImp(poller) { + +} + +void HlsPlayerImp::setOnPacket(const TSSegment::onSegment &cb){ + _on_ts = cb; +} + +void HlsPlayerImp::onPacket(const char *data,uint64_t len) { + if (_on_ts) { + _on_ts(data, len); + } + + if (!_decoder) { + _decoder = DecoderImp::createDecoder(DecoderImp::decoder_ts, dynamic_pointer_cast(shared_from_this())); + } + + if (_decoder) { + _decoder->input((uint8_t *) data, len); + } +} + +void HlsPlayerImp::onAllTrackReady() { + PlayerImp::onPlayResult(SockException(Err_success,"play hls success")); +} + +void HlsPlayerImp::onPlayResult(const SockException &ex) { + +} + +vector HlsPlayerImp::getTracks(bool trackReady) const { + return MediaSink::getTracks(trackReady); +} + + +}//namespace mediakit \ No newline at end of file diff --git a/src/Http/HlsPlayer.h b/src/Http/HlsPlayer.h new file mode 100644 index 00000000..d9cf9766 --- /dev/null +++ b/src/Http/HlsPlayer.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef HTTP_HLSPLAYER_H +#define HTTP_HLSPLAYER_H + +#include +#include "Util/util.h" +#include "Poller/Timer.h" +#include "Http/HttpDownloader.h" +#include "Player/MediaPlayer.h" +#include "HlsParser.h" +#include "HttpTSPlayer.h" +#include "Rtp/Decoder.h" +#include "Rtp/TSDecoder.h" + +using namespace toolkit; +namespace mediakit { + +class HlsPlayer : public HttpClientImp , public PlayerBase , public HlsParser{ +public: + HlsPlayer(const EventPoller::Ptr &poller); + ~HlsPlayer() override; + + /** + * 开始播放 + * @param strUrl + */ + void play(const string &strUrl) override; + + /** + * 停止播放 + */ + void teardown() override; + +protected: + /** + * 收到ts包 + * @param data ts数据负载 + * @param len ts包长度 + */ + virtual void onPacket(const char *data, uint64_t len) = 0; + +private: + /** + * 解析m3u8成功 + * @param is_m3u8_inner 是否为m3u8列表 + * @param sequence ts列表seq + * @param ts_map ts列表或m3u8列表 + */ + void onParsed(bool is_m3u8_inner,int64_t sequence,const map &ts_map) override; + /** + * 收到http回复头 + * @param status 状态码,譬如:200 OK + * @param headers http头 + * @return 返回后续content的长度;-1:后续数据全是content;>=0:固定长度content + * 需要指出的是,在http头中带有Content-Length字段时,该返回值无效 + */ + int64_t onResponseHeader(const string &status,const HttpHeader &headers) override; + /** + * 收到http conten数据 + * @param buf 数据指针 + * @param size 数据大小 + * @param recvedSize 已收数据大小(包含本次数据大小),当其等于totalSize时将触发onResponseCompleted回调 + * @param totalSize 总数据大小 + */ + void onResponseBody(const char *buf,int64_t size,int64_t recvedSize,int64_t totalSize) override; + + /** + * 接收http回复完毕, + */ + void onResponseCompleted() override; + + /** + * http链接断开回调 + * @param ex 断开原因 + */ + void onDisconnect(const SockException &ex) override; + + /** + * 重定向事件 + * @param url 重定向url + * @param temporary 是否为临时重定向 + * @return 是否继续 + */ + bool onRedirectUrl(const string &url,bool temporary) override; + +private: + void playDelay(); + float delaySecond(); + void playNextTs(bool force = false); + void teardown_l(const SockException &ex); + void play_l(); + void onPacket_l(const char *data, uint64_t len); + +private: + struct UrlComp { + //url忽略?后面的参数 + bool operator()(const string& __x, const string& __y) const { + return split(__x,"?")[0] < split(__y,"?")[0]; + } + }; + +private: + bool _is_m3u8 = false; + bool _first = true; + int64_t _last_sequence = -1; + string _m3u8; + Timer::Ptr _timer; + Timer::Ptr _timer_ts; + list _ts_list; + list _ts_url_sort; + list _m3u8_list; + set _ts_url_cache; + HttpTSPlayer::Ptr _http_ts_player; + TSSegment _segment; +}; + +class HlsPlayerImp : public PlayerImp , public MediaSink{ +public: + typedef std::shared_ptr Ptr; + HlsPlayerImp(const EventPoller::Ptr &poller = nullptr); + ~HlsPlayerImp() override {}; + void setOnPacket(const TSSegment::onSegment &cb); + +private: + void onPacket(const char *data, uint64_t len) override; + void onAllTrackReady() override; + void onPlayResult(const SockException &ex) override; + vector getTracks(bool trackReady = true) const override; +private: + TSSegment::onSegment _on_ts; + DecoderImp::Ptr _decoder; +}; + +}//namespace mediakit +#endif //HTTP_HLSPLAYER_H diff --git a/src/Http/HttpClient.h b/src/Http/HttpClient.h index f78934d2..e69e9694 100644 --- a/src/Http/HttpClient.h +++ b/src/Http/HttpClient.h @@ -48,8 +48,7 @@ public: } }; -class HttpClient : public TcpClient , public HttpRequestSplitter -{ +class HttpClient : public TcpClient , public HttpRequestSplitter{ public: typedef StrCaseMap HttpHeader; typedef std::shared_ptr Ptr; diff --git a/src/Http/HttpClientImp.h b/src/Http/HttpClientImp.h index 392770c3..25093f1b 100644 --- a/src/Http/HttpClientImp.h +++ b/src/Http/HttpClientImp.h @@ -13,9 +13,7 @@ #include "HttpClient.h" #include "Util/SSLBox.h" - using namespace toolkit; - namespace mediakit { class HttpClientImp: public TcpClientWithSSL { @@ -28,5 +26,4 @@ protected: }; } /* namespace mediakit */ - #endif /* SRC_HTTP_HTTPCLIENTIMP_H_ */ diff --git a/src/Http/HttpTSPlayer.cpp b/src/Http/HttpTSPlayer.cpp new file mode 100644 index 00000000..5c02c174 --- /dev/null +++ b/src/Http/HttpTSPlayer.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#include "HttpTSPlayer.h" +namespace mediakit { + +HttpTSPlayer::HttpTSPlayer(const EventPoller::Ptr &poller, bool split_ts){ + _segment.setOnSegment([this](const char *data, uint64_t len) { onPacket(data, len); }); + _poller = poller ? poller : EventPollerPool::Instance().getPoller(); + _split_ts = split_ts; +} + +HttpTSPlayer::~HttpTSPlayer() {} + +int64_t HttpTSPlayer::onResponseHeader(const string &status, const HttpClient::HttpHeader &headers) { + if (status != "200" && status != "206") { + //http状态码不符合预期 + shutdown(SockException(Err_other, StrPrinter << "bad http status code:" + status)); + return 0; + } + auto contet_type = const_cast< HttpClient::HttpHeader &>(headers)["Content-Type"]; + if (contet_type.find("video/mp2t") == 0 || contet_type.find("video/mpeg") == 0) { + _is_ts_content = true; + } + + //后续是不定长content + return -1; +} + +void HttpTSPlayer::onResponseBody(const char *buf, int64_t size, int64_t recvedSize, int64_t totalSize) { + if (recvedSize == size) { + //开始接收数据 + if (buf[0] == TS_SYNC_BYTE) { + //这是ts头 + _is_first_packet_ts = true; + } else { + WarnL << "可能不是http-ts流"; + } + } + + if (_split_ts) { + _segment.input(buf, size); + } else { + onPacket(buf, size); + } +} + +void HttpTSPlayer::onResponseCompleted() { + //接收完毕 + shutdown(SockException(Err_success, "play completed")); +} + +void HttpTSPlayer::onDisconnect(const SockException &ex) { + if (_on_disconnect) { + _on_disconnect(ex); + _on_disconnect = nullptr; + } +} + +void HttpTSPlayer::onPacket(const char *data, uint64_t len) { + if (_on_segment) { + _on_segment(data, len); + } +} + +void HttpTSPlayer::setOnDisconnect(const HttpTSPlayer::onShutdown &cb) { + _on_disconnect = cb; +} + +void HttpTSPlayer::setOnPacket(const TSSegment::onSegment &cb) { + _on_segment = cb; +} + +}//namespace mediakit \ No newline at end of file diff --git a/src/Http/HttpTSPlayer.h b/src/Http/HttpTSPlayer.h new file mode 100644 index 00000000..ec1aa129 --- /dev/null +++ b/src/Http/HttpTSPlayer.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef HTTP_HTTPTSPLAYER_H +#define HTTP_HTTPTSPLAYER_H + +#include "Http/HttpDownloader.h" +#include "Player/MediaPlayer.h" +#include "Rtp/TSDecoder.h" +using namespace toolkit; +namespace mediakit { + +//http-ts播发器,未实现ts解复用 +class HttpTSPlayer : public HttpClientImp{ +public: + typedef function onShutdown; + typedef std::shared_ptr Ptr; + + HttpTSPlayer(const EventPoller::Ptr &poller = nullptr, bool split_ts = true); + ~HttpTSPlayer() override ; + + //设置异常断开回调 + void setOnDisconnect(const onShutdown &cb); + //设置接收ts包回调 + void setOnPacket(const TSSegment::onSegment &cb); + +protected: + ///HttpClient override/// + int64_t onResponseHeader(const string &status,const HttpHeader &headers) override; + void onResponseBody(const char *buf,int64_t size,int64_t recvedSize,int64_t totalSize) override; + void onResponseCompleted() override; + void onDisconnect(const SockException &ex) override ; + + //收到ts包 + virtual void onPacket(const char *data, uint64_t len); + +private: + //是否为mpegts负载 + bool _is_ts_content = false; + //第一个包是否为ts包 + bool _is_first_packet_ts = false; + //是否判断是否是ts并split + bool _split_ts; + TSSegment _segment; + onShutdown _on_disconnect; + TSSegment::onSegment _on_segment; +}; + +}//namespace mediakit +#endif //HTTP_HTTPTSPLAYER_H diff --git a/src/Player/PlayerBase.cpp b/src/Player/PlayerBase.cpp index 26c9f788..6e3f1c53 100644 --- a/src/Player/PlayerBase.cpp +++ b/src/Player/PlayerBase.cpp @@ -12,10 +12,17 @@ #include "PlayerBase.h" #include "Rtsp/RtspPlayerImp.h" #include "Rtmp/RtmpPlayerImp.h" +#include "Http/HlsPlayer.h" using namespace toolkit; namespace mediakit { +//字符串是否以xx结尾 +static bool end_of(const string &str, const string &substr){ + auto pos = str.rfind(substr); + return pos != string::npos && pos == str.size() - substr.size(); +} + PlayerBase::Ptr PlayerBase::createPlayer(const EventPoller::Ptr &poller,const string &strUrl) { static auto releasePlayer = [](PlayerBase *ptr){ onceToken token(nullptr,[&](){ @@ -41,6 +48,10 @@ PlayerBase::Ptr PlayerBase::createPlayer(const EventPoller::Ptr &poller,const st return PlayerBase::Ptr(new RtmpPlayerImp(poller),releasePlayer); } + if (strcasecmp("http",prefix.data()) == 0 && end_of(strUrl, ".m3u8")) { + return PlayerBase::Ptr(new HlsPlayerImp(poller),releasePlayer); + } + return PlayerBase::Ptr(new RtspPlayerImp(poller),releasePlayer); } From bab11b426e66ab2eb100caf55428e0d8ee1feca0 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 17 May 2020 19:06:14 +0800 Subject: [PATCH 034/100] =?UTF-8?q?hls=E6=92=AD=E6=94=BE=E5=99=A8=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Extension/H264.h | 5 +++++ tests/test_player.cpp | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Extension/H264.h b/src/Extension/H264.h index dbc5cfb1..437edcca 100644 --- a/src/Extension/H264.h +++ b/src/Extension/H264.h @@ -228,6 +228,11 @@ private: } break; + case H264Frame::NAL_SEI:{ + //忽略SEI + break; + } + default: VideoTrack::inputFrame(frame); break; diff --git a/tests/test_player.cpp b/tests/test_player.cpp index 2954a43c..8ca20d82 100644 --- a/tests/test_player.cpp +++ b/tests/test_player.cpp @@ -116,7 +116,8 @@ int main(int argc, char *argv[]) { } AnyStorage::Ptr storage(new AnyStorage); - viedoTrack->addDelegate(std::make_shared([storage](const Frame::Ptr &frame) { + viedoTrack->addDelegate(std::make_shared([storage](const Frame::Ptr &frame_in) { + auto frame = Frame::getCacheAbleFrame(frame_in); SDLDisplayerHelper::Instance().doTask([frame,storage]() { auto &decoder = (*storage)["decoder"]; auto &displayer = (*storage)["displayer"]; From 0e2616157438a89568c7affa6b5602a32c96b73b Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 17 May 2020 19:10:08 +0800 Subject: [PATCH 035/100] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dhls=E6=92=AD=E6=94=BE?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E4=B8=8D=E5=9B=9E=E8=B0=83=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/HlsPlayer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Http/HlsPlayer.cpp b/src/Http/HlsPlayer.cpp index 16499084..16111752 100644 --- a/src/Http/HlsPlayer.cpp +++ b/src/Http/HlsPlayer.cpp @@ -254,7 +254,9 @@ void HlsPlayerImp::onAllTrackReady() { } void HlsPlayerImp::onPlayResult(const SockException &ex) { - + if(ex){ + PlayerImp::onPlayResult(ex); + } } vector HlsPlayerImp::getTracks(bool trackReady) const { From 4315a7e0371d33e3ed7603b0dbd5a56a4fd25978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Mon, 18 May 2020 09:10:35 +0800 Subject: [PATCH 036/100] Update README.md --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 0eabd5e8..d3d67ab5 100644 --- a/README.md +++ b/README.md @@ -30,22 +30,21 @@ ## 功能清单 -- RTSP - - RTSP 服务器,支持RTMP/MP4转RTSP - - RTSPS 服务器,支持亚马逊echo show这样的设备 - - RTSP 播放器,支持RTSP代理,支持生成静音音频 - - RTSP 推流客户端与服务器 +- RTSP[S] + - RTSP[S] 服务器,支持RTMP/MP4/HLS转RTSP[S],支持亚马逊echo show这样的设备 + - RTSP[S] 播放器,支持RTSP代理,支持生成静音音频 + - RTSP[S] 推流客户端与服务器 - 支持 `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/G711外无法转协议 -- RTMP - - RTMP 播放服务器,支持RTSP/MP4转RTMP - - RTMP 发布服务器,支持录制发布流 - - RTMP 播放器,支持RTMP代理,支持生成静音音频 - - RTMP 推流客户端 +- RTMP[S] + - RTMP[S] 播放服务器,支持RTSP/MP4/HLS转RTMP + - RTMP[S] 发布服务器,支持录制发布流 + - RTMP[S] 播放器,支持RTMP代理,支持生成静音音频 + - RTMP[S] 推流客户端 - 支持http[s]-flv直播 - 支持websocket-flv直播 - 支持任意编码格式的rtmp推流,只是除H264/H265/AAC/G711外无法转协议 @@ -55,6 +54,7 @@ - 支持HLS文件生成,自带HTTP文件服务器 - 通过cookie追踪技术,可以模拟HLS播放为长连接,实现丰富的业务逻辑 - 支持完备的HLS用户追踪、播放统计等业务功能,可以实现HLS按需拉流等业务 + - 支持HLS播发器,支持拉流HLS转rtsp/rtmp/mp4 - HTTP[S] - 服务器支持`目录索引生成`,`文件下载`,`表单提交请求` From 0c1bb482dde495db7d3e03e3b31b8a9d1d0dfb42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Mon, 18 May 2020 09:17:15 +0800 Subject: [PATCH 037/100] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index d3d67ab5..977dd029 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,9 @@ - 提供c api sdk - 支持FFmpeg拉流代理任意格式的流 - 支持http api生成并返回实时截图 + +## 更新日志 + - 2020/5/17 新增支持hls播发器,支持hls拉流代理 ## 编译以及测试 From 7bc7d95249ac3afbfaec6b1a8c39c82b841aecb7 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 18 May 2020 09:24:51 +0800 Subject: [PATCH 038/100] =?UTF-8?q?hls=E6=94=AF=E6=8C=81https?= 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 6e3f1c53..526c8ffc 100644 --- a/src/Player/PlayerBase.cpp +++ b/src/Player/PlayerBase.cpp @@ -48,7 +48,7 @@ PlayerBase::Ptr PlayerBase::createPlayer(const EventPoller::Ptr &poller,const st return PlayerBase::Ptr(new RtmpPlayerImp(poller),releasePlayer); } - if (strcasecmp("http",prefix.data()) == 0 && end_of(strUrl, ".m3u8")) { + if ((strcasecmp("http",prefix.data()) == 0 || strcasecmp("https",prefix.data()) == 0) && end_of(strUrl, ".m3u8")) { return PlayerBase::Ptr(new HlsPlayerImp(poller),releasePlayer); } From 477475a60f70ed2e93ff3652102f7ebaece6ff4b Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 18 May 2020 15:31:49 +0800 Subject: [PATCH 039/100] =?UTF-8?q?=E6=89=B9=E9=87=8F=E6=B7=BB=E5=8A=A0bom?= =?UTF-8?q?=E5=A4=B4=E3=80=81=E6=89=B9=E9=87=8F=E6=9B=BF=E6=8D=A2tab?= =?UTF-8?q?=E5=AD=97=E7=AC=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/include/mk_common.h | 10 +++---- src/Common/Device.h | 6 ++--- src/Extension/Factory.cpp | 2 +- src/Extension/Opus.cpp | 2 +- src/Extension/Opus.h | 2 +- src/Http/HlsParser.cpp | 2 +- src/Http/HlsParser.h | 2 +- src/Http/HlsPlayer.cpp | 2 +- src/Http/HlsPlayer.h | 2 +- src/Http/HttpTSPlayer.cpp | 2 +- src/Http/HttpTSPlayer.h | 2 +- src/Record/MP4.cpp | 2 +- src/Record/MP4.h | 2 +- src/Record/MP4Demuxer.cpp | 2 +- src/Record/MP4Demuxer.h | 2 +- src/Rtsp/RtspSession.cpp | 36 ++++++++++++------------- tests/bom.cpp | 57 +++++++++------------------------------ tests/tab.cpp | 2 -- tests/test_player.cpp | 2 +- tests/test_rtp.cpp | 2 +- 20 files changed, 54 insertions(+), 87 deletions(-) diff --git a/api/include/mk_common.h b/api/include/mk_common.h index c9657a9a..6363d22e 100755 --- a/api/include/mk_common.h +++ b/api/include/mk_common.h @@ -17,12 +17,12 @@ #ifndef MediaKitApi_STATIC #if defined(MediaKitApi_EXPORTS) - #define API_EXPORT __declspec(dllexport) - #else - #define API_EXPORT __declspec(dllimport) - #endif + #define API_EXPORT __declspec(dllexport) + #else + #define API_EXPORT __declspec(dllimport) + #endif - #define API_CALL __cdecl + #define API_CALL __cdecl #else #define API_EXPORT #define API_CALL diff --git a/src/Common/Device.h b/src/Common/Device.h index 2e2d61f5..a2129d98 100644 --- a/src/Common/Device.h +++ b/src/Common/Device.h @@ -43,9 +43,9 @@ public: class AudioInfo { public: CodecId codecId = CodecAAC; - int iChannel; - int iSampleBit; - int iSampleRate; + int iChannel; + int iSampleBit; + int iSampleRate; }; /** diff --git a/src/Extension/Factory.cpp b/src/Extension/Factory.cpp index 037c5f98..4bdc7c43 100644 --- a/src/Extension/Factory.cpp +++ b/src/Extension/Factory.cpp @@ -226,7 +226,7 @@ RtmpCodec::Ptr Factory::getRtmpCodecByTrack(const Track::Ptr &track) { case CodecH264 : return std::make_shared(track); case CodecAAC : return std::make_shared(track); case CodecH265 : return std::make_shared(track); - case CodecG711A : + case CodecG711A : case CodecG711U : return std::make_shared(track); default : WarnL << "暂不支持该CodecId:" << track->getCodecName(); return nullptr; } diff --git a/src/Extension/Opus.cpp b/src/Extension/Opus.cpp index e4e15a57..e9dea37d 100644 --- a/src/Extension/Opus.cpp +++ b/src/Extension/Opus.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. * * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). diff --git a/src/Extension/Opus.h b/src/Extension/Opus.h index b763a2da..17e28c06 100644 --- a/src/Extension/Opus.h +++ b/src/Extension/Opus.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. * * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). diff --git a/src/Http/HlsParser.cpp b/src/Http/HlsParser.cpp index 613dcb59..6dce3837 100644 --- a/src/Http/HlsParser.cpp +++ b/src/Http/HlsParser.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved. * * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). diff --git a/src/Http/HlsParser.h b/src/Http/HlsParser.h index f33b46d0..7a0ed7fe 100644 --- a/src/Http/HlsParser.h +++ b/src/Http/HlsParser.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved. * * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). diff --git a/src/Http/HlsPlayer.cpp b/src/Http/HlsPlayer.cpp index 16111752..d1b78186 100644 --- a/src/Http/HlsPlayer.cpp +++ b/src/Http/HlsPlayer.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved. * * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). diff --git a/src/Http/HlsPlayer.h b/src/Http/HlsPlayer.h index d9cf9766..bfeed863 100644 --- a/src/Http/HlsPlayer.h +++ b/src/Http/HlsPlayer.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved. * * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). diff --git a/src/Http/HttpTSPlayer.cpp b/src/Http/HttpTSPlayer.cpp index 5c02c174..fb525f11 100644 --- a/src/Http/HttpTSPlayer.cpp +++ b/src/Http/HttpTSPlayer.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved. * * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). diff --git a/src/Http/HttpTSPlayer.h b/src/Http/HttpTSPlayer.h index ec1aa129..6b12a453 100644 --- a/src/Http/HttpTSPlayer.h +++ b/src/Http/HttpTSPlayer.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2020 The ZLMediaKit project authors. All Rights Reserved. * * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). diff --git a/src/Record/MP4.cpp b/src/Record/MP4.cpp index 571efa16..a0152ef1 100644 --- a/src/Record/MP4.cpp +++ b/src/Record/MP4.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. * * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). diff --git a/src/Record/MP4.h b/src/Record/MP4.h index 15e201f9..b76362a9 100644 --- a/src/Record/MP4.h +++ b/src/Record/MP4.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. * * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). diff --git a/src/Record/MP4Demuxer.cpp b/src/Record/MP4Demuxer.cpp index f157f9a5..7b0fadd4 100644 --- a/src/Record/MP4Demuxer.cpp +++ b/src/Record/MP4Demuxer.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. * * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). diff --git a/src/Record/MP4Demuxer.h b/src/Record/MP4Demuxer.h index a626d05c..5565cc22 100644 --- a/src/Record/MP4Demuxer.h +++ b/src/Record/MP4Demuxer.h @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. * * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index c1baafa2..b2e8e088 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -737,22 +737,22 @@ void RtspSession::handleReq_Play(const Parser &parser) { } bool useBuf = true; - _enableSendRtp = false; + _enableSendRtp = false; float iStartTime = 0; - if (strRange.size() && !_bFirstPlay) { + if (strRange.size() && !_bFirstPlay) { //这个是seek操作 - auto strStart = FindField(strRange.data(), "npt=", "-"); - if (strStart == "now") { - strStart = "0"; - } - iStartTime = 1000 * atof(strStart.data()); - InfoP(this) << "rtsp seekTo(ms):" << iStartTime; - useBuf = !pMediaSrc->seekTo(iStartTime); - }else if(pMediaSrc->totalReaderCount() == 0){ - //第一个消费者 - pMediaSrc->seekTo(0); - } - _bFirstPlay = false; + auto strStart = FindField(strRange.data(), "npt=", "-"); + if (strStart == "now") { + strStart = "0"; + } + iStartTime = 1000 * atof(strStart.data()); + InfoP(this) << "rtsp seekTo(ms):" << iStartTime; + useBuf = !pMediaSrc->seekTo(iStartTime); + }else if(pMediaSrc->totalReaderCount() == 0){ + //第一个消费者 + pMediaSrc->seekTo(0); + } + _bFirstPlay = false; _StrPrinter rtp_info; for(auto &track : _aTrackInfo){ @@ -772,10 +772,10 @@ void RtspSession::handleReq_Play(const Parser &parser) { rtp_info.pop_back(); - sendRtspResponse("200 OK", - {"Range", StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << (useBuf? pMediaSrc->getTimeStamp(TrackInvalid) / 1000.0 : iStartTime / 1000), - "RTP-Info",rtp_info - }); + sendRtspResponse("200 OK", + {"Range", StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << (useBuf? pMediaSrc->getTimeStamp(TrackInvalid) / 1000.0 : iStartTime / 1000), + "RTP-Info",rtp_info + }); _enableSendRtp = true; setSocketFlags(); diff --git a/tests/bom.cpp b/tests/bom.cpp index 46f1e1a8..d541858b 100644 --- a/tests/bom.cpp +++ b/tests/bom.cpp @@ -61,40 +61,6 @@ public: } }; -void get_file_path(const char *path, const char *file_name, char *file_path) { - strcpy(file_path, path); - if (file_path[strlen(file_path) - 1] != '/') { - strcat(file_path, "/"); - } - strcat(file_path, file_name); -} - -template -void for_each_file(const char *path, FUNC &&func){ - DIR *dir; - dirent *dir_info; - char file_path[PATH_MAX]; - if (File::is_file(path)) { - func(path); - return; - } - if (File::is_dir(path)) { - if ((dir = opendir(path)) == NULL) { - closedir(dir); - return; - } - while ((dir_info = readdir(dir)) != NULL) { - if (File::is_special_dir(dir_info->d_name)) { - continue; - } - get_file_path(path, dir_info->d_name, file_path); - for_each_file(file_path,std::forward(func)); - } - closedir(dir); - return; - } -} - static const char s_bom[] = "\xEF\xBB\xBF"; void add_or_rm_bom(const char *file,bool rm_bom){ @@ -159,23 +125,26 @@ int main(int argc, char *argv[]) { bool no_filter = filter_set.find("*") != filter_set.end(); //设置日志 Logger::Instance().add(std::make_shared()); - path = File::absolutePath(path, ""); - for_each_file(path.data(),[&](const char *path){ - if(!no_filter){ + File::scanDir(path, [&](const string &path, bool isDir) { + if (isDir) { + return true; + } + if (!no_filter) { //开启了过滤器 - auto pos = strstr(path,"."); - if(pos == nullptr){ + auto pos = strstr(path.data(), "."); + if (pos == nullptr) { //没有后缀 - return; + return true; } auto ext = pos + 1; - if(filter_set.find(ext) == filter_set.end()){ + if (filter_set.find(ext) == filter_set.end()) { //后缀不匹配 - return; + return true; } } //该文件匹配 - process_file(path,rm_bom); - }); + process_file(path.data(), rm_bom); + return true; + }, true); return 0; } diff --git a/tests/tab.cpp b/tests/tab.cpp index 7382f75d..98acd45e 100644 --- a/tests/tab.cpp +++ b/tests/tab.cpp @@ -128,8 +128,6 @@ int main(int argc, char *argv[]) { bool no_filter = filter_set.find("*") != filter_set.end(); //设置日志 Logger::Instance().add(std::make_shared()); - path = File::absolutePath(path, ""); - DebugL << path; File::scanDir(path, [&](const string &path, bool isDir) { if (isDir) { return true; diff --git a/tests/test_player.cpp b/tests/test_player.cpp index 8ca20d82..5417030d 100644 --- a/tests/test_player.cpp +++ b/tests/test_player.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. * * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). diff --git a/tests/test_rtp.cpp b/tests/test_rtp.cpp index 58686f2c..a6b25b58 100644 --- a/tests/test_rtp.cpp +++ b/tests/test_rtp.cpp @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. * * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). From cca9b1758a417f6de3c087306a87b600685d3c9a Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 18 May 2020 15:34:44 +0800 Subject: [PATCH 040/100] =?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 4ede70fc..bbb77017 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit 4ede70fc435eb0a4d3a752b521170d86440b3935 +Subproject commit bbb77017bfa48d3abaaa0bf7ab890333141c2dbc From 5cc4258ba23e9d5aa96209eac98a1b815cd34252 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 19 May 2020 10:47:46 +0800 Subject: [PATCH 041/100] =?UTF-8?q?=E5=85=BC=E5=AE=B9OPTIONS=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=E8=BF=94=E5=9B=9E401=E3=80=81302=E7=AD=89=E5=91=BD?= =?UTF-8?q?=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtsp/RtspPlayer.cpp | 21 +++++++++++++-------- src/Rtsp/RtspPlayer.h | 1 + 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/Rtsp/RtspPlayer.cpp b/src/Rtsp/RtspPlayer.cpp index 6e1aed29..297266f7 100644 --- a/src/Rtsp/RtspPlayer.cpp +++ b/src/Rtsp/RtspPlayer.cpp @@ -154,12 +154,12 @@ bool RtspPlayer::handleAuthenticationFailure(const string ¶msStr) { return false; } -void RtspPlayer::handleResDESCRIBE(const Parser& parser) { +bool RtspPlayer::handleResponse(const string &cmd, const Parser &parser){ string authInfo = parser["WWW-Authenticate"]; //发送DESCRIBE命令后的回复 if ((parser.Url() == "401") && handleAuthenticationFailure(authInfo)) { sendOptions(); - return; + return false; } if(parser.Url() == "302" || parser.Url() == "301"){ auto newUrl = parser["Location"]; @@ -167,14 +167,19 @@ void RtspPlayer::handleResDESCRIBE(const Parser& parser) { throw std::runtime_error("未找到Location字段(跳转url)"); } play(newUrl); - return; + return false; } if (parser.Url() != "200") { - throw std::runtime_error( - StrPrinter << "DESCRIBE:" << parser.Url() << " " << parser.Tail() << endl); + throw std::runtime_error(StrPrinter << cmd << ":" << parser.Url() << " " << parser.Tail() << endl); + } + return true; +} + +void RtspPlayer::handleResDESCRIBE(const Parser& parser) { + if (!handleResponse("DESCRIBE", parser)) { + return; } _content_base = parser["Content-Base"]; - if(_content_base.empty()){ _content_base = _play_url; } @@ -351,8 +356,8 @@ void RtspPlayer::sendDescribe() { void RtspPlayer::sendOptions(){ _on_response = [this](const Parser& parser){ - if (parser.Url() != "200") { - throw std::runtime_error(StrPrinter << "OPTIONS:" << parser.Url() << " " << parser.Tail() << endl); + if (!handleResponse("OPTIONS", parser)) { + return; } //获取服务器支持的命令 _supported_cmd.clear(); diff --git a/src/Rtsp/RtspPlayer.h b/src/Rtsp/RtspPlayer.h index 97eadc24..5a7b8efc 100644 --- a/src/Rtsp/RtspPlayer.h +++ b/src/Rtsp/RtspPlayer.h @@ -94,6 +94,7 @@ private: void handleResDESCRIBE(const Parser &parser); bool handleAuthenticationFailure(const string &wwwAuthenticateParamsStr); void handleResPAUSE(const Parser &parser, int type); + bool handleResponse(const string &cmd, const Parser &parser); void sendOptions(); void sendSetup(unsigned int uiTrackIndex); From 425c74e7302f3999cc6aabd7c9efd9785fe8325c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Tue, 19 May 2020 16:03:22 +0800 Subject: [PATCH 042/100] Update README_en.md --- README_en.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README_en.md b/README_en.md index a85441ca..6e261981 100644 --- a/README_en.md +++ b/README_en.md @@ -15,18 +15,18 @@ ## Features -- RTSP +- RTSP[S] - RTSP[S] server,support rtsp push. - - RTSP player and pusher. + - RTSP[S] player and pusher. - RTP Transport : `rtp over udp` `rtp over tcp` `rtp over http` `rtp udp multicast` . - Basic/Digest/Url Authentication. - H264/H265/AAC/G711 codec. - Recorded as mp4. - Vod of mp4. -- RTMP - - RTMP server,support player and pusher. - - RTMP player and pusher. +- RTMP[S] + - RTMP[S] server,support player and pusher. + - RTMP[S] player and pusher. - Support HTTP-FLV player. - H264/H265/AAC/G711 codec. - Recorded as flv or mp4. @@ -36,6 +36,7 @@ - HLS - RTSP RTMP can be converted into HLS,built-in HTTP server. - Play authentication based on cookie. + - Support HLS player, support streaming HLS proxy to RTSP / RTMP / MP4. - HTTP[S] - HTTP server,suppor directory meun、RESTful http api. @@ -53,6 +54,7 @@ - Play and push authentication. - Pull stream on Demand. - Support TS / PS streaming push through RTP,and it can be converted to RTSP / RTMP / HLS / FLV. + - Support real-time online screenshot http api. - Protocol conversion: @@ -67,6 +69,7 @@ | RTMP --> MP4 | Y | Y | Y | N | | MP4 --> RTSP[S] | Y | Y | Y | N | | MP4 --> RTMP | Y | Y | Y | N | +| HLS --> RTSP/RTMP/MP4 | Y | Y | Y | N | - Stream generation: @@ -106,7 +109,7 @@ | RTMP Pusher | Y | | HTTP[S] | Y | | WebSocket[S] | Y | - +| HLS player | Y | ## System Requirements From 3c1e0531deb5d0ffb536fff742be2b11cc8d036b Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 21 May 2020 11:44:57 +0800 Subject: [PATCH 043/100] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BE=AA=E7=8E=AF?= =?UTF-8?q?=E5=BC=95=E7=94=A8=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/HlsPlayer.cpp | 2 +- src/Rtp/Decoder.cpp | 4 ++-- src/Rtp/Decoder.h | 6 +++--- src/Rtp/RtpProcess.cpp | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Http/HlsPlayer.cpp b/src/Http/HlsPlayer.cpp index d1b78186..0e5b55c9 100644 --- a/src/Http/HlsPlayer.cpp +++ b/src/Http/HlsPlayer.cpp @@ -241,7 +241,7 @@ void HlsPlayerImp::onPacket(const char *data,uint64_t len) { } if (!_decoder) { - _decoder = DecoderImp::createDecoder(DecoderImp::decoder_ts, dynamic_pointer_cast(shared_from_this())); + _decoder = DecoderImp::createDecoder(DecoderImp::decoder_ts, this); } if (_decoder) { diff --git a/src/Rtp/Decoder.cpp b/src/Rtp/Decoder.cpp index 2e3349dd..5c8a7662 100644 --- a/src/Rtp/Decoder.cpp +++ b/src/Rtp/Decoder.cpp @@ -42,7 +42,7 @@ static Decoder::Ptr createDecoder_l(DecoderImp::Type type) { ///////////////////////////////////////////////////////////// -DecoderImp::Ptr DecoderImp::createDecoder(Type type, const MediaSinkInterface::Ptr &sink){ +DecoderImp::Ptr DecoderImp::createDecoder(Type type, MediaSinkInterface *sink){ auto decoder = createDecoder_l(type); if(!decoder){ return nullptr; @@ -54,7 +54,7 @@ int DecoderImp::input(const uint8_t *data, int bytes){ return _decoder->input(data, bytes); } -DecoderImp::DecoderImp(const Decoder::Ptr &decoder,const MediaSinkInterface::Ptr &sink){ +DecoderImp::DecoderImp(const Decoder::Ptr &decoder, MediaSinkInterface *sink){ _decoder = decoder; _sink = sink; _decoder->setOnDecode([this](int stream,int codecid,int flags,int64_t pts,int64_t dts,const void *data,int bytes){ diff --git a/src/Rtp/Decoder.h b/src/Rtp/Decoder.h index c20bc65d..a74ee9f2 100644 --- a/src/Rtp/Decoder.h +++ b/src/Rtp/Decoder.h @@ -53,7 +53,7 @@ public: typedef std::shared_ptr Ptr; ~DecoderImp() = default; - static Ptr createDecoder(Type type, const MediaSinkInterface::Ptr &sink); + static Ptr createDecoder(Type type, MediaSinkInterface *sink); int input(const uint8_t *data, int bytes); protected: @@ -61,12 +61,12 @@ protected: void onFrame(const Frame::Ptr &frame); private: - DecoderImp(const Decoder::Ptr &decoder, const MediaSinkInterface::Ptr &sink); + DecoderImp(const Decoder::Ptr &decoder, MediaSinkInterface *sink); void onDecode(int stream,int codecid,int flags,int64_t pts,int64_t dts,const void *data,int bytes); private: Decoder::Ptr _decoder; - MediaSinkInterface::Ptr _sink; + MediaSinkInterface *_sink; FrameMerger _merger; int _codecid_video = 0; int _codecid_audio = 0; diff --git a/src/Rtp/RtpProcess.cpp b/src/Rtp/RtpProcess.cpp index 008c7406..a3c72a8d 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -152,11 +152,11 @@ void RtpProcess::onRtpDecode(const uint8_t *packet, int bytes, uint32_t timestam if (checkTS(packet, bytes)) { //猜测是ts负载 InfoP(this) << "judged to be TS"; - _decoder = DecoderImp::createDecoder(DecoderImp::decoder_ts, shared_from_this()); + _decoder = DecoderImp::createDecoder(DecoderImp::decoder_ts, this); } else { //猜测是ps负载 InfoP(this) << "judged to be PS"; - _decoder = DecoderImp::createDecoder(DecoderImp::decoder_ps, shared_from_this()); + _decoder = DecoderImp::createDecoder(DecoderImp::decoder_ps, this); } } From c02438f9c85a9f5db83659f4bcddbc63231817e4 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 21 May 2020 14:10:27 +0800 Subject: [PATCH 044/100] =?UTF-8?q?hls=E6=92=AD=E6=94=BE=E5=99=A8=E5=85=BC?= =?UTF-8?q?=E5=AE=B9=E5=B8=A6=E5=8F=82=E6=95=B0=E7=9A=84url?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Player/PlayerBase.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Player/PlayerBase.cpp b/src/Player/PlayerBase.cpp index 526c8ffc..ffe54f28 100644 --- a/src/Player/PlayerBase.cpp +++ b/src/Player/PlayerBase.cpp @@ -23,14 +23,20 @@ static bool end_of(const string &str, const string &substr){ return pos != string::npos && pos == str.size() - substr.size(); } -PlayerBase::Ptr PlayerBase::createPlayer(const EventPoller::Ptr &poller,const string &strUrl) { +PlayerBase::Ptr PlayerBase::createPlayer(const EventPoller::Ptr &poller,const string &url_in) { static auto releasePlayer = [](PlayerBase *ptr){ onceToken token(nullptr,[&](){ delete ptr; }); ptr->teardown(); }; - string prefix = FindField(strUrl.data(), NULL, "://"); + string url = url_in; + string prefix = FindField(url.data(), NULL, "://"); + auto pos = url.find('?'); + if (pos != string::npos) { + //去除?后面的字符串 + url = url.substr(0, pos); + } if (strcasecmp("rtsps",prefix.data()) == 0) { return PlayerBase::Ptr(new TcpClientWithSSL(poller),releasePlayer); @@ -48,7 +54,7 @@ PlayerBase::Ptr PlayerBase::createPlayer(const EventPoller::Ptr &poller,const st return PlayerBase::Ptr(new RtmpPlayerImp(poller),releasePlayer); } - if ((strcasecmp("http",prefix.data()) == 0 || strcasecmp("https",prefix.data()) == 0) && end_of(strUrl, ".m3u8")) { + if ((strcasecmp("http",prefix.data()) == 0 || strcasecmp("https",prefix.data()) == 0) && end_of(url, ".m3u8")) { return PlayerBase::Ptr(new HlsPlayerImp(poller),releasePlayer); } From 51a86f502fe2cdd27580666a3a6639edbdffbc12 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 21 May 2020 14:10:55 +0800 Subject: [PATCH 045/100] =?UTF-8?q?=E5=AE=8C=E5=96=84h264=E5=B8=A7split?= =?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/Extension/H265.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Extension/H265.h b/src/Extension/H265.h index 31a68415..e7bd30c7 100644 --- a/src/Extension/H265.h +++ b/src/Extension/H265.h @@ -199,7 +199,7 @@ public: */ void inputFrame(const Frame::Ptr &frame) override{ int type = H265_TYPE(*((uint8_t *)frame->data() + frame->prefixSize())); - if(frame->configFrame()){ + if(frame->configFrame() || type == H265Frame::NAL_SEI_PREFIX){ splitH264(frame->data(), frame->size(), frame->prefixSize(), [&](const char *ptr, int len, int prefix){ H265FrameInternal::Ptr sub_frame = std::make_shared(frame, (char*)ptr, len, prefix); inputFrame_l(sub_frame); From 4130910574cc8c4862b69bf8c1264ef8e5803d3a Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 21 May 2020 18:01:57 +0800 Subject: [PATCH 046/100] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=A4=B4=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=8C=85=E5=90=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bf4568b1..05075705 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,10 @@ set(MediaServer_Root ${CMAKE_CURRENT_SOURCE_DIR}/3rdpart/media-server) #设置头文件目录 INCLUDE_DIRECTORIES(${ToolKit_Root}) INCLUDE_DIRECTORIES(${MediaKit_Root}) +INCLUDE_DIRECTORIES(${MediaServer_Root}/libmpeg/include) +INCLUDE_DIRECTORIES(${MediaServer_Root}/libmov/include) +INCLUDE_DIRECTORIES(${MediaServer_Root}/libflv/include) +INCLUDE_DIRECTORIES(${MediaServer_Root}/librtp/include) set(ENABLE_HLS true) set(ENABLE_OPENSSL true) @@ -106,7 +110,6 @@ if(ENABLE_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) @@ -123,11 +126,9 @@ if(ENABLE_MP4) 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(flv STATIC ${src_flv}) @@ -144,7 +145,6 @@ if(ENABLE_RTPPROXY AND ENABLE_HLS) 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) From 06d61cf195f584e8f82c6f2e18b918ff12d903fc Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 25 May 2020 12:07:38 +0800 Subject: [PATCH 047/100] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=85=A8=E5=B1=80?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E6=88=B3=E8=A6=86=E7=9B=96=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/config.ini | 6 ++- src/Common/MultiMediaSourceMuxer.cpp | 63 +++++++++++++++++++++++++++- src/Common/MultiMediaSourceMuxer.h | 1 + src/Common/config.cpp | 2 + src/Common/config.h | 3 ++ 5 files changed, 73 insertions(+), 2 deletions(-) diff --git a/conf/config.ini b/conf/config.ini index d87c9547..78abb92d 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -18,7 +18,6 @@ snap=%s -i %s -y -f mjpeg -t 0.001 %s #可以为相对(相对于本可执行程序目录)或绝对路径 log=./ffmpeg/ffmpeg.log - [general] #是否启用虚拟主机 enableVhost=0 @@ -48,6 +47,11 @@ publishToMP4=0 #合并写缓存大小(单位毫秒),合并写指服务器缓存一定的数据后才会一次性写入socket,这样能提高性能,但是会提高延时 #开启后会同时关闭TCP_NODELAY并开启MSG_MORE mergeWriteMS=0 +#全局的时间戳覆盖开关,在转协议时,对frame进行时间戳覆盖 +#该开关对rtsp/rtmp/rtp推流、rtsp/rtmp/hls拉流代理转协议时生效 +#会直接影响rtsp/rtmp/hls/mp4/flv等协议的时间戳 +#同协议情况下不影响(例如rtsp/rtmp推流,那么播放rtsp/rtmp时不会影响时间戳) +modifyStamp=0 [hls] #hls写文件的buf大小,调整参数可以提高文件io性能 diff --git a/src/Common/MultiMediaSourceMuxer.cpp b/src/Common/MultiMediaSourceMuxer.cpp index 58217d2b..70a20413 100644 --- a/src/Common/MultiMediaSourceMuxer.cpp +++ b/src/Common/MultiMediaSourceMuxer.cpp @@ -298,8 +298,69 @@ void MultiMediaSourceMuxer::resetTracks() { _muxer->resetTracks(); } +//该类实现frame级别的时间戳覆盖 +class FrameModifyStamp : public Frame{ +public: + typedef std::shared_ptr Ptr; + FrameModifyStamp(const Frame::Ptr &frame, Stamp &stamp){ + _frame = frame; + //覆盖时间戳 + stamp.revise(frame->dts(), frame->pts(), _dts, _pts, true); + } + ~FrameModifyStamp() override {} + + uint32_t dts() const override{ + return _dts; + } + + uint32_t pts() const override{ + return _pts; + } + + uint32_t prefixSize() const override { + return _frame->prefixSize(); + } + + bool keyFrame() const override { + return _frame->keyFrame(); + } + + bool configFrame() const override { + return _frame->configFrame(); + } + + bool cacheAble() const override { + return _frame->cacheAble(); + } + + char *data() const override { + return _frame->data(); + } + + uint32_t size() const override { + return _frame->size(); + } + + CodecId getCodecId() const override { + return _frame->getCodecId(); + } +private: + Frame::Ptr _frame; + int64_t _dts; + int64_t _pts; +}; + void MultiMediaSourceMuxer::inputFrame(const Frame::Ptr &frame) { - _muxer->inputFrame(frame); + GET_CONFIG(bool,modify_stamp,General::kModifyStamp); + if(!modify_stamp){ + //未开启时间戳覆盖 + _muxer->inputFrame(frame); + }else{ + //开启了时间戳覆盖 + FrameModifyStamp::Ptr new_frame = std::make_shared(frame,_stamp[frame->getTrackType()]); + //输入时间戳覆盖后的帧 + _muxer->inputFrame(new_frame); + } } bool MultiMediaSourceMuxer::isEnabled(){ diff --git a/src/Common/MultiMediaSourceMuxer.h b/src/Common/MultiMediaSourceMuxer.h index 04109745..2ba2aa99 100644 --- a/src/Common/MultiMediaSourceMuxer.h +++ b/src/Common/MultiMediaSourceMuxer.h @@ -178,6 +178,7 @@ public: private: MultiMuxerPrivate::Ptr _muxer; std::weak_ptr _listener; + Stamp _stamp[2]; }; }//namespace mediakit diff --git a/src/Common/config.cpp b/src/Common/config.cpp index 8a22bea1..d8f2b933 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -67,6 +67,7 @@ const string kPublishToRtxp = GENERAL_FIELD"publishToRtxp"; const string kPublishToHls = GENERAL_FIELD"publishToHls"; const string kPublishToMP4 = GENERAL_FIELD"publishToMP4"; const string kMergeWriteMS = GENERAL_FIELD"mergeWriteMS"; +const string kModifyStamp = GENERAL_FIELD"modifyStamp"; onceToken token([](){ mINI::Instance()[kFlowThreshold] = 1024; @@ -79,6 +80,7 @@ onceToken token([](){ mINI::Instance()[kPublishToHls] = 1; mINI::Instance()[kPublishToMP4] = 0; mINI::Instance()[kMergeWriteMS] = 0; + mINI::Instance()[kModifyStamp] = 0; },nullptr); }//namespace General diff --git a/src/Common/config.h b/src/Common/config.h index 3948879f..27b4cefa 100644 --- a/src/Common/config.h +++ b/src/Common/config.h @@ -174,6 +174,8 @@ extern const string kPublishToMP4 ; //合并写缓存大小(单位毫秒),合并写指服务器缓存一定的数据后才会一次性写入socket,这样能提高性能,但是会提高延时 //开启后会同时关闭TCP_NODELAY并开启MSG_MORE extern const string kMergeWriteMS ; +//全局的时间戳覆盖开关,在转协议时,对frame进行时间戳覆盖 +extern const string kModifyStamp; }//namespace General @@ -217,6 +219,7 @@ extern const string kDirectProxy; ////////////RTMP服务器配置/////////// namespace Rtmp { +//rtmp推流时间戳覆盖开关 extern const string kModifyStamp; //握手超时时间,默认15秒 extern const string kHandshakeSecond; From 893564d0be79e2f636829c37b759fcf6dfe90470 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 25 May 2020 13:51:00 +0800 Subject: [PATCH 048/100] =?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 --- src/Extension/AAC.h | 12 +++++------ src/Extension/AACRtp.cpp | 4 ++-- src/Extension/AACRtp.h | 4 ++-- src/Extension/Factory.cpp | 2 +- src/Extension/G711.h | 10 ++++----- src/Extension/G711Rtp.cpp | 4 ++-- src/Extension/G711Rtp.h | 4 ++-- src/Extension/H264.h | 12 +++++------ src/Extension/H264Rtp.cpp | 4 ++-- src/Extension/H264Rtp.h | 4 ++-- src/Extension/H265.h | 12 +++++------ src/Extension/H265Rtp.cpp | 4 ++-- src/Extension/H265Rtp.h | 4 ++-- src/Extension/Opus.h | 10 ++++----- src/Http/WebSocketClient.h | 6 +++--- src/Http/WebSocketSession.h | 2 +- src/Http/WebSocketSplitter.cpp | 38 +++++++++++++++++----------------- src/Http/WebSocketSplitter.h | 14 ++++++------- src/Rtsp/RtpCodec.cpp | 6 +++--- src/Rtsp/RtpCodec.h | 10 ++++----- src/Rtsp/Rtsp.h | 12 +++++------ 21 files changed, 89 insertions(+), 89 deletions(-) diff --git a/src/Extension/AAC.h b/src/Extension/AAC.h index 4783d1d5..feb65bd7 100644 --- a/src/Extension/AAC.h +++ b/src/Extension/AAC.h @@ -173,21 +173,21 @@ public: * 构造函数 * @param aac_cfg aac两个字节的配置描述 * @param sample_rate 音频采样率 - * @param playload_type rtp playload type 默认98 + * @param payload_type rtp payload type 默认98 * @param bitrate 比特率 */ AACSdp(const string &aac_cfg, int sample_rate, int channels, - int playload_type = 98, - int bitrate = 128) : Sdp(sample_rate,playload_type){ - _printer << "m=audio 0 RTP/AVP " << playload_type << "\r\n"; + int payload_type = 98, + int bitrate = 128) : Sdp(sample_rate,payload_type){ + _printer << "m=audio 0 RTP/AVP " << payload_type << "\r\n"; _printer << "b=AS:" << bitrate << "\r\n"; - _printer << "a=rtpmap:" << playload_type << " MPEG4-GENERIC/" << sample_rate << "/" << channels << "\r\n"; + _printer << "a=rtpmap:" << payload_type << " MPEG4-GENERIC/" << sample_rate << "/" << channels << "\r\n"; char configStr[32] = {0}; snprintf(configStr, sizeof(configStr), "%02X%02X", (uint8_t)aac_cfg[0], (uint8_t)aac_cfg[1]); - _printer << "a=fmtp:" << playload_type << " streamtype=5;profile-level-id=1;mode=AAC-hbr;" + _printer << "a=fmtp:" << payload_type << " streamtype=5;profile-level-id=1;mode=AAC-hbr;" << "sizelength=13;indexlength=3;indexdeltalength=3;config=" << configStr << "\r\n"; _printer << "a=control:trackID=" << (int)TrackAudio << "\r\n"; } diff --git a/src/Extension/AACRtp.cpp b/src/Extension/AACRtp.cpp index 3baadb36..d8c257cf 100644 --- a/src/Extension/AACRtp.cpp +++ b/src/Extension/AACRtp.cpp @@ -16,12 +16,12 @@ namespace mediakit{ AACRtpEncoder::AACRtpEncoder(uint32_t ui32Ssrc, uint32_t ui32MtuSize, uint32_t ui32SampleRate, - uint8_t ui8PlayloadType, + uint8_t ui8PayloadType, uint8_t ui8Interleaved) : RtpInfo(ui32Ssrc, ui32MtuSize, ui32SampleRate, - ui8PlayloadType, + ui8PayloadType, ui8Interleaved){ } diff --git a/src/Extension/AACRtp.h b/src/Extension/AACRtp.h index e4758fef..b82b19e2 100644 --- a/src/Extension/AACRtp.h +++ b/src/Extension/AACRtp.h @@ -56,13 +56,13 @@ public: * @param ui32Ssrc ssrc * @param ui32MtuSize mtu 大小 * @param ui32SampleRate 采样率 - * @param ui8PlayloadType pt类型 + * @param ui8PayloadType pt类型 * @param ui8Interleaved rtsp interleaved 值 */ AACRtpEncoder(uint32_t ui32Ssrc, uint32_t ui32MtuSize, uint32_t ui32SampleRate, - uint8_t ui8PlayloadType = 97, + uint8_t ui8PayloadType = 97, uint8_t ui8Interleaved = TrackAudio * 2); ~AACRtpEncoder() {} diff --git a/src/Extension/Factory.cpp b/src/Extension/Factory.cpp index 4bdc7c43..b59e166f 100644 --- a/src/Extension/Factory.cpp +++ b/src/Extension/Factory.cpp @@ -115,7 +115,7 @@ RtpCodec::Ptr Factory::getRtpEncoderBySdp(const Sdp::Ptr &sdp) { } auto mtu = (sdp->getTrackType() == TrackVideo ? video_mtu : audio_mtu); auto sample_rate = sdp->getSampleRate(); - auto pt = sdp->getPlayloadType(); + auto pt = sdp->getPayloadType(); auto interleaved = sdp->getTrackType() * 2; auto codec_id = sdp->getCodecId(); switch (codec_id){ diff --git a/src/Extension/G711.h b/src/Extension/G711.h index b2d22b23..04e11b6a 100644 --- a/src/Extension/G711.h +++ b/src/Extension/G711.h @@ -83,16 +83,16 @@ public: * G711采样率固定为8000 * @param codecId G711A G711U * @param sample_rate 音频采样率 - * @param playload_type rtp playload + * @param payload_type rtp payload * @param bitrate 比特率 */ G711Sdp(CodecId codecId, int sample_rate, int channels, - int playload_type = 98, - int bitrate = 128) : Sdp(sample_rate,playload_type), _codecId(codecId){ - _printer << "m=audio 0 RTP/AVP " << playload_type << "\r\n"; - _printer << "a=rtpmap:" << playload_type << (codecId == CodecG711A ? " PCMA/" : " PCMU/") << sample_rate << "/" << channels << "\r\n"; + int payload_type = 98, + int bitrate = 128) : Sdp(sample_rate,payload_type), _codecId(codecId){ + _printer << "m=audio 0 RTP/AVP " << payload_type << "\r\n"; + _printer << "a=rtpmap:" << payload_type << (codecId == CodecG711A ? " PCMA/" : " PCMU/") << sample_rate << "/" << channels << "\r\n"; _printer << "a=control:trackID=" << (int)TrackAudio << "\r\n"; } diff --git a/src/Extension/G711Rtp.cpp b/src/Extension/G711Rtp.cpp index e9a77d1e..8e7a5cf8 100644 --- a/src/Extension/G711Rtp.cpp +++ b/src/Extension/G711Rtp.cpp @@ -62,12 +62,12 @@ void G711RtpDecoder::onGetG711(const G711Frame::Ptr &frame) { G711RtpEncoder::G711RtpEncoder(uint32_t ui32Ssrc, uint32_t ui32MtuSize, uint32_t ui32SampleRate, - uint8_t ui8PlayloadType, + uint8_t ui8PayloadType, uint8_t ui8Interleaved) : RtpInfo(ui32Ssrc, ui32MtuSize, ui32SampleRate, - ui8PlayloadType, + ui8PayloadType, ui8Interleaved) { } diff --git a/src/Extension/G711Rtp.h b/src/Extension/G711Rtp.h index f829b7fc..f76f4a70 100644 --- a/src/Extension/G711Rtp.h +++ b/src/Extension/G711Rtp.h @@ -58,13 +58,13 @@ public: * @param ui32Ssrc ssrc * @param ui32MtuSize mtu 大小 * @param ui32SampleRate 采样率 - * @param ui8PlayloadType pt类型 + * @param ui8PayloadType pt类型 * @param ui8Interleaved rtsp interleaved 值 */ G711RtpEncoder(uint32_t ui32Ssrc, uint32_t ui32MtuSize, uint32_t ui32SampleRate, - uint8_t ui8PlayloadType = 0, + uint8_t ui8PayloadType = 0, uint8_t ui8Interleaved = TrackAudio * 2); ~G711RtpEncoder() {} diff --git a/src/Extension/H264.h b/src/Extension/H264.h index 437edcca..974ec817 100644 --- a/src/Extension/H264.h +++ b/src/Extension/H264.h @@ -289,18 +289,18 @@ public: * * @param sps 264 sps,不带0x00000001头 * @param pps 264 pps,不带0x00000001头 - * @param playload_type rtp playload type 默认96 + * @param payload_type rtp payload type 默认96 * @param bitrate 比特率 */ H264Sdp(const string &strSPS, const string &strPPS, - int playload_type = 96, - int bitrate = 4000) : Sdp(90000,playload_type) { + int payload_type = 96, + int bitrate = 4000) : Sdp(90000,payload_type) { //视频通道 - _printer << "m=video 0 RTP/AVP " << playload_type << "\r\n"; + _printer << "m=video 0 RTP/AVP " << payload_type << "\r\n"; _printer << "b=AS:" << bitrate << "\r\n"; - _printer << "a=rtpmap:" << playload_type << " H264/" << 90000 << "\r\n"; - _printer << "a=fmtp:" << playload_type << " packetization-mode=1; profile-level-id="; + _printer << "a=rtpmap:" << payload_type << " H264/" << 90000 << "\r\n"; + _printer << "a=fmtp:" << payload_type << " packetization-mode=1; profile-level-id="; char strTemp[100]; uint32_t profile_level_id = 0; diff --git a/src/Extension/H264Rtp.cpp b/src/Extension/H264Rtp.cpp index d9b4b9f6..b9789c86 100644 --- a/src/Extension/H264Rtp.cpp +++ b/src/Extension/H264Rtp.cpp @@ -204,12 +204,12 @@ void H264RtpDecoder::onGetH264(const H264Frame::Ptr &frame) { H264RtpEncoder::H264RtpEncoder(uint32_t ui32Ssrc, uint32_t ui32MtuSize, uint32_t ui32SampleRate, - uint8_t ui8PlayloadType, + uint8_t ui8PayloadType, uint8_t ui8Interleaved) : RtpInfo(ui32Ssrc, ui32MtuSize, ui32SampleRate, - ui8PlayloadType, + ui8PayloadType, ui8Interleaved) { } diff --git a/src/Extension/H264Rtp.h b/src/Extension/H264Rtp.h index 3453bf3e..2b5038b1 100644 --- a/src/Extension/H264Rtp.h +++ b/src/Extension/H264Rtp.h @@ -62,13 +62,13 @@ public: * @param ui32Ssrc ssrc * @param ui32MtuSize mtu大小 * @param ui32SampleRate 采样率,强制为90000 - * @param ui8PlayloadType pt类型 + * @param ui8PayloadType pt类型 * @param ui8Interleaved rtsp interleaved */ H264RtpEncoder(uint32_t ui32Ssrc, uint32_t ui32MtuSize = 1400, uint32_t ui32SampleRate = 90000, - uint8_t ui8PlayloadType = 96, + uint8_t ui8PayloadType = 96, uint8_t ui8Interleaved = TrackVideo * 2); ~H264RtpEncoder() {} diff --git a/src/Extension/H265.h b/src/Extension/H265.h index e7bd30c7..5279908b 100644 --- a/src/Extension/H265.h +++ b/src/Extension/H265.h @@ -315,19 +315,19 @@ public: * 构造函数 * @param sps 265 sps,不带0x00000001头 * @param pps 265 pps,不带0x00000001头 - * @param playload_type rtp playload type 默认96 + * @param payload_type rtp payload type 默认96 * @param bitrate 比特率 */ H265Sdp(const string &strVPS, const string &strSPS, const string &strPPS, - int playload_type = 96, - int bitrate = 4000) : Sdp(90000,playload_type) { + int payload_type = 96, + int bitrate = 4000) : Sdp(90000,payload_type) { //视频通道 - _printer << "m=video 0 RTP/AVP " << playload_type << "\r\n"; + _printer << "m=video 0 RTP/AVP " << payload_type << "\r\n"; _printer << "b=AS:" << bitrate << "\r\n"; - _printer << "a=rtpmap:" << playload_type << " H265/" << 90000 << "\r\n"; - _printer << "a=fmtp:" << playload_type << " "; + _printer << "a=rtpmap:" << payload_type << " H265/" << 90000 << "\r\n"; + _printer << "a=fmtp:" << payload_type << " "; _printer << "sprop-vps="; _printer << encodeBase64(strVPS) << "; "; _printer << "sprop-sps="; diff --git a/src/Extension/H265Rtp.cpp b/src/Extension/H265Rtp.cpp index daa190a0..771867ec 100644 --- a/src/Extension/H265Rtp.cpp +++ b/src/Extension/H265Rtp.cpp @@ -140,12 +140,12 @@ void H265RtpDecoder::onGetH265(const H265Frame::Ptr &frame) { H265RtpEncoder::H265RtpEncoder(uint32_t ui32Ssrc, uint32_t ui32MtuSize, uint32_t ui32SampleRate, - uint8_t ui8PlayloadType, + uint8_t ui8PayloadType, uint8_t ui8Interleaved) : RtpInfo(ui32Ssrc, ui32MtuSize, ui32SampleRate, - ui8PlayloadType, + ui8PayloadType, ui8Interleaved) { } diff --git a/src/Extension/H265Rtp.h b/src/Extension/H265Rtp.h index 33e28b5f..6c8bbe38 100644 --- a/src/Extension/H265Rtp.h +++ b/src/Extension/H265Rtp.h @@ -63,13 +63,13 @@ public: * @param ui32Ssrc ssrc * @param ui32MtuSize mtu大小 * @param ui32SampleRate 采样率,强制为90000 - * @param ui8PlayloadType pt类型 + * @param ui8PayloadType pt类型 * @param ui8Interleaved rtsp interleaved */ H265RtpEncoder(uint32_t ui32Ssrc, uint32_t ui32MtuSize = 1400, uint32_t ui32SampleRate = 90000, - uint8_t ui8PlayloadType = 96, + uint8_t ui8PayloadType = 96, uint8_t ui8Interleaved = TrackVideo * 2); ~H265RtpEncoder() {} diff --git a/src/Extension/Opus.h b/src/Extension/Opus.h index 17e28c06..0a6e56d3 100644 --- a/src/Extension/Opus.h +++ b/src/Extension/Opus.h @@ -80,15 +80,15 @@ public: /** * 构造opus sdp * @param sample_rate 音频采样率 - * @param playload_type rtp playload + * @param payload_type rtp payload * @param bitrate 比特率 */ OpusSdp(int sample_rate, int channels, - int playload_type = 98, - int bitrate = 128) : Sdp(sample_rate,playload_type){ - _printer << "m=audio 0 RTP/AVP " << playload_type << "\r\n"; - _printer << "a=rtpmap:" << playload_type << " opus/" << sample_rate << "/" << channels << "\r\n"; + int payload_type = 98, + int bitrate = 128) : Sdp(sample_rate,payload_type){ + _printer << "m=audio 0 RTP/AVP " << payload_type << "\r\n"; + _printer << "a=rtpmap:" << payload_type << " opus/" << sample_rate << "/" << channels << "\r\n"; _printer << "a=control:trackID=" << (int)TrackAudio << "\r\n"; } diff --git a/src/Http/WebSocketClient.h b/src/Http/WebSocketClient.h index 36e67516..d22f7c28 100644 --- a/src/Http/WebSocketClient.h +++ b/src/Http/WebSocketClient.h @@ -193,7 +193,7 @@ protected: //WebSocketSplitter override /** - * 收到一个webSocket数据包包头,后续将继续触发onWebSocketDecodePlayload回调 + * 收到一个webSocket数据包包头,后续将继续触发onWebSocketDecodePayload回调 * @param header 数据包头 */ void onWebSocketDecodeHeader(const WebSocketHeader &header) override{ @@ -205,9 +205,9 @@ protected: * @param header 数据包包头 * @param ptr 负载数据指针 * @param len 负载数据长度 - * @param recved 已接收数据长度(包含本次数据长度),等于header._playload_len时则接受完毕 + * @param recved 已接收数据长度(包含本次数据长度),等于header._payload_len时则接受完毕 */ - void onWebSocketDecodePlayload(const WebSocketHeader &header, const uint8_t *ptr, uint64_t len, uint64_t recved) override{ + void onWebSocketDecodePayload(const WebSocketHeader &header, const uint8_t *ptr, uint64_t len, uint64_t recved) override{ _payload.append((char *)ptr,len); } diff --git a/src/Http/WebSocketSession.h b/src/Http/WebSocketSession.h index 0c4ff278..ed9dd140 100644 --- a/src/Http/WebSocketSession.h +++ b/src/Http/WebSocketSession.h @@ -161,7 +161,7 @@ protected: * @param len * @param recved */ - void onWebSocketDecodePlayload(const WebSocketHeader &packet,const uint8_t *ptr,uint64_t len,uint64_t recved) override { + void onWebSocketDecodePayload(const WebSocketHeader &packet,const uint8_t *ptr,uint64_t len,uint64_t recved) override { _remian_data.append((char *)ptr,len); } diff --git a/src/Http/WebSocketSplitter.cpp b/src/Http/WebSocketSplitter.cpp index 72e9927a..701b373b 100644 --- a/src/Http/WebSocketSplitter.cpp +++ b/src/Http/WebSocketSplitter.cpp @@ -72,16 +72,16 @@ begin_decode: CHECK_LEN(1); _mask_flag = (*ptr & 0x80) >> 7; - _playload_len = (*ptr & 0x7F); + _payload_len = (*ptr & 0x7F); ptr += 1; - if (_playload_len == 126) { + if (_payload_len == 126) { CHECK_LEN(2); - _playload_len = (*ptr << 8) | *(ptr + 1); + _payload_len = (*ptr << 8) | *(ptr + 1); ptr += 2; - } else if (_playload_len == 127) { + } else if (_payload_len == 127) { CHECK_LEN(8); - _playload_len = ((uint64_t) ptr[0] << (8 * 7)) | + _payload_len = ((uint64_t) ptr[0] << (8 * 7)) | ((uint64_t) ptr[1] << (8 * 6)) | ((uint64_t) ptr[2] << (8 * 5)) | ((uint64_t) ptr[3] << (8 * 4)) | @@ -98,9 +98,9 @@ begin_decode: } _got_header = true; _mask_offset = 0; - _playload_offset = 0; + _payload_offset = 0; onWebSocketDecodeHeader(*this); - if(_playload_len == 0){ + if(_payload_len == 0){ onWebSocketDecodeComplete(*this); } } @@ -109,19 +109,19 @@ begin_decode: uint64_t remain = len - (ptr - data); if(remain > 0){ - uint64_t playload_slice_len = remain; - if(playload_slice_len + _playload_offset > _playload_len){ - playload_slice_len = _playload_len - _playload_offset; + uint64_t payload_slice_len = remain; + if(payload_slice_len + _payload_offset > _payload_len){ + payload_slice_len = _payload_len - _payload_offset; } - _playload_offset += playload_slice_len; - onPlayloadData(ptr,playload_slice_len); + _payload_offset += payload_slice_len; + onPayloadData(ptr, payload_slice_len); - if(_playload_offset == _playload_len){ + if(_payload_offset == _payload_len){ onWebSocketDecodeComplete(*this); //这是下一个包 - remain -= playload_slice_len; - ptr += playload_slice_len; + remain -= payload_slice_len; + ptr += payload_slice_len; _got_header = false; if(remain > 0){ @@ -138,14 +138,14 @@ begin_decode: _remain_data.clear(); } -void WebSocketSplitter::onPlayloadData(uint8_t *ptr, uint64_t len) { +void WebSocketSplitter::onPayloadData(uint8_t *data, uint64_t len) { if(_mask_flag){ - for(int i = 0; i < len ; ++i,++ptr){ - *(ptr) ^= _mask[(i + _mask_offset) % 4]; + for(int i = 0; i < len ; ++i,++data){ + *(data) ^= _mask[(i + _mask_offset) % 4]; } _mask_offset = (_mask_offset + len) % 4; } - onWebSocketDecodePlayload(*this, _mask_flag ? ptr - len : ptr, len, _playload_offset); + onWebSocketDecodePayload(*this, _mask_flag ? data - len : data, len, _payload_offset); } void WebSocketSplitter::encode(const WebSocketHeader &header,const Buffer::Ptr &buffer) { diff --git a/src/Http/WebSocketSplitter.h b/src/Http/WebSocketSplitter.h index 1d5e154e..05e49648 100644 --- a/src/Http/WebSocketSplitter.h +++ b/src/Http/WebSocketSplitter.h @@ -51,7 +51,7 @@ public: uint8_t _reserved; Type _opcode; bool _mask_flag; - uint64_t _playload_len; + uint64_t _payload_len; vector _mask; }; @@ -62,7 +62,7 @@ public: /** * 输入数据以便解包webSocket数据以及处理粘包问题 - * 可能触发onWebSocketDecodeHeader和onWebSocketDecodePlayload回调 + * 可能触发onWebSocketDecodeHeader和onWebSocketDecodePayload回调 * @param data 需要解包的数据,可能是不完整的包或多个包 * @param len 数据长度 */ @@ -77,7 +77,7 @@ public: void encode(const WebSocketHeader &header,const Buffer::Ptr &buffer); protected: /** - * 收到一个webSocket数据包包头,后续将继续触发onWebSocketDecodePlayload回调 + * 收到一个webSocket数据包包头,后续将继续触发onWebSocketDecodePayload回调 * @param header 数据包头 */ virtual void onWebSocketDecodeHeader(const WebSocketHeader &header) {}; @@ -87,9 +87,9 @@ protected: * @param header 数据包包头 * @param ptr 负载数据指针 * @param len 负载数据长度 - * @param recved 已接收数据长度(包含本次数据长度),等于header._playload_len时则接受完毕 + * @param recved 已接收数据长度(包含本次数据长度),等于header._payload_len时则接受完毕 */ - virtual void onWebSocketDecodePlayload(const WebSocketHeader &header, const uint8_t *ptr, uint64_t len, uint64_t recved) {}; + virtual void onWebSocketDecodePayload(const WebSocketHeader &header, const uint8_t *ptr, uint64_t len, uint64_t recved) {}; /** @@ -105,12 +105,12 @@ protected: */ virtual void onWebSocketEncodeData(const Buffer::Ptr &buffer){}; private: - void onPlayloadData(uint8_t *data,uint64_t len); + void onPayloadData(uint8_t *data, uint64_t len); private: string _remain_data; int _mask_offset = 0; bool _got_header = false; - uint64_t _playload_offset = 0; + uint64_t _payload_offset = 0; }; } /* namespace mediakit */ diff --git a/src/Rtsp/RtpCodec.cpp b/src/Rtsp/RtpCodec.cpp index 5343b4fb..6930b1e2 100644 --- a/src/Rtsp/RtpCodec.cpp +++ b/src/Rtsp/RtpCodec.cpp @@ -28,18 +28,18 @@ RtpPacket::Ptr RtpInfo::makeRtp(TrackType type, const void* data, unsigned int l pucRtp[2] = ui16RtpLen >> 8; pucRtp[3] = ui16RtpLen & 0x00FF; pucRtp[4] = 0x80; - pucRtp[5] = (mark << 7) | _ui8PlayloadType; + pucRtp[5] = (mark << 7) | _ui8PayloadType; memcpy(&pucRtp[6], &sq, 2); memcpy(&pucRtp[8], &ts, 4); //ssrc memcpy(&pucRtp[12], &sc, 4); if(data){ - //playload + //payload memcpy(&pucRtp[16], data, len); } - rtppkt->PT = _ui8PlayloadType; + rtppkt->PT = _ui8PayloadType; rtppkt->interleaved = _ui8Interleaved; rtppkt->mark = mark; rtppkt->sequence = _ui16Sequence; diff --git a/src/Rtsp/RtpCodec.h b/src/Rtsp/RtpCodec.h index c8645d70..16a6f88a 100644 --- a/src/Rtsp/RtpCodec.h +++ b/src/Rtsp/RtpCodec.h @@ -66,7 +66,7 @@ public: RtpInfo(uint32_t ui32Ssrc, uint32_t ui32MtuSize, uint32_t ui32SampleRate, - uint8_t ui8PlayloadType, + uint8_t ui8PayloadType, uint8_t ui8Interleaved) { if(ui32Ssrc == 0){ ui32Ssrc = ((uint64_t)this) & 0xFFFFFFFF; @@ -74,7 +74,7 @@ public: _ui32Ssrc = ui32Ssrc; _ui32SampleRate = ui32SampleRate; _ui32MtuSize = ui32MtuSize; - _ui8PlayloadType = ui8PlayloadType; + _ui8PayloadType = ui8PayloadType; _ui8Interleaved = ui8Interleaved; } @@ -84,8 +84,8 @@ public: return _ui8Interleaved; } - int getPlayloadType() const { - return _ui8PlayloadType; + int getPayloadType() const { + return _ui8PayloadType; } int getSampleRate() const { @@ -110,7 +110,7 @@ protected: uint32_t _ui32Ssrc; uint32_t _ui32SampleRate; uint32_t _ui32MtuSize; - uint8_t _ui8PlayloadType; + uint8_t _ui8PayloadType; uint8_t _ui8Interleaved; uint16_t _ui16Sequence = 0; uint32_t _ui32TimeStamp = 0; diff --git a/src/Rtsp/Rtsp.h b/src/Rtsp/Rtsp.h index 7eb55a66..3e7a22e7 100644 --- a/src/Rtsp/Rtsp.h +++ b/src/Rtsp/Rtsp.h @@ -188,11 +188,11 @@ public: /** * 构造sdp * @param sample_rate 采样率 - * @param playload_type pt类型 + * @param payload_type pt类型 */ - Sdp(uint32_t sample_rate, uint8_t playload_type){ + Sdp(uint32_t sample_rate, uint8_t payload_type){ _sample_rate = sample_rate; - _playload_type = playload_type; + _payload_type = payload_type; } virtual ~Sdp(){} @@ -207,8 +207,8 @@ public: * 获取pt * @return */ - uint8_t getPlayloadType() const{ - return _playload_type; + uint8_t getPayloadType() const{ + return _payload_type; } /** @@ -219,7 +219,7 @@ public: return _sample_rate; } private: - uint8_t _playload_type; + uint8_t _payload_type; uint32_t _sample_rate; }; From c20a1337a525e50291f6206e78d804bc4142fefe Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 25 May 2020 14:28:02 +0800 Subject: [PATCH 049/100] =?UTF-8?q?=E5=AE=8C=E5=96=84rtp=E4=B8=A2=E5=8C=85?= =?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/Extension/H264Rtp.cpp | 2 +- src/Extension/H265Rtp.cpp | 2 +- src/Rtp/RtpProcess.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Extension/H264Rtp.cpp b/src/Extension/H264Rtp.cpp index b9789c86..481c57a7 100644 --- a/src/Extension/H264Rtp.cpp +++ b/src/Extension/H264Rtp.cpp @@ -157,7 +157,7 @@ bool H264RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { if (rtppack->sequence != _lastSeq + 1 && rtppack->sequence != 0) { //中间的或末尾的rtp包,其seq必须连续(如果回环了则判定为连续),否则说明rtp丢包,那么该帧不完整,必须得丢弃 _h264frame->_buffer.clear(); - WarnL << "rtp sequence不连续: " << rtppack->sequence << " != " << _lastSeq << " + 1,该帧被废弃"; + WarnL << "rtp丢包: " << rtppack->sequence << " != " << _lastSeq << " + 1,该帧被废弃"; return false; } diff --git a/src/Extension/H265Rtp.cpp b/src/Extension/H265Rtp.cpp index 771867ec..d1f52bba 100644 --- a/src/Extension/H265Rtp.cpp +++ b/src/Extension/H265Rtp.cpp @@ -96,7 +96,7 @@ bool H265RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) { if (rtppack->sequence != _lastSeq + 1 && rtppack->sequence != 0) { //中间的或末尾的rtp包,其seq必须连续(如果回环了则判定为连续),否则说明rtp丢包,那么该帧不完整,必须得丢弃 _h265frame->_buffer.clear(); - WarnL << "rtp sequence不连续: " << rtppack->sequence << " != " << _lastSeq << " + 1,该帧被废弃"; + WarnL << "rtp丢包: " << rtppack->sequence << " != " << _lastSeq << " + 1,该帧被废弃"; return false; } diff --git a/src/Rtp/RtpProcess.cpp b/src/Rtp/RtpProcess.cpp index a3c72a8d..d8057c2b 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -130,7 +130,7 @@ static inline bool checkTS(const uint8_t *packet, int bytes){ void RtpProcess::onRtpSorted(const RtpPacket::Ptr &rtp, int) { if(rtp->sequence != _sequence + 1 && rtp->sequence != 0){ - WarnP(this) << rtp->sequence << " != " << _sequence << "+1"; + WarnP(this) << "rtp丢包:" << rtp->sequence << " != " << _sequence << "+1" << ",公网环境下请使用tcp方式推流"; } _sequence = rtp->sequence; if(_save_file_rtp){ From 07089ea4e25c2d30f0c4658173425bf73b8b09da Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 25 May 2020 14:36:07 +0800 Subject: [PATCH 050/100] =?UTF-8?q?websocket=E5=8D=8F=E8=AE=AE=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E9=9A=8F=E6=9C=BA=E6=8E=A9=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/WebSocketSplitter.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Http/WebSocketSplitter.h b/src/Http/WebSocketSplitter.h index 05e49648..9b2bbea3 100644 --- a/src/Http/WebSocketSplitter.h +++ b/src/Http/WebSocketSplitter.h @@ -44,7 +44,12 @@ public: CONTROL_RSVF = 0xF } Type; public: - WebSocketHeader() : _mask(4){} + WebSocketHeader() : _mask(4){ + //获取_mask内部buffer的内存地址,该内存是malloc开辟的,地址为随机 + uint64_t ptr = (uint64_t)(&_mask[0]); + //根据内存地址设置掩码随机数 + _mask.assign((uint8_t*)(&ptr), (uint8_t*)(&ptr) + 4); + } virtual ~WebSocketHeader(){} public: bool _fin; From 5ae887a27994c2a118fedd8dff7213637694f53c Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 25 May 2020 14:36:58 +0800 Subject: [PATCH 051/100] =?UTF-8?q?websocket=E5=AE=A2=E6=88=B7=E7=AB=AF?= =?UTF-8?q?=E9=94=80=E6=AF=81=E6=97=B6=E4=B8=BB=E5=8A=A8=E5=8F=91=E9=80=81?= =?UTF-8?q?close=E4=BF=A1=E4=BB=A4:=20#311?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/WebSocketClient.h | 18 +++++++++++++++++- tests/test_wsClient.cpp | 9 +++++---- tests/test_wsServer.cpp | 32 +++++++++++++++++--------------- 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/Http/WebSocketClient.h b/src/Http/WebSocketClient.h index d22f7c28..2010da0f 100644 --- a/src/Http/WebSocketClient.h +++ b/src/Http/WebSocketClient.h @@ -94,6 +94,20 @@ public: _onRecv = nullptr; sendRequest(http_url,fTimeOutSec); } + + void closeWsClient(){ + if(!_onRecv){ + //未连接 + return; + } + WebSocketHeader header; + header._fin = true; + header._reserved = 0; + header._opcode = CLOSE; + //客户端需要加密 + header._mask_flag = true; + WebSocketSplitter::encode(header, nullptr); + } protected: //HttpClientImp override @@ -328,7 +342,9 @@ public: WebSocketClient(ArgsType &&...args) : ClientTypeImp(std::forward(args)...){ _wsClient.reset(new HttpWsClient(*this)); } - ~WebSocketClient() override {} + ~WebSocketClient() override { + _wsClient->closeWsClient(); + } /** * 重载startConnect方法, diff --git a/tests/test_wsClient.cpp b/tests/test_wsClient.cpp index 54100bdd..26b1970a 100644 --- a/tests/test_wsClient.cpp +++ b/tests/test_wsClient.cpp @@ -59,10 +59,11 @@ int main(int argc, char *argv[]) { Logger::Instance().add(std::make_shared()); Logger::Instance().setWriter(std::make_shared()); - WebSocketClient::Ptr client = std::make_shared >(); - client->startConnect("121.40.165.18",8800); - - sem.wait(); + { + WebSocketClient::Ptr client = std::make_shared >(); + client->startConnect("127.0.0.1", 80); + sem.wait(); + } return 0; } diff --git a/tests/test_wsServer.cpp b/tests/test_wsServer.cpp index e93ea7e7..1a342cef 100644 --- a/tests/test_wsServer.cpp +++ b/tests/test_wsServer.cpp @@ -96,26 +96,28 @@ int main(int argc, char *argv[]) { SSL_Initor::Instance().loadCertificate((exeDir() + "ssl.p12").data()); - TcpServer::Ptr httpSrv(new TcpServer()); - //http服务器,支持websocket - httpSrv->start >(80);//默认80 + { + TcpServer::Ptr httpSrv(new TcpServer()); + //http服务器,支持websocket + httpSrv->start >(80);//默认80 - TcpServer::Ptr httpsSrv(new TcpServer()); - //https服务器,支持websocket - httpsSrv->start >(443);//默认443 + TcpServer::Ptr httpsSrv(new TcpServer()); + //https服务器,支持websocket + httpsSrv->start >(443);//默认443 - TcpServer::Ptr httpSrvOld(new TcpServer()); - //兼容之前的代码(但是不支持根据url选择生成TcpSession类型) - httpSrvOld->start >(8080); + 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/,进行测试"; + DebugL << "连接 ws://127.0.0.1/xxxx,ws://127.0.0.1/ 测试的效果将不同,支持根据url选择不同的处理逻辑"; + //设置退出信号处理函数 + static semaphore sem; + signal(SIGINT, [](int) { sem.post(); });// 设置退出信号 + sem.wait(); + } - //设置退出信号处理函数 - static semaphore sem; - signal(SIGINT, [](int) { sem.post(); });// 设置退出信号 - sem.wait(); return 0; } From 68d910e2292494f2b34247d6c6b74bb4bd036376 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 25 May 2020 16:40:41 +0800 Subject: [PATCH 052/100] =?UTF-8?q?=E9=99=90=E5=88=B6rtsp/rtmp=E6=8E=A8?= =?UTF-8?q?=E6=B5=81url=E5=90=88=E6=B3=95=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtmp/RtmpSession.cpp | 6 ++++++ src/Rtsp/RtspSession.cpp | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index c2e55863..d30eafaf 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -158,6 +158,12 @@ void RtmpSession::onCmd_publish(AMFDecoder &dec) { setSocketFlags(); }; + if(_mediaInfo._app.empty() || _mediaInfo._streamid.empty()){ + //不允许莫名其妙的推流url + onRes("rtmp推流url非法", false, false, false); + return; + } + Broadcast::PublishAuthInvoker invoker = [weakSelf,onRes,pToken](const string &err,bool enableRtxp,bool enableHls,bool enableMP4){ auto strongSelf = weakSelf.lock(); if(!strongSelf){ diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index b2e8e088..c8f7e04e 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -240,6 +240,12 @@ void RtspSession::handleReq_ANNOUNCE(const Parser &parser) { _mediaInfo.parse(full_url); } + if(_mediaInfo._app.empty() || _mediaInfo._streamid.empty()){ + //推流rtsp url必须最少两级(rtsp://host/app/stream_id),不允许莫名其妙的推流url + sendRtspResponse("403 Forbidden", {"Content-Type", "text/plain"}, "rtsp推流url非法,最少确保两级rtsp url"); + throw SockException(Err_shutdown,StrPrinter << "rtsp推流url非法:" << full_url); + } + SdpParser sdpParser(parser.Content()); _strSession = makeRandStr(12); _aTrackInfo = sdpParser.getAvailableTrack(); From 20d375798679accfcd3e66b80526682430457c4b Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 25 May 2020 17:38:36 +0800 Subject: [PATCH 053/100] =?UTF-8?q?http-flv/ws-flv=E5=85=88=E8=A7=A6?= =?UTF-8?q?=E5=8F=91on=5Fplay=E5=86=8D=E8=A7=A6=E5=8F=91on=5Fstream=5Fnot?= =?UTF-8?q?=5Ffound?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/HttpSession.cpp | 88 ++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/src/Http/HttpSession.cpp b/src/Http/HttpSession.cpp index 65ed9b3a..f161342f 100644 --- a/src/Http/HttpSession.cpp +++ b/src/Http/HttpSession.cpp @@ -188,65 +188,65 @@ 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(),[weakSelf,bClose,this,cb](const MediaSource::Ptr &src){ + + //鉴权结果回调 + auto onRes = [cb, weakSelf, bClose](const string &err){ auto strongSelf = weakSelf.lock(); - if(!strongSelf){ + if (!strongSelf) { //本对象已经销毁 return; } - auto rtmp_src = dynamic_pointer_cast(src); - if(!rtmp_src){ - //未找到该流 - sendNotFound(bClose); + + if(!err.empty()){ + //播放鉴权失败 + strongSelf->sendResponse("401 Unauthorized", bClose, nullptr, KeyValue(), std::make_shared(err)); return; } - //找到流了 - auto onRes = [this,rtmp_src,cb](const string &err){ - bool authSuccess = err.empty(); - if(!authSuccess){ - sendResponse("401 Unauthorized", true, nullptr, KeyValue(), std::make_shared(err)); - return ; + + //异步查找rtmp流 + MediaSource::findAsync(strongSelf->_mediaInfo, strongSelf, [weakSelf, bClose, cb](const MediaSource::Ptr &src) { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { + //本对象已经销毁 + return; + } + auto rtmp_src = dynamic_pointer_cast(src); + if (!rtmp_src) { + //未找到该流 + strongSelf->sendNotFound(bClose); + return; } - if(!cb) { + if (!cb) { //找到rtmp源,发送http头,负载后续发送 - sendResponse("200 OK", false, "video/x-flv",KeyValue(),nullptr,true); - }else{ + strongSelf->sendResponse("200 OK", false, "video/x-flv", KeyValue(), nullptr, true); + } else { + //自定义发送http头 cb(); } //http-flv直播牺牲延时提升发送性能 - setSocketFlags(); + strongSelf->setSocketFlags(); + strongSelf->start(strongSelf->getPoller(), rtmp_src); + strongSelf->_is_flv_stream = true; + }); + }; - try{ - start(getPoller(),rtmp_src); - _is_flv_stream = true; - }catch (std::exception &ex){ - //该rtmp源不存在 - shutdown(SockException(Err_shutdown,"rtmp mediasource released")); - } - }; - - weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - Broadcast::AuthInvoker invoker = [weakSelf,onRes](const string &err){ - auto strongSelf = weakSelf.lock(); - if(!strongSelf){ - return; - } - strongSelf->async([weakSelf,onRes,err](){ - auto strongSelf = weakSelf.lock(); - if(!strongSelf){ - return; - } - onRes(err); - }); - }; - auto flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed,_mediaInfo,invoker,static_cast(*this)); - if(!flag){ - //该事件无人监听,默认不鉴权 - onRes(""); + Broadcast::AuthInvoker invoker = [weakSelf, onRes](const string &err) { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { + return; } - }); + strongSelf->async([onRes, err]() { + onRes(err); + }); + }; + + auto flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed, _mediaInfo, invoker, static_cast(*this)); + if (!flag) { + //该事件无人监听,默认不鉴权 + onRes(""); + } return true; } From 8d1801339c38429481f615361900cfcf812fb84a Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 25 May 2020 18:22:21 +0800 Subject: [PATCH 054/100] =?UTF-8?q?=E4=BC=98=E5=8C=96rtsp=E9=89=B4?= =?UTF-8?q?=E6=9D=83=E4=BA=8B=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/config.ini | 3 +- src/Rtsp/RtspSession.cpp | 226 ++++++++++++++++++++------------------- src/Rtsp/RtspSession.h | 8 +- 3 files changed, 123 insertions(+), 114 deletions(-) diff --git a/conf/config.ini b/conf/config.ini index 78abb92d..e4ba3cc1 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -85,8 +85,9 @@ on_publish=https://127.0.0.1/index/hook/on_publish on_record_mp4=https://127.0.0.1/index/hook/on_record_mp4 #rtsp播放鉴权事件,此事件中比对rtsp的用户名密码 on_rtsp_auth=https://127.0.0.1/index/hook/on_rtsp_auth -#rtsp播放是否开启鉴权事件,置空则关闭rtsp鉴权。rtsp播放鉴权还支持url方式鉴权 +#rtsp播放是否开启专属鉴权事件,置空则关闭rtsp鉴权。rtsp播放鉴权还支持url方式鉴权 #建议开发者统一采用url参数方式鉴权,rtsp用户名密码鉴权一般在设备上用的比较多 +#开启rtsp专属鉴权后,将不再触发on_play鉴权事件 on_rtsp_realm=https://127.0.0.1/index/hook/on_rtsp_realm #远程telnet调试鉴权事件 on_shell_login=https://127.0.0.1/index/hook/on_shell_login diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index c8f7e04e..57c1647b 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -318,38 +318,80 @@ void RtspSession::handleReq_RECORD(const Parser &parser){ } } -void RtspSession::handleReq_Describe(const Parser &parser) { +void RtspSession::emitOnPlay(){ weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); + //url鉴权回调 + auto onRes = [weakSelf](const string &err) { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { + return; + } + if (!err.empty()) { + //播放url鉴权失败 + strongSelf->sendRtspResponse("401 Unauthorized", {"Content-Type", "text/plain"}, err); + strongSelf->shutdown(SockException(Err_shutdown, StrPrinter << "401 Unauthorized:" << err)); + return; + } + strongSelf->onAuthSuccess(); + }; + + Broadcast::AuthInvoker invoker = [weakSelf, onRes](const string &err) { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { + return; + } + strongSelf->async([onRes, err, weakSelf]() { + onRes(err); + }); + }; + + //广播通用播放url鉴权事件 + auto flag = _emit_on_play ? false : NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed, _mediaInfo, invoker, static_cast(*this)); + if (!flag) { + //该事件无人监听,默认不鉴权 + onRes(""); + } + //已经鉴权过了 + _emit_on_play = true; +} + +void RtspSession::handleReq_Describe(const Parser &parser) { //该请求中的认证信息 auto authorization = parser["Authorization"]; - onGetRealm invoker = [weakSelf,authorization](const string &realm){ + weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); + //rtsp专属鉴权是否开启事件回调 + onGetRealm invoker = [weakSelf, authorization](const string &realm) { auto strongSelf = weakSelf.lock(); - if(!strongSelf){ + if (!strongSelf) { //本对象已经销毁 return; } //切换到自己的线程然后执行 - strongSelf->async([weakSelf,realm,authorization](){ + strongSelf->async([weakSelf, realm, authorization]() { auto strongSelf = weakSelf.lock(); - if(!strongSelf){ + if (!strongSelf) { //本对象已经销毁 return; } - if(realm.empty()){ - //无需认证,回复sdp - strongSelf->onAuthSuccess(); + if (realm.empty()) { + //无需rtsp专属认证, 那么继续url通用鉴权认证(on_play) + strongSelf->emitOnPlay(); return; } - //该流需要认证 - strongSelf->onAuthUser(realm,authorization); + //该流需要rtsp专属认证,开启rtsp专属认证后,将不再触发url通用鉴权认证(on_play) + strongSelf->_rtsp_realm = realm; + strongSelf->onAuthUser(realm, authorization); }); - }; - //广播是否需要认证事件 - if(!NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastOnGetRtspRealm,_mediaInfo,invoker,static_cast(*this))){ - //无人监听此事件,说明无需认证 - invoker(""); + if(_rtsp_realm.empty()){ + //广播是否需要rtsp专属认证事件 + if (!NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastOnGetRtspRealm, _mediaInfo, invoker, static_cast(*this))) { + //无人监听此事件,说明无需认证 + invoker(""); + } + }else{ + invoker(_rtsp_realm); } } void RtspSession::onAuthSuccess() { @@ -725,113 +767,75 @@ void RtspSession::handleReq_Play(const Parser &parser) { send_SessionNotFound(); throw SockException(Err_shutdown,_aTrackInfo.empty() ? "can not find any availabe track when play" : "session not found when play"); } + auto pMediaSrc = _pMediaSrc.lock(); + if(!pMediaSrc){ + send_StreamNotFound(); + shutdown(SockException(Err_shutdown,"rtsp stream released")); + return; + } + + bool useBuf = true; + _enableSendRtp = false; + float iStartTime = 0; auto strRange = parser["Range"]; - auto onRes = [this,strRange](const string &err){ - bool authSuccess = err.empty(); - if(!authSuccess){ - //第一次play是播放,否则是恢复播放。只对播放鉴权 - sendRtspResponse("401 Unauthorized", {"Content-Type", "text/plain"}, err); - shutdown(SockException(Err_shutdown,StrPrinter << "401 Unauthorized:" << err)); + if (strRange.size()) { + //这个是seek操作 + auto strStart = FindField(strRange.data(), "npt=", "-"); + if (strStart == "now") { + strStart = "0"; + } + iStartTime = 1000 * atof(strStart.data()); + InfoP(this) << "rtsp seekTo(ms):" << iStartTime; + useBuf = !pMediaSrc->seekTo(iStartTime); + } else if (pMediaSrc->totalReaderCount() == 0) { + //第一个消费者 + pMediaSrc->seekTo(0); + } + + _StrPrinter rtp_info; + for (auto &track : _aTrackInfo) { + if (track->_inited == false) { + //还有track没有setup + shutdown(SockException(Err_shutdown, "track not setuped")); return; } + track->_ssrc = pMediaSrc->getSsrc(track->_type); + track->_seq = pMediaSrc->getSeqence(track->_type); + track->_time_stamp = pMediaSrc->getTimeStamp(track->_type); - auto pMediaSrc = _pMediaSrc.lock(); - if(!pMediaSrc){ - send_StreamNotFound(); - shutdown(SockException(Err_shutdown,"rtsp stream released")); - return; - } + rtp_info << "url=" << _strContentBase << "/" << track->_control_surffix << ";" + << "seq=" << track->_seq << ";" + << "rtptime=" << (int) (track->_time_stamp * (track->_samplerate / 1000)) << ","; + } - bool useBuf = true; - _enableSendRtp = false; - float iStartTime = 0; - if (strRange.size() && !_bFirstPlay) { - //这个是seek操作 - auto strStart = FindField(strRange.data(), "npt=", "-"); - if (strStart == "now") { - strStart = "0"; - } - iStartTime = 1000 * atof(strStart.data()); - InfoP(this) << "rtsp seekTo(ms):" << iStartTime; - useBuf = !pMediaSrc->seekTo(iStartTime); - }else if(pMediaSrc->totalReaderCount() == 0){ - //第一个消费者 - pMediaSrc->seekTo(0); - } - _bFirstPlay = false; + rtp_info.pop_back(); + sendRtspResponse("200 OK", + {"Range", StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << (useBuf? pMediaSrc->getTimeStamp(TrackInvalid) / 1000.0 : iStartTime / 1000), + "RTP-Info",rtp_info + }); - _StrPrinter rtp_info; - for(auto &track : _aTrackInfo){ - if (track->_inited == false) { - //还有track没有setup - shutdown(SockException(Err_shutdown,"track not setuped")); - return; - } - track->_ssrc = pMediaSrc->getSsrc(track->_type); - track->_seq = pMediaSrc->getSeqence(track->_type); - track->_time_stamp = pMediaSrc->getTimeStamp(track->_type); + _enableSendRtp = true; + setSocketFlags(); - rtp_info << "url=" << _strContentBase << "/" << track->_control_surffix << ";" - << "seq=" << track->_seq << ";" - << "rtptime=" << (int)(track->_time_stamp * (track->_samplerate / 1000)) << ","; - } - - rtp_info.pop_back(); - - sendRtspResponse("200 OK", - {"Range", StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << (useBuf? pMediaSrc->getTimeStamp(TrackInvalid) / 1000.0 : iStartTime / 1000), - "RTP-Info",rtp_info - }); - - _enableSendRtp = true; - setSocketFlags(); - - if (!_pRtpReader && _rtpType != Rtsp::RTP_MULTICAST) { - weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - _pRtpReader = pMediaSrc->getRing()->attach(getPoller(),useBuf); - _pRtpReader->setDetachCB([weakSelf]() { - auto strongSelf = weakSelf.lock(); - if(!strongSelf) { - return; - } - strongSelf->shutdown(SockException(Err_shutdown,"rtsp ring buffer detached")); - }); - _pRtpReader->setReadCB([weakSelf](const RtspMediaSource::RingDataType &pack) { - auto strongSelf = weakSelf.lock(); - if(!strongSelf) { - return; - } - if(strongSelf->_enableSendRtp) { - strongSelf->sendRtpPacket(pack); - } - }); - } - }; - - weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); - Broadcast::AuthInvoker invoker = [weakSelf,onRes](const string &err){ - auto strongSelf = weakSelf.lock(); - if(!strongSelf){ - return; - } - strongSelf->async([weakSelf,onRes,err](){ + if (!_pRtpReader && _rtpType != Rtsp::RTP_MULTICAST) { + weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); + _pRtpReader = pMediaSrc->getRing()->attach(getPoller(), useBuf); + _pRtpReader->setDetachCB([weakSelf]() { auto strongSelf = weakSelf.lock(); - if(!strongSelf){ + if (!strongSelf) { return; } - onRes(err); + strongSelf->shutdown(SockException(Err_shutdown, "rtsp ring buffer detached")); + }); + _pRtpReader->setReadCB([weakSelf](const RtspMediaSource::RingDataType &pack) { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { + return; + } + if (strongSelf->_enableSendRtp) { + strongSelf->sendRtpPacket(pack); + } }); - }; - if(_bFirstPlay){ - //第一次收到play命令,需要鉴权 - auto flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed,_mediaInfo,invoker,static_cast(*this)); - if(!flag){ - //该事件无人监听,默认不鉴权 - onRes(""); - } - }else{ - //后面是seek或恢复命令,不需要鉴权 - onRes(""); } } diff --git a/src/Rtsp/RtspSession.h b/src/Rtsp/RtspSession.h index eadafdff..c7e3160c 100644 --- a/src/Rtsp/RtspSession.h +++ b/src/Rtsp/RtspSession.h @@ -160,6 +160,8 @@ private: void onAuthBasic(const string &realm,const string &strBase64); //校验md5方式的认证加密 void onAuthDigest(const string &realm,const string &strMd5); + //触发url鉴权事件 + void emitOnPlay(); //发送rtp给客户端 void sendRtpPacket(const RtspMediaSource::RingDataType &pkt); @@ -179,8 +181,10 @@ private: string _strContentBase; //Session号 string _strSession; - //是否第一次播放,第一次播放需要鉴权,第二次播放属于暂停恢复 - bool _bFirstPlay = true; + //记录是否需要rtsp专属鉴权,防止重复触发事件 + string _rtsp_realm; + //是否已经触发on_play事件 + bool _emit_on_play = false; //url解析后保存的相关信息 MediaInfo _mediaInfo; //rtsp播放器绑定的直播源 From 0626f9a2bfd88736f3b12e9118601f76a9614fd0 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 25 May 2020 18:33:42 +0800 Subject: [PATCH 055/100] =?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 --- src/Rtsp/RtspSession.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index 57c1647b..45308943 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -765,7 +765,7 @@ void RtspSession::handleReq_Setup(const Parser &parser) { void RtspSession::handleReq_Play(const Parser &parser) { if (_aTrackInfo.empty() || parser["Session"] != _strSession) { send_SessionNotFound(); - throw SockException(Err_shutdown,_aTrackInfo.empty() ? "can not find any availabe track when play" : "session not found when play"); + throw SockException(Err_shutdown,_aTrackInfo.empty() ? "can not find any available track when play" : "session not found when play"); } auto pMediaSrc = _pMediaSrc.lock(); if(!pMediaSrc){ From e063cb3a003b898d3588f2164f9a32a13123226d Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 25 May 2020 21:54:43 +0800 Subject: [PATCH 056/100] =?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=84rtsp=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtsp/RtspSplitter.cpp | 19 +++++++++++++++++++ src/Rtsp/RtspSplitter.h | 1 + 2 files changed, 20 insertions(+) diff --git a/src/Rtsp/RtspSplitter.cpp b/src/Rtsp/RtspSplitter.cpp index caaf1054..1c97f369 100644 --- a/src/Rtsp/RtspSplitter.cpp +++ b/src/Rtsp/RtspSplitter.cpp @@ -10,10 +10,29 @@ #include #include "RtspSplitter.h" +#include "Util/logger.h" +#include "Util/util.h" namespace mediakit{ const char *RtspSplitter::onSearchPacketTail(const char *data, int len) { + auto ret = onSearchPacketTail_l(data, len); + if(ret){ + return ret; + } + + if (len > 256 * 1024) { + //rtp大于256KB + ret = (char *) memchr(data, '$', len); + if (!ret) { + WarnL << "rtp缓存溢出:" << hexdump(data, 1024); + reset(); + } + } + return ret; +} + +const char *RtspSplitter::onSearchPacketTail_l(const char *data, int len) { if(!_enableRecvRtp || data[0] != '$'){ //这是rtsp包 _isRtpPacket = false; diff --git a/src/Rtsp/RtspSplitter.h b/src/Rtsp/RtspSplitter.h index c319410c..3cd35821 100644 --- a/src/Rtsp/RtspSplitter.h +++ b/src/Rtsp/RtspSplitter.h @@ -48,6 +48,7 @@ protected: virtual int64_t getContentLength(Parser &parser); protected: const char *onSearchPacketTail(const char *data,int len) override ; + const char *onSearchPacketTail_l(const char *data,int len) ; int64_t onRecvHeader(const char *data,uint64_t len) override; void onRecvContent(const char *data,uint64_t len) override; private: From eddb5adcb2a6c9039e77aeac5e1fc2342a2d0c7a Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 26 May 2020 09:18:34 +0800 Subject: [PATCH 057/100] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E9=94=99=E8=AF=AF=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conf/config.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/config.ini b/conf/config.ini index e4ba3cc1..981b458e 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -4,7 +4,7 @@ apiDebug=1 #一些比较敏感的http api在访问时需要提供secret,否则无权限调用 #如果是通过127.0.0.1访问,那么可以不提供secret secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc -#截图保存路径根目录,截图通过http api(/index/api/makeSnap)生成和获取 +#截图保存路径根目录,截图通过http api(/index/api/getSnap)生成和获取 snapRoot=./www/snap/ [ffmpeg] From ff7914e441f5d7093ee348c85f7556999027d942 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 26 May 2020 10:11:58 +0800 Subject: [PATCH 058/100] =?UTF-8?q?=E4=BF=AE=E6=94=B9MediaSource=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 --- server/FFmpegSource.cpp | 3 +-- server/WebApi.cpp | 4 ++-- src/Common/MediaSource.cpp | 17 +++++++++-------- src/Common/MediaSource.h | 9 +++++++-- src/Record/Recorder.cpp | 4 ++-- src/Rtmp/FlvMuxer.cpp | 2 +- src/Rtmp/RtmpSession.cpp | 3 +-- src/Rtsp/RtpMultiCaster.cpp | 1 + src/Rtsp/RtspSession.cpp | 3 +-- 9 files changed, 25 insertions(+), 21 deletions(-) diff --git a/server/FFmpegSource.cpp b/server/FFmpegSource.cpp index ec9e918d..95f079c7 100644 --- a/server/FFmpegSource.cpp +++ b/server/FFmpegSource.cpp @@ -118,8 +118,7 @@ void FFmpegSource::findAsync(int maxWaitMS, const function &session, bool retry, - const function &cb){ - auto src = MediaSource::find(info._schema, info._vhost, info._app, info._streamid, true); +void MediaSource::findAsync_l(const MediaInfo &info, const std::shared_ptr &session, bool retry, const function &cb){ + auto src = MediaSource::find_l(info._schema, info._vhost, info._app, info._streamid, true); if(src || !retry){ cb(src); return; @@ -248,7 +247,11 @@ void MediaSource::findAsync(const MediaInfo &info, const std::shared_ptr weakSender = sender.shared_from_this(); - _async_close_timer = std::make_shared(close_delay, [weakSender,is_mp4_vod]() { + _async_close_timer = std::make_shared(stream_none_reader_delay / 1000.0, [weakSender,is_mp4_vod]() { auto strongSender = weakSender.lock(); if (!strongSender) { //对象已经销毁 @@ -467,7 +468,7 @@ MediaSource::Ptr MediaSource::createFromMP4(const string &schema, const string & try { MP4Reader::Ptr pReader(new MP4Reader(vhost, app, stream, filePath)); pReader->startReadMP4(); - return MediaSource::find(schema, vhost, app, stream, false); + return MediaSource::find(schema, vhost, app, stream); } catch (std::exception &ex) { WarnL << ex.what(); return nullptr; diff --git a/src/Common/MediaSource.h b/src/Common/MediaSource.h index ab0a0cfa..299cf709 100644 --- a/src/Common/MediaSource.h +++ b/src/Common/MediaSource.h @@ -134,7 +134,7 @@ public: virtual bool isRecording(Recorder::type type); // 同步查找流 - static Ptr find(const string &schema, const string &vhost, const string &app, const string &id, bool bMake = true) ; + static Ptr find(const string &schema, const string &vhost, const string &app, const string &id); // 异步查找流 static void findAsync(const MediaInfo &info, const std::shared_ptr &session, const function &cb); // 遍历所有流 @@ -142,9 +142,14 @@ public: // 从mp4文件生成MediaSource static MediaSource::Ptr createFromMP4(const string &schema, const string &vhost, const string &app, const string &stream, const string &filePath = "", bool checkApp = true); + protected: void regist() ; - bool unregist() ; + bool unregist(); + +private: + static Ptr find_l(const string &schema, const string &vhost, const string &app, const string &id, bool bMake); + static void findAsync_l(const MediaInfo &info, const std::shared_ptr &session, bool retry, const function &cb); private: string _strSchema; string _strVhost; diff --git a/src/Record/Recorder.cpp b/src/Record/Recorder.cpp index 2ab341b7..2f0314cb 100644 --- a/src/Record/Recorder.cpp +++ b/src/Record/Recorder.cpp @@ -80,11 +80,11 @@ std::shared_ptr Recorder::createRecorder(type type, const st } static MediaSource::Ptr getMediaSource(const string &vhost, const string &app, const string &stream_id){ - auto src = MediaSource::find(RTMP_SCHEMA, vhost, app, stream_id, false); + auto src = MediaSource::find(RTMP_SCHEMA, vhost, app, stream_id); if(src){ return src; } - return MediaSource::find(RTSP_SCHEMA, vhost, app, stream_id, false); + return MediaSource::find(RTSP_SCHEMA, vhost, app, stream_id); } bool Recorder::isRecording(type type, const string &vhost, const string &app, const string &stream_id){ diff --git a/src/Rtmp/FlvMuxer.cpp b/src/Rtmp/FlvMuxer.cpp index bc29cf31..35564867 100644 --- a/src/Rtmp/FlvMuxer.cpp +++ b/src/Rtmp/FlvMuxer.cpp @@ -167,7 +167,7 @@ void FlvMuxer::stop() { ///////////////////////////////////////////////////////FlvRecorder///////////////////////////////////////////////////// void FlvRecorder::startRecord(const EventPoller::Ptr &poller,const string &vhost, const string &app, const string &stream,const string &file_path) { - startRecord(poller,dynamic_pointer_cast(MediaSource::find(RTMP_SCHEMA,vhost,app,stream,false)),file_path); + startRecord(poller,dynamic_pointer_cast(MediaSource::find(RTMP_SCHEMA,vhost,app,stream)),file_path); } void FlvRecorder::startRecord(const EventPoller::Ptr &poller,const RtmpMediaSource::Ptr &media, const string &file_path) { diff --git a/src/Rtmp/RtmpSession.cpp b/src/Rtmp/RtmpSession.cpp index d30eafaf..95076682 100644 --- a/src/Rtmp/RtmpSession.cpp +++ b/src/Rtmp/RtmpSession.cpp @@ -130,8 +130,7 @@ void RtmpSession::onCmd_publish(AMFDecoder &dec) { auto src = dynamic_pointer_cast(MediaSource::find(RTMP_SCHEMA, _mediaInfo._vhost, _mediaInfo._app, - _mediaInfo._streamid, - false)); + _mediaInfo._streamid)); bool authSuccess = err.empty(); bool ok = (!src && !_pPublisherSrc && authSuccess); AMFValue status(AMF_OBJECT); diff --git a/src/Rtsp/RtpMultiCaster.cpp b/src/Rtsp/RtpMultiCaster.cpp index b341a0fb..cf3a12b0 100644 --- a/src/Rtsp/RtpMultiCaster.cpp +++ b/src/Rtsp/RtpMultiCaster.cpp @@ -81,6 +81,7 @@ RtpMultiCaster::~RtpMultiCaster() { _pReader->setDetachCB(nullptr); DebugL; } + RtpMultiCaster::RtpMultiCaster(const EventPoller::Ptr &poller,const string &strLocalIp,const string &strVhost,const string &strApp,const string &strStream) { auto src = dynamic_pointer_cast(MediaSource::find(RTSP_SCHEMA,strVhost,strApp, strStream)); if(!src){ diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index 45308943..d73b11f1 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -221,8 +221,7 @@ void RtspSession::handleReq_ANNOUNCE(const Parser &parser) { auto src = dynamic_pointer_cast(MediaSource::find(RTSP_SCHEMA, _mediaInfo._vhost, _mediaInfo._app, - _mediaInfo._streamid, - false)); + _mediaInfo._streamid)); if(src){ sendRtspResponse("406 Not Acceptable", {"Content-Type", "text/plain"}, "Already publishing."); string err = StrPrinter << "ANNOUNCE:" From 876111698e2f181cf111c6d72ea82038e1199f57 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 26 May 2020 10:30:39 +0800 Subject: [PATCH 059/100] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E6=88=B3=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/Stamp.cpp | 20 +++++++++++++------- src/Common/Stamp.h | 1 + 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Common/Stamp.cpp b/src/Common/Stamp.cpp index 878fd183..5201fb21 100644 --- a/src/Common/Stamp.cpp +++ b/src/Common/Stamp.cpp @@ -44,12 +44,15 @@ void Stamp::setPlayBack(bool playback) { void Stamp::syncTo(Stamp &other){ _sync_master = &other; + _sync_finished = false; } void Stamp::revise(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out,bool modifyStamp) { revise_l(dts,pts,dts_out,pts_out,modifyStamp); - if(modifyStamp || _playback){ - //自动生成时间戳或回放,不需要做音视频同步 + if(_sync_finished || modifyStamp || _playback){ + //自动生成时间戳或回放或同步完毕 + if(dts_out < 0) { dts_out = 0; } + if(pts_out < 0) { pts_out = 0; } return; } @@ -65,11 +68,18 @@ void Stamp::revise(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_out, _sync_master = nullptr; } - if(dts_out < 0 || dts_out < _last_relativeStamp){ + if (dts_out < 0 || dts_out < _last_relativeStamp) { //相对时间戳小于0,或者小于上次的时间戳, //那么说明是同步时间戳导致的,在这个过渡期内,我们一直返回上次的结果(目的是为了防止时间戳回退) pts_out = _last_relativeStamp + (pts_out - dts_out); dts_out = _last_relativeStamp; + } else if(!_sync_master){ + //音视频同步过渡期完毕 + _sync_finished = true; + } + + if(pts_out < 0){ + pts_out = dts_out; } } @@ -110,10 +120,6 @@ void Stamp::revise_l(int64_t dts, int64_t pts, int64_t &dts_out, int64_t &pts_ou } pts_out = dts_out + pts_dts_diff; - if(pts_out < 0){ - //时间戳不能小于0 - pts_out = 0; - } } void Stamp::setRelativeStamp(int64_t relativeStamp) { diff --git a/src/Common/Stamp.h b/src/Common/Stamp.h index 2e497177..cf374277 100644 --- a/src/Common/Stamp.h +++ b/src/Common/Stamp.h @@ -83,6 +83,7 @@ private: SmoothTicker _ticker; bool _playback = false; Stamp *_sync_master = nullptr; + bool _sync_finished = true; }; //dts生成器, From 7d8dbb2a4b6f2b3d903778bdb9e42f333f40a974 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 26 May 2020 12:11:44 +0800 Subject: [PATCH 060/100] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dwebsocket=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=AB=AF=E7=9B=B8=E5=85=B3bug:#311?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/WebSocketClient.h | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/Http/WebSocketClient.h b/src/Http/WebSocketClient.h index 2010da0f..93f140be 100644 --- a/src/Http/WebSocketClient.h +++ b/src/Http/WebSocketClient.h @@ -124,7 +124,8 @@ protected: if(Sec_WebSocket_Accept == const_cast(headers)["Sec-WebSocket-Accept"]){ //success onWebSocketException(SockException()); - return 0; + //后续全是websocket负载数据 + return -1; } shutdown(SockException(Err_shutdown,StrPrinter << "Sec-WebSocket-Accept mismatch")); return 0; @@ -139,6 +140,16 @@ protected: */ void onResponseCompleted() override {} + /** + * 接收websocket负载数据 + */ + void onResponseBody(const char *buf,int64_t size,int64_t recvedSize,int64_t totalSize) override{ + if(_onRecv){ + //完成websocket握手后,拦截websocket数据并解析 + _onRecv(buf, size); + } + }; + //TcpClient override /** @@ -181,20 +192,6 @@ protected: HttpClientImp::onConnect(ex); } - /** - * tcp收到数据 - * @param pBuf - */ - void onRecv(const Buffer::Ptr &pBuf) override{ - if(_onRecv){ - //完成websocket握手后,拦截websocket数据并解析 - _onRecv(pBuf); - }else{ - //websocket握手数据 - HttpClientImp::onRecv(pBuf); - } - } - /** * tcp连接断开 * @param ex @@ -299,9 +296,9 @@ private: //触发连接成功事件 _delegate.onConnect(ex); //拦截websocket数据接收 - _onRecv = [this](const Buffer::Ptr &pBuf){ + _onRecv = [this](const char *data, int len){ //解析websocket数据包 - this->WebSocketSplitter::decode((uint8_t*)pBuf->data(),pBuf->size()); + this->WebSocketSplitter::decode((uint8_t *)data, len); }; return; } @@ -320,7 +317,7 @@ private: private: string _Sec_WebSocket_Key; - function _onRecv; + function _onRecv; ClientTypeImp &_delegate; string _payload; }; From 7b90daab24038c89f3bdf59ac7a0e9cfae8c2bc6 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 26 May 2020 17:00:51 +0800 Subject: [PATCH 061/100] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dh264/h265=20prefixSiz?= =?UTF-8?q?e=E8=AE=A1=E7=AE=97=E9=94=99=E8=AF=AF=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/Device.cpp | 26 ++++---------------------- src/Extension/H264.cpp | 15 +++++++++++++++ src/Extension/H264.h | 2 +- src/Rtp/Decoder.cpp | 4 ++-- 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/Common/Device.cpp b/src/Common/Device.cpp index 71f42b39..37a0056b 100644 --- a/src/Common/Device.cpp +++ b/src/Common/Device.cpp @@ -78,14 +78,6 @@ void DevChannel::inputH264(const char *data, int len, uint32_t dts, uint32_t pts if(pts == 0){ pts = dts; } - int prefixeSize; - if (memcmp("\x00\x00\x00\x01", data, 4) == 0) { - prefixeSize = 4; - } else if (memcmp("\x00\x00\x01", data, 3) == 0) { - prefixeSize = 3; - } else { - prefixeSize = 0; - } //由于rtmp/hls/mp4需要缓存时间戳相同的帧, //所以使用FrameNoCacheAble类型的帧反而会在转换成FrameCacheAble时多次内存拷贝 @@ -93,9 +85,8 @@ void DevChannel::inputH264(const char *data, int len, uint32_t dts, uint32_t pts H264Frame::Ptr frame = std::make_shared(); frame->_dts = dts; frame->_pts = pts; - frame->_buffer.assign("\x00\x00\x00\x01",4); - frame->_buffer.append(data + prefixeSize, len - prefixeSize); - frame->_prefix_size = 4; + frame->_buffer.assign(data, len); + frame->_prefix_size = prefixSize(data,len); inputFrame(frame); } @@ -106,14 +97,6 @@ void DevChannel::inputH265(const char *data, int len, uint32_t dts, uint32_t pts if(pts == 0){ pts = dts; } - int prefixeSize; - if (memcmp("\x00\x00\x00\x01", data, 4) == 0) { - prefixeSize = 4; - } else if (memcmp("\x00\x00\x01", data, 3) == 0) { - prefixeSize = 3; - } else { - prefixeSize = 0; - } //由于rtmp/hls/mp4需要缓存时间戳相同的帧, //所以使用FrameNoCacheAble类型的帧反而会在转换成FrameCacheAble时多次内存拷贝 @@ -121,9 +104,8 @@ void DevChannel::inputH265(const char *data, int len, uint32_t dts, uint32_t pts H265Frame::Ptr frame = std::make_shared(); frame->_dts = dts; frame->_pts = pts; - frame->_buffer.assign("\x00\x00\x00\x01",4); - frame->_buffer.append(data + prefixeSize, len - prefixeSize); - frame->_prefix_size = 4; + frame->_buffer.assign(data, len); + frame->_prefix_size = prefixSize(data,len); inputFrame(frame); } diff --git a/src/Extension/H264.cpp b/src/Extension/H264.cpp index 0a5314ae..0d591ea4 100644 --- a/src/Extension/H264.cpp +++ b/src/Extension/H264.cpp @@ -74,6 +74,21 @@ void splitH264(const char *ptr, int len, int prefix, const std::function &cb); - +int prefixSize(const char *ptr, int len); /** * 264帧类 */ diff --git a/src/Rtp/Decoder.cpp b/src/Rtp/Decoder.cpp index 5c8a7662..e177ed8e 100644 --- a/src/Rtp/Decoder.cpp +++ b/src/Rtp/Decoder.cpp @@ -129,7 +129,7 @@ void DecoderImp::onDecode(int stream,int codecid,int flags,int64_t pts,int64_t d 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) { - onFrame(std::make_shared(buffer->data(), buffer->size(), dts, pts,4)); + onFrame(std::make_shared(buffer->data(), buffer->size(), dts, pts, prefixSize(buffer->data(), buffer->size()))); }); break; } @@ -148,7 +148,7 @@ void DecoderImp::onDecode(int stream,int codecid,int flags,int64_t pts,int64_t d } 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) { - onFrame(std::make_shared(buffer->data(), buffer->size(), dts, pts, 4)); + onFrame(std::make_shared(buffer->data(), buffer->size(), dts, pts, prefixSize(buffer->data(), buffer->size()))); }); break; } From 53ca43fb7d32ea868fbbec898779336c992b9b00 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 26 May 2020 21:04:53 +0800 Subject: [PATCH 062/100] =?UTF-8?q?=E6=8F=90=E9=AB=98=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Extension/H264.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Extension/H264.cpp b/src/Extension/H264.cpp index 0d591ea4..910e79e2 100644 --- a/src/Extension/H264.cpp +++ b/src/Extension/H264.cpp @@ -79,13 +79,20 @@ int prefixSize(const char *ptr, int len){ return 0; } - if (memcmp("\x00\x00\x01", ptr, 3) == 0) { - return 3; + if (ptr[0] != 0x00 || ptr[1] != 0x00) { + //不是0x00 00开头 + return 0; } - if (memcmp("\x00\x00\x00\x01", ptr, 4) == 0) { + if (ptr[2] == 0x00 && ptr[3] == 0x01) { + //是0x00 00 00 01 return 4; } + + if (ptr[2] == 0x01) { + //是0x00 00 01 + return 3; + } return 0; } From a7bcfd566b809f9887be6ee9ab6c4eb032f5e3ee Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 26 May 2020 23:19:13 +0800 Subject: [PATCH 063/100] =?UTF-8?q?=E5=B9=B3=E6=BB=91=E5=8A=A8=E6=92=AD?= =?UTF-8?q?=E6=94=BEhls?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Http/HlsPlayer.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++ src/Http/HlsPlayer.h | 7 ++++++ 2 files changed, 57 insertions(+) diff --git a/src/Http/HlsPlayer.cpp b/src/Http/HlsPlayer.cpp index 0e5b55c9..519297b6 100644 --- a/src/Http/HlsPlayer.cpp +++ b/src/Http/HlsPlayer.cpp @@ -256,12 +256,62 @@ void HlsPlayerImp::onAllTrackReady() { void HlsPlayerImp::onPlayResult(const SockException &ex) { if(ex){ PlayerImp::onPlayResult(ex); + }else{ + _stamp[TrackAudio].syncTo(_stamp[TrackVideo]); + _ticker.resetTime(); + weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); + //每50毫秒执行一次 + _timer = std::make_shared(0.05, [weakSelf]() { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { + return false; + } + strongSelf->onTick(); + return true; + }, getPoller()); } } +void HlsPlayerImp::onShutdown(const SockException &ex) { + PlayerImp::onShutdown(ex); + _timer = nullptr; +} + vector HlsPlayerImp::getTracks(bool trackReady) const { return MediaSink::getTracks(trackReady); } +void HlsPlayerImp::inputFrame(const Frame::Ptr &frame) { + //计算相对时间戳 + int64_t dts, pts; + _stamp[frame->getTrackType()].revise(frame->dts(), frame->pts(), dts, pts); + //根据时间戳缓存frame + _frame_cache.emplace(dts, Frame::getCacheAbleFrame(frame)); + + while (!_frame_cache.empty()) { + if (_frame_cache.rbegin()->first - _frame_cache.begin()->first > 30 * 1000) { + //缓存超过30秒,强制消费掉 + MediaSink::inputFrame(_frame_cache.begin()->second); + _frame_cache.erase(_frame_cache.begin()); + continue; + } + //缓存小于30秒 + break; + } +} + +void HlsPlayerImp::onTick() { + auto it = _frame_cache.begin(); + while (it != _frame_cache.end()) { + if (it->first > _ticker.elapsedTime()) { + //这些帧还未到时间播放 + break; + } + //消费掉已经到期的帧 + MediaSink::inputFrame(it->second); + it = _frame_cache.erase(it); + } +} + }//namespace mediakit \ No newline at end of file diff --git a/src/Http/HlsPlayer.h b/src/Http/HlsPlayer.h index bfeed863..bda64a77 100644 --- a/src/Http/HlsPlayer.h +++ b/src/Http/HlsPlayer.h @@ -135,9 +135,16 @@ private: void onAllTrackReady() override; void onPlayResult(const SockException &ex) override; vector getTracks(bool trackReady = true) const override; + void inputFrame(const Frame::Ptr &frame) override; + void onShutdown(const SockException &ex) override; + void onTick(); private: TSSegment::onSegment _on_ts; DecoderImp::Ptr _decoder; + multimap _frame_cache; + Timer::Ptr _timer; + Ticker _ticker; + Stamp _stamp[2]; }; }//namespace mediakit From dea36cfc847f5855445c09da4360448be6519530 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 27 May 2020 11:09:02 +0800 Subject: [PATCH 064/100] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E5=86=99=E9=80=BB=E8=BE=91=EF=BC=8C=E7=A1=AE=E4=BF=9DGOP?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E7=AC=AC=E4=B8=80=E5=B8=A7=E4=B8=BA=E5=85=B3?= =?UTF-8?q?=E9=94=AE=E5=B8=A7=E5=B9=B6=E7=A1=AE=E4=BF=9D=E9=9F=B3=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E6=95=B0=E6=8D=AE=E7=9A=84=E4=BA=A4=E7=BB=87=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/MediaSource.cpp | 53 +++++++++++++---------------- src/Common/MediaSource.h | 70 ++++++++------------------------------ src/Rtmp/RtmpMediaSource.h | 30 ++++------------ src/Rtsp/RtspMediaSource.h | 32 +++++------------ 4 files changed, 53 insertions(+), 132 deletions(-) diff --git a/src/Common/MediaSource.cpp b/src/Common/MediaSource.cpp index ea5319d9..6b3c4519 100644 --- a/src/Common/MediaSource.cpp +++ b/src/Common/MediaSource.cpp @@ -479,57 +479,52 @@ MediaSource::Ptr MediaSource::createFromMP4(const string &schema, const string & #endif //ENABLE_MP4 } -static bool isFlushAble_default(bool is_audio, uint32_t last_stamp, uint32_t new_stamp, int cache_size) { +static bool isFlushAble_default(bool is_video, uint32_t last_stamp, uint32_t new_stamp, int cache_size) { if (new_stamp < last_stamp) { //时间戳回退(可能seek中) return true; } - if (!is_audio) { - //这是视频,时间戳发送变化或者缓存超过1024个 - return last_stamp != new_stamp || cache_size >= 1024; - } - - //这是音频,缓存超过100ms或者缓存个数超过10个 - return new_stamp > last_stamp + 100 || cache_size > 10; + //时间戳发送变化或者缓存超过1024个,sendmsg接口一般最多只能发送1024个数据包 + return last_stamp != new_stamp || cache_size >= 1024; } -static bool isFlushAble_merge(bool is_audio, uint32_t last_stamp, uint32_t new_stamp, int cache_size, int merge_ms) { +static bool isFlushAble_merge(bool is_video, uint32_t last_stamp, uint32_t new_stamp, int cache_size, int merge_ms) { if (new_stamp < last_stamp) { //时间戳回退(可能seek中) return true; } - if(new_stamp > last_stamp + merge_ms){ + if (new_stamp > last_stamp + merge_ms) { //时间戳增量超过合并写阈值 return true; } - if (!is_audio) { - //这是视频,缓存数超过1024个,这个逻辑用于避免时间戳异常的流导致的内存暴增问题 - //而且sendmsg接口一般最多只能发送1024个数据包 - return cache_size >= 1024; - } - - //这是音频,音频缓存超过20个 - return cache_size > 20; + //缓存数超过1024个,这个逻辑用于避免时间戳异常的流导致的内存暴增问题 + //而且sendmsg接口一般最多只能发送1024个数据包 + return cache_size >= 1024; } -bool FlushPolicy::isFlushAble(uint32_t new_stamp, int cache_size) { - bool ret = false; - GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS); - if (mergeWriteMS <= 0) { - //关闭了合并写或者合并写阈值小于等于0 - ret = isFlushAble_default(_is_audio, _last_stamp, new_stamp, cache_size); +bool FlushPolicy::isFlushAble(bool is_video, bool is_key, uint32_t new_stamp, int cache_size) { + bool flush_flag = false; + if (is_key && is_video) { + //遇到关键帧flush掉前面的数据,确保关键帧为该组数据的第一帧,确保GOP缓存有效 + flush_flag = true; } else { - ret = isFlushAble_merge(_is_audio, _last_stamp, new_stamp, cache_size, mergeWriteMS); + GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS); + if (mergeWriteMS <= 0) { + //关闭了合并写或者合并写阈值小于等于0 + flush_flag = isFlushAble_default(is_video, _last_stamp[is_video], new_stamp, cache_size); + } else { + flush_flag = isFlushAble_merge(is_video, _last_stamp[is_video], new_stamp, cache_size, mergeWriteMS); + } } - if (ret) { -// DebugL << _is_audio << " " << _last_stamp << " " << new_stamp; - _last_stamp = new_stamp; + if (flush_flag) { +// DebugL << is_video << " " << _last_stamp[is_video] << " " << new_stamp; + _last_stamp[is_video] = new_stamp; } - return ret; + return flush_flag; } } /* namespace mediakit */ \ No newline at end of file diff --git a/src/Common/MediaSource.h b/src/Common/MediaSource.h index 299cf709..6c775934 100644 --- a/src/Common/MediaSource.h +++ b/src/Common/MediaSource.h @@ -164,10 +164,7 @@ private: ///缓存刷新策略类 class FlushPolicy { public: - FlushPolicy(bool is_audio) { - _is_audio = is_audio; - }; - + FlushPolicy() = default; ~FlushPolicy() = default; uint32_t getStamp(const RtpPacket::Ptr &packet) { @@ -178,45 +175,45 @@ public: return packet->timeStamp; } - bool isFlushAble(uint32_t new_stamp, int cache_size); + bool isFlushAble(bool is_video, bool is_key, uint32_t new_stamp, int cache_size); + private: - bool _is_audio; - uint32_t _last_stamp= 0; + uint32_t _last_stamp[2] = {0, 0}; }; -/// 视频合并写缓存模板 +/// 合并写缓存模板 /// \tparam packet 包类型 /// \tparam policy 刷新缓存策略 /// \tparam packet_list 包缓存类型 template > > -class VideoPacketCache { +class PacketCache { public: - VideoPacketCache() : _policy(false) { + PacketCache(){ _cache = std::make_shared(); } - virtual ~VideoPacketCache() = default; + virtual ~PacketCache() = default; - void inputVideo(const std::shared_ptr &rtp, bool key_pos) { - if (_policy.isFlushAble(_policy.getStamp(rtp), _cache->size())) { + void inputPacket(bool is_video, const std::shared_ptr &pkt, bool key_pos) { + if (_policy.isFlushAble(is_video, key_pos, _policy.getStamp(pkt), _cache->size())) { flushAll(); } //追加数据到最后 - _cache->emplace_back(rtp); + _cache->emplace_back(pkt); if (key_pos) { _key_pos = key_pos; } } - virtual void onFlushVideo(std::shared_ptr &, bool key_pos) = 0; + virtual void onFlush(std::shared_ptr &, bool key_pos) = 0; private: void flushAll() { if (_cache->empty()) { return; } - onFlushVideo(_cache, _key_pos); + onFlush(_cache, _key_pos); _cache = std::make_shared(); _key_pos = false; } @@ -227,44 +224,5 @@ private: bool _key_pos = false; }; -/// 音频频合并写缓存模板 -/// \tparam packet 包类型 -/// \tparam policy 刷新缓存策略 -/// \tparam packet_list 包缓存类型 -template > > -class AudioPacketCache { -public: - AudioPacketCache() : _policy(true) { - _cache = std::make_shared(); - } - - virtual ~AudioPacketCache() = default; - - void inputAudio(const std::shared_ptr &rtp) { - if (_policy.isFlushAble(_policy.getStamp(rtp), _cache->size())) { - flushAll(); - } - //追加数据到最后 - _cache->emplace_back(rtp); - } - - virtual void onFlushAudio(std::shared_ptr &) = 0; - -private: - void flushAll() { - if (_cache->empty()) { - return; - } - onFlushAudio(_cache); - _cache = std::make_shared(); - } - -private: - policy _policy; - std::shared_ptr _cache; -}; - } /* namespace mediakit */ - - -#endif //ZLMEDIAKIT_MEDIASOURCE_H +#endif //ZLMEDIAKIT_MEDIASOURCE_H \ No newline at end of file diff --git a/src/Rtmp/RtmpMediaSource.h b/src/Rtmp/RtmpMediaSource.h index 23a0c6ad..0e0c7091 100644 --- a/src/Rtmp/RtmpMediaSource.h +++ b/src/Rtmp/RtmpMediaSource.h @@ -33,9 +33,6 @@ using namespace toolkit; #define RTMP_GOP_SIZE 512 namespace mediakit { -typedef VideoPacketCache RtmpVideoCache; -typedef AudioPacketCache RtmpAudioCache; - /** * rtmp媒体源的数据抽象 * rtmp有关键的三要素,分别是metadata、config帧,普通帧 @@ -43,7 +40,7 @@ typedef AudioPacketCache RtmpAudioCache; * 只要生成了这三要素,那么要实现rtmp推流、rtmp服务器就很简单了 * rtmp推拉流协议中,先传递metadata,然后传递config帧,然后一直传递普通帧 */ -class RtmpMediaSource : public MediaSource, public RingDelegate, public RtmpVideoCache, public RtmpAudioCache{ +class RtmpMediaSource : public MediaSource, public RingDelegate, public PacketCache{ public: typedef std::shared_ptr Ptr; typedef std::shared_ptr > RingDataType; @@ -149,12 +146,7 @@ public: regist(); } } - - if(pkt->typeId == MSG_VIDEO){ - RtmpVideoCache::inputVideo(pkt, key); - }else{ - RtmpAudioCache::inputAudio(pkt); - } + PacketCache::inputPacket(pkt->typeId == MSG_VIDEO, pkt, key); } /** @@ -175,21 +167,13 @@ public: private: /** - * 批量flush时间戳相同的视频rtmp包时触发该函数 - * @param rtmp_list 时间戳相同的rtmp包列表 + * 批量flush rtmp包时触发该函数 + * @param rtmp_list rtmp包列表 * @param key_pos 是否包含关键帧 */ - void onFlushVideo(std::shared_ptr > &rtmp_list, bool key_pos) override { - _ring->write(rtmp_list, key_pos); - } - - /** - * 批量flush一定数量的音频rtmp包时触发该函数 - * @param rtmp_list rtmp包列表 - */ - void onFlushAudio(std::shared_ptr > &rtmp_list) override{ - //只有音频的话,就不存在gop缓存的意义 - _ring->write(rtmp_list, !_have_video); + void onFlush(std::shared_ptr > &rtmp_list, bool key_pos) override { + //如果不存在视频,那么就没有存在GOP缓存的意义,所以is_key一直为true确保一直清空GOP缓存 + _ring->write(rtmp_list, _have_video ? key_pos : true); } /** diff --git a/src/Rtsp/RtspMediaSource.h b/src/Rtsp/RtspMediaSource.h index caededbf..4e2657e0 100644 --- a/src/Rtsp/RtspMediaSource.h +++ b/src/Rtsp/RtspMediaSource.h @@ -30,16 +30,13 @@ using namespace toolkit; #define RTP_GOP_SIZE 512 namespace mediakit { -typedef VideoPacketCache RtpVideoCache; -typedef AudioPacketCache RtpAudioCache; - - /** +/** * rtsp媒体源的数据抽象 * rtsp有关键的两要素,分别是sdp、rtp包 * 只要生成了这两要素,那么要实现rtsp推流、rtsp服务器就很简单了 * rtsp推拉流协议中,先传递sdp,然后再协商传输方式(tcp/udp/组播),最后一直传递rtp */ -class RtspMediaSource : public MediaSource, public RingDelegate, public RtpVideoCache, public RtpAudioCache { +class RtspMediaSource : public MediaSource, public RingDelegate, public PacketCache { public: typedef ResourcePool PoolType; typedef std::shared_ptr Ptr; @@ -175,32 +172,19 @@ public: regist(); } } - - if(rtp->type == TrackVideo){ - RtpVideoCache::inputVideo(rtp, keyPos); - }else{ - RtpAudioCache::inputAudio(rtp); - } + PacketCache::inputPacket(rtp->type == TrackVideo, rtp, keyPos); } private: /** - * 批量flush时间戳相同的视频rtp包时触发该函数 - * @param rtp_list 时间戳相同的rtp包列表 + * 批量flush rtp包时触发该函数 + * @param rtp_list rtp包列表 * @param key_pos 是否包含关键帧 */ - void onFlushVideo(std::shared_ptr > &rtp_list, bool key_pos) override { - _ring->write(rtp_list, key_pos); - } - - /** - * 批量flush一定数量的音频rtp包时触发该函数 - * @param rtp_list rtp包列表 - */ - void onFlushAudio(std::shared_ptr > &rtp_list) override{ - //只有音频的话,就不存在gop缓存的意义 - _ring->write(rtp_list, !_have_video); + void onFlush(std::shared_ptr > &rtp_list, bool key_pos) override { + //如果不存在视频,那么就没有存在GOP缓存的意义,所以is_key一直为true确保一直清空GOP缓存 + _ring->write(rtp_list, _have_video ? key_pos : true); } /** From 0548256c309ac1cd9db48d56b8351daf40d131a9 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 27 May 2020 11:25:56 +0800 Subject: [PATCH 065/100] =?UTF-8?q?=E5=90=88=E5=B9=B6=E5=86=99=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E4=BC=98=E5=8C=96=E5=AF=B9rtp=E7=9A=84=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/MediaSource.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Common/MediaSource.cpp b/src/Common/MediaSource.cpp index 6b3c4519..7d9ba2f8 100644 --- a/src/Common/MediaSource.cpp +++ b/src/Common/MediaSource.cpp @@ -480,8 +480,8 @@ MediaSource::Ptr MediaSource::createFromMP4(const string &schema, const string & } static bool isFlushAble_default(bool is_video, uint32_t last_stamp, uint32_t new_stamp, int cache_size) { - if (new_stamp < last_stamp) { - //时间戳回退(可能seek中) + if (new_stamp + 500 < last_stamp) { + //时间戳回退比较大(可能seek中),由于rtp中时间戳是pts,是可能存在一定程度的回退的 return true; } @@ -490,8 +490,8 @@ static bool isFlushAble_default(bool is_video, uint32_t last_stamp, uint32_t new } static bool isFlushAble_merge(bool is_video, uint32_t last_stamp, uint32_t new_stamp, int cache_size, int merge_ms) { - if (new_stamp < last_stamp) { - //时间戳回退(可能seek中) + if (new_stamp + 500 < last_stamp) { + //时间戳回退比较大(可能seek中),由于rtp中时间戳是pts,是可能存在一定程度的回退的 return true; } @@ -521,7 +521,6 @@ bool FlushPolicy::isFlushAble(bool is_video, bool is_key, uint32_t new_stamp, in } if (flush_flag) { -// DebugL << is_video << " " << _last_stamp[is_video] << " " << new_stamp; _last_stamp[is_video] = new_stamp; } return flush_flag; From 57fce2948253a6b7db56d1f1bfcd80d52f95b8d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Wed, 27 May 2020 14:52:48 +0800 Subject: [PATCH 066/100] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 977dd029..164af99b 100644 --- a/README.md +++ b/README.md @@ -89,8 +89,7 @@ ## 编译以及测试 - -请参考wiki:[快速开始](https://github.com/xiongziliang/ZLMediaKit/wiki/%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B) +**编译前务必仔细参考wiki:[快速开始](https://github.com/xiongziliang/ZLMediaKit/wiki/%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B)操作**,由于不看wiki导致源码缺失编译失败的问题将被直接移出QQ群,或无回复直接关闭issue!!! ## 怎么使用 From 2050ed9db671f5bfb3eae0b85b2c541ad537cdc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Wed, 27 May 2020 14:59:49 +0800 Subject: [PATCH 067/100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 164af99b..bf5483f5 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ ## 编译以及测试 -**编译前务必仔细参考wiki:[快速开始](https://github.com/xiongziliang/ZLMediaKit/wiki/%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B)操作**,由于不看wiki导致源码缺失编译失败的问题将被直接移出QQ群,或无回复直接关闭issue!!! +**编译前务必仔细参考wiki:[快速开始](https://github.com/xiongziliang/ZLMediaKit/wiki/%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B)操作!!!** ## 怎么使用 From ebc1ee2c6e985d66632a6fee4177117fbba73e2e Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 28 May 2020 12:44:25 +0800 Subject: [PATCH 068/100] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 1 - tests/test_rtp.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 74d63149..2ee5d1f6 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -148,7 +148,6 @@ static inline void addHttpListener(){ NoticeCenter::Instance().addListener(nullptr, Broadcast::kBroadcastHttpRequest, [](BroadcastHttpRequestArgs) { auto it = s_map_api.find(parser.Url()); if (it == s_map_api.end()) { - consumed = false; return; } //该api已被消费 diff --git a/tests/test_rtp.cpp b/tests/test_rtp.cpp index a6b25b58..d5d68511 100644 --- a/tests/test_rtp.cpp +++ b/tests/test_rtp.cpp @@ -58,7 +58,7 @@ static bool loadFile(const char *path){ RtpSelector::Instance().inputRtp(nullptr,rtp,len, &addr,&timeStamp); if(timeStamp_last){ auto diff = timeStamp - timeStamp_last; - if(diff > 0){ + if(diff > 0 && diff < 500){ usleep(diff * 1000); } } From 41509800bce952280ed2a93c08c6350a14a7b99b Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 28 May 2020 17:03:12 +0800 Subject: [PATCH 069/100] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=AF=B9=E6=97=A0met?= =?UTF-8?q?adata=E7=9A=84rtmp=E6=B5=81=E7=9A=84=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/Extension/Factory.cpp | 18 ++++++++++++-- src/Extension/Factory.h | 3 ++- src/Rtmp/Rtmp.cpp | 19 +++++++++++++++ src/Rtmp/Rtmp.h | 3 ++- src/Rtmp/RtmpDemuxer.cpp | 34 ++++++++++++-------------- src/Rtmp/RtmpDemuxer.h | 2 +- src/Rtmp/RtmpMediaSourceImp.h | 22 +++++++++++++++-- src/Rtmp/RtmpMuxer.cpp | 46 +++-------------------------------- src/Rtmp/amf.cpp | 22 ++++++----------- src/Rtmp/amf.h | 6 ++--- 10 files changed, 88 insertions(+), 87 deletions(-) diff --git a/src/Extension/Factory.cpp b/src/Extension/Factory.cpp index b59e166f..276ce140 100644 --- a/src/Extension/Factory.cpp +++ b/src/Extension/Factory.cpp @@ -221,13 +221,27 @@ Track::Ptr Factory::getAudioTrackByAmf(const AMFValue& amf, int sample_rate, int return getTrackByCodecId(codecId, sample_rate, channels, sample_bit); } -RtmpCodec::Ptr Factory::getRtmpCodecByTrack(const Track::Ptr &track) { +RtmpCodec::Ptr Factory::getRtmpCodecByTrack(const Track::Ptr &track, bool is_encode) { switch (track->getCodecId()){ case CodecH264 : return std::make_shared(track); case CodecAAC : return std::make_shared(track); case CodecH265 : return std::make_shared(track); case CodecG711A : - case CodecG711U : return std::make_shared(track); + case CodecG711U : { + auto audio_track = dynamic_pointer_cast(track); + if (is_encode && (audio_track->getAudioSampleRate() != 8000 || + audio_track->getAudioChannel() != 1 || + audio_track->getAudioSampleBit() != 16)) { + //rtmp对g711只支持8000/1/16规格,但是ZLMediaKit可以解析其他规格的G711 + WarnL << "RTMP只支持8000/1/16规格的G711,目前规格是:" + << audio_track->getAudioSampleRate() << "/" + << audio_track->getAudioChannel() << "/" + << audio_track->getAudioSampleBit() + << ",该音频已被忽略"; + return nullptr; + } + return std::make_shared(track); + } default : WarnL << "暂不支持该CodecId:" << track->getCodecName(); return nullptr; } } diff --git a/src/Extension/Factory.h b/src/Extension/Factory.h index 6b678d34..74b70c58 100644 --- a/src/Extension/Factory.h +++ b/src/Extension/Factory.h @@ -59,8 +59,9 @@ public: /** * 根据Track获取Rtmp的编解码器 * @param track 媒体描述对象 + * @param is_encode 是否为编码器还是解码器 */ - static RtmpCodec::Ptr getRtmpCodecByTrack(const Track::Ptr &track); + static RtmpCodec::Ptr getRtmpCodecByTrack(const Track::Ptr &track, bool is_encode); /** * 根据codecId获取rtmp的codec描述 diff --git a/src/Rtmp/Rtmp.cpp b/src/Rtmp/Rtmp.cpp index 4749f3ea..db0078f4 100644 --- a/src/Rtmp/Rtmp.cpp +++ b/src/Rtmp/Rtmp.cpp @@ -100,4 +100,23 @@ uint8_t getAudioRtmpFlags(const Track::Ptr &track){ } +void Metadata::addTrack(AMFValue &metadata, const Track::Ptr &track) { + Metadata::Ptr new_metadata; + switch (track->getTrackType()) { + case TrackVideo: { + new_metadata = std::make_shared(dynamic_pointer_cast(track)); + } + break; + case TrackAudio: { + new_metadata = std::make_shared(dynamic_pointer_cast(track)); + } + break; + default: + return; + } + + new_metadata->getMetadata().object_for_each([&](const std::string &key, const AMFValue &value) { + metadata.set(key, value); + }); +} }//namespace mediakit \ No newline at end of file diff --git a/src/Rtmp/Rtmp.h b/src/Rtmp/Rtmp.h index 22f0775f..f9d3d8e6 100644 --- a/src/Rtmp/Rtmp.h +++ b/src/Rtmp/Rtmp.h @@ -220,6 +220,8 @@ public: const AMFValue &getMetadata() const{ return _metadata; } + + static void addTrack(AMFValue &metadata, const Track::Ptr &track); protected: AMFValue _metadata; }; @@ -261,7 +263,6 @@ private: CodecId _codecId; }; - class AudioMeta : public Metadata{ public: typedef std::shared_ptr Ptr; diff --git a/src/Rtmp/RtmpDemuxer.cpp b/src/Rtmp/RtmpDemuxer.cpp index 4848a960..6e38b0d7 100644 --- a/src/Rtmp/RtmpDemuxer.cpp +++ b/src/Rtmp/RtmpDemuxer.cpp @@ -13,60 +13,56 @@ namespace mediakit { -void RtmpDemuxer::loadMetaData(const AMFValue &val){ +bool RtmpDemuxer::loadMetaData(const AMFValue &val){ + bool ret = false; try { int audiosamplerate = 0; int audiochannels = 0; int audiosamplesize = 0; const AMFValue *audiocodecid = nullptr; const AMFValue *videocodecid = nullptr; - val.object_for_each([&](const string &key, const AMFValue &val) { if (key == "duration") { _fDuration = val.as_number(); return; } - - if(key == "audiosamplerate"){ + if (key == "audiosamplerate") { audiosamplerate = val.as_integer(); return; } - - if(key == "audiosamplesize"){ + if (key == "audiosamplesize") { audiosamplesize = val.as_integer(); return; } - - if(key == "stereo"){ + if (key == "stereo") { audiochannels = val.as_boolean() ? 2 : 1; return; } - - if(key == "videocodecid"){ + if (key == "videocodecid") { //找到视频 videocodecid = &val; return; } - - if(key == "audiocodecid"){ + if (key == "audiocodecid") { //找到音频 audiocodecid = &val; return; } }); - - if(videocodecid){ + if (videocodecid) { //有视频 + ret = true; makeVideoTrack(*videocodecid); } - - if(audiocodecid){ + if (audiocodecid) { //有音频 + ret = true; makeAudioTrack(*audiocodecid, audiosamplerate, audiochannels, audiosamplesize); } - }catch (std::exception &ex){ + } catch (std::exception &ex) { WarnL << ex.what(); } + return ret; } bool RtmpDemuxer::inputRtmp(const RtmpPacket::Ptr &pkt) { @@ -105,7 +101,7 @@ void RtmpDemuxer::makeVideoTrack(const AMFValue &videoCodec) { _videoTrack = dynamic_pointer_cast(Factory::getVideoTrackByAmf(videoCodec)); if (_videoTrack) { //生成rtmpCodec对象以便解码rtmp - _videoRtmpDecoder = Factory::getRtmpCodecByTrack(_videoTrack); + _videoRtmpDecoder = Factory::getRtmpCodecByTrack(_videoTrack, false); if (_videoRtmpDecoder) { //设置rtmp解码器代理,生成的frame写入该Track _videoRtmpDecoder->addDelegate(_videoTrack); @@ -123,7 +119,7 @@ void RtmpDemuxer::makeAudioTrack(const AMFValue &audioCodec,int sample_rate, int _audioTrack = dynamic_pointer_cast(Factory::getAudioTrackByAmf(audioCodec, sample_rate, channels, sample_bit)); if (_audioTrack) { //生成rtmpCodec对象以便解码rtmp - _audioRtmpDecoder = Factory::getRtmpCodecByTrack(_audioTrack); + _audioRtmpDecoder = Factory::getRtmpCodecByTrack(_audioTrack, false); if (_audioRtmpDecoder) { //设置rtmp解码器代理,生成的frame写入该Track _audioRtmpDecoder->addDelegate(_audioTrack); diff --git a/src/Rtmp/RtmpDemuxer.h b/src/Rtmp/RtmpDemuxer.h index fbff5d2e..7c6bdfa7 100644 --- a/src/Rtmp/RtmpDemuxer.h +++ b/src/Rtmp/RtmpDemuxer.h @@ -30,7 +30,7 @@ public: RtmpDemuxer() = default; virtual ~RtmpDemuxer() = default; - void loadMetaData(const AMFValue &metadata); + bool loadMetaData(const AMFValue &metadata); /** * 开始解复用 diff --git a/src/Rtmp/RtmpMediaSourceImp.h b/src/Rtmp/RtmpMediaSourceImp.h index e78f1286..4e8aae87 100644 --- a/src/Rtmp/RtmpMediaSourceImp.h +++ b/src/Rtmp/RtmpMediaSourceImp.h @@ -49,8 +49,13 @@ public: * 设置metadata */ void setMetaData(const AMFValue &metadata) override{ - _demuxer->loadMetaData(metadata); - RtmpMediaSource::setMetaData(metadata); + if(!_demuxer->loadMetaData(metadata)){ + //该metadata无效,需要重新生成 + _metadata = metadata; + _recreate_metadata = true; + }else{ + RtmpMediaSource::setMetaData(metadata); + } } /** @@ -138,6 +143,11 @@ public: _muxer->addTrack(track); track->addDelegate(_muxer); } + + if(_recreate_metadata){ + //需要重新生成metadata + Metadata::addTrack(_metadata,track); + } } /** @@ -146,11 +156,19 @@ public: void onAllTrackReady() override{ setTrackSource(_muxer); _all_track_ready = true; + + if(_recreate_metadata){ + //需要重新生成metadata + RtmpMediaSource::setMetaData(_metadata); + } } + private: RtmpDemuxer::Ptr _demuxer; MultiMediaSourceMuxer::Ptr _muxer; + AMFValue _metadata; bool _all_track_ready = false; + bool _recreate_metadata = false; }; } /* namespace mediakit */ diff --git a/src/Rtmp/RtmpMuxer.cpp b/src/Rtmp/RtmpMuxer.cpp index 2547642e..50686d01 100644 --- a/src/Rtmp/RtmpMuxer.cpp +++ b/src/Rtmp/RtmpMuxer.cpp @@ -23,47 +23,9 @@ RtmpMuxer::RtmpMuxer(const TitleMeta::Ptr &title) { } void RtmpMuxer::addTrack(const Track::Ptr &track) { - //根据track生产metadata - Metadata::Ptr metadata; - switch (track->getTrackType()){ - case TrackVideo:{ - metadata = std::make_shared(dynamic_pointer_cast(track)); - } - break; - case TrackAudio:{ - metadata = std::make_shared(dynamic_pointer_cast(track)); - } - break; - default: - return; - - } - - switch (track->getCodecId()){ - case CodecG711A: - case CodecG711U:{ - auto audio_track = dynamic_pointer_cast(track); - if(!audio_track){ - return; - } - if (audio_track->getAudioSampleRate() != 8000 || - audio_track->getAudioChannel() != 1 || - audio_track->getAudioSampleBit() != 16) { - WarnL << "RTMP只支持8000/1/16规格的G711,目前规格是:" - << audio_track->getAudioSampleRate() << "/" - << audio_track->getAudioChannel() << "/" - << audio_track->getAudioSampleBit() - << ",该音频已被忽略"; - return; - } - break; - } - default : break; - } - auto &encoder = _encoder[track->getTrackType()]; //生成rtmp编码器,克隆该Track,防止循环引用 - encoder = Factory::getRtmpCodecByTrack(track->clone()); + encoder = Factory::getRtmpCodecByTrack(track->clone(), true); if (!encoder) { return; } @@ -71,10 +33,8 @@ void RtmpMuxer::addTrack(const Track::Ptr &track) { //设置rtmp输出环形缓存 encoder->setRtmpRing(_rtmpRing); - //添加其metadata - metadata->getMetadata().object_for_each([&](const std::string &key, const AMFValue &value){ - _metadata.set(key,value); - }); + //添加metadata + Metadata::addTrack(_metadata,track); } void RtmpMuxer::inputFrame(const Frame::Ptr &frame) { diff --git a/src/Rtmp/amf.cpp b/src/Rtmp/amf.cpp index c19136cb..ca4c8077 100644 --- a/src/Rtmp/amf.cpp +++ b/src/Rtmp/amf.cpp @@ -44,6 +44,7 @@ inline void AMFValue::destroy() { break; } } + inline void AMFValue::init() { switch (_type) { case AMF_OBJECT: @@ -60,14 +61,13 @@ inline void AMFValue::init() { default: break; } - } + AMFValue::AMFValue(AMFType type) : _type(type) { init(); } - AMFValue::~AMFValue() { destroy(); } @@ -78,7 +78,6 @@ AMFValue::AMFValue(const char *s) : *_value.string = s; } - AMFValue::AMFValue(const std::string &s) : _type(AMF_STRING) { init(); @@ -108,15 +107,7 @@ AMFValue::AMFValue(const AMFValue &from) : *this = from; } -AMFValue::AMFValue(AMFValue &&from) { - *this = std::forward(from); -} - -AMFValue& AMFValue::operator =(const AMFValue &from) { - return *this = const_cast(from); - -} -AMFValue& AMFValue::operator =(AMFValue &&from) { +AMFValue& AMFValue::operator = (const AMFValue &from) { destroy(); _type = from._type; init(); @@ -144,7 +135,6 @@ AMFValue& AMFValue::operator =(AMFValue &&from) { break; } return *this; - } void AMFValue::clear() { @@ -236,7 +226,6 @@ string AMFValue::to_string() const{ } } - const AMFValue& AMFValue::operator[](const char *str) const { if (_type != AMF_OBJECT && _type != AMF_ECMA_ARRAY) { throw std::runtime_error("AMF not a object"); @@ -338,6 +327,7 @@ AMFEncoder & AMFEncoder::operator <<(const char *s) { } return *this; } + AMFEncoder & AMFEncoder::operator <<(const std::string &s) { if (!s.empty()) { buf += char(AMF0_STRING); @@ -349,18 +339,22 @@ AMFEncoder & AMFEncoder::operator <<(const std::string &s) { } return *this; } + AMFEncoder & AMFEncoder::operator <<(std::nullptr_t) { buf += char(AMF0_NULL); return *this; } + AMFEncoder & AMFEncoder::write_undefined() { buf += char(AMF0_UNDEFINED); return *this; } + AMFEncoder & AMFEncoder::operator <<(const int n){ return (*this) << (double)n; } + AMFEncoder & AMFEncoder::operator <<(const double n) { buf += char(AMF0_NUMBER); uint64_t encoded = 0; diff --git a/src/Rtmp/amf.h b/src/Rtmp/amf.h index 830fbe28..9b29b11e 100644 --- a/src/Rtmp/amf.h +++ b/src/Rtmp/amf.h @@ -40,6 +40,7 @@ public: typedef std::map mapType; typedef std::vector arrayType; + ~AMFValue(); AMFValue(AMFType type = AMF_NULL); AMFValue(const char *s); AMFValue(const std::string &s); @@ -47,10 +48,7 @@ public: AMFValue(int i); AMFValue(bool b); AMFValue(const AMFValue &from); - AMFValue(AMFValue &&from); - AMFValue &operator =(const AMFValue &from); - AMFValue &operator =(AMFValue &&from); - ~AMFValue(); + AMFValue &operator = (const AMFValue &from); void clear(); AMFType type() const ; From 406c5477e0cb9545fdb4f4259dd4330b6c145d10 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 28 May 2020 18:01:03 +0800 Subject: [PATCH 070/100] =?UTF-8?q?=E6=97=A0metadata=E4=B8=94=E4=B8=8D?= =?UTF-8?q?=E8=83=BD=E8=8E=B7=E5=8F=96Track=E7=9A=84rtmp=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=E8=83=BD=E6=B3=A8=E5=86=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Rtmp/RtmpMediaSource.h | 8 ++++++++ src/Rtmp/RtmpMediaSourceImp.h | 7 +++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Rtmp/RtmpMediaSource.h b/src/Rtmp/RtmpMediaSource.h index 0e0c7091..5718c94f 100644 --- a/src/Rtmp/RtmpMediaSource.h +++ b/src/Rtmp/RtmpMediaSource.h @@ -107,6 +107,14 @@ public: } } + /** + * 更新metadata + */ + void updateMetaData(const AMFValue &metadata) { + lock_guard lock(_mtx); + _metadata = metadata; + } + /** * 输入rtmp包 * @param pkt rtmp包 diff --git a/src/Rtmp/RtmpMediaSourceImp.h b/src/Rtmp/RtmpMediaSourceImp.h index 4e8aae87..d2a1267a 100644 --- a/src/Rtmp/RtmpMediaSourceImp.h +++ b/src/Rtmp/RtmpMediaSourceImp.h @@ -53,9 +53,8 @@ public: //该metadata无效,需要重新生成 _metadata = metadata; _recreate_metadata = true; - }else{ - RtmpMediaSource::setMetaData(metadata); } + RtmpMediaSource::setMetaData(metadata); } /** @@ -158,8 +157,8 @@ public: _all_track_ready = true; if(_recreate_metadata){ - //需要重新生成metadata - RtmpMediaSource::setMetaData(_metadata); + //更新metadata + RtmpMediaSource::updateMetaData(_metadata); } } From de96a4f0c8381a0dc87a98b7f4927bb66365a1a0 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 28 May 2020 18:19:39 +0800 Subject: [PATCH 071/100] =?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/Rtmp/RtmpMediaSourceImp.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Rtmp/RtmpMediaSourceImp.h b/src/Rtmp/RtmpMediaSourceImp.h index d2a1267a..a779f30c 100644 --- a/src/Rtmp/RtmpMediaSourceImp.h +++ b/src/Rtmp/RtmpMediaSourceImp.h @@ -142,11 +142,6 @@ public: _muxer->addTrack(track); track->addDelegate(_muxer); } - - if(_recreate_metadata){ - //需要重新生成metadata - Metadata::addTrack(_metadata,track); - } } /** @@ -156,8 +151,11 @@ public: setTrackSource(_muxer); _all_track_ready = true; - if(_recreate_metadata){ + if (_recreate_metadata) { //更新metadata + for (auto &track : _muxer->getTracks()) { + Metadata::addTrack(_metadata, track); + } RtmpMediaSource::updateMetaData(_metadata); } } From fb4435fca98374347f80c99bee20c84541fcdb46 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 30 May 2020 10:16:15 +0800 Subject: [PATCH 072/100] =?UTF-8?q?rtp=E6=8E=A8=E6=B5=81=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E5=87=BAframe=E6=89=8D=E5=88=B7=E6=96=B0=E4=BF=9D=E6=B4=BB?= =?UTF-8?q?=E8=AE=A1=E6=97=B6=E5=99=A8?= 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 d8057c2b..e6d3c967 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -115,7 +115,6 @@ bool RtpProcess::inputRtp(const Socket::Ptr &sock, const char *data, int data_le } _total_bytes += data_len; - _last_rtp_time.resetTime(); bool ret = handleOneRtp(0,_track,(unsigned char *)data,data_len); if(dts_out){ *dts_out = _dts; @@ -169,6 +168,7 @@ void RtpProcess::onRtpDecode(const uint8_t *packet, int bytes, uint32_t timestam } void RtpProcess::inputFrame(const Frame::Ptr &frame){ + _last_rtp_time.resetTime(); _dts = frame->dts(); if (_save_file_video && frame->getTrackType() == TrackVideo) { fwrite((uint8_t *) frame->data(), frame->size(), 1, _save_file_video.get()); From 31ad2caa6b098def62505667d2c6ab33ecb13d51 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 30 May 2020 11:49:36 +0800 Subject: [PATCH 073/100] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E6=8A=9B=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E6=9B=BF=E4=BB=A3asset=EF=BC=8C=E6=8F=90=E9=AB=98?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=A8=B3=E5=AE=9A=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/assert.h | 31 +++++++++++++++++++++++++++++++ CMakeLists.txt | 1 + src/Common/config.cpp | 7 +++++++ 3 files changed, 39 insertions(+) create mode 100644 3rdpart/assert.h diff --git a/3rdpart/assert.h b/3rdpart/assert.h new file mode 100644 index 00000000..37ba8324 --- /dev/null +++ b/3rdpart/assert.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Use of this source code is governed by MIT license that can be found in the + * LICENSE file in the root of the source tree. All contributing project authors + * may be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef ZLMEDIAKIT_ASSERT_H +#define ZLMEDIAKIT_ASSERT_H + +#include +#ifndef NDEBUG + #ifdef assert + #undef assert + #endif//assert + + #ifdef __cplusplus + extern "C" { + #endif + extern void Assert_Throw(int failed, const char *exp, const char *func, const char *file, int line); + #ifdef __cplusplus + } + #endif + + #define assert(exp) Assert_Throw(!(exp), #exp, __FUNCTION__, __FILE__, __LINE__); +#endif//NDEBUG + +#endif //ZLMEDIAKIT_ASSERT_H diff --git a/CMakeLists.txt b/CMakeLists.txt index 05075705..68f3a03f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ INCLUDE_DIRECTORIES(${MediaServer_Root}/libmpeg/include) INCLUDE_DIRECTORIES(${MediaServer_Root}/libmov/include) INCLUDE_DIRECTORIES(${MediaServer_Root}/libflv/include) INCLUDE_DIRECTORIES(${MediaServer_Root}/librtp/include) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/3rdpart) set(ENABLE_HLS true) set(ENABLE_OPENSSL true) diff --git a/src/Common/config.cpp b/src/Common/config.cpp index d8f2b933..b2e00ba5 100644 --- a/src/Common/config.cpp +++ b/src/Common/config.cpp @@ -295,3 +295,10 @@ const string kBenchmarkMode = "benchmark_mode"; } // namespace mediakit +void Assert_Throw(int failed, const char *exp, const char *func, const char *file, int line){ + if(failed) { + _StrPrinter printer; + printer << "Assertion failed: (" << exp << "), function " << func << ", file " << file << ", line " << line << "."; + throw std::runtime_error(printer); + } +} From 932da46c3aca1be63fb387d7fb8dc58acc23fde6 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 30 May 2020 12:01:44 +0800 Subject: [PATCH 074/100] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=AC=AC=E4=B8=89?= =?UTF-8?q?=E6=96=B9=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/media-server | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdpart/media-server b/3rdpart/media-server index abc08f61..2d585a44 160000 --- a/3rdpart/media-server +++ b/3rdpart/media-server @@ -1 +1 @@ -Subproject commit abc08f61bb1250b94d252cfeaea249527912dd3b +Subproject commit 2d585a4410351f69dd4a4667faff82750b574afc From aeb2f910c1272e9d0b2eff0d15082407526ad8dd Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 30 May 2020 13:52:09 +0800 Subject: [PATCH 075/100] =?UTF-8?q?release=E7=BC=96=E8=AF=91=E6=97=B6?= =?UTF-8?q?=E4=BF=AE=E5=A4=8Dassert=E5=AE=8F=E7=9B=B8=E5=85=B3=E8=AD=A6?= =?UTF-8?q?=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/assert.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/3rdpart/assert.h b/3rdpart/assert.h index 37ba8324..efb2224f 100644 --- a/3rdpart/assert.h +++ b/3rdpart/assert.h @@ -26,6 +26,8 @@ #endif #define assert(exp) Assert_Throw(!(exp), #exp, __FUNCTION__, __FILE__, __LINE__); +#else + #define assert(e) ((void)0) #endif//NDEBUG #endif //ZLMEDIAKIT_ASSERT_H From b7049b1407165b277a7824205fc9c321c2485bcd Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 30 May 2020 14:43:08 +0800 Subject: [PATCH 076/100] =?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/Rtmp/RtmpDemuxer.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Rtmp/RtmpDemuxer.cpp b/src/Rtmp/RtmpDemuxer.cpp index 6e38b0d7..ad4b84b7 100644 --- a/src/Rtmp/RtmpDemuxer.cpp +++ b/src/Rtmp/RtmpDemuxer.cpp @@ -106,7 +106,6 @@ void RtmpDemuxer::makeVideoTrack(const AMFValue &videoCodec) { //设置rtmp解码器代理,生成的frame写入该Track _videoRtmpDecoder->addDelegate(_videoTrack); onAddTrack(_videoTrack); - _tryedGetVideoTrack = true; } else { //找不到相应的rtmp解码器,该track无效 _videoTrack.reset(); @@ -124,7 +123,6 @@ void RtmpDemuxer::makeAudioTrack(const AMFValue &audioCodec,int sample_rate, int //设置rtmp解码器代理,生成的frame写入该Track _audioRtmpDecoder->addDelegate(_audioTrack); onAddTrack(_audioTrack); - _tryedGetAudioTrack = true; } else { //找不到相应的rtmp解码器,该track无效 _audioTrack.reset(); From 974260156ef2b0b8968ab1f8adf23a8bf4e8141d Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sat, 30 May 2020 18:33:28 +0800 Subject: [PATCH 077/100] =?UTF-8?q?=E9=98=B2=E6=AD=A2=E8=AF=AF=E6=8A=A5?= =?UTF-8?q?=E8=AD=A6=E5=91=8A=E6=97=A5=E5=BF=97?= 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 e6d3c967..7deb0e0a 100644 --- a/src/Rtp/RtpProcess.cpp +++ b/src/Rtp/RtpProcess.cpp @@ -128,7 +128,7 @@ static inline bool checkTS(const uint8_t *packet, int bytes){ } void RtpProcess::onRtpSorted(const RtpPacket::Ptr &rtp, int) { - if(rtp->sequence != _sequence + 1 && rtp->sequence != 0){ + if(rtp->sequence != _sequence + 1 && _sequence != 0){ WarnP(this) << "rtp丢包:" << rtp->sequence << " != " << _sequence << "+1" << ",公网环境下请使用tcp方式推流"; } _sequence = rtp->sequence; From fe095a41175ce14d33ea1705be242f5ce42bd523 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 1 Jun 2020 11:17:50 +0800 Subject: [PATCH 078/100] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E9=87=8D=E5=AE=9A=E5=90=91=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/Process.cpp | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/server/Process.cpp b/server/Process.cpp index 709721f3..40030444 100644 --- a/server/Process.cpp +++ b/server/Process.cpp @@ -10,7 +10,6 @@ #include #include - #ifndef _WIN32 #include #include @@ -56,30 +55,27 @@ void Process::run(const string &cmd, const string &log_file_tmp) { } if (_pid == 0) { //子进程关闭core文件生成 - struct rlimit rlim = { 0,0 }; + struct rlimit rlim = {0, 0}; setrlimit(RLIMIT_CORE, &rlim); //在启动子进程时,暂时禁用SIGINT、SIGTERM信号 - // ignore the SIGINT and SIGTERM signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); string log_file; if (log_file_tmp.empty()) { + //未指定子进程日志文件时,重定向至/dev/null log_file = "/dev/null"; - } - else { + } else { log_file = StrPrinter << log_file_tmp << "." << getpid(); } - 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::create_path(log_file.data(), mode); - if ((log_fd = ::open(log_file.c_str(), flags, mode)) < 0) { + //重定向shell日志至文件 + auto fp = File::create_file(log_file.data(), "ab"); + if (!fp) { fprintf(stderr, "open log file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno)); - } - else { + } else { + auto log_fd = fileno(fp); // 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)); @@ -87,13 +83,12 @@ void Process::run(const string &cmd, const string &log_file_tmp) { 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); + // 关闭日志文件 + ::fclose(fp); } 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. + //关闭父进程继承的fd for (int i = 3; i < 1024; i++) { ::close(i); } @@ -101,9 +96,9 @@ void Process::run(const string &cmd, const string &log_file_tmp) { 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++) { + for (int i = 0; i < (int) params.size(); i++) { std::string &p = params[i]; - charpv_params[i] = (char *)p.data(); + charpv_params[i] = (char *) p.data(); } // EOF: NULL charpv_params[params.size()] = NULL; From 09995a7ed8a07e56491101e650d8b1a0fd3f8479 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 4 Jun 2020 16:17:03 +0800 Subject: [PATCH 079/100] =?UTF-8?q?=E5=AE=8C=E5=96=84websocket=20c=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/source/mk_tcp.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/api/source/mk_tcp.cpp b/api/source/mk_tcp.cpp index 4795141e..793ad31f 100644 --- a/api/source/mk_tcp.cpp +++ b/api/source/mk_tcp.cpp @@ -144,10 +144,12 @@ API_EXPORT uint16_t API_CALL mk_tcp_server_start(uint16_t port, mk_tcp_type type s_tcp_server[type]->start >(port); break; case mk_type_ws: - s_tcp_server[type]->start>(port); + //此处你也可以修改WebSocketHeader::BINARY + s_tcp_server[type]->start >(port); break; case mk_type_wss: - s_tcp_server[type]->start>(port); + //此处你也可以修改WebSocketHeader::BINARY + s_tcp_server[type]->start >(port); break; default: return 0; @@ -208,8 +210,10 @@ TcpClientForC::Ptr *mk_tcp_client_create_l(mk_tcp_client_events *events, mk_tcp_ case mk_type_ssl: return (TcpClientForC::Ptr *)new shared_ptr >(new TcpSessionWithSSL(events)); case mk_type_ws: + //此处你也可以修改WebSocketHeader::BINARY return (TcpClientForC::Ptr *)new shared_ptr >(new WebSocketClient(events)); case mk_type_wss: + //此处你也可以修改WebSocketHeader::BINARY return (TcpClientForC::Ptr *)new shared_ptr >(new WebSocketClient(events)); default: return nullptr; From 8ef8c91f2ef7893ec53ee23cd4c0e282a915d151 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 4 Jun 2020 16:17:41 +0800 Subject: [PATCH 080/100] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=AC=AC=E4=B8=89?= =?UTF-8?q?=E6=96=B9=E5=BA=93,=E8=A7=A3=E5=86=B3rtp=E4=B8=A2=E5=8C=85?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E6=97=A0=E6=B3=95=E8=A7=A3=E6=9E=90=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 --- 3rdpart/media-server | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdpart/media-server b/3rdpart/media-server index 2d585a44..1ce64284 160000 --- a/3rdpart/media-server +++ b/3rdpart/media-server @@ -1 +1 @@ -Subproject commit 2d585a4410351f69dd4a4667faff82750b574afc +Subproject commit 1ce642840df605de29e6acefa13d97c63021121f From b08ea0fcc7f4006beb087541d2f91addb835cb51 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 8 Jun 2020 14:56:17 +0800 Subject: [PATCH 081/100] =?UTF-8?q?windows=E4=B8=8B=E5=AD=90=E8=BF=9B?= =?UTF-8?q?=E7=A8=8B=E6=94=AF=E6=8C=81=E6=97=A5=E5=BF=97=E9=87=8D=E5=AE=9A?= =?UTF-8?q?=E5=90=91=E3=80=81=E7=AD=89=E5=BE=85=E5=AD=90=E8=BF=9B=E7=A8=8B?= =?UTF-8?q?=E6=8E=A8=E5=87=BA=E3=80=81=E8=8E=B7=E5=8F=96=E5=AD=90=E8=BF=9B?= =?UTF-8?q?=E7=A8=8B=E9=80=80=E5=87=BA=E7=A0=81=E7=AD=89=E7=89=B9=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/FFmpegSource.cpp | 11 ++-- server/Process.cpp | 123 +++++++++++++++++++++++++++++++++------- 2 files changed, 108 insertions(+), 26 deletions(-) diff --git a/server/FFmpegSource.cpp b/server/FFmpegSource.cpp index 95f079c7..0ed137a2 100644 --- a/server/FFmpegSource.cpp +++ b/server/FFmpegSource.cpp @@ -24,19 +24,16 @@ const string kSnap = FFmpeg_FIELD"snap"; onceToken token([]() { #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"; - mINI::Instance()[kSnap] = "%s -i %s -loglevel quiet -y -f mjpeg -t 0.001 %s"; + string ffmpeg_bin = trim(System::execute("where ffmpeg")); #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"; + string ffmpeg_bin = trim(System::execute("which ffmpeg")); #endif //默认ffmpeg命令路径为环境变量中路径 mINI::Instance()[kBin] = ffmpeg_bin.empty() ? "ffmpeg" : ffmpeg_bin; //ffmpeg日志保存路径 mINI::Instance()[kLog] = "./ffmpeg/ffmpeg.log"; + 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"; }); } diff --git a/server/Process.cpp b/server/Process.cpp index 40030444..f877bdfc 100644 --- a/server/Process.cpp +++ b/server/Process.cpp @@ -16,6 +16,7 @@ #else //#include #include +#include #endif #include @@ -31,23 +32,44 @@ using namespace toolkit; void Process::run(const string &cmd, const string &log_file_tmp) { kill(2000); #ifdef _WIN32 - STARTUPINFO si; - PROCESS_INFORMATION pi; - ZeroMemory(&si, sizeof(si)); //结构体初始化; - ZeroMemory(&pi, sizeof(pi)); + STARTUPINFO si = {0}; + PROCESS_INFORMATION pi = {0}; + string log_file; + if (log_file_tmp.empty()) { + //未指定子进程日志文件时,重定向至/dev/null + log_file = "NUL"; + } + else { + log_file = StrPrinter << log_file_tmp << "." << getCurrentMillisecond(); + } + + //重定向shell日志至文件 + auto fp = File::create_file(log_file.data(), "ab"); + if (!fp) { + fprintf(stderr, "open log file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno)); + } else { + auto log_fd = (HANDLE)(_get_osfhandle(fileno(fp))); + // dup to stdout and stderr. + si.wShowWindow = SW_HIDE; + // STARTF_USESHOWWINDOW:The wShowWindow member contains additional information. + // STARTF_USESTDHANDLES:The hStdInput, hStdOutput, and hStdError members contain additional information. + si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + si.hStdError = log_fd; + si.hStdOutput = log_fd; + } LPTSTR lpDir = const_cast(cmd.data()); - - if (CreateProcess(NULL, lpDir, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)){ + if (CreateProcess(NULL, lpDir, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)){ //下面两行关闭句柄,解除本进程和新进程的关系,不然有可能 不小心调用TerminateProcess函数关掉子进程 CloseHandle(pi.hProcess); CloseHandle(pi.hThread); - _pid = pi.dwProcessId; + fprintf(fp, "\r\n\r\n#### pid=%d,cmd=%s #####\r\n\r\n", _pid, cmd.data()); InfoL << "start child proces " << _pid; } else { - WarnL << "start child proces fail: " << GetLastError(); + WarnL << "start child proces fail: " << get_uv_errmsg(); } + fclose(fp); #else _pid = fork(); if (_pid < 0) { @@ -125,16 +147,42 @@ static bool s_wait(pid_t pid,int *exit_code_ptr,bool block) { if (pid <= 0) { return false; } - int status = 0; #ifdef _WIN32 HANDLE hProcess = NULL; - hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); //打开目标进程 - if (hProcess == NULL) { + hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); //打开目标进程 + if (!hProcess) { + WarnL << "OpenProcess failed:" << get_uv_errmsg(); + return false; + } + + DWORD code = 0; + if (block) { + //一直等待 + code = WaitForSingleObject(hProcess, INFINITE); + } else { + code = WaitForSingleObject(hProcess, 0); + } + + if(code == WAIT_FAILED || code == WAIT_OBJECT_0){ + //子进程已经退出了,获取子进程退出代码 + DWORD exitCode = 0; + if(GetExitCodeProcess(hProcess, &exitCode) && exit_code_ptr){ + *exit_code_ptr = exitCode; + } + CloseHandle(hProcess); return false; } CloseHandle(hProcess); + if(code == WAIT_TIMEOUT){ + //子进程还在线 + return true; + } + //不太可能运行到此处 + WarnL << "WaitForSingleObject ret:" << code; + return false; #else + int status = 0; pid_t p = waitpid(pid, &status, block ? 0 : WNOHANG); int exit_code = (status & 0xFF00) >> 8; if (exit_code_ptr) { @@ -148,11 +196,42 @@ static bool s_wait(pid_t pid,int *exit_code_ptr,bool block) { InfoL << "process terminated, pid=" << pid << ", exit code=" << exit_code; return false; } -#endif // _WIN32 - return true; +#endif // _WIN32 } +#ifdef _WIN32 +// Inspired from http://stackoverflow.com/a/15281070/1529139 +// and http://stackoverflow.com/q/40059902/1529139 +bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent){ + bool success = false; + DWORD thisConsoleId = GetCurrentProcessId(); + // Leave current console if it exists + // (otherwise AttachConsole will return ERROR_ACCESS_DENIED) + bool consoleDetached = (FreeConsole() != FALSE); + + if (AttachConsole(dwProcessId) != FALSE){ + // Add a fake Ctrl-C handler for avoid instant kill is this console + // WARNING: do not revert it or current program will be also killed + SetConsoleCtrlHandler(nullptr, true); + success = (GenerateConsoleCtrlEvent(dwCtrlEvent, 0) != FALSE); + FreeConsole(); + } + + if (consoleDetached){ + // Create a new console if previous was deleted by OS + if (AttachConsole(thisConsoleId) == FALSE){ + int errorCode = GetLastError(); + if (errorCode == 31){ + // 31=ERROR_GEN_FAILURE + AllocConsole(); + } + } + } + return success; +} +#endif // _WIN32 + static void s_kill(pid_t pid,int max_delay,bool force){ if (pid <= 0) { //pid无效 @@ -161,13 +240,20 @@ static void s_kill(pid_t pid,int max_delay,bool force){ #ifdef _WIN32 HANDLE hProcess = NULL; hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); //打开目标进程 - if (hProcess == NULL) { - WarnL << "\nOpen Process fAiled: " << GetLastError(); + if (!hProcess) { + //子进程可能已经推出了 return; } - DWORD ret = TerminateProcess(hProcess, 0); //结束目标进程 - if (ret == 0) { - WarnL << GetLastError; + if(force){ + //强制关闭 + DWORD ret = TerminateProcess(hProcess, 0); //结束目标进程 + CloseHandle(hProcess); + if (ret == 0) { + WarnL << "TerminateProcess " << pid << " failed:" << get_uv_errmsg(); + } + }else{ + //非强制关闭,发生Ctr+C信号 + signalCtrl(pid, CTRL_C_EVENT); } #else if (::kill(pid, force ? SIGKILL : SIGTERM) == -1) { @@ -177,7 +263,6 @@ static void s_kill(pid_t pid,int max_delay,bool force){ } #endif // _WIN32 - if(force){ //发送SIGKILL信号后,阻塞等待退出 s_wait(pid, NULL, true); From 20e8e1d7ee39e8cc45bfd9e91d30fd8b6baeb4fa Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 8 Jun 2020 15:49:32 +0800 Subject: [PATCH 082/100] =?UTF-8?q?=E5=AE=8C=E5=96=84windows=E4=B8=8B?= =?UTF-8?q?=E5=AD=90=E8=BF=9B=E7=A8=8B=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/Process.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/server/Process.cpp b/server/Process.cpp index f877bdfc..1bc6bdd5 100644 --- a/server/Process.cpp +++ b/server/Process.cpp @@ -151,7 +151,7 @@ static bool s_wait(pid_t pid,int *exit_code_ptr,bool block) { HANDLE hProcess = NULL; hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); //打开目标进程 if (!hProcess) { - WarnL << "OpenProcess failed:" << get_uv_errmsg(); + //子进程不在线 return false; } @@ -166,7 +166,7 @@ static bool s_wait(pid_t pid,int *exit_code_ptr,bool block) { if(code == WAIT_FAILED || code == WAIT_OBJECT_0){ //子进程已经退出了,获取子进程退出代码 DWORD exitCode = 0; - if(GetExitCodeProcess(hProcess, &exitCode) && exit_code_ptr){ + if(exit_code_ptr && GetExitCodeProcess(hProcess, &exitCode)){ *exit_code_ptr = exitCode; } CloseHandle(hProcess); @@ -244,6 +244,9 @@ static void s_kill(pid_t pid,int max_delay,bool force){ //子进程可能已经推出了 return; } + //windows下目前没有比较好的手段往子进程发送SIGTERM或信号 + //所以杀死子进程的方式全部强制为立即关闭 + force = true; if(force){ //强制关闭 DWORD ret = TerminateProcess(hProcess, 0); //结束目标进程 From d0fc37db65da5f6be4eff2159adf7ab7c5de42bf Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 8 Jun 2020 15:51:12 +0800 Subject: [PATCH 083/100] =?UTF-8?q?=E4=BF=AE=E5=A4=8DFFmpeg=E6=8E=A8?= =?UTF-8?q?=E6=B5=81=E7=BB=99=E7=AC=AC=E4=B8=89=E6=96=B9=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8=E6=97=B6=E4=B8=8D=E9=87=8D=E8=AF=95=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/FFmpegSource.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/server/FFmpegSource.cpp b/server/FFmpegSource.cpp index 0ed137a2..50492f22 100644 --- a/server/FFmpegSource.cpp +++ b/server/FFmpegSource.cpp @@ -196,7 +196,19 @@ void FFmpegSource::startTimer(int timeout_ms) { //推流给其他服务器的,我们通过判断FFmpeg进程是否在线,如果FFmpeg推流中断,那么它应该会自动退出 if (!strongSelf->_process.wait(false)) { //ffmpeg不在线,重新拉流 - strongSelf->play(strongSelf->_src_url, strongSelf->_dst_url, timeout_ms, [](const SockException &) {}); + strongSelf->play(strongSelf->_src_url, strongSelf->_dst_url, timeout_ms, [weakSelf](const SockException &ex) { + if(!ex){ + //没有错误 + return; + } + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { + //自身已经销毁 + return; + } + //上次重试时间超过10秒,那么再重试FFmpeg拉流 + strongSelf->startTimer(10 * 1000); + }); } } return true; From a3089f9a7e623a3e373793fa760bf685a7f7ba82 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 8 Jun 2020 16:26:55 +0800 Subject: [PATCH 084/100] =?UTF-8?q?=E5=AE=8C=E5=96=84=E8=BF=9B=E7=A8=8B?= =?UTF-8?q?=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/Process.cpp | 74 +++++++++++++++++++--------------------------- server/Process.h | 1 + 2 files changed, 32 insertions(+), 43 deletions(-) diff --git a/server/Process.cpp b/server/Process.cpp index 1bc6bdd5..f0a101b1 100644 --- a/server/Process.cpp +++ b/server/Process.cpp @@ -61,16 +61,16 @@ void Process::run(const string &cmd, const string &log_file_tmp) { LPTSTR lpDir = const_cast(cmd.data()); if (CreateProcess(NULL, lpDir, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)){ //下面两行关闭句柄,解除本进程和新进程的关系,不然有可能 不小心调用TerminateProcess函数关掉子进程 - CloseHandle(pi.hProcess); CloseHandle(pi.hThread); _pid = pi.dwProcessId; - fprintf(fp, "\r\n\r\n#### pid=%d,cmd=%s #####\r\n\r\n", _pid, cmd.data()); - InfoL << "start child proces " << _pid; + _handle = pi.hProcess; + fprintf(fp, "\r\n\r\n#### pid=%d,cmd=%s #####\r\n\r\n", _pid, cmd.data()); + InfoL << "start child proces " << _pid << ", log file:" << log_file; } else { WarnL << "start child proces fail: " << get_uv_errmsg(); } - fclose(fp); -#else + fclose(fp); +#else _pid = fork(); if (_pid < 0) { throw std::runtime_error(StrPrinter << "fork child process falied,err:" << get_uv_errmsg()); @@ -125,6 +125,8 @@ void Process::run(const string &cmd, const string &log_file_tmp) { // EOF: NULL charpv_params[params.size()] = NULL; + InfoL << "start child proces " << _pid << ", log file:" << log_file; + // TODO: execv or execvp auto ret = execv(params[0].c_str(), charpv_params); if (ret < 0) { @@ -132,7 +134,6 @@ void Process::run(const string &cmd, const string &log_file_tmp) { } exit(ret); } - InfoL << "start child proces " << _pid; #endif // _WIN32 } @@ -143,42 +144,33 @@ void Process::run(const string &cmd, const string &log_file_tmp) { * @param block 是否阻塞等待 * @return 进程是否还在运行 */ -static bool s_wait(pid_t pid,int *exit_code_ptr,bool block) { +static bool s_wait(pid_t pid, void *handle, int *exit_code_ptr, bool block) { if (pid <= 0) { return false; } #ifdef _WIN32 - HANDLE hProcess = NULL; - hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); //打开目标进程 - if (!hProcess) { - //子进程不在线 - return false; - } - DWORD code = 0; if (block) { //一直等待 - code = WaitForSingleObject(hProcess, INFINITE); + code = WaitForSingleObject(handle, INFINITE); } else { - code = WaitForSingleObject(hProcess, 0); + code = WaitForSingleObject(handle, 0); } - + if(code == WAIT_FAILED || code == WAIT_OBJECT_0){ //子进程已经退出了,获取子进程退出代码 DWORD exitCode = 0; - if(exit_code_ptr && GetExitCodeProcess(hProcess, &exitCode)){ + if(exit_code_ptr && GetExitCodeProcess(handle, &exitCode)){ *exit_code_ptr = exitCode; } - CloseHandle(hProcess); return false; } - CloseHandle(hProcess); if(code == WAIT_TIMEOUT){ //子进程还在线 return true; } - //不太可能运行到此处 + //不太可能运行到此处 WarnL << "WaitForSingleObject ret:" << code; return false; #else @@ -232,30 +224,20 @@ bool signalCtrl(DWORD dwProcessId, DWORD dwCtrlEvent){ } #endif // _WIN32 -static void s_kill(pid_t pid,int max_delay,bool force){ +static void s_kill(pid_t pid, void *handle, int max_delay, bool force) { if (pid <= 0) { //pid无效 return; } #ifdef _WIN32 - HANDLE hProcess = NULL; - hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); //打开目标进程 - if (!hProcess) { - //子进程可能已经推出了 - return; - } //windows下目前没有比较好的手段往子进程发送SIGTERM或信号 //所以杀死子进程的方式全部强制为立即关闭 force = true; if(force){ - //强制关闭 - DWORD ret = TerminateProcess(hProcess, 0); //结束目标进程 - CloseHandle(hProcess); - if (ret == 0) { - WarnL << "TerminateProcess " << pid << " failed:" << get_uv_errmsg(); - } + //强制关闭子进程 + TerminateProcess(handle, 0); }else{ - //非强制关闭,发生Ctr+C信号 + //非强制关闭,发送Ctr+C信号 signalCtrl(pid, CTRL_C_EVENT); } #else @@ -266,32 +248,38 @@ static void s_kill(pid_t pid,int max_delay,bool force){ } #endif // _WIN32 - if(force){ + if (force) { //发送SIGKILL信号后,阻塞等待退出 - s_wait(pid, NULL, true); + s_wait(pid, handle, nullptr, true); DebugL << "force kill " << pid << " success!"; return; } //发送SIGTERM信号后,2秒后检查子进程是否已经退出 - WorkThreadPool::Instance().getPoller()->doDelayTask(max_delay,[pid](){ - if (!s_wait(pid, nullptr, false)) { + WorkThreadPool::Instance().getPoller()->doDelayTask(max_delay, [pid, handle]() { + if (!s_wait(pid, handle, nullptr, false)) { //进程已经退出了 return 0; } //进程还在运行 WarnL << "process still working,force kill it:" << pid; - s_kill(pid,0, true); + s_kill(pid, handle, 0, true); return 0; }); } -void Process::kill(int max_delay,bool force) { +void Process::kill(int max_delay, bool force) { if (_pid <= 0) { return; } - s_kill(_pid,max_delay,force); + s_kill(_pid, _handle, max_delay, force); _pid = -1; +#ifdef _WIN32 + if(_handle){ + CloseHandle(_handle); + _handle = nullptr; + } +#endif } Process::~Process() { @@ -301,7 +289,7 @@ Process::~Process() { Process::Process() {} bool Process::wait(bool block) { - return s_wait(_pid,&_exit_code,block); + return s_wait(_pid, _handle, &_exit_code, block); } int Process::exit_code() { diff --git a/server/Process.h b/server/Process.h index 3c870f9d..e408649c 100644 --- a/server/Process.h +++ b/server/Process.h @@ -31,6 +31,7 @@ public: int exit_code(); private: pid_t _pid = -1; + void *_handle = nullptr; int _exit_code = 0; }; From ab847ff575cc6caee2f1b612ebece81a24533e88 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 8 Jun 2020 16:41:49 +0800 Subject: [PATCH 085/100] =?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 --- server/Process.cpp | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/server/Process.cpp b/server/Process.cpp index f0a101b1..10ff1e59 100644 --- a/server/Process.cpp +++ b/server/Process.cpp @@ -38,15 +38,14 @@ void Process::run(const string &cmd, const string &log_file_tmp) { if (log_file_tmp.empty()) { //未指定子进程日志文件时,重定向至/dev/null log_file = "NUL"; - } - else { + } else { log_file = StrPrinter << log_file_tmp << "." << getCurrentMillisecond(); } //重定向shell日志至文件 auto fp = File::create_file(log_file.data(), "ab"); if (!fp) { - fprintf(stderr, "open log file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno)); + fprintf(stderr, "open log file %s failed:%d(%s)\r\n", log_file.data(), get_uv_error(), get_uv_errmsg()); } else { auto log_fd = (HANDLE)(_get_osfhandle(fileno(fp))); // dup to stdout and stderr. @@ -65,15 +64,23 @@ void Process::run(const string &cmd, const string &log_file_tmp) { _pid = pi.dwProcessId; _handle = pi.hProcess; fprintf(fp, "\r\n\r\n#### pid=%d,cmd=%s #####\r\n\r\n", _pid, cmd.data()); - InfoL << "start child proces " << _pid << ", log file:" << log_file; + InfoL << "start child process " << _pid << ", log file:" << log_file; } else { - WarnL << "start child proces fail: " << get_uv_errmsg(); + WarnL << "start child process fail: " << get_uv_errmsg(); } fclose(fp); #else + string log_file; + if (log_file_tmp.empty()) { + //未指定子进程日志文件时,重定向至/dev/null + log_file = "/dev/null"; + } else { + log_file = StrPrinter << log_file_tmp << "." << getpid(); + } + _pid = fork(); if (_pid < 0) { - throw std::runtime_error(StrPrinter << "fork child process falied,err:" << get_uv_errmsg()); + throw std::runtime_error(StrPrinter << "fork child process failed,err:" << get_uv_errmsg()); } if (_pid == 0) { //子进程关闭core文件生成 @@ -84,26 +91,18 @@ void Process::run(const string &cmd, const string &log_file_tmp) { signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); - string log_file; - if (log_file_tmp.empty()) { - //未指定子进程日志文件时,重定向至/dev/null - log_file = "/dev/null"; - } else { - log_file = StrPrinter << log_file_tmp << "." << getpid(); - } - //重定向shell日志至文件 auto fp = File::create_file(log_file.data(), "ab"); if (!fp) { - fprintf(stderr, "open log file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno)); + fprintf(stderr, "open log file %s failed:%d(%s)\r\n", log_file.data(), get_uv_error(), get_uv_errmsg()); } else { auto log_fd = fileno(fp); // 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)); + fprintf(stderr, "dup2 stdout file %s failed:%d(%s)\r\n", log_file.data(), get_uv_error(), get_uv_errmsg()); } if (dup2(log_fd, STDERR_FILENO) < 0) { - fprintf(stderr, "dup2 stderr file %s failed:%d(%s)\r\n", log_file.data(), errno, strerror(errno)); + fprintf(stderr, "dup2 stderr file %s failed:%d(%s)\r\n", log_file.data(), get_uv_error(), get_uv_errmsg()); } // 关闭日志文件 ::fclose(fp); @@ -125,15 +124,15 @@ void Process::run(const string &cmd, const string &log_file_tmp) { // EOF: NULL charpv_params[params.size()] = NULL; - InfoL << "start child proces " << _pid << ", log file:" << log_file; - // 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)); + fprintf(stderr, "fork process failed:%d(%s)\r\n", get_uv_error(), get_uv_errmsg()); } exit(ret); } + + InfoL << "start child process " << _pid << ", log file:" << log_file; #endif // _WIN32 } From 72e8ab6ced9c64a347c7d0ae2b32eb6fe4956051 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 8 Jun 2020 18:59:32 +0800 Subject: [PATCH 086/100] =?UTF-8?q?=E6=8F=90=E9=AB=98=E6=80=A7=E8=83=BD?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E5=B7=A5=E5=85=B7=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_benchmark.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_benchmark.cpp b/tests/test_benchmark.cpp index a3214bc8..75693fd9 100644 --- a/tests/test_benchmark.cpp +++ b/tests/test_benchmark.cpp @@ -44,6 +44,10 @@ int main(int argc, char *argv[]) { auto playerCnt = atoi(argv[1]);//启动的播放器个数 atomic_int alivePlayerCnt(0); + //由于所有播放器都是再一个timer里面创建的,默认情况下所有播放器会绑定该timer所在的poller线程 + //为了提高性能,poller分配策略关闭优先返回当前线程的策略 + EventPollerPool::Instance().preferCurrentThread(false); + //每隔若干毫秒启动一个播放器(如果一次性全部启动,服务器和客户端可能都承受不了) Timer timer0(atoi(argv[2])/1000.0f,[&]() { MediaPlayer::Ptr player(new MediaPlayer()); From aca6c18e56983d7f5e7aacb495f1e441719b5845 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 8 Jun 2020 19:21:45 +0800 Subject: [PATCH 087/100] =?UTF-8?q?=E9=99=90=E5=88=B6select=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=8F=8F=E8=BF=B0=E7=AC=A6=E4=B8=AA=E6=95=B0?= 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 bbb77017..b655b5c5 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit bbb77017bfa48d3abaaa0bf7ab890333141c2dbc +Subproject commit b655b5c537d5d2ed75bc2b89b7827bc906e8f378 From 487c993baf84a48e5ee59075ec7530fc18858503 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 8 Jun 2020 20:03:51 +0800 Subject: [PATCH 088/100] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dselect=E5=85=A8?= =?UTF-8?q?=E5=B1=80=E9=99=90=E5=88=B6=E6=96=87=E4=BB=B6=E6=8F=8F=E8=BF=B0?= =?UTF-8?q?=E7=AC=A6=E4=B8=AA=E6=95=B0=E7=9A=84bug:#333?= 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 b655b5c5..5030af90 160000 --- a/3rdpart/ZLToolKit +++ b/3rdpart/ZLToolKit @@ -1 +1 @@ -Subproject commit b655b5c537d5d2ed75bc2b89b7827bc906e8f378 +Subproject commit 5030af90126ea8f01ded6744ae8abdf549d00a81 From ca8e65f75fb843e7477d80f390205f7358ae773d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Tue, 9 Jun 2020 10:14:09 +0800 Subject: [PATCH 089/100] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bf5483f5..4b2c2edf 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,7 @@ bash build_docker_images.sh - [基于ZLMediaKit主线的管理WEB网站](https://gitee.com/kkkkk5G/MediaServerUI) - [DotNetCore的RESTful客户端](https://github.com/MingZhuLiu/ZLMediaKit.DotNetCore.Sdk) - [GB28181-2016网络视频平台](https://github.com/swwheihei/wvp) + - [node-js版本的GB28181平台]https://gitee.com/hfwudao/GB28181_Node_Http ## 授权协议 From 6091d87b5f4be06cf61f033fd7d2d430efc1bfdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=8F=E6=A5=9A?= <771730766@qq.com> Date: Tue, 9 Jun 2020 10:14:35 +0800 Subject: [PATCH 090/100] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b2c2edf..414be43c 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ bash build_docker_images.sh - [基于ZLMediaKit主线的管理WEB网站](https://gitee.com/kkkkk5G/MediaServerUI) - [DotNetCore的RESTful客户端](https://github.com/MingZhuLiu/ZLMediaKit.DotNetCore.Sdk) - [GB28181-2016网络视频平台](https://github.com/swwheihei/wvp) - - [node-js版本的GB28181平台]https://gitee.com/hfwudao/GB28181_Node_Http + - [node-js版本的GB28181平台](https://gitee.com/hfwudao/GB28181_Node_Http) ## 授权协议 From 7df092074d35e1befa6792d601973d0ddc0d78e9 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Tue, 9 Jun 2020 14:21:57 +0800 Subject: [PATCH 091/100] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dm3u8=E8=B5=B7?= =?UTF-8?q?=E5=A7=8B=E9=98=B6=E6=AE=B5SEQUENCE=E9=94=99=E8=AF=AF=E7=9A=84b?= =?UTF-8?q?ug:#288?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Record/HlsMaker.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Record/HlsMaker.cpp b/src/Record/HlsMaker.cpp index c54c0e62..0ae69d16 100644 --- a/src/Record/HlsMaker.cpp +++ b/src/Record/HlsMaker.cpp @@ -13,8 +13,6 @@ namespace mediakit { HlsMaker::HlsMaker(float seg_duration, uint32_t seg_number) { //最小允许设置为0,0个切片代表点播 - seg_number = MAX(0,seg_number); - seg_duration = MAX(1,seg_duration); _seg_number = seg_number; _seg_duration = seg_duration; } @@ -34,6 +32,8 @@ void HlsMaker::makeIndexFile(bool eof) { } } + auto sequence = _seg_number ? (_file_index > _seg_number ? _file_index - _seg_number : 0LL) : 0LL; + string m3u8; snprintf(file_content,sizeof(file_content), "#EXTM3U\n" @@ -42,7 +42,7 @@ void HlsMaker::makeIndexFile(bool eof) { "#EXT-X-TARGETDURATION:%u\n" "#EXT-X-MEDIA-SEQUENCE:%llu\n", (maxSegmentDuration + 999) / 1000, - _seg_number ? _file_index : 0); + sequence); m3u8.assign(file_content); From cecfe7ba54e81717b728dd9955579f2f64141df7 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 10 Jun 2020 10:33:48 +0800 Subject: [PATCH 092/100] =?UTF-8?q?=E6=88=AA=E5=9B=BEapi=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- README_en.md | 2 +- conf/config.ini | 2 ++ server/WebApi.cpp | 34 ++++++++++++++++++++++------------ logo.png => www/logo.png | Bin 5 files changed, 26 insertions(+), 14 deletions(-) rename logo.png => www/logo.png (100%) diff --git a/README.md b/README.md index 414be43c..0b022a90 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![logo](https://raw.githubusercontent.com/zlmediakit/ZLMediaKit/master/logo.png) +![logo](https://raw.githubusercontent.com/zlmediakit/ZLMediaKit/master/www/logo.png) [english readme](https://github.com/xiongziliang/ZLMediaKit/blob/master/README_en.md) diff --git a/README_en.md b/README_en.md index 6e261981..485bd5ce 100644 --- a/README_en.md +++ b/README_en.md @@ -1,4 +1,4 @@ -![logo](https://raw.githubusercontent.com/zlmediakit/ZLMediaKit/master/logo.png) +![logo](https://raw.githubusercontent.com/zlmediakit/ZLMediaKit/master/www/logo.png) # A lightweight ,high performance and stable stream server and client framework based on C++11. diff --git a/conf/config.ini b/conf/config.ini index 981b458e..8f5fe93f 100644 --- a/conf/config.ini +++ b/conf/config.ini @@ -6,6 +6,8 @@ apiDebug=1 secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc #截图保存路径根目录,截图通过http api(/index/api/getSnap)生成和获取 snapRoot=./www/snap/ +#默认截图图片,在启动FFmpeg截图后但是截图还未生成时,可以返回默认的预设图片 +defaultSnap=./www/logo.png [ffmpeg] #FFmpeg可执行程序绝对路径 diff --git a/server/WebApi.cpp b/server/WebApi.cpp index 2ee5d1f6..d596604b 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -8,11 +8,12 @@ * may be found in the AUTHORS file in the root of the source tree. */ +#include +#include #include #include #include #include -#include #include "jsoncpp/json.h" #include "Util/util.h" #include "Util/logger.h" @@ -51,12 +52,13 @@ typedef enum { const string kApiDebug = API_FIELD"apiDebug"; const string kSecret = API_FIELD"secret"; const string kSnapRoot = API_FIELD"snapRoot"; +const string kDefaultSnap = API_FIELD"defaultSnap"; static onceToken token([]() { mINI::Instance()[kApiDebug] = "1"; mINI::Instance()[kSecret] = "035c73f7-bb6b-4889-a715-d9eb2d1925cc"; mINI::Instance()[kSnapRoot] = "./www/snap/"; - + mINI::Instance()[kDefaultSnap] = "./www/logo.png"; }); }//namespace API @@ -850,24 +852,32 @@ void installWebApi() { if(!snap_path.empty()){ StrCaseMap headerOut; - headerOut["Content-Type"] = HttpFileManager::getContentType(".jpeg"); + struct stat statbuf = {0}; + GET_CONFIG(string, defaultSnap, API::kDefaultSnap); + if (!defaultSnap.empty() && !(stat(snap_path.data(), &statbuf) == 0 && statbuf.st_size != 0)) { + //空文件,则返回预设图片(也就是FFmpeg生成截图中空档期的默认图片) + snap_path = File::absolutePath(defaultSnap, ""); + headerOut["Content-Type"] = HttpFileManager::getContentType(snap_path.data()); + } else { + //之前生成的截图文件,我们默认为jpeg格式 + headerOut["Content-Type"] = HttpFileManager::getContentType(".jpeg"); + } + //返回图片给http客户端 invoker.responseFile(headerIn,headerOut,snap_path); - return ; + return; } //无截图或者截图已经过期 snap_path = StrPrinter << scan_path << time(NULL) << ".jpeg"; - { - //生成一个空文件,目的是顺便创建文件夹路径, - //同时防止在FFmpeg生成截图途中不停的尝试调用该api启动FFmpeg生成相同的截图 - //当然,我们可以拷贝一个"正在截图中"的图来替换这个空图,这需要开发者自己实现 - auto file = File::create_file(snap_path.data(), "wb"); - if(file){ - fclose(file); - } + //生成一个空文件,目的是顺便创建文件夹路径, + //同时防止在FFmpeg生成截图途中不停的尝试调用该api启动FFmpeg生成相同的截图 + auto file = File::create_file(snap_path.data(), "wb"); + if (file) { + fclose(file); } + //启动FFmpeg进程,开始截图 FFmpegSnap::makeSnap(allArgs["url"],snap_path,allArgs["timeout_sec"],[invoker,headerIn,snap_path](bool success){ if(!success){ //生成截图失败,可能残留空文件 diff --git a/logo.png b/www/logo.png similarity index 100% rename from logo.png rename to www/logo.png From 9c646d13168170eda85557ec549ec1c94f0449d1 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Wed, 10 Jun 2020 10:50:37 +0800 Subject: [PATCH 093/100] =?UTF-8?q?=E5=AE=8C=E5=96=84=E9=A2=84=E8=AE=BE?= =?UTF-8?q?=E6=88=AA=E5=9B=BE=E7=9B=B8=E5=85=B3=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/WebApi.cpp | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/server/WebApi.cpp b/server/WebApi.cpp index d596604b..20a653aa 100644 --- a/server/WebApi.cpp +++ b/server/WebApi.cpp @@ -821,13 +821,31 @@ void installWebApi() { val["data"]["paths"] = paths; }); - GET_CONFIG(string, snap_root, API::kSnapRoot); + static auto responseSnap = [](const string &snap_path, + const HttpSession::KeyValue &headerIn, + const HttpSession::HttpResponseInvoker &invoker) { + StrCaseMap headerOut; + struct stat statbuf = {0}; + GET_CONFIG(string, defaultSnap, API::kDefaultSnap); + if (!(stat(snap_path.data(), &statbuf) == 0 && statbuf.st_size != 0) && !defaultSnap.empty()) { + //空文件且设置了预设图,则返回预设图片(也就是FFmpeg生成截图中空档期的默认图片) + const_cast(snap_path) = File::absolutePath(defaultSnap, ""); + headerOut["Content-Type"] = HttpFileManager::getContentType(snap_path.data()); + } else { + //之前生成的截图文件,我们默认为jpeg格式 + headerOut["Content-Type"] = HttpFileManager::getContentType(".jpeg"); + } + //返回图片给http客户端 + invoker.responseFile(headerIn, headerOut, snap_path); + }; //获取截图缓存或者实时截图 //http://127.0.0.1/index/api/getSnap?url=rtmp://127.0.0.1/record/robot.mp4&timeout_sec=10&expire_sec=3 api_regist2("/index/api/getSnap", [](API_ARGS2){ CHECK_SECRET(); CHECK_ARGS("url", "timeout_sec", "expire_sec"); + GET_CONFIG(string, snap_root, API::kSnapRoot); + int expire_sec = allArgs["expire_sec"]; auto scan_path = File::absolutePath(MD5(allArgs["url"]).hexdigest(), snap_root) + "/"; string snap_path; @@ -851,19 +869,7 @@ void installWebApi() { }); if(!snap_path.empty()){ - StrCaseMap headerOut; - struct stat statbuf = {0}; - GET_CONFIG(string, defaultSnap, API::kDefaultSnap); - if (!defaultSnap.empty() && !(stat(snap_path.data(), &statbuf) == 0 && statbuf.st_size != 0)) { - //空文件,则返回预设图片(也就是FFmpeg生成截图中空档期的默认图片) - snap_path = File::absolutePath(defaultSnap, ""); - headerOut["Content-Type"] = HttpFileManager::getContentType(snap_path.data()); - } else { - //之前生成的截图文件,我们默认为jpeg格式 - headerOut["Content-Type"] = HttpFileManager::getContentType(".jpeg"); - } - //返回图片给http客户端 - invoker.responseFile(headerIn,headerOut,snap_path); + responseSnap(snap_path, headerIn, invoker); return; } @@ -883,10 +889,7 @@ void installWebApi() { //生成截图失败,可能残留空文件 File::delete_file(snap_path.data()); } - - StrCaseMap headerOut; - headerOut["Content-Type"] = HttpFileManager::getContentType(".jpeg"); - invoker.responseFile(headerIn, headerOut, snap_path); + responseSnap(snap_path, headerIn, invoker); }); }); From 6b7e4f5f52b530c2bedaba2c2ddbedce7821e0e2 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 11 Jun 2020 09:22:28 +0800 Subject: [PATCH 094/100] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=AD=90=E8=BF=9B?= =?UTF-8?q?=E7=A8=8B=E6=97=A5=E5=BF=97=E8=B7=AF=E5=BE=84=E5=9B=BA=E5=AE=9A?= =?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 --- server/Process.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/server/Process.cpp b/server/Process.cpp index 10ff1e59..7a5f7c18 100644 --- a/server/Process.cpp +++ b/server/Process.cpp @@ -70,19 +70,18 @@ void Process::run(const string &cmd, const string &log_file_tmp) { } fclose(fp); #else - string log_file; - if (log_file_tmp.empty()) { - //未指定子进程日志文件时,重定向至/dev/null - log_file = "/dev/null"; - } else { - log_file = StrPrinter << log_file_tmp << "." << getpid(); - } - _pid = fork(); if (_pid < 0) { throw std::runtime_error(StrPrinter << "fork child process failed,err:" << get_uv_errmsg()); } if (_pid == 0) { + string log_file; + if (log_file_tmp.empty()) { + //未指定子进程日志文件时,重定向至/dev/null + log_file = "/dev/null"; + } else { + log_file = StrPrinter << log_file_tmp << "." << getpid(); + } //子进程关闭core文件生成 struct rlimit rlim = {0, 0}; setrlimit(RLIMIT_CORE, &rlim); @@ -132,6 +131,13 @@ void Process::run(const string &cmd, const string &log_file_tmp) { exit(ret); } + string log_file; + if (log_file_tmp.empty()) { + //未指定子进程日志文件时,重定向至/dev/null + log_file = "/dev/null"; + } else { + log_file = StrPrinter << log_file_tmp << "." << _pid; + } InfoL << "start child process " << _pid << ", log file:" << log_file; #endif // _WIN32 } From 0e47dc94f985d68fada52a207c39774310a37d9d Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 11 Jun 2020 17:19:10 +0800 Subject: [PATCH 095/100] =?UTF-8?q?aac=20config=E4=B8=8D=E5=86=8D=E9=99=90?= =?UTF-8?q?=E5=88=B6=E4=B8=A4=E4=B8=AA=E5=AD=97=E8=8A=82=EF=BC=9A#336?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Extension/AAC.h | 10 +++++++--- src/Extension/Factory.cpp | 17 ++++++----------- src/Record/MP4Muxer.cpp | 3 ++- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/Extension/AAC.h b/src/Extension/AAC.h index feb65bd7..d53b7f49 100644 --- a/src/Extension/AAC.h +++ b/src/Extension/AAC.h @@ -77,7 +77,7 @@ public: if(aac_cfg.size() < 2){ throw std::invalid_argument("adts配置必须最少2个字节"); } - _cfg = aac_cfg.substr(0,2); + _cfg = aac_cfg; onReady(); } @@ -185,8 +185,12 @@ public: _printer << "b=AS:" << bitrate << "\r\n"; _printer << "a=rtpmap:" << payload_type << " MPEG4-GENERIC/" << sample_rate << "/" << channels << "\r\n"; - char configStr[32] = {0}; - snprintf(configStr, sizeof(configStr), "%02X%02X", (uint8_t)aac_cfg[0], (uint8_t)aac_cfg[1]); + string configStr; + char buf[4] = {0}; + for(auto &ch : aac_cfg){ + snprintf(buf, sizeof(buf), "%02X", (uint8_t)ch); + configStr.append(buf); + } _printer << "a=fmtp:" << payload_type << " streamtype=5;profile-level-id=1;mode=AAC-hbr;" << "sizelength=13;indexlength=3;indexdeltalength=3;config=" << configStr << "\r\n"; _printer << "a=control:trackID=" << (int)TrackAudio << "\r\n"; diff --git a/src/Extension/Factory.cpp b/src/Extension/Factory.cpp index 276ce140..3a8fce31 100644 --- a/src/Extension/Factory.cpp +++ b/src/Extension/Factory.cpp @@ -33,17 +33,12 @@ Track::Ptr Factory::getTrackBySdp(const SdpTrack::Ptr &track) { return nullptr; } string aac_cfg; - - unsigned int cfg1; - sscanf(aac_cfg_str.substr(0, 2).data(), "%02X", &cfg1); - cfg1 &= 0x00FF; - aac_cfg.push_back(cfg1); - - unsigned int cfg2; - sscanf(aac_cfg_str.substr(2, 2).data(), "%02X", &cfg2); - cfg2 &= 0x00FF; - aac_cfg.push_back(cfg2); - + for(int i = 0 ; i < aac_cfg_str.size() / 2 ; ++i ){ + unsigned int cfg; + sscanf(aac_cfg_str.substr(i * 2, 2).data(), "%02X", &cfg); + cfg &= 0x00FF; + aac_cfg.push_back((char)cfg); + } return std::make_shared(aac_cfg); } diff --git a/src/Record/MP4Muxer.cpp b/src/Record/MP4Muxer.cpp index a543126a..c62c3dbb 100644 --- a/src/Record/MP4Muxer.cpp +++ b/src/Record/MP4Muxer.cpp @@ -202,7 +202,8 @@ void MP4Muxer::addTrack(const Track::Ptr &track) { audio_track->getAudioChannel(), audio_track->getAudioSampleBit() * audio_track->getAudioChannel(), audio_track->getAudioSampleRate(), - audio_track->getAacCfg().data(), 2); + audio_track->getAacCfg().data(), + audio_track->getAacCfg().size()); if(track_id < 0){ WarnL << "添加AAC Track失败:" << track_id; return; From b9006a90d44641e3cb960fb50bfe5ec2008d36df Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 11 Jun 2020 17:40:09 +0800 Subject: [PATCH 096/100] =?UTF-8?q?=E4=BC=98=E5=8C=96rtmp=E6=8E=A8?= =?UTF-8?q?=E6=B5=81=E6=97=B6=EF=BC=8Caac=20config=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E4=B8=8D=E5=AE=8C=E6=95=B4=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Extension/AAC.h | 13 ++++++++++--- src/Extension/AACRtmp.cpp | 25 ++++++++++++++++--------- src/Extension/AACRtmp.h | 11 +++++++---- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/Extension/AAC.h b/src/Extension/AAC.h index d53b7f49..6a3bd8ba 100644 --- a/src/Extension/AAC.h +++ b/src/Extension/AAC.h @@ -71,10 +71,17 @@ public: /** * 构造aac类型的媒体 - * @param aac_cfg aac两个字节的配置信息 + * @param aac_cfg aac配置信息 */ AACTrack(const string &aac_cfg){ - if(aac_cfg.size() < 2){ + setAacCfg(aac_cfg); + } + + /** + * 设置aac 配置信息 + */ + void setAacCfg(const string &aac_cfg){ + if (aac_cfg.size() < 2) { throw std::invalid_argument("adts配置必须最少2个字节"); } _cfg = aac_cfg; @@ -82,7 +89,7 @@ public: } /** - * 获取aac两个字节的配置 + * 获取aac 配置信息 */ const string &getAacCfg() const{ return _cfg; diff --git a/src/Extension/AACRtmp.cpp b/src/Extension/AACRtmp.cpp index 2009dbc9..05a5af52 100644 --- a/src/Extension/AACRtmp.cpp +++ b/src/Extension/AACRtmp.cpp @@ -13,8 +13,9 @@ namespace mediakit{ -AACRtmpDecoder::AACRtmpDecoder() { - _adts = obtainFrame(); +AACRtmpDecoder::AACRtmpDecoder(const Track::Ptr &track) { + _frame = obtainFrame(); + _track = dynamic_pointer_cast(track); } AACFrame::Ptr AACRtmpDecoder::obtainFrame() { @@ -38,13 +39,19 @@ static string getAacCfg(const RtmpPacket &thiz) { WarnL << "bad aac cfg!"; return ret; } - ret = thiz.strBuf.substr(2, 2); + ret = thiz.strBuf.substr(2); return ret; } bool AACRtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt, bool) { if (pkt->isCfgFrame()) { _aac_cfg = getAacCfg(*pkt); + if (_track) { + //设置aac config + _track->setAacCfg(_aac_cfg); + //不再强引用 + _track = nullptr; + } return false; } if (!_aac_cfg.empty()) { @@ -54,20 +61,20 @@ bool AACRtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt, bool) { } void AACRtmpDecoder::onGetAAC(const char* data, int len, uint32_t stamp) { - _adts->_dts = stamp; + _frame->_dts = stamp; //先追加数据 - _adts->_buffer.append(data, len); + _frame->_buffer.append(data, len); //覆盖adts头 - dumpAacConfig(_aac_cfg, _adts->size(), (uint8_t *) _adts->data()); + dumpAacConfig(_aac_cfg, _frame->size(), (uint8_t *) _frame->data()); //写入环形缓存 - RtmpCodec::inputFrame(_adts); - _adts = obtainFrame(); + RtmpCodec::inputFrame(_frame); + _frame = obtainFrame(); } ///////////////////////////////////////////////////////////////////////////////////// -AACRtmpEncoder::AACRtmpEncoder(const Track::Ptr &track) { +AACRtmpEncoder::AACRtmpEncoder(const Track::Ptr &track): AACRtmpDecoder(track) { _track = dynamic_pointer_cast(track); } diff --git a/src/Extension/AACRtmp.h b/src/Extension/AACRtmp.h index 653ac122..0f5d2f4b 100644 --- a/src/Extension/AACRtmp.h +++ b/src/Extension/AACRtmp.h @@ -23,7 +23,7 @@ class AACRtmpDecoder : public RtmpCodec , public ResourcePoolHelper { public: typedef std::shared_ptr Ptr; - AACRtmpDecoder(); + AACRtmpDecoder(const Track::Ptr &track); ~AACRtmpDecoder() {} /** @@ -38,10 +38,12 @@ public: } protected: - void onGetAAC(const char* data, int len, uint32_t stamp); + void onGetAAC(const char *data, int len, uint32_t stamp); AACFrame::Ptr obtainFrame(); + protected: - AACFrame::Ptr _adts; + AACFrame::Ptr _frame; + AACTrack::Ptr _track; string _aac_cfg; }; @@ -72,11 +74,12 @@ public: * 生成config包 */ void makeConfigPacket() override; + private: void makeAudioConfigPkt(); + private: uint8_t _audio_flv_flags; - AACTrack::Ptr _track; }; }//namespace mediakit From efa92752c7bc06b6bdbaa56fc89daec11c3f2278 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 11 Jun 2020 19:21:46 +0800 Subject: [PATCH 097/100] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=AF=B9=E9=AB=98?= =?UTF-8?q?=E8=A7=84=E6=A0=BCaac=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/Extension/AAC.cpp | 48 ++++++++++++++++++++++++++++++++++--- src/Extension/AAC.h | 10 ++++---- src/Extension/AACRtmp.cpp | 50 ++++++++++++++++----------------------- src/Extension/AACRtmp.h | 11 ++++----- src/Extension/AACRtp.cpp | 45 +++++++++++++++++++---------------- src/Extension/AACRtp.h | 9 +++++-- 6 files changed, 107 insertions(+), 66 deletions(-) diff --git a/src/Extension/AAC.cpp b/src/Extension/AAC.cpp index e138ccf2..b034a462 100644 --- a/src/Extension/AAC.cpp +++ b/src/Extension/AAC.cpp @@ -9,6 +9,9 @@ */ #include "AAC.h" +#ifdef ENABLE_MP4 +#include "mpeg4-aac.h" +#endif namespace mediakit{ @@ -89,7 +92,8 @@ static void parseAacConfig(const string &config, AdtsHeader &adts) { adts.no_raw_data_blocks_in_frame = 0; } -string makeAacConfig(const uint8_t *hex){ +string makeAacConfig(const uint8_t *hex, int length){ +#ifndef ENABLE_MP4 if (!(hex[0] == 0xFF && (hex[1] & 0xF0) == 0xF0)) { return ""; } @@ -112,20 +116,58 @@ string makeAacConfig(const uint8_t *hex){ audioSpecificConfig[0] = (audioObjectType << 3) | (sampling_frequency_index >> 1); audioSpecificConfig[1] = (sampling_frequency_index << 7) | (channel_configuration << 3); return string((char *)audioSpecificConfig,2); +#else + struct mpeg4_aac_t aac = {0}; + if (mpeg4_aac_adts_load(hex, length, &aac) > 0) { + char buf[32] = {0}; + int len = mpeg4_aac_audio_specific_config_save(&aac, (uint8_t *) buf, sizeof(buf)); + if (len > 0) { + return string(buf, len); + } + } + WarnL << "生成aac config失败, adts header:" << hexdump(hex, length); + return ""; +#endif } -void dumpAacConfig(const string &config, int length, uint8_t *out){ +int dumpAacConfig(const string &config, int length, uint8_t *out, int out_size) { +#ifndef ENABLE_MP4 AdtsHeader header; parseAacConfig(config, header); header.aac_frame_length = length; dumpAdtsHeader(header, out); + return ADTS_HEADER_LEN; +#else + struct mpeg4_aac_t aac = {0}; + int ret = mpeg4_aac_audio_specific_config_load((uint8_t *) config.data(), config.size(), &aac); + if (ret > 0) { + ret = mpeg4_aac_adts_save(&aac, length, out, out_size); + } + if (ret < 0) { + WarnL << "生成adts头失败:" << ret << ", aac config:" << hexdump(config.data(), config.size()); + } + return ret; +#endif } -void parseAacConfig(const string &config, int &samplerate, int &channels){ +bool parseAacConfig(const string &config, int &samplerate, int &channels){ +#ifndef ENABLE_MP4 AdtsHeader header; parseAacConfig(config, header); samplerate = samplingFrequencyTable[header.sf_index]; channels = header.channel_configuration; + return true; +#else + struct mpeg4_aac_t aac = {0}; + int ret = mpeg4_aac_audio_specific_config_load((uint8_t *) config.data(), config.size(), &aac); + if (ret > 0) { + samplerate = aac.sampling_frequency; + channels = aac.channels; + return true; + } + WarnL << "获取aac采样率、声道数失败:" << hexdump(config.data(), config.size()); + return false; +#endif } Sdp::Ptr AACTrack::getSdp() { diff --git a/src/Extension/AAC.h b/src/Extension/AAC.h index 6a3bd8ba..e024bb90 100644 --- a/src/Extension/AAC.h +++ b/src/Extension/AAC.h @@ -17,9 +17,9 @@ namespace mediakit{ -string makeAacConfig(const uint8_t *hex); -void dumpAacConfig(const string &config, int length, uint8_t *out); -void parseAacConfig(const string &config, int &samplerate, int &channels); +string makeAacConfig(const uint8_t *hex, int length); +int dumpAacConfig(const string &config, int length, uint8_t *out, int out_size); +bool parseAacConfig(const string &config, int &samplerate, int &channels); /** * aac帧,包含adts头 @@ -137,9 +137,9 @@ public: void inputFrame(const Frame::Ptr &frame) override{ if (_cfg.empty()) { //未获取到aac_cfg信息 - if (frame->prefixSize() >= ADTS_HEADER_LEN) { + if (frame->prefixSize()) { //7个字节的adts头 - _cfg = makeAacConfig((uint8_t *) (frame->data())); + _cfg = makeAacConfig((uint8_t *) (frame->data()), frame->prefixSize()); onReady(); } else { WarnL << "无法获取adts头!"; diff --git a/src/Extension/AACRtmp.cpp b/src/Extension/AACRtmp.cpp index 05a5af52..48102a2d 100644 --- a/src/Extension/AACRtmp.cpp +++ b/src/Extension/AACRtmp.cpp @@ -13,20 +13,6 @@ namespace mediakit{ -AACRtmpDecoder::AACRtmpDecoder(const Track::Ptr &track) { - _frame = obtainFrame(); - _track = dynamic_pointer_cast(track); -} - -AACFrame::Ptr AACRtmpDecoder::obtainFrame() { - //从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象 - auto frame = ResourcePoolHelper::obtainObj(); - frame->_prefix_size = ADTS_HEADER_LEN; - //预留7个字节的空位以便后续覆盖 - frame->_buffer.assign(ADTS_HEADER_LEN,(char)0); - return frame; -} - static string getAacCfg(const RtmpPacket &thiz) { string ret; if (thiz.getMediaType() != FLV_CODEC_AAC) { @@ -46,12 +32,6 @@ static string getAacCfg(const RtmpPacket &thiz) { bool AACRtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt, bool) { if (pkt->isCfgFrame()) { _aac_cfg = getAacCfg(*pkt); - if (_track) { - //设置aac config - _track->setAacCfg(_aac_cfg); - //不再强引用 - _track = nullptr; - } return false; } if (!_aac_cfg.empty()) { @@ -61,20 +41,30 @@ bool AACRtmpDecoder::inputRtmp(const RtmpPacket::Ptr &pkt, bool) { } void AACRtmpDecoder::onGetAAC(const char* data, int len, uint32_t stamp) { - _frame->_dts = stamp; - //先追加数据 - _frame->_buffer.append(data, len); - //覆盖adts头 - dumpAacConfig(_aac_cfg, _frame->size(), (uint8_t *) _frame->data()); + auto frame = ResourcePoolHelper::obtainObj(); + + //生成adts头 + char adts_header[32] = {0}; + auto size = dumpAacConfig(_aac_cfg, len, (uint8_t *) adts_header, sizeof(adts_header)); + if (size > 0) { + frame->_buffer.assign(adts_header, size); + frame->_prefix_size = size; + } else { + frame->_buffer.clear(); + frame->_prefix_size = 0; + } + + //追加负载数据 + frame->_buffer.append(data, len); + frame->_dts = stamp; //写入环形缓存 - RtmpCodec::inputFrame(_frame); - _frame = obtainFrame(); + RtmpCodec::inputFrame(frame); } ///////////////////////////////////////////////////////////////////////////////////// -AACRtmpEncoder::AACRtmpEncoder(const Track::Ptr &track): AACRtmpDecoder(track) { +AACRtmpEncoder::AACRtmpEncoder(const Track::Ptr &track) { _track = dynamic_pointer_cast(track); } @@ -91,9 +81,9 @@ void AACRtmpEncoder::makeConfigPacket() { void AACRtmpEncoder::inputFrame(const Frame::Ptr &frame) { if (_aac_cfg.empty()) { - if (frame->prefixSize() >= 7) { + if (frame->prefixSize()) { //包含adts头,从adts头获取aac配置信息 - _aac_cfg = makeAacConfig((uint8_t *) (frame->data())); + _aac_cfg = makeAacConfig((uint8_t *) (frame->data()), frame->prefixSize()); } makeConfigPacket(); } diff --git a/src/Extension/AACRtmp.h b/src/Extension/AACRtmp.h index 0f5d2f4b..dc7f0d62 100644 --- a/src/Extension/AACRtmp.h +++ b/src/Extension/AACRtmp.h @@ -23,7 +23,7 @@ class AACRtmpDecoder : public RtmpCodec , public ResourcePoolHelper { public: typedef std::shared_ptr Ptr; - AACRtmpDecoder(const Track::Ptr &track); + AACRtmpDecoder() {} ~AACRtmpDecoder() {} /** @@ -37,13 +37,10 @@ public: return CodecAAC; } -protected: +private: void onGetAAC(const char *data, int len, uint32_t stamp); - AACFrame::Ptr obtainFrame(); -protected: - AACFrame::Ptr _frame; - AACTrack::Ptr _track; +private: string _aac_cfg; }; @@ -80,6 +77,8 @@ private: private: uint8_t _audio_flv_flags; + AACTrack::Ptr _track; + string _aac_cfg; }; }//namespace mediakit diff --git a/src/Extension/AACRtp.cpp b/src/Extension/AACRtp.cpp index d8c257cf..08f8307c 100644 --- a/src/Extension/AACRtp.cpp +++ b/src/Extension/AACRtp.cpp @@ -56,30 +56,30 @@ void AACRtpEncoder::inputFrame(const Frame::Ptr &frame) { } void AACRtpEncoder::makeAACRtp(const void *data, unsigned int len, bool mark, uint32_t uiStamp) { - RtpCodec::inputRtp(makeRtp(getTrackType(),data,len,mark,uiStamp), false); + RtpCodec::inputRtp(makeRtp(getTrackType(), data, len, mark, uiStamp), false); } ///////////////////////////////////////////////////////////////////////////////////// -AACRtpDecoder::AACRtpDecoder(const Track::Ptr &track){ +AACRtpDecoder::AACRtpDecoder(const Track::Ptr &track) { auto aacTrack = dynamic_pointer_cast(track); - if(!aacTrack || !aacTrack->ready()){ + if (!aacTrack || !aacTrack->ready()) { WarnL << "该aac track无效!"; - }else{ + } else { _aac_cfg = aacTrack->getAacCfg(); } - _adts = obtainFrame(); + _frame = obtainFrame(); } + AACRtpDecoder::AACRtpDecoder() { - _adts = obtainFrame(); + _frame = obtainFrame(); } AACFrame::Ptr AACRtpDecoder::obtainFrame() { //从缓存池重新申请对象,防止覆盖已经写入环形缓存的对象 auto frame = ResourcePoolHelper::obtainObj(); - frame->_prefix_size = ADTS_HEADER_LEN; - //预留7个字节的空位以便后续覆盖 - frame->_buffer.assign(ADTS_HEADER_LEN,(char)0); + frame->_prefix_size = 0; + frame->_buffer.clear(); return frame; } @@ -94,19 +94,18 @@ bool AACRtpDecoder::inputRtp(const RtpPacket::Ptr &rtppack, bool key_pos) { //忽略Au-Header区 ptr += 2 + au_header_count * 2; - static const uint32_t max_size = AAC_MAX_FRAME_SIZE - ADTS_HEADER_LEN; while (ptr < end) { auto size = (uint32_t) (end - ptr); - if(size > max_size){ - size = max_size; + if (size > AAC_MAX_FRAME_SIZE) { + size = AAC_MAX_FRAME_SIZE; } - if (_adts->size() + size > AAC_MAX_FRAME_SIZE) { + if (_frame->size() + size > AAC_MAX_FRAME_SIZE) { //数据太多了,先清空 flushData(); } //追加aac数据 - _adts->_buffer.append((char *)ptr, size); - _adts->_dts = rtppack->timeStamp; + _frame->_buffer.append((char *) ptr, size); + _frame->_dts = rtppack->timeStamp; ptr += size; } @@ -118,15 +117,21 @@ bool AACRtpDecoder::inputRtp(const RtpPacket::Ptr &rtppack, bool key_pos) { } void AACRtpDecoder::flushData() { - if (_adts->size() == ADTS_HEADER_LEN) { + if (_frame->_buffer.empty()) { //没有有效数据 return; } - //覆盖adts头 - dumpAacConfig(_aac_cfg, _adts->size(), (uint8_t *) _adts->data()); - RtpCodec::inputFrame(_adts); - _adts = obtainFrame(); + //插入adts头 + char adts_header[32] = {0}; + auto size = dumpAacConfig(_aac_cfg, _frame->_buffer.size(), (uint8_t *) adts_header, sizeof(adts_header)); + if (size > 0) { + //插入adts头 + _frame->_buffer.insert(0, adts_header, size); + _frame->_prefix_size = size; + } + RtpCodec::inputFrame(_frame); + _frame = obtainFrame(); } diff --git a/src/Extension/AACRtp.h b/src/Extension/AACRtp.h index b82b19e2..7b646204 100644 --- a/src/Extension/AACRtp.h +++ b/src/Extension/AACRtp.h @@ -31,16 +31,19 @@ public: */ bool inputRtp(const RtpPacket::Ptr &rtp, bool key_pos = false) override; - CodecId getCodecId() const override{ + CodecId getCodecId() const override { return CodecAAC; } + protected: AACRtpDecoder(); + private: AACFrame::Ptr obtainFrame(); void flushData(); + private: - AACFrame::Ptr _adts; + AACFrame::Ptr _frame; string _aac_cfg; }; @@ -71,8 +74,10 @@ public: * @param frame 带dats头的aac数据 */ void inputFrame(const Frame::Ptr &frame) override; + private: void makeAACRtp(const void *pData, unsigned int uiLen, bool bMark, uint32_t uiStamp); + private: unsigned char _aucSectionBuf[1600]; }; From f03351a71a2b6f7cf001cb15e9fbaa7655d581df Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Thu, 11 Jun 2020 23:06:01 +0800 Subject: [PATCH 098/100] =?UTF-8?q?=E4=BF=AE=E6=94=B9dts=E7=94=9F=E6=88=90?= =?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/Common/Stamp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common/Stamp.cpp b/src/Common/Stamp.cpp index 5201fb21..e5fd631c 100644 --- a/src/Common/Stamp.cpp +++ b/src/Common/Stamp.cpp @@ -175,7 +175,7 @@ bool DtsGenerator::getDts_l(uint32_t pts, uint32_t &dts){ //已经出现多次非B帧的情况,那么我们就能知道P帧间B帧的个数 _sorter_max_size = _frames_since_last_max_pts; //我们记录P帧间时间间隔(也就是多个B帧时间戳增量累计) - _dts_pts_offset = (pts - _last_max_pts) / 2; + _dts_pts_offset = (pts - _last_max_pts); } //遇到P帧或关键帧,连续B帧计数清零 _frames_since_last_max_pts = 0; From 5f50441f435c7a9bdcd88abc69631037fca615e6 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 12 Jun 2020 18:17:49 +0800 Subject: [PATCH 099/100] =?UTF-8?q?=E9=99=8D=E4=BD=8E=E7=AC=AC=E4=B8=89?= =?UTF-8?q?=E6=96=B9=E5=BA=93=E5=A4=B4=E6=96=87=E4=BB=B6=E4=BE=9D=E8=B5=96?= =?UTF-8?q?=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 12 ++++++++---- src/Rtp/Decoder.cpp | 10 +++++++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 68f3a03f..c23279c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,10 +39,6 @@ set(MediaServer_Root ${CMAKE_CURRENT_SOURCE_DIR}/3rdpart/media-server) #设置头文件目录 INCLUDE_DIRECTORIES(${ToolKit_Root}) INCLUDE_DIRECTORIES(${MediaKit_Root}) -INCLUDE_DIRECTORIES(${MediaServer_Root}/libmpeg/include) -INCLUDE_DIRECTORIES(${MediaServer_Root}/libmov/include) -INCLUDE_DIRECTORIES(${MediaServer_Root}/libflv/include) -INCLUDE_DIRECTORIES(${MediaServer_Root}/librtp/include) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/3rdpart) set(ENABLE_HLS true) @@ -62,6 +58,8 @@ if (OPENSSL_FOUND AND ENABLE_OPENSSL) include_directories(${OPENSSL_INCLUDE_DIR}) add_definitions(-DENABLE_OPENSSL) list(APPEND LINK_LIB_LIST ${OPENSSL_LIBRARIES}) +else() + message(WARNING "openssl未找到,rtmp将不支持flash播放器,https/wss/rtsps/rtmps也将失效") endif () #查找mysql是否安装 @@ -109,6 +107,7 @@ if(ENABLE_HLS) message(STATUS "ENABLE_HLS defined") add_definitions(-DENABLE_HLS) + include_directories(${MediaServer_Root}/libmpeg/include) aux_source_directory(${MediaServer_Root}/libmpeg/include src_mpeg) aux_source_directory(${MediaServer_Root}/libmpeg/source src_mpeg) @@ -125,6 +124,9 @@ if(ENABLE_MP4) message(STATUS "ENABLE_MP4 defined") add_definitions(-DENABLE_MP4) + include_directories(${MediaServer_Root}/libmov/include) + include_directories(${MediaServer_Root}/libflv/include) + aux_source_directory(${MediaServer_Root}/libmov/include src_mov) aux_source_directory(${MediaServer_Root}/libmov/source src_mov) @@ -143,6 +145,8 @@ endif() #添加rtp库用于rtp转ps/ts if(ENABLE_RTPPROXY AND ENABLE_HLS) message(STATUS "ENABLE_RTPPROXY defined") + include_directories(${MediaServer_Root}/librtp/include) + 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) diff --git a/src/Rtp/Decoder.cpp b/src/Rtp/Decoder.cpp index e177ed8e..cd8e566d 100644 --- a/src/Rtp/Decoder.cpp +++ b/src/Rtp/Decoder.cpp @@ -11,12 +11,15 @@ #include "Decoder.h" #include "PSDecoder.h" #include "TSDecoder.h" -#include "mpeg-ts-proto.h" #include "Extension/H264.h" #include "Extension/H265.h" #include "Extension/AAC.h" #include "Extension/G711.h" +#if defined(ENABLE_RTPPROXY) || defined(ENABLE_HLS) +#include "mpeg-ts-proto.h" +#endif + namespace mediakit { static Decoder::Ptr createDecoder_l(DecoderImp::Type type) { switch (type){ @@ -62,6 +65,7 @@ DecoderImp::DecoderImp(const Decoder::Ptr &decoder, MediaSinkInterface *sink){ }); } +#if defined(ENABLE_RTPPROXY) || defined(ENABLE_HLS) #define SWITCH_CASE(codec_id) case codec_id : return #codec_id static const char *getCodecName(int codec_id) { switch (codec_id) { @@ -198,6 +202,9 @@ void DecoderImp::onDecode(int stream,int codecid,int flags,int64_t pts,int64_t d break; } } +#else +void DecoderImp::onDecode(int stream,int codecid,int flags,int64_t pts,int64_t dts,const void *data,int bytes) {} +#endif void DecoderImp::onTrack(const Track::Ptr &track) { _sink->addTrack(track); @@ -208,3 +215,4 @@ void DecoderImp::onFrame(const Frame::Ptr &frame) { } }//namespace mediakit + From f881108dfe04bdf036d319fd7b8aadf1ee89cd0e Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Mon, 15 Jun 2020 09:13:45 +0800 Subject: [PATCH 100/100] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=AC=AC=E4=B8=89?= =?UTF-8?q?=E6=96=B9=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 3rdpart/media-server | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdpart/media-server b/3rdpart/media-server index 1ce64284..576216c6 160000 --- a/3rdpart/media-server +++ b/3rdpart/media-server @@ -1 +1 @@ -Subproject commit 1ce642840df605de29e6acefa13d97c63021121f +Subproject commit 576216c64bf3bcdc5e787da2adb3e169bdd97118