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_;