From 0f2809a5ac5477a6134ebafb4680597252f8a5c5 Mon Sep 17 00:00:00 2001 From: "asapersson@webrtc.org" Date: Fri, 21 Feb 2014 08:14:45 +0000 Subject: [PATCH] Add RTCP packet class. Adds packet types: sr, rr, bye, fir. BUG=2450 R=mflodman@webrtc.org, stefan@webrtc.org Review URL: https://webrtc-codereview.appspot.com/8079004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@5592 4adac7df-926f-26a2-2b94-8c16560cd09d --- webrtc/modules/modules.gyp | 4 +- webrtc/modules/rtp_rtcp/source/rtcp_packet.cc | 309 ++++++++++++++ webrtc/modules/rtp_rtcp/source/rtcp_packet.h | 389 ++++++++++++++++++ .../rtp_rtcp/source/rtcp_packet_unittest.cc | 312 ++++++++++++++ webrtc/modules/rtp_rtcp/source/rtp_rtcp.gypi | 2 + webrtc/test/rtcp_packet_parser.cc | 47 +++ webrtc/test/rtcp_packet_parser.h | 235 +++++++++++ webrtc/test/test.gyp | 11 + 8 files changed, 1308 insertions(+), 1 deletion(-) create mode 100644 webrtc/modules/rtp_rtcp/source/rtcp_packet.cc create mode 100644 webrtc/modules/rtp_rtcp/source/rtcp_packet.h create mode 100644 webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc create mode 100644 webrtc/test/rtcp_packet_parser.cc create mode 100644 webrtc/test/rtcp_packet_parser.h diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp index d1c0bd1a7..f18551c12 100644 --- a/webrtc/modules/modules.gyp +++ b/webrtc/modules/modules.gyp @@ -99,6 +99,7 @@ '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers', '<(webrtc_root)/test/test.gyp:test_support_main', '<(webrtc_root)/test/test.gyp:frame_generator', + '<(webrtc_root)/test/test.gyp:rtcp_packet_parser', ], 'sources': [ 'audio_coding/main/acm2/acm_receiver_unittest.cc', @@ -203,8 +204,9 @@ 'rtp_rtcp/source/producer_fec_unittest.cc', 'rtp_rtcp/source/receive_statistics_unittest.cc', 'rtp_rtcp/source/rtcp_format_remb_unittest.cc', - 'rtp_rtcp/source/rtcp_sender_unittest.cc', + 'rtp_rtcp/source/rtcp_packet_unittest.cc', 'rtp_rtcp/source/rtcp_receiver_unittest.cc', + 'rtp_rtcp/source/rtcp_sender_unittest.cc', 'rtp_rtcp/source/rtp_fec_unittest.cc', 'rtp_rtcp/source/rtp_format_vp8_unittest.cc', 'rtp_rtcp/source/rtp_format_vp8_test_helper.cc', diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet.cc new file mode 100644 index 000000000..0c3197fe1 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet.cc @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.h" + +#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" +#include "webrtc/system_wrappers/interface/trace.h" + +namespace webrtc { +namespace rtcp { +namespace { +void AssignUWord8(uint8_t* buffer, uint16_t* offset, uint8_t value) { + buffer[(*offset)++] = value; +} +void AssignUWord16(uint8_t* buffer, uint16_t* offset, uint16_t value) { + ModuleRTPUtility::AssignUWord16ToBuffer(buffer + *offset, value); + *offset += 2; +} +void AssignUWord24(uint8_t* buffer, uint16_t* offset, uint32_t value) { + ModuleRTPUtility::AssignUWord24ToBuffer(buffer + *offset, value); + *offset += 3; +} +void AssignUWord32(uint8_t* buffer, uint16_t* offset, uint32_t value) { + ModuleRTPUtility::AssignUWord32ToBuffer(buffer + *offset, value); + *offset += 4; +} + +// From RFC 3550, RTP: A Transport Protocol for Real-Time Applications. +// +// Sender report +// 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| RC | PT=SR=200 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of sender | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | NTP timestamp, most significant word | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | NTP timestamp, least significant word | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | RTP timestamp | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | sender's packet count | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | sender's octet count | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + +void CreateSenderReport(const RTCPUtility::RTCPPacketSR& sr, + uint8_t* buffer, + uint16_t* pos) { + const uint16_t kLength = 6 + (6 * sr.NumberOfReportBlocks); + AssignUWord8(buffer, pos, 0x80 + sr.NumberOfReportBlocks); + AssignUWord8(buffer, pos, RTCPUtility::PT_SR); + AssignUWord16(buffer, pos, kLength); + AssignUWord32(buffer, pos, sr.SenderSSRC); + AssignUWord32(buffer, pos, sr.NTPMostSignificant); + AssignUWord32(buffer, pos, sr.NTPLeastSignificant); + AssignUWord32(buffer, pos, sr.RTPTimestamp); + AssignUWord32(buffer, pos, sr.SenderPacketCount); + AssignUWord32(buffer, pos, sr.SenderOctetCount); +} + +// Receiver report, header (RFC 3550). +// +// 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| RC | PT=RR=201 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of packet sender | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + +void CreateReceiverReport(const RTCPUtility::RTCPPacketRR& rr, + uint8_t* buffer, + uint16_t* pos) { + const uint16_t kLength = 1 + (6 * rr.NumberOfReportBlocks); + AssignUWord8(buffer, pos, 0x80 + rr.NumberOfReportBlocks); + AssignUWord8(buffer, pos, RTCPUtility::PT_RR); + AssignUWord16(buffer, pos, kLength); + AssignUWord32(buffer, pos, rr.SenderSSRC); +} + +// Report block (RFC 3550). +// +// 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_1 (SSRC of first source) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | fraction lost | cumulative number of packets lost | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | extended highest sequence number received | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | interarrival jitter | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | last SR (LSR) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | delay since last SR (DLSR) | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + +void CreateReportBlock( + const RTCPUtility::RTCPPacketReportBlockItem& report_block, + uint8_t* buffer, + uint16_t* pos) { + AssignUWord32(buffer, pos, report_block.SSRC); + AssignUWord8(buffer, pos, report_block.FractionLost); + AssignUWord24(buffer, pos, report_block.CumulativeNumOfPacketsLost); + AssignUWord32(buffer, pos, report_block.ExtendedHighestSequenceNumber); + AssignUWord32(buffer, pos, report_block.Jitter); + AssignUWord32(buffer, pos, report_block.LastSR); + AssignUWord32(buffer, pos, report_block.DelayLastSR); +} + +// Bye packet (BYE) (RFC 3550). +// +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| SC | PT=BYE=203 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC/CSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : ... : +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// (opt) | length | reason for leaving ... +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateBye(const RTCPUtility::RTCPPacketBYE& bye, + const std::vector& csrcs, + uint8_t* buffer, + uint16_t* pos) { + const uint8_t kNumSsrcAndCsrcs = 1 + csrcs.size(); + AssignUWord8(buffer, pos, 0x80 + kNumSsrcAndCsrcs); + AssignUWord8(buffer, pos, RTCPUtility::PT_BYE); + AssignUWord16(buffer, pos, kNumSsrcAndCsrcs); + AssignUWord32(buffer, pos, bye.SenderSSRC); + for (std::vector::const_iterator it = csrcs.begin(); + it != csrcs.end(); ++it) { + AssignUWord32(buffer, pos, *it); + } +} + +// RFC 4585: Feedback format. +// +// Common packet format: +// +// 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 | PT | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of packet sender | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of media source | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : Feedback Control Information (FCI) : +// : +// +// Full intra request (FIR) (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 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Seq nr. | Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateFirRequest(const RTCPUtility::RTCPPacketPSFBFIR& fir, + const RTCPUtility::RTCPPacketPSFBFIRItem& fir_item, + uint8_t* buffer, + uint16_t* pos) { + const uint16_t kLength = 4; + const uint8_t kFmt = 4; + AssignUWord8(buffer, pos, 0x80 + kFmt); + AssignUWord8(buffer, pos, RTCPUtility::PT_PSFB); + AssignUWord16(buffer, pos, kLength); + AssignUWord32(buffer, pos, fir.SenderSSRC); + AssignUWord32(buffer, pos, 0); + AssignUWord32(buffer, pos, fir_item.SSRC); + AssignUWord8(buffer, pos, fir_item.CommandSequenceNumber); + AssignUWord24(buffer, pos, 0); +} + +void AppendReportBlocks(const std::vector& report_blocks, + uint8_t* buffer, + uint16_t* pos) { + for (std::vector::const_iterator it = report_blocks.begin(); + it != report_blocks.end(); ++it) { + (*it)->Create(buffer, pos); + } +} +} // namespace + +void RtcpPacket::Append(RtcpPacket* packet) { + assert(packet); + appended_packets_.push_back(packet); +} + +RawPacket RtcpPacket::Build() const { + uint16_t len = 0; + uint8_t packet[IP_PACKET_SIZE]; + CreateAndAddAppended(packet, &len, IP_PACKET_SIZE); + return RawPacket(packet, len); +} + +void RtcpPacket::Build(uint8_t* packet, uint16_t* len, uint16_t max_len) const { + *len = 0; + CreateAndAddAppended(packet, len, max_len); +} + +void RtcpPacket::CreateAndAddAppended(uint8_t* packet, + uint16_t* len, + uint16_t max_len) const { + Create(packet, len, max_len); + for (std::vector::const_iterator it = appended_packets_.begin(); + it != appended_packets_.end(); ++it) { + (*it)->CreateAndAddAppended(packet, len, max_len); + } +} + +void Empty::Create(uint8_t* packet, uint16_t* len, uint16_t max_len) const { +} + +void SenderReport::Create(uint8_t* packet, + uint16_t* len, + uint16_t max_len) const { + if (*len + Length() > max_len) { + WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1, + "Max packet size reached, skipped SR."); + return; + } + CreateSenderReport(sr_, packet, len); + AppendReportBlocks(report_blocks_, packet, len); +} + +void SenderReport::WithReportBlock(ReportBlock* block) { + assert(block); + if (report_blocks_.size() >= kMaxNumberOfReportBlocks) { + WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1, + "Max report block size reached."); + return; + } + report_blocks_.push_back(block); + sr_.NumberOfReportBlocks = report_blocks_.size(); +} + +void ReceiverReport::Create(uint8_t* packet, + uint16_t* len, + uint16_t max_len) const { + if (*len + Length() > max_len) { + WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1, + "Max packet size reached, skipped RR."); + return; + } + CreateReceiverReport(rr_, packet, len); + AppendReportBlocks(report_blocks_, packet, len); +} + +void ReceiverReport::WithReportBlock(ReportBlock* block) { + assert(block); + if (report_blocks_.size() >= kMaxNumberOfReportBlocks) { + WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1, + "Max report block size reached."); + return; + } + report_blocks_.push_back(block); + rr_.NumberOfReportBlocks = report_blocks_.size(); +} + +void Bye::Create(uint8_t* packet, uint16_t* len, uint16_t max_len) const { + if (*len + Length() > max_len) { + WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1, + "Max packet size reached, skipped BYE."); + return; + } + CreateBye(bye_, csrcs_, packet, len); +} + +void Bye::WithCsrc(uint32_t csrc) { + if (csrcs_.size() >= kMaxNumberOfCsrcs) { + WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1, + "Max CSRC size reached."); + return; + } + csrcs_.push_back(csrc); +} + +void Fir::Create(uint8_t* packet, uint16_t* len, uint16_t max_len) const { + if (*len + Length() > max_len) { + WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1, + "Max packet size reached, skipped FIR."); + return; + } + CreateFirRequest(fir_, fir_item_, packet, len); +} + +void ReportBlock::Create(uint8_t* packet, uint16_t* len) const { + CreateReportBlock(report_block_, packet, len); +} +} // namespace rtcp +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet.h new file mode 100644 index 000000000..13ad808b9 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet.h @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#ifndef WEBRTC_MODULES_RTP_RTCP_RTCP_PACKET_H_ +#define WEBRTC_MODULES_RTP_RTCP_RTCP_PACKET_H_ + +#include + +#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" +#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h" +#include "webrtc/typedefs.h" + +namespace webrtc { +namespace rtcp { + +class RawPacket; +class ReportBlock; + +// Class for building RTCP packets. +// +// Example: +// ReportBlock report_block; +// report_block.To(234) +// report_block.FractionLost(10); +// +// ReceiverReport rr; +// rr.From(123); +// rr.WithReportBlock(&report_block) +// +// Fir fir; +// fir.From(123); +// fir.To(234) +// fir.WithCommandSeqNum(123); +// +// uint16_t len = 0; // Builds an intra frame request +// uint8_t packet[kPacketSize]; // with sequence number 123. +// fir.Build(packet, &len, kPacketSize); +// +// RawPacket packet = fir.Build(); // Returns a RawPacket holding +// // the built rtcp packet. +// +// rr.Append(&fir) // Builds a compound RTCP packet with +// RawPacket packet = rr.Build(); // a receiver report, report block +// // and fir message. + +class RtcpPacket { + public: + virtual ~RtcpPacket() {} + + void Append(RtcpPacket* packet); + + RawPacket Build() const; + + void Build(uint8_t* packet, uint16_t* len, uint16_t max_len) const; + + protected: + RtcpPacket() {} + + virtual void Create( + uint8_t* packet, uint16_t* len, uint16_t max_len) const = 0; + + void CreateAndAddAppended( + uint8_t* packet, uint16_t* len, uint16_t max_len) const; + + private: + std::vector appended_packets_; +}; + +class Empty : public RtcpPacket { + public: + Empty() {} + + virtual ~Empty() {} + + protected: + virtual void Create(uint8_t* packet, uint16_t* len, uint16_t max_len) const; +}; + +//// From RFC 3550, RTP: A Transport Protocol for Real-Time Applications. +// +// RTCP sender report (RFC 3550). +// +// 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| RC | PT=SR=200 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of sender | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | NTP timestamp, most significant word | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | NTP timestamp, least significant word | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | RTP timestamp | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | sender's packet count | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | sender's octet count | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | report block(s) | +// | .... | + +class SenderReport : public RtcpPacket { + public: + SenderReport() + : RtcpPacket() { + memset(&sr_, 0, sizeof(sr_)); + } + + virtual ~SenderReport() {} + + void From(uint32_t ssrc) { + sr_.SenderSSRC = ssrc; + } + void WithNtpSec(uint32_t sec) { + sr_.NTPMostSignificant = sec; + } + void WithNtpFrac(uint32_t frac) { + sr_.NTPLeastSignificant = frac; + } + void WithRtpTimestamp(uint32_t rtp_timestamp) { + sr_.RTPTimestamp = rtp_timestamp; + } + void WithPacketCount(uint32_t packet_count) { + sr_.SenderPacketCount = packet_count; + } + void WithOctetCount(uint32_t octet_count) { + sr_.SenderOctetCount = octet_count; + } + void WithReportBlock(ReportBlock* block); + + enum { kMaxNumberOfReportBlocks = 0x1f }; + + protected: + virtual void Create(uint8_t* packet, uint16_t* len, uint16_t max_len) const; + + private: + uint16_t Length() const { + const uint16_t kSrBlockLen = 28; + const uint16_t kReportBlockLen = 24; + return kSrBlockLen + report_blocks_.size() * kReportBlockLen; + } + + RTCPUtility::RTCPPacketSR sr_; + std::vector report_blocks_; +}; + +// +// RTCP receiver report (RFC 3550). +// +// 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| RC | PT=RR=201 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of packet sender | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | report block(s) | +// | .... | + +class ReceiverReport : public RtcpPacket { + public: + ReceiverReport() + : RtcpPacket() { + memset(&rr_, 0, sizeof(rr_)); + } + + virtual ~ReceiverReport() {} + + void From(uint32_t ssrc) { + rr_.SenderSSRC = ssrc; + } + void WithReportBlock(ReportBlock* block); + + enum { kMaxNumberOfReportBlocks = 0x1f }; + + protected: + virtual void Create(uint8_t* packet, uint16_t* len, uint16_t max_len) const; + + private: + uint16_t Length() const { + const uint16_t kRrBlockLen = 8; + const uint16_t kReportBlockLen = 24; + return kRrBlockLen + report_blocks_.size() * kReportBlockLen; + } + + RTCPUtility::RTCPPacketRR rr_; + std::vector report_blocks_; +}; + +// +// RTCP report block (RFC 3550). +// +// 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_1 (SSRC of first source) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | fraction lost | cumulative number of packets lost | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | extended highest sequence number received | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | interarrival jitter | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | last SR (LSR) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | delay since last SR (DLSR) | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ + +class ReportBlock { + public: + ReportBlock() { + memset(&report_block_, 0, sizeof(report_block_)); + } + + ~ReportBlock() {} + + void To(uint32_t ssrc) { + report_block_.SSRC = ssrc; + } + void WithFractionLost(uint8_t fraction_lost) { + report_block_.FractionLost = fraction_lost; + } + void WithCumPacketsLost(uint32_t cum_packets_lost) { + report_block_.CumulativeNumOfPacketsLost = cum_packets_lost; + } + void WithExtHighestSeqNum(uint32_t ext_highest_seq_num) { + report_block_.ExtendedHighestSequenceNumber = ext_highest_seq_num; + } + void WithJitter(uint32_t jitter) { + report_block_.Jitter = jitter; + } + void WithLastSr(uint32_t last_sr) { + report_block_.LastSR = last_sr; + } + void WithDelayLastSr(uint32_t delay_last_sr) { + report_block_.DelayLastSR = delay_last_sr; + } + + void Create(uint8_t* array, uint16_t* cur_pos) const; + + private: + RTCPUtility::RTCPPacketReportBlockItem report_block_; +}; + +// +// Bye packet (BYE) (RFC 3550). +// +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| SC | PT=BYE=203 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC/CSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : ... : +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// (opt) | length | reason for leaving ... +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class Bye : public RtcpPacket { + public: + Bye() + : RtcpPacket() { + memset(&bye_, 0, sizeof(bye_)); + } + + virtual ~Bye() {} + + void From(uint32_t ssrc) { + bye_.SenderSSRC = ssrc; + } + void WithCsrc(uint32_t csrc); + + enum { kMaxNumberOfCsrcs = 0x1f - 1 }; + + protected: + virtual void Create(uint8_t* packet, uint16_t* len, uint16_t max_len) const; + + private: + uint16_t Length() const { + const uint16_t kByeBlockLen = 8 + 4*csrcs_.size(); + return kByeBlockLen; + } + + RTCPUtility::RTCPPacketBYE bye_; + std::vector csrcs_; +}; + +// RFC 4585: Feedback format. +// +// Common packet format: +// +// 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 | PT | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of packet sender | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of media source | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : Feedback Control Information (FCI) : +// : + + +// Full intra request (FIR) (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 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | Seq nr. | Reserved | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class Fir : public RtcpPacket { + public: + Fir() + : RtcpPacket() { + memset(&fir_, 0, sizeof(fir_)); + memset(&fir_item_, 0, sizeof(fir_item_)); + } + + virtual ~Fir() {} + + void From(uint32_t ssrc) { + fir_.SenderSSRC = ssrc; + } + void To(uint32_t ssrc) { + fir_item_.SSRC = ssrc; + } + void WithCommandSeqNum(uint8_t seq_num) { + fir_item_.CommandSequenceNumber = seq_num; + } + + protected: + virtual void Create(uint8_t* packet, uint16_t* len, uint16_t max_len) const; + + private: + uint16_t Length() const { + const uint16_t kFirBlockLen = 20; + return kFirBlockLen; + } + + RTCPUtility::RTCPPacketPSFBFIR fir_; + RTCPUtility::RTCPPacketPSFBFIRItem fir_item_; +}; + +// Class holding a RTCP packet. +// +// Takes a built rtcp packet. +// RawPacket raw_packet(buffer, len); +// +// To access the raw packet: +// raw_packet.buffer(); - pointer to the raw packet +// raw_packet.buffer_length(); - the length of the raw packet + +class RawPacket { + public: + RawPacket(const uint8_t* buffer, uint16_t len) { + assert(len <= IP_PACKET_SIZE); + memcpy(packet_, buffer, len); + packet_length_ = len; + } + + const uint8_t* buffer() { + return packet_; + } + uint16_t buffer_length() const { + return packet_length_; + } + + private: + uint16_t packet_length_; + uint8_t packet_[IP_PACKET_SIZE]; +}; + +} // namespace rtcp +} // namespace webrtc +#endif // WEBRTC_MODULES_RTP_RTCP_RTCP_PACKET_H_ diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc new file mode 100644 index 000000000..0114acd89 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + * This file includes unit tests for the RtcpPacket. + */ + +#include "testing/gtest/include/gtest/gtest.h" + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.h" +#include "webrtc/test/rtcp_packet_parser.h" + +using webrtc::rtcp::Bye; +using webrtc::rtcp::Empty; +using webrtc::rtcp::Fir; +using webrtc::rtcp::SenderReport; +using webrtc::rtcp::RawPacket; +using webrtc::rtcp::ReceiverReport; +using webrtc::rtcp::ReportBlock; +using webrtc::test::RtcpPacketParser; + +namespace webrtc { + +const uint32_t kSenderSsrc = 0x12345678; +const uint32_t kRemoteSsrc = 0x23456789; + +TEST(RtcpPacketTest, Rr) { + ReceiverReport rr; + rr.From(kSenderSsrc); + + RawPacket packet = rr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.receiver_report()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.receiver_report()->Ssrc()); + EXPECT_EQ(0, parser.report_block()->num_packets()); +} + +TEST(RtcpPacketTest, RrWithOneReportBlock) { + ReportBlock rb; + rb.To(kRemoteSsrc); + rb.WithFractionLost(55); + rb.WithCumPacketsLost(0x111111); + rb.WithExtHighestSeqNum(0x22222222); + rb.WithJitter(0x33333333); + rb.WithLastSr(0x44444444); + rb.WithDelayLastSr(0x55555555); + + ReceiverReport rr; + rr.From(kSenderSsrc); + rr.WithReportBlock(&rb); + + RawPacket packet = rr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.receiver_report()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.receiver_report()->Ssrc()); + EXPECT_EQ(1, parser.report_block()->num_packets()); + EXPECT_EQ(kRemoteSsrc, parser.report_block()->Ssrc()); + EXPECT_EQ(55U, parser.report_block()->FractionLost()); + EXPECT_EQ(0x111111U, parser.report_block()->CumPacketLost()); + EXPECT_EQ(0x22222222U, parser.report_block()->ExtHighestSeqNum()); + EXPECT_EQ(0x33333333U, parser.report_block()->Jitter()); + EXPECT_EQ(0x44444444U, parser.report_block()->LastSr()); + EXPECT_EQ(0x55555555U, parser.report_block()->DelayLastSr()); +} + +TEST(RtcpPacketTest, RrWithTwoReportBlocks) { + ReportBlock rb1; + rb1.To(kRemoteSsrc); + ReportBlock rb2; + rb2.To(kRemoteSsrc + 1); + + ReceiverReport rr; + rr.From(kSenderSsrc); + rr.WithReportBlock(&rb1); + rr.WithReportBlock(&rb2); + + RawPacket packet = rr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.receiver_report()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.receiver_report()->Ssrc()); + EXPECT_EQ(2, parser.report_block()->num_packets()); + EXPECT_EQ(1, parser.report_blocks_per_ssrc(kRemoteSsrc)); + EXPECT_EQ(1, parser.report_blocks_per_ssrc(kRemoteSsrc + 1)); +} + +TEST(RtcpPacketTest, Sr) { + SenderReport sr; + sr.From(kSenderSsrc); + sr.WithNtpSec(0x11111111); + sr.WithNtpFrac(0x22222222); + sr.WithRtpTimestamp(0x33333333); + sr.WithPacketCount(0x44444444); + sr.WithOctetCount(0x55555555); + + RawPacket packet = sr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + + EXPECT_EQ(1, parser.sender_report()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.sender_report()->Ssrc()); + EXPECT_EQ(0x11111111U, parser.sender_report()->NtpSec()); + EXPECT_EQ(0x22222222U, parser.sender_report()->NtpFrac()); + EXPECT_EQ(0x33333333U, parser.sender_report()->RtpTimestamp()); + EXPECT_EQ(0x44444444U, parser.sender_report()->PacketCount()); + EXPECT_EQ(0x55555555U, parser.sender_report()->OctetCount()); + EXPECT_EQ(0, parser.report_block()->num_packets()); +} + +TEST(RtcpPacketTest, SrWithOneReportBlock) { + ReportBlock rb; + rb.To(kRemoteSsrc); + + SenderReport sr; + sr.From(kSenderSsrc); + sr.WithReportBlock(&rb); + + RawPacket packet = sr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.sender_report()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.sender_report()->Ssrc()); + EXPECT_EQ(1, parser.report_block()->num_packets()); + EXPECT_EQ(kRemoteSsrc, parser.report_block()->Ssrc()); +} + +TEST(RtcpPacketTest, SrWithTwoReportBlocks) { + ReportBlock rb1; + rb1.To(kRemoteSsrc); + ReportBlock rb2; + rb2.To(kRemoteSsrc + 1); + + SenderReport sr; + sr.From(kSenderSsrc); + sr.WithReportBlock(&rb1); + sr.WithReportBlock(&rb2); + + RawPacket packet = sr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.sender_report()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.sender_report()->Ssrc()); + EXPECT_EQ(2, parser.report_block()->num_packets()); + EXPECT_EQ(1, parser.report_blocks_per_ssrc(kRemoteSsrc)); + EXPECT_EQ(1, parser.report_blocks_per_ssrc(kRemoteSsrc + 1)); +} + +TEST(RtcpPacketTest, Fir) { + Fir fir; + fir.From(kSenderSsrc); + fir.To(kRemoteSsrc); + fir.WithCommandSeqNum(123); + + RawPacket packet = fir.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.fir()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.fir()->Ssrc()); + EXPECT_EQ(1, parser.fir_item()->num_packets()); + EXPECT_EQ(kRemoteSsrc, parser.fir_item()->Ssrc()); + EXPECT_EQ(123U, parser.fir_item()->SeqNum()); +} + +TEST(RtcpPacketTest, AppendPacket) { + Fir fir; + ReportBlock rb; + ReceiverReport rr; + rr.From(kSenderSsrc); + rr.WithReportBlock(&rb); + rr.Append(&fir); + + RawPacket packet = rr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.receiver_report()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.receiver_report()->Ssrc()); + EXPECT_EQ(1, parser.report_block()->num_packets()); + EXPECT_EQ(1, parser.fir()->num_packets()); +} + +TEST(RtcpPacketTest, AppendPacketOnEmpty) { + Empty empty; + ReceiverReport rr; + rr.From(kSenderSsrc); + empty.Append(&rr); + + RawPacket packet = empty.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.receiver_report()->num_packets()); + EXPECT_EQ(0, parser.report_block()->num_packets()); +} + +TEST(RtcpPacketTest, AppendPacketWithOwnAppendedPacket) { + Fir fir; + Bye bye; + ReportBlock rb; + + ReceiverReport rr; + rr.WithReportBlock(&rb); + rr.Append(&fir); + + SenderReport sr; + sr.Append(&bye); + sr.Append(&rr); + + RawPacket packet = sr.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.sender_report()->num_packets()); + EXPECT_EQ(1, parser.receiver_report()->num_packets()); + EXPECT_EQ(1, parser.report_block()->num_packets()); + EXPECT_EQ(1, parser.bye()->num_packets()); + EXPECT_EQ(1, parser.fir()->num_packets()); +} + +TEST(RtcpPacketTest, Bye) { + Bye bye; + bye.From(kSenderSsrc); + + RawPacket packet = bye.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.bye()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.bye()->Ssrc()); +} + +TEST(RtcpPacketTest, ByeWithCsrcs) { + Fir fir; + Bye bye; + bye.From(kSenderSsrc); + bye.WithCsrc(0x22222222); + bye.WithCsrc(0x33333333); + bye.Append(&fir); + + RawPacket packet = bye.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.bye()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.bye()->Ssrc()); + EXPECT_EQ(1, parser.fir()->num_packets()); +} + +TEST(RtcpPacketTest, BuildWithInputBuffer) { + Fir fir; + ReportBlock rb; + ReceiverReport rr; + rr.From(kSenderSsrc); + rr.WithReportBlock(&rb); + rr.Append(&fir); + + const uint16_t kRrLength = 8; + const uint16_t kReportBlockLength = 24; + const uint16_t kFirLength = 20; + + uint16_t len = 0; + uint8_t packet[kRrLength + kReportBlockLength + kFirLength]; + rr.Build(packet, &len, kRrLength + kReportBlockLength + kFirLength); + + RtcpPacketParser parser; + parser.Parse(packet, len); + EXPECT_EQ(1, parser.receiver_report()->num_packets()); + EXPECT_EQ(1, parser.report_block()->num_packets()); + EXPECT_EQ(1, parser.fir()->num_packets()); +} + +TEST(RtcpPacketTest, BuildWithTooSmallBuffer) { + ReportBlock rb; + ReceiverReport rr; + rr.From(kSenderSsrc); + rr.WithReportBlock(&rb); + + const uint16_t kRrLength = 8; + const uint16_t kReportBlockLength = 24; + + // No packet. + uint16_t len = 0; + uint8_t packet[kRrLength + kReportBlockLength - 1]; + rr.Build(packet, &len, kRrLength + kReportBlockLength - 1); + RtcpPacketParser parser; + parser.Parse(packet, len); + EXPECT_EQ(0, len); +} + +TEST(RtcpPacketTest, BuildWithTooSmallBuffer_LastBlockFits) { + Fir fir; + ReportBlock rb; + ReceiverReport rr; + rr.From(kSenderSsrc); + rr.WithReportBlock(&rb); + rr.Append(&fir); + + const uint16_t kRrLength = 8; + const uint16_t kReportBlockLength = 24; + + uint16_t len = 0; + uint8_t packet[kRrLength + kReportBlockLength - 1]; + rr.Build(packet, &len, kRrLength + kReportBlockLength - 1); + RtcpPacketParser parser; + parser.Parse(packet, len); + EXPECT_EQ(0, parser.receiver_report()->num_packets()); + EXPECT_EQ(0, parser.report_block()->num_packets()); + EXPECT_EQ(1, parser.fir()->num_packets()); +} +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp.gypi b/webrtc/modules/rtp_rtcp/source/rtp_rtcp.gypi index 070845bc7..0a8c901e5 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp.gypi +++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp.gypi @@ -36,6 +36,8 @@ 'rtp_rtcp_config.h', 'rtp_rtcp_impl.cc', 'rtp_rtcp_impl.h', + 'rtcp_packet.cc', + 'rtcp_packet.h', 'rtcp_receiver.cc', 'rtcp_receiver.h', 'rtcp_receiver_help.cc', diff --git a/webrtc/test/rtcp_packet_parser.cc b/webrtc/test/rtcp_packet_parser.cc new file mode 100644 index 000000000..2e5880e2b --- /dev/null +++ b/webrtc/test/rtcp_packet_parser.cc @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/test/rtcp_packet_parser.h" + +namespace webrtc { +namespace test { + +RtcpPacketParser::RtcpPacketParser() {} + +RtcpPacketParser::~RtcpPacketParser() {} + +void RtcpPacketParser::Parse(const void *data, int len) { + const uint8_t* packet = static_cast(data); + RTCPUtility::RTCPParserV2 parser(packet, len, true); + for (RTCPUtility::RTCPPacketTypes type = parser.Begin(); + type != RTCPUtility::kRtcpNotValidCode; + type = parser.Iterate()) { + if (type == RTCPUtility::kRtcpSrCode) { + sender_report_.Set(parser.Packet().SR); + } else if (type == RTCPUtility::kRtcpRrCode) { + receiver_report_.Set(parser.Packet().RR); + } else if (type == RTCPUtility::kRtcpByeCode) { + bye_.Set(parser.Packet().BYE); + } else if (type == RTCPUtility::kRtcpReportBlockItemCode) { + report_block_.Set(parser.Packet().ReportBlockItem); + ++report_blocks_per_ssrc_[parser.Packet().ReportBlockItem.SSRC]; + } else if (type == RTCPUtility::kRtcpPsfbFirCode) { + fir_.Set(parser.Packet().FIR); + } else if (type == webrtc::RTCPUtility::kRtcpPsfbFirItemCode) { + fir_item_.Set(parser.Packet().FIRItem); + } else if (type == RTCPUtility::kRtcpRtpfbNackCode) { + nack_.Set(parser.Packet().NACK); + } else if (type == RTCPUtility::kRtcpRtpfbNackItemCode) { + nack_item_.Set(parser.Packet().NACKItem); + } + } +} +} // namespace test +} // namespace webrtc diff --git a/webrtc/test/rtcp_packet_parser.h b/webrtc/test/rtcp_packet_parser.h new file mode 100644 index 000000000..a9e650cf2 --- /dev/null +++ b/webrtc/test/rtcp_packet_parser.h @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + * + */ + +#ifndef WEBRTC_TEST_RTCP_PACKET_PARSER_H_ +#define WEBRTC_TEST_RTCP_PACKET_PARSER_H_ + +#include +#include + +#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" +#include "webrtc/typedefs.h" + +namespace webrtc { +namespace test { + +class RtcpPacketParser; + +class SenderReport { + public: + SenderReport() : num_packets_(0) {} + ~SenderReport() {} + + int num_packets() { return num_packets_; } + uint32_t Ssrc() { return sr_.SenderSSRC; } + uint32_t NtpSec() { return sr_.NTPMostSignificant; } + uint32_t NtpFrac() { return sr_.NTPLeastSignificant; } + uint32_t RtpTimestamp() { return sr_.RTPTimestamp; } + uint32_t PacketCount() { return sr_.SenderPacketCount; } + uint32_t OctetCount() { return sr_.SenderOctetCount; } + + private: + friend class RtcpPacketParser; + void Set(const RTCPUtility::RTCPPacketSR& sr) { + sr_ = sr; + ++num_packets_; + } + + int num_packets_; + RTCPUtility::RTCPPacketSR sr_; +}; + +class ReceiverReport { + public: + ReceiverReport() : num_packets_(0) {} + ~ReceiverReport() {} + + int num_packets() { return num_packets_; } + uint32_t Ssrc() { return rr_.SenderSSRC; } + + private: + friend class RtcpPacketParser; + void Set(const RTCPUtility::RTCPPacketRR& rr) { + rr_ = rr; + ++num_packets_; + } + + int num_packets_; + RTCPUtility::RTCPPacketRR rr_; +}; + +class ReportBlock { + public: + ReportBlock() : num_packets_(0) {} + ~ReportBlock() {} + + int num_packets() { return num_packets_; } + uint32_t Ssrc() { return rb_.SSRC; } + uint8_t FractionLost() { return rb_.FractionLost; } + uint32_t CumPacketLost() { return rb_.CumulativeNumOfPacketsLost; } + uint32_t ExtHighestSeqNum() { return rb_.ExtendedHighestSequenceNumber; } + uint32_t Jitter() { return rb_.Jitter; } + uint32_t LastSr() { return rb_.LastSR; } + uint32_t DelayLastSr() { return rb_.DelayLastSR; } + + private: + friend class RtcpPacketParser; + void Set(const RTCPUtility::RTCPPacketReportBlockItem& rb) { + rb_ = rb; + ++num_packets_; + } + + int num_packets_; + RTCPUtility::RTCPPacketReportBlockItem rb_; +}; + +class Bye { + public: + Bye() : num_packets_(0) {} + ~Bye() {} + + int num_packets() { return num_packets_; } + uint32_t Ssrc() { return bye_.SenderSSRC; } + + private: + friend class RtcpPacketParser; + void Set(const RTCPUtility::RTCPPacketBYE& bye) { + bye_ = bye; + ++num_packets_; + } + + int num_packets_; + RTCPUtility::RTCPPacketBYE bye_; +}; + +class Fir { + public: + Fir() : num_packets_(0) {} + ~Fir() {} + + int num_packets() { return num_packets_; } + uint32_t Ssrc() { return fir_.SenderSSRC; } + + private: + friend class RtcpPacketParser; + void Set(const RTCPUtility::RTCPPacketPSFBFIR& fir) { + fir_ = fir; + ++num_packets_; + } + + int num_packets_; + RTCPUtility::RTCPPacketPSFBFIR fir_; +}; + +class FirItem { + public: + FirItem() : num_packets_(0) {} + ~FirItem() {} + + int num_packets() { return num_packets_; } + uint32_t Ssrc() { return fir_item_.SSRC; } + uint8_t SeqNum() { return fir_item_.CommandSequenceNumber; } + + private: + friend class RtcpPacketParser; + void Set(const RTCPUtility::RTCPPacketPSFBFIRItem& fir_item) { + fir_item_ = fir_item; + ++num_packets_; + } + + int num_packets_; + RTCPUtility::RTCPPacketPSFBFIRItem fir_item_; +}; + +class Nack { + public: + Nack() : num_packets_(0) {} + ~Nack() {} + + int num_packets() { return num_packets_; } + uint32_t Ssrc() { return nack_.SenderSSRC; } + uint32_t MediaSsrc() { return nack_.MediaSSRC; } + + private: + friend class RtcpPacketParser; + void Set(const RTCPUtility::RTCPPacketRTPFBNACK& nack) { + nack_ = nack; + ++num_packets_; + } + + int num_packets_; + RTCPUtility::RTCPPacketRTPFBNACK nack_; +}; + +class NackItem { + public: + NackItem() : num_packets_(0) {} + ~NackItem() {} + + int num_packets() { return num_packets_; } + std::vector last_nack_list() { + assert(num_packets_ > 0); + return last_nack_list_; + } + + private: + friend class RtcpPacketParser; + void Set(const RTCPUtility::RTCPPacketRTPFBNACKItem& nack_item) { + last_nack_list_.clear(); + last_nack_list_.push_back(nack_item.PacketID); + for (int i = 0; i < 16; ++i) { + if (nack_item.BitMask & (1 << i)) { + last_nack_list_.push_back(nack_item.PacketID + i + 1); + } + } + ++num_packets_; + } + + int num_packets_; + std::vector last_nack_list_; +}; + + +class RtcpPacketParser { + public: + RtcpPacketParser(); + ~RtcpPacketParser(); + + void Parse(const void *packet, int packet_len); + + SenderReport* sender_report() { return &sender_report_; } + ReceiverReport* receiver_report() { return &receiver_report_; } + ReportBlock* report_block() { return &report_block_; } + Bye* bye() { return &bye_; } + Fir* fir() { return &fir_; } + FirItem* fir_item() { return &fir_item_; } + Nack* nack() { return &nack_; } + NackItem* nack_item() { return &nack_item_; } + + int report_blocks_per_ssrc(uint32_t ssrc) { + return report_blocks_per_ssrc_[ssrc]; + } + + private: + SenderReport sender_report_; + ReceiverReport receiver_report_; + ReportBlock report_block_; + Bye bye_; + Fir fir_; + FirItem fir_item_; + Nack nack_; + NackItem nack_item_; + + std::map report_blocks_per_ssrc_; +}; +} // namespace test +} // namespace webrtc +#endif // WEBRTC_TEST_RTCP_PACKET_PARSER_H_ diff --git a/webrtc/test/test.gyp b/webrtc/test/test.gyp index 0051bcee9..e920d6ea6 100644 --- a/webrtc/test/test.gyp +++ b/webrtc/test/test.gyp @@ -52,6 +52,17 @@ '<(webrtc_root)/common_video/common_video.gyp:common_video', ], }, + { + 'target_name': 'rtcp_packet_parser', + 'type': 'static_library', + 'sources': [ + 'rtcp_packet_parser.cc', + 'rtcp_packet_parser.h', + ], + 'dependencies': [ + '<(webrtc_root)/modules/modules.gyp:rtp_rtcp', + ], + }, { 'target_name': 'test_support', 'type': 'static_library',