webrtc/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc
Danil Chapovalov 52518633fb In RtcpReceiver implement calling NetworkLinkRtcpObserver interface
With intent to fully replace RtcpBandwidthObserver interface
and half of the TransportFeedbackObserver interface

RtcpBandwidthObserver interfaces passed bitrate and time variables as
raw ints, NetworkLinkRtcpObserver uses more expressive types.

Bug: webrtc:13757, webrtc:8239
Change-Id: I0a8c8de626fbe0c190a0a1a9f6733d863494401c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/304700
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Reviewed-by: Åsa Persson <asapersson@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#40043}
2023-05-10 18:58:31 +00:00

1722 lines
67 KiB
C++

/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "modules/rtp_rtcp/source/rtcp_transceiver_impl.h"
#include <memory>
#include <utility>
#include <vector>
#include "absl/memory/memory.h"
#include "api/rtp_headers.h"
#include "api/test/create_time_controller.h"
#include "api/test/time_controller.h"
#include "api/units/data_rate.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "api/video/video_bitrate_allocation.h"
#include "modules/rtp_rtcp/include/receive_statistics.h"
#include "modules/rtp_rtcp/include/report_block_data.h"
#include "modules/rtp_rtcp/mocks/mock_network_link_rtcp_observer.h"
#include "modules/rtp_rtcp/mocks/mock_rtcp_rtt_stats.h"
#include "modules/rtp_rtcp/source/rtcp_packet/app.h"
#include "modules/rtp_rtcp/source/rtcp_packet/bye.h"
#include "modules/rtp_rtcp/source/rtcp_packet/compound_packet.h"
#include "modules/rtp_rtcp/source/time_util.h"
#include "system_wrappers/include/clock.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "test/mock_transport.h"
#include "test/rtcp_packet_parser.h"
namespace webrtc {
namespace {
using ::testing::_;
using ::testing::ElementsAre;
using ::testing::ElementsAreArray;
using ::testing::Ge;
using ::testing::NiceMock;
using ::testing::Property;
using ::testing::Return;
using ::testing::SizeIs;
using ::testing::StrictMock;
using ::testing::UnorderedElementsAre;
using ::testing::WithArg;
using ::webrtc::rtcp::Bye;
using ::webrtc::rtcp::CompoundPacket;
using ::webrtc::rtcp::ReportBlock;
using ::webrtc::rtcp::SenderReport;
using ::webrtc::test::RtcpPacketParser;
class MockReceiveStatisticsProvider : public ReceiveStatisticsProvider {
public:
MOCK_METHOD(std::vector<ReportBlock>, RtcpReportBlocks, (size_t), (override));
};
class MockMediaReceiverRtcpObserver : public MediaReceiverRtcpObserver {
public:
MOCK_METHOD(void, OnSenderReport, (uint32_t, NtpTime, uint32_t), (override));
MOCK_METHOD(void, OnBye, (uint32_t), (override));
MOCK_METHOD(void,
OnBitrateAllocation,
(uint32_t, const VideoBitrateAllocation&),
(override));
};
class MockRtpStreamRtcpHandler : public RtpStreamRtcpHandler {
public:
MockRtpStreamRtcpHandler() {
// With each next call increase number of sent packets and bytes to simulate
// active RTP sender.
ON_CALL(*this, SentStats).WillByDefault([this] {
RtpStats stats;
stats.set_num_sent_packets(++num_calls_);
stats.set_num_sent_bytes(1'000 * num_calls_);
return stats;
});
}
MOCK_METHOD(RtpStats, SentStats, (), (override));
MOCK_METHOD(void,
OnNack,
(uint32_t, rtc::ArrayView<const uint16_t>),
(override));
MOCK_METHOD(void, OnFir, (uint32_t), (override));
MOCK_METHOD(void, OnPli, (uint32_t), (override));
MOCK_METHOD(void, OnReport, (const ReportBlockData&), (override));
private:
int num_calls_ = 0;
};
constexpr TimeDelta kReportPeriod = TimeDelta::Seconds(1);
constexpr TimeDelta kAlmostForever = TimeDelta::Seconds(2);
constexpr TimeDelta kTimePrecision = TimeDelta::Millis(1);
MATCHER_P(Near, value, "") {
return arg > value - kTimePrecision && arg < value + kTimePrecision;
}
// Helper to wait for an rtcp packet produced on a different thread/task queue.
class FakeRtcpTransport : public webrtc::Transport {
public:
explicit FakeRtcpTransport(TimeController& time) : time_(time) {}
bool SendRtcp(const uint8_t* data, size_t size) override {
sent_rtcp_ = true;
return true;
}
bool SendRtp(const uint8_t*, size_t, const webrtc::PacketOptions&) override {
ADD_FAILURE() << "RtcpTransciver shouldn't send rtp packets.";
return true;
}
// Returns true when packet was received by the transport.
bool WaitPacket() {
bool got_packet = time_.Wait([this] { return sent_rtcp_; }, kAlmostForever);
// Clear the 'event' to allow waiting for multiple packets.
sent_rtcp_ = false;
return got_packet;
}
private:
TimeController& time_;
bool sent_rtcp_ = false;
};
class RtcpParserTransport : public webrtc::Transport {
public:
explicit RtcpParserTransport(RtcpPacketParser* parser) : parser_(parser) {}
// Returns total number of rtcp packet received.
int num_packets() const { return num_packets_; }
private:
bool SendRtcp(const uint8_t* data, size_t size) override {
++num_packets_;
parser_->Parse(data, size);
return true;
}
bool SendRtp(const uint8_t*, size_t, const webrtc::PacketOptions&) override {
ADD_FAILURE() << "RtcpTransciver shouldn't send rtp packets.";
return true;
}
RtcpPacketParser* const parser_;
int num_packets_ = 0;
};
class RtcpTransceiverImplTest : public ::testing::Test {
public:
RtcpTransceiverConfig DefaultTestConfig() {
// RtcpTransceiverConfig default constructor sets default values for prod.
// Test doesn't need to support all key features: Default test config
// returns valid config with all features turned off.
RtcpTransceiverConfig config;
config.clock = time_->GetClock();
config.outgoing_transport = &null_transport_;
config.schedule_periodic_compound_packets = false;
config.initial_report_delay = kReportPeriod / 2;
config.report_period = kReportPeriod;
return config;
}
TimeController& time_controller() { return *time_; }
Timestamp CurrentTime() { return time_->GetClock()->CurrentTime(); }
void AdvanceTime(TimeDelta time) { time_->AdvanceTime(time); }
std::unique_ptr<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue() {
return time_->GetTaskQueueFactory()->CreateTaskQueue(
"rtcp", TaskQueueFactory::Priority::NORMAL);
}
private:
MockTransport null_transport_;
std::unique_ptr<TimeController> time_ = CreateSimulatedTimeController();
};
TEST_F(RtcpTransceiverImplTest, NeedToStopPeriodicTaskToDestroyOnTaskQueue) {
FakeRtcpTransport transport(time_controller());
auto queue = CreateTaskQueue();
RtcpTransceiverConfig config = DefaultTestConfig();
config.task_queue = queue.get();
config.schedule_periodic_compound_packets = true;
config.outgoing_transport = &transport;
auto* rtcp_transceiver = new RtcpTransceiverImpl(config);
// Wait for a periodic packet.
EXPECT_TRUE(transport.WaitPacket());
bool done = false;
queue->PostTask([rtcp_transceiver, &done] {
rtcp_transceiver->StopPeriodicTask();
delete rtcp_transceiver;
done = true;
});
ASSERT_TRUE(time_controller().Wait([&] { return done; }, kAlmostForever));
}
TEST_F(RtcpTransceiverImplTest, CanBeDestroyedRightAfterCreation) {
FakeRtcpTransport transport(time_controller());
auto queue = CreateTaskQueue();
RtcpTransceiverConfig config = DefaultTestConfig();
config.task_queue = queue.get();
config.schedule_periodic_compound_packets = true;
config.outgoing_transport = &transport;
bool done = false;
queue->PostTask([&] {
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.StopPeriodicTask();
done = true;
});
ASSERT_TRUE(time_controller().Wait([&] { return done; }, kAlmostForever));
}
TEST_F(RtcpTransceiverImplTest, CanDestroyAfterTaskQueue) {
FakeRtcpTransport transport(time_controller());
auto queue = CreateTaskQueue();
RtcpTransceiverConfig config = DefaultTestConfig();
config.task_queue = queue.get();
config.schedule_periodic_compound_packets = true;
config.outgoing_transport = &transport;
auto* rtcp_transceiver = new RtcpTransceiverImpl(config);
// Wait for a periodic packet.
EXPECT_TRUE(transport.WaitPacket());
queue = nullptr;
delete rtcp_transceiver;
}
TEST_F(RtcpTransceiverImplTest, DelaysSendingFirstCompondPacket) {
auto queue = CreateTaskQueue();
FakeRtcpTransport transport(time_controller());
RtcpTransceiverConfig config = DefaultTestConfig();
config.schedule_periodic_compound_packets = true;
config.outgoing_transport = &transport;
config.initial_report_delay = TimeDelta::Millis(10);
config.task_queue = queue.get();
absl::optional<RtcpTransceiverImpl> rtcp_transceiver;
Timestamp started = CurrentTime();
queue->PostTask([&] { rtcp_transceiver.emplace(config); });
EXPECT_TRUE(transport.WaitPacket());
EXPECT_GE(CurrentTime() - started, config.initial_report_delay);
// Cleanup.
bool done = false;
queue->PostTask([&] {
rtcp_transceiver->StopPeriodicTask();
rtcp_transceiver.reset();
done = true;
});
ASSERT_TRUE(time_controller().Wait([&] { return done; }, kAlmostForever));
}
TEST_F(RtcpTransceiverImplTest, PeriodicallySendsPackets) {
auto queue = CreateTaskQueue();
FakeRtcpTransport transport(time_controller());
RtcpTransceiverConfig config = DefaultTestConfig();
config.schedule_periodic_compound_packets = true;
config.outgoing_transport = &transport;
config.initial_report_delay = TimeDelta::Zero();
config.report_period = kReportPeriod;
config.task_queue = queue.get();
absl::optional<RtcpTransceiverImpl> rtcp_transceiver;
Timestamp time_just_before_1st_packet = Timestamp::MinusInfinity();
queue->PostTask([&] {
// Because initial_report_delay_ms is set to 0, time_just_before_the_packet
// should be very close to the time_of_the_packet.
time_just_before_1st_packet = CurrentTime();
rtcp_transceiver.emplace(config);
});
EXPECT_TRUE(transport.WaitPacket());
EXPECT_TRUE(transport.WaitPacket());
Timestamp time_just_after_2nd_packet = CurrentTime();
EXPECT_GE(time_just_after_2nd_packet - time_just_before_1st_packet,
config.report_period);
// Cleanup.
bool done = false;
queue->PostTask([&] {
rtcp_transceiver->StopPeriodicTask();
rtcp_transceiver.reset();
done = true;
});
ASSERT_TRUE(time_controller().Wait([&] { return done; }, kAlmostForever));
}
TEST_F(RtcpTransceiverImplTest, SendCompoundPacketDelaysPeriodicSendPackets) {
auto queue = CreateTaskQueue();
FakeRtcpTransport transport(time_controller());
RtcpTransceiverConfig config = DefaultTestConfig();
config.schedule_periodic_compound_packets = true;
config.outgoing_transport = &transport;
config.initial_report_delay = TimeDelta::Zero();
config.report_period = kReportPeriod;
config.task_queue = queue.get();
absl::optional<RtcpTransceiverImpl> rtcp_transceiver;
queue->PostTask([&] { rtcp_transceiver.emplace(config); });
// Wait for the first packet.
EXPECT_TRUE(transport.WaitPacket());
// Send non periodic one after half period.
bool non_periodic = false;
Timestamp time_of_non_periodic_packet = Timestamp::MinusInfinity();
queue->PostDelayedTask(
[&] {
time_of_non_periodic_packet = CurrentTime();
rtcp_transceiver->SendCompoundPacket();
non_periodic = true;
},
config.report_period / 2);
// Though non-periodic packet is scheduled just in between periodic, due to
// small period and task queue flakiness it migth end-up 1ms after next
// periodic packet. To be sure duration after non-periodic packet is tested
// wait for transport after ensuring non-periodic packet was sent.
EXPECT_TRUE(
time_controller().Wait([&] { return non_periodic; }, kAlmostForever));
EXPECT_TRUE(transport.WaitPacket());
// Wait for next periodic packet.
EXPECT_TRUE(transport.WaitPacket());
Timestamp time_of_last_periodic_packet = CurrentTime();
EXPECT_GE(time_of_last_periodic_packet - time_of_non_periodic_packet,
config.report_period);
// Cleanup.
bool done = false;
queue->PostTask([&] {
rtcp_transceiver->StopPeriodicTask();
rtcp_transceiver.reset();
done = true;
});
ASSERT_TRUE(time_controller().Wait([&] { return done; }, kAlmostForever));
}
TEST_F(RtcpTransceiverImplTest, SendsNoRtcpWhenNetworkStateIsDown) {
MockTransport mock_transport;
RtcpTransceiverConfig config = DefaultTestConfig();
config.initial_ready_to_send = false;
config.outgoing_transport = &mock_transport;
RtcpTransceiverImpl rtcp_transceiver(config);
EXPECT_CALL(mock_transport, SendRtcp(_, _)).Times(0);
const uint8_t raw[] = {1, 2, 3, 4};
const std::vector<uint16_t> sequence_numbers = {45, 57};
const uint32_t ssrcs[] = {123};
rtcp_transceiver.SendCompoundPacket();
rtcp_transceiver.SendRawPacket(raw);
rtcp_transceiver.SendNack(ssrcs[0], sequence_numbers);
rtcp_transceiver.SendPictureLossIndication(ssrcs[0]);
rtcp_transceiver.SendFullIntraRequest(ssrcs, true);
}
TEST_F(RtcpTransceiverImplTest, SendsRtcpWhenNetworkStateIsUp) {
MockTransport mock_transport;
RtcpTransceiverConfig config = DefaultTestConfig();
config.initial_ready_to_send = false;
config.outgoing_transport = &mock_transport;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SetReadyToSend(true);
EXPECT_CALL(mock_transport, SendRtcp(_, _)).Times(5);
const uint8_t raw[] = {1, 2, 3, 4};
const std::vector<uint16_t> sequence_numbers = {45, 57};
const uint32_t ssrcs[] = {123};
rtcp_transceiver.SendCompoundPacket();
rtcp_transceiver.SendRawPacket(raw);
rtcp_transceiver.SendNack(ssrcs[0], sequence_numbers);
rtcp_transceiver.SendPictureLossIndication(ssrcs[0]);
rtcp_transceiver.SendFullIntraRequest(ssrcs, true);
}
TEST_F(RtcpTransceiverImplTest, SendsPeriodicRtcpWhenNetworkStateIsUp) {
auto queue = CreateTaskQueue();
FakeRtcpTransport transport(time_controller());
RtcpTransceiverConfig config = DefaultTestConfig();
config.schedule_periodic_compound_packets = true;
config.initial_ready_to_send = false;
config.outgoing_transport = &transport;
config.task_queue = queue.get();
absl::optional<RtcpTransceiverImpl> rtcp_transceiver;
rtcp_transceiver.emplace(config);
queue->PostTask([&] { rtcp_transceiver->SetReadyToSend(true); });
EXPECT_TRUE(transport.WaitPacket());
// Cleanup.
bool done = false;
queue->PostTask([&] {
rtcp_transceiver->StopPeriodicTask();
rtcp_transceiver.reset();
done = true;
});
ASSERT_TRUE(time_controller().Wait([&] { return done; }, kAlmostForever));
}
TEST_F(RtcpTransceiverImplTest, SendsMinimalCompoundPacket) {
const uint32_t kSenderSsrc = 12345;
RtcpTransceiverConfig config = DefaultTestConfig();
config.feedback_ssrc = kSenderSsrc;
config.cname = "cname";
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
config.schedule_periodic_compound_packets = false;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SendCompoundPacket();
// Minimal compound RTCP packet contains sender or receiver report and sdes
// with cname.
ASSERT_GT(rtcp_parser.receiver_report()->num_packets(), 0);
EXPECT_EQ(rtcp_parser.receiver_report()->sender_ssrc(), kSenderSsrc);
ASSERT_GT(rtcp_parser.sdes()->num_packets(), 0);
ASSERT_EQ(rtcp_parser.sdes()->chunks().size(), 1u);
EXPECT_EQ(rtcp_parser.sdes()->chunks()[0].ssrc, kSenderSsrc);
EXPECT_EQ(rtcp_parser.sdes()->chunks()[0].cname, config.cname);
}
TEST_F(RtcpTransceiverImplTest, AvoidsEmptyPacketsInReducedMode) {
MockTransport transport;
EXPECT_CALL(transport, SendRtcp).Times(0);
NiceMock<MockReceiveStatisticsProvider> receive_statistics;
RtcpTransceiverConfig config = DefaultTestConfig();
config.outgoing_transport = &transport;
config.rtcp_mode = webrtc::RtcpMode::kReducedSize;
config.receive_statistics = &receive_statistics;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SendCompoundPacket();
}
TEST_F(RtcpTransceiverImplTest, AvoidsEmptyReceiverReportsInReducedMode) {
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
NiceMock<MockReceiveStatisticsProvider> receive_statistics;
RtcpTransceiverConfig config = DefaultTestConfig();
config.outgoing_transport = &transport;
config.rtcp_mode = webrtc::RtcpMode::kReducedSize;
config.receive_statistics = &receive_statistics;
// Set it to produce something (RRTR) in the "periodic" rtcp packets.
config.non_sender_rtt_measurement = true;
RtcpTransceiverImpl rtcp_transceiver(config);
// Rather than waiting for the right time to produce the periodic packet,
// trigger it manually.
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(rtcp_parser.receiver_report()->num_packets(), 0);
EXPECT_GT(rtcp_parser.xr()->num_packets(), 0);
}
TEST_F(RtcpTransceiverImplTest, SendsNoRembInitially) {
const uint32_t kSenderSsrc = 12345;
RtcpTransceiverConfig config = DefaultTestConfig();
config.feedback_ssrc = kSenderSsrc;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
config.schedule_periodic_compound_packets = false;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(transport.num_packets(), 1);
EXPECT_EQ(rtcp_parser.remb()->num_packets(), 0);
}
TEST_F(RtcpTransceiverImplTest, SetRembIncludesRembInNextCompoundPacket) {
const uint32_t kSenderSsrc = 12345;
RtcpTransceiverConfig config = DefaultTestConfig();
config.feedback_ssrc = kSenderSsrc;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
config.schedule_periodic_compound_packets = false;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SetRemb(/*bitrate_bps=*/10000, /*ssrcs=*/{54321, 64321});
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(rtcp_parser.remb()->num_packets(), 1);
EXPECT_EQ(rtcp_parser.remb()->sender_ssrc(), kSenderSsrc);
EXPECT_EQ(rtcp_parser.remb()->bitrate_bps(), 10000);
EXPECT_THAT(rtcp_parser.remb()->ssrcs(), ElementsAre(54321, 64321));
}
TEST_F(RtcpTransceiverImplTest, SetRembUpdatesValuesToSend) {
const uint32_t kSenderSsrc = 12345;
RtcpTransceiverConfig config = DefaultTestConfig();
config.feedback_ssrc = kSenderSsrc;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
config.schedule_periodic_compound_packets = false;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SetRemb(/*bitrate_bps=*/10000, /*ssrcs=*/{54321, 64321});
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(rtcp_parser.remb()->num_packets(), 1);
EXPECT_EQ(rtcp_parser.remb()->bitrate_bps(), 10000);
EXPECT_THAT(rtcp_parser.remb()->ssrcs(), ElementsAre(54321, 64321));
rtcp_transceiver.SetRemb(/*bitrate_bps=*/70000, /*ssrcs=*/{67321});
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(rtcp_parser.remb()->num_packets(), 2);
EXPECT_EQ(rtcp_parser.remb()->bitrate_bps(), 70000);
EXPECT_THAT(rtcp_parser.remb()->ssrcs(), ElementsAre(67321));
}
TEST_F(RtcpTransceiverImplTest, SetRembSendsImmediatelyIfSendRembOnChange) {
const uint32_t kSenderSsrc = 12345;
RtcpTransceiverConfig config = DefaultTestConfig();
config.send_remb_on_change = true;
config.feedback_ssrc = kSenderSsrc;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
config.schedule_periodic_compound_packets = false;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SetRemb(/*bitrate_bps=*/10000, /*ssrcs=*/{});
EXPECT_EQ(rtcp_parser.remb()->num_packets(), 1);
EXPECT_EQ(rtcp_parser.remb()->sender_ssrc(), kSenderSsrc);
EXPECT_EQ(rtcp_parser.remb()->bitrate_bps(), 10000);
// If there is no change, the packet is not sent immediately.
rtcp_transceiver.SetRemb(/*bitrate_bps=*/10000, /*ssrcs=*/{});
EXPECT_EQ(rtcp_parser.remb()->num_packets(), 1);
rtcp_transceiver.SetRemb(/*bitrate_bps=*/20000, /*ssrcs=*/{});
EXPECT_EQ(rtcp_parser.remb()->num_packets(), 2);
EXPECT_EQ(rtcp_parser.remb()->bitrate_bps(), 20000);
}
TEST_F(RtcpTransceiverImplTest,
SetRembSendsImmediatelyIfSendRembOnChangeReducedSize) {
const uint32_t kSenderSsrc = 12345;
RtcpTransceiverConfig config = DefaultTestConfig();
config.send_remb_on_change = true;
config.rtcp_mode = webrtc::RtcpMode::kReducedSize;
config.feedback_ssrc = kSenderSsrc;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
config.schedule_periodic_compound_packets = false;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SetRemb(/*bitrate_bps=*/10000, /*ssrcs=*/{});
EXPECT_EQ(rtcp_parser.remb()->num_packets(), 1);
EXPECT_EQ(rtcp_parser.remb()->sender_ssrc(), kSenderSsrc);
EXPECT_EQ(rtcp_parser.remb()->bitrate_bps(), 10000);
}
TEST_F(RtcpTransceiverImplTest, SetRembIncludesRembInAllCompoundPackets) {
const uint32_t kSenderSsrc = 12345;
RtcpTransceiverConfig config = DefaultTestConfig();
config.feedback_ssrc = kSenderSsrc;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
config.schedule_periodic_compound_packets = false;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SetRemb(/*bitrate_bps=*/10000, /*ssrcs=*/{54321, 64321});
rtcp_transceiver.SendCompoundPacket();
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(transport.num_packets(), 2);
EXPECT_EQ(rtcp_parser.remb()->num_packets(), 2);
}
TEST_F(RtcpTransceiverImplTest, SendsNoRembAfterUnset) {
const uint32_t kSenderSsrc = 12345;
RtcpTransceiverConfig config = DefaultTestConfig();
config.feedback_ssrc = kSenderSsrc;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
config.schedule_periodic_compound_packets = false;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SetRemb(/*bitrate_bps=*/10000, /*ssrcs=*/{54321, 64321});
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(transport.num_packets(), 1);
ASSERT_EQ(rtcp_parser.remb()->num_packets(), 1);
rtcp_transceiver.UnsetRemb();
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(transport.num_packets(), 2);
EXPECT_EQ(rtcp_parser.remb()->num_packets(), 1);
}
TEST_F(RtcpTransceiverImplTest, ReceiverReportUsesReceiveStatistics) {
const uint32_t kSenderSsrc = 12345;
const uint32_t kMediaSsrc = 54321;
MockReceiveStatisticsProvider receive_statistics;
std::vector<ReportBlock> report_blocks(1);
report_blocks[0].SetMediaSsrc(kMediaSsrc);
EXPECT_CALL(receive_statistics, RtcpReportBlocks(_))
.WillRepeatedly(Return(report_blocks));
RtcpTransceiverConfig config = DefaultTestConfig();
config.feedback_ssrc = kSenderSsrc;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
config.receive_statistics = &receive_statistics;
config.schedule_periodic_compound_packets = false;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SendCompoundPacket();
ASSERT_GT(rtcp_parser.receiver_report()->num_packets(), 0);
EXPECT_EQ(rtcp_parser.receiver_report()->sender_ssrc(), kSenderSsrc);
ASSERT_THAT(rtcp_parser.receiver_report()->report_blocks(),
SizeIs(report_blocks.size()));
EXPECT_EQ(rtcp_parser.receiver_report()->report_blocks()[0].source_ssrc(),
kMediaSsrc);
}
TEST_F(RtcpTransceiverImplTest, MultipleObserversOnSameSsrc) {
const uint32_t kRemoteSsrc = 12345;
StrictMock<MockMediaReceiverRtcpObserver> observer1;
StrictMock<MockMediaReceiverRtcpObserver> observer2;
RtcpTransceiverConfig config = DefaultTestConfig();
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, &observer1);
rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, &observer2);
const NtpTime kRemoteNtp(0x9876543211);
const uint32_t kRemoteRtp = 0x444555;
SenderReport sr;
sr.SetSenderSsrc(kRemoteSsrc);
sr.SetNtp(kRemoteNtp);
sr.SetRtpTimestamp(kRemoteRtp);
auto raw_packet = sr.Build();
EXPECT_CALL(observer1, OnSenderReport(kRemoteSsrc, kRemoteNtp, kRemoteRtp));
EXPECT_CALL(observer2, OnSenderReport(kRemoteSsrc, kRemoteNtp, kRemoteRtp));
rtcp_transceiver.ReceivePacket(raw_packet, Timestamp::Micros(0));
}
TEST_F(RtcpTransceiverImplTest, DoesntCallsObserverAfterRemoved) {
const uint32_t kRemoteSsrc = 12345;
StrictMock<MockMediaReceiverRtcpObserver> observer1;
StrictMock<MockMediaReceiverRtcpObserver> observer2;
RtcpTransceiverConfig config = DefaultTestConfig();
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, &observer1);
rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, &observer2);
SenderReport sr;
sr.SetSenderSsrc(kRemoteSsrc);
auto raw_packet = sr.Build();
rtcp_transceiver.RemoveMediaReceiverRtcpObserver(kRemoteSsrc, &observer1);
EXPECT_CALL(observer1, OnSenderReport(_, _, _)).Times(0);
EXPECT_CALL(observer2, OnSenderReport(_, _, _));
rtcp_transceiver.ReceivePacket(raw_packet, Timestamp::Micros(0));
}
TEST_F(RtcpTransceiverImplTest, CallsObserverOnSenderReportBySenderSsrc) {
const uint32_t kRemoteSsrc1 = 12345;
const uint32_t kRemoteSsrc2 = 22345;
StrictMock<MockMediaReceiverRtcpObserver> observer1;
StrictMock<MockMediaReceiverRtcpObserver> observer2;
RtcpTransceiverConfig config = DefaultTestConfig();
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc1, &observer1);
rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc2, &observer2);
const NtpTime kRemoteNtp(0x9876543211);
const uint32_t kRemoteRtp = 0x444555;
SenderReport sr;
sr.SetSenderSsrc(kRemoteSsrc1);
sr.SetNtp(kRemoteNtp);
sr.SetRtpTimestamp(kRemoteRtp);
auto raw_packet = sr.Build();
EXPECT_CALL(observer1, OnSenderReport(kRemoteSsrc1, kRemoteNtp, kRemoteRtp));
EXPECT_CALL(observer2, OnSenderReport).Times(0);
rtcp_transceiver.ReceivePacket(raw_packet, Timestamp::Micros(0));
}
TEST_F(RtcpTransceiverImplTest, CallsObserverOnByeBySenderSsrc) {
const uint32_t kRemoteSsrc1 = 12345;
const uint32_t kRemoteSsrc2 = 22345;
StrictMock<MockMediaReceiverRtcpObserver> observer1;
StrictMock<MockMediaReceiverRtcpObserver> observer2;
RtcpTransceiverConfig config = DefaultTestConfig();
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc1, &observer1);
rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc2, &observer2);
Bye bye;
bye.SetSenderSsrc(kRemoteSsrc1);
auto raw_packet = bye.Build();
EXPECT_CALL(observer1, OnBye(kRemoteSsrc1));
EXPECT_CALL(observer2, OnBye(_)).Times(0);
rtcp_transceiver.ReceivePacket(raw_packet, Timestamp::Micros(0));
}
TEST_F(RtcpTransceiverImplTest, CallsObserverOnTargetBitrateBySenderSsrc) {
const uint32_t kRemoteSsrc1 = 12345;
const uint32_t kRemoteSsrc2 = 22345;
StrictMock<MockMediaReceiverRtcpObserver> observer1;
StrictMock<MockMediaReceiverRtcpObserver> observer2;
RtcpTransceiverConfig config = DefaultTestConfig();
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc1, &observer1);
rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc2, &observer2);
webrtc::rtcp::TargetBitrate target_bitrate;
target_bitrate.AddTargetBitrate(0, 0, /*target_bitrate_kbps=*/10);
target_bitrate.AddTargetBitrate(0, 1, /*target_bitrate_kbps=*/20);
target_bitrate.AddTargetBitrate(1, 0, /*target_bitrate_kbps=*/40);
target_bitrate.AddTargetBitrate(1, 1, /*target_bitrate_kbps=*/80);
webrtc::rtcp::ExtendedReports xr;
xr.SetSenderSsrc(kRemoteSsrc1);
xr.SetTargetBitrate(target_bitrate);
auto raw_packet = xr.Build();
VideoBitrateAllocation bitrate_allocation;
bitrate_allocation.SetBitrate(0, 0, /*bitrate_bps=*/10000);
bitrate_allocation.SetBitrate(0, 1, /*bitrate_bps=*/20000);
bitrate_allocation.SetBitrate(1, 0, /*bitrate_bps=*/40000);
bitrate_allocation.SetBitrate(1, 1, /*bitrate_bps=*/80000);
EXPECT_CALL(observer1, OnBitrateAllocation(kRemoteSsrc1, bitrate_allocation));
EXPECT_CALL(observer2, OnBitrateAllocation(_, _)).Times(0);
rtcp_transceiver.ReceivePacket(raw_packet, Timestamp::Micros(0));
}
TEST_F(RtcpTransceiverImplTest, SkipsIncorrectTargetBitrateEntries) {
const uint32_t kRemoteSsrc = 12345;
MockMediaReceiverRtcpObserver observer;
RtcpTransceiverConfig config = DefaultTestConfig();
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, &observer);
webrtc::rtcp::TargetBitrate target_bitrate;
target_bitrate.AddTargetBitrate(0, 0, /*target_bitrate_kbps=*/10);
target_bitrate.AddTargetBitrate(0, webrtc::kMaxTemporalStreams, 20);
target_bitrate.AddTargetBitrate(webrtc::kMaxSpatialLayers, 0, 40);
webrtc::rtcp::ExtendedReports xr;
xr.SetTargetBitrate(target_bitrate);
xr.SetSenderSsrc(kRemoteSsrc);
auto raw_packet = xr.Build();
VideoBitrateAllocation expected_allocation;
expected_allocation.SetBitrate(0, 0, /*bitrate_bps=*/10000);
EXPECT_CALL(observer, OnBitrateAllocation(kRemoteSsrc, expected_allocation));
rtcp_transceiver.ReceivePacket(raw_packet, Timestamp::Micros(0));
}
TEST_F(RtcpTransceiverImplTest, CallsObserverOnByeBehindSenderReport) {
const uint32_t kRemoteSsrc = 12345;
MockMediaReceiverRtcpObserver observer;
RtcpTransceiverConfig config = DefaultTestConfig();
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, &observer);
CompoundPacket compound;
auto sr = std::make_unique<SenderReport>();
sr->SetSenderSsrc(kRemoteSsrc);
compound.Append(std::move(sr));
auto bye = std::make_unique<Bye>();
bye->SetSenderSsrc(kRemoteSsrc);
compound.Append(std::move(bye));
auto raw_packet = compound.Build();
EXPECT_CALL(observer, OnBye(kRemoteSsrc));
EXPECT_CALL(observer, OnSenderReport(kRemoteSsrc, _, _));
rtcp_transceiver.ReceivePacket(raw_packet, Timestamp::Micros(0));
}
TEST_F(RtcpTransceiverImplTest, CallsObserverOnByeBehindUnknownRtcpPacket) {
const uint32_t kRemoteSsrc = 12345;
MockMediaReceiverRtcpObserver observer;
RtcpTransceiverConfig config = DefaultTestConfig();
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, &observer);
CompoundPacket compound;
// Use Application-Defined rtcp packet as unknown.
auto app = std::make_unique<webrtc::rtcp::App>();
compound.Append(std::move(app));
auto bye = std::make_unique<Bye>();
bye->SetSenderSsrc(kRemoteSsrc);
compound.Append(std::move(bye));
auto raw_packet = compound.Build();
EXPECT_CALL(observer, OnBye(kRemoteSsrc));
rtcp_transceiver.ReceivePacket(raw_packet, Timestamp::Micros(0));
}
TEST_F(RtcpTransceiverImplTest,
WhenSendsReceiverReportSetsLastSenderReportTimestampPerRemoteSsrc) {
const uint32_t kRemoteSsrc1 = 4321;
const uint32_t kRemoteSsrc2 = 5321;
std::vector<ReportBlock> statistics_report_blocks(2);
statistics_report_blocks[0].SetMediaSsrc(kRemoteSsrc1);
statistics_report_blocks[1].SetMediaSsrc(kRemoteSsrc2);
MockReceiveStatisticsProvider receive_statistics;
EXPECT_CALL(receive_statistics, RtcpReportBlocks(_))
.WillOnce(Return(statistics_report_blocks));
RtcpTransceiverConfig config = DefaultTestConfig();
config.schedule_periodic_compound_packets = false;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
config.receive_statistics = &receive_statistics;
RtcpTransceiverImpl rtcp_transceiver(config);
const NtpTime kRemoteNtp(0x9876543211);
// Receive SenderReport for RemoteSsrc1, but no report for RemoteSsrc2.
SenderReport sr;
sr.SetSenderSsrc(kRemoteSsrc1);
sr.SetNtp(kRemoteNtp);
auto raw_packet = sr.Build();
rtcp_transceiver.ReceivePacket(raw_packet, Timestamp::Micros(0));
// Trigger sending ReceiverReport.
rtcp_transceiver.SendCompoundPacket();
EXPECT_GT(rtcp_parser.receiver_report()->num_packets(), 0);
const auto& report_blocks = rtcp_parser.receiver_report()->report_blocks();
ASSERT_EQ(report_blocks.size(), 2u);
// RtcpTransceiverImpl doesn't guarantee order of the report blocks
// match result of ReceiveStatisticsProvider::RtcpReportBlocks callback,
// but for simplicity of the test asume it is the same.
ASSERT_EQ(report_blocks[0].source_ssrc(), kRemoteSsrc1);
EXPECT_EQ(report_blocks[0].last_sr(), CompactNtp(kRemoteNtp));
ASSERT_EQ(report_blocks[1].source_ssrc(), kRemoteSsrc2);
// No matching Sender Report for kRemoteSsrc2, LastSR fields has to be 0.
EXPECT_EQ(report_blocks[1].last_sr(), 0u);
}
TEST_F(RtcpTransceiverImplTest,
WhenSendsReceiverReportCalculatesDelaySinceLastSenderReport) {
const uint32_t kRemoteSsrc1 = 4321;
const uint32_t kRemoteSsrc2 = 5321;
std::vector<ReportBlock> statistics_report_blocks(2);
statistics_report_blocks[0].SetMediaSsrc(kRemoteSsrc1);
statistics_report_blocks[1].SetMediaSsrc(kRemoteSsrc2);
MockReceiveStatisticsProvider receive_statistics;
EXPECT_CALL(receive_statistics, RtcpReportBlocks(_))
.WillOnce(Return(statistics_report_blocks));
RtcpTransceiverConfig config = DefaultTestConfig();
config.schedule_periodic_compound_packets = false;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
config.receive_statistics = &receive_statistics;
RtcpTransceiverImpl rtcp_transceiver(config);
auto receive_sender_report = [&](uint32_t remote_ssrc) {
SenderReport sr;
sr.SetSenderSsrc(remote_ssrc);
rtcp_transceiver.ReceivePacket(sr.Build(), CurrentTime());
};
receive_sender_report(kRemoteSsrc1);
time_controller().AdvanceTime(TimeDelta::Millis(100));
receive_sender_report(kRemoteSsrc2);
time_controller().AdvanceTime(TimeDelta::Millis(100));
// Trigger ReceiverReport back.
rtcp_transceiver.SendCompoundPacket();
EXPECT_GT(rtcp_parser.receiver_report()->num_packets(), 0);
const auto& report_blocks = rtcp_parser.receiver_report()->report_blocks();
ASSERT_EQ(report_blocks.size(), 2u);
// RtcpTransceiverImpl doesn't guarantee order of the report blocks
// match result of ReceiveStatisticsProvider::RtcpReportBlocks callback,
// but for simplicity of the test asume it is the same.
ASSERT_EQ(report_blocks[0].source_ssrc(), kRemoteSsrc1);
EXPECT_THAT(CompactNtpRttToTimeDelta(report_blocks[0].delay_since_last_sr()),
Near(TimeDelta::Millis(200)));
ASSERT_EQ(report_blocks[1].source_ssrc(), kRemoteSsrc2);
EXPECT_THAT(CompactNtpRttToTimeDelta(report_blocks[1].delay_since_last_sr()),
Near(TimeDelta::Millis(100)));
}
TEST_F(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));
RtcpTransceiverConfig config = DefaultTestConfig();
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_F(RtcpTransceiverImplTest, AttachMaxNumberOfReportBlocksToCompoundPacket) {
MockReceiveStatisticsProvider receive_statistics;
EXPECT_CALL(receive_statistics, RtcpReportBlocks)
.WillOnce([](size_t max_blocks) {
return std::vector<ReportBlock>(max_blocks);
});
RtcpTransceiverConfig config = DefaultTestConfig();
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_F(RtcpTransceiverImplTest, SendsNack) {
const uint32_t kSenderSsrc = 1234;
const uint32_t kRemoteSsrc = 4321;
std::vector<uint16_t> kMissingSequenceNumbers = {34, 37, 38};
RtcpTransceiverConfig config = DefaultTestConfig();
config.feedback_ssrc = kSenderSsrc;
config.schedule_periodic_compound_packets = false;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SendNack(kRemoteSsrc, kMissingSequenceNumbers);
EXPECT_EQ(rtcp_parser.nack()->num_packets(), 1);
EXPECT_EQ(rtcp_parser.nack()->sender_ssrc(), kSenderSsrc);
EXPECT_EQ(rtcp_parser.nack()->media_ssrc(), kRemoteSsrc);
EXPECT_EQ(rtcp_parser.nack()->packet_ids(), kMissingSequenceNumbers);
}
TEST_F(RtcpTransceiverImplTest, ReceivesNack) {
static constexpr uint32_t kRemoteSsrc = 4321;
static constexpr uint32_t kMediaSsrc1 = 1234;
static constexpr uint32_t kMediaSsrc2 = 1235;
std::vector<uint16_t> kMissingSequenceNumbers = {34, 37, 38};
RtcpTransceiverConfig config = DefaultTestConfig();
RtcpTransceiverImpl rtcp_transceiver(config);
MockRtpStreamRtcpHandler local_stream1;
MockRtpStreamRtcpHandler local_stream2;
EXPECT_CALL(local_stream1,
OnNack(kRemoteSsrc, ElementsAreArray(kMissingSequenceNumbers)));
EXPECT_CALL(local_stream2, OnNack).Times(0);
EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc1, &local_stream1));
EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc2, &local_stream2));
rtcp::Nack nack;
nack.SetSenderSsrc(kRemoteSsrc);
nack.SetMediaSsrc(kMediaSsrc1);
nack.SetPacketIds(kMissingSequenceNumbers);
rtcp_transceiver.ReceivePacket(nack.Build(), config.clock->CurrentTime());
}
TEST_F(RtcpTransceiverImplTest, RequestKeyFrameWithPictureLossIndication) {
const uint32_t kSenderSsrc = 1234;
const uint32_t kRemoteSsrc = 4321;
RtcpTransceiverConfig config = DefaultTestConfig();
config.feedback_ssrc = kSenderSsrc;
config.schedule_periodic_compound_packets = false;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SendPictureLossIndication(kRemoteSsrc);
EXPECT_EQ(transport.num_packets(), 1);
EXPECT_EQ(rtcp_parser.pli()->num_packets(), 1);
EXPECT_EQ(rtcp_parser.pli()->sender_ssrc(), kSenderSsrc);
EXPECT_EQ(rtcp_parser.pli()->media_ssrc(), kRemoteSsrc);
}
TEST_F(RtcpTransceiverImplTest, ReceivesPictureLossIndication) {
static constexpr uint32_t kRemoteSsrc = 4321;
static constexpr uint32_t kMediaSsrc1 = 1234;
static constexpr uint32_t kMediaSsrc2 = 1235;
RtcpTransceiverConfig config = DefaultTestConfig();
RtcpTransceiverImpl rtcp_transceiver(config);
MockRtpStreamRtcpHandler local_stream1;
MockRtpStreamRtcpHandler local_stream2;
EXPECT_CALL(local_stream1, OnPli(kRemoteSsrc));
EXPECT_CALL(local_stream2, OnPli).Times(0);
EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc1, &local_stream1));
EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc2, &local_stream2));
rtcp::Pli pli;
pli.SetSenderSsrc(kRemoteSsrc);
pli.SetMediaSsrc(kMediaSsrc1);
rtcp_transceiver.ReceivePacket(pli.Build(), config.clock->CurrentTime());
}
TEST_F(RtcpTransceiverImplTest, RequestKeyFrameWithFullIntraRequest) {
const uint32_t kSenderSsrc = 1234;
const uint32_t kRemoteSsrcs[] = {4321, 5321};
RtcpTransceiverConfig config = DefaultTestConfig();
config.feedback_ssrc = kSenderSsrc;
config.schedule_periodic_compound_packets = false;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SendFullIntraRequest(kRemoteSsrcs, true);
EXPECT_EQ(rtcp_parser.fir()->num_packets(), 1);
EXPECT_EQ(rtcp_parser.fir()->sender_ssrc(), kSenderSsrc);
EXPECT_EQ(rtcp_parser.fir()->requests()[0].ssrc, kRemoteSsrcs[0]);
EXPECT_EQ(rtcp_parser.fir()->requests()[1].ssrc, kRemoteSsrcs[1]);
}
TEST_F(RtcpTransceiverImplTest, RequestKeyFrameWithFirIncreaseSeqNoPerSsrc) {
RtcpTransceiverConfig config = DefaultTestConfig();
config.schedule_periodic_compound_packets = false;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
RtcpTransceiverImpl rtcp_transceiver(config);
const uint32_t kBothRemoteSsrcs[] = {4321, 5321};
const uint32_t kOneRemoteSsrc[] = {4321};
rtcp_transceiver.SendFullIntraRequest(kBothRemoteSsrcs, true);
ASSERT_EQ(rtcp_parser.fir()->requests()[0].ssrc, kBothRemoteSsrcs[0]);
uint8_t fir_sequence_number0 = rtcp_parser.fir()->requests()[0].seq_nr;
ASSERT_EQ(rtcp_parser.fir()->requests()[1].ssrc, kBothRemoteSsrcs[1]);
uint8_t fir_sequence_number1 = rtcp_parser.fir()->requests()[1].seq_nr;
rtcp_transceiver.SendFullIntraRequest(kOneRemoteSsrc, true);
ASSERT_EQ(rtcp_parser.fir()->requests().size(), 1u);
ASSERT_EQ(rtcp_parser.fir()->requests()[0].ssrc, kBothRemoteSsrcs[0]);
EXPECT_EQ(rtcp_parser.fir()->requests()[0].seq_nr, fir_sequence_number0 + 1);
rtcp_transceiver.SendFullIntraRequest(kBothRemoteSsrcs, true);
ASSERT_EQ(rtcp_parser.fir()->requests().size(), 2u);
ASSERT_EQ(rtcp_parser.fir()->requests()[0].ssrc, kBothRemoteSsrcs[0]);
EXPECT_EQ(rtcp_parser.fir()->requests()[0].seq_nr, fir_sequence_number0 + 2);
ASSERT_EQ(rtcp_parser.fir()->requests()[1].ssrc, kBothRemoteSsrcs[1]);
EXPECT_EQ(rtcp_parser.fir()->requests()[1].seq_nr, fir_sequence_number1 + 1);
}
TEST_F(RtcpTransceiverImplTest, SendFirDoesNotIncreaseSeqNoIfOldRequest) {
RtcpTransceiverConfig config = DefaultTestConfig();
config.schedule_periodic_compound_packets = false;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
RtcpTransceiverImpl rtcp_transceiver(config);
const uint32_t kBothRemoteSsrcs[] = {4321, 5321};
rtcp_transceiver.SendFullIntraRequest(kBothRemoteSsrcs, true);
ASSERT_EQ(rtcp_parser.fir()->requests().size(), 2u);
ASSERT_EQ(rtcp_parser.fir()->requests()[0].ssrc, kBothRemoteSsrcs[0]);
uint8_t fir_sequence_number0 = rtcp_parser.fir()->requests()[0].seq_nr;
ASSERT_EQ(rtcp_parser.fir()->requests()[1].ssrc, kBothRemoteSsrcs[1]);
uint8_t fir_sequence_number1 = rtcp_parser.fir()->requests()[1].seq_nr;
rtcp_transceiver.SendFullIntraRequest(kBothRemoteSsrcs, false);
ASSERT_EQ(rtcp_parser.fir()->requests().size(), 2u);
ASSERT_EQ(rtcp_parser.fir()->requests()[0].ssrc, kBothRemoteSsrcs[0]);
EXPECT_EQ(rtcp_parser.fir()->requests()[0].seq_nr, fir_sequence_number0);
ASSERT_EQ(rtcp_parser.fir()->requests()[1].ssrc, kBothRemoteSsrcs[1]);
EXPECT_EQ(rtcp_parser.fir()->requests()[1].seq_nr, fir_sequence_number1);
}
TEST_F(RtcpTransceiverImplTest, ReceivesFir) {
static constexpr uint32_t kRemoteSsrc = 4321;
static constexpr uint32_t kMediaSsrc1 = 1234;
static constexpr uint32_t kMediaSsrc2 = 1235;
RtcpTransceiverConfig config = DefaultTestConfig();
RtcpTransceiverImpl rtcp_transceiver(config);
MockRtpStreamRtcpHandler local_stream1;
MockRtpStreamRtcpHandler local_stream2;
EXPECT_CALL(local_stream1, OnFir(kRemoteSsrc));
EXPECT_CALL(local_stream2, OnFir).Times(0);
EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc1, &local_stream1));
EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc2, &local_stream2));
rtcp::Fir fir;
fir.SetSenderSsrc(kRemoteSsrc);
fir.AddRequestTo(kMediaSsrc1, /*seq_num=*/13);
rtcp_transceiver.ReceivePacket(fir.Build(), config.clock->CurrentTime());
}
TEST_F(RtcpTransceiverImplTest, IgnoresReceivedFirWithRepeatedSequenceNumber) {
static constexpr uint32_t kRemoteSsrc = 4321;
static constexpr uint32_t kMediaSsrc1 = 1234;
static constexpr uint32_t kMediaSsrc2 = 1235;
RtcpTransceiverConfig config = DefaultTestConfig();
RtcpTransceiverImpl rtcp_transceiver(config);
MockRtpStreamRtcpHandler local_stream1;
MockRtpStreamRtcpHandler local_stream2;
EXPECT_CALL(local_stream1, OnFir(kRemoteSsrc)).Times(1);
EXPECT_CALL(local_stream2, OnFir(kRemoteSsrc)).Times(2);
EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc1, &local_stream1));
EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc2, &local_stream2));
rtcp::Fir fir1;
fir1.SetSenderSsrc(kRemoteSsrc);
fir1.AddRequestTo(kMediaSsrc1, /*seq_num=*/132);
fir1.AddRequestTo(kMediaSsrc2, /*seq_num=*/10);
rtcp_transceiver.ReceivePacket(fir1.Build(), config.clock->CurrentTime());
// Repeat request for MediaSsrc1 - expect it to be ignored,
// Change FIR sequence number for MediaSsrc2 - expect a 2nd callback.
rtcp::Fir fir2;
fir2.SetSenderSsrc(kRemoteSsrc);
fir2.AddRequestTo(kMediaSsrc1, /*seq_num=*/132);
fir2.AddRequestTo(kMediaSsrc2, /*seq_num=*/13);
rtcp_transceiver.ReceivePacket(fir2.Build(), config.clock->CurrentTime());
}
TEST_F(RtcpTransceiverImplTest, ReceivedFirTracksSequenceNumberPerRemoteSsrc) {
static constexpr uint32_t kRemoteSsrc1 = 4321;
static constexpr uint32_t kRemoteSsrc2 = 4323;
static constexpr uint32_t kMediaSsrc = 1234;
RtcpTransceiverConfig config = DefaultTestConfig();
RtcpTransceiverImpl rtcp_transceiver(config);
MockRtpStreamRtcpHandler local_stream;
EXPECT_CALL(local_stream, OnFir(kRemoteSsrc1));
EXPECT_CALL(local_stream, OnFir(kRemoteSsrc2));
EXPECT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc, &local_stream));
rtcp::Fir fir1;
fir1.SetSenderSsrc(kRemoteSsrc1);
fir1.AddRequestTo(kMediaSsrc, /*seq_num=*/13);
rtcp_transceiver.ReceivePacket(fir1.Build(), config.clock->CurrentTime());
// Use the same FIR sequence number, but different sender SSRC.
rtcp::Fir fir2;
fir2.SetSenderSsrc(kRemoteSsrc2);
fir2.AddRequestTo(kMediaSsrc, /*seq_num=*/13);
rtcp_transceiver.ReceivePacket(fir2.Build(), config.clock->CurrentTime());
}
TEST_F(RtcpTransceiverImplTest, KeyFrameRequestCreatesCompoundPacket) {
const uint32_t kRemoteSsrcs[] = {4321};
RtcpTransceiverConfig config = DefaultTestConfig();
// Turn periodic off to ensure sent rtcp packet is explicitly requested.
config.schedule_periodic_compound_packets = false;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
config.rtcp_mode = webrtc::RtcpMode::kCompound;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SendFullIntraRequest(kRemoteSsrcs, true);
// Test sent packet is compound by expecting presense of receiver report.
EXPECT_EQ(transport.num_packets(), 1);
EXPECT_EQ(rtcp_parser.receiver_report()->num_packets(), 1);
}
TEST_F(RtcpTransceiverImplTest, KeyFrameRequestCreatesReducedSizePacket) {
const uint32_t kRemoteSsrcs[] = {4321};
RtcpTransceiverConfig config = DefaultTestConfig();
// Turn periodic off to ensure sent rtcp packet is explicitly requested.
config.schedule_periodic_compound_packets = false;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
config.rtcp_mode = webrtc::RtcpMode::kReducedSize;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SendFullIntraRequest(kRemoteSsrcs, true);
// Test sent packet is reduced size by expecting absense of receiver report.
EXPECT_EQ(transport.num_packets(), 1);
EXPECT_EQ(rtcp_parser.receiver_report()->num_packets(), 0);
}
TEST_F(RtcpTransceiverImplTest, SendsXrRrtrWhenEnabled) {
const uint32_t kSenderSsrc = 4321;
RtcpTransceiverConfig config = DefaultTestConfig();
config.feedback_ssrc = kSenderSsrc;
config.schedule_periodic_compound_packets = false;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
config.non_sender_rtt_measurement = true;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SendCompoundPacket();
NtpTime ntp_time_now = config.clock->CurrentNtpTime();
EXPECT_EQ(rtcp_parser.xr()->num_packets(), 1);
EXPECT_EQ(rtcp_parser.xr()->sender_ssrc(), kSenderSsrc);
ASSERT_TRUE(rtcp_parser.xr()->rrtr());
EXPECT_EQ(rtcp_parser.xr()->rrtr()->ntp(), ntp_time_now);
}
TEST_F(RtcpTransceiverImplTest, RepliesToRrtrWhenEnabled) {
static constexpr uint32_t kSenderSsrc[] = {4321, 9876};
RtcpTransceiverConfig config = DefaultTestConfig();
config.reply_to_non_sender_rtt_measurement = true;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp::ExtendedReports xr;
rtcp::Rrtr rrtr;
rrtr.SetNtp(NtpTime(uint64_t{0x1111'2222'3333'4444}));
xr.SetRrtr(rrtr);
xr.SetSenderSsrc(kSenderSsrc[0]);
rtcp_transceiver.ReceivePacket(xr.Build(), CurrentTime());
AdvanceTime(TimeDelta::Millis(1'500));
rrtr.SetNtp(NtpTime(uint64_t{0x4444'5555'6666'7777}));
xr.SetRrtr(rrtr);
xr.SetSenderSsrc(kSenderSsrc[1]);
rtcp_transceiver.ReceivePacket(xr.Build(), CurrentTime());
AdvanceTime(TimeDelta::Millis(500));
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(rtcp_parser.xr()->num_packets(), 1);
static constexpr uint32_t kComactNtpOneSecond = 0x0001'0000;
EXPECT_THAT(rtcp_parser.xr()->dlrr().sub_blocks(),
UnorderedElementsAre(
rtcp::ReceiveTimeInfo(kSenderSsrc[0], 0x2222'3333,
/*delay=*/2 * kComactNtpOneSecond),
rtcp::ReceiveTimeInfo(kSenderSsrc[1], 0x5555'6666,
/*delay=*/kComactNtpOneSecond / 2)));
}
TEST_F(RtcpTransceiverImplTest, CanReplyToRrtrOnceForAllLocalSsrcs) {
static constexpr uint32_t kRemoteSsrc = 4321;
static constexpr uint32_t kLocalSsrcs[] = {1234, 5678};
RtcpTransceiverConfig config = DefaultTestConfig();
config.reply_to_non_sender_rtt_measurement = true;
config.reply_to_non_sender_rtt_mesaurments_on_all_ssrcs = false;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
RtcpTransceiverImpl rtcp_transceiver(config);
MockRtpStreamRtcpHandler local_sender0;
MockRtpStreamRtcpHandler local_sender1;
rtcp_transceiver.AddMediaSender(kLocalSsrcs[0], &local_sender0);
rtcp_transceiver.AddMediaSender(kLocalSsrcs[1], &local_sender1);
rtcp::ExtendedReports xr;
rtcp::Rrtr rrtr;
rrtr.SetNtp(NtpTime(uint64_t{0x1111'2222'3333'4444}));
xr.SetRrtr(rrtr);
xr.SetSenderSsrc(kRemoteSsrc);
rtcp_transceiver.ReceivePacket(xr.Build(), CurrentTime());
AdvanceTime(TimeDelta::Millis(1'500));
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(rtcp_parser.xr()->num_packets(), 1);
}
TEST_F(RtcpTransceiverImplTest, CanReplyToRrtrForEachLocalSsrc) {
static constexpr uint32_t kRemoteSsrc = 4321;
static constexpr uint32_t kLocalSsrc[] = {1234, 5678};
RtcpTransceiverConfig config = DefaultTestConfig();
config.reply_to_non_sender_rtt_measurement = true;
config.reply_to_non_sender_rtt_mesaurments_on_all_ssrcs = true;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
RtcpTransceiverImpl rtcp_transceiver(config);
MockRtpStreamRtcpHandler local_sender0;
MockRtpStreamRtcpHandler local_sender1;
rtcp_transceiver.AddMediaSender(kLocalSsrc[0], &local_sender0);
rtcp_transceiver.AddMediaSender(kLocalSsrc[1], &local_sender1);
rtcp::ExtendedReports xr;
rtcp::Rrtr rrtr;
rrtr.SetNtp(NtpTime(uint64_t{0x1111'2222'3333'4444}));
xr.SetRrtr(rrtr);
xr.SetSenderSsrc(kRemoteSsrc);
rtcp_transceiver.ReceivePacket(xr.Build(), CurrentTime());
AdvanceTime(TimeDelta::Millis(1'500));
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(rtcp_parser.xr()->num_packets(), 2);
}
TEST_F(RtcpTransceiverImplTest, SendsNoXrRrtrWhenDisabled) {
RtcpTransceiverConfig config = DefaultTestConfig();
config.schedule_periodic_compound_packets = false;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
config.non_sender_rtt_measurement = false;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(transport.num_packets(), 1);
// Extended reports rtcp packet might be included for another reason,
// but it shouldn't contain rrtr block.
EXPECT_FALSE(rtcp_parser.xr()->rrtr());
}
TEST_F(RtcpTransceiverImplTest, PassRttFromDlrrToLinkObserver) {
const uint32_t kSenderSsrc = 4321;
MockNetworkLinkRtcpObserver link_observer;
RtcpTransceiverConfig config = DefaultTestConfig();
config.feedback_ssrc = kSenderSsrc;
config.network_link_observer = &link_observer;
config.non_sender_rtt_measurement = true;
RtcpTransceiverImpl rtcp_transceiver(config);
Timestamp send_time = Timestamp::Seconds(5678);
Timestamp receive_time = send_time + TimeDelta::Millis(110);
rtcp::ReceiveTimeInfo rti;
rti.ssrc = kSenderSsrc;
rti.last_rr = CompactNtp(config.clock->ConvertTimestampToNtpTime(send_time));
rti.delay_since_last_rr = SaturatedToCompactNtp(TimeDelta::Millis(10));
rtcp::ExtendedReports xr;
xr.AddDlrrItem(rti);
EXPECT_CALL(link_observer,
OnRttUpdate(receive_time, Near(TimeDelta::Millis(100))));
rtcp_transceiver.ReceivePacket(xr.Build(), receive_time);
}
TEST_F(RtcpTransceiverImplTest, CalculatesRoundTripTimeFromReportBlocks) {
MockNetworkLinkRtcpObserver link_observer;
RtcpTransceiverConfig config = DefaultTestConfig();
config.network_link_observer = &link_observer;
RtcpTransceiverImpl rtcp_transceiver(config);
TimeDelta rtt = TimeDelta::Millis(100);
Timestamp send_time = Timestamp::Seconds(5678);
Timestamp receive_time = send_time + TimeDelta::Millis(110);
rtcp::ReceiverReport rr;
rtcp::ReportBlock rb1;
rb1.SetLastSr(CompactNtp(config.clock->ConvertTimestampToNtpTime(
receive_time - rtt - TimeDelta::Millis(10))));
rb1.SetDelayLastSr(SaturatedToCompactNtp(TimeDelta::Millis(10)));
rr.AddReportBlock(rb1);
rtcp::ReportBlock rb2;
rb2.SetLastSr(CompactNtp(config.clock->ConvertTimestampToNtpTime(
receive_time - rtt - TimeDelta::Millis(20))));
rb2.SetDelayLastSr(SaturatedToCompactNtp(TimeDelta::Millis(20)));
rr.AddReportBlock(rb2);
EXPECT_CALL(link_observer, OnRttUpdate(receive_time, Near(rtt)));
rtcp_transceiver.ReceivePacket(rr.Build(), receive_time);
}
TEST_F(RtcpTransceiverImplTest, IgnoresUnknownSsrcInDlrr) {
const uint32_t kSenderSsrc = 4321;
const uint32_t kUnknownSsrc = 4322;
MockNetworkLinkRtcpObserver link_observer;
MockTransport null_transport;
RtcpTransceiverConfig config = DefaultTestConfig();
config.feedback_ssrc = kSenderSsrc;
config.schedule_periodic_compound_packets = false;
config.outgoing_transport = &null_transport;
config.non_sender_rtt_measurement = true;
config.network_link_observer = &link_observer;
RtcpTransceiverImpl rtcp_transceiver(config);
Timestamp time = Timestamp::Micros(12345678);
webrtc::rtcp::ReceiveTimeInfo rti;
rti.ssrc = kUnknownSsrc;
rti.last_rr = CompactNtp(config.clock->ConvertTimestampToNtpTime(time));
webrtc::rtcp::ExtendedReports xr;
xr.AddDlrrItem(rti);
auto raw_packet = xr.Build();
EXPECT_CALL(link_observer, OnRttUpdate).Times(0);
rtcp_transceiver.ReceivePacket(raw_packet, time + TimeDelta::Millis(100));
}
TEST_F(RtcpTransceiverImplTest, ParsesTransportFeedback) {
MockNetworkLinkRtcpObserver link_observer;
RtcpTransceiverConfig config = DefaultTestConfig();
config.network_link_observer = &link_observer;
Timestamp receive_time = Timestamp::Seconds(5678);
RtcpTransceiverImpl rtcp_transceiver(config);
EXPECT_CALL(link_observer, OnTransportFeedback(receive_time, _))
.WillOnce(WithArg<1>([](const rtcp::TransportFeedback& message) {
EXPECT_EQ(message.GetBaseSequence(), 321);
EXPECT_THAT(message.GetReceivedPackets(), SizeIs(2));
}));
rtcp::TransportFeedback tb;
tb.SetBase(/*base_sequence=*/321, Timestamp::Micros(15));
tb.AddReceivedPacket(/*base_sequence=*/321, Timestamp::Micros(15));
tb.AddReceivedPacket(/*base_sequence=*/322, Timestamp::Micros(17));
rtcp_transceiver.ReceivePacket(tb.Build(), receive_time);
}
TEST_F(RtcpTransceiverImplTest, ParsesRemb) {
MockNetworkLinkRtcpObserver link_observer;
RtcpTransceiverConfig config = DefaultTestConfig();
config.network_link_observer = &link_observer;
Timestamp receive_time = Timestamp::Seconds(5678);
RtcpTransceiverImpl rtcp_transceiver(config);
EXPECT_CALL(link_observer,
OnReceiverEstimatedMaxBitrate(receive_time,
DataRate::BitsPerSec(1'234'000)));
rtcp::Remb remb;
remb.SetBitrateBps(1'234'000);
rtcp_transceiver.ReceivePacket(remb.Build(), receive_time);
}
TEST_F(RtcpTransceiverImplTest,
CombinesReportBlocksFromSenderAndRecieverReports) {
MockNetworkLinkRtcpObserver link_observer;
RtcpTransceiverConfig config = DefaultTestConfig();
config.network_link_observer = &link_observer;
Timestamp receive_time = Timestamp::Seconds(5678);
RtcpTransceiverImpl rtcp_transceiver(config);
// Assemble compound packet with multiple rtcp packets in it.
rtcp::CompoundPacket packet;
auto sr = std::make_unique<rtcp::SenderReport>();
sr->SetSenderSsrc(1234);
sr->SetReportBlocks(std::vector<ReportBlock>(31));
packet.Append(std::move(sr));
auto rr1 = std::make_unique<rtcp::ReceiverReport>();
rr1->SetReportBlocks(std::vector<ReportBlock>(31));
packet.Append(std::move(rr1));
auto rr2 = std::make_unique<rtcp::ReceiverReport>();
rr2->SetReportBlocks(std::vector<ReportBlock>(2));
packet.Append(std::move(rr2));
EXPECT_CALL(link_observer, OnReport(receive_time, SizeIs(64)));
rtcp_transceiver.ReceivePacket(packet.Build(), receive_time);
}
TEST_F(RtcpTransceiverImplTest,
CallbackOnReportBlocksFromSenderAndReceiverReports) {
static constexpr uint32_t kRemoteSsrc = 5678;
// Has registered sender, report block attached to sender report.
static constexpr uint32_t kMediaSsrc1 = 1234;
// No registered sender, report block attached to receiver report.
// Such report block shouldn't prevent handling following report block.
static constexpr uint32_t kMediaSsrc2 = 1235;
// Has registered sender, no report block attached.
static constexpr uint32_t kMediaSsrc3 = 1236;
// Has registered sender, report block attached to receiver report.
static constexpr uint32_t kMediaSsrc4 = 1237;
MockNetworkLinkRtcpObserver link_observer;
RtcpTransceiverConfig config = DefaultTestConfig();
Timestamp receive_time = Timestamp::Seconds(5678);
RtcpTransceiverImpl rtcp_transceiver(config);
MockRtpStreamRtcpHandler local_stream1;
MockRtpStreamRtcpHandler local_stream3;
MockRtpStreamRtcpHandler local_stream4;
EXPECT_CALL(local_stream1,
OnReport(Property(&ReportBlockData::sender_ssrc, kRemoteSsrc)));
EXPECT_CALL(local_stream3, OnReport).Times(0);
EXPECT_CALL(local_stream4,
OnReport(Property(&ReportBlockData::sender_ssrc, kRemoteSsrc)));
ASSERT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc1, &local_stream1));
ASSERT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc3, &local_stream3));
ASSERT_TRUE(rtcp_transceiver.AddMediaSender(kMediaSsrc4, &local_stream4));
// Assemble compound packet with multiple RTCP packets in it.
rtcp::CompoundPacket packet;
auto sr = std::make_unique<rtcp::SenderReport>();
sr->SetSenderSsrc(kRemoteSsrc);
std::vector<ReportBlock> rb(1);
rb[0].SetMediaSsrc(kMediaSsrc1);
sr->SetReportBlocks(std::move(rb));
packet.Append(std::move(sr));
auto rr = std::make_unique<rtcp::ReceiverReport>();
rr->SetSenderSsrc(kRemoteSsrc);
rb = std::vector<ReportBlock>(2);
rb[0].SetMediaSsrc(kMediaSsrc2);
rb[1].SetMediaSsrc(kMediaSsrc4);
rr->SetReportBlocks(std::move(rb));
packet.Append(std::move(rr));
rtcp_transceiver.ReceivePacket(packet.Build(), receive_time);
}
TEST_F(RtcpTransceiverImplTest, FailsToRegisterTwoSendersWithTheSameSsrc) {
RtcpTransceiverImpl rtcp_transceiver(DefaultTestConfig());
MockRtpStreamRtcpHandler sender1;
MockRtpStreamRtcpHandler sender2;
EXPECT_TRUE(rtcp_transceiver.AddMediaSender(/*local_ssrc=*/10001, &sender1));
EXPECT_FALSE(rtcp_transceiver.AddMediaSender(/*local_ssrc=*/10001, &sender2));
EXPECT_TRUE(rtcp_transceiver.AddMediaSender(/*local_ssrc=*/10002, &sender2));
EXPECT_TRUE(rtcp_transceiver.RemoveMediaSender(/*local_ssrc=*/10001));
EXPECT_FALSE(rtcp_transceiver.RemoveMediaSender(/*local_ssrc=*/10001));
}
TEST_F(RtcpTransceiverImplTest, SendsSenderReport) {
static constexpr uint32_t kFeedbackSsrc = 123;
static constexpr uint32_t kSenderSsrc = 12345;
RtcpTransceiverConfig config = DefaultTestConfig();
config.feedback_ssrc = kFeedbackSsrc;
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
config.schedule_periodic_compound_packets = false;
RtcpTransceiverImpl rtcp_transceiver(config);
RtpStreamRtcpHandler::RtpStats sender_stats;
sender_stats.set_num_sent_packets(10);
sender_stats.set_num_sent_bytes(1000);
sender_stats.set_last_rtp_timestamp(0x3333);
sender_stats.set_last_capture_time(CurrentTime() - TimeDelta::Seconds(2));
sender_stats.set_last_clock_rate(0x1000);
MockRtpStreamRtcpHandler sender;
ON_CALL(sender, SentStats).WillByDefault(Return(sender_stats));
rtcp_transceiver.AddMediaSender(kSenderSsrc, &sender);
rtcp_transceiver.SendCompoundPacket();
ASSERT_GT(rtcp_parser.sender_report()->num_packets(), 0);
EXPECT_EQ(rtcp_parser.sender_report()->sender_ssrc(), kSenderSsrc);
EXPECT_EQ(rtcp_parser.sender_report()->ntp(),
time_controller().GetClock()->CurrentNtpTime());
EXPECT_EQ(rtcp_parser.sender_report()->rtp_timestamp(), 0x3333u + 0x2000u);
EXPECT_EQ(rtcp_parser.sender_report()->sender_packet_count(), 10u);
EXPECT_EQ(rtcp_parser.sender_report()->sender_octet_count(), 1000u);
}
TEST_F(RtcpTransceiverImplTest,
MaySendBothSenderReportAndReceiverReportInTheSamePacket) {
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
std::vector<ReportBlock> statistics_report_blocks(40);
MockReceiveStatisticsProvider receive_statistics;
EXPECT_CALL(receive_statistics, RtcpReportBlocks(/*max_blocks=*/Ge(40u)))
.WillOnce(Return(statistics_report_blocks));
RtcpTransceiverConfig config = DefaultTestConfig();
config.outgoing_transport = &transport;
config.receive_statistics = &receive_statistics;
RtcpTransceiverImpl rtcp_transceiver(config);
MockRtpStreamRtcpHandler sender;
rtcp_transceiver.AddMediaSender(/*ssrc=*/12345, &sender);
rtcp_transceiver.SendCompoundPacket();
// Expect a single RTCP packet with a sender and a receiver reports in it.
EXPECT_EQ(transport.num_packets(), 1);
ASSERT_EQ(rtcp_parser.sender_report()->num_packets(), 1);
ASSERT_EQ(rtcp_parser.receiver_report()->num_packets(), 1);
// Sender report may contain up to 31 report blocks, thus remaining 9 report
// block should be attached to the receiver report.
EXPECT_THAT(rtcp_parser.sender_report()->report_blocks(), SizeIs(31));
EXPECT_THAT(rtcp_parser.receiver_report()->report_blocks(), SizeIs(9));
}
TEST_F(RtcpTransceiverImplTest, RotatesSendersWhenAllSenderReportDoNotFit) {
// Send 6 compound packet, each should contain 5 sender reports,
// each of 6 senders should be mentioned 5 times.
static constexpr int kNumSenders = 6;
static constexpr uint32_t kSenderSsrc[kNumSenders] = {10, 20, 30, 40, 50, 60};
static constexpr int kSendersPerPacket = 5;
// RtcpPacketParser remembers only latest block for each type, but this test
// is about sending multiple sender reports in the same packet, thus need
// a more advance parser: RtcpTranceiver
RtcpTransceiverConfig receiver_config = DefaultTestConfig();
RtcpTransceiverImpl rtcp_receiver(receiver_config);
// Main expectatation: all senders are spread equally across multiple packets.
NiceMock<MockMediaReceiverRtcpObserver> receiver[kNumSenders];
for (int i = 0; i < kNumSenders; ++i) {
SCOPED_TRACE(i);
EXPECT_CALL(receiver[i], OnSenderReport(kSenderSsrc[i], _, _))
.Times(kSendersPerPacket);
rtcp_receiver.AddMediaReceiverRtcpObserver(kSenderSsrc[i], &receiver[i]);
}
MockTransport transport;
EXPECT_CALL(transport, SendRtcp)
.Times(kNumSenders)
.WillRepeatedly([&](const uint8_t* data, size_t size) {
rtcp_receiver.ReceivePacket(rtc::MakeArrayView(data, size),
CurrentTime());
return true;
});
RtcpTransceiverConfig config = DefaultTestConfig();
config.outgoing_transport = &transport;
// Limit packet to have space just for kSendersPerPacket sender reports.
// Sender report without report blocks require 28 bytes.
config.max_packet_size = kSendersPerPacket * 28;
RtcpTransceiverImpl rtcp_transceiver(config);
NiceMock<MockRtpStreamRtcpHandler> sender[kNumSenders];
for (int i = 0; i < kNumSenders; ++i) {
rtcp_transceiver.AddMediaSender(kSenderSsrc[i], &sender[i]);
}
for (int i = 1; i <= kNumSenders; ++i) {
SCOPED_TRACE(i);
rtcp_transceiver.SendCompoundPacket();
}
}
TEST_F(RtcpTransceiverImplTest, SkipsSenderReportForInactiveSender) {
static constexpr uint32_t kSenderSsrc[] = {12345, 23456};
RtcpTransceiverConfig config = DefaultTestConfig();
RtcpPacketParser rtcp_parser;
RtcpParserTransport transport(&rtcp_parser);
config.outgoing_transport = &transport;
RtcpTransceiverImpl rtcp_transceiver(config);
RtpStreamRtcpHandler::RtpStats sender_stats[2];
NiceMock<MockRtpStreamRtcpHandler> sender[2];
ON_CALL(sender[0], SentStats).WillByDefault([&] { return sender_stats[0]; });
ON_CALL(sender[1], SentStats).WillByDefault([&] { return sender_stats[1]; });
rtcp_transceiver.AddMediaSender(kSenderSsrc[0], &sender[0]);
rtcp_transceiver.AddMediaSender(kSenderSsrc[1], &sender[1]);
// Start with both senders beeing active.
sender_stats[0].set_num_sent_packets(10);
sender_stats[0].set_num_sent_bytes(1'000);
sender_stats[1].set_num_sent_packets(5);
sender_stats[1].set_num_sent_bytes(2'000);
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(transport.num_packets(), 1);
EXPECT_EQ(rtcp_parser.sender_report()->num_packets(), 2);
// Keep 1st sender active, but make 2nd second look inactive by returning the
// same RtpStats.
sender_stats[0].set_num_sent_packets(15);
sender_stats[0].set_num_sent_bytes(2'000);
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(transport.num_packets(), 2);
EXPECT_EQ(rtcp_parser.sender_report()->num_packets(), 3);
EXPECT_EQ(rtcp_parser.sender_report()->sender_ssrc(), kSenderSsrc[0]);
// Swap active sender.
sender_stats[1].set_num_sent_packets(20);
sender_stats[1].set_num_sent_bytes(3'000);
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(transport.num_packets(), 3);
EXPECT_EQ(rtcp_parser.sender_report()->num_packets(), 4);
EXPECT_EQ(rtcp_parser.sender_report()->sender_ssrc(), kSenderSsrc[1]);
// Activate both senders again.
sender_stats[0].set_num_sent_packets(20);
sender_stats[0].set_num_sent_bytes(3'000);
sender_stats[1].set_num_sent_packets(25);
sender_stats[1].set_num_sent_bytes(3'500);
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(transport.num_packets(), 4);
EXPECT_EQ(rtcp_parser.sender_report()->num_packets(), 6);
}
} // namespace
} // namespace webrtc