diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet.cc index a4cdfd95de..f6d3bd3d71 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_packet.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet.cc @@ -13,6 +13,10 @@ #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" #include "webrtc/system_wrappers/interface/logging.h" +using webrtc::RTCPUtility::kBtDlrr; +using webrtc::RTCPUtility::kBtReceiverReferenceTime; +using webrtc::RTCPUtility::kBtVoipMetric; + using webrtc::RTCPUtility::PT_APP; using webrtc::RTCPUtility::PT_BYE; using webrtc::RTCPUtility::PT_IJ; @@ -69,6 +73,24 @@ void AssignUWord32(uint8_t* buffer, size_t* offset, uint32_t value) { *offset += 4; } +void ComputeMantissaAnd6bitBase2Exponent(uint32_t input_base10, + uint8_t bits_mantissa, + uint32_t* mantissa, + uint8_t* exp) { + // input_base10 = mantissa * 2^exp + assert(bits_mantissa <= 32); + uint32_t mantissa_max = (1 << bits_mantissa) - 1; + uint8_t exponent = 0; + for (uint32_t i = 0; i < 64; ++i) { + if (input_base10 <= (mantissa_max << i)) { + exponent = i; + break; + } + } + *exp = exponent; + *mantissa = (input_base10 >> exponent); +} + size_t BlockToHeaderLength(size_t length_in_bytes) { // Length in 32-bit words minus 1. assert(length_in_bytes > 0); @@ -440,6 +462,264 @@ void CreateFir(const RTCPPacketPSFBFIR& fir, AssignUWord8(buffer, pos, fir_item.CommandSequenceNumber); AssignUWord24(buffer, pos, 0); } + +void CreateTmmbrItem(const RTCPPacketRTPFBTMMBRItem& tmmbr_item, + uint8_t* buffer, + size_t* pos) { + uint32_t bitrate_bps = tmmbr_item.MaxTotalMediaBitRate * 1000; + uint32_t mantissa = 0; + uint8_t exp = 0; + ComputeMantissaAnd6bitBase2Exponent(bitrate_bps, 17, &mantissa, &exp); + + AssignUWord32(buffer, pos, tmmbr_item.SSRC); + AssignUWord8(buffer, pos, (exp << 2) + ((mantissa >> 15) & 0x03)); + AssignUWord8(buffer, pos, mantissa >> 7); + AssignUWord8(buffer, pos, (mantissa << 1) + + ((tmmbr_item.MeasuredOverhead >> 8) & 0x01)); + AssignUWord8(buffer, pos, tmmbr_item.MeasuredOverhead); +} + +// Temporary Maximum Media Stream Bit Rate Request (TMMBR) (RFC 5104). +// +// FCI: +// +// 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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | MxTBR Exp | MxTBR Mantissa |Measured Overhead| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateTmmbr(const RTCPPacketRTPFBTMMBR& tmmbr, + const RTCPPacketRTPFBTMMBRItem& tmmbr_item, + size_t length, + uint8_t* buffer, + size_t* pos) { + const uint8_t kFmt = 3; + CreateHeader(kFmt, PT_RTPFB, length, buffer, pos); + AssignUWord32(buffer, pos, tmmbr.SenderSSRC); + AssignUWord32(buffer, pos, kUnusedMediaSourceSsrc0); + CreateTmmbrItem(tmmbr_item, buffer, pos); +} + +// Temporary Maximum Media Stream Bit Rate Notification (TMMBN) (RFC 5104). +// +// FCI: +// +// 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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | MxTBR Exp | MxTBR Mantissa |Measured Overhead| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateTmmbn(const RTCPPacketRTPFBTMMBN& tmmbn, + const std::vector& tmmbn_items, + size_t length, + uint8_t* buffer, + size_t* pos) { + const uint8_t kFmt = 4; + CreateHeader(kFmt, PT_RTPFB, length, buffer, pos); + AssignUWord32(buffer, pos, tmmbn.SenderSSRC); + AssignUWord32(buffer, pos, kUnusedMediaSourceSsrc0); + for (uint8_t i = 0; i < tmmbn_items.size(); ++i) { + CreateTmmbrItem(tmmbn_items[i], buffer, pos); + } +} + +// Receiver Estimated Max Bitrate (REMB) (draft-alvestrand-rmcat-remb). +// +// 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| FMT=15 | PT=206 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of packet sender | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of media source | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Unique identifier 'R' 'E' 'M' 'B' | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Num SSRC | BR Exp | BR Mantissa | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC feedback | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | ... | + +void CreateRemb(const RTCPPacketPSFBAPP& remb, + const RTCPPacketPSFBREMBItem& remb_item, + size_t length, + uint8_t* buffer, + size_t* pos) { + uint32_t mantissa = 0; + uint8_t exp = 0; + ComputeMantissaAnd6bitBase2Exponent(remb_item.BitRate, 18, &mantissa, &exp); + + const uint8_t kFmt = 15; + CreateHeader(kFmt, PT_PSFB, length, buffer, pos); + AssignUWord32(buffer, pos, remb.SenderSSRC); + AssignUWord32(buffer, pos, kUnusedMediaSourceSsrc0); + AssignUWord8(buffer, pos, 'R'); + AssignUWord8(buffer, pos, 'E'); + AssignUWord8(buffer, pos, 'M'); + AssignUWord8(buffer, pos, 'B'); + AssignUWord8(buffer, pos, remb_item.NumberOfSSRCs); + AssignUWord8(buffer, pos, (exp << 2) + ((mantissa >> 16) & 0x03)); + AssignUWord8(buffer, pos, mantissa >> 8); + AssignUWord8(buffer, pos, mantissa); + for (uint8_t i = 0; i < remb_item.NumberOfSSRCs; ++i) { + AssignUWord32(buffer, pos, remb_item.SSRCs[i]); + } +} + +// From RFC 3611: RTP Control Protocol Extended Reports (RTCP XR). +// +// Format for XR packets: +// +// 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|reserved | PT=XR=207 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : report blocks : +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateXrHeader(const RTCPPacketXR& header, + size_t length, + uint8_t* buffer, + size_t* pos) { + CreateHeader(0U, PT_XR, length, buffer, pos); + AssignUWord32(buffer, pos, header.OriginatorSSRC); +} + +void CreateXrBlockHeader(uint8_t block_type, + uint16_t block_length, + uint8_t* buffer, + size_t* pos) { + AssignUWord8(buffer, pos, block_type); + AssignUWord8(buffer, pos, 0); + AssignUWord16(buffer, pos, block_length); +} + +// Receiver Reference Time Report Block (RFC 3611). +// +// 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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | BT=4 | reserved | block length = 2 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | NTP timestamp, most significant word | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | NTP timestamp, least significant word | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateRrtr(const std::vector& rrtrs, + uint8_t* buffer, + size_t* pos) { + const uint16_t kBlockLength = 2; + for (std::vector::const_iterator it = + rrtrs.begin(); it != rrtrs.end(); ++it) { + CreateXrBlockHeader(kBtReceiverReferenceTime, kBlockLength, buffer, pos); + AssignUWord32(buffer, pos, (*it).NTPMostSignificant); + AssignUWord32(buffer, pos, (*it).NTPLeastSignificant); + } +} + +// DLRR Report Block (RFC 3611). +// +// 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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | BT=5 | reserved | block length | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | SSRC_1 (SSRC of first receiver) | sub- +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block +// | last RR (LRR) | 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | delay since last RR (DLRR) | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | SSRC_2 (SSRC of second receiver) | sub- +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block +// : ... : 2 + +void CreateDlrr(const std::vector& dlrrs, + uint8_t* buffer, + size_t* pos) { + for (std::vector::const_iterator it = dlrrs.begin(); + it != dlrrs.end(); ++it) { + if ((*it).empty()) { + continue; + } + uint16_t block_length = 3 * (*it).size(); + CreateXrBlockHeader(kBtDlrr, block_length, buffer, pos); + for (Xr::DlrrBlock::const_iterator it_block = (*it).begin(); + it_block != (*it).end(); ++it_block) { + AssignUWord32(buffer, pos, (*it_block).SSRC); + AssignUWord32(buffer, pos, (*it_block).LastRR); + AssignUWord32(buffer, pos, (*it_block).DelayLastRR); + } + } +} + +// VoIP Metrics Report Block (RFC 3611). +// +// 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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | BT=7 | reserved | block length = 8 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of source | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | loss rate | discard rate | burst density | gap density | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | burst duration | gap duration | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | round trip delay | end system delay | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | signal level | noise level | RERL | Gmin | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | R factor | ext. R factor | MOS-LQ | MOS-CQ | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | RX config | reserved | JB nominal | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | JB maximum | JB abs max | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateVoipMetric(const std::vector& metrics, + uint8_t* buffer, + size_t* pos) { + const uint16_t kBlockLength = 8; + for (std::vector::const_iterator it = + metrics.begin(); it != metrics.end(); ++it) { + CreateXrBlockHeader(kBtVoipMetric, kBlockLength, buffer, pos); + AssignUWord32(buffer, pos, (*it).SSRC); + AssignUWord8(buffer, pos, (*it).lossRate); + AssignUWord8(buffer, pos, (*it).discardRate); + AssignUWord8(buffer, pos, (*it).burstDensity); + AssignUWord8(buffer, pos, (*it).gapDensity); + AssignUWord16(buffer, pos, (*it).burstDuration); + AssignUWord16(buffer, pos, (*it).gapDuration); + AssignUWord16(buffer, pos, (*it).roundTripDelay); + AssignUWord16(buffer, pos, (*it).endSystemDelay); + AssignUWord8(buffer, pos, (*it).signalLevel); + AssignUWord8(buffer, pos, (*it).noiseLevel); + AssignUWord8(buffer, pos, (*it).RERL); + AssignUWord8(buffer, pos, (*it).Gmin); + AssignUWord8(buffer, pos, (*it).Rfactor); + AssignUWord8(buffer, pos, (*it).extRfactor); + AssignUWord8(buffer, pos, (*it).MOSLQ); + AssignUWord8(buffer, pos, (*it).MOSCQ); + AssignUWord8(buffer, pos, (*it).RXconfig); + AssignUWord8(buffer, pos, 0); + AssignUWord16(buffer, pos, (*it).JBnominal); + AssignUWord16(buffer, pos, (*it).JBmax); + AssignUWord16(buffer, pos, (*it).JBabsMax); + } +} } // namespace void RtcpPacket::Append(RtcpPacket* packet) { @@ -691,5 +971,120 @@ void Fir::Create(uint8_t* packet, size_t* length, size_t max_length) const { CreateFir(fir_, fir_item_, BlockToHeaderLength(BlockLength()), packet, length); } + +void Remb::Create(uint8_t* packet, size_t* length, size_t max_length) const { + if (*length + BlockLength() > max_length) { + LOG(LS_WARNING) << "Max packet size reached."; + return; + } + CreateRemb(remb_, remb_item_, BlockToHeaderLength(BlockLength()), packet, + length); +} + +void Remb::AppliesTo(uint32_t ssrc) { + if (remb_item_.NumberOfSSRCs >= kMaxNumberOfSsrcs) { + LOG(LS_WARNING) << "Max number of REMB feedback SSRCs reached."; + return; + } + remb_item_.SSRCs[remb_item_.NumberOfSSRCs++] = ssrc; +} + +void Tmmbr::Create(uint8_t* packet, size_t* length, size_t max_length) const { + if (*length + BlockLength() > max_length) { + LOG(LS_WARNING) << "Max packet size reached."; + return; + } + CreateTmmbr(tmmbr_, tmmbr_item_, BlockToHeaderLength(BlockLength()), packet, + length); +} + +void Tmmbn::WithTmmbr(uint32_t ssrc, uint32_t bitrate_kbps, uint16_t overhead) { + assert(overhead <= 0x1ff); + if (tmmbn_items_.size() >= kMaxNumberOfTmmbrs) { + LOG(LS_WARNING) << "Max TMMBN size reached."; + return; + } + RTCPPacketRTPFBTMMBRItem tmmbn_item; + tmmbn_item.SSRC = ssrc; + tmmbn_item.MaxTotalMediaBitRate = bitrate_kbps; + tmmbn_item.MeasuredOverhead = overhead; + tmmbn_items_.push_back(tmmbn_item); +} + +void Tmmbn::Create(uint8_t* packet, size_t* length, size_t max_length) const { + if (*length + BlockLength() > max_length) { + LOG(LS_WARNING) << "Max packet size reached."; + return; + } + CreateTmmbn(tmmbn_, tmmbn_items_, BlockToHeaderLength(BlockLength()), packet, + length); +} + +void Xr::Create(uint8_t* packet, size_t* length, size_t max_length) const { + if (*length + BlockLength() > max_length) { + LOG(LS_WARNING) << "Max packet size reached."; + return; + } + CreateXrHeader(xr_header_, BlockToHeaderLength(BlockLength()), packet, + length); + CreateRrtr(rrtr_blocks_, packet, length); + CreateDlrr(dlrr_blocks_, packet, length); + CreateVoipMetric(voip_metric_blocks_, packet, length); +} + +void Xr::WithRrtr(Rrtr* rrtr) { + assert(rrtr); + if (rrtr_blocks_.size() >= kMaxNumberOfRrtrBlocks) { + LOG(LS_WARNING) << "Max RRTR blocks reached."; + return; + } + rrtr_blocks_.push_back(rrtr->rrtr_block_); +} + +void Xr::WithDlrr(Dlrr* dlrr) { + assert(dlrr); + if (dlrr_blocks_.size() >= kMaxNumberOfDlrrBlocks) { + LOG(LS_WARNING) << "Max DLRR blocks reached."; + return; + } + dlrr_blocks_.push_back(dlrr->dlrr_block_); +} + +void Xr::WithVoipMetric(VoipMetric* voip_metric) { + assert(voip_metric); + if (voip_metric_blocks_.size() >= kMaxNumberOfVoipMetricBlocks) { + LOG(LS_WARNING) << "Max Voip Metric blocks reached."; + return; + } + voip_metric_blocks_.push_back(voip_metric->metric_); +} + +size_t Xr::DlrrLength() const { + const size_t kBlockHeaderLen = 4; + const size_t kSubBlockLen = 12; + size_t length = 0; + for (std::vector::const_iterator it = dlrr_blocks_.begin(); + it != dlrr_blocks_.end(); ++it) { + if (!(*it).empty()) { + length += kBlockHeaderLen + kSubBlockLen * (*it).size(); + } + } + return length; +} + +void Dlrr::WithDlrrItem(uint32_t ssrc, + uint32_t last_rr, + uint32_t delay_last_rr) { + if (dlrr_block_.size() >= kMaxNumberOfDlrrItems) { + LOG(LS_WARNING) << "Max DLRR items reached."; + return; + } + RTCPPacketXRDLRRReportBlockItem dlrr; + dlrr.SSRC = ssrc; + dlrr.LastRR = last_rr; + dlrr.DelayLastRR = delay_last_rr; + dlrr_block_.push_back(dlrr); +} + } // namespace rtcp } // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet.h index f60e848b50..0ea98a2804 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_packet.h +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet.h @@ -26,7 +26,10 @@ namespace rtcp { enum { kCommonFbFmtLength = 12 }; enum { kReportBlockLength = 24 }; +class Dlrr; class RawPacket; +class Rrtr; +class VoipMetric; // Class for building RTCP packets. // @@ -82,12 +85,16 @@ class RtcpPacket { class Empty : public RtcpPacket { public: - Empty() {} + Empty() : RtcpPacket() {} virtual ~Empty() {} protected: - virtual void Create(uint8_t* packet, size_t* length, size_t max_length) const; + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(Empty); }; // From RFC 3550, RTP: A Transport Protocol for Real-Time Applications. @@ -212,6 +219,8 @@ class SenderReport : public RtcpPacket { RTCPUtility::RTCPPacketSR sr_; std::vector report_blocks_; + + DISALLOW_COPY_AND_ASSIGN(SenderReport); }; // @@ -254,6 +263,8 @@ class ReceiverReport : public RtcpPacket { RTCPUtility::RTCPPacketRR rr_; std::vector report_blocks_; + + DISALLOW_COPY_AND_ASSIGN(ReceiverReport); }; // Transmission Time Offsets in RTP Streams (RFC 5450). @@ -394,6 +405,8 @@ class Bye : public RtcpPacket { RTCPUtility::RTCPPacketBYE bye_; std::vector csrcs_; + + DISALLOW_COPY_AND_ASSIGN(Bye); }; // Application-Defined packet (APP) (RFC 3550). @@ -660,8 +673,7 @@ class Rpsi : public RtcpPacket { class Fir : public RtcpPacket { public: - Fir() - : RtcpPacket() { + Fir() : RtcpPacket() { memset(&fir_, 0, sizeof(fir_)); memset(&fir_item_, 0, sizeof(fir_item_)); } @@ -692,6 +704,354 @@ class Fir : public RtcpPacket { RTCPUtility::RTCPPacketPSFBFIRItem fir_item_; }; +// Temporary Maximum Media Stream Bit Rate Request (TMMBR) (RFC 5104). +// +// FCI: +// +// 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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | MxTBR Exp | MxTBR Mantissa |Measured Overhead| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class Tmmbr : public RtcpPacket { + public: + Tmmbr() : RtcpPacket() { + memset(&tmmbr_, 0, sizeof(tmmbr_)); + memset(&tmmbr_item_, 0, sizeof(tmmbr_item_)); + } + + virtual ~Tmmbr() {} + + void From(uint32_t ssrc) { + tmmbr_.SenderSSRC = ssrc; + } + void To(uint32_t ssrc) { + tmmbr_item_.SSRC = ssrc; + } + void WithBitrateKbps(uint32_t bitrate_kbps) { + tmmbr_item_.MaxTotalMediaBitRate = bitrate_kbps; + } + void WithOverhead(uint16_t overhead) { + assert(overhead <= 0x1ff); + tmmbr_item_.MeasuredOverhead = overhead; + } + + protected: + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const OVERRIDE; + + private: + size_t BlockLength() const { + const size_t kFciLen = 8; + return kCommonFbFmtLength + kFciLen; + } + + RTCPUtility::RTCPPacketRTPFBTMMBR tmmbr_; + RTCPUtility::RTCPPacketRTPFBTMMBRItem tmmbr_item_; + + DISALLOW_COPY_AND_ASSIGN(Tmmbr); +}; + +// Temporary Maximum Media Stream Bit Rate Notification (TMMBN) (RFC 5104). +// +// FCI: +// +// 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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | MxTBR Exp | MxTBR Mantissa |Measured Overhead| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class Tmmbn : public RtcpPacket { + public: + Tmmbn() : RtcpPacket() { + memset(&tmmbn_, 0, sizeof(tmmbn_)); + } + + virtual ~Tmmbn() {} + + void From(uint32_t ssrc) { + tmmbn_.SenderSSRC = ssrc; + } + void WithTmmbr(uint32_t ssrc, uint32_t bitrate_kbps, uint16_t overhead); + + protected: + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const OVERRIDE; + + private: + enum { kMaxNumberOfTmmbrs = 50 }; + + size_t BlockLength() const { + const size_t kFciLen = 8; + return kCommonFbFmtLength + kFciLen * tmmbn_items_.size(); + } + + RTCPUtility::RTCPPacketRTPFBTMMBN tmmbn_; + std::vector tmmbn_items_; + + DISALLOW_COPY_AND_ASSIGN(Tmmbn); +}; + +// Receiver Estimated Max Bitrate (REMB) (draft-alvestrand-rmcat-remb). +// +// 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| FMT=15 | PT=206 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of packet sender | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of media source | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Unique identifier 'R' 'E' 'M' 'B' | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Num SSRC | BR Exp | BR Mantissa | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC feedback | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | ... + +class Remb : public RtcpPacket { + public: + Remb() : RtcpPacket() { + memset(&remb_, 0, sizeof(remb_)); + memset(&remb_item_, 0, sizeof(remb_item_)); + } + + virtual ~Remb() {} + + void From(uint32_t ssrc) { + remb_.SenderSSRC = ssrc; + } + void AppliesTo(uint32_t ssrc); + + void WithBitrateBps(uint32_t bitrate_bps) { + remb_item_.BitRate = bitrate_bps; + } + + protected: + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const OVERRIDE; + + private: + enum { kMaxNumberOfSsrcs = 0xff }; + + size_t BlockLength() const { + return (remb_item_.NumberOfSSRCs + 5) * 4; + } + + RTCPUtility::RTCPPacketPSFBAPP remb_; + RTCPUtility::RTCPPacketPSFBREMBItem remb_item_; + + DISALLOW_COPY_AND_ASSIGN(Remb); +}; + +// From RFC 3611: RTP Control Protocol Extended Reports (RTCP XR). +// +// Format for XR packets: +// +// 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|reserved | PT=XR=207 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : report blocks : +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class Xr : public RtcpPacket { + public: + typedef std::vector DlrrBlock; + Xr() : RtcpPacket() { + memset(&xr_header_, 0, sizeof(xr_header_)); + } + + virtual ~Xr() {} + + void From(uint32_t ssrc) { + xr_header_.OriginatorSSRC = ssrc; + } + void WithRrtr(Rrtr* rrtr); + void WithDlrr(Dlrr* dlrr); + void WithVoipMetric(VoipMetric* voip_metric); + + protected: + virtual void Create( + uint8_t* packet, size_t* length, size_t max_length) const OVERRIDE; + + private: + enum { kMaxNumberOfRrtrBlocks = 50 }; + enum { kMaxNumberOfDlrrBlocks = 50 }; + enum { kMaxNumberOfVoipMetricBlocks = 50 }; + + size_t BlockLength() const { + const size_t kXrHeaderLength = 8; + return kXrHeaderLength + RrtrLength() + DlrrLength() + VoipMetricLength(); + } + + size_t RrtrLength() const { + const size_t kRrtrBlockLength = 12; + return kRrtrBlockLength * rrtr_blocks_.size(); + } + + size_t DlrrLength() const; + + size_t VoipMetricLength() const { + const size_t kVoipMetricBlockLength = 36; + return kVoipMetricBlockLength * voip_metric_blocks_.size(); + } + + RTCPUtility::RTCPPacketXR xr_header_; + std::vector rrtr_blocks_; + std::vector dlrr_blocks_; + std::vector voip_metric_blocks_; + + DISALLOW_COPY_AND_ASSIGN(Xr); +}; + +// Receiver Reference Time Report Block (RFC 3611). +// +// 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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | BT=4 | reserved | block length = 2 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | NTP timestamp, most significant word | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | NTP timestamp, least significant word | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class Rrtr { + public: + Rrtr() { + memset(&rrtr_block_, 0, sizeof(rrtr_block_)); + } + ~Rrtr() {} + + void WithNtpSec(uint32_t sec) { + rrtr_block_.NTPMostSignificant = sec; + } + void WithNtpFrac(uint32_t frac) { + rrtr_block_.NTPLeastSignificant = frac; + } + + private: + friend class Xr; + RTCPUtility::RTCPPacketXRReceiverReferenceTimeItem rrtr_block_; + + DISALLOW_COPY_AND_ASSIGN(Rrtr); +}; + +// DLRR Report Block (RFC 3611). +// +// 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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | BT=5 | reserved | block length | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | SSRC_1 (SSRC of first receiver) | sub- +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block +// | last RR (LRR) | 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | delay since last RR (DLRR) | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | SSRC_2 (SSRC of second receiver) | sub- +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block +// : ... : 2 + +class Dlrr { + public: + Dlrr() {} + ~Dlrr() {} + + void WithDlrrItem(uint32_t ssrc, uint32_t last_rr, uint32_t delay_last_rr); + + private: + friend class Xr; + enum { kMaxNumberOfDlrrItems = 100 }; + + std::vector dlrr_block_; + + DISALLOW_COPY_AND_ASSIGN(Dlrr); +}; + +// VoIP Metrics Report Block (RFC 3611). +// +// 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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | BT=7 | reserved | block length = 8 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of source | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | loss rate | discard rate | burst density | gap density | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | burst duration | gap duration | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | round trip delay | end system delay | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | signal level | noise level | RERL | Gmin | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | R factor | ext. R factor | MOS-LQ | MOS-CQ | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | RX config | reserved | JB nominal | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | JB maximum | JB abs max | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class VoipMetric { + public: + VoipMetric() { + memset(&metric_, 0, sizeof(metric_)); + } + ~VoipMetric() {} + + void To(uint32_t ssrc) { metric_.SSRC = ssrc; } + void LossRate(uint8_t loss_rate) { metric_.lossRate = loss_rate; } + void DiscardRate(uint8_t discard_rate) { metric_.discardRate = discard_rate; } + void BurstDensity(uint8_t burst_density) { + metric_.burstDensity = burst_density; + } + void GapDensity(uint8_t gap_density) { metric_.gapDensity = gap_density; } + void BurstDuration(uint16_t burst_duration) { + metric_.burstDuration = burst_duration; + } + void GapDuration(uint16_t gap_duration) { + metric_.gapDuration = gap_duration; + } + void RoundTripDelay(uint16_t round_trip_delay) { + metric_.roundTripDelay = round_trip_delay; + } + void EndSystemDelay(uint16_t end_system_delay) { + metric_.endSystemDelay = end_system_delay; + } + void SignalLevel(uint8_t signal_level) { metric_.signalLevel = signal_level; } + void NoiseLevel(uint8_t noise_level) { metric_.noiseLevel = noise_level; } + void Rerl(uint8_t rerl) { metric_.RERL = rerl; } + void Gmin(uint8_t gmin) { metric_.Gmin = gmin; } + void Rfactor(uint8_t rfactor) { metric_.Rfactor = rfactor; } + void ExtRfactor(uint8_t extrfactor) { metric_.extRfactor = extrfactor; } + void MosLq(uint8_t moslq) { metric_.MOSLQ = moslq; } + void MosCq(uint8_t moscq) { metric_.MOSCQ = moscq; } + void RxConfig(uint8_t rxconfig) { metric_.RXconfig = rxconfig; } + void JbNominal(uint16_t jbnominal) { metric_.JBnominal = jbnominal; } + void JbMax(uint16_t jbmax) { metric_.JBmax = jbmax; } + void JbAbsMax(uint16_t jbabsmax) { metric_.JBabsMax = jbabsmax; } + + private: + friend class Xr; + RTCPUtility::RTCPPacketXRVOIPMetricItem metric_; + + DISALLOW_COPY_AND_ASSIGN(VoipMetric); +}; + // Class holding a RTCP packet. // // Takes a built rtcp packet. diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc index aa25c2e5f9..095022efc2 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc @@ -17,6 +17,7 @@ using webrtc::rtcp::App; using webrtc::rtcp::Bye; +using webrtc::rtcp::Dlrr; using webrtc::rtcp::Empty; using webrtc::rtcp::Fir; using webrtc::rtcp::Ij; @@ -27,8 +28,15 @@ using webrtc::rtcp::SenderReport; using webrtc::rtcp::Sli; using webrtc::rtcp::RawPacket; using webrtc::rtcp::ReceiverReport; +using webrtc::rtcp::Remb; using webrtc::rtcp::ReportBlock; using webrtc::rtcp::Rpsi; +using webrtc::rtcp::Rrtr; +using webrtc::rtcp::SenderReport; +using webrtc::rtcp::Tmmbn; +using webrtc::rtcp::Tmmbr; +using webrtc::rtcp::VoipMetric; +using webrtc::rtcp::Xr; using webrtc::test::RtcpPacketParser; namespace webrtc { @@ -589,4 +597,307 @@ TEST(RtcpPacketTest, BuildWithTooSmallBuffer_LastBlockFits) { EXPECT_EQ(0, parser.report_block()->num_packets()); EXPECT_EQ(1, parser.fir()->num_packets()); } + +TEST(RtcpPacketTest, Remb) { + Remb remb; + remb.From(kSenderSsrc); + remb.AppliesTo(kRemoteSsrc); + remb.AppliesTo(kRemoteSsrc + 1); + remb.AppliesTo(kRemoteSsrc + 2); + remb.WithBitrateBps(261011); + + RawPacket packet = remb.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.psfb_app()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.psfb_app()->Ssrc()); + EXPECT_EQ(1, parser.remb_item()->num_packets()); + EXPECT_EQ(261011, parser.remb_item()->last_bitrate_bps()); + std::vector ssrcs = parser.remb_item()->last_ssrc_list(); + EXPECT_EQ(kRemoteSsrc, ssrcs[0]); + EXPECT_EQ(kRemoteSsrc + 1, ssrcs[1]); + EXPECT_EQ(kRemoteSsrc + 2, ssrcs[2]); +} + +TEST(RtcpPacketTest, Tmmbr) { + Tmmbr tmmbr; + tmmbr.From(kSenderSsrc); + tmmbr.To(kRemoteSsrc); + tmmbr.WithBitrateKbps(312); + tmmbr.WithOverhead(60); + + RawPacket packet = tmmbr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.tmmbr()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.tmmbr()->Ssrc()); + EXPECT_EQ(1, parser.tmmbr_item()->num_packets()); + EXPECT_EQ(312U, parser.tmmbr_item()->BitrateKbps()); + EXPECT_EQ(60U, parser.tmmbr_item()->Overhead()); +} + +TEST(RtcpPacketTest, TmmbnWithNoItem) { + Tmmbn tmmbn; + tmmbn.From(kSenderSsrc); + + RawPacket packet = tmmbn.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.tmmbn()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.tmmbn()->Ssrc()); + EXPECT_EQ(0, parser.tmmbn_items()->num_packets()); +} + +TEST(RtcpPacketTest, TmmbnWithOneItem) { + Tmmbn tmmbn; + tmmbn.From(kSenderSsrc); + tmmbn.WithTmmbr(kRemoteSsrc, 312, 60); + + RawPacket packet = tmmbn.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.tmmbn()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.tmmbn()->Ssrc()); + EXPECT_EQ(1, parser.tmmbn_items()->num_packets()); + EXPECT_EQ(kRemoteSsrc, parser.tmmbn_items()->Ssrc(0)); + EXPECT_EQ(312U, parser.tmmbn_items()->BitrateKbps(0)); + EXPECT_EQ(60U, parser.tmmbn_items()->Overhead(0)); +} + +TEST(RtcpPacketTest, TmmbnWithTwoItems) { + Tmmbn tmmbn; + tmmbn.From(kSenderSsrc); + tmmbn.WithTmmbr(kRemoteSsrc, 312, 60); + tmmbn.WithTmmbr(kRemoteSsrc + 1, 1288, 40); + + RawPacket packet = tmmbn.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.tmmbn()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.tmmbn()->Ssrc()); + EXPECT_EQ(2, parser.tmmbn_items()->num_packets()); + EXPECT_EQ(kRemoteSsrc, parser.tmmbn_items()->Ssrc(0)); + EXPECT_EQ(312U, parser.tmmbn_items()->BitrateKbps(0)); + EXPECT_EQ(60U, parser.tmmbn_items()->Overhead(0)); + EXPECT_EQ(kRemoteSsrc + 1, parser.tmmbn_items()->Ssrc(1)); + EXPECT_EQ(1288U, parser.tmmbn_items()->BitrateKbps(1)); + EXPECT_EQ(40U, parser.tmmbn_items()->Overhead(1)); +} + +TEST(RtcpPacketTest, XrWithNoReportBlocks) { + Xr xr; + xr.From(kSenderSsrc); + + RawPacket packet = xr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.xr_header()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.xr_header()->Ssrc()); +} + +TEST(RtcpPacketTest, XrWithRrtr) { + Rrtr rrtr; + rrtr.WithNtpSec(0x11111111); + rrtr.WithNtpFrac(0x22222222); + Xr xr; + xr.From(kSenderSsrc); + xr.WithRrtr(&rrtr); + + RawPacket packet = xr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.xr_header()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.xr_header()->Ssrc()); + EXPECT_EQ(1, parser.rrtr()->num_packets()); + EXPECT_EQ(0x11111111U, parser.rrtr()->NtpSec()); + EXPECT_EQ(0x22222222U, parser.rrtr()->NtpFrac()); +} + +TEST(RtcpPacketTest, XrWithTwoRrtrBlocks) { + Rrtr rrtr1; + rrtr1.WithNtpSec(0x11111111); + rrtr1.WithNtpFrac(0x22222222); + Rrtr rrtr2; + rrtr2.WithNtpSec(0x33333333); + rrtr2.WithNtpFrac(0x44444444); + Xr xr; + xr.From(kSenderSsrc); + xr.WithRrtr(&rrtr1); + xr.WithRrtr(&rrtr2); + + RawPacket packet = xr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.xr_header()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.xr_header()->Ssrc()); + EXPECT_EQ(2, parser.rrtr()->num_packets()); + EXPECT_EQ(0x33333333U, parser.rrtr()->NtpSec()); + EXPECT_EQ(0x44444444U, parser.rrtr()->NtpFrac()); +} + +TEST(RtcpPacketTest, XrWithDlrrWithOneSubBlock) { + Dlrr dlrr; + dlrr.WithDlrrItem(0x11111111, 0x22222222, 0x33333333); + Xr xr; + xr.From(kSenderSsrc); + xr.WithDlrr(&dlrr); + + RawPacket packet = xr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.xr_header()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.xr_header()->Ssrc()); + EXPECT_EQ(1, parser.dlrr()->num_packets()); + EXPECT_EQ(1, parser.dlrr_items()->num_packets()); + EXPECT_EQ(0x11111111U, parser.dlrr_items()->Ssrc(0)); + EXPECT_EQ(0x22222222U, parser.dlrr_items()->LastRr(0)); + EXPECT_EQ(0x33333333U, parser.dlrr_items()->DelayLastRr(0)); +} + +TEST(RtcpPacketTest, XrWithDlrrWithTwoSubBlocks) { + Dlrr dlrr; + dlrr.WithDlrrItem(0x11111111, 0x22222222, 0x33333333); + dlrr.WithDlrrItem(0x44444444, 0x55555555, 0x66666666); + Xr xr; + xr.From(kSenderSsrc); + xr.WithDlrr(&dlrr); + + RawPacket packet = xr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.xr_header()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.xr_header()->Ssrc()); + EXPECT_EQ(1, parser.dlrr()->num_packets()); + EXPECT_EQ(2, parser.dlrr_items()->num_packets()); + EXPECT_EQ(0x11111111U, parser.dlrr_items()->Ssrc(0)); + EXPECT_EQ(0x22222222U, parser.dlrr_items()->LastRr(0)); + EXPECT_EQ(0x33333333U, parser.dlrr_items()->DelayLastRr(0)); + EXPECT_EQ(0x44444444U, parser.dlrr_items()->Ssrc(1)); + EXPECT_EQ(0x55555555U, parser.dlrr_items()->LastRr(1)); + EXPECT_EQ(0x66666666U, parser.dlrr_items()->DelayLastRr(1)); +} + +TEST(RtcpPacketTest, XrWithTwoDlrrBlocks) { + Dlrr dlrr1; + dlrr1.WithDlrrItem(0x11111111, 0x22222222, 0x33333333); + Dlrr dlrr2; + dlrr2.WithDlrrItem(0x44444444, 0x55555555, 0x66666666); + Xr xr; + xr.From(kSenderSsrc); + xr.WithDlrr(&dlrr1); + xr.WithDlrr(&dlrr2); + + RawPacket packet = xr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.xr_header()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.xr_header()->Ssrc()); + EXPECT_EQ(2, parser.dlrr()->num_packets()); + EXPECT_EQ(2, parser.dlrr_items()->num_packets()); + EXPECT_EQ(0x11111111U, parser.dlrr_items()->Ssrc(0)); + EXPECT_EQ(0x22222222U, parser.dlrr_items()->LastRr(0)); + EXPECT_EQ(0x33333333U, parser.dlrr_items()->DelayLastRr(0)); + EXPECT_EQ(0x44444444U, parser.dlrr_items()->Ssrc(1)); + EXPECT_EQ(0x55555555U, parser.dlrr_items()->LastRr(1)); + EXPECT_EQ(0x66666666U, parser.dlrr_items()->DelayLastRr(1)); +} + +TEST(RtcpPacketTest, XrWithVoipMetric) { + VoipMetric metric; + metric.To(kRemoteSsrc); + metric.LossRate(1); + metric.DiscardRate(2); + metric.BurstDensity(3); + metric.GapDensity(4); + metric.BurstDuration(0x1111); + metric.GapDuration(0x2222); + metric.RoundTripDelay(0x3333); + metric.EndSystemDelay(0x4444); + metric.SignalLevel(5); + metric.NoiseLevel(6); + metric.Rerl(7); + metric.Gmin(8); + metric.Rfactor(9); + metric.ExtRfactor(10); + metric.MosLq(11); + metric.MosCq(12); + metric.RxConfig(13); + metric.JbNominal(0x5555); + metric.JbMax(0x6666); + metric.JbAbsMax(0x7777); + + Xr xr; + xr.From(kSenderSsrc); + xr.WithVoipMetric(&metric); + + RawPacket packet = xr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.xr_header()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.xr_header()->Ssrc()); + EXPECT_EQ(1, parser.voip_metric()->num_packets()); + EXPECT_EQ(kRemoteSsrc, parser.voip_metric()->Ssrc()); + EXPECT_EQ(1, parser.voip_metric()->LossRate()); + EXPECT_EQ(2, parser.voip_metric()->DiscardRate()); + EXPECT_EQ(3, parser.voip_metric()->BurstDensity()); + EXPECT_EQ(4, parser.voip_metric()->GapDensity()); + EXPECT_EQ(0x1111, parser.voip_metric()->BurstDuration()); + EXPECT_EQ(0x2222, parser.voip_metric()->GapDuration()); + EXPECT_EQ(0x3333, parser.voip_metric()->RoundTripDelay()); + EXPECT_EQ(0x4444, parser.voip_metric()->EndSystemDelay()); + EXPECT_EQ(5, parser.voip_metric()->SignalLevel()); + EXPECT_EQ(6, parser.voip_metric()->NoiseLevel()); + EXPECT_EQ(7, parser.voip_metric()->Rerl()); + EXPECT_EQ(8, parser.voip_metric()->Gmin()); + EXPECT_EQ(9, parser.voip_metric()->Rfactor()); + EXPECT_EQ(10, parser.voip_metric()->ExtRfactor()); + EXPECT_EQ(11, parser.voip_metric()->MosLq()); + EXPECT_EQ(12, parser.voip_metric()->MosCq()); + EXPECT_EQ(13, parser.voip_metric()->RxConfig()); + EXPECT_EQ(0x5555, parser.voip_metric()->JbNominal()); + EXPECT_EQ(0x6666, parser.voip_metric()->JbMax()); + EXPECT_EQ(0x7777, parser.voip_metric()->JbAbsMax()); +} + +TEST(RtcpPacketTest, XrWithMultipleReportBlocks) { + Rrtr rrtr; + Dlrr dlrr; + dlrr.WithDlrrItem(1, 2, 3); + VoipMetric metric; + Xr xr; + xr.From(kSenderSsrc); + xr.WithRrtr(&rrtr); + xr.WithDlrr(&dlrr); + xr.WithVoipMetric(&metric); + + RawPacket packet = xr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.xr_header()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.xr_header()->Ssrc()); + EXPECT_EQ(1, parser.rrtr()->num_packets()); + EXPECT_EQ(1, parser.dlrr()->num_packets()); + EXPECT_EQ(1, parser.dlrr_items()->num_packets()); + EXPECT_EQ(1, parser.voip_metric()->num_packets()); +} + +TEST(RtcpPacketTest, DlrrWithoutItemNotIncludedInPacket) { + Rrtr rrtr; + Dlrr dlrr; + VoipMetric metric; + Xr xr; + xr.From(kSenderSsrc); + xr.WithRrtr(&rrtr); + xr.WithDlrr(&dlrr); + xr.WithVoipMetric(&metric); + + RawPacket packet = xr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.xr_header()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.xr_header()->Ssrc()); + EXPECT_EQ(1, parser.rrtr()->num_packets()); + EXPECT_EQ(0, parser.dlrr()->num_packets()); + EXPECT_EQ(1, parser.voip_metric()->num_packets()); +} } // namespace webrtc diff --git a/webrtc/test/rtcp_packet_parser.cc b/webrtc/test/rtcp_packet_parser.cc index 69c50d1b58..558bee379e 100644 --- a/webrtc/test/rtcp_packet_parser.cc +++ b/webrtc/test/rtcp_packet_parser.cc @@ -80,6 +80,41 @@ void RtcpPacketParser::Parse(const void *data, int len) { case RTCPUtility::kRtcpRtpfbNackItemCode: nack_item_.Set(parser.Packet().NACKItem); break; + case RTCPUtility::kRtcpPsfbAppCode: + psfb_app_.Set(parser.Packet().PSFBAPP); + break; + case RTCPUtility::kRtcpPsfbRembItemCode: + remb_item_.Set(parser.Packet().REMBItem); + break; + case RTCPUtility::kRtcpRtpfbTmmbrCode: + tmmbr_.Set(parser.Packet().TMMBR); + break; + case RTCPUtility::kRtcpRtpfbTmmbrItemCode: + tmmbr_item_.Set(parser.Packet().TMMBRItem); + break; + case RTCPUtility::kRtcpRtpfbTmmbnCode: + tmmbn_.Set(parser.Packet().TMMBN); + tmmbn_items_.Clear(); + break; + case RTCPUtility::kRtcpRtpfbTmmbnItemCode: + tmmbn_items_.Set(parser.Packet().TMMBNItem); + break; + case RTCPUtility::kRtcpXrHeaderCode: + xr_header_.Set(parser.Packet().XR); + dlrr_items_.Clear(); + break; + case RTCPUtility::kRtcpXrReceiverReferenceTimeCode: + rrtr_.Set(parser.Packet().XRReceiverReferenceTimeItem); + break; + case RTCPUtility::kRtcpXrDlrrReportBlockCode: + dlrr_.Set(); + break; + case RTCPUtility::kRtcpXrDlrrReportBlockItemCode: + dlrr_items_.Set(parser.Packet().XRDLRRReportBlockItem); + break; + case RTCPUtility::kRtcpXrVoipMetricCode: + voip_metric_.Set(parser.Packet().XRVOIPMetricItem); + break; default: break; } diff --git a/webrtc/test/rtcp_packet_parser.h b/webrtc/test/rtcp_packet_parser.h index a09674f304..f7d36ba804 100644 --- a/webrtc/test/rtcp_packet_parser.h +++ b/webrtc/test/rtcp_packet_parser.h @@ -378,6 +378,254 @@ class NackItem : public PacketType { std::vector last_nack_list_; }; +class PsfbApp : public PacketType { + public: + PsfbApp() {} + virtual ~PsfbApp() {} + + uint32_t Ssrc() const { return psfb_app_.SenderSSRC; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketPSFBAPP& psfb_app) { + psfb_app_ = psfb_app; + ++num_packets_; + } + + RTCPUtility::RTCPPacketPSFBAPP psfb_app_; +}; + +class RembItem : public PacketType { + public: + RembItem() : last_bitrate_bps_(0) {} + virtual ~RembItem() {} + + int last_bitrate_bps() const { return last_bitrate_bps_; } + std::vector last_ssrc_list() { + return last_ssrc_list_; + } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketPSFBREMBItem& remb_item) { + last_bitrate_bps_ = remb_item.BitRate; + last_ssrc_list_.clear(); + last_ssrc_list_.insert( + last_ssrc_list_.end(), + remb_item.SSRCs, + remb_item.SSRCs + remb_item.NumberOfSSRCs); + ++num_packets_; + } + + uint32_t last_bitrate_bps_; + std::vector last_ssrc_list_; +}; + +class Tmmbr : public PacketType { + public: + Tmmbr() {} + virtual ~Tmmbr() {} + + uint32_t Ssrc() const { return tmmbr_.SenderSSRC; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketRTPFBTMMBR& tmmbr) { + tmmbr_ = tmmbr; + ++num_packets_; + } + + RTCPUtility::RTCPPacketRTPFBTMMBR tmmbr_; +}; + +class TmmbrItem : public PacketType { + public: + TmmbrItem() {} + virtual ~TmmbrItem() {} + + uint32_t Ssrc() const { return tmmbr_item_.SSRC; } + uint32_t BitrateKbps() const { return tmmbr_item_.MaxTotalMediaBitRate; } + uint32_t Overhead() const { return tmmbr_item_.MeasuredOverhead; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketRTPFBTMMBRItem& tmmbr_item) { + tmmbr_item_ = tmmbr_item; + ++num_packets_; + } + + RTCPUtility::RTCPPacketRTPFBTMMBRItem tmmbr_item_; +}; + + +class Tmmbn : public PacketType { + public: + Tmmbn() {} + virtual ~Tmmbn() {} + + uint32_t Ssrc() const { return tmmbn_.SenderSSRC; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketRTPFBTMMBN& tmmbn) { + tmmbn_ = tmmbn; + ++num_packets_; + } + + RTCPUtility::RTCPPacketRTPFBTMMBN tmmbn_; +}; + +class TmmbnItems : public PacketType { + public: + TmmbnItems() {} + virtual ~TmmbnItems() {} + + uint32_t Ssrc(uint8_t num) const { + assert(num < tmmbns_.size()); + return tmmbns_[num].SSRC; + } + uint32_t BitrateKbps(uint8_t num) const { + assert(num < tmmbns_.size()); + return tmmbns_[num].MaxTotalMediaBitRate; + } + uint32_t Overhead(uint8_t num) const { + assert(num < tmmbns_.size()); + return tmmbns_[num].MeasuredOverhead; + } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketRTPFBTMMBNItem& tmmbn_item) { + tmmbns_.push_back(tmmbn_item); + ++num_packets_; + } + void Clear() { tmmbns_.clear(); } + + std::vector tmmbns_; +}; + +class XrHeader : public PacketType { + public: + XrHeader() {} + virtual ~XrHeader() {} + + uint32_t Ssrc() const { return xr_header_.OriginatorSSRC; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketXR& xr_header) { + xr_header_ = xr_header; + ++num_packets_; + } + + RTCPUtility::RTCPPacketXR xr_header_; +}; + +class Rrtr : public PacketType { + public: + Rrtr() {} + virtual ~Rrtr() {} + + uint32_t NtpSec() const { return rrtr_.NTPMostSignificant; } + uint32_t NtpFrac() const { return rrtr_.NTPLeastSignificant; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketXRReceiverReferenceTimeItem& rrtr) { + rrtr_ = rrtr; + ++num_packets_; + } + + RTCPUtility::RTCPPacketXRReceiverReferenceTimeItem rrtr_; +}; + +class Dlrr : public PacketType { + public: + Dlrr() {} + virtual ~Dlrr() {} + + private: + friend class RtcpPacketParser; + + void Set() { ++num_packets_; } +}; + +class DlrrItems : public PacketType { + public: + DlrrItems() {} + virtual ~DlrrItems() {} + + uint32_t Ssrc(uint8_t num) const { + assert(num < dlrrs_.size()); + return dlrrs_[num].SSRC; + } + uint32_t LastRr(uint8_t num) const { + assert(num < dlrrs_.size()); + return dlrrs_[num].LastRR; + } + uint32_t DelayLastRr(uint8_t num) const { + assert(num < dlrrs_.size()); + return dlrrs_[num].DelayLastRR; + } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketXRDLRRReportBlockItem& dlrr) { + dlrrs_.push_back(dlrr); + ++num_packets_; + } + void Clear() { dlrrs_.clear(); } + + std::vector dlrrs_; +}; + +class VoipMetric : public PacketType { + public: + VoipMetric() {} + virtual ~VoipMetric() {} + + uint32_t Ssrc() const { return voip_metric_.SSRC; } + uint8_t LossRate() { return voip_metric_.lossRate; } + uint8_t DiscardRate() { return voip_metric_.discardRate; } + uint8_t BurstDensity() { return voip_metric_.burstDensity; } + uint8_t GapDensity() { return voip_metric_.gapDensity; } + uint16_t BurstDuration() { return voip_metric_.burstDuration; } + uint16_t GapDuration() { return voip_metric_.gapDuration; } + uint16_t RoundTripDelay() { return voip_metric_.roundTripDelay; } + uint16_t EndSystemDelay() { return voip_metric_.endSystemDelay; } + uint8_t SignalLevel() { return voip_metric_.signalLevel; } + uint8_t NoiseLevel() { return voip_metric_.noiseLevel; } + uint8_t Rerl() { return voip_metric_.RERL; } + uint8_t Gmin() { return voip_metric_.Gmin; } + uint8_t Rfactor() { return voip_metric_.Rfactor; } + uint8_t ExtRfactor() { return voip_metric_.extRfactor; } + uint8_t MosLq() { return voip_metric_.MOSLQ; } + uint8_t MosCq() { return voip_metric_.MOSCQ; } + uint8_t RxConfig() { return voip_metric_.RXconfig; } + uint16_t JbNominal() { return voip_metric_.JBnominal; } + uint16_t JbMax() { return voip_metric_.JBmax; } + uint16_t JbAbsMax() { return voip_metric_.JBabsMax; } + + private: + friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketXRVOIPMetricItem& voip_metric) { + voip_metric_ = voip_metric; + ++num_packets_; + } + + RTCPUtility::RTCPPacketXRVOIPMetricItem voip_metric_; +}; + class RtcpPacketParser { public: RtcpPacketParser(); @@ -403,6 +651,17 @@ class RtcpPacketParser { FirItem* fir_item() { return &fir_item_; } Nack* nack() { return &nack_; } NackItem* nack_item() { return &nack_item_; } + PsfbApp* psfb_app() { return &psfb_app_; } + RembItem* remb_item() { return &remb_item_; } + Tmmbr* tmmbr() { return &tmmbr_; } + TmmbrItem* tmmbr_item() { return &tmmbr_item_; } + Tmmbn* tmmbn() { return &tmmbn_; } + TmmbnItems* tmmbn_items() { return &tmmbn_items_; } + XrHeader* xr_header() { return &xr_header_; } + Rrtr* rrtr() { return &rrtr_; } + Dlrr* dlrr() { return &dlrr_; } + DlrrItems* dlrr_items() { return &dlrr_items_; } + VoipMetric* voip_metric() { return &voip_metric_; } int report_blocks_per_ssrc(uint32_t ssrc) { return report_blocks_per_ssrc_[ssrc]; @@ -427,6 +686,17 @@ class RtcpPacketParser { FirItem fir_item_; Nack nack_; NackItem nack_item_; + PsfbApp psfb_app_; + RembItem remb_item_; + Tmmbr tmmbr_; + TmmbrItem tmmbr_item_; + Tmmbn tmmbn_; + TmmbnItems tmmbn_items_; + XrHeader xr_header_; + Rrtr rrtr_; + Dlrr dlrr_; + DlrrItems dlrr_items_; + VoipMetric voip_metric_; std::map report_blocks_per_ssrc_; };