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/webrtc@5592 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/modules/modules.gyp b/modules/modules.gyp
index d1c0bd1..f18551c 100644
--- a/modules/modules.gyp
+++ b/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/modules/rtp_rtcp/source/rtcp_packet.cc b/modules/rtp_rtcp/source/rtcp_packet.cc
new file mode 100644
index 0000000..0c3197f
--- /dev/null
+++ b/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<uint32_t>& 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<uint32_t>::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<ReportBlock*>& report_blocks,
+ uint8_t* buffer,
+ uint16_t* pos) {
+ for (std::vector<ReportBlock*>::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<RtcpPacket*>::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/modules/rtp_rtcp/source/rtcp_packet.h b/modules/rtp_rtcp/source/rtcp_packet.h
new file mode 100644
index 0000000..13ad808
--- /dev/null
+++ b/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 <vector>
+
+#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<RtcpPacket*> 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<ReportBlock*> 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<ReportBlock*> 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<uint32_t> 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/modules/rtp_rtcp/source/rtcp_packet_unittest.cc b/modules/rtp_rtcp/source/rtcp_packet_unittest.cc
new file mode 100644
index 0000000..0114acd
--- /dev/null
+++ b/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/modules/rtp_rtcp/source/rtp_rtcp.gypi b/modules/rtp_rtcp/source/rtp_rtcp.gypi
index 070845b..0a8c901 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp.gypi
+++ b/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/test/rtcp_packet_parser.cc b/test/rtcp_packet_parser.cc
new file mode 100644
index 0000000..2e5880e
--- /dev/null
+++ b/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<const uint8_t*>(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/test/rtcp_packet_parser.h b/test/rtcp_packet_parser.h
new file mode 100644
index 0000000..a9e650c
--- /dev/null
+++ b/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 <map>
+#include <vector>
+
+#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<uint16_t> 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<uint16_t> 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<uint32_t, int> report_blocks_per_ssrc_;
+};
+} // namespace test
+} // namespace webrtc
+#endif // WEBRTC_TEST_RTCP_PACKET_PARSER_H_
diff --git a/test/test.gyp b/test/test.gyp
index 0051bce..e920d6e 100644
--- a/test/test.gyp
+++ b/test/test.gyp
@@ -53,6 +53,17 @@
],
},
{
+ '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',
'dependencies': [