diff --git a/src/Rtcp/Rtcp.cpp b/src/Rtcp/Rtcp.cpp index 220a8439..86af2821 100644 --- a/src/Rtcp/Rtcp.cpp +++ b/src/Rtcp/Rtcp.cpp @@ -9,6 +9,7 @@ */ #include +#include #include "Rtcp.h" #include "Util/logger.h" @@ -87,10 +88,20 @@ string RtcpHeader::dumpString() const { RtcpPli *rtcp = (RtcpPli *)this; return rtcp->dumpString(); } + + case RtcpType::RTCP_BYE: { + RtcpBye *rtcp = (RtcpBye *)this; + return rtcp->dumpString(); + } + default: return StrPrinter << dumpHeader() << hexdump((char *)this + sizeof(*this), length << 2); } } +size_t RtcpHeader::getSize() const { + return (1 + length) << 2; +} + void RtcpHeader::net2Host(size_t len){ switch ((RtcpType)pt) { case RtcpType::RTCP_SR: { @@ -116,6 +127,12 @@ void RtcpHeader::net2Host(size_t len){ pli->net2Host(len); break; } + + case RtcpType::RTCP_BYE: { + RtcpBye *bye = (RtcpBye *)this; + bye->net2Host(len); + break; + } default: throw std::runtime_error(StrPrinter << "未处理的rtcp包:" << rtcpTypeToStr((RtcpType) this->pt)); } } @@ -127,6 +144,10 @@ vector RtcpHeader::loadFromBytes(char *data, size_t len){ while (remain > (ssize_t) sizeof(RtcpHeader)) { RtcpHeader *rtcp = (RtcpHeader *) ptr; auto rtcp_len = (1 + ntohs(rtcp->length)) << 2; + if (remain < rtcp_len) { + WarnL << "非法的rtcp包,声明的长度超过实际数据长度"; + break; + } try { rtcp->net2Host(rtcp_len); ret.emplace_back(rtcp); @@ -144,7 +165,7 @@ class BufferRtcp : public Buffer { public: BufferRtcp(std::shared_ptr rtcp) { _rtcp = std::move(rtcp); - _size = (htons(_rtcp->length) + 1) << 2; + _size = (ntohs(_rtcp->length) + 1) << 2; } ~BufferRtcp() override {} @@ -440,4 +461,97 @@ void RtcpPli::net2Host(size_t size) { ssrc_media = ntohl(ssrc_media); } +//////////////////////////////////////////////////////////////////// + +std::shared_ptr RtcpBye::create(const std::initializer_list &ssrcs, const string &reason) { + assert(reason.size() <= 0xFF); + auto bytes = alignSize(sizeof(RtcpHeader) + sizeof(uint32_t) * ssrcs.size() + 1 + reason.size()); + auto ptr = (RtcpBye *) new char[bytes]; + setupHeader(ptr, RtcpType::RTCP_BYE, ssrcs.size(), bytes); + + auto *ssrc_ptr = &(((RtcpBye *) ptr)->ssrc); + for (auto ssrc : ssrcs) { + *ssrc_ptr = htonl(ssrc); + ++ssrc_ptr; + } + + if (!reason.empty()) { + uint8_t *reason_len_ptr = (uint8_t *) ptr + sizeof(RtcpHeader) + sizeof(uint32_t) * ssrcs.size(); + *reason_len_ptr = reason.size() & 0xFF; + memcpy(reason_len_ptr + 1, reason.data(), *reason_len_ptr); + } + + return std::shared_ptr(ptr, [](RtcpBye *ptr) { + delete[] (char *) ptr; + }); +} + +vector RtcpBye::getSSRC() { + vector ret; + uint32_t *ssrc_ptr = &ssrc; + for (size_t i = 0; i < report_count; ++i) { + ret.emplace_back(ssrc_ptr); + ssrc_ptr += 1; + } + return ret; +} + +string RtcpBye::getReason() const { + auto *reason_len_ptr = &reason_len + sizeof(ssrc) * (report_count - 1); + if (reason_len_ptr + 1 >= (uint8_t *) this + getSize()) { + return ""; + } + return string((char *) reason_len_ptr + 1, *reason_len_ptr); +} + +string RtcpBye::dumpString() const { + _StrPrinter printer; + printer << RtcpHeader::dumpHeader(); + for(auto ssrc : ((RtcpBye *)this)->getSSRC()) { + printer << "ssrc:" << *ssrc << "\r\n"; + } + printer << "reason:" << getReason(); + return std::move(printer); +} + +void RtcpBye::net2Host(size_t size) { + static const size_t kMinSize = sizeof(RtcpHeader); + CHECK_MIN_SIZE(size, kMinSize); + RtcpHeader::net2Host(); + + uint32_t *ssrc_ptr = &ssrc; + size_t offset = kMinSize; + size_t i = 0; + for (; i < report_count && offset + sizeof(ssrc) <= size; ++i) { + *ssrc_ptr = ntohl(*ssrc_ptr); + ssrc_ptr += 1; + offset += sizeof(ssrc); + } + //修正ssrc个数 + CHECK_LENGTH(size, i); + + if (offset < size) { + uint8_t *reason_len_ptr = &reason_len + sizeof(ssrc) * (report_count - 1); + if (reason_len_ptr + 1 + *reason_len_ptr > (uint8_t *) this + size) { + WarnL << "invalid rtcp bye reason length"; + //修正reason_len长度 + *reason_len_ptr = ((uint8_t *) this + size - reason_len_ptr - 1) & 0xFF; + } + } +} + +#if 0 +#include "Util/onceToken.h" + +static toolkit::onceToken token([](){ + auto bye = RtcpBye::create({1,2,3,4,5,6}, "this is a bye reason"); + auto buffer = RtcpHeader::toBuffer(bye); + + auto rtcps = RtcpHeader::loadFromBytes(buffer->data(), buffer->size()); + for(auto rtcp : rtcps){ + std::cout << rtcp->dumpString() << std::endl; + } +}); +#endif + }//namespace mediakit \ No newline at end of file diff --git a/src/Rtcp/Rtcp.h b/src/Rtcp/Rtcp.h index 8fcc1a1a..c7a1556f 100644 --- a/src/Rtcp/Rtcp.h +++ b/src/Rtcp/Rtcp.h @@ -124,6 +124,12 @@ public: */ string dumpString() const; + /** + * 根据length字段获取rtcp总长度 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + size_t getSize() const; + protected: /** * 网络字节序转换为主机字节序 @@ -465,9 +471,7 @@ public: public: /** - * 创建SDES包,只赋值了RtcpHeader以及SdesItem对象的length和text部分 - * @param item_text SdesItem列表,只赋值length和text部分 - * @return SDES包 + * 创建pli包 */ static std::shared_ptr create(); @@ -485,6 +489,66 @@ private: void net2Host(size_t size); } PACKED; +//BYE +/* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |V=2|P| SC | PT=BYE=203 | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC/CSRC | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + : ... : + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +(opt) | length | reason for leaving ... + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +class RtcpBye : public RtcpHeader { +public: + friend class RtcpHeader; + /* 变长,根据count决定有多少个ssrc */ + uint32_t ssrc; + + /** 中间可能有若干个 ssrc **/ + + /* 可选 */ + uint8_t reason_len; + char reason; + +public: + /** + * 创建bye包 + * @param ssrc ssrc列表 + * @param reason 原因 + * @return rtcp bye包 + */ + static std::shared_ptr create(const std::initializer_list &ssrc, const string &reason); + + /** + * 获取ssrc列表 + */ + vector getSSRC(); + + /** + * 获取原因 + */ + string getReason() const; + +private: + /** + * 打印字段详情 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + string dumpString() const; + + /** + * 网络字节序转换为主机字节序 + * @param size 字节长度,防止内存越界 + */ + void net2Host(size_t size); +} PACKED; + #if defined(_WIN32) #pragma pack(pop) #endif // defined(_WIN32)