summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc')
-rw-r--r--third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc1401
1 files changed, 1401 insertions, 0 deletions
diff --git a/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc
new file mode 100644
index 0000000000..7039fe7eb6
--- /dev/null
+++ b/third_party/libwebrtc/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc
@@ -0,0 +1,1401 @@
+/*
+ * 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 <deque>
+#include <limits>
+#include <memory>
+#include <string>
+#include <tuple>
+
+#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.h"
+#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h"
+#include "logging/rtc_event_log/encoder/rtc_event_log_encoder_v3.h"
+#include "logging/rtc_event_log/events/rtc_event_alr_state.h"
+#include "logging/rtc_event_log/events/rtc_event_audio_network_adaptation.h"
+#include "logging/rtc_event_log/events/rtc_event_audio_playout.h"
+#include "logging/rtc_event_log/events/rtc_event_audio_receive_stream_config.h"
+#include "logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h"
+#include "logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h"
+#include "logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h"
+#include "logging/rtc_event_log/events/rtc_event_probe_cluster_created.h"
+#include "logging/rtc_event_log/events/rtc_event_probe_result_failure.h"
+#include "logging/rtc_event_log/events/rtc_event_probe_result_success.h"
+#include "logging/rtc_event_log/events/rtc_event_rtcp_packet_incoming.h"
+#include "logging/rtc_event_log/events/rtc_event_rtcp_packet_outgoing.h"
+#include "logging/rtc_event_log/events/rtc_event_rtp_packet_incoming.h"
+#include "logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h"
+#include "logging/rtc_event_log/events/rtc_event_video_receive_stream_config.h"
+#include "logging/rtc_event_log/events/rtc_event_video_send_stream_config.h"
+#include "logging/rtc_event_log/rtc_event_log_parser.h"
+#include "logging/rtc_event_log/rtc_event_log_unittest_helper.h"
+#include "modules/audio_coding/audio_network_adaptor/include/audio_network_adaptor_config.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/bye.h"
+#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
+#include "rtc_base/fake_clock.h"
+#include "rtc_base/random.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+class RtcEventLogEncoderTest
+ : public ::testing::TestWithParam<
+ std::tuple<int, RtcEventLog::EncodingType, size_t, bool>> {
+ protected:
+ RtcEventLogEncoderTest()
+ : seed_(std::get<0>(GetParam())),
+ prng_(seed_),
+ encoding_type_(std::get<1>(GetParam())),
+ event_count_(std::get<2>(GetParam())),
+ force_repeated_fields_(std::get<3>(GetParam())),
+ gen_(seed_ * 880001UL),
+ verifier_(encoding_type_) {
+ switch (encoding_type_) {
+ case RtcEventLog::EncodingType::Legacy:
+ encoder_ = std::make_unique<RtcEventLogEncoderLegacy>();
+ break;
+ case RtcEventLog::EncodingType::NewFormat:
+ encoder_ = std::make_unique<RtcEventLogEncoderNewFormat>();
+ break;
+ case RtcEventLog::EncodingType::ProtoFree:
+ encoder_ = std::make_unique<RtcEventLogEncoderV3>();
+ break;
+ }
+ encoded_ =
+ encoder_->EncodeLogStart(rtc::TimeMillis(), rtc::TimeUTCMillis());
+ }
+ ~RtcEventLogEncoderTest() override = default;
+
+ // ANA events have some optional fields, so we want to make sure that we get
+ // correct behavior both when all of the values are there, as well as when
+ // only some.
+ void TestRtcEventAudioNetworkAdaptation(
+ const std::vector<std::unique_ptr<RtcEventAudioNetworkAdaptation>>&);
+
+ template <typename EventType>
+ std::unique_ptr<EventType> NewRtpPacket(
+ uint32_t ssrc,
+ const RtpHeaderExtensionMap& extension_map);
+
+ template <typename ParsedType>
+ const std::vector<ParsedType>* GetRtpPacketsBySsrc(
+ const ParsedRtcEventLog* parsed_log,
+ uint32_t ssrc);
+
+ template <typename EventType, typename ParsedType>
+ void TestRtpPackets();
+
+ std::deque<std::unique_ptr<RtcEvent>> history_;
+ std::unique_ptr<RtcEventLogEncoder> encoder_;
+ ParsedRtcEventLog parsed_log_;
+ const uint64_t seed_;
+ Random prng_;
+ const RtcEventLog::EncodingType encoding_type_;
+ const size_t event_count_;
+ const bool force_repeated_fields_;
+ test::EventGenerator gen_;
+ test::EventVerifier verifier_;
+ std::string encoded_;
+};
+
+void RtcEventLogEncoderTest::TestRtcEventAudioNetworkAdaptation(
+ const std::vector<std::unique_ptr<RtcEventAudioNetworkAdaptation>>&
+ events) {
+ ASSERT_TRUE(history_.empty()) << "Function should be called once per test.";
+
+ for (auto& event : events) {
+ history_.push_back(event->Copy());
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+ const auto& ana_configs = parsed_log_.audio_network_adaptation_events();
+
+ ASSERT_EQ(ana_configs.size(), events.size());
+ for (size_t i = 0; i < events.size(); ++i) {
+ verifier_.VerifyLoggedAudioNetworkAdaptationEvent(*events[i],
+ ana_configs[i]);
+ }
+}
+
+template <>
+std::unique_ptr<RtcEventRtpPacketIncoming> RtcEventLogEncoderTest::NewRtpPacket(
+ uint32_t ssrc,
+ const RtpHeaderExtensionMap& extension_map) {
+ return gen_.NewRtpPacketIncoming(ssrc, extension_map, false);
+}
+
+template <>
+std::unique_ptr<RtcEventRtpPacketOutgoing> RtcEventLogEncoderTest::NewRtpPacket(
+ uint32_t ssrc,
+ const RtpHeaderExtensionMap& extension_map) {
+ return gen_.NewRtpPacketOutgoing(ssrc, extension_map, false);
+}
+
+template <>
+const std::vector<LoggedRtpPacketIncoming>*
+RtcEventLogEncoderTest::GetRtpPacketsBySsrc(const ParsedRtcEventLog* parsed_log,
+ uint32_t ssrc) {
+ const auto& incoming_streams = parsed_log->incoming_rtp_packets_by_ssrc();
+ for (const auto& stream : incoming_streams) {
+ if (stream.ssrc == ssrc) {
+ return &stream.incoming_packets;
+ }
+ }
+ return nullptr;
+}
+
+template <>
+const std::vector<LoggedRtpPacketOutgoing>*
+RtcEventLogEncoderTest::GetRtpPacketsBySsrc(const ParsedRtcEventLog* parsed_log,
+ uint32_t ssrc) {
+ const auto& outgoing_streams = parsed_log->outgoing_rtp_packets_by_ssrc();
+ for (const auto& stream : outgoing_streams) {
+ if (stream.ssrc == ssrc) {
+ return &stream.outgoing_packets;
+ }
+ }
+ return nullptr;
+}
+
+template <typename EventType, typename ParsedType>
+void RtcEventLogEncoderTest::TestRtpPackets() {
+ // SSRCs will be randomly assigned out of this small pool, significant only
+ // in that it also covers such edge cases as SSRC = 0 and SSRC = 0xffffffff.
+ // The pool is intentionally small, so as to produce collisions.
+ const std::vector<uint32_t> kSsrcPool = {0x00000000, 0x12345678, 0xabcdef01,
+ 0xffffffff, 0x20171024, 0x19840730,
+ 0x19831230};
+
+ // TODO(terelius): Test extensions for legacy encoding, too.
+ RtpHeaderExtensionMap extension_map;
+ if (encoding_type_ != RtcEventLog::EncodingType::Legacy) {
+ extension_map = gen_.NewRtpHeaderExtensionMap(true);
+ }
+
+ // Simulate `event_count_` RTP packets, with SSRCs assigned randomly
+ // out of the small pool above.
+ std::map<uint32_t, std::vector<std::unique_ptr<EventType>>> events_by_ssrc;
+ for (size_t i = 0; i < event_count_; ++i) {
+ const uint32_t ssrc = kSsrcPool[prng_.Rand(kSsrcPool.size() - 1)];
+ std::unique_ptr<EventType> event =
+ (events_by_ssrc[ssrc].empty() || !force_repeated_fields_)
+ ? NewRtpPacket<EventType>(ssrc, extension_map)
+ : events_by_ssrc[ssrc][0]->Copy();
+ history_.push_back(event->Copy());
+ events_by_ssrc[ssrc].emplace_back(std::move(event));
+ }
+
+ // Encode and parse.
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ // For each SSRC, make sure the RTP packets associated with it to have been
+ // correctly encoded and parsed.
+ for (auto it = events_by_ssrc.begin(); it != events_by_ssrc.end(); ++it) {
+ const uint32_t ssrc = it->first;
+ const auto& original_packets = it->second;
+ const std::vector<ParsedType>* parsed_rtp_packets =
+ GetRtpPacketsBySsrc<ParsedType>(&parsed_log_, ssrc);
+ ASSERT_NE(parsed_rtp_packets, nullptr);
+ ASSERT_EQ(original_packets.size(), parsed_rtp_packets->size());
+ for (size_t i = 0; i < original_packets.size(); ++i) {
+ verifier_.VerifyLoggedRtpPacket<EventType, ParsedType>(
+ *original_packets[i], (*parsed_rtp_packets)[i]);
+ }
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventAlrState) {
+ std::vector<std::unique_ptr<RtcEventAlrState>> events(event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ events[i] = (i == 0 || !force_repeated_fields_) ? gen_.NewAlrState()
+ : events[0]->Copy();
+ history_.push_back(events[i]->Copy());
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+ const auto& alr_state_events = parsed_log_.alr_state_events();
+
+ ASSERT_EQ(alr_state_events.size(), event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedAlrStateEvent(*events[i], alr_state_events[i]);
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventRouteChange) {
+ if (encoding_type_ == RtcEventLog::EncodingType::Legacy) {
+ return;
+ }
+ std::vector<std::unique_ptr<RtcEventRouteChange>> events(event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ events[i] = (i == 0 || !force_repeated_fields_) ? gen_.NewRouteChange()
+ : events[0]->Copy();
+ history_.push_back(events[i]->Copy());
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+ const auto& route_change_events = parsed_log_.route_change_events();
+
+ ASSERT_EQ(route_change_events.size(), event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedRouteChangeEvent(*events[i], route_change_events[i]);
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventRemoteEstimate) {
+ std::vector<std::unique_ptr<RtcEventRemoteEstimate>> events(event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ events[i] = (i == 0 || !force_repeated_fields_)
+ ? gen_.NewRemoteEstimate()
+ : std::make_unique<RtcEventRemoteEstimate>(*events[0]);
+ history_.push_back(std::make_unique<RtcEventRemoteEstimate>(*events[i]));
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+ const auto& parsed_events = parsed_log_.remote_estimate_events();
+
+ ASSERT_EQ(parsed_events.size(), event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedRemoteEstimateEvent(*events[i], parsed_events[i]);
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationBitrate) {
+ std::vector<std::unique_ptr<RtcEventAudioNetworkAdaptation>> events(
+ event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ if (i == 0 || !force_repeated_fields_) {
+ auto runtime_config = std::make_unique<AudioEncoderRuntimeConfig>();
+ const int bitrate_bps = rtc::checked_cast<int>(
+ prng_.Rand(0, std::numeric_limits<int32_t>::max()));
+ runtime_config->bitrate_bps = bitrate_bps;
+ events[i] = std::make_unique<RtcEventAudioNetworkAdaptation>(
+ std::move(runtime_config));
+ } else {
+ events[i] = events[0]->Copy();
+ }
+ }
+ TestRtcEventAudioNetworkAdaptation(events);
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationFrameLength) {
+ std::vector<std::unique_ptr<RtcEventAudioNetworkAdaptation>> events(
+ event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ if (i == 0 || !force_repeated_fields_) {
+ auto runtime_config = std::make_unique<AudioEncoderRuntimeConfig>();
+ const int frame_length_ms = prng_.Rand(1, 1000);
+ runtime_config->frame_length_ms = frame_length_ms;
+ events[i] = std::make_unique<RtcEventAudioNetworkAdaptation>(
+ std::move(runtime_config));
+ } else {
+ events[i] = events[0]->Copy();
+ }
+ }
+ TestRtcEventAudioNetworkAdaptation(events);
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationPacketLoss) {
+ std::vector<std::unique_ptr<RtcEventAudioNetworkAdaptation>> events(
+ event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ if (i == 0 || !force_repeated_fields_) {
+ // To simplify the test, we just check powers of two.
+ const float plr = std::pow(0.5f, prng_.Rand(1, 8));
+ auto runtime_config = std::make_unique<AudioEncoderRuntimeConfig>();
+ runtime_config->uplink_packet_loss_fraction = plr;
+ events[i] = std::make_unique<RtcEventAudioNetworkAdaptation>(
+ std::move(runtime_config));
+ } else {
+ events[i] = events[0]->Copy();
+ }
+ }
+ TestRtcEventAudioNetworkAdaptation(events);
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationFec) {
+ std::vector<std::unique_ptr<RtcEventAudioNetworkAdaptation>> events(
+ event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ if (i == 0 || !force_repeated_fields_) {
+ auto runtime_config = std::make_unique<AudioEncoderRuntimeConfig>();
+ runtime_config->enable_fec = prng_.Rand<bool>();
+ events[i] = std::make_unique<RtcEventAudioNetworkAdaptation>(
+ std::move(runtime_config));
+ } else {
+ events[i] = events[0]->Copy();
+ }
+ }
+ TestRtcEventAudioNetworkAdaptation(events);
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationDtx) {
+ std::vector<std::unique_ptr<RtcEventAudioNetworkAdaptation>> events(
+ event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ if (i == 0 || !force_repeated_fields_) {
+ auto runtime_config = std::make_unique<AudioEncoderRuntimeConfig>();
+ runtime_config->enable_dtx = prng_.Rand<bool>();
+ events[i] = std::make_unique<RtcEventAudioNetworkAdaptation>(
+ std::move(runtime_config));
+ } else {
+ events[i] = events[0]->Copy();
+ }
+ }
+ TestRtcEventAudioNetworkAdaptation(events);
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationChannels) {
+ std::vector<std::unique_ptr<RtcEventAudioNetworkAdaptation>> events(
+ event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ if (i == 0 || !force_repeated_fields_) {
+ auto runtime_config = std::make_unique<AudioEncoderRuntimeConfig>();
+ runtime_config->num_channels = prng_.Rand(1, 2);
+ events[i] = std::make_unique<RtcEventAudioNetworkAdaptation>(
+ std::move(runtime_config));
+ } else {
+ events[i] = events[0]->Copy();
+ }
+ }
+ TestRtcEventAudioNetworkAdaptation(events);
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventAudioNetworkAdaptationAll) {
+ std::vector<std::unique_ptr<RtcEventAudioNetworkAdaptation>> events(
+ event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ if (i == 0 || !force_repeated_fields_) {
+ auto runtime_config = std::make_unique<AudioEncoderRuntimeConfig>();
+ runtime_config->bitrate_bps = rtc::checked_cast<int>(
+ prng_.Rand(0, std::numeric_limits<int32_t>::max()));
+ runtime_config->frame_length_ms = prng_.Rand(1, 1000);
+ runtime_config->uplink_packet_loss_fraction =
+ std::pow(0.5f, prng_.Rand(1, 8));
+ runtime_config->enable_fec = prng_.Rand<bool>();
+ runtime_config->enable_dtx = prng_.Rand<bool>();
+ runtime_config->num_channels = prng_.Rand(1, 2);
+ events[i] = std::make_unique<RtcEventAudioNetworkAdaptation>(
+ std::move(runtime_config));
+ } else {
+ events[i] = events[0]->Copy();
+ }
+ }
+ TestRtcEventAudioNetworkAdaptation(events);
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventAudioPlayout) {
+ // SSRCs will be randomly assigned out of this small pool, significant only
+ // in that it also covers such edge cases as SSRC = 0 and SSRC = 0xffffffff.
+ // The pool is intentionally small, so as to produce collisions.
+ const std::vector<uint32_t> kSsrcPool = {0x00000000, 0x12345678, 0xabcdef01,
+ 0xffffffff, 0x20171024, 0x19840730,
+ 0x19831230};
+
+ std::map<uint32_t, std::vector<std::unique_ptr<RtcEventAudioPlayout>>>
+ original_events_by_ssrc;
+ for (size_t i = 0; i < event_count_; ++i) {
+ const uint32_t ssrc = kSsrcPool[prng_.Rand(kSsrcPool.size() - 1)];
+ std::unique_ptr<RtcEventAudioPlayout> event =
+ (original_events_by_ssrc[ssrc].empty() || !force_repeated_fields_)
+ ? gen_.NewAudioPlayout(ssrc)
+ : original_events_by_ssrc[ssrc][0]->Copy();
+ history_.push_back(event->Copy());
+ original_events_by_ssrc[ssrc].push_back(std::move(event));
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ const auto& parsed_playout_events_by_ssrc =
+ parsed_log_.audio_playout_events();
+
+ // Same number of distinct SSRCs.
+ ASSERT_EQ(parsed_playout_events_by_ssrc.size(),
+ original_events_by_ssrc.size());
+
+ for (auto& original_event_it : original_events_by_ssrc) {
+ const uint32_t ssrc = original_event_it.first;
+ const auto& original_playout_events = original_event_it.second;
+
+ const auto& parsed_event_it = parsed_playout_events_by_ssrc.find(ssrc);
+ ASSERT_TRUE(parsed_event_it != parsed_playout_events_by_ssrc.end());
+ const auto& parsed_playout_events = parsed_event_it->second;
+
+ // Same number playout events for the SSRC under examination.
+ ASSERT_EQ(original_playout_events.size(), parsed_playout_events.size());
+
+ for (size_t i = 0; i < original_playout_events.size(); ++i) {
+ verifier_.VerifyLoggedAudioPlayoutEvent(*original_playout_events[i],
+ parsed_playout_events[i]);
+ }
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventNetEqSetMinimumDelayDecoded) {
+ // SSRCs will be randomly assigned out of this small pool, significant only
+ // in that it also covers such edge cases as SSRC = 0 and SSRC = 0xffffffff.
+ // The pool is intentionally small, so as to produce collisions.
+ const std::vector<uint32_t> kSsrcPool = {0x00000000, 0x12345678, 0xabcdef01,
+ 0xffffffff, 0x20171024, 0x19840730,
+ 0x19831230};
+ std::map<uint32_t, std::vector<std::unique_ptr<RtcEventNetEqSetMinimumDelay>>>
+ original_events_by_ssrc;
+ for (size_t i = 0; i < event_count_; ++i) {
+ const uint32_t ssrc = kSsrcPool[prng_.Rand(kSsrcPool.size() - 1)];
+ std::unique_ptr<RtcEventNetEqSetMinimumDelay> event =
+ (original_events_by_ssrc[ssrc].empty() || !force_repeated_fields_)
+ ? gen_.NewNetEqSetMinimumDelay(ssrc)
+ : original_events_by_ssrc[ssrc][0]->Copy();
+ history_.push_back(event->Copy());
+ original_events_by_ssrc[ssrc].push_back(std::move(event));
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ const auto& parsed_neteq_set_minimum_delay_events_by_ssrc =
+ parsed_log_.neteq_set_minimum_delay_events();
+
+ if (encoding_type_ == RtcEventLog::EncodingType::Legacy) {
+ ASSERT_EQ(parsed_neteq_set_minimum_delay_events_by_ssrc.size(), 0u);
+ return;
+ }
+
+ // Same number of distinct SSRCs.
+ ASSERT_EQ(parsed_neteq_set_minimum_delay_events_by_ssrc.size(),
+ original_events_by_ssrc.size());
+
+ for (auto& original_event_it : original_events_by_ssrc) {
+ const uint32_t ssrc = original_event_it.first;
+ const auto& original_neteq_set_minimum_delay_events =
+ original_event_it.second;
+
+ const auto& parsed_event_it =
+ parsed_neteq_set_minimum_delay_events_by_ssrc.find(ssrc);
+ ASSERT_TRUE(parsed_event_it !=
+ parsed_neteq_set_minimum_delay_events_by_ssrc.end());
+ const auto& parsed_neteq_set_minimum_delay_events = parsed_event_it->second;
+
+ // Same number playout events for the SSRC under examination.
+ ASSERT_EQ(original_neteq_set_minimum_delay_events.size(),
+ parsed_neteq_set_minimum_delay_events.size());
+
+ for (size_t i = 0; i < original_neteq_set_minimum_delay_events.size();
+ ++i) {
+ verifier_.VerifyLoggedNetEqSetMinimumDelay(
+ *original_neteq_set_minimum_delay_events[i],
+ parsed_neteq_set_minimum_delay_events[i]);
+ }
+ }
+}
+
+// TODO(eladalon/terelius): Test with multiple events in the batch.
+TEST_P(RtcEventLogEncoderTest, RtcEventAudioReceiveStreamConfig) {
+ uint32_t ssrc = prng_.Rand<uint32_t>();
+ RtpHeaderExtensionMap extensions = gen_.NewRtpHeaderExtensionMap();
+ std::unique_ptr<RtcEventAudioReceiveStreamConfig> event =
+ gen_.NewAudioReceiveStreamConfig(ssrc, extensions);
+ history_.push_back(event->Copy());
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+ const auto& audio_recv_configs = parsed_log_.audio_recv_configs();
+
+ ASSERT_EQ(audio_recv_configs.size(), 1u);
+ verifier_.VerifyLoggedAudioRecvConfig(*event, audio_recv_configs[0]);
+}
+
+// TODO(eladalon/terelius): Test with multiple events in the batch.
+TEST_P(RtcEventLogEncoderTest, RtcEventAudioSendStreamConfig) {
+ uint32_t ssrc = prng_.Rand<uint32_t>();
+ RtpHeaderExtensionMap extensions = gen_.NewRtpHeaderExtensionMap();
+ std::unique_ptr<RtcEventAudioSendStreamConfig> event =
+ gen_.NewAudioSendStreamConfig(ssrc, extensions);
+ history_.push_back(event->Copy());
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+ const auto& audio_send_configs = parsed_log_.audio_send_configs();
+
+ ASSERT_EQ(audio_send_configs.size(), 1u);
+ verifier_.VerifyLoggedAudioSendConfig(*event, audio_send_configs[0]);
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventBweUpdateDelayBased) {
+ std::vector<std::unique_ptr<RtcEventBweUpdateDelayBased>> events(
+ event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ events[i] = (i == 0 || !force_repeated_fields_)
+ ? gen_.NewBweUpdateDelayBased()
+ : events[0]->Copy();
+ history_.push_back(events[i]->Copy());
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ const auto& bwe_delay_updates = parsed_log_.bwe_delay_updates();
+ ASSERT_EQ(bwe_delay_updates.size(), event_count_);
+
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedBweDelayBasedUpdate(*events[i], bwe_delay_updates[i]);
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventBweUpdateLossBased) {
+ std::vector<std::unique_ptr<RtcEventBweUpdateLossBased>> events(event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ events[i] = (i == 0 || !force_repeated_fields_)
+ ? gen_.NewBweUpdateLossBased()
+ : events[0]->Copy();
+ history_.push_back(events[i]->Copy());
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ const auto& bwe_loss_updates = parsed_log_.bwe_loss_updates();
+ ASSERT_EQ(bwe_loss_updates.size(), event_count_);
+
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedBweLossBasedUpdate(*events[i], bwe_loss_updates[i]);
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventGenericPacketReceived) {
+ if (encoding_type_ == RtcEventLog::EncodingType::Legacy) {
+ return;
+ }
+ std::vector<std::unique_ptr<RtcEventGenericPacketReceived>> events(
+ event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ events[i] = (i == 0 || !force_repeated_fields_)
+ ? gen_.NewGenericPacketReceived()
+ : events[0]->Copy();
+ history_.push_back(events[i]->Copy());
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ const auto& packets_received = parsed_log_.generic_packets_received();
+ ASSERT_EQ(packets_received.size(), event_count_);
+
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedGenericPacketReceived(*events[i],
+ packets_received[i]);
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventGenericPacketSent) {
+ if (encoding_type_ == RtcEventLog::EncodingType::Legacy) {
+ return;
+ }
+ std::vector<std::unique_ptr<RtcEventGenericPacketSent>> events(event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ events[i] = (i == 0 || !force_repeated_fields_)
+ ? gen_.NewGenericPacketSent()
+ : events[0]->Copy();
+ history_.push_back(events[i]->Copy());
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ const auto& packets_sent = parsed_log_.generic_packets_sent();
+ ASSERT_EQ(packets_sent.size(), event_count_);
+
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedGenericPacketSent(*events[i], packets_sent[i]);
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventGenericAcksReceived) {
+ if (encoding_type_ == RtcEventLog::EncodingType::Legacy) {
+ return;
+ }
+ std::vector<std::unique_ptr<RtcEventGenericAckReceived>> events(event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ events[i] = (i == 0 || !force_repeated_fields_)
+ ? gen_.NewGenericAckReceived()
+ : events[0]->Copy();
+ history_.push_back(events[i]->Copy());
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ const auto& decoded_events = parsed_log_.generic_acks_received();
+ ASSERT_EQ(decoded_events.size(), event_count_);
+
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedGenericAckReceived(*events[i], decoded_events[i]);
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventDtlsTransportState) {
+ std::vector<std::unique_ptr<RtcEventDtlsTransportState>> events(event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ events[i] = (i == 0 || !force_repeated_fields_)
+ ? gen_.NewDtlsTransportState()
+ : events[0]->Copy();
+ history_.push_back(events[i]->Copy());
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ const auto& dtls_transport_states = parsed_log_.dtls_transport_states();
+ if (encoding_type_ == RtcEventLog::EncodingType::Legacy) {
+ ASSERT_EQ(dtls_transport_states.size(), 0u);
+ return;
+ }
+
+ ASSERT_EQ(dtls_transport_states.size(), event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedDtlsTransportState(*events[i],
+ dtls_transport_states[i]);
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventDtlsWritableState) {
+ std::vector<std::unique_ptr<RtcEventDtlsWritableState>> events(event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ events[i] = (i == 0 || !force_repeated_fields_)
+ ? gen_.NewDtlsWritableState()
+ : events[0]->Copy();
+ history_.push_back(events[i]->Copy());
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ const auto& dtls_writable_states = parsed_log_.dtls_writable_states();
+ if (encoding_type_ == RtcEventLog::EncodingType::Legacy) {
+ ASSERT_EQ(dtls_writable_states.size(), 0u);
+ return;
+ }
+
+ ASSERT_EQ(dtls_writable_states.size(), event_count_);
+
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedDtlsWritableState(*events[i],
+ dtls_writable_states[i]);
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventFrameDecoded) {
+ // SSRCs will be randomly assigned out of this small pool, significant only
+ // in that it also covers such edge cases as SSRC = 0 and SSRC = 0xffffffff.
+ // The pool is intentionally small, so as to produce collisions.
+ const std::vector<uint32_t> kSsrcPool = {0x00000000, 0x12345678, 0xabcdef01,
+ 0xffffffff, 0x20171024, 0x19840730,
+ 0x19831230};
+
+ std::map<uint32_t, std::vector<std::unique_ptr<RtcEventFrameDecoded>>>
+ original_events_by_ssrc;
+ for (size_t i = 0; i < event_count_; ++i) {
+ const uint32_t ssrc = kSsrcPool[prng_.Rand(kSsrcPool.size() - 1)];
+ std::unique_ptr<RtcEventFrameDecoded> event =
+ (original_events_by_ssrc[ssrc].empty() || !force_repeated_fields_)
+ ? gen_.NewFrameDecodedEvent(ssrc)
+ : original_events_by_ssrc[ssrc][0]->Copy();
+ history_.push_back(event->Copy());
+ original_events_by_ssrc[ssrc].push_back(std::move(event));
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ auto status = parsed_log_.ParseString(encoded_);
+ if (!status.ok())
+ RTC_LOG(LS_ERROR) << status.message();
+ ASSERT_TRUE(status.ok());
+
+ const auto& decoded_frames_by_ssrc = parsed_log_.decoded_frames();
+ if (encoding_type_ == RtcEventLog::EncodingType::Legacy) {
+ ASSERT_EQ(decoded_frames_by_ssrc.size(), 0u);
+ return;
+ }
+
+ // Same number of distinct SSRCs.
+ ASSERT_EQ(decoded_frames_by_ssrc.size(), original_events_by_ssrc.size());
+
+ for (const auto& original_event_it : original_events_by_ssrc) {
+ const uint32_t ssrc = original_event_it.first;
+ const std::vector<std::unique_ptr<RtcEventFrameDecoded>>& original_frames =
+ original_event_it.second;
+
+ const auto& parsed_event_it = decoded_frames_by_ssrc.find(ssrc);
+ ASSERT_TRUE(parsed_event_it != decoded_frames_by_ssrc.end());
+ const std::vector<LoggedFrameDecoded>& parsed_frames =
+ parsed_event_it->second;
+
+ // Same number events for the SSRC under examination.
+ ASSERT_EQ(original_frames.size(), parsed_frames.size());
+
+ for (size_t i = 0; i < original_frames.size(); ++i) {
+ verifier_.VerifyLoggedFrameDecoded(*original_frames[i], parsed_frames[i]);
+ }
+ }
+}
+
+// TODO(eladalon/terelius): Test with multiple events in the batch.
+TEST_P(RtcEventLogEncoderTest, RtcEventIceCandidatePairConfig) {
+ std::unique_ptr<RtcEventIceCandidatePairConfig> event =
+ gen_.NewIceCandidatePairConfig();
+ history_.push_back(event->Copy());
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+ const auto& ice_candidate_pair_configs =
+ parsed_log_.ice_candidate_pair_configs();
+
+ ASSERT_EQ(ice_candidate_pair_configs.size(), 1u);
+ verifier_.VerifyLoggedIceCandidatePairConfig(*event,
+ ice_candidate_pair_configs[0]);
+}
+
+// TODO(eladalon/terelius): Test with multiple events in the batch.
+TEST_P(RtcEventLogEncoderTest, RtcEventIceCandidatePair) {
+ std::unique_ptr<RtcEventIceCandidatePair> event = gen_.NewIceCandidatePair();
+ history_.push_back(event->Copy());
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+ const auto& ice_candidate_pair_events =
+ parsed_log_.ice_candidate_pair_events();
+
+ ASSERT_EQ(ice_candidate_pair_events.size(), 1u);
+ verifier_.VerifyLoggedIceCandidatePairEvent(*event,
+ ice_candidate_pair_events[0]);
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventLoggingStarted) {
+ const int64_t timestamp_ms = prng_.Rand(1'000'000'000);
+ const int64_t utc_time_ms = prng_.Rand(1'000'000'000);
+
+ // Overwrite the previously encoded LogStart event.
+ encoded_ = encoder_->EncodeLogStart(timestamp_ms * 1000, utc_time_ms * 1000);
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+ const auto& start_log_events = parsed_log_.start_log_events();
+
+ ASSERT_EQ(start_log_events.size(), 1u);
+ verifier_.VerifyLoggedStartEvent(timestamp_ms * 1000, utc_time_ms * 1000,
+ start_log_events[0]);
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventLoggingStopped) {
+ const int64_t start_timestamp_ms = prng_.Rand(1'000'000'000);
+ const int64_t start_utc_time_ms = prng_.Rand(1'000'000'000);
+
+ // Overwrite the previously encoded LogStart event.
+ encoded_ = encoder_->EncodeLogStart(start_timestamp_ms * 1000,
+ start_utc_time_ms * 1000);
+
+ const int64_t stop_timestamp_ms =
+ prng_.Rand(start_timestamp_ms, 2'000'000'000);
+ encoded_ += encoder_->EncodeLogEnd(stop_timestamp_ms * 1000);
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+ const auto& stop_log_events = parsed_log_.stop_log_events();
+
+ ASSERT_EQ(stop_log_events.size(), 1u);
+ verifier_.VerifyLoggedStopEvent(stop_timestamp_ms * 1000, stop_log_events[0]);
+}
+
+// TODO(eladalon/terelius): Test with multiple events in the batch.
+TEST_P(RtcEventLogEncoderTest, RtcEventProbeClusterCreated) {
+ std::unique_ptr<RtcEventProbeClusterCreated> event =
+ gen_.NewProbeClusterCreated();
+ history_.push_back(event->Copy());
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+ const auto& bwe_probe_cluster_created_events =
+ parsed_log_.bwe_probe_cluster_created_events();
+
+ ASSERT_EQ(bwe_probe_cluster_created_events.size(), 1u);
+ verifier_.VerifyLoggedBweProbeClusterCreatedEvent(
+ *event, bwe_probe_cluster_created_events[0]);
+}
+
+// TODO(eladalon/terelius): Test with multiple events in the batch.
+TEST_P(RtcEventLogEncoderTest, RtcEventProbeResultFailure) {
+ std::unique_ptr<RtcEventProbeResultFailure> event =
+ gen_.NewProbeResultFailure();
+ history_.push_back(event->Copy());
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+ const auto& bwe_probe_failure_events = parsed_log_.bwe_probe_failure_events();
+
+ ASSERT_EQ(bwe_probe_failure_events.size(), 1u);
+ verifier_.VerifyLoggedBweProbeFailureEvent(*event,
+ bwe_probe_failure_events[0]);
+}
+
+// TODO(eladalon/terelius): Test with multiple events in the batch.
+TEST_P(RtcEventLogEncoderTest, RtcEventProbeResultSuccess) {
+ std::unique_ptr<RtcEventProbeResultSuccess> event =
+ gen_.NewProbeResultSuccess();
+ history_.push_back(event->Copy());
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+ const auto& bwe_probe_success_events = parsed_log_.bwe_probe_success_events();
+
+ ASSERT_EQ(bwe_probe_success_events.size(), 1u);
+ verifier_.VerifyLoggedBweProbeSuccessEvent(*event,
+ bwe_probe_success_events[0]);
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventRtcpPacketIncoming) {
+ if (force_repeated_fields_) {
+ // RTCP packets maybe delivered twice (once for audio and once for video).
+ // As a work around, we're removing duplicates in the parser.
+ return;
+ }
+
+ std::vector<std::unique_ptr<RtcEventRtcpPacketIncoming>> events(event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ events[i] = (i == 0 || !force_repeated_fields_)
+ ? gen_.NewRtcpPacketIncoming()
+ : events[0]->Copy();
+ history_.push_back(events[i]->Copy());
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ const auto& incoming_rtcp_packets = parsed_log_.incoming_rtcp_packets();
+ ASSERT_EQ(incoming_rtcp_packets.size(), event_count_);
+
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedRtcpPacketIncoming(*events[i],
+ incoming_rtcp_packets[i]);
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventRtcpPacketOutgoing) {
+ std::vector<std::unique_ptr<RtcEventRtcpPacketOutgoing>> events(event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ events[i] = (i == 0 || !force_repeated_fields_)
+ ? gen_.NewRtcpPacketOutgoing()
+ : events[0]->Copy();
+ history_.push_back(events[i]->Copy());
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ const auto& outgoing_rtcp_packets = parsed_log_.outgoing_rtcp_packets();
+ ASSERT_EQ(outgoing_rtcp_packets.size(), event_count_);
+
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedRtcpPacketOutgoing(*events[i],
+ outgoing_rtcp_packets[i]);
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventRtcpReceiverReport) {
+ if (force_repeated_fields_) {
+ return;
+ }
+
+ rtc::ScopedFakeClock fake_clock;
+ fake_clock.SetTime(Timestamp::Millis(prng_.Rand<uint32_t>()));
+
+ for (auto direction : {kIncomingPacket, kOutgoingPacket}) {
+ std::vector<rtcp::ReceiverReport> events(event_count_);
+ std::vector<int64_t> timestamps_ms(event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ timestamps_ms[i] = rtc::TimeMillis();
+ events[i] = gen_.NewReceiverReport();
+ rtc::Buffer buffer = events[i].Build();
+ if (direction == kIncomingPacket) {
+ history_.push_back(
+ std::make_unique<RtcEventRtcpPacketIncoming>(buffer));
+ } else {
+ history_.push_back(
+ std::make_unique<RtcEventRtcpPacketOutgoing>(buffer));
+ }
+ fake_clock.AdvanceTime(TimeDelta::Millis(prng_.Rand(0, 1000)));
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ const auto& receiver_reports = parsed_log_.receiver_reports(direction);
+ ASSERT_EQ(receiver_reports.size(), event_count_);
+
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedReceiverReport(timestamps_ms[i], events[i],
+ receiver_reports[i]);
+ }
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventRtcpSenderReport) {
+ if (force_repeated_fields_) {
+ return;
+ }
+
+ rtc::ScopedFakeClock fake_clock;
+ fake_clock.SetTime(Timestamp::Millis(prng_.Rand<uint32_t>()));
+
+ for (auto direction : {kIncomingPacket, kOutgoingPacket}) {
+ std::vector<rtcp::SenderReport> events(event_count_);
+ std::vector<int64_t> timestamps_ms(event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ timestamps_ms[i] = rtc::TimeMillis();
+ events[i] = gen_.NewSenderReport();
+ rtc::Buffer buffer = events[i].Build();
+ if (direction == kIncomingPacket) {
+ history_.push_back(
+ std::make_unique<RtcEventRtcpPacketIncoming>(buffer));
+ } else {
+ history_.push_back(
+ std::make_unique<RtcEventRtcpPacketOutgoing>(buffer));
+ }
+ fake_clock.AdvanceTime(TimeDelta::Millis(prng_.Rand(0, 1000)));
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ const auto& sender_reports = parsed_log_.sender_reports(direction);
+ ASSERT_EQ(sender_reports.size(), event_count_);
+
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedSenderReport(timestamps_ms[i], events[i],
+ sender_reports[i]);
+ }
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventRtcpExtendedReports) {
+ if (force_repeated_fields_) {
+ return;
+ }
+
+ rtc::ScopedFakeClock fake_clock;
+ fake_clock.SetTime(Timestamp::Millis(prng_.Rand<uint32_t>()));
+
+ for (auto direction : {kIncomingPacket, kOutgoingPacket}) {
+ std::vector<rtcp::ExtendedReports> events(event_count_);
+ std::vector<int64_t> timestamps_ms(event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ timestamps_ms[i] = rtc::TimeMillis();
+ events[i] = gen_.NewExtendedReports();
+ rtc::Buffer buffer = events[i].Build();
+ if (direction == kIncomingPacket) {
+ history_.push_back(
+ std::make_unique<RtcEventRtcpPacketIncoming>(buffer));
+ } else {
+ history_.push_back(
+ std::make_unique<RtcEventRtcpPacketOutgoing>(buffer));
+ }
+ fake_clock.AdvanceTime(TimeDelta::Millis(prng_.Rand(0, 1000)));
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ const auto& extended_reports = parsed_log_.extended_reports(direction);
+ ASSERT_EQ(extended_reports.size(), event_count_);
+
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedExtendedReports(timestamps_ms[i], events[i],
+ extended_reports[i]);
+ }
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventRtcpFir) {
+ if (force_repeated_fields_) {
+ return;
+ }
+
+ rtc::ScopedFakeClock fake_clock;
+ fake_clock.SetTime(Timestamp::Millis(prng_.Rand<uint32_t>()));
+
+ for (auto direction : {kIncomingPacket, kOutgoingPacket}) {
+ std::vector<rtcp::Fir> events(event_count_);
+ std::vector<int64_t> timestamps_ms(event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ timestamps_ms[i] = rtc::TimeMillis();
+ events[i] = gen_.NewFir();
+ rtc::Buffer buffer = events[i].Build();
+ if (direction == kIncomingPacket) {
+ history_.push_back(
+ std::make_unique<RtcEventRtcpPacketIncoming>(buffer));
+ } else {
+ history_.push_back(
+ std::make_unique<RtcEventRtcpPacketOutgoing>(buffer));
+ }
+ fake_clock.AdvanceTime(TimeDelta::Millis(prng_.Rand(0, 1000)));
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ const auto& firs = parsed_log_.firs(direction);
+ ASSERT_EQ(firs.size(), event_count_);
+
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedFir(timestamps_ms[i], events[i], firs[i]);
+ }
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventRtcpPli) {
+ if (force_repeated_fields_) {
+ return;
+ }
+
+ rtc::ScopedFakeClock fake_clock;
+ fake_clock.SetTime(Timestamp::Millis(prng_.Rand<uint32_t>()));
+
+ for (auto direction : {kIncomingPacket, kOutgoingPacket}) {
+ std::vector<rtcp::Pli> events(event_count_);
+ std::vector<int64_t> timestamps_ms(event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ timestamps_ms[i] = rtc::TimeMillis();
+ events[i] = gen_.NewPli();
+ rtc::Buffer buffer = events[i].Build();
+ if (direction == kIncomingPacket) {
+ history_.push_back(
+ std::make_unique<RtcEventRtcpPacketIncoming>(buffer));
+ } else {
+ history_.push_back(
+ std::make_unique<RtcEventRtcpPacketOutgoing>(buffer));
+ }
+ fake_clock.AdvanceTime(TimeDelta::Millis(prng_.Rand(0, 1000)));
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ const auto& plis = parsed_log_.plis(direction);
+ ASSERT_EQ(plis.size(), event_count_);
+
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedPli(timestamps_ms[i], events[i], plis[i]);
+ }
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventRtcpBye) {
+ if (force_repeated_fields_) {
+ return;
+ }
+
+ rtc::ScopedFakeClock fake_clock;
+ fake_clock.SetTime(Timestamp::Millis(prng_.Rand<uint32_t>()));
+
+ for (auto direction : {kIncomingPacket, kOutgoingPacket}) {
+ std::vector<rtcp::Bye> events(event_count_);
+ std::vector<int64_t> timestamps_ms(event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ timestamps_ms[i] = rtc::TimeMillis();
+ events[i] = gen_.NewBye();
+ rtc::Buffer buffer = events[i].Build();
+ if (direction == kIncomingPacket) {
+ history_.push_back(
+ std::make_unique<RtcEventRtcpPacketIncoming>(buffer));
+ } else {
+ history_.push_back(
+ std::make_unique<RtcEventRtcpPacketOutgoing>(buffer));
+ }
+ fake_clock.AdvanceTime(TimeDelta::Millis(prng_.Rand(0, 1000)));
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ const auto& byes = parsed_log_.byes(direction);
+ ASSERT_EQ(byes.size(), event_count_);
+
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedBye(timestamps_ms[i], events[i], byes[i]);
+ }
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventRtcpNack) {
+ if (force_repeated_fields_) {
+ return;
+ }
+
+ rtc::ScopedFakeClock fake_clock;
+ fake_clock.SetTime(Timestamp::Millis(prng_.Rand<uint32_t>()));
+
+ for (auto direction : {kIncomingPacket, kOutgoingPacket}) {
+ std::vector<rtcp::Nack> events(event_count_);
+ std::vector<int64_t> timestamps_ms(event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ timestamps_ms[i] = rtc::TimeMillis();
+ events[i] = gen_.NewNack();
+ rtc::Buffer buffer = events[i].Build();
+ if (direction == kIncomingPacket) {
+ history_.push_back(
+ std::make_unique<RtcEventRtcpPacketIncoming>(buffer));
+ } else {
+ history_.push_back(
+ std::make_unique<RtcEventRtcpPacketOutgoing>(buffer));
+ }
+ fake_clock.AdvanceTime(TimeDelta::Millis(prng_.Rand(0, 1000)));
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ const auto& nacks = parsed_log_.nacks(direction);
+ ASSERT_EQ(nacks.size(), event_count_);
+
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedNack(timestamps_ms[i], events[i], nacks[i]);
+ }
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventRtcpRemb) {
+ if (force_repeated_fields_) {
+ return;
+ }
+
+ rtc::ScopedFakeClock fake_clock;
+ fake_clock.SetTime(Timestamp::Millis(prng_.Rand<uint32_t>()));
+
+ for (auto direction : {kIncomingPacket, kOutgoingPacket}) {
+ std::vector<rtcp::Remb> events(event_count_);
+ std::vector<int64_t> timestamps_ms(event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ timestamps_ms[i] = rtc::TimeMillis();
+ events[i] = gen_.NewRemb();
+ rtc::Buffer buffer = events[i].Build();
+ if (direction == kIncomingPacket) {
+ history_.push_back(
+ std::make_unique<RtcEventRtcpPacketIncoming>(buffer));
+ } else {
+ history_.push_back(
+ std::make_unique<RtcEventRtcpPacketOutgoing>(buffer));
+ }
+ fake_clock.AdvanceTime(TimeDelta::Millis(prng_.Rand(0, 1000)));
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ const auto& rembs = parsed_log_.rembs(direction);
+ ASSERT_EQ(rembs.size(), event_count_);
+
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedRemb(timestamps_ms[i], events[i], rembs[i]);
+ }
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventRtcpTransportFeedback) {
+ if (force_repeated_fields_) {
+ return;
+ }
+
+ rtc::ScopedFakeClock fake_clock;
+ fake_clock.SetTime(Timestamp::Millis(prng_.Rand<uint32_t>()));
+
+ for (auto direction : {kIncomingPacket, kOutgoingPacket}) {
+ std::vector<rtcp::TransportFeedback> events;
+ events.reserve(event_count_);
+ std::vector<int64_t> timestamps_ms(event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ timestamps_ms[i] = rtc::TimeMillis();
+ events.emplace_back(gen_.NewTransportFeedback());
+ rtc::Buffer buffer = events[i].Build();
+ if (direction == kIncomingPacket) {
+ history_.push_back(
+ std::make_unique<RtcEventRtcpPacketIncoming>(buffer));
+ } else {
+ history_.push_back(
+ std::make_unique<RtcEventRtcpPacketOutgoing>(buffer));
+ }
+ fake_clock.AdvanceTime(TimeDelta::Millis(prng_.Rand(0, 1000)));
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ const auto& transport_feedbacks =
+ parsed_log_.transport_feedbacks(direction);
+ ASSERT_EQ(transport_feedbacks.size(), event_count_);
+
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedTransportFeedback(timestamps_ms[i], events[i],
+ transport_feedbacks[i]);
+ }
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventRtcpLossNotification) {
+ if (force_repeated_fields_) {
+ return;
+ }
+
+ rtc::ScopedFakeClock fake_clock;
+ fake_clock.SetTime(Timestamp::Millis(prng_.Rand<uint32_t>()));
+
+ for (auto direction : {kIncomingPacket, kOutgoingPacket}) {
+ std::vector<rtcp::LossNotification> events;
+ events.reserve(event_count_);
+ std::vector<int64_t> timestamps_ms(event_count_);
+ for (size_t i = 0; i < event_count_; ++i) {
+ timestamps_ms[i] = rtc::TimeMillis();
+ events.emplace_back(gen_.NewLossNotification());
+ rtc::Buffer buffer = events[i].Build();
+ if (direction == kIncomingPacket) {
+ history_.push_back(
+ std::make_unique<RtcEventRtcpPacketIncoming>(buffer));
+ } else {
+ history_.push_back(
+ std::make_unique<RtcEventRtcpPacketOutgoing>(buffer));
+ }
+ fake_clock.AdvanceTime(TimeDelta::Millis(prng_.Rand(0, 1000)));
+ }
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+
+ const auto& loss_notifications = parsed_log_.loss_notifications(direction);
+ ASSERT_EQ(loss_notifications.size(), event_count_);
+
+ for (size_t i = 0; i < event_count_; ++i) {
+ verifier_.VerifyLoggedLossNotification(timestamps_ms[i], events[i],
+ loss_notifications[i]);
+ }
+ }
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventRtpPacketIncoming) {
+ TestRtpPackets<RtcEventRtpPacketIncoming, LoggedRtpPacketIncoming>();
+}
+
+TEST_P(RtcEventLogEncoderTest, RtcEventRtpPacketOutgoing) {
+ TestRtpPackets<RtcEventRtpPacketOutgoing, LoggedRtpPacketOutgoing>();
+}
+
+// TODO(eladalon/terelius): Test with multiple events in the batch.
+TEST_P(RtcEventLogEncoderTest, RtcEventVideoReceiveStreamConfig) {
+ uint32_t ssrc = prng_.Rand<uint32_t>();
+ RtpHeaderExtensionMap extensions = gen_.NewRtpHeaderExtensionMap();
+ std::unique_ptr<RtcEventVideoReceiveStreamConfig> event =
+ gen_.NewVideoReceiveStreamConfig(ssrc, extensions);
+ history_.push_back(event->Copy());
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+ const auto& video_recv_configs = parsed_log_.video_recv_configs();
+
+ ASSERT_EQ(video_recv_configs.size(), 1u);
+ verifier_.VerifyLoggedVideoRecvConfig(*event, video_recv_configs[0]);
+}
+
+// TODO(eladalon/terelius): Test with multiple events in the batch.
+TEST_P(RtcEventLogEncoderTest, RtcEventVideoSendStreamConfig) {
+ uint32_t ssrc = prng_.Rand<uint32_t>();
+ RtpHeaderExtensionMap extensions = gen_.NewRtpHeaderExtensionMap();
+ std::unique_ptr<RtcEventVideoSendStreamConfig> event =
+ gen_.NewVideoSendStreamConfig(ssrc, extensions);
+ history_.push_back(event->Copy());
+
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+ ASSERT_TRUE(parsed_log_.ParseString(encoded_).ok());
+ const auto& video_send_configs = parsed_log_.video_send_configs();
+
+ ASSERT_EQ(video_send_configs.size(), 1u);
+ verifier_.VerifyLoggedVideoSendConfig(*event, video_send_configs[0]);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ RandomSeeds,
+ RtcEventLogEncoderTest,
+ ::testing::Combine(/* Random seed*: */ ::testing::Values(1, 2, 3, 4, 5),
+ /* Encoding: */
+ ::testing::Values(RtcEventLog::EncodingType::Legacy,
+ RtcEventLog::EncodingType::NewFormat),
+ /* Event count: */ ::testing::Values(1, 2, 10, 100),
+ /* Repeated fields: */ ::testing::Bool()));
+
+class RtcEventLogEncoderSimpleTest
+ : public ::testing::TestWithParam<RtcEventLog::EncodingType> {
+ protected:
+ RtcEventLogEncoderSimpleTest() : encoding_type_(GetParam()) {
+ switch (encoding_type_) {
+ case RtcEventLog::EncodingType::Legacy:
+ encoder_ = std::make_unique<RtcEventLogEncoderLegacy>();
+ break;
+ case RtcEventLog::EncodingType::NewFormat:
+ encoder_ = std::make_unique<RtcEventLogEncoderNewFormat>();
+ break;
+ case RtcEventLog::EncodingType::ProtoFree:
+ encoder_ = std::make_unique<RtcEventLogEncoderV3>();
+ break;
+ }
+ encoded_ =
+ encoder_->EncodeLogStart(rtc::TimeMillis(), rtc::TimeUTCMillis());
+ }
+ ~RtcEventLogEncoderSimpleTest() override = default;
+
+ std::deque<std::unique_ptr<RtcEvent>> history_;
+ std::unique_ptr<RtcEventLogEncoder> encoder_;
+ ParsedRtcEventLog parsed_log_;
+ const RtcEventLog::EncodingType encoding_type_;
+ std::string encoded_;
+};
+
+TEST_P(RtcEventLogEncoderSimpleTest, RtcEventLargeCompoundRtcpPacketIncoming) {
+ // Create a compound packet containing multiple Bye messages.
+ rtc::Buffer packet;
+ size_t index = 0;
+ for (int i = 0; i < 8; i++) {
+ rtcp::Bye bye;
+ std::string reason(255, 'a'); // Add some arbitrary data.
+ bye.SetReason(reason);
+ bye.SetSenderSsrc(0x12345678);
+ packet.SetSize(packet.size() + bye.BlockLength());
+ bool created =
+ bye.Create(packet.data(), &index, packet.capacity(), nullptr);
+ ASSERT_TRUE(created);
+ ASSERT_EQ(index, packet.size());
+ }
+
+ EXPECT_GT(packet.size(), static_cast<size_t>(IP_PACKET_SIZE));
+ auto event = std::make_unique<RtcEventRtcpPacketIncoming>(packet);
+ history_.push_back(event->Copy());
+ encoded_ += encoder_->EncodeBatch(history_.begin(), history_.end());
+
+ ParsedRtcEventLog::ParseStatus status = parsed_log_.ParseString(encoded_);
+ ASSERT_TRUE(status.ok()) << status.message();
+
+ const auto& incoming_rtcp_packets = parsed_log_.incoming_rtcp_packets();
+ ASSERT_EQ(incoming_rtcp_packets.size(), 1u);
+ ASSERT_EQ(incoming_rtcp_packets[0].rtcp.raw_data.size(), packet.size());
+ EXPECT_EQ(memcmp(incoming_rtcp_packets[0].rtcp.raw_data.data(), packet.data(),
+ packet.size()),
+ 0);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ LargeCompoundRtcp,
+ RtcEventLogEncoderSimpleTest,
+ ::testing::Values(RtcEventLog::EncodingType::Legacy,
+ RtcEventLog::EncodingType::NewFormat));
+
+} // namespace webrtc