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': [