Add NACK and RPSI packet types to RTCP packet builder.
Fixes bug found when parsing received RPSI packet.

BUG=2450
R=mflodman@webrtc.org, stefan@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/17419004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@6194 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet.cc
index a127bc1..51c86aa 100644
--- a/webrtc/modules/rtp_rtcp/source/rtcp_packet.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet.cc
@@ -13,9 +13,28 @@
 #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
 #include "webrtc/system_wrappers/interface/logging.h"
 
+using webrtc::RTCPUtility::PT_BYE;
+using webrtc::RTCPUtility::PT_PSFB;
+using webrtc::RTCPUtility::PT_RR;
+using webrtc::RTCPUtility::PT_RTPFB;
+using webrtc::RTCPUtility::PT_SR;
+
+using webrtc::RTCPUtility::RTCPPacketBYE;
+using webrtc::RTCPUtility::RTCPPacketPSFBFIR;
+using webrtc::RTCPUtility::RTCPPacketPSFBFIRItem;
+using webrtc::RTCPUtility::RTCPPacketPSFBRPSI;
+using webrtc::RTCPUtility::RTCPPacketReportBlockItem;
+using webrtc::RTCPUtility::RTCPPacketRR;
+using webrtc::RTCPUtility::RTCPPacketRTPFBNACK;
+using webrtc::RTCPUtility::RTCPPacketRTPFBNACKItem;
+using webrtc::RTCPUtility::RTCPPacketSR;
+
 namespace webrtc {
 namespace rtcp {
 namespace {
+// Unused SSRC of media source, set to 0.
+const uint32_t kUnusedMediaSourceSsrc0 = 0;
+
 void AssignUWord8(uint8_t* buffer, uint16_t* offset, uint8_t value) {
   buffer[(*offset)++] = value;
 }
@@ -32,9 +51,34 @@
   *offset += 4;
 }
 
+uint16_t BlockToHeaderLength(uint16_t length_in_bytes) {
+  // Length in 32-bit words minus 1.
+  assert(length_in_bytes > 0);
+  assert(length_in_bytes % 4 == 0);
+  return (length_in_bytes / 4) - 1;
+}
+
 // From RFC 3550, RTP: A Transport Protocol for Real-Time Applications.
 //
-//  Sender report
+// RTP header 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| RC/FMT  |      PT       |             length            |
+//  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+void CreateHeader(uint8_t count_or_format,  // Depends on packet type.
+                  uint8_t packet_type,
+                  uint16_t length,
+                  uint8_t* buffer,
+                  uint16_t* pos) {
+  const uint8_t kVersion = 2;
+  AssignUWord8(buffer, pos, (kVersion << 6) + count_or_format);
+  AssignUWord8(buffer, pos, packet_type);
+  AssignUWord16(buffer, pos, length);
+}
+
+//  Sender report (SR) (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
 //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -53,13 +97,11 @@
 //  |                      sender's octet count                     |
 //  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
 
-void CreateSenderReport(const RTCPUtility::RTCPPacketSR& sr,
+void CreateSenderReport(const RTCPPacketSR& sr,
+                        uint16_t length,
                         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);
+  CreateHeader(sr.NumberOfReportBlocks, PT_SR, length, buffer, pos);
   AssignUWord32(buffer, pos, sr.SenderSSRC);
   AssignUWord32(buffer, pos, sr.NTPMostSignificant);
   AssignUWord32(buffer, pos, sr.NTPLeastSignificant);
@@ -68,7 +110,7 @@
   AssignUWord32(buffer, pos, sr.SenderOctetCount);
 }
 
-//  Receiver report, header (RFC 3550).
+//  Receiver report (RR), 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
 //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -77,13 +119,11 @@
 //  |                     SSRC of packet sender                     |
 //  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
 
-void CreateReceiverReport(const RTCPUtility::RTCPPacketRR& rr,
+void CreateReceiverReport(const RTCPPacketRR& rr,
+                          uint16_t length,
                           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);
+  CreateHeader(rr.NumberOfReportBlocks, PT_RR, length, buffer, pos);
   AssignUWord32(buffer, pos, rr.SenderSSRC);
 }
 
@@ -104,10 +144,9 @@
 //  |                   delay since last SR (DLSR)                  |
 //  +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
 
-void CreateReportBlock(
-    const RTCPUtility::RTCPPacketReportBlockItem& report_block,
-    uint8_t* buffer,
-    uint16_t* pos) {
+void CreateReportBlock(const 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);
@@ -130,14 +169,12 @@
 // (opt) |     length    |               reason for leaving            ...
 //       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
-void CreateBye(const RTCPUtility::RTCPPacketBYE& bye,
+void CreateBye(const RTCPPacketBYE& bye,
                const std::vector<uint32_t>& csrcs,
+               uint16_t length,
                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);
+  CreateHeader(length, PT_BYE, length, buffer, pos);
   AssignUWord32(buffer, pos, bye.SenderSSRC);
   for (std::vector<uint32_t>::const_iterator it = csrcs.begin();
        it != csrcs.end(); ++it) {
@@ -161,6 +198,64 @@
 //   :            Feedback Control Information (FCI)                 :
 //   :
 //
+
+// Generic NACK (RFC 4585).
+//
+// 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
+//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//   |            PID                |             BLP               |
+//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+void CreateNack(const RTCPPacketRTPFBNACK& nack,
+                const std::vector<RTCPPacketRTPFBNACKItem>& nack_fields,
+                uint16_t length,
+                uint8_t* buffer,
+                uint16_t* pos) {
+  const uint8_t kFmt = 1;
+  CreateHeader(kFmt, PT_RTPFB, length, buffer, pos);
+  AssignUWord32(buffer, pos, nack.SenderSSRC);
+  AssignUWord32(buffer, pos, nack.MediaSSRC);
+  for (std::vector<RTCPPacketRTPFBNACKItem>::const_iterator
+      it = nack_fields.begin(); it != nack_fields.end(); ++it) {
+    AssignUWord16(buffer, pos, (*it).PacketID);
+    AssignUWord16(buffer, pos, (*it).BitMask);
+  }
+}
+
+// Reference picture selection indication (RPSI) (RFC 4585).
+//
+// 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
+//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//   |      PB       |0| Payload Type|    Native RPSI bit string     |
+//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//   |   defined per codec          ...                | Padding (0) |
+//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+void CreateRpsi(const RTCPPacketPSFBRPSI& rpsi,
+                uint8_t padding_bytes,
+                uint16_t length,
+                uint8_t* buffer,
+                uint16_t* pos) {
+  // Native bit string should be a multiple of 8 bits.
+  assert(rpsi.NumberOfValidBits % 8 == 0);
+  const uint8_t kFmt = 3;
+  CreateHeader(kFmt, PT_PSFB, length, buffer, pos);
+  AssignUWord32(buffer, pos, rpsi.SenderSSRC);
+  AssignUWord32(buffer, pos, rpsi.MediaSSRC);
+  AssignUWord8(buffer, pos, padding_bytes * 8);
+  AssignUWord8(buffer, pos, rpsi.PayloadType);
+  memcpy(buffer + *pos, rpsi.NativeBitString, rpsi.NumberOfValidBits / 8);
+  *pos += rpsi.NumberOfValidBits / 8;
+  memset(buffer + *pos, 0, padding_bytes);
+  *pos += padding_bytes;
+}
+
 // Full intra request (FIR) (RFC 5104).
 //
 // FCI:
@@ -173,27 +268,26 @@
 //   | 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;
+void CreateFir(const RTCPPacketPSFBFIR& fir,
+               const RTCPPacketPSFBFIRItem& fir_item,
+               uint16_t length,
+               uint8_t* buffer,
+               uint16_t* pos) {
   const uint8_t kFmt = 4;
-  AssignUWord8(buffer, pos, 0x80 + kFmt);
-  AssignUWord8(buffer, pos, RTCPUtility::PT_PSFB);
-  AssignUWord16(buffer, pos, kLength);
+  CreateHeader(kFmt, PT_PSFB, length, buffer, pos);
   AssignUWord32(buffer, pos, fir.SenderSSRC);
-  AssignUWord32(buffer, pos, 0);
+  AssignUWord32(buffer, pos, kUnusedMediaSourceSsrc0);
   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) {
+template <typename T>
+void AppendBlocks(const std::vector<T*>& blocks,
+                  uint8_t* buffer,
+                  uint16_t* pos) {
+  for (typename std::vector<T*>::const_iterator it = blocks.begin();
+       it != blocks.end(); ++it) {
     (*it)->Create(buffer, pos);
   }
 }
@@ -236,8 +330,8 @@
     LOG(LS_WARNING) << "Max packet size reached.";
     return;
   }
-  CreateSenderReport(sr_, packet, len);
-  AppendReportBlocks(report_blocks_, packet, len);
+  CreateSenderReport(sr_, BlockToHeaderLength(Length()), packet, len);
+  AppendBlocks(report_blocks_, packet, len);
 }
 
 void SenderReport::WithReportBlock(ReportBlock* block) {
@@ -257,8 +351,8 @@
     LOG(LS_WARNING) << "Max packet size reached.";
     return;
   }
-  CreateReceiverReport(rr_, packet, len);
-  AppendReportBlocks(report_blocks_, packet, len);
+  CreateReceiverReport(rr_, BlockToHeaderLength(Length()), packet, len);
+  AppendBlocks(report_blocks_, packet, len);
 }
 
 void ReceiverReport::WithReportBlock(ReportBlock* block) {
@@ -271,12 +365,16 @@
   rr_.NumberOfReportBlocks = report_blocks_.size();
 }
 
