From 84fde961d702b17c309ea256c1f748cbdc121af8 Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Fri, 14 Dec 2018 17:46:12 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E5=8F=96RTP=E8=A7=A3=E6=9E=90?= =?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/Rtsp/RtpReceiver.cpp | 160 +++++++++++++++++++++++++++++++++++++++ src/Rtsp/RtpReceiver.h | 87 +++++++++++++++++++++ src/Rtsp/RtspPlayer.cpp | 130 ++++--------------------------- src/Rtsp/RtspPlayer.h | 20 +++-- 4 files changed, 269 insertions(+), 128 deletions(-) create mode 100644 src/Rtsp/RtpReceiver.cpp create mode 100644 src/Rtsp/RtpReceiver.h diff --git a/src/Rtsp/RtpReceiver.cpp b/src/Rtsp/RtpReceiver.cpp new file mode 100644 index 00000000..67f6d4c1 --- /dev/null +++ b/src/Rtsp/RtpReceiver.cpp @@ -0,0 +1,160 @@ +/* + * MIT License + * + * Copyright (c) 2016 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include + +#include "Common/config.h" +#include "RtspPlayer.h" +#include "H264/SPSParser.h" +#include "Util/MD5.h" +#include "Util/mini.h" +#include "Util/util.h" +#include "Util/base64.h" +#include "Network/sockutil.h" +#include "RtpReceiver.h" + +#define POP_HEAD(trackidx) \ + auto it = _amapRtpSort[trackidx].begin(); \ + onRtpSorted(it->second, trackidx); \ + _amapRtpSort[trackidx].erase(it); + +# define AV_RB16(x) \ + ((((const uint8_t*)(x))[0] << 8) | \ + ((const uint8_t*)(x))[1]) + + +namespace mediakit { + +RtpReceiver::RtpReceiver() {} +RtpReceiver::~RtpReceiver() {} + +bool RtpReceiver::handleOneRtp(int iTrackidx,SdpTrack::Ptr &track, unsigned char *pucData, unsigned int uiLen) { + auto pt_ptr=_pktPool.obtain(); + auto &rtppt=*pt_ptr; + rtppt.interleaved = track->_interleaved; + rtppt.length = uiLen + 4; + + rtppt.mark = pucData[1] >> 7; + rtppt.PT = pucData[1] & 0x7F; + //序列号 + memcpy(&rtppt.sequence,pucData+2,2);//内存对齐 + rtppt.sequence = ntohs(rtppt.sequence); + //时间戳 + memcpy(&rtppt.timeStamp, pucData+4, 4);//内存对齐 + //时间戳转换成毫秒 + rtppt.timeStamp = ntohl(rtppt.timeStamp) * 1000L / track->_samplerate; + //ssrc + memcpy(&rtppt.ssrc,pucData+8,4);//内存对齐 + rtppt.ssrc = ntohl(rtppt.ssrc); + rtppt.type = track->_type; + if (track->_ssrc == 0) { + track->_ssrc = rtppt.ssrc; + //保存SSRC + } else if (track->_ssrc != rtppt.ssrc) { + //ssrc错误 + WarnL << "ssrc错误"; + if (_aui32SsrcErrorCnt[iTrackidx]++ > 10) { + track->_ssrc = rtppt.ssrc; + WarnL << "ssrc更换!"; + } + return false; + } + _aui32SsrcErrorCnt[iTrackidx] = 0; + + rtppt.payload[0] = '$'; + rtppt.payload[1] = rtppt.interleaved; + rtppt.payload[2] = (uiLen & 0xFF00) >> 8; + rtppt.payload[3] = (uiLen & 0x00FF); + + rtppt.offset = 16; + int csrc = pucData[0] & 0x0f; + int ext = pucData[0] & 0x10; + rtppt.offset += 4 * csrc; + if (ext) { + if(uiLen < rtppt.offset){ + return false; + } + /* calculate the header extension length (stored as number of 32-bit words) */ + ext = (AV_RB16(pucData + rtppt.offset - 2) + 1) << 2; + rtppt.offset += ext; + } + + memcpy(rtppt.payload + 4, pucData, uiLen); + + /////////////////////////////////RTP排序逻辑/////////////////////////////////// + if(rtppt.sequence != (uint16_t)(_aui16LastSeq[iTrackidx] + 1) && _aui16LastSeq[iTrackidx] != 0){ + //包乱序或丢包 + _aui64SeqOkCnt[iTrackidx] = 0; + _abSortStarted[iTrackidx] = true; + //WarnL << "包乱序或丢包:" << trackidx <<" " << rtppt.sequence << " " << _aui16LastSeq[trackidx]; + }else{ + //正确序列的包 + _aui64SeqOkCnt[iTrackidx]++; + } + _aui16LastSeq[iTrackidx] = rtppt.sequence; + + //开始排序缓存 + if (_abSortStarted[iTrackidx]) { + _amapRtpSort[iTrackidx].emplace(rtppt.sequence, pt_ptr); + GET_CONFIG_AND_REGISTER(uint32_t,clearCount,Rtp::kClearCount); + GET_CONFIG_AND_REGISTER(uint32_t,maxRtpCount,Rtp::kMaxRtpCount); + if (_aui64SeqOkCnt[iTrackidx] >= clearCount) { + //网络环境改善,需要清空排序缓存 + _aui64SeqOkCnt[iTrackidx] = 0; + _abSortStarted[iTrackidx] = false; + while (_amapRtpSort[iTrackidx].size()) { + POP_HEAD(iTrackidx) + } + } else if (_amapRtpSort[iTrackidx].size() >= maxRtpCount) { + //排序缓存溢出 + POP_HEAD(iTrackidx) + } + }else{ + //正确序列 + onRtpSorted(pt_ptr, iTrackidx); + } + ////////////////////////////////////////////////////////////////////////////////// + return true; +} + +void RtpReceiver::clear() { + CLEAR_ARR(_aui16LastSeq) + CLEAR_ARR(_aui32SsrcErrorCnt) + CLEAR_ARR(_aui64SeqOkCnt) + CLEAR_ARR(_abSortStarted) + + _amapRtpSort[0].clear(); + _amapRtpSort[1].clear(); +} + +void RtpReceiver::setPoolSize(int size) { + _pktPool.setSize(size); +} + +}//namespace mediakit diff --git a/src/Rtsp/RtpReceiver.h b/src/Rtsp/RtpReceiver.h new file mode 100644 index 00000000..29166e32 --- /dev/null +++ b/src/Rtsp/RtpReceiver.h @@ -0,0 +1,87 @@ +/* + * MIT License + * + * Copyright (c) 2016 xiongziliang <771730766@qq.com> + * + * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef ZLMEDIAKIT_RTPRECEIVER_H +#define ZLMEDIAKIT_RTPRECEIVER_H + + +#include +#include +#include "Rtsp.h" +#include "RtspSession.h" +#include "RtspMediaSource.h" +#include "Player/PlayerBase.h" +#include "Util/util.h" +#include "Util/logger.h" +#include "Util/TimeTicker.h" +#include "Poller/Timer.h" +#include "Network/Socket.h" +#include "Network/TcpClient.h" +#include "RtspSplitter.h" + +using namespace std; +using namespace toolkit; + +namespace mediakit { + +class RtpReceiver { +public: + RtpReceiver(); + virtual ~RtpReceiver(); +protected: + + /** + * 输入数据指针生成并排序rtp包 + * @param iTrackidx track下标索引 + * @param track sdp track相关信息 + * @param pucData rtp数据指针 + * @param uiLen rtp数据指针长度 + * @return 解析成功返回true + */ + bool handleOneRtp(int iTrackidx,SdpTrack::Ptr &track, unsigned char *pucData, unsigned int uiLen); + + /** + * rtp数据包排序后输出 + * @param rtppt rtp数据包 + * @param trackidx track索引 + */ + virtual void onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx){} + void clear(); + void setPoolSize(int size); +private: + uint32_t _aui32SsrcErrorCnt[2] = { 0, 0 }; + /* RTP包排序所用参数 */ + uint16_t _aui16LastSeq[2] = { 0 , 0 }; + uint64_t _aui64SeqOkCnt[2] = { 0 , 0}; + bool _abSortStarted[2] = { 0 , 0}; + map _amapRtpSort[2]; + RtspMediaSource::PoolType _pktPool; +}; + +}//namespace mediakit + + +#endif //ZLMEDIAKIT_RTPRECEIVER_H diff --git a/src/Rtsp/RtspPlayer.cpp b/src/Rtsp/RtspPlayer.cpp index cdbd744b..1f9c7a5b 100644 --- a/src/Rtsp/RtspPlayer.cpp +++ b/src/Rtsp/RtspPlayer.cpp @@ -41,18 +41,11 @@ using namespace toolkit; namespace mediakit { -#define POP_HEAD(trackidx) \ - auto it = _amapRtpSort[trackidx].begin(); \ - onRecvRTP_l(it->second, trackidx); \ - _amapRtpSort[trackidx].erase(it); - -#define RTP_BUF_SIZE (4 * 1024) - const char kRtspMd5Nonce[] = "rtsp_md5_nonce"; const char kRtspRealm[] = "rtsp_realm"; RtspPlayer::RtspPlayer(void){ - _pktPool.setSize(64); + RtpReceiver::setPoolSize(64); } RtspPlayer::~RtspPlayer(void) { DebugL<bindUdpSock(port, "0.0.0.0")) { @@ -346,10 +335,10 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex) return; } if(((struct sockaddr_in *)addr)->sin_addr.s_addr != srcIP) { - WarnL << "收到请他地址的UDP数据:" << inet_ntoa(((struct sockaddr_in *) addr)->sin_addr); + WarnL << "收到其他地址的UDP数据:" << inet_ntoa(((struct sockaddr_in *) addr)->sin_addr); return; } - strongSelf->handleOneRtp(i,(unsigned char *)buf->data(),buf->size()); + strongSelf->handleOneRtp(i,strongSelf->_aTrackInfo[i],(unsigned char *)buf->data(),buf->size()); }); } /////////////////////////心跳///////////////////////////////// @@ -470,105 +459,12 @@ void RtspPlayer::onRtpPacket(const char *data, uint64_t len) { trackIdx = getTrackIndexByInterleaved(interleaved); } if (trackIdx != -1) { - handleOneRtp(trackIdx, (unsigned char *)data + 4, len - 4); + handleOneRtp(trackIdx,_aTrackInfo[trackIdx],(unsigned char *)data + 4, len - 4); } } -# define AV_RB16(x) \ - ((((const uint8_t*)(x))[0] << 8) | \ - ((const uint8_t*)(x))[1]) - -bool RtspPlayer::handleOneRtp(int iTrackidx, unsigned char *pucData, unsigned int uiLen) { - auto &track = _aTrackInfo[iTrackidx]; - auto pt_ptr=_pktPool.obtain(); - auto &rtppt=*pt_ptr; - rtppt.interleaved = track->_interleaved; - rtppt.length = uiLen + 4; - - rtppt.mark = pucData[1] >> 7; - rtppt.PT = pucData[1] & 0x7F; - //序列号 - memcpy(&rtppt.sequence,pucData+2,2);//内存对齐 - rtppt.sequence = ntohs(rtppt.sequence); - //时间戳 - memcpy(&rtppt.timeStamp, pucData+4, 4);//内存对齐 - //时间戳转换成毫秒 - rtppt.timeStamp = ntohl(rtppt.timeStamp) * 1000L / track->_samplerate; - //ssrc - memcpy(&rtppt.ssrc,pucData+8,4);//内存对齐 - rtppt.ssrc = ntohl(rtppt.ssrc); - rtppt.type = track->_type; - if (track->_ssrc == 0) { - track->_ssrc = rtppt.ssrc; - //保存SSRC - } else if (track->_ssrc != rtppt.ssrc) { - //ssrc错误 - WarnL << "ssrc错误"; - if (_aui32SsrcErrorCnt[iTrackidx]++ > 10) { - track->_ssrc = rtppt.ssrc; - WarnL << "ssrc更换!"; - } - return false; - } - _aui32SsrcErrorCnt[iTrackidx] = 0; - - rtppt.payload[0] = '$'; - rtppt.payload[1] = rtppt.interleaved; - rtppt.payload[2] = (uiLen & 0xFF00) >> 8; - rtppt.payload[3] = (uiLen & 0x00FF); - - rtppt.offset = 16; - int csrc = pucData[0] & 0x0f; - int ext = pucData[0] & 0x10; - rtppt.offset += 4 * csrc; - if (ext) { - if(uiLen < rtppt.offset){ - return false; - } - /* calculate the header extension length (stored as number of 32-bit words) */ - ext = (AV_RB16(pucData + rtppt.offset - 2) + 1) << 2; - rtppt.offset += ext; - } - - memcpy(rtppt.payload + 4, pucData, uiLen); - - /////////////////////////////////RTP排序逻辑/////////////////////////////////// - if(rtppt.sequence != (uint16_t)(_aui16LastSeq[iTrackidx] + 1) && _aui16LastSeq[iTrackidx] != 0){ - //包乱序或丢包 - _aui64SeqOkCnt[iTrackidx] = 0; - _abSortStarted[iTrackidx] = true; - //WarnL << "包乱序或丢包:" << trackidx <<" " << rtppt.sequence << " " << _aui16LastSeq[trackidx]; - }else{ - //正确序列的包 - _aui64SeqOkCnt[iTrackidx]++; - } - _aui16LastSeq[iTrackidx] = rtppt.sequence; - - //开始排序缓存 - if (_abSortStarted[iTrackidx]) { - _amapRtpSort[iTrackidx].emplace(rtppt.sequence, pt_ptr); - GET_CONFIG_AND_REGISTER(uint32_t,clearCount,Rtp::kClearCount); - GET_CONFIG_AND_REGISTER(uint32_t,maxRtpCount,Rtp::kMaxRtpCount); - if (_aui64SeqOkCnt[iTrackidx] >= clearCount) { - //网络环境改善,需要清空排序缓存 - _aui64SeqOkCnt[iTrackidx] = 0; - _abSortStarted[iTrackidx] = false; - while (_amapRtpSort[iTrackidx].size()) { - POP_HEAD(iTrackidx) - } - } else if (_amapRtpSort[iTrackidx].size() >= maxRtpCount) { - //排序缓存溢出 - POP_HEAD(iTrackidx) - } - }else{ - //正确序列 - onRecvRTP_l(pt_ptr, iTrackidx); - } - ////////////////////////////////////////////////////////////////////////////////// - return true; -} -void RtspPlayer::onRecvRTP_l(const RtpPacket::Ptr &rtppt, int trackidx){ +void RtspPlayer::onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx){ //统计丢包率 if (_aui16FirstSeq[trackidx] == 0 || rtppt->sequence < _aui16FirstSeq[trackidx]) { _aui16FirstSeq[trackidx] = rtppt->sequence; diff --git a/src/Rtsp/RtspPlayer.h b/src/Rtsp/RtspPlayer.h index b4cda203..30f0738b 100644 --- a/src/Rtsp/RtspPlayer.h +++ b/src/Rtsp/RtspPlayer.h @@ -40,6 +40,7 @@ #include "Network/Socket.h" #include "Network/TcpClient.h" #include "RtspSplitter.h" +#include "RtpReceiver.h" using namespace std; using namespace toolkit; @@ -47,7 +48,7 @@ using namespace toolkit; namespace mediakit { //实现了rtsp播放器协议部分的功能 -class RtspPlayer: public PlayerBase,public TcpClient, public RtspSplitter { +class RtspPlayer: public PlayerBase,public TcpClient, public RtspSplitter, public RtpReceiver { public: typedef std::shared_ptr Ptr; @@ -76,9 +77,15 @@ protected: * @param len */ void onRtpPacket(const char *data,uint64_t len) override ; + + /** + * rtp数据包排序后输出 + * @param rtppt rtp数据包 + * @param trackidx track索引 + */ + void onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx) override; private: void onShutdown_l(const SockException &ex); - void onRecvRTP_l(const RtpPacket::Ptr &pRtppt, int iTrackidx); void onRecvRTP_l(const RtpPacket::Ptr &pRtppt, const SdpTrack::Ptr &track); void onPlayResult_l(const SockException &ex); @@ -95,8 +102,6 @@ private: void handleResDESCRIBE(const Parser &parser); bool handleAuthenticationFailure(const string &wwwAuthenticateParamsStr); void handleResPAUSE(const Parser &parser, bool bPause); - //处理一个rtp包 - bool handleOneRtp(int iTrackidx, unsigned char *ucData, unsigned int uiLen); //发送SETUP命令 bool sendSetup(unsigned int uiTrackIndex); @@ -110,19 +115,12 @@ private: vector _aTrackInfo; function _onHandshake; - RtspMediaSource::PoolType _pktPool; Socket::Ptr _apUdpSock[2]; //rtsp info string _strSession; unsigned int _uiCseq = 1; - uint32_t _aui32SsrcErrorCnt[2] = { 0, 0 }; string _strContentBase; eRtpType _eType = RTP_TCP; - /* RTP包排序所用参数 */ - uint16_t _aui16LastSeq[2] = { 0 , 0 }; - uint64_t _aui64SeqOkCnt[2] = { 0 , 0}; - bool _abSortStarted[2] = { 0 , 0}; - map _amapRtpSort[2]; /* 丢包率统计需要用到的参数 */ uint16_t _aui16FirstSeq[2] = { 0 , 0};