* 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/rtcp_packet_parser.h"
namespace webrtc {
namespace {
using ::testing::_;
using ::testing::ElementsAre;
using ::testing::ElementsAreArray;
using ::testing::Ge;
using ::testing::MockFunction;
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() {
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;
}
class FakeRtcpTransport {
public:
explicit FakeRtcpTransport(TimeController& time) : time_(time) {}
std::function<void(rtc::ArrayView<const uint8_t>)> AsStdFunction() {
return [this](rtc::ArrayView<const uint8_t>) { sent_rtcp_ = true; };
}
bool WaitPacket() {
bool got_packet = time_.Wait([this] { return sent_rtcp_; }, kAlmostForever);
sent_rtcp_ = false;
return got_packet;
}
private:
TimeController& time_;
bool sent_rtcp_ = false;
};
std::function<void(rtc::ArrayView<const uint8_t>)> RtcpParserTransport(
RtcpPacketParser& parser) {
return [&parser](rtc::ArrayView<const uint8_t> packet) {
return parser.Parse(packet);
};
}
class RtcpTransceiverImplTest : public ::testing::Test {
public:
RtcpTransceiverConfig DefaultTestConfig() {
RtcpTransceiverConfig config;
config.clock = time_->GetClock();
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:
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.rtcp_transport = transport.AsStdFunction();
auto* rtcp_transceiver = new RtcpTransceiverImpl(config);
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.rtcp_transport = transport.AsStdFunction();
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.rtcp_transport = transport.AsStdFunction();
auto* rtcp_transceiver = new RtcpTransceiverImpl(config);
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.rtcp_transport = transport.AsStdFunction();
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);
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.rtcp_transport = transport.AsStdFunction();
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([&] {
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);
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.rtcp_transport = transport.AsStdFunction();
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); });
EXPECT_TRUE(transport.WaitPacket());
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);
EXPECT_TRUE(
time_controller().Wait([&] { return non_periodic; }, kAlmostForever));
EXPECT_TRUE(transport.WaitPacket());
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);
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) {
MockFunction<void(rtc::ArrayView<const uint8_t>)> mock_transport;
RtcpTransceiverConfig config = DefaultTestConfig();
config.initial_ready_to_send = false;
config.rtcp_transport = mock_transport.AsStdFunction();
RtcpTransceiverImpl rtcp_transceiver(config);
EXPECT_CALL(mock_transport, Call).Times(0);
const std::vector<uint16_t> sequence_numbers = {45, 57};
const uint32_t ssrcs[] = {123};
rtcp_transceiver.SendCompoundPacket();
rtcp_transceiver.SendNack(ssrcs[0], sequence_numbers);
rtcp_transceiver.SendPictureLossIndication(ssrcs[0]);
rtcp_transceiver.SendFullIntraRequest(ssrcs, true);
}
TEST_F(RtcpTransceiverImplTest, SendsRtcpWhenNetworkStateIsUp) {
MockFunction<void(rtc::ArrayView<const uint8_t>)> mock_transport;
RtcpTransceiverConfig config = DefaultTestConfig();
config.initial_ready_to_send = false;
config.rtcp_transport = mock_transport.AsStdFunction();
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SetReadyToSend(true);
EXPECT_CALL(mock_transport, Call).Times(4);
const std::vector<uint16_t> sequence_numbers = {45, 57};
const uint32_t ssrcs[] = {123};
rtcp_transceiver.SendCompoundPacket();
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.rtcp_transport = transport.AsStdFunction();
config.task_queue = queue.get();
absl::optional<RtcpTransceiverImpl> rtcp_transceiver;
rtcp_transceiver.emplace(config);
queue->PostTask([&] { rtcp_transceiver->SetReadyToSend(true); });
EXPECT_TRUE(transport.WaitPacket());
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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
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_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) {
MockFunction<void(rtc::ArrayView<const uint8_t>)> transport;
EXPECT_CALL(transport, Call).Times(0);
NiceMock<MockReceiveStatisticsProvider> receive_statistics;
RtcpTransceiverConfig config = DefaultTestConfig();
config.rtcp_transport = transport.AsStdFunction();
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;
NiceMock<MockReceiveStatisticsProvider> receive_statistics;
RtcpTransceiverConfig config = DefaultTestConfig();
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
config.rtcp_mode = webrtc::RtcpMode::kReducedSize;
config.receive_statistics = &receive_statistics;
config.non_sender_rtt_measurement = true;
RtcpTransceiverImpl rtcp_transceiver(config);
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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
config.schedule_periodic_compound_packets = false;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
config.schedule_periodic_compound_packets = false;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SetRemb(10000, {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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
config.schedule_periodic_compound_packets = false;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SetRemb(10000, {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(70000, {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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
config.schedule_periodic_compound_packets = false;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SetRemb(10000, {});
EXPECT_EQ(rtcp_parser.remb()->num_packets(), 1);
EXPECT_EQ(rtcp_parser.remb()->sender_ssrc(), kSenderSsrc);
EXPECT_EQ(rtcp_parser.remb()->bitrate_bps(), 10000);
rtcp_transceiver.SetRemb(10000, {});
EXPECT_EQ(rtcp_parser.remb()->num_packets(), 1);
rtcp_transceiver.SetRemb(20000, {});
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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
config.schedule_periodic_compound_packets = false;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SetRemb(10000, {});
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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
config.schedule_periodic_compound_packets = false;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SetRemb(10000, {54321, 64321});
rtcp_transceiver.SendCompoundPacket();
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
config.schedule_periodic_compound_packets = false;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SetRemb(10000, {54321, 64321});
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{1});
ASSERT_EQ(rtcp_parser.remb()->num_packets(), 1);
rtcp_transceiver.UnsetRemb();
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
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, 10);
target_bitrate.AddTargetBitrate(0, 1, 20);
target_bitrate.AddTargetBitrate(1, 0, 40);
target_bitrate.AddTargetBitrate(1, 1, 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, 10000);
bitrate_allocation.SetBitrate(0, 1, 20000);
bitrate_allocation.SetBitrate(1, 0, 40000);
bitrate_allocation.SetBitrate(1, 1, 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, 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, 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;
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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
config.receive_statistics = &receive_statistics;
RtcpTransceiverImpl rtcp_transceiver(config);
const NtpTime kRemoteNtp(0x9876543211);
SenderReport sr;
sr.SetSenderSsrc(kRemoteSsrc1);
sr.SetNtp(kRemoteNtp);
auto raw_packet = sr.Build();
rtcp_transceiver.ReceivePacket(raw_packet, Timestamp::Micros(0));
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);
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);
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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
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));
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);
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(Ge(40u)))
.WillOnce(Return(statistics_report_blocks));
RtcpTransceiverConfig config = DefaultTestConfig();
RtcpPacketParser rtcp_parser;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
config.receive_statistics = &receive_statistics;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{1});
EXPECT_EQ(rtcp_parser.receiver_report()->num_packets(), 2);
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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
config.receive_statistics = &receive_statistics;
RtcpTransceiverImpl rtcp_transceiver(config);
EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{0});
rtcp_transceiver.SendPictureLossIndication(123);
EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SendPictureLossIndication(kRemoteSsrc);
EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
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, 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, 132);
fir1.AddRequestTo(kMediaSsrc2, 10);
rtcp_transceiver.ReceivePacket(fir1.Build(), config.clock->CurrentTime());
rtcp::Fir fir2;
fir2.SetSenderSsrc(kRemoteSsrc);
fir2.AddRequestTo(kMediaSsrc1, 132);
fir2.AddRequestTo(kMediaSsrc2, 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, 13);
rtcp_transceiver.ReceivePacket(fir1.Build(), config.clock->CurrentTime());
rtcp::Fir fir2;
fir2.SetSenderSsrc(kRemoteSsrc2);
fir2.AddRequestTo(kMediaSsrc, 13);
rtcp_transceiver.ReceivePacket(fir2.Build(), config.clock->CurrentTime());
}
TEST_F(RtcpTransceiverImplTest, KeyFrameRequestCreatesCompoundPacket) {
const uint32_t kRemoteSsrcs[] = {4321};
RtcpTransceiverConfig config = DefaultTestConfig();
config.schedule_periodic_compound_packets = false;
RtcpPacketParser rtcp_parser;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
config.rtcp_mode = webrtc::RtcpMode::kCompound;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SendFullIntraRequest(kRemoteSsrcs, true);
EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{1});
EXPECT_EQ(rtcp_parser.receiver_report()->num_packets(), 1);
}
TEST_F(RtcpTransceiverImplTest, KeyFrameRequestCreatesReducedSizePacket) {
const uint32_t kRemoteSsrcs[] = {4321};
RtcpTransceiverConfig config = DefaultTestConfig();
config.schedule_periodic_compound_packets = false;
RtcpPacketParser rtcp_parser;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
config.rtcp_mode = webrtc::RtcpMode::kReducedSize;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SendFullIntraRequest(kRemoteSsrcs, true);
EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
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,
2 * kComactNtpOneSecond),
rtcp::ReceiveTimeInfo(kSenderSsrc[1], 0x5555'6666,
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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
config.non_sender_rtt_measurement = false;
RtcpTransceiverImpl rtcp_transceiver(config);
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{1});
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;
RtcpTransceiverConfig config = DefaultTestConfig();
config.feedback_ssrc = kSenderSsrc;
config.schedule_periodic_compound_packets = false;
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(321, Timestamp::Micros(15));
tb.AddReceivedPacket(321, Timestamp::Micros(15));
tb.AddReceivedPacket(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);
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;
static constexpr uint32_t kMediaSsrc1 = 1234;
static constexpr uint32_t kMediaSsrc2 = 1235;
static constexpr uint32_t kMediaSsrc3 = 1236;
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));
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(10001, &sender1));
EXPECT_FALSE(rtcp_transceiver.AddMediaSender(10001, &sender2));
EXPECT_TRUE(rtcp_transceiver.AddMediaSender(10002, &sender2));
EXPECT_TRUE(rtcp_transceiver.RemoveMediaSender(10001));
EXPECT_FALSE(rtcp_transceiver.RemoveMediaSender(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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
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;
std::vector<ReportBlock> statistics_report_blocks(40);
MockReceiveStatisticsProvider receive_statistics;
EXPECT_CALL(receive_statistics, RtcpReportBlocks(Ge(40u)))
.WillOnce(Return(statistics_report_blocks));
RtcpTransceiverConfig config = DefaultTestConfig();
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
config.receive_statistics = &receive_statistics;
RtcpTransceiverImpl rtcp_transceiver(config);
MockRtpStreamRtcpHandler sender;
rtcp_transceiver.AddMediaSender(12345, &sender);
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{1});
ASSERT_EQ(rtcp_parser.sender_report()->num_packets(), 1);
ASSERT_EQ(rtcp_parser.receiver_report()->num_packets(), 1);
EXPECT_THAT(rtcp_parser.sender_report()->report_blocks(), SizeIs(31));
EXPECT_THAT(rtcp_parser.receiver_report()->report_blocks(), SizeIs(9));
}
TEST_F(RtcpTransceiverImplTest, RotatesSendersWhenAllSenderReportDoNotFit) {
static constexpr int kNumSenders = 6;
static constexpr uint32_t kSenderSsrc[kNumSenders] = {10, 20, 30, 40, 50, 60};
static constexpr int kSendersPerPacket = 5;
RtcpTransceiverConfig receiver_config = DefaultTestConfig();
RtcpTransceiverImpl rtcp_receiver(receiver_config);
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]);
}
MockFunction<void(rtc::ArrayView<const uint8_t>)> transport;
EXPECT_CALL(transport, Call)
.Times(kNumSenders)
.WillRepeatedly([&](rtc::ArrayView<const uint8_t> packet) {
rtcp_receiver.ReceivePacket(packet, CurrentTime());
return true;
});
RtcpTransceiverConfig config = DefaultTestConfig();
config.rtcp_transport = transport.AsStdFunction();
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;
config.rtcp_transport = RtcpParserTransport(rtcp_parser);
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]);
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(rtcp_parser.processed_rtcp_packets(), size_t{1});
EXPECT_EQ(rtcp_parser.sender_report()->num_packets(), 2);
sender_stats[0].set_num_sent_packets(15);
sender_stats[0].set_num_sent_bytes(2'000);
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{2});
EXPECT_EQ(rtcp_parser.sender_report()->num_packets(), 3);
EXPECT_EQ(rtcp_parser.sender_report()->sender_ssrc(), kSenderSsrc[0]);
sender_stats[1].set_num_sent_packets(20);
sender_stats[1].set_num_sent_bytes(3'000);
rtcp_transceiver.SendCompoundPacket();
EXPECT_EQ(rtcp_parser.processed_rtcp_packets(), size_t{3});
EXPECT_EQ(rtcp_parser.sender_report()->num_packets(), 4);
EXPECT_EQ(rtcp_parser.sender_report()->sender_ssrc(), kSenderSsrc[1]);
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(rtcp_parser.processed_rtcp_packets(), size_t{4});
EXPECT_EQ(rtcp_parser.sender_report()->num_packets(), 6);
}
}
}