+void ReportBlock::Create(uint8_t* packet, uint16_t* len) const {
+  CreateReportBlock(report_block_, packet, len);
+}
+
 void Bye::Create(uint8_t* packet, uint16_t* len, uint16_t max_len) const {
   if (*len + Length() > max_len) {
     LOG(LS_WARNING) << "Max packet size reached.";
     return;
   }
-  CreateBye(bye_, csrcs_, packet, len);
+  CreateBye(bye_, csrcs_, BlockToHeaderLength(Length()), packet, len);
 }
 
 void Bye::WithCsrc(uint32_t csrc) {
@@ -287,16 +385,81 @@
   csrcs_.push_back(csrc);
 }
 
+void Nack::Create(uint8_t* packet, uint16_t* len, uint16_t max_len) const {
+  assert(!nack_fields_.empty());
+  if (*len + Length() > max_len) {
+    LOG(LS_WARNING) << "Max packet size reached.";
+    return;
+  }
+  CreateNack(nack_, nack_fields_, BlockToHeaderLength(Length()), packet, len);
+}
+
+void Nack::WithList(const uint16_t* nack_list, int length) {
+  assert(nack_list);
+  assert(nack_fields_.empty());
+  int i = 0;
+  while (i < length) {
+    uint16_t pid = nack_list[i++];
+    // Bitmask specifies losses in any of the 16 packets following the pid.
+    uint16_t bitmask = 0;
+    while (i < length) {
+      int shift = static_cast<uint16_t>(nack_list[i] - pid) - 1;
+      if (shift >= 0 && shift <= 15) {
+        bitmask |= (1 << shift);
+        ++i;
+      } else {
+        break;
+      }
+    }
+    RTCPUtility::RTCPPacketRTPFBNACKItem item;
+    item.PacketID = pid;
+    item.BitMask = bitmask;
+    nack_fields_.push_back(item);
+  }
+}
+
+void Rpsi::Create(uint8_t* packet, uint16_t* len, uint16_t max_len) const {
+  assert(rpsi_.NumberOfValidBits > 0);
+  if (*len + Length() > max_len) {
+    LOG(LS_WARNING) << "Max packet size reached.";
+    return;
+  }
+  CreateRpsi(rpsi_, padding_bytes_, BlockToHeaderLength(Length()), packet, len);
+}
+
+void Rpsi::WithPictureId(uint64_t picture_id) {
+  const uint32_t kPidBits = 7;
+  const uint64_t k7MsbZeroMask = 0x1ffffffffffffff;
+  uint8_t required_bytes = 0;
+  uint64_t shifted_pid = picture_id;
+  do {
+    ++required_bytes;
+    shifted_pid = (shifted_pid >> kPidBits) & k7MsbZeroMask;
+  } while (shifted_pid > 0);
+
+  // Convert picture id to native bit string (natively defined by the video
+  // codec).
+  int pos = 0;
+  for (int i = required_bytes - 1; i > 0; i--) {
+    rpsi_.NativeBitString[pos++] =
+        0x80 | static_cast<uint8_t>(picture_id >> (i * kPidBits));
+  }
+  rpsi_.NativeBitString[pos++] = static_cast<uint8_t>(picture_id & 0x7f);
+  rpsi_.NumberOfValidBits = pos * 8;
+
+  // Calculate padding bytes (to reach next 32-bit boundary, 1, 2 or 3 bytes).
+  padding_bytes_ = 4 - ((2 + required_bytes) % 4);
+  if (padding_bytes_ == 4) {
+    padding_bytes_ = 0;
+  }
+}
+
 void Fir::Create(uint8_t* packet, uint16_t* len, uint16_t max_len) const {
   if (*len + Length() > max_len) {
     LOG(LS_WARNING) << "Max packet size reached.";
     return;
   }
-  CreateFirRequest(fir_, fir_item_, packet, len);
-}
-
-void ReportBlock::Create(uint8_t* packet, uint16_t* len) const {
-  CreateReportBlock(report_block_, packet, len);
+  CreateFir(fir_, fir_item_, BlockToHeaderLength(Length()), 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
index 13ad808..641bd7f 100644
--- a/webrtc/modules/rtp_rtcp/source/rtcp_packet.h
+++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet.h
@@ -244,7 +244,7 @@
     report_block_.DelayLastSR = delay_last_sr;
   }
 
-  void Create(uint8_t* array, uint16_t* cur_pos) const;
+  void Create(uint8_t* packet, uint16_t* len) const;
 
  private:
   RTCPUtility::RTCPPacketReportBlockItem report_block_;
@@ -285,7 +285,7 @@
 
  private:
   uint16_t Length() const {
-    const uint16_t kByeBlockLen = 8 + 4*csrcs_.size();
+    const uint16_t kByeBlockLen = 8 + 4 * csrcs_.size();
     return kByeBlockLen;
   }
 
@@ -309,6 +309,98 @@
 //   :            Feedback Control Information (FCI)                 :
 //   :
 
+// Generic NACK (RFC 4585).
+//
+// 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
+//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//   |            PID                |             BLP               |
+//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+class Nack : public RtcpPacket {
+ public:
+  Nack()
+    : RtcpPacket() {
+    memset(&nack_, 0, sizeof(nack_));
+  }
+
+  virtual ~Nack() {}
+
+  void From(uint32_t ssrc) {
+    nack_.SenderSSRC = ssrc;
+  }
+  void To(uint32_t ssrc) {
+    nack_.MediaSSRC = ssrc;
+  }
+  void WithList(const uint16_t* nack_list, int length);
+
+ protected:
+  virtual void Create(uint8_t* packet, uint16_t* len, uint16_t max_len) const
+      OVERRIDE;
+
+ private:
+  uint16_t Length() const {
+    const uint16_t kNackBlockLen = 4 * (3 + nack_fields_.size());
+    return kNackBlockLen;
+  }
+
+  RTCPUtility::RTCPPacketRTPFBNACK nack_;
+  std::vector<RTCPUtility::RTCPPacketRTPFBNACKItem> nack_fields_;
+
+  DISALLOW_COPY_AND_ASSIGN(Nack);
+};
+
+// Reference picture selection indication (RPSI) (RFC 4585).
+//
+// 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
+//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//   |      PB       |0| Payload Type|    Native RPSI bit string     |
+//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//   |   defined per codec          ...                | Padding (0) |
+//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+class Rpsi : public RtcpPacket {
+ public:
+  Rpsi()
+    : RtcpPacket(),
+      padding_bytes_(0) {
+    memset(&rpsi_, 0, sizeof(rpsi_));
+  }
+
+  virtual ~Rpsi() {}
+
+  void From(uint32_t ssrc) {
+    rpsi_.SenderSSRC = ssrc;
+  }
+  void To(uint32_t ssrc) {
+    rpsi_.MediaSSRC = ssrc;
+  }
+  void WithPayloadType(uint8_t payload) {
+    assert(payload <= 0x7f);
+    rpsi_.PayloadType = payload;
+  }
+  void WithPictureId(uint64_t picture_id);
+
+ protected:
+  virtual void Create(uint8_t* packet, uint16_t* len, uint16_t max_len) const
+      OVERRIDE;
+
+ private:
+  uint16_t Length() const {
+    const uint16_t kRpsiBlockLen =
+        12 + 2 + (rpsi_.NumberOfValidBits / 8) + padding_bytes_;
+    return kRpsiBlockLen;
+  }
+
+  uint8_t padding_bytes_;
+  RTCPUtility::RTCPPacketPSFBRPSI rpsi_;
+
+  DISALLOW_COPY_AND_ASSIGN(Rpsi);
+};
 
 // Full intra request (FIR) (RFC 5104).
 //
diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc
index 0114acd..d7ae914 100644
--- a/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc
@@ -18,11 +18,13 @@
 using webrtc::rtcp::Bye;
 using webrtc::rtcp::Empty;
 using webrtc::rtcp::Fir;
-using webrtc::rtcp::SenderReport;
+using webrtc::rtcp::Nack;
 using webrtc::rtcp::RawPacket;
 using webrtc::rtcp::ReceiverReport;
 using webrtc::rtcp::ReportBlock;
+using webrtc::rtcp::Rpsi;
 using webrtc::test::RtcpPacketParser;
+using webrtc::rtcp::SenderReport;
 
 namespace webrtc {
 
@@ -152,6 +154,121 @@
   EXPECT_EQ(1, parser.report_blocks_per_ssrc(kRemoteSsrc + 1));
 }
 
+TEST(RtcpPacketTest, Nack) {
+  Nack nack;
+  const uint16_t kList[] = {0, 1, 3, 8, 16};
+  const uint16_t kListLength = sizeof(kList) / sizeof(kList[0]);
+  nack.From(kSenderSsrc);
+  nack.To(kRemoteSsrc);
+  nack.WithList(kList, kListLength);
+  RawPacket packet = nack.Build();
+  RtcpPacketParser parser;
+  parser.Parse(packet.buffer(), packet.buffer_length());
+  EXPECT_EQ(1, parser.nack()->num_packets());
+  EXPECT_EQ(kSenderSsrc, parser.nack()->Ssrc());
+  EXPECT_EQ(kRemoteSsrc, parser.nack()->MediaSsrc());
+  EXPECT_EQ(1, parser.nack_item()->num_packets());
+  std::vector<uint16_t> seqs = parser.nack_item()->last_nack_list();
+  EXPECT_EQ(kListLength, seqs.size());
+  for (size_t i = 0; i < kListLength; ++i) {
+    EXPECT_EQ(kList[i], seqs[i]);
+  }
+}
+
+TEST(RtcpPacketTest, NackWithWrap) {
+  Nack nack;
+  const uint16_t kList[] = {65500, 65516, 65534, 65535, 0, 1, 3, 20, 100};
+  const uint16_t kListLength = sizeof(kList) / sizeof(kList[0]);
+  nack.From(kSenderSsrc);
+  nack.To(kRemoteSsrc);
+  nack.WithList(kList, kListLength);
+  RawPacket packet = nack.Build();
+  RtcpPacketParser parser;
+  parser.Parse(packet.buffer(), packet.buffer_length());
+  EXPECT_EQ(1, parser.nack()->num_packets());
+  EXPECT_EQ(kSenderSsrc, parser.nack()->Ssrc());
+  EXPECT_EQ(kRemoteSsrc, parser.nack()->MediaSsrc());
+  EXPECT_EQ(4, parser.nack_item()->num_packets());
+  std::vector<uint16_t> seqs = parser.nack_item()->last_nack_list();
+  EXPECT_EQ(kListLength, seqs.size());
+  for (size_t i = 0; i < kListLength; ++i) {
+    EXPECT_EQ(kList[i], seqs[i]);
+  }
+}
+
+TEST(RtcpPacketTest, Rpsi) {
+  Rpsi rpsi;
+  // 1000001 (7 bits = 1 byte in native string).
+  const uint64_t kPictureId = 0x41;
+  const uint16_t kNumberOfValidBytes = 1;
+  rpsi.WithPayloadType(100);
+  rpsi.WithPictureId(kPictureId);
+
+  RawPacket packet = rpsi.Build();
+  RtcpPacketParser parser;
+  parser.Parse(packet.buffer(), packet.buffer_length());
+  EXPECT_EQ(100, parser.rpsi()->PayloadType());
+  EXPECT_EQ(kNumberOfValidBytes * 8, parser.rpsi()->NumberOfValidBits());
+  EXPECT_EQ(kPictureId, parser.rpsi()->PictureId());
+}
+
+TEST(RtcpPacketTest, RpsiWithTwoByteNativeString) {
+  Rpsi rpsi;
+  // |1 0000001 (7 bits = 1 byte in native string).
+  const uint64_t kPictureId = 0x81;
+  const uint16_t kNumberOfValidBytes = 2;
+  rpsi.WithPictureId(kPictureId);
+
+  RawPacket packet = rpsi.Build();
+  RtcpPacketParser parser;
+  parser.Parse(packet.buffer(), packet.buffer_length());
+  EXPECT_EQ(kNumberOfValidBytes * 8, parser.rpsi()->NumberOfValidBits());
+  EXPECT_EQ(kPictureId, parser.rpsi()->PictureId());
+}
+
+TEST(RtcpPacketTest, RpsiWithThreeByteNativeString) {
+  Rpsi rpsi;
+  // 10000|00 100000|0 1000000 (7 bits = 1 byte in native string).
+  const uint64_t kPictureId = 0x102040;
+  const uint16_t kNumberOfValidBytes = 3;
+  rpsi.WithPictureId(kPictureId);
+
+  RawPacket packet = rpsi.Build();
+  RtcpPacketParser parser;
+  parser.Parse(packet.buffer(), packet.buffer_length());
+  EXPECT_EQ(kNumberOfValidBytes * 8, parser.rpsi()->NumberOfValidBits());
+  EXPECT_EQ(kPictureId, parser.rpsi()->PictureId());
+}
+
+TEST(RtcpPacketTest, RpsiWithFourByteNativeString) {
+  Rpsi rpsi;
+  // 1000|001 00001|01 100001|1 1000010 (7 bits = 1 byte in native string).
+  const uint64_t kPictureId = 0x84161C2;
+  const uint16_t kNumberOfValidBytes = 4;
+  rpsi.WithPictureId(kPictureId);
+
+  RawPacket packet = rpsi.Build();
+  RtcpPacketParser parser;
+  parser.Parse(packet.buffer(), packet.buffer_length());
+  EXPECT_EQ(kNumberOfValidBytes * 8, parser.rpsi()->NumberOfValidBits());
+  EXPECT_EQ(kPictureId, parser.rpsi()->PictureId());
+}
+
+TEST(RtcpPacketTest, RpsiWithMaxPictureId) {
+  Rpsi rpsi;
+  // 1 1111111| 1111111 1|111111 11|11111 111|1111 1111|111 11111|
+  // 11 111111|1 1111111 (7 bits = 1 byte in native string).
+  const uint64_t kPictureId = 0xffffffffffffffff;
+  const uint16_t kNumberOfValidBytes = 10;
+  rpsi.WithPictureId(kPictureId);
+
+  RawPacket packet = rpsi.Build();
+  RtcpPacketParser parser;
+  parser.Parse(packet.buffer(), packet.buffer_length());
+  EXPECT_EQ(kNumberOfValidBytes * 8, parser.rpsi()->NumberOfValidBits());
+  EXPECT_EQ(kPictureId, parser.rpsi()->PictureId());
+}
+
 TEST(RtcpPacketTest, Fir) {
   Fir fir;
   fir.From(kSenderSsrc);
diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_utility.cc b/webrtc/modules/rtp_rtcp/source/rtcp_utility.cc
index 705a38b..9acab73 100644
--- a/webrtc/modules/rtp_rtcp/source/rtcp_utility.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtcp_utility.cc
@@ -1266,31 +1266,27 @@
     }
 }
 
