Added support for sending and receiving RTCP XR packets:
- Receiver reference time report block - DLRR report block (RFC3611). BUG=1613 R=mflodman@webrtc.org, stefan@webrtc.org Review URL: https://webrtc-codereview.appspot.com/2196010 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4898 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
c0167702d3
commit
8469f7b328
@ -96,7 +96,9 @@ enum RTCPPacketType
|
||||
kRtcpSli = 0x4000,
|
||||
kRtcpRpsi = 0x8000,
|
||||
kRtcpRemb = 0x10000,
|
||||
kRtcpTransmissionTimeOffset = 0x20000
|
||||
kRtcpTransmissionTimeOffset = 0x20000,
|
||||
kRtcpXrReceiverReferenceTime = 0x40000,
|
||||
kRtcpXrDlrrReportBlock = 0x80000
|
||||
};
|
||||
|
||||
enum KeyFrameRequestMethod
|
||||
@ -177,6 +179,13 @@ struct RTCPReportBlock {
|
||||
uint32_t delaySinceLastSR;
|
||||
};
|
||||
|
||||
struct RtcpReceiveTimeInfo {
|
||||
// Fields as described by RFC 3611 4.5.
|
||||
uint32_t sourceSSRC;
|
||||
uint32_t lastRR;
|
||||
uint32_t delaySinceLastRR;
|
||||
};
|
||||
|
||||
typedef std::list<RTCPReportBlock> ReportBlockList;
|
||||
|
||||
class RtpData
|
||||
|
@ -13,6 +13,8 @@
|
||||
#include <assert.h> //assert
|
||||
#include <string.h> //memset
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h"
|
||||
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
||||
@ -46,6 +48,8 @@ RTCPReceiver::RTCPReceiver(const int32_t id, Clock* clock,
|
||||
_remoteSenderInfo(),
|
||||
_lastReceivedSRNTPsecs(0),
|
||||
_lastReceivedSRNTPfrac(0),
|
||||
_lastReceivedXRNTPsecs(0),
|
||||
_lastReceivedXRNTPfrac(0),
|
||||
_receivedInfoMap(),
|
||||
_packetTimeOutMS(0),
|
||||
_lastReceivedRrMs(0),
|
||||
@ -259,6 +263,30 @@ RTCPReceiver::NTP(uint32_t *ReceivedNTPsecs,
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool RTCPReceiver::LastReceivedXrReferenceTimeInfo(
|
||||
RtcpReceiveTimeInfo* info) const {
|
||||
assert(info);
|
||||
CriticalSectionScoped lock(_criticalSectionRTCPReceiver);
|
||||
if (_lastReceivedXRNTPsecs == 0 && _lastReceivedXRNTPfrac == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
info->sourceSSRC = _remoteXRReceiveTimeInfo.sourceSSRC;
|
||||
info->lastRR = _remoteXRReceiveTimeInfo.lastRR;
|
||||
|
||||
// Get the delay since last received report (RFC 3611).
|
||||
uint32_t receive_time = RTCPUtility::MidNtp(_lastReceivedXRNTPsecs,
|
||||
_lastReceivedXRNTPfrac);
|
||||
|
||||
uint32_t ntp_sec = 0;
|
||||
uint32_t ntp_frac = 0;
|
||||
_clock->CurrentNtp(ntp_sec, ntp_frac);
|
||||
uint32_t now = RTCPUtility::MidNtp(ntp_sec, ntp_frac);
|
||||
|
||||
info->delaySinceLastRR = now - receive_time;
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t
|
||||
RTCPReceiver::SenderInfoReceived(RTCPSenderInfo* senderInfo) const
|
||||
{
|
||||
@ -316,6 +344,15 @@ RTCPReceiver::IncomingRTCPPacket(RTCPPacketInformation& rtcpPacketInformation,
|
||||
case RTCPUtility::kRtcpSdesCode:
|
||||
HandleSDES(*rtcpParser);
|
||||
break;
|
||||
case RTCPUtility::kRtcpXrHeaderCode:
|
||||
HandleXrHeader(*rtcpParser, rtcpPacketInformation);
|
||||
break;
|
||||
case RTCPUtility::kRtcpXrReceiverReferenceTimeCode:
|
||||
HandleXrReceiveReferenceTime(*rtcpParser, rtcpPacketInformation);
|
||||
break;
|
||||
case RTCPUtility::kRtcpXrDlrrReportBlockCode:
|
||||
HandleXrDlrrReportBlock(*rtcpParser, rtcpPacketInformation);
|
||||
break;
|
||||
case RTCPUtility::kRtcpXrVoipMetricCode:
|
||||
HandleXRVOIPMetric(*rtcpParser, rtcpPacketInformation);
|
||||
break;
|
||||
@ -863,6 +900,85 @@ void RTCPReceiver::HandleBYE(RTCPUtility::RTCPParserV2& rtcpParser) {
|
||||
rtcpParser.Iterate();
|
||||
}
|
||||
|
||||
void RTCPReceiver::HandleXrHeader(
|
||||
RTCPUtility::RTCPParserV2& parser,
|
||||
RTCPPacketInformation& rtcpPacketInformation) {
|
||||
const RTCPUtility::RTCPPacket& packet = parser.Packet();
|
||||
|
||||
rtcpPacketInformation.xr_originator_ssrc = packet.XR.OriginatorSSRC;
|
||||
|
||||
parser.Iterate();
|
||||
}
|
||||
|
||||
void RTCPReceiver::HandleXrReceiveReferenceTime(
|
||||
RTCPUtility::RTCPParserV2& parser,
|
||||
RTCPPacketInformation& rtcpPacketInformation) {
|
||||
const RTCPUtility::RTCPPacket& packet = parser.Packet();
|
||||
|
||||
_remoteXRReceiveTimeInfo.sourceSSRC =
|
||||
rtcpPacketInformation.xr_originator_ssrc;
|
||||
|
||||
_remoteXRReceiveTimeInfo.lastRR = RTCPUtility::MidNtp(
|
||||
packet.XRReceiverReferenceTimeItem.NTPMostSignificant,
|
||||
packet.XRReceiverReferenceTimeItem.NTPLeastSignificant);
|
||||
|
||||
_clock->CurrentNtp(_lastReceivedXRNTPsecs, _lastReceivedXRNTPfrac);
|
||||
|
||||
rtcpPacketInformation.rtcpPacketTypeFlags |= kRtcpXrReceiverReferenceTime;
|
||||
|
||||
parser.Iterate();
|
||||
}
|
||||
|
||||
void RTCPReceiver::HandleXrDlrrReportBlock(
|
||||
RTCPUtility::RTCPParserV2& parser,
|
||||
RTCPPacketInformation& rtcpPacketInformation) {
|
||||
const RTCPUtility::RTCPPacket& packet = parser.Packet();
|
||||
// Iterate through sub-block(s), if any.
|
||||
RTCPUtility::RTCPPacketTypes packet_type = parser.Iterate();
|
||||
|
||||
while (packet_type == RTCPUtility::kRtcpXrDlrrReportBlockItemCode) {
|
||||
HandleXrDlrrReportBlockItem(packet, rtcpPacketInformation);
|
||||
packet_type = parser.Iterate();
|
||||
}
|
||||
}
|
||||
|
||||
void RTCPReceiver::HandleXrDlrrReportBlockItem(
|
||||
const RTCPUtility::RTCPPacket& packet,
|
||||
RTCPPacketInformation& rtcpPacketInformation) {
|
||||
if (registered_ssrcs_.find(packet.XRDLRRReportBlockItem.SSRC) ==
|
||||
registered_ssrcs_.end()) {
|
||||
// Not to us.
|
||||
return;
|
||||
}
|
||||
|
||||
rtcpPacketInformation.xr_dlrr_item = true;
|
||||
|
||||
// To avoid problem with acquiring _criticalSectionRTCPSender while holding
|
||||
// _criticalSectionRTCPReceiver.
|
||||
_criticalSectionRTCPReceiver->Leave();
|
||||
|
||||
int64_t send_time_ms;
|
||||
bool found = _rtpRtcp.SendTimeOfXrRrReport(
|
||||
packet.XRDLRRReportBlockItem.LastRR, &send_time_ms);
|
||||
|
||||
_criticalSectionRTCPReceiver->Enter();
|
||||
|
||||
if (!found) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The DelayLastRR field is in units of 1/65536 sec.
|
||||
// uint32_t delay_rr_ms =
|
||||
// (((packet.XRDLRRReportBlockItem.DelayLastRR & 0x0000ffff) * 1000) >> 16) +
|
||||
// (((packet.XRDLRRReportBlockItem.DelayLastRR & 0xffff0000) >> 16) * 1000);
|
||||
|
||||
// TODO(asapersson): Not yet used.
|
||||
// int32_t rtt =_clock->CurrentNtpInMilliseconds() - delay_rr_ms - send_time_ms;
|
||||
// rtt = std::max(rtt, 1);
|
||||
|
||||
rtcpPacketInformation.rtcpPacketTypeFlags |= kRtcpXrDlrrReportBlock;
|
||||
}
|
||||
|
||||
// no need for critsect we have _criticalSectionRTCPReceiver
|
||||
void
|
||||
RTCPReceiver::HandleXRVOIPMetric(RTCPUtility::RTCPParserV2& rtcpParser,
|
||||
|
@ -69,6 +69,8 @@ public:
|
||||
uint32_t *RTCPArrivalTimeFrac,
|
||||
uint32_t *rtcp_timestamp) const;
|
||||
|
||||
bool LastReceivedXrReferenceTimeInfo(RtcpReceiveTimeInfo* info) const;
|
||||
|
||||
// get rtt
|
||||
int32_t RTT(uint32_t remoteSSRC,
|
||||
uint16_t* RTT,
|
||||
@ -133,6 +135,21 @@ protected:
|
||||
|
||||
void HandleSDESChunk(RTCPUtility::RTCPParserV2& rtcpParser);
|
||||
|
||||
void HandleXrHeader(RTCPUtility::RTCPParserV2& parser,
|
||||
RTCPHelp::RTCPPacketInformation& rtcpPacketInformation);
|
||||
|
||||
void HandleXrReceiveReferenceTime(
|
||||
RTCPUtility::RTCPParserV2& parser,
|
||||
RTCPHelp::RTCPPacketInformation& rtcpPacketInformation);
|
||||
|
||||
void HandleXrDlrrReportBlock(
|
||||
RTCPUtility::RTCPParserV2& parser,
|
||||
RTCPHelp::RTCPPacketInformation& rtcpPacketInformation);
|
||||
|
||||
void HandleXrDlrrReportBlockItem(
|
||||
const RTCPUtility::RTCPPacket& packet,
|
||||
RTCPHelp::RTCPPacketInformation& rtcpPacketInformation);
|
||||
|
||||
void HandleXRVOIPMetric(RTCPUtility::RTCPParserV2& rtcpParser,
|
||||
RTCPHelp::RTCPPacketInformation& rtcpPacketInformation);
|
||||
|
||||
@ -223,6 +240,12 @@ protected:
|
||||
uint32_t _lastReceivedSRNTPsecs;
|
||||
uint32_t _lastReceivedSRNTPfrac;
|
||||
|
||||
// Received XR receive time report.
|
||||
RtcpReceiveTimeInfo _remoteXRReceiveTimeInfo;
|
||||
// Time when the report was received.
|
||||
uint32_t _lastReceivedXRNTPsecs;
|
||||
uint32_t _lastReceivedXRNTPfrac;
|
||||
|
||||
// Received report blocks.
|
||||
std::map<uint32_t, RTCPHelp::RTCPReportBlockInformation*>
|
||||
_receivedReportBlockMap;
|
||||
|
@ -34,6 +34,8 @@ RTCPPacketInformation::RTCPPacketInformation()
|
||||
ntp_secs(0),
|
||||
ntp_frac(0),
|
||||
rtp_timestamp(0),
|
||||
xr_originator_ssrc(0),
|
||||
xr_dlrr_item(false),
|
||||
VoIPMetric(NULL) {
|
||||
}
|
||||
|
||||
|
@ -80,6 +80,8 @@ public:
|
||||
uint32_t ntp_frac;
|
||||
uint32_t rtp_timestamp;
|
||||
|
||||
uint32_t xr_originator_ssrc;
|
||||
bool xr_dlrr_item;
|
||||
RTCPVoIPMetric* VoIPMetric;
|
||||
|
||||
private:
|
||||
|
@ -137,6 +137,44 @@ class PacketBuilder {
|
||||
Add32(0); // Delay since last SR.
|
||||
}
|
||||
|
||||
void AddXrHeader(uint32_t sender_ssrc) {
|
||||
AddRtcpHeader(207, 0);
|
||||
Add32(sender_ssrc);
|
||||
}
|
||||
|
||||
void AddXrReceiverReferenceTimeBlock(uint32_t ntp_sec, uint32_t ntp_frac) {
|
||||
Add8(4); // Block type.
|
||||
Add8(0); // Reserved.
|
||||
Add16(2); // Length.
|
||||
Add64(ntp_sec, ntp_frac); // NTP timestamp.
|
||||
}
|
||||
|
||||
void AddXrDlrrBlock(std::vector<uint32_t>& remote_ssrc) {
|
||||
ASSERT_LT(pos_ + 4 + static_cast<int>(remote_ssrc.size())*4,
|
||||
kMaxPacketSize-1) << "Max buffer size reached.";
|
||||
Add8(5); // Block type.
|
||||
Add8(0); // Reserved.
|
||||
Add16(remote_ssrc.size() * 3); // Length.
|
||||
for (size_t i = 0; i < remote_ssrc.size(); ++i) {
|
||||
Add32(remote_ssrc.at(i)); // Receiver SSRC.
|
||||
Add32(0x10203); // Last RR.
|
||||
Add32(0x40506); // Delay since last RR.
|
||||
}
|
||||
}
|
||||
|
||||
void AddXrVoipBlock(uint32_t remote_ssrc, uint8_t loss) {
|
||||
Add8(7); // Block type.
|
||||
Add8(0); // Reserved.
|
||||
Add16(8); // Length.
|
||||
Add32(remote_ssrc); // Receiver SSRC.
|
||||
Add8(loss); // Loss rate.
|
||||
Add8(0); // Remaining statistics (RFC 3611) are set to zero.
|
||||
Add16(0);
|
||||
Add64(0, 0);
|
||||
Add64(0, 0);
|
||||
Add64(0, 0);
|
||||
}
|
||||
|
||||
const uint8_t* packet() {
|
||||
PatchLengthField();
|
||||
return buffer_;
|
||||
@ -228,7 +266,7 @@ class RtcpReceiverTest : public ::testing::Test {
|
||||
// Injects an RTCP packet into the receiver.
|
||||
// Returns 0 for OK, non-0 for failure.
|
||||
int InjectRtcpPacket(const uint8_t* packet,
|
||||
uint16_t packet_len) {
|
||||
uint16_t packet_len) {
|
||||
RTCPUtility::RTCPParserV2 rtcpParser(packet,
|
||||
packet_len,
|
||||
true); // Allow non-compound RTCP
|
||||
@ -255,6 +293,10 @@ class RtcpReceiverTest : public ::testing::Test {
|
||||
rtcp_packet_info_.ntp_secs = rtcpPacketInformation.ntp_secs;
|
||||
rtcp_packet_info_.ntp_frac = rtcpPacketInformation.ntp_frac;
|
||||
rtcp_packet_info_.rtp_timestamp = rtcpPacketInformation.rtp_timestamp;
|
||||
rtcp_packet_info_.xr_dlrr_item = rtcpPacketInformation.xr_dlrr_item;
|
||||
if (rtcpPacketInformation.VoIPMetric) {
|
||||
rtcp_packet_info_.AddVoIPMetric(rtcpPacketInformation.VoIPMetric);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -287,6 +329,165 @@ TEST_F(RtcpReceiverTest, InjectSrPacket) {
|
||||
kRtcpSr & rtcp_packet_info_.rtcpPacketTypeFlags);
|
||||
}
|
||||
|
||||
TEST_F(RtcpReceiverTest, XrPacketWithZeroReportBlocksIgnored) {
|
||||
PacketBuilder p;
|
||||
p.AddXrHeader(0x2345);
|
||||
EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length()));
|
||||
EXPECT_EQ(0U, rtcp_packet_info_.rtcpPacketTypeFlags);
|
||||
}
|
||||
|
||||
TEST_F(RtcpReceiverTest, InjectXrVoipPacket) {
|
||||
const uint32_t kSourceSsrc = 0x123456;
|
||||
std::set<uint32_t> ssrcs;
|
||||
ssrcs.insert(kSourceSsrc);
|
||||
rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs);
|
||||
|
||||
const uint8_t kLossRate = 123;
|
||||
PacketBuilder p;
|
||||
p.AddXrHeader(0x2345);
|
||||
p.AddXrVoipBlock(kSourceSsrc, kLossRate);
|
||||
EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length()));
|
||||
ASSERT_TRUE(rtcp_packet_info_.VoIPMetric != NULL);
|
||||
EXPECT_EQ(kLossRate, rtcp_packet_info_.VoIPMetric->lossRate);
|
||||
EXPECT_EQ(kRtcpXrVoipMetric, rtcp_packet_info_.rtcpPacketTypeFlags);
|
||||
}
|
||||
|
||||
TEST_F(RtcpReceiverTest, InjectXrReceiverReferenceTimePacket) {
|
||||
PacketBuilder p;
|
||||
p.AddXrHeader(0x2345);
|
||||
p.AddXrReceiverReferenceTimeBlock(0x10203, 0x40506);
|
||||
EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length()));
|
||||
EXPECT_EQ(kRtcpXrReceiverReferenceTime,
|
||||
rtcp_packet_info_.rtcpPacketTypeFlags);
|
||||
}
|
||||
|
||||
TEST_F(RtcpReceiverTest, InjectXrDlrrPacketWithNoSubBlock) {
|
||||
const uint32_t kSourceSsrc = 0x123456;
|
||||
std::set<uint32_t> ssrcs;
|
||||
ssrcs.insert(kSourceSsrc);
|
||||
rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs);
|
||||
std::vector<uint32_t> remote_ssrcs;
|
||||
|
||||
PacketBuilder p;
|
||||
p.AddXrHeader(0x2345);
|
||||
p.AddXrDlrrBlock(remote_ssrcs);
|
||||
EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length()));
|
||||
EXPECT_EQ(0U, rtcp_packet_info_.rtcpPacketTypeFlags);
|
||||
EXPECT_FALSE(rtcp_packet_info_.xr_dlrr_item);
|
||||
}
|
||||
|
||||
TEST_F(RtcpReceiverTest, XrDlrrPacketNotToUsIgnored) {
|
||||
const uint32_t kSourceSsrc = 0x123456;
|
||||
std::set<uint32_t> ssrcs;
|
||||
ssrcs.insert(kSourceSsrc);
|
||||
rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs);
|
||||
std::vector<uint32_t> remote_ssrcs;
|
||||
remote_ssrcs.push_back(kSourceSsrc+1);
|
||||
|
||||
PacketBuilder p;
|
||||
p.AddXrHeader(0x2345);
|
||||
p.AddXrDlrrBlock(remote_ssrcs);
|
||||
EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length()));
|
||||
EXPECT_EQ(0U, rtcp_packet_info_.rtcpPacketTypeFlags);
|
||||
EXPECT_FALSE(rtcp_packet_info_.xr_dlrr_item);
|
||||
}
|
||||
|
||||
TEST_F(RtcpReceiverTest, InjectXrDlrrPacketWithSubBlock) {
|
||||
const uint32_t kSourceSsrc = 0x123456;
|
||||
std::set<uint32_t> ssrcs;
|
||||
ssrcs.insert(kSourceSsrc);
|
||||
rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs);
|
||||
std::vector<uint32_t> remote_ssrcs;
|
||||
remote_ssrcs.push_back(kSourceSsrc);
|
||||
|
||||
PacketBuilder p;
|
||||
p.AddXrHeader(0x2345);
|
||||
p.AddXrDlrrBlock(remote_ssrcs);
|
||||
EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length()));
|
||||
// The parser should note the DLRR report block item, but not flag the packet
|
||||
// since the RTT is not estimated.
|
||||
EXPECT_TRUE(rtcp_packet_info_.xr_dlrr_item);
|
||||
}
|
||||
|
||||
TEST_F(RtcpReceiverTest, InjectXrDlrrPacketWithMultipleSubBlocks) {
|
||||
const uint32_t kSourceSsrc = 0x123456;
|
||||
std::set<uint32_t> ssrcs;
|
||||
ssrcs.insert(kSourceSsrc);
|
||||
rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs);
|
||||
std::vector<uint32_t> remote_ssrcs;
|
||||
remote_ssrcs.push_back(kSourceSsrc+2);
|
||||
remote_ssrcs.push_back(kSourceSsrc+1);
|
||||
remote_ssrcs.push_back(kSourceSsrc);
|
||||
|
||||
PacketBuilder p;
|
||||
p.AddXrHeader(0x2345);
|
||||
p.AddXrDlrrBlock(remote_ssrcs);
|
||||
EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length()));
|
||||
// The parser should note the DLRR report block item, but not flag the packet
|
||||
// since the RTT is not estimated.
|
||||
EXPECT_TRUE(rtcp_packet_info_.xr_dlrr_item);
|
||||
}
|
||||
|
||||
TEST_F(RtcpReceiverTest, InjectXrPacketWithMultipleReportBlocks) {
|
||||
const uint8_t kLossRate = 123;
|
||||
const uint32_t kSourceSsrc = 0x123456;
|
||||
std::set<uint32_t> ssrcs;
|
||||
ssrcs.insert(kSourceSsrc);
|
||||
rtcp_receiver_->SetSsrcs(kSourceSsrc, ssrcs);
|
||||
std::vector<uint32_t> remote_ssrcs;
|
||||
remote_ssrcs.push_back(kSourceSsrc);
|
||||
|
||||
PacketBuilder p;
|
||||
p.AddXrHeader(0x2345);
|
||||
p.AddXrDlrrBlock(remote_ssrcs);
|
||||
p.AddXrVoipBlock(kSourceSsrc, kLossRate);
|
||||
p.AddXrReceiverReferenceTimeBlock(0x10203, 0x40506);
|
||||
|
||||
EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length()));
|
||||
EXPECT_EQ(static_cast<unsigned int>(kRtcpXrReceiverReferenceTime +
|
||||
kRtcpXrVoipMetric),
|
||||
rtcp_packet_info_.rtcpPacketTypeFlags);
|
||||
// The parser should note the DLRR report block item, but not flag the packet
|
||||
// since the RTT is not estimated.
|
||||
EXPECT_TRUE(rtcp_packet_info_.xr_dlrr_item);
|
||||
}
|
||||
|
||||
TEST(RtcpUtilityTest, MidNtp) {
|
||||
const uint32_t kNtpSec = 0x12345678;
|
||||
const uint32_t kNtpFrac = 0x23456789;
|
||||
const uint32_t kNtpMid = 0x56782345;
|
||||
EXPECT_EQ(kNtpMid, RTCPUtility::MidNtp(kNtpSec, kNtpFrac));
|
||||
}
|
||||
|
||||
TEST_F(RtcpReceiverTest, LastReceivedXrReferenceTimeInfoInitiallyFalse) {
|
||||
RtcpReceiveTimeInfo info;
|
||||
EXPECT_FALSE(rtcp_receiver_->LastReceivedXrReferenceTimeInfo(&info));
|
||||
}
|
||||
|
||||
TEST_F(RtcpReceiverTest, GetLastReceivedXrReferenceTimeInfo) {
|
||||
const uint32_t kSenderSsrc = 0x123456;
|
||||
const uint32_t kNtpSec = 0x10203;
|
||||
const uint32_t kNtpFrac = 0x40506;
|
||||
const uint32_t kNtpMid = RTCPUtility::MidNtp(kNtpSec, kNtpFrac);
|
||||
|
||||
PacketBuilder p;
|
||||
p.AddXrHeader(kSenderSsrc);
|
||||
p.AddXrReceiverReferenceTimeBlock(kNtpSec, kNtpFrac);
|
||||
EXPECT_EQ(0, InjectRtcpPacket(p.packet(), p.length()));
|
||||
EXPECT_EQ(kRtcpXrReceiverReferenceTime,
|
||||
rtcp_packet_info_.rtcpPacketTypeFlags);
|
||||
|
||||
RtcpReceiveTimeInfo info;
|
||||
EXPECT_TRUE(rtcp_receiver_->LastReceivedXrReferenceTimeInfo(&info));
|
||||
EXPECT_EQ(kSenderSsrc, info.sourceSSRC);
|
||||
EXPECT_EQ(kNtpMid, info.lastRR);
|
||||
EXPECT_EQ(0U, info.delaySinceLastRR);
|
||||
|
||||
system_clock_.AdvanceTimeMilliseconds(1000);
|
||||
EXPECT_TRUE(rtcp_receiver_->LastReceivedXrReferenceTimeInfo(&info));
|
||||
EXPECT_EQ(65536U, info.delaySinceLastRR);
|
||||
}
|
||||
|
||||
TEST_F(RtcpReceiverTest, ReceiveReportTimeout) {
|
||||
const uint32_t kSenderSsrc = 0x10203;
|
||||
const uint32_t kSourceSsrc = 0x40506;
|
||||
|
@ -77,6 +77,8 @@ RTCPSender::FeedbackState::FeedbackState(ModuleRtpRtcpImpl* module)
|
||||
last_rr_ntp_frac = last_ntp_frac;
|
||||
remote_sr = last_remote_sr;
|
||||
|
||||
has_last_xr_rr = module->LastReceivedXrReferenceTimeInfo(&last_xr_rr);
|
||||
|
||||
uint32_t send_bitrate = 0, tmp;
|
||||
module->BitrateSent(&send_bitrate, &tmp, &tmp, &tmp);
|
||||
this->send_bitrate = send_bitrate;
|
||||
@ -90,7 +92,8 @@ RTCPSender::FeedbackState::FeedbackState()
|
||||
send_bitrate(0),
|
||||
last_rr_ntp_secs(0),
|
||||
last_rr_ntp_frac(0),
|
||||
remote_sr(0) {}
|
||||
remote_sr(0),
|
||||
has_last_xr_rr(false) {}
|
||||
|
||||
RTCPSender::RTCPSender(const int32_t id,
|
||||
const bool audio,
|
||||
@ -128,6 +131,8 @@ RTCPSender::RTCPSender(const int32_t id,
|
||||
_lastSendReport(),
|
||||
_lastRTCPTime(),
|
||||
|
||||
last_xr_rr_(),
|
||||
|
||||
_CSRCs(0),
|
||||
_CSRC(),
|
||||
_includeCSRCs(true),
|
||||
@ -148,6 +153,8 @@ RTCPSender::RTCPSender(const int32_t id,
|
||||
_appName(),
|
||||
_appData(NULL),
|
||||
_appLength(0),
|
||||
|
||||
xrSendReceiverReferenceTimeEnabled_(false),
|
||||
_xrSendVoIPMetric(false),
|
||||
_xrVoIPMetric(),
|
||||
_nackCount(0),
|
||||
@ -222,12 +229,15 @@ RTCPSender::Init()
|
||||
}
|
||||
_appLength = 0;
|
||||
|
||||
xrSendReceiverReferenceTimeEnabled_ = false;
|
||||
|
||||
_xrSendVoIPMetric = false;
|
||||
|
||||
memset(&_xrVoIPMetric, 0, sizeof(_xrVoIPMetric));
|
||||
memset(_CNAME, 0, sizeof(_CNAME));
|
||||
memset(_lastSendReport, 0, sizeof(_lastSendReport));
|
||||
memset(_lastRTCPTime, 0, sizeof(_lastRTCPTime));
|
||||
last_xr_rr_.clear();
|
||||
|
||||
_nackCount = 0;
|
||||
_pliCount = 0;
|
||||
@ -591,6 +601,21 @@ RTCPSender::SendTimeOfSendReport(const uint32_t sendReport)
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool RTCPSender::SendTimeOfXrRrReport(uint32_t mid_ntp,
|
||||
int64_t* time_ms) const {
|
||||
CriticalSectionScoped lock(_criticalSectionRTCPSender);
|
||||
|
||||
if (last_xr_rr_.empty()) {
|
||||
return false;
|
||||
}
|
||||
std::map<uint32_t, int64_t>::const_iterator it = last_xr_rr_.find(mid_ntp);
|
||||
if (it == last_xr_rr_.end()) {
|
||||
return false;
|
||||
}
|
||||
*time_ms = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t RTCPSender::AddExternalReportBlock(
|
||||
uint32_t SSRC,
|
||||
const RTCPReportBlock* reportBlock) {
|
||||
@ -1502,6 +1527,107 @@ RTCPSender::BuildBYE(uint8_t* rtcpbuffer, int& pos)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t RTCPSender::BuildReceiverReferenceTime(uint8_t* buffer,
|
||||
int& pos,
|
||||
uint32_t ntp_sec,
|
||||
uint32_t ntp_frac) {
|
||||
const int kRrTimeBlockLength = 20;
|
||||
if (pos + kRrTimeBlockLength >= IP_PACKET_SIZE) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (last_xr_rr_.size() >= RTCP_NUMBER_OF_SR) {
|
||||
last_xr_rr_.erase(last_xr_rr_.begin());
|
||||
}
|
||||
last_xr_rr_.insert(std::pair<uint32_t, int64_t>(
|
||||
RTCPUtility::MidNtp(ntp_sec, ntp_frac),
|
||||
Clock::NtpToMs(ntp_sec, ntp_frac)));
|
||||
|
||||
// Add XR header.
|
||||
buffer[pos++] = 0x80;
|
||||
buffer[pos++] = 207;
|
||||
buffer[pos++] = 0; // XR packet length.
|
||||
buffer[pos++] = 4; // XR packet length.
|
||||
|
||||
// Add our own SSRC.
|
||||
ModuleRTPUtility::AssignUWord32ToBuffer(buffer + pos, _SSRC);
|
||||
pos += 4;
|
||||
|
||||
// 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
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | BT=4 | reserved | block length = 2 |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | NTP timestamp, most significant word |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | NTP timestamp, least significant word |
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
// Add Receiver Reference Time Report block.
|
||||
buffer[pos++] = 4; // BT.
|
||||
buffer[pos++] = 0; // Reserved.
|
||||
buffer[pos++] = 0; // Block length.
|
||||
buffer[pos++] = 2; // Block length.
|
||||
|
||||
// NTP timestamp.
|
||||
ModuleRTPUtility::AssignUWord32ToBuffer(buffer + pos, ntp_sec);
|
||||
pos += 4;
|
||||
ModuleRTPUtility::AssignUWord32ToBuffer(buffer + pos, ntp_frac);
|
||||
pos += 4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t RTCPSender::BuildDlrr(uint8_t* buffer,
|
||||
int& pos,
|
||||
const RtcpReceiveTimeInfo& info) {
|
||||
const int kDlrrBlockLength = 24;
|
||||
if (pos + kDlrrBlockLength >= IP_PACKET_SIZE) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
// Add XR header.
|
||||
buffer[pos++] = 0x80;
|
||||
buffer[pos++] = 207;
|
||||
buffer[pos++] = 0; // XR packet length.
|
||||
buffer[pos++] = 5; // XR packet length.
|
||||
|
||||
// Add our own SSRC.
|
||||
ModuleRTPUtility::AssignUWord32ToBuffer(buffer + pos, _SSRC);
|
||||
pos += 4;
|
||||
|
||||
// 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
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | BT=5 | reserved | block length |
|
||||
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||
// | SSRC_1 (SSRC of first receiver) | sub-
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
|
||||
// | last RR (LRR) | 1
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
// | delay since last RR (DLRR) |
|
||||
// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||
// | SSRC_2 (SSRC of second receiver) | sub-
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
|
||||
// : ... : 2
|
||||
|
||||
// Add DLRR sub block.
|
||||
buffer[pos++] = 5; // BT.
|
||||
buffer[pos++] = 0; // Reserved.
|
||||
buffer[pos++] = 0; // Block length.
|
||||
buffer[pos++] = 3; // Block length.
|
||||
|
||||
// NTP timestamp.
|
||||
ModuleRTPUtility::AssignUWord32ToBuffer(buffer + pos, info.sourceSSRC);
|
||||
pos += 4;
|
||||
ModuleRTPUtility::AssignUWord32ToBuffer(buffer + pos, info.lastRR);
|
||||
pos += 4;
|
||||
ModuleRTPUtility::AssignUWord32ToBuffer(buffer + pos, info.delaySinceLastRR);
|
||||
pos += 4;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t
|
||||
RTCPSender::BuildVoIPMetric(uint8_t* rtcpbuffer, int& pos)
|
||||
{
|
||||
@ -1652,7 +1778,18 @@ int RTCPSender::PrepareRTCP(const FeedbackState& feedback_state,
|
||||
rtcpPacketTypeFlags |= kRtcpTmmbn;
|
||||
_sendTMMBN = false;
|
||||
}
|
||||
|
||||
if (xrSendReceiverReferenceTimeEnabled_ &&
|
||||
(rtcpPacketTypeFlags & kRtcpReport))
|
||||
{
|
||||
if (!_sending)
|
||||
{
|
||||
rtcpPacketTypeFlags |= kRtcpXrReceiverReferenceTime;
|
||||
}
|
||||
if (feedback_state.has_last_xr_rr)
|
||||
{
|
||||
rtcpPacketTypeFlags |= kRtcpXrDlrrReportBlock;
|
||||
}
|
||||
}
|
||||
if(_method == kRtcpCompound)
|
||||
{
|
||||
if(_sending)
|
||||
@ -1893,6 +2030,27 @@ int RTCPSender::PrepareRTCP(const FeedbackState& feedback_state,
|
||||
return position;
|
||||
}
|
||||
}
|
||||
if (rtcpPacketTypeFlags & kRtcpXrReceiverReferenceTime)
|
||||
{
|
||||
buildVal = BuildReceiverReferenceTime(rtcp_buffer,
|
||||
position,
|
||||
NTPsec,
|
||||
NTPfrac);
|
||||
if (buildVal == -1) {
|
||||
return -1;
|
||||
} else if (buildVal == -2) {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
if (rtcpPacketTypeFlags & kRtcpXrDlrrReportBlock)
|
||||
{
|
||||
buildVal = BuildDlrr(rtcp_buffer, position, feedback_state.last_xr_rr);
|
||||
if (buildVal == -1) {
|
||||
return -1;
|
||||
} else if (buildVal == -2) {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
@ -2020,6 +2178,11 @@ RTCPSender::SetRTCPVoIPMetrics(const RTCPVoIPMetric* VoIPMetric)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RTCPSender::SendRtcpXrReceiverReferenceTime(bool enable) {
|
||||
CriticalSectionScoped lock(_criticalSectionRTCPSender);
|
||||
xrSendReceiverReferenceTimeEnabled_ = enable;
|
||||
}
|
||||
|
||||
// called under critsect _criticalSectionRTCPSender
|
||||
int32_t RTCPSender::WriteAllReportBlocksToBuffer(
|
||||
uint8_t* rtcpbuffer,
|
||||
|
@ -63,6 +63,9 @@ public:
|
||||
uint32_t last_rr_ntp_frac;
|
||||
uint32_t remote_sr;
|
||||
|
||||
bool has_last_xr_rr;
|
||||
RtcpReceiveTimeInfo last_xr_rr;
|
||||
|
||||
// Used when generating TMMBR.
|
||||
ModuleRtpRtcpImpl* module;
|
||||
};
|
||||
@ -107,6 +110,8 @@ public:
|
||||
|
||||
uint32_t SendTimeOfSendReport(const uint32_t sendReport);
|
||||
|
||||
bool SendTimeOfXrRrReport(uint32_t mid_ntp, int64_t* time_ms) const;
|
||||
|
||||
bool TimeToSendRTCPReport(const bool sendKeyframeBeforeRTP = false) const;
|
||||
|
||||
uint32_t LastSendReport(uint32_t& lastRTCPTime);
|
||||
@ -164,6 +169,8 @@ public:
|
||||
|
||||
int32_t SetRTCPVoIPMetrics(const RTCPVoIPMetric* VoIPMetric);
|
||||
|
||||
void SendRtcpXrReceiverReferenceTime(bool enable);
|
||||
|
||||
int32_t SetCSRCs(const uint32_t arrOfCSRC[kRtpCsrcSize],
|
||||
const uint8_t arrLength);
|
||||
|
||||
@ -250,6 +257,14 @@ private:
|
||||
const uint16_t* nackList,
|
||||
std::string* nackString);
|
||||
|
||||
int32_t BuildReceiverReferenceTime(uint8_t* buffer,
|
||||
int& pos,
|
||||
uint32_t ntp_sec,
|
||||
uint32_t ntp_frac);
|
||||
int32_t BuildDlrr(uint8_t* buffer,
|
||||
int& pos,
|
||||
const RtcpReceiveTimeInfo& info);
|
||||
|
||||
private:
|
||||
int32_t _id;
|
||||
const bool _audio;
|
||||
@ -289,6 +304,10 @@ private:
|
||||
uint32_t _lastSendReport[RTCP_NUMBER_OF_SR]; // allow packet loss and RTT above 1 sec
|
||||
uint32_t _lastRTCPTime[RTCP_NUMBER_OF_SR];
|
||||
|
||||
// Sent XR receiver reference time report.
|
||||
// <mid ntp (mid 32 bits of the 64 bits NTP timestamp), send time in ms>.
|
||||
std::map<uint32_t, int64_t> last_xr_rr_;
|
||||
|
||||
// send CSRCs
|
||||
uint8_t _CSRCs;
|
||||
uint32_t _CSRC[kRtpCsrcSize];
|
||||
@ -314,6 +333,9 @@ private:
|
||||
uint8_t* _appData;
|
||||
uint16_t _appLength;
|
||||
|
||||
// True if sending of XR Receiver reference time report is enabled.
|
||||
bool xrSendReceiverReferenceTimeEnabled_;
|
||||
|
||||
// XR VoIP metric
|
||||
bool _xrSendVoIPMetric;
|
||||
RTCPVoIPMetric _xrVoIPMetric;
|
||||
|
@ -274,30 +274,30 @@ class RtcpSenderTest : public ::testing::Test {
|
||||
protected:
|
||||
RtcpSenderTest()
|
||||
: over_use_detector_options_(),
|
||||
system_clock_(Clock::GetRealTimeClock()),
|
||||
clock_(1335900000),
|
||||
rtp_payload_registry_(new RTPPayloadRegistry(
|
||||
0, RTPPayloadStrategy::CreateStrategy(false))),
|
||||
remote_bitrate_observer_(),
|
||||
remote_bitrate_estimator_(
|
||||
RemoteBitrateEstimatorFactory().Create(
|
||||
&remote_bitrate_observer_,
|
||||
system_clock_)),
|
||||
receive_statistics_(ReceiveStatistics::Create(system_clock_)) {
|
||||
&clock_)),
|
||||
receive_statistics_(ReceiveStatistics::Create(&clock_)) {
|
||||
test_transport_ = new TestTransport();
|
||||
|
||||
RtpRtcp::Configuration configuration;
|
||||
configuration.id = 0;
|
||||
configuration.audio = false;
|
||||
configuration.clock = system_clock_;
|
||||
configuration.clock = &clock_;
|
||||
configuration.outgoing_transport = test_transport_;
|
||||
configuration.remote_bitrate_estimator = remote_bitrate_estimator_.get();
|
||||
|
||||
rtp_rtcp_impl_ = new ModuleRtpRtcpImpl(configuration);
|
||||
rtp_receiver_.reset(RtpReceiver::CreateVideoReceiver(
|
||||
0, system_clock_, test_transport_, NULL, rtp_payload_registry_.get()));
|
||||
0, &clock_, test_transport_, NULL, rtp_payload_registry_.get()));
|
||||
rtcp_sender_ =
|
||||
new RTCPSender(0, false, system_clock_, receive_statistics_.get());
|
||||
rtcp_receiver_ = new RTCPReceiver(0, system_clock_, rtp_rtcp_impl_);
|
||||
new RTCPSender(0, false, &clock_, receive_statistics_.get());
|
||||
rtcp_receiver_ = new RTCPReceiver(0, &clock_, rtp_rtcp_impl_);
|
||||
test_transport_->SetRTCPReceiver(rtcp_receiver_);
|
||||
// Initialize
|
||||
EXPECT_EQ(0, rtcp_sender_->Init());
|
||||
@ -317,7 +317,7 @@ class RtcpSenderTest : public ::testing::Test {
|
||||
}
|
||||
|
||||
OverUseDetectorOptions over_use_detector_options_;
|
||||
Clock* system_clock_;
|
||||
SimulatedClock clock_;
|
||||
scoped_ptr<RTPPayloadRegistry> rtp_payload_registry_;
|
||||
scoped_ptr<RtpReceiver> rtp_receiver_;
|
||||
ModuleRtpRtcpImpl* rtp_rtcp_impl_;
|
||||
@ -397,6 +397,70 @@ TEST_F(RtcpSenderTest, TestCompound_NoRtpReceived) {
|
||||
kRtcpTransmissionTimeOffset);
|
||||
}
|
||||
|
||||
TEST_F(RtcpSenderTest, TestXrReceiverReferenceTime) {
|
||||
EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound));
|
||||
RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_);
|
||||
EXPECT_EQ(0, rtcp_sender_->SetSendingStatus(feedback_state, false));
|
||||
rtcp_sender_->SendRtcpXrReceiverReferenceTime(true);
|
||||
EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state, kRtcpReport));
|
||||
|
||||
EXPECT_TRUE(test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags &
|
||||
kRtcpXrReceiverReferenceTime);
|
||||
}
|
||||
|
||||
TEST_F(RtcpSenderTest, TestNoXrReceiverReferenceTimeIfSending) {
|
||||
EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound));
|
||||
RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_);
|
||||
EXPECT_EQ(0, rtcp_sender_->SetSendingStatus(feedback_state, true));
|
||||
rtcp_sender_->SendRtcpXrReceiverReferenceTime(true);
|
||||
EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state, kRtcpReport));
|
||||
|
||||
EXPECT_FALSE(test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags &
|
||||
kRtcpXrReceiverReferenceTime);
|
||||
}
|
||||
|
||||
TEST_F(RtcpSenderTest, TestNoXrReceiverReferenceTimeIfNotEnabled) {
|
||||
EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound));
|
||||
RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_);
|
||||
EXPECT_EQ(0, rtcp_sender_->SetSendingStatus(feedback_state, false));
|
||||
rtcp_sender_->SendRtcpXrReceiverReferenceTime(false);
|
||||
EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state, kRtcpReport));
|
||||
|
||||
EXPECT_FALSE(test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags &
|
||||
kRtcpXrReceiverReferenceTime);
|
||||
}
|
||||
|
||||
TEST_F(RtcpSenderTest, TestSendTimeOfXrRrReport) {
|
||||
EXPECT_EQ(0, rtcp_sender_->SetRTCPStatus(kRtcpCompound));
|
||||
RTCPSender::FeedbackState feedback_state(rtp_rtcp_impl_);
|
||||
EXPECT_EQ(0, rtcp_sender_->SetSendingStatus(feedback_state, false));
|
||||
rtcp_sender_->SendRtcpXrReceiverReferenceTime(true);
|
||||
uint32_t ntp_sec;
|
||||
uint32_t ntp_frac;
|
||||
clock_.CurrentNtp(ntp_sec, ntp_frac);
|
||||
uint32_t initial_mid_ntp = RTCPUtility::MidNtp(ntp_sec, ntp_frac);
|
||||
|
||||
// No packet sent.
|
||||
int64_t time_ms;
|
||||
EXPECT_FALSE(rtcp_sender_->SendTimeOfXrRrReport(initial_mid_ntp, &time_ms));
|
||||
|
||||
// Send XR RR packets.
|
||||
for (int i = 0; i <= RTCP_NUMBER_OF_SR; ++i) {
|
||||
EXPECT_EQ(0, rtcp_sender_->SendRTCP(feedback_state, kRtcpReport));
|
||||
EXPECT_TRUE(test_transport_->rtcp_packet_info_.rtcpPacketTypeFlags &
|
||||
kRtcpXrReceiverReferenceTime);
|
||||
|
||||
clock_.CurrentNtp(ntp_sec, ntp_frac);
|
||||
uint32_t mid_ntp = RTCPUtility::MidNtp(ntp_sec, ntp_frac);
|
||||
EXPECT_TRUE(rtcp_sender_->SendTimeOfXrRrReport(mid_ntp, &time_ms));
|
||||
EXPECT_EQ(clock_.CurrentNtpInMilliseconds(), time_ms);
|
||||
clock_.AdvanceTimeMilliseconds(1000);
|
||||
}
|
||||
|
||||
// The first report should no longer be stored.
|
||||
EXPECT_FALSE(rtcp_sender_->SendTimeOfXrRrReport(initial_mid_ntp, &time_ms));
|
||||
}
|
||||
|
||||
// This test is written to verify actual behaviour. It does not seem
|
||||
// to make much sense to send an empty TMMBN, since there is no place
|
||||
// to put an actual limit here. It's just information that no limit
|
||||
|
@ -15,8 +15,14 @@
|
||||
#include <string.h> // memcpy
|
||||
|
||||
namespace webrtc {
|
||||
// RTCPParserV2 : currently read only
|
||||
|
||||
namespace RTCPUtility {
|
||||
uint32_t MidNtp(uint32_t ntp_sec, uint32_t ntp_frac) {
|
||||
return (ntp_sec << 16) + (ntp_frac >> 16);
|
||||
} // end RTCPUtility
|
||||
}
|
||||
|
||||
// RTCPParserV2 : currently read only
|
||||
RTCPUtility::RTCPParserV2::RTCPParserV2(const uint8_t* rtcpData,
|
||||
size_t rtcpDataLength,
|
||||
bool rtcpReducedSizeEnable)
|
||||
@ -110,6 +116,12 @@ RTCPUtility::RTCPParserV2::Iterate()
|
||||
case State_PSFB_REMBItem:
|
||||
IteratePsfbREMBItem();
|
||||
break;
|
||||
case State_XRItem:
|
||||
IterateXrItem();
|
||||
break;
|
||||
case State_XR_DLLRItem:
|
||||
IterateXrDlrrItem();
|
||||
break;
|
||||
case State_AppItem:
|
||||
IterateAppItem();
|
||||
break;
|
||||
@ -198,7 +210,6 @@ RTCPUtility::RTCPParserV2::IterateTopLevel()
|
||||
// Nothing supported found, continue to next block!
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
case PT_APP:
|
||||
@ -229,6 +240,26 @@ RTCPUtility::RTCPParserV2::IterateTopLevel()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RTCPUtility::RTCPParserV2::IterateXrItem()
|
||||
{
|
||||
const bool success = ParseXRItem();
|
||||
if (!success)
|
||||
{
|
||||
Iterate();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RTCPUtility::RTCPParserV2::IterateXrDlrrItem()
|
||||
{
|
||||
const bool success = ParseXRDLRRReportBlockItem();
|
||||
if (!success)
|
||||
{
|
||||
Iterate();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RTCPUtility::RTCPParserV2::IterateReportBlockItem()
|
||||
{
|
||||
@ -833,7 +864,6 @@ RTCPUtility::RTCPParserV2::ParseBYEItem()
|
||||
bool RTCPUtility::RTCPParserV2::ParseXR()
|
||||
{
|
||||
const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData;
|
||||
|
||||
if (length < 8)
|
||||
{
|
||||
EndCurrentBlock();
|
||||
@ -847,8 +877,11 @@ bool RTCPUtility::RTCPParserV2::ParseXR()
|
||||
_packet.XR.OriginatorSSRC += *_ptrRTCPData++ << 8;
|
||||
_packet.XR.OriginatorSSRC += *_ptrRTCPData++;
|
||||
|
||||
return ParseXRItem();
|
||||
_packetType = kRtcpXrHeaderCode;
|
||||
_state = State_XRItem;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
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
|
||||
@ -858,37 +891,133 @@ bool RTCPUtility::RTCPParserV2::ParseXR()
|
||||
: type-specific block contents :
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
bool
|
||||
RTCPUtility::RTCPParserV2::ParseXRItem()
|
||||
{
|
||||
const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData;
|
||||
const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData;
|
||||
|
||||
if (length < 4) //
|
||||
{
|
||||
EndCurrentBlock();
|
||||
return false;
|
||||
}
|
||||
if (length < 4) {
|
||||
_state = State_TopLevel;
|
||||
EndCurrentBlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t blockType = *_ptrRTCPData++;
|
||||
uint8_t typeSpecific = *_ptrRTCPData++;
|
||||
uint8_t blockType = *_ptrRTCPData++;
|
||||
_ptrRTCPData++; // Ignore reserved.
|
||||
|
||||
uint16_t blockLength = *_ptrRTCPData++ << 8;
|
||||
blockLength = *_ptrRTCPData++;
|
||||
uint16_t blockLength = *_ptrRTCPData++ << 8;
|
||||
blockLength = *_ptrRTCPData++;
|
||||
|
||||
if(blockType == 7 && typeSpecific == 0)
|
||||
{
|
||||
if(blockLength != 8)
|
||||
{
|
||||
EndCurrentBlock();
|
||||
return false;
|
||||
}
|
||||
return ParseXRVOIPMetricItem();
|
||||
}else
|
||||
{
|
||||
EndCurrentBlock();
|
||||
return false;
|
||||
}
|
||||
if (blockType == 4 && blockLength == 2)
|
||||
{
|
||||
return ParseXRReceiverReferenceTimeItem();
|
||||
}
|
||||
else if (blockType == 5 && (blockLength % 3) == 0)
|
||||
{
|
||||
_packetType = kRtcpXrDlrrReportBlockCode;
|
||||
_state = State_XR_DLLRItem;
|
||||
_numberOfBlocks = blockLength / 3;
|
||||
return true;
|
||||
}
|
||||
else if (blockType == 7 && blockLength == 8)
|
||||
{
|
||||
return ParseXRVOIPMetricItem();
|
||||
}
|
||||
|
||||
// Not supported.
|
||||
_state = State_TopLevel;
|
||||
EndCurrentBlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| BT=4 | reserved | block length = 2 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| NTP timestamp, most significant word |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| NTP timestamp, least significant word |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
bool RTCPUtility::RTCPParserV2::ParseXRReceiverReferenceTimeItem() {
|
||||
const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData;
|
||||
if (length < 8) {
|
||||
_state = State_TopLevel;
|
||||
EndCurrentBlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
_packet.XRReceiverReferenceTimeItem.NTPMostSignificant =
|
||||
*_ptrRTCPData++ << 24;
|
||||
_packet.XRReceiverReferenceTimeItem.NTPMostSignificant +=
|
||||
*_ptrRTCPData++ << 16;
|
||||
_packet.XRReceiverReferenceTimeItem.NTPMostSignificant +=
|
||||
*_ptrRTCPData++ << 8;
|
||||
_packet.XRReceiverReferenceTimeItem.NTPMostSignificant += *_ptrRTCPData++;
|
||||
|
||||
_packet.XRReceiverReferenceTimeItem.NTPLeastSignificant =
|
||||
*_ptrRTCPData++ << 24;
|
||||
_packet.XRReceiverReferenceTimeItem.NTPLeastSignificant +=
|
||||
*_ptrRTCPData++ << 16;
|
||||
_packet.XRReceiverReferenceTimeItem.NTPLeastSignificant +=
|
||||
*_ptrRTCPData++ << 8;
|
||||
_packet.XRReceiverReferenceTimeItem.NTPLeastSignificant += *_ptrRTCPData++;
|
||||
|
||||
_packetType = kRtcpXrReceiverReferenceTimeCode;
|
||||
_state = State_XRItem;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* 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
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| BT=5 | reserved | block length |
|
||||
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||
| SSRC_1 (SSRC of first receiver) | sub-
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
|
||||
| last RR (LRR) | 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| delay since last RR (DLRR) |
|
||||
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||
| SSRC_2 (SSRC of second receiver) | sub-
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block
|
||||
: ... : 2
|
||||
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||||
*/
|
||||
|
||||
bool RTCPUtility::RTCPParserV2::ParseXRDLRRReportBlockItem() {
|
||||
const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData;
|
||||
if (_numberOfBlocks == 0) {
|
||||
_state = State_XRItem;
|
||||
return false;
|
||||
}
|
||||
if (length < 12) {
|
||||
_state = State_TopLevel;
|
||||
EndCurrentBlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
_packet.XRDLRRReportBlockItem.SSRC = *_ptrRTCPData++ << 24;
|
||||
_packet.XRDLRRReportBlockItem.SSRC += *_ptrRTCPData++ << 16;
|
||||
_packet.XRDLRRReportBlockItem.SSRC += *_ptrRTCPData++ << 8;
|
||||
_packet.XRDLRRReportBlockItem.SSRC += *_ptrRTCPData++;
|
||||
|
||||
_packet.XRDLRRReportBlockItem.LastRR = *_ptrRTCPData++ << 24;
|
||||
_packet.XRDLRRReportBlockItem.LastRR += *_ptrRTCPData++ << 16;
|
||||
_packet.XRDLRRReportBlockItem.LastRR += *_ptrRTCPData++ << 8;
|
||||
_packet.XRDLRRReportBlockItem.LastRR += *_ptrRTCPData++;
|
||||
|
||||
_packet.XRDLRRReportBlockItem.DelayLastRR = *_ptrRTCPData++ << 24;
|
||||
_packet.XRDLRRReportBlockItem.DelayLastRR += *_ptrRTCPData++ << 16;
|
||||
_packet.XRDLRRReportBlockItem.DelayLastRR += *_ptrRTCPData++ << 8;
|
||||
_packet.XRDLRRReportBlockItem.DelayLastRR += *_ptrRTCPData++;
|
||||
|
||||
_packetType = kRtcpXrDlrrReportBlockItemCode;
|
||||
--_numberOfBlocks;
|
||||
_state = State_XR_DLLRItem;
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
0 1 2 3
|
||||
@ -913,6 +1042,7 @@ RTCPUtility::RTCPParserV2::ParseXRItem()
|
||||
| JB maximum | JB abs max |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
bool
|
||||
RTCPUtility::RTCPParserV2::ParseXRVOIPMetricItem()
|
||||
{
|
||||
@ -920,6 +1050,7 @@ RTCPUtility::RTCPParserV2::ParseXRVOIPMetricItem()
|
||||
|
||||
if (length < 28)
|
||||
{
|
||||
_state = State_TopLevel;
|
||||
EndCurrentBlock();
|
||||
return false;
|
||||
}
|
||||
@ -967,6 +1098,7 @@ RTCPUtility::RTCPParserV2::ParseXRVOIPMetricItem()
|
||||
_packet.XRVOIPMetricItem.JBabsMax = *_ptrRTCPData++ << 8;
|
||||
_packet.XRVOIPMetricItem.JBabsMax += *_ptrRTCPData++;
|
||||
|
||||
_state = State_XRItem;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
namespace webrtc {
|
||||
namespace RTCPUtility {
|
||||
uint32_t MidNtp(uint32_t ntp_sec, uint32_t ntp_frac);
|
||||
|
||||
// CNAME
|
||||
struct RTCPCnameInformation
|
||||
{
|
||||
@ -74,6 +76,19 @@ namespace RTCPUtility {
|
||||
// RFC 3611
|
||||
uint32_t OriginatorSSRC;
|
||||
};
|
||||
struct RTCPPacketXRReceiverReferenceTimeItem
|
||||
{
|
||||
// RFC 3611 4.4
|
||||
uint32_t NTPMostSignificant;
|
||||
uint32_t NTPLeastSignificant;
|
||||
};
|
||||
struct RTCPPacketXRDLRRReportBlockItem
|
||||
{
|
||||
// RFC 3611 4.5
|
||||
uint32_t SSRC;
|
||||
uint32_t LastRR;
|
||||
uint32_t DelayLastRR;
|
||||
};
|
||||
struct RTCPPacketXRVOIPMetricItem
|
||||
{
|
||||
// RFC 3611 4.7
|
||||
@ -228,6 +243,8 @@ namespace RTCPUtility {
|
||||
RTCPPacketPSFBFIRItem FIRItem;
|
||||
|
||||
RTCPPacketXR XR;
|
||||
RTCPPacketXRReceiverReferenceTimeItem XRReceiverReferenceTimeItem;
|
||||
RTCPPacketXRDLRRReportBlockItem XRDLRRReportBlockItem;
|
||||
RTCPPacketXRVOIPMetricItem XRVOIPMetricItem;
|
||||
|
||||
RTCPPacketAPP APP;
|
||||
@ -274,6 +291,10 @@ namespace RTCPUtility {
|
||||
kRtcpRtpfbSrReqCode,
|
||||
|
||||
// RFC 3611
|
||||
kRtcpXrHeaderCode,
|
||||
kRtcpXrReceiverReferenceTimeCode,
|
||||
kRtcpXrDlrrReportBlockCode,
|
||||
kRtcpXrDlrrReportBlockItemCode,
|
||||
kRtcpXrVoipMetricCode,
|
||||
|
||||
kRtcpAppCode,
|
||||
@ -353,6 +374,7 @@ namespace RTCPUtility {
|
||||
State_PSFB_AppItem, // Application specific FCI item
|
||||
State_PSFB_REMBItem, // Application specific REMB item
|
||||
State_XRItem,
|
||||
State_XR_DLLRItem,
|
||||
State_AppItem
|
||||
};
|
||||
|
||||
@ -371,6 +393,8 @@ namespace RTCPUtility {
|
||||
void IteratePsfbAppItem();
|
||||
void IteratePsfbREMBItem();
|
||||
void IterateAppItem();
|
||||
void IterateXrItem();
|
||||
void IterateXrDlrrItem();
|
||||
|
||||
void Validate();
|
||||
void EndCurrentBlock();
|
||||
@ -391,6 +415,8 @@ namespace RTCPUtility {
|
||||
|
||||
bool ParseXR();
|
||||
bool ParseXRItem();
|
||||
bool ParseXRReceiverReferenceTimeItem();
|
||||
bool ParseXRDLRRReportBlockItem();
|
||||
bool ParseXRVOIPMetricItem();
|
||||
|
||||
bool ParseFBCommon(const RTCPCommonHeader& header);
|
||||
|
@ -1536,6 +1536,11 @@ uint32_t ModuleRtpRtcpImpl::SendTimeOfSendReport(
|
||||
return rtcp_sender_.SendTimeOfSendReport(send_report);
|
||||
}
|
||||
|
||||
bool ModuleRtpRtcpImpl::SendTimeOfXrRrReport(
|
||||
uint32_t mid_ntp, int64_t* time_ms) const {
|
||||
return rtcp_sender_.SendTimeOfXrRrReport(mid_ntp, time_ms);
|
||||
}
|
||||
|
||||
void ModuleRtpRtcpImpl::OnReceivedNACK(
|
||||
const std::list<uint16_t>& nack_sequence_numbers) {
|
||||
if (!rtp_sender_.StorePackets() ||
|
||||
@ -1566,6 +1571,11 @@ int32_t ModuleRtpRtcpImpl::LastReceivedNTP(
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ModuleRtpRtcpImpl::LastReceivedXrReferenceTimeInfo(
|
||||
RtcpReceiveTimeInfo* info) const {
|
||||
return rtcp_receiver_.LastReceivedXrReferenceTimeInfo(info);
|
||||
}
|
||||
|
||||
bool ModuleRtpRtcpImpl::UpdateRTCPReceiveInformationTimers() {
|
||||
// If this returns true this channel has timed out.
|
||||
// Periodically check if this is true and if so call UpdateTMMBR.
|
||||
|
@ -324,6 +324,8 @@ class ModuleRtpRtcpImpl : public RtpRtcp {
|
||||
uint32_t& NTPfrac,
|
||||
uint32_t& remote_sr);
|
||||
|
||||
virtual bool LastReceivedXrReferenceTimeInfo(RtcpReceiveTimeInfo* info) const;
|
||||
|
||||
virtual int32_t BoundingSet(bool& tmmbr_owner, TMMBRSet*& bounding_set_rec);
|
||||
|
||||
virtual void BitrateSent(uint32_t* total_rate,
|
||||
@ -333,6 +335,8 @@ class ModuleRtpRtcpImpl : public RtpRtcp {
|
||||
|
||||
virtual uint32_t SendTimeOfSendReport(const uint32_t send_report);
|
||||
|
||||
virtual bool SendTimeOfXrRrReport(uint32_t mid_ntp, int64_t* time_ms) const;
|
||||
|
||||
// Good state of RTP receiver inform sender.
|
||||
virtual int32_t SendRTCPReferencePictureSelection(
|
||||
const uint64_t picture_id) OVERRIDE;
|
||||
|
Loading…
Reference in New Issue
Block a user