In RtcpTransceiver support sending more than one rtcp receiver report per packet
Bug: webrtc:8239 Change-Id: I7670b8c10fbcfad7238afecd96edd55ad65dd3aa Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/249792 Reviewed-by: Emil Lundmark <lndmrk@webrtc.org> Commit-Queue: Danil Chapovalov <danilchap@webrtc.org> Cr-Commit-Position: refs/heads/main@{#35913}
This commit is contained in:

committed by
WebRTC LUCI CQ

parent
38b80a1d44
commit
8c0aaae664
@ -10,6 +10,7 @@
|
||||
|
||||
#include "modules/rtp_rtcp/source/rtcp_transceiver_impl.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/algorithm/container.h"
|
||||
@ -441,41 +442,112 @@ void RtcpTransceiverImpl::SchedulePeriodicCompoundPackets(int64_t delay_ms) {
|
||||
});
|
||||
}
|
||||
|
||||
void RtcpTransceiverImpl::CreateCompoundPacket(PacketSender* sender) {
|
||||
RTC_DCHECK(sender->IsEmpty());
|
||||
const uint32_t sender_ssrc = config_.feedback_ssrc;
|
||||
Timestamp now = config_.clock->CurrentTime();
|
||||
rtcp::ReceiverReport receiver_report;
|
||||
receiver_report.SetSenderSsrc(sender_ssrc);
|
||||
receiver_report.SetReportBlocks(CreateReportBlocks(now));
|
||||
if (config_.rtcp_mode == RtcpMode::kCompound ||
|
||||
!receiver_report.report_blocks().empty()) {
|
||||
sender->AppendPacket(receiver_report);
|
||||
void RtcpTransceiverImpl::FillReports(Timestamp now,
|
||||
size_t reserved_bytes,
|
||||
PacketSender& rtcp_sender) {
|
||||
// Sender/receiver reports should be first in the RTCP packet.
|
||||
RTC_DCHECK(rtcp_sender.IsEmpty());
|
||||
|
||||
size_t available_bytes = config_.max_packet_size;
|
||||
if (reserved_bytes > available_bytes) {
|
||||
// Because reserved_bytes is unsigned, substracting would underflow and will
|
||||
// not produce desired result.
|
||||
available_bytes = 0;
|
||||
} else {
|
||||
available_bytes -= reserved_bytes;
|
||||
}
|
||||
|
||||
if (!config_.cname.empty() && !sender->IsEmpty()) {
|
||||
rtcp::Sdes sdes;
|
||||
bool added = sdes.AddCName(config_.feedback_ssrc, config_.cname);
|
||||
RTC_DCHECK(added) << "Failed to add cname " << config_.cname
|
||||
<< " to rtcp sdes packet.";
|
||||
sender->AppendPacket(sdes);
|
||||
static constexpr size_t kReceiverReportSizeBytes = 8;
|
||||
static constexpr size_t kFullReceiverReportSizeBytes =
|
||||
kReceiverReportSizeBytes +
|
||||
rtcp::ReceiverReport::kMaxNumberOfReportBlocks *
|
||||
rtcp::ReportBlock::kLength;
|
||||
size_t max_full_receiver_reports =
|
||||
available_bytes / kFullReceiverReportSizeBytes;
|
||||
size_t max_report_blocks = max_full_receiver_reports *
|
||||
rtcp::ReceiverReport::kMaxNumberOfReportBlocks;
|
||||
size_t available_bytes_for_last_receiver_report =
|
||||
available_bytes -
|
||||
max_full_receiver_reports * kFullReceiverReportSizeBytes;
|
||||
if (available_bytes_for_last_receiver_report >= kReceiverReportSizeBytes) {
|
||||
max_report_blocks +=
|
||||
(available_bytes_for_last_receiver_report - kReceiverReportSizeBytes) /
|
||||
rtcp::ReportBlock::kLength;
|
||||
}
|
||||
if (remb_) {
|
||||
|
||||
std::vector<rtcp::ReportBlock> report_blocks =
|
||||
CreateReportBlocks(now, max_report_blocks);
|
||||
|
||||
size_t num_receiver_reports =
|
||||
(report_blocks.size() + rtcp::ReceiverReport::kMaxNumberOfReportBlocks -
|
||||
1) /
|
||||
rtcp::ReceiverReport::kMaxNumberOfReportBlocks;
|
||||
|
||||
// In compund mode each RTCP packet has to start with a sender or receiver
|
||||
// report.
|
||||
if (config_.rtcp_mode == RtcpMode::kCompound && num_receiver_reports == 0) {
|
||||
num_receiver_reports = 1;
|
||||
}
|
||||
|
||||
auto report_block_it = report_blocks.begin();
|
||||
for (size_t i = 0; i < num_receiver_reports; ++i) {
|
||||
rtcp::ReceiverReport receiver_report;
|
||||
receiver_report.SetSenderSsrc(config_.feedback_ssrc);
|
||||
size_t num_blocks =
|
||||
std::min<size_t>(rtcp::ReceiverReport::kMaxNumberOfReportBlocks,
|
||||
report_blocks.end() - report_block_it);
|
||||
std::vector<rtcp::ReportBlock> sub_blocks(report_block_it,
|
||||
report_block_it + num_blocks);
|
||||
receiver_report.SetReportBlocks(std::move(sub_blocks));
|
||||
report_block_it += num_blocks;
|
||||
rtcp_sender.AppendPacket(receiver_report);
|
||||
}
|
||||
// All report blocks should be attached at this point.
|
||||
RTC_DCHECK_EQ(report_blocks.end() - report_block_it, 0);
|
||||
}
|
||||
|
||||
void RtcpTransceiverImpl::CreateCompoundPacket(Timestamp now,
|
||||
size_t reserved_bytes,
|
||||
PacketSender& sender) {
|
||||
RTC_DCHECK(sender.IsEmpty());
|
||||
absl::optional<rtcp::Sdes> sdes;
|
||||
if (!config_.cname.empty()) {
|
||||
sdes.emplace();
|
||||
bool added = sdes->AddCName(config_.feedback_ssrc, config_.cname);
|
||||
RTC_DCHECK(added) << "Failed to add CNAME " << config_.cname
|
||||
<< " to RTCP SDES packet.";
|
||||
reserved_bytes += sdes->BlockLength();
|
||||
}
|
||||
if (remb_.has_value()) {
|
||||
reserved_bytes += remb_->BlockLength();
|
||||
}
|
||||
if (config_.non_sender_rtt_measurement) {
|
||||
// 4 bytes for common RTCP header + 4 bytes for the ExtenedReports header.
|
||||
reserved_bytes += (4 + 4 + rtcp::Rrtr::kLength);
|
||||
}
|
||||
|
||||
FillReports(now, reserved_bytes, sender);
|
||||
const uint32_t sender_ssrc = config_.feedback_ssrc;
|
||||
|
||||
if (sdes.has_value() && !sender.IsEmpty()) {
|
||||
sender.AppendPacket(*sdes);
|
||||
}
|
||||
if (remb_.has_value()) {
|
||||
remb_->SetSenderSsrc(sender_ssrc);
|
||||
sender->AppendPacket(*remb_);
|
||||
sender.AppendPacket(*remb_);
|
||||
}
|
||||
// TODO(bugs.webrtc.org/8239): Do not send rrtr if this packet starts with
|
||||
// SenderReport instead of ReceiverReport
|
||||
// when RtcpTransceiver supports rtp senders.
|
||||
if (config_.non_sender_rtt_measurement) {
|
||||
rtcp::ExtendedReports xr;
|
||||
xr.SetSenderSsrc(sender_ssrc);
|
||||
|
||||
rtcp::Rrtr rrtr;
|
||||
rrtr.SetNtp(config_.clock->ConvertTimestampToNtpTime(now));
|
||||
xr.SetRrtr(rrtr);
|
||||
|
||||
xr.SetSenderSsrc(sender_ssrc);
|
||||
sender->AppendPacket(xr);
|
||||
sender.AppendPacket(xr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -483,8 +555,9 @@ void RtcpTransceiverImpl::SendPeriodicCompoundPacket() {
|
||||
auto send_packet = [this](rtc::ArrayView<const uint8_t> packet) {
|
||||
config_.outgoing_transport->SendRtcp(packet.data(), packet.size());
|
||||
};
|
||||
Timestamp now = config_.clock->CurrentTime();
|
||||
PacketSender sender(send_packet, config_.max_packet_size);
|
||||
CreateCompoundPacket(&sender);
|
||||
CreateCompoundPacket(now, /*reserved_bytes=*/0, sender);
|
||||
sender.Send();
|
||||
}
|
||||
|
||||
@ -510,8 +583,11 @@ void RtcpTransceiverImpl::SendImmediateFeedback(
|
||||
PacketSender sender(send_packet, config_.max_packet_size);
|
||||
// Compound mode requires every sent rtcp packet to be compound, i.e. start
|
||||
// with a sender or receiver report.
|
||||
if (config_.rtcp_mode == RtcpMode::kCompound)
|
||||
CreateCompoundPacket(&sender);
|
||||
if (config_.rtcp_mode == RtcpMode::kCompound) {
|
||||
Timestamp now = config_.clock->CurrentTime();
|
||||
CreateCompoundPacket(now, /*reserved_bytes=*/rtcp_packet.BlockLength(),
|
||||
sender);
|
||||
}
|
||||
|
||||
sender.AppendPacket(rtcp_packet);
|
||||
sender.Send();
|
||||
@ -522,14 +598,12 @@ void RtcpTransceiverImpl::SendImmediateFeedback(
|
||||
}
|
||||
|
||||
std::vector<rtcp::ReportBlock> RtcpTransceiverImpl::CreateReportBlocks(
|
||||
Timestamp now) {
|
||||
Timestamp now,
|
||||
size_t num_max_blocks) {
|
||||
if (!config_.receive_statistics)
|
||||
return {};
|
||||
// TODO(danilchap): Support sending more than
|
||||
// `ReceiverReport::kMaxNumberOfReportBlocks` per compound rtcp packet.
|
||||
std::vector<rtcp::ReportBlock> report_blocks =
|
||||
config_.receive_statistics->RtcpReportBlocks(
|
||||
rtcp::ReceiverReport::kMaxNumberOfReportBlocks);
|
||||
config_.receive_statistics->RtcpReportBlocks(num_max_blocks);
|
||||
uint32_t last_sr = 0;
|
||||
uint32_t last_delay = 0;
|
||||
for (rtcp::ReportBlock& report_block : report_blocks) {
|
||||
|
@ -104,14 +104,24 @@ class RtcpTransceiverImpl {
|
||||
|
||||
void ReschedulePeriodicCompoundPackets();
|
||||
void SchedulePeriodicCompoundPackets(int64_t delay_ms);
|
||||
// Appends RTCP receiver reports with attached report blocks to the `sender`.
|
||||
// Uses up to `config_.max_packet_size - reserved_bytes`
|
||||
void FillReports(Timestamp now,
|
||||
size_t reserved_bytes,
|
||||
PacketSender& rtcp_sender);
|
||||
|
||||
// Creates compound RTCP packet, as defined in
|
||||
// https://tools.ietf.org/html/rfc5506#section-2
|
||||
void CreateCompoundPacket(PacketSender* sender);
|
||||
void CreateCompoundPacket(Timestamp now,
|
||||
size_t reserved_bytes,
|
||||
PacketSender& rtcp_sender);
|
||||
|
||||
// Sends RTCP packets.
|
||||
void SendPeriodicCompoundPacket();
|
||||
void SendImmediateFeedback(const rtcp::RtcpPacket& rtcp_packet);
|
||||
// Generate Report Blocks to be send in Sender or Receiver Report.
|
||||
std::vector<rtcp::ReportBlock> CreateReportBlocks(Timestamp now);
|
||||
// Generate Report Blocks to be send in Sender or Receiver Reports.
|
||||
std::vector<rtcp::ReportBlock> CreateReportBlocks(Timestamp now,
|
||||
size_t num_max_blocks);
|
||||
|
||||
const RtcpTransceiverConfig config_;
|
||||
|
||||
|
@ -40,6 +40,7 @@ namespace {
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::Ge;
|
||||
using ::testing::NiceMock;
|
||||
using ::testing::Return;
|
||||
using ::testing::SizeIs;
|
||||
@ -946,6 +947,61 @@ TEST(RtcpTransceiverImplTest,
|
||||
EXPECT_EQ(CompactNtpRttToMs(report_blocks[1].delay_since_last_sr()), 100);
|
||||
}
|
||||
|
||||
TEST(RtcpTransceiverImplTest, MaySendMultipleReceiverReportInSinglePacket) {
|
||||
std::vector<ReportBlock> statistics_report_blocks(40);
|
||||
MockReceiveStatisticsProvider receive_statistics;
|
||||
EXPECT_CALL(receive_statistics, RtcpReportBlocks(/*max_blocks=*/Ge(40u)))
|
||||
.WillOnce(Return(statistics_report_blocks));
|
||||
|
||||
SimulatedClock clock(0);
|
||||
RtcpTransceiverConfig config = DefaultTestConfig();
|
||||
config.clock = &clock;
|
||||
RtcpPacketParser rtcp_parser;
|
||||
RtcpParserTransport transport(&rtcp_parser);
|
||||
config.outgoing_transport = &transport;
|
||||
config.receive_statistics = &receive_statistics;
|
||||
RtcpTransceiverImpl rtcp_transceiver(config);
|
||||
|
||||
// Trigger ReceiverReports.
|
||||
rtcp_transceiver.SendCompoundPacket();
|
||||
|
||||
// Expect a single RTCP packet with multiple receiver reports in it.
|
||||
EXPECT_EQ(transport.num_packets(), 1);
|
||||
// Receiver report may contain up to 31 report blocks, thus 2 reports are
|
||||
// needed to carry 40 blocks: 31 in the first, 9 in the last.
|
||||
EXPECT_EQ(rtcp_parser.receiver_report()->num_packets(), 2);
|
||||
// RtcpParser remembers just the last receiver report, thus can't check number
|
||||
// of blocks in the first receiver report.
|
||||
EXPECT_THAT(rtcp_parser.receiver_report()->report_blocks(), SizeIs(9));
|
||||
}
|
||||
|
||||
TEST(RtcpTransceiverImplTest, AttachMaxNumberOfReportBlocksToCompoundPacket) {
|
||||
MockReceiveStatisticsProvider receive_statistics;
|
||||
EXPECT_CALL(receive_statistics, RtcpReportBlocks)
|
||||
.WillOnce([](size_t max_blocks) {
|
||||
return std::vector<ReportBlock>(max_blocks);
|
||||
});
|
||||
SimulatedClock clock(0);
|
||||
RtcpTransceiverConfig config = DefaultTestConfig();
|
||||
config.clock = &clock;
|
||||
config.rtcp_mode = RtcpMode::kCompound;
|
||||
RtcpPacketParser rtcp_parser;
|
||||
RtcpParserTransport transport(&rtcp_parser);
|
||||
config.outgoing_transport = &transport;
|
||||
config.receive_statistics = &receive_statistics;
|
||||
RtcpTransceiverImpl rtcp_transceiver(config);
|
||||
|
||||
EXPECT_EQ(transport.num_packets(), 0);
|
||||
// Send some fast feedback message. Because of compound mode, report blocks
|
||||
// should be attached.
|
||||
rtcp_transceiver.SendPictureLossIndication(/*ssrc=*/123);
|
||||
|
||||
// Expect single RTCP packet with multiple receiver reports and a PLI.
|
||||
EXPECT_EQ(transport.num_packets(), 1);
|
||||
EXPECT_GT(rtcp_parser.receiver_report()->num_packets(), 1);
|
||||
EXPECT_EQ(rtcp_parser.pli()->num_packets(), 1);
|
||||
}
|
||||
|
||||
TEST(RtcpTransceiverImplTest, SendsNack) {
|
||||
const uint32_t kSenderSsrc = 1234;
|
||||
const uint32_t kRemoteSsrc = 4321;
|
||||
|
Reference in New Issue
Block a user