-bool
-RTCPUtility::RTCPParserV2::ParseRPSIItem()
-{
-    // RFC 4585 6.3.3.  Reference Picture Selection Indication (RPSI)
-    /*
-    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
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |      PB       |0| Payload Type|    Native RPSI bit string     |
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    |   defined per codec          ...                | Padding (0) |
-    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-    */
+bool RTCPUtility::RTCPParserV2::ParseRPSIItem() {
+
+    // RFC 4585 6.3.3.  Reference Picture Selection Indication (RPSI).
+    //
+    //  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
+    //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    //  |      PB       |0| Payload Type|    Native RPSI bit string     |
+    //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+    //  |   defined per codec          ...                | Padding (0) |
+    //  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
     const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData;
 
-    if (length < 4)
-    {
+    if (length < 4) {
         _state = State_TopLevel;
 
         EndCurrentBlock();
         return false;
     }
-    if(length > 2+RTCP_RPSI_DATA_SIZE)
-    {
+    if (length > 2 + RTCP_RPSI_DATA_SIZE) {
         _state = State_TopLevel;
 
         EndCurrentBlock();
@@ -1299,12 +1295,14 @@
 
     _packetType = kRtcpPsfbRpsiCode;
 
-    uint8_t paddingBits = *_ptrRTCPData++;
+    uint8_t padding_bits = *_ptrRTCPData++;
     _packet.RPSI.PayloadType = *_ptrRTCPData++;
 
-    memcpy(_packet.RPSI.NativeBitString, _ptrRTCPData, length-2);
+    memcpy(_packet.RPSI.NativeBitString, _ptrRTCPData, length - 2);
+    _ptrRTCPData += length - 2;
 
-    _packet.RPSI.NumberOfValidBits = uint16_t(length-2)*8 - paddingBits;
+    _packet.RPSI.NumberOfValidBits =
+        static_cast<uint16_t>(length - 2) * 8 - padding_bits;
     return true;
 }
 
diff --git a/webrtc/test/rtcp_packet_parser.cc b/webrtc/test/rtcp_packet_parser.cc
index 2e5880e..9ae8543 100644
--- a/webrtc/test/rtcp_packet_parser.cc
+++ b/webrtc/test/rtcp_packet_parser.cc
@@ -23,25 +23,54 @@
   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);
+    switch (type) {
+      case RTCPUtility::kRtcpSrCode:
+        sender_report_.Set(parser.Packet().SR);
+        break;
+      case RTCPUtility::kRtcpRrCode:
+        receiver_report_.Set(parser.Packet().RR);
+        break;
+      case RTCPUtility::kRtcpByeCode:
+        bye_.Set(parser.Packet().BYE);
+        break;
+      case RTCPUtility::kRtcpReportBlockItemCode:
+        report_block_.Set(parser.Packet().ReportBlockItem);
+        ++report_blocks_per_ssrc_[parser.Packet().ReportBlockItem.SSRC];
+        break;
+      case RTCPUtility::kRtcpPsfbRpsiCode:
+        rpsi_.Set(parser.Packet().RPSI);
+        break;
+      case RTCPUtility::kRtcpPsfbFirCode:
+        fir_.Set(parser.Packet().FIR);
+        break;
+      case RTCPUtility::kRtcpPsfbFirItemCode:
+        fir_item_.Set(parser.Packet().FIRItem);
+        break;
+      case RTCPUtility::kRtcpRtpfbNackCode:
+        nack_.Set(parser.Packet().NACK);
+        nack_item_.Clear();
+        break;
+      case RTCPUtility::kRtcpRtpfbNackItemCode:
+        nack_item_.Set(parser.Packet().NACKItem);
+        break;
+      default:
+        break;
     }
   }
 }
+
+uint64_t Rpsi::PictureId() const {
+  assert(num_packets_ > 0);
+  uint16_t num_bytes = rpsi_.NumberOfValidBits / 8;
+  assert(num_bytes > 0);
+  uint64_t picture_id = 0;
+  for (uint16_t i = 0; i < num_bytes - 1; ++i) {
+    picture_id += (rpsi_.NativeBitString[i] & 0x7f);
+    picture_id <<= 7;
+  }
+  picture_id += (rpsi_.NativeBitString[num_bytes - 1] & 0x7f);
+  return picture_id;
+}
+
 }  // namespace test
 }  // namespace webrtc
