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
This commit is contained in:
asapersson@webrtc.org 2014-05-20 09:53:51 +00:00
parent 2db9f45038
commit a826006132
6 changed files with 574 additions and 149 deletions

View File

@ -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 @@ void AssignUWord32(uint8_t* buffer, uint16_t* offset, uint32_t value) {
*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 @@ void AssignUWord32(uint8_t* buffer, uint16_t* offset, uint32_t value) {
// | 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 @@ void CreateSenderReport(const RTCPUtility::RTCPPacketSR& sr,
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 @@ void CreateSenderReport(const RTCPUtility::RTCPPacketSR& sr,
// | 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,8 +144,7 @@ void CreateReceiverReport(const RTCPUtility::RTCPPacketRR& rr,
// | delay since last SR (DLSR) |
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
void CreateReportBlock(
const RTCPUtility::RTCPPacketReportBlockItem& report_block,
void CreateReportBlock(const RTCPPacketReportBlockItem& report_block,
uint8_t* buffer,
uint16_t* pos) {
AssignUWord32(buffer, pos, report_block.SSRC);
@ -130,14 +169,12 @@ void CreateReportBlock(
// (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 @@ void CreateBye(const RTCPUtility::RTCPPacketBYE& bye,
// : 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 @@ void CreateBye(const RTCPUtility::RTCPPacketBYE& bye,
// | Seq nr. | Reserved |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
void CreateFirRequest(const RTCPUtility::RTCPPacketPSFBFIR& fir,
const RTCPUtility::RTCPPacketPSFBFIRItem& fir_item,
void CreateFir(const RTCPPacketPSFBFIR& fir,
const RTCPPacketPSFBFIRItem& fir_item,
uint16_t length,
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);
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,
template <typename T>
void AppendBlocks(const std::vector<T*>& blocks,
uint8_t* buffer,
uint16_t* pos) {
for (std::vector<ReportBlock*>::const_iterator it = report_blocks.begin();
it != report_blocks.end(); ++it) {
for (typename std::vector<T*>::const_iterator it = blocks.begin();
it != blocks.end(); ++it) {
(*it)->Create(buffer, pos);
}
}
@ -236,8 +330,8 @@ void SenderReport::Create(uint8_t* packet,
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 @@ void ReceiverReport::Create(uint8_t* packet,
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 @@ void ReceiverReport::WithReportBlock(ReportBlock* block) {
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 @@ void Bye::WithCsrc(uint32_t csrc) {
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

View File

@ -244,7 +244,7 @@ class ReportBlock {
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 @@ class Bye : public RtcpPacket {
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 @@ class Bye : public RtcpPacket {
// : 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).
//

View File

@ -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 @@ TEST(RtcpPacketTest, SrWithTwoReportBlocks) {
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);

View File

@ -1266,31 +1266,27 @@ RTCPUtility::RTCPParserV2::ParseFBCommon(const RTCPCommonHeader& header)
}
}
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 @@ RTCPUtility::RTCPParserV2::ParseRPSIItem()
_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;
}

View File

@ -23,25 +23,54 @@ void RtcpPacketParser::Parse(const void *data, int len) {
for (RTCPUtility::RTCPPacketTypes type = parser.Begin();
type != RTCPUtility::kRtcpNotValidCode;
type = parser.Iterate()) {
if (type == RTCPUtility::kRtcpSrCode) {
switch (type) {
case RTCPUtility::kRtcpSrCode:
sender_report_.Set(parser.Packet().SR);
} else if (type == RTCPUtility::kRtcpRrCode) {
break;
case RTCPUtility::kRtcpRrCode:
receiver_report_.Set(parser.Packet().RR);
} else if (type == RTCPUtility::kRtcpByeCode) {
break;
case RTCPUtility::kRtcpByeCode:
bye_.Set(parser.Packet().BYE);
} else if (type == RTCPUtility::kRtcpReportBlockItemCode) {
break;
case RTCPUtility::kRtcpReportBlockItemCode:
report_block_.Set(parser.Packet().ReportBlockItem);
++report_blocks_per_ssrc_[parser.Packet().ReportBlockItem.SSRC];
} else if (type == RTCPUtility::kRtcpPsfbFirCode) {
break;
case RTCPUtility::kRtcpPsfbRpsiCode:
rpsi_.Set(parser.Packet().RPSI);
break;
case RTCPUtility::kRtcpPsfbFirCode:
fir_.Set(parser.Packet().FIR);
} else if (type == webrtc::RTCPUtility::kRtcpPsfbFirItemCode) {
break;
case RTCPUtility::kRtcpPsfbFirItemCode:
fir_item_.Set(parser.Packet().FIRItem);
} else if (type == RTCPUtility::kRtcpRtpfbNackCode) {
break;
case RTCPUtility::kRtcpRtpfbNackCode:
nack_.Set(parser.Packet().NACK);
} else if (type == RTCPUtility::kRtcpRtpfbNackItemCode) {
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

View File

@ -23,167 +23,192 @@ namespace test {
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 @@ class NackItem {
}
++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 @@ class RtcpPacketParser {
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 @@ class RtcpPacketParser {
ReceiverReport receiver_report_;
ReportBlock report_block_;
Bye bye_;
Rpsi rpsi_;
Fir fir_;
FirItem fir_item_;
Nack nack_;