blob: aa793331d98f8ac5d017fc2d2772a3489060acae [file] [log] [blame]
/*
* Copyright (c) 2025 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 "video/timing/simulator/rtc_event_log_driver.h"
#include <cstdint>
#include <memory>
#include <utility>
#include "absl/strings/string_view.h"
#include "api/environment/environment.h"
#include "api/units/time_delta.h"
#include "logging/rtc_event_log/rtc_event_log_parser.h"
#include "modules/rtp_rtcp/source/rtp_packet_received.h"
#include "rtc_base/checks.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "video/timing/simulator/test/parsed_rtc_event_log_builder.h"
namespace webrtc::video_timing_simulator {
namespace {
using ::testing::_;
constexpr absl::string_view kEmptyFieldTrialsString = "";
constexpr uint32_t kSsrc1 = 123456;
constexpr uint32_t kSsrc2 = 456789;
class MockRtcEventLogDriverStream : public RtcEventLogDriver::StreamInterface {
public:
MOCK_METHOD(void,
InsertPacket,
(const RtpPacketReceived& rtp_packet),
(override));
MOCK_METHOD(void, Close, (), (override));
};
class MockRtcEventLogDriverStreamFactory {
public:
MockRtcEventLogDriverStreamFactory()
: stream1_(std::make_unique<MockRtcEventLogDriverStream>()),
stream2_(std::make_unique<MockRtcEventLogDriverStream>()),
stream1_ptr_(stream1_.get()),
stream2_ptr_(stream2_.get()) {}
~MockRtcEventLogDriverStreamFactory() = default;
std::unique_ptr<MockRtcEventLogDriverStream> Create(Environment,
uint32_t ssrc) {
if (ssrc == kSsrc1) {
RTC_CHECK(stream1_) << "Stream 1 was already moved";
return std::move(stream1_);
}
if (ssrc == kSsrc2) {
RTC_CHECK(stream2_) << "Stream 2 was already moved";
return std::move(stream2_);
}
RTC_CHECK_NOTREACHED();
return std::make_unique<MockRtcEventLogDriverStream>();
}
int NumStreamsCreated() const {
return static_cast<int>(stream1_ == nullptr) +
static_cast<int>(stream2_ == nullptr);
}
// Unique pointers for ownership.
std::unique_ptr<MockRtcEventLogDriverStream> stream1_;
std::unique_ptr<MockRtcEventLogDriverStream> stream2_;
// Raw pointers for expectations.
MockRtcEventLogDriverStream* stream1_ptr_;
MockRtcEventLogDriverStream* stream2_ptr_;
};
class RtcEventLogDriverTest : public ::testing::Test {
protected:
auto BuildStreamFactory() {
return [this](Environment env, uint32_t ssrc) {
return stream_factory_.Create(env, ssrc);
};
}
MockRtcEventLogDriverStreamFactory stream_factory_;
ParsedRtcEventLogBuilder parsed_log_builder_;
};
TEST_F(RtcEventLogDriverTest, EmptyLogDoesNotCreateStreams) {
std::unique_ptr<ParsedRtcEventLog> parsed_log = parsed_log_builder_.Build();
RtcEventLogDriver driver(RtcEventLogDriver::Config(), parsed_log.get(),
kEmptyFieldTrialsString, BuildStreamFactory());
driver.Simulate();
EXPECT_EQ(stream_factory_.NumStreamsCreated(), 0);
}
TEST_F(RtcEventLogDriverTest, LoggedVideoRecvConfigCreatesStream) {
parsed_log_builder_.LogVideoRecvConfig(kSsrc1);
std::unique_ptr<ParsedRtcEventLog> parsed_log = parsed_log_builder_.Build();
RtcEventLogDriver driver(RtcEventLogDriver::Config(), parsed_log.get(),
kEmptyFieldTrialsString, BuildStreamFactory());
EXPECT_CALL(*stream_factory_.stream1_ptr_, Close());
driver.Simulate();
EXPECT_EQ(stream_factory_.NumStreamsCreated(), 1);
}
TEST_F(RtcEventLogDriverTest, LoggedVideoRecvConfigsCreateStreams) {
parsed_log_builder_.LogVideoRecvConfig(kSsrc1);
parsed_log_builder_.LogVideoRecvConfig(kSsrc2);
std::unique_ptr<ParsedRtcEventLog> parsed_log = parsed_log_builder_.Build();
RtcEventLogDriver driver(RtcEventLogDriver::Config(), parsed_log.get(),
kEmptyFieldTrialsString, BuildStreamFactory());
EXPECT_CALL(*stream_factory_.stream1_ptr_, Close());
EXPECT_CALL(*stream_factory_.stream2_ptr_, Close());
driver.Simulate();
EXPECT_EQ(stream_factory_.NumStreamsCreated(), 2);
}
class CountingRtcEventLogDriverStreamFactoryFactory {
public:
auto BuildStreamFactory() {
return [this](Environment env, uint32_t ssrc) {
++num_streams_created_;
return std::make_unique<MockRtcEventLogDriverStream>();
};
}
int NumStreamsCreated() const { return num_streams_created_; }
private:
int num_streams_created_ = 0;
};
TEST_F(RtcEventLogDriverTest, ReusesStreamIfConfigured) {
parsed_log_builder_.LogVideoRecvConfig(kSsrc1);
parsed_log_builder_.LogVideoRecvConfig(kSsrc1);
std::unique_ptr<ParsedRtcEventLog> parsed_log = parsed_log_builder_.Build();
CountingRtcEventLogDriverStreamFactoryFactory counter;
RtcEventLogDriver driver(RtcEventLogDriver::Config{.reuse_streams = true},
parsed_log.get(), kEmptyFieldTrialsString,
counter.BuildStreamFactory());
driver.Simulate();
EXPECT_EQ(counter.NumStreamsCreated(), 1);
}
TEST_F(RtcEventLogDriverTest, DoesNotReuseStreamIfConfigured) {
parsed_log_builder_.LogVideoRecvConfig(kSsrc1);
parsed_log_builder_.LogVideoRecvConfig(kSsrc1);
std::unique_ptr<ParsedRtcEventLog> parsed_log = parsed_log_builder_.Build();
CountingRtcEventLogDriverStreamFactoryFactory counter;
RtcEventLogDriver driver(RtcEventLogDriver::Config{.reuse_streams = false},
parsed_log.get(), kEmptyFieldTrialsString,
counter.BuildStreamFactory());
driver.Simulate();
EXPECT_EQ(counter.NumStreamsCreated(), 2);
}
TEST_F(RtcEventLogDriverTest, FirstLoggedEventSetsSimulationClock) {
parsed_log_builder_.LogVideoRecvConfig(kSsrc1);
std::unique_ptr<ParsedRtcEventLog> parsed_log = parsed_log_builder_.Build();
RtcEventLogDriver driver(RtcEventLogDriver::Config(), parsed_log.get(),
kEmptyFieldTrialsString, BuildStreamFactory());
driver.Simulate();
EXPECT_EQ(driver.GetCurrentTimeForTesting(),
parsed_log_builder_.CurrentTime() +
RtcEventLogDriver::kShutdownAdvanceTimeSlack);
}
TEST_F(RtcEventLogDriverTest, LoggedEventAdvancesSimulationClock) {
parsed_log_builder_.LogVideoRecvConfig(kSsrc1);
parsed_log_builder_.AdvanceTime(TimeDelta::Millis(50));
parsed_log_builder_.LogVideoRecvConfig(kSsrc2);
std::unique_ptr<ParsedRtcEventLog> parsed_log = parsed_log_builder_.Build();
RtcEventLogDriver driver(RtcEventLogDriver::Config(), parsed_log.get(),
kEmptyFieldTrialsString, BuildStreamFactory());
driver.Simulate();
EXPECT_EQ(driver.GetCurrentTimeForTesting(),
parsed_log_builder_.CurrentTime() +
RtcEventLogDriver::kShutdownAdvanceTimeSlack);
}
TEST_F(RtcEventLogDriverTest, LoggedRtpPacketIncomingInsertsPacketIntoStream) {
parsed_log_builder_.LogVideoRecvConfig(kSsrc1);
parsed_log_builder_.LogRtpPacketIncoming(kSsrc1);
std::unique_ptr<ParsedRtcEventLog> parsed_log = parsed_log_builder_.Build();
EXPECT_CALL(*stream_factory_.stream1_ptr_, InsertPacket(_));
RtcEventLogDriver driver(RtcEventLogDriver::Config(), parsed_log.get(),
kEmptyFieldTrialsString, BuildStreamFactory());
driver.Simulate();
}
TEST_F(RtcEventLogDriverTest,
LoggedRtpPacketIncomingsInsertsPacketsIntoStreams) {
parsed_log_builder_.LogVideoRecvConfig(kSsrc1);
parsed_log_builder_.LogVideoRecvConfig(kSsrc2);
parsed_log_builder_.LogRtpPacketIncoming(kSsrc1);
parsed_log_builder_.LogRtpPacketIncoming(kSsrc2);
std::unique_ptr<ParsedRtcEventLog> parsed_log = parsed_log_builder_.Build();
EXPECT_CALL(*stream_factory_.stream1_ptr_, InsertPacket(_));
EXPECT_CALL(*stream_factory_.stream2_ptr_, InsertPacket(_));
RtcEventLogDriver driver(RtcEventLogDriver::Config(), parsed_log.get(),
kEmptyFieldTrialsString, BuildStreamFactory());
driver.Simulate();
}
} // namespace
} // namespace webrtc::video_timing_simulator