diff --git a/webrtc/test/rtcp_packet_parser.h b/webrtc/test/rtcp_packet_parser.h
index a9e650c..4db58c3 100644
--- a/webrtc/test/rtcp_packet_parser.h
+++ b/webrtc/test/rtcp_packet_parser.h
@@ -23,167 +23,192 @@
 
 class RtcpPacketParser;
 
-class SenderReport {
+class PacketType {
  public:
-  SenderReport() : num_packets_(0) {}
-  ~SenderReport() {}
+  virtual ~PacketType() {}
 
-  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; }
+  int num_packets() const { return num_packets_; }
+
+ protected:
+  PacketType() : num_packets_(0) {}
+
+  int num_packets_;
+};
+
+class SenderReport : public PacketType {
+ public:
+  SenderReport() {}
+  virtual ~SenderReport() {}
+
+  uint32_t Ssrc() const { return sr_.SenderSSRC; }
+  uint32_t NtpSec() const { return sr_.NTPMostSignificant; }
+  uint32_t NtpFrac() const { return sr_.NTPLeastSignificant; }
+  uint32_t RtpTimestamp() const { return sr_.RTPTimestamp; }
+  uint32_t PacketCount() const { return sr_.SenderPacketCount; }
+  uint32_t OctetCount() const { 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 {
+class ReceiverReport : public PacketType {
  public:
-  ReceiverReport() : num_packets_(0) {}
-  ~ReceiverReport() {}
+  ReceiverReport() {}
+  virtual ~ReceiverReport() {}
 
-  int num_packets() { return num_packets_; }
-  uint32_t Ssrc() { return rr_.SenderSSRC; }
+  uint32_t Ssrc() const { 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 {
+class ReportBlock : public PacketType {
  public:
-  ReportBlock() : num_packets_(0) {}
-  ~ReportBlock() {}
+  ReportBlock() {}
+  virtual ~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; }
+  uint32_t Ssrc() const { return rb_.SSRC; }
+  uint8_t FractionLost() const { return rb_.FractionLost; }
+  uint32_t CumPacketLost() const { return rb_.CumulativeNumOfPacketsLost; }
+  uint32_t ExtHighestSeqNum() const { return rb_.ExtendedHighestSequenceNumber;}
+  uint32_t Jitter() const { return rb_.Jitter; }
+  uint32_t LastSr() const { return rb_.LastSR; }
+  uint32_t DelayLastSr()const  { 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 {
+class Bye : public PacketType {
  public:
-  Bye() : num_packets_(0) {}
-  ~Bye() {}
+  Bye() {}
+  virtual ~Bye() {}
 
-  int num_packets() { return num_packets_; }
-  uint32_t Ssrc() { return bye_.SenderSSRC; }
+  uint32_t Ssrc() const { 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 {
+class Rpsi : public PacketType {
  public:
-  Fir() : num_packets_(0) {}
-  ~Fir() {}
+  Rpsi() {}
+  virtual ~Rpsi() {}
 
-  int num_packets() { return num_packets_; }
-  uint32_t Ssrc() { return fir_.SenderSSRC; }
+  uint32_t Ssrc() const { return rpsi_.SenderSSRC; }
+  uint32_t MediaSsrc() const { return rpsi_.MediaSSRC; }
+  uint8_t PayloadType() const { return rpsi_.PayloadType; }
+  uint16_t NumberOfValidBits() const { return rpsi_.NumberOfValidBits; }
+  uint64_t PictureId() const;
 
  private:
   friend class RtcpPacketParser;
+
+  void Set(const RTCPUtility::RTCPPacketPSFBRPSI& rpsi) {
+    rpsi_ = rpsi;
+    ++num_packets_;
+  }
+
+  RTCPUtility::RTCPPacketPSFBRPSI rpsi_;
+};
+
+class Fir : public PacketType {
+ public:
+  Fir() {}
+  virtual ~Fir() {}
+
+  uint32_t Ssrc() const { 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 {
+class FirItem : public PacketType {
  public:
-  FirItem() : num_packets_(0) {}
-  ~FirItem() {}
+  FirItem() {}
+  virtual ~FirItem() {}
 
-  int num_packets() { return num_packets_; }
-  uint32_t Ssrc() { return fir_item_.SSRC; }
-  uint8_t SeqNum() { return fir_item_.CommandSequenceNumber; }
+  uint32_t Ssrc() const { return fir_item_.SSRC; }
+  uint8_t SeqNum() const { 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 {
+class Nack : public PacketType {
  public:
-  Nack() : num_packets_(0) {}
-  ~Nack() {}
+  Nack() {}
+  virtual ~Nack() {}
 
-  int num_packets() { return num_packets_; }
-  uint32_t Ssrc() { return nack_.SenderSSRC; }
-  uint32_t MediaSsrc() { return nack_.MediaSSRC; }
+  uint32_t Ssrc() const { return nack_.SenderSSRC; }
+  uint32_t MediaSsrc() const { 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 {
+class NackItem : public PacketType {
  public:
-  NackItem() : num_packets_(0) {}
-  ~NackItem() {}
+  NackItem() {}
+  virtual ~NackItem() {}
 
-  int num_packets() { return num_packets_; }
-  std::vector<uint16_t> last_nack_list() {
-    assert(num_packets_ > 0);
+  std::vector<uint16_t> last_nack_list() const {
     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)) {
@@ -192,12 +217,11 @@
     }
     ++num_packets_;
   }
+  void Clear() { last_nack_list_.clear(); }
 
-  int num_packets_;
   std::vector<uint16_t> last_nack_list_;
 };
 
-
 class RtcpPacketParser {
  public:
   RtcpPacketParser();
@@ -209,6 +233,7 @@
   ReceiverReport* receiver_report() { return &receiver_report_; }
   ReportBlock* report_block() { return &report_block_; }
   Bye* bye() { return &bye_; }
+  Rpsi* rpsi() { return &rpsi_; }
   Fir* fir() { return &fir_; }
   FirItem* fir_item() { return &fir_item_; }
   Nack* nack() { return &nack_; }
@@ -223,6 +248,7 @@
   ReceiverReport receiver_report_;
   ReportBlock report_block_;
   Bye bye_;
+  Rpsi rpsi_;
   Fir fir_;
   FirItem fir_item_;
   Nack nack_;