From 5c6560f55d55211ad36d10d1619b8e2c27d7831a Mon Sep 17 00:00:00 2001 From: xiongziliang <771730766@qq.com> Date: Sun, 31 Jan 2021 19:18:17 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E5=86=99rtcp=E6=A1=86=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Common/macros.h | 41 ++++ src/Rtcp/Rtcp.cpp | 405 ++++++++++++++++++++++++++++++++++ src/Rtcp/Rtcp.h | 464 +++++++++++++++++++++++++++++++++++++++ src/Rtcp/RtcpContext.cpp | 147 +++++++++++++ src/Rtcp/RtcpContext.h | 118 ++++++++++ src/Rtsp/RtpReceiver.cpp | 3 +- src/Rtsp/RtpReceiver.h | 9 +- src/Rtsp/Rtsp.cpp | 11 + src/Rtsp/Rtsp.h | 2 + src/Rtsp/RtspPlayer.cpp | 261 +++++++--------------- src/Rtsp/RtspPlayer.h | 29 +-- src/Rtsp/RtspPusher.cpp | 126 +++++++++-- src/Rtsp/RtspPusher.h | 19 +- src/Rtsp/RtspSession.cpp | 171 ++++++--------- src/Rtsp/RtspSession.h | 17 +- tests/test_rtcp.cpp | 131 +++++++++++ 16 files changed, 1617 insertions(+), 337 deletions(-) create mode 100644 src/Common/macros.h create mode 100644 src/Rtcp/Rtcp.cpp create mode 100644 src/Rtcp/Rtcp.h create mode 100644 src/Rtcp/RtcpContext.cpp create mode 100644 src/Rtcp/RtcpContext.h create mode 100644 tests/test_rtcp.cpp diff --git a/src/Common/macros.h b/src/Common/macros.h new file mode 100644 index 00000000..969b8976 --- /dev/null +++ b/src/Common/macros.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xia-chu/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_MACROS_H +#define ZLMEDIAKIT_MACROS_H + +#if defined(__MACH__) +#include + #include + #define __BYTE_ORDER BYTE_ORDER + #define __BIG_ENDIAN BIG_ENDIAN + #define __LITTLE_ENDIAN LITTLE_ENDIAN +#elif defined(__linux__) + #include + #include +#elif defined(_WIN32) + #define BIG_ENDIAN 1 + #define LITTLE_ENDIAN 0 + #define BYTE_ORDER LITTLE_ENDIAN + #define __BYTE_ORDER BYTE_ORDER + #define __BIG_ENDIAN BIG_ENDIAN + #define __LITTLE_ENDIAN LITTLE_ENDIAN +#endif + +#ifndef PACKED + #if !defined(_WIN32) + #define PACKED __attribute__((packed)) + #else + #define PACKED + #endif //!defined(_WIN32) +#endif + + +#endif //ZLMEDIAKIT_MACROS_H diff --git a/src/Rtcp/Rtcp.cpp b/src/Rtcp/Rtcp.cpp new file mode 100644 index 00000000..24545718 --- /dev/null +++ b/src/Rtcp/Rtcp.cpp @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xia-chu/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 "Rtcp.h" +#include "Util/logger.h" + +namespace mediakit { + +const char *rtcpTypeToStr(RtcpType type){ + switch (type){ +#define SWITCH_CASE(key, value) case RtcpType::key : return #value "(" #key ")"; + RTCP_PT_MAP(SWITCH_CASE) +#undef SWITCH_CASE + default: return "unknown rtcp pt"; + } +} + +const char *sdesTypeToStr(SdesType type){ + switch (type){ +#define SWITCH_CASE(key, value) case SdesType::key : return #value "(" #key ")"; + SDES_TYPE_MAP(SWITCH_CASE) +#undef SWITCH_CASE + default: return "unknown source description type"; + } +} + +static size_t alignSize(size_t bytes) { + return 4 * (size_t)((bytes + 3) / 4); +} + +static void setupHeader(RtcpHeader *rtcp, RtcpType type, size_t report_count, size_t total_bytes) { + rtcp->version = 2; + rtcp->padding = 0; + if (report_count > 0x1F) { + throw std::invalid_argument(StrPrinter << "rtcp report_count最大赋值为31,当前为:" << report_count); + } + //items总个数 + rtcp->report_count = report_count; + rtcp->pt = (uint8_t) type; + //不包含rtcp头的长度 + rtcp->length = htons((uint16_t)((total_bytes / 4) - 1)); +} + +///////////////////////////////////////////////////////////////////////////// + +void RtcpHeader::net2Host() { + length = ntohs(length); +} + +string RtcpHeader::dumpHeader() const{ + _StrPrinter printer; + printer << "version:" << version << "\r\n"; + printer << "padding:" << padding << "\r\n"; + printer << "report_count:" << report_count << "\r\n"; + printer << "pt:" << rtcpTypeToStr((RtcpType)pt) << "\r\n"; + printer << "length:" << length << "\r\n"; + printer << "--------\r\n"; + return printer; +} + +string RtcpHeader::dumpString() const { + switch ((RtcpType)pt) { + case RtcpType::RTCP_SR: { + RtcpSR *rtcp = (RtcpSR *)this; + return rtcp->dumpString(); + } + + case RtcpType::RTCP_RR: { + RtcpRR *rtcp = (RtcpRR *)this; + return rtcp->dumpString(); + } + + case RtcpType::RTCP_SDES: { + RtcpSdes *rtcp = (RtcpSdes *)this; + return rtcp->dumpString(); + } + default: return StrPrinter << dumpHeader() << hexdump((char *)this + sizeof(*this), length * 4); + } +} + +void RtcpHeader::net2Host(size_t len){ + switch ((RtcpType)pt) { + case RtcpType::RTCP_SR: { + RtcpSR *sr = (RtcpSR *)this; + sr->net2Host(len); + break; + } + + case RtcpType::RTCP_RR: { + RtcpRR *rr = (RtcpRR *)this; + rr->net2Host(len); + break; + } + + case RtcpType::RTCP_SDES: { + RtcpSdes *sdes = (RtcpSdes *)this; + sdes->net2Host(len); + break; + } + default: throw std::runtime_error(StrPrinter << "未处理的rtcp包:" << rtcpTypeToStr((RtcpType) this->pt)); + } +} + +vector RtcpHeader::loadFromBytes(char *data, size_t len){ + vector ret; + ssize_t remain = len; + char *ptr = data; + while (remain > (ssize_t) sizeof(RtcpHeader)) { + RtcpHeader *rtcp = (RtcpHeader *) ptr; + auto rtcp_len = 4 * (1 + ntohs(rtcp->length)); + try { + rtcp->net2Host(rtcp_len); + ret.emplace_back(rtcp); + } catch (std::exception &ex) { + //不能处理的rtcp包,或者无法解析的rtcp包,忽略掉 + WarnL << ex.what(); + } + ptr += rtcp_len; + remain -= rtcp_len; + } + return ret; +} + +class BufferRtcp : public Buffer { +public: + BufferRtcp(std::shared_ptr rtcp) { + _rtcp = std::move(rtcp); + _size = (htons(_rtcp->length) + 1) * 4; + } + + ~BufferRtcp() override {} + + char *data() const override { + return (char *) _rtcp.get(); + } + + size_t size() const override { + return _size; + } + +private: + std::size_t _size; + std::shared_ptr _rtcp; +}; + +Buffer::Ptr RtcpHeader::toBuffer(std::shared_ptr rtcp) { + return std::make_shared(std::move(rtcp)); +} + +///////////////////////////////////////////////////////////////////////////// + +std::shared_ptr RtcpSR::create(size_t item_count) { + auto bytes = alignSize(sizeof(RtcpSR) - sizeof(ReportItem) + item_count * sizeof(ReportItem)); + auto ptr = (RtcpSR *) new char[bytes]; + setupHeader(ptr, RtcpType::RTCP_SR, item_count, bytes); + return std::shared_ptr(ptr, [](RtcpSR *ptr) { + delete[] (char *) ptr; + }); +} + +string RtcpSR::getNtpStamp() const{ + struct timeval tv; + tv.tv_sec = ntpmsw - 0x83AA7E80; + tv.tv_usec = (decltype(tv.tv_usec))(ntplsw / ((double) (((uint64_t) 1) << 32) * 1.0e-6)); + return LogChannel::printTime(tv); +} + +void RtcpSR::setNtpStamp(struct timeval tv) { + ntpmsw = htonl(tv.tv_sec + 0x83AA7E80); /* 0x83AA7E80 is the number of seconds from 1900 to 1970 */ + ntplsw = htonl((uint32_t) ((double) tv.tv_usec * (double) (((uint64_t) 1) << 32) * 1.0e-6)); +} + +string RtcpSR::dumpString() const{ + _StrPrinter printer; + printer << RtcpHeader::dumpHeader(); + printer << "ssrc:" << ssrc << "\r\n"; + printer << "ntpmsw:" << ntpmsw << "\r\n"; + printer << "ntplsw:" << ntplsw << "\r\n"; + printer << "ntp time:" << getNtpStamp() << "\r\n"; + printer << "rtpts:" << rtpts << "\r\n"; + printer << "packet_count:" << packet_count << "\r\n"; + printer << "octet_count:" << octet_count << "\r\n"; + auto items = ((RtcpSR *)this)->getItemList(); + auto i = 0; + for (auto &item : items) { + printer << "---- item:" << i++ << " ----\r\n"; + printer << item->dumpString(); + } + return printer; +} + +#define CHECK_MIN_SIZE(size, kMinSize) \ +if (size < kMinSize) { \ + throw std::out_of_range(StrPrinter << rtcpTypeToStr((RtcpType)pt) << " 长度不足:" << size << " < " << kMinSize); \ +} + +#define CHECK_LENGTH(size, item_count) \ +/*修正个数,防止getItemList时内存越界*/ \ +if (report_count != item_count) { \ + WarnL << rtcpTypeToStr((RtcpType)pt) << " report_count 字段不正确,已修正为:" << (int)report_count << " -> " << item_count; \ + report_count = item_count; \ +} \ +if ((size_t) (length + 1) * 4 != size) { \ + WarnL << rtcpTypeToStr((RtcpType)pt) << " length字段不正确:" << (size_t) (length + 1) * 4 << " != " << size; \ +} + +void RtcpSR::net2Host(size_t size) { + static const size_t kMinSize = sizeof(RtcpSR) - sizeof(items); + CHECK_MIN_SIZE(size, kMinSize); + + RtcpHeader::net2Host(); + ssrc = ntohl(ssrc); + ntpmsw = ntohl(ntpmsw); + ntplsw = ntohl(ntplsw); + rtpts = ntohl(rtpts); + packet_count = ntohl(packet_count); + octet_count = ntohl(octet_count); + + ReportItem *ptr = &items; + int item_count = 0; + for(int i = 0; i < (int)report_count && (char *)(ptr) + sizeof(ReportItem) <= (char *)(this) + size; ++i){ + ptr->net2Host(); + ++ptr; + ++item_count; + } + CHECK_LENGTH(size, item_count); +} + +vector RtcpSR::getItemList(){ + vector ret; + ReportItem *ptr = &items; + for (int i = 0; i < (int) report_count; ++i) { + ret.emplace_back(ptr); + ++ptr; + } + return ret; +} + +///////////////////////////////////////////////////////////////////////////// + +string ReportItem::dumpString() const{ + _StrPrinter printer; + printer << "ssrc:" << ssrc << "\r\n"; + printer << "fraction:" << fraction << "\r\n"; + printer << "cumulative:" << cumulative << "\r\n"; + printer << "seq_cycles:" << seq_cycles << "\r\n"; + printer << "seq_max:" << seq_max << "\r\n"; + printer << "jitter:" << jitter << "\r\n"; + printer << "last_sr_stamp:" << last_sr_stamp << "\r\n"; + printer << "delay_since_last_sr:" << delay_since_last_sr << "\r\n"; + return printer; +} + +void ReportItem::net2Host() { + ssrc = ntohl(ssrc); + cumulative = ntohl(cumulative ) >> 8; + seq_cycles = ntohs(seq_cycles); + seq_max = ntohs(seq_max); + jitter = ntohl(jitter); + last_sr_stamp = ntohl(last_sr_stamp); + delay_since_last_sr = ntohl(delay_since_last_sr); +} + +///////////////////////////////////////////////////////////////////////////// + +std::shared_ptr RtcpRR::create(size_t item_count) { + auto bytes = alignSize(sizeof(RtcpRR) - sizeof(ReportItem) + item_count * sizeof(ReportItem)); + auto ptr = (RtcpRR *) new char[bytes]; + setupHeader(ptr, RtcpType::RTCP_RR, item_count, bytes); + return std::shared_ptr(ptr, [](RtcpRR *ptr) { + delete[] (char *) ptr; + }); +} + +string RtcpRR::dumpString() const{ + _StrPrinter printer; + printer << RtcpHeader::dumpHeader(); + printer << "ssrc:" << ssrc << "\r\n"; + auto items = ((RtcpRR *)this)->getItemList(); + auto i = 0; + for (auto &item : items) { + printer << "---- item:" << i++ << " ----\r\n"; + printer << item->dumpString(); + } + return printer; +} + +void RtcpRR::net2Host(size_t size) { + static const size_t kMinSize = sizeof(RtcpRR) - sizeof(items); + CHECK_MIN_SIZE(size, kMinSize); + RtcpHeader::net2Host(); + ssrc = ntohl(ssrc); + + ReportItem *ptr = &items; + int item_count = 0; + for(int i = 0; i < (int)report_count && (char *)(ptr) + sizeof(ReportItem) <= (char *)(this) + size; ++i){ + ptr->net2Host(); + ++ptr; + ++item_count; + } + CHECK_LENGTH(size, item_count); +} + +vector RtcpRR::getItemList() { + vector ret; + ReportItem *ptr = &items; + for (int i = 0; i < (int) report_count; ++i) { + ret.emplace_back(ptr); + ++ptr; + } + return ret; +} + +///////////////////////////////////////////////////////////////////////////// + +void SdesItem::net2Host() { + ssrc = ntohl(ssrc); +} + +size_t SdesItem::totalBytes() const{ + return alignSize(minSize() + length); +} + +size_t SdesItem::minSize() { + return sizeof(SdesItem) - sizeof(text); +} + +string SdesItem::dumpString() const{ + _StrPrinter printer; + printer << "ssrc:" << ssrc << "\r\n"; + printer << "type:" << sdesTypeToStr((SdesType) type) << "\r\n"; + printer << "length:" << (int) length << "\r\n"; + printer << "text:" << (length ? string(&text, length) : "") << "\r\n"; + return printer; +} + +///////////////////////////////////////////////////////////////////////////// + +std::shared_ptr RtcpSdes::create(const std::initializer_list &item_text) { + size_t item_total_size = 0; + for (auto &text : item_text) { + //统计所有SdesItem对象占用的空间 + item_total_size += alignSize(SdesItem::minSize() + (0xFF & text.size())); + } + auto bytes = alignSize(sizeof(RtcpSdes) - sizeof(SdesItem) + item_total_size); + auto ptr = (RtcpSdes *) new char[bytes]; + auto item_ptr = &ptr->items; + for (auto &text : item_text) { + item_ptr->length = (0xFF & text.size()); + //确保赋值\0为RTCP_SDES_END + memcpy(&(item_ptr->text), text.data(), item_ptr->length + 1); + item_ptr = (SdesItem *) ((char *) item_ptr + item_ptr->totalBytes()); + } + + setupHeader(ptr, RtcpType::RTCP_SDES, item_text.size(), bytes); + return std::shared_ptr(ptr, [](RtcpSdes *ptr) { + delete [] (char *) ptr; + }); +} + +string RtcpSdes::dumpString() const { + _StrPrinter printer; + printer << RtcpHeader::dumpHeader(); + auto items = ((RtcpSdes *)this)->getItemList(); + auto i = 0; + for (auto &item : items) { + printer << "---- item:" << i++ << " ----\r\n"; + printer << item->dumpString(); + } + return printer; +} + +void RtcpSdes::net2Host(size_t size) { + static const size_t kMinSize = sizeof(RtcpSdes) - sizeof(items); + CHECK_MIN_SIZE(size, kMinSize); + RtcpHeader::net2Host(); + SdesItem *ptr = &items; + int item_count = 0; + for(int i = 0; i < (int)report_count && (char *)(ptr) + SdesItem::minSize() <= (char *)(this) + size; ++i){ + ptr->net2Host(); + ptr = (SdesItem *) ((char *) ptr + ptr->totalBytes()); + ++item_count; + } + CHECK_LENGTH(size, item_count); +} + +vector RtcpSdes::getItemList() { + vector ret; + SdesItem *ptr = &items; + for (int i = 0; i < (int) report_count; ++i) { + ret.emplace_back(ptr); + ptr = (SdesItem *) ((char *) ptr + ptr->totalBytes()); + } + return ret; +} + +}//namespace mediakit \ No newline at end of file diff --git a/src/Rtcp/Rtcp.h b/src/Rtcp/Rtcp.h new file mode 100644 index 00000000..6bbb660c --- /dev/null +++ b/src/Rtcp/Rtcp.h @@ -0,0 +1,464 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xia-chu/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_RTCP_H +#define ZLMEDIAKIT_RTCP_H + +#include +#include +#include "Util/util.h" +#include "Network/Buffer.h" +#include "Common/macros.h" +using namespace std; +using namespace toolkit; + +namespace mediakit { + +#if defined(_WIN32) +#pragma pack(push, 1) +#endif // defined(_WIN32) + +//https://datatracker.ietf.org/doc/rfc3550 + +#define RTCP_PT_MAP(XX) \ + XX(RTCP_FIR, 192) \ + XX(RTCP_NACK, 193) \ + XX(RTCP_SMPTETC, 194) \ + XX(RTCP_IJ, 195) \ + XX(RTCP_SR, 200) \ + XX(RTCP_RR, 201) \ + XX(RTCP_SDES, 202) \ + XX(RTCP_BYE, 203) \ + XX(RTCP_APP, 204) \ + XX(RTCP_RTPFB, 205) \ + XX(RTCP_PSFB, 206) \ + XX(RTCP_XR, 207) \ + XX(RTCP_AVB, 208) \ + XX(RTCP_RSI, 209) \ + XX(RTCP_TOKEN, 210) + +#define SDES_TYPE_MAP(XX) \ + XX(RTCP_SDES_END, 0) \ + XX(RTCP_SDES_CNAME, 1) \ + XX(RTCP_SDES_NAME, 2) \ + XX(RTCP_SDES_EMAIL, 3) \ + XX(RTCP_SDES_PHONE, 4) \ + XX(RTCP_SDES_LOC, 5) \ + XX(RTCP_SDES_TOOL, 6) \ + XX(RTCP_SDES_NOTE, 7) \ + XX(RTCP_SDES_PRIVATE, 8) + +//rtcp类型枚举 +enum class RtcpType : uint8_t { +#define XX(key, value) key = value, + RTCP_PT_MAP(XX) +#undef XX +}; + +//sdes类型枚举 +enum class SdesType : uint8_t { +#define XX(key, value) key = value, + SDES_TYPE_MAP(XX) +#undef XX +}; + +/** + * RtcpType转描述字符串 + */ +const char *rtcpTypeToStr(RtcpType type); + +/** + * SdesType枚举转描述字符串 + */ +const char *sdesTypeToStr(SdesType type); + +class RtcpHeader { +public: +#if __BYTE_ORDER == __BIG_ENDIAN + //版本号,固定为2 + uint32_t version: 2; + //padding,固定为0 + uint32_t padding: 1; + //reception report count + uint32_t report_count: 5; +#else + //reception report count + uint32_t report_count: 5; + //padding,固定为0 + uint32_t padding: 1; + //版本号,固定为2 + uint32_t version: 2; +#endif + //rtcp类型,RtcpType + uint32_t pt: 8; + //长度 + uint32_t length: 16; + +public: + /** + * 解析rtcp并转换网络字节序为主机字节序,返回RtcpHeader派生类列表 + * @param data 数据指针 + * @param size 数据总长度 + * @return rtcp对象列表,无需free + */ + static vector loadFromBytes(char *data, size_t size); + + /** + * rtcp包转Buffer对象 + * @param rtcp rtcp包对象智能指针 + * @return Buffer对象 + */ + static Buffer::Ptr toBuffer(std::shared_ptr rtcp); + + /** + * 打印rtcp相关字段详情(调用派生类的dumpString函数) + * 内部会判断是什么类型的派生类 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + string dumpString() const; + +protected: + /** + * 网络字节序转换为主机字节序 + */ + void net2Host(); + + /** + * 打印字段详情 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + string dumpHeader() const; + +private: + /** + * 调用派生类的net2Host函数 + * @param size rtcp字符长度 + */ + void net2Host(size_t size); + +} PACKED; + +///////////////////////////////////////////////////////////////////////////// + +//ReportBlock +class ReportItem { +public: + friend class RtcpSR; + friend class RtcpRR; + + uint32_t ssrc; + //Fraction lost + uint32_t fraction: 8; + //Cumulative number of packets lost + uint32_t cumulative: 24; + //Sequence number cycles count + uint16_t seq_cycles; + //Highest sequence number received + uint16_t seq_max; + //Interarrival jitter + uint32_t jitter; + //Last SR timestamp, NTP timestamp,(ntpmsw & 0xFFFF) << 16 | (ntplsw >> 16) & 0xFFFF) + uint32_t last_sr_stamp; + //Delay since last SR timestamp,expressed in units of 1/65536 seconds + uint32_t delay_since_last_sr; + +private: + /** + * 打印字段详情 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + string dumpString() const; + + /** + * 网络字节序转换为主机字节序 + */ + void net2Host(); +}PACKED; + +/* + * 6.4.1 SR: Sender Report RTCP Packet + + 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 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +header |V=2|P| RC | PT=SR=200 | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of sender | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +sender | NTP timestamp, most significant word | +info +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | NTP timestamp, least significant word | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | RTP timestamp | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | sender's packet count | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | sender's octet count | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +report | SSRC_1 (SSRC of first source) | +block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 1 | fraction lost | cumulative number of packets lost | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | extended highest sequence number received | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | interarrival jitter | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | last SR (LSR) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | delay since last SR (DLSR) | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +report | SSRC_2 (SSRC of second source) | +block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 2 : ... : + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + | profile-specific extensions | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +// sender report +class RtcpSR : public RtcpHeader { +public: + friend class RtcpHeader; + uint32_t ssrc; + // ntp timestamp MSW(in second) + uint32_t ntpmsw; + // ntp timestamp LSW(in picosecond) + uint32_t ntplsw; + // rtp timestamp + uint32_t rtpts; + // sender packet count + uint32_t packet_count; + // sender octet count + uint32_t octet_count; + //可能有很多个 + ReportItem items; + +public: + /** + * 创建SR包,只赋值了RtcpHeader部分(网络字节序) + * @param item_count ReportItem对象个数 + * @return SR包 + */ + static std::shared_ptr create(size_t item_count); + + /** + * 设置ntpmsw与ntplsw字段为网络字节序 + * @param tv 时间 + */ + void setNtpStamp(struct timeval tv); + + /** + * 返回ntp时间的字符串 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + string getNtpStamp() const; + + /** + * 获取ReportItem对象指针列表 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + vector getItemList(); + +private: + /** + * 打印字段详情 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + string dumpString() const; + + /** + * 网络字节序转换为主机字节序 + * @param size 字节长度,防止内存越界 + */ + void net2Host(size_t size); +} PACKED; + +///////////////////////////////////////////////////////////////////////////// + +/* + * 6.4.2 RR: Receiver Report RTCP Packet + + 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 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +header |V=2|P| RC | PT=RR=201 | length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SSRC of packet sender | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +report | SSRC_1 (SSRC of first source) | +block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 1 | fraction lost | cumulative number of packets lost | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | extended highest sequence number received | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | interarrival jitter | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | last SR (LSR) | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | delay since last SR (DLSR) | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +report | SSRC_2 (SSRC of second source) | +block +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + 2 : ... : + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + | profile-specific extensions | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +//Receiver Report +class RtcpRR : public RtcpHeader { +public: + friend class RtcpHeader; + + uint32_t ssrc; + //可能有很多个 + ReportItem items; + +public: + /** + * 创建RR包,只赋值了RtcpHeader部分 + * @param item_count ReportItem对象个数 + * @return RR包 + */ + static std::shared_ptr create(size_t item_count); + + /** + * 获取ReportItem对象指针列表 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + vector getItemList(); + +private: + /** + * 网络字节序转换为主机字节序 + * @param size 字节长度,防止内存越界 + */ + void net2Host(size_t size); + + /** + * 打印字段详情 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + string dumpString() const; + +} PACKED; + +///////////////////////////////////////////////////////////////////////////// + +/* + * 6.5 SDES: Source Description RTCP Packet + 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 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +header |V=2|P| SC | PT=SDES=202 | length | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +chunk | SSRC/CSRC_1 | + 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SDES items | + | ... | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +chunk | SSRC/CSRC_2 | + 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | SDES items | + | ... | + +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + */ + +/* + +SDES items 定义 +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 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| SdesType | length | user and domain name ... ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +//Source description item +class SdesItem { +public: + friend class RtcpSdes; + + uint32_t ssrc; + //SdesType + uint8_t type; + //text长度股,可以为0 + uint8_t length; + //不定长 + char text; + //最后以RTCP_SDES_END结尾 + //只字段为占位字段,不代表真实位置 + uint8_t end; + +public: + /** + * 返回改对象字节长度 + */ + size_t totalBytes() const; + + /** + * 本对象最少长度 + */ + static size_t minSize(); + +private: + /** + * 打印字段详情 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + string dumpString() const; + + /** + * 网络字节序转换为主机字节序 + */ + void net2Host(); +} PACKED; + +//Source description +class RtcpSdes : public RtcpHeader { +public: + friend class RtcpHeader; + + //可能有很多个 + SdesItem items; + +public: + /** + * 创建SDES包,只赋值了RtcpHeader以及SdesItem对象的length和text部分 + * @param item_text SdesItem列表,只赋值length和text部分 + * @return SDES包 + */ + static std::shared_ptr create(const std::initializer_list &item_text); + + /** + * 获取SdesItem对象指针列表 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + vector getItemList(); + +private: + /** + * 打印字段详情 + * 使用net2Host转换成主机字节序后才可使用此函数 + */ + string dumpString() const; + + /** + * 网络字节序转换为主机字节序 + * @param size 字节长度,防止内存越界 + */ + void net2Host(size_t size); +} PACKED; + +#if defined(_WIN32) +#pragma pack(pop) +#endif // defined(_WIN32) + +} //namespace mediakit +#endif //ZLMEDIAKIT_RTCP_H diff --git a/src/Rtcp/RtcpContext.cpp b/src/Rtcp/RtcpContext.cpp new file mode 100644 index 00000000..39ccd06d --- /dev/null +++ b/src/Rtcp/RtcpContext.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xia-chu/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 "RtcpContext.h" +using namespace toolkit; + +namespace mediakit { + +void RtcpContext::clear(){ + memset(this, 0, sizeof(RtcpContext)); +} + +RtcpContext::RtcpContext(uint32_t sample_rate){ + _sample_rate = sample_rate; +} + +void RtcpContext::onRtp(uint16_t seq, uint32_t stamp, size_t bytes) { + _bytes += bytes; + ++_packets; + + auto sys_stamp = getCurrentMillisecond(); + if (_last_rtp_sys_stamp) { + //计算时间戳抖动值 + double diff = double(int64_t(sys_stamp) - int64_t(_last_rtp_sys_stamp) - int64_t(stamp) + int64_t(_last_rtp_stamp)); + if (diff < 0) { + diff = -diff; + } + //抖动单位为采样次数 + diff *= (_sample_rate / 1000.0); + _jitter += (diff - _jitter) / 16.0; + } else { + _jitter = 0; + } + + if (_last_rtp_seq > 0xFF00 && seq < 0xFF && (!_seq_cycles || _packets - _last_cycle_packets > 0x1FFF)) { + //上次seq大于0xFF00且本次seq小于0xFF, + //且未发生回环或者距离上次回环间隔超过0x1FFF个包,则认为回环 + ++_seq_cycles; + _last_cycle_packets = _packets; + _seq_max = seq; + } else if (seq > _seq_max) { + //本次回环前最大seq + _seq_max = seq; + } + + if (!_seq_base) { + //记录第一个rtp的seq + _seq_base = seq; + } else if (!_seq_cycles && seq < _seq_base) { + //未发生回环,那么取最新的seq为基准seq + _seq_base = seq; + } + + _last_rtp_stamp = stamp; + _last_rtp_sys_stamp = sys_stamp; + _last_rtp_seq = seq; +} + +void RtcpContext::onRtcp(RtcpHeader *rtcp){ + if ((RtcpType) rtcp->pt != RtcpType::RTCP_SR) { + return; + } + auto rtcp_sr = (RtcpSR *)(rtcp); + /** + last SR timestamp (LSR): 32 bits + The middle 32 bits out of 64 in the NTP timestamp (as explained in + Section 4) received as part of the most recent RTCP sender report + (SR) packet from source SSRC_n. If no SR has been received yet, + the field is set to zero. + */ + _last_sr_lsr = ((rtcp_sr->ntpmsw & 0xFFFF) << 16) | ((rtcp_sr->ntplsw >> 16) & 0xFFFF); + _last_sr_ntp_sys = getCurrentMillisecond(); +} + +size_t RtcpContext::getExpectedPackets() const{ + return (_seq_cycles << 16) + _seq_max - _seq_base + 1; +} + +size_t RtcpContext::getExpectedPacketsInterval(){ + auto expected = getExpectedPackets(); + auto ret = expected - _last_expected; + _last_expected = expected; + return ret; +} + +size_t RtcpContext::getLost(){ + return getExpectedPackets() - _packets; +} + +size_t RtcpContext::geLostInterval(){ + auto lost = getLost(); + auto ret = lost - _last_lost; + _last_lost = lost; + return ret; +} + +Buffer::Ptr RtcpContext::createRtcpSR(uint32_t rtcp_ssrc){ + auto rtcp = RtcpSR::create(0); + rtcp->ssrc = htonl(rtcp_ssrc); + + struct timeval tv; + gettimeofday(&tv, NULL); + rtcp->setNtpStamp(tv); + + //转换成rtp时间戳 + rtcp->rtpts = htonl(uint32_t(_last_rtp_stamp * (_sample_rate / 1000.0))); + rtcp->packet_count = htonl((uint32_t)_packets); + rtcp->octet_count = htonl((uint32_t)_bytes); + return RtcpHeader::toBuffer(std::move(rtcp)); +} + +Buffer::Ptr RtcpContext::createRtcpRR(uint32_t rtcp_ssrc, uint32_t rtp_ssrc){ + auto rtcp = RtcpRR::create(1); + rtcp->ssrc = htonl(rtcp_ssrc); + + ReportItem *item = (ReportItem *) &rtcp->items; + item->ssrc = htonl(rtp_ssrc); + + uint8_t fraction = 0; + auto expected_interval = getExpectedPacketsInterval(); + if (expected_interval) { + fraction = uint8_t(geLostInterval() << 8 / expected_interval); + } + + item->fraction = fraction; + item->cumulative = htonl(uint32_t(getLost())) >> 8; + item->seq_cycles = htons(_seq_cycles); + item->seq_max = htons(_seq_max); + item->jitter = htonl(uint32_t(_jitter)); + item->last_sr_stamp = htonl(_last_sr_lsr); + + // now - Last SR time,单位毫秒 + auto delay = getCurrentMillisecond() - _last_sr_ntp_sys; + // in units of 1/65536 seconds + auto dlsr = (uint32_t)(delay / 1000.0f * 65536); + item->delay_since_last_sr = htonl(_last_sr_lsr ? dlsr : 0); + return RtcpHeader::toBuffer(rtcp); +} + +}//namespace mediakit \ No newline at end of file diff --git a/src/Rtcp/RtcpContext.h b/src/Rtcp/RtcpContext.h new file mode 100644 index 00000000..06bac452 --- /dev/null +++ b/src/Rtcp/RtcpContext.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xia-chu/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_RTCPCONTEXT_H +#define ZLMEDIAKIT_RTCPCONTEXT_H + +#include +#include +#include "Rtcp.h" + +namespace mediakit { + +class RtcpContext { +public: + using Ptr = std::shared_ptr; + /** + * 创建rtcp上下文 + * @param sample_rate 音频采用率,视频一般为90000 + */ + RtcpContext(uint32_t sample_rate); + + /** + * 输出或输入rtp时调用 + * @param seq rtp的seq + * @param stamp rtp的时间戳,单位毫秒 + * @param bytes rtp数据长度 + */ + void onRtp(uint16_t seq, uint32_t stamp, size_t bytes); + + /** + * 输入sr rtcp包 + * @param rtcp 输入一个rtcp + */ + void onRtcp(RtcpHeader *rtcp); + + /** + * 计算总丢包数 + */ + size_t getLost(); + + /** + * 返回理应收到的rtp数 + */ + size_t getExpectedPackets() const; + + /** + * 创建SR rtcp包 + * @param rtcp_ssrc rtcp的ssrc + * @return rtcp包 + */ + Buffer::Ptr createRtcpSR(uint32_t rtcp_ssrc); + + /** + * 创建RR rtcp包 + * @param rtcp_ssrc rtcp的ssrc + * @param rtp_ssrc rtp的ssrc + * @return rtcp包 + */ + Buffer::Ptr createRtcpRR(uint32_t rtcp_ssrc, uint32_t rtp_ssrc); + + /** + * 清空状态 + */ + void clear(); + +private: + /** + * 上次结果与本次结果间应收包数 + */ + size_t getExpectedPacketsInterval(); + + /** + * 上次结果与本次结果间丢包个数 + */ + size_t geLostInterval(); + +private: + //时间戳抖动值 + double _jitter = 0; + //视频默认90000,音频为采样率 + uint32_t _sample_rate; + //收到或发送的rtp的字节数 + size_t _bytes = 0; + //收到或发送的rtp的个数 + size_t _packets = 0; + //第一个seq的值 + uint16_t _seq_base = 0; + //rtp最大seq + uint16_t _seq_max = 0; + //rtp回环次数 + uint16_t _seq_cycles = 0; + //上次回环发生时,记录的rtp包数 + size_t _last_cycle_packets = 0; + //上次的seq + uint16_t _last_rtp_seq = 0; + //上次的rtp时间戳,毫秒 + uint32_t _last_rtp_stamp = 0; + //上次的rtp的系统时间戳(毫秒)用于统计抖动 + uint64_t _last_rtp_sys_stamp = 0; + //上次统计的丢包总数 + size_t _last_lost = 0; + //上次统计应收rtp包总数 + size_t _last_expected = 0; + //上次收到sr包时计算出的Last SR timestamp + uint32_t _last_sr_lsr = 0; + //上次收到sr时的系统时间戳,单位毫秒 + uint64_t _last_sr_ntp_sys = 0; +}; + +}//namespace mediakit +#endif //ZLMEDIAKIT_RTCPCONTEXT_H diff --git a/src/Rtsp/RtpReceiver.cpp b/src/Rtsp/RtpReceiver.cpp index 620d8319..b0702613 100644 --- a/src/Rtsp/RtpReceiver.cpp +++ b/src/Rtsp/RtpReceiver.cpp @@ -30,7 +30,7 @@ RtpReceiver::RtpReceiver() { } RtpReceiver::~RtpReceiver() {} -bool RtpReceiver::handleOneRtp(int track_index, TrackType type, int samplerate, unsigned char *rtp_raw_ptr, size_t rtp_raw_len) { +bool RtpReceiver::handleOneRtp(int track_index, TrackType type, int samplerate, uint8_t *rtp_raw_ptr, size_t rtp_raw_len) { if (rtp_raw_len < 12) { WarnL << "rtp包太小:" << rtp_raw_len; return false; @@ -136,6 +136,7 @@ bool RtpReceiver::handleOneRtp(int track_index, TrackType type, int samplerate, memcpy(payload_ptr + 4, rtp_raw_ptr, rtp_raw_len); //排序rtp auto seq = rtp_ptr->sequence; + onBeforeRtpSorted(rtp_ptr, track_index); _rtp_sortor[track_index].sortPacket(seq, std::move(rtp_ptr)); return true; } diff --git a/src/Rtsp/RtpReceiver.h b/src/Rtsp/RtpReceiver.h index 4aab788a..1b804490 100644 --- a/src/Rtsp/RtpReceiver.h +++ b/src/Rtsp/RtpReceiver.h @@ -175,7 +175,7 @@ protected: * @param rtp_raw_len rtp数据指针长度 * @return 解析成功返回true */ - bool handleOneRtp(int track_index, TrackType type, int samplerate, unsigned char *rtp_raw_ptr, size_t rtp_raw_len); + bool handleOneRtp(int track_index, TrackType type, int samplerate, uint8_t *rtp_raw_ptr, size_t rtp_raw_len); /** * rtp数据包排序后输出 @@ -184,6 +184,13 @@ protected: */ virtual void onRtpSorted(const RtpPacket::Ptr &rtp, int track_index) {} + /** + * 解析出rtp但还未排序 + * @param rtp rtp数据包 + * @param track_index track索引 + */ + virtual void onBeforeRtpSorted(const RtpPacket::Ptr &rtp, int track_index) {} + void clear(); void setPoolSize(size_t size); size_t getJitterSize(int track_index) const; diff --git a/src/Rtsp/Rtsp.cpp b/src/Rtsp/Rtsp.cpp index d6cf72b8..d3ca95e6 100644 --- a/src/Rtsp/Rtsp.cpp +++ b/src/Rtsp/Rtsp.cpp @@ -414,4 +414,15 @@ string printSSRC(uint32_t ui32Ssrc) { return tmp; } +Buffer::Ptr makeRtpOverTcpPrefix(uint16_t size, uint8_t interleaved){ + auto rtp_tcp = std::make_shared(4); + auto ptr = rtp_tcp->data(); + ptr[0] = '$'; + ptr[1] = interleaved; + ptr[2] = (size >> 8) & 0xFF; + ptr[3] = size & 0xFF; + rtp_tcp->setSize(4); + return rtp_tcp; +} + }//namespace mediakit \ No newline at end of file diff --git a/src/Rtsp/Rtsp.h b/src/Rtsp/Rtsp.h index e57cf74a..a3c5e0d8 100644 --- a/src/Rtsp/Rtsp.h +++ b/src/Rtsp/Rtsp.h @@ -82,6 +82,8 @@ public: size_t offset; }; +Buffer::Ptr makeRtpOverTcpPrefix(uint16_t size, uint8_t interleaved); + class RtpPayload{ public: static int getClockRate(int pt); diff --git a/src/Rtsp/RtspPlayer.cpp b/src/Rtsp/RtspPlayer.cpp index c2c6fe26..47c95ec1 100644 --- a/src/Rtsp/RtspPlayer.cpp +++ b/src/Rtsp/RtspPlayer.cpp @@ -15,9 +15,8 @@ #include "Common/config.h" #include "RtspPlayer.h" #include "Util/MD5.h" -#include "Util/util.h" #include "Util/base64.h" -#include "Network/sockutil.h" +#include "Rtcp/Rtcp.h" using namespace toolkit; using namespace mediakit::Client; @@ -53,13 +52,10 @@ void RtspPlayer::teardown(){ _session_id.clear(); _content_base.clear(); RtpReceiver::clear(); + _rtcp_context.clear(); 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) _play_check_timer.reset(); _rtp_check_timer.reset(); @@ -119,7 +115,12 @@ void RtspPlayer::onRecv(const Buffer::Ptr& buf) { _rtp_recv_ticker.resetTime(); return; } - input(buf->data(), buf->size()); + try { + input(buf->data(), buf->size()); + } catch (exception &e) { + SockException ex(Err_other, e.what()); + onPlayResult_l(ex, !_play_check_timer); + } } void RtspPlayer::onErr(const SockException &ex) { @@ -196,15 +197,15 @@ void RtspPlayer::handleResDESCRIBE(const Parser& parser) { SdpParser sdpParser(parser.Content()); //解析sdp _sdp_track = sdpParser.getAvailableTrack(); - auto title = sdpParser.getTrack(TrackTitle); - if (_sdp_track.empty()) { throw std::runtime_error("无有效的Sdp Track"); } if (!onCheckSDP(sdpParser.toString())) { throw std::runtime_error("onCheckSDP faied"); } - + for (auto &track : _sdp_track) { + _rtcp_context.emplace_back(std::make_shared(track->_samplerate)); + } sendSetup(0); } @@ -331,7 +332,7 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int track_idx) { return; } strongSelf->handleOneRtp(track_idx, strongSelf->_sdp_track[track_idx]->_type, - strongSelf->_sdp_track[track_idx]->_samplerate, (unsigned char *) buf->data(), buf->size()); + strongSelf->_sdp_track[track_idx]->_samplerate, (uint8_t *) buf->data(), buf->size()); }); if(pRtcpSockRef) { @@ -345,7 +346,7 @@ void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int track_idx) { WarnL << "收到其他地址的rtcp数据:" << SockUtil::inet_ntoa(((struct sockaddr_in *) addr)->sin_addr); return; } - strongSelf->onRtcpPacket(track_idx, strongSelf->_sdp_track[track_idx], (unsigned char *) buf->data(), buf->size()); + strongSelf->onRtcpPacket(track_idx, strongSelf->_sdp_track[track_idx], (uint8_t *) buf->data(), buf->size()); }); } } @@ -477,174 +478,44 @@ void RtspPlayer::onRtpPacket(const char *data, size_t len) { uint8_t interleaved = data[1]; if(interleaved %2 == 0){ trackIdx = getTrackIndexByInterleaved(interleaved); - handleOneRtp(trackIdx, _sdp_track[trackIdx]->_type, _sdp_track[trackIdx]->_samplerate, (unsigned char *)data + 4, len - 4); + handleOneRtp(trackIdx, _sdp_track[trackIdx]->_type, _sdp_track[trackIdx]->_samplerate, (uint8_t *)data + 4, len - 4); }else{ trackIdx = getTrackIndexByInterleaved(interleaved - 1); - onRtcpPacket(trackIdx, _sdp_track[trackIdx], (unsigned char *) data + 4, len - 4); + onRtcpPacket(trackIdx, _sdp_track[trackIdx], (uint8_t *) data + 4, len - 4); } } //此处预留rtcp处理函数 -void RtspPlayer::onRtcpPacket(int track_idx, SdpTrack::Ptr &track, unsigned char *data, size_t len){} - -#if 0 -//改代码提取自FFmpeg,参考之 -// Receiver Report - avio_w8(pb, (RTP_VERSION << 6) + 1); /* 1 report block */ - avio_w8(pb, RTCP_RR); - avio_wb16(pb, 7); /* length in words - 1 */ - // our own SSRC: we use the server's SSRC + 1 to avoid conflicts - avio_wb32(pb, s->ssrc + 1); - avio_wb32(pb, s->ssrc); // server SSRC - // some placeholders we should really fill... - // RFC 1889/p64 - extended_max = stats->cycles + stats->max_seq; - expected = extended_max - stats->base_seq; - lost = expected - stats->received; - lost = FFMIN(lost, 0xffffff); // clamp it since it's only 24 bits... - expected_interval = expected - stats->expected_prior; - stats->expected_prior = expected; - received_interval = stats->received - stats->received_prior; - stats->received_prior = stats->received; - lost_interval = expected_interval - received_interval; - if (expected_interval == 0 || lost_interval <= 0) - fraction = 0; - else - fraction = (lost_interval << 8) / expected_interval; - - fraction = (fraction << 24) | lost; - - avio_wb32(pb, fraction); /* 8 bits of fraction, 24 bits of total packets lost */ - avio_wb32(pb, extended_max); /* max sequence received */ - avio_wb32(pb, stats->jitter >> 4); /* jitter */ - - if (s->last_rtcp_ntp_time == AV_NOPTS_VALUE) { - avio_wb32(pb, 0); /* last SR timestamp */ - avio_wb32(pb, 0); /* delay since last SR */ - } else { - uint32_t middle_32_bits = s->last_rtcp_ntp_time >> 16; // this is valid, right? do we need to handle 64 bit values special? - uint32_t delay_since_last = av_rescale(av_gettime_relative() - s->last_rtcp_reception_time, - 65536, AV_TIME_BASE); - - avio_wb32(pb, middle_32_bits); /* last SR timestamp */ - avio_wb32(pb, delay_since_last); /* delay since last SR */ - } - - // CNAME - avio_w8(pb, (RTP_VERSION << 6) + 1); /* 1 report block */ - avio_w8(pb, RTCP_SDES); - len = strlen(s->hostname); - avio_wb16(pb, (7 + len + 3) / 4); /* length in words - 1 */ - avio_wb32(pb, s->ssrc + 1); - avio_w8(pb, 0x01); - avio_w8(pb, len); - avio_write(pb, s->hostname, len); - avio_w8(pb, 0); /* END */ - // padding - for (len = (7 + len) % 4; len % 4; len++) - avio_w8(pb, 0); -#endif - -void RtspPlayer::sendReceiverReport(bool over_tcp, int track_idx){ - 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 = _sdp_track[track_idx]; - auto &counter = _rtcp_counter[track_idx]; - - aui8Rtcp[0] = '$'; - aui8Rtcp[1] = track->_interleaved + 1; - aui8Rtcp[2] = (sizeof(aui8Rtcp) - 4) >> 8; - aui8Rtcp[3] = (sizeof(aui8Rtcp) - 4) & 0xFF; - - pui8Rtcp_RR[0] = 0x81;/* 1 report block */ - pui8Rtcp_RR[1] = 0xC9;//RTCP_RR - pui8Rtcp_RR[2] = 0x00; - pui8Rtcp_RR[3] = 0x07;/* length in words - 1 */ - - auto track_ssrc = track->_ssrc ? track->_ssrc : getSSRC(track_idx); - // our own SSRC: we use the server's SSRC + 1 to avoid conflicts - uint32_t ssrc = htonl(track_ssrc + 1); - memcpy(&pui8Rtcp_RR[4], &ssrc, 4); - - // server SSRC - ssrc = htonl(track_ssrc); - memcpy(&pui8Rtcp_RR[8], &ssrc, 4); - - //FIXME: 8 bits of fraction, 24 bits of total packets lost - pui8Rtcp_RR[12] = 0x00; - pui8Rtcp_RR[13] = 0x00; - pui8Rtcp_RR[14] = 0x00; - pui8Rtcp_RR[15] = 0x00; - - //FIXME: max sequence received - uint16_t cycleCount = (uint16_t)getCycleCount(track_idx); - pui8Rtcp_RR[16] = (cycleCount >> 8) & 0xFF; - pui8Rtcp_RR[17] = cycleCount & 0xFF; - pui8Rtcp_RR[18] = counter.pktCnt >> 8; - pui8Rtcp_RR[19] = counter.pktCnt & 0xFF; - - uint32_t jitter = htonl((uint32_t)getJitterSize(track_idx)); - //FIXME: jitter - memcpy(pui8Rtcp_RR + 20, &jitter, 4); - /* last SR timestamp */ - memcpy(pui8Rtcp_RR + 24, &counter.lastTimeStamp, 4); - uint32_t msInc = htonl(ntohl(counter.timeStamp) - ntohl(counter.lastTimeStamp)); - /* delay since last SR */ - memcpy(pui8Rtcp_RR + 28, &msInc, 4); - - // CNAME - pui8Rtcp_SDES[0] = 0x81; - pui8Rtcp_SDES[1] = 0xCA; - pui8Rtcp_SDES[2] = 0x00; - pui8Rtcp_SDES[3] = 0x06; - - memcpy(&pui8Rtcp_SDES[4], &ssrc, 4); - - pui8Rtcp_SDES[8] = 0x01; - pui8Rtcp_SDES[9] = 0x0f; - memcpy(&pui8Rtcp_SDES[10], s_cname, sizeof(s_cname)); - pui8Rtcp_SDES[10 + sizeof(s_cname)] = 0x00; - - if (over_tcp) { - send(obtainBuffer((char *) aui8Rtcp, sizeof(aui8Rtcp))); - } else if (_rtcp_sock[track_idx]) { - _rtcp_sock[track_idx]->send((char *) aui8Rtcp + 4, sizeof(aui8Rtcp) - 4); +void RtspPlayer::onRtcpPacket(int track_idx, SdpTrack::Ptr &track, uint8_t *data, size_t len){ + auto rtcp_arr = RtcpHeader::loadFromBytes((char *) data, len); + for (auto &rtcp : rtcp_arr) { + _rtcp_context[track_idx]->onRtcp(rtcp); } } void RtspPlayer::onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx){ - //统计丢包率 - if (_rtp_seq_start[trackidx] == 0 || rtppt->sequence < _rtp_seq_start[trackidx]) { - _rtp_seq_start[trackidx] = rtppt->sequence; - _rtp_recv_count[trackidx] = 0; - } - _rtp_recv_count[trackidx] ++; - _rtp_seq_now[trackidx] = rtppt->sequence; _stamp[trackidx] = rtppt->timeStamp; - //计算相对时间戳 - onRecvRTP_l(rtppt, _sdp_track[trackidx]); + _rtp_recv_ticker.resetTime(); + onRecvRTP(rtppt, _sdp_track[trackidx]); } float RtspPlayer::getPacketLossRate(TrackType type) const{ + size_t lost = 0, expected = 0; try { auto track_idx = getTrackIndexByTrackType(type); - if (_rtp_seq_now[track_idx] - _rtp_seq_start[track_idx] + 1 == 0) { - return 0; - } - return (float)(1.0f - (double) _rtp_recv_count[track_idx] / (_rtp_seq_now[track_idx] - _rtp_seq_start[track_idx] + 1)); + auto ctx = _rtcp_context[track_idx]; + lost = ctx->getLost(); + expected = ctx->getExpectedPackets(); } catch (...) { - uint64_t totalRecv = 0; - uint64_t totalSend = 0; - 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); + for (auto &ctx : _rtcp_context) { + lost += ctx->getLost(); + expected += ctx->getExpectedPackets(); } - if (totalSend == 0) { - return 0; - } - return (float)(1.0f - (double) totalRecv / totalSend); } + if (!expected) { + return 0; + } + return (float) (double(lost) / double(expected)); } uint32_t RtspPlayer::getProgressMilliSecond() const{ @@ -720,35 +591,51 @@ void RtspPlayer::sendRtspRequest(const string &cmd, const string &url,const StrC SockSender::send(std::move(printer)); } -void RtspPlayer::onRecvRTP_l(const RtpPacket::Ptr &rtp, const SdpTrack::Ptr &track) { - _rtp_recv_ticker.resetTime(); - onRecvRTP(rtp, track); +void RtspPlayer::onBeforeRtpSorted(const RtpPacket::Ptr &rtp, int track_idx){ + auto &rtcp_ctx = _rtcp_context[track_idx]; + rtcp_ctx->onRtp(rtp->sequence, rtp->timeStamp, rtp->size() - 4); - int track_idx = getTrackIndexByTrackType(rtp->type); - RtcpCounter &counter = _rtcp_counter[track_idx]; - counter.pktCnt = rtp->sequence; auto &ticker = _rtcp_send_ticker[track_idx]; - if (ticker.elapsedTime() > 3 * 1000) { - //每3秒发送一次心跳,rtcp与rtsp信令轮流心跳,该特性用于兼容issue:642 - if (_send_rtcp) { - counter.lastTimeStamp = counter.timeStamp; - //直接保存网络字节序 - memcpy(&counter.timeStamp, rtp->data() + 8, 4); - if (counter.lastTimeStamp != 0) { - sendReceiverReport(_rtp_type == Rtsp::RTP_TCP, track_idx); - ticker.resetTime(); - _send_rtcp = false; - } - } else { - //有些rtsp服务器需要rtcp保活,有些需要发送信令保活 - if (track_idx == 0) { - //只需要发送一次心跳信令包 - sendKeepAlive(); - ticker.resetTime(); - _send_rtcp = true; - } - } + if (ticker.elapsedTime() < 3 * 1000) { + //时间未到 + return; } + auto &rtcp_flag = _send_rtcp[track_idx]; + + //每3秒发送一次心跳,rtcp与rtsp信令轮流心跳,该特性用于兼容issue:642 + //有些rtsp服务器需要rtcp保活,有些需要发送信令保活 + + //发送信令保活 + if (!rtcp_flag) { + if (track_idx == 0) { + sendKeepAlive(); + } + ticker.resetTime(); + //下次发送rtcp保活 + rtcp_flag = true; + return; + } + + //发送rtcp + static auto send_rtcp = [](RtspPlayer *thiz, int index, Buffer::Ptr ptr) { + if (thiz->_rtp_type == Rtsp::RTP_TCP) { + auto &track = thiz->_sdp_track[index]; + thiz->send(makeRtpOverTcpPrefix((uint16_t) (ptr->size()), track->_interleaved + 1)); + thiz->send(std::move(ptr)); + } else { + thiz->_rtcp_sock[index]->send(std::move(ptr)); + } + }; + + auto rtcp = rtcp_ctx->createRtcpRR(rtp->ssrc + 1, rtp->ssrc); + auto rtcp_sdes = RtcpSdes::create({SERVER_NAME}); + rtcp_sdes->items.type = (uint8_t) SdesType::RTCP_SDES_CNAME; + rtcp_sdes->items.ssrc = htonl(rtp->ssrc); + send_rtcp(this, track_idx, std::move(rtcp)); + send_rtcp(this, track_idx, RtcpHeader::toBuffer(rtcp_sdes)); + ticker.resetTime(); + //下次发送信令保活 + rtcp_flag = false; } void RtspPlayer::onPlayResult_l(const SockException &ex , bool handshake_done) { diff --git a/src/Rtsp/RtspPlayer.h b/src/Rtsp/RtspPlayer.h index 87f9e3de..22d9fafa 100644 --- a/src/Rtsp/RtspPlayer.h +++ b/src/Rtsp/RtspPlayer.h @@ -25,6 +25,7 @@ #include "RtspSplitter.h" #include "RtpReceiver.h" #include "Common/Stamp.h" +#include "Rtcp/RtcpContext.h" using namespace std; using namespace toolkit; @@ -71,6 +72,13 @@ protected: */ void onRtpSorted(const RtpPacket::Ptr &rtp, int track_idx) override; + /** + * 解析出rtp但还未排序 + * @param rtp rtp数据包 + * @param track_index track索引 + */ + void onBeforeRtpSorted(const RtpPacket::Ptr &rtp, int track_index) override; + /** * 收到RTCP包回调 * @param track_idx track索引 @@ -78,7 +86,7 @@ protected: * @param data rtcp内容 * @param len rtcp内容长度 */ - virtual void onRtcpPacket(int track_idx, SdpTrack::Ptr &track, unsigned char *data, size_t len); + virtual void onRtcpPacket(int track_idx, SdpTrack::Ptr &track, uint8_t *data, size_t len); /////////////TcpClient override///////////// void onConnect(const SockException &err) override; @@ -86,7 +94,6 @@ protected: void onErr(const SockException &ex) override; private: - void onRecvRTP_l(const RtpPacket::Ptr &rtp, const SdpTrack::Ptr &track); void onPlayResult_l(const SockException &ex , bool handshake_done); int getTrackIndexByInterleaved(int interleaved) const; @@ -106,14 +113,13 @@ private: 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 over_tcp, int track_idx); void createUdpSockIfNecessary(int track_idx); private: //是否为性能测试模式 bool _benchmark_mode = false; //轮流发送rtcp与GET_PARAMETER保活 - bool _send_rtcp = true; + bool _send_rtcp[2] = {true, true}; string _play_url; vector _sdp_track; @@ -132,10 +138,6 @@ private: string _content_base; Rtsp::eRtpType _rtp_type = Rtsp::RTP_TCP; - /* 丢包率统计需要用到的参数 */ - 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}; @@ -143,14 +145,13 @@ private: Ticker _rtp_recv_ticker; std::shared_ptr _play_check_timer; std::shared_ptr _rtp_check_timer; - - //rtcp统计,trackid idx 为数组下标 - RtcpCounter _rtcp_counter[2]; - //rtcp发送时间,trackid idx 为数组下标 - Ticker _rtcp_send_ticker[2]; - //服务器支持的命令 set _supported_cmd; + ////////// rtcp //////////////// + //rtcp发送时间,trackid idx 为数组下标 + Ticker _rtcp_send_ticker[2]; + //统计rtp并发送rtcp + vector _rtcp_context; }; } /* namespace mediakit */ diff --git a/src/Rtsp/RtspPusher.cpp b/src/Rtsp/RtspPusher.cpp index 38354653..e180c0b0 100644 --- a/src/Rtsp/RtspPusher.cpp +++ b/src/Rtsp/RtspPusher.cpp @@ -38,7 +38,8 @@ void RtspPusher::sendTeardown(){ void RtspPusher::teardown() { sendTeardown(); reset(); - CLEAR_ARR(_udp_socks); + CLEAR_ARR(_rtp_sock); + CLEAR_ARR(_rtcp_sock); _nonce.clear(); _realm.clear(); _track_vec.clear(); @@ -148,6 +149,22 @@ void RtspPusher::onWholeRtspPacket(Parser &parser) { parser.Clear(); } +void RtspPusher::onRtpPacket(const char *data, size_t len) { + int trackIdx = -1; + uint8_t interleaved = data[1]; + if (interleaved % 2 != 0) { + trackIdx = getTrackIndexByInterleaved(interleaved - 1); + onRtcpPacket(trackIdx, _track_vec[trackIdx], (uint8_t *) data + 4, len - 4); + } +} + +void RtspPusher::onRtcpPacket(int track_idx, SdpTrack::Ptr &track, uint8_t *data, size_t len){ + auto rtcp_arr = RtcpHeader::loadFromBytes((char *) data, len); + for (auto &rtcp : rtcp_arr) { + _rtcp_context[track_idx]->onRtcp(rtcp); + } +} + void RtspPusher::sendAnnounce() { auto src = _push_src.lock(); if (!src) { @@ -156,11 +173,12 @@ void RtspPusher::sendAnnounce() { //解析sdp _sdp_parser.load(src->getSdp()); _track_vec = _sdp_parser.getAvailableTrack(); - if (_track_vec.empty()) { throw std::runtime_error("无有效的Sdp Track"); } - + for (auto &track : _track_vec) { + _rtcp_context.emplace_back(std::make_shared(track->_samplerate)); + } _on_res_func = std::bind(&RtspPusher::handleResAnnounce, this, placeholders::_1); sendRtspRequest("ANNOUNCE", _url, {}, src->getSdp()); } @@ -229,14 +247,13 @@ bool RtspPusher::handleAuthenticationFailure(const string ¶ms_str) { //有必要的情况下创建udp端口 void RtspPusher::createUdpSockIfNecessary(int track_idx){ - auto &rtp_sock = _udp_socks[track_idx]; - if (!rtp_sock) { - rtp_sock = createSocket(); - //rtp随机端口 - if (!rtp_sock->bindUdpSock(0, get_local_ip().data())) { - rtp_sock.reset(); - throw std::runtime_error("open rtp sock failed"); - } + auto &rtpSockRef = _rtp_sock[track_idx]; + auto &rtcpSockRef = _rtcp_sock[track_idx]; + if (!rtpSockRef || !rtcpSockRef) { + std::pair pr = std::make_pair(createSocket(), createSocket()); + makeSockPair(pr, get_local_ip()); + rtpSockRef = pr.first; + rtcpSockRef = pr.second; } } @@ -256,7 +273,7 @@ void RtspPusher::sendSetup(unsigned int track_idx) { break; case Rtsp::RTP_UDP: { createUdpSockIfNecessary(track_idx); - int port = _udp_socks[track_idx]->get_local_port(); + int port = _rtp_sock[track_idx]->get_local_port(); sendRtspRequest("SETUP", base_url, {"Transport", StrPrinter << "RTP/AVP;unicast;client_port=" << port << "-" << port + 1}); } @@ -266,7 +283,6 @@ void RtspPusher::sendSetup(unsigned int track_idx) { } } - void RtspPusher::handleResSetup(const Parser &parser, unsigned int track_idx) { if (parser.Url() != "200") { throw std::runtime_error(StrPrinter << "SETUP:" << parser.Url() << " " << parser.Tail() << endl); @@ -289,12 +305,40 @@ void RtspPusher::handleResSetup(const Parser &parser, unsigned int track_idx) { createUdpSockIfNecessary(track_idx); const char *strPos = "server_port="; auto port_str = FindField((transport + ";").data(), strPos, ";"); - uint16_t port = atoi(FindField(port_str.data(), NULL, "-").data()); + uint16_t rtp_port = atoi(FindField(port_str.data(), NULL, "-").data()); + uint16_t rtcp_port = atoi(FindField(port_str.data(), "-", NULL).data()); + auto &rtp_sock = _rtp_sock[track_idx]; + auto &rtcp_sock = _rtcp_sock[track_idx]; + struct sockaddr_in rtpto; - rtpto.sin_port = ntohs(port); + //设置rtp发送目标,为后续发送rtp做准备 + rtpto.sin_port = ntohs(rtp_port); rtpto.sin_family = AF_INET; rtpto.sin_addr.s_addr = inet_addr(get_peer_ip().data()); - _udp_socks[track_idx]->setSendPeerAddr((struct sockaddr *) &(rtpto)); + rtp_sock->setSendPeerAddr((struct sockaddr *) &(rtpto)); + + //设置rtcp发送目标,为后续发送rtcp做准备 + rtpto.sin_port = ntohs(rtcp_port); + rtpto.sin_family = AF_INET; + rtpto.sin_addr.s_addr = inet_addr(get_peer_ip().data()); + rtcp_sock->setSendPeerAddr((struct sockaddr *)&(rtpto)); + + auto srcIP = inet_addr(get_peer_ip().data()); + weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); + if(rtcp_sock) { + //设置rtcp over udp接收回调处理函数 + rtcp_sock->setOnRead([srcIP, track_idx, weakSelf](const Buffer::Ptr &buf, struct sockaddr *addr , int addr_len) { + auto strongSelf = weakSelf.lock(); + if (!strongSelf) { + return; + } + if (((struct sockaddr_in *) addr)->sin_addr.s_addr != srcIP) { + WarnL << "收到其他地址的rtcp数据:" << SockUtil::inet_ntoa(((struct sockaddr_in *) addr)->sin_addr); + return; + } + strongSelf->onRtcpPacket(track_idx, strongSelf->_track_vec[track_idx], (uint8_t *) buf->data(), buf->size()); + }); + } } RtspSplitter::enableRecvRtp(_rtp_type == Rtsp::RTP_TCP); @@ -313,13 +357,42 @@ void RtspPusher::sendOptions() { sendRtspRequest("OPTIONS", _content_base); } -inline void RtspPusher::sendRtpPacket(const RtspMediaSource::RingDataType &pkt) { +void RtspPusher::updateRtcpContext(const RtpPacket::Ptr &rtp){ + int track_index = getTrackIndexByTrackType(rtp->type); + auto &ticker = _rtcp_send_ticker[track_index]; + auto &rtcp_ctx = _rtcp_context[track_index]; + rtcp_ctx->onRtp(rtp->sequence, rtp->timeStamp, rtp->size() - 4); + + //send rtcp every 5 second + if (ticker.elapsedTime() > 5 * 1000) { + ticker.resetTime(); + static auto send_rtcp = [](RtspPusher *thiz, int index, Buffer::Ptr ptr) { + if (thiz->_rtp_type == Rtsp::RTP_TCP) { + auto &track = thiz->_track_vec[index]; + thiz->send(makeRtpOverTcpPrefix((uint16_t) (ptr->size()), track->_interleaved + 1)); + thiz->send(std::move(ptr)); + } else { + thiz->_rtcp_sock[index]->send(std::move(ptr)); + } + }; + + auto rtcp = rtcp_ctx->createRtcpSR(rtp->ssrc + 1); + auto rtcp_sdes = RtcpSdes::create({SERVER_NAME}); + rtcp_sdes->items.type = (uint8_t) SdesType::RTCP_SDES_CNAME; + rtcp_sdes->items.ssrc = htonl(rtp->ssrc); + send_rtcp(this, track_index, std::move(rtcp)); + send_rtcp(this, track_index, RtcpHeader::toBuffer(rtcp_sdes)); + } +} + +void RtspPusher::sendRtpPacket(const RtspMediaSource::RingDataType &pkt) { switch (_rtp_type) { case Rtsp::RTP_TCP: { size_t i = 0; auto size = pkt->size(); setSendFlushFlag(false); pkt->for_each([&](const RtpPacket::Ptr &rtp) { + updateRtcpContext(rtp); if (++i == size) { setSendFlushFlag(true); } @@ -333,8 +406,9 @@ inline void RtspPusher::sendRtpPacket(const RtspMediaSource::RingDataType &pkt) size_t i = 0; auto size = pkt->size(); pkt->for_each([&](const RtpPacket::Ptr &rtp) { + updateRtcpContext(rtp); int iTrackIndex = getTrackIndexByTrackType(rtp->type); - auto &pSock = _udp_socks[iTrackIndex]; + auto &pSock = _rtp_sock[iTrackIndex]; if (!pSock) { shutdown(SockException(Err_shutdown, "udp sock not opened yet")); return; @@ -349,8 +423,20 @@ inline void RtspPusher::sendRtpPacket(const RtspMediaSource::RingDataType &pkt) } } -inline int RtspPusher::getTrackIndexByTrackType(TrackType type) { - for (unsigned int i = 0; i < _track_vec.size(); i++) { +int RtspPusher::getTrackIndexByInterleaved(int interleaved) const { + for (int i = 0; i < (int)_track_vec.size(); ++i) { + if (_track_vec[i]->_interleaved == interleaved) { + return i; + } + } + if (_track_vec.size() == 1) { + return 0; + } + throw SockException(Err_shutdown, StrPrinter << "no such track with interleaved:" << interleaved); +} + +int RtspPusher::getTrackIndexByTrackType(TrackType type) const{ + for (int i = 0; i < (int)_track_vec.size(); ++i) { if (type == _track_vec[i]->_type) { return i; } diff --git a/src/Rtsp/RtspPusher.h b/src/Rtsp/RtspPusher.h index dd0b27ff..5958d0b1 100644 --- a/src/Rtsp/RtspPusher.h +++ b/src/Rtsp/RtspPusher.h @@ -21,6 +21,7 @@ #include "Network/TcpClient.h" #include "RtspSplitter.h" #include "Pusher/PusherBase.h" +#include "Rtcp/RtcpContext.h" using namespace std; using namespace toolkit; @@ -51,7 +52,9 @@ protected: //RtspSplitter override void onWholeRtspPacket(Parser &parser) override ; - void onRtpPacket(const char *data,size_t len) override {}; + void onRtpPacket(const char *data,size_t len) override; + + virtual void onRtcpPacket(int track_idx, SdpTrack::Ptr &track, uint8_t *data, size_t len); private: void onPublishResult(const SockException &ex, bool handshake_done); @@ -66,7 +69,8 @@ private: void handleResSetup(const Parser &parser, unsigned int track_idx); bool handleAuthenticationFailure(const string ¶ms_str); - inline int getTrackIndexByTrackType(TrackType type); + int getTrackIndexByInterleaved(int interleaved) const; + int getTrackIndexByTrackType(TrackType type) const; void sendRtpPacket(const RtspMediaSource::RingDataType & pkt) ; void sendRtspRequest(const string &cmd, const string &url ,const StrCaseMap &header = StrCaseMap(),const string &sdp = "" ); @@ -74,6 +78,7 @@ private: void createUdpSockIfNecessary(int track_idx); void setSocketFlags(); + void updateRtcpContext(const RtpPacket::Ptr &pkt); private: unsigned int _cseq = 1; @@ -87,7 +92,10 @@ private: string _content_base; SdpParser _sdp_parser; vector _track_vec; - Socket::Ptr _udp_socks[2]; + //RTP端口,trackid idx 为数组下标 + Socket::Ptr _rtp_sock[2]; + //RTCP端口,trackid idx 为数组下标 + Socket::Ptr _rtcp_sock[2]; //超时功能实现 std::shared_ptr _publish_timer; //心跳定时器 @@ -98,6 +106,11 @@ private: Event _on_shutdown; Event _on_published; function _on_res_func; + ////////// rtcp //////////////// + //rtcp发送时间,trackid idx 为数组下标 + Ticker _rtcp_send_ticker[2]; + //统计rtp并发送rtcp + vector _rtcp_context; }; } /* namespace mediakit */ diff --git a/src/Rtsp/RtspSession.cpp b/src/Rtsp/RtspSession.cpp index c73b808f..63dd4fc3 100644 --- a/src/Rtsp/RtspSession.cpp +++ b/src/Rtsp/RtspSession.cpp @@ -13,15 +13,9 @@ #include "Common/config.h" #include "UDPServer.h" #include "RtspSession.h" -#include "Util/mini.h" #include "Util/MD5.h" #include "Util/base64.h" -#include "Util/onceToken.h" -#include "Util/TimeTicker.h" -#include "Util/NoticeCenter.h" -#include "Network/sockutil.h" - -#define RTSP_SERVER_SEND_RTCP 0 +#include "Rtcp/Rtcp.h" using namespace std; using namespace toolkit; @@ -171,21 +165,25 @@ void RtspSession::onWholeRtspPacket(Parser &parser) { } void RtspSession::onRtpPacket(const char *data, size_t len) { - if(!_push_src){ - return; - } - uint8_t interleaved = data[1]; - if(interleaved %2 == 0){ + if (interleaved % 2 == 0) { + if (!_push_src) { + return; + } auto track_idx = getTrackIndexByInterleaved(interleaved); - handleOneRtp(track_idx, _sdp_track[track_idx]->_type, _sdp_track[track_idx]->_samplerate, (unsigned char *) data + 4, len - 4); - }else{ + handleOneRtp(track_idx, _sdp_track[track_idx]->_type, _sdp_track[track_idx]->_samplerate, (uint8_t *) data + 4, len - 4); + } else { auto track_idx = getTrackIndexByInterleaved(interleaved - 1); onRtcpPacket(track_idx, _sdp_track[track_idx], data + 4, len - 4); } } -void RtspSession::onRtcpPacket(int track_idx, SdpTrack::Ptr &track, const char *data, size_t len){} +void RtspSession::onRtcpPacket(int track_idx, SdpTrack::Ptr &track, const char *data, size_t len){ + auto rtcp_arr = RtcpHeader::loadFromBytes((char *) data, len); + for (auto &rtcp : rtcp_arr) { + _rtcp_context[track_idx]->onRtcp(rtcp); + } +} ssize_t RtspSession::getContentLength(Parser &parser) { if(parser.Method() == "POST"){ @@ -224,18 +222,26 @@ void RtspSession::handleReq_ANNOUNCE(const Parser &parser) { if(_media_info._app.empty() || _media_info._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); + static constexpr auto err = "rtsp推流url非法,最少确保两级rtsp url"; + sendRtspResponse("403 Forbidden", {"Content-Type", "text/plain"}, err); + throw SockException(Err_shutdown, StrPrinter << err << ":" << full_url); } SdpParser sdpParser(parser.Content()); _sessionid = makeRandStr(12); _sdp_track = sdpParser.getAvailableTrack(); - + if (_sdp_track.empty()) { + //sdp无效 + static constexpr auto err = "sdp中无有效track"; + sendRtspResponse("403 Forbidden", {"Content-Type", "text/plain"}, err); + throw SockException(Err_shutdown,StrPrinter << err << ":" << full_url); + } + for (auto &track : _sdp_track) { + _rtcp_context.emplace_back(std::make_shared(track->_samplerate)); + } _push_src = std::make_shared(_media_info._vhost, _media_info._app, _media_info._streamid); _push_src->setListener(dynamic_pointer_cast(shared_from_this())); _push_src->setSdp(sdpParser.toString()); - sendRtspResponse("200 OK",{"Content-Base", _content_base + "/"}); } @@ -395,11 +401,14 @@ void RtspSession::onAuthSuccess() { strongSelf->_sdp_track = SdpParser(rtsp_src->getSdp()).getAvailableTrack(); if (strongSelf->_sdp_track.empty()) { //该流无效 - DebugL << "无trackInfo,该流无效"; + WarnL << "sdp中无有效track,该流无效:" << rtsp_src->getSdp(); strongSelf->send_StreamNotFound(); strongSelf->shutdown(SockException(Err_shutdown,"can not find any available track in sdp")); return; } + for (auto &track : strongSelf->_sdp_track) { + strongSelf->_rtcp_context.emplace_back(std::make_shared(track->_samplerate)); + } strongSelf->_sessionid = makeRandStr(12); strongSelf->_play_src = rtsp_src; for(auto &track : strongSelf->_sdp_track){ @@ -590,15 +599,15 @@ void RtspSession::onAuthUser(const string &realm,const string &authorization){ } } -inline void RtspSession::send_StreamNotFound() { +void RtspSession::send_StreamNotFound() { sendRtspResponse("404 Stream Not Found",{"Connection","Close"}); } -inline void RtspSession::send_UnsupportedTransport() { +void RtspSession::send_UnsupportedTransport() { sendRtspResponse("461 Unsupported Transport",{"Connection","Close"}); } -inline void RtspSession::send_SessionNotFound() { +void RtspSession::send_SessionNotFound() { sendRtspResponse("454 Session Not Found",{"Connection","Close"}); } @@ -899,7 +908,7 @@ void RtspSession::handleReq_SET_PARAMETER(const Parser &parser) { sendRtspResponse("200 OK"); } -inline void RtspSession::send_NotAcceptable() { +void RtspSession::send_NotAcceptable() { sendRtspResponse("406 Not Acceptable",{"Connection","Close"}); } @@ -913,7 +922,7 @@ void RtspSession::onRtpSorted(const RtpPacket::Ptr &rtp, int track_idx) { _push_src->onWrite(rtp, false); } -inline void RtspSession::onRcvPeerUdpData(int interleaved, const Buffer::Ptr &buf, const struct sockaddr &addr) { +void RtspSession::onRcvPeerUdpData(int interleaved, const Buffer::Ptr &buf, const struct sockaddr &addr) { //这是rtcp心跳包,说明播放器还存活 _alive_ticker.resetTime(); @@ -921,7 +930,7 @@ inline void RtspSession::onRcvPeerUdpData(int interleaved, const Buffer::Ptr &bu if (_push_src) { //这是rtsp推流上来的rtp包 auto &ref = _sdp_track[interleaved / 2]; - handleOneRtp(interleaved / 2, ref->_type, ref->_samplerate, (unsigned char *) buf->data(), buf->size()); + handleOneRtp(interleaved / 2, ref->_type, ref->_samplerate, (uint8_t *) buf->data(), buf->size()); } else if (!_udp_connected_flags.count(interleaved)) { //这是rtsp播放器的rtp打洞包 _udp_connected_flags.emplace(interleaved); @@ -937,7 +946,7 @@ inline void RtspSession::onRcvPeerUdpData(int interleaved, const Buffer::Ptr &bu } } -inline void RtspSession::startListenPeerUdpData(int track_idx) { +void RtspSession::startListenPeerUdpData(int track_idx) { weak_ptr weakSelf = dynamic_pointer_cast(shared_from_this()); auto srcIP = inet_addr(get_peer_ip().data()); auto onUdpData = [weakSelf,srcIP](const Buffer::Ptr &buf, struct sockaddr *peer_addr, int interleaved){ @@ -1052,7 +1061,7 @@ bool RtspSession::sendRtspResponse(const string &res_code, const std::initialize return sendRtspResponse(res_code,header_map,sdp,protocol); } -inline int RtspSession::getTrackIndexByTrackType(TrackType type) { +int RtspSession::getTrackIndexByTrackType(TrackType type) { for (unsigned int i = 0; i < _sdp_track.size(); i++) { if (type == _sdp_track[i]->_type) { return i; @@ -1064,7 +1073,7 @@ inline int RtspSession::getTrackIndexByTrackType(TrackType type) { throw SockException(Err_shutdown, StrPrinter << "no such track with type:" << (int) type); } -inline int RtspSession::getTrackIndexByControlSuffix(const string &controlSuffix) { +int RtspSession::getTrackIndexByControlSuffix(const string &controlSuffix) { for (unsigned int i = 0; i < _sdp_track.size(); i++) { if (controlSuffix == _sdp_track[i]->_control_surffix) { return i; @@ -1076,7 +1085,7 @@ inline int RtspSession::getTrackIndexByControlSuffix(const string &controlSuffix throw SockException(Err_shutdown, StrPrinter << "no such track with suffix:" << controlSuffix); } -inline int RtspSession::getTrackIndexByInterleaved(int interleaved){ +int RtspSession::getTrackIndexByInterleaved(int interleaved){ for (unsigned int i = 0; i < _sdp_track.size(); i++) { if (_sdp_track[i]->_interleaved == interleaved) { return i; @@ -1114,21 +1123,37 @@ std::shared_ptr RtspSession::getOriginSock(MediaSource &sender) const return const_cast(this)->shared_from_this(); } -inline void RtspSession::onSendRtpPacket(const RtpPacket::Ptr &pkt){ -#if RTSP_SERVER_SEND_RTCP - int track_index = getTrackIndexByTrackType(pkt->type); - RtcpCounter &counter = _rtcp_counter[track_index]; - counter.pktCnt += 1; - counter.octCount += (pkt->size() - pkt->offset); +void RtspSession::onBeforeRtpSorted(const RtpPacket::Ptr &rtp, int track_index){ + updateRtcpContext(rtp); +} + +void RtspSession::updateRtcpContext(const RtpPacket::Ptr &rtp){ + int track_index = getTrackIndexByTrackType(rtp->type); + auto &rtcp_ctx = _rtcp_context[track_index]; + rtcp_ctx->onRtp(rtp->sequence, rtp->timeStamp, rtp->size() - 4); + auto &ticker = _rtcp_send_tickers[track_index]; + //send rtcp every 5 second if (ticker.elapsedTime() > 5 * 1000) { - //send rtcp every 5 second ticker.resetTime(); - //直接保存网络字节序 - memcpy(&counter.time_stamp, pkt->data() + 8, 4); - sendSenderReport(_rtp_type == Rtsp::RTP_TCP, track_index); + + static auto send_rtcp = [](RtspSession *thiz, int index, Buffer::Ptr ptr) { + if (thiz->_rtp_type == Rtsp::RTP_TCP) { + auto &track = thiz->_sdp_track[index]; + thiz->send(makeRtpOverTcpPrefix((uint16_t)(ptr->size()), track->_interleaved + 1)); + thiz->send(std::move(ptr)); + } else { + thiz->_rtcp_socks[index]->send(std::move(ptr)); + } + }; + + auto rtcp = _push_src ? rtcp_ctx->createRtcpRR(rtp->ssrc + 1, rtp->ssrc) : rtcp_ctx->createRtcpSR(rtp->ssrc + 1); + auto rtcp_sdes = RtcpSdes::create({SERVER_NAME}); + rtcp_sdes->items.type = (uint8_t)SdesType::RTCP_SDES_CNAME; + rtcp_sdes->items.ssrc = htonl(rtp->ssrc); + send_rtcp(this, track_index, std::move(rtcp)); + send_rtcp(this, track_index, RtcpHeader::toBuffer(rtcp_sdes)); } -#endif } void RtspSession::sendRtpPacket(const RtspMediaSource::RingDataType &pkt) { @@ -1138,7 +1163,7 @@ void RtspSession::sendRtpPacket(const RtspMediaSource::RingDataType &pkt) { auto size = pkt->size(); setSendFlushFlag(false); pkt->for_each([&](const RtpPacket::Ptr &rtp) { - onSendRtpPacket(rtp); + updateRtcpContext(rtp); if (++i == size) { setSendFlushFlag(true); } @@ -1150,7 +1175,7 @@ void RtspSession::sendRtpPacket(const RtspMediaSource::RingDataType &pkt) { size_t i = 0; auto size = pkt->size(); pkt->for_each([&](const RtpPacket::Ptr &rtp) { - onSendRtpPacket(rtp); + updateRtcpContext(rtp); int track_index = getTrackIndexByTrackType(rtp->type); auto &pSock = _rtp_socks[track_index]; if (!pSock) { @@ -1168,66 +1193,6 @@ void RtspSession::sendRtpPacket(const RtspMediaSource::RingDataType &pkt) { } } -void RtspSession::sendSenderReport(bool over_tcp, int track_index) { - static const char s_cname[] = "ZLMediaKitRtsp"; - uint8_t rtcp_buf[4 + 28 + 10 + sizeof(s_cname) + 1] = {0}; - uint8_t *rtcp_sr = rtcp_buf + 4, *rtcp_sdes = rtcp_sr + 28; - auto &track = _sdp_track[track_index]; - auto &counter = _rtcp_counter[track_index]; - - rtcp_buf[0] = '$'; - rtcp_buf[1] = track->_interleaved + 1; - rtcp_buf[2] = (sizeof(rtcp_buf) - 4) >> 8; - rtcp_buf[3] = (sizeof(rtcp_buf) - 4) & 0xFF; - - rtcp_sr[0] = 0x80; - rtcp_sr[1] = 0xC8; - rtcp_sr[2] = 0x00; - rtcp_sr[3] = 0x06; - - uint32_t ssrc = htonl(track->_ssrc); - memcpy(&rtcp_sr[4], &ssrc, 4); - - uint32_t msw; - uint32_t lsw; - struct timeval tv; - gettimeofday(&tv, NULL); - msw = tv.tv_sec + 0x83AA7E80; /* 0x83AA7E80 is the number of seconds from 1900 to 1970 */ - lsw = (uint32_t) ((double) tv.tv_usec * (double) (((uint64_t) 1) << 32) * 1.0e-6); - - msw = htonl(msw); - memcpy(&rtcp_sr[8], &msw, 4); - - lsw = htonl(lsw); - memcpy(&rtcp_sr[12], &lsw, 4); - //直接使用网络字节序 - memcpy(&rtcp_sr[16], &counter.timeStamp, 4); - - uint32_t pktCnt = htonl(counter.pktCnt); - memcpy(&rtcp_sr[20], &pktCnt, 4); - - uint32_t octCount = htonl(counter.octCount); - memcpy(&rtcp_sr[24], &octCount, 4); - - rtcp_sdes[0] = 0x81; - rtcp_sdes[1] = 0xCA; - rtcp_sdes[2] = 0x00; - rtcp_sdes[3] = 0x06; - - memcpy(&rtcp_sdes[4], &ssrc, 4); - - rtcp_sdes[8] = 0x01; - rtcp_sdes[9] = 0x0f; - memcpy(&rtcp_sdes[10], s_cname, sizeof(s_cname)); - rtcp_sdes[10 + sizeof(s_cname)] = 0x00; - - if (over_tcp) { - send(obtainBuffer((char *) rtcp_buf, sizeof(rtcp_buf))); - } else { - _rtcp_socks[track_index]->send((char *) rtcp_buf + 4, sizeof(rtcp_buf) - 4); - } -} - void RtspSession::setSocketFlags(){ GET_CONFIG(int, mergeWriteMS, General::kMergeWriteMS); if(mergeWriteMS > 0) { diff --git a/src/Rtsp/RtspSession.h b/src/Rtsp/RtspSession.h index d469238e..51a1a670 100644 --- a/src/Rtsp/RtspSession.h +++ b/src/Rtsp/RtspSession.h @@ -26,6 +26,7 @@ #include "RtpReceiver.h" #include "RtspMediaSourceImp.h" #include "Common/Stamp.h" +#include "Rtcp/RtcpContext.h" using namespace std; using namespace toolkit; @@ -79,6 +80,7 @@ protected: ////RtpReceiver override//// void onRtpSorted(const RtpPacket::Ptr &rtp, int track_idx) override; + void onBeforeRtpSorted(const RtpPacket::Ptr &rtp, int track_index) override; ///////MediaSourceEvent override/////// // 关闭 @@ -152,12 +154,11 @@ private: //发送rtp给客户端 void sendRtpPacket(const RtspMediaSource::RingDataType &pkt); //触发rtcp发送 - void onSendRtpPacket(const RtpPacket::Ptr &rtp); + void updateRtcpContext(const RtpPacket::Ptr &rtp); //回复客户端 bool sendRtspResponse(const string &res_code, const std::initializer_list &header, const string &sdp = "", const char *protocol = "RTSP/1.0"); bool sendRtspResponse(const string &res_code, const StrCaseMap &header = StrCaseMap(), const string &sdp = "", const char *protocol = "RTSP/1.0"); - //服务器发送rtcp - void sendSenderReport(bool over_tcp, int track_idx); + //设置socket标志 void setSocketFlags(); @@ -196,11 +197,6 @@ private: //sdp里面有效的track,包含音频或视频 vector _sdp_track; - //rtcp统计,trackid idx 为数组下标 - RtcpCounter _rtcp_counter[2]; - //rtcp发送时间,trackid idx 为数组下标 - Ticker _rtcp_send_tickers[2]; - ////////RTP over udp//////// //RTP端口,trackid idx 为数组下标 Socket::Ptr _rtp_socks[2]; @@ -216,6 +212,11 @@ private: //一次发送 get 一次发送post,需要通过x-sessioncookie关联起来 string _http_x_sessioncookie; function _on_recv; + ////////// rtcp //////////////// + //rtcp发送时间,trackid idx 为数组下标 + Ticker _rtcp_send_tickers[2]; + //统计rtp并发送rtcp + vector _rtcp_context; }; /** diff --git a/tests/test_rtcp.cpp b/tests/test_rtcp.cpp new file mode 100644 index 00000000..cc6e8523 --- /dev/null +++ b/tests/test_rtcp.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved. + * + * This file is part of ZLMediaKit(https://github.com/xia-chu/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 "Rtcp/Rtcp.h" +#include "Util/logger.h" +using namespace std; +using namespace toolkit; +using namespace mediakit; + +void printRtcp(const std::shared_ptr &buffer){ + auto rtcp_arr = RtcpHeader::loadFromBytes(buffer->data(), buffer->size()); + //转换为主机字节序方可打印 + InfoL << "\r\n" << rtcp_arr[0]->dumpString(); +} + +std::shared_ptr makeRtcpSR() { + auto rtcp = RtcpSR::create(3); + rtcp->ssrc = htonl(1); + struct timeval tv; + gettimeofday(&tv, NULL); + rtcp->setNtpStamp(tv); + rtcp->rtpts = htonl(2); + rtcp->packet_count = htonl(3); + rtcp->octet_count = htonl(4); + auto i = 5; + for (auto &ptr : rtcp->getItemList()) { + ReportItem *item = (ReportItem *) ptr; + item->ssrc = htonl(i++); + item->fraction = i++; + item->cumulative = htonl(i++) >> 8; + item->seq_cycles = htons(i++); + item->seq_max = htons(i++); + item->jitter = htonl(i++); + item->last_sr_stamp = htonl(i++); + item->delay_since_last_sr = htonl(i++); + } + //返回网络字节序 + return RtcpHeader::toBuffer(rtcp); +} + +std::shared_ptr makeRtcpRR() { + auto rtcp = RtcpRR::create(3); + rtcp->ssrc = htonl(1); + auto i = 5; + for (auto &ptr : rtcp->getItemList()) { + ReportItem *item = (ReportItem *) ptr; + item->ssrc = htonl(i++); + item->fraction = i++; + item->cumulative = htonl(i++) >> 8; + item->seq_cycles = htons(i++); + item->seq_max = htons(i++); + item->jitter = htonl(i++); + item->last_sr_stamp = htonl(i++); + item->delay_since_last_sr = htonl(i++); + } + //返回网络字节序 + return RtcpHeader::toBuffer(rtcp); +} + +std::shared_ptr makeRtcpSDES() { + auto rtcp = RtcpSdes::create({"zlmediakit", "", "https://github.com/xia-chu/ZLMediaKit", "1213642868@qq.com", "123456789012345678"}); + auto i = 5; + auto items = rtcp->getItemList(); + items[0]->type = (uint8_t)SdesType::RTCP_SDES_CNAME; + items[0]->ssrc = htonl(i++); + + items[1]->type = (uint8_t)SdesType::RTCP_SDES_NOTE; + items[1]->ssrc = htonl(i++); + + items[2]->type = (uint8_t)SdesType::RTCP_SDES_LOC; + items[2]->ssrc = htonl(i++); + + items[3]->type = (uint8_t)SdesType::RTCP_SDES_EMAIL; + items[3]->ssrc = htonl(i++); + + items[4]->type = (uint8_t)SdesType::RTCP_SDES_PHONE; + items[4]->ssrc = htonl(i++); + + //返回网络字节序 + return RtcpHeader::toBuffer(rtcp); +} + + +int main(int argc, char *argv[]){ + Logger::Instance().add(std::make_shared()); + { + static char rtcp_data[] = "\x81\xca\x00\x05\x70\xd8\xac\x1b\x01\x0b\x7a\x73\x68\x50\x43\x40" + "\x7a\x73\x68\x50\x43\x00\x00\x00" + "\x81\xc9\x00\x07\x70\xd8\xac\x1b\x55\x66\x77\x88\x00\x00\x00\x00" + "\x00\x00\x0d\x21\x00\x00\x00\x32\xdd\xf1\x00\x00\x00\x03\x4f\x67" + "\x80\xc8\x00\x06\x55\x66\x77\x88\xe3\x70\xdd\xf1\x00\x00\xc2\xb8" + "\x00\x21\xe4\x90\x00\x00\x0b\x81\x00\x2f\x6a\x60"; + auto rtcp_arr = RtcpHeader::loadFromBytes(rtcp_data, sizeof(rtcp_data) - 1); + for (auto &rtcp : rtcp_arr) { + DebugL << "\r\n" << rtcp->dumpString(); + } + + } + + { + printRtcp(makeRtcpSR()); + printRtcp(makeRtcpRR()); + printRtcp(makeRtcpSDES()); + } + + { + string str; + auto sr = makeRtcpSR(); + auto rr = makeRtcpRR(); + auto sdes = makeRtcpSDES(); + str.append(sr->data(), sr->size()); + str.append(rr->data(), rr->size()); + str.append(sdes->data(), sdes->size()); + //测试内存越界 + char *data = new char[str.size()]; + memcpy(data, str.data(), str.size()); + auto rtcp_arr = RtcpHeader::loadFromBytes(data, str.size()); + for (auto &rtcp : rtcp_arr) { + WarnL << "\r\n" << rtcp->dumpString(); + } + delete [] data; + } + +} \ No newline at